@charset "UTF-8";
/* ==========================================================================
   鸡鸣驿 文件中转站 · 统一样式表（下载站风格 · 主色 B 蓝 #1668dc）
   结构：令牌 → 基础 → 外壳/导航 → 登录 → 通用组件 → 上传页 → 列表页 → 弹窗 → 响应式 → 切页过渡
   注意：.page 采用 fixed 覆盖层，body 里的第三方注入元素不会影响布局（防抖动）
   ========================================================================== */

/* ============================ 1. 设计令牌 ============================ */
:root{
  /* 主色 */
  --brand:#1668dc;
  --brand-hover:#1257bb;
  --brand-weak:#eaf2fe;
  --brand-line:#cfe0fb;

  /* 中性色 */
  --bg:#f6f7f9;
  --card:#fff;
  --line:#e8eaed;
  --line-soft:#f0f2f5;
  --text:#1a1a2e;
  --text-2:#5b6472;
  --text-3:#9aa1ab;

  /* 功能色 */
  --ok:#12a150;
  --ok-weak:#e9f7ef;
  --warn:#f5a524;
  --warn-weak:#fef6e7;
  --danger:#e5484d;
  --danger-weak:#fdeceb;

  /* 类型色 */
  --t-video:#7c5cff;  --t-video-bg:#f1ecfe;
  --t-image:#0f9e8e;  --t-image-bg:#e4f7f5;
  --t-archive:#d98a12;--t-archive-bg:#fef3e2;
  --t-doc:#1668dc;    --t-doc-bg:#eaf2fe;
  --t-file:#6b7280;   --t-file-bg:#f0f2f5;

  /* 形状 */
  --r-sm:8px; --r-md:12px; --r-lg:16px; --r-pill:999px;
  --s1:0 1px 2px rgba(16,24,40,.06);
  --s2:0 4px 12px rgba(16,24,40,.08);
  --s3:0 12px 32px rgba(16,24,40,.12);
  --head-h:60px;
}

