Files
TripLog/assets/style.css
T
laoyang 48237c8947 节点详情:照片灯箱浏览、参与人点击编辑、花销只记总计
1) 照片与附件不再新开窗口看
   之前点缩略图直接 target=_blank 打开原始文件,看完还得切回来。
   现在统一走当前页灯箱(templates/_lightbox.php,详情页照片墙与
   照片管理页共用):左右箭头 / 键盘 ←→ / 手机滑动切上一张下一张,
   Esc 或点背景关闭,带「第几张 / 共几张」计数并预加载相邻两张。
   非图片附件(PDF 等)仍保留直接打开与下载。

2) 参与人可以直接改了
   之前加完只能移出重加。现在每个参与人旁有「编辑」,点开预填弹窗
   (角色标签 / 心情 / 签到时间),保存仍走 members/store ——
   Members::save() 本来就是 upsert(同节点同人一条),服务端零改动。
   失败回填按 user_id 精确匹配,只回填刚才没保存成的那个人。

3) 花销去掉预算,只记真实花费
   明细上移,底部一行「总计(N 笔)¥X」。表单不再有「预算」选项,
   查询一律 is_budget=0(历史预算行不再出现在列表与合计里);
   活动页统计同步改成「共 N 笔 · 总计」。列保留在库里不删数据。

已验证:本地全链 11 步全绿(php -l / 渲染 286 项 / SQL / PDO / CSS /
Mime / amap / 内联 JS×11 / 布局几何 / 表单交互 73 项);远端 e2e
296 项 0 失败(含灯箱、编辑弹窗、历史预算行排除的专项断言);
部署后双 PHP 渲染逐字节一致、60 文件哈希一致,线上 style.css 含新规则。
2026-09-23 14:59:02 +08:00

