/* 批量工具页专属样式
   由 src/web/templates/batch/{ping,tcping,http,dns}.html 的内联 <style> 抽取合并而来。
   四页共用一份样式，其中 http/dns 额外包含 .cell-status / .select-field /
   .cell-records / .cell-count / .checkbox-line 等规则。 */

.batch-card {
   background: #fff;
   border: 1px solid #dfe8ec;
   border-radius: .25rem;
   box-shadow: 0 2px 8px rgba(37, 65, 80, .06);
   padding: 20px;
   margin-bottom: 20px;
}

/* ⚠️ 去掉「卡中卡」：.batch-card 已经是白卡片（圆角/边框/阴影/20px 内边距），
   而里面的 form 同时带着 .tool-form，会被 `.tool-page>.tool-form`（nav-skin.css）
   再加一层 padding:35px 50px + 边框 + 阴影 + hover 位移，于是同一块区域
   出现两道相距很近的边框、留白翻倍、鼠标移上去只有内层卡片在抖。
   这里把内层 form 完全"扁平化"成交卡片里的普通容器：
   · padding/margin 归零 —— 留白交给外层 .batch-card 唯一控制；
   · 边框与阴影去掉，hover 同权重压回（只写 box-shadow:none 不够，
     否则 border-color 仍会被 hover 规则改成 #d3dde6 而描出边框）。
   ⚠️ 选择器必须写成 .batch-card > .batch-form（(0,2,0) 起）才压得住
   nav-skin.css 的 .tool-page>.tool-form（同为 (0,2,0)，靠文件加载顺序取胜，
   不可依赖）。 */
.batch-card>.batch-form,
.batch-card>.batch-form:hover {
   padding: 0;
   margin: 0;
   border: 0;
   border-radius: 0;
   box-shadow: none;
   background: transparent;
   transition: none;
}

/* 布局基准（对齐 itdog）：左列输入区（吃满剩余）+ 右栏 304px 操作区，
   顶边对齐。gap 收到 12px —— 第二张参考图里输入框与按钮列之间的缝很窄，
   20px 会让两栏像两块互不相关的卡片。 */
.batch-form {
   display: flex;
   flex-wrap: wrap;
   gap: 12px;
   align-items: flex-start;
}

.batch-form-main {
   flex: 1 1 380px;
   display: flex;
   flex-direction: column;
   min-width: 0;
}

/* 标题已按参考图去掉（见 batch/ping.html 里 <label class="sr-only">）。
   保留一条兜底：除 .sr-only 外的 label 一律不占位，避免以后有人在
   batch 页里加了 label 又把高度顶出去。 */
.batch-form-main>label:not(.sr-only) {
   display: none;
}

/* —— 输入区 ——
   参考图的口径：**输入框与右侧按钮行同高、同一张"面板"的观感**，
   不再是一个带圆角边框的小卡片浮在卡片里：
   · 无背景填色（白底）—— 外层 .batch-card 已是白底，再填灰会形成"卡中卡"；
   · 极淡边框 #e9edf1，四角 4px（比外层 12px 明显更方，层级才分得清）；
   · 行高 1.9，多行域名列表扫读整齐；
   · resize: vertical 保留（用户偶尔要拉高看长列表）。 */
.batch-form-main textarea {
   width: 100%;
   flex: 1 1 auto;
   min-height: 178px;
   background: #fff;
   border: 1px solid #e9edf1;
   border-radius: 4px;
   padding: 12px 16px;
   font-family: inherit;
   font-size: 13px;
   line-height: 1.9;
   color: #3b4a55;
   resize: vertical;
   box-shadow: none;
   /* 干掉浏览器默认内阴影 */
   transition: border-color .15s, box-shadow .15s;
}

/* placeholder 是这里唯一的"说明文字"（标题已去掉），因此给它正文级的可读性：
   压得太浅会让人以为框里已经有内容。 */
.batch-form-main textarea::placeholder {
   color: #a8b3bd;
}

.batch-form-main textarea:hover {
   border-color: #dbe3ea;
}

.batch-form-main textarea:focus {
   outline: none;
   border-color: #4aa3f5;
   box-shadow: 0 0 0 3px rgba(42, 157, 244, .12);
}

/* 滚动条收窄圆角：多行列表常驻滚动，默认宽滚动条很突兀 */
.batch-form-main textarea::-webkit-scrollbar {
   width: 8px;
}

.batch-form-main textarea::-webkit-scrollbar-track {
   background: transparent;
}

.batch-form-main textarea::-webkit-scrollbar-thumb {
   background: #d5dee5;
   border-radius: 4px;
}

.batch-form-main textarea::-webkit-scrollbar-thumb:hover {
   background: #b7c6cf;
}

/* 底部说明：参考图里它紧贴输入框下方、与输入框左内边距对齐，字号 12px 浅灰。 */
.batch-form-main .input-hint {
   margin-top: 10px;
   color: #97a2ac;
   font-size: 12px;
   line-height: 1.6;
}

/* 右栏加宽到 304px：三个按钮并排（开始测试 / 节点选择 / 完整截图）时，
   280px 会把文字挤到折行。

   ===== 这里为什么要 `flex-direction: row` + `flex-wrap: wrap` =====
   右栏里其实有**三块**东西，且需要两种排法：
     · 前两块（开始测试 / 完整截图）→ 要**水平并排**在同一行；
     · 第三块（.form-toggles 选项面板）→ 要**独占一整行**、铺满 304px。
   又因为「不在模板加容器、不改类名」的约束，不能把前两块包一层单独的
   flex 行，所以用**同一个容器 + 允许换行**来同时满足两种排法：
     - 容器改成横向，前两块自然并排；
     - `.form-toggles` 自己写 `flex: 0 0 100%`（见下段），把行占满 ⇒ 被迫换到
       第二行 ⇒ 视觉上仍然在按钮下方、通栏。
   这就是"两个按钮一行、面板另起一行"不用改模板的实现方式。

   ⚠️ 因此 `.batch-form-side` **不能再是 column**。改成 row 后，`align-items`
   在交叉轴（纵向）上的默认 stretch 会让同一行内的高度互相对齐 ——
   这恰好是我们想要的（两颗按钮都 44px，一致）。 */
.batch-form-side {
   width: 304px;
   flex: 0 0 304px;
   display: flex;
   flex-direction: row;
   flex-wrap: wrap;
   gap: 10px;
}

.batch-form-side .btn {
   justify-content: center;
}

/* ===== 右栏两颗按钮：开始测试 / 完整截图 =====

   ⚠️ **纯 CSS 实现，模板里没有任何额外容器或专用类名**（用户要求不在模板加类）。
   模板里这两颗按钮就是 .batch-form-side 的第 1、2 个直接子元素：

     <div class="batch-form-side">
       <button class="btn btn-start">开始测试</button>      ← :nth-child(1)
       <button class="btn btn-secondary">完整截图</button>  ← :nth-child(2)
       <div class="form-toggles">…</div>                    ← :nth-child(3)，不受影响
     </div>

   所以用 `:nth-child(n)` 定位 —— 模板不加容器、不改类名也能拿到同样的尺寸与配色。
   ⚠️ 这条结构约定是硬约束：若将来右栏新增/调换按钮顺序，必须同步改这里的 n。

   为什么不用 `> .btn`：.batch-form-side 的子元素里只有这两颗是 .btn，但
   .form-toggles 内部也可能出现 .btn（展开/收起之类），`>` 虽然够用，
   却无法区分"哪一颗是主按钮、哪一颗是截图"，而两者配色不同 —— 必须用序号。 */

/* 尺寸与配色：两颗等宽、各 44px 高、圆角 3px，同一行水平并排。
   ⚠️ 宽度用 `flex: 1 1 0` 平分，不再写死 126px：
   容器现在是**横向** flex（304px + gap 10px），两颗各得 (304-10)/2 = 147px，
   写死 126px 会白白留下 31px 的空隙、右边缘参差不齐。
   `1 1 0` 的 basis 取 0 ⇒ 严格等分，与内容长短无关（「开始测试」「完整截图」
   都是四字，本来也一样宽，但 basis:0 能保证将来文案变化后仍然等宽）。

   ⚠️ **不要再加 `align-self: flex-start`**：那条是上一版为了抵消 column 容器
   的横向 stretch 而写的；现在容器是 row，交叉轴变成纵向，flex-start 会让按钮
   退化成内容高度、不再撑到 44px（等于把 height 变成上限而非定值）。
   保持默认 stretch + 显式 height:44px 即可。

   ⚠️ **也不要写 `flex: 0 0 126px` 这种简写**：上一版按钮变成大方块正是因为
   容器是 column 时 basis 作用在高度上。现在容器是 row，basis 才是宽度；
   但用 `1 1 0` 比定值更合适，无需再引入固定像素。

   44px 是**含边框**的目标尺寸，必须 border-box，否则实际是 46px 高。
   box-sizing 写在这里而不是靠全局 * 规则：批量页曾被某个局部内容区
   （admin 表格等）覆盖过，写清楚更稳。

   display:inline-flex + 居中：`justify-content: center` 让文字在按钮内居中；
   文字里的 <svg> 是行内元素，inline-block 会跟基线走、在 44px 高按钮里偏下
   1~2px，flex 的 align-items:center 才让图标与文字落在同一中线上。 */
