/* ============================================================
   filedrop 样式
   约束：
     · 服务端设了 CSP，style-src 只有 'self' —— 不能有内联 style
     · 不引任何外部字体/CDN，纯 CSS 绘制图标
     · 自动跟随系统深色模式
   ============================================================ */

:root {
  --bg: #f4f6f9;
  --surface: #ffffff;
  --surface-2: #fafbfc;
  --surface-3: #f1f3f7;
  --border: #e3e7ee;
  --border-strong: #cfd6e0;
  --text: #1c2230;
  --muted: #667085;
  --accent: #2563eb;
  --accent-text: #ffffff;
  --accent-soft: #e9efff;
  --danger: #d92d20;
  --danger-soft: #fdecea;
  --ok: #12805c;
  --ok-soft: #e6f6ef;
  /* 文件类型徽章的配色（浅色）。
     原来所有文件共用同一个 .ico.file 图标，.zip / .mp4 / .png / .txt 看起来
     一模一样，这一列等于零信息量。现在按扩展名给一块带颜色的缩写。 */
  --k-img-bg: #dcfce7;  --k-img-fg: #166534;
  --k-vid-bg: #fee2e2;  --k-vid-fg: #991b1b;
  --k-aud-bg: #f3e8ff;  --k-aud-fg: #6b21a8;
  --k-zip-bg: #fef3c7;  --k-zip-fg: #92400e;
  --k-doc-bg: #dbeafe;  --k-doc-fg: #1e40af;
  --k-code-bg: #e2e8f0; --k-code-fg: #334155;
  --k-oth-bg: var(--surface-3); --k-oth-fg: var(--muted);
  --radius: 10px;
  --radius-sm: 7px;
  --shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .04);
  --shadow-lg: 0 4px 12px rgba(16, 24, 40, .08);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1218;
    --surface: #171b23;
    --surface-2: #1b2029;
    --surface-3: #232936;
    --border: #2a3140;
    --border-strong: #3a4354;
    --text: #e7eaf0;
    --muted: #98a1b2;
    --accent: #6b9bff;
    --accent-text: #0f1218;
    --accent-soft: #1d2942;
    --danger: #ff8078;
    --danger-soft: #3a1e1d;
    --ok: #4ade9f;
    --ok-soft: #123024;
    --k-img-bg: #0d2b1c;  --k-img-fg: #6ee7a8;
    --k-vid-bg: #3a1a1a;  --k-vid-fg: #fca5a5;
    --k-aud-bg: #2a1a3d;  --k-aud-fg: #d8b4fe;
    --k-zip-bg: #3a2a10;  --k-zip-fg: #fcd34d;
    --k-doc-bg: #12253d;  --k-doc-fg: #93c5fd;
    --k-code-bg: #232936; --k-code-fg: #cbd5e1;
    --k-oth-bg: var(--surface-3); --k-oth-fg: var(--muted);
    --shadow: 0 1px 2px rgba(0, 0, 0, .35);
    --shadow-lg: 0 6px 20px rgba(0, 0, 0, .45);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  -webkit-text-size-adjust: 100%;
}

/* ---------- 长文本在哪里换行 ----------
   不能在 body 上全局设 overflow-wrap: anywhere —— 那会让**每一个**文本节点
   都能从任意位置断开，导航项这种短标签会被拆成"分享 / 链接"两行，
   看起来就是"上面俩字、下面接着栏目"。
   所以只在真正可能很长的容器上开，其余保持普通换行规则。 */
td, .err, .notice, .warnbar, label, .hint, .readonly {
  overflow-wrap: anywhere;
}
/* 这些是短标签，永远不该从中间断开 */
header nav a, header .brand, .badge, .btn, button, th, .sortlink {
  white-space: nowrap;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------- 顶栏 ---------- */
header {
  position: sticky; top: 0; z-index: 10;
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px;
  padding: 0 20px; min-height: 56px;
  /* 两行 background 是刻意的降级：不支持 color-mix 的老浏览器保留第一行，
     否则这条声明会被整条丢弃、顶栏背景变透明。 */
  background: var(--surface);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  -webkit-  border-bottom: 1px solid var(--border);
}
header .brand {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 700; font-size: 16px; letter-spacing: -.01em;
}
/* 标题旁的品牌图标。
   用和网站图标（favicon）同一张图，保证浏览器标签页和页面内是同一个图案。
   background-color 是兜底：万一图标文件没部署上，至少还是个有颜色的方块，
   不会整个消失。 */
header .brand::before {
  content: ""; width: 22px; height: 22px; border-radius: 6px;
  background-color: var(--accent);
  background-image: url("/static/favicon-32.png");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  flex: 0 0 auto;
}
header nav { display: flex; gap: 4px; align-items: center; }
header nav a {
  padding: 6px 11px; border-radius: var(--radius-sm); color: var(--muted);
  font-size: 14px; font-weight: 500;
}
header nav a:hover { background: var(--surface-3); color: var(--text); text-decoration: none; }
/* 当前所在的页面高亮，一眼知道自己在哪 */
header nav a.on {
  background: var(--accent-soft); color: var(--accent); font-weight: 600;
}
/* 用户功能（文件/上传/分享链接）和后台管理之间的分隔线：
   让"哪些是我的功能、哪些是管理功能"一眼可分 */
header nav .sep {
  width: 1px; height: 16px; background: var(--border-strong);
  margin: 0 7px; flex: 0 0 auto;
}
header .who {
  margin-left: auto; color: var(--muted); font-size: 13px;
  display: inline-flex; align-items: center; gap: 8px;
  /* 用户名可能很长，允许换行/断行，别把它顶出屏幕 */
  flex-wrap: wrap; min-width: 0; word-break: break-all;
}
header form { margin: 0; }

/* ---------- 布局 ---------- */
/* 上下留白收了一档：原来的 22/56px 让每个栏目之间都空一大截，
   手机上尤其明显（一屏看不了几个栏目）。 */
main { max-width: 1040px; margin: 0 auto; padding: 18px 20px 40px; }
h1, h2 { font-size: 18px; font-weight: 650; letter-spacing: -.01em; margin: 0 0 10px; }
h2.mt { margin-top: 22px; }

/* ---------- 面包屑 ---------- */
.crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px;
  margin: 0 0 12px; font-size: 14px; color: var(--muted);
}
.crumbs a { padding: 3px 8px; border-radius: 6px; color: var(--muted); }
/* 深目录里单层文件夹名可能很长，不换行会把整条面包屑顶出屏幕 */
.crumbs a { max-width: 100%; word-break: break-all; }
.crumbs a:hover { background: var(--surface-3); color: var(--text); text-decoration: none; }
.crumbs a:last-child { color: var(--text); font-weight: 600; }
/* 面包屑之间补一个分隔符，否则一串链接看不出层级 */
.crumbs a:not(:last-child)::after { content: "/"; color: var(--border-strong); margin-left: 8px; }

/* 上传者那一列只对管理员有意义：普通用户的列表里它是空的，
   留着会多出一列空白，所以整列隐藏（表头、单元格一起）。 */
.tablewrap.no-owner .owner-col { display: none; }
/* 页面上的 SHA-256 值：等宽 + 允许从任意位置折行（64 个十六进制字符在手机上要能断） */
.hashline {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  overflow-wrap: anywhere;
}
/* 分享页（文件夹列表）里的文件名现在是个"换票"按钮而不是链接：
   外观必须和原来的链接一致，否则整列看起来就变样了。 */
