:root { --accent: #2f7d6e; --accent-soft: #e8f3f0; --bg: #f6f7f8; --panel: #ffffff; --line: #e5e7ea; --line-soft: #f0f1f3; --text: #1f2328; --text-2: #5b6169; /* 次要文字。原来是 #8b9199,对白底只有 3.18:1、对浅灰底 2.81:1, 低于 WCAG AA 的 4.5:1(页面副标题、KPI 标签、筛选计数全用这个色阶)。 改成 #666d75 之后:白底 5.24 / 浅灰底 4.63 / 绿浅底 4.62, 既达标又仍比 --text-2(5.53)浅,层次没丢。 */ --text-3: #666d75; /* 会随明暗切换的「面」色,抽出来暗色只需换这一组 */ --surface: #ffffff; /* 输入框 / 按钮 / 浮层的底 */ --glass: rgba(255, 255, 255, .92); /* 顶栏毛玻璃 */ --glass-strong: rgba(255, 255, 255, .97); /* 地图面板 / 图例 */ --row-hover: #fbfcfc; --bg-2: #fafbfb; /* 比卡片略深一层的面(行内编辑表单、空态框) */ --on-accent: #ffffff; /* 主色底上的文字 */ --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); --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; /* 顶栏高度:窄屏更矮。地图页的定位跟它对齐,避免出现几像素的缝。 */ --nav-h: 54px; /* 刘海屏 / 底部 Home 指示条的安全区。不支持 env() 的浏览器回落到 0。 */ --safe-t: env(safe-area-inset-top, 0px); --safe-r: env(safe-area-inset-right, 0px); --safe-b: env(safe-area-inset-bottom, 0px); --safe-l: env(safe-area-inset-left, 0px); } * { box-sizing: border-box; } html, body { margin: 0; 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; -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; } /* ---------- 顶栏 ---------- */ .nav { position: sticky; top: 0; z-index: 500; background: var(--glass); backdrop-filter: saturate(1.6) blur(10px); border-bottom: 1px solid var(--line); /* 横屏时刘海在左右,竖屏时状态栏在顶部 */ padding: var(--safe-t) var(--safe-r) 0 var(--safe-l); } .nav-inner { max-width: 1120px; margin: 0 auto; padding: 0 20px; 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:hover { text-decoration: none; } .brand .avatar { font-size: 18px; } .nav-links { display: flex; gap: 4px; flex: 1; } .nav-links a { padding: 5px 11px; border-radius: 7px; color: var(--text-2); font-size: 13.5px; 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); } /* ---------- 布局 ---------- */ .wrap { max-width: 1120px; margin: 0 auto; padding: 22px calc(20px + var(--safe-r)) calc(60px + var(--safe-b)) calc(20px + var(--safe-l)); } .wrap-wide { max-width: 100%; padding: 0; } .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; } .card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 18px; margin-bottom: 16px; } .card > h2 { font-size: 15px; margin-bottom: 2px; display: flex; align-items: center; gap: 8px; } /* .card-body 是数据编辑页折叠用的容器,卡片内容被它包住后不再是 .card 的直接子元素, 所以下面两条用「后代」而不是「子」选择器——否则折叠脚本一加上,间距就全没了。 */ .card .card-sub { color: var(--text-3); font-size: 12.5px; margin-bottom: 14px; } .card.collapsed .card-body { display: none; } /* 只有带 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; } .card[data-card] > h2 .card-toggle:hover { color: var(--text); } .grid { display: grid; gap: 16px; } /* grid 子项默认 min-width:auto(= 不小于自身 min-content),而卡片里常常放着 带 min-width 的表格——不加这条,卡片会拒绝收缩、把**整页**顶宽,出现横向滚动条 (/stats、/report 在手机上整页 692px 就是这么来的)。压到 0 之后由卡片内部的 .tw 自己横向滚动。 */ .grid > * { min-width: 0; } .g2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } .g3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } .g4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } @media (max-width: 900px) { .g3, .g4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-width: 620px) { :root { --nav-h: 50px; } .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 { padding: 5px 7px; font-size: 13px; } .nav-right .who { display: none; } .nav-right .btn.sm { padding: 4px 9px; font-size: 12.5px; } } /* ---------- 窄屏触控与表单(移动端适配) ---------- */ /* 手指的落点比鼠标粗得多:导航链接原本只有 40×31、筛选标签 59×28、小按钮 45×30, 都低于 44×44 的通行下限,最容易点的就是它们。这里只放宽尺寸与内边距,不动结构。 */ @media (max-width: 620px) { .nav-links a, .btn, .btn.sm, .chip { min-height: 44px; } .nav-links a { display: inline-flex; align-items: center; padding: 8px 9px; } /* 内边距与控件尺寸的窄屏覆盖在文件末尾(那几组选择器的基础规则在下面, 写在这里会被它们盖掉);这里只放基础规则里没有的属性。 */ /* iOS 聚焦字号小于 16px 的控件时会自动放大整页——用户点完输入框页面突然变大, 还要手动缩回去。窄屏统一提到 16px 规避(桌面端不受影响,保持紧凑)。 */ :root { --field-fs: 16px; } /* 表格里的控件仍然紧凑:后台那张表列很多,16px 会把列撑得很难用。 表格里横向可滚,iOS 缩放的影响也小。 */ .photo-table, table.tb { --field-fs: 14px; } } .only-sm { display: none; } @media (max-width: 620px) { .hide-sm { display: none; } .only-sm { display: inline; } /* 窄屏用地势与手势缩放,把控件让给底部 */ .map-panel { top: 8px; } } /* ---------- 统计数字 ---------- */ /* grid-auto-rows: 1fr 让所有格子等高:否则某一格的值换行时,那一整行都比别行高 */ /* 用 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); } @media (max-width: 900px) { .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } } .kpi { flex: 1 1 158px; min-width: 0; background: var(--panel); padding: 15px 16px; } /* 字号跟着视口收:窄屏上「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; } /* ---------- 图表(纯 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; } /* 整行可点(下钻为筛选):给一点悬停反馈,并让点击区域覆盖整行; 柱条本身只有 9px 高,靠「行」来接收点击才够手指点 */ a.bar-row { margin: 0 -6px; padding: 4px 6px; border-radius: 6px; 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; } @media (max-width: 620px) { .bar-row { grid-template-columns: 84px 1fr 92px; } /* 图表行在手机上要能点中:整行 ~37px。再往上加(44px)会把「类型分布」这种 60 多行的图撑到两倍高,翻起来更难受,这里取个折中。 */ 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; } .cols .col:hover { opacity: 1; } .cols-x { display: flex; gap: 3px; font-size: 10px; color: var(--text-3); margin-top: 5px; } .cols-x span { flex: 1; text-align: center; overflow: hidden; white-space: nowrap; } /* ---------- 逐月气温 ---------- */ .temp-chart { margin-top: 16px; } .temp-head { font-size: 12.5px; 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-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: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.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; } /* ---------- 列表 ---------- */ .list { display: flex; flex-direction: column; } .item { display: grid; grid-template-columns: 40px 1fr auto; gap: 12px; align-items: center; padding: 11px 2px; border-bottom: 1px solid var(--line-soft); } .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; } .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; } .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; } .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; } /* ---------- 表单 ---------- */ label.f { display: block; margin-bottom: 13px; } label.f > span { display: block; font-size: 12.5px; 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-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-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); } .flatpickr-day:hover { background: var(--accent-soft); } /* 控件字号走变量:窄屏要整体提到 16px(iOS 聚焦小于 16px 会自动放大整页), 而基础选择器 input[type=…], select, textarea 的特异性高于媒体查询里的单选择器, 直接在里面写 font-size 是压不过的——用可继承的自定义属性就没有这个问题。 */ /* 按「排除掉不该套文本框样式的类型」来写,而不是逐个枚举 text/number/date—— 枚举漏了 datetime-local(后台编辑页有 160 多个),那些框就一直是浏览器默认外观: 没有圆角、没有内边距、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; 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 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; } .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.danger { color: #b42318; border-color: #f0cecb; } .btn.danger:hover { background: var(--err-bg); } .row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } .spacer { flex: 1; } /* ---------- 表格 ---------- */ table.tb { width: 100%; border-collapse: collapse; font-size: 13px; } table.tb th { text-align: left; font-weight: 500; color: var(--text-3); font-size: 12px; 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); } /* 操作列(桌面要横着一排按钮,不折行)。原来是写在模板上的 inline style,而 white-space 会 继承进行内编辑表单,堆叠态下「已选:…重选」那一行就顶破了卡片——inline 又压不过后面的覆盖, 所以挪成类,放不下宽表时在文件末尾统一放开。 */ table.tb td.ops { white-space: nowrap; } table.tb tr:hover td { background: var(--row-hover); } table.tb td.num, table.tb th.num { text-align: right; font-variant-numeric: tabular-nums; } .tw { overflow-x: auto; } /* 表格要给个内容下限:table 是 width:100%,而 CJK 允许逐字断行,没有下限时窄屏会把 每列压到一两个字宽(「一般公共场所」会排成 6 行、一行一个字)。有了下限,放不下就 交给上面的 .tw 横向滚动,列宽不再被榨干。 */ .tw > table.tb { min-width: 640px; } /* 地点库这张有 8 列(多一列「图片」),640px 下「河池市」这种 3 字城市会被挤成两行 (实测 640 给到 54px、700 给到 59px,正好回到一行)。只给这张加下限,别的表不动 —— 列少的表跟着抬到 700 会平白多出一截横向滚动。 */ .tw > table.tb.places-table { min-width: 700px; } /* 从地点详情页跳进来定位到的那一行(?open=):淡色底,长列表里一眼能认出目标。 用底色而不是动画——动画会被 prefers-reduced-motion 跳过,那时就完全看不出来了。 */ table.tb tr.located td, table.tb tr.located:hover td { background: var(--accent-soft); } /* ---------- 地图 ---------- */ #map { height: 100%; width: 100%; background: #eef1f2; } .map-page { position: fixed; inset: calc(var(--nav-h) + var(--safe-t)) var(--safe-r) var(--safe-b) var(--safe-l); } .map-panel { position: absolute; top: 12px; left: 12px; z-index: 400; 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); } /* 内边距挂在表单上,不挂面板:窄屏的折叠条与操作行要 sticky 到面板的**内边缘**, 面板自己带 padding 的话它们只能粘在内容区边界上,边上会留一条缝让滚动内容穿过去 (实测:折叠条粘在内容区顶、上方 13px 仍能看到内容滚过)。 */ #filter-form { padding: 13px; } /* 时间范围预设行:原来靠行内 style 加的下边距,挪到类上,好让窄屏媒体查询能一起改。 */ .map-panel .presets { margin-bottom: 10px; } /* 「含家/工作」单独一行(不属于时间段预设)。窄屏预设行是单行横滑的, 它留在里面会被滑到屏幕外;单独一行上下各留 10px,读起来也像另一个开关。 */ .map-panel .hw-row { margin-bottom: 10px; } .map-panel h3 { font-size: 13px; margin-bottom: 9px; color: var(--text-2); } /* 筛选面板的折叠开关:桌面上面板只占 286px、不碍事,按钮直接不显示,只在窄屏出现 */ .panel-fold { display: none; } /* 筛选面板里可折叠的两块(地点类型 / 更多筛选):这两块在窄屏各占两百多像素, 是面板高度的大头。默认值在 map.html 的脚本里按视口定(桌面展开、窄屏收起); 收起时标题里显示已选/已生效的那几项,否则收起后就不知道当前选了什么。 */ .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; } .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); } .type-fold > summary .tf-sel, .adv-fold > summary .tf-sel { 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[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; } .map-legend { /* bottom 让开右下角那行底图版权(约 15px 高),否则两者会叠在一起 */ position: absolute; bottom: 26px; right: 12px; z-index: 400; 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); } .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%; } .maplink { display: block; padding: 8px 0; border-bottom: 1px solid var(--line-soft); } .maplink b { font-weight: 500; } .maplink span { color: var(--text-3); font-size: 12px; display: block; } .dot-marker { border-radius: 50%; border: 1.5px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.16); } /* 迷雾浓度的样式从内联挪出来:窄屏要把它收成一个可展开的小标签—— 不然「图层 / 迷雾浓度 / 缩放」三个控件竖着堆在右下角,占掉近 180px。 */ .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; } .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-slider { display: none; } .fog-opacity.open .fog-slider { display: block; } } /* 长列表划到底后的「回到顶部」。地图页不滚动,所以它不会出现。 */ .to-top { position: fixed; right: calc(16px + var(--safe-r)); bottom: calc(20px + var(--safe-b)); z-index: 900; 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); } .to-top[hidden] { display: none; } .to-top:hover { color: var(--text); border-color: var(--text-3); } /* 时间轴回放时新出现的点:冒出来 + 淡入(配合 JS 里的逐个延时,像"扫过来")。 点在 Leaflet 里是 overlay pane 里的 ,用 transform-box: fill-box 让缩放以自身 中心为原点,才不会跑到左上角去。 */ @media (prefers-reduced-motion: no-preference) { @keyframes pt-in { from { opacity: .1; transform: scale(.2); } 55% { opacity: 1; } to { opacity: .9; transform: scale(1); } } .leaflet-overlay-pane path.pt-new { animation: pt-in .55s cubic-bezier(.22, .9, .28, 1) both; transform-box: fill-box; transform-origin: center; } /* 聚合圆圈是 DOM 元素、而且 Leaflet 用 transform 给它定位, 所以只能动透明度 —— 再加 scale 会把它从原位挪走。 */ @keyframes pt-in-dom { from { opacity: .1; } 60% { opacity: 1; } to { opacity: .92; } } .leaflet-marker-pane .marker-cluster.pt-new { animation: pt-in-dom .5s ease-out both; } /* 数字变了轻弹一下:弹内层 div(只放数字),外层带 Leaflet 的定位 transform 不能动 */ @keyframes pt-pulse { 0% { transform: scale(1); } 45% { transform: scale(1.3); } 100% { transform: scale(1); } } .leaflet-marker-pane .marker-cluster > div.pt-pulse { animation: pt-pulse .32s ease-out; } } /* 时间轴回放:地图底部中间的一条。窄屏上抬高一档,给右下角的底图版权让位。 和左侧的日期区间不是一回事——区间是「只看这段时间」,这里是「走到这一刻为止」。 */ .timeline-bar { position: absolute; left: 50%; transform: translateX(-50%); bottom: 12px; z-index: 650; 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); 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 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; } .timeline-bar .tl-play { width: 30px; height: 30px; font-size: 13px; 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; } } /* 地图上的状态条(加载失败 + 重试)。放底部中间:左上角是筛选面板、右下是图例与版权。 */ .map-status { position: absolute; left: 50%; transform: translateX(-50%); 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); } .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; } .map-status .btn:hover { background: rgba(255,255,255,.22); } @media (max-width: 620px) { /* 上限 50%(原 44%):折叠条本身占 44px,不加这 6% 的话展开后能看到的筛选内容 反而比改动前更少。50% 刚好把那一条的高度补回来。 */ .map-panel { left: 8px; right: 8px; width: auto; max-height: 50%; top: 8px; } .map-legend { display: none; } /* 窄屏上面板铺满整行、最多能吃掉 50% 的屏幕,所以默认收起成一条: 只剩「筛选 + 数量 + 箭头」约 52px 高,想改条件再点开。 「筛选」两字与箭头之间的数量在展开时也保留——表单里那行重复的计数已去掉, 两块状态共用这一个数字,展开/收起都能看到当前筛出多少个点。 展开时它 sticky 在面板顶部:表单比可视窗高,滚到下面之后还得能点回来收起。 不透明底是因为滚动内容会从它下面穿过(面板本身已无内边距,它天然铺满整行)。 */ .panel-fold { /* width:100% 不能省:这是