.batch-form-side>.btn:nth-child(1),
.batch-form-side>.btn:nth-child(2) {
   display: inline-flex;
   align-items: center;
   justify-content: center;
   flex: 1 1 0;
   min-width: 0;
   height: 44px;
   box-sizing: border-box;
   padding: 0;
   border-radius: 3px;
   border: 1px solid transparent;
   font-size: 14px;
   font-weight: 400;
   line-height: 1;
   white-space: nowrap;
}

/* ===== 按钮图标（::before + mask 绘制，模板里没有 <svg>） =====

   ⚠️ 这两颗按钮在模板里**只有文字**，本来就没有 <svg class="btn-icon">；
   而约束是「不改模板、也不在模板里加类名」，所以图标不能用内联 SVG 补。
   这里用 `::before` + `mask` 把 SVG 路径当"形状"用：伪元素本身只是一个
   14×14 的方块，`mask` 按路径把它抠成图标轮廓，再用 `background:
   currentColor` 填色 —— 于是图标自动跟随按钮文字色（白字 → 白图标），
   hover 换色时也同步，不需要为蓝/橙两种底色各写一份。

   图标路径**直接沿用工具页**（partials/tool_form_ping.html）：
   · 第 1 颗「开始测试」→ 那个循环箭头（单次测试按钮的图标）
   · 第 2 颗「完整截图」→ 相机（capture-btn 的图标）
   这样批量页与工具页的同一操作看起来是同一个图标，符合"跨页面一致"的要求。
   几何口径也保持一致：viewBox 24、stroke-width 2、圆头圆角。

   ⚠️ mask 里的 SVG 必须把描边写成 `stroke='%23fff'`（URL 编码的 #fff）：
   mask 取的是**亮度**，白色 = 可见区域，黑色 = 透明。写成 currentColor
   在 data URI 里不生效（没有继承上下文），图标会整个消失。
   ⚠️ `-webkit-mask` 与 `mask` 各写一份：Safari 仍需前缀。
   ⚠️ 图标与文字的间隙用 margin-right，不用 gap：与 base.css 的 `.btn-icon`
   口径一致（那颗也是 margin-right:6px）。 */
/* ⚠️ 只在支持 mask 的浏览器里生成图标：不支持的话，伪元素会退化成一个
   14×14 的**实心色块**（比没有图标更难看）。`@supports` 判断 `mask-image`
   或 `-webkit-mask-image` 任一可用即可，老浏览器干脆不生成。 */
@supports (mask-image: url("x")) or (-webkit-mask-image: url("x")) {
   .batch-form-side>.btn:nth-child(1)::before,
   .batch-form-side>.btn:nth-child(2)::before {
      content: "";
      flex: 0 0 auto;
      width: 14px;
      height: 14px;
      margin-right: 6px;
      background-color: currentColor;
      -webkit-mask-repeat: no-repeat;
      mask-repeat: no-repeat;
      -webkit-mask-position: center;
      mask-position: center;
      -webkit-mask-size: 100% 100%;
      mask-size: 100% 100%;
   }
}

/* 第 1 颗：循环箭头（同工具页「单次测试」）。
   两条路径：外圈弧 + 回环箭头 / 内圈弧 + 回环箭头，与工具页逐字一致。 */
.batch-form-side>.btn:nth-child(1)::before {
   -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.49 9A9 9 0 0 0 5.64 5.64L4 7'/%3E%3Cpath d='M4 3v4h4'/%3E%3Cpath d='M3.51 15a9 9 0 0 0 14.85 3.36L20 17'/%3E%3Cpath d='M20 21v-4h-4'/%3E%3C/svg%3E");
   mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.49 9A9 9 0 0 0 5.64 5.64L4 7'/%3E%3Cpath d='M4 3v4h4'/%3E%3Cpath d='M3.51 15a9 9 0 0 0 14.85 3.36L20 17'/%3E%3Cpath d='M20 21v-4h-4'/%3E%3C/svg%3E");
}

/* 第 2 颗：相机（同工具页「完整截图」capture-btn）。
   机身圆角矩形 + 顶部取景凸起 + 镜头圆，与工具页逐字一致。 */
.batch-form-side>.btn:nth-child(2)::before {
   -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5A2.5 2.5 0 0 1 5.5 6h1.7a1.5 1.5 0 0 0 1.3-.8l.6-1a1.5 1.5 0 0 1 1.3-.8h3.2a1.5 1.5 0 0 1 1.3.8l.6 1a1.5 1.5 0 0 0 1.3.8h1.7A2.5 2.5 0 0 1 21 8.5v8A2.5 2.5 0 0 1 18.5 19h-13A2.5 2.5 0 0 1 3 16.5z'/%3E%3Ccircle cx='12' cy='12.2' r='3.2'/%3E%3C/svg%3E");
   mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5A2.5 2.5 0 0 1 5.5 6h1.7a1.5 1.5 0 0 0 1.3-.8l.6-1a1.5 1.5 0 0 1 1.3-.8h3.2a1.5 1.5 0 0 1 1.3.8l.6 1a1.5 1.5 0 0 0 1.3.8h1.7A2.5 2.5 0 0 1 21 8.5v8A2.5 2.5 0 0 1 18.5 19h-13A2.5 2.5 0 0 1 3 16.5z'/%3E%3Ccircle cx='12' cy='12.2' r='3.2'/%3E%3C/svg%3E");
}

/* 第 1 颗：开始测试（主按钮，实心蓝）。
   ⚠️ 底色 #4285ed 与工具页主按钮同值（`.tool-page > .tool-form .btn-primary`，
   result-panel.css）；hover #326fce 也直接沿用那条的取值。此前是
   #2a9df4 / #1d8cd9，与同屏的进度条、选中胶囊、其它工具页按钮都不一致 ——
   批量页一进来就能看出是"另一套蓝"。 */
.batch-form-side>.btn:nth-child(1) {
   background: #4285ed;
   border-color: #4285ed;
   color: #fff;
}

.batch-form-side>.btn:nth-child(1):hover {
   background: #326fce;
   border-color: #326fce;
}

/* 第 2 颗：完整截图（橙色）。导出截图是批量页的高频动作，
   白底灰字的次级样式视觉权重不够，故提为主色。

   模板里它带的类仍是 `btn-secondary`（原样保留，未改名）。原先这里有一条
   `.batch-form-side .btn-secondary { 白底 / 38px }` 的次级样式，已被本段
   `:nth-child(2)` 整体取代（那条规则在「不加类名」的前提下只会和橙色打架），
   **已删除**。所以现在右栏不存在"白底灰字"的第二颗按钮了。 */
.batch-form-side>.btn:nth-child(2) {
   background: #f5a33a;
   border-color: #f5a33a;
   color: #fff;
}

.batch-form-side>.btn:nth-child(2):hover {
   background: #e69326;
   border-color: #e69326;
}

/* 选项面板（对齐参考图）：浅灰底 + 无外框 + 3px 圆角，与按钮行同一列宽。
   参考图里这块是"按钮下方的一条灰带"，没有再描一圈边框 —— 描边会和
   .batch-card 的外框叠出两道平行线。

   ⚠️⚠️ `flex: 0 0 100%` 是"两个按钮一行、面板另起一行"的关键：
   容器已改成 row + wrap（见 .batch-form-side 段），前两块按钮各占约一半、
   共占满第一行；本面板声明 basis 100% ⇒ 第一行放不下它 ⇒ **被迫换行**，
   于是它独占第二行并且通栏（304px）。去掉这条，面板会挤到按钮右边变成
   第三列，或者被压扁 —— 两种都不是要的效果。

   ⚠️ 只写 `flex: 0 0 100%`，**不要再叠一条 `width:100%`**：basis 已经决定
   宽度，再写 width 属于重复声明；且两者一旦在后续改动里被拆开（例如有人改了
   basis 却没改 width），实际宽度会变得难以推断。唯一来源更好维护。
   `max-width: 100%` 兜底：防止 basis 100% 与 gap 相加后把本行撑出容器。 */
.batch-form-side .form-toggles {
   flex: 0 0 100%;
   max-width: 100%;
   background: #f5f7f9;
   border: 0;
   border-radius: 3px;
   padding: 12px 14px;
   display: flex;
   flex-direction: column;
   gap: 12px;
}

.batch-form-side .form-toggles>label {
   display: flex;
   align-items: center;
   gap: 8px;
   font-size: 12px;
   color: #5c707d;
   cursor: pointer;
}

