Files
FamilyBook/assets/css/app.css
T
laoyang 7f00d3e553 feat(kin,movies): 家谱改用第三方库 family-chart;观影设置按钮改纯图标并统一页头度量
家谱(v13):
- 自研布局是死结,不是样式问题:连线的横杆是网格项,只能横跨「父母列→子女列」
  整段区间,中间夹着与血缘无关的人(如媳妇那侧的岳父母)时必然从卡片上穿过。
  换成 family-chart(D3 生态,MIT)后,排布只走本家血脉 + 配偶,姻亲不进画布,
  这个死结在构造上就不存在了。
- 接入踩到并修掉的坑:main_id = data[0].id(「我」必须排最前);
  卡片回调里自定义字段在 d.data.data(双层);库的 family-chart.css 是死文件
  (全挂在 .f3 作用域,运行时没有 .f3 元素);库的 svg 撑满选择器永不命中
  (必须自己补 .kin-chart svg.main_svg);setCanAdd/setOnCardClick 不在 chart 上
  (写错会抛异常,把 updateTree 一起带走 → 整张图空白,页面照样 200)。
- 关掉 setSingleParentEmptyCard:库默认给孩子只登记一位父母的人自动补一张无名
  配偶卡,看着像「凭空多出一个配偶」。
- 卡片用原生 <a> 链成员页,不依赖库的点击回调。

观影按钮(用户本轮原话):
- 「记一场电影」与「设置」高度仍不一致 —— 根因是文字按钮高度由 padding+line-height
  自然推出(43.8px),而 .btn-icon 写死了 40px。抽出 --act-* / --act-h 变量,两边同源。
- 设置按钮去掉「设置」二字,只留图标,图标换成三条横线(☰)。
- 设置按钮在所有页面排最右(.movie-set 用 margin-left:auto 顶到行尾)。
- 除设置外其他按钮都有颜色,与设置区分(主按钮实心 / 次要浅底 / 危险淡红)。
  顺带修掉一个特异性 bug:删除按钮被 .btn 品牌底盖成实心红,与「编辑」难分辨。
- 设置下拉四个条目文字前都加图标(行内 SVG,不引外部字体)。

测试与部署:
- 新增 _fb_kin_check.py(桌面,12 条)+ _fb_kin_falsify.py(6/6)证明
  「整张家谱空白」这类只靠通用几何抓不到的坏法能被抓住。
- _fb_geom_mobile.py 家谱段重写为窄屏语义(库按容器 fit 缩放是正常行为,
  改为断「比例没被压扁 / 缩放不至于看不清」),并加 _fb_kin_mobile_falsify.py(4/4)。
- _fb_btn_check.py(59 条)+ _fb_btn_falsify.py(3/3)覆盖观影页头按钮五条需求。
- familybook_deploy.sh / familybook_smoke.sh 的家谱断言改为第三方库形态,
  设置按钮断言改为 btn-icon 纯图标 + 三条横线 + 条目图标。
- 新增 .gitattributes:vendor 库与 .min.js 标 -text,原样入库不被改写行尾。
2026-10-01 19:35:48 +08:00

1467 lines
70 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; }
/* 首页头图 */
.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;
}
/* ---------------- 家谱(以我为中心) ----------------
v13:改由第三方库 **family-chart**(D3 生态,MIT)画,不再自研网格布局。
为什么不再自研:v12 的「整棵树一个 grid」几何上对齐了,但连线的横杆是**网格项**,
只能横跨「从父母列到子女列」的整段列区间 —— 中间只要夹着跟这条血缘无关的人
(最典型是配偶带进来的岳父母),横杆就会从他们卡片中间穿过去。这是布局范式的死结,
不是调样式能救的;换成库的树算法之后这层结构本身就不存在了。
⚠️ 库自己的 family-chart.css **没有引入**:实测库里所有规则都写在 `.f3` 作用域下,
而运行时并不会生成任何 `.f3` 元素(卡片定位全靠 inline style)——引进来是死文件,
白白多 16KB。所以下面这些是**库真正需要**的那几条,全部收在 `.kin-chart` 作用域里。
⚠️ 卡片/连线都不是我们生成的:卡片是库建的真实 <div>(.card_cont > .card > 我们的
.kin-card),连线是 svg 里的 <path class="link">。改版式只能从这两个钩子下手。 */
.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;
/* 库自己接管拖拽/缩放,别让浏览器插手 */
touch-action: none;
}
/* 库在容器里自己建 #f3Canvas(position:relative; width/height:100%),
再往里插 svg.main_svg 与 #htmlSvg。这里只要保证那两层撑满。 */
.kin-chart #f3Canvas,
.kin-chart #htmlSvg { width: 100%; height: 100%; }
/* ⚠️ 库的 `.f3 svg.main_svg{width:100%;height:100%}` 选择器**永远不会命中**
(SVG 的直接父是 #f3Canvas,中间没有 .f3 元素)→ svg 会退回默认的 300×150,
于是库按 300×150 算 fit,整棵树被缩到 0.4 倍、卡片只有 37px 宽。
这里直接点名修掉。 */
.kin-chart svg.main_svg { width: 100%; height: 100%; display: block; }
/* 连线:库默认 stroke="#fff"(深色主题),在浅色纸底上等于隐形 —— 必须覆盖成褐灰。
用 !important 是因为颜色是**写在 path 的行内属性**上的(stroke="#fff"),
普通 CSS 声明(无论特异性)都压不过行内属性;这个钩子是唯一入口。 */
.kin-chart .link,
.kin-chart path.link { stroke: #C9BDA9 !important; stroke-width: 1.5 !important; }
/* 卡片:库给 .card_cont 定位、给 .card 一律加 translate(-50%,-50%)(居中锚点),
我们只负责卡面观感。 */
.kin-chart .card { background: transparent; border: 0; }
.kin-card {
display: flex; flex-direction: column; align-items: center; gap: 3px;
width: 116px; box-sizing: border-box; padding: 9px 8px 8px;
text-align: center;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 14px;
box-shadow: var(--shadow);
color: var(--ink);
transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.kin-chart .card:hover .kin-card {
transform: translateY(-2px);
box-shadow: var(--shadow-l);
border-color: var(--brand-2);
}
.kin-card .kin-ph {
width: 46px; height: 46px; border-radius: 50%; 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: 13px; font-weight: 600; line-height: 1.3; }
.kin-card .kin-lb { font-size: 11px; color: var(--brand); line-height: 1.35; }
.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; }
.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;
}
.viewer-tag:hover { text-decoration: none; background: var(--line); }
.viewer-tag img { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; }
/* ---- 详情页 ---- */
.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 img,
.viewers-list .rv-avatar-text { width: 28px; height: 28px; }
.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);
}