Files
TripLog/assets/style.css
T
laoyang 5645a8db5c 首页地图引线加箭头指向真实位置(批 23)
① 被扇开的卡片,引线改成「从卡片指向真实坐标」:箭头画在真实位置那一端
  (marker-end + SVG marker),终点退开 DOT_R+2 px 免得盖住圆点。活动多了
  也能一眼看出这张卡片归哪个点 —— 这是用户「活动特别多会不会乱」的核心诉求。

② 箭头 marker 的 id 逐枚唯一(home-arrow-<下标>):每枚标记的 content 各自
  带一份 <defs>,同页 id 重名会指向先出现的那份,属隐患。

验证:本地全链新增 10c2「首页地图密集活动排布」步骤 —— 密集夹具
(8 个活动:东北簇 3 + 长三角簇 3 + 孤立三亚/成都)断言卡片两两不叠、
都在地图容器内、扇开的都有箭头且箭头落点离自己真实坐标 <= 8px、marker id
不重名、孤立的两枚原地不动、密集下仍可点进活动;_map16 亦加两条箭头断言。
部署哨兵新增批 23 段 4 条(数代码形式字面量,防注释打挂)。
2026-09-26 20:41:28 +08:00

1430 lines
65 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; }
/* 批 21:详情页顶部不再放整幅 16:9 大封面(原大图容器样式已删),
改用列表同款 .act-row(含 .act-row-cover,仍是 16:9)—— 见「活动列表」一节。 */
/* ── 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); }
/* 批 11:表单缩略图旁的「✂ 裁剪」按钮 —— 上传后不再自动弹裁剪框,点它才弹 */
.photo-card > .photo-crop-btn {
position: absolute; top: 6px; left: 6px;
border: 0; border-radius: 6px; padding: 3px 9px;
font-size: 12px; line-height: 1.4; cursor: pointer;
background: rgba(20, 24, 34, .62); color: #fff;
}
.photo-card > .photo-crop-btn:hover { background: rgba(20, 24, 34, .85); }
/* 详情页上的小照片墙 */
.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);
}
/* 批 8:详情页附件墙里的图标格 —— 和图片缩略图一样占满 4:3 格子 */
.photo-card .attachment-icon {
height: 100%; aspect-ratio: 4 / 3; font-size: 34px;
}
/* 批 9:视频缩略图 = 静音迷你 <video preload="metadata">,只拉头部显示第一帧。
盖在图标上;视频解不出来时透明,露出图标(pointer-events 让点击穿透到灯箱链接)。
show.php 附件墙:图标撑满 4:3 格 → 缩略图全格覆盖 */
.photo-card-icon .video-thumb-mini {
position: absolute; inset: 0; width: 100%; height: 100%;
object-fit: cover; background: transparent; pointer-events: none;
}
/* attachments.php 管理页(video 带 .video-thumb 类):卡片顶部是 96px 高的图标栏
(下有文字区),缩略图只盖图标区 —— 高度须与 .attachment-icon 的 96px 保持一致 */
video.video-thumb.video-thumb-mini {
position: absolute; top: 0; left: 0; width: 100%; height: 96px;
object-fit: cover; background: transparent; pointer-events: none;
}
.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 / .act-row-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); }
/* 批 21:把图片拖进上传区时给个高亮反馈(松手即走原有的裁剪流程) */
.cover-upload.dragover .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; }
}
/* ── 18. 活动页节点卡片:参与人(头像 + 名字) ────────────────────────────── */
.node-members {
display: flex; flex-wrap: wrap; gap: 5px;
margin-top: 8px; padding-top: 8px;
border-top: 1px dashed var(--border);
}
.node-member-chip {
display: inline-flex; align-items: center; gap: 5px;
padding: 2px 9px 2px 3px;
background: var(--bg-soft);
border: 1px solid var(--border);
border-radius: 999px;
font-size: 12px; color: var(--text-soft);
text-decoration: none;
max-width: 100%;
}
.node-member-chip:hover { border-color: var(--accent); color: var(--accent); }
.node-member-chip img,
.node-member-avatar-fallback {
width: 20px; height: 20px;
border-radius: 50%;
object-fit: cover;
flex: 0 0 auto;
}
.node-member-avatar-fallback {
display: inline-flex; align-items: center; justify-content: center;
background: var(--accent-soft, var(--bg-soft));
color: var(--accent);
font-size: 11px; font-weight: 650;
}
.node-member-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.node-member-emotion { font-size: 13px; line-height: 1; }
/* ── 19. 灯箱的视频 / PDF / 下载视图 ──────────────────────────────────────── */
.lightbox-video {
max-width: min(1100px, 92vw);
max-height: 74vh;
border-radius: var(--radius);
background: #000;
box-shadow: var(--shadow-lg);
}
.lightbox-frame {
width: min(1000px, 92vw);
height: 78vh;
border: 0;
border-radius: var(--radius);
background: #fff;
box-shadow: var(--shadow-lg);
}
.lightbox-file {
display: flex; flex-direction: column; align-items: center; gap: 12px;
padding: 36px 28px;
background: var(--surface);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
max-width: min(420px, 88vw);
text-align: center;
}
.lightbox-file-icon { font-size: 52px; line-height: 1; }
.lightbox-file-text {
margin: 0; font-size: 15px; font-weight: 650; color: var(--text);
overflow-wrap: anywhere;
}
.lightbox-file-hint { margin: 0; font-size: 13px; color: var(--text-mute); }
.lightbox-file-dl { display: inline-block; }
/* ── 20. 活动页路线地图(批 10)──────────────────────────────────────────── */
/* 批 12:详情页标签页 —— 地图独占一页、尽量大;节点详情切到另一页 */
.route-tabs { display: flex; gap: 8px; margin: 0 0 14px; }
.route-tab {
border: 1px solid var(--border); background: var(--surface);
border-radius: 999px; padding: 8px 18px; font-size: 14px;
cursor: pointer; color: var(--text-soft);
}
.route-tab:hover { border-color: var(--border-strong); color: var(--text); }
.route-tab.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
.route-panel[hidden] { display: none; }
.route-map-section { margin-top: 0; }
.route-map {
height: clamp(480px, 72vh, 860px);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
background: var(--bg-soft);
}
.route-map-hint { margin-top: 8px; font-size: 12.5px; }
.route-map-fallback { padding: 24px; text-align: center; color: var(--text-mute); }
/* 节点圆形标记:有图片附件放图片,没有放类型 emoji;左上角序号 */
.route-marker {
position: relative; width: 40px; height: 40px; border-radius: 50%;
border: 3px solid #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, .35);
background: var(--surface); cursor: pointer; overflow: visible;
display: flex; align-items: center; justify-content: center;
font-size: 20px;
}
.route-marker img {
width: 100%; height: 100%; border-radius: 50%;
object-fit: cover; display: block;
}
.route-marker-emoji { line-height: 1; }
.route-marker-num {
position: absolute; top: -6px; right: -6px; min-width: 17px; height: 17px;
border-radius: 999px; background: #ef4444; color: #fff;
font-size: 11px; font-weight: 700; line-height: 17px;
text-align: center; padding: 0 3px;
}
/* 到达地小圆点 */
.route-end-dot {
width: 12px; height: 12px; border-radius: 50%;
background: #ef4444; border: 2px solid #fff;
box-shadow: 0 1px 4px rgba(0, 0, 0, .3);
}
.route-legend-end { color: #ef4444; font-weight: 700; }
@media (max-width: 640px) {
.route-map { height: 62vh; }
.route-tab { padding: 7px 14px; font-size: 13px; }
}
/* ── 21. 节点表单:参与人勾选 + 位置详细地址(批 13)───────────── */
.member-pick { display: flex; flex-wrap: wrap; gap: 8px; }
.member-pick-item {
display: inline-flex; align-items: center; gap: 6px;
padding: 7px 12px; border: 1px solid var(--border);
border-radius: 999px; background: var(--surface); cursor: pointer;
font-size: 14px; transition: background .15s, border-color .15s;
}
.member-pick-item:hover { border-color: var(--border-strong); }
.member-pick-item input[type="checkbox"] { margin: 0; accent-color: var(--accent); }
.member-pick-item:has(input:checked) {
border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, #fff);
}
.member-pick-avatar {
width: 22px; height: 22px; border-radius: 50%; object-fit: cover; display: block;
}
/* 联想选中后回填的详细地址(让人看见记下来的是什么) */
.loc-detail { margin-top: 6px; font-size: 12.5px; word-break: break-all; }
/* ── 22. 首页:旅行地图 + 活动列表两个标签页(批 16,批 22 改卡片)── */
/* 一次旅行一枚「方形小卡片」:左边封面图(没有放 📍),右边活动名 +
地点/参与人摘要。同城/邻近的旅行被扇开到外围时,用一条引线连回真实位置
(线是 marker content 里的 SVG;外层画布 pointer-events:none,
不挡底图拖拽,只有卡片本身可点)。 */
.home-pin { position: relative; width: 600px; height: 600px; pointer-events: none; }
/* AMap 的标记包装层 .amap-marker 尺寸跟着 content(600×600)走、默认可命中,
会把别家卡片的点击整层挡住(amap-marker intercepts pointer events)。
整层禁掉命中、只留卡片本身可点 —— 点击事件沿 DOM 冒泡,AMap 的
marker click 照常触发。 */
#home-map-canvas .amap-marker { pointer-events: none !important; }
.home-pin svg { position: absolute; left: 0; top: 0; overflow: visible; }
.home-pin-line { stroke: #8aa0b8; stroke-width: 1.5; }
.home-pin-dot { fill: #3b82f6; }
/* 引线末端的箭头(指向真实位置那个圆点)—— 活动多了也分得清哪张卡片归哪个点 */
.home-arrow { fill: #8aa0b8; }
.home-card {
position: absolute; transform: translate(-50%, -50%);
display: flex; align-items: center; gap: 7px;
padding: 5px 10px 5px 5px; background: rgba(255, 255, 255, .96);
border: 1px solid var(--border); border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, .25);
cursor: pointer; pointer-events: auto;
width: max-content; max-width: 190px;
}
.home-card:hover { border-color: var(--accent); }
.home-card-img {
width: 42px; height: 42px; border-radius: 7px; overflow: hidden; flex: none;
background: var(--bg-soft); display: flex; align-items: center; justify-content: center;
font-size: 20px;
}
.home-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.home-card-body { min-width: 0; }
.home-card-title {
font-size: 12.5px; font-weight: 600; color: var(--text); line-height: 1.35;
max-width: 122px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.home-card-sub {
font-size: 11px; color: var(--text-soft); line-height: 1.4; margin-top: 1px;
max-width: 122px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 列表:每行一条 —— 左边封面、右边标题/简述/参与人/摘要 */
.act-list { display: flex; flex-direction: column; gap: 12px; }
.act-row {
display: grid; grid-template-columns: 176px 1fr; gap: 16px;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 12px;
transition: box-shadow .15s, border-color .15s;
}
.act-row:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
/* 同上:列表行的封面也按 16:9(原来是 4/3,会把横向构图上下切掉) */
.act-row-cover {
position: relative; aspect-ratio: 16 / 9; border-radius: var(--radius-sm, 8px);
overflow: hidden; background: var(--bg-soft);
}
.act-row-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.act-row-cover-ph {
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
font-size: 26px; color: var(--text-mute);
}
.act-row-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.act-row-body h3 { margin: 0; font-size: 16px; }
.act-row-desc {
margin: 0; font-size: 13.5px; line-height: 1.5;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
overflow: hidden;
}
.act-row-members { display: flex; flex-wrap: wrap; gap: 6px; }
.act-member {
display: inline-flex; align-items: center; gap: 5px;
padding: 2px 9px 2px 2px; border-radius: 999px; background: var(--bg-soft);
font-size: 12.5px; color: var(--text-soft); max-width: 150px;
}
.act-member img { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; display: block; }
.act-member-initial {
width: 20px; height: 20px; border-radius: 50%; background: var(--accent); color: #fff;
font-size: 11px; display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.act-member-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.act-member-more { font-size: 12.5px; color: var(--text-mute); align-self: center; }
.act-row-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; }
/* 批 21:详情页顶部复用列表行样式展示「小封面 + 参与人/统计」。
它不是链接(详情页就在这儿),压掉 hover 反馈、留出与标题区的间距。 */
.act-row-static { margin-bottom: 20px; cursor: default; }
.act-row-static:hover { border-color: var(--border); box-shadow: none; }
@media (max-width: 560px) {
.act-row { grid-template-columns: 104px 1fr; gap: 12px; padding: 10px; }
}
/* ── 23. 地图尺寸(批 21 调整)──────────────────────────────────────────── */
/* 行程路线 / 旅行地图:**与正文版心同宽**。
批 18 曾让它突破版心铺满整个视口(JS 负 margin 出血),用户反馈
「上面正文固定宽、下面地图铺满屏」看着奇怪,批 21 改回一致 ——
宽度跟随 .container 的内容区,模板里的 bleed() 已删,这里也不再有
border-radius/border 的去边框覆盖(恢复 .route-map 默认圆角与描边)。
高度仍跟着视口高度走 —— 屏幕越大容器越大,地图显示的地理范围也越大。 */
#home-panel-map .route-map,
#route-panel-map .route-map {
height: clamp(460px, calc(100vh - 190px), 1000px);
}