.batch-form-side .form-toggles .toggle-row {
   margin-top: 6px;
   display: flex;
   gap: 12px;
   flex-wrap: wrap;
}

.batch-form-side .form-toggles .toggle-row label {
   font-size: 12px;
}

.batch-form-side .form-toggles .probe-options {
   display: flex;
   flex-direction: column;
   row-gap: 14px;
   width: 100%;
}

/* 每行：左表头(92px) + 右控件(1fr)，独立 grid 避免 display:contents 兼容空白 */
.form-row {
   display: grid;
   grid-template-columns: 92px 1fr;
   align-items: center;
   column-gap: 12px;
   width: 100%;
}

.form-label {
   font-size: 13px;
   font-weight: 600;
   color: #254150;
   text-align: left;
   white-space: nowrap;
}

/* IP 版本：单选框（对齐 itdog 参考图）
   —— 参考图的单选是**自绘圆环**：未选中 16px 灰色圆圈 + 1.5px 边框，
      选中变成蓝色圆环 + 内部实心蓝点，且圆与文字间距 10px。
      原生 input 的 appearance 是系统外观（Windows 下是蓝色小圆），
      尺寸/配色都改不动，必须 appearance:none 自己画。
   ⚠️ 不能改模板（input 与 label 是并列兄弟节点，没有包裹容器），
      所以 label 的点击区域只能靠 label[for] 原生行为，不能靠父级 :has()。 */
.segmented {
   display: inline-flex;
   gap: 18px;
   align-items: center;
   flex-wrap: wrap;
}

.segmented input[type="radio"] {
   appearance: none;
   -webkit-appearance: none;
   width: 16px;
   height: 16px;
   margin: 0;
   border: 1.5px solid #c2ccd4;
   border-radius: 50%;
   background: #fff;
   cursor: pointer;
   flex: 0 0 auto;
   position: relative;
   transition: border-color .15s, box-shadow .15s;
}

/* 选中：边框转蓝 + 内部 8px 实心点（用 ::after 画，避免另加 DOM） */
.segmented input[type="radio"]:checked {
   border-color: #2a9df4;
   background: #fff;
}

.segmented input[type="radio"]:checked::after {
   content: "";
   position: absolute;
   top: 50%;
   left: 50%;
   width: 8px;
   height: 8px;
   margin: -4px 0 0 -4px;
   border-radius: 50%;
   background: #2a9df4;
}

.segmented input[type="radio"]:hover {
   border-color: #8fbdec;
}

.segmented input[type="radio"]:checked:hover {
   border-color: #2a9df4;
}

/* 键盘聚焦：外扩一圈淡蓝光晕（原生 outline 在自绘圆环上会显得方且偏位） */
.segmented input[type="radio"]:focus-visible {
   outline: none;
   box-shadow: 0 0 0 3px rgba(42, 157, 244, .18);
}

.segmented label {
   font-size: 13px;
   color: #4a5a66;
   cursor: pointer;
   user-select: none;
}

/* 节点线路说明徽标 */
.badges {
   display: flex;
   flex-wrap: wrap;
   gap: 6px;
}

.badge {
   display: inline-flex;
   align-items: center;
   gap: 4px;
   padding: 3px 8px;
   border-radius: 12px;
   background: #eef5fb;
   color: #2a6fae;
   font-size: 11px;
}

.checkbox-line {
   display: flex;
   align-items: center;
   gap: 8px;
   font-size: 13px;
   color: #5c707d;
   cursor: pointer;
}

.checkbox-line input[type="checkbox"] {
   width: 15px;
   height: 15px;
   accent-color: #2a9df4;
   cursor: pointer;
   margin: 0;
}

.select-field {
   height: 32px;
   padding: 0 8px;
   border: 1px solid #c5d3da;
   border-radius: 6px;
   font-size: 12px;
   color: #44525c;
   background: #fff;
   width: 100%;
}

/* DNS 输入框：复用公共 partial dns.choice，适配批量侧 form-toggles 容器 */
.batch-form-side .form-toggles .probe-options-label {
   font-size: 13px;
   font-weight: 600;
   color: #254150;
   text-align: left;
   white-space: nowrap;
}

.batch-form-side .form-toggles .probe-dns-choice {
   width: 100%;
   gap: 10px;
   margin-left: 0;
   display: flex;
   align-items: center;
}

.batch-form-side .form-toggles .probe-dns-choice .dns-combobox {
   display: flex;
   align-items: center;
   flex: 1;
   width: auto;
}

.batch-form-side .form-toggles .probe-dns-choice .dns-input {
   flex: 1;
   width: 100%;
   min-width: 0;
   height: 32px;
   padding: 0 10px;
   border: 1px solid #d9e0e6;
   border-radius: 4px;
   font-size: 11px;
   color: #44525c;
   background: #fff;
   transition: border-color .15s, box-shadow .15s;
}

.batch-form-side .form-toggles .probe-dns-choice .dns-input::placeholder {
   color: #a6b1ba;
}

.batch-form-side .form-toggles .probe-dns-choice .dns-input:focus {
   border-color: #4285ed;
   box-shadow: 0 0 0 2px rgba(66, 133, 237, .15);
   outline: none;
}

/* ========== 结果区（对齐参考图）==========
   参考图的结果区是**一张白底大表**，没有第二层卡片描边/阴影：
   表头上方一条工具条（标题 + 胶囊筛选 + 复制 + 右侧进度），
   下面是通栏的表格，横向网格线很淡、靠留白分行。
   因此这里把原先的 12px 圆角 + 阴影收敛成 3px 圆角 + 1px 浅边，
   与上方 .batch-card（12px）拉开层级差，避免又一处"卡中卡"。 */
.batch-results {
   background: #fff;
   border: 1px solid #e6ecf0;
   border-radius: calc(.25rem - 0px) calc(.25rem - 0px) 0 0;
   box-shadow: none;
   overflow: hidden;
   margin-top: 10px;
}

/* 初始隐藏结果区，等探测开始后由 batch-*.js 用 style.display='block' 展示。
   内联 style 的 display 优先级高于类选择器，因此展示逻辑不受这条规则影响。 */
.batch-results.is-hidden {
   display: none;
}

/* 结果区工具栏。模板里的子元素只有两个：
      .batch-tabs  —— 五个筛选胶囊
      .batch-meta  —— 内部依次是 #batch-counter / .batch-progress / #copy-results
   而参考图（用户 2026-10-20 提供）要求这一行是：
      [| 检测结果]  [全部][100%响应][部分响应][响应率>0%][100%超时]  [⧉ 复制数据]  当前显示：10/10     ----     [10 个目标需要检测，当前进度：] ▬▬▬
   即：标题 + 筛选 + 复制 + 计数 全在**左侧连成一条**，右侧只留进度。

   ⚠️ 不改模板怎么做到"一半在左、一半在右"：用 `display: contents`
   把 .batch-meta 这个盒子拆掉，让它的三个子项直接参与外层 flex 排布
   （见下段），再用 order 指定各自位置。这一步是整个改动的支点。 */
.batch-results-header {
   display: flex;
   align-items: center;
   justify-content: flex-start;
   gap: 10px;
   padding: 1rem !important;
   border-bottom: 1px solid #edf1f4;
   flex-wrap: wrap;
}

/* ⚠️ 关键：`display: contents` 让 .batch-meta 这个盒子"消失"，
   它的三个子项（计数 / 进度条 / 复制按钮）直接参与**外层** flex 的排布。
   这样才可能把「复制数据」「当前显示」挪到筛选胶囊右边（和左组连成一条），
   而把进度条单独推到最右 —— 如果 .batch-meta 仍然是个实体盒子，它的子项
   只能整体待在一侧，无法一半在左一半在右。
   ⚠️ display:contents 的元素自身不产生盒子，因此它身上的
   background / padding / border / gap 全部失效（本元素本来也没有这些）。
   不要因为"想给右侧加间距"再回来写 .batch-meta 的 padding —— 不会生效。 */
.batch-meta {
   display: contents;
}

/* 外层顺序：筛选组(1) → 复制数据(2) → 当前显示(3) → 进度条(4)。
   这些子项现在同属 .batch-results-header 这一个 flex 容器，order 才有效。
   ⚠️ .batch-tabs 自身是个 flex 容器（横排胶囊），「检测结果」标题是它的
   ::before —— 所以标题 + 五个胶囊是**作为一个整体**参与外层排序的，
   不会被拆到别处。 */
.batch-tabs {
   order: 1;
}

/* 「检测结果」标题：模板里没有这个元素，用 ::before 补出来（不改模板）。
   ⚠️ 用 content 生成而不是伪元素里塞 <span>，是因为它只是纯文本 + 左侧竖线。
   ⚠️ 竖线用 border-left 而不是 ::before 的额外元素：少一层嵌套、也不用定位。
   参考图里竖线是**蓝色实线、约 3px 宽、与文字同高**，文字 15px 深灰。 */
