前端改造:设计令牌 + 卡片层级 + 图表可读性 + 类型色收口 + 窄屏导航

先出方案(frontend-plan-2026-10-09.md)再动手,四批按顺序落地。方法上先存基线、再用同一份
探针在改动前后逐值 diff —— 这批里有 6 处问题只有「量」才看得见,看截图全是正常的。

A 设计令牌(app.css 为主)
- 字阶收敛到 6 档 --fs-xs/sm/md/lg/xl/2xl = 11/13/15/21/26/34px。原来 app.css 有 19 种字号、
  模板里另有 54 处内联字号,光 12~15px 之间就挤了 4 档 —— 层级挤不出来,只能靠加粗硬顶。
- 两个「字外档」不是漏的:--fs-control: 16px(窄屏表单控件必须 ≥16px,否则 iOS 聚焦放大整页,
  属功能约束);--glyph-*(✕/箭头/空态图标按控件尺寸定,硬塞进字阶只会把按钮撑坏)。
- 字重收敛到 400/500/600(删掉 650 与 700)。
- 圆角 4 档 --r-*、阴影 3 档 --sh-1/2/3(暗色整套覆盖)、间距 --sp-1..5、
  卡片内边距单列 --card-pad(与折叠卡片标题行的负 margin 绑在一起,改一个即改两处)。
- 门禁 TestNoBareFontSizePx:静态扫 app.css 与全部模板,出现 font-size: <数字>px 即红。

B 卡片分主次
- .card--hero / .card(默认)/ .card--quiet 三档。quiet 走「页面底色 + 细边 + 无阴影」——
  第一版拿 --bg-2 当底色,实测它与页面底 #f6f7f8 只差 4/255,看不出边界、像破了个洞。
- 首屏 KPI 从「4×2 等重白盒」改成 .kpi-lead(主数 + 一行上下文)+ 5 个常规指标,8 个数字一个没少。

C 图表说人话
- 月度分布:柱底加基线;峰值柱不透明拉满、柱顶标次数(.pv),副标题里不再重复写「最高 N 次」。
- 一天中的分布:把「0/6/12/18 四个数字」换成 6 小时一段的标尺(深夜/上午/下午/晚上),
  用同一套 24 列网格,段边界与柱子严格对齐。
- 首页月度图同样加峰值锚点(给 HomeData 补 MaxMonthly,与统计页同口径 maxOf)。
- 空态没动:查下来已经统一(8 页共用 emptystate、局部走 .empty)。

D1 类型色语义化(结论与计划不同,按实测改了)
- 先算对比度:白底上 yellow 1.87 / mint 2.12 / orange 2.20 / green 2.22 / cyan 2.54 / teal 2.57,
  6 个色连图形对象 3:1 都到不了;暗底只有 indigo 2.90 不够。
- 所以**没有**把类型色用到 chips / 表格类型列 / KPI 强调 —— 那几处要的是文字色,只会更难读。
  这也解释了全站为什么一直只用 8px 小圆点做身份标记。浅色那 6 个色一个没动:
  它们是手机端与地图上的同一套身份色,改暗会破坏对应关系。
- 实际做的:色值收口成 --cat-* / --cat-*-soft(26 个变量);模板 colorof → csscolor / csssoft(19 处);
  暗色只提亮 indigo(2.90 → 3.55:1);colorOf 成了死代码,删掉。