.nameform { display: inline; margin: 0; max-width: 100%; }
.namebtn {
  border: 0; background: none; margin: 0;
  /* 负外边距 + 内边距：把可点区域做大（手机上更好点），但视觉位置一点不变 */
  padding: 4px 6px; margin: -4px -6px;
  font: inherit; font-weight: 500; color: var(--text);
  cursor: pointer; text-align: left;
  /* 关键：button 默认不折行，长文件名会把整张表顶宽（375px 屏上出现内部横向滚动）。
     必须显式允许换行，并且允许在任意字符处断（中文长名字没有空格可断）。 */
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
  max-width: 100%;
}
.namebtn:hover { color: var(--accent); text-decoration: underline; }
/* 「下载器链接」：次要动作，字号小一号、灰色，不抢文件名的视觉权重 */
.linkbtn {
  border: 0; background: none; margin: 0 0 0 6px;
  padding: 4px 6px; margin-right: -6px;
  font: inherit; font-size: 12px; color: var(--muted);
  cursor: pointer; white-space: nowrap;
}
.linkbtn:hover { color: var(--accent); text-decoration: underline; }
/* 按钮几何的统一来源。手机上调大是为了触摸目标（>=40px 才好点）。 */
:root {
  --btn-h: 36px;
  --btn-h-tiny: 30px;
  --btn-pad-x: 14px;
  --btn-fs: 14px;
}

/* ---------- 工具条 ---------- */
.actions {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin-bottom: 12px;
}
.actions .spacer { margin-left: auto; }

/* 行内表单 / 行内标签（新建文件夹、重置密码、上传页的复选框） */
.inline { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0; }
label.inline { color: var(--text); font-size: 13px; display: inline-flex; }
label.inline input[type=checkbox] { width: auto; margin: 0; }

/* ---------- 按钮 ---------- */
button, .btn {
  /* 统一按钮几何：高度、左右内边距、字号都由变量控制。
     这样 a.btn 和 button 完全一致 —— 之前 button.tiny 是**元素限定**选择器，
     列表里 <a class="btn tiny"> 拿不到，导致同一行"下载/分享"比"删除"大一号。 */
  font: inherit; font-size: var(--btn-fs); font-weight: 500;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: var(--btn-h); padding: 0 var(--btn-pad-x);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface); color: var(--text);
  cursor: pointer; white-space: nowrap;
  transition: background .12s ease, border-color .12s ease;
}
button:hover, .btn:hover { background: var(--surface-3); text-decoration: none; }
button:active, .btn:active { transform: translateY(1px); }
.btn.primary, button.primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-text);
  font-weight: 600;
}
.btn.primary:hover, button.primary:hover {
  background: color-mix(in srgb, var(--accent) 88%, #000);
  border-color: color-mix(in srgb, var(--accent) 88%, #000);
}
.btn.danger, button.danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 35%, var(--border));
}
.btn.danger:hover, button.danger:hover { background: var(--danger-soft); }
.btn.link, button.link { border: none; background: none; color: var(--accent); padding: 4px 6px; }
.btn.link:hover, button.link:hover { background: var(--surface-3); }
.btn.tiny, button.tiny { --btn-h: var(--btn-h-tiny); --btn-pad-x: 10px; --btn-fs: 13px; }

/* ---------- 输入 ---------- */
/* input[type=search] 一定要在这一串里：漏掉它的话，搜索框会用浏览器自带外观，
   高度只有 21px（别的输入框 39.7px）、内边距和圆角也都不一样 ——
   同一个工具条里就它看着"不是一套东西"。 */
input[type=text], input[type=password], input[type=number], input[type=search], select {
  font: inherit; font-size: 14px; color: var(--text);
  padding: 8px 11px; border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); background: var(--surface);
  max-width: 100%; transition: border-color .12s, box-shadow .12s;
}
/* WebKit 的搜索框还有一层原生外观，要显式关掉才肯吃上面的样式 */
input[type=search] { -webkit-appearance: none; appearance: none; }
input:focus, select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
label { display: block; margin: 9px 0; font-size: 14px; color: var(--muted); }
label input, label select { display: block; width: 100%; margin-top: 5px; color: var(--text); }

/* ---------- 卡片 ---------- */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 18px; max-width: 440px; width: 100%;
}
.center {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: 20px;
}
.center .card { padding: 28px; }
.center h1 { font-size: 22px; margin-bottom: 6px; text-align: center; }
.center .sub { text-align: center; color: var(--muted); font-size: 13px; margin: 0 0 18px; }
.center button[type=submit] { width: 100%; justify-content: center; margin-top: 8px; padding: 10px; }

/* ---------- 提示条 ---------- */
.err, .notice, .warnbar {
  padding: 10px 14px; border-radius: var(--radius-sm);
  font-size: 14px; margin: 0 0 16px; border: 1px solid;
}
/* 每条都先写一个不依赖 color-mix 的降级值 */
.err { color: var(--danger); background: var(--danger-soft); border-color: var(--border); }
.err { border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.notice { color: var(--ok); background: var(--ok-soft); border-color: var(--border); }
.notice { border-color: color-mix(in srgb, var(--ok) 28%, transparent); }
.warnbar { color: var(--text); background: var(--accent-soft); border-color: var(--border); }
.warnbar { border-color: color-mix(in srgb, var(--accent) 25%, transparent); }
.hint { color: var(--muted); font-size: 13px; margin: 10px 0 0; line-height: 1.6; }
/* 帮助文字里举例用的代码片段。
   刻意**不加 nowrap**：里面有"项目文档/需求说明.pdf"和 aria2c 那种长命令，
   在手机上禁止换行就会把整页顶出横向滚动条。 */
.hint code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; background: var(--surface-3); color: var(--text);
  padding: 1px 5px; border-radius: 4px;
  overflow-wrap: anywhere;
}

/* ---------- 表格 / 文件列表 ---------- */
.tablewrap {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  /* 用 auto 而不是 hidden：万一表格在窄屏上还是比屏幕宽，
     用户能横向滑动看到，而不是内容被直接裁掉、还没有任何提示。 */
  overflow-x: auto;
}
table { width: 100%; border-collapse: collapse; }
th, td {
  padding: 10px 14px; text-align: left; font-size: 14px;
  border-bottom: 1px solid var(--border); vertical-align: middle;
}
th {
  background: var(--surface-2); font-weight: 600; font-size: 12.5px;
  color: var(--muted); text-transform: uppercase; letter-spacing: .04em;
  padding-top: 10px; padding-bottom: 10px;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }
td.act { text-align: right; white-space: nowrap; }
/* 「分享」和「删除」并排一行，且垂直居中对齐。
   放在 .actrow 里而不是直接给 td 设 flex —— 在 td 上用 flex 会让它失去
   table-cell 布局，整行的列就对不齐了。 */
.actrow {
    /* 桌面列表里三颗按钮必须排成一行：flex-wrap 是给窄屏和网格视图用的，
       写在基础规则里会让桌面也换行 —— 结果就是"删除"被挤到第二行。 */
    display: inline-flex; align-items: center; gap: 10px; white-space: nowrap; }