.batch-tabs::before {
   content: "检测结果";
   display: inline-flex;
   align-items: center;
   flex: 0 0 auto;
   border-left: 3px solid #2a9df4;
   padding-left: 8px;
   margin-right: 14px;
   font-size: .9375rem;
   font-weight: 400;
   color: #2b3d4a;
   line-height: 20px;
   white-space: nowrap;
}

.batch-meta #copy-results {
   order: 2;
}

.batch-meta #batch-counter {
   order: 3;
}

.batch-meta .batch-progress {
   order: 4;
   /* 🚨 这里**不再写** margin-left:auto —— 那个 auto 原本用来把进度条推到行末，
      但「目标数提示」插在它前面，若 auto 留在进度条上，提示会留在左侧、
      进度条单独飞到最右，中间空一大截。auto 已改给提示（见下条）。 */
}

/* 目标数提示：排在进度条**紧左边**，与它一起构成右组的「N 个目标… ▬▬▬」。
   它由 JS 运行时创建插入，也是 .batch-meta 的子元素，所以必须参与 order 排序；
   不给 order 会拿到默认值 0，跑到最左边跟筛选胶囊挤在一起。
   ⚠️ 与 .batch-progress 同为 order:4，靠 DOM 顺序决定先后（提示先插入 ⇒ 在左）。
   ⚠️ `margin-left:auto` 放在这里而不是进度条上：auto 撑开的是它**左侧**的空间，
   于是「提示 + 进度条」作为一个整体被推到行末，两者之间始终保持 10px。 */
.batch-meta .batch-target-hint {
   order: 4;
}

/* ⚠️ `margin-left:auto` 只能挂在**有内容**的提示上。
   无目标时 JS 把内容清空，元素被 `.batch-target-hint:empty { display:none }` 隐藏，
   display:none 的元素不参与 flex 布局、它的 auto 也就不起作用 ⇒ 进度条会
   掉回行首（紧跟「当前显示」）。所以这里用 :not(:empty) 限定，
   没内容时改由进度条自己吃 auto，保证条始终贴右。 */
.batch-meta .batch-target-hint:not(:empty) {
   margin-left: auto;
}

/* 提示为空时的兜底：进度条重新承担"推到行末"的职责。
   用 :empty + 兄弟选择器，只在提示确实被隐藏时生效，两者不会同时撑开。 */
.batch-meta .batch-target-hint:empty + .batch-progress {
   margin-left: auto;
}

/* 左组（标题 + 筛选 + 复制数据 + 当前显示 x/y）：四件都在描述
   "当前这批行"——标题标明区域、胶囊决定筛哪批、按钮拿走这批、计数说出这批有多少。
   用 10px 间距连成一条，视觉上是一组控件而不是散件。
   ⚠️ 【现状】模板里并没有 `.batch-head-left` 这个容器（四个批量页都是
   `.batch-meta` 装着计数/进度/复制按钮），所以本段是历史残留、不生效。
   真正生效的计数与复制按钮样式见下面 `.batch-meta #batch-counter` 与
   `.batch-meta #copy-results` 两段。 */
.batch-head-left {
   display: flex;
   align-items: center;
   gap: 10px;
   flex-wrap: wrap;
}

/* 「当前显示 x/y」：与全站工具页（ping/dns/tcping/http）同款配色 ——
   标签灰 #737b83、数字蓝 #4285ed 且加粗。那套定义在 site/ping.css 的
   `.result-count-label` / `.result-count-label b` 里，取值照抄但选择器不能复用：
   本页计数是裸 `<span id="batch-counter">`，没有 `.result-count-label` 类。
   ⚠️ 字号取 13px（工具页是 14px）：批量页元信息行整体 13px 口径（跟随 td 与
   .batch-meta），这里单独放到 14px 会比旁边的筛选胶囊还显眼。
   ⚠️ 灰色写在**父级 span** 上，蓝色写在 `<b>` 上 —— JS 用 innerHTML 把数字包进
   <b>，这样"标签灰 / 数字蓝"的分层才成立。若某条写入路径退回 textContent，
   整串就会变灰（历史 bug，四处入口已统一到 setCounter()）。
   nowrap 防止窄屏被折成"当前显示："换行"4/4"。 */
/* ⚠️ 选择器从 `.batch-head-left` 改成 `.batch-meta`：模板里「当前显示 x/y」实际在
   `.batch-meta` 容器内（左组 .batch-head-left 在四个批量页里都不存在）。
   此前写成 batch-head-left 导致**整条规则从未命中**，计数一直是 12px 默认灰，
   分子也不显蓝 —— 截图里「当前显示：2/2」全是灰字正是这个原因。
   id 选择器本身特异性已够（(1,1,0)），无需再加权重。 */
.batch-meta #batch-counter {
   font-size: 13px;
   color: #737b83;
   white-space: nowrap;
}

/* 计数里**只有分子**是蓝色（= 当前筛出来的行数），斜杠与分母保持灰色。
   这是工具页的口径：`当前显示：<b>N</b>/M`，<b> 只包分子。
   ⚠️ 别把整个 x/y 都包进 <b>，否则分母也变蓝、整个"5/5"糊成一块蓝字，
   看不出"筛出几条 / 共几条"的对比。JS 侧见 setCounter(shown, total)。
   ⚠️ 阈值比较语义：蓝色是"现在看到多少"，灰色是"总数"，两者分色才有信息量。 */
.batch-meta #batch-counter b {
   color: #4285ed;
   font-weight: 500;
}

/* 「检测结果」前面那根蓝色竖线：参考图用它标出"这里是结果区的起点"，
   比加粗标题更省横向空间，也不与胶囊筛选抢注意力。 */
.batch-results-title {
   display: inline-flex;
   align-items: center;
   font-size: 15px;
   font-weight: 500;
   color: #2b3d4a;
   padding-left: 10px;
   margin-right: 6px;
   border-left: 3px solid #2a9df4;
   line-height: 20px;
}

/* 筛选胶囊（检测结果标签）：未选中是白底灰字描边，选中是实心蓝 —— 参考图里
   五个胶囊大小一致、靠状态而不是大小来区分。高度 26px 与右侧进度条行高对齐。

   ⚠️ 配色与全站筛选标签统一为 #4285ed（= css/site/ping.css 的
   `.ping-filter button.is-active`、result-panel.css 的工具页主按钮、
   site/ping.css 的进度条填充）。此前这里是 #2a9df4（青蓝）与 #2a6fae（teal），
   和右侧进度条、和其它工具页放在一起是三种不同的蓝，一屏之内就露馅。
   未选中态也更贴近 ping 页：边框 #e2e8ed、字 #84929c。 */
.batch-tabs {
   display: flex;
   gap: 6px;
   flex-wrap: wrap;
}

.batch-tabs button {
   background: #fff;
   border: 1px solid #e2e8ed;
   border-radius: 3px;
   padding: 0 10px;
   height: 23px;
   line-height: 24px;
   font-size: 14px;
   color: #84929c;
   cursor: pointer;
   transition: background .15s, color .15s, border-color .15s;
}

/* 悬停：只改边框与字色、不铺底 —— 铺底会和"选中态"的实心蓝混淆。
   ⚠️ 必须带 :not(.is-active)：否则鼠标划过选中项也会被染成蓝字蓝框，
   与 .is-active 同权重、谁写在后谁赢，选中项一悬停就"褪色"。 */
.batch-tabs button:not(.is-active):hover {
   border-color: #a9c9ea;
   color: #4285ed;
}

.batch-tabs button.is-active {
   background: #4285ed;
   border-color: #4285ed;
   color: #fff;
}

