
:root{
  --bg:#FFF7F0; --ink:#242D3C; --sub:#64748B;
  --green:#20C972; --green-d:#18A85C;
  --sky:#ABD1E0; --pink:#F7BEC4; --purple:#E1D8F4; --yellow:#FFE066;
}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
/* 彻底禁止横向滑动：html 上的 overflow 会传递到视口，clip 比 hidden 更彻底
   （不创建滚动容器，也不会在 iOS 上被"橡皮筋"拖出空白）。两行写是给老内核兜底。 */
html{overflow-x:hidden;overflow-x:clip;-webkit-text-size-adjust:100%;text-size-adjust:100%}
body{overflow-x:hidden;overflow-x:clip;width:100%;max-width:100%;position:relative;
  background:var(--bg);color:var(--ink);
  font-family:"PingFang SC","Microsoft YaHei",system-ui,sans-serif;
  min-height:100vh;padding:16px 14px calc(24px + env(safe-area-inset-bottom));
  overscroll-behavior-x:none}
/* 长串（邮箱、直链、无空格英文标题）不许把容器撑宽 */
.title,.meta,.foot,.sub,.status,.dlprog,.gtools,.card,textarea,.btn,.tag{
  overflow-wrap:anywhere;word-break:break-word}
.wrap,.card,#result,.btnrow,.actions,.gtools,#copybox,#copybox textarea{max-width:100%;min-width:0}
.wrap{width:100%;max-width:560px;margin:0 auto}
/* ---- 头部 ---- */
.head{display:flex;align-items:center;gap:10px;margin:6px 2px 14px}
/* 文字块必须能收缩：flex item 默认 min-width:auto 会被内容（含 nowrap 段）撑宽，
   导致副标题永远不折行、整块顶出卡片右边缘 */
.head > div{min-width:0}
.logo{width:40px;height:40px;flex:none}
.logo svg{display:block}
h1{font-size:20px;font-weight:800;letter-spacing:.5px;line-height:1.3}
/* 品牌名跟在主标题后面，略小一号但同色，读起来是一个整体；
   nowrap 保证「（VideoDL）」不会从中间断行 */
h1 .brand{font-size:.82em;font-weight:800;color:var(--ink);
  letter-spacing:0;white-space:nowrap}
/* 副标题按「整段」排：每段 nowrap，放不下就整段换行，
   避免出现「网页端 / 桌面」这种从词中间断开的难看得行尾 */
.sub{font-size:11px;color:var(--sub);margin-top:2px;line-height:1.65;
  display:flex;flex-wrap:wrap;align-items:baseline;gap:0 5px}
.sub > span{white-space:nowrap}
/* 窄屏放不下时会折行，此时藏掉竖线改用间距分隔，免得行首孤零零一个「｜」 */
@media(max-width:520px){
  .sub .sep{display:none}
  .sub{gap:0 8px}
}
/* ---- 卡片 ---- */
.card{background:#fff;border:2.5px solid var(--ink);border-radius:18px;
  box-shadow:5px 5px 0 var(--ink);padding:16px 14px;margin-bottom:16px}