/* 「分享」用主色描边、「删除」用红色，两个动作一眼分得开 */
td.act a.share { border-color: var(--accent); color: var(--accent); }
td.act a.share:hover { background: var(--accent-soft); }
td.act form { display: inline; margin: 0; }
/* 窄屏上才露出「大小 · 时间」的第二行；宽屏走独立的表格列 */
.meta-sm { display: none; }
/* 注意：不要在 td 上直接用 display:flex——那会让单元格失去 table-cell 布局，
   整行的列就对不齐了。所以内容外面再包一层。 */
.namecell { display: flex; align-items: center; gap: 10px; min-width: 0; }
.namecell a { color: var(--text); font-weight: 500; word-break: break-all; }
.namecell a:hover { color: var(--accent); }
/* 窄屏第二行里的"大小"部分。数字用等宽数字，异步填进来时不会左右跳动。 */
.meta-sm .m-size { font-variant-numeric: tabular-nums; }
/* 名称 + 窄屏下才出现的「大小 · 时间」第二行 */
.nametext { min-width: 0; }
td.size, td.time { color: var(--muted); font-size: 13px; white-space: nowrap; }
.empty { color: var(--muted); text-align: center; padding: 34px 16px; font-size: 14px; }

/* 纯 CSS 图标。
   必须显式 display:inline-block：在 flex 容器里它会被块化所以没问题，
   但在分享页 hero 那种块级容器里它默认是 inline，宽高会被忽略、图标塌掉。 */
.ico { position: relative; display: inline-block; width: 20px; height: 20px; flex: 0 0 20px; vertical-align: middle; }
/* 文件夹图标和文件类型徽章**一样大**（32px 的方块）。
   原来它是 20px，而文件那一列是 32px 的彩色徽章，同一列里文件夹显得又小又虚，
   像是"图标画小了"。图形全部按百分比定位，所以放大到分享页的 44/56px 也不会变形。 */
.ico.dir { width: 32px; height: 32px; flex: 0 0 32px; }
.ico.dir::before {
  content: ""; position: absolute; inset: 30% 3% 7% 3%;
  background: var(--accent); opacity: .34; border-radius: 3px;
}
.ico.dir::after {
  content: ""; position: absolute; left: 3%; top: 13%; width: 46%; height: 22%;
  background: var(--accent); opacity: .58; border-radius: 3px 3px 0 0;
}
/* 没有扩展名的文件（README、Makefile 这类）走这个通用文件图标，
   同样要和 32px 的徽章一样大，否则又是一列大、一列小。
   图形按百分比画，所以网格里的 48px、分享页的 44/56px 都不会变形。 */
.ico.file { width: 32px; height: 32px; flex: 0 0 32px; }
.ico.file::before {
  /* 纸张：约 3:4 的竖长方形。以前是 inset: 8% 27%，在 32px 盒子里只有
     15×27px 的细高条，加上只有 5.7px 宽的"横线"，看着就像图标坏了。 */
  content: ""; position: absolute; inset: 11% 21% 9% 21%;
  border: 2px solid var(--border-strong); border-radius: 3px;
  background: var(--surface);
}
.ico.file::after {
  /* 三条横线用百分比渐变画，所以放大到 44/56px 也不会变形
     （以前用写死 px 的 box-shadow 偏移，放大了就错位） */
  content: ""; position: absolute; left: 31%; right: 31%; top: 31%; bottom: 25%;
  background: repeating-linear-gradient(to bottom,
    var(--border-strong) 0 13%, transparent 13% 37%);
  border-radius: 1px;
}

/* 类型徽章：一个按类别着色的圆角方块 + 扩展名缩写。
   **方形**，不是长方形 —— 之前是 38×24 的扁块，看着又胖又长。
   文字来自 data-ext 属性（content: attr()）——纯 CSS，不引外部资源，
   也没有内联 style，所以严格 CSP（style-src 'self'）下照样能用。 */
.ico.ext {
  width: 32px; height: 32px; flex: 0 0 32px;
  display: inline-grid; place-items: center;
  border-radius: 8px; overflow: hidden;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px; font-weight: 700; letter-spacing: 0;
  background: var(--k-oth-bg); color: var(--k-oth-fg);
}
.ico.ext::before { content: attr(data-ext); }
.ico.ext.img  { background: var(--k-img-bg);  color: var(--k-img-fg); }
.ico.ext.vid  { background: var(--k-vid-bg);  color: var(--k-vid-fg); }
.ico.ext.aud  { background: var(--k-aud-bg);  color: var(--k-aud-fg); }
.ico.ext.zip  { background: var(--k-zip-bg);  color: var(--k-zip-fg); }
.ico.ext.doc  { background: var(--k-doc-bg);  color: var(--k-doc-fg); }
.ico.ext.code { background: var(--k-code-bg); color: var(--k-code-fg); }

/* 工具条右侧的「共 N 项」 */
.count { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }
th.act { text-align: right; }

/* ---------- 拖拽上传区 ---------- */
/* 设计要点：整个区域就是一个「选择文件」按钮，点哪里都能打开文件选择框。
   这是网盘产品的通行做法（Google Drive / Dropbox / WeTransfer 都这样），
   比并排摆两个同等分量的大按钮清楚得多——之前那样用户根本分不清哪个是哪个。
   文件夹上传是次要动作，收成下面一行小字链接。
   宽度 860px 居中：拉满整行（1000px）里面太空，缩到 680px 又不像"主操作区"。
   上传页的复选框和说明文字也跟着一起居中，整块才是一体的。 */
.drop {
  border: 2px dashed var(--border-strong); border-radius: var(--radius);
  background: var(--surface); margin: 0 auto 14px; max-width: 860px;
  transition: border-color .15s, background .15s;
}
.drop.over { border-color: var(--accent); background: var(--accent-soft); }
.dropzone {
  display: block; margin: 0; padding: 18px 20px 16px;
  text-align: center; color: var(--muted); cursor: pointer;
}
.dropzone strong { display: block; color: var(--text); font-size: 16px; margin: 10px 0 4px; }
.dzsub { font-size: 13px; }
.dzalt { margin: 0; padding: 0 20px 16px; text-align: center; font-size: 13px; }
.dzalt label {
  display: inline; margin: 0; font-size: 13px; color: var(--accent);
  cursor: pointer; text-decoration: underline;
}

/* 上传箭头：内联 SVG（写在 upload.html 里）。
   之前是用"旋转 45° 的方块 + 两条边框"拼出来的，几何上很容易歪，
   而且各浏览器对边框圆角的处理还不一致。SVG 用精确坐标画，
   任何缩放都不会变形，也不需要任何外部资源（不违反 CSP）。 */
.dzico { display: block; width: 38px; height: 38px; margin: 0 auto; }
.dzico-bg { fill: var(--accent-soft); }
.dzico-fg { fill: var(--accent); }

/* 可排序的表头（点一下切换升/降序） */
.sortlink { color: var(--muted); display: inline-flex; align-items: center; gap: 4px; }
.sortlink:hover { color: var(--accent); text-decoration: none; }
.sortlink .arrow { font-size: 11px; color: var(--accent); }

/* 空目录里的上传按钮 */
.emptyact { display: block; margin-top: 14px; }

/* 视觉隐藏但保留在 DOM 里（仍然可聚焦、可被 label 触发）。
   直接用 display:none 会让键盘用户没法聚焦到文件输入框。 */
.vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- 多选与批量删除 ---------- */
th.pick, td.pick { width: 1%; white-space: nowrap; padding-right: 0; }
.pickbox, #pickall {
  width: 16px; height: 16px; margin: 0; vertical-align: middle;
  accent-color: var(--accent); cursor: pointer;
}
/* 选中了才出现的操作条 */
.batchbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  background: var(--accent-soft); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 8px 12px; margin: 0 0 10px;
  font-size: 13.5px;
}
/* 必须显式写这一条：[hidden] 来自浏览器默认样式表，选择器权重比 .batchbar
   低，所以上面那句 display:flex 会把它覆盖掉、让"隐藏"的条一直显示出来。 */
.batchbar[hidden] { display: none; }
.batchbar button { flex: 0 0 auto; }
.batchbar button[disabled] { opacity: .5; cursor: not-allowed; }

/* ---------- 网格视图 ----------
   纯 CSS：只是给 .tablewrap 加一个 .grid 类，把表格重排成卡片网格。
   规则在**文件最后**（见「网格视图（必须放在最后）」那一段）——
   手机端的 .tablewrap.cards 和它权重相同，谁写在后面谁生效。 */

/* ---------- 全盘搜索 ---------- */
.searchbar {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  margin-bottom: 14px;
}
.searchbar input[type=search] { flex: 1 1 220px; min-width: 0; }
.searchbar button { flex: 0 0 auto; }
/* 搜索结果里"根目录"那种次要文字 */
.muted-inline { color: var(--muted); }

/* ---------- 在线预览 ---------- */
/* 预览页的「上一个 / 下一个 / 下载」导航条（.viewnav / .btn.off / .vname）
   已经删了：预览页改成只留一个「下载文件」按钮，模板里没有这些元素了。 */