/* 复制数据（标签式轻按钮，紧贴筛选胶囊右侧）。

   🚨 【下方整段 `.batch-head-left …` 规则当前不生效，属于历史残留】
   模板 `templates/batch/*.html` 里**没有** `.batch-head-left` 容器、也**没有**
   `.btn-copy` 类：那颗按钮实际是 `.batch-meta > button.btn.btn-secondary#copy-results`。
   全项目搜索确认 `.batch-head-left` / `.btn-copy` 只出现在本文件里，没有任何
   模板或 JS 引用。保留是为了万一将来模板改回左组结构时还能直接复用，但
   **改复制按钮样式请改下面 `.batch-meta #copy-results` 那一段**，改这一段不会有任何效果。

   以下为原设计说明（针对旧结构）：
   ⚠️ 它挂的容器是 `.batch-head-left`（左组），选择器必须与模板一致：
   容器一改、选择器没跟着改，按钮会整体退回 `.btn` 默认的白底灰字。
   ⚠️ 这里刻意**不**复用 .btn-secondary 那套标准按钮：它同排的都是 23px 的
   筛选胶囊与 12px 小字，标准按钮（32~36px、更大内边距）插在中间会把这一行撑高、
   且看起来像"另起一组的操作"。所以尺寸跟进胶囊/元信息行：同高、同圆角、同色阶。

   —— 配色：橙色「淡底 + 橙字 + 无描边」——
   ⚠️ 这是**全站「复制」动作的统一色**，取自 ping 页 `.copy-ip-btn`
   （底 #fff3dd / 字 #ed9d36）。批量页此前的白底灰字与筛选胶囊完全同款，
   五个胶囊后面再挂一个长得一样的按钮，用户分不清哪个是"筛选"哪个是"操作"。
   换成橙色后它自己成为一个动作入口，且与右栏的「完整截图」橙色呼应。
   ⚠️ 不要加边框：参考样式是纯色块，加 1px 描边会让 23px 高的小按钮显脏。
   为了让**去掉边框后总宽度与旁边胶囊对齐**，padding 由 10px 收到 9px
   并补 `border:1px solid transparent` 占位 —— 后者是为了让 box-sizing 下
   高度与胶囊严格同高（缺了它，无边框元素比有边框的矮 2px）。

   图标：`.btn-icon`（base.css）给的是 margin-right:6px + flex:0 0 auto，
   与全站其它按钮共用同一口径，不再单独写 margin。
   ⚠️ 必须 display:inline-flex：<svg> 是行内元素，直接摆在文字前会跟着
   基线走、在 23px 高的小按钮里偏下 1~2px；flex + align-items:center
   才让图标与文字在同一中线上。
   ⚠️ line-height 必须归零重置：父级按钮的 line-height:24px 会残留到
   flex 容器上，把内容整体往下推。 */
/* ⚠️ 边框必须写死成 transparent 而不是靠"不写"：外层 `.btn:hover`
   （base.css）会把 border-color 改成 #4A90D9，若这里没写 border-color，
   鼠标一移上去就会出现一圈蓝边 —— 这正是"去边框后仍然显脏"的常见坑。 */
.batch-head-left .btn-copy {
   display: inline-flex;
   align-items: center;
   background: #fff3dd;
   border: 1px solid transparent;
   border-color: transparent;
   border-radius: 3px;
   color: #ed9d36;
   font-size: 14px;
   height: 23px;
   padding: 0 9px;
   line-height: 1;
   cursor: pointer;
   transition: background .15s, color .15s;
}

/* 悬停：底色加深一档而不是变蓝 —— 橙色系按钮的 hover 若转成蓝字，
   会瞬间"换了身份"，和右侧「完整截图」的橙色也对不上。
   #fde8c4 是 #fff3dd 同色相加深约 8%，只够看出"可按"，不抢眼。 */
.batch-head-left .btn-copy:hover {
   background: #fde8c4;
   border-color: transparent;
   color: #d8871f;
}

/* 按下：再深一档，给点击一个有落差的反馈（小按钮没有影子，只能靠底色） */
.batch-head-left .btn-copy:active {
   background: #fbdfb0;
}

/* 键盘聚焦：橙色描边光环，与按钮色同系（焦点环不能只有颜色深浅差，
   否则键盘用户看不出焦点落在哪）。
   ⚠️ 必须写出来覆盖外层 `.btn:focus-visible`（base.css 里是蓝色描边）：
   蓝环套在橙底按钮上是两种色相硬碰，观感像"焦点跑到别的控件上了"。 */
.batch-head-left .btn-copy:focus-visible {
   outline: 2px solid #f0ac4a;
   outline-offset: 1px;
}

/* 图标尺寸：13px。与按钮 14px 字略小一档，避免图标比汉字还"重"；
   内联 width/height 属性已给 13，这里再写一次防止别处 svg 规则覆盖。
   stroke="currentColor"（写在模板上）保证 hover 变色时图标同步。 */
.batch-head-left .btn-copy .btn-icon {
   width: 13px;
   height: 13px;
   flex: 0 0 auto;
   margin-right: 5px;
}

/* —— 右组：目标数提示 + 进度条 ——
   同一条横线上两件事：「N 个目标需要检测，当前进度：」→ 胶囊进度条
   （百分比居中压字）。这两件描述的都是**整批检测的规模与进度**。

   ⚠️「当前显示 x/y」与「复制数据」已移回左组，和筛选胶囊放在一起 ——
   它们作用于"筛选后的可见行"，与"检测了多少个目标"不是一回事。
   混在右组会让人把「4/4」读成检测进度，与旁边的进度条打架。

   与上一版的差别（三处都是刻意的）：
   1. 进度条**始终是胶囊形**：直边条像根"分隔线"而不是进度，两端全圆才有
      "填充"的量感。当前尺寸 180×12（见 .batch-progress）。
   2. 百分比改回**居中压字**（原先是右端小字）。右端小字在 0% 时贴在
      浅灰轨道最右边，读起来像"轨道上的标注"；居中后它是"填充的一部分"。
      文字用 ::after 挂在**填充条**上（不是轨道）：只会在已完成的区域内出现，
      不会被误读成"整根轨道即 100%"。条太窄装不下字时退化为隐藏，见 is-low。
   3. 加一层内描边（inset box-shadow）压住白字边缘，避免 100% 时白字与
      填充蓝糊成一片。

   ⚠️ 数字块：参考图里目标数「N」外套一层浅蓝圆角底，样式见下方
   `.batch-target-hint-n` 段（由 JS 用真实 span 包住数字，不再用
   ::first-letter —— 那套在 attr() 拼接场景下行为不可靠，已废弃）。

   🚨 【这里原本还有一条 `.batch-meta { display:flex; flex:1 1 auto;
   justify-content:flex-end }`，已删除】
   那条会让 .batch-meta 变成一个实体 flex 盒子，把三个子项全锁在右侧 ——
   与上面 `.batch-meta { display:contents }` 直接冲突（同特异性、后者靠后，
   会覆盖掉前者），导致「复制数据 / 当前显示」怎么也挪不到筛选胶囊右边。
   删除后只保留 contents 那一条，子项统一由外层 flex + order 排布。 */

/* ===== 「复制数据」按钮加复制图标（纯 CSS，模板未改）=====

   ⚠️ 模板里这颗按钮是 `.batch-meta > button.btn.btn-secondary#copy-results`，
   内部**只有文字、没有任何 <svg>**，而且用户明确要求不动模板 —— 所以图标
   用 `::before` + `mask` 画出来，不去模板里插标签。

   为什么用 mask 而不是 background-image：
   图标要跟着按钮文字一起变色（hover 时变深），background-image 的 SVG 里
   `stroke` 是写死的、拿不到 `currentColor`。mask 的做法是"用图形开个洞、
   再把 background-color 填进去"，因此 `background-color: currentColor`
   就能让图标永远与文字同色，hover / active / disabled 都不用额外写规则。

   SVG 路径取自 local.html 的 `.local-copy`（同一个"两个叠放的方框"复制图标），
   保持全站复制图标形状一致：
     <rect x=9 y=9 w=12 h=12 rx=2/>      右上那张纸
     <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>  左下那张纸

   ⚠️ data URI 里的 `<` `>` `"` `#` 全部做了百分号转义（%3C %3E %22 %23）：
   未转义的 `<` 与 `#` 在部分浏览器下会让整张图静默失效（表现为"图标空白但
   控制台不报错"，很难查）。
   ⚠️ 只写了 viewBox 和 stroke 属性：mask 模式下 SVG 的填充/描边颜色
   全被忽略（只取 alpha 通道），所以不必也不能设 currentColor —— 它在这里
   只是让图形本身"有墨水"，实际颜色完全由下面的 background-color 决定。 */
/* 按钮本体：按参考图做成「白底 + 1px 蓝框 + 蓝字 + 复制图标」，
   与左侧筛选胶囊同高、同圆角，视觉上是这一行里的一个并列控件而不是大按钮。

   ⚠️ 模板里它挂的是 `btn btn-secondary`（白底灰字）。这里用 id 选择器覆盖，
   特异性 (1,0,0) 足以压过 `.batch-meta .btn` / `.btn-secondary` 的任何组合，
   所以**不需要改模板类名**。
   ⚠️ 高度 23px 必须与 `.batch-tabs button` 严格一致：两者同排，差 1px 就会
   出现"按钮比胶囊高半格"的错位。字号也取 14px 与胶囊同号。 */
.batch-meta #copy-results {
   display: inline-flex;
   align-items: center;
   justify-content: center;
   gap: 5px;
   height: 23px;
   padding: 0 10px;
   background: #fff;
   border: 1px solid #a9c9ea;
   border-radius: 3px;
   color: #4285ed;
   font-size: 14px;
   line-height: 1;
   white-space: nowrap;
   transition: background .15s ease, color .15s ease, border-color .15s ease;
}

/* 悬停：淡蓝底 + 加深边框 —— 不能直接变实心蓝，否则和"选中的筛选胶囊"
   撞在一起，用户会以为这个按钮也是当前筛选状态。
   ⚠️ border-color 必须显式写：base.css 的 `.btn:hover` 会改成 #4A90D9，
   与这里的 #a9c9ea 是两个不同的蓝，不压住会跳色。 */
