Files
FamilyBook/assets/css/app.css
T
laoyang c9037f79be 家谱 v22:改按 family-chart 官方用法接,并放开代数上限
用户指出「只需要按照 json 格式,把以我为中心的关联数据全部取出来,
全都封装进去就行了,id 用用户表的 id,性别名字生日头像都是现成的,
只需要把配偶、父母和孩子放进去用 id 关联」——并强调「之前一定是你
调用的方法不对」。

== 验证结论(先量再改)==
用**官方示例自己的数据**(donatso 的 data.json,13 人,格式完全官方)
跑了一遍:库画出 11 人,**配偶 Andrea 的父母 Zadro/Zadra 一个都没画**。
那两位就在数据里、rels.parents 也指向他们、格式与官方示例一字不差。
→ 说明「按格式全塞进去就能画全」在**姻亲**这一项上不成立:
  库的算法只走「树根的祖先链 + 后代 + 树根的兄弟姐妹 + 各人配偶」,
  **不会从配偶继续向上/向下 walk**。这是库的算法边界,不是格式/关联问题。

== 但用户的批评有一半是对的:调用方式确实有我没做足的地方 ==
官方示例是:<div id="FamilyChart" class="f3"> + 引 family-chart.css。
我之前**既没加 f3 类、也没引库 CSS**,而是自己在 app.css 里手补
`.kin-chart svg.main_svg{width:100%;height:100%}` 去绕开 300×150 那个坑 ——
属于"用歪门邪道达到同样效果"。现在按官方来:
  · 容器加 `f3` 类(库的 CSS 全部以 .f3 作用域)
  · 引入官方 family-chart.css(新增 vendor 文件)
  · 在 app.css 里把库的深色主题变量覆盖成家书宣纸色
    (--male-color / --background-color / --text-color 等)
  · 自己的 svg 撑满那条规则删掉(官方 CSS 已提供)

== 放开代数上限(用户要求「全部取出来」)==
官方示例**不调** setAncestryDepth / setProgenyDepth —— 库在"未设置"时
会跳过深度截断,等于不限代数。我之前写死了 2/3,真实家庭一超过就被砍。
现在不设这两个值。服务端 familyMap 的 up/down 也从 (2,2) 放到 (9,9)
(这两个参数只影响**代数标号**,超出会压到边界档导致曾祖辈与祖辈混层;
遍历本身是走遍整个连通图的,人员收录数不受影响)。

== 工具同步 ==
- familybook_deploy.sh:新增 v22 三条契约(vendor CSS 在 / 模板引了它 /
  容器有 f3 类),并加**反向断言**「不许再出现 setAncestryDepth|setProgenyDepth」;
  旧的"卡片尺寸/间距"断言同步。
- _fb_kin_geom_falsify.py:注入②的原锚点(setProgenyDepth)已随本次删除,
  换成「禁掉上溯循环」——树根退回「我」,伯父/堂兄/堂嫂/堂侄/侄子那批旁支
  会离开画面,J 项必须红。3/3 全抓住。
- ⚠️ 又踩一次「注释撞断言字面量」:新写的注释里提到 setProgenyDepth 函数名,
  把「不许再设代数上限」这条反向断言直接打红。改注释避开字面量后恢复。

== 战绩 ==
e2e 549/0 · 桌面几何 368/0 · 移动端 516/0 · 交互 97/0 · 升级专测 123/0
家谱可视化 24/0 · 家谱版面 10/0 · 反向验证 3/3 · 静态断言空跑 RC=0
部署 + 线上自检全过。
2026-10-02 20:09:28 +08:00