.viewer {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 16px; margin-bottom: 16px; text-align: center;
}
.vimg { max-width: 100%; max-height: 76vh; height: auto; border-radius: var(--radius-sm); }
.vvid { max-width: 100%; max-height: 76vh; width: auto; border-radius: var(--radius-sm); background: #000; }
.vaud { width: 100%; max-width: 520px; margin: 20px auto; display: block; }
.vpdf { width: 100%; height: 78vh; border: 0; border-radius: var(--radius-sm); background: var(--surface-3); }
.vtext {
  margin: 0; text-align: left; max-height: 76vh; overflow: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere;
}
/* 「预览」用绿色描边，和「分享」（主色蓝）、「删除」（红）三个动作互相区分 */
td.act a.preview { border-color: var(--border-strong); color: var(--ok); }
td.act a.preview { border-color: color-mix(in srgb, var(--ok) 45%, var(--border)); }
td.act a.preview:hover { background: var(--ok-soft); }

/* ---------- 分享链接二维码 ---------- */
.qrbtn { padding: 5px 10px; font-size: 13px; }
.qrrow td { background: var(--surface-2); }
/* [hidden] 必须真的隐藏。
   手机端把表格重排成卡片时写了 .tablewrap.cards tr{display:flex}、
   网格视图里有 .tablewrap.grid tr{display:block}，这两个选择器的权重都比
   浏览器默认的 [hidden]{display:none} 高，于是"藏起来"的二维码行照样渲染成
   一条空行 —— 每条分享链接下面都多出一块莫名其妙的空白。
   权重 (0,3,1) 明确压过那两条，不依赖书写顺序。 */
.tablewrap.cards tr[hidden],
.tablewrap.grid tr[hidden] { display: none; }
.qrbox { display: flex; justify-content: center; padding: 6px; }
/* pixelated：二维码放大时保持方块锐利，不然浏览器插值会糊掉边缘、影响扫描 */
.qrcanvas { image-rendering: pixelated; border-radius: 6px; background: #fff; }

/* ---------- 上传进度 ---------- */
/* 注：原来这里有个「总进度面板」（.totalbar / .totalmeta），
   后来进度改成每个文件一行（见下面的 .rowlist），模板里已经没有这些元素了。 */
.rowlist { display: flex; flex-direction: column; gap: 10px; margin-top: 18px; }
.row {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 12px 14px; box-shadow: var(--shadow);
}
.row .meta {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; font-size: 13px; color: var(--muted);
}
.row .meta .nm { color: var(--text); word-break: break-all; }
/* 文件名那一格必须能伸缩，而且**不能被状态文字挤成一条竖线**。
   状态文字是 flex: 0 0 auto（不收缩），文件名如果不设 min-width:0 + 合理下限，
   长状态文字会把它压到一列一个字 —— 就是"字体变成竖着的、很长"那个现象。
   现在：文件名占主位（basis 60%），放不下时状态文字换到下一行。 */
.row .meta { flex-wrap: wrap; }
.row .meta .nm { flex: 1 1 60%; min-width: 0; }
.row .meta .st { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* 行内右侧：状态文字 + 暂停按钮（暂停和进度在同一个地方） */
.row .meta .rt { display: inline-flex; align-items: center; gap: 8px; flex: 0 1 auto; }
.row .meta .pz { padding: 3px 10px; font-size: 12px; }
/* 注：这里原来还有一套自造的播放器控制条（.vctrl / .vmore / .vmorebox /
   .vspdlist）和「总进度面板」（.totalbar / .totalmeta）。播放控制后来全部
   交回浏览器原生控件、上传进度也改成每行一行，模板里已经不再有这些元素了，
   所以把死样式删掉了（约 1.5KB）。 */
.row .meta .st.ok { color: var(--ok); }
.row .meta .st.bad { color: var(--danger); }
.bar {
  height: 6px; background: var(--surface-3); border-radius: 999px;
  overflow: hidden; margin-top: 9px;
}
.bar > i {
  display: block; height: 100%; width: 0; border-radius: 999px;
  background: var(--accent);
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 60%, #22d3ee));
  transition: width .18s ease;
}

/* ---------- 分享页 ---------- */
.share-hero {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  padding: 26px 22px; text-align: center; margin-bottom: 16px;
}
.share-hero .fname {
  font-size: 19px; font-weight: 650; word-break: break-all;
  margin: 14px 0 6px; display: block; color: var(--text);
}
/* 文件来自哪个文件夹。只有一个文件名的话收件人不知道是哪一份。 */
.share-hero .fpath {
  color: var(--muted); font-size: 13px; word-break: break-all;
  margin-top: 14px;
}
.share-hero .fmeta { color: var(--muted); font-size: 13px; margin-bottom: 20px; }
/* 主按钮和"复制下载链接"必须一样大：以前只放大了 .dl，两个按钮并排高低不齐。
     强调靠颜色，不靠尺寸。 */
.share-hero .btn { --btn-h: 46px; --btn-pad-x: 22px; --btn-fs: 15px; }
/* 分享页的大图标。
   **里面的图形全部按百分比定位**，这样容器换成任何尺寸都不会变形。
   之前是写死 px 的（按 44×44 画的），而手机端把容器改成了 76×46，
   图形没跟着变，就成了压扁的"扁长"样子。 */
.share-hero .ico { margin: 0 auto; width: 44px; height: 44px; }
.share-hero .ico.dir::before { inset: 27% 9% 11% 9%; border-radius: 14%; }
.share-hero .ico.dir::after { left: 9%; top: 14%; width: 45%; height: 27%; border-radius: 14% 14% 0 0; }
/* 分享页的大号类型徽章（方形，圆角更大一点，看着舒服） */
.ico.ext.big {
  width: 76px; height: 76px; flex: 0 0 76px;
  font-size: 16px; border-radius: 16px;
}

/* ---------- 只读输入（分享链接） ---------- */
/* 用 textarea 而不是 input：input 是单行的，长链接在手机上只能看到开头一截。
   textarea 会自动换行，整条链接一眼看得全。 */
.readonly {
  width: 100%; min-width: 240px; display: block;
  font: 13px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--muted); background: var(--surface-3);
  border-color: var(--border); cursor: pointer;
  resize: none; overflow: hidden;
  /* 支持的浏览器按内容自动撑高，不支持的由 app.js 兜底 */
  field-sizing: content;
}
/* 分享链接那一格给个宽度下限，免得宽屏上被别的列挤扁 */
.linkcell { min-width: 220px; }
/* 列多的表格（后台 7 列、分享页 6 列）在 1040px 的版心里摆不平。
   交给浏览器自动分配时，它会把「创建者」按整条用户名撑到 200px 以上，
   真正长的「分享的内容」反而只剩 150px，长路径被压成一列竖排的字。
   所以版心够宽时直接定死列宽，谁也不抢谁的；窗口更窄（<1001px）时版心放不下，
   就退回原来的自动分配 + 表格自己横向滚动，别把日期和按钮挤坏。
   手机上（<700px）这些表走卡片布局，与此无关。 */
@media (min-width: 1001px) {
  .tablewrap.cols-fixed table { table-layout: fixed; }
  .tablewrap.cols-fixed th, .tablewrap.cols-fixed td { padding-left: 10px; padding-right: 10px; }
  .tablewrap.cols-fixed th { white-space: normal; }
  /* 后台「所有分享链接」：7 列 */
  .tablewrap.cols-7 th:nth-child(1) { width: 25%; }  /* 分享链接 */
  .tablewrap.cols-7 th:nth-child(2) { width: 25%; }  /* 分享的内容 */
  .tablewrap.cols-7 th:nth-child(3) { width: 9%; }   /* 需要密码 */
  .tablewrap.cols-7 th:nth-child(4) { width: 14%; }  /* 失效时间 */
  .tablewrap.cols-7 th:nth-child(5) { width: 9%; }   /* 已下载 / 上限 */
  .tablewrap.cols-7 th:nth-child(6) { width: 10%; }  /* 创建者 */
  .tablewrap.cols-7 th:nth-child(7) { width: 8%; }   /* 操作 */
  /* 分享页「已创建的分享链接」：6 列。
     右边几列的内容是 nowrap 的（日期、"已下载 0 / 1 次"、两颗按钮），
     它们会把所在的列顶到内容的宽度 —— 给少了整张表就会被撑出容器、
     又冒出横向滚动条。所以按内容实际需要分配，剩下的才给长文本列。 */
  .tablewrap.cols-6 th:nth-child(1) { width: 28%; }  /* 分享链接 */
  .tablewrap.cols-6 th:nth-child(2) { width: 25%; }  /* 分享的内容 */
  .tablewrap.cols-6 th:nth-child(3) { width: 8%; }   /* 需要密码 */
  .tablewrap.cols-6 th:nth-child(4) { width: 14%; }  /* 失效时间 */
  .tablewrap.cols-6 th:nth-child(5) { width: 12%; }  /* 已下载 / 上限 */
  .tablewrap.cols-6 th:nth-child(6) { width: 13%; }  /* 操作 */
  /* 创建者那格继承的是 .time 的 nowrap（那是给日期列用的），配窄列宽会把
     用户名切掉，所以这里把换行规则还回来。 */
  .tablewrap.cols-fixed .creator { white-space: normal; overflow-wrap: anywhere; }
  .tablewrap.cols-fixed td.cell-path { word-break: break-word; }
}
/* 后台「所有分享链接」用的是 <input class="readonly">，不是分享页那个会自动
   换行的 <textarea>。field-sizing: content 会让它按链接的真实长度撑到 430px 上下，
   整张表被顶到 1179px —— 桌面端于是**永远**挂着一条横向滚动条。
   单行输入框本来就能左右滚动、点一下还能全选，不需要按内容撑宽。 */
input.readonly { field-sizing: fixed; min-width: 0; }
/* 窄屏下补在链接下面的"分享：xxx"（宽屏有独立的一列，这行是隐藏的） */
.linktarget { color: var(--muted); }
.badge {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 600;
  background: var(--surface-3); color: var(--muted);
}
.badge.on { background: var(--accent-soft); color: var(--accent); }
.badge.perm { background: var(--ok-soft); color: var(--ok); }

/* 文本预览的"加载更多"：让用户能把整个文件看完 */
.vmorewrap {
  display: flex; align-items: center; justify-content: center;
  gap: 12px; margin-top: 12px; flex-wrap: wrap;
}
.vmorewrap[hidden] { display: none; }
.vprogress { color: var(--muted); font-size: 12.5px; font-variant-numeric: tabular-nums; }

/* 预览页：压缩包列表 / 信息卡 */
.vtitle { color: var(--text); font-size: 14px; font-weight: 600; margin: 16px 0 8px; text-align: left; }
.ziplist, .infolist { width: 100%; font-size: 13px; }
.ziplist td, .infolist td { padding: 6px 10px; border-bottom: 1px solid var(--border); text-align: left; }
.ziplist .zname { color: var(--text); word-break: break-all; }
.ziplist .zsize { color: var(--muted); white-space: nowrap; text-align: right; width: 1%; }
.infolist td:first-child { color: var(--muted); width: 90px; white-space: nowrap; }
.infolist td:last-child { color: var(--text); word-break: break-all; }
.vtext.mono { font-size: 12px; line-height: 1.5; }
/* 视频播不了时的提示（替代一片黑） */
.vfallback {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 22px 18px; text-align: center;
  color: var(--muted); font-size: 14px; line-height: 1.7;
}
.vfallback strong { color: var(--text); }
/* ---------- 日志查看 ---------- *//* pre + 自动换行：日志行里可能有很长的路径，
   不换行的话手机上会横向溢出。 */
.logview {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  max-height: 70vh; overflow: auto;
}
.logview pre {
  margin: 0; padding: 12px 14px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; line-height: 1.55; color: var(--text);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* ---------- 响应式 ---------- */
/* 只在桌面显示的东西（例如文件夹上传：手机浏览器不支持 webkitdirectory） */
.only-desktop { display: block; }

@media (max-width: 900px) {
  main { padding: 14px 12px 32px; }
  header { padding: 0 12px; gap: 4px 10px; }
  /* 「用户名 · 角色」和「退出」排在同一行。
     原来 .who 占满整行，退出按钮被挤到单独一行，看着像凭空多出来一个按钮。 */
  header .who { width: auto; order: 3; margin: 0 0 8px; flex: 1 1 auto; }
  header form { order: 3; margin: 0 0 8px; }
  th, td { padding: 10px 12px; }
  .hide-sm { display: none; }
  /* 窄屏下把被藏掉的「大小」和「修改时间」挪到文件名下面。
     直接隐藏的话，手机上完全看不出文件多大、文件夹占了多少空间 ——
     而这两条恰恰是最常看的信息。 */
  .meta-sm { display: block; font-size: 12px; color: var(--muted); margin-top: 3px; }
  .namecell { align-items: flex-start; }
  .readonly { min-width: 0; }
  table { font-size: 13px; }
  /* 窄屏上文件和文件夹的操作按钮改成竖排会错位，所以包在 .actrow 里
     保持一行、垂直居中，见上面的 .actrow 定义。 */
  .actrow {
  /* 允许换行：网格卡片只有 150px 宽，「打包下载」四个字放不下时换行而不是溢出 */
  flex-wrap: wrap;
  row-gap: 4px; gap: 8px; }
  /* 手机上浏览器都不支持 webkitdirectory，文件夹上传在这种设备上点了
     只会弹出一个单文件选择框，反而让人以为多选坏了。所以直接不显示。 */
  .only-desktop { display: none; }
  /* 上传进度那行状态文字（"从断点 3.2/8.0 MB · 1.2 MB/s"）允许换行 */
  .row .meta .st { white-space: normal; }
  /* 分享页大图标在窄屏上小一点，给长文件名让出宽度。
     **必须是正方形**：里面的图形按百分比画，容器一旦不是方的就会变形。 */
  .share-hero .ico { width: 56px; height: 56px; flex-basis: 56px; }
  .share-hero .ico.ext.big { width: 64px; height: 64px; flex-basis: 64px; font-size: 14px; border-radius: 14px; }
  .share-hero .fname { font-size: 17px; }
  /* 工具条在窄屏上更紧凑 */
  .actions { gap: 8px; }
  /* 面包屑在手机上一行放不下时逐级换行，不横向滚动 */
  .crumbs { gap: 0 2px; font-size: 13px; }

  /* ---------- 表格在手机上转成卡片 ----------
     后台的账号表有 5 列，光是"新密码"那个输入框就把表格撑得比屏幕宽，
     于是整块可以左右滑动——主流网盘在手机上都不这么做，而是把每一行
     变成一张卡片：第一行是「用户名 + 角色」，下面是操作。
     这样既没有横向滚动，用户名也不会被迫换行。 */
  .tablewrap.cards { overflow-x: visible; }
  .tablewrap.cards thead { display: none; }
  .tablewrap.cards table,
  .tablewrap.cards tbody { display: block; width: auto; }
  .tablewrap.cards tr {
    /* align-items: flex-start —— 复选框要跟"文件名第一行"对齐。
       用 center 的话它会被居中到"名字+大小/时间"整块的高度上，
       看起来漂在两行之间，"勾这一行"的归属感也变弱。 */
    display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px;
    padding: 10px 12px; border-bottom: 1px solid var(--border);
  }
  .tablewrap.cards td { display: block; padding: 0; border: 0; }
  /* 第一行：用户名 + 角色并排（不许上下堆） */
  .tablewrap.cards td.u-name {
    font-weight: 650; font-size: 15px; flex: 0 1 auto; min-width: 0;
  }
  .tablewrap.cards td.u-role { flex: 0 0 auto; margin-left: auto; }
  /* 第二行：新密码输入框 + 重置按钮，撑满整行 */
  .tablewrap.cards td.u-pass { flex: 1 1 100%; }
  .tablewrap.cards td.u-pass form { display: flex; gap: 8px; }
  .tablewrap.cards td.u-pass input { flex: 1 1 auto; min-width: 0; }
  /* 第三行：删除 */
  .tablewrap.cards td.act { flex: 1 1 100%; text-align: left; }
  /* 分享链接页也走卡片：表格横向滚动会把链接框挤到屏幕外，文字完全看不见 */
  .tablewrap.cards td.linkcell { flex: 1 1 100%; min-width: 0; }
  .tablewrap.cards td.linkcell textarea { min-width: 0; font-size: 12px; }
  /* 卡片里的多选框要给个实在的尺寸。
     td.pick 的 width:1% 是给表格布局用的，到了卡片布局里就变成"1% 宽"，
     再被上面的 input{max-width:100%} 一压，复选框只剩 3px —— 手机上既看不见
     也点不到，等于多选和批量删除在手机上是废的。 */
  .tablewrap.cards td.pick {
    align-self: flex-start; margin-top: 2px; flex: 0 0 auto; width: 26px; padding: 0; }
  .tablewrap.cards td.pick .pickbox { width: 20px; height: 20px; max-width: none; }
  /* 文件名那一格必须**能收缩**。它是可能很长的文件名，而下面那条通配规则
     会把没有类名的单元格设成 flex: 0 0 auto（= 绝不允许收缩），于是长名字
     不会换行、直接把整页顶宽，右边被切在屏幕外。 */
  /* flex-basis 取 0 而不是 auto：这样文件名能和左边的多选框挤在同一行，
     不会因为"内容太宽"被换到下一行、把每张卡片白顶高一行。 */
  .tablewrap.cards td.name-cell { flex: 1 1 0%; min-width: 0; }
  .tablewrap.cards td:not(.linkcell):not(.act):not(.u-name):not(.u-role):not(.u-pass):not(.name-cell) {
    flex: 0 0 auto; color: var(--muted); font-size: 12.5px;
  }
}

/* =================================================================
   手机端适配（只追加，不改动上面任何既有规则）
   ================================================================= */
@media (max-width: 900px) {
  /* 卡片布局里 inline 的 meta 已经显示大小/时间，
     表格那两列会变成第二份（还有第二个「计算中…」）→ 隐藏 */
  .tablewrap.cards td.size,
  .tablewrap.cards td.time,
  .tablewrap.cards td.hide-sm { display: none !important; }
  .tablewrap.cards td,
  .tablewrap.cards td.u-name { min-width: 0; word-break: normal; overflow-wrap: break-word; }
  .namecell { width: 100%; align-items: flex-start; }
  /* 图标和**第一行文件名**垂直居中。顶部对齐（flex-start）时，图标盒子的
     上边缘贴着文字块上边缘，而图标比一行字高，看着就像"浮在文字上面"。
     往上提 6px 正好让 32px 的图标和一行 19.5px 的文字对齐。 */
  .namecell .ico { margin-top: -6px; }
  .namecell a { flex: 1 1 auto; min-width: 0; word-break: normal; overflow-wrap: anywhere; line-height: 1.5; }
  /* 手机上这些行内文字链只有 17–19px 高，手指不好点。
     文件名是 flex 子项，加 padding 会把整行撑开，所以用伪元素把可点区域
     往外扩一圈 —— 文字和排版一点都不动。 */
  .namecell a { position: relative; }
  .namecell a::after { content: ""; position: absolute; inset: -6px -8px; }
  /* 段落里的行内链接：它是纯 inline，上下 padding 不会撑开行高（只把
     可点区域往外扩），正好用来加大点击范围。 */
  .hint a { padding-top: 5px; padding-bottom: 5px; }
  .nametext { width: 100%; overflow-wrap: anywhere; }
  .meta-sm { white-space: normal; }
  .actrow {
  /* 允许换行：网格卡片只有 150px 宽，「打包下载」四个字放不下时换行而不是溢出 */
  flex-wrap: wrap;
  row-gap: 4px; flex-wrap: wrap; white-space: normal; row-gap: 8px; }
  .tablewrap.cards td.act { display: flex; justify-content: flex-end; text-align: right; }
  .tablewrap.cards td.u-role { margin-left: 0; }
  .tablewrap.cards td.linkcell { min-width: 0; width: 100%; }
  .tablewrap.cards td.linkcell textarea { width: 100%; min-width: 0; font-size: 12px; word-break: break-all; }
  input, select, textarea { max-width: 100%; box-sizing: border-box; }
  .card { padding: 14px; }
  .card > label { display: block; margin-bottom: 10px; }
  .card input, .card select { width: 100%; }
  .actions { flex-wrap: wrap; }
}
/* 表单统一左对齐（分享页之前一会儿居中一会儿靠左） */
form.card { text-align: left; }
form.card > label { display: block; margin-bottom: 10px; color: var(--muted); font-size: 13px; }
form.card > label > input,
form.card > label > select { display: block; width: 100%; margin-top: 5px; box-sizing: border-box; }
.fieldhelp { display: block; margin-top: 5px; color: var(--muted); font-size: 12px; line-height: 1.5; }
.hint.small { font-size: 12px; color: var(--muted); }
/* 行内表单控件垂直居中（修按钮/输入框错位） */
.inline, td.act form, td.u-pass form { display: inline-flex; align-items: center; gap: 8px; margin: 0; }
/* 居中了还不算齐：**高度也得一样**。
   输入框上下内边距 8px（连边框约 39.7px），按钮默认 7px（37.7px），
   .tiny 按钮只有 5px（31.5px）—— 差 2px / 8px 在肉眼下就是"按钮没和输入框对齐"。
   所以同一行里的按钮按输入框的高度来。 */
.inline button, .inline .btn,
.searchbar button, .searchbar .btn { padding-top: 8px; padding-bottom: 8px; }
td.u-pass button, td.u-pass .btn { padding: 8px 14px; font-size: 14px; }

/* 后台「新建账号」和分享页「创建分享链接」：字段少，用 440px 的卡片会在右边
   空出一大片。桌面端让卡片占满版心，字段按可用宽度自动排成 2–3 列；
   说明文字和提交按钮横跨整行（不然它们会占掉一个字段格，看着莫名其妙）。
   手机上不生效，还是原来的竖排卡片。 */
@media (min-width: 901px) {
  .card.fields-row {
    /* align-items: start —— 标签+输入框按顶边对齐。
       曾经改成 end（想消掉按钮上方空白），结果说明文字行数不同时，
       同一行里的输入框上下错落近百像素，第一眼就是"摆放很乱"。 */
    max-width: none; display: grid; gap: 2px 18px; align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  }
  .card.fields-row > label { margin: 0 0 4px; }
  .card.fields-row > .hint { grid-column: 1 / -1; margin: 8px 0 2px; }
  .card.fields-row > button { grid-column: 1 / -1; justify-self: start; margin-top: 6px; }
}

/* =================================================================
   网格视图（必须放在最后）
   ------------------------------------------------------------------
   为什么位置这么讲究：文件列表的容器同时挂着 .cards 和 .grid 两个类。
   手机端那批 .tablewrap.cards 规则（把表格拆成卡片）和这里的 .tablewrap.grid
   规则**权重完全相同**，CSS 只认"谁写在后面"。这段以前写在中间，
   于是手机上点「网格视图」时 tbody 的 display:grid 被后面的 .cards 覆盖成
   display:block —— 网格根本没生效，看着和列表模式一样。
   放在文件最后，两种视图才能各归各家。
   ================================================================= */
.tablewrap.grid { overflow: visible; }
.tablewrap.grid table { display: block; }
.tablewrap.grid thead { display: none; }
/* 多列瀑布流（masonry）而不是等高等宽的网格。
   原来用 grid：同一行里所有卡片被拉成一样高（矮的那张下面一片空白），
   于是"短名字也用这么长的栏目"。column-width 让浏览器自己决定放几列，
   每张卡片保持自己的高度，短名字的卡片就只有一小块。 */
.tablewrap.grid tbody {
  display: block;
  padding: 14px;
  column-width: 150px;
  column-gap: 12px;
}
.tablewrap.grid tr {
  position: relative; display: block;
  break-inside: avoid;              /* 一张卡片不许被拆到两列 */
  -webkit-column-break-inside: avoid;
  margin: 0 0 12px;
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px 10px 12px; text-align: center; background: var(--surface);
}
.tablewrap.grid tr:hover { background: var(--surface-2); }
.tablewrap.grid td { display: block; padding: 0; border: 0; }
.tablewrap.grid td.pick {
  position: absolute; top: 8px; left: 8px; width: auto; padding: 0;
}
.tablewrap.grid .namecell { flex-direction: column; align-items: center; gap: 8px; }
.tablewrap.grid .nametext { text-align: center; }
.tablewrap.grid .ico { width: 40px; height: 40px; flex-basis: 40px; }
.tablewrap.grid .ico.ext { width: 48px; height: 48px; flex-basis: 48px; font-size: 12px; border-radius: 10px; }
/* 网格里文件夹图标也要和文件徽章一样大，不然又是一列大、一列小 */
.tablewrap.grid .ico.dir { width: 48px; height: 48px; flex-basis: 48px; }
.tablewrap.grid .ico.file { width: 48px; height: 48px; flex-basis: 48px; }
/* 网格卡片里的操作按钮：三个按钮在窄卡片里会"这张挤一行、那张换行"，
   每张卡片排布都不一样，看着就是没对齐。缩小一点让它们稳定排成一行。
   注意要连裸 button 一起管：删除是 <button class="danger tiny">，没有 .btn 类，
   只写 .btn 的话它仍按 .tiny 的尺寸渲染，比两个 <a class="btn"> 宽一圈，
   于是三个按钮刚好放不下、又被挤到第二行。 */
.tablewrap.grid .actrow {
  /* 允许换行：网格卡片只有 150px 宽，「打包下载」四个字放不下时换行而不是溢出 */
  flex-wrap: wrap;
  row-gap: 4px; gap: 4px; }
.tablewrap.grid td.act .btn,
.tablewrap.grid td.act button { padding: 3px 7px; font-size: 12px; }
/* 网格里也要看到大小和时间（列表视图里它们由 .meta-sm 承担） */
.tablewrap.grid .meta-sm { display: block; }
.tablewrap.grid td.size, .tablewrap.grid td.time { display: none; }
.tablewrap.grid td.act { text-align: center; margin-top: 10px; }
/* "返回上级""空目录"这类横跨整行的行，在瀑布流里要独占一行 */
.tablewrap.grid tr.spanner {
  column-span: all; text-align: left; padding: 12px 14px;
}
/* 手机端的卡片规则把每行设成了 flex，网格模式要还回块级盒子 */
.tablewrap.grid.cards tr { display: block; }
.tablewrap.grid .actrow {
  /* 允许换行：网格卡片只有 150px 宽，「打包下载」四个字放不下时换行而不是溢出 */
  flex-wrap: wrap;
  row-gap: 4px; justify-content: center; }
.tablewrap.grid tr.spanner td, .tablewrap.grid tr.empty td { text-align: left; }

@media (max-width: 900px) {
  /* 窄屏上 column-width:150px 只会算出一列 —— 一个文件就把整行占满，看着像被放大了。
     这里明确要两列（375px 下每列约 150px）。 */
  .tablewrap.grid tbody { column-count: 2; column-width: auto; padding: 10px; column-gap: 10px; }
  .tablewrap.grid tr { padding: 12px 8px 10px; margin-bottom: 10px; }
  .tablewrap.grid td.act .btn,
  .tablewrap.grid td.act button { padding: 3px 6px; font-size: 11px; }
}

/* =================================================================
   上传页：整页做成一条居中的 860px 栏
   ------------------------------------------------------------------
   拖放区、复选框、每个文件的进度行、底部说明共用同一条中轴，
   否则会出现"框居中、文字贴左边"的错位。 */
.upload-page > * { max-width: 860px; margin-left: auto; margin-right: auto; }
.inline button, td.act button, td.u-pass form button { line-height: 1.5; vertical-align: middle; }

/* =================================================================
   手机端：按钮加大到好点的尺寸，并砍掉不必要的动效（1 核小机器 + 手机 GPU）
   ================================================================= */
@media (max-width: 900px) {
  :root {
    --btn-h: 42px;      /* 触摸目标：36px 在手机上偏小 */
    --btn-h-tiny: 36px;
    --btn-pad-x: 13px;
  }
  /* 列表里的小按钮别撑太高，但也要够点 */
  .tablewrap.cards td.act .btn,
  .tablewrap.cards td.act button { min-height: var(--btn-h-tiny); }
}

/* 尊重系统的"减少动态效果"：这类用户往往也是低端机 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* =================================================================
   桌面列表的操作列：三颗按钮等宽、右对齐
   以前第一颗按钮的宽度取决于文字（"下载"2 字 / "打包下载"4 字），
   于是每行的"分享""删除"左右位置都不一样，整列看着毛毛躁躁。
   现在用等宽轨道，行与行之间对齐成一条竖线。
   ================================================================= */
/* 701px 起就用等宽轨道：原来只在 ≥1001px 生效，701–1000px（平板/分屏）
   这一段四颗按钮宽度由内容决定，会折行或把表格顶宽。 */
@media (min-width: 901px) {
  .tablewrap:not(.grid):not(.cols-fixed) td.act .actrow {
    --btn-pad-x: 8px;              /* 等宽轨道下压缩内边距，"打包下载"也放得下 */
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 80px;       /* 每颗按钮一样宽 */
    gap: 8px;
    justify-content: end;
    align-items: center;
  }
  /* 格子的直接子元素（<a> 或包着删除按钮的 <form>）撑满自己的轨道 */
  .tablewrap:not(.grid):not(.cols-fixed) td.act .actrow > *,
  .tablewrap:not(.grid):not(.cols-fixed) td.act .actrow > form > button { width: 100%; }
  .tablewrap:not(.grid):not(.cols-fixed) td.act .actrow > form { display: block; }

  /* "上传者"列被长文件名挤成"管理/员"两行 —— 明确不许折行 */
  .tablewrap th.owner-col, .tablewrap td.owner-col { white-space: nowrap; }
  .tablewrap th.size, .tablewrap td.size,
  .tablewrap th.time, .tablewrap td.time { white-space: nowrap; }
}

/* --- 触摸目标补强 --- */
/* 1) 手机端复选框 20px → 22px。
      选择器必须写成 .tablewrap.cards td.pick .pickbox（和 app.css 里那条 20px 的规则同样具体），
      否则会被它压住 —— 第一版就踩了这个坑。
      #pickall 是表头全选框，带 ID，特异性最高，只能用它自己的 ID 压。 */
@media (max-width: 900px) {
  .tablewrap.cards td.pick .pickbox { width: 22px; height: 22px; max-width: none; }
  .tablewrap.cards th.pick #pickall { width: 20px; height: 20px; }
  .tablewrap.cards td.pick {
    align-self: flex-start; margin-top: 2px; width: 30px; }
}
/* 2) "↑ 上一级"、桌面排序表头、面包屑这类文本链接：内边距 + 负外边距把命中区域
      撑到约 30px 高，视觉位置和行高一点不变。 */
.spanner a, .sortlink, .crumbs a { display: inline-block; padding: 6px 6px; margin: -6px -6px; }

/* --- 上传窗口放大（桌面） --- */
@media (min-width: 901px) {
  /* 上传区再放大一些：它是这一页唯一的主操作，原来只有一条窄带。 */
  .drop { max-width: 900px; }
  .upload-page > * { max-width: 900px; }
  .dropzone { padding: 54px 28px 48px; }
  .dzico { width: 64px; height: 64px; }
  .dropzone strong { font-size: 20px; }
  .dropzone { padding: 34px 24px 30px; }
  .dropzone strong { font-size: 18px; margin: 12px 0 6px; }
  .dzico { width: 52px; height: 52px; }
  .dzsub { font-size: 14px; }
  .dzalt { padding: 0 24px 20px; }
}

/* --- 内联改名输入框 --- */
/* 就地把文件名变成输入框：字号、行高继承原文字，宽度跟着单元格走，
   这样列表的行高和列宽都不会因为进入编辑态而跳动。 */
.renamebox {
  font: inherit;
  font-size: inherit;
  width: 100%;
  min-width: 8ch;
  max-width: 100%;
  padding: 3px 6px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
}

/* 空目录的空态：卡片布局下 td 会被通配规则变成"按内容宽"，
   text-align:center 因此失效、整块左贴边。这里让它独占整行并居中。 */
.tablewrap.cards td.empty { flex: 1 1 100%; text-align: center; padding: 24px 16px; }

/* 网格视图里也要隐藏"上传者"：桌面列表里它是独立单元格，
   网格段把所有 td 变成 block，于是管理员的卡片上会多出一行孤零零的用户名。 */
.tablewrap.grid td.owner-col, .tablewrap.grid th.owner-col { display: none; }

/* =================================================================
   断点说明（2026-09 调整）
   ------------------------------------------------------------------
   原来"表格转卡片"在 700px 结束，而桌面专属规则（等宽操作列、nowrap 列）
   从 701px 开始 —— 701–900px 这段（横屏手机、小笔记本、分屏窗口）表格
   既没卡片化、又要塞下 6 列，名称列被压到 14px 宽：文件名变成一列竖排字、
   行高上千像素、"删除"按钮直接跑到屏幕外。
   现在：≤900px 一律走卡片布局，≥901px 才是桌面表格。
   ================================================================= */

/* --- 手机端触控与分组修正 --- --- */
@media (max-width: 900px) {
  /* 操作按钮改成左对齐：右对齐时"删除"正好落在右手拇指常驻区，
     最容易误触；左对齐后它离手指最远。 */
  .tablewrap.cards td.act { justify-content: flex-start; text-align: left; }

  /* 工具条：每组独占一行，输入框撑满 —— 原来三行的右边缘各不相同，
     看着像没排版（输入框是浏览器默认宽，不随容器伸缩）。 */
  .actions .inline { flex: 1 1 100%; }
  .actions .inline input[type=text],
  .actions .inline input[type=search] { flex: 1 1 auto; min-width: 0; }
}

/* 压缩包里的目录行：可点击、带一点"能进去"的暗示 */
.ziplist a.zipdir { font-weight: 600; }
.ziplist a.zipdir::after { content: " ›"; color: var(--muted); }
.ziplist td.zname a { text-decoration: none; }
.ziplist td.zname a:hover { text-decoration: underline; }

/* --- 打磨：点击区与控件尺寸 --- */
/* 压缩包里的条目是高 19px 的文本链接，手机上不好点：
   用伪元素把命中区域撑到约 31px，视觉位置不变（和文件列表的名字链接同一套做法）。 */
.ziplist td.zname a { position: relative; }
.ziplist td.zname a::after { content: ""; position: absolute; inset: -6px -10px; }

/* 桌面端的复选框原来只有 16px，鼠标点着也偏小 */
@media (min-width: 901px) {
  .tablewrap .pickbox,
  .tablewrap th.pick #pickall { width: 18px; height: 18px; max-width: none; }
}