.batch-meta #copy-results:hover {
   background: #edf4ff;
   border-color: #4285ed;
   color: #326fce;
}

.batch-meta #copy-results:active {
   background: #e0ecff;
}

/* 键盘焦点：蓝色描边光环，与按钮同色系（base.css 默认的蓝边太细，看不出来）。 */
.batch-meta #copy-results:focus-visible {
   outline: 2px solid #a9c9ea;
   outline-offset: 1px;
}

.batch-meta #copy-results::before {
   content: "";
   flex: 0 0 auto;
   /* 用 em 而不是 px：按钮字号（14px）若再调整，图标自动跟随，不必两处一起改。
      1em = 14px，与按钮文字视觉等重；再大会比汉字还"重"。
      做法与 ping.css 的 `.copy-ip-btn svg { width:1em; height:1em }` 同口径。 */
   width: 1em;
   height: 1em;
   /* 与文字同色：蓝 #4285ed → hover #326fce，图标自动跟随，无需额外规则 */
   background-color: currentColor;
   -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='9' y='9' width='12' height='12' rx='2'/%3E%3Cpath d='M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
   mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='9' y='9' width='12' height='12' rx='2'/%3E%3Cpath d='M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
}

/* ===== 目标数提示：「N 个目标需要检测，当前进度：」，贴在进度条左侧 =====

   ⚠️ 这个 span **不是模板里的**，由 batch-ping/dns/tcping/http.js 在运行时
   创建并插入到 `.batch-progress` 之前（见各 JS 的 updateTargetHint）。
   模板没有该元素、也不允许加，所以别在模板里找它 —— 刷新页面后它就在进度条左边。

   为什么用真实 span 而不是 `.batch-progress::before { content: attr(...) }`：
   参考图里数字「N」外面套了一层**浅蓝圆角底**，伪元素产出的文本无法再拆出
   「数字」这一小段单独上底色（::first-letter 在 attr() 拼接串上行为不可靠），
   所以需要一个真实子元素 `> .batch-target-hint-n` 包住数字。

   ⚠️ 选择器用 `.batch-target-hint`（无前缀）：它是运行时加的类名，不属于模板，
   不会和"不改模板类名"的约束冲突。若将来改名，只需同步四个 JS 里的一处字符串。 */
.batch-target-hint {
   display: inline-block;
   flex: 0 0 auto;
   /* 与右侧进度条之间留 10px，避免文字贴住轨道左端 */
   margin-right: 10px;
   color: #4a5b68;
   font-size: 13px;
   line-height: 1.5;
   white-space: nowrap;
   /* 无目标时 JS 会把内容清空，元素本身仍有 margin —— 隐藏掉避免留白 */
}

.batch-target-hint:empty {
   display: none;
}

/* 数字色块：浅蓝底 + 蓝字 + 圆角，取值与全站「浅蓝底 + 蓝字」一致 ——
   #e7efff / #4285ed，和 ping.css 的 `.ping-progress-meta strong`、
   本页 `.panel-count` 同值。 */
.batch-target-hint-n {
   display: inline-block;
   padding: 1px 6px;
   margin-right: 3px;
   background: #e7efff;
   border-radius: 4px;
   color: #4285ed;
   font-size: 13px;
   font-weight: 600;
   line-height: 1.4;
   /* 数字用等宽体避免「1」比「10」窄一截时光学上偏位；
      tabular-nums 让多位数宽度稳定，切目标数时色块不会跳宽。 */
   font-variant-numeric: tabular-nums;
}

/* 进度条：胶囊形、**180 × 12**（用户指定，与单目标页 .probe-progress-track
   的 12×180 同口径）。
   ⚠️ 尺寸不要再按"参考图更粗"往上加：18px 时百分比文字有足够空间压在条内，
   但整行（筛选胶囊 23px）会被它顶高；12px 是这个工具栏行高的上限。

   轨道 #e8eef3：比 #eef1f4 稍深一档。12px 下轨道更窄，太浅会完全看不出
   "还有多少没走完"，所以这里不再往浅调。
   填充 #4285ed：**全站统一的主蓝**（= 工具页 .btn-primary hover 前、
   ping 页 .probe-progress-track i、http 页 .http-progress-track i、以及
   本页选中的筛选胶囊）。此前是 #4aa3f5，与左侧选中胶囊的 #2a9df4 差着
   一个色阶，一条横线上两块蓝、一冷一亮。

   圆角 = 高度的一半（12 / 2 = 6）才是两端全圆；沿用 9px 会在 12px 高的条上
   算不过账（圆角半径大于半高，浏览器会按半高收敛，表现为两端略方）。 */
/* 进度条：原本是 180×12 的胶囊条。为了在**左侧**补一句
   「N 个目标需要检测，当前进度：」，这里把它拆成「文字 + 条」两部分：
     · ::before  → 提示文字，绝对定位到条左边；
     · 元素自身 → 只保留右侧 180px 的轨道与填充。
   实现方式是给元素加 `padding-left` 让出文字位，再用 background-clip
   限定底色只画在条的区域 —— **不加 DOM、不加类名**。

   ⚠️ 关键：`background` 只能画在内容区，所以底色改由 `background-clip: content-box`
   承担；同时 `border-radius` 也只作用于内容盒，胶囊形依然正确。
   ⚠️ `overflow: hidden` 从轨道上移除：它会连带裁掉左侧的 ::before 文字。
   填充条自己已有 border-radius，不需要靠父级裁切。
   ⚠️ 轨道宽度仍固定 180px（flex-basis 与 width 同步改），只是把这段宽度
   从"元素总宽"变成"内容区宽度"。 */
.batch-progress {
   position: relative;
   flex: 0 0 180px;
   width: 180px;
   height: 12px;
   background: #e8eef3;
   border-radius: 6px;
   overflow: hidden;
}

/* 填充条：它的百分比宽度是相对**父级内容盒**（180px 轨道）算的，
   父级加了 padding-left 后内容盒依然是 180px，所以这里不需要改宽度逻辑；
   只把圆角限定在条的两端（左侧不再是元素最左边缘，圆角照样可以全圆）。 */
.batch-progress-bar {
   position: relative;
   height: 100%;
   width: 0%;
   background: #4285ed;
   border-radius: 6px;
   transition: width .3s ease;
   /* 内描边压住白字边缘，100% 时字与蓝底不糊。
      12px 高度下 1px 内描边已占 1/6 高，不能再加粗。 */
   box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18);
}

/* 百分比：挂在**填充条**上而不是轨道上 —— 这样文字只会在已完成的区域内出现，
   不会被误读成"整个进度是 100%"（轨道上的居中字会一直停在中间不动）。
   left:0/right:0 + text-align:center = 在条内居中；条比文字窄时由 overflow 裁掉。

   ⚠️ 字号 11 → 10px：条高收到 12px 后，11px 字的行盒（≈15px）比条还高，
   上下会被 overflow 各裁掉 1~2px，字顶和字底发毛。10px 的行盒 ≈13px，
   仍然要溢出一点，所以额外把 line-height 写死成 1（=10px），
   让字形严格落在 12px 条内、且上下各留 1px 呼吸。
   ⚠️ 10px 是本行最小字号，不能再小：再小「100.00%」的两位小数会糊成一片。 */
.batch-progress-bar::after {
   content: attr(data-pct);
   position: absolute;
   inset: 0;
   display: flex;
   align-items: center;
   justify-content: center;
   font-size: 10px;
   line-height: 1;
   font-weight: 700;
   color: #fff;
   letter-spacing: .02em;
   pointer-events: none;
   white-space: nowrap;
   overflow: hidden;
}

/* 低进度（条宽度装不下文字，约 <25%）时，白字会被裁成半截。
   ⚠️ 阈值由 JS 判定（batch-ping.js 的 setProgress），因为 CSS 拿不到进度值。
   这里只管"低进度时把字藏起来"，数值同步写在 JS 那边改。
   ⚠️ 但文字挂在填充条上是刻意的（见上），所以不把字挪出去 ——
   挪出去会变成"文字浮在灰色轨道上"，与 100% 时的位置不一致、读数会跳。
   改为：条太窄时把百分比交给同一条线上的「当前显示 x/y」去表达
   （那个数字本来就是精确值），条本身只保留纯色填充，视觉上更干净。 */
.batch-progress.is-low .batch-progress-bar::after {
   content: none;
}

.batch-table-wrap {
   overflow-x: auto;
   max-height: 70vh;
   overflow-y: auto;
}