/* ============================ 2. 基础 ============================ */
/* 根元素：永远可滚动但不显示滚动条 → 浏览器插件往 body 注入元素时不会因滚动条槽变化推动版面 */
html{overflow-y:scroll;scrollbar-width:none;scrollbar-gutter:stable;-webkit-text-size-adjust:100%}
*{box-sizing:border-box;margin:0;padding:0}
body{
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei",Arial,sans-serif;
  background:var(--bg);color:var(--text);font-size:14px;line-height:1.6;
  -webkit-font-smoothing:antialiased;
}
a{color:var(--brand);text-decoration:none}
svg{display:block}
input,button,select,textarea{font-family:inherit;font-size:inherit}
input[type=checkbox]{width:16px;height:16px;accent-color:var(--brand);cursor:pointer;vertical-align:middle;flex:0 0 auto}
input[type=text],input[type=password]{color:var(--text)}
input::placeholder{color:#b8bfc9}
:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

/* 应用滚动外壳：fixed 覆盖层，隔离 body 上被注入的元素（防抖动） */
.page{position:fixed;top:0;left:0;right:0;bottom:0;overflow:auto;scrollbar-gutter:stable;overscroll-behavior:contain}
.page.center{display:flex}

/* ============================ 3. 顶部导航 ============================ */
.site-head{
  position:sticky;top:0;z-index:30;
  background:rgba(255,255,255,.9);backdrop-filter:saturate(180%) blur(12px);
  -webkit-backdrop-filter:saturate(180%) blur(12px);
  border-bottom:1px solid var(--line);
}
.head-in{max-width:1120px;margin:0 auto;padding:0 20px;height:var(--head-h);display:flex;align-items:center;gap:26px}
.brand{display:flex;align-items:center;gap:10px;flex:0 0 auto;color:inherit}
.brand-logo{
  width:34px;height:34px;border-radius:10px;flex:0 0 auto;
  background:linear-gradient(135deg,#3b8cf7,#1257bb);color:#fff;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 4px 10px rgba(22,104,220,.28);
}
.brand-logo svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.brand-name{font-size:16px;font-weight:700;letter-spacing:.3px;color:var(--text)}
.brand-name small{display:block;font-size:11px;font-weight:400;color:var(--text-3);letter-spacing:.2px;line-height:1.2}
.snav{display:flex;align-items:center;gap:4px;flex:1;min-width:0}
.snav a{padding:8px 14px;border-radius:var(--r-sm);font-size:14px;color:var(--text-2);transition:.18s;white-space:nowrap}
.snav a:hover{background:#f1f4f8;color:var(--text)}
.snav a.active{background:var(--brand-weak);color:var(--brand);font-weight:600}
.head-user{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--text-2);flex:0 0 auto}
.avatar{
  width:30px;height:30px;border-radius:50%;flex:0 0 auto;
  background:linear-gradient(135deg,#3b8cf7,#1257bb);color:#fff;
  font-size:12px;font-weight:600;display:flex;align-items:center;justify-content:center;
}
.link-btn{
  background:none;border:0;color:var(--text-3);font-size:13px;cursor:pointer;
  padding:6px 9px;border-radius:var(--r-sm);transition:.18s;
}
.link-btn:hover{color:var(--danger);background:var(--danger-weak)}

/* ============================ 4. 主体容器 ============================ */
.main{max-width:1120px;margin:0 auto;padding:26px 20px 16px}
.main.narrow{max-width:760px}
.page-head{margin-bottom:18px}
.page-head h1{font-size:22px;font-weight:700;letter-spacing:.3px}
.page-head .sub{color:var(--text-2);font-size:13px;margin-top:6px}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--s1)}
.card.pad{padding:22px}
.sub{color:var(--text-2);font-size:13px}

/* ============================ 5. 登录页 ============================ */
.login-card{
  width:100%;max-width:340px;margin:auto;position:relative;overflow:hidden;
  background:#fff;border-radius:18px;box-shadow:var(--s3);padding:38px 30px 30px;text-align:center;
}
.login-card::before{content:"";position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,#3b8cf7,#1257bb)}
.login-logo{
  width:58px;height:58px;margin:0 auto 18px;border-radius:16px;
  background:linear-gradient(135deg,#3b8cf7,#1257bb);color:#fff;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 8px 20px rgba(22,104,220,.3);
}
.login-logo svg{width:28px;height:28px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.login-card h1{font-size:22px;font-weight:700;letter-spacing:.3px}
.login-card .sub{margin:8px 0 26px}
.input-group{margin-bottom:16px;text-align:left}
.input-group label{display:block;font-size:13px;color:var(--text-2);margin-bottom:7px;font-weight:500}
.input-group input{
  width:100%;padding:12px 14px;border:1px solid #d6dbe3;border-radius:10px;
  font-size:16px;background:#fbfcfe;outline:none;transition:.18s;
}
.input-group input:focus{border-color:var(--brand);background:#fff;box-shadow:0 0 0 3px rgba(22,104,220,.12)}
.error-msg{
  display:none;margin-bottom:14px;padding:10px 12px;border-radius:var(--r-sm);
  font-size:13px;color:#b32b30;background:var(--danger-weak);border:1px solid #f4c7c7;text-align:left;
}

/* ============================ 6. 按钮 ============================ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:11px 26px;border:1px solid transparent;border-radius:10px;
  font-size:15px;cursor:pointer;transition:.18s;
  background:var(--brand);color:#fff;box-shadow:0 2px 8px rgba(22,104,220,.22);
}
.btn:hover{background:var(--brand-hover);box-shadow:0 4px 14px rgba(22,104,220,.28)}
.btn:disabled{background:#e3e7ee;color:#a2aab6;box-shadow:none;cursor:not-allowed}
.btn.block{width:100%}
.btn.danger{background:var(--danger);box-shadow:0 2px 8px rgba(229,72,77,.22)}
.btn.danger:hover{background:#cf3b40;box-shadow:0 4px 14px rgba(229,72,77,.28)}
.btn-row{display:flex;gap:10px;margin-top:18px}
.btn-row .btn{flex:0 0 auto}

/* ============================ 7. 文件类型方标 ============================ */
.ft{width:36px;height:36px;border-radius:10px;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center}
.ft svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.ft-sm{width:28px;height:28px;border-radius:8px}
.ft-sm svg{width:15px;height:15px}
.ft-video{background:var(--t-video-bg);color:var(--t-video)}
.ft-image{background:var(--t-image-bg);color:var(--t-image)}
.ft-archive{background:var(--t-archive-bg);color:var(--t-archive)}
.ft-doc{background:var(--t-doc-bg);color:var(--t-doc)}
.ft-file{background:var(--t-file-bg);color:var(--t-file)}

/* ============================ 8. 上传页 ============================ */
.upload-zone{
  position:relative;border:2px dashed #c6d1e0;border-radius:14px;background:#fbfcfe;
  padding:38px 20px;text-align:center;cursor:pointer;transition:.2s;
}
.upload-zone:hover{border-color:var(--brand);background:var(--brand-weak)}
.upload-zone.dragover{border-color:var(--brand);background:var(--brand-weak);box-shadow:0 0 0 4px rgba(22,104,220,.1)}
.uz-ico{
  width:56px;height:56px;margin:0 auto 14px;border-radius:16px;background:#fff;
  border:1px solid var(--brand-line);color:var(--brand);
  display:flex;align-items:center;justify-content:center;box-shadow:var(--s1);
}
.uz-ico svg{width:26px;height:26px;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.upload-zone .hint{font-size:15px;font-weight:500;color:var(--text);line-height:1.5}
.upload-zone .hint small{display:block;font-size:12px;font-weight:400;color:var(--text-3);margin-top:6px}
#fileInput{display:none}
.limit-hint{margin-top:16px;text-align:center;font-size:12px;color:var(--text-3)}

/* 已选文件清单 */
#fileList{display:none;text-align:left;margin-top:16px}
.file-item{
  display:flex;align-items:center;gap:10px;margin-bottom:6px;padding:10px 12px;
  background:#f9fafc;border:1px solid var(--line);border-radius:10px;font-size:13px;
}
.file-item .file-name{flex:1;min-width:0;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.file-item .file-size{flex:0 0 auto;font-size:12px;color:var(--text-3)}

/* 上传进度卡片 */
#progressList{display:none;text-align:left;margin-top:16px}
.prog-card{
  display:none;margin-top:8px;padding:14px;background:#fff;
  border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--s1);transition:border-color .2s;
}
.prog-card .prog-header{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.prog-card .prog-name{
  flex:1;min-width:0;display:flex;align-items:center;gap:9px;
  font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.prog-card .prog-name .pname{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.prog-card .prog-status{
  flex:0 0 auto;padding:3px 10px;border-radius:var(--r-pill);
  font-size:12px;color:var(--text-2);background:#f0f2f5;white-space:nowrap;
}
.prog-card .prog-bar-bg{height:8px;border-radius:var(--r-pill);background:#eef1f5;overflow:hidden}
.prog-card .prog-bar{
  height:100%;width:0;border-radius:var(--r-pill);
  background:linear-gradient(90deg,#3b8cf7,#1668dc);transition:width .15s ease;
}
.prog-card .prog-detail{display:flex;justify-content:space-between;margin-top:7px;font-size:12px;color:var(--text-3)}
.prog-card.done{border-color:#c4e8d3}
.prog-card.done .prog-bar{background:linear-gradient(90deg,#2bbd6a,#12a150)}
.prog-card.done .prog-status{color:#0f7a3d;background:var(--ok-weak)}
.prog-card.error{border-color:#f4c7c7}
.prog-card.error .prog-bar{background:linear-gradient(90deg,#f0716f,#e5484d)}
.prog-card.error .prog-status{color:#b32b30;background:var(--danger-weak)}
.prog-card.dup{border-color:#f7dfae}
.prog-card.dup .prog-bar{background:linear-gradient(90deg,#ffc65c,#f5a524);width:100%!important}
.prog-card.dup .prog-status{color:#8a5b06;background:var(--warn-weak)}

/* 分享链接条 */
.share-box{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:12px;padding:10px;
  background:var(--brand-weak);border:1px solid var(--brand-line);border-radius:10px;
  font-size:12px;color:var(--brand);
}
.share-box input{
  flex:1;min-width:120px;padding:8px 10px;border:1px solid var(--brand-line);border-radius:7px;
  font-size:12px;color:var(--text-2);background:#fff;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
.share-box button{
  padding:8px 14px;border:0;border-radius:7px;background:var(--brand);color:#fff;
  font-size:12px;cursor:pointer;white-space:nowrap;transition:.18s;
}
.share-box button:hover{background:var(--brand-hover)}

/* ============================ 9. 文件列表页 ============================ */
.stat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-bottom:18px}
.stat-card{
  display:flex;align-items:center;gap:14px;padding:16px 18px;background:#fff;
  border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--s1);
}
.sc-ico{width:42px;height:42px;border-radius:12px;flex:0 0 auto;display:flex;align-items:center;justify-content:center}
.sc-ico svg{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.sc-blue{background:var(--brand-weak);color:var(--brand)}
.sc-teal{background:var(--t-image-bg);color:var(--t-image)}
.sc-violet{background:var(--t-video-bg);color:var(--t-video)}
.sc-num{font-size:22px;font-weight:700;line-height:1.25;letter-spacing:.3px}
.sc-label{font-size:12px;color:var(--text-3);margin-top:2px}

.toolbar{
  position:sticky;top:calc(var(--head-h) + 8px);z-index:20;
  display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin-bottom:14px;padding:12px 14px;
  background:rgba(255,255,255,.96);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--s1);
  font-size:13px;color:var(--text-2);
}
.toolbar .sel-info{color:var(--text-2);margin-right:auto}
.toolbar .sel-info b{color:var(--brand);font-size:15px;padding:0 2px}
.tool-btn{
  padding:8px 16px;border:1px solid var(--line);border-radius:9px;background:#fff;
  color:var(--text-2);font-size:13px;cursor:pointer;transition:.18s;white-space:nowrap;
}
.tool-btn:hover{background:#f6f8fa;border-color:#d4dae2;color:var(--text)}
.tool-btn.danger{border-color:#f4c7c7;color:var(--danger)}
.tool-btn.danger:hover{background:var(--danger-weak);border-color:#e9a5a5}
.tool-btn:disabled,.tool-btn.danger:disabled{
  color:#b7bec9;background:#f6f7f9;border-color:var(--line);cursor:not-allowed;
}

.table-wrap{background:#fff;border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--s1);overflow-x:auto}
table{width:100%;border-collapse:collapse;font-size:13px;min-width:940px}
th{
  background:#f8fafc;color:var(--text-2);font-weight:600;text-align:left;
  padding:12px 14px;white-space:nowrap;border-bottom:1px solid var(--line);
}
td{padding:12px 14px;border-bottom:1px solid var(--line-soft);color:var(--text);vertical-align:middle}
tbody tr:last-child td{border-bottom:none}
tbody tr{transition:background .15s}
tbody tr:hover td{background:#fafbfd}
tbody tr.selected td{background:var(--brand-weak)}
.col-check{width:44px;text-align:center}
.col-num{width:60px;color:var(--text-3)}
.col-op{width:92px;text-align:center}
.fname{display:flex;align-items:center;gap:10px;min-width:200px}
.fname a{color:var(--text);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:300px;transition:.18s}
.fname a:hover{color:var(--brand)}
.nowrap{white-space:nowrap}
.share-cell{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-width:290px}
.share-cell input{
  flex:1 1 100%;min-width:0;padding:7px 10px;border:1px solid var(--line);border-radius:7px;
  font-size:12px;color:var(--text-2);background:#fafbfc;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
.copy-btn,.dl-btn,.qr-btn,.limit-btn{
  display:inline-flex;align-items:center;gap:5px;padding:7px 12px;border:0;border-radius:var(--r-sm);
  background:var(--brand-weak);color:var(--brand);font-size:12px;cursor:pointer;transition:.18s;white-space:nowrap;
  text-decoration:none;
}
.copy-btn:hover,.qr-btn:hover,.limit-btn:hover{background:#dbe8fb}
.dl-btn{background:var(--brand);color:#fff}
.dl-btn:hover{background:var(--brand-hover)}
.del-btn{
  display:inline-block;padding:7px 13px;border:1px solid #f4c7c7;border-radius:var(--r-sm);
  background:#fff;color:var(--danger);font-size:12px;cursor:pointer;transition:.18s;white-space:nowrap;
}
.del-btn:hover{background:var(--danger-weak);border-color:#e9a5a5}

/* 分享码开关（列表行内）：固定占一整行，落在「下载 / 复制 / 二维码 / 限次」这一行的下面，
   开关打开后多出来的分享码字符不会再跟其它按钮抢位置（PC 与手机同一排法） */
.code-cell{display:flex;flex:1 1 100%;align-items:center;gap:8px;min-height:22px}
.sw{display:inline-flex;align-items:center;gap:7px;cursor:pointer;-webkit-user-select:none;user-select:none}
.sw input{position:absolute;width:0;height:0;opacity:0}
.sw-track{
  position:relative;flex:0 0 auto;width:34px;height:19px;border-radius:var(--r-pill);
  background:#d6dbe3;transition:.2s;
}
.sw-dot{
  position:absolute;top:2px;left:2px;width:15px;height:15px;border-radius:50%;background:#fff;
  box-shadow:0 1px 3px rgba(16,24,40,.22);transition:.2s;
}
.sw input:checked + .sw-track{background:var(--brand)}
.sw input:checked + .sw-track .sw-dot{left:17px}
.sw input:focus-visible + .sw-track{outline:2px solid var(--brand);outline-offset:2px}
.sw input:disabled + .sw-track{opacity:.55;cursor:not-allowed}
.sw-text{font-size:12px;color:var(--text-3);transition:.18s}
.code-cell.on .sw-text{color:var(--brand)}
.code-val{
  display:none;padding:3px 9px;border:1px solid var(--brand-line);border-radius:6px;
  background:var(--brand-weak);color:var(--brand);font-size:12px;font-weight:600;letter-spacing:1px;
  cursor:pointer;transition:.18s;white-space:nowrap;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
.code-cell.on .code-val{display:inline-block}
.code-val:hover{background:#dbe8fb}

/* 下载次数上限（列表行内）：按钮挨着分享码开关，点开是带输入框的弹窗 */
.limit-cell{display:flex;flex:0 1 auto;align-items:center;min-height:22px}
.limit-cell.on .limit-btn{box-shadow:inset 0 0 0 1px var(--brand-line);font-weight:600}
.limit-btn:disabled{opacity:.6;cursor:not-allowed}
/* 次数用满：按钮和次数胶囊一起转红，一眼看出链接已经停了 */
.limit-cell.usedup .limit-btn{background:var(--danger-weak);color:var(--danger);box-shadow:inset 0 0 0 1px #e9a5a5}
.dl-count.full{background:var(--danger-weak);color:var(--danger)}
.dl-limit{margin-left:2px;font-size:12px;font-weight:600;color:var(--text-3)}
.dl-limit.full{color:var(--danger)}
.dl-count{
  display:inline-block;min-width:36px;padding:3px 8px;border-radius:7px;text-align:center;
  font-size:12px;font-weight:600;color:var(--brand);background:var(--brand-weak);
}
.empty{padding:56px 10px;text-align:center;color:var(--text-3);font-size:14px}
.empty svg{width:52px;height:52px;margin:0 auto 14px;fill:none;stroke:#c8d0da;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.pager{display:flex;justify-content:center;align-items:center;gap:12px;margin-top:20px;font-size:13px;color:var(--text-3)}
.pager a{padding:8px 16px;border:1px solid var(--brand-line);border-radius:9px;background:#fff;transition:.18s}
.pager a:hover{background:var(--brand-weak)}
.pager span{padding:8px 4px}
.tip{margin-top:16px;text-align:left;font-size:12px;color:var(--text-3);line-height:1.8}
/* 全站页脚版权行：写法参考 bookmark.nanlou.net（Copyright © 年份 主体 版权所有） */
.site-foot{
  margin-top:26px;padding-top:16px;border-top:1px solid var(--line);
  text-align:center;font-size:12px;color:var(--text-3);line-height:1.7;
}
.site-foot p{margin:0}
/* 居中卡片里的页脚（登录页 / 分享码页 / 失效页）：贴卡片底部，不加分隔线 */
.site-foot.in-card{margin-top:22px;padding-top:0;border-top:0}
.err{
  margin-bottom:16px;padding:12px 14px;border-radius:10px;border:1px solid #f4c7c7;
  background:var(--danger-weak);color:#b32b30;font-size:13px;
}

/* ============================ 10. 弹窗（自绘，替代系统 confirm / alert） ============================ */
.modal-mask{
  position:fixed;inset:0;z-index:80;display:none;
  align-items:center;justify-content:center;padding:20px;
  background:rgba(16,24,40,.45);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
}
.modal-mask.open{display:flex;animation:maskIn .18s ease}
.modal{
  width:100%;max-width:364px;padding:26px 24px 20px;background:#fff;
  border-radius:16px;box-shadow:var(--s3);text-align:center;
  animation:modalIn .22s cubic-bezier(.2,.85,.3,1.05);
}
.modal-ico{
  width:52px;height:52px;margin:0 auto 14px;border-radius:15px;
  display:flex;align-items:center;justify-content:center;
  background:var(--danger-weak);color:var(--danger);
}
.modal-ico svg{width:26px;height:26px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.modal-ico.warn{background:var(--warn-weak);color:#c47d0a}
.modal-title{font-size:17px;font-weight:700;letter-spacing:.2px}
.modal-desc{margin-top:9px;font-size:13px;color:var(--text-2);line-height:1.7;word-break:break-word}
/* 待删除文件清单：默认只展示前几个，其余折行成一句 */
.modal-list{display:flex;flex-direction:column;gap:5px;margin-top:11px;max-height:132px;overflow:auto;text-align:left}
.modal-list span{
  padding:7px 10px;border:1px solid var(--line);border-radius:8px;background:#f8fafc;
  font-size:12px;color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.modal-list span.more{background:#fff;border-style:dashed;color:var(--text-3);text-align:center}
.modal-note{
  margin-top:12px;padding:9px 11px;border-radius:9px;text-align:left;
  background:var(--warn-weak);border:1px solid #f7dfae;color:#8a5b06;font-size:12px;line-height:1.65;
}
/* 需要填数字的弹窗（限次）：输入框居中放大，手机上键盘类型走数字、字号 16px 防自动缩放 */
.modal-input{display:none;margin-top:14px}
.modal-input.on{display:block}
.modal-input input{
  width:100%;padding:11px 13px;border:1px solid var(--line);border-radius:10px;background:#fafbfc;
  font-size:16px;color:var(--text);text-align:center;letter-spacing:1px;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
.modal-input input:focus{outline:0;border-color:var(--brand);background:#fff;box-shadow:0 0 0 3px rgba(22,104,220,.12)}
.modal-input input::placeholder{letter-spacing:0;font-size:13px}
.modal-actions{display:flex;gap:10px;margin-top:22px}
.modal-actions .btn{flex:1;padding:11px 0;font-size:14px}
.btn.ghost{background:#fff;border-color:#d6dbe3;color:var(--text-2);box-shadow:none}
.btn.ghost:hover{background:#f6f8fa;border-color:#c8d0da;color:var(--text);box-shadow:none}
/* 弹窗打开时锁住背后的滚动，避免滚轮把列表带跑 */
.page.lock{overflow:hidden}
/* 分享二维码弹窗：白底方框保证扫码对比度，手机上一眼能扫 */
.qr-modal{max-width:320px}
.qr-name{margin-top:6px;font-size:12px;color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.qr-box{
  width:212px;height:212px;margin:16px auto 0;padding:8px;
  display:flex;align-items:center;justify-content:center;
  background:#fff;border:1px solid var(--line);border-radius:12px;
}
.qr-box svg{display:block;width:100%;height:100%}
.qr-box.fail{display:block;width:auto;height:auto;padding:14px;font-size:12px;color:var(--text-2);line-height:1.7;background:#fafbfc}
.qr-link{
  margin-top:12px;padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:#f8fafc;
  font-size:11px;color:var(--text-2);line-height:1.6;word-break:break-all;text-align:left;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
@keyframes maskIn{from{opacity:0}to{opacity:1}}
@keyframes modalIn{from{opacity:0;transform:translateY(10px) scale(.97)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.modal-mask.open,.modal{animation:none}}

/* ============================ 11. 设备互传（聊天页） ============================ */
/* 这一页是「应用外壳」：顶部栏 + 消息区（自己滚动）+ 底部输入条。
   只在 .page.chat-page 上用 flex 撑满，其它页面保持原来的文档流，互不影响。 */
.page.chat-page{display:flex;flex-direction:column}
.chat-page > .site-head{flex:0 0 auto}
.main.chat{flex:1;min-height:0;width:100%;display:flex;flex-direction:column;padding:18px 20px 12px}

/* --- 房间信息条 --- */
/* 左栏是两行（标题 / 状态），右栏（房码徽章 + 二维码 + 换房码）只有一行：
   交叉轴用 flex-start，右栏才会跟左栏首行对齐；否则它会居中于两行的左栏、比房码低半行。
   三个元素统一 28px 高、间距统一 8px，看上去才是一条线 */
.chat-bar{
  display:flex;align-items:flex-start;gap:8px;flex:0 0 auto;
  padding:10px 14px;background:#fff;border:1px solid var(--line);border-bottom:0;
  border-radius:var(--r-md) var(--r-md) 0 0;
}
.cb-left{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
/* 标题行：房码徽章与「我的设备」同一行，靠右对齐 */
.cb-row{display:flex;align-items:center;gap:8px;min-width:0}
.cb-title{font-size:14px;font-weight:600;white-space:nowrap}
.cb-meta{font-size:12px;color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cb-right{flex:0 0 auto;display:flex;align-items:center;gap:8px}
.cb-btn{
  display:inline-flex;align-items:center;justify-content:center;height:28px;padding:0 12px;
  border:1px solid var(--brand-line);border-radius:8px;background:#fff;
  color:var(--brand);font-size:12px;cursor:pointer;transition:.18s;white-space:nowrap;
}
.cb-btn:hover{background:var(--brand-weak)}
.cb-code{
  flex:0 0 auto;margin-left:auto;display:inline-flex;align-items:center;gap:5px;height:28px;
  padding:0 10px;border-radius:var(--r-pill);background:var(--brand-weak);
  border:1px solid var(--brand-line);font-size:12px;color:var(--text-3);
  line-height:1;white-space:nowrap;
}
.cb-code b{
  color:var(--brand);font-weight:600;letter-spacing:1.5px;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}

/* --- 二维码折叠面板 --- */
.chat-qr{
  flex:0 0 auto;padding:16px 14px 14px;background:#fff;border:1px solid var(--line);border-top:0;
  text-align:center;
}
.chat-qr[hidden]{display:none}
.chat-code{margin-top:10px;font-size:14px;color:var(--text-2)}
.chat-code b{letter-spacing:3px;font-size:18px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.chat-qr-btns{display:flex;gap:8px;justify-content:center;margin-top:12px}
.chat-qr-tip{margin-top:12px;font-size:12px;color:var(--text-3);line-height:1.7;text-align:left}

/* --- 对话区 --- */
.chat-panel{
  flex:1;min-height:0;display:flex;flex-direction:column;
  background:#fff;border:1px solid var(--line);box-shadow:var(--s1);
  border-radius:0 0 var(--r-md) var(--r-md);transition:border-color .18s,box-shadow .18s;
}
.chat-panel.dropping{border-color:var(--brand);box-shadow:0 0 0 3px rgba(22,104,220,.12)}
.chat-body{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;padding:16px 14px 4px;display:flex;flex-direction:column}
.chat-msgs{display:flex;flex-direction:column;gap:12px}
.chat-empty{margin:auto;padding:20px 10px;text-align:center;font-size:13px;color:var(--text-3);line-height:2}
.chat-empty[hidden]{display:none}
.chat-time{margin:6px 0;align-self:center;font-size:11px;color:var(--text-3)}
/* 系统提示：仍是居中一行小胶囊，不受上面「头像 + 内容」那套影响 */
.msg.sys{display:block;max-width:none;align-self:center;text-align:center;font-size:11px;color:var(--text-3)}
.msg.sys span{display:inline-block;padding:4px 10px;border-radius:var(--r-pill);background:#f2f4f7;line-height:1.7;max-width:80%}

/* 气泡：外侧是发送人头像，里面竖排「名字 / 气泡 / 时间」。
   左右看「是不是我发的」（自己靠右、对方靠左），颜色看「发送人身份」：
   房主发的绿、访客发的白 —— 同一条消息在哪台设备上看，颜色和名字都一样 */
.msg{display:flex;gap:8px;align-items:flex-start;max-width:min(600px,86%)}
.msg.me{flex-direction:row-reverse;align-self:flex-end}   /* row-reverse：头像留在外侧（右边） */
.msg.them{align-self:flex-start}
.msg-main{flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start}
.msg.me .msg-main{align-items:flex-end}
.msg-ava{
  flex:0 0 auto;width:34px;height:34px;border-radius:50%;overflow:hidden;
  display:flex;align-items:center;justify-content:center;
  font-size:13px;font-weight:600;color:#fff;user-select:none;
}
.msg.me .msg-ava{background:linear-gradient(135deg,#3b8cf7,#1257bb)}
.msg.them .msg-ava{background:linear-gradient(135deg,#9aa4b2,#68707d)}
/* 对方的名字（自己一侧不写名字，按聊天的习惯） */
.msg-who{margin:0 0 4px 2px;font-size:11px;color:var(--text-3);line-height:1.4}
.bubble{padding:10px 12px;border-radius:14px;font-size:13px;line-height:1.6;word-break:break-word;box-shadow:var(--s1)}
/* 颜色按发送人身份：房主发的用浅蓝底 + 深色字；访客发的白底描边。
   和名字、左右位置一样，在哪台设备上看都一致 */
.msg.host .bubble{background:#bfdfff;color:#141414}
.msg.peer .bubble{background:#fff;border:1px solid var(--line)}
/* 尖角朝自己那一侧（这是位置，不是身份） */
.msg.me .bubble{border-bottom-right-radius:5px}
.msg.them .bubble{border-bottom-left-radius:5px}
.bub-head{display:flex;align-items:center;gap:8px;min-width:0}
.bub-name{min-width:0;max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.bub-text{white-space:pre-wrap}
/* 图片气泡：只放小缩略图（服务端给的 webp，等比缩放不裁切），点开才是原图。
   display:table 让盒子贴着图片收缩（不能撑满，否则图片窄的那截会露出灰色占位底），
   再把左右 margin 设成 auto，缩略图就在气泡里居中了。
   气泡里的子元素都是 JS createElement 拼的，没有空白文本节点，不用担心行盒间隙 */
.bub-img{display:table;max-width:100%;margin:8px auto 0;border-radius:10px;overflow:hidden;background:#f2f4f7;cursor:zoom-in}
.bub-img img{display:block;width:auto;height:auto;max-width:200px;max-height:200px}
.msg.host .bub-img{background:rgba(0,0,0,.06)}
.bub-info{display:flex;flex-wrap:wrap;gap:8px;margin-top:7px;font-size:11px;opacity:.88}
.bub-tag{padding:1px 7px;border-radius:var(--r-pill);background:rgba(255,255,255,.22)}
.msg.host .bub-tag{background:rgba(255,255,255,.6)}
.msg.peer .bub-tag{background:var(--ok-weak);color:#0f7a3d}
.bub-btns{display:flex;gap:6px;margin-top:9px;flex-wrap:wrap}
.bub-btn{
  padding:5px 11px;border:1px solid transparent;border-radius:8px;font-size:12px;cursor:pointer;
  white-space:nowrap;background:rgba(255,255,255,.18);color:inherit;transition:.18s;
}
.msg.peer .bub-btn{background:#f4f6f9;color:var(--text-2)}
.bub-btn:hover{filter:brightness(.96)}
.msg.host .bub-btn{color:#141414;background:rgba(255,255,255,.55)}
.msg.host .bub-btn.ghost{background:rgba(255,255,255,.32)}
.msg.peer .bub-btn.ghost{background:#fff;border-color:var(--brand-line);color:var(--brand)}
.bub-btn.danger{background:var(--danger);color:#fff}
.msg.host .bub-btn.danger{background:var(--danger);color:#fff}   /* 绿底上的「删除」仍红底白字 */
.msg.peer .bub-btn.danger{background:var(--danger-weak);color:#b32b30}
/* 时间那一行：能撤回的内容后面跟一枚「撤回」小胶囊（只有自己发的和房主看得到） */
.bub-time{display:flex;align-items:center;gap:6px;margin-top:4px;font-size:11px;color:var(--text-3)}
.bub-revoke{
  position:relative;padding:2px 9px;border:0;border-radius:var(--r-pill);background:#f2f4f7;color:var(--text-2);
  font:inherit;font-size:11px;line-height:1.6;cursor:pointer;transition:.18s;
}
.bub-revoke:hover{background:var(--danger-weak);color:var(--danger)}

/* 撤回后的占位：仍是居中一行，换成虚线框 + 斜体，一眼看出「这里原本有内容，已经收回」 */
.msg.sys.revoked span{background:transparent;border:1px dashed var(--line);color:var(--text-3);font-style:italic}

/* --- 图片大图预览：点气泡里的缩略图弹这一层，只看不下载 --- */
.img-view{
  position:fixed;inset:0;z-index:90;display:none;flex-direction:column;
  background:rgba(16,24,40,.86);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);
}
.img-view.open{display:flex;animation:maskIn .18s ease}
/* 滚动容器：图片放到原图尺寸、比屏幕大时，就靠它拖动查看。
   用 margin:auto 而不是 align-items:center —— 后者会让溢出部分滚不到左上角。 */
/* touch-action: 单指/双指平移照样能滚，但浏览器自己那套整页捏合缩放交给 JS 处理
   （捏合时再 preventDefault），否则双指一捏会把整个页面放大 */
.iv-stage{flex:1;min-height:0;display:flex;overflow:auto;padding:56px 24px 48px;
  touch-action:pan-x pan-y;overscroll-behavior:contain}
/* 刚打开先给一张「适应屏幕的小图」 */
.img-view img{
  margin:auto;display:block;
  max-width:min(72vw,640px);max-height:64vh;
  border-radius:10px;background:#fff;box-shadow:var(--s3);cursor:zoom-in;
}
/* JS 一旦接管尺寸（点开原图 / 滚轮缩放），就撤掉 CSS 的适屏上限，
   由内联的像素宽高说了算；否则 1:1 的大图会被这两个 max-* 压回去 */
.img-view img.free{max-width:none;max-height:none}
.img-view .iv-close{
  position:absolute;top:14px;right:16px;width:38px;height:38px;border:0;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.16);color:#fff;font-size:20px;line-height:1;cursor:pointer;
}
.img-view .iv-close:hover{background:rgba(255,255,255,.3)}
.img-view .iv-name{
  position:absolute;left:0;right:0;bottom:14px;padding:0 24px;text-align:center;
  font-size:12px;color:rgba(255,255,255,.85);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* 预览打开时锁住背景滚动 */
body.lock{overflow:hidden}

/* 发送中的气泡：进度条 */
.bub-bar{height:6px;margin-top:9px;border-radius:var(--r-pill);background:rgba(255,255,255,.35);overflow:hidden}
.bub-bar i{display:block;height:100%;width:0;border-radius:var(--r-pill);background:#fff;transition:width .18s ease}
/* 浅蓝气泡里的进度条换成深色，否则白条在浅蓝上看不清 */
.msg.host .bub-bar{background:rgba(0,0,0,.12)}
.msg.host .bub-bar i{background:var(--brand)}

/* --- 下载进度：「下载」按钮的下边一行（文字 + 暂停/取消，再下面一条进度条） ---
   这里的进度条不复用 .bub-bar：那边是给「发送中」的气泡调的（白/品牌蓝填充），
   而下载这一行在浅蓝底（房主发的）和纯白底（访客发的）两种气泡上都要看得清，
   所以轨道和填充都用中性色自己定；文字靠 .ok / .bad 两个状态类换色。
   （状态色是按 WCAG AA 4.5:1 挑的：#0b5c2e 在纯白和 #bfdfff 上都达标，失败红用 #b32b30） */
.bub-dl{margin-top:9px;padding-top:9px;border-top:1px solid rgba(0,0,0,.09)}
.bub-dl[hidden],.bub-dl-bar[hidden]{display:none}
/* 这一行的宽度是定死的（下载开始时由 JS 量好写上去，见 transfer.js 的 dlFreeze）：
   气泡本身按内容撑宽，进度文字一变长气泡就变宽，里头的按钮跟着挪 —— 「暂停」眼看就跑了。
   宽度定死之后，按钮用 order:-1 占第一行，文字排在它下面（min-width 让它放不下就整块换行，
   而不是被省略号切掉尾巴），于是：气泡宽度、按钮位置都只跟行宽有关，跟文字长短无关。
   align-items:flex-start 是防「按钮行跟文字并排时被文字行数带着上下跑」。 */
.bub-dl-top{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap}
.bub-dl-txt{flex:1 1 auto;min-width:140px;font-size:11.5px;line-height:1.6;overflow-wrap:break-word}
.bub-dl-btns{display:flex;gap:6px;flex:0 0 auto;order:-1}
.bub-dl-btns .bub-btn{padding:3px 9px;font-size:11.5px}
.bub-dl-bar{height:5px;margin-top:7px;border-radius:var(--r-pill);background:rgba(0,0,0,.12);overflow:hidden}
.bub-dl-bar i{display:block;height:100%;width:0;border-radius:var(--r-pill);background:var(--brand);transition:width .18s ease}
.bub-dl.ok .bub-dl-txt{color:#0b5c2e}
.bub-dl.bad .bub-dl-txt{color:#b32b30}

/* --- 底部输入条 --- */
.chat-compose{flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:10px 0 0}
.compose-add{
  flex:0 0 auto;width:40px;height:40px;border-radius:50%;border:1px solid var(--brand-line);
  background:#fff;color:var(--brand);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.18s;
}
.compose-add:hover{background:var(--brand-weak)}
.compose-add svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}
.compose-input{
  flex:1;min-width:0;padding:11px 14px;border:1px solid #d6dbe3;border-radius:10px;
  font-size:15px;background:#fff;outline:none;transition:.18s;
}
.compose-input:focus{border-color:var(--brand);box-shadow:0 0 0 3px rgba(22,104,220,.12)}
.compose-send{flex:0 0 auto;padding:11px 20px;font-size:14px}

/* 提示条与「有新消息」浮标 */
/* 提示条（已撤回 / 已删除 / 上传完成…）落在窗口正中，微信那种：原来贴在输入框上面太靠下了。
   pointer-events:none 是必要的——它只是提示，浮在中间时不能挡住气泡上的点击 */
.chat-toast{
  position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:50;max-width:82vw;
  padding:9px 14px;border-radius:var(--r-pill);background:rgba(26,26,46,.9);color:#fff;
  font-size:12px;line-height:1.6;text-align:center;pointer-events:none;
}
.chat-toast[hidden]{display:none}
.chat-newmsg{
  position:fixed;right:18px;bottom:78px;z-index:50;padding:7px 13px;
  border:1px solid var(--brand-line);border-radius:var(--r-pill);background:#fff;color:var(--brand);
  font-size:12px;cursor:pointer;box-shadow:var(--s2);
}
.chat-newmsg[hidden]{display:none}

/* 聊天页里的版权行：固定在底部输入条下方（不跟消息滚动），做成一条居中小灰字 */
.site-foot.in-chat{flex:0 0 auto;margin:12px 0 0;padding-top:0;border-top:0;text-align:center}

@media (max-width:640px){
  .main.chat{padding:12px 14px 10px}
  .chat-bar{padding:9px 12px;flex-wrap:wrap;row-gap:6px}
  /* 让「标题+房码」「状态」「按钮」各自参与换行：房码徽章才能贴到信息条最右边 */
  .cb-left{display:contents}
  .cb-row{flex:1 1 100%}
  .cb-meta{flex:1 1 auto;min-width:0;white-space:normal}
  .cb-right{flex:0 0 auto}
  .cb-title{font-size:13px}
  .cb-code{padding:0 8px;font-size:11px}
  .cb-code b{font-size:12px;letter-spacing:1px}
  .chat-body{padding:12px 10px 4px}
  .msg{max-width:92%}
  .msg-ava{width:30px;height:30px;font-size:12px}
  .bub-name{max-width:52vw}
  /* 手机上缩略图：宽度放开了，高度仍压住，竖图不会把气泡顶到半屏高 */
  .bub-img img{max-width:180px;max-height:132px}
  .compose-send{padding:11px 14px}
  /* 「有新消息」浮标跟着输入框走；提示条已经在窗口正中，这里不要再给它 bottom（否则会跟 top 一起把它拉变形） */
  .chat-newmsg{bottom:74px}
  /* 「撤回」胶囊：小屏要让手指点得中，用伪元素把可点区域往外撑一圈（约 34×33），
     胶囊本身的排版尺寸不变 —— 不会把时间行顶高，也不会顶到上面的气泡上 */
  .bub-time{font-size:12px}
  .bub-revoke{padding:2px 10px}
  .bub-revoke::after{content:"";position:absolute;left:-7px;right:-7px;top:-7px;bottom:-7px}
  .iv-stage{padding:46px 12px 40px}
  .img-view img{max-width:84vw;max-height:58vh}
  .img-view .iv-close{top:8px;right:10px}
}

/* ============================ 12. 响应式 ============================ */
@media (max-width:900px){
  .stat-grid{grid-template-columns:1fr}
  /* 表格 → 卡片行（纯 CSS 改造，DOM 结构不变） */
  .table-wrap{border:0;box-shadow:none;background:transparent;overflow:visible}
  table{min-width:0;display:block}
  thead{display:none}
  tbody{display:block}
  tbody tr{
    display:block;position:relative;margin-bottom:10px;padding:13px 14px;
    background:#fff;border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--s1);
  }
  tbody tr:hover td,tbody tr.selected td{background:transparent}
  tbody tr.selected{background:var(--brand-weak);border-color:var(--brand-line)}
  tbody tr:last-child td{border-bottom:0}
  td{display:block;padding:0;border:0}
  td.col-check{position:absolute;top:13px;right:14px;width:auto}
  td.col-check input{width:18px;height:18px}
  td.col-num{width:auto;display:inline-block;margin-bottom:9px;padding:2px 8px;border-radius:6px;background:#f2f4f7;font-size:11px}
  td:nth-child(3) .fname{gap:9px;padding-right:28px}
  td:nth-child(3) .fname a{max-width:none;white-space:normal;word-break:break-all}
  td:nth-child(4),td:nth-child(5),td:nth-child(7){display:inline-block;margin-top:9px;font-size:12px;color:var(--text-3)}
  td:nth-child(4){margin-right:12px}
  td:nth-child(4)::before{content:"大小 "}
  td:nth-child(5)::before{content:"上传于 "}
  /* 下载次数列按类名定位：管理组比普通会员多一列「上传者」，
     用 nth-child 定位会整体错位（「次」跑到分享链接列末尾、次数只剩一个数字） */
  td.col-dl{display:inline-block;margin-top:9px;font-size:12px;color:var(--text-3)}
  td.col-dl::before{content:"已下载 "}
  td.col-dl::after{content:" 次"}
  td:nth-child(6){margin-top:11px}
  /* 分享区：链接输入框独占一行、下载 / 复制 / 二维码 / 限次一行、分享码开关再一行（见 .share-cell 基础规则） */
  .share-cell{min-width:0}
  /* 限次按钮同样排进这行，窄屏放不下就整块换行，不做压缩 */
  .limit-cell{min-height:34px}
  td.col-op{width:auto;margin-top:11px;text-align:left}
  .del-btn{width:100%;padding:10px}
  .toolbar{top:0;position:static;padding:11px 12px}
  .toolbar .sel-info{flex:1 0 auto}
  .tool-btn{flex:1 1 auto}
}
@media (max-width:640px){
  .main{padding:18px 14px 16px}
  .head-in{height:auto;padding:10px 14px;flex-wrap:wrap;gap:10px}
  .brand-name small{display:none}
  .snav{order:3;flex:1 0 100%;gap:6px;overflow-x:auto;padding-bottom:2px}
  .snav a{padding:7px 12px;font-size:13px}
  .head-user{margin-left:auto}
  .uname{display:none}
  .card.pad{padding:16px}
  .upload-zone{padding:30px 16px}
  .btn-row{flex-direction:column}
  .btn-row .btn{width:100%}
  .prog-card .prog-name{white-space:normal}
  .login-card{padding:34px 24px 28px;border-radius:16px}
  /* 小屏：分享区按钮加大点击区（下载 / 复制 / 二维码 / 限次），手指点得中 */
  .share-cell .dl-btn,.share-cell .copy-btn,.share-cell .qr-btn,.share-cell .limit-btn{padding:9px 13px}
  .share-cell .limit-btn{min-height:34px}
  .sc-num{font-size:20px}
  .modal{padding:22px 18px 18px;border-radius:14px}
  .modal-list{max-height:108px}
}

/* ============================ 13. 文件日志页 ============================ */
/* 日志表列少，比文件管理那张表窄一点；一行的信息挤在「日志」一列里，靠大小字号分层 */
.log-table{min-width:880px}
.log-table .col-time{width:158px}
.log-table .col-note{width:230px}
.log-top{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.log-type{
  display:inline-flex;align-items:center;padding:2px 9px;border-radius:var(--r-pill);
  font-size:11.5px;font-weight:600;letter-spacing:.2px;background:#eef2f7;color:var(--text-2);
}
.log-type.upload{background:var(--brand-weak);color:var(--brand)}
.log-type.download{background:#e6f6ef;color:#177a4d}
/* 红字用 #b32b30 而不是 --danger(#e5484d)：浅红底上的对 4.5:1 要求，--danger 只有 3.4:1 */
.log-type.delete{background:var(--danger-weak);color:#b32b30}
.log-type.keep{background:#f1ecfe;color:#5b3ec8}
/* 撤回：琥珀色，和「删除」的红区分开——撤回只是把内容收回，文件被删的那条另有记录 */
.log-type.revoke{background:var(--warn-weak);color:#8a5406}
/* 只给失败的行挂这个标：一眼能从整页里挑出出问题的那几条 */
.log-bad{
  display:inline-flex;align-items:center;padding:2px 8px;border-radius:var(--r-pill);
  font-size:11px;font-weight:600;background:var(--danger-weak);color:#b32b30;
}
.log-file{display:flex;align-items:center;gap:8px;min-width:0}
.log-name{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:320px}
.log-size{flex:0 0 auto;font-size:11.5px;color:var(--text-3)}
.log-src{margin-top:4px;font-size:11.5px;color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 备注：成功给一个绿勾（#177a4d 在浅底上 5.3:1，够 4.5:1），失败原因是主角 */
.log-why{color:#b32b30;font-size:12px;line-height:1.7}
.log-ok{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:600;color:#177a4d}
.log-ok svg{width:14px;height:14px;flex:0 0 auto;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.log-none{color:var(--text-3)}

@media (max-width:900px){
  /* 手机上表格已经变卡片行，这里得把桌面那条 .log-table{min-width:880px} 收回：
     它是类选择器，基础规则里的 table{min-width:0} 压不住，不收的话卡片仍是 880px 宽，
     .del-btn{width:100%} 就跟着撑成一条 850px 的横条跑出屏幕。 */
  .log-table{min-width:0}
  /* 卡片模式里的列序规则是按文件管理那张表写的，日志表列不一样，这里按自己的列序重写 */
  .log-table .log-name{max-width:none;white-space:normal;word-break:break-all}
  .log-table .log-src{white-space:normal}
  .log-table td:nth-child(3) .log-top{padding-right:28px}   /* 给右上角的复选框让位 */
  .log-table td:nth-child(4),
  .log-table td:nth-child(5){display:block;margin-top:9px;font-size:12px;color:var(--text-3)}
  .log-table td:nth-child(4)::before{content:"时间 "}
  .log-table td:nth-child(5)::before{content:"备注 "}
  .log-table td:nth-child(6){margin-top:11px}
  .log-ok svg{width:13px;height:13px}
}

/* ============================ 14. 切页过渡 ============================ */
/* 上传中心 / 文件管理 / 设备互传 / 文件日志之间是同源整页跳转。
   让浏览器在新旧文档的快照之间自己淡入淡出，页面脚本一行都不用改：
   支持这套规则的浏览器切页不再有白屏闪一下，不支持的浏览器直接忽略整段，行为跟以前完全一样。
   头部单独命名 → 跳转时头部原地不动，只有内容区在过渡，观感是「换了块内容」而不是「翻了一页」。 */
@view-transition{navigation:auto}
.site-head{view-transition-name:site-head}
/* 过渡时新旧两张快照会叠在根元素上，底色给成页面背景色，避免半透明快照露出白底 */
::view-transition{background:var(--bg,#f6f7f9)}
/* 头部在两张文档里高度一致，别让浏览器给它做缩放变形 */
::view-transition-group(site-head){animation-duration:.22s}
/* 系统开了「减少动态效果」就别淡入淡出，直接换 */
@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*){animation:none!important}
}

/* ============================ 15. 会员中心 ============================ */
/* 页签：横向滚动，窄屏不换行不挤字 */
.tabs{display:flex;gap:4px;margin-bottom:16px;border-bottom:1px solid var(--line);overflow-x:auto}
.tab{padding:9px 14px;font-size:14px;color:var(--text-2);border-bottom:2px solid transparent;white-space:nowrap;transition:.18s}
.tab:hover{color:var(--text)}
.tab.active{color:var(--brand);border-bottom-color:var(--brand);font-weight:600}

/* 操作结果提示（POST + 303 回来后在页顶显示一次） */
.flash{
  margin-bottom:16px;padding:11px 14px;border-radius:10px;font-size:13px;
  border:1px solid #f4c7c7;background:var(--danger-weak);color:#b32b30;
}
.flash.ok{border-color:#bfe3cd;background:var(--ok-weak);color:#177a4d}

/* 身份卡：头像 + 名字 + 组 / 到期 */
.acc-hero{display:flex;align-items:center;gap:16px;padding:18px 20px;margin-bottom:18px;flex-wrap:wrap}
.hero-ava{
  width:52px;height:52px;border-radius:14px;flex:0 0 auto;
  background:linear-gradient(135deg,#3b8cf7,#1257bb);color:#fff;
  font-size:20px;font-weight:600;display:flex;align-items:center;justify-content:center;
  box-shadow:0 4px 12px rgba(22,104,220,.24);
}
.hero-main{flex:1 1 260px;min-width:0}
.hero-name{font-size:17px;font-weight:700;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.hero-sub{font-size:13px;color:var(--text-2);margin-top:6px;line-height:1.75}
.hero-actions{flex:0 0 auto}

/* 组 / 状态标签（沿用日志页 .log-type 的观感） */
.g-badge{
  display:inline-flex;align-items:center;padding:2px 9px;border-radius:var(--r-pill);
  font-size:11.5px;font-weight:600;letter-spacing:.2px;background:#eef2f7;color:var(--text-2);
}
.g-badge.brand{background:var(--brand-weak);color:var(--brand)}
.g-badge.warn{background:var(--warn-weak);color:#8a5a00}
.g-badge.danger{background:var(--danger-weak);color:#b32b30}

/* 存储用量条：形状沿用上传页的进度条，语义是「用了多少」 */
.bar-bg{height:8px;border-radius:var(--r-pill);background:#eef1f5;overflow:hidden;margin-top:8px}
.bar{height:100%;border-radius:var(--r-pill);background:var(--brand);transition:width .3s}
.bar.warn{background:var(--warn)}
.bar.danger{background:var(--danger)}

/* 键值行：左边项目名、右边数值 */
.kv{margin:14px 0 0;padding:0;list-style:none}
.kv li{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:11px 0;border-bottom:1px solid var(--line-soft);font-size:13.5px}
.kv li:last-child{border-bottom:0}
.kv .k{color:var(--text-2);flex:0 0 auto}
.kv .v{text-align:right;font-weight:600}

.sec-title{font-size:15px;font-weight:700}
.sec-sub{font-size:12.5px;color:var(--text-3);margin-top:5px;line-height:1.7}

/* 表单：三列栅格，窄屏落成一列 */
.form-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:16px}
.fld{display:flex;flex-direction:column;gap:6px;min-width:0}
.fld.wide{grid-column:span 3}
.fld label{font-size:12.5px;color:var(--text-2);font-weight:500}
.fld .hint{font-size:11.5px;color:var(--text-3)}
.fld input[type=text],.fld input[type=number],.fld input[type=date],.fld select,.fld textarea{
  width:100%;padding:9px 11px;border:1px solid #d6dbe3;border-radius:9px;
  font-size:14px;background:#fbfcfe;color:var(--text);outline:none;transition:.18s;
}
.fld input:focus,.fld select:focus,.fld textarea:focus{border-color:var(--brand);background:#fff;box-shadow:0 0 0 3px rgba(22,104,220,.12)}
.fld textarea{resize:vertical;min-height:64px}
.chk-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.chk{display:inline-flex;align-items:center;gap:7px;font-size:13px;color:var(--text-2);cursor:pointer}
.form-actions{display:flex;gap:10px;align-items:center;margin-top:16px;flex-wrap:wrap}

/* 会员组 / 会员两张表 */
.acc-table{min-width:900px}
.acc-table td.col-op{width:auto;text-align:left}
.acc-table .tool-btn{text-decoration:none}
.tbl-inline{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.tbl-inline select,.tbl-inline input[type=date]{
  padding:7px 9px;border:1px solid #d6dbe3;border-radius:8px;
  font-size:12.5px;background:#fbfcfe;color:var(--text);
}
.tbl-inline .tool-btn{padding:7px 12px}
/* 表格里的删除表单不要额外占高（td 是 flex 时按钮会被拉高） */
.acc-table td form{margin:0}

@media (max-width:900px){
  /* 卡片行模式：列名靠 data-l 带出来，列序调整不用改这里 */
  .acc-table{min-width:0}
  .acc-table td:not(.col-num){display:block;margin-top:9px;font-size:12.5px}
  .acc-table td::before{content:attr(data-l);display:block;color:var(--text-3);font-size:11.5px;margin-bottom:3px}
  .acc-table td.col-num::before{content:none}
  .acc-table td.col-op{margin-top:12px}
  .tbl-inline{width:100%}
  .tbl-inline select,.tbl-inline input[type=date]{flex:1 1 120px;min-width:0}
  .form-grid{grid-template-columns:1fr}
  .fld.wide{grid-column:span 1}
  .kv li{font-size:13px}
}


/* ---------- 16. 卡密（兑换框、码的显示、兑换记录） ---------- */
/* 兑换框：输入框吃掉剩余宽度，按钮固定不缩（手机上自动换行成一列） */
.redeem-form{display:flex;gap:10px;align-items:stretch;flex-wrap:wrap;margin-top:14px}
.redeem-form .code-input{
  flex:1 1 240px;min-width:0;padding:11px 13px;border:1px solid #d6dbe3;border-radius:10px;
  font-size:15px;letter-spacing:1px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  background:#fbfcfe;color:var(--text);text-transform:uppercase;
}
.redeem-form .code-input:focus{outline:none;border-color:var(--brand);background:#fff;box-shadow:0 0 0 3px rgba(37,99,235,.12)}
.redeem-form .btn{flex:0 0 auto;padding:11px 26px}

/* 卡密本体：等宽字体 + 分组，方便对着念、对着抄 */
.mono{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12.5px;letter-spacing:.5px;white-space:nowrap;
  background:#f2f4f7;border-radius:6px;padding:3px 7px;color:var(--text);
}

/* 兑换记录：一行一条，左边时间右边说明 */
.redeem-log{list-style:none;margin:12px 0 0;padding:0}
.redeem-log li{
  display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;
  padding:9px 0;border-top:1px solid #eef1f5;font-size:13px;
}
.redeem-log li:first-child{border-top:none}
.redeem-log .rl-time{color:var(--text-3);font-size:12.5px;flex:0 0 auto}
.redeem-log .rl-note{color:var(--text-2);flex:1 1 240px;min-width:0;word-break:break-word}
.redeem-log li.ok .rl-note{color:#0f7a4a}
.redeem-log li.bad .rl-note{color:#c0392b}

@media (max-width:900px){
  .redeem-form .btn{width:100%}
  .redeem-log .rl-time{flex:0 0 100%}
}

/* ==================== 17. 安全中心 ==================== */
/* 体检结果清单：左边一道色条区分级别，右侧是结论与处置建议 */
.sec-checks{list-style:none;margin:14px 0 0;padding:0}
.sec-checks li{display:flex;gap:12px;padding:12px 14px;border:1px solid var(--line);border-left-width:3px;border-radius:10px;margin-bottom:10px;background:#fff}
.sec-checks li.lv-ok{border-left-color:var(--ok)}
.sec-checks li.lv-warn{border-left-color:var(--warn);background:#fffdf6}
.sec-checks li.lv-fail{border-left-color:var(--danger);background:#fff8f8}
.sec-checks li.lv-info{border-left-color:#c8d2dd}
.sec-checks .ck-badge{flex:0 0 auto;margin-top:1px}
.sec-checks .ck-main{min-width:0;flex:1}
.sec-checks .ck-main b{font-size:13.5px}
.sec-checks .ck-detail{color:var(--text-2);font-size:12.5px;margin-top:3px;line-height:1.65;word-break:break-word}
.sec-checks .ck-hint{color:#8a6d1a;font-size:12.5px;margin-top:5px;line-height:1.6}
.sec-checks .mono{font-size:12px}
.sc-red{background:var(--danger-weak);color:var(--danger)}
/* 设置页里的只读信息块 */
.ro-box{background:var(--bg);border:1px solid var(--line);border-radius:10px;padding:10px 12px;font-size:12.5px;line-height:1.95;color:var(--text-2)}
.ro-box b{color:var(--text)}
/* 审计筛选的当前项 */
.toolbar .tool-btn.on{background:var(--brand-weak);border-color:var(--brand-line);color:var(--brand)}
/* 表格里的空状态（页面级的 .empty 内边距太大，放进 td 会撑成一大块空白） */
.acc-table td.empty{padding:26px 12px;text-align:center;color:var(--text-3);font-size:13px}
@media (max-width:900px){
  .sec-checks li{flex-wrap:wrap}
  .sec-checks .ck-badge{order:-1}
  .sec-checks .ck-main{flex:1 1 100%}
  .fld.wide{grid-column:span 1}
}

/* 会员组不含「限次」功能时：按钮留着（点开有说明），但一眼看出是锁着的 */
.limit-cell.lock .limit-btn{border-style:dashed;background:#fff;color:var(--text-3)}
.limit-cell.lock .limit-btn:hover{border-color:var(--brand-line);background:var(--brand-weak);color:var(--brand)}