- ⚠️ 返回值必须是 template.CSS:html/template 的 CSS 过滤器不认识 var(),普通 string 会被静默
  换成 ZgotmplZ —— 页面上所有圆点当场变透明,而且不报错、模板测试照过。挂了它等于关掉那道过滤,
  所以数据侧色值先过严格校验(只有 # + 6 位 hex 放行),否则 #"><img… 能逃出 style 属性。
  守卫 TestCSSColorSurvivesTemplateSanitizer 把两个方向都钉住(做过变异验证)。

D2 窄屏导航
- 改前 ≤620px 靠横向滚动兜底:390 溢 63px、320 溢 133px,「统计」被切成半个字紧贴后面的按钮,
  渐隐提示实际看不出来。现在链接本体抽成 {{define "navitems"}} 渲染两遍:桌面 .nav-links 平铺、
  窄屏 .nav-menu(原生 <details>,不靠 JS 也能开合);JS 只补点外面 / Esc / 点链接后收起。
- ⚠️ 顶栏 --z-nav 抬到 1200:菜单是 .nav 的子元素、自己抬不动层叠上下文,不抬就会被地图页的
  --z-map-overlay(1100) 盖住。已在 /map 上用 elementFromPoint 实测。
- 守卫 TestNarrowNavCollapsesToMenu 钉「CSS 两半 + 模板两个类 + 渲染两遍」的成对关系。

途中发现并一并修掉的(都只有量 DOM 才看得见)
- Leaflet 的 .leaflet-container 字号是 0.75rem、跟着根字号漂:根 14 → 13px 后地图署名/比例尺
  掉到 9.8px(低于本站 11px 下限)、弹窗正文也跟着缩。已按自己的字阶钉死。
- h1..h4 原来只设了字重与 margin、没设字号,靠浏览器默认的 2em/1.5em/1.17em —— 同样跟着根字号漂:
  /stats 那两个 h2 从 21px 变成 19.5px,比同页卡片标题大一圈。已显式给出字号。
- <b>/<strong> 浏览器默认 700,比全站最高层级(标题 600)还重,已钉成 600。

回归
- ./scripts/verify.sh:PASS=348 FAIL=0 SKIP=0
- internal/fog 加 -race:PASS=22 FAIL=0 SKIP=0
- JS 单测 34/34
- 移动端 320 / 390 溢出全 0(改前 /admin 在 320 还溢 34px);桌面 1440 与改前一致(−15 是滚动条宽)
- 全站 7 个页面都没有低于 11px 的文字

说明:A/B 与 C/D 本想拆两笔提交,但 app.css / layout.html / view.go 里两批改动交织,按文件拆不开、
按 hunk 拆又容易做出「中间那笔编译不过」的历史,所以合成一笔、在消息里分段。
This commit is contained in:
tamakyi
2026-10-09 17:01:08 +08:00
parent d4e4257a23
commit ab7ccd22ce
32 changed files with 997 additions and 283 deletions
+332 -165
View File
@@ -19,11 +19,70 @@
--row-hover: #fbfcfc;
--bg-2: #fafbfb; /* 比卡片略深一层的面(行内编辑表单、空态框) */
--on-accent: #ffffff; /* 主色底上的文字 */
/* ---------- 类型色(活动 / 交通方式)----------
色名是 rond 里的 Apple 日历色名(与 Go 侧 appleColors 的键一一对应,模板经
csscolor / csscolorsoft 取用)。地图那份调色板在 rond-map.js 里、**故意独立**:
地图瓦片在暗色下保持浅色,标记得用浅色底上那套值,不能跟着页面的暗色变量走。
⚠️ 这套色的**明度差异很大**:在白底上 yellow 1.87、mint 2.12、orange 2.20、green 2.22、
cyan 2.54、teal 2.57 —— 都远低于图形对象 3:1。所以它们只能当「身份标记」(圆点、柱条),
**别拿去当文字色或承载唯一信息的图形**,这也正是全站只用小圆点而不是彩色标签的原因。 */
--cat-red: #ff3b30; --cat-orange: #ff9500; --cat-yellow: #e8b700;
--cat-green: #34c759; --cat-mint: #00c7be; --cat-teal: #30b0c7;
--cat-cyan: #32ade6; --cat-blue: #007aff; --cat-indigo: #5856d6;
--cat-purple: #af52de; --cat-pink: #ff2d55; --cat-brown: #a2845e;
--cat-gray: #8e8e93;
/* 色名不认识时的兜底,与 Go 里 cssColor 的 var(--cat-none) 对齐 */
--cat-none: #5b6169;
/* 浅底(emoji 方块那种):半透明叠在卡片上,浅色暗色自适应。
原来是模板里给 hex 拼 "22" 当 alpha——换成 3 位色值或 var() 就散了,这里收口。 */
--cat-red-soft: rgba(255, 59, 48, .14); --cat-orange-soft: rgba(255, 149, 0, .14);
--cat-yellow-soft: rgba(232, 183, 0, .14); --cat-green-soft: rgba(52, 199, 89, .14);
--cat-mint-soft: rgba(0, 199, 190, .14); --cat-teal-soft: rgba(48, 176, 199, .14);
--cat-cyan-soft: rgba(50, 173, 230, .14); --cat-blue-soft: rgba(0, 122, 255, .14);
--cat-indigo-soft: rgba(88, 86, 214, .14); --cat-purple-soft: rgba(175, 82, 222, .14);
--cat-pink-soft: rgba(255, 45, 85, .14); --cat-brown-soft: rgba(162, 132, 94, .16);
--cat-gray-soft: rgba(142, 142, 147, .16);
--cat-none-soft: rgba(91, 97, 105, .14);
--ok-bg: #eef8f5; --ok-fg: #17604f; --ok-line: #cfe6e0;
--err-bg: #fdf3f2; --err-fg: #b42318; --err-line: #f3dcda;
--info-bg: #f2f6fd; --info-fg: #2c5282; --info-line: #dbe5f5;
--radius: 10px;
--shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 1px 3px rgba(16, 24, 40, .06);
/* 圆角 4 档。原来是 7 / 8 / 9 / 10 一堆近似值混着用,肉眼分不出、改一处要改一片。 */
--r-xs: 3px; /* 小方块(热力格、气温柱顶) */
--r-sm: 6px; /* 控件(按钮、输入框、导航链接、标签) */
--r-md: 8px; /* 中号面(缩略图、提示条) */
--r-lg: 12px; /* 卡片与浮层 */
/* 阴影 3 档,暗色模式整套覆盖(见文件末尾的 prefers-color-scheme) */
--sh-1: 0 1px 2px rgba(16, 24, 40, .04), 0 1px 3px rgba(16, 24, 40, .06);
--sh-2: 0 4px 14px rgba(16, 24, 40, .10), 0 1px 3px rgba(16, 24, 40, .06);
--sh-3: 0 10px 30px rgba(16, 24, 40, .12), 0 2px 6px rgba(16, 24, 40, .06);
/* 间距 5 档。本批只用在卡片内边距这类「主节奏」上,其余逐批替换。 */
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;
/* 卡片内边距单列一个变量:折叠卡片的标题行要用负 margin 顶到边(见 .filters-card),
两处必须永远相等,所以绑成同一个值。 */
--card-pad: 18px;
/* ---------- 字阶 ----------
文本只有 6 档。原来是 10/10.5/11/11.5/12/12.5/12.8/13/13.5/14/14.5/15/17/18/21px
十几档挤在一起,12~15px 之间就塞了 4 档 —— 层级挤不出来,只能靠加粗硬顶。 */
--fs-xs: 11px; /* 脚注 / 单位 / 轴标 / 图例 */
--fs-sm: 13px; /* 正文(默认) */
--fs-md: 15px; /* 卡片标题 / 强调文本 */
--fs-lg: 21px; /* 页面标题 */
--fs-xl: 26px; /* KPI 数值 */
--fs-2xl: 34px; /* 首屏主数 */
/* 表单控件字号单独一档:窄屏必须 ≥16px,否则 iOS 一聚焦就把整页放大。
它不属于文本层级,所以不并进上面的字阶。 */
--fs-control: 16px;
/* 字形尺寸(✕ / 箭头 / 空态图标 / 表情):按控件尺寸定,不是文字层级。
故意不进字阶 —— 把 22px 的 ✕ 塞进 26px 的字阶只会把按钮撑坏。 */
--glyph-xs: 12px;
--glyph-sm: 15px;
--glyph-md: 18px;
--glyph-lg: 22px;
--glyph-xl: 30px;
--glyph-2xl: 40px;
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
/* 顶栏高度:窄屏更矮。地图页的定位跟它对齐,避免出现几像素的缝。 */
--nav-h: 54px;
@@ -33,6 +92,10 @@
分两档是为了保住原有次序:加载状态条要压在所有浮层之上。 */
--z-map-overlay: 1100;
--z-map-status: 1150;
/* 顶栏。必须压过地图页的浮层(--z-map-overlay = 1100):窄屏的导航菜单是从顶栏
展开的,而它属于 .nav 这个层叠上下文、自己抬不动 —— 只有把整条顶栏放到浮层之上,
展开后才不会被地图的筛选面板盖住(实测过 elementFromPoint)。 */
--z-nav: 1200;
/* 刘海屏 / 底部 Home 指示条的安全区。不支持 env() 的浏览器回落到 0。 */
--safe-t: env(safe-area-inset-top, 0px);
--safe-r: env(safe-area-inset-right, 0px);
@@ -47,17 +110,28 @@ html, body {
padding: 0;
background: var(--bg);
color: var(--text);
font: 14px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", sans-serif;
font-size: var(--fs-sm);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -.01em; }
/* 显式给字号。这条规则以前只管字重和 margin,字号靠浏览器默认的 2em / 1.5em / 1.17em
—— 也就是跟着**根字号**漂:根从 14px 降到 13px 后,没被 `.card > h2` 命中的那些 h2
从 21px 悄悄变成 19.5px(/stats 的「我的结论」「访客点赞」两块),比同页卡片标题大一圈。 */
h1 { font-size: var(--fs-lg); }
h2 { font-size: var(--fs-md); }
h3, h4 { font-size: var(--fs-sm); }
/* <b>/<strong> 的浏览器默认是 bold(700),而全站层级只到 600 —— 不钉住的话
行内加粗会比标题还重。 */
b, strong { font-weight: 600; }
/* ---------- 顶栏 ---------- */
.nav {
position: sticky; top: 0; z-index: 500;
position: sticky; top: 0; z-index: var(--z-nav);
background: var(--glass);
backdrop-filter: saturate(1.6) blur(10px);
border-bottom: 1px solid var(--line);
@@ -66,41 +140,108 @@ h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -.01em; }
}
.nav-inner {
max-width: 1120px; margin: 0 auto; padding: 0 20px;
/* 菜单是相对 .nav-inner 绝对定位的(要铺满整条),所以这里得有定位上下文 */
position: relative;
height: var(--nav-h); display: flex; align-items: center; gap: 22px;
}
.brand { display: flex; align-items: center; gap: 8px; font-weight: 600; color: var(--text); font-size: 15px; }
.brand { display: flex; align-items: center; gap: 8px; font-weight: 600; color: var(--text); font-size: var(--fs-md); }
.brand:hover { text-decoration: none; }
.brand .avatar { font-size: 18px; }
.nav-links { display: flex; gap: 4px; flex: 1; }
.brand .avatar { font-size: var(--glyph-md); }
/* 链接行自带横向兜底:620px 以下整行会收进 .nav-menu,这条就只为「以后又加了入口、
宽屏也放不下」兜底 —— 宁可让它能滑,也别把最右边那个字切掉一半。 */
.nav-links { display: flex; gap: 4px; flex: 1; overflow-x: auto; scrollbar-width: none; }
.nav-links::-webkit-scrollbar { display: none; }
/* 还能往右滑时右缘渐隐(由 nav 的脚本按 scrollLeft 加/去这个类)。用 mask 而不是盖一层
渐变伪元素:伪元素会把链接文字一起遮住,mask 是让内容本身渐隐。 */
.nav-links.overflow-right {
-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
.nav-links a {
padding: 5px 11px; border-radius: 7px; color: var(--text-2); font-size: 13.5px; white-space: nowrap;
padding: 5px 11px; border-radius: var(--r-sm); color: var(--text-2); font-size: var(--fs-sm); white-space: nowrap;
}
.nav-links a:hover { background: var(--line-soft); color: var(--text); text-decoration: none; }
.nav-links a.on { background: var(--accent-soft); color: var(--accent); font-weight: 500; }
.nav-right { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--text-3); }
.nav-right { display: flex; align-items: center; gap: 10px; font-size: var(--fs-sm); color: var(--text-3); }
/* 窄屏导航菜单。620px 以下链接行一定放不下(390 溢 63px、320 溢 133px),整行收进这里。
用原生 <details>,不靠 JS 也能开合;JS 只补三件原生不给的事:点外面 / Esc / 点链接后收起。 */
.nav-menu { display: none; }
.nav-menu > summary {
list-style: none; cursor: pointer; user-select: none; white-space: nowrap;
display: inline-flex; align-items: center; gap: 4px;
min-height: 44px; padding: 0 10px; border-radius: var(--r-sm);
color: var(--text-2); font-size: var(--fs-sm);
}
.nav-menu > summary::-webkit-details-marker { display: none; }
.nav-menu > summary::after { content: "▾"; font-size: var(--glyph-xs); color: var(--text-3); }
.nav-menu[open] > summary { background: var(--accent-soft); color: var(--accent); }
.nav-menu[open] > summary::after { content: "▴"; }
.nav-menu-body {
position: absolute; left: 0; right: 0; top: 100%; z-index: 1;
display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px; padding: 6px;
background: var(--panel); border-bottom: 1px solid var(--line); box-shadow: var(--sh-2);
}
.nav-menu-body a {
display: flex; align-items: center; min-height: 44px; padding: 0 12px;
border-radius: var(--r-sm); color: var(--text-2); font-size: var(--fs-sm);
}
.nav-menu-body a:hover { background: var(--line-soft); color: var(--text); text-decoration: none; }
.nav-menu-body a.on { background: var(--accent-soft); color: var(--accent); font-weight: 500; }
/* ---------- 布局 ---------- */
.wrap { max-width: 1120px; margin: 0 auto;
padding: 22px calc(20px + var(--safe-r)) calc(60px + var(--safe-b)) calc(20px + var(--safe-l)); }
.page-head { margin-bottom: 18px; display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.page-head h1 { font-size: 21px; }
.page-head .sub { color: var(--text-3); font-size: 13px; margin-top: 3px; }
.page-head h1 { font-size: var(--fs-lg); }
.page-head .sub { color: var(--text-3); font-size: var(--fs-sm); margin-top: 3px; }
.card {
background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
box-shadow: var(--shadow); padding: 18px; margin-bottom: 16px;
background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
box-shadow: var(--sh-1); padding: var(--card-pad); margin-bottom: var(--sp-4);
}
.card > h2 { font-size: 15px; margin-bottom: 2px; display: flex; align-items: center; gap: 8px; }
.card > h2 { font-size: var(--fs-md); margin-bottom: 2px; display: flex; align-items: center; gap: var(--sp-2); }
/* .card-body 是数据编辑页折叠用的容器,卡片内容被它包住后不再是 .card 的直接子元素,
所以下面两条用「后代」而不是「子」选择器——否则折叠脚本一加上,间距就全没了。 */
.card .card-sub { color: var(--text-3); font-size: 12.5px; margin-bottom: 14px; }
.card .card-sub { color: var(--text-3); font-size: var(--fs-sm); margin-bottom: var(--sp-3); }
.card.collapsed .card-body { display: none; }
/* 卡片三档。原来全站只有一种白盒(量下来 17 个盒子只有 2 种样式),KPI / 地图 / 图表 /
表格 / 筛选器全等重,扫视时没有落点。分档不是装饰,是为了「一眼知道先看哪」:
hero 只给首屏主数区与地图,quiet 只给纯说明性的内容。 */
.card--hero { border-color: transparent; box-shadow: var(--sh-3); padding: calc(var(--card-pad) + var(--sp-2)); }
/* quiet 用页面底色 + 细边 + 无阴影 = 贴在页面上的扁平面板。
注意别拿 --bg-2 (#fafbfb) 当底色:它和页面底 #f6f7f8 只差 4/255,
实测根本看不出边界,卡片会像「破了个洞」而不是「轻了一档」。 */
.card--quiet { background: var(--bg); border-color: var(--line); box-shadow: none; }
.card--quiet > h2 { font-size: var(--fs-sm); color: var(--text-2); }
/* 首屏主数:把最重要的那个数从 KPI 格里拎出来单独一行。
原来 8 个数字一样大一样重,扫过去不知道看哪个 —— 层级靠「只让一个特别大」,
而不是把所有的都调大。 */
.kpi-lead {
position: relative; overflow: hidden;
background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
box-shadow: var(--sh-3);
padding: calc(var(--card-pad) + var(--sp-1)) var(--card-pad) var(--card-pad);
margin-bottom: var(--sp-3);
}
.kpi-lead::before {
content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--accent);
}
.kpi-lead .kl-v {
font-size: clamp(var(--fs-xl), 7.2vw, var(--fs-2xl)); font-weight: 600;
letter-spacing: -.02em; line-height: 1.1; font-variant-numeric: tabular-nums;
}
.kpi-lead .kl-v small { font-size: var(--fs-md); font-weight: 500; color: var(--text-3); margin-left: var(--sp-1); }
.kpi-lead .kl-k { color: var(--text-2); font-size: var(--fs-sm); margin-top: var(--sp-1); }
.kpi-lead .kl-x { color: var(--text-3); font-size: var(--fs-sm); margin-top: var(--sp-2); }
/* 只有带 data-card 的卡片(数据编辑页)能折叠:标题整行可点,右侧一个箭头 */
.card[data-card] > h2 { cursor: pointer; }
.card[data-card] > h2 .card-toggle {
margin-left: auto; flex: 0 0 auto; cursor: pointer;
background: none; border: 0; padding: 0 2px; color: var(--text-3);
font: inherit; font-size: 12px; line-height: 1;
font: inherit; font-size: var(--glyph-xs); line-height: 1;
}
.card[data-card] > h2 .card-toggle:hover { color: var(--text); }
.grid { display: grid; gap: 16px; }
@@ -118,21 +259,13 @@ h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -.01em; }
.g2, .g3, .g4 { grid-template-columns: 1fr; }
.wrap { padding: 16px calc(14px + var(--safe-r)) calc(48px + var(--safe-b)) calc(14px + var(--safe-l)); }
.nav-inner { padding: 0 10px; gap: 6px; }
/* 窄屏只留头像,导航可横向滚动,避免换行把布局撑坏 */
/* 窄屏只留头像 */
.brand .t { display: none; }
.nav-links { overflow-x: auto; scrollbar-width: none; gap: 0; }
.nav-links::-webkit-scrollbar { display: none; }
/* 还能往右滑的时候右缘渐隐一下(由 nav 后面那几行脚本按 scrollLeft 加/去这个类)。
用 mask 而不是盖一层渐变伪元素:伪元素会把链接文字一起遮住,mask 是让内容本身渐隐。
不支持 mask 的浏览器只是少个提示,滑动照旧。 */
.nav-links.overflow-right {
-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
/* 内边距由下面「窄屏触控」那块给(同特异性、写在后面才生效);这里只留字号。 */
.nav-links a { font-size: 13px; }
/* 链接行整行收进 .nav-menu(样式见上面那段),这里不再横向滚动 */
.nav-links { display: none; }
.nav-menu { display: block; flex: 1; }
.nav-right .who { display: none; }
.nav-right .btn.sm { padding: 4px 9px; font-size: 12.5px; }
.nav-right .btn.sm { padding: 4px 9px; font-size: var(--fs-sm); }
}
/* ---------- 窄屏触控与表单(移动端适配) ---------- */
@@ -164,26 +297,26 @@ h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -.01em; }
/* 用 flex + grow 而不是固定列数的网格:KPI 个数常常是 6、7 这种除不尽列数的,
网格会在最后一行留下空格子——而间隙是用底色画的,空格子就露成灰块,很难看。
flex 会让最后一行自己撑满,任何个数都不会有空格。 */
.kpis { display: flex; flex-wrap: wrap; gap: 1px; background: var(--line); border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); }
.kpi { flex: 1 1 158px; min-width: 0; background: var(--panel); padding: 15px 16px; }
.kpis { display: flex; flex-wrap: wrap; gap: 1px; background: var(--line); border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--line); }
.kpi { flex: 1 1 158px; min-width: 0; background: var(--panel); padding: var(--sp-4); }
/* 字号跟着视口收:窄屏上「112天19小时」这种值放不下就会折成两行,把整行撑高 */
.kpi .v { font-size: clamp(17px, 5.6vw, 23px); font-weight: 650; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.kpi .v small { font-size: 13px; font-weight: 500; color: var(--text-3); margin-left: 3px; }
.kpi .k { color: var(--text-3); font-size: 12.5px; margin-top: 1px; }
.kpi .v { font-size: clamp(var(--fs-md), 5.6vw, var(--fs-xl)); font-weight: 600; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.kpi .v small { font-size: var(--fs-sm); font-weight: 500; color: var(--text-3); margin-left: 3px; }
.kpi .k { color: var(--text-3); font-size: var(--fs-sm); margin-top: 1px; }
/* ---------- 图表(纯 CSS/SVG) ---------- */
.bars { display: flex; flex-direction: column; gap: 9px; }
.bar-row { display: grid; grid-template-columns: 128px 1fr 104px; align-items: center; gap: 10px; font-size: 13px; }
.bar-row { display: grid; grid-template-columns: 128px 1fr 104px; align-items: center; gap: 10px; font-size: var(--fs-sm); }
/* 整行可点(下钻为筛选):给一点悬停反馈,并让点击区域覆盖整行;
柱条本身只有 9px 高,靠「行」来接收点击才够手指点 */
a.bar-row { margin: 0 -6px; padding: 4px 6px; border-radius: 6px; color: inherit; text-decoration: none; }
a.bar-row { margin: 0 -6px; padding: 4px 6px; border-radius: var(--r-sm); color: inherit; text-decoration: none; }
a.bar-row:hover { background: var(--line-soft); text-decoration: none; }
a.col { display: block; text-decoration: none; }
a.col:hover { opacity: 1; filter: brightness(1.12); }
.bar-row .nm { color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bar-track { background: var(--line-soft); border-radius: 5px; height: 9px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 5px; background: var(--accent); min-width: 2px; }
.bar-row .vv { text-align: right; color: var(--text-2); font-variant-numeric: tabular-nums; font-size: 12.5px; white-space: nowrap; }
.bar-track { background: var(--line-soft); border-radius: var(--r-sm); height: 9px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: var(--r-sm); background: var(--accent); min-width: 2px; }
.bar-row .vv { text-align: right; color: var(--text-2); font-variant-numeric: tabular-nums; font-size: var(--fs-sm); white-space: nowrap; }
@media (max-width: 620px) {
.bar-row { grid-template-columns: 84px 1fr 92px; }
/* 图表行在手机上要能点中:整行 ~37px。再往上加(44px)会把「类型分布」这种
@@ -191,35 +324,54 @@ a.col:hover { opacity: 1; filter: brightness(1.12); }
a.bar-row { padding: 8px 6px; }
}
.cols { display: flex; align-items: flex-end; gap: 3px; height: 120px; }
.cols .col { flex: 1; background: var(--accent); border-radius: 3px 3px 0 0; min-height: 2px; opacity: .85; }
/* 基线:柱子底下压一道浅线,让「高度从哪儿量起」有个参照(原来柱子直接悬在空白里,
高低只能靠互相比)。
padding-top 是给峰值柱顶上那个数字留的位置:height 走 border-box,内容区仍是 120px。 */
.cols { position: relative; display: flex; align-items: flex-end; gap: 3px; height: 136px; padding-top: 16px; border-bottom: 1px solid var(--line); }
.cols .col { position: relative; flex: 1; background: var(--accent); border-radius: var(--r-xs) var(--r-xs) 0 0; min-height: 2px; opacity: .8; }
.cols .col:hover { opacity: 1; }
.cols-x { display: flex; gap: 3px; font-size: 10px; color: var(--text-3); margin-top: 5px; }
/* 峰值柱:不透明拉满,并在顶上标出次数。数值本来写在副标题里,但它不告诉你「哪一根最高」,
标在柱子上才是能一眼找到的锚点。 */
.cols .col.peak { opacity: 1; }
.cols .col .pv {
position: absolute; left: 50%; bottom: calc(100% + 2px); transform: translateX(-50%);
font-size: var(--fs-xs); line-height: 1.2; color: var(--text-2);
font-variant-numeric: tabular-nums; white-space: nowrap;
}
.cols-x { display: flex; gap: 3px; font-size: var(--fs-xs); color: var(--text-3); margin-top: 5px; }
.cols-x span { flex: 1; text-align: center; overflow: hidden; white-space: nowrap; }
/* 一天 24 根柱,光看柱子分不出「上午 / 下午」。这条标尺用同一套 24 列网格(不是 flex:6),
所以每段边界和柱子严格对齐;每段直接写上时段和小时区间,就不用再单独留一行坐标轴。 */
.cols-band { display: grid; grid-template-columns: repeat(24, minmax(0, 1fr)); gap: 3px; margin-top: var(--sp-2); }
.cols-band span {
grid-column: span 6; text-align: center; white-space: nowrap;
font-size: var(--fs-xs); color: var(--text-3);
background: var(--line-soft); border-radius: var(--r-xs); padding: 2px 0;
}
/* ---------- 逐月气温 ---------- */
.temp-chart { margin-top: 16px; }
.temp-head { font-size: 12.5px; color: var(--text-3); margin-bottom: 8px; }
.temp-head { font-size: var(--fs-sm); color: var(--text-3); margin-bottom: 8px; }
/* 横向可滚动:柱子固定宽,十几二十个月挤在窄屏上也不会把标签压成一团 */
.temp-strip { display: flex; gap: 4px; overflow-x: auto; padding-bottom: 4px; }
.temp-col { flex: 0 0 36px; display: flex; flex-direction: column; align-items: center; gap: 3px; cursor: default; }
.temp-col .tc-v { font-size: 10.5px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.temp-col .tc-v { font-size: var(--fs-xs); color: var(--text-2); font-variant-numeric: tabular-nums; }
.temp-col .tc-track { width: 100%; height: 84px; display: flex; align-items: flex-end; }
.temp-col .tc-bar { width: 100%; border-radius: 4px 4px 0 0; min-height: 3px; }
.temp-col .tc-m { font-size: 10.5px; color: var(--text-3); white-space: nowrap; }
.temp-col .tc-bar { width: 100%; border-radius: var(--r-xs) var(--r-xs) 0 0; min-height: 3px; }
.temp-col .tc-m { font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }
.temp-col:hover .tc-bar { filter: brightness(1.08); }
.temp-col:hover .tc-v { color: var(--text); }
/* ---------- 日历热力 ---------- */
.heat { display: flex; gap: 3px; overflow-x: auto; padding-bottom: 4px; }
.heat .wk { display: flex; flex-direction: column; gap: 3px; }
.heat .d { width: 11px; height: 11px; border-radius: 2.5px; background: var(--line-soft); }
.heat .d { width: 11px; height: 11px; border-radius: var(--r-xs); background: var(--line-soft); }
.heat .d.l1 { background: #cfe6e0; }
.heat .d.l2 { background: #9dcfc3; }
.heat .d.l3 { background: #5fae9c; }
.heat .d.l4 { background: var(--accent); }
.heat-legend { display: flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--text-3); margin-top: 8px; }
.heat-legend .d { width: 11px; height: 11px; border-radius: 2.5px; }
.heat-legend { display: flex; align-items: center; gap: 5px; font-size: var(--fs-xs); color: var(--text-3); margin-top: 8px; }
.heat-legend .d { width: 11px; height: 11px; border-radius: var(--r-xs); }
/* ---------- 列表 ---------- */
.list { display: flex; flex-direction: column; }
@@ -229,33 +381,33 @@ a.col:hover { opacity: 1; filter: brightness(1.12); }
}
.item:last-child { border-bottom: 0; }
.item .ic {
width: 34px; height: 34px; border-radius: 9px; display: flex; align-items: center; justify-content: center;
background: var(--line-soft); font-size: 17px;
width: 34px; height: 34px; border-radius: var(--r-md); display: flex; align-items: center; justify-content: center;
background: var(--line-soft); font-size: var(--glyph-md);
}
.item .t { font-weight: 500; }
.item .t a { color: var(--text); }
.item .m { color: var(--text-3); font-size: 12.5px; margin-top: 1px; }
.item .r { text-align: right; color: var(--text-2); font-size: 12.5px; white-space: nowrap; }
.item .m { color: var(--text-3); font-size: var(--fs-sm); margin-top: 1px; }
.item .r { text-align: right; color: var(--text-2); font-size: var(--fs-sm); white-space: nowrap; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 20px;
background: var(--line-soft); color: var(--text-2); font-size: 12.5px; border: 1px solid transparent;
display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 999px;
background: var(--line-soft); color: var(--text-2); font-size: var(--fs-sm); border: 1px solid transparent;
}
.chip.on { background: var(--accent-soft); color: var(--accent); border-color: #cfe6e0; font-weight: 500; }
.chip .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.tag { display: inline-block; padding: 1px 7px; border-radius: 5px; background: var(--accent-soft); color: var(--accent); font-size: 11.5px; }
.tag { display: inline-block; padding: 1px 7px; border-radius: var(--r-sm); background: var(--accent-soft); color: var(--accent); font-size: var(--fs-xs); }
/* ---------- 表单 ---------- */
label.f { display: block; margin-bottom: 13px; }
label.f > span { display: block; font-size: 12.5px; color: var(--text-2); margin-bottom: 5px; }
label.f > span { display: block; font-size: var(--fs-sm); color: var(--text-2); margin-bottom: 5px; }
/* 必填标记:字段名后的红色星号,以及表单顶部的统一说明 */
.req { color: #b42318; font-weight: 700; margin-left: 2px; }
.req-tip { font-size: 12.5px; color: var(--text-2); margin: 0 0 10px; }
.req { color: #b42318; font-weight: 600; margin-left: 2px; }
.req-tip { font-size: var(--fs-sm); color: var(--text-2); margin: 0 0 10px; }
.req-tip .req { margin-right: 3px; }
/* flatpickr 日期时间选择器:贴合站点风格 */
.flatpickr-calendar { border-radius: 10px; box-shadow: 0 8px 30px rgba(0,0,0,.14); font-family: inherit; }
.flatpickr-calendar { border-radius: var(--r-lg); box-shadow: var(--sh-2); font-family: inherit; }
.flatpickr-day.selected, .flatpickr-day.selected:hover { background: var(--accent); border-color: var(--accent); }
.flatpickr-day.today { border-color: var(--accent); }
.flatpickr-day.today:hover { background: var(--accent-soft); color: var(--text); }
@@ -268,30 +420,30 @@ label.f > span { display: block; font-size: 12.5px; color: var(--text-2); margin
没有圆角、没有内边距、13.33px 的字,跟旁边的输入框明显不是一套。 */
input:not([type=checkbox]):not([type=radio]):not([type=color]):not([type=range]):not([type=file]):not([type=submit]):not([type=button]):not([type=reset]):not([type=image]):not([type=hidden]),
select, textarea {
width: 100%; padding: 7px 10px; border: 1px solid var(--line); border-radius: 7px; font: inherit;
width: 100%; padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--r-sm); font: inherit;
font-size: var(--field-fs, inherit);
background: var(--surface); color: var(--text); outline: none;
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.check { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--text-2); margin-bottom: 8px; word-break: keep-all; overflow-wrap: break-word; }
.check { display: flex; align-items: center; gap: 7px; font-size: var(--fs-sm); color: var(--text-2); margin-bottom: 8px; word-break: keep-all; overflow-wrap: break-word; }
.check input { width: 15px; height: 15px; accent-color: var(--accent); }
.nobr { white-space: nowrap; }
.btn {
display: inline-flex; align-items: center; gap: 6px; padding: 7px 14px; border-radius: 7px;
border: 1px solid var(--line); background: var(--surface); color: var(--text); font: inherit; font-size: 13.5px; cursor: pointer;
display: inline-flex; align-items: center; gap: 6px; padding: 7px 14px; border-radius: var(--r-sm);
border: 1px solid var(--line); background: var(--surface); color: var(--text); font: inherit; font-size: var(--fs-sm); cursor: pointer;
}
.btn:hover { background: var(--line-soft); text-decoration: none; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn.primary:hover { filter: brightness(1.06); }
.btn.sm { padding: 4px 10px; font-size: 12.5px; }
.btn.sm { padding: 4px 10px; font-size: var(--fs-sm); }
.btn.danger { color: #b42318; border-color: #f0cecb; }
.btn.danger:hover { background: var(--err-bg); }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
/* ---------- 表格 ---------- */
table.tb { width: 100%; border-collapse: collapse; font-size: 13px; }
table.tb { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
table.tb th {
text-align: left; font-weight: 500; color: var(--text-3); font-size: 12px;
text-align: left; font-weight: 500; color: var(--text-3); font-size: var(--fs-sm);
padding: 7px 10px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
table.tb td { padding: 9px 10px; border-bottom: 1px solid var(--line-soft); }
@@ -323,7 +475,7 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
.map-panel {
position: absolute; top: 12px; left: 12px; z-index: var(--z-map-overlay); width: 286px; max-height: calc(100% - 24px);
overflow-y: auto; background: var(--glass-strong); backdrop-filter: blur(8px);
border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow);
border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-2);
}
/* 内边距挂在表单上,不挂面板:窄屏的折叠条与操作行要 sticky 到面板的**内边缘**,
面板自己带 padding 的话它们只能粘在内容区边界上,边上会留一条缝让滚动内容穿过去
@@ -334,7 +486,7 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
/* 「含家/工作」单独一行(不属于时间段预设)。窄屏预设行是单行横滑的,
它留在里面会被滑到屏幕外;单独一行上下各留 10px,读起来也像另一个开关。 */
.map-panel .hw-row { margin-bottom: 10px; }
.map-panel h3 { font-size: 13px; margin-bottom: 9px; color: var(--text-2); }
.map-panel h3 { font-size: var(--fs-sm); margin-bottom: 9px; color: var(--text-2); }
/* 筛选面板的折叠开关:桌面上面板只占 286px、不碍事,按钮直接不显示,只在窄屏出现 */
.panel-fold { display: none; }
/* 筛选面板里可折叠的两块(地点类型 / 更多筛选):这两块在窄屏各占两百多像素,
@@ -342,7 +494,7 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
收起时标题里显示已选/已生效的那几项,否则收起后就不知道当前选了什么。 */
.type-fold > summary, .adv-fold > summary {
list-style: none; display: flex; align-items: center; gap: 6px;
font-size: 13px; color: var(--text-2); cursor: pointer; padding: 2px 0 8px;
font-size: var(--fs-sm); color: var(--text-2); cursor: pointer; padding: 2px 0 8px;
}
.type-fold > summary::-webkit-details-marker, .adv-fold > summary::-webkit-details-marker { display: none; }
.type-fold > summary:hover, .adv-fold > summary:hover { color: var(--accent); }
@@ -350,17 +502,17 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
flex: 0 1 auto; min-width: 0; color: var(--text-3);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.type-fold > summary .tf-arrow, .adv-fold > summary .tf-arrow { flex: 0 0 auto; margin-left: auto; font-size: 11px; color: var(--text-3); }
.type-fold > summary .tf-arrow, .adv-fold > summary .tf-arrow { flex: 0 0 auto; margin-left: auto; font-size: var(--fs-xs); color: var(--text-3); }
.type-fold[open] > summary .tf-arrow, .adv-fold[open] > summary .tf-arrow { transform: rotate(90deg); }
/* 展开后标签行/控件自己就说明了当前值,标题里那份就不必再占一行 */
.type-fold[open] > summary .tf-sel, .adv-fold[open] > summary .tf-sel { display: none; }
/* 「更多筛选」里的 h3 是二级标题,别跟折叠标题撞成一样大 */
.adv-fold > h3 { font-size: 12.5px; color: var(--text-3); margin-bottom: 8px; }
.adv-fold > h3 { font-size: var(--fs-sm); color: var(--text-3); margin-bottom: 8px; }
.map-legend {
/* bottom 让开右下角那行底图版权(约 15px 高),否则两者会叠在一起 */
position: absolute; bottom: 26px; right: 12px; z-index: var(--z-map-overlay); background: var(--glass-strong);
border: 1px solid var(--line); border-radius: 8px; padding: 8px 11px; font-size: 12px;
max-height: 40%; overflow-y: auto; box-shadow: var(--shadow);
border: 1px solid var(--line); border-radius: var(--r-md); padding: 8px 11px; font-size: var(--fs-sm);
max-height: 40%; overflow-y: auto; box-shadow: var(--sh-2);
}
.map-legend .lg { display: flex; align-items: center; gap: 6px; margin-top: 4px; color: var(--text-2); }
.map-legend .lg i { width: 9px; height: 9px; border-radius: 50%; }
@@ -369,11 +521,11 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
.fog-opacity { background: var(--surface); padding: 6px 8px; line-height: 1.2; }
.fog-opacity .fog-label {
display: block; text-align: left; border: 0; background: none; padding: 0;
font: inherit; font-size: 11px; color: var(--text-2); cursor: default;
font: inherit; font-size: var(--fs-xs); color: var(--text-2); cursor: default;
}
.fog-opacity .fog-slider { display: block; width: 96px; margin: 4px 0 0; accent-color: var(--accent); }
@media (max-width: 620px) {
.fog-opacity .fog-label { cursor: pointer; font-size: 12px; }
.fog-opacity .fog-label { cursor: pointer; font-size: var(--fs-sm); }
.fog-opacity .fog-slider { display: none; }
.fog-opacity.open .fog-slider { display: block; }
}
@@ -382,8 +534,8 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
.to-top {
position: fixed; right: calc(16px + var(--safe-r)); bottom: calc(20px + var(--safe-b)); z-index: var(--z-map-status);
width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--line);
background: var(--panel); color: var(--text-2); font-size: 20px; line-height: 1;
cursor: pointer; box-shadow: var(--shadow);
background: var(--panel); color: var(--text-2); font-size: var(--glyph-lg); line-height: 1;
cursor: pointer; box-shadow: var(--sh-2);
}
.to-top[hidden] { display: none; }
.to-top:hover { color: var(--text); border-color: var(--text-3); }
@@ -430,23 +582,23 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
bottom: 12px; z-index: var(--z-map-overlay);
display: flex; align-items: center; gap: 10px;
max-width: min(560px, calc(100% - 24px));
padding: 7px 12px; border-radius: 10px;
background: var(--glass-strong); border: 1px solid var(--line); box-shadow: var(--shadow);
padding: 7px 12px; border-radius: var(--r-lg);
background: var(--glass-strong); border: 1px solid var(--line); box-shadow: var(--sh-2);
backdrop-filter: blur(8px);
}
.timeline-bar[hidden] { display: none; }
.timeline-bar input[type=range] { flex: 1 1 auto; min-width: 90px; accent-color: var(--accent); margin: 0; }
.timeline-bar .tl-label { flex: 0 0 auto; font-size: 12px; color: var(--text-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
.timeline-bar .tl-label { flex: 0 0 auto; font-size: var(--fs-sm); color: var(--text-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
.timeline-bar button {
flex: 0 0 auto; border: 1px solid var(--line); background: var(--surface); color: var(--text);
border-radius: 7px; cursor: pointer; font: inherit; font-size: 12px; line-height: 1;
border-radius: var(--r-sm); cursor: pointer; font: inherit; font-size: var(--fs-sm); line-height: 1;
}
.timeline-bar .tl-play { width: 30px; height: 30px; font-size: 13px; padding: 0; }
.timeline-bar .tl-play { width: 30px; height: 30px; font-size: var(--fs-sm); padding: 0; }
.timeline-bar .tl-reset { padding: 7px 9px; }
.timeline-bar button:hover { background: var(--line-soft); }
@media (max-width: 620px) {
.timeline-bar { bottom: 26px; gap: 8px; padding: 6px 9px; }
.timeline-bar .tl-label { font-size: 11.5px; }
.timeline-bar .tl-label { font-size: var(--fs-xs); }
}
/* 地图上的状态条(加载失败 + 重试)。放底部中间:左上角是筛选面板、右下是图例与版权。 */
@@ -455,7 +607,7 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
bottom: 62px;
z-index: 600; display: flex; align-items: center; gap: 10px;
max-width: calc(100% - 24px); background: rgba(30,40,50,.92); color: #fff;
border-radius: 8px; padding: 8px 12px; font-size: 13px; box-shadow: var(--shadow);
border-radius: var(--r-md); padding: 8px 12px; font-size: var(--fs-sm); box-shadow: var(--sh-2);
}
.map-status[hidden] { display: none; }
.map-status .btn { border-color: rgba(255,255,255,.35); background: rgba(255,255,255,.12); color: #fff; flex: 0 0 auto; }
@@ -476,12 +628,12 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
于是它只盖住左边一小条,右边的内容会从粘性条旁边穿过去。 */
display: flex; align-items: center; gap: 8px; width: 100%; min-height: 44px;
padding: 0 13px; border: 0; background: var(--glass-strong); color: var(--text-2);
font: inherit; font-size: 13.5px; font-weight: 500; text-align: left; cursor: pointer;
font: inherit; font-size: var(--fs-sm); font-weight: 500; text-align: left; cursor: pointer;
position: sticky; top: 0; z-index: 2;
}
.map-panel:not(.collapsed) .panel-fold { border-bottom: 1px solid var(--line); }
.panel-fold .pf-n { color: var(--text-3); font-size: 12px; font-weight: 400; }
.panel-fold .pf-c { margin-left: auto; padding-left: 4px; font-size: 12px; color: var(--text-3); }
.panel-fold .pf-n { color: var(--text-3); font-size: var(--fs-sm); font-weight: 400; }
.panel-fold .pf-c { margin-left: auto; padding-left: var(--sp-1); font-size: var(--glyph-xs); color: var(--text-3); }
.panel-fold:hover { color: var(--text); }
.panel-fold:hover .pf-c { color: var(--text); }
/* 「查询 / 重置」粘在面板底部:展开后表单 600+px、可视窗不到 400px,
@@ -500,21 +652,30 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
.map-panel .presets > .chip { flex: 0 0 auto; }
.map-panel .map-count { display: none; }
}
.map-preview { height: 320px; border-radius: 8px; overflow: hidden; border: 1px solid var(--line); }
/* Leaflet 把 .leaflet-container 的字号写成 0.75rem(见 vendor/leaflet/leaflet.css 的
「general typography」),于是它跟着**根字号**漂:根从 14px 降到 13px 后,
右下角的署名与比例尺缩到了 9.8px(掉到本站 11px 的下限之下),弹窗正文也跟着缩。
这里按自己的字阶钉死,分两档:容器 = 正文(弹窗正文是 1.08333em,跟着它走),
署名与比例尺 = 脚注。 */
.leaflet-container { font-size: var(--fs-sm); }
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale-line { font-size: var(--fs-xs); }
.map-preview { height: 320px; border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--line); }
/* ---------- 地点图片 ---------- */
/* 管理面板里的缩略图墙 */
.imgs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; }
.imgs-cell { position: relative; margin: 0; border-radius: 8px; overflow: hidden; border: 1px solid var(--line); background: var(--line-soft); aspect-ratio: 4 / 3; }
.imgs-cell { position: relative; margin: 0; border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--line); background: var(--line-soft); aspect-ratio: 4 / 3; }
.imgs-cell img { width: 100%; height: 100%; object-fit: cover; display: block; cursor: zoom-in; }
.imgs-del {
position: absolute; top: 5px; right: 5px; width: 22px; height: 22px; border-radius: 50%;
border: 0; background: rgba(0,0,0,.55); color: #fff; font-size: 15px; line-height: 1;
border: 0; background: rgba(0,0,0,.55); color: #fff; font-size: var(--glyph-sm); line-height: 1;
cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.imgs-del:hover { background: #b42318; }
.imgs-busy { opacity: .45; pointer-events: none; }
.imgs-hint { font-size: 12.5px; color: var(--text-3); margin-top: 8px; min-height: 18px; }
.imgs-hint { font-size: var(--fs-sm); color: var(--text-3); margin-top: 8px; min-height: 18px; }
.imgs-hint.ok { color: #17604f; }
.imgs-hint.err { color: #b42318; }
/* 存储没配好时把「选择图片」变成不可点的样子,而不是留个能点但必失败的按钮 */
@@ -530,26 +691,26 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
/* 横滑留给我们做切换,纵向留给浏览器;pinch-zoom 让双指放大仍可用系统手势 */
touch-action: pan-y pinch-zoom;
}
.imgs-stage img { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; border-radius: 8px; }
.imgs-stage img { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; border-radius: var(--r-md); }
/* 双击放大:图放大到两倍、容器可拖动看局部(用尺寸而不是 transform,放大后不糊) */
.imgs-stage.zoomed { touch-action: pan-x pan-y; align-items: flex-start; justify-content: flex-start; }
.imgs-stage.zoomed img { max-width: none; max-height: none; width: 200%; border-radius: 0; }
.imgs-close {
position: fixed; top: calc(12px + var(--safe-t)); right: calc(14px + var(--safe-r)); z-index: 2001;
width: 44px; height: 44px; border-radius: 50%;
border: 1px solid rgba(255,255,255,.3); background: rgba(0,0,0,.4); color: #fff; font-size: 22px;
border: 1px solid rgba(255,255,255,.3); background: rgba(0,0,0,.4); color: #fff; font-size: var(--glyph-lg);
line-height: 1; cursor: pointer;
}
.imgs-close:hover { background: rgba(255,255,255,.18); }
.imgs-count {
position: fixed; top: calc(20px + var(--safe-t)); left: calc(18px + var(--safe-l)); z-index: 2001;
color: rgba(255,255,255,.78); font-size: 13px;
color: rgba(255,255,255,.78); font-size: var(--fs-sm);
}
/* 上一张 / 下一张:做成整条竖带,拇指够得着,不用瞄小按钮 */
.imgs-nav {
position: fixed; top: 50%; transform: translateY(-50%); z-index: 2001;
width: 44px; height: 76px; border: 0; border-radius: 10px; cursor: pointer;
background: rgba(255,255,255,.12); color: #fff; font-size: 30px; line-height: 1;
width: 44px; height: 76px; border: 0; border-radius: var(--r-lg); cursor: pointer;
background: rgba(255,255,255,.12); color: #fff; font-size: var(--glyph-xl); line-height: 1;
}
.imgs-nav:hover { background: rgba(255,255,255,.24); }
.imgs-prev { left: calc(6px + var(--safe-l)); }
@@ -558,26 +719,26 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
/* ---------- 「停一下」提示页(内容对访客不开放之类) ---------- */
.notice-body {
min-height: 100vh; margin: 0; display: flex; align-items: center; justify-content: center;
padding: 24px 18px; background: var(--bg);
padding: var(--sp-5) 18px; background: var(--bg);
}
.notice-card {
max-width: 420px; text-align: center; background: var(--panel); border: 1px solid var(--line);
border-radius: var(--radius); padding: 30px 26px; box-shadow: var(--shadow);
border-radius: var(--r-lg); padding: 30px 26px; box-shadow: var(--sh-2);
}
.notice-card .notice-ico { font-size: 40px; line-height: 1; margin-bottom: 12px; }
.notice-card h1 { font-size: 18px; margin: 0 0 8px; }
.notice-card p { color: var(--text-2); font-size: 13.5px; margin: 0 0 18px; }
.notice-card .notice-tip { color: var(--text-3); font-size: 12px; margin-top: 12px; }
.notice-card .notice-ico { font-size: var(--glyph-2xl); line-height: 1; margin-bottom: var(--sp-3); }
.notice-card h1 { font-size: var(--fs-lg); margin: 0 0 var(--sp-2); }
.notice-card p { color: var(--text-2); font-size: var(--fs-sm); margin: 0 0 18px; }
.notice-card .notice-tip { color: var(--text-3); font-size: var(--fs-sm); margin-top: 12px; }
/* 后台设置页的「标签 / 值」信息行(地图底图、图片存储状态)。
这个 class 一直没写样式,之前是两段光秃秃的 inline 文本;长一点的 URL
(比如对象存储的 endpoint)会把整页撑宽 —— 所以要 minmax(0,1fr) + 允许断词。 */
.kv { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 4px 12px; font-size: 13px; }
.kv { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 4px 12px; font-size: var(--fs-sm); }
.kv .k { color: var(--text-3); }
.kv .v { color: var(--text); overflow-wrap: anywhere; }
@media (max-width: 620px) {
.kv { grid-template-columns: 1fr; gap: 2px; }
.kv .k { font-size: 12px; }
.kv .k { font-size: var(--fs-sm); }
}
/* URL 这类超长 token 不该把容器顶宽 */
.mono { overflow-wrap: anywhere; }
@@ -608,13 +769,19 @@ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-
--bg-2: #191d21;
/* 浅绿底上用白字只有 2.4:1,反过来用深字才够 */
--on-accent: #0f1b18;
/* 类型色只有 indigo 在深底上不够:#5856d6 对 #1c2024 只有 2.90:1,其余都在 3.9 以上。
掺 10% 白提亮到 3.55:1。别的色一概不动 —— 它们本来就是浅亮色,深底上只会更清楚,
改了反而与手机端 / 地图上的颜色对不上。 */
--cat-indigo: #6967da;
--ok-bg: #16302a; --ok-fg: #7fd3bb; --ok-line: #245046;
--err-bg: #33201e; --err-fg: #f0a49b; --err-line: #5a3330;
--info-bg: #1b2534; --info-fg: #9dbdea; --info-line: #2f4055;
--shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 1px 3px rgba(0, 0, 0, .35);
--sh-1: 0 1px 2px rgba(0, 0, 0, .45), 0 1px 3px rgba(0, 0, 0, .35);
--sh-2: 0 6px 18px rgba(0, 0, 0, .50);
--sh-3: 0 12px 34px rgba(0, 0, 0, .55);
}
/* 兜底:还有两处没走变量的白底 */
.flatpickr-calendar { background: var(--panel); border-color: var(--line); box-shadow: var(--shadow); }
.flatpickr-calendar { background: var(--panel); border-color: var(--line); box-shadow: var(--sh-2); }
.flatpickr-day { color: var(--text); }
.flatpickr-day.prevMonthDay, .flatpickr-day.nextMonthDay { color: var(--text-3); }
.flatpickr-day:hover { background: var(--line-soft); color: var(--text); }
@@ -667,40 +834,40 @@ input:focus-visible, select:focus-visible, textarea:focus-visible, summary:focus
/* ---------- 「已选条件」行 ---------- */
.active-filters { margin-bottom: 12px; align-items: center; }
.active-filters .af-label { font-size: 12.5px; color: var(--text-3); }
.active-filters .af-label { font-size: var(--fs-sm); color: var(--text-3); }
.active-filters .chip { user-select: none; }
.active-filters .af-x { margin-left: 4px; opacity: .6; font-size: 13px; }
.active-filters .af-x { margin-left: 4px; opacity: .6; font-size: var(--fs-sm); }
.active-filters .chip:hover .af-x { opacity: 1; }
.active-filters .af-clear { background: transparent; color: var(--text-3); border-color: var(--line); }
/* ---------- 其他 ---------- */
.alert { padding: 9px 13px; border-radius: 8px; font-size: 13px; margin-bottom: 14px; }
.alert { padding: 9px 13px; border-radius: var(--r-md); font-size: var(--fs-sm); margin-bottom: 14px; }
.alert.err { background: var(--err-bg); color: var(--err-fg); border: 1px solid var(--err-line); }
.alert.ok { background: var(--ok-bg); color: var(--ok-fg); border: 1px solid var(--ok-line); }
.alert.info { background: var(--info-bg); color: var(--info-fg); border: 1px solid var(--info-line); }
.muted { color: var(--text-3); }
.mono { font-family: var(--mono); font-size: 12px; }
.mono { font-family: var(--mono); font-size: var(--fs-sm); }
.empty { text-align: center; padding: 40px 16px; color: var(--text-3); }
.empty .big { font-size: 30px; margin-bottom: 8px; }
.pager { display: flex; gap: 6px; align-items: center; justify-content: center; margin-top: 18px; font-size: 13px; }
.pager a, .pager span { padding: 4px 10px; border-radius: 6px; border: 1px solid var(--line); color: var(--text-2); }
.empty .big { font-size: var(--glyph-xl); margin-bottom: var(--sp-2); }
.pager { display: flex; gap: 6px; align-items: center; justify-content: center; margin-top: 18px; font-size: var(--fs-sm); }
.pager a, .pager span { padding: 4px 10px; border-radius: var(--r-sm); border: 1px solid var(--line); color: var(--text-2); }
.pager a:hover { background: var(--line-soft); text-decoration: none; }
.pager .cur { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-3); font-size: 12.5px; text-align: center; }
footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-3); font-size: var(--fs-sm); text-align: center; }
.timeline-day { margin-bottom: 20px; }
.timeline-day .dh {
position: sticky; top: var(--nav-h); background: var(--bg); z-index: 5; padding: 6px 0 7px;
font-size: 13px; color: var(--text-2); font-weight: 500; display: flex; gap: 8px; align-items: baseline;
font-size: var(--fs-sm); color: var(--text-2); font-weight: 500; display: flex; gap: 8px; align-items: baseline;
}
.timeline-day .dh .muted { font-size: 12px; }
.timeline-day .dh .muted { font-size: var(--fs-sm); }
.tl-item { display: grid; grid-template-columns: 62px 20px 1fr; gap: 10px; align-items: start; padding: 7px 0; }
.tl-item .tm { color: var(--text-3); font-size: 12.5px; font-variant-numeric: tabular-nums; padding-top: 1px; }
.tl-item .tm { color: var(--text-3); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; padding-top: 1px; }
.tl-item .ln { display: flex; justify-content: center; position: relative; }
.tl-item .ln::before { content: ""; position: absolute; top: 12px; bottom: -12px; width: 1px; background: var(--line); }
.tl-item:last-child .ln::before { display: none; }
.tl-item .ln i { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); margin-top: 5px; position: relative; z-index: 1; }
.tl-item .bd { font-size: 13.5px; }
.tl-item .bd .meta { color: var(--text-3); font-size: 12.5px; }
.tl-item .bd { font-size: var(--fs-sm); }
.tl-item .bd .meta { color: var(--text-3); font-size: var(--fs-sm); }
/* 聚合气泡改用主题色,默认的黄蓝配色和站点风格不一致 */
.marker-cluster-small,
@@ -716,7 +883,7 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
/* 更多筛选类型 */
.more-types { margin: 2px 0 10px; }
.more-types summary { font-size: 12.5px; color: var(--text-3); cursor: pointer; padding: 3px 0; }
.more-types summary { font-size: var(--fs-sm); color: var(--text-3); cursor: pointer; padding: 3px 0; }
.more-types summary:hover { color: var(--accent); }
.more-types .chips { margin-top: 8px; }
@@ -726,14 +893,14 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
/* 推荐 / 踩雷 表态按钮 */
.vote-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.vote-label { color: var(--text-3); font-size: 12.5px; min-width: 62px; }
.vote-label { color: var(--text-3); font-size: var(--fs-sm); min-width: 62px; }
/* 「推荐 / 踩雷」必须并排:把它们包成不可换行的组,窄屏时只让后面那句提示换行 */
.vote-btns { display: inline-flex; gap: 8px; flex-wrap: nowrap; }
.vote-btn {
display: inline-flex; align-items: center; gap: 5px;
padding: 6px 14px; border-radius: 999px; white-space: nowrap; flex: 0 0 auto;
border: 1px solid var(--line); background: var(--surface); color: var(--text);
font-size: 13.5px; font-weight: 500; cursor: pointer; line-height: 1;
font-size: var(--fs-sm); font-weight: 500; cursor: pointer; line-height: 1;
transition: background .12s, border-color .12s, color .12s;
}
.vote-btn:hover { border-color: var(--accent); color: var(--accent); }
@@ -749,7 +916,7 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
.btn.danger { color: var(--err-fg); border-color: var(--err-line); }
}
.vote-btn:disabled { opacity: .55; cursor: default; }
.vote-tip { font-size: 12.5px; color: var(--text-3); }
.vote-tip { font-size: var(--fs-sm); color: var(--text-3); }
.vote-tip.up { color: #1f8f43; }
.vote-tip.down { color: #c62a20; }
/* 站长页面上访客票数只读展示:沿用「我的结论」同一套行结构,尺寸/行距完全一致,只弱化配色 */
@@ -767,22 +934,22 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
/* 地图气泡:地点信息 + 只读备注 + 就地表决 */
.mp { min-width: 240px; max-width: 300px; }
.mp-n { font-weight: 600; margin-bottom: 2px; }
.mp-m { color: var(--text-3); font-size: 12px; line-height: 1.6; }
.mp-m { color: var(--text-3); font-size: var(--fs-sm); line-height: 1.6; }
.mp-note {
margin-top: 6px; padding: 6px 8px; border-radius: 6px;
margin-top: 6px; padding: 6px 8px; border-radius: var(--r-sm);
background: var(--bg, #f5f6f8); color: var(--text);
font-size: 12.5px; line-height: 1.55; white-space: pre-line;
font-size: var(--fs-sm); line-height: 1.55; white-space: pre-line;
max-height: 150px; overflow-y: auto;
}
.mp-foot { margin-top: 7px; font-size: 12.5px; }
.mp-foot { margin-top: 7px; font-size: var(--fs-sm); }
/* nowrap:气泡本来就窄,「👎 踩雷」被挤到第二行会很难看 */
.mp-vote { display: flex; align-items: center; gap: 6px; margin-top: 8px; flex-wrap: nowrap; }
.mp-vl { flex: 0 0 auto; color: var(--text-3); font-size: 12px; white-space: nowrap; }
.mp-vl { flex: 0 0 auto; color: var(--text-3); font-size: var(--fs-sm); white-space: nowrap; }
.mp-vb {
display: inline-flex; align-items: center; gap: 4px;
padding: 4px 10px; border-radius: 999px;
border: 1px solid var(--line); background: var(--surface); color: var(--text);
font-size: 12.5px; font-weight: 500; line-height: 1; cursor: pointer;
font-size: var(--fs-sm); font-weight: 500; line-height: 1; cursor: pointer;
flex: 0 0 auto; white-space: nowrap;
transition: background .12s, border-color .12s, color .12s;
}
@@ -794,9 +961,9 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
/* 备注块:rond 回填的备注是按次一行的,必须保留换行 */
.note-block {
margin-top: 4px; padding: 7px 9px; border-radius: 8px;
margin-top: 4px; padding: 7px 9px; border-radius: var(--r-md);
background: var(--bg, #f5f6f8); color: var(--text-2);
font-size: 12.8px; line-height: 1.65; white-space: pre-line;
font-size: var(--fs-sm); line-height: 1.65; white-space: pre-line;
}
/* 报告导出为 PDF / 长图:隐藏导航、页脚与操作按钮,卡片避免跨页断裂 */
@@ -820,7 +987,7 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
.inline-edit[open] { display: block; }
/* 行内编辑表单的宽度:桌面靠 --efw 撑到够填(各表不同,见模板里的 inline 变量),
放不下宽表时下面会放开这个下限——不然 320px 的表单会把 294px 的卡片顶破。 */
.inline-edit form { background: var(--bg-2, #fafbfb); border: 1px solid var(--line); border-radius: 10px; padding: 12px; min-width: var(--efw, 280px); }
.inline-edit form { background: var(--bg-2, #fafbfb); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 12px; min-width: var(--efw, 280px); }
.inline-edit form .f { margin-bottom: 8px; }
/* 「按名称搜坐标」输入框 + 按钮:必须始终同一行(含移动端),底边对齐。
@@ -828,7 +995,7 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
* 且 .row 会换行。这里显式 nowrap + 归零外边距,并让输入框可收缩。 */
.search-row { display: flex; flex-wrap: nowrap; align-items: flex-end; gap: 8px; margin-bottom: 12px; }
.search-row > label.f { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
.search-row .btn { flex: 0 0 auto; font-size: 14px; }
.search-row .btn { flex: 0 0 auto; font-size: var(--fs-sm); }
.search-row > .act { display: flex; flex: 0 0 auto; gap: 8px; }
/* 按名称搜坐标的候选列表:整行可点,名称与地址分两行 */
@@ -850,31 +1017,31 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
.filter-row > .act { display: flex; flex: 0 0 auto; gap: 8px; }
.geo-item {
display: block; width: 100%; text-align: left; cursor: pointer;
padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px;
padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--r-md);
background: var(--panel); color: var(--text); font: inherit;
}
.geo-item:hover { background: var(--line-soft); }
.geo-name { display: block; font-size: 13.5px; }
.geo-addr { display: block; color: var(--text-3); font-size: 12px; margin-top: 2px; }
.geo-name { display: block; font-size: var(--fs-sm); }
.geo-addr { display: block; color: var(--text-3); font-size: var(--fs-sm); margin-top: 2px; }
/* ---------- 专题 ---------- */
/* 必须带上 #map:站点里 #map{height:100%} 是 ID 选择器,
单靠 .topic-map 这个类压不过它,容器会被算成 0 高(地图看不见)。 */
#map.topic-map { height: 520px; width: 100%; border-radius: 10px; overflow: hidden; }
#map.topic-map { height: 520px; width: 100%; border-radius: var(--r-lg); overflow: hidden; }
@media (max-width: 620px) { #map.topic-map { height: 360px; } }
.topic-card { display: block; text-decoration: none; color: inherit; transition: box-shadow .15s, transform .15s; }
.topic-card:hover { box-shadow: 0 6px 22px rgba(0,0,0,.08); transform: translateY(-1px); }
.topic-card:hover { box-shadow: var(--sh-2); transform: translateY(-1px); }
.topic-head { display: flex; align-items: center; gap: 8px; }
.topic-head h2 { margin: 0; }
/* 地图兜底提示:脚本没就绪时留在容器里,解释为什么没有地图 */
.map-fallback {
display: flex; align-items: center; justify-content: center; height: 100%;
padding: 20px; text-align: center; color: var(--text-3); font-size: 13px;
padding: 20px; text-align: center; color: var(--text-3); font-size: var(--fs-sm);
}
/* 专题:时间范围独立成行(精确到分钟,塞进 KPI 网格会被挤成多行) */
.topic-range { margin-top: 5px; color: var(--text-2); font-size: 13.5px; font-variant-numeric: tabular-nums; }
.topic-range { margin-top: 5px; color: var(--text-2); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.topic-card .topic-range { margin: 7px 0 0; }
.topic-desc { color: var(--text-2); white-space: pre-wrap; line-height: 1.65; }
.kpis.kpis-3 .kpi { flex: 1 1 30%; }
@@ -882,13 +1049,13 @@ footer.ft { max-width: 1120px; margin: 0 auto; padding: 20px; color: var(--text-
/* 三个数字都很短,窄屏保持三列比 2+1 更好看 */
.topic-card .kpis.kpis-3 .kpi { flex: 1 1 30%; }
.topic-card .kpi { padding: 11px 12px; }
.topic-range { font-size: 12.5px; }
.topic-range { font-size: var(--fs-sm); }
}
/* ---------- 表单里的「文字 + 内联按钮」组合 ---------- */
/* label 的文字行右侧放次要操作(活动改名/删除),不额外占高度,也就不会把整格撑高 */
label.f > span.span-actions { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.mini-actions { display: flex; gap: 8px; font-size: 12px; }
.mini-actions { display: flex; gap: 8px; font-size: var(--fs-sm); }
.mini-actions a { color: var(--text-3); }
.mini-actions a:hover { color: var(--accent); }
.mini-actions a.danger:hover { color: #b42318; }
@@ -900,7 +1067,7 @@ label.f > span.span-actions { display: flex; align-items: center; justify-conten
/* 到访列表里的收藏星标:点一下就切换,不用展开行内编辑 */
.star {
background: none; border: 0; padding: 0 3px; cursor: pointer; line-height: 1;
font-size: 15px; color: var(--text-3); vertical-align: -1px;
font-size: var(--glyph-sm); color: var(--text-3); vertical-align: -1px;
}
.star:hover { color: #e8b700; }
.star.on { color: #e8b700; }
@@ -912,11 +1079,11 @@ label.f > span.span-actions { display: flex; align-items: center; justify-conten
background: rgba(0, 0, 0, .45);
}
.point-modal-box {
width: 100%; max-width: 660px; background: var(--panel); border-radius: 12px;
padding: 14px; box-shadow: 0 12px 40px rgba(0, 0, 0, .25);
width: 100%; max-width: 660px; background: var(--panel); border-radius: var(--r-lg);
padding: 14px; box-shadow: var(--sh-3);
}
.point-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.point-modal-map { height: 360px; border-radius: 10px; overflow: hidden; }
.point-modal-map { height: 360px; border-radius: var(--r-lg); overflow: hidden; }
@media (max-width: 620px) {
#point-modal { padding: 16px 10px; }
.point-modal-map { height: 260px; }
@@ -931,7 +1098,7 @@ label.f > span.span-actions { display: flex; align-items: center; justify-conten
.photo-table { table-layout: fixed; width: 100%; min-width: 880px; }
.photo-table th, .photo-table td { padding: 7px 8px; vertical-align: top; }
.photo-table input, .photo-table select { width: 100%; min-width: 0; box-sizing: border-box; }
.photo-table input[type=datetime-local] { font-size: 12.5px; padding: 6px 6px; }
.photo-table input[type=datetime-local] { font-size: var(--fs-sm); padding: 6px 6px; }
.photo-table input[type=checkbox] { width: auto; }
.photo-table .chip { white-space: nowrap; }
@@ -954,14 +1121,14 @@ label.f > span.span-actions { display: flex; align-items: center; justify-conten
border-bottom: 1px solid var(--line-soft);
}
.photo-map {
height: 200px; width: 100%; border-radius: 10px; overflow: hidden;
height: 200px; width: 100%; border-radius: var(--r-lg); overflow: hidden;
border: 1px solid var(--line); transition: height .18s ease;
}
/* 粘住以后收矮:200px 的浮层会挡住从下面滚过去的半个屏幕,收到 132px 还能看清
高亮出来的那个点,占的高度也只剩一条。说明文字这时候就没必要留着了。 */
#map-card.stuck .photo-map { height: 132px; }
#map-card.stuck .card-sub { display: none; }
#map-card.stuck { box-shadow: 0 6px 14px rgba(16, 24, 40, .07); }
#map-card.stuck { box-shadow: var(--sh-1); }
@media (max-width: 700px) {
.photo-map { height: 170px; }
#map-card.stuck .photo-map { height: 110px; }
@@ -993,9 +1160,9 @@ label.f > span.span-actions { display: flex; align-items: center; justify-conten
table.tb select, .photo-table select { min-height: 0; }
/* file / color 这两种被基础规则排除在外(不套文本框外观),也就没吃到
--field-fs,窄屏仍是 13.3px ⇒ iOS 一聚焦就放大。单独提上来。 */
input[type=file], input[type=color] { font-size: 16px; }
input[type=file], input[type=color] { font-size: var(--fs-control); }
/* 11.5px 的标签在手机上偏小(/stats 一页 29 处、/ 15 处) */
.tag { font-size: 12px; }
.tag { font-size: var(--fs-sm); }
/* 导航左上角那个 logo 是回首页的入口,原来只有 25×29:给它撑到 44 高,
横向也补到 44(.brand .t 在窄屏被隐藏,加内边距就够了)。 */
.brand { min-width: 44px; min-height: 44px; justify-content: center; }
@@ -1007,10 +1174,10 @@ label.f > span.span-actions { display: flex; align-items: center; justify-conten
top 取 --nav-h:导航是 sticky 的,写 0 会被压在它下面。
这些规则整条在媒体查询里 ⇒ 桌面端 .collapsed 不生效,等于没影响。 */
.filters-card > .panel-fold {
margin: -18px -18px 14px; border-radius: var(--radius) var(--radius) 0 0; top: var(--nav-h);
margin: calc(var(--card-pad) * -1) calc(var(--card-pad) * -1) 14px; border-radius: var(--r-lg) var(--r-lg) 0 0; top: var(--nav-h);
}
.filters-card.collapsed { padding: 0; }
.filters-card.collapsed > .panel-fold { margin: 0; border-radius: var(--radius); }
.filters-card.collapsed > .panel-fold { margin: 0; border-radius: var(--r-lg); }
.filters-card.collapsed > :not(.panel-fold) { display: none; }
}
@@ -1038,21 +1205,21 @@ label.f > span.span-actions { display: flex; align-items: center; justify-conten
*/
dialog.prompt-box {
border: 1px solid var(--line);
border-radius: var(--radius);
border-radius: var(--r-lg);
padding: 0;
min-width: 0; width: min(420px, calc(100vw - 32px));
background: var(--surface);
color: var(--text);
box-shadow: 0 8px 28px rgba(16, 24, 40, .18);
box-shadow: var(--sh-2);
}
dialog.prompt-box::backdrop { background: rgba(16, 24, 40, .38); }
.prompt-box .pb-body { padding: 14px 16px 12px; }
.prompt-box .pb-title { font-size: 14.5px; font-weight: 500; margin-bottom: 8px; }
.prompt-box .pb-title { font-size: var(--fs-md); font-weight: 500; margin-bottom: 8px; }
/* 输入框字号必须 ≥16px:iOS Safari 聚焦时会自动放大页面,16px 以下会把布局顶歪 */
.prompt-box .pb-input {
width: 100%; font-size: 16px; font-family: inherit; color: var(--text);
width: 100%; font-size: var(--fs-control); font-family: inherit; color: var(--text);
background: var(--surface); border: 1px solid var(--line);
border-radius: 7px; padding: 7px 10px; min-height: 40px;
border-radius: var(--r-sm); padding: 7px 10px; min-height: 40px;
}
.prompt-box .pb-input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
.prompt-box .pb-acts { display: flex; gap: 8px; justify-content: flex-end; padding: 0 16px 14px; }
@@ -1067,12 +1234,12 @@ dialog.prompt-box::backdrop { background: rgba(16, 24, 40, .38); }
.prompt-fallback {
position: fixed; inset: 8px 8px auto 8px; z-index: 2000;
display: flex; gap: 8px; align-items: center;
padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius);
background: var(--surface); box-shadow: 0 8px 28px rgba(16, 24, 40, .18);
padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-lg);
background: var(--surface); box-shadow: var(--sh-2);
}
.prompt-fallback input {
flex: 1; min-width: 0; font-size: 16px; font-family: inherit;
border: 1px solid var(--line); border-radius: 7px; padding: 7px 10px; min-height: 40px;
flex: 1; min-width: 0; font-size: var(--fs-control); font-family: inherit;
border: 1px solid var(--line); border-radius: var(--r-sm); padding: 7px 10px; min-height: 40px;
}
@media (max-width: 800px) {
@@ -1092,7 +1259,7 @@ dialog.prompt-box::backdrop { background: rgba(16, 24, 40, .38); }
}
.tw > table.tb.tb-stack > tbody > tr > td::before {
content: attr(data-label); position: absolute; left: 0; top: 2px;
width: 5em; color: var(--text-3); font-size: 12px;
width: 5em; color: var(--text-3); font-size: var(--fs-sm);
}
/* 首格是这一行的主角(地点名 / 天气 / 方式…):不缩进、字号抬一档当小标题;
操作格也是这一类(表头为空、整行铺开)。它们里面的内容是长名称或编辑表单,
@@ -1100,7 +1267,7 @@ dialog.prompt-box::backdrop { background: rgba(16, 24, 40, .38); }
于是「已选:#403 2026-09-13 19:53 · 某某地」这一行在窄处不换行、把卡片顶出 44px。
堆叠态每格本来就是一整行,nowrap 没有意义,放开。 */
.tw > table.tb.tb-stack > tbody > tr > td:not([data-label]) {
padding-left: 0; margin-bottom: 2px; font-size: 14px; white-space: normal;
padding-left: 0; margin-bottom: 2px; font-size: var(--fs-sm); white-space: normal;
}
/* 操作格(有的带「操作」标签、有的表头本来就是空的)在堆叠态整行铺开,
桌面那条 nowrap 会让格里的编辑表单不换行、把卡片顶出几十像素 */