/* 表格字号：**表头 13px**，与全站结果表统一（home.css 的
   `.tool-page .result-table th` 是 13px/400/#39424a，ping/http/dns/traceroute
   全部对齐这个口径）。数据格仍是 12px —— 批量矩阵是"密表"，一个屏幕要看几十行
   ×N 条线路，12px 是密度上限；表头只有一行，放大到 13px 不影响行数，
   且能让"列名"与"数据"形成层级差。
   ⚠️ 若把数据格也改成 13px，6 列 × 30 行的延迟数字会明显挤，慎动。
   行高 1.5 + padding 11px 12px 保证 13px 字在 26px 左右的表头行里不顶边。 */
.batch-matrix-table {
   width: 100%;
   border-collapse: collapse;
   font-size: 12px;
}

/* 表头：**左对齐**、背景 #F7F7F7、字重 500、浅灰分隔线。
   排序：每个表头右上角一枚可点击的上下双箭头（仿 itdog 的 ⇕ 控件），
   样式与交互见下方「表头排序控件」一节的说明。
   ⚠️ 表头背景是 #F7F7F7，所以**冻结列的表头格必须单独写同色**：
   前两列（序号 / 检测目标）是 position:sticky + background:#fff，
   不覆盖的话横向滚动时会看到"灰表头里嵌着两块白格子"。 */
.batch-matrix-table th {
   background: #F7F7F7;
   color: #5f6f7b;
   font-weight: 500;
   /* 字号 13px + 字距 .02em：与全站结果表表头同一口径（home.css 的
       `.tool-page .result-table th`），避免批量页表头比 ping/http/dns 小一号。
       13px 下 padding 由 11px 收到 10px —— 字大了却不收，表头行会明显变厚，
       与数据行 60px 的节奏脱节。 */
   font-size: 13px;
   letter-spacing: .02em;
   padding: 10px 12px;
   border-bottom: 1px solid #e8edf1;
   white-space: nowrap;
   position: sticky;
   top: 0;
   text-align: left;
   z-index: 3;
}

/* —— 表头排序控件 ——
   DOM（由 JS 注入，见 batch-ping.js 的 injectSortButtons）：
   <th class="is-sortable">
       <button class="th-sort" data-sort="target" aria-label="按检测目标排序">
           <span class="th-label">检测目标</span>
           <span class="th-sort-ico">
               <i class="th-sort-tri up"></i><i class="th-sort-tri down"></i>
           </span>
       </button>
   </th>

   设计要点（对齐参考图）：
   · 两颗**实心三角**上下叠成 ⇕。不用字符 ⇅ / emoji —— 字符在不同 DPI 下
     粗细不一，且无法单独给上下任一三角上色来指示当前方向。
   · 常态两枚都淡灰；某一向激活时该三角变深、另一枚退到几乎不可见，
     "当前升序还是降序"一眼可辨（只有一根箭头时无从判断方向）。
   · 整块 button 包住列名 + 箭头 = 更大热区（10px 宽的三角本身仍偏小，
     直接当点击目标不够；批量页表头是高频点击区）。
   · 列名与箭头间距 4px，箭头紧贴列名而不飘到列尾，与参考图一致。 */
.batch-matrix-table th.is-sortable {
   padding-right: 8px;
}

/* 排序按钮包住列名：整块（列名 + 箭头）都可点击，热区更大、也更符合
   "点表头就排序"的直觉；display:flex 让列名与箭头基线对齐。 */
.batch-matrix-table th.is-sortable>button.th-sort {
   display: inline-flex;
   align-items: center;
   gap: 4px;
   max-width: 100%;
   background: none;
   border: 0;
   padding: 0;
   margin: 0;
   font: inherit;
   color: inherit;
   cursor: pointer;
   white-space: nowrap;
}

.batch-matrix-table th.is-sortable>button.th-sort:hover {
   color: #2a6fae;
}

.batch-matrix-table th.is-sortable>button.th-sort:focus-visible {
   outline: 2px solid #4aa3f5;
   outline-offset: 2px;
   border-radius: 2px;
}

/* 三角容器：宽 10px、高 15px，两枚三角（各 6px）+ 2px 间隙。
   尺寸从 7×4 放大到 10×6 的取舍：
   · 4px 底宽在 12px 表头字旁像两粒灰尘，鼠标悬停时才勉强看出是箭头；
     6px 底宽与 12px 字形高度（数字/汉字的 x-height）比例接近，可读性够。
   · 底宽再大（8px+）就会在 26px 高的表头里显得笨重，且列名必须让位。
   · 两枚三角之间的间隙 1→2px：放大后 1px 缝会糊成一整块菱形。
   点击热区仍是外层 button（整块列名+箭头），这里是纯视觉。 */
.th-sort-ico {
   display: inline-flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   gap: 2px;
   width: 10px;
   height: 15px;
   flex: 0 0 auto;
}

.th-sort-tri {
   width: 0;
   height: 0;
   display: block;
   border-left: 5px solid transparent;
   border-right: 5px solid transparent;
   transition: border-color .12s;
}

/* 上三角：border-bottom 着色，尖端朝上。底宽 10 + 高 6 = 略钝的等腰三角，
   比正三角（等高同宽）更接近常见排序箭头的形状。 */
.th-sort-tri.up {
   border-bottom: 6px solid #c3ccd3;
}

/* 下三角：border-top 着色，尖端朝下 */
.th-sort-tri.down {
   border-top: 6px solid #c3ccd3;
}

/* 升序（asc）：上三角为主，下三角退到几乎不可见 */
.batch-matrix-table th.is-asc .th-sort-tri.up {
   border-bottom-color: #4a5b68;
}

.batch-matrix-table th.is-asc .th-sort-tri.down {
   border-top-color: #dfe5ea;
}

/* 降序（desc）：下三角为主 */
.batch-matrix-table th.is-desc .th-sort-tri.down {
   border-top-color: #4a5b68;
}

.batch-matrix-table th.is-desc .th-sort-tri.up {
   border-bottom-color: #dfe5ea;
}

/* 该列正在排序时列名也加深，给出"整列参与排序"的额外提示 */
.batch-matrix-table th.is-asc>button.th-sort,
.batch-matrix-table th.is-desc>button.th-sort {
   color: #2b3d4a;
}

/* 数据格（参考图口径）：行高 60px 上下、四边内边距 14px 16px、
   横线 #eef1f4。**不加纵线** —— 靠列间距分列，密线会让 6 列数字糊成表格墙。
   ⚠️ 字号：整张表的内容区统一 13px（与延迟数字同级）。此前 td 继承 table 的
   12px、只有 .cell-latency 是 13px，同一格里"主数字 13 / 明细 12"两档混排，
   读起来像两个图层；现在正文一档 13px，明细行靠 .cell-detail 的浅灰色区分层级，
   不再靠字号差。
   ⚠️ 行高用**无单位 1.5**：写死 px 的话字号一变行高不跟着变，13px 字会贴边。 */
.batch-matrix-table td {
   padding: 14px 16px;
   border-bottom: 1px solid #eef1f4;
   vertical-align: middle;
   text-align: left;
   font-size: 13px;
   line-height: 1.5;
}

.batch-matrix-table tbody tr {
   transition: background .12s;
}

.batch-matrix-table tbody tr:hover {
   background: #f7fbff;
}

/* 前两列冻结（横向滚动时保持可读）：序号 80px / 目标 200px。
   目标给到 200px 是因为 www.163.com 这类 11 字符域名要完整显示、且 IPv6 目标
   也尽量落入同列宽（仍不可完全避免），不给省略号（域名截断后无法辨认是
   哪台机器）。
   ⚠️ 三处必须同步：本行两个 width、下一行第 2 列的 sticky left、以及
   `left: 80px`（= 序号列宽）。序号列改宽了却漏改 left，横向滚动时光标一过
   边界，目标列会与序号列重叠出一条白缝。
   ⚠️ max-width 与 width 同值：max-width 单独写会与 table-layout 的自动分配
   打架（列会被内容撑过 max-width 或反之被压窄），两者一致才算真正定宽。 */
.batch-matrix-table td:first-child,
.batch-matrix-table th:first-child {
   position: sticky;
   left: 0;
   background: #fff;
   z-index: 2;
   min-width: 80px;
   width: 80px;
   max-width: 80px;
}

.batch-matrix-table td:nth-child(2),
.batch-matrix-table th:nth-child(2) {
   position: sticky;
   left: 80px;
   background: #fff;
   z-index: 2;
   min-width: 200px;
   width: 200px;
   max-width: 200px;
}

/* ⚠️ 排序按钮的层叠上下文：th 是 z-index:3 的 sticky 表头，按钮若继承
   th 的 stacking context 会被相邻的冻结列（z-index:2）压住一角，
   表现为横向滚动时三角在列边界处被裁掉一半。单独抬到 4。
   （th 自身 overflow 保持默认 visible，不裁切子元素。） */
.batch-matrix-table thead th.is-sortable>button.th-sort {
   position: relative;
   z-index: 4;
}

/* 排序三角在 13px 表头里重新校粒度：上一版按 12px 表头定的 10×15，
   字一放大就显小。这里保持几何不变、把整体抬高 1px 与 13px 字的中线对齐
   （margin-top 而非改容器高，免得 th 行高被顶开）。 */
