/* ============================================================ 家书 FamilyBook · 界面样式 配色取自图标:宣纸米 / 砖红封面 / 深褐书脊 / 纸白 原则:扁平、留白充足、字重克制(正文 400,标题 500-600) ============================================================ */ :root { --paper: #F7F3EA; --surface: #FFFFFF; --ink: #2C231D; --muted: #7E6B5A; --faint: #A8988A; --brand: #A84A28; --brand-2: #C96F45; --brand-ink:#6E3820; --green: #6F9E5C; --red: #C0533F; --line: rgba(44, 35, 29, .10); --line-2: rgba(44, 35, 29, .06); --radius: 16px; --radius-s: 10px; --shadow: 0 1px 2px rgba(44, 35, 29, .05); --shadow-l: 0 4px 16px rgba(44, 35, 29, .08); /* ---- 动作按钮的统一度量 ---- 用户提过三次「设置按钮和记一场电影按钮高度要一致」。为杜绝再犯, 这里把按钮高度拆成 4 个变量,并算出唯一的高度: --act-h = 上下内边距 + 行高 + 上下边框 = 9*2 + 14*1.7 + 1*2 = 43.8px 文字按钮靠 padding + line-height 自然撑到这个高度; **只有图标的方形按钮**(.btn-icon)显式写高度,但取的是同一个 --act-h, 这样两边永远相等,改一处就同步。 */ --act-pad-y: 9px; --act-pad-x: 16px; --act-font: 14px; --act-line: 1.7; --act-h: calc(var(--act-pad-y) * 2 + var(--act-font) * var(--act-line) + 1px * 2); } * { box-sizing: border-box; } html, body { height: 100%; } body { margin: 0; background: var(--paper); color: var(--ink); font: 15px/1.7 -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", sans-serif; -webkit-font-smoothing: antialiased; } a { color: var(--brand); text-decoration: none; } a:hover { text-decoration: underline; } /* ---------------- 顶栏 ---------------- */ .topbar { position: sticky; top: 0; z-index: 20; background: var(--surface); border-bottom: 1px solid var(--line); } .topbar-inner { max-width: 1080px; margin: 0 auto; padding: 0 24px; height: 60px; display: flex; align-items: center; gap: 24px; } .brand { display: flex; align-items: center; gap: 10px; color: var(--ink); } .brand:hover { text-decoration: none; } .brand img { width: 30px; height: 30px; border-radius: 8px; display: block; } .brand span { font-size: 16px; font-weight: 600; letter-spacing: .5px; } .nav { display: flex; gap: 4px; margin-left: 8px; } .nav a { padding: 6px 12px; border-radius: 999px; color: var(--muted); font-size: 14px; } .nav a:hover { background: var(--line-2); text-decoration: none; } .nav a.on { background: var(--brand); color: #fff; } .topbar-right { margin-left: auto; display: flex; align-items: center; gap: 12px; } .who { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 14px; } /* 姓名 + 头像是个下拉菜单的「把手」——hover 时给点反馈,让人知道它可点 */ .who-menu { position: relative; } .who-menu > summary { list-style: none; cursor: pointer; padding: 4px 6px; border-radius: 999px; } .who-menu > summary::-webkit-details-marker { display: none; } .who-menu > summary:hover { } /* 统一段处理 */ .who-menu > summary:hover .who-name { color: var(--brand); } .who-menu[open] > summary { background: var(--line-2); } .who-menu[open] > summary .caret { transform: rotate(180deg); } .who img { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; } .who img { transition: box-shadow .15s ease; } .who-menu > summary:hover img { box-shadow: 0 0 0 2px var(--brand-2); } /* 那个小三角:没有它,用户不知道"名字能点开" */ .caret { width: 0; height: 0; margin-left: -2px; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid var(--faint); transition: transform .15s ease; } .menu-list { position: absolute; right: 0; top: calc(100% + 8px); z-index: 30; min-width: 148px; padding: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-s); box-shadow: var(--shadow-l); display: flex; flex-direction: column; } .menu-list a { padding: 8px 12px; border-radius: 8px; color: var(--ink); font-size: 14px; white-space: nowrap; } .menu-list a:hover { background: var(--line-2); text-decoration: none; color: var(--brand); } /* 退出放在最下面并隔一条线:它是"离开",和上面几个"改点什么"不是一类动作 */ .menu-list .menu-exit { margin-top: 4px; padding-top: 9px; border-top: 1px solid var(--line-2); border-radius: 0 0 8px 8px; color: var(--muted); } .menu-list .menu-exit:hover { color: var(--brand); } /* ---- 观影页的「设置」下拉:图标按钮 + 永远贴右 ---- 用户明确要求(2026-10-01): · 去掉「设置」二字,只留**三条横线**图标(☰); · 在**所有页面都排在最右侧**(位置统一); · 点开后的每条条目,**文字前都要有图标**; · 除设置外,其它按钮都要**有颜色**,跟设置区分开。 贴右的做法:`margin-left:auto` 把它推到动作区最右端 —— 这样无论各页把片段 require 在 actions 的哪个位置(有的写在最前、有的写在最后),它都贴右, 不必逐页调顺序,也就不会有"某个页面漏了"的情况。 */ .movie-set { position: relative; margin-left: auto; } .movie-set > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 6px; } .movie-set > summary::-webkit-details-marker { display: none; } .movie-set[open] > summary { border-color: var(--brand); color: var(--brand); } /* 方形图标按钮:只有图标、无文字。高度**必须**取 --act-h, 与「记一场电影」等实心按钮同高(那个高度由 --act-pad-y/--act-font/--act-line 推出)。 ⚠️ 别在这里另写一个数字(曾写死 40px,比文字按钮的 43.8px 矮了 3.8px,被用户连续投诉)。 */ .btn-icon { background: #fff; color: var(--ink); border: 1px solid var(--line); padding: 0 !important; width: var(--act-h); height: var(--act-h); min-width: var(--act-h); display: inline-flex; align-items: center; justify-content: center; line-height: 1; } .btn-icon:hover { color: var(--brand); border-color: var(--brand); } /* 底色交给统一段 */ .btn-icon svg { display: block; } /* 观影设置菜单:从右上角改成**按钮下方右对齐**(按钮贴右,菜单也贴右才不会跑出屏外) */ .movie-set .menu-list { left: auto; right: 0; top: calc(100% + 8px); } .movie-set .menu-list a { display: flex; align-items: center; gap: 10px; } .movie-set .menu-list a .mi { flex: 0 0 16px; color: var(--muted); } .movie-set .menu-list a:hover .mi, .movie-set .menu-list a.on .mi { color: currentColor; } .movie-set .menu-list a.on { color: var(--brand); background: var(--line-2); } /* ---- 日程页的设置下拉(v24 用户 2026-10-03 第 4、5 条)---- 它比观影/任务那个 ☰ 内容多:里面有个**表单**(默认视图 + 显示哪些本子)。 本子清单会随共享/新建变长,所以给列表一个最大高度 + 滚动, 否则菜单会顶出屏幕、底下的「保存」按钮够不着。 */ .cal-set .menu-list { min-width: 260px; max-width: min(320px, 92vw); } .cal-set-list { padding: 10px; gap: 0; } .cal-set-form { display: block; } .cal-set-block { padding: 6px 2px; } .cal-set-block + .cal-set-block { border-top: 1px solid var(--line-2); margin-top: 4px; } .cal-set-h { font-size: 12px; color: var(--muted); margin-bottom: 6px; } .cal-set-views { display: flex; gap: 12px; flex-wrap: wrap; } .cal-set-view { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; cursor: pointer; } .cal-set-view input { accent-color: var(--brand); } /* 本子清单:最多占 42vh,超了内部滚动 —— 菜单本身不滚,否则保存按钮会被推出可视区 */ .cal-set-books { max-height: 42vh; overflow-y: auto; } .cal-set-book { display: flex; align-items: center; gap: 7px; padding: 5px 4px; font-size: 13px; cursor: pointer; border-radius: 6px; white-space: normal; } .cal-set-book:hover { background: var(--line-2); } .cal-set-book input { accent-color: var(--brand); flex: 0 0 auto; } .cal-set-book .color-preview { width: 11px; height: 11px; border-radius: 3px; flex: 0 0 auto; } .cal-set-book .cal-name { overflow: hidden; text-overflow: ellipsis; } .cal-set-book .node-badge { margin-left: auto; flex: 0 0 auto; } .cal-set-foot { margin-top: 10px; } .cal-set-foot .btn { width: 100%; justify-content: center; } .cal-set-links { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line-2); } .cal-set-links a { display: flex; align-items: center; gap: 10px; white-space: normal; } .cal-set-links a .mi { flex: 0 0 16px; color: var(--muted); } .cal-set-links a:hover .mi { color: currentColor; } /* 点了日历空白处就建日程 —— 鼠标要能看出那里可点(用户第 5 条)。 ⚠️ 只给「能写」的人光标:只读账号点上去弹不出表单,给个十字光标是骗人。 */ .tg-col.rt-writable, .cal-cell.rt-writable { cursor: crosshair; } .link-quiet { color: var(--muted); font-size: 14px; } .link-quiet:hover { color: var(--brand); } /* 顶栏里的文字一律不许换行 —— 一换行就是「首/页」「名/册」竖排,很难看 */ .nav a, .topbar-right a, .who-name { white-space: nowrap; } /* ---------------- 布局 ---------------- */ .container { max-width: 1080px; margin: 0 auto; padding: 28px 24px 64px; } .container-narrow { max-width: 560px; margin: 0 auto; padding: 28px 24px 64px; } .page-head { display: flex; align-items: flex-end; gap: 16px; margin-bottom: 20px; } .page-head h1 { font-size: 22px; font-weight: 600; margin: 0; } .page-head .sub { color: var(--muted); font-size: 14px; } .page-head .actions { margin-left: auto; display: flex; gap: 8px; align-items: center; } /* ---- 页头动作区的完整规格见**文件靠后**的「页头动作区」一节 ---- 刻意放后面:.btn / .btn-ghost / .btn-icon 等基类定义在文件中部, CSS 同特异性「后写的赢」——放这里会被它们覆盖(踩过:设置按钮被染成品牌底、 btn-ghost 仍是白的)。此处只留布局,样式集中在后面一处,便于对照。 */ .section { margin-top: 28px; } .section-title { display: flex; align-items: center; gap: 10px; font-size: 15px; font-weight: 600; margin: 0 0 12px; } .section-title .hint { font-weight: 400; color: var(--muted); font-size: 13px; } /* ---------------- 卡片 ---------------- */ .card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); } .card-pad { padding: 20px; } /* 相邻的两块之间一定要有缝 —— 影院管理页「添加影院」与「影院列表」、 年度报告的几张卡,曾经紧贴成一个整块,看不出是两个东西。 统一在这里给间距,而不是每个页面各写一次(写一次就会漏一处)。 */ .card + .card { margin-top: 18px; } /* ⚠️ 上面**只写 .card + .card**:.section 自己有 margin-top:28px, 若在这里再写 `.card + .section { margin-top:18px }`,特异性 0,2,0 会压过 `.section` 的 0,1,0,反而把原本 28px 的间距改小 —— 修 A 不能顺手弄坏 B。 */ /* 首页头图 */ .hero { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 26px 28px; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; } .hero-logo { width: 64px; height: 64px; border-radius: 16px; flex: 0 0 auto; } .hero-main { flex: 1 1 240px; min-width: 0; } .hero h1 { font-size: 24px; font-weight: 600; margin: 0 0 4px; } .hero .motto { color: var(--muted); margin: 0; font-size: 14px; } /* 那句小字到底是谁的:自己写的那句要标一下,否则用户会以为这是系统统一发的 */ .motto-from { display: inline-block; margin-left: 6px; padding: 0 7px; border-radius: 999px; background: var(--line-2); font-size: 11px; color: var(--faint); } .motto-edit { margin-left: 8px; font-size: 12px; color: var(--brand-2); white-space: nowrap; } /* 详情页顶部引用本人的一句话 */ .motto-line { margin: -8px 0 16px; padding-left: 12px; border-left: 3px solid var(--brand-2); color: var(--muted); font-size: 14px; } /* ⚠️ .hero-stats / .stat 已随用户 2026-10-03 第 3 条撤掉首页那三个统计, 这里保留规则本身(其它页面可能还在用 .stat 这个类名),不再由首页引用。 */ .hero-stats { margin-left: auto; display: flex; gap: 28px; } .stat { text-align: center; } .stat b { display: block; font-size: 24px; font-weight: 600; line-height: 1.2; } .stat span { font-size: 12px; color: var(--muted); } /* 用户第 3 条:标题下那句个人 slogan */ .hero-slogan { color: var(--muted); margin: 4px 0 0; font-size: 14px; } /* 用户第 3 条:「我的资料 / 我的家谱」换成**图标按钮**(原来是一排文字按钮)。 margin-left:auto 把它推到最右,与原来统计块的位置一致。 */ .hero-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; } .icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 12px; border: 1px solid var(--line); background: var(--surface); color: var(--muted); transition: border-color .15s ease, color .15s ease, background .15s ease; } .icon-btn:hover { color: var(--brand); border-color: var(--brand-2); /* 底色交给文末「全站悬停统一」段(中性薄雾) */ background: var(--hov-veil, rgba(44,35,29,.05)); } .icon-btn svg { display: block; } /* ---- 用户第 3 条:首页的统计信息块(问候 + 天气 + 几个数字) ---- */ /* ⚠️ v24 第 24 批:这里原有 6 条 `.home-brief*` 旧规则(纵向排列的问候/天气/数字), 本批把首页信息面板改成"左边内容 + 右边天气贴右端"的横排结构后, 那 6 条**全部成了死样式**(模板里已经没有任何元素用 home-brief-hello / home-brief-weather / home-brief-stats / w-icon / w-temp / w-desc)。 ⇒ 就地删掉,不留"两处定义同一个东西": 上批(第 23 批)就是因为"末尾追加、旧规则留着",漏了 flex-direction, 四个按钮竖排把标题行撑到 175px —— 那个坑的成因正是**旧规则没删**。 新结构见文件末尾「v24 第 24 批 …首页信息面板」那一段(.hb-* / .hb-weather)。 */ /* ---- 用户第 3 条:首页的模块小结卡 ---- */ .mod-cards { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); } .mod-card { display: flex; flex-direction: column; gap: 6px; padding: 14px 16px; border-radius: var(--radius); border: 1px solid var(--line); background: var(--surface); border-left: 3px solid var(--mc, var(--brand)); color: var(--ink); text-decoration: none; transition: box-shadow .15s ease, border-color .15s ease; } .mod-card:hover { box-shadow: var(--shadow); text-decoration: none; } .mod-card-head { display: flex; align-items: center; gap: 8px; color: var(--mc, var(--brand)); } .mod-card-icon { display: inline-flex; flex: 0 0 auto; } .mod-card-name { font-size: 13px; font-weight: 600; } .mod-card-big { font-size: 20px; font-weight: 600; color: var(--ink); } .mod-card-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; } .mod-card-lines li { font-size: 12.5px; color: var(--muted); } /* ---------------- 成员卡 ---------------- */ /* 用户 2026-10-03 第 4 条:名册从「卡片网格」改成**竖向列表**—— 一行一个人,左边头像、中间姓名+关系+手机号/生日、右边操作。 列表形态下拖动排序也更直观(上下移动,不会像网格那样左右跳)。 */ .people-grid { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; } /* ⚠️ 整行是**一条 flex**:[拖柄] [头像+信息(占满剩下的)] [右侧动作]。 原来右侧的「编辑 / ⋯」各自写死 right:46px / right:8px 绝对定位, 于是再往里加第三个按钮只能继续猜偏移、且「分组」按钮宽度随分组名变化必然重叠; 改 flex 之后按钮自然排开,姓名那边也不用靠 padding-right 让位了。 */ .person { display: flex; align-items: stretch; color: var(--ink); position: relative; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-s); transition: border-color .15s ease, box-shadow .15s ease; /* v24 第 9 批 #5:千人名册不「卡死」的关键 —— 屏外的卡片浏览器直接跳过 布局/绘制(4 万+ DOM 节点的排版是一次性成本的大头)。 contain-intrinsic-size 给屏外卡片一个估算高度撑滚动条, auto 前缀让浏览器记住真实渲染高度,滚过后就精确了。 ⚠️ 拖动排序用的是实时 getBoundingClientRect:目标被自动滚进视口时 content-visibility 会立刻出真实布局,落点计算不受影响(drag probe 已验证)。 */ content-visibility: auto; contain-intrinsic-size: auto 68px; } .person:hover { border-color: var(--brand-2); box-shadow: var(--shadow); } /* 头像:列表里用小方图(正方形、cover 裁切),不再是整块大图 */ .person .ph { background: #F1E7D6; overflow: hidden; width: 52px; height: 52px; border-radius: 8px; flex: 0 0 auto; } .person .ph img { width: 100%; height: 100%; object-fit: cover; display: block; } .person .person-main { display: flex; align-items: center; gap: 12px; padding: 10px 12px; color: var(--ink); text-decoration: none; flex: 1 1 auto; min-width: 0; /* 剩下的宽度都给它,按钮那头不挤它 */ } .person .pb { flex: 1 1 auto; min-width: 0; } .person .pname { font-weight: 600; font-size: 15px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .person .pmeta { color: var(--muted); font-size: 12.5px; margin-top: 2px; } /* 中间那几个"重要信息":手机 / 生日 */ .person .pfacts { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 4px; font-size: 13px; } .person .pfact { color: var(--ink); display: inline-flex; align-items: baseline; gap: 5px; } .person .pfact-k { color: var(--muted); font-size: 11px; padding: 1px 5px; border-radius: 4px; background: var(--line-2); } /* ⚠️⚠️ 过滤用的行必须能真的藏起来。 `[hidden]` 的 display:none 是**浏览器默认样式**,优先级最低 —— 只要作者样式里 给同一个元素写了 display(.person 是 display:block、.pp-item 是 flex、 .att-item 是 inline-flex…),就会把它盖掉:元素"标注了 hidden 却照样显示", 于是「输入即筛选」看着完全没反应(列表一条都不少)。 这个坑本项目已经踩过两次(.att-item、.pp-item),所以统一写成一条通则: 凡是靠 [hidden] 隐藏的列表行,都必须在作者样式里显式再声明一次。 */ [data-search][hidden], .pick-row[hidden], .person[hidden] { display: none !important; } .badge { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 500; line-height: 1.7; background: var(--line-2); color: var(--muted); } .badge-admin { background: #F6E3D7; color: var(--brand-ink); } .badge-open { background: #E6F0DC; color: #4A7226; } .badge-me { background: var(--brand); color: #fff; } /* 生日条 —— 一个人可能同时出两条(农历与阳历不在同一天), 落在同一天时并成一条、kinds 里挂多个标签,正好对应"要分别提醒"。 */ .bdays { display: flex; gap: 12px; flex-wrap: wrap; } .bday { display: flex; align-items: center; gap: 12px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 10px 18px 10px 10px; box-shadow: var(--shadow); color: var(--ink); text-decoration: none; transition: transform .15s ease, box-shadow .15s ease; } .bday:hover { transform: translateY(-2px); box-shadow: var(--shadow-l); text-decoration: none; } .bday img { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; } .bday b { font-weight: 500; } .bday .d { color: var(--brand); font-size: 13px; } .bday-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 3px; } .bday-date { font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums; } /* 「身份证生日 / 阳历生日 / 农历生日」的标签,首页与详情页共用 */ .bd-kind { display: inline-block; padding: 0 7px; border-radius: 999px; font-size: 11px; line-height: 1.8; background: var(--line-2); color: var(--muted); } .bd-kind-id { background: #E6F0DC; color: #4A7226; } .bd-kind-solar { background: #F6E3D7; color: var(--brand-ink); } .bd-kind-lunar { background: #E8E4F2; color: #55477E; } /* 详情页的三个生日 */ .bd-list { list-style: none; margin: 0; padding: 0; } .bd-list li { display: flex; align-items: baseline; gap: 8px; margin-bottom: 3px; flex-wrap: wrap; } .bd-list .bd-val { font-variant-numeric: tabular-nums; } /* ---------------- 表单 ---------------- */ .form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } /* ⚠️ min-width:0 是必需的(v11 实测踩到):.field 是 grid 项,默认 min-width:auto 会被内部 的 min-width:auto 约 150px(带日历图标 + 占位数字)—— 两个日期框 + 「至」在 360px 下会横向溢出(实测 scrollW 390 > clientW 360)。 给 0 并让它们等比收缩;这是继 .field(select)之后同一页的第二个溢出源。 */ .date-range .input { flex: 1 1 0; min-width: 0; } .results-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 0 2px 12px; } .results-head h3 { margin: 0; font-size: 17px; } .results-head .sub { color: var(--muted); font-size: 13px; } /* ---- 年度报告 ---- */ .year-pick { display: inline-block; } .report-grid { display: grid; gap: 14px; margin-bottom: 16px; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); } .report-grid.two { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } .report-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 18px 20px; box-shadow: var(--shadow); } .report-card .n { font-size: 32px; font-weight: 700; color: var(--brand); line-height: 1.15; font-variant-numeric: tabular-nums; } .report-card .l { font-size: 13px; color: var(--muted); } .report-card .v { font-size: 19px; font-weight: 600; margin-top: 2px; } .report-card .s { font-size: 12px; color: var(--faint); margin-top: 4px; } /* 月份竖柱图(纯 CSS) */ .vgrid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 6px; align-items: end; height: 160px; margin-top: 4px; } .vcol { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; gap: 3px; } .vcol .vbar { width: 68%; min-height: 2px; background: linear-gradient(var(--brand-2), var(--brand)); border-radius: 5px 5px 2px 2px; transition: height .4s ease; } .vcol .vlab { font-size: 11px; color: var(--muted); } .vcol .vnum { font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; } .kw-cloud { display: flex; flex-wrap: wrap; gap: 8px; } .kw-cloud .kw { font-size: 14px; padding: 4px 12px; border-radius: 999px; background: var(--line-2); color: var(--brand-ink); border: 1px solid var(--line); } /* ==================== v12:旅程 / 日程 / 资源 / 任务 ==================== */ /* 四个新模块共用一套"卡片骨架"(.card + .card-pad + .cinema-h2 的小标题), 下面只写各模块**特有**的部件,避免又造一套并行的卡片。 */ /* ---- 旅程 ---- */ .trip-list { display: flex; flex-direction: column; gap: 12px; } .trip-card { display: flex; gap: 16px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px; text-decoration: none; color: inherit; transition: transform .18s ease, box-shadow .18s ease; } .trip-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-l); text-decoration: none; } .trip-cover { width: 132px; height: 96px; flex: 0 0 auto; border-radius: var(--radius-s); overflow: hidden; background: #F1E7D6; display: flex; align-items: center; justify-content: center; } .trip-cover img { width: 100%; height: 100%; object-fit: cover; display: block; } .trip-cover-empty { font-size: 34px; opacity: .55; } .trip-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; } .trip-title { font-size: 17px; font-weight: 600; margin: 0; } .trip-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 13px; color: var(--muted); } .trip-sum { margin: 0; font-size: 14px; color: var(--muted); line-height: 1.6; } /* 老数据(只有 events.note、还没搬成 summary)时的一句提示 —— 见 templates/trips/show.php 里第 19 批 第 4 条的说明。 */ .trip-sum-legacy { margin: 0; font-size: 13px; color: var(--muted); background: #FDF3EC; border: 1px dashed rgba(168, 74, 40, .3); border-radius: var(--radius-s); padding: 8px 10px; } .trip-sum-legacy a { font-weight: 600; } .trip-people { display: flex; flex-wrap: wrap; gap: 4px 8px; } .trip-hero { display: flex; gap: 20px; flex-wrap: wrap; } /* ⚠️ v24 第 16 批 用户第 1 条:「题图应该按照 16:9 显示,因为添加的时候, 是按照这个比例裁剪的,现在的显示比例不对」—— 原来是 4:3 把 16:9 的图裁掉了两边。 这个数字必须与 ImageSlot 的 'cover' 槽位比例**一致**(部署断言专门比对两处)。 */ .trip-hero-cover { width: 420px; max-width: 100%; aspect-ratio: 16 / 9; flex: 0 0 auto; border-radius: var(--radius-s); overflow: hidden; background: #F1E7D6; display: flex; align-items: center; justify-content: center; } .trip-hero-cover img { width: 100%; height: 100%; object-fit: cover; display: block; } .trip-hero-body { flex: 1 1 320px; min-width: 0; display: flex; flex-direction: column; gap: 10px; } .trip-loc { margin: 0; font-size: 14px; color: var(--muted); } .cover-now { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; } .cover-now img { width: 96px; height: 64px; object-fit: cover; border-radius: 8px; display: block; } .photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; } .photo-cell { margin: 0; background: #F1E7D6; border-radius: var(--radius-s); overflow: hidden; } .photo-cell a { display: block; } .photo-cell img { width: 100%; height: 120px; object-fit: cover; display: block; } .photo-del { padding: 6px; text-align: center; } /* ---- 行程节点 ---- */ .node-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; } .node-row { display: flex; gap: 12px; align-items: flex-start; padding: 12px 14px; border: 1px solid var(--line-2); border-radius: var(--radius-s); background: #FDFBF7; } .node-row.status-cancelled { opacity: .62; } .node-row.status-done .node-head b { color: var(--muted); } .node-ico { font-size: 20px; flex: 0 0 auto; line-height: 1.4; } .node-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 5px; } .node-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .node-head b { font-size: 15px; } .node-badge { font-size: 11px; padding: 1px 8px; border-radius: 999px; background: var(--line-2); color: var(--muted); white-space: nowrap; } .node-row.status-done .node-badge { background: #E6F0DC; color: #4A7226; } .node-place, .node-time { font-size: 12px; color: var(--muted); } .node-detail { margin: 0; font-size: 14px; line-height: 1.65; } /* v24 第 19 批 用户第 7 条:动作按钮**横着排列**、放在行程**右上角**。 「计划行程的三个按钮,修改、完成、未成行,横着排列,放在行程右上角」 「实际行程的编辑和删除按钮,也放在行程的右上角」 ⚠️ 原来是 `flex-direction: column`(竖着一列、贴右下),用户要的是横排靠上。 改成 row + align-self:flex-start(贴着行的顶部对齐),窄屏再允许换行。 */ .node-ops { flex: 0 0 auto; display: flex; flex-direction: row; flex-wrap: wrap; gap: 6px; align-items: flex-start; justify-content: flex-end; align-self: flex-start; } .node-edit > summary { cursor: pointer; } .node-add { margin-top: 14px; border-top: 1px solid var(--line-2); padding-top: 12px; } .node-add > summary { cursor: pointer; font-size: 14px; } .node-form { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; } .nf-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } .nf-grid .field.full { grid-column: 1 / -1; } .nf-members { display: flex; flex-wrap: wrap; gap: 6px 16px; } /* ---- 附件行(旅程节点 / 资源 / 任务共用) ---- */ .attach-row { display: flex; flex-wrap: wrap; gap: 8px; } .attach-item { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--brand-ink); text-decoration: none; } .attach-item:hover { border-color: var(--brand); text-decoration: none; } /* ---- 日程(月视图,群晖 Calendar 的外观) ---- */ .cal-wrap { display: grid; grid-template-columns: 230px 1fr; gap: 18px; align-items: start; } /* 用户 2026-10-03 第 4 条:左侧「我的日历本」那一栏撤了,日历占满整宽。 ⚠️ 用**单列**改写而不是删掉 .cal-wrap:`.cal-wrap` 还被别处(窄屏媒体查询) 引用着,删掉会连带把那条声明变空。单列 = 视觉上就是"充满整个宽度"。 */ .cal-wrap-full { grid-template-columns: 1fr; } .cal-side { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px 18px; } .cal-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; } .cal-item { display: flex; flex-direction: column; gap: 4px; } .cal-item > .node-badge { align-self: flex-start; } .cal-toggle { display: flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; } .cal-toggle input { accent-color: var(--brand); } .cal-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .cal-note { font-size: 12px; color: var(--faint); line-height: 1.6; margin: 12px 0 0; } .cal-main { min-width: 0; } .cal-add { margin-bottom: 18px; } .cal-grid-card { padding: 0; overflow: hidden; } .cal-head { display: grid; grid-template-columns: repeat(7, 1fr); border-bottom: 1px solid var(--line); } .cal-wd { padding: 8px 0; text-align: center; font-size: 12px; color: var(--muted); } .cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); } .cal-cell { min-height: 104px; padding: 6px 6px 8px; border-right: 1px solid var(--line-2); border-bottom: 1px solid var(--line-2); display: flex; flex-direction: column; gap: 4px; } .cal-cell:nth-child(7n) { border-right: 0; } .cal-cell:nth-last-child(-n+7) { border-bottom: 0; } .cal-cell.is-out { background: #FCFAF6; } .cal-cell.is-out .cal-dnum { color: var(--faint); } .cal-cell.is-today { background: #FDF6F1; } .cal-dnum { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; padding-left: 2px; } .cal-cell.is-today .cal-dnum { color: #fff; background: var(--brand); border-radius: 999px; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; } .cal-evs { display: flex; flex-direction: column; gap: 3px; } .cal-ev { display: block; font-size: 12px; line-height: 1.35; color: #fff; padding: 2px 6px; border-radius: 5px; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .cal-ev:hover { text-decoration: none; filter: brightness(1.08); } .cal-ev.is-allday { font-weight: 500; } .cal-more { font-size: 11px; color: var(--faint); padding-left: 2px; } .ev-del { margin-top: 16px; } .cal-share-name { font-size: 13px; color: var(--muted); } @media (max-width: 860px) { .cal-wrap { grid-template-columns: 1fr; } .cal-cell { min-height: 78px; } } /* ---- 资源 ---- */ .res-list { display: flex; flex-direction: column; gap: 8px; } .res-item { border: 1px solid var(--line-2); border-radius: var(--radius-s); background: #FDFBF7; } .res-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 12px 14px; cursor: pointer; list-style: none; } .res-head::-webkit-details-marker { display: none; } .res-head:hover { background: #FAF7F1; } .res-title { font-size: 15px; font-weight: 600; } .res-n { font-size: 12px; color: var(--faint); } .res-body { padding: 0 14px 14px; display: flex; flex-direction: column; gap: 10px; } .res-inline { display: inline; margin: 0; } .res-shares { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .res-label { font-size: 12px; color: var(--muted); } /* 🔴 v24 第 23 批:这条规则**必须改而不是在后面再覆盖一条**。 旧值是 `flex-direction: column` —— 那是给「展开区里四个内联表单竖着排」用的布局, 而第 23 批把四个动作搬进了弹框,`.res-ops` 变成了**标题行右端那一排按钮**。 本轮实测:只在文件末尾追加 `display/gap/margin-left`,**没写 `flex-direction`**, 于是四个按钮仍然**竖着排**,把标题行从 40px 撑到 **175px 高** (真机几何实测 ops.top=382.9 / title.top=445.6 —— 差 63px,一眼就看出不在同一排)。 ⚠️ 这也是"两处定义同一个东西"的经典代价:覆盖式修法天生容易漏子属性, 而下一次改的人只会看到其中一处。**改类名语义时,旧规则要删/改,不要叠。** */ .res-ops { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; } /* ---- 资源列表:搜索在上(用户 2026-10-03 第 6 条)、排序与截断提示(第 10 条)---- */ .res-search-card { display: flex; flex-direction: column; gap: 10px; } .res-search { margin-bottom: 0; } /* 搜索框给宽一点:资源标题长(「不动产证 / 学区房合同」),窄了看不全 */ .res-search .input { max-width: none; flex: 1 1 auto; min-width: 0; } .res-sortbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } .res-sortbar .seg { margin: 0; } .res-showall { margin-left: auto; } .res-more { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-2); } /* ---- 任务看板 ---- ⚠️ 列数不再固定为 3:v24(用户 2026-10-03 第 7 条)起**一列 = 用户自己的一个看板**, 看板能加能删,写死 repeat(3, 1fr) 的话加第四个看板就直接掉到第二行去 (看起来像"加了个看板结果没显示")。 改成"横向自动排 + 可横向滚动":几列都放得下,列多了也不挤。 */ .kanban { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(230px, 1fr); gap: 14px; align-items: start; overflow-x: auto; padding-bottom: 2px; } .kb-col { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; } .kb-head { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--line-2); font-size: 14px; } .kb-head .kb-n { margin-left: auto; font-size: 12px; color: var(--faint); } .kb-body { padding: 12px; display: flex; flex-direction: column; gap: 10px; min-height: 90px; } .kb-body.is-over { background: #FDF6F1; outline: 2px dashed var(--brand-2); outline-offset: -4px; } .kb-card { border: 1px solid var(--line-2); border-radius: var(--radius-s); background: #FDFBF7; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; } .kb-card.is-dragging { opacity: .45; } .kb-card.is-overdue { border-color: rgba(192, 83, 63, .4); } .kb-title { font-size: 14px; font-weight: 500; color: var(--ink); } .kb-title:hover { color: var(--brand); } .kb-meta { display: flex; flex-wrap: wrap; gap: 4px 8px; font-size: 12px; color: var(--muted); } /* ---- 状态徽标(v24 第 14 批 用户第 2 条:每张卡都有,点一下推进到下一档 ---- 三档各有颜色:待办灰 / 进行中品牌色 / 已完成绿 —— 不靠位置也能一眼看出做到哪一步。 ⚠️ .is-static(只读账号)长得一样但不可点:不给可点的错觉。 */ .kb-st { display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: 999px; border: 1px solid transparent; font-size: 11px; line-height: 1.7; white-space: nowrap; cursor: pointer; } .kb-st.is-todo { background: var(--line-2); color: var(--muted); border-color: var(--line); } .kb-st.is-doing { background: rgba(168, 74, 40, .12); color: var(--brand-ink); border-color: rgba(168, 74, 40, .3); } .kb-st.is-done { background: rgba(111, 158, 92, .14); color: #4C7038; border-color: rgba(111, 158, 92, .35); } .kb-st.is-static { cursor: default; } .kb-st.is-doing:hover { border-color: var(--brand); } /* .is-on = "当前就是这一档"(读书详情页的四个状态按钮用它标选中): 加一圈内描边而不是换底色 —— 底色已经表达"这一档是什么颜色", 再换一次就会分不清"选中"和"这一档"。 */ .kb-st.is-on { box-shadow: inset 0 0 0 1px currentColor; font-weight: 600; } .kb-due.is-late, .kb-late { color: var(--red); } .kb-ico { white-space: nowrap; } .kb-sub { display: flex; align-items: center; gap: 8px; } .kb-bar { flex: 1 1 auto; height: 5px; border-radius: 999px; background: var(--line-2); overflow: hidden; } .kb-bar i { display: block; height: 100%; background: var(--green); } .kb-sub-n { font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; } .kb-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .kb-move { margin-left: auto; display: flex; gap: 4px; } .kb-empty { padding: 6px 2px; } .hidden-form { display: none; } /* ---- 任务详情 ---- */ .sub-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; } .sub-row { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid var(--line-2); border-radius: var(--radius-s); background: #FDFBF7; } .sub-row.is-done .sub-title { color: var(--muted); text-decoration: line-through; } .sub-check { background: none; border: 0; cursor: pointer; font-size: 17px; line-height: 1; padding: 0 2px; color: var(--brand); } .sub-check.is-static { cursor: default; color: var(--muted); } .sub-title { flex: 1 1 auto; min-width: 0; font-size: 14px; } .cmt-list { list-style: none; margin: 0 0 12px; padding: 0; display: flex; flex-direction: column; gap: 10px; } .cmt-row { border-top: 1px solid var(--line-2); padding-top: 10px; } .cmt-who { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .cmt-time { font-size: 11px; color: var(--faint); } .cmt-body { font-size: 14px; line-height: 1.7; margin: 6px 0; } .cmt-body .mention { color: var(--brand-ink); background: #F6E3D7; border-radius: 4px; padding: 0 3px; } .cmt-form { margin-top: 6px; } .task-danger { margin-top: 18px; display: flex; justify-content: flex-end; } /* ---- 「危险操作区」(成员详情页的删除,用户 2026-10-03 第 2 条)---- 刻意做成**页面最下方一块低调的折叠区**:破坏性操作不该一眼看到、更不该一键完成。 视觉上只用一条虚线分隔 + 灰色摘要,不抢上面正常内容的注意力。 */ .danger-zone { margin-top: 28px; padding-top: 14px; border-top: 1px dashed var(--line); } .danger-zone > details > summary { cursor: pointer; font-size: 13px; } .danger-zone .del-confirm { margin-top: 10px; padding: 12px 14px; border: 1px solid rgba(178, 45, 45, .25); border-radius: var(--radius-s); background: #FDF6F4; } /* ---- 表单标签更醒目(用户 2026-10-03 第 6 条)---- 「每一个输入的名字,都不够醒目,例如姓名、性别,这些字应该变大一点,加粗, 不能跟提示一样大小」—— 提醒(.tip)是 12px 的灰字,而标签原来是 13-14px, 两者太接近,扫一眼分不出"哪一行在哪"。这里把标签统一抬到 15px / 600 字重 / 深一点的墨色,与 .tip / .hint 明确拉开层级。 */ .form-grid > .field > label, .form-grid > .field > .field-label { font-weight: 600; font-size: 15px; color: var(--ink); } /* 「我的资料 / 编辑成员」那张表里也有不在 form-grid 直接子级的 label(嵌套块), 一并覆盖;用 :not(.job-lb) 排除工作经历行里的那些小标签(见下)。 */ .card .field > label:not(.job-lb):not(.check-item):not(.chk-inline) { font-weight: 600; font-size: 15px; color: var(--ink); } /* ⚠️ 但 .tip / .hint / .small 这些"说明文字"必须保持小灰字,别被上面那条带大 */ .card .field > label.tip, .card .field > label.hint, .card .field > label.small { font-weight: 400; font-size: 12.5px; color: var(--muted); } /* ---- 头像选择器(用户第 6 条:头像放最前 + 去掉「选择文件」按钮)---- 点那张(默认)头像就是选文件 —— 图本身就是按钮,不再另起一行控件。 默认头像用 .avatar-default 叠一个"+"角标,提示这里可点。 */ .avatar-field { margin-bottom: 4px; } .avatar-picker { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; } .avatar-picker #avatar-preview { width: 96px; height: 96px; border-radius: 12px; object-fit: cover; border: 1px solid var(--line); background: #F1E7D6; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; } .avatar-picker #avatar-preview:hover { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(168,74,40,.12); } .avatar-picker #avatar-preview[data-has-avatar="0"] { border-style: dashed; } .avatar-hint { line-height: 1.7; } /* ---- 旅程题图(v24 第 14 批 用户第 3 条):与头像同一套裁剪,比例 16:9 ---- 预览块本身就是"选文件"的点击区(与头像一样,省掉一整行重复的文件控件)。 ⚠️ 比例写死成 16/9 是**故意**的:CSS 不读 ImageSlot,而槽位里的比例就是 16:9, 两边各写一份会漂移 —— 部署脚本有一条断言比对 app.js 与 CSS 里的比例数字。 */ .cover-picker { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; } .cover-picker #cover-preview { width: 240px; max-width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 12px; border: 1px solid var(--line); background: #F1E7D6; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; } .cover-picker #cover-preview:hover { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(168,74,40,.12); } .cover-picker #cover-preview[data-has-cover="0"] { border-style: dashed; } .trip-people-block { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; } /* ---- 工作经历(用户第 4 条:可以有多个)---- */ .job-list { display: flex; flex-direction: column; gap: 8px; } .job-row { display: grid; gap: 8px; align-items: end; /* 单位 / 职务 / 职称 各占一份,起止各一份窄的,最后是删除按钮 */ grid-template-columns: 1.4fr 1fr 1fr 130px 130px auto; padding: 10px 12px; border: 1px solid var(--line-2); border-radius: var(--radius-s); background: #FDFBF7; } .job-cell { display: flex; flex-direction: column; gap: 4px; min-width: 0; } .job-lb { font-size: 12px; color: var(--muted); font-weight: 500; } .job-cell .input { padding: 7px 9px; font-size: 14px; } .job-del { align-self: end; } @media (max-width: 860px) { /* 窄屏:一段经历折成两列,别硬塞六列(会挤成没法填) */ .job-row { grid-template-columns: 1fr 1fr; } .job-row .job-cell:first-child { grid-column: 1 / -1; } .job-del { grid-column: 1 / -1; justify-self: end; } } @media (max-width: 860px) { .kanban { grid-template-columns: 1fr; } .nf-grid { grid-template-columns: 1fr; } } /* ---- 个人设置里的「模块」两块 ---- */ .mod-order .mod-row { cursor: grab; } .mod-order .mod-row.is-dragged { opacity: .45; cursor: grabbing; } .mod-order .mod-row.is-drop-before { box-shadow: inset 0 3px 0 var(--brand); } .mod-order .mod-row.is-drop-after { box-shadow: inset 0 -3px 0 var(--brand); } .mod-pick { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; cursor: pointer; } .mod-pick input { accent-color: var(--brand); } .mod-swatches { display: flex; flex-wrap: wrap; gap: 8px 18px; } .mod-swatch { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); } .mod-colors { display: flex; flex-direction: column; gap: 10px; } .mod-color-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } .mod-color-name { flex: 0 0 76px; font-size: 14px; } .mod-preset { display: flex; flex-wrap: wrap; gap: 4px; } .mod-preset .preset-color { cursor: pointer; } /* ---- 纪念日设置下拉(v24 用户 2026-10-03 第 9 条)---- */ .ann-set .menu-list { min-width: 260px; max-width: min(320px, 92vw); } .ann-set-list { padding: 10px; gap: 0; } .ann-kind-list { list-style: none; margin: 0; padding: 0; max-height: 46vh; overflow-y: auto; } .ann-kind-list li { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 5px 2px; border-bottom: 1px solid var(--line-2); } .ann-kind-list li:last-child { border-bottom: 0; } .ann-kind-name { font-size: 13.5px; font-weight: 500; } .ann-kind-list li .small { margin-left: auto; } /* ---- 首页 dashboard ---- */ .hero-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; } .dash-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; } .dash-row { display: flex; align-items: baseline; gap: 10px; padding: 8px 2px; border-top: 1px solid var(--line-2); flex-wrap: wrap; } .dash-row:first-child { border-top: 0; } .dash-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; } .dash-when { flex: 0 0 96px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; } .dash-what { flex: 1 1 auto; min-width: 0; font-size: 14px; } .dash-tag { flex: 0 0 auto; font-size: 11px; color: var(--faint); } /* ---- v24 第 11 批 #4:影院下拉(带色点)+ 新建影院弹框 ---- 原生