.lab{font-size:12px;font-weight:700;color:var(--sub);margin-bottom:8px;letter-spacing:1px}
textarea{width:100%;border:2.5px solid var(--ink);border-radius:12px;
  padding:12px 12px;font-size:16px;line-height:1.5;color:var(--ink);
  background:#fff;outline:none;resize:none;height:86px;font-family:inherit}
textarea:focus{background:#FFFDF5}
/* ---- 按钮 ---- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;
  border:2.5px solid var(--ink);border-radius:12px;font-weight:800;
  font-size:15px;padding:12px 18px;cursor:pointer;user-select:none;
  box-shadow:4px 4px 0 var(--ink);transition:transform .06s,box-shadow .06s;
  text-decoration:none;font-family:inherit}
.btn:active{transform:translate(4px,4px);box-shadow:0 0 0 var(--ink)}
.btn.green{background:var(--green);color:#fff}
.btn.sky{background:var(--sky);color:var(--ink)}
.btn.pink{background:var(--pink);color:var(--ink)}
.btn.yellow{background:var(--yellow);color:var(--ink)}
.btn.white{background:#fff;color:var(--ink)}
.btn.big{width:100%;font-size:17px;padding:14px}
.btn.sm{font-size:13px;padding:8px 12px;box-shadow:3px 3px 0 var(--ink)}
.btn:active{transform:translate(3px,3px);box-shadow:0 0 0 var(--ink)}
.btnrow{display:flex;gap:10px;margin-top:12px}
.btnrow .btn{flex:1}
/* ---- 更多资源（自定义下拉）---- */
/* 为什么不用原生 select：option 里做不出「名称 + 旁边一行灰色说明」的排版，
   而产品介绍恰好要挂在下拉项上（卡片里不常驻任何介绍文字）。
   按钮字号仍须 ≥16px：iOS 聚焦输入类控件会放大整页。 */
.dlrow{display:flex;gap:10px;align-items:stretch}
.dlsel{position:relative;flex:1 1 auto;min-width:0}
.dlcur{display:flex;align-items:center;gap:8px;width:100%;
  border:2.5px solid var(--ink);border-radius:12px;background:#fff;color:var(--ink);
  font-family:inherit;font-weight:700;font-size:clamp(15px,4vw,16px);text-align:left;
  padding:clamp(9px,2.5vw,13px) 34px clamp(9px,2.5vw,13px) clamp(10px,3vw,14px);
  cursor:pointer;outline:none}
.dlcur:focus-visible{background:#FFFDF5}
.dlcur .nm{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dlcur .ar{flex:none;width:14px;height:9px;transition:transform .15s}
.dlsel.open .dlcur .ar{transform:rotate(180deg)}
.dlmenu{display:none;position:absolute;left:0;right:0;top:calc(100% + 8px);z-index:30;
  margin:0;padding:6px;list-style:none;background:#fff;
  border:2.5px solid var(--ink);border-radius:14px;box-shadow:5px 5px 0 var(--ink);
  max-height:min(54vh,340px);overflow:auto;-webkit-overflow-scrolling:touch}
.dlsel.open .dlmenu{display:block}
/* 每一项固定两行：第一行名称，第二行小字介绍。所有资源统一这个结构，
   介绍再长也只是第二行内部折行，不会把名称挤走。 */
.dlmenu li{display:flex;flex-direction:column;align-items:flex-start;gap:3px;
  padding:9px 10px;border-radius:10px;cursor:pointer}
.dlmenu li + li{margin-top:2px}
.dlmenu li:hover{background:#FFF4E4}
.dlmenu li.on{background:rgba(32,201,114,.15)}
.dlmenu .nm{display:block;width:100%;font-weight:800;
  font-size:clamp(13.5px,3.6vw,15px);color:var(--ink);line-height:1.4;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dlmenu .mt{display:block;width:100%;
  font-size:clamp(10px,2.7vw,11.5px);color:var(--sub);font-weight:600;
  line-height:1.55;word-break:break-word}
/* 说明按段折行：每段整体不拆开，段之间用「·」连接（分隔符粘在前一段末尾，
   这样折行时行尾最多多一个点，不会出现行首孤零零的「·」） */
.dlmenu .mt .s{display:inline-block;white-space:nowrap}
.dlmenu .mt .s:not(:last-child)::after{content:" · ";color:var(--sub)}
.dlrow .btn{flex:0 0 auto;white-space:nowrap}
/* 卡片自身不裁剪，但下拉要盖住下面的结果区 */
#dlbox{position:relative;z-index:5}
/* ---- 状态 ---- */
.status{margin-top:12px;font-size:13px;color:var(--sub);display:none;
  align-items:center;gap:8px}
.status.show{display:flex}
.spin{width:14px;height:14px;border:2.5px solid var(--ink);border-top-color:transparent;
  border-radius:50%;animation:rot .7s linear infinite;flex:none}
@keyframes rot{to{transform:rotate(360deg)}}
.err{color:#D64550;font-weight:700}
/* ---- 结果 ---- */
#result{display:none}
.meta{font-size:12px;color:var(--sub);line-height:1.8}
.meta b{color:var(--ink)}
.cover{width:100%;border:2.5px solid var(--ink);border-radius:14px;
  display:block;margin:10px 0;background:#F5EFE6;min-height:120px}
.player{width:100%;border:2.5px solid var(--ink);border-radius:14px;
  display:block;margin:10px 0 4px;background:#000;box-shadow:4px 4px 0 var(--ink)}
.title{font-size:16px;font-weight:800;line-height:1.45;margin-bottom:4px}
.tag{display:inline-block;background:var(--purple);border:2px solid var(--ink);
  border-radius:8px;font-size:11px;font-weight:700;padding:2px 8px;margin-right:6px}
.audio{display:flex;align-items:center;gap:10px;background:var(--pink);
  border:2.5px solid var(--ink);border-radius:12px;padding:10px 12px;margin-top:12px}
.audio .nm{flex:1;font-size:13px;font-weight:700}
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:10px}
.grid .cell{position:relative;border:2.5px solid var(--ink);border-radius:12px;
  overflow:hidden;box-shadow:3px 3px 0 var(--ink);aspect-ratio:3/4;
  background:#F5EFE6;cursor:pointer;user-select:none;-webkit-user-select:none}
.grid .cell img{width:100%;height:100%;object-fit:cover;display:block;
  transition:opacity .12s}
.grid .cell .no{position:absolute;left:6px;top:6px;background:#fff;border:2px solid var(--ink);
  border-radius:6px;font-size:11px;font-weight:800;padding:1px 6px}
/* 勾选标记：默认全选（对齐桌面端），点缩略图切换 */
.grid .cell .pick{position:absolute;right:6px;top:6px;width:24px;height:24px;
  border:2.5px solid var(--ink);border-radius:50%;background:var(--green);color:#fff;
  font-size:14px;font-weight:900;line-height:1;display:flex;align-items:center;
  justify-content:center}
.grid .cell:not(.on) .pick{background:#fff;color:transparent}
.grid .cell:not(.on) img{opacity:.4}
.gtools{display:flex;align-items:center;gap:8px;margin-top:12px;flex-wrap:wrap}
.gtools .gcnt{font-size:12px;font-weight:700;color:var(--sub);margin-right:auto}
.gtools .gcnt b{color:var(--ink);font-size:14px}
.btn.off{background:#E9E5DE;color:#9AA3AF;box-shadow:3px 3px 0 #C2BBAF;cursor:not-allowed}
.dlprog{display:none;margin-top:10px;font-size:12px;font-weight:700;line-height:1.65;
  color:var(--green-d)}
.dlprog.show{display:block}
.dlprog.err{color:#D64550}
.foot{text-align:center;font-size:11px;color:#A8B2BF;margin-top:6px;line-height:1.8}
.foot a.icp{color:inherit;text-decoration:none;border-bottom:1px dotted #A8B2BF;transition:color .15s,border-color .15s}
.foot a.icp:hover{color:#5B6B7C;border-bottom:1px solid #5B6B7C}
.actions{display:flex;gap:10px;margin-top:14px}
.actions .btn{flex:1}

/* ==================== 响应式：一套代码适配手机 / 平板 / 桌面 ==================== */
/* 思路：字号和间距改用 clamp() 随视口平滑浮动（不再靠固定 px 硬撑），
   容器宽度按断点分档；全站始终单栏，避免"手机挤成蚂蚁、大屏拉成一条窄柱" */
h1{font-size:clamp(19px,4.6vw,23px)}
.sub{font-size:clamp(10.5px,2.9vw,12px)}
.lab{font-size:clamp(11.5px,3vw,12.5px)}
/* 关键：输入框字号必须 ≥16px。低于 16px 时 iOS Safari/微信在聚焦瞬间会
   自动放大整个页面且不缩回，表现为"页面变大、能左右滑动、右边被裁" */
textarea{font-size:clamp(16px,4.2vw,17px);height:clamp(78px,22vw,96px)}
.btn{font-size:clamp(13.5px,3.7vw,16px);
  padding:clamp(9px,2.5vw,13px) clamp(10px,3.2vw,20px)}
.btn.big{font-size:clamp(15px,4.2vw,17px)}
.btn.sm{font-size:clamp(12px,3.2vw,13px);padding:8px 12px}
.title{font-size:clamp(15px,4vw,19px)}
.meta{font-size:clamp(11.5px,3.1vw,13px)}
.status{font-size:clamp(12px,3.2vw,13.5px)}
.dlprog{font-size:clamp(11.5px,3.1vw,13px)}
.foot{font-size:clamp(10.5px,2.8vw,12px)}
.audio .nm{font-size:clamp(12px,3.2vw,13.5px)}
body{padding:clamp(10px,2.6vw,20px) clamp(10px,2.6vw,20px)
  calc(clamp(16px,3vw,26px) + env(safe-area-inset-bottom))}
.wrap{max-width:min(100%,620px)}
.card{padding:clamp(13px,3.4vw,20px) clamp(11px,3vw,18px);
  border-radius:clamp(14px,3.6vw,20px);margin-bottom:clamp(12px,3vw,18px)}

/* 按钮组：窄屏不挤爆也不横向溢出，放不下就自然换行 */
.btnrow,.actions{flex-wrap:wrap}
.btnrow .btn,.actions .btn{min-width:0;flex:1 1 auto}
.btnrow{gap:clamp(6px,2vw,10px)}
.actions{gap:clamp(7px,2vw,10px)}
.actions .btn{flex:1 1 42%}

/* 图集：列数随可用宽度自适应（手机 3 列，平板 4~5 列，桌面 6 列） */
.grid{grid-template-columns:repeat(auto-fill,minmax(clamp(86px,24vw,118px),1fr))}
.grid .cell{border-radius:clamp(9px,2.4vw,12px);box-shadow:clamp(2px,0.6vw,3px) clamp(2px,0.6vw,3px) 0 var(--ink)}
.grid .cell .pick{width:clamp(20px,5.4vw,24px);height:clamp(20px,5.4vw,24px);font-size:clamp(12px,3.4vw,14px)}

/* 播放器/封面：大屏不要顶到天花板，矮屏不要占满一屏 */
.player{max-height:min(70vh,560px);object-fit:contain}
.cover{max-height:60vh;object-fit:contain}

/* 超小屏（≤360，老机型或分屏）：压紧边框与阴影，换来不溢出 */
@media(max-width:360px){
  h1{font-size:17.5px}
  .logo{width:34px;height:34px}
  .logo svg{width:34px;height:34px}
  .btn{padding:9px 9px;box-shadow:3px 3px 0 var(--ink)}
  .card{padding:12px 10px;box-shadow:4px 4px 0 var(--ink)}
  .grid{gap:8px}
  .gtools{gap:6px}
}

/* 横屏矮屏：视频只占半屏高，别逼人滚动找按钮 */
@media(max-height:520px) and (orientation:landscape){
  .player{max-height:52vh}
  body{padding-top:8px}
}

/* 桌面宽屏（≥900）：仍然单栏上下排列，解析结果紧跟解析卡下方。
   2026-11-06 用户要求取消「左输入 / 右结果」两栏：结果不再并排、不再 sticky，
   .wrap.has-result 只作为「结果薄雾」的样式钩子，不再参与布局。
   #result 在 index.html 里已挪到 #pcard 之后，测速卡与更多资源顺延到结果下方。 */
@media(min-width:900px){
  .wrap .grid{grid-template-columns:repeat(auto-fill,minmax(112px,1fr))}
  .wrap .actions .btn{flex:1 1 0}
  textarea{height:112px}
}


/* ===== 纯黑主题（2026-11-06）：Antigravity 背景层 + 深色下的可读性 =====
   #bg-root 固定在视口最底层，.wrap 抬到上层；背景不吃指针事件，
   组件内部的 window 级 pointer 监听仍然生效。WebGL2 不可用时组件静默退出，
   页面自动回落成纯黑底，无需额外兜底。 */
:root{--bg:#000}
html{background:#000}
#bg-root{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden}
#bg-root .antigravity{width:100%;height:100%}
.wrap{position:relative;z-index:1}

/* 卡片外只有页头标题/副标题是深色字，纯黑底上必须提亮。
   卡片、下拉、输入框、结果区本身都是白底，内部文字与描边一律不动。 */
h1,h1 .brand{color:#F2F6FC}
.sub{color:#B9C5D6}
.sub .sep{color:#6B7B90}

/* 页头文字压在粒子上会发花：用伪元素铺一层黑晕，绝对定位不参与 flex 布局。
   isolation 建立独立层叠上下文，::before 的 -1 层只在本块内下沉，不会掉到背景层后面。
   必须用径向渐变而非纯色块 + box-shadow：Stardust 的白色粒子均匀密布，
   纯色衬底会露出一条明显的矩形"挖空"边界。 */
.head{position:relative;isolation:isolate}
.head::before{content:"";position:absolute;inset:-18px -34px;z-index:-1;pointer-events:none;
  background:radial-gradient(ellipse 68% 125% at 50% 50%,
    rgba(0,0,0,.95) 0%,rgba(0,0,0,.82) 45%,rgba(0,0,0,0) 100%)}

/* ---- 纯黑极简（2026-11-06 18:35：用户从四套实拍预览里选定）----
   彻底去掉中间面板：卡片透明、无边框、无阴影、无毛玻璃，输入框和按钮直接浮在星尘上；
   中央铺一层范围更大的径向黑晕把粒子压暗，保证文字可读，四周星空仍完整。
   文字体系沿用深色版（重定义 --ink/--sub 让 .lab/.status/.meta/.title 等自动跟随），
   只补「浅底必须配深字」的例外。业务 JS / 布局 / 字号 / iOS ≥16px 铁律全未动。

   备选实拍预览：`主题预览_1_磨砂白.png`（半透冷白面板）/ `_3_实色深灰.png`（不透明深灰）
   / `_4_墨绿玻璃.png`（墨绿半透）。想换回有面板的版本，整段替换成对应 CSS 即可。 */
:root{--ink:#EDF3FF; --sub:#AABBD2}

/* 面板消失，只留控件；左右 padding 一起去掉，内容直接对齐星空 */
.card{background:transparent;border:0;box-shadow:none;
  -webkit-backdrop-filter:none;backdrop-filter:none;
  padding-left:0;padding-right:0}
.lab{color:#C7D2FE}

/* 输入框 / 下拉按钮：半透明玻璃，聚焦时略提亮 */
textarea,.dlcur{background:rgba(255,255,255,.07);
  border:1px solid rgba(199,210,254,.30);color:var(--ink);box-shadow:none}
textarea::placeholder{color:rgba(178,195,218,.85)}
textarea:focus,.dlcur:focus-visible{background:rgba(255,255,255,.11);
  border-color:rgba(199,210,254,.50)}
.dlmenu{background:rgba(14,18,30,.97);
  border:1px solid rgba(199,210,254,.28);
  box-shadow:0 20px 48px rgba(0,0,0,.72)}
.dlmenu li:hover{background:rgba(199,210,254,.10)}
.dlmenu li.on{background:rgba(32,201,114,.20)}
.dlmenu .nm{color:var(--ink)}
.dlmenu .mt{color:var(--sub)}

/* 按钮：品牌色块保留，去掉黑硬描边/硬阴影 */
.btn{border:1px solid rgba(255,255,255,.16);box-shadow:0 6px 18px rgba(0,0,0,.30)}
.btn.sm{box-shadow:0 4px 12px rgba(0,0,0,.28)}
.btn:active{transform:translateY(2px);box-shadow:0 2px 8px rgba(0,0,0,.28)}
.btn.sky,.btn.pink,.btn.yellow{color:#242D3C}
.btn.white{background:rgba(255,255,255,.09);color:var(--ink);
  border-color:rgba(199,210,254,.28)}
.btn.off{background:rgba(255,255,255,.06);color:rgba(170,187,210,.55);
  border-color:rgba(255,255,255,.08);box-shadow:none}

/* 结果区：浅底元素一律改半透明深色，避免无面板时冒出白块 */
.tag{background:rgba(225,216,244,.16);color:#E6DCFF;
  border:1px solid rgba(225,216,244,.28)}
.audio{background:rgba(247,190,196,.13);color:var(--ink);
  border:1px solid rgba(247,190,196,.26)}
.cover{background:rgba(255,255,255,.05);border:1px solid rgba(199,210,254,.22)}
.player{border:1px solid rgba(199,210,254,.22);box-shadow:none}
.grid .cell{background:rgba(255,255,255,.05);
  border:1px solid rgba(199,210,254,.22);box-shadow:none}
.grid .cell .no{background:rgba(10,14,24,.88);color:var(--ink);
  border:1px solid rgba(199,210,254,.28)}
.spin{border-color:var(--sub)}
.err,.dlprog.err{color:#FF8A93}
.dlprog{color:#4BE09A}

/* 中央黑晕：比页头/页脚那两处大得多，把中间星尘压暗托住文字，
   边缘透明渐隐，四周星空不受影响。wrap 的 z-index:1 保证伪元素只在块内下沉。 */
.wrap::before{content:"";position:absolute;inset:-70px -90px -50px;z-index:-1;
  pointer-events:none;
  background:radial-gradient(ellipse 62% 58% at 50% 38%,
    rgba(0,0,0,.94) 0%,rgba(0,0,0,.80) 52%,rgba(0,0,0,0) 88%)}

/* 有结果时（单栏内容变长），完全无面板会让说明小字被星尘粒子干扰。
   只给结果卡铺一层几乎看不见的深色薄雾，首页仍然是无面板极简。
   特异性高于上面的 .card，能把左右 padding 找回来。 */
.wrap.has-result #result .card{
  background:rgba(6,9,18,.58);
  border:1px solid rgba(199,210,254,.14);
  border-radius:18px;
  padding-left:clamp(12px,2vw,18px);
  padding-right:clamp(12px,2vw,18px);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
}

/* 页脚浅灰小字同样用径向渐变黑晕：中心挡住粒子，边缘自然融入星空 */
.foot{
  position:relative;
  color:#A8B2BF;
  padding:8px 22px;
  background:radial-gradient(ellipse 66% 135% at 50% 50%,
    rgba(0,0,0,.96) 0%,rgba(0,0,0,.84) 48%,rgba(0,0,0,0) 100%);
  text-shadow:0 0 4px #000;
}
.foot a.icp{border-bottom-color:#A8B2BF}
.foot a.icp:hover{color:#20C972;border-bottom-color:#20C972}

/* ===== 宽屏中央黑晕收小（2026-11-06 22:07，用户「没有白色颗粒的那一块改小一点」）
   桌面大屏上，上面的大范围径向黑晕会压出一整片看不到星尘的纯黑区，像个「挖空」。
   仅 ≥900px 收小外扩范围与椭圆半径（中心黑度不变，文字区仍压得住），让四周星尘更完整；
   手机 / 窄屏继续用原参数，避免测速小字被白色颗粒干扰（实测收小后手机会发花）。
   必须放在基础规则之后，否则同特异性会被后面的 .wrap::before 覆盖。 */
@media(min-width:900px){
  .wrap::before{inset:-40px -50px -30px;
    background:radial-gradient(ellipse 50% 48% at 50% 40%,
      rgba(0,0,0,.94) 0%,rgba(0,0,0,.78) 55%,rgba(0,0,0,0) 90%)}
}

/* ===== 交互反馈层（2026-11-06 23:10，用户「各项功能交互比较生硬」第一批）=====
   只补「中间状态」：悬停、键盘聚焦、禁用、就绪、出现/消失。
   不改布局 / 配色 / 字号 / 业务逻辑；整段删掉即可回滚到硬切版本。
   体检依据：改前全站 179 条规则里 .btn:hover = 0、按钮 focus = 0、
   prefers-reduced-motion = 0，只有 6 条 transition。 */

/* 按钮三态：原来只有 :active 的 2px 位移，桌面鼠标悬停毫无反馈 */
.btn{transition:transform .18s ease,box-shadow .18s ease,filter .18s ease,
  background-color .18s ease,border-color .18s ease}
.btn:not(:disabled):not(.off):hover{filter:brightness(1.10);
  transform:translateY(-2px);box-shadow:0 10px 24px rgba(0,0,0,.42)}
.btn:not(:disabled):not(.off):active{transform:translateY(2px);
  box-shadow:0 2px 8px rgba(0,0,0,.28)}
/* 键盘 Tab 焦点：深色底上用浅色描边，图集格子 / 下拉项这类非 button 也可聚焦 */
.btn:focus-visible,.dlcur:focus-visible,.dlmenu li:focus-visible,
.grid .cell:focus-visible,a:focus-visible,textarea:focus-visible{
  outline:2px solid rgba(199,210,254,.95);outline-offset:3px}
.btn:disabled,.btn.off{filter:none;transform:none;box-shadow:none}

/* 两阶段操作的「第二步可以点了」信号：存入相册备好文件后点亮 */
.btn.ready{box-shadow:0 0 0 2px rgba(32,201,114,.55),0 8px 22px rgba(0,0,0,.36)}
.btn.ready:hover{box-shadow:0 0 0 2px rgba(32,201,114,.55),0 10px 24px rgba(0,0,0,.42)}

/* 提示统一淡入淡出。隐藏仍靠 display，但由 JS 先挂 .leaving 播完淡出再摘 .show，
   这样既不会留下空白占位，也不会「啪」地消失。 */
@keyframes vdlIn{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}
@keyframes vdlOut{from{opacity:1;transform:none}to{opacity:0;transform:translateY(-4px)}}
@keyframes vdlResultIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.status.show,.dlprog.show,
#spdNote:not([hidden]),.spd-metrics:not([hidden]){animation:vdlIn .22s ease-out both}
.status.leaving,.dlprog.leaving{
  animation:vdlOut .18s ease-in both}
#result.enter{animation:vdlResultIn .30s ease-out both}
.dlsel.open .dlmenu{animation:vdlIn .16s ease-out both}
.dlmenu li,.grid .cell .pick,.grid .cell img{transition:background .15s ease,opacity .18s ease,color .15s ease}

/* 系统「减少动态效果」：一次性关掉全部 CSS 动效（WebGL 星尘不受 CSS 影响） */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;
    animation-iteration-count:1!important;transition-duration:.001ms!important;
    scroll-behavior:auto!important}
}

/* ===== 交互反馈第二批（2026-11-06 23:35，用户「开始第二批」）=====
   ① 统一 toast：把分散在 #status / #copytip / #dlTip 三处的短反馈收到视口底部
   ② 微信引导条改为纯黑极简（原来是黄底黑框，和整站完全不是一个世界）
   ③ 图集缩略图骨架 + 逐张下载进度条与「停止」
   ④ 下拉菜单键盘可达（见 app.js）
   本节同样可整段删除回滚。 */

/* ① 全局轻提示：固定视口底部居中，不随滚动跑、不占文档流、不吃指针事件。
   长任务进度仍留在卡片里（#status / #dlprog 是「状态」，不是「提示」）。 */
.toast{position:fixed;left:50%;bottom:calc(18px + env(safe-area-inset-bottom));
  transform:translateX(-50%);z-index:200;display:none;pointer-events:none;
  max-width:min(92vw,560px);padding:10px 18px;border-radius:999px;
  background:rgba(12,17,28,.94);border:1px solid rgba(199,210,254,.22);
  color:#EDF3FF;font-size:clamp(12px,3.2vw,13.5px);font-weight:700;line-height:1.5;
  text-align:center;box-shadow:0 12px 32px rgba(0,0,0,.6);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
.toast.show{display:block;animation:vdlToastIn .22s ease-out both}
.toast.leaving{animation:vdlToastOut .18s ease-in both}
.toast.err{color:#FF8A93;border-color:rgba(255,138,147,.42)}
@keyframes vdlToastIn{from{opacity:0;transform:translate(-50%,10px)}
  to{opacity:1;transform:translate(-50%,0)}}
@keyframes vdlToastOut{from{opacity:1;transform:translate(-50%,0)}
  to{opacity:0;transform:translate(-50%,8px)}}

/* ② 微信内置浏览器引导条：深色半透 + 琥珀色点缀，和星尘背景同一套语言 */
.wxguide{display:flex;gap:9px;align-items:flex-start;margin:0 0 14px;
  padding:11px 13px;border-radius:14px;
  background:rgba(255,224,102,.09);border:1px solid rgba(255,224,102,.32);
  color:#FFE9A3;font-size:clamp(11.5px,3.1vw,12.5px);font-weight:700;
  line-height:1.7;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
.wxguide .k{flex:none;font-size:14px;line-height:1.35}
.wxguide b{color:#FFE066}

/* ③ 图集骨架屏：图还没到时铺一层缓慢流动的高光，避免一排排黑格子像坏图。
   图 一旦 load 成功（含 onerror 换成占位图）就给 .cell 挂 .loaded，骨架自动消失。 */
.grid .cell:not(.loaded)::after{content:"";position:absolute;inset:0;z-index:1;
  pointer-events:none;
  background:linear-gradient(100deg,rgba(255,255,255,.03) 25%,
    rgba(255,255,255,.11) 45%,rgba(255,255,255,.03) 65%);
  background-size:220% 100%;animation:vdlShimmer 1.25s linear infinite}
@keyframes vdlShimmer{from{background-position:120% 0}to{background-position:-80% 0}}
.grid .cell img{opacity:0;transition:opacity .28s ease}
.grid .cell.loaded img{opacity:1}
.grid .cell.loaded:not(.on) img{opacity:.4}
.grid .cell .no,.grid .cell .pick{z-index:2}
/* 逐张下载：已触发的缩略图亮一圈绿边，用户能看出下到第几张 */
.grid .cell.saved{box-shadow:0 0 0 2px rgba(32,201,114,.75)}

/* ③ 逐张下载进度条 + 停止按钮（只在下载中出现，其余时候整行隐藏） */
.dlrun{display:none;align-items:center;gap:10px;margin-top:8px}
.dlrun:not([hidden]){display:flex}
.dlbar{flex:1 1 auto;height:6px;border-radius:999px;overflow:hidden;
  background:rgba(199,210,254,.14)}
.dlbar i{display:block;height:100%;width:0;border-radius:999px;
  background:linear-gradient(90deg,#20C972,#4BE09A);transition:width .3s ease}
.dlrun .btn{flex:none}

/* ④ 下拉项自身聚焦时内缩描边，避免被菜单的 overflow 裁掉 */
.dlmenu li:focus-visible{outline-offset:-2px}
