Files
FeedLite/assets/style.css
T
laoyang 27614d8ca8 统一全部表单控件尺寸与外观;删除下载地址字段
用户:「输入框、按钮、下拉框特别丑,一点设计感都没有,高度太随意,
      全面检查一下。还有那个下载链接要它干什么?」

一、表单控件(这是主要工作量)
  原来样式散在 6 处(.field / .filter-row / .filters / .mail-test / .admin 各一套),
  padding 有 8/9/12px 三种,select 与 input 高度不等,并排放参差不齐。

  改成一套体系:
  - CSS 变量统一尺度:--ctl-h 38px / --ctl-h-sm 32px / --ctl-h-lg 42px,
    统一圆角 9px、统一左右内边距 12px、统一边框与阴影
  - 高度用「固定 height + line-height 居中」而不是 padding 撑
  - 🔴 选择器必须**宽松**(按 type 通吃,不用 .field 之类限定)。
     第一版限制了容器,导致后台零散布局里的裸 select/input 只有 19/21px,
     和旁边 38px 按钮并排,正是用户说的高度随意。
     放开后实测:19px → 38px,全部一致
  - 下拉框自绘箭头(内联 SVG),不再用各平台不一的原生箭头,右侧留 34px
  - 搜索框加左侧放大镜图标,并去掉 Chrome 自带清除按钮
  - 聚焦统一 brand 光晕,悬停边框加深
  - 按钮也改固定 height(原来 padding 撑出 38.3px,与输入框差 0.3px)
  - .btn.sm 必须显式改 height,否则 padding 被 height 吃掉、小按钮还是 38px

  文件选择按钮:原生控件重做成真按钮
    <label class=file-btn> 包住 input[type=file]
    input 用 clip 视觉隐藏(不能用 display:none,那样点不到也聚焦不了)
    按钮内显示「选择图片」+ 图标 + 选中后的文件名

二、删除「下载地址」字段
  它唯一用途:状态改为「已解决」时把邮件按钮换成「下载最新版本」。
  收益小、维护成本高(每个软件都要填一条 URL)。
  - 后台表单项删除
  - mailer 里对应的判断分支也删掉,按钮统一「查看详情」
  - ⚠️ 数据库 download_url 列**保留不删**:删列不可逆,万一要恢复还得迁一次

验证(用控件总览测试页 + 真实页面实测):
  10 类控件高度全部 38px(文本/数字/下拉/搜索/文件按钮/主按钮/小按钮…),
  文本域 110px,同一行垂直中心完全一致(935.3)
  裸 select/input 从 19/21px 修正到 38px
  下拉箭头 SVG 生效、右内边距 34px;搜索框放大镜生效、左内边距 36px
  真实三个页面(提交页/列表页/详情页)逐一检查:
    没有 checkbox/radio/file 被误改,所有可见控件高度收敛为 38(文本域 110)
  下载地址字段:admin.php 输出与源码中均无 download_url 表单项
  5 个路由 200
2026-11-04 19:17:20 +08:00