1786 lines
87 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
家书 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 { background: var(--line-2); }
.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 { background: #FAF7F1; 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); }
.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 { 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); }
/* ---------------- 成员卡 ---------------- */
.people-grid {
display: grid; gap: 16px; margin-top: 4px;
grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
}
.person {
display: block; color: var(--ink); position: relative;
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--radius); overflow: hidden;
box-shadow: var(--shadow);
transition: transform .15s ease, box-shadow .15s ease;
}
.person:hover { transform: translateY(-2px); box-shadow: var(--shadow-l); text-decoration: none; }
.person .ph {
background: #F1E7D6; aspect-ratio: 1 / 1; overflow: hidden;
}
.person .ph img { width: 100%; height: 100%; object-fit: cover; display: block; }
.person .pb { padding: 12px 14px 14px; }
.person .pname { font-weight: 600; font-size: 15px; display: flex; align-items: center; gap: 6px; }
.person .pmeta { color: var(--muted); font-size: 13px; margin-top: 2px; }
.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
会被内部 <select> 的**最长选项文字**撑开 —— 高级搜索页在 360/375px 下整页横向溢出
(scrollW 390 > clientW 360),而「影院」下拉正是长选项。给了 0 之后列才能收进版心,
下拉自身由 width:100% 跟着收。与 .hero-main / .person-move .select 同一套做法。 */
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field.full { grid-column: 1 / -1; }
.field label { font-size: 13px; color: var(--muted); font-weight: 500; }
.field .tip { font-size: 12px; color: var(--muted); }
.input, .select, textarea.input {
width: 100%; padding: 10px 12px; font: inherit; font-size: 14px;
color: var(--ink); background: #fff;
border: 1px solid var(--line); border-radius: var(--radius-s);
transition: border-color .15s ease, box-shadow .15s ease;
}
.input:focus, .select:focus, textarea.input:focus {
outline: none; border-color: var(--brand);
box-shadow: 0 0 0 3px rgba(168, 74, 40, .12);
}
textarea.input { min-height: 76px; resize: vertical; }
.err { color: var(--red); font-size: 12px; }
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
padding: var(--act-pad-y) var(--act-pad-x);
font: inherit; font-size: var(--act-font); line-height: var(--act-line); font-weight: 500;
border-radius: var(--radius-s); border: 1px solid transparent; cursor: pointer;
background: var(--brand); color: #fff; transition: background .15s ease;
}
.btn:hover { background: #944022; text-decoration: none; }
.btn-ghost { background: #fff; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { background: #FAF7F1; }
.btn-danger { background: #fff; color: var(--red); border-color: rgba(192,83,63,.35); }
.btn-danger:hover { background: #FDF1EE; }
.btn-sm { padding: 5px 12px; font-size: 13px; }
.btn-row { display: flex; gap: 10px; margin-top: 20px; }
/* ---------------- 详情 ---------------- */
.detail { display: grid; grid-template-columns: 260px 1fr; gap: 24px; }
.detail-photo {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--radius); padding: 16px; text-align: center;
box-shadow: var(--shadow);
}
.detail-photo img {
width: 100%; aspect-ratio: 1/1; object-fit: cover;
border-radius: 12px; display: block;
}
.detail-photo .name { font-size: 18px; font-weight: 600; margin: 14px 0 4px; }
.detail-photo .sub { color: var(--muted); font-size: 13px; }
.kv { display: flex; flex-direction: column; }
.kv-row {
display: grid; grid-template-columns: 96px 1fr; gap: 12px;
padding: 12px 0; border-bottom: 1px solid var(--line-2);
}
.kv-row:last-child { border-bottom: 0; }
.kv-row dt { color: var(--muted); font-size: 13px; }
.kv-row dd { margin: 0; font-size: 14px; word-break: break-all; }
.mono { font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; letter-spacing: .5px; }
/* ---------------- 提示 ---------------- */
.flash { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.flash-item {
padding: 10px 14px; border-radius: var(--radius-s); font-size: 14px;
background: #E6F0DC; color: #3D6B1F;
}
.flash-item.err { background: #F8DDDA; color: #A1341F; }
.empty {
background: var(--surface); border: 1px dashed var(--line);
border-radius: var(--radius); padding: 40px 20px; text-align: center;
color: var(--muted);
}
.empty b { display: block; color: var(--ink); font-weight: 500; margin-bottom: 4px; }
/* ---------------- 登录 / 安装 ---------------- */
.auth-wrap {
min-height: 100vh; display: flex; align-items: center; justify-content: center;
padding: 40px 20px;
}
.auth-card {
width: 100%; max-width: 400px; background: var(--surface);
border: 1px solid var(--line); border-radius: 20px; box-shadow: var(--shadow-l);
padding: 32px;
}
/* 图标 + 「用手机号登录你的家书」+ 家庭名,整块水平居中。
⚠️ 只有这一块居中:下面的表单仍然左对齐(label 左对齐才好读),
「忘记密码」那行本来就是居中,所以视觉上不会歪。 */
.auth-head { text-align: center; }
.auth-card .logo {
display: block; margin: 0 auto 14px;
width: 56px; height: 56px; border-radius: 14px;
}
.auth-card h1 { font-size: 20px; font-weight: 600; margin: 0 0 4px; }
.auth-card .lead { color: var(--muted); font-size: 14px; margin: 0 0 24px; }
.auth-card .btn { width: 100%; margin-top: 20px; }
.auth-foot { margin-top: 18px; font-size: 13px; color: var(--muted); text-align: center; }
/* ---------------- 其他 ---------------- */
.muted { color: var(--muted); }
.small { font-size: 13px; }
.nowrap { white-space: nowrap; }
.search { display: flex; gap: 8px; margin-bottom: 16px; }
.search .input { max-width: 260px; }
/* ---------------- 日期 + 时:分:秒 组合控件(templates/_datetime.php) ----------------
原生 datetime-local 的显示格式由浏览器 UI 语言/系统时间格式决定,
中文环境渲染成「下午 03:30」且没有秒,所以自己拼一个。
日期部分仍是原生 date 控件 → app.js 的 showPicker 让它整框可点。 */
.dt-input { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.dt-input .dt-date {
flex: 1 1 150px; min-width: 0;
padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius-s);
background: var(--surface); color: var(--ink); font: inherit;
accent-color: var(--brand);
}
.dt-input .dt-date { cursor: pointer; } /* 整框可点,光标也要说明这一点 */
.dt-input .dt-date::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .65; }
.dt-time { display: flex; align-items: center; gap: 3px; }
.dt-input .dt-hour,
.dt-input .dt-minute,
.dt-input .dt-second {
padding: 9px 6px; border: 1px solid var(--line); border-radius: var(--radius-s);
background: var(--surface); color: var(--ink); font: inherit;
font-variant-numeric: tabular-nums; cursor: pointer;
}
.dt-input .dt-colon { color: var(--muted); font-weight: 600; }
/* 「以哪个生日为准」的两块:被选中的那块描一圈,一眼看出填的是哪边 */
.bd-source { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.bd-pane { border: 1px solid transparent; border-radius: var(--radius); padding: 10px 12px 2px; }
.bd-pane.is-primary { border-color: var(--brand-2); background: #FDF6F1; }
.bd-pane .bd-pane-k { font-size: 13px; font-weight: 600; margin: 0 0 8px; }
.bd-readonly {
display: inline-block; padding: 6px 10px; border-radius: var(--radius-s);
background: var(--line-2); color: var(--muted); font-size: 14px;
font-variant-numeric: tabular-nums;
}
/* ---------------- 称谓标签 ---------------- */
.rel-label {
display: inline-block; padding: 1px 9px; border-radius: 999px;
background: #F6E3D7; color: var(--brand-ink);
font-size: 11px; font-weight: 500; line-height: 1.8;
margin-right: 2px;
}
/* ---------------- 家谱(以我为中心) ----------------
v22:按 family-chart **官方用法**接(容器 class="f3" + 引 family-chart.css),
库把 data[0].id 当 main_id,所以把「最高祖先」排第一位以画出尽可能多的血亲旁支;
加载后再把镜头平移回「我」,兼顾"看到全树"与"以自己为中心"。
⚠️ 免费版 family-chart 的算法只画「树根的祖先链 + 后代 + 树根的兄弟姐妹 + 各人配偶」,
**不会从配偶继续向上/向下 walk**。这一点已用**官方示例自己的数据**验证过:
官方 data.json 里 13 人(含配偶 Andrea 的父母 Zadro/Zadra,格式完全按官方),
库只画出 11 人 —— 配偶方那两位长辈一个都没画。
所以「把数据按格式全塞进去就能画全」在姻亲这一项上不成立,是库的算法边界。 */
.kin-tools { display: flex; gap: 8px; justify-content: flex-end; margin: 0 0 10px; }
.kin-chart {
position: relative;
width: 100%;
height: clamp(420px, 62vh, 720px);
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
cursor: grab;
/* 拖拽/缩放由库接管,别让浏览器插手滚动 */
touch-action: none;
}
.kin-chart:active { cursor: grabbing; }
/* 库的 `.f3` 会自带一套**深色主题**(--background-color: rgb(33,33,33) / --text-color: #fff)。
家书是宣纸浅色,必须在这里把库的主题变量改掉;否则卡片文字会变白、看不出内容。
只覆盖颜色变量,**不动**库的结构规则(那些是撑满/定位必需的)。 */
.kin-chart.f3 {
--male-color: #F1E7D6; /* 库的男卡底色,改成纸色(我们自己的 .kin-card 会再盖一层) */
--female-color: #F1E7D6;
--genderless-color: #F1E7D6;
--background-color: transparent;
--text-color: var(--ink);
font-family: inherit;
}
/* 隐藏 family-chart 自带的空导航栏(我们用自己的「看全部 / 回到我」按钮) */
.kin-chart .f3-nav-cont { display: none !important; }
/* 官方 CSS 已经给了 `.f3 svg.main_svg { width:100%; height:100% }`(撑满画布),
这里只补一个 display:block,避免 svg 作为 inline 元素多出基线空隙。 */
.kin-chart svg.main_svg { display: block; z-index: 1; }
/* family-chart 的 HTML 卡片层 #htmlSvg 与 SVG 连线层由库的 onZoom 同步 transform。
卡片容器 .card_cont 已经 absolute + translate(x,y);里面再放我们的 .kin-card。 */
.kin-chart #htmlSvg { z-index: 2; }
.kin-chart .card_cont { pointer-events: none; }
/* ⚠️ 库的性别底色规则是 `.f3 div.card-male .card-inner`(3 段),
比 `.kin-chart .card_cont .card-inner`(3 段)**同级**,靠先后顺序定胜负 ——
所以这份覆盖必须写在**引库 CSS 之后**(app.css 在 layout 里就是这么排的)。
把外层中转容器全部归零,只留我们自己的 .kin-card。 */
.kin-chart .card_cont > .card,
.kin-chart .card_cont .card-inner,
.kin-chart.f3 .card_cont .card-inner {
background: transparent;
padding: 0;
border-radius: 0;
min-height: 0;
width: auto;
height: auto;
outline: none;
}
.kin-chart .card_cont .card-inner { display: block; }
/* v21:卡片尺寸与 setCardDim({w:140, h:120}) 对齐 */
.kin-card {
position: absolute;
display: flex; flex-direction: column; align-items: center;
gap: 0;
width: 140px; height: 120px;
box-sizing: border-box; padding: 4px 4px 3px;
text-align: center;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 10px;
box-shadow: 0 1px 3px rgba(43,38,32,.10);
color: var(--ink);
transform: translate(-50%, -50%); /* 坐标给的是卡片中心 */
transition: border-color .15s ease, box-shadow .15s ease;
overflow: hidden;
pointer-events: auto; /* 父 .card_cont 是 none,这里恢复 */
}
.kin-card:hover {
box-shadow: var(--shadow-l); border-color: var(--brand-2); text-decoration: none;
}
.kin-card .kin-ph {
width: 100%; height: 70px; border-radius: 7px; overflow: hidden;
background: #F1E7D6; flex: 0 0 auto;
}
.kin-card .kin-ph img { width: 100%; height: 100%; object-fit: cover; display: block; }
.kin-card .kin-nm {
font-size: 12.5px; font-weight: 600; line-height: 1.25; max-width: 100%;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
margin-top: 4px;
}
/* 称呼在姓名下面一行,用品牌色的浅字(参考实现是「(称呼)」) */
.kin-card .kin-lb {
font-size: 10.5px; color: var(--brand); line-height: 1.2; max-width: 100%;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kin-card .kin-mark { font-size: 10px; color: var(--faint); }
/* 「我」:品牌色描边 + 光圈 */
.kin-card.is-me {
border-color: var(--brand);
box-shadow: 0 0 0 3px rgba(168, 74, 40, .12), var(--shadow);
}
.kin-card.is-me .kin-lb { color: var(--brand-ink); font-weight: 600; }
/* 「待确认」(只填了称呼、没对应到真人):虚线 */
.kin-card.is-ph { border-style: dashed; background: #FCFAF6; }
/* family-chart 默认连线颜色改淡,与之前自研版一致。
库在 path 上写了 inline stroke="#fff",必须用 !important 覆盖。 */
.kin-chart svg.main_svg .links_view path.link {
fill: none;
stroke: #C9BDA9 !important;
stroke-width: 1.6;
}
.tree-legend { font-size: 12px; color: var(--muted); margin: 14px 2px 0; }
/* 详情页里的迷你家谱 */
.mini-tree { display: flex; flex-direction: column; gap: 8px; }
.mini-gen { display: flex; flex-wrap: wrap; gap: 8px; }
.mini-node {
display: flex; align-items: center; gap: 7px;
padding: 5px 12px 5px 5px; border-radius: 999px;
background: #FAF7F1; border: 1px solid var(--line);
color: var(--ink); font-size: 13px;
}
.mini-node:hover { text-decoration: none; border-color: var(--brand); }
.mini-node img { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; }
.mini-node .mn-label { font-size: 11px; color: var(--brand); }
.mini-node.is-me { border-color: var(--brand); background: #F6E3D7; }
/* ---------------- 亲属关系编辑 ---------------- */
.rel-summary {
display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px; margin-bottom: 18px;
}
.rel-sum-item {
background: #FAF7F1; border: 1px solid var(--line-2);
border-radius: var(--radius-s); padding: 10px 14px;
}
.rel-sum-k { display: block; font-size: 12px; color: var(--muted); margin-bottom: 3px; }
.rel-sum-v { font-size: 14px; }
.rel-sum-v a { margin-right: 6px; }
.rel-add { border-top: 1px solid var(--line-2); padding-top: 16px; }
.rel-add-title { font-size: 14px; font-weight: 600; margin: 0 0 12px; }
.rel-form {
display: grid; grid-template-columns: 1fr 1fr 1.4fr auto;
gap: 12px; align-items: end;
}
.rel-list { margin-top: 16px; font-size: 13px; }
.rel-list summary { cursor: pointer; color: var(--muted); }
.rel-list ul { margin: 10px 0 0; padding-left: 18px; }
.rel-list li { margin-bottom: 6px; }
.rel-list .btn { margin-left: 6px; }
/* ---------------- 三角色 / 存储设置 ---------------- */
.role-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
.role-card { background: #FAF7F1; border: 1px solid var(--line-2); border-radius: var(--radius-s); padding: 14px 16px; }
.rc-name { font-weight: 600; margin-bottom: 5px; }
.rc-desc { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.65; }
.radio-row { display: flex; flex-direction: column; gap: 10px; }
.radio-item {
display: flex; gap: 10px; align-items: flex-start;
background: #FAF7F1; border: 1px solid var(--line-2);
border-radius: var(--radius-s); padding: 12px 14px; cursor: pointer;
}
.radio-item input { margin-top: 3px; }
.check-item { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; cursor: pointer; }
.check-item input { margin-top: 3px; }
.oss-box {
border: 1px solid var(--line-2); border-radius: var(--radius-s);
padding: 12px 16px; background: #FCFAF6;
}
.oss-box summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--muted); }
.plain-list { margin: 0; padding-left: 18px; font-size: 14px; }
.plain-list li { margin-bottom: 7px; }
@media (max-width: 720px) {
.rel-form { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
/* 390px 宽度放不下整套顶栏:只留 图标 + 导航 + 头像 + 退出 */
.topbar-inner { gap: 6px; padding: 0 10px; height: 54px; }
.brand img { width: 26px; height: 26px; }
.nav { gap: 0; }
.nav a { padding: 6px 8px; font-size: 13px; }
.topbar-right { gap: 6px; }
.who-name, .who-pwd { display: none; } /* 名字与「改密码」收起,设置页里都有 */
.who img { width: 26px; height: 26px; }
.container, .container-narrow { padding: 20px 16px 48px; }
.form-grid { grid-template-columns: 1fr; }
.detail { grid-template-columns: 1fr; }
.hero { padding: 20px; gap: 14px; }
.hero h1 { font-size: 20px; }
.hero-stats { margin-left: 0; gap: 20px; }
.page-head { flex-wrap: wrap; align-items: center; }
.page-head .actions { margin-left: 0; width: 100%; }
.kv-row { grid-template-columns: 84px 1fr; }
.search { flex-wrap: wrap; }
.search .input { max-width: none; flex: 1 1 auto; }
}
/* ---------------- 「我怎么称呼家人」(视角属性,见 app/Nickname.php) ---------------- */
.nick-list { display: flex; flex-direction: column; gap: 8px; }
.nick-row {
display: grid; grid-template-columns: minmax(120px, auto) 1fr auto;
gap: 10px; align-items: center;
padding: 8px 10px; border-radius: var(--radius-s);
background: #FAF7F1; border: 1px solid var(--line-2);
}
.nick-who { display: flex; align-items: center; gap: 8px; font-size: 14px; min-width: 0; }
.nick-who img { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
.nick-who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 详情页上的单个"我叫他什么"输入行 */
.nick-inline { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.nick-inline .input { flex: 1 1 220px; max-width: 340px; }
@media (max-width: 720px) {
.nick-row { grid-template-columns: 1fr; }
.nick-inline .input { max-width: none; }
}
/* ---------------- 连通性测试与诊断(设置页) ---------------- */
.test-actions { display: flex; flex-direction: column; gap: 10px; }
.test-actions form { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.oss-diag {
margin-top: 16px; border-radius: var(--radius-s); overflow: hidden;
border: 1px solid var(--line);
}
.oss-diag-head {
display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
padding: 10px 14px; font-size: 13px;
background: var(--line-2);
}
.oss-diag.is-ok .oss-diag-head { background: #E6F0DC; color: #3F6021; }
.oss-diag.is-bad .oss-diag-head { background: #F6E3D7; color: var(--brand-ink); }
.oss-diag-table { width: 100%; border-collapse: collapse; font-size: 13px; background: var(--surface); }
.oss-diag-table th,
.oss-diag-table td { padding: 8px 12px; text-align: left; vertical-align: top; border-top: 1px solid var(--line-2); }
.oss-diag-table th { font-weight: 500; color: var(--muted); font-size: 12px; }
.oss-diag-table td:nth-child(1) { white-space: nowrap; }
.oss-diag-table td:nth-child(3) { white-space: nowrap; }
.oss-diag-table tr.row-ok td:nth-child(2) { color: #4A7226; }
.oss-diag-table tr.row-bad td:nth-child(2) { color: var(--red); }
@media (max-width: 720px) {
.oss-diag-table th:nth-child(3),
.oss-diag-table td:nth-child(3) { display: none; }
}
/* ============================================================
以下都是 2026-09-30 那一批反馈加的东西
============================================================ */
/* 只给读屏用的标签:视觉上不占地,但无障碍名还在(别用 display:none,那会一起藏掉) */
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* 区块之间的小间隔(比写 style="margin-top:20px" 好读) */
.stack-gap { height: 20px; }
/* ---------------- 名册:按关系分组分栏 ---------------- */
.group-block { margin-top: 24px; }
.group-head {
display: flex; align-items: baseline; gap: 8px;
margin: 0 0 10px; padding-bottom: 8px;
border-bottom: 1px solid var(--line);
font-size: 15px; font-weight: 600;
}
.group-head .group-title { color: var(--ink); }
/* 分组名前面那道砖红短线,让分栏在长页面上更容易一眼定位 */
.group-head .group-title::before {
content: ''; display: inline-block; vertical-align: middle;
width: 3px; height: 13px; margin-right: 8px; border-radius: 2px;
background: var(--brand);
}
.group-head .group-n { margin-left: auto; font-weight: 400; font-size: 12px; color: var(--faint); }
/* 卡片改成 div > a + details:表单不能套在链接里(非法 HTML,浏览器会把 DOM 拆开) */
.person-main { display: block; color: var(--ink); }
.person-main:hover { text-decoration: none; }
.person-more {
border-top: 1px solid var(--line-2); background: #FCFAF6;
}
.person-more > summary {
list-style: none; cursor: pointer; padding: 7px 14px;
font-size: 12px; color: var(--muted);
}
.person-more > summary::-webkit-details-marker { display: none; }
.person-more > summary::after { content: ' ▾'; }
.person-more[open] > summary::after { content: ' ▴'; }
.person-more > summary:hover { color: var(--brand); }
.person-move { display: flex; gap: 6px; padding: 0 14px 12px; }
.person-move .select { flex: 1 1 auto; min-width: 0; padding: 6px 8px; font-size: 13px; }
.person-move .btn { flex: 0 0 auto; }
/* ---------------- 名册:拖动排序 / 拖到别栏改分组 ----------------
拖动是"锦上添花":没有它,卡片上的「⋯ → 调整分组」表单照样能改分组,
手机上 HTML5 拖放本来也不生效,那套表单就是窄屏的正路。 */
.roster.is-droppable .person { cursor: grab; }
.roster.is-droppable .person.is-dragged { opacity: .45; cursor: grabbing; }
.drag-handle {
position: absolute; left: 8px; top: 8px; z-index: 2;
width: 22px; height: 22px; line-height: 22px; text-align: center;
border-radius: 6px; font-size: 14px;
color: var(--muted); background: rgba(255, 255, 255, .82);
border: 1px solid var(--line-2);
pointer-events: none; /* 手柄只是个记号,抓取要落在整张卡上 */
}
.drag-handle-inline { color: var(--muted); }
/* 空栏平时收起来(七个预置分组可能只用到三个,摊开会显得名册破破烂烂)——
但**拖动时必须露出来**,否则"想拖到一个现在没人的分组里"就没地方放。 */
.group-block.is-empty { display: none; }
.roster.is-dragging .group-block.is-empty {
display: block; opacity: .9;
border: 1px dashed var(--line);
border-radius: var(--radius); padding: 10px; margin-top: 18px;
background: rgba(250, 247, 241, .6);
}
.group-drop-hint { display: none; margin-left: auto; font-weight: 400; font-size: 12px; color: var(--brand); }
.roster.is-dragging .group-block .group-drop-hint { display: inline; }
/* 拖动时把"有多少人"让出位置给落点提示,两者都挤在右上角会很乱 */
.roster.is-dragging .group-block .group-n { display: none; }
.roster.is-dragging .people-grid { min-height: 24px; }
/* 拖完/没拖成的轻提示(2 秒自己消失,不打断操作) */
.roster-note {
position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 12px);
z-index: 200; padding: 8px 16px; border-radius: 999px;
background: rgba(44, 35, 29, .88); color: #fff; font-size: 13px;
opacity: 0; pointer-events: none; transition: opacity .18s ease, transform .18s ease;
}
.roster-note.is-show { opacity: 1; transform: translate(-50%, 0); }
.roster-note.is-err { background: rgba(178, 45, 45, .92); }
/* ---------------- 分享名片(成员详情页) ---------------- */
.share-wrap { display: inline-block; }
.share-wrap > summary { list-style: none; cursor: pointer; display: inline-block; }
.share-wrap > summary::-webkit-details-marker { display: none; }
.share-wrap .inline-form { margin-top: 10px; }
/* ---------------- 「从家里已有的人里挑」 ---------------- */
.pick-list { list-style: none; margin: 0 0 4px; padding: 0; display: grid; gap: 8px; }
.pick-row { margin: 0; }
.pick-item {
display: flex; align-items: center; gap: 10px; cursor: pointer;
padding: 8px 12px; border-radius: var(--radius-s);
background: #FAF7F1; border: 1px solid var(--line-2);
}
.pick-item:hover { border-color: var(--brand); }
.pick-item input[type="checkbox"] { width: 16px; height: 16px; flex: 0 0 auto; accent-color: var(--brand); }
.pick-ph { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; background: #F1E7D6; }
.pick-name { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* ---------------- 个人设置:分组顺序 ---------------- */
.order-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.order-row {
display: flex; align-items: center; gap: 10px;
padding: 8px 12px; border-radius: var(--radius-s);
background: #FAF7F1; border: 1px solid var(--line-2);
}
.order-no {
flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
background: var(--brand); color: #fff;
font-size: 12px; line-height: 22px; text-align: center;
}
.order-name { flex: 1 1 auto; min-width: 0; font-size: 14px; }
.order-count { flex: 0 0 auto; font-size: 12px; color: var(--faint); }
.order-move { flex: 0 0 auto; display: flex; gap: 4px; }
.order-move .btn { padding: 2px 8px; line-height: 1.4; }
.order-move .btn[disabled] { opacity: .35; cursor: default; }
/* ---------------- 个人设置:关系分类管理 ---------------- */
.inline-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.inline-form .input { flex: 1 1 200px; max-width: 320px; }
.group-manage { list-style: none; margin: 16px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.group-manage-row {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
padding: 8px 12px; border-radius: var(--radius-s);
background: #FAF7F1; border: 1px solid var(--line-2);
}
.group-rename { display: flex; gap: 8px; align-items: center; flex: 1 1 240px; min-width: 0; }
.group-rename .input { flex: 1 1 auto; min-width: 0; max-width: 220px; padding: 6px 10px; font-size: 13px; }
.group-manage-row .group-count { flex: 0 0 auto; }
.del-wrap > summary { list-style: none; cursor: pointer; }
.del-wrap[open] { flex: 1 1 100%; }
.del-confirm { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 8px 0 2px; }
/* ---- 页头动作区:统一尺寸 + 除设置外都要「有颜色」 ----
⚠️ 本节刻意放在**文件靠后**:.btn / .btn-ghost / .btn-danger / .btn-icon
的定义都在前面,CSS 同特异性时「后写的赢」——放前面会被它们覆盖
(实测过:放前面时 .btn-icon 白底被 .btn 的品牌底盖掉、.btn-ghost 也还是白的)。
用户明确提过三次:
·「设置按钮和记一场电影按钮高度要一致」
·「返回等按钮和记一场电影等按钮的大小高度的统一问题」
·「设置按钮…只保留图标,图标换成三条横线;除设置,其他按钮都要有颜色」 */
.page-head .actions > .btn,
.page-head .actions > .movie-set > summary.btn,
.page-head .actions > form > .btn,
.page-head .actions > form > button.btn {
padding: var(--act-pad-y) var(--act-pad-x); font-size: var(--act-font);
}
/* 层 1:主按钮(裸 .btn,无修饰类)—— 品牌实心。
⚠️ `.btn` 同时出现在 `<a class="btn">` 和 `<form><button class="btn">`,
两条路径的选择器都要写出来,且**特异性必须相等**,否则后面层 2/3
按类名匹配的规则压不住它(下层靠"同特异性 + 后写"取胜)。 */
.page-head .actions > .btn:not(.btn-icon),
.page-head .actions > form > .btn:not(.btn-icon),
.page-head .actions > form > button.btn:not(.btn-icon) {
background: var(--brand); color: #fff; border-color: transparent;
}
.page-head .actions > .btn:not(.btn-icon):hover,
.page-head .actions > form > button.btn:not(.btn-icon):hover { background: #944022; }
/* 层 2:次要按钮(.btn-ghost)—— 品牌描边 + 极浅品牌底,比主按钮轻一档。
a / button 两种形态各写一条、特异性对齐(见层 1 的警告)。 */
.page-head .actions > .btn-ghost:not(.btn-icon),
.page-head .actions > a.btn-ghost:not(.btn-icon),
.page-head .actions > form > .btn-ghost:not(.btn-icon),
.page-head .actions > form > button.btn-ghost:not(.btn-icon) {
background: #FDF7F2; color: var(--brand); border-color: rgba(168, 74, 40, .45);
}
.page-head .actions > .btn-ghost:not(.btn-icon):hover,
.page-head .actions > a.btn-ghost:not(.btn-icon):hover,
.page-head .actions > form > .btn-ghost:not(.btn-icon):hover,
.page-head .actions > form > button.btn-ghost:not(.btn-icon):hover {
background: #F7EDE4; border-color: var(--brand);
}
/* 层 3:危险按钮(.btn-danger)—— 淡红底 + 红字,同样要有颜色、但不能看着像主按钮。
⚠️ 实测踩过:只写 `.actions > form > .btn-danger`(0,4,0)会被层 1 的
`.actions > form > button.btn`(0,4,1)压掉 —— 详情页的「删除」按钮
在浏览器里显示成品牌砖红实心,跟「编辑」几乎分不出来。补上 `button.`
形态后特异性升到 (0,4,2),才真正生效。 */
.page-head .actions > .btn-danger:not(.btn-icon),
.page-head .actions > a.btn-danger:not(.btn-icon),
.page-head .actions > form > .btn-danger:not(.btn-icon),
.page-head .actions > form > button.btn-danger:not(.btn-icon) {
background: #FDF1EE; color: var(--red); border-color: rgba(192, 83, 63, .45);
}
.page-head .actions > .btn-danger:not(.btn-icon):hover,
.page-head .actions > a.btn-danger:not(.btn-icon):hover,
.page-head .actions > form > .btn-danger:not(.btn-icon):hover,
.page-head .actions > form > button.btn-danger:not(.btn-icon):hover { background: #F7E4DF; }
/* 「设置」图标按钮:**唯一无色的**(白底描边、方形),与上面所有彩色按钮区分。
padding 归零、做成正方形,但高度必须仍是 --act-h(= 文字按钮的 43.8px)——
写死过 40px,比旁边按钮矮 3.8px,是用户反复反馈的那个问题。 */
.page-head .actions > .movie-set > summary.btn-icon,
.page-head .actions > .movie-set > summary.btn {
padding: 0;
width: var(--act-h); height: var(--act-h); min-width: var(--act-h); line-height: 1;
background: #fff; color: var(--ink); border: 1px solid var(--line);
}
.page-head .actions > .movie-set > summary.btn-icon:hover,
.page-head .actions > .movie-set > summary.btn:hover {
background: #FAF7F1; border-color: var(--brand); color: var(--brand);
}
.movie-set[open] > summary.btn-icon,
.movie-set[open] > summary.btn { border-color: var(--brand); color: var(--brand); }
.btn.danger { color: var(--red); border-color: rgba(192, 83, 63, .35); }
.btn.danger:hover { background: rgba(192, 83, 63, .08); }
/* ---------------- 图片裁剪浮层 ---------------- */
/* 覆盖层默认不占空间;打开时铺满整屏,把底下的页面锁住(裁剪时不该能点到别处) */
.crop-overlay { display: none; }
.crop-overlay.is-open {
display: flex; position: fixed; inset: 0; z-index: 100;
align-items: center; justify-content: center;
padding: 20px; background: rgba(44, 35, 29, .55);
}
.crop-panel {
background: var(--surface); border-radius: var(--radius);
box-shadow: var(--shadow-l); padding: 16px;
max-width: calc(100vw - 24px); max-height: calc(100vh - 24px); overflow: auto;
}
.crop-head {
display: flex; align-items: baseline; gap: 8px;
font-size: 15px; font-weight: 600; margin-bottom: 12px;
}
.crop-head .crop-shape { font-size: 12px; font-weight: 400; color: var(--muted); }
.crop-stage {
position: relative; margin: 0 auto; overflow: auto;
background: #F1E7D6; border-radius: var(--radius-s);
touch-action: none; /* 手机上拖框时别把页面一起滚走 */
user-select: none;
}
/* 图片的宽高由 JS 按倍率写死(放大后比舞台大,靠舞台自身滚动看别处)——
所以这里**不能**写 width/height:100%,否则缩放就永远没效果。 */
.crop-img { display: block; max-width: none; }
/* 缩放条 —— 用户明确要求"裁剪的时候要能缩放" */
.crop-zoom-row {
display: flex; align-items: center; gap: 10px;
margin-top: 12px; font-size: 13px; color: var(--muted);
}
.crop-zoom { flex: 1; min-width: 120px; accent-color: var(--brand); }
.crop-zoom-val { font-variant-numeric: tabular-nums; min-width: 42px; text-align: right; }
/* 框外的部分压暗 —— 这是"你只留下框里这块"最直观的表达 */
.crop-box {
position: absolute; cursor: move;
border: 2px solid #fff;
box-shadow: 0 0 0 9999px rgba(44, 35, 29, .5);
}
.crop-box::before {
content: ''; position: absolute; inset: 0;
border: 1px dashed rgba(255, 255, 255, .75); pointer-events: none;
}
.crop-handle {
position: absolute; right: -8px; bottom: -8px;
width: 20px; height: 20px; border-radius: 50%;
background: #fff; border: 2px solid var(--brand);
cursor: nwse-resize;
}
.crop-tip { color: var(--muted); font-size: 12px; margin: 12px 0 0; }
.crop-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px; }
@media (max-width: 720px) {
.order-move .btn { padding: 4px 10px; }
.group-manage-row { align-items: flex-start; }
.crop-panel { padding: 12px; }
}
/* ===== 补充联系方式(可多条):标注 + 内容 一行两组,窄屏摞起来 ===== */
.ct-row { display: flex; gap: 8px; margin-bottom: 8px; }
.ct-row .ct-label { flex: 0 0 140px; }
.ct-row .input { flex: 1; }
@media (max-width: 720px) {
.ct-row { flex-direction: column; gap: 6px; }
.ct-row .ct-label { flex: none; }
}
/* ===== 观影 v9:瀑布流海报墙 + MovieLog 式详情/评价/影院 ===== */
/* ---- 搜索栏 ---- */
.search-bar {
display: flex; gap: 10px; align-items: center;
background: var(--surface); padding: 8px 16px;
border-radius: 12px; box-shadow: var(--shadow);
border: 1px solid var(--line); max-width: 500px; margin-bottom: 20px;
}
.search-bar input[type="text"] {
flex: 1; border: none; outline: none; padding: 8px 10px;
font-size: 15px; background: transparent; color: var(--ink);
}
.search-bar button {
background: var(--brand); color: #fff; border: none;
padding: 8px 20px; border-radius: 8px; font-weight: 600;
cursor: pointer; white-space: nowrap;
}
.search-bar button:hover { background: var(--brand-2); }
.search-clear { color: var(--muted); font-size: 14px; padding: 8px 4px; white-space: nowrap; }
/* ---- 瀑布流海报墙 ---- */
.waterfall {
display: grid; gap: 20px; padding: 4px 0;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.movie-card {
background: var(--surface); border-radius: var(--radius);
overflow: hidden; box-shadow: var(--shadow); cursor: pointer;
border: 1px solid var(--line); display: flex; flex-direction: column;
text-decoration: none; color: var(--ink);
}
.movie-card:hover {
transform: translateY(-6px); box-shadow: var(--shadow-l);
border-color: var(--brand-2); text-decoration: none;
}
.card-image {
width: 100%; aspect-ratio: 2 / 3; background: var(--line-2);
overflow: hidden; display: flex; align-items: center; justify-content: center;
position: relative; flex-shrink: 0;
}
.card-image img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .4s ease; }
.movie-card:hover .card-image img { transform: scale(1.03); }
.placeholder-poster {
width: 100%; height: 100%; display: flex; flex-direction: column;
align-items: center; justify-content: center; gap: 6px;
background: linear-gradient(135deg, #4a3a30, #2C231D);
color: rgba(255, 255, 255, .55); font-size: 42px; font-weight: 600;
}
.placeholder-poster .placeholder-title {
font-size: 14px; text-align: center; padding: 0 12px;
color: rgba(255, 255, 255, .75); font-weight: 500;
}
.card-body { padding: 14px 16px 16px; flex: 1; display: flex; flex-direction: column; gap: 6px; }
.card-title {
font-size: 16px; font-weight: 600; color: var(--ink);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card-meta {
display: flex; flex-wrap: wrap; gap: 6px 12px;
font-size: 13px; color: var(--muted);
}
.card-meta span { display: inline-flex; align-items: center; gap: 3px; }
.meta-rating { color: var(--brand); font-weight: 600; }
.meta-rating-max { color: var(--muted); font-weight: 400; }
.card-viewers { display: flex; flex-wrap: wrap; gap: 4px 8px; margin-top: auto; }
.viewer-tag {
display: inline-flex; align-items: center; gap: 4px; font-size: 12px;
padding: 2px 8px; border-radius: 12px; background: var(--line-2);
color: var(--ink); text-decoration: none;
/* 底色/字色/边线由 person_chip() 写成行内样式(每人一个颜色,来自他的头像主色);
这里只给**没有行内样式时的兜底**(旧的灰底标签):1px 透明边,
这样有边线和没边线的两种标签高度完全一致,不会一行高一行矮。 */
border: 1px solid transparent;
}
.viewer-tag:hover { text-decoration: none; filter: brightness(.96); }
.viewer-tag .chip-av { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
.viewer-tag .chip-av-text {
background: var(--faint); color: #fff;
display: inline-flex; align-items: center; justify-content: center;
font-size: 10px; line-height: 1;
}
.viewer-tag .chip-name { white-space: nowrap; }
/* ---- 详情页 ---- */
.detail-hero {
display: grid; grid-template-columns: 300px 1fr; gap: 32px;
background: var(--surface); border-radius: var(--radius);
padding: 28px; box-shadow: var(--shadow); margin-bottom: 20px;
border: 1px solid var(--line);
}
.detail-poster {
border-radius: 12px; overflow: hidden; background: var(--line-2);
aspect-ratio: 2 / 3; display: flex; align-items: center; justify-content: center;
}
.detail-poster img { width: 100%; height: 100%; object-fit: cover; }
.detail-poster .placeholder-poster { font-size: 48px; }
.detail-poster .placeholder-poster .placeholder-title { font-size: 16px; }
.detail-info { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.detail-info h1 { font-size: 26px; font-weight: 700; margin: 0 0 4px; color: var(--ink); }
.detail-info .meta-row { display: flex; flex-wrap: wrap; gap: 8px 16px; font-size: 14px; color: var(--muted); }
.detail-info .meta-row span { display: inline-flex; align-items: center; gap: 4px; }
.info-text { color: var(--ink); line-height: 1.7; font-size: 15px; }
.remark-text {
background: var(--line-2); padding: 12px 16px; border-radius: 8px;
color: var(--ink); border-left: 4px solid var(--brand); font-size: 14px;
}
.detail-section {
background: var(--surface); border-radius: var(--radius);
padding: 24px 28px; box-shadow: var(--shadow); margin-bottom: 20px;
border: 1px solid var(--line);
}
.detail-section h3 {
font-size: 17px; font-weight: 600; margin: 0 0 16px; color: var(--ink);
display: flex; align-items: center; gap: 10px;
}
.detail-section h3 .badge {
font-size: 13px; font-weight: 400; background: var(--line-2);
padding: 2px 12px; border-radius: 20px; color: var(--muted);
}
.empty-hint { color: var(--muted); font-size: 14px; margin: 0; }
.viewers-list { display: flex; flex-wrap: wrap; gap: 12px; }
.viewers-list .viewer-tag { padding: 4px 12px 4px 6px; font-size: 14px; }
.viewers-list .viewer-tag .chip-av { width: 28px; height: 28px; }
.viewers-list .viewer-tag .chip-av-text { font-size: 13px; }
.cinema-info {
display: grid; grid-template-columns: 1fr 1fr; gap: 8px 24px; font-size: 15px;
}
.cinema-info .label { color: var(--muted); }
.cinema-info .value { color: var(--ink); font-weight: 500; }
/* ---- 影像记录 ---- */
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px; }
.gallery-item {
border-radius: 10px; overflow: hidden; background: var(--line-2);
aspect-ratio: 4 / 3; display: flex; align-items: center; justify-content: center;
cursor: pointer; transition: transform .2s; position: relative;
border: 1px solid var(--line);
}
.gallery-item:hover { transform: scale(1.02); }
.gallery-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery-item .label-badge {
position: absolute; bottom: 8px; left: 8px;
background: rgba(0, 0, 0, .7); color: #fff; font-size: 11px;
padding: 2px 12px; border-radius: 12px;
}
/* ---- 图片灯箱 ---- */
.lb-openable { cursor: zoom-in; }
.lb-openable:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.lightbox {
position: fixed; inset: 0; z-index: 300; display: none;
align-items: center; justify-content: center;
background: rgba(0, 0, 0, .88); overscroll-behavior: contain;
}
.lightbox.open { display: flex; }
.lb-stage { display: flex; align-items: center; justify-content: center; max-width: 92vw; max-height: 82vh; }
.lb-img {
max-width: 92vw; max-height: 78vh; width: auto; height: auto;
object-fit: contain; border-radius: 8px; box-shadow: 0 20px 60px rgba(0, 0, 0, .5);
user-select: none; -webkit-user-drag: none;
}
.lb-btn {
position: absolute; border: none; cursor: pointer; color: #fff;
background: rgba(255, 255, 255, .12); display: flex;
align-items: center; justify-content: center;
transition: background-color .2s ease; font-family: inherit;
}
.lb-btn:hover { background: rgba(255, 255, 255, .26); }
.lb-btn[hidden] { display: none; }
.lb-close { top: 14px; right: 16px; width: 40px; height: 40px; border-radius: 50%; font-size: 18px; }
.lb-nav { top: 50%; transform: translateY(-50%); width: 48px; height: 48px; border-radius: 50%; font-size: 28px; padding-bottom: 4px; }
.lb-prev { left: 14px; }
.lb-next { right: 14px; }
.lb-bar {
position: absolute; left: 0; right: 0; bottom: 0; padding: 14px 20px;
display: flex; align-items: center; justify-content: space-between; gap: 12px;
color: #fff; font-size: 14px;
background: linear-gradient(to top, rgba(0, 0, 0, .6), rgba(0, 0, 0, 0));
}
.lb-caption { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-counter { flex: 0 0 auto; opacity: .75; font-variant-numeric: tabular-nums; }
/* ---- 评价与评论 ---- */
.rv-composer > summary { list-style: none; cursor: pointer; }
.rv-composer > summary::-webkit-details-marker { display: none; }
.rv-open-btn {
display: inline-block; padding: 8px 18px; border-radius: 10px;
background: linear-gradient(135deg, var(--brand), var(--brand-2));
color: #fff; font-size: 14px; font-weight: 600; user-select: none;
}
.rv-open-btn:hover { text-decoration: none; box-shadow: var(--shadow-l); }
.rv-composer[open] > .rv-open-btn { background: var(--line-2); color: var(--muted); }
.rv-act-btn {
display: inline-block; padding: 4px 12px; border: 1px solid var(--line);
border-radius: 20px; background: var(--surface); color: var(--muted);
font-size: 12.5px; user-select: none; cursor: pointer;
}
.rv-act-btn:hover { border-color: var(--brand); color: var(--brand); text-decoration: none; }
.rv-composer[open] > .rv-act-btn { border-color: var(--brand); background: rgba(168, 74, 40, .08); color: var(--brand); font-weight: 600; }
.rv-form { background: var(--line-2); border-radius: 12px; padding: 16px 18px; margin-top: 10px; }
.rv-composer > .rv-form { margin-bottom: 0; }
.rv-form-inline { padding: 12px 14px; }
.rv-rating-row { display: flex; align-items: center; gap: 8px 14px; flex-wrap: wrap; margin-bottom: 12px; }
.rv-rating-label { font-size: 14px; font-weight: 600; color: var(--ink); }
/* ---- 10 星评分(v10):空心 ☆ 灰、点亮 ★ 金黄。
DOM 按 10..1 倒序渲染 + row-reverse,视觉即 1..10 从左到右;
「点亮的星星」由 JS 加 .lit(含悬停预览),.no-js 兜底用 :has() 只亮已选中那一档。 ---- */
.rv-scale { display: flex; gap: 2px; }
.rv-stars { flex-direction: row-reverse; justify-content: flex-end; }
.rv-star { position: relative; cursor: pointer; line-height: 1; }
.rv-star input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.rv-star .st-glyph { font-size: 28px; line-height: 1; }
.rv-star .st-off { color: #c9cdd4; }
.rv-star .st-on { display: none; color: #f5b301; }
.rv-star.lit .st-on { display: inline; }
.rv-star.lit .st-off { display: none; }
.rv-star:hover .st-off { color: #aeb4be; }
.rv-star:focus-within { outline: 2px solid var(--brand); outline-offset: 1px; border-radius: 4px; }
/* 无 JS 兜底:至少选中的那一档及其左边(视觉)会亮 */
.rv-stars.no-js .rv-star:has(input:checked) .st-on,
.rv-stars.no-js .rv-star:has(input:checked) ~ .rv-star .st-on { display: inline; }
.rv-stars.no-js .rv-star:has(input:checked) .st-off,
.rv-stars.no-js .rv-star:has(input:checked) ~ .rv-star .st-off { display: none; }
/* 评价列表里的静态星级展示 */
.stars-disp { display: inline-flex; gap: 1px; font-size: 17px; line-height: 1; vertical-align: -2px; }
.stars-disp .d-on { color: #f5b301; }
.stars-disp .d-off { color: #d3d7de; }
/* ---- 同行观影人选择(v10):chips + 弹窗 + 拼音首字母联想(v11 起带头像) ---- */
.att-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.att-chips:empty { display: none; }
.att-chip {
display: inline-flex; align-items: center; gap: 6px; padding: 3px 6px 3px 4px;
border: 1px solid var(--line); border-radius: 999px; background: var(--surface);
font-size: 13px;
}
.att-chip img {
width: 22px; height: 22px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
background: #F1E7D6;
}
.att-chip .att-x {
border: 0; background: none; color: var(--muted); font-size: 15px;
line-height: 1; padding: 0 2px; cursor: pointer;
}
.att-chip .att-x:hover { color: #c0392b; }
.att-modal {
position: fixed; inset: 0; z-index: 60; display: flex; align-items: center;
justify-content: center; background: rgba(0, 0, 0, .35); padding: 16px;
}
.att-modal[hidden] { display: none; }
.att-modal-box {
background: var(--surface); border-radius: 12px; box-shadow: 0 12px 40px rgba(0, 0, 0, .2);
width: min(420px, 100%); max-height: 70vh; display: flex; flex-direction: column;
padding: 14px 16px; box-sizing: border-box;
}
.att-modal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.att-close {
border: 0; background: none; font-size: 20px; line-height: 1;
color: var(--muted); cursor: pointer; padding: 2px 6px;
}
.att-modal-box .input { margin-bottom: 8px; }
.att-list { overflow-y: auto; border: 1px solid var(--line); border-radius: 8px; }
.att-item {
display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer;
border-bottom: 1px solid var(--line-2); font-size: 14px;
}
.att-item:last-child { border-bottom: 0; }
.att-item:hover { background: var(--line-2); }
.att-item input { accent-color: var(--brand); flex: 0 0 auto; }
.att-item .att-av {
width: 30px; height: 30px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
background: #F1E7D6;
}
.att-item .att-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att-item .att-initials { margin-left: auto; color: var(--muted); font-size: 12px; }
.att-empty { padding: 10px 12px; color: var(--muted); font-size: 13px; }
/* ---- 豆瓣自动补充(v10):片名行 + 下拉候选 + 海报预览 ---- */
.db-title-row { display: flex; gap: 8px; align-items: center; }
.db-title-row .input { flex: 1 1 auto; }
.db-title-row .btn { flex: 0 0 auto; white-space: nowrap; }
.db-suggest {
margin-top: 6px; border: 1px solid var(--line); border-radius: 10px;
background: var(--surface); overflow: hidden; max-height: 300px; overflow-y: auto;
}
.db-suggest[hidden] { display: none; }
.db-item {
display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 12px;
border: 0; border-bottom: 1px solid var(--line-2); background: none;
text-align: left; cursor: pointer; font: inherit; color: var(--ink);
}
.db-item:last-child { border-bottom: 0; }
.db-item:hover { background: var(--line-2); }
.db-item img { width: 32px; height: 45px; object-fit: cover; border-radius: 4px; background: var(--line-2); flex: 0 0 auto; }
.db-item-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.db-item-tx b { font-size: 14px; }
.db-item-tx small { color: var(--muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.db-poster-preview { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.db-poster-preview[hidden] { display: none; }
.db-poster-preview img {
width: 60px; height: 90px; object-fit: cover; border-radius: 6px;
border: 1px solid var(--line); background: var(--line-2);
}
/* 详情页英雄区里的豆瓣信息块 */
.db-meta {
display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline;
margin: 8px 0 2px; padding: 8px 12px; border-radius: 10px;
background: var(--line-2); font-size: 13px; color: var(--ink);
}
.db-meta .db-rating { color: #f5b301; font-weight: 600; }
.db-meta .db-rating b { font-size: 16px; }
.db-meta .db-line { display: inline-flex; gap: 6px; align-items: baseline; min-width: 0; }
.db-meta .db-label { color: var(--muted); flex: 0 0 auto; }
.db-meta .db-link { margin-left: auto; color: var(--brand); text-decoration: none; white-space: nowrap; }
.db-meta .db-link:hover { text-decoration: underline; }
.rv-word { font-size: 13px; color: var(--muted); }
.rv-word.is-set { color: var(--brand); font-weight: 600; }
.rv-textarea {
width: 100%; min-height: 92px; resize: vertical; padding: 12px 14px;
border: 1px solid var(--line); border-radius: 10px; font-size: 15px;
font-family: inherit; line-height: 1.7; background: var(--surface);
color: var(--ink); box-sizing: border-box;
}
.rv-textarea:focus { outline: none; border-color: var(--brand); }
.rv-textarea-sm { min-height: 56px; font-size: 14px; }
.rv-form-foot { display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-top: 10px; flex-wrap: wrap; }
.rv-form-foot .rv-hint { margin-right: auto; }
.rv-hint { font-size: 12px; color: var(--muted); }
.rv-pick-label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.rv-empty { color: var(--muted); font-size: 14px; }
.rv-list { display: flex; flex-direction: column; gap: 18px; }
.rv-item { border-top: 1px solid var(--line-2); padding-top: 16px; }
.rv-item:first-child { border-top: none; padding-top: 0; }
.rv-head { display: flex; align-items: center; gap: 10px; }
.rv-avatar { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
.rv-avatar-sm { width: 26px; height: 26px; }
.rv-avatar-text {
display: inline-flex; align-items: center; justify-content: center;
background: var(--line-2); color: var(--muted); font-weight: 600; font-size: 15px;
}
.rv-who { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.rv-name { font-size: 14px; font-weight: 600; color: var(--ink); }
/* ⚠️ 写着「我」的那个徽标(类名是「评价」区块 + 「我的」后缀)v11 删掉了:
它在 .rv-who(flex column)里被 align-items:stretch 拉成整行宽的色带,
星星旁边显出一条丑长的品牌色条。删除按钮已经表达了「这是我写的」,
不需要徽标。别再把它加回来。 */
.rv-time { display: block; font-size: 12px; color: var(--muted); }
.rv-score { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px; }
.rv-score .stars-disp { font-size: 16px; }
.rv-score-max { font-size: 12px; font-weight: 500; color: var(--muted); font-variant-numeric: tabular-nums; }
.rv-del { margin-left: auto; }
.rv-del-btn {
background: none; border: none; cursor: pointer; color: var(--muted);
font-size: 14px; line-height: 1; padding: 4px 6px; border-radius: 6px;
}
.rv-del-btn:hover { background: rgba(192, 83, 63, .12); color: var(--red); }
.rv-content {
margin: 8px 0 2px 44px; font-size: 15px; line-height: 1.75;
color: var(--ink); word-break: break-word;
}
.rv-updated { margin-left: 44px; font-size: 12px; color: var(--muted); }
.rv-comments { margin: 10px 0 10px 44px; display: flex; flex-direction: column; gap: 8px; }
.rv-comment { display: flex; gap: 8px; background: var(--line-2); border-radius: 10px; padding: 8px 12px; }
.rv-c-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.rv-c-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rv-c-head .rv-name { font-size: 13px; }
.rv-c-text { font-size: 14px; line-height: 1.7; color: var(--ink); word-break: break-word; }
.rv-c-acts { margin-top: 6px; }
.rv-thread { display: flex; flex-direction: column; gap: 6px; }
.rv-comment-reply { margin-left: 22px; }
.rv-at { font-size: 12.5px; color: var(--muted); margin-bottom: 2px; }
.rv-at span { color: var(--brand); font-weight: 600; }
.rv-actions { display: flex; align-items: flex-start; gap: 8px 10px; flex-wrap: wrap; margin: 12px 0 0 44px; }
.rv-feelings { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 2px; }
.rv-item > .rv-feelings { margin-left: 44px; }
.rv-feeling-pick { margin-top: 8px; }
.feel-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.feel-chip { display: inline-flex; }
.feel-pick { position: relative; }
.feel-pick input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.feel-inner {
display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px;
border: 1px solid var(--line); border-radius: 20px; background: var(--surface);
font-size: 13px; color: var(--muted); cursor: pointer; user-select: none;
}
.feel-pick:hover .feel-inner { border-color: var(--brand); }
.feel-pick input:checked + .feel-inner { border-color: var(--brand); background: rgba(168, 74, 40, .10); color: var(--brand); font-weight: 600; }
.feel-pick:focus-within .feel-inner { outline: 2px solid var(--brand); outline-offset: 1px; }
.feel-emoji { font-size: 14px; }
.feel-chip.is-static {
align-items: center; gap: 4px; padding: 2px 10px; border-radius: 20px;
background: rgba(168, 74, 40, .10); color: var(--brand); font-size: 12px;
}
/* ---- 影院管理页 ---- */
.cinema-h2 { font-size: 17px; font-weight: 600; margin: 0 0 14px; }
.cinema-select-row { display: flex; gap: 10px; align-items: center; }
.cinema-select-row .select { flex: 1; }
.color-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.color-picker { width: 60px; height: 40px; padding: 2px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); cursor: pointer; }
.color-hex { font-size: 14px; color: var(--muted); font-family: monospace; }
.preset-palette { display: flex; flex-wrap: wrap; gap: 6px; }
.preset-color {
display: inline-block; width: 28px; height: 28px; border-radius: 50%;
cursor: pointer; border: 2px solid transparent; transition: transform .2s, border-color .2s;
}
.preset-color:hover { transform: scale(1.1); }
.preset-color.active { border-color: var(--ink); }
.color-preview {
display: inline-block; width: 14px; height: 14px; border-radius: 3px;
margin-right: 6px; vertical-align: middle; border: 1px solid var(--line);
}
.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th { text-align: left; padding: 10px 8px; border-bottom: 2px solid var(--line); color: var(--muted); font-weight: 600; }
.table td { padding: 10px 8px; border-bottom: 1px solid var(--line-2); }
.table tr:hover td { background: var(--line-2); }
/* ---- 观影响应式 ---- */
@media (max-width: 720px) {
.waterfall { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
.card-body { padding: 10px 12px 12px; }
.card-title { font-size: 14px; }
.card-meta { font-size: 11px; gap: 4px 10px; }
.detail-hero { grid-template-columns: 1fr; padding: 20px; }
.detail-poster { max-width: 240px; margin: 0 auto; }
.cinema-info { grid-template-columns: 1fr; }
.gallery-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
.detail-section { padding: 16px 18px; }
.detail-info h1 { font-size: 22px; }
.rv-content, .rv-updated, .rv-comments, .rv-actions, .rv-item > .rv-feelings { margin-left: 0; }
.rv-comment-reply { margin-left: 10px; }
.lb-nav { width: 40px; height: 40px; font-size: 24px; }
.lb-prev { left: 6px; }
.lb-next { right: 6px; }
.lb-img { max-width: 88vw; max-height: 70vh; }
}
/* ============================================================
v11:观影统计 / 高级搜索 / 年度报告(从 MovieLog 迁入)
版式沿用家书的暖纸配色;图表全用 CSS(不引 CDN 图表库)。
============================================================ */
/* ---- 统计仪表板 ---- */
.stats-grid {
display: grid; gap: 14px; margin-bottom: 16px;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.stat-card {
background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
padding: 16px 18px; text-align: center; box-shadow: var(--shadow);
}
.stat-card .number { font-size: 30px; font-weight: 700; color: var(--brand); line-height: 1.2; font-variant-numeric: tabular-nums; }
.stat-card .label { font-size: 13px; color: var(--muted); margin-top: 2px; }
.stats-span {
display: flex; align-items: baseline; gap: 12px; margin-bottom: 16px;
}
.stats-span .label { font-size: 13px; color: var(--muted); }
.stats-span .value { font-size: 16px; font-weight: 600; }
.chart-card {
background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
padding: 18px 22px; box-shadow: var(--shadow); margin-bottom: 16px;
}
.chart-card h3 { font-size: 16px; font-weight: 600; margin: 0 0 14px; display: flex; align-items: baseline; gap: 8px; }
.chart-card h3 .hint { font-size: 12px; color: var(--faint); font-weight: 400; }
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 720px) { .two-col { grid-template-columns: 1fr; } }
.bar-chart { display: flex; flex-direction: column; gap: 8px; }
.bar-item { display: flex; align-items: center; gap: 12px; }
.bar-item .bar-label {
width: 150px; flex: 0 0 auto; font-size: 13px; color: var(--ink);
display: flex; align-items: center; gap: 6px;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bar-item .bar-label .bar-sub { color: var(--faint); font-size: 11px; }
.bar-item .bar-avatar { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
.bar-item .color-dot { display: inline-block; width: 11px; height: 11px; border-radius: 3px; flex: 0 0 auto; }
.bar-item .bar-track {
flex: 1; height: 20px; background: var(--line-2); border-radius: 6px; overflow: hidden;
}
.bar-item .bar-fill {
display: block; height: 100%; border-radius: 6px; background: var(--brand);
}
.bar-item .bar-fill-alt { background: var(--brand-2); }
.bar-item .bar-fill-green { background: var(--green); }
.bar-item .bar-count {
width: 40px; flex: 0 0 auto; text-align: right; font-size: 13px; font-weight: 600;
font-variant-numeric: tabular-nums;
}
@media (max-width: 720px) {
.bar-item .bar-label { width: 92px; font-size: 12px; }
.bar-item .bar-count { width: 30px; }
.chart-card { padding: 14px 16px; }
}
/* ---- 高级搜索 ---- */
.adv-form { margin-bottom: 18px; }
.date-range { display: flex; align-items: center; gap: 8px; }
.date-range .to { color: var(--muted); font-size: 13px; flex: 0 0 auto; }
/* ⚠️ 原生 <input type="date"> 的 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; }
.trip-people { display: flex; flex-wrap: wrap; gap: 4px 8px; }
.trip-hero { display: flex; gap: 20px; flex-wrap: wrap; }
.trip-hero-cover {
width: 260px; max-width: 100%; aspect-ratio: 4 / 3; 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; }
.node-ops { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }
.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; }
.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-size { font-size: 11px; color: var(--faint); }
.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); }
.res-ops { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.res-share-add > summary { cursor: pointer; }
/* ---- 任务看板 ---- */
.kanban { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; align-items: start; }
.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); }
.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; }
@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; }
/* ---- 首页 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); }
@media (max-width: 620px) {
.dash-when { flex: 0 0 auto; }
}
/* ---- v14:导航条从 4 个模块变成最多 8 个,窄屏必须能横向滑动 ----
⚠️ 这是本轮**实测出来的回归**:模块从 4 个加到 8 个之后,
375px 视口下 scrollWidth=472 > clientWidth=375,**整页横向溢出**
(顶栏把 body 撑宽了)。以前 4 个模块刚好塞得下,所以从没有人给 .nav 写过窄屏规则。
修法用「导航自己横向滚动」而不是「换行」:
换行会把顶栏从 60px 撑成两行,sticky 顶栏一高一矮很难看,而且滚动时页面会跳。
⚠️ 断点必须是 **720px**(站点既有的"窄屏留白 16px"断点),不能用 860:
746px 宽时若只改顶栏留白,就会出现「顶栏 x=16、正文 x=24」错位 8px
(几何检查会直接报出来)。窄屏规则只在站点真正切到小留白的那一档生效。 */
@media (max-width: 720px) {
.topbar-inner { gap: 12px; padding: 0 16px; }
/* min-width:0 是关键:flex 子项默认 min-width:auto,会拒绝被压缩,
于是内部再 overflow-x 也撑不出滚动条,而是把父级一起顶宽。 */
.nav {
flex: 1 1 auto;
min-width: 0;
margin-left: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none; /* Firefox */
}
.nav::-webkit-scrollbar { display: none; } /* Chrome/Safari:藏掉滚动条,仍可滑 */
.nav a { flex: 0 0 auto; padding: 6px 10px; }
.topbar-right { margin-left: 0; gap: 6px; }
/* 窄屏把「家书」两个字收起来,只留图标 —— 名字占的宽度正好够多放两个模块 */
.brand span { display: none; }
.who .who-name { display: none; }
}