1162 lines
53 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.
/* ============================================================================
家庭出行记录 · 样式表
----------------------------------------------------------------------------
组织方式:设计令牌 → 基础 → 布局 → 组件 → 页面 → 响应式
约定:
· 颜色/间距/圆角一律走 CSS 变量,改主题只动 :root
· 不写 !important;靠顺序与具体度控制
· 组件类名与模板一一对应,改名前先搜模板
========================================================================== */
/* ── 1. 设计令牌 ─────────────────────────────────────────────────────────── */
:root {
--bg: #f5f6f8;
--bg-soft: #eceef2;
--surface: #ffffff;
--surface-2: #fafbfc;
--border: #e3e6ec;
--border-strong: #cfd5df;
--text: #1c2029;
--text-soft: #5b6371;
--text-mute: #8b93a1;
--accent: #3d6bf5;
--accent-dark: #2f56cf;
--accent-soft: #eaf0ff;
--accent-ring: rgba(61, 107, 245, .18);
--success: #12855a;
--success-soft: #e4f6ee;
--warn: #b7791f;
--warn-soft: #fdf3e0;
--danger: #d63b3f;
--danger-soft: #fdecec;
--radius-sm: 8px;
--radius: 12px;
--radius-lg: 16px;
--shadow-xs: 0 1px 2px rgba(22, 28, 45, .06);
--shadow-sm: 0 2px 8px rgba(22, 28, 45, .07);
--shadow-md: 0 8px 24px rgba(22, 28, 45, .10);
--shadow-lg: 0 18px 48px rgba(22, 28, 45, .16);
/* ── 版心 ─────────────────────────────────────────────────────────────
顶栏与正文必须共用同一套「左右留白」,否则宽屏下两者左边缘会错开。 */
--container: 1080px; /* 正文列的目标宽度 */
--gutter: clamp(14px, 3.2vw, 24px); /* 窄屏时内容与视口边缘的最小留白 */
--topbar-h: 60px;
/* 版心左右留白:视口比 --container 宽时,把它推到「让正文列正好等于
--container 且居中」的位置;视口不够宽时退化为 --gutter。
⚠️ 顶栏与 .container 只共用这一个值,两边都不许再叠加
`max-width + margin auto` —— 那等于把内容推两次。曾经就是这么错的:
1920px 下正文列只剩 240px,2560px 下直接塌成 0,整页错位。
⚠️ 这里用 100% 而不是 100vw:100vw 含滚动条宽度,会让顶栏比正文
偏出半个滚动条。100% 相对各自包含块,两边算出来才严格相等。 */
--shell-pad: max(var(--gutter), calc((100% - var(--container)) / 2));
}
* { box-sizing: border-box; }
/* 图片一律不得宽过所在容器 —— 这是兜底,不是主要手段。
主因见下面 .avatar-preview / .cover-preview 的注释:尺寸要写在 img 上。
(这里只约束宽度,高度仍由各自的规则决定;单靠这条救不了「400×400 的方图
塞进 84px 圆框」那种高度溢出。) */
img { max-width: 100%; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "Segoe UI", Roboto, sans-serif;
font-size: 15px;
line-height: 1.62;
color: var(--text);
background: var(--bg);
-webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-dark); text-decoration: underline; }
h1, h2, h3, h4 { margin: 0 0 .4em; line-height: 1.3; font-weight: 650; letter-spacing: -.01em; }
h1 { font-size: 22px; }
h2 { font-size: 17px; }
h3 { font-size: 15px; }
p { margin: 0 0 .8em; }
hr { border: 0; border-top: 1px solid var(--border); margin: 20px 0; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* 工具类 */
.muted { color: var(--text-mute); font-size: 13px; }
.danger { color: var(--danger); }
.meta { color: var(--text-mute); font-size: 12.5px; }
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.section-title {
font-size: 15px; font-weight: 650; margin: 28px 0 12px;
display: flex; align-items: center; gap: 8px;
}
.section-title::before {
content: ''; width: 3px; height: 15px; border-radius: 2px; background: var(--accent);
}
/* ── 2. 顶栏 ─────────────────────────────────────────────────────────────── */
.topbar {
position: sticky; top: 0; z-index: 40;
display: flex; align-items: center; justify-content: space-between;
gap: 12px;
min-height: var(--topbar-h);
padding: 8px var(--shell-pad); /* 与 .container 同一个版心留白 */
background: rgba(255, 255, 255, .88);
backdrop-filter: saturate(180%) blur(12px);
-webkit-backdrop-filter: saturate(180%) blur(12px);
border-bottom: 1px solid var(--border);
}
.brand {
display: inline-flex; align-items: center; gap: 8px;
min-width: 0; /* 允许品牌文字省略,别把顶栏撑破 */
font-weight: 680; font-size: 16px; color: var(--text); letter-spacing: -.01em;
}
.brand:hover { text-decoration: none; color: var(--text); }
.brand-mark { font-size: 18px; line-height: 1; }
.brand-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 右侧一整组(新建 + 菜单)整体不许被压缩:窄屏宁可省略名字,也别把菜单挤没 */
.topbar-right { display: flex; align-items: center; gap: 10px; flex: none; min-width: 0; }
/* ── 3. 右上角下拉菜单 ───────────────────────────────────────────────────── */
.menu { position: relative; flex: none; }
.menu-trigger {
list-style: none;
display: inline-flex; align-items: center; gap: 8px;
padding: 5px 10px 5px 5px;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--surface);
cursor: pointer;
user-select: none;
transition: border-color .15s, box-shadow .15s, background .15s;
}
.menu-trigger::-webkit-details-marker { display: none; }
.menu-trigger:hover { border-color: var(--border-strong); box-shadow: var(--shadow-xs); }
.menu[open] > .menu-trigger {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-ring);
background: var(--surface);
}
.menu-name {
font-size: 14px; font-weight: 550;
max-width: 7em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.menu-caret { font-size: 10px; color: var(--text-mute); transition: transform .18s; }
.menu[open] .menu-caret { transform: rotate(180deg); }
.menu-panel {
position: absolute; right: 0; top: calc(100% + 8px);
/* 宽:按最长菜单项撑开(不换行),但窄屏下不许宽过视口 */
width: max-content;
min-width: 224px;
max-width: min(320px, calc(100vw - var(--gutter) * 2));
/* 高:13 个菜单项接起来有 500 多 px,笔记本(768 高)会顶出屏幕底部。
给上限 + 滚动,菜单项再多也不会「乱七八糟」。 */
max-height: calc(100vh - var(--topbar-h) - 24px);
overflow-y: auto;
overscroll-behavior: contain;
padding: 6px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow-md);
animation: menu-in .14s ease-out;
z-index: 50;
}
@keyframes menu-in {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
}
.menu-head { padding: 8px 10px 10px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.menu-head-name { font-weight: 620; font-size: 14px; }
.menu-head-role { font-size: 12px; color: var(--text-mute); }
.menu-item {
display: flex; align-items: center; gap: 10px;
padding: 8px 10px;
border-radius: var(--radius-sm);
color: var(--text); font-size: 14px; text-decoration: none;
white-space: nowrap; /* 菜单项不折行,靠面板宽度自适应 */
transition: background .12s;
}
.menu-item:hover { background: var(--bg-soft); text-decoration: none; color: var(--text); }
.menu-icon { width: 18px; text-align: center; font-size: 15px; opacity: .9; }
.menu-sep {
font-size: 11px; color: var(--text-mute);
padding: 10px 10px 4px; letter-spacing: .06em;
}
.menu-item-danger { color: var(--danger); }
.menu-item-danger:hover { background: var(--danger-soft); color: var(--danger); }
/* ── 4. 容器与页头 ───────────────────────────────────────────────────────── */
.container {
/* 这里**故意不写 max-width**:正文列宽度由 --shell-pad 反推 ——
视口够宽时 padding 会把它挤到正好 var(--container) 宽且居中;
这与 .topbar 用的是同一个公式,所以两者左边缘必然对齐。
曾经这里把 max-width 也设成了 --container,再配 `margin: 0 auto` 和同一份
padding:等于把内容推两次。1920px 下正文列只剩 240px、2560px 下塌成 0,
是本次「整页错位」的直接原因。改这里前请先想清楚这一点。 */
margin: 0 auto;
padding: 24px var(--shell-pad) 64px;
}
.container-guest {
max-width: 420px;
display: flex; align-items: center; justify-content: center;
min-height: 100vh; padding: 24px 16px;
}
.page-header {
display: flex; align-items: flex-start; justify-content: space-between;
gap: 16px; flex-wrap: wrap;
margin-bottom: 20px;
}
.page-header h1 { margin-bottom: 3px; }
.page-header .muted { margin: 0; }
.page-header > div:first-child { min-width: 0; }
.footer {
text-align: center; color: var(--text-mute); font-size: 12.5px;
padding: 20px 16px 36px;
}
/* ── 5. 按钮 ─────────────────────────────────────────────────────────────── */
.btn, .btn-primary, .btn-danger, .btn-sm, .btn-primary-sm {
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
padding: 8px 15px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--surface);
color: var(--text);
font-size: 14px; font-family: inherit; font-weight: 550; line-height: 1.4;
cursor: pointer; text-decoration: none; white-space: nowrap;
transition: background .15s, border-color .15s, box-shadow .15s, transform .06s;
}
.btn:hover, .btn-sm:hover {
background: var(--surface-2); border-color: var(--border-strong);
text-decoration: none; color: var(--text);
}
.btn:active, .btn-primary:active, .btn-danger:active { transform: translateY(1px); }
.btn-primary, .btn-primary-sm {
background: var(--accent); border-color: var(--accent); color: #fff;
box-shadow: var(--shadow-xs);
}
.btn-primary:hover, .btn-primary-sm:hover {
background: var(--accent-dark); border-color: var(--accent-dark);
color: #fff; text-decoration: none;
}
.btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-danger:hover { background: #bd3135; border-color: #bd3135; color: #fff; text-decoration: none; }
.btn-sm, .btn-primary-sm { padding: 5px 11px; font-size: 13px; }
.topbar-new { flex: none; }
/* ── 6. 提示 / 空态 / 标签 ───────────────────────────────────────────────── */
.alert {
display: flex; align-items: flex-start; gap: 8px;
padding: 11px 14px; margin-bottom: 18px;
background: var(--accent-soft);
border: 1px solid #cddbff;
border-left: 3px solid var(--accent);
border-radius: var(--radius-sm);
color: #1f3d8f; font-size: 14px;
}
.empty {
text-align: center; color: var(--text-mute);
padding: 40px 20px; font-size: 14px;
background: var(--surface);
border: 1px dashed var(--border-strong);
border-radius: var(--radius);
}
.empty p { margin: 0; }
.tag-default, .tag-self, .tag-temp, .status-tag, .mode-tag, .veh-tag, .role-tag, .node-type {
display: inline-flex; align-items: center; gap: 4px;
padding: 2px 8px; border-radius: 999px;
font-size: 12px; font-weight: 550; line-height: 1.7; white-space: nowrap;
}
.tag-default { background: var(--bg-soft); color: var(--text-soft); }
.tag-self { background: var(--success-soft); color: var(--success); }
.tag-temp { background: var(--warn-soft); color: var(--warn); }
.node-type { background: var(--accent-soft); color: var(--accent-dark); }
.status-planned { background: var(--bg-soft); color: var(--text-soft); }
.status-ongoing { background: var(--accent-soft); color: var(--accent-dark); }
.status-completed { background: var(--success-soft); color: var(--success); }
.status-cancelled { background: var(--danger-soft); color: var(--danger); }
/* 账号状态(用户列表用文字 + 颜色,不是胶囊标签) */
.status-active { color: var(--success); font-size: 13px; font-weight: 550; }
.status-inactive { color: var(--text-mute); font-size: 13px; }
.mode-tag { background: var(--bg-soft); color: var(--text-soft); }
.mode-drive_own, .mode-drive_rental { background: #e9f2ff; color: #1e5bb8; }
.mode-train { background: #eaf1ff; color: #2f56cf; }
.mode-plane { background: #e8f6fb; color: #17708f; }
.mode-ship { background: #e6f7f4; color: #106b60; }
.mode-walk, .mode-bike { background: var(--success-soft); color: var(--success); }
.mode-subway, .mode-bus, .mode-taxi { background: var(--warn-soft); color: var(--warn); }
.veh-own { background: var(--bg-soft); color: var(--text-soft); }
.veh-rental { background: var(--warn-soft); color: var(--warn); }
.role-admin { background: #f0e9ff; color: #6b3fc4; }
.role-user { background: var(--bg-soft); color: var(--text-soft); }
.money-over { color: var(--danger); }
.money-under { color: var(--success); }
/* ── 7. 卡片 ─────────────────────────────────────────────────────────────── */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
gap: 16px;
}
.card {
display: flex; flex-direction: column;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
transition: transform .16s, box-shadow .16s, border-color .16s;
}
.card:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); border-color: var(--border-strong); }
.card-cover { position: relative; aspect-ratio: 16 / 9; background: var(--bg-soft); overflow: hidden; }
.card-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-cover-placeholder, .cover-placeholder {
display: flex; align-items: center; justify-content: center;
height: 100%; min-height: 96px;
background: linear-gradient(135deg, #eef2fb, #e6ebf7);
color: #9aa6bd; font-size: 30px;
}
.card-body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.card-body h3 { margin: 0; font-size: 15.5px; }
.card-body h3 a { color: var(--text); }
.card-body h3 a:hover { color: var(--accent); text-decoration: none; }
.activity-cover { position: relative; border-radius: var(--radius-lg); overflow: hidden; margin-bottom: 20px; }
.activity-cover img { width: 100%; max-height: 320px; object-fit: cover; display: block; }
.activity-cover .cover-placeholder { min-height: 150px; }
/* ── 8. 时间轴与节点卡片 ─────────────────────────────────────────────────── */
.timeline-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
.timeline-col > h2 { margin-bottom: 12px; }
.node-card {
background: var(--surface);
border: 1px solid var(--border);
border-left: 3px solid var(--border-strong);
border-radius: var(--radius);
padding: 13px 15px;
margin-bottom: 12px;
transition: box-shadow .15s, border-color .15s;
}
.node-card:hover { box-shadow: var(--shadow-sm); }
.node-plan { border-left-color: #9db4f0; }
.node-reality { border-left-color: var(--success); }
.node-cancelled { opacity: .62; }
.node-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.node-title { font-weight: 620; font-size: 15px; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.node-icon { font-size: 15px; }
.node-time { color: var(--text-mute); font-size: 12.5px; margin-top: 2px; }
.node-loc {
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
font-size: 13px; color: var(--text-soft); margin-top: 6px;
}
.loc-start, .loc-end { display: inline-flex; align-items: center; gap: 4px; }
.loc-arrow { color: var(--text-mute); }
.node-transport { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 7px; }
.node-cancel-reason { font-size: 12.5px; color: var(--danger); margin-top: 6px; }
.node-actions {
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
margin-top: 10px; padding-top: 9px; border-top: 1px dashed var(--border);
}
.node-action-link, .node-action-strong {
font-size: 12.5px; color: var(--accent); padding: 3px 7px; border-radius: 6px;
}
.node-action-link:hover, .node-action-strong:hover { background: var(--accent-soft); text-decoration: none; }
.node-action-strong { font-weight: 600; }
.node-action-note { font-size: 12.5px; color: var(--text-mute); }
.node-action-btn, .node-action-form button {
font-size: 12.5px; padding: 3px 9px; border-radius: 6px;
border: 1px solid var(--border); background: var(--surface); color: var(--text-soft);
cursor: pointer; font-family: inherit;
}
.node-action-btn:hover, .node-action-form button:hover { border-color: var(--border-strong); color: var(--text); }
.node-action-form { display: inline; }
/* ── 9. 表单 ─────────────────────────────────────────────────────────────── */
.form { max-width: 720px; }
.form-plain { max-width: none; }
.form-row { margin-bottom: 15px; }
.form-row > label {
display: block; margin-bottom: 5px;
font-size: 13px; font-weight: 600; color: var(--text-soft);
}
.form-row .muted { margin-top: 4px; }
input[type="text"], input[type="password"], input[type="number"], input[type="datetime-local"],
input[type="date"], input[type="email"], input[type="tel"], input[type="url"], input[type="search"],
select, textarea {
width: 100%;
padding: 9px 11px;
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
background: var(--surface);
color: var(--text);
font-size: 14px; font-family: inherit; line-height: 1.5;
transition: border-color .15s, box-shadow .15s;
}
input:focus, select:focus, textarea:focus {
outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring);
}
input:disabled, select:disabled, textarea:disabled { background: var(--bg-soft); color: var(--text-mute); }
input[type="file"] { font-size: 13px; padding: 6px 0; border: 0; background: none; width: auto; }
textarea { resize: vertical; min-height: 84px; }
select { cursor: pointer; }
/* 时间框:点框内任何一处都能弹选择器(由 layout.php 里的 showPicker 负责,
原生只有点右侧那个小图标才弹)。光标给个「整块可点」的暗示,别让人以为只有图标能点。 */
input[type="datetime-local"], input[type="date"] { cursor: pointer; }
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; padding: 2px; }
/* ── 日期 + 24 小时制「时:分」组合输入(templates/_datetime.php)──────────────
原生 datetime-local 的显示格式由浏览器 UI 语言/系统时间格式决定,中文环境渲染成
「下午 03:30」,页面和元素的 lang 都压不住(实测),所以拆成 date + 时/分两个下拉。
24 小时制由控件本身决定,浏览器改不掉。 */
.dt-input { display: flex; align-items: center; gap: 6px; }
.dt-input .dt-date { flex: 1 1 120px; min-width: 0; }
.dt-input .dt-hour,
.dt-input .dt-minute {
flex: 0 0 64px; width: 64px; padding: 9px 4px;
text-align: center; text-align-last: center;
}
.dt-input .dt-colon { color: var(--text-mute); font-weight: 650; }
/* 裁剪入口(节点表单):「逐张裁剪」按钮 + 一句说明 */
.crop-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.crop-toolbar .muted { margin-top: 0; font-size: 12px; }
/* 节点表单待上传卡片上的「已裁剪」角标。
.photo-badge 本来是给照片页 .photo-item(flex 纵向布局)的流式元素用的,
这里换到 .photo-card(position:relative)就得改成绝对定位,压在缩略图左上角。 */
.photo-card > .photo-badge {
position: absolute; top: 6px; left: 6px; margin: 0;
background: rgba(18, 133, 90, .92); color: #fff;
}
.form-actions {
display: flex; align-items: center; gap: 10px;
margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--border);
}
.fieldset {
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 15px 16px 4px;
margin: 0 0 16px;
background: var(--surface-2);
}
.fieldset > legend {
padding: 0 7px; font-size: 13px; font-weight: 620; color: var(--text-soft);
}
.inline-panel {
padding: 14px 16px; margin: 12px 0;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.panel {
padding: 18px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
margin-bottom: 18px;
}
.radio-group { display: flex; flex-wrap: wrap; gap: 8px; }
.radio-label {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 11px; border: 1px solid var(--border-strong); border-radius: 999px;
font-size: 13px; cursor: pointer; background: var(--surface);
}
.radio-label:hover { border-color: var(--accent); }
.radio-label input { margin: 0; }
.check-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.checkbox-label {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 11px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
font-size: 13px; cursor: pointer; background: var(--surface); max-width: 100%;
}
.checkbox-label:hover { border-color: var(--accent); background: var(--accent-soft); }
.checkbox-label input { margin: 0; }
.checkbox-label span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 高德地址联想下拉 */
.amap-dropdown {
position: absolute; left: 0; right: 0; top: calc(100% + 4px);
max-height: 260px; overflow-y: auto;
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-md);
z-index: 30;
}
.amap-dropdown-item { padding: 9px 12px; cursor: pointer; border-bottom: 1px solid var(--border); }
.amap-dropdown-item:last-child { border-bottom: 0; }
.amap-dropdown-item:hover, .amap-dropdown-item.active { background: var(--accent-soft); }
.amap-dropdown-name { font-size: 13.5px; }
.amap-dropdown-sub { font-size: 12px; color: var(--text-mute); }
/* 起点 / 终点的「导航到这里」。
有没有坐标决定它是什么链接,所以由服务端与 JS 共同决定显隐;
必须排在 .form-row .muted 之后(同权重,靠后者生效)。 */
.loc-nav { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.loc-nav-link {
display: inline-flex; align-items: center; gap: 4px;
font-size: 13px; font-weight: 560; line-height: 1.4;
padding: 4px 11px; border-radius: 999px;
background: var(--accent-soft); color: var(--accent);
border: 1px solid transparent;
}
.loc-nav-link:hover { border-color: var(--accent); text-decoration: none; }
.loc-nav .muted { margin-top: 0; font-size: 12px; }
/* 详情页里跟在地址后面的那个链接(不是整块,只是行内追加一个胶囊) */
.loc-nav-inline { margin-left: 8px; vertical-align: middle; font-size: 12.5px; padding: 3px 9px; }
/* emoji 选择 */
.emoji-picker { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.emoji-btn {
width: 34px; height: 34px; font-size: 17px; line-height: 1;
display: inline-flex; align-items: center; justify-content: center;
border: 1px solid var(--border); border-radius: var(--radius-sm);
background: var(--surface); cursor: pointer; padding: 0;
}
.emoji-btn:hover { border-color: var(--accent); background: var(--accent-soft); }
/* 表格 */
.data-table { width: 100%; border-collapse: collapse; font-size: 14px; background: var(--surface); }
.data-table th, .data-table td { padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--border); }
.data-table th { font-size: 12.5px; font-weight: 620; color: var(--text-mute); background: var(--surface-2); white-space: nowrap; }
.data-table tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: var(--surface-2); }
.data-table td.actions { white-space: nowrap; }
.data-table td.actions a, .data-table td.actions button { margin-right: 8px; }
/* ── 10. 详情页 ──────────────────────────────────────────────────────────── */
.detail-grid {
display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 14px 22px;
padding: 16px 18px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.detail-item { min-width: 0; }
.detail-item-wide { grid-column: 1 / -1; }
.detail-label { font-size: 12px; color: var(--text-mute); margin-bottom: 2px; }
.detail-value { font-size: 14px; word-break: break-word; }
/* 花销汇总条 */
.activity-stats { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 18px; }
.activity-stat {
flex: 1 1 140px;
padding: 11px 14px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
display: flex; flex-direction: column; gap: 1px;
}
.activity-stat .detail-label { margin: 0; }
.activity-stat strong { font-size: 16px; }
/* ── 11. 评论 ────────────────────────────────────────────────────────────── */
.comment-thread { margin-top: 8px; }
.comment-list { display: flex; flex-direction: column; gap: 14px; }
.comment-item {
padding: 12px 14px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.comment-child {
margin-left: 22px;
background: var(--surface-2);
border-left: 2px solid var(--accent-soft);
}
.comment-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 5px; }
.comment-author { font-weight: 620; font-size: 13.5px; }
.comment-time { font-size: 12px; color: var(--text-mute); }
.comment-body { font-size: 14px; white-space: pre-wrap; word-break: break-word; }
.comment-replies { margin-top: 10px; display: flex; flex-direction: column; gap: 10px; }
.comment-del-form { display: inline; margin-left: auto; }
.comment-del {
border: 0; background: none; color: var(--text-mute);
font-size: 15px; line-height: 1; cursor: pointer; padding: 2px 6px; border-radius: 6px;
}
.comment-del:hover { background: var(--danger-soft); color: var(--danger); }
.comment-form { margin-top: 14px; }
.comment-form-inline { margin-top: 8px; }
.comment-form textarea, .comment-form-inline textarea { min-height: 68px; }
.comment-reply-box { margin-top: 10px; }
/* ── 12. 参与人 ──────────────────────────────────────────────────────────── */
.member-list { display: flex; flex-direction: column; gap: 10px; }
.member-row {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
padding: 10px 13px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.member-name { font-weight: 600; font-size: 14px; }
.member-role { display: inline-flex; flex-wrap: wrap; gap: 5px; }
.member-emotion { font-size: 17px; line-height: 1; }
.member-checkin { font-size: 12.5px; color: var(--text-mute); }
.member-del-form { display: inline; margin-left: auto; }
.member-edit {
margin-left: auto;
border: 1px solid var(--border-strong); background: var(--surface);
color: var(--text-soft); font-size: 12.5px;
padding: 3px 10px; border-radius: 999px; cursor: pointer;
}
.member-edit:hover { border-color: var(--accent); color: var(--accent); }
/* 有「编辑」按钮时,移出按钮紧跟其后即可,不再负责把整行推到最右 */
.member-row .member-edit + .member-del-form { margin-left: 0; }
.member-del-form button {
border: 0; background: none; color: var(--text-mute);
font-size: 15px; cursor: pointer; padding: 2px 6px; border-radius: 6px;
}
.member-del-form button:hover { background: var(--danger-soft); color: var(--danger); }
.emotion-grid { display: flex; flex-wrap: wrap; gap: 6px; }
.emotion-option {
display: inline-flex; align-items: center; gap: 5px;
padding: 5px 10px; border: 1px solid var(--border-strong); border-radius: 999px;
cursor: pointer; font-size: 14px; background: var(--surface);
}
.emotion-option:hover { border-color: var(--accent); }
.emotion-option input { margin: 0; }
.emotion-face { font-size: 16px; line-height: 1; }
.emotion-text { font-size: 12.5px; color: var(--text-soft); }
/* ── 13. 花销(只记真实花销:上面一笔一笔,下面总计) ────────────────────── */
.expense-list { display: flex; flex-direction: column; gap: 10px; }
.expense-row {
display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
padding: 11px 14px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.expense-main { min-width: 0; }
.expense-cat { font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.expense-desc { font-size: 13px; color: var(--text-soft); margin-top: 2px; }
.expense-sub { font-size: 12.5px; color: var(--text-mute); margin-top: 3px; }
.expense-amount { font-size: 15px; font-weight: 650; white-space: nowrap; }
.expense-total {
display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
margin-top: 12px; padding: 11px 14px;
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: var(--radius);
font-size: 13.5px; color: var(--text-soft);
}
.expense-total strong { font-size: 18px; font-weight: 750; color: var(--text); }
/* ── 14. 附件与照片 ──────────────────────────────────────────────────────── */
.drop-zone {
border: 2px dashed var(--border-strong);
border-radius: var(--radius);
background: var(--surface);
padding: 22px 16px;
text-align: center;
cursor: pointer;
transition: border-color .15s, background .15s;
}
.drop-zone:hover, .drop-zone.dragging { border-color: var(--accent); background: var(--accent-soft); }
.drop-zone-inner { color: var(--text-soft); }
.drop-zone-inner .muted { margin-top: 4px; }
.file-list { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.file-row {
font-size: 13px; color: var(--text-soft);
padding: 7px 11px; background: var(--surface-2);
border: 1px solid var(--border); border-radius: var(--radius-sm);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 待上传照片的暂存区 */
.photo-stage { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 14px; }
.photo-item {
width: 132px; background: var(--surface);
border: 1px solid var(--border); border-radius: var(--radius);
overflow: hidden; display: flex; flex-direction: column;
}
.photo-thumb { width: 100%; height: 96px; object-fit: cover; background: var(--bg-soft); display: block; }
.photo-info { padding: 6px 8px 0; font-size: 11.5px; color: var(--text-mute); }
.photo-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-soft); }
.photo-actions { display: flex; gap: 4px; padding: 6px 8px 8px; }
.photo-actions button {
flex: 1; font-size: 11.5px; padding: 4px; cursor: pointer;
border: 1px solid var(--border); border-radius: 6px;
background: var(--surface); color: var(--text-soft); font-family: inherit;
}
.photo-actions button:hover { border-color: var(--accent); color: var(--accent); }
.photo-badge {
margin: 6px 8px 0; align-self: flex-start;
font-size: 11px; padding: 1px 6px; border-radius: 999px;
background: var(--success-soft); color: var(--success);
}
/* 已上传的照片墙 */
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 12px;
}
.photo-grid-sm { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }
/* 已有附件的「勾选即删除」格子:整格可点,勾上后变灰 + 红勾。
原生 checkbox 藏起来但保留可聚焦,键盘也能用(勾选框上会有 focus 环)。 */
.photo-pick {
position: relative; display: block; cursor: pointer; margin: 0;
border: 1px solid var(--border); border-radius: var(--radius);
overflow: hidden; background: var(--surface);
transition: border-color .15s, box-shadow .15s;
}
.photo-pick:hover { border-color: var(--accent); }
.photo-pick img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; background: var(--bg-soft); }
.photo-pick-ext {
display: flex; align-items: center; justify-content: center;
aspect-ratio: 4 / 3; font-size: 12px; font-weight: 600;
color: var(--text-mute); background: var(--bg-soft);
}
.photo-pick input { position: absolute; width: 0; height: 0; opacity: 0; }
.photo-pick-mark {
position: absolute; top: 6px; right: 6px;
width: 22px; height: 22px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 13px; line-height: 1; color: transparent;
background: rgba(20, 24, 34, .45);
border: 1px solid rgba(255, 255, 255, .85);
transition: background .15s, color .15s, border-color .15s;
}
.photo-pick input:checked ~ img,
.photo-pick input:checked ~ .photo-pick-ext { opacity: .35; filter: grayscale(1); }
.photo-pick input:checked ~ .photo-pick-mark { background: var(--danger); border-color: var(--danger); color: #fff; }
.photo-pick input:focus-visible ~ .photo-pick-mark { box-shadow: 0 0 0 3px var(--accent-ring); }
/* 表单里刚选中的待上传文件(复用 .photo-card 的样式,只是要一个能挂 × 按钮的位置) */
.photo-card > .photo-del { position: absolute; top: 6px; right: 6px; }
.photo-card {
position: relative; display: block;
border: 1px solid var(--border); border-radius: var(--radius);
overflow: hidden; background: var(--surface);
transition: box-shadow .15s, transform .15s;
}
.photo-card:hover { box-shadow: var(--shadow-sm); transform: translateY(-2px); }
.photo-card > a { display: block; color: inherit; }
.photo-card > a:hover { text-decoration: none; }
.photo-card img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; background: var(--bg-soft); }
/* 存储后端标记(本地 / OSS),压在缩略图左上角 */
.photo-tag {
position: absolute; top: 6px; left: 6px;
font-size: 10.5px; padding: 1px 6px; border-radius: 999px;
background: rgba(20, 24, 34, .6); color: #fff; letter-spacing: .3px;
}
.photo-tag-oss { background: rgba(232, 122, 45, .92); }
.photo-cap { padding: 8px 10px 10px; font-size: 12px; color: var(--text-mute); }
.photo-cap-name {
display: block; font-size: 12.5px; color: var(--text);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.photo-cap-sub {
display: block; margin-top: 2px; font-size: 11.5px; color: var(--text-mute);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.photo-cap-links { display: block; margin-top: 7px; }
.photo-cap-links a { font-size: 12px; margin-right: 12px; }
.photo-del-form { position: absolute; top: 6px; right: 6px; margin: 0; opacity: 0; transition: opacity .15s; }
.photo-card:hover .photo-del-form, .photo-del-form:focus-within { opacity: 1; }
.photo-del {
width: 26px; height: 26px; border-radius: 50%;
border: 0; cursor: pointer; line-height: 1;
background: rgba(20, 24, 34, .55); color: #fff; font-size: 15px;
display: flex; align-items: center; justify-content: center;
}
.photo-del:hover { background: var(--danger); }
/* 详情页上的小照片墙 */
.photo-more {
display: flex; flex-direction: column; align-items: center; justify-content: center;
aspect-ratio: 4 / 3; border: 1px dashed var(--border-strong);
border-radius: var(--radius); background: var(--surface-2);
color: var(--text-soft); font-size: 18px; font-weight: 620;
}
.photo-more span { font-size: 11.5px; font-weight: 400; color: var(--text-mute); }
.photo-more:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.photo-bar { display: flex; align-items: center; gap: 12px; margin-top: 12px; flex-wrap: wrap; }
/* 通用附件卡(非图片也用) */
.attachment-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px;
}
.attachment-card {
border: 1px solid var(--border); border-radius: var(--radius);
overflow: hidden; background: var(--surface); display: flex; flex-direction: column;
}
.attachment-link { display: block; color: inherit; }
.attachment-link:hover { text-decoration: none; }
.attachment-thumb { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; background: var(--bg-soft); }
.attachment-icon {
display: flex; align-items: center; justify-content: center;
height: 96px; font-size: 30px; background: var(--bg-soft);
}
.attachment-meta { padding: 9px 11px; display: flex; align-items: center; gap: 6px; }
.attachment-name {
font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0;
}
.attachment-sub { font-size: 11.5px; color: var(--text-mute); }
.attachment-delete {
border: 0; background: none; color: var(--text-mute);
font-size: 16px; line-height: 1; cursor: pointer; padding: 2px 6px; border-radius: 6px; flex: none;
}
.attachment-delete:hover { background: var(--danger-soft); color: var(--danger); }
/* ── 15. 弹窗与裁剪 ──────────────────────────────────────────────────────── */
.modal {
position: fixed; inset: 0; z-index: 100;
display: none; align-items: center; justify-content: center;
padding: 20px;
}
.modal.open { display: flex; }
.modal-backdrop { position: absolute; inset: 0; background: rgba(18, 22, 32, .58); }
.modal-content {
position: relative; z-index: 1;
width: 100%; max-width: 460px;
max-height: calc(100vh - 40px); overflow: auto;
background: var(--surface);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
padding: 20px;
}
.modal-content-wide { max-width: 720px; }
.modal-actions {
display: flex; justify-content: flex-end; gap: 10px;
margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border);
}
.crop-container { width: 100%; max-height: 60vh; overflow: hidden; background: #111; border-radius: var(--radius-sm); }
.crop-container-wide { max-height: 66vh; }
/* Cropper 需要按原始尺寸接管这张图(自己算内联宽高),不能被 max-width 夹住;
容器自带 overflow:hidden,所以就算图超出也只会被裁掉,不会撑破页面。 */
.crop-container img { max-width: none; display: block; }
/* 预览容器只负责「框」:定尺寸、圆角、把图片裁进去。
⚠️ width / height / object-fit 必须写在 **img** 上,不能写在外层 div 上 ——
写错地方图片就完全不受约束,会按原始尺寸铺出来:头像 400×400、封面
1920×1080,直接把下面的表单盖住(这就是「头像非常大、整页错位」的根)。
对照写对了的 .card-cover img / .activity-cover img。 */
.avatar-preview {
position: relative; flex: none;
width: 84px; height: 84px; border-radius: 50%;
overflow: hidden; /* 把方图裁成圆,并兜住任何溢出 */
background: var(--bg-soft); border: 2px dashed var(--border-strong);
}
.cover-preview {
position: relative;
width: 100%; aspect-ratio: 16 / 9;
overflow: hidden;
border-radius: var(--radius); background: var(--bg-soft);
border: 2px dashed var(--border-strong);
}
/* 图和占位块都用绝对定位铺满容器:
百分比相对 padding box 解析,宽高一定确定。
⚠️ 不要用 `display: grid; place-items: center` + `width: 100%` ——
自动列宽会反过来依赖元素自身宽度,形成循环,浏览器只能把图算成 0 宽
(实测就是这么坏的:img 宽 0、只剩一个空框)。 */
.avatar-preview > img,
.cover-preview > img {
position: absolute; inset: 0;
display: block; width: 100%; height: 100%; object-fit: cover;
}
.avatar-preview > .avatar-placeholder,
.cover-preview > .cover-placeholder {
position: absolute; inset: 0;
width: 100%; height: 100%;
display: flex; align-items: center; justify-content: center;
}
/* 上传区只是「预览 + 操作按钮」的布局容器,不是那个虚线圆框本身。
⚠️ 这里曾经把 84×84 的圆形尺寸套在 .avatar-upload 上,而模板把预览、
按钮、提示文字都放在它**内部** → 三样东西全被塞进那个小圆里互相重叠
(编辑用户页的「选择图片/移除」就压在头像上)。圆框属于
.avatar-preview / .cover-preview,容器只负责排列。 */
.avatar-upload {
display: flex; align-items: center; gap: 16px;
width: auto; height: auto;
border: 0; border-radius: 0; background: none;
}
.cover-upload {
display: flex; flex-direction: column; gap: 12px;
width: 100%; height: auto;
border: 0; border-radius: 0; background: none;
}
/* "+" 占位块:尺寸已经被 `.avatar-preview > .avatar-placeholder` 铺满了,这里只管样子 */
.avatar-placeholder {
border-radius: 50%;
background: var(--bg-soft); color: var(--text-mute); font-size: 26px;
}
/* 悬停高亮落在预览框上(那才是"会被换掉的东西") */
.avatar-preview, .cover-preview { transition: border-color .15s, background .15s; }
.avatar-upload:hover .avatar-preview,
.cover-upload:hover .cover-preview { border-color: var(--accent); background: var(--accent-soft); }
.avatar-upload-actions, .cover-upload-actions {
display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-top: 0;
}
/* ── 16. 头像与用户 ──────────────────────────────────────────────────────── */
/* 这两类是直接写在 <img>(或首字母 <span>)上的,所以尺寸放这里是对的。
注意别把 .avatar-placeholder 的 84px 带进来:它常与 .avatar-sm 叠用
(用户列表里用首字母当头像),靠下面那条组合规则把字号压回 13px。 */
.avatar-xs, .avatar-sm {
border-radius: 50%; object-fit: cover; flex: none;
display: inline-flex; align-items: center; justify-content: center;
overflow: hidden;
}
.avatar-xs { width: 26px; height: 26px; }
.avatar-sm { width: 30px; height: 30px; }
.avatar-initial { background: var(--accent); color: #fff; font-size: 12px; font-weight: 620; }
.avatar-sm.avatar-placeholder,
.avatar-xs.avatar-placeholder {
font-size: 13px; font-weight: 620;
background: var(--accent-soft); color: var(--accent);
}
.user-cell { display: flex; align-items: center; gap: 9px; }
.user-info { display: inline-flex; align-items: center; gap: 8px; }
/* ── 17. 登录页 ──────────────────────────────────────────────────────────── */
.login-wrapper { width: 100%; }
.login-form {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
padding: 28px 24px;
}
.login-form h1 { font-size: 19px; text-align: center; margin-bottom: 4px; }
.login-form > p { text-align: center; margin-bottom: 20px; }
/* ── 18. 存储设置页 ──────────────────────────────────────────────────────── */
.settings-status { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 18px; }
.settings-chip {
flex: 1 1 190px;
padding: 11px 14px;
background: var(--surface);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
border-radius: var(--radius);
font-size: 13px;
}
.settings-chip.ok { border-left-color: var(--success); }
.settings-chip.warn { border-left-color: var(--warn); }
.settings-chip > div:first-child { font-size: 12px; color: var(--text-mute); }
.settings-chip strong { font-size: 14px; }
.settings-hint { font-size: 12.5px; color: var(--text-mute); margin-top: 5px; line-height: 1.6; }
.settings-hint code { background: var(--bg-soft); padding: 1px 5px; border-radius: 4px; font-size: 12px; word-break: break-all; }
.oss-fields { display: none; }
.oss-fields.show { display: block; }
.test-result {
margin-top: 12px; padding: 11px 13px; border-radius: var(--radius-sm);
font-size: 13px; display: none; white-space: pre-wrap; word-break: break-word;
}
.test-result.show { display: block; }
.test-result.ok { background: var(--success-soft); color: var(--success); border: 1px solid #b9e4d1; }
.test-result.bad { background: var(--danger-soft); color: var(--danger); border: 1px solid #f3c9ca; }
.role-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.role-chip {
font-size: 12.5px; padding: 3px 10px; border-radius: 999px;
border: 1px solid var(--border-strong); background: var(--surface);
cursor: pointer; font-family: inherit; color: var(--text-soft);
}
.role-chip:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
/* ── 19. 形态开关(自有车 / 租车 等按需显示的字段) ─────────────────────── */
.own-only, .rental-only, .mode-field { display: none; }
/* ── 20. 附件页与表单补充 ───────────────────────────────────────────────── */
/* 表单里的小字说明 */
.form-hint { font-size: 12.5px; color: var(--text-mute); margin-top: 5px; line-height: 1.65; }
.form-hint-error { color: var(--danger); font-weight: 560; }
.form-hint code, .settings-hint code, .alert code {
background: var(--bg-soft); padding: 1px 5px; border-radius: 4px;
font-size: 12px; word-break: break-all;
}
.alert-warn { background: var(--warn-soft); color: var(--warn); border-color: #ecd9b4; }
.text-danger { color: var(--danger); }
.req { color: var(--danger); font-weight: 620; }
.inline-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.inline-row > input { flex: 1 1 220px; min-width: 0; }
/* 上传区 */
.drop-zone-icon { font-size: 30px; line-height: 1.1; }
.drop-zone-title { margin: 6px 0 0; font-size: 14.5px; color: var(--text); }
.drop-zone-hint { font-size: 12.5px; margin: 4px 0 0; }
/* 待上传暂存区 */
.stage-head {
display: flex; align-items: center; justify-content: space-between;
gap: 12px; margin-top: 18px; flex-wrap: wrap;
}
.stage-count { font-size: 13px; color: var(--text-soft); }
.stage-tools { display: flex; gap: 8px; }
.photo-thumb-icon {
display: flex; align-items: center; justify-content: center;
font-size: 28px; height: 96px; background: var(--bg-soft);
}
.photo-badge-plain { background: var(--bg-soft); color: var(--text-soft); }
.photo-actions button[disabled] { opacity: .45; cursor: not-allowed; }
/* 上传进度 */
.upload-progress { margin-top: 14px; }
.upload-track {
height: 8px; border-radius: 999px; background: var(--bg-soft); overflow: hidden;
}
.upload-bar {
height: 100%; width: 0; border-radius: 999px;
background: var(--accent); transition: width .2s ease;
}
.upload-text { margin-top: 6px; font-size: 12.5px; color: var(--text-mute); }
/* 弹窗标题 */
.modal-title { font-size: 16px; margin: 0 0 4px; }
.modal-hint { font-size: 12.5px; color: var(--text-mute); margin: 0 0 12px; }
/* ── 21. 响应式 ──────────────────────────────────────────────────────────── */
@media (max-width: 820px) {
.timeline-grid { grid-template-columns: 1fr; }
.card-grid { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }
}
@media (max-width: 640px) {
body { font-size: 14.5px; }
/* 留白仍走 --shell-pad(此时它等于 --gutter):断点里也不能改成固定值,
否则窄屏下顶栏与正文又会对不齐。 */
.container { padding: 16px var(--shell-pad) 52px; }
.container-guest { padding: 20px 14px; }
.topbar { padding: 8px var(--shell-pad); }
.brand-text { display: none; }
.menu-name { display: none; }
.menu-trigger { padding: 4px; gap: 4px; }
.menu-panel { min-width: 208px; }
.two-col { grid-template-columns: 1fr; }
.card-grid, .photo-grid, .attachment-grid {
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
}
.detail-grid { grid-template-columns: 1fr; }
.page-header { flex-direction: column; align-items: stretch; }
.page-header .btn, .page-header .btn-primary { align-self: flex-start; }
.comment-child { margin-left: 10px; }
.modal { padding: 12px; }
.modal-content { padding: 16px; }
.topbar-new { padding: 5px 9px; font-size: 12.5px; }
.expense-row { flex-direction: column; }
.expense-amount { align-self: flex-end; }
.photo-item { width: calc(50% - 6px); }
.settings-chip { flex: 1 1 100%; }
.inline-row { align-items: stretch; }
}
/* ── 17. 照片灯箱:当前页弹窗看大图,左右箭头/键盘/滑动切换 ────────────────── */
.lightbox {
position: fixed; inset: 0; z-index: 120;
display: none; align-items: center; justify-content: center;
}
.lightbox.open { display: flex; }
.lightbox-backdrop { position: absolute; inset: 0; background: rgba(10, 13, 20, .88); }
.lightbox-body {
position: relative; z-index: 1;
margin: 0; max-width: min(1100px, 92vw);
display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.lightbox-img {
max-width: min(1100px, 92vw);
max-height: 78vh;
object-fit: contain;
border-radius: var(--radius);
box-shadow: var(--shadow-lg);
background: transparent;
}
.lightbox-cap {
max-width: min(1100px, 92vw);
color: rgba(255, 255, 255, .82); font-size: 13px;
text-align: center; overflow-wrap: anywhere;
}
.lightbox-nav {
position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
width: 52px; height: 52px;
border: 0; border-radius: 50%;
background: rgba(255, 255, 255, .14); color: #fff;
font-size: 34px; line-height: 1;
cursor: pointer; user-select: none;
display: flex; align-items: center; justify-content: center;
}
.lightbox-nav:hover { background: rgba(255, 255, 255, .28); }
.lightbox-prev { left: max(12px, calc((100vw - min(1100px, 92vw)) / 2 - 72px)); }
.lightbox-next { right: max(12px, calc((100vw - min(1100px, 92vw)) / 2 - 72px)); }
.lightbox-close {
position: absolute; top: 14px; right: 16px; z-index: 2;
width: 42px; height: 42px;
border: 0; border-radius: 50%;
background: rgba(255, 255, 255, .14); color: #fff;
font-size: 26px; line-height: 1; cursor: pointer;
}
.lightbox-close:hover { background: rgba(255, 255, 255, .28); }
.lightbox-count {
position: absolute; top: 24px; left: 20px; z-index: 2;
color: rgba(255, 255, 255, .75); font-size: 13px;
font-variant-numeric: tabular-nums;
}
@media (max-width: 700px) {
.lightbox-nav { width: 46px; height: 46px; font-size: 30px; }
.lightbox-prev { left: 8px; }
.lightbox-next { right: 8px; }
.lightbox-img { max-height: 66vh; }
}