.batch-matrix-table th.is-sortable .th-sort-ico {
   margin-top: -1px;
}

/* 悬停行时冻结列背景跟着行色走，否则 sticky 列仍是白底、出现"断层"。
   （只作用于 tbody，不影响表头。） */
.batch-matrix-table tbody tr:hover td,
.batch-matrix-table tbody tr:hover th {
   background: #f7fbff;
}

/* 前两列（序号 / 检测目标）内容居中：这两列是"标识"而非"可比数值"，
   左对齐 + 80/200px 定宽会在右侧留出一片空白，视觉上像是没对齐；
   居中后列宽对称包裹内容，和右侧线路列的数值形成"标识居中 / 数据左齐"的分层。
   ⚠️ 线路列保持左对齐（td 的 text-align:left）：延迟数字 + 百分比明细是
   可纵向比较的数值，左对齐才能让小数点上下对齐，居中反而更难扫。
   ⚠️ 表头与单元格必须一起改，只改 td 会得到"表头靠左、数据居中"的错位。
   排序按钮是 inline-flex，父级 text-align:center 能把整块（列名+三角）居中。 */
.batch-matrix-table td:first-child,
.batch-matrix-table th:first-child,
.batch-matrix-table td:nth-child(2),
.batch-matrix-table th:nth-child(2) {
   text-align: center;
}

/* 冻结列（序号 / 检测目标）的**表头格**补回 #F7F7F7：
   这必须排在上面两条 background:#fff 之后，同优先级靠顺序压制；
   同时要给到与 th 规则一致的层叠位置，否则横向滚动时灰表头里会嵌两块白格。 */
.batch-matrix-table thead th:first-child,
.batch-matrix-table thead th:nth-child(2) {
   background: #F7F7F7;
}

/* 线路栏：不设固定宽，让浏览器按剩余宽度均分（参考图 6 列几乎等宽、
   填满整行）。固定 150px 会让列数少时整片留白挤在右侧，列数多时溢出横向滚动。
   ⚠️ 表头**不再按运营商上色**：参考图的列名一律是中性深灰，
   颜色全部让给延迟数值（那是这页唯一需要"扫"的信息）。
   原先 telecom 红 / unicom 蓝 / mobile 绿的上色会与单元格里的绿橙红打架，
   一屏看过去左右判读标准不一致。 */
/* 线路栏表头：JS 注入排序按钮后，列名被包进 button，text-overflow 的
   作用对象从 th 变成内部 .th-label span —— 省略号要写在 span 上，
   否则超长列名会把排序三角挤出列宽（截断箭头会让人以为不可排序）。 */
.batch-matrix-table th[class^="col-"] {
   min-width: 120px;
   color: #5f6f7b;
   white-space: nowrap;
   overflow: hidden;
}

.batch-matrix-table th[class^="col-"] button.th-sort {
   max-width: 100%;
}

.batch-matrix-table th[class^="col-"] .th-label {
   overflow: hidden;
   text-overflow: ellipsis;
   min-width: 0;
   max-width: 100%;
}

/* 目标名：常规字重，完整显示不截断（见上，域名截断等于丢信息）。
   字号随 td 统一 13px，这里不再单独设（与序号列同号，两列标识看起来才是一体的）。 */
.cell-target {
   font-weight: 400;
   color: #3b4a55;
   white-space: nowrap;
}

/* —— 待探测占位格：loading 转圈图标 ——
   原来格子里写「探测中...」灰色文字，一行 5 列重复五遍，批量页动辄几十行时
   屏上一片"探测中..."，信息量为零还抢注意力。现在改为一个转圈 spinner，
   语义不变（未返回）但不再占用阅读。
   与单目标结果页（base.css 的 .skeleton-cell:empty::after）同一套画法：
   「全透明圆 + 单边有色 border」再整体旋转，省掉 SVG / data-URI / 额外标签。
   ⚠️ 只能用 ::after 且 td 内**不能再有文本/子元素**（JS 侧传空内容），
   否则圈会排在文字后面。
   ⚠️ 尺寸 14px / 线宽 2px：批量表正文 12px，16px 的圈在这种密表里偏大，
   会把行高撑起来；14px 与延迟数字的视觉重量更接近。
   ⚠️ 优先级要压过 base.css 里 `.result-row-pending .skeleton-cell` 那条：
   批量表不在 .result-row-pending 里，天然不冲突，这里独立写死即可。 */
.batch-matrix-table td.cell-pending {
   color: #b0bcc4;
   /* 字号跟随 td 的 13px（不再单独降到 12px）：探测中 → 出结果时不发生字号跳变，
       行高也就稳住了。 */
   min-height: 20px;
}

.batch-matrix-table td.cell-pending::after {
   content: "";
   display: inline-block;
   width: 14px;
   height: 14px;
   box-sizing: border-box;
   border: 2px solid #bfd4f8;
   /* 底环：浅蓝，与弧段同色系 */
   border-top-color: #2f7cf6;
   /* 弧段：亮蓝，与全站 loading 主色一致 */
   border-radius: 50%;
   vertical-align: -3px;
   /* 与单元格文字基线对齐 */
   animation: batch-cell-spin .7s linear infinite;
}

@keyframes batch-cell-spin {
   to {
      transform: rotate(360deg);
   }
}

/* 尊重系统的"减少动态效果"：持续旋转在密集表格里是持续视觉干扰，
   偏好减少动效的用户改为静态圆环（仍能读出"未就绪"）。 */
@media (prefers-reduced-motion: reduce) {
   .batch-matrix-table td.cell-pending::after {
      animation: none;
   }
}

/* 状态字（超时/失败等）：字号随 td 的 13px，只加字重区分主次。 */
.cell-status {
   font-weight: 500;
}

.cell-status.ok {
   color: #27ae60;
}

.cell-status.warn {
   color: #e6a23c;
}

.cell-status.err {
   color: #e74c3c;
}

/* 延迟数字：这页的核心记忆点 —— 加粗、按快慢分色，一眼扫出慢目标。
   分档：≤100ms 绿（健康）→ ≤300ms 橙（偏慢）→ >300ms 红（很慢），超时红。
   参考图口径：**13px 加粗、行首第一行**，其下两行 12px 浅灰（响应IP / 位置），
   三者左对齐成一条竖线、行距紧凑（1.5），整格高度 ≈ 60px。 */
.cell-latency {
   font-size: 14px;
   font-weight: 500;
   color: #1aa260;
   line-height: 1.5;
}

.cell-latency.mid {
   color: #e8892b;
}

.cell-latency.slow {
   color: #e74c3c;
}

.cell-latency.timeout {
   color: #e74c3c;
}

.cell-latency.partial {
   color: #e8b02b;
}

/* DNS 记录列表：字号随 td 的 13px（正文一档），只靠 1.45 行距收紧。 */
.cell-records {
   color: #3b4a55;
   line-height: 1.45;
   word-break: break-all;
}

.cell-records .rec {
   display: block;
}

.cell-count {
   font-size: 11px;
   color: #97a2ac;
   margin-top: 2px;
}

/* 单元格内的两行说明（响应IP / 位置）：与延迟同列左对齐、浅灰 #949fa8。
   字号同样跟 td 走 13px —— 与延迟数字只差**颜色**（#949fa8 vs 分色主数字），
   不靠字号差分层：同格里 13/12 两档混排会让明细行看起来像是"寄生"的注释。
   行距 1.45 保持紧凑，整格高度仍 ≈ 60px。 */
.cell-detail {
   color: #949fa8;
   line-height: 1.45;
}

.cell-detail span {
   display: block;
}

/* 「响应IP: …」在 DNS 解析失败时要单独标红（与单目标 ping/tcping/http 页的
   「解析失败」红字同一语义、同一支红 #e0362d）。
   为什么不用 common.js 的 renderTargetCellChecked：批量表这一格是「延迟 + 响应IP
   + 位置」三行合体，没有独立的目标细胞，只能给那一行 <span> 加类。
   类名由 batch-ping / batch-tcping / batch-http.js 输出（.is-resolve-failed），
   判定统一走 common.js 的 isResolveFailed。 */
.cell-detail span.is-resolve-failed {
   color: #e0362d;
}

/* 超时行整行浅红底（参考图第 8 行）：批量扫异常时，红色底色比"红色文字"
   更快圈出问题行 —— 文字红色在 6 列 × N 行的密度下很容易看漏。
   ⚠️ 这是行级底色，必须与 hover、冻结列的 background 一起写，
   否则前两列 sticky 仍是白底，形成一条竖向断层。 */
.batch-matrix-table tbody tr.row-all-down td,
.batch-matrix-table tbody tr.row-all-down th {
   background: #fff5f5;
}

.batch-empty {
   padding: 40px;
   text-align: center;
   color: #91a0a9;
}