1199 lines
57 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.
/* FeedLite · 轻反馈 —— 简洁、克制的基础视觉基调 */
:root{
--fg:#1f2329; --muted:#6b7280; --line:#e5e7eb; --line-strong:#d7dbe0;
--bg:#f5f6f8; --card:#ffffff; --brand:#0057B8; --brand-600:#004a9e; --brand-soft:#e8f0f9;
/* 顶栏品牌图标:与官网首页同一枚应用图标(内联,离线预览也能显示) */
--icon-mark:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAADsOAAA7DgHMtqGDAAADj0lEQVR4nO1b205TQRTdGB/UB78B0BdfRNA3NUT/QPHMHIgaP0BeMPqoog8mJqDyCfoBImJnDuCFyEWhIGiUegONimkFodYEaqpj9qEEwZbS0zOXXlaykqZNO2uvM9M9Z8/ZABkhysBy9gLhl4Dwh0B5CAj7CZQLo4iaCJtwNRLWDITVZI4tY+CMAOGvtQfnnSGwnWNuLFmBBnYAZU8NCMAvDoLlVGwseCtwAAgLGyDaZ7IZsJ3D6wdP+CEg7Jd+sdIYBytQm+bKOxVAecQAkfJngsV3rolelBXYms/EgdV/jJRTA0SpJeF1K1c/v1OdRwPYxFL8dtc+7WJ00Q5U4/S/rF2INrKLkNzeiqIkYfcBCHujXYg+hgAoixkgRBNZDHL5gU22IxraxsXdkYiYnlsUid9/hGzgGDhWRzAi6tvGXQ05mQAev1jZ2CtGp6JCN0Ymo64WpQZUNvaKSDQuTEE4GhcVXk0AD9PehCu/FsH3UW/LAbL8Aq55U2HfGJNvQOdoRJiKO8GwfAO+zi0KU/Hl+6J8AxIKUp1XoDbpBqTCp9kFsftsf/aDe2TVuX7xeXYhpRYtBrR0TikLfpmt9z6YY8DQu3mx5XiXsuC3nuh2054xBiBwe9rzYkZ0P5dLHGO9P2JtBpiCkgG0NANEQaTBbSe7xdWOyfxYAi2S0uD2Uz35YcCQpDRoXRvLDwNkpMEnb+c9bbu1GWAKSgbQ0gwQUpdAIsW69JoGNzc4oq71mW+32Epuh6dT7MNzTYNY2fUDeIss3YCOYMTXNIjV3NhCwhcDbg8pKInVpymKekmDj17O+hY8gl5XUBTdZDu+TVk/MayqLA7JgxE8jDAFqKX8tKKDEfhn7aaryqi+8p5PhWgOBiwvBzyMwHo8lqRVHY7iWO3DYXfNazscBQ88cyuUMcCmmyFlekC1Acgr7env8fEz1XpA+YDJ88WP31bq+vga39OhBbQMmtwG72p67BJf69IB2gY2haBbgG4C5T+0i9BFwqNQlI/JrnpclrIHxf6gZLN2Ifp4Hg2oKd4ZwKuSzRI8VHzBs1crDRPYWqZbkGragSNrWmb4oHZRysj6/u8lpKy8aJqmsC8yTc9grdtapl2kNMaBsoMZeged/QXbOIl9kRuC5fYQDhRQ8H3uEs8OArvJ6tztovYAckh1lB2FnGEHqt0GI9w6uoaY2GXCYkvt86iRXwDL2bOR0P4Cy8Hrhv+SooQAAAAASUVORK5CYII=");
--ok:#0f9d58; --ok-soft:#e9f7ef; --warn:#b7791f; --warn-soft:#fff8e6;
--danger:#d93025; --danger-soft:#fdecea; --shadow:0 1px 3px rgba(16,24,40,.06);
/* ── 表单控件尺度(2026-11-04 统一)──
* 用户:「输入框、下拉框高度太随意,一点设计感都没有。」
* 之前 padding 有 8px/9px/12px 三种,select 和 input 高度还不一样,
* 并排放在一起参差不齐。现在只保留三档,全部按高度对齐:
* --ctl-h-sm 32px 表格内 / 紧凑行
* --ctl-h 38px 常规表单(默认)
* --ctl-h-lg 42px 主要操作区
* 高度用固定值 + line-height 居中的方式,而不是靠 padding 撑,
* 这样 input / select / button 放同一行时基线必然一致。 */
--ctl-h-sm:32px; --ctl-h:38px; --ctl-h-lg:42px;
--ctl-r:9px; /* 统一圆角 */
--ctl-px:12px; /* 统一左右内边距 */
--ctl-bd:1px solid var(--line-strong);
--ctl-shadow:0 1px 2px rgba(16,24,40,.04);
--ctl-shadow-focus:0 0 0 3px var(--brand-soft);
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
margin:0; background:var(--bg); color:var(--fg);
font-family:-apple-system,BlinkMacSystemFont,"HarmonyOS Sans SC","HarmonyOS Sans","PingFang SC","Helvetica Neue","Microsoft YaHei",sans-serif;
font-size:15.5px; line-height:1.75; letter-spacing:.01em;
}
.wrap{max-width:880px; margin:0 auto; padding:0 16px}
a{color:var(--brand); text-decoration:none}
a:hover{text-decoration:underline}
h1{font-size:23px; margin:0 0 8px; font-weight:700}
h2{font-size:17px; margin:0 0 12px; font-weight:700}
p{margin:8px 0}
.muted{color:var(--muted)}
.small{font-size:13px}
.dot{color:var(--line-strong); padding:0 2px}
/* ---------- 顶栏(与官网首页同一套导航样式) / 页脚 ---------- */
.topbar{
position:sticky; top:0; z-index:20;
background:rgba(255,255,255,.82);
backdrop-filter:saturate(180%) blur(14px);
-webkit-backdrop-filter:saturate(180%) blur(14px);
border-bottom:1px solid var(--line);
/* 软件抽屉是 absolute + top:100%,需要它来做定位参照,否则会跑到页面最上面 */
}
.topbar .wrap{display:flex; align-items:center; gap:14px; min-height:66px; padding-top:9px; padding-bottom:9px; position:relative; box-sizing:border-box}
/* 顶栏品牌位:**左图标 + 右文字列**两栏
*
* [📅] 同步日历反馈系统 ← 文字列
* 优秀的鸿蒙CalDav同步客户端
*
* 🔴 用户 2026-11-03:「图标应该在标题和简介的正中间…
* 让标题和它下面的简介合起来和图标高度一样,两个对齐上下。」
* ⇒ 图标的垂直中线要与**两行文字整体**的中线重合,不是只对齐第一行。
*
* ⚠️ 关键在 align-items:center 作用在「图标 vs 文字列」这一层:
* 图标是 .brand-ic,文字列是 .brand-txt(内部再上下排 title/note)。
* 以前图标和标题被包在同一个 .sw-badge 里(单行结构),
* note 挂在它下面,图标天然只能对齐第一行 —— 那种结构做不到居中。
*/
.topbar .brand-mini{display:flex; flex-direction:row; align-items:center; gap:10px; font-weight:700; font-size:17px; color:var(--fg); min-width:0; line-height:1.25}
.topbar .brand-mini:hover{text-decoration:none; color:var(--fg)}
/* 图标:固定 30×30 的方框,自身内容居中,不参与文字行高计算 */
.topbar .brand-ic{
width:30px; height:30px; flex:none;
display:flex; align-items:center; justify-content:center;
border-radius:9px; overflow:hidden;
}
.topbar .brand-ic img{width:30px; height:30px; object-fit:contain; display:block; border-radius:9px}
.topbar .brand-ic-ch{
font-size:17px; line-height:1; color:var(--swc, var(--brand)); font-weight:700;
}
/* 文字列:上下两行,自己成一组 */
.topbar .brand-txt{display:flex; flex-direction:column; justify-content:center; gap:1px; min-width:0}
.topbar .brand-title{
font-size:17px; font-weight:700; letter-spacing:.2px; line-height:1.25;
color:var(--swc, var(--brand));
overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.topbar .brand-mini-note{
font-size:12.5px; font-weight:400; color:var(--muted);
line-height:1.3;
overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
max-width:520px;
}
/* 顶栏里的软件标识:17px 字 + 32px 图标 ——
* 比「选择软件」标签(13.5px)大两号,又不至于压过内容区标题。 */
.topbar .brand-mini .sw-badge{gap:9px}
.topbar .brand-mini .sw-badge-nm{font-size:17px; font-weight:700; letter-spacing:.2px}
@media (max-width:560px){
.topbar .wrap{min-height:60px; padding-top:8px; padding-bottom:8px}
.topbar .brand-mini{font-size:15.5px; gap:8px}
.topbar .brand-ic{width:26px; height:26px}
.topbar .brand-ic img{width:26px; height:26px}
.topbar .brand-ic-ch{font-size:15px}
.topbar .brand-title{font-size:15.5px}
.topbar .brand-mini-note{font-size:11.5px; max-width:60vw}
.topbar .brand-mini .sw-badge-nm{font-size:15.5px}
.topbar .brand-mini .sw-badge-img{width:26px !important; height:26px !important}
.topbar .brand-mini .sw-badge{gap:7px}
}
/* ---------- 右上角三横线按钮 + 下拉菜单 ---------- */
.sw-toggle{
flex:none; width:38px; height:38px; padding:0; margin-left:2px;
display:inline-flex; flex-direction:column; align-items:center; justify-content:center; gap:4px;
background:transparent; border:0; border-radius:9px;
cursor:pointer;
}
.sw-toggle span{
display:block; width:18px; height:2px; border-radius:2px;
background:var(--fg);
}
.sw-toggle:hover{background:var(--brand-soft)}
.sw-toggle[aria-expanded="true"]{background:var(--brand-soft)}
/* ---------- 软件下拉菜单(右上角三横线) ----------
* 参考家书 FamilyBook 顶栏那个 popup 的做法:**内容多高就多高的小浮窗**,
* 紧贴按钮下方、宽度由内容撑开,不是铺满整屏的面板,也不加遮罩。
* 用户 2026-11-03:「不用做那么复杂,就是点了之后拉下来一个很窄的框,
* 把所有的内容都铺在上面就可以了」。
*
* ⚠️ 用过两版都不对:
* ① 宽屏横排 / 窄屏竖排两套 —— 手机上和正文撞在一起
* ② 整幅 absolute 面板 + 遮罩 —— 太重了,像侧边栏而不是下拉菜单
* 现在只有这一种形态:小浮窗,贴按钮右侧对齐。 */
.sw-panel{
position:absolute; top:calc(100% - 4px); right:12px;
z-index:60;
min-width:172px; max-width:min(280px, calc(100vw - 24px));
padding:6px;
background:var(--card);
border:1px solid var(--line);
border-radius:12px;
box-shadow:0 10px 28px -8px rgba(20,30,50,.28), 0 2px 6px rgba(20,30,50,.08);
max-height:calc(100vh - 90px);
overflow:auto;
}
/* 菜单里不再需要「选择软件」这个标题 —— 按钮本身已经说明用途 */
.sw-panel-hd{display:none}
.sw-panel .wrap{padding:0; max-width:none}
/* 一条一行,竖排 */
.sw-panel-list{display:flex; flex-direction:column; gap:1px}
.sw-item{
display:flex; align-items:center; gap:8px;
padding:8px 10px; border-radius:8px; color:var(--fg);
font-size:14px; white-space:nowrap;
border:0;
}
.sw-item:hover{background:var(--brand-soft); text-decoration:none; color:var(--fg)}
.sw-item.on{background:var(--brand-soft)}
.sw-item.on .sw-badge-nm{font-weight:700}
.sw-item .sw-badge{gap:7px}
.sw-item .sw-badge-nm{font-size:14px; font-weight:500}
.sw-item .sw-badge-img{width:18px !important; height:18px !important}
/* 「当前」标记缩成一个小圆点,别在窄框里占地方 */
.sw-item-cur{
margin-left:auto;
width:6px; height:6px; flex:none; border-radius:50%;
background:var(--brand);
font-size:0; line-height:0; overflow:hidden; text-indent:-999px;
}
.topbar .brand-text{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.topbar .logo-mark{
width:30px; height:30px; flex:none; display:block; border-radius:8px;
background-image:var(--icon-mark); background-size:cover; background-position:center;
box-shadow:var(--shadow);
}
.topbar .spacer{flex:1}
.topbar .navlink{color:var(--muted); font-size:14.5px; padding:8px 10px; border-radius:8px; white-space:nowrap}
.topbar .navlink:hover{color:var(--brand); background:var(--brand-soft); text-decoration:none}
.topbar .btn-mini{
flex:none; font-size:14.5px; font-weight:600; color:#fff; background:var(--brand);
padding:9px 18px; border-radius:999px; white-space:nowrap;
box-shadow:0 4px 14px -4px rgba(0,87,184,.55);
}
.topbar .btn-mini:hover{background:var(--brand-600); color:#fff; text-decoration:none}
.hide-sm{display:inline-block}
@media (max-width:640px){
.hide-sm{display:none}
.topbar .wrap{gap:10px}
.topbar .brand-mini{font-size:15.5px; gap:9px}
.topbar .logo-mark{width:28px; height:28px}
}
main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
.foot{border-top:1px solid var(--line); background:var(--card); padding:22px 0 34px; margin-top:40px}
.foot p{margin:4px 0; text-align:center; font-size:13px; color:var(--muted)}
.foot a{color:var(--muted); text-decoration:underline}
/* 版权句 + 管理后台入口:同一行居中,窄屏自动换行 */
.foot-line{display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:0 6px}
.foot-line .dot{padding:0}
/* ---------- 按钮 ---------- */
/* 按钮高度与输入框严格一致(--ctl-h)。
* 🔴 原来靠 padding:8px + line-height:1.4 撑,算出来 38.3px,
* 而输入框是固定 38px —— 并排时差 0.3px,放大看会有轻微的错位感。
* 改成固定 height + inline-flex 垂直居中,与控件完全同高。 */
.btn{
display:inline-flex; align-items:center; justify-content:center;
height:var(--ctl-h); padding:0 16px;
border:1px solid var(--line-strong); background:var(--card);
color:var(--fg); border-radius:var(--ctl-r);
font-size:14.5px; line-height:1;
cursor:pointer; font-family:inherit; white-space:nowrap;
box-shadow:var(--ctl-shadow);
transition:border-color .12s, background .12s, color .12s;
}
.btn:hover{border-color:var(--brand); color:var(--brand); text-decoration:none}
.btn.primary{background:var(--brand); border-color:var(--brand); color:#fff; font-weight:600}
.btn.primary:hover{background:var(--brand-600); color:#fff}
/* ---------- 页头 ---------- */
.page-head{display:flex; align-items:flex-start; gap:16px; margin:6px 0 18px}
.page-head .btn{margin-left:auto; flex:none; margin-top:4px}
/* ---------- 首页:轻反馈介绍页 ----------
* 用户 2026-11-03:首页不列问题,只做介绍 + 指路。
* 因为访客总是从某个软件过来的,他要的是「怎么进自己那个软件的反馈」,
* 而不是看别人的问题列表。 */
.home{max-width:820px; margin:0 auto; padding:8px 0 40px}
.home-hero{text-align:center; padding:26px 0 30px}
.home-hero h1{
font-size:30px; line-height:1.3; margin:0 0 12px;
letter-spacing:-.4px;
}
.home-lead{
font-size:15.5px; line-height:1.75; color:var(--muted);
margin:0 auto; max-width:600px;
}
.home-feats{
display:grid; grid-template-columns:repeat(2, minmax(0,1fr));
gap:12px; margin:0 0 34px;
}
@media (max-width:600px){ .home-feats{grid-template-columns:1fr} }
.home-feat{
background:var(--card); border:1px solid var(--line);
border-radius:12px; padding:16px 18px;
}
.home-feat-ic{font-size:20px; line-height:1; margin-bottom:8px}
.home-feat h3{margin:0 0 6px; font-size:15px; font-weight:700}
.home-feat p{margin:0; font-size:13.5px; line-height:1.7; color:var(--muted)}
.home-enter{
background:var(--card); border:1px solid var(--line);
border-radius:14px; padding:22px 24px;
}
.home-enter h2{margin:0 0 10px; font-size:17px}
.home-enter-tip{
font-size:14px; color:var(--muted); line-height:1.8; margin:0 0 16px;
display:flex; align-items:center; flex-wrap:wrap; gap:6px;
}
.home-enter-tip b{color:var(--fg); font-weight:600}
/* 小号三横线示意,让「右上角那个设置按钮」有具体形象可对照 */
.home-menu-demo{
display:inline-flex; flex-direction:column; justify-content:center; gap:2.5px;
width:22px; height:22px; padding:4px; box-sizing:border-box;
border:1px solid var(--line-strong); border-radius:6px;
background:var(--bg, #f6f8fb);
}
.home-menu-demo i{display:block; width:100%; height:1.5px; background:var(--fg); border-radius:2px}
.home-sw-list{display:flex; flex-direction:column; gap:8px}
.home-sw{
display:flex; align-items:center; gap:12px;
padding:11px 14px; border-radius:11px;
border:1px solid var(--line); background:var(--card);
color:var(--fg);
}
.home-sw:hover{
text-decoration:none; color:var(--fg);
border-color:var(--swc); background:var(--bg, #f6f8fb);
}
.home-sw-ic{
width:36px; height:36px; flex:none; border-radius:9px;
display:flex; align-items:center; justify-content:center;
font-size:22px; line-height:1;
}
.home-sw-ic img{width:36px; height:36px; object-fit:contain; display:block}
.home-sw-nm{font-size:15px; font-weight:600; color:var(--swc)}
.home-sw-go{margin-left:auto; font-size:13px; color:var(--muted); flex:none}
.home-sw:hover .home-sw-go{color:var(--swc)}
.home-enter-alt{margin:12px 0 0}
.page-head h1{margin-bottom:4px}
.page-head .muted{font-size:14px; margin:0}
/* ---------- 后台处理按钮栏 ---------- */
.act-bar{
margin:0 0 18px; padding:14px 16px;
background:var(--card); border:1px solid var(--line); border-radius:12px;
}
.act-bar-title{
font-size:12.5px; font-weight:700; letter-spacing:.5px;
color:var(--muted); text-transform:uppercase; margin-bottom:10px;
}
.act-bar-body{display:flex; flex-wrap:wrap; gap:8px; align-items:center}
.act-inline{display:inline}
.act-btn{white-space:nowrap}
.act-btn.is-cur{
opacity:.55; cursor:default;
box-shadow:none;
}
.act-btn.ok{border-color:#b7dfc2; color:#1e7b3c}
.act-btn.ok:hover:not(:disabled){background:#f0f9f2}
.act-btn.warn{border-color:#f0dfae; color:#8a6a15}
.act-btn.warn:hover:not(:disabled){background:#fff9ec}
.act-state.on{
display:inline-block; padding:6px 12px; border-radius:99px;
font-size:12.5px; font-weight:600;
background:#f0f9f2; color:#1e7b3c; border:1px solid #b7dfc2;
}
.act-hint{
margin-left:auto; font-size:12.5px; color:var(--muted);
}
@media (max-width:560px){
.act-hint{margin-left:0; flex-basis:100%}
}
.act-note{
margin-top:14px; padding-top:14px; border-top:1px dashed var(--line);
}
/* ---------- 软件标识:图标 + 名字(多软件区分的主视觉) ----------
* 🔴 一律不要背景色:图标本身就是一张图/一个 emoji,再垫一个色块会糊在一起,
* 反而看不清。直接「图标 + 文字」并排是识别度最高的做法。
* 邮件模板里有等价的 table 写法,两边观感要对得上。 */
.sw-badge{display:inline-flex; align-items:center; gap:7px; min-width:0; vertical-align:middle}
.sw-badge-ic{
display:inline-flex; align-items:center; justify-content:center;
flex:none; line-height:1; letter-spacing:0;
background:none; border:0; box-shadow:none;
}
.sw-badge-nm{
font-weight:600; font-size:14px; color:var(--fg);
overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.sw-badge-sm .sw-badge-nm{font-size:14px}
.sw-badge-md .sw-badge-nm{font-size:16px; font-weight:600}
/* 内容区:只是软件标识,字号刻意小于顶栏标题(17px),
* 避免「上面的字比下面的小」这种主次颠倒。颜色同样跟随软件主题色。 */
.sw-badge-lg .sw-badge-nm{font-size:15px; font-weight:600; color:var(--swc, var(--fg))}
/* 顶栏主标题:比「选择软件」标签(13.5px)大两号 = 17px,且大于内容区的 15px。
* 颜色跟随软件主题色(--swc 由 PHP 内联写到 .brand-mini 上),
* 让「同步日历」「密码生成器」这些标题一眼可辨。 */
.sw-badge-brand .sw-badge-nm{font-size:17px; font-weight:700; letter-spacing:.2px; color:var(--swc, var(--fg))}
/* 页头副行:图标 + 软件名 */
/* (已删).page-sw-id —— 内容区那行软件标识已移除,
* 软件名与简介统一由顶栏承担,避免与顶栏标题重复。 */
/* 提交页顶部的软件上下文条:同样无背景色,只留一行轻分隔 */
.sw-context{
display:flex; align-items:center; gap:10px; flex-wrap:wrap;
margin:0 0 16px; padding:0 0 12px; border-bottom:1px solid var(--line);
}
.sw-context .sw-badge-nm{color:var(--fg)}
/* 后台软件编辑:图标快选 */
.icon-presets{display:flex; gap:4px; flex-wrap:wrap; margin-top:8px}
.icon-preset{
width:34px; height:34px; border:1px solid var(--line-strong); border-radius:9px;
background:var(--card); cursor:pointer; font-size:17px; line-height:1; padding:0;
display:inline-flex; align-items:center; justify-content:center;
}
.icon-preset:hover{border-color:var(--brand); background:var(--brand-soft)}
/* ---------- 软件图标(上传图片) ---------- */
/* 图标图片:外框与字符版完全一致,换图不会让布局跳动 */
/* 图标图片:不留背景、不加圆角底衬,直接贴图 */
.sw-badge-img{
display:inline-block; object-fit:contain; flex:none;
background:none; border:0; box-shadow:none;
}
.chip.on .sw-badge-img{box-shadow:0 0 0 1px rgba(255,255,255,.5)}
/* 后台图标上传行 */
.icon-up{display:flex; align-items:flex-start; gap:14px; flex-wrap:wrap; margin-top:6px}
.icon-prev{
width:56px; height:56px; flex:none; border-radius:12px; overflow:hidden;
border:1px solid var(--line); background:var(--card);
display:flex; align-items:center; justify-content:center;
}
.icon-prev img{width:100%; height:100%; object-fit:cover; display:block}
.icon-prev-ph{
width:100%; height:100%; display:flex; align-items:center; justify-content:center;
color:#fff; font-size:24px; font-weight:600;
}
.icon-up-main{flex:1; min-width:220px; display:flex; flex-direction:column; align-items:flex-start}
/* ⚠️ 不要在这里给 input[type=file] 设任何样式 ——
* 它已被 .file-btn 视觉隐藏(clip),设 max-width/font-size 之类
* 会把它从"视觉隐藏"变成"露出一个小方块",反而破坏新按钮外观。 */
/* 上传状态提示:选中文件后立刻告诉用户「选到了什么、合不合格」。
* 用户 2026-11-04:「上传的图标没有显示,也没有成功/失败提示。」 */
.icon-up-tip{
display:block; margin-top:7px; font-size:12.5px; line-height:1.6;
color:var(--muted); word-break:break-all;
}
.icon-up-tip.is-ok{color:var(--ok, #16794a)}
.icon-up-tip.is-err{color:var(--danger, #c0392b); font-weight:600}
/* chip 里也不用白底反色了 —— 图标本来就没有底色 */
.chip.on .sw-badge-nm{color:#fff}
.chip .sw-badge-ic{background:none !important; color:inherit !important; box-shadow:none}
/* ---------- 主题色选择器 ---------- */
.sw-dot{
display:inline-block; width:10px; height:10px; border-radius:50%;
flex:none; margin-right:2px; vertical-align:middle;
box-shadow:0 0 0 1px rgba(0,0,0,.12);
}
.color-row{display:flex; align-items:center; gap:10px; flex-wrap:wrap; position:relative}
.color-input{
width:46px; height:34px; padding:2px; border:1px solid var(--line-strong);
border-radius:9px; background:var(--card); cursor:pointer; flex:none;
}
/* ---------- 自绘色板 ----------
* 用户 2026-11-04:「需要一个完整的色板,右侧那些默认颜色就不要了。」
*
* 🔴 色板必须 position:fixed —— 后台这个表单在 <details class="tax-form-box">
* 里面,而它有 overflow:hidden。用 absolute 会被裁掉下半部分,
* 连带色相条一起看不见,用户就以为"只有蓝色"(只能看到当前色相)。
* fixed 直接相对视口定位,任何祖先的 overflow 都管不到它。
* 位置由 JS 按触发按钮的坐标算出来写进 style。 */
.color-trigger{
display:inline-flex; align-items:center; gap:9px;
padding:5px 12px 5px 6px; height:34px;
border:1px solid var(--line-strong); border-radius:9px;
background:var(--card); cursor:pointer; flex:none;
font-size:13px; font-family:ui-monospace,Menlo,Consolas,monospace;
color:var(--fg);
}
.color-trigger:hover{border-color:var(--brand)}
.color-trigger-chip{
width:22px; height:22px; border-radius:6px; flex:none;
background:var(--cur, #0057B8);
box-shadow:inset 0 0 0 1px rgba(0,0,0,.10);
}
.color-trigger-hex{font-weight:600; letter-spacing:.2px}
.color-trigger-caret{font-size:10px; color:var(--muted); margin-left:1px}
.cp{
position:fixed; z-index:9999;
width:372px; padding:14px;
background:var(--card); border:1px solid var(--line-strong);
border-radius:14px; box-shadow:0 12px 36px rgba(16,24,40,.24);
user-select:none; box-sizing:border-box;
}
.cp-hd{
font-size:12.5px; font-weight:700; color:var(--muted);
letter-spacing:.3px; margin:0 0 10px;
}
/* 画布容器:相对定位,光标挂在这里面,坐标计算就不用管面板 padding */
.cp-sv-wrap{position:relative; border-radius:9px; overflow:hidden; line-height:0}
.cp-sv{
display:block; width:100%; height:200px; border-radius:9px;
cursor:crosshair;
}
.cp-cursor{
position:absolute; width:15px; height:15px; margin:-7.5px 0 0 -7.5px;
border:2px solid #fff; border-radius:50%; pointer-events:none;
box-shadow:0 0 0 1px rgba(0,0,0,.4), 0 1px 3px rgba(0,0,0,.3);
}
/* 两条滑条:色相 + 明度
* 🔴 色相条必须**看得出是光谱** —— 用户反馈「好像只有蓝色」,
* 是因为之前色相条被 overflow 裁掉了,只看到上面那片蓝色的 HSV 板。
* 所以这里把色相条做高(18px)、加圆角描边,一眼就知道"能选所有颜色"。 */
.cp-slider{margin:12px 0 0}
.cp-slider input[type=range]{
-webkit-appearance:none; appearance:none; width:100%; height:18px; border-radius:9px;
outline:none; cursor:pointer; display:block;
box-shadow:inset 0 0 0 1px rgba(0,0,0,.10);
}
.cp-hue input[type=range]{
background:linear-gradient(to right,
#f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
.cp-val input[type=range]{background:linear-gradient(to right, #000, #fff)}
.cp-slider input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none; width:20px; height:20px; border-radius:50%;
background:#fff; border:2px solid #fff;
box-shadow:0 0 0 1px rgba(0,0,0,.45), 0 1px 4px rgba(0,0,0,.3); cursor:pointer;
}
.cp-slider input[type=range]::-moz-range-thumb{
width:18px; height:18px; border-radius:50%; background:#fff;
border:2px solid #fff; box-shadow:0 0 0 1px rgba(0,0,0,.45); cursor:pointer;
}
.cp-foot{display:flex; align-items:center; gap:8px; margin-top:13px}
.cp-preview{
width:30px; height:30px; border-radius:8px; flex:none;
box-shadow:inset 0 0 0 1px rgba(0,0,0,.12);
}
.cp-hex{
flex:1; min-width:0; height:30px; padding:0 9px;
border:1px solid var(--line-strong); border-radius:8px;
font-family:ui-monospace,Menlo,Consolas,monospace; font-size:13px;
text-transform:uppercase; background:var(--card); color:var(--fg);
}
.cp-ok{flex:none; height:32px !important; padding:0 14px !important; font-size:13px !important}
.color-presets{display:flex; gap:6px; flex-wrap:wrap}
.color-preset{
width:26px; height:26px; border-radius:50%; border:2px solid transparent;
cursor:pointer; padding:0; flex:none; transition:transform .12s;
}
.color-preset:hover{transform:scale(1.12)}
.color-preset.on{border-color:var(--fg); box-shadow:0 0 0 2px var(--card) inset}
/* ---------- 邮件测试 ---------- */
.mail-test{
display:flex; flex-wrap:wrap; align-items:center; gap:10px;
padding:12px 14px; margin:6px 0 16px;
background:var(--card); border:1px dashed var(--line); border-radius:11px;
}
.mail-test input[type=email]{
flex:1 1 220px; min-width:180px;
/* 边框/高度/圆角统一在「统一表单控件」段里定义,这里只管宽度 */
}
.mail-test-out{flex:1 1 100%; font-size:13px; line-height:1.6}
.mail-test-out.ok{color:var(--ok)}
.mail-test-out.err{color:#c9302c}
/* RAM 角色探测结果 */
.role-ok{
padding:8px 11px; border-radius:9px; font-size:13.5px;
background:var(--ok-soft, #eef8f0); border:1px solid #cfe8d5;
}
.role-warn{
padding:8px 11px; border-radius:9px; font-size:13.5px;
background:#fff8e6; border:1px solid #f0e0b0; color:#8a6d1f;
}
/* ---------- 表单就地校验的错误提示 ---------- */
.field.has-error > input,
.field.has-error > textarea,
.field.has-error > select,
.field.has-error .chips{
border-color:#d9534f !important;
box-shadow:0 0 0 3px rgba(217,83,79,.12) !important;
}
.field-error{
display:block; margin-top:6px; font-style:normal;
font-size:12.5px; color:#c9302c; line-height:1.5;
}
.field-error::before{content:"⚠ "; font-style:normal}
/* ---------- 后台:软件 / 分类 / 状态 ---------- */
.card-list{display:flex; flex-direction:column; gap:8px; margin-bottom:16px}
.tax-row{
display:flex; align-items:flex-start; gap:12px; flex-wrap:wrap;
padding:12px 14px; background:var(--card); border:1px solid var(--line);
border-radius:11px;
}
.tax-row.on{border-color:var(--brand); box-shadow:0 0 0 1px var(--brand) inset}
.tax-row.off{opacity:.62}
.tax-main{flex:1 1 320px; min-width:0}
.tax-name{font-weight:600; font-size:15.5px; text-decoration:none; color:var(--fg)}
.tax-name:hover{color:var(--brand)}
.tax-name.plain{font-weight:600}
.tax-slug{
display:inline-block; margin-left:8px; font-size:12px; color:var(--muted);
font-family:ui-monospace,Consolas,monospace;
}
.tax-meta{margin-left:10px; font-size:12.5px; color:var(--muted)}
.tax-note{margin-top:5px; font-size:13px; color:var(--muted); line-height:1.5}
.tax-acts{display:flex; flex-wrap:wrap; gap:6px; align-items:center}
.inline-form{display:inline}
/* 小号按钮:基类是固定 height,这里必须显式改 height 而不是改 padding,
* 否则 padding 会被 height 吃掉、按钮还是 38px 高。 */
.btn.sm{height:var(--ctl-h-sm); padding:0 11px; font-size:13px; border-radius:8px}
.btn.danger{border-color:#e0b4b4; color:#b4242c}
.btn.danger:hover{background:#fdf2f2; border-color:#b4242c}
.tax-form-box{
margin:18px 0 8px; border:1px solid var(--line); border-radius:11px;
background:var(--card); overflow:hidden;
}
.tax-form-box>summary{
cursor:pointer; padding:12px 14px; font-weight:600; font-size:14.5px;
background:var(--bg-soft, #f7f8fa); user-select:none;
}
.tax-form-box>summary:hover{color:var(--brand)}
.tax-form-box[open]>summary{border-bottom:1px solid var(--line)}
.sm-form{padding:14px; margin:0}
.sm-form .field{margin-bottom:10px}
/* ---------- 软件切换(一级) ---------- */
.software-bar{
display:flex; flex-wrap:wrap; align-items:center; gap:8px;
margin-bottom:14px; padding-bottom:14px; border-bottom:1px solid var(--line);
}
.software-bar .sw-label{font-size:13.5px; color:var(--muted); font-weight:600}
.software-bar .chips{margin-bottom:0}
/* ---------- 筛选(二级:分类 / 状态) ---------- */
.filters{margin-bottom:16px}
.chips{display:flex; flex-wrap:wrap; gap:8px; margin-bottom:12px}
.chip{
padding:5px 13px; border-radius:999px; background:var(--card);
border:1px solid var(--line); font-size:13.5px; color:var(--fg);
}
.chip:hover{border-color:var(--brand); color:var(--brand); text-decoration:none}
.chip.on{background:var(--brand); border-color:var(--brand); color:#fff; font-weight:600}
.filter-row{display:flex; flex-wrap:wrap; gap:9px; align-items:center}
/* 控件的具体样式统一在上面的「统一表单控件」段里定义(避免多处覆盖打架) */
/* ---------- 列表 ---------- */
.list{display:flex; flex-direction:column; gap:12px}
.item{
display:block; background:var(--card); border:1px solid var(--line);
border-radius:13px; padding:15px 17px; box-shadow:var(--shadow); color:inherit;
}
.item:hover{border-color:var(--brand); text-decoration:none; box-shadow:0 3px 12px rgba(0,87,184,.10)}
/* 列表项:
* 行1 = 标题(顶头、单独一行)
* 行2 = 分类 / 状态 标签
* 行3 = 摘要、缩略图、作者信息
*
* 🔴 用户 2026-11-03:标签别放在标题前面。
* 以前 badges 和标题挤在同一个 flex 行里,标签占掉左边一大截,
* 标题被推到右边、长度不够就换行,很难看。
* 现在标题独占一行、从左边缘开始,标签另起一行。 */
/* ⚠️ .item-top 仍被 admin.php 的三处内部列表使用(后台待审核 / 详情头 / 操作日志)。
* 前台列表已改用 .item-head + .item-tags(标题独占一行),这里保留供后台用。 */
.item-top{display:flex; align-items:center; flex-wrap:wrap; gap:8px}
.item-head{display:flex; align-items:flex-start; gap:8px}
.item-title{font-weight:600; font-size:16px; flex:1; min-width:0; line-height:1.45; word-break:break-word}
.item-tags{display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:7px}
.item-ex{color:var(--muted); font-size:14px; margin:8px 0 8px; word-break:break-word}
.item-meta{display:flex; flex-wrap:wrap; align-items:center; gap:6px; font-size:13px; color:var(--muted)}
.item-meta .who{color:#3a424d; font-weight:600}
/* 带 token 回复时展示只读身份(昵称 + 邮箱) */
.reply-identity{
display:flex; align-items:center; flex-wrap:wrap; gap:8px;
padding:9px 12px; border-radius:9px;
background:var(--bg, #f6f8fb); border:1px solid var(--line);
font-size:14px;
}
.reply-identity-nm{font-weight:600; color:var(--fg)}
.reply-identity-mail{
color:var(--muted); font-size:13px;
padding:1px 8px; border-radius:99px; background:var(--card);
border:1px solid var(--line);
}
/* 提交人凭 token 看自己未公开反馈时的提示条 */
.owner-note{
display:flex; flex-direction:column; gap:4px;
margin:0 0 16px; padding:12px 14px;
background:var(--warn-soft, #fff8e6);
border:1px solid #f0d9a8; border-left:3px solid #d99b1f;
border-radius:10px; font-size:13.5px; line-height:1.65;
color:#6b5313;
}
.owner-note b{font-size:14px; color:#5a4610}
/* ---------- 后台「待审核」列表 ----------
* 列表式:一屏能看多条,右侧固定一个「通过并公开」。
* 详情/关闭说明留在管理页里填,列表页不塞表单。 */
.plist{display:flex; flex-direction:column; gap:10px}
.prow{
display:flex; align-items:flex-start; gap:16px;
background:var(--card); border:1px solid var(--line);
border-radius:12px; padding:13px 16px;
}
.prow:hover{border-color:var(--line-strong)}
.prow-main{flex:1; min-width:0}
/* 后台待审核条目:标题顶头独占一行 → 标签行 → 摘要 → 作者信息
* ⚠️ .prow-top 仍被「操作日志」那处复用(日志条目只有动作+编号,单行更合适),
* 所以这里保留它,另外新增 .prow-head / .prow-tags 给反馈条目用。 */
.prow-top{display:flex; align-items:center; flex-wrap:wrap; gap:8px}
.prow-head{display:flex; align-items:flex-start; gap:8px}
.prow-tags{display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:7px}
.prow-title{
font-weight:600; font-size:15.5px; color:var(--fg);
flex:1; min-width:0; word-break:break-word; line-height:1.45;
}
.prow-title:hover{color:var(--brand); text-decoration:underline}
.prow-ex{
color:var(--muted); font-size:13.5px; margin:6px 0;
word-break:break-word; display:-webkit-box; -webkit-line-clamp:2;
-webkit-box-orient:vertical; overflow:hidden;
}
.prow-meta{display:flex; flex-wrap:wrap; align-items:center; gap:6px; font-size:12.5px; color:var(--muted)}
.prow-meta .who{color:#3a424d; font-weight:600}
.prow-act{flex:none; padding-top:2px}
.prow-act .btn{white-space:nowrap}
@media (max-width:600px){
.prow{flex-direction:column; gap:10px}
.prow-act{align-self:flex-start}
}
/* 问题编号:便于按 id 逐条跟进与引用 */
.item-id{
flex:none; font-size:12.5px; font-weight:600; color:var(--muted);
font-variant-numeric:tabular-nums; letter-spacing:.02em;
border:1px solid var(--line); border-radius:6px; padding:0 7px; line-height:1.9;
}
/* ---------- 徽标 ---------- */
.badge{
display:inline-block; font-size:12px; line-height:1.6; padding:1px 9px;
border-radius:999px; background:var(--brand-soft); color:var(--brand);
font-weight:600; white-space:nowrap;
}
.badge.st{background:#f1f3f5; color:#5b636e}
.badge.st-open{background:var(--warn-soft); color:var(--warn)}
/* 「未公开」= 还没审过,用中性灰,不要和「已关闭」混淆 */
.badge.st-hidden{background:#eef0f3; color:#6b7684}
.badge.st-doing{background:var(--brand-soft); color:var(--brand)}
.badge.st-resolved{background:var(--ok-soft); color:var(--ok)}
.badge.st-closed{background:#f1f3f5; color:#8a929c}
.badge.official{background:var(--brand); color:#fff}
/* 软件徽章:比分类更"重",一眼看出属于哪个软件 */
/* 软件徽标:每个软件用自己的主题色 + 图标,方便一眼区分是哪个软件。
* 颜色由 PHP 内联写入(--swc / --swc-soft),这里只管形态。 */
.badge.sw{
display:inline-flex; align-items:center; gap:5px;
background:var(--swc-soft, #eef2f8); color:var(--swc, #2b4a74);
/* ⚠️ 不要加 border —— 用户 2026-11-03 指出「两个标签中间加个线干嘛」。
* 分类/状态标签都没有边框,只有软件标签有,并排显示时那道描边
* 看起来就像凭空插了一条分隔线。统一成无边框的胶囊。 */
font-weight:600;
padding:1px 9px 1px 6px;
}
.badge.sw .badge-sw-ic{
display:inline-flex; align-items:center; justify-content:center;
width:15px; height:15px; flex:none; line-height:1;
}
.badge.sw .badge-sw-ic img{width:15px; height:15px; display:block; object-fit:contain}
.badge.sw .badge-sw-nm{white-space:nowrap}
.badge.st-wontfix{background:#f6eef0; color:#a13c53}
/* ---------- 分页 ---------- */
.pager{display:flex; align-items:center; gap:12px; margin:22px 0 0; justify-content:center}
.pager-info{font-size:13.5px; color:var(--muted)}
/* ---------- 空态 ---------- */
.empty{
background:var(--card); border:1px dashed var(--line-strong); border-radius:13px;
padding:34px 20px; text-align:center; color:var(--muted);
}
.empty.small{padding:20px}
/* ---------- 面包屑 ---------- */
.crumbs{margin:4px 0 14px; font-size:14px}
/* ---------- 主题 / 回复 ---------- */
.subject{background:var(--card); border:1px solid var(--line); border-radius:14px; padding:20px 22px; box-shadow:var(--shadow)}
/* 详情页三段:标题(顶头独占一行)→ 标签行 → 作者信息行。
* 🔴 用户 2026-11-03:软件名/分类/状态这三个标签原来在标题前面,
* 把标题挤到右边去了。现在标题在最上面顶头,标签另起一行放下面。 */
.subject-head{display:flex; align-items:flex-start; gap:10px}
.subject-title{font-size:21px; margin:0; flex:1; min-width:0; line-height:1.4; word-break:break-word}
.subject-tags{display:flex; flex-wrap:wrap; align-items:center; gap:7px; margin:10px 0 0}
.subject-body{margin-top:14px; white-space:normal; word-break:break-word; line-height:1.85}
.subject-body br{content:""}
.replies{margin-top:22px}
.reply{
background:var(--card); border:1px solid var(--line); border-left:3px solid var(--line-strong);
border-radius:11px; padding:13px 16px; margin-bottom:11px;
}
.reply.admin{border-left-color:var(--brand); background:linear-gradient(180deg,var(--brand-soft),var(--card) 42%)}
.reply-head{display:flex; align-items:center; gap:8px; font-size:13px; color:var(--muted); margin-bottom:6px}
.reply-head .who{font-weight:600; color:#3a424d; font-size:14px}
.reply-body{word-break:break-word; line-height:1.8}
/* ---------- 表单 ---------- */
/* ══════════════════════════════════════════════════════════════
* 统一表单控件(2026-11-04 重做)
*
* 用户:「输入框、下拉框、选择图片那个按钮、排序输入框、前端筛选的
* 输入框下拉框,都特别丑,一点设计感都没有。高度太随意。」
*
* 之前的问题:
* ① 样式散落在 5-6 处(.field / .filter-row / .filters / .admin 各自一套)
* ② padding 有 8px / 9px / 12px 三种,select 与 input 高度不等,
* 并排放一起参差不齐
* ③ select 用浏览器默认箭头,各平台样式不一,还贴边
* ④ file input 完全是原始外观
*
* 现在:一套基类 .ctl + 统一变量,所有控件高度/圆角/边框/聚焦环一致。
* 高度用「固定 height + line-height 居中」而不是 padding 撑,
* 保证 input / select / button 同行时基线必然对齐。
* ══════════════════════════════════════════════════════════════ */
/* --- 基类:所有文本类控件 ---
* 🔴 选择器要**宽松**。之前限定在 .field / .filters 里面,
* 结果任何不在这些容器里的控件(后台零散布局里的 select、input)
* 都退化成浏览器默认外观,高度只有 19~21px,跟旁边 38px 的按钮并排
* 参差不齐 —— 这正是用户说的"高度太随意"。
* 现在按 type 通吃,再用 :not() 排除掉不该套的(checkbox/radio/color/file/range)。 */
input[type=text]:not(.cp-hex),
input[type=email],
input[type=tel],
input[type=password],
input[type=url],
input[type=number],
input[type=search],
select,
textarea:not(.cp-hex){
height:var(--ctl-h);
padding:0 var(--ctl-px);
border:var(--ctl-bd);
border-radius:var(--ctl-r);
background:var(--card);
color:var(--fg);
font-family:inherit;
font-size:14.5px;
line-height:calc(var(--ctl-h) - 2px); /* 减掉上下边框,垂直居中 */
box-shadow:var(--ctl-shadow);
transition:border-color .12s, box-shadow .12s;
-webkit-appearance:none;
appearance:none;
}
/* 文本域不能用固定高度 */
textarea{height:auto; min-height:110px; padding:9px var(--ctl-px); line-height:1.7; resize:vertical}
/* 数字框保留原生上下箭头(用户要改排序,有箭头更直观) */
input[type=number]{-moz-appearance:auto; appearance:auto; -webkit-appearance:auto}
/* --- 聚焦态:统一一圈柔和的 brand 光晕 --- */
input[type=text]:focus, input[type=email]:focus, input[type=tel]:focus,
input[type=password]:focus, input[type=url]:focus, input[type=number]:focus,
input[type=search]:focus, select:focus, textarea:focus{
outline:none;
border-color:var(--brand);
box-shadow:var(--ctl-shadow-focus);
}
/* --- 悬停:边框略深,给出可交互暗示 --- */
input[type=text]:hover:not(:focus), input[type=email]:hover:not(:focus),
input[type=tel]:hover:not(:focus), input[type=password]:hover:not(:focus),
input[type=url]:hover:not(:focus), input[type=number]:hover:not(:focus),
input[type=search]:hover:not(:focus), select:hover:not(:focus),
textarea:hover:not(:focus){
border-color:#c3c9d1;
}
/* --- 下拉框:自绘箭头 ---
* 原生箭头各平台差异大且贴边。这里用内联 SVG 画一个 12px 的 V 形,
* 右侧留 34px 空间,再给 padding-right 避开它。
* ⚠️ 背景必须同时写 background-color 和 background-image,
* 否则 -webkit-appearance:none 后底色会丢失。 */
.field select,
.filter-row select,
.filters select,
select{
padding-right:34px;
background-color:var(--card);
background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%236b7280' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat:no-repeat;
background-position:right 12px center;
cursor:pointer;
}
select:hover{
background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%231f2329' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* --- 搜索框:左侧放大镜图标 --- */
.filter-row input[type=search]{flex:1; min-width:170px; padding-left:36px;
background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Ccircle cx='6.2' cy='6.2' r='4.6' fill='none' stroke='%236b7280' stroke-width='1.5'/%3E%3Cpath d='M9.8 9.8 13.4 13.4' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat:no-repeat;
background-position:12px center;
}
/* 去掉 Chrome 自带的搜索框清除按钮(和我们的图标风格不搭) */
.filter-row input[type=search]::-webkit-search-decoration,
.filter-row input[type=search]::-webkit-search-cancel-button{
-webkit-appearance:none; appearance:none;
}
/* --- 文件选择按钮:把原生丑控件重做成一个真正的按钮 ---
* 用户:「选择图片那个按钮太丑。」
* 做法:隐藏原生 input(但不能 display:none,否则不可点/不可聚焦),
* 用 <label class="file-btn"> 包一个自绘按钮,点击穿透到 input。 */
.file-btn{
display:inline-flex; align-items:center; gap:9px;
height:var(--ctl-h); padding:0 14px 0 12px;
border:var(--ctl-bd); border-radius:var(--ctl-r);
background:var(--card); color:var(--fg);
font-size:14px; font-weight:600; cursor:pointer;
box-shadow:var(--ctl-shadow);
transition:border-color .12s, background .12s;
max-width:100%;
}
.file-btn:hover{border-color:var(--brand); background:var(--brand-soft); color:var(--brand)}
.file-btn:active{transform:translateY(.5px)}
.file-btn-ic{
width:17px; height:17px; flex:none;
background:currentColor;
-webkit-mask:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 3v10M6 9l4 4 4-4M4 16h12' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
mask:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 3v10M6 9l4 4 4-4M4 16h12' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
}
.file-btn-name{
font-weight:400; color:var(--muted); font-size:13px;
overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:190px;
}
/* 原生 input 视觉隐藏,但仍可点击/可聚焦(不能用 display:none) */
.file-btn input[type=file]{
position:absolute; width:1px; height:1px; opacity:0;
overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap;
}
/* --- 数字框 + 前后缀,让「排序」这种小输入看起来有设计感 --- */
.num-wrap{display:inline-flex; align-items:center; gap:0; position:relative}
.num-wrap .num-suffix{
position:absolute; right:12px; font-size:12.5px; color:var(--muted);
pointer-events:none;
}
.num-wrap input[type=number]{padding-right:44px}
/* ---------- 提交页容器 */
* 🔴 原来只有 max-width:640px,**没有居中** —— 在 880px 的内容区里,
* 表单贴左边、右侧空出 240px 白,看起来像"页面缺了一块"。
* 用户 2026-11-03 指出这一点。现在:加 margin 居中,宽度放到 720px,
* 既不至于像正文那么宽(长行难读),也不会显得抠掉一块。 */
.narrow{max-width:720px; margin-left:auto; margin-right:auto}
/* ---------- 提交成功页 ----------
* 用户 2026-11-03:提交完不要跳走,要给回执 + 凭据 + 后续去处。 */
.submitted{
background:var(--card); border:1px solid var(--line);
border-radius:16px; padding:30px 32px 26px; margin-top:14px;
box-shadow:var(--shadow);
text-align:center;
}
.submitted-ok{
width:52px; height:52px; margin:0 auto 14px; border-radius:50%;
background:var(--ok-soft, #e8f6ee); color:var(--ok, #16794a);
font-size:26px; line-height:52px; font-weight:700;
}
.submitted h1{font-size:22px; margin:0 0 10px}
.submitted-lead{
font-size:14px; line-height:1.75; color:var(--muted);
margin:0 auto 20px; max-width:520px;
}
.submitted-lead b{color:var(--fg)}
/* 凭据框:要显眼但不能吓人 —— 用中性浅底 + 等宽字,看起来像"码" */
.token-box{
text-align:left; margin:0 0 22px; padding:15px 16px;
background:var(--bg, #f6f8fb); border:1px solid var(--line);
border-radius:11px;
}
.token-box-hd{
font-size:12.5px; font-weight:700; color:var(--muted);
letter-spacing:.3px; margin-bottom:8px;
}
.token-code{
font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size:14px; font-weight:600; color:var(--fg);
padding:10px 12px; background:var(--card);
border:1px dashed var(--line-strong); border-radius:8px;
word-break:break-all; user-select:all; line-height:1.5;
}
.token-box-tip{
font-size:12.5px; line-height:1.7; color:var(--muted); margin:10px 0 0;
}
.token-box-warn{
font-size:12.5px; line-height:1.7; color:#8a5a10; margin:8px 0 0;
padding:7px 10px; background:#fff8e6; border-radius:6px;
}
.submitted-next{
font-size:13px; color:var(--muted); margin:0 0 10px; text-align:left;
}
.submitted-actions{
display:flex; flex-wrap:wrap; gap:8px; justify-content:flex-start;
margin:0 0 22px;
}
/* (已删)submitted-others / submitted-sw ——
* 成功页不再列其他软件的入口,跨软件只有顶栏三横线菜单一个入口。 */
.submitted-credit{
margin:18px 0 0; padding-top:16px; border-top:1px solid var(--line);
font-size:12.5px; color:var(--muted); text-align:left;
word-break:break-all;
}
@media (max-width:600px){
.submitted{padding:24px 18px 20px}
.submitted-actions .btn{flex:1 1 auto; text-align:center}
}
.card-form{background:var(--card); border:1px solid var(--line); border-radius:14px; padding:20px 22px; box-shadow:var(--shadow); margin-top:14px}
.field{display:block; margin-bottom:16px}
.field > span{display:block; font-size:14px; font-weight:600; margin-bottom:6px}
.field .req{color:var(--danger)}
.field .hint{font-style:normal; font-weight:400; font-size:12.5px; color:var(--muted); margin-left:6px}
/* 控件的边框/高度/圆角/聚焦态统一在上方「统一表单控件」段里定义。
* 这里只保留 .field 的宽度约束 —— 表单里的控件一律占满整行。 */
.field > input[type=text],
.field > input[type=email],
.field > input[type=tel],
.field > input[type=password],
.field > input[type=url],
.field > input[type=number],
.field > input[type=search],
.field > select,
.field > textarea{width:100%}
/* 短小的数字类字段(如「排序」)不该铺满整行 —— 收窄到 140px,
* 视觉上更像"一个小参数"而不是"一个大输入框"。 */
.field-narrow{max-width:140px}
.field-narrow > input{width:100% !important}
@media (max-width:560px){ .field-narrow{max-width:none} }
.row2{display:grid; grid-template-columns:1fr 1fr; gap:14px}
@media (max-width:560px){ .row2{grid-template-columns:1fr} }
.captcha{margin:18px 0 0; padding:14px 16px; background:#fafbfc; border:1px solid var(--line); border-radius:11px}
.captcha .field{margin-bottom:0}
.captcha-line{display:flex; align-items:center; gap:10px; flex-wrap:wrap}
.captcha-line .c-q{
flex:1; min-width:200px; font-weight:600; color:var(--brand);
background:var(--brand-soft); border-radius:8px; padding:8px 12px;
}
.captcha-line input{flex:none; width:110px}
.captcha-line .reload{font-size:13.5px; white-space:nowrap}
.form-actions{margin-top:20px}
.form-actions .btn{padding:10px 26px; font-size:15px}
.admin-status{margin-bottom:16px}
/* 蜜罐字段:对用户不可见,但保留在 DOM 中诱捕机器人 */
.hp-field{position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden}
/* ---------- 提示条 ---------- */
.flash{border-radius:10px; padding:11px 15px; font-size:14px; margin:0 0 16px}
.flash.error{background:var(--danger-soft); border:1px solid #f6c6c1; color:#9c2b21}
.flash.ok{background:var(--ok-soft); border:1px solid #bfe5cd; color:#0b6b3d}
/* ---------- 附件上传(浏览器直传 OSS) ---------- */
.uploader-box{
margin:18px 0 0; padding:14px 16px; background:#fafbfc;
border:1px solid var(--line); border-radius:11px;
}
.uploader{border-radius:10px; transition:background .15s, box-shadow .15s}
.uploader + .uploader{margin-top:14px; padding-top:14px; border-top:1px dashed var(--line)}
.uploader.dragging{background:var(--brand-soft); box-shadow:0 0 0 2px var(--brand) inset}
.up-head{display:flex; align-items:center; gap:10px; flex-wrap:wrap}
.up-title{font-size:14px; font-weight:600}
.up-head .btn{margin-left:auto; padding:6px 14px; font-size:13.5px; flex:none}
.up-file{display:none}
.up-list{display:flex; flex-wrap:wrap; gap:9px; margin-top:10px}
.up-list:empty{display:none}
.up-item{
position:relative; display:flex; gap:10px; align-items:center; width:100%;
padding:8px 10px; border:1px solid var(--line); border-radius:10px; background:var(--card);
}
.up-thumb{
width:52px; height:52px; flex:none; border-radius:8px; overflow:hidden; background:#eef1f4;
display:flex; align-items:center; justify-content:center;
}
.up-thumb img{width:100%; height:100%; object-fit:cover; display:block}
.up-thumb-video{font-size:10px; font-weight:700; color:var(--muted); letter-spacing:.08em}
.up-info{flex:1; min-width:0}
.up-name{font-size:13.5px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.up-state{font-size:12.5px; color:var(--muted); word-break:break-word}
.up-bar{height:4px; border-radius:2px; background:#e6eaee; overflow:hidden; margin-top:5px}
.up-bar i{display:block; height:100%; width:0; background:var(--brand); transition:width .2s}
.up-item.done .up-bar{display:none}
.up-item.fail{border-color:#f6c6c1; background:var(--danger-soft)}
.up-item.fail .up-state{color:var(--danger)}
.up-del{
flex:none; border:1px solid var(--line-strong); background:var(--card); color:var(--muted);
border-radius:7px; padding:5px 10px; font-size:12.5px; cursor:pointer; font-family:inherit;
}
.up-del:hover{border-color:var(--danger); color:var(--danger)}
.up-del:disabled{opacity:.45; cursor:default}
/* ---------- 附件展示 ---------- */
/* align-items:flex-start —— 否则同行里有视频(多了标题栏)时,图片盒子会被拉伸,底部留白 */
.atts{display:flex; flex-wrap:wrap; align-items:flex-start; gap:10px; margin-top:14px}
.atts .att{
margin:0; max-width:100%; border:1px solid var(--line); border-radius:11px;
overflow:hidden; background:var(--card);
}
.atts .att-image img{display:block; width:220px; height:auto; max-height:200px; object-fit:cover}
.atts.compact .att-image img{width:150px; max-height:140px}
.atts .att-video{flex:1 1 320px; max-width:440px}
.atts .att-video video{display:block; width:100%; max-height:330px; background:#000}
.att-video-ph{
width:360px; max-width:100%; height:200px; background:#11161c; color:#8b97a5;
display:flex; align-items:center; justify-content:center; font-size:13px;
}
.atts figcaption{
display:flex; gap:8px; align-items:center; padding:6px 9px; font-size:12px; color:var(--muted);
}
.atts .att-name{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.atts .att-size{margin-left:auto; flex:none}
.thumbs{display:flex; gap:8px; margin:0 0 8px}
.thumbs img{
width:78px; height:78px; object-fit:cover; display:block;
border-radius:9px; border:1px solid var(--line);
}
.att-tag{color:var(--brand); font-weight:600}
/* ---------- 附件灯箱(点击放大 + 左右切换) ---------- */
.atts .att-image .att-lb{display:block; line-height:0; cursor:zoom-in}
.atts .att-image .att-lb:focus-visible{outline:2px solid var(--brand); outline-offset:2px}
.att-zoom{
flex:none; border:1px solid var(--line-strong); background:var(--card); color:var(--muted);
border-radius:6px; padding:2px 9px; font-size:12px; line-height:1.6; cursor:pointer; font-family:inherit;
}
.att-zoom:hover{border-color:var(--brand); color:var(--brand)}
html.lb-open,body.lb-open{overflow:hidden}
.lb{
position:fixed; inset:0; z-index:1000; background:rgba(12,14,17,.94);
display:flex; flex-direction:column; animation:lb-in .16s ease-out;
}
.lb[hidden]{display:none}
@keyframes lb-in{from{opacity:0} to{opacity:1}}
.lb-stage{flex:1; min-height:0; position:relative}
.lb-media{
position:absolute; inset:0; z-index:1; padding:48px 62px;
display:flex; align-items:center; justify-content:center;
}
.lb-media img,.lb-media video{
display:block; max-width:100%; max-height:100%; border-radius:6px;
box-shadow:0 10px 44px rgba(0,0,0,.55);
}
.lb-media video{background:#000; width:auto}
.lb-media.loading::after{
content:''; position:absolute; width:26px; height:26px; border-radius:50%;
border:3px solid rgba(255,255,255,.26); border-top-color:#fff;
animation:lb-spin .8s linear infinite;
}
@keyframes lb-spin{to{transform:rotate(360deg)}}
.lb-nav{
position:absolute; top:50%; transform:translateY(-50%); z-index:2;
width:46px; height:46px; padding:0 0 3px; border:0; border-radius:50%; cursor:pointer;
background:rgba(255,255,255,.14); color:#fff; font-size:26px; line-height:1;
display:flex; align-items:center; justify-content:center; font-family:inherit;
}
.lb-nav:hover{background:rgba(255,255,255,.32)}
.lb-nav[hidden]{display:none}
.lb-prev{left:14px}
.lb-next{right:14px}
.lb-x{
position:absolute; top:14px; right:14px; z-index:3;
width:40px; height:40px; border:0; border-radius:50%; cursor:pointer;
background:rgba(255,255,255,.14); color:#fff; font-size:17px; font-family:inherit;
}
.lb-x:hover{background:rgba(255,255,255,.32)}
.lb-bar{
flex:none; display:flex; align-items:center; gap:10px; flex-wrap:wrap;
padding:10px 18px 16px; color:#e9edf2; font-size:13.5px;
}
.lb-name{font-weight:600; max-width:58vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.lb-meta{color:#9aa4b0; white-space:nowrap}
.lb-orig{margin-left:auto; color:#9ec5ff; white-space:nowrap}
.lb-orig[hidden]{display:none}
@media (max-width:560px){
.lb-media{padding:40px 10px}
.lb-nav{width:38px; height:38px; font-size:22px}
.lb-prev{left:6px} .lb-next{right:6px}
.lb-name{max-width:44vw}
}
/* ---------- 深色模式 ---------- */
@media (prefers-color-scheme: dark){
:root{
--fg:#e6e8eb; --muted:#9aa2ad; --line:#2c3138; --line-strong:#3a4149;
--bg:#15181c; --card:#1c2024; --brand:#5aa2ff; --brand-soft:#1e2a3d;
--ok:#4ec98a; --ok-soft:#16301f; --warn:#e2b559; --warn-soft:#2a2517;
--danger:#ff8a80; --danger-soft:#33191a; --shadow:0 1px 3px rgba(0,0,0,.35);
}
.item-meta .who,.reply-head .who{color:#cfd4da}
.field > span{color:#e6e8eb}
.captcha{background:#21262b}
.uploader-box{background:#21262b}
.up-thumb{background:#2c3138}
.up-bar{background:#2c3138}
.up-item.fail{border-color:#5a2a2a}
.badge.st{background:#242a30; color:#a8b0ba}
.badge.st-closed{background:#242a30; color:#8a929c}
.topbar{background:rgba(21,24,28,.82)}
.topbar .navlink:hover{background:var(--brand-soft)}
.btn:hover{border-color:var(--brand)}
.reply.admin{background:linear-gradient(180deg,var(--brand-soft),var(--card) 42%)}
.flash.error{background:var(--danger-soft); border-color:#5a2a2a; color:#ffb4ab}
.flash.ok{background:var(--ok-soft); border-color:#26503a; color:#9fe3bd}
}
/* ---- 管理后台 ---- */
.tabs{display:flex; flex-wrap:wrap; gap:8px; margin:16px 0 4px; border-bottom:1px solid var(--line); padding-bottom:10px}
.tab{padding:7px 14px; border-radius:999px; font-size:14px; color:var(--muted); background:var(--card); border:1px solid var(--line)}
.tab:hover{color:var(--brand); border-color:var(--brand)}
.tab.on{background:var(--brand); border-color:var(--brand); color:#fff}
.grid2{display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:12px 16px; margin:12px 0}
.grid2 .field{margin:0}
.check{display:flex; align-items:center; gap:8px; font-size:14px; color:var(--muted); margin:8px 0}
.check input{width:16px; height:16px}
.btn.danger{border-color:#e08b84; color:#c0392b}
.btn.danger:hover{background:#fdecea}
.filters{display:flex; flex-wrap:wrap; gap:8px; margin:18px 0 4px}
.filters input[type=text]{flex:1 1 220px; min-width:180px}
.filters select{max-width:160px}
@media (max-width:640px){
.grid2{grid-template-columns:1fr}
.filters input[type=text],.filters select{width:100%; max-width:none}
}