/* ==========================================================================
   index 页专属样式（登录 + 设备列表控制台）
   依据：server/docs/UI改版设计规范-2026-11.md §1–§5（基础规范）
         server/docs/UI深化规范-2026-11.md（深化规范，冲突时优先）
   本轮（深化轮）在第一轮颜色与三态基础上做：排版节奏 / 字号阶梯 / 招牌特色 / 细节打磨：
   - 登录页：Aurora 漂移底光（global.css .aurora 组件 + deco-aurora 装饰元素）、
     登录卡 signature-border 玻璃质感、渐变小方标、display 级标题（30/700/-0.02em）、
     卡片 scale+fade 入场 + 表单 40ms stagger、输入聚焦光效、主按钮辉光。
   - 控制台：HUD 面板 signature-border + 大写 micro 标签、视频区（截图缩略图）
     圆角内嵌、可交互元素按深化规范 §4 目录补齐三态、状态徽标 tabular-nums。
   说明：
   - 公共体系（令牌 / .btn / .card / .nav / .modal / .badge / .toast 等）一律在
     global.css，本文件只保留 index 页特有布局与页面级表现；招牌效果统一引用
     global.css 已就位的组件令牌（--signature-gradient / --glow-* / --aurora-alpha-*），
     浅色主题变体由令牌层自动切换，本文件不再散写主题覆盖。
   - 颜色全部走令牌：令牌定义行之外不出现裸色值，rgba 一律引用 *-rgb 令牌。
   - 字号一律对齐深化规范 §2 阶梯（--fs-display/--fs-h1/--fs-h2/--fs-caption/--fs-micro）。
   - 类名 .device-card 系列是 public/app.js buildDeviceCardHtml() 生成的 DOM 契约，
     重命名会导致缩略图/复选框/备注/分组等功能失效，故保持原名只改视觉。
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. 登录视图（#loginSection 由 app.js 切换显隐）
   页面底 = --bg-base（body 全局）；Aurora 底光由本区块首个子元素
   <div class="deco-aurora aurora" aria-hidden="true"> 提供（global.css §3.11.3：
   两团 radial 光斑、60s 超慢漂移、--aurora-alpha-a/b 深色 0.16/0.12 ≤0.18、
   浅色 0.08/0.06 自动减半），卡片 z-index:1 盖过光斑。
   -------------------------------------------------------------------------- */
#loginSection.page-wrap {
  position: relative;
  justify-content: center;
  /* 裁掉溢出视口的光斑，避免把页面撑出横向滚动 */
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   1.1 登录卡：玻璃拟态 + signature 渐变描边（深化规范 §3.1 登录卡 + §5 登录页）
   玻璃底需要半透明 padding-box 填充，与双层背景描边互斥，故描边用
   mask 挖环伪元素实现（发光边框两层结构）：仅 @supports 内启用，
   不支持时回退到普通 1px 面板边框，渐变层绝不裸奔盖住内容。
   豁免登记（深化评审 #11）：此处刻意偏离 §3.1 的「双层背景」写法——
   --bg-glass 半透明填充与不透明 padding-box 填充互斥，属技术必要而非
   风格发挥；@supports 回退保证降级安全，视觉结果与 §3.1 等效（1px 渐变环）。
   -------------------------------------------------------------------------- */
.login-panel {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 400px;
  padding: var(--space-6);
  background: var(--bg-glass);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg), var(--card-highlight);
  backdrop-filter: blur(var(--backdrop-blur));
  -webkit-backdrop-filter: blur(var(--backdrop-blur));
  /* 卡片入场 scale+fade（深化规范 §5 登录页），240ms 上限内 */
  animation: login-panel-in 0.24s var(--ease-spring) both;
}

@keyframes login-panel-in {
  from {
    opacity: 0;
    transform: translateY(10px) scale(0.97);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* 招牌渐变描边：1px 环 = 渐变填充减去 content-box 内区（mask-composite 挖环），
   不占布局、不盖内容；遮罩色用不透明的 --text-on-primary 令牌（只取 alpha 通道） */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
  .login-panel {
    border-color: transparent;
  }

  .login-panel::before {
    content: "";
    position: absolute;
    inset: -1px; /* 撑满 border-box，环恰好落在 1px 边框位 */
    border-radius: inherit;
    padding: 1px;
    background: var(--signature-gradient);
    -webkit-mask: linear-gradient(var(--text-on-primary), var(--text-on-primary)) content-box,
      linear-gradient(var(--text-on-primary), var(--text-on-primary));
    mask: linear-gradient(var(--text-on-primary), var(--text-on-primary)) content-box,
      linear-gradient(var(--text-on-primary), var(--text-on-primary));
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    pointer-events: none;
  }
}

/* --------------------------------------------------------------------------
   1.2 登录卡内部：标题区 + 表单
   入场 stagger：头部 0ms → 两个输入组 40/80ms → 主按钮 120ms
   （递进 40ms、上限 240ms 内；复用 global.css 的 rise-in 关键帧）。
   -------------------------------------------------------------------------- */
.login-header {
  margin-bottom: var(--space-5);
  text-align: center;
  animation: rise-in 0.22s var(--ease-spring) both;
}

/* 渐变小方标：40px 靛蓝→紫圆角方块 + 白色几何符号（规范 §5 / 深化 §3.5，
   全站唯一「图标位」），纯伪元素实现，不动 HTML */
.login-header::before {
  content: "◆";
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin: 0 auto var(--space-4);
  font-size: 16px;   /* ◆ 符号尺寸取字号阶梯内的 16px 档 */
  line-height: 1;
  color: var(--text-on-primary);
  background: var(--accent-gradient);
  border-radius: var(--radius);
  box-shadow: var(--shadow-primary), var(--card-highlight);
}

/* display 级标题（深化规范 §2 阶梯）：30px / 700 / lh 1.15 / -0.02em */
.login-title {
  font-size: var(--fs-display);
  font-weight: 700;
  line-height: var(--lh-display);
  color: var(--text-main);
  letter-spacing: var(--ls-display);
}

/* 副标题：secondary 口径 13px / --text-regular；标题下 4px 的页头节奏 */
.login-subtitle {
  margin-top: var(--space-1);
  font-size: var(--fs-secondary);
  line-height: var(--lh-secondary);
  color: var(--text-regular);
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.login-form > * {
  animation: rise-in 0.22s var(--ease-spring) both;
}

.login-form > :nth-child(1) {
  animation-delay: 40ms;
}

.login-form > :nth-child(2) {
  animation-delay: 80ms;
}

.login-form > :nth-child(3) {
  animation-delay: 120ms;
}

.login-form .form-group {
  width: 100%;
}

/* 输入聚焦光效（深化规范 §5 登录页）：主色边框 + 双层外圈，
   外圈第二层为品牌柔光；label 随 focus-within 提亮形成响应联动 */
.login-form .form-label {
  transition: color var(--transition-fast);
}

.login-form .form-group:focus-within .form-label {
  color: var(--primary);
}

.login-form .form-input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-ring),
    0 0 28px -6px rgba(var(--primary-rgb), 0.5);
}

/* 登录主按钮：global .btn--primary 渐变底 + 三态之上，加静息品牌辉光，
   hover 抬升 + 收敛外圈环 + 更强的落晕（深化规范 §3.2「主按钮辉光」） */
.login-submit-btn {
  width: 100%;
  margin-top: var(--space-2);
  min-height: 42px;
  font-size: var(--font-size-md);
  font-weight: 600;
  box-shadow: var(--shadow-primary), var(--card-highlight);
}

.login-submit-btn:hover:not(:disabled) {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: var(--glow-ring), 0 8px 28px -6px rgba(var(--primary-rgb), 0.55);
}

.login-submit-btn:active:not(:disabled) {
  filter: brightness(0.97);
  transform: translateY(0) scale(0.98);
  box-shadow: var(--shadow-sm);
}

/* --------------------------------------------------------------------------
   1.3 登录/注册切换 tab（注册功能增量，新类名，不改既有类规则）
   双等宽分段控件：胶囊底 + 激活段浮起，主题令牌随明暗切换。
   -------------------------------------------------------------------------- */
.login-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: var(--space-4);
  padding: 4px;
  background: color-mix(in srgb, var(--text-main) 8%, transparent);
  border-radius: 10px;
}

.login-tab {
  flex: 1;
  padding: 8px 12px;
  font-size: var(--fs-secondary);
  font-weight: 600;
  color: var(--text-regular);
  background: transparent;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast),
    box-shadow var(--transition-fast);
}

.login-tab:hover {
  color: var(--text-main);
}

.login-tab.is-active {
  color: var(--text-main);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-sm);
}

/* 注册表单的显示/隐藏切换（.hidden 为 global.css 既有工具类，这里补 display 兜底） */
#registerForm.hidden {
  display: none;
}

/* --------------------------------------------------------------------------
   2. 状态条：账号信息 / 剩余时长 / 编码模式 / 刷新
   （#topAction、#remainingTimeBadge、#codecModeBadge 的内容由 app.js 注入）
   -------------------------------------------------------------------------- */
.console-statusbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  max-width: 1400px;
  width: 100%;
  margin: 0 auto;
  padding: var(--space-1) var(--space-4) 0;
  font-size: var(--fs-caption);
}

.console-statusbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
  flex-wrap: wrap;
}

/* app.js renderTopAction() 注入的元素（类名固定，这里做兼容样式） */
#topAction {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* 大写微标签口径（深化规范 §2 micro）：11px / 600 / +0.08em / --text-muted */
.top-label {
  font-size: var(--fs-micro);
  font-weight: 600;
  line-height: var(--lh-micro);
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* 药丸链接：常态低调 → hover 悬浮底 → 按压缩放，三态齐全（深化规范 §4 链接行） */
#topAction .action-btn {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 12px;
  font-size: var(--font-size-xs);
  font-weight: 500;
  color: var(--text-regular);
  background: transparent;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-pill);
  text-decoration: none;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background var(--transition-fast), transform var(--transition-fast),
    box-shadow var(--transition-fast);
}

#topAction .action-btn:hover {
  color: var(--text-main);
  background: var(--bg-hover);
  border-color: var(--panel-border-strong);
}

#topAction .action-btn:active {
  transform: scale(0.96);
}

/* 状态徽标：数字 tabular（深化规范 §1「数字一律 tabular-nums」）+ hover 亮度 +5%
   （深化规范 §4 徽标行；本页徽标仅作状态展示，hover 反馈保持轻微） */
.badge {
  font-variant-numeric: tabular-nums;
  transition: filter var(--transition-fast);
}

.badge:hover {
  filter: brightness(1.05);
}

/* 剩余时长告急态（app.js 挂 .urgent）：语义危险色软底 + 淡边 */
.badge.urgent {
  color: var(--danger-text);
  background: var(--danger-soft);
  border-color: rgba(var(--danger-rgb), 0.4);
}

/* --------------------------------------------------------------------------
   3. 统计卡：页面顶部的核心数据 HUD（注册设备总数）
   视觉：signature 渐变描边锚定面板 + 渐变大数字（display 阶梯），
   右侧留给自动刷新进度条；区块间距对齐 24px 节奏（深化规范 §2 页面头部模式）。
   深化评审 #2：删除原「左侧 3px 渐变竖条」（::before --accent-gradient），
   进度条也改主色实色（见 .refresh-bar-fill）——控制台视图招牌效果收回到
   规范上限 ≤3 处：统计卡描边 / 渐变主数字 / 列表卡低饱和描边。
   -------------------------------------------------------------------------- */
.dashboard-stats {
  max-width: 1400px;
  width: 100%;
  margin: 0 auto var(--space-5);
  padding: var(--space-4) var(--space-5);
  border: 1px solid transparent;
  /* signature-border（深化规范 §3.1）：双层背景描边，内层填充 = --signature-fill */
  background:
    linear-gradient(var(--signature-fill), var(--signature-fill)) padding-box,
    var(--signature-gradient) border-box;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm), var(--card-highlight);
  position: relative;
  overflow: hidden;
}

/* 渐变竖条已删（深化评审 #2：规范未列举的指示位不做招牌效果，规则整体移除） */

.core-data {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* 面板名 = 大写微标签（深化规范 §5 控制台行）：11px / 600 / 大写 / +0.08em */
.core-data-label {
  font-size: var(--fs-micro);
  font-weight: 600;
  line-height: var(--lh-micro);
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* 主强调数字：display 阶梯 30px / 700 / lh 1.15 / -0.02em + tabular-nums，
   Inter 命中 tnum 特性（深化规范 §1），保留规范的渐变字手法 */
.core-data-value {
  font-size: var(--fs-display);
  font-weight: 700;
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  font-variant-numeric: tabular-nums;
  width: fit-content;
  background: var(--accent-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

.core-data-sub {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--text-muted);
}

/* 15s 自动刷新进度条（app.js startAutoRefresh 驱动）：主色实色指示
   （深化评审 #2：去渐变——2px 激活条是规范未列举的自选位置，不做招牌效果） */
.refresh-bar {
  height: 2px;
  margin-top: var(--space-3);
  background: transparent;
}

.refresh-bar-fill {
  height: 100%;
  width: 0;
  background: var(--primary);
  border-radius: var(--radius-pill);
  transition: width 0.2s linear;
}

/* --------------------------------------------------------------------------
   4. 设备列表主面板
   大面积面板用低饱和描边变体（--signature-gradient-soft，深化规范 §3.1「低饱和版」），
   让渐变大数字与内容保持视觉主角，符合「每页招牌效果克制」原则。
   -------------------------------------------------------------------------- */
.list-card {
  max-width: 1400px;
  width: 100%;
  margin: 0 auto var(--space-5);
  padding: var(--space-5);
  border: 1px solid transparent;
  background:
    linear-gradient(var(--signature-fill), var(--signature-fill)) padding-box,
    var(--signature-gradient-soft) border-box;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm), var(--card-highlight);
}

.list-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--border-faint);
  /* .head-refresh 进度线绝对定位在底缘，需要这里作为定位基准 */
  position: relative;
}

/* 面板名上方的 HUD 刻度：大写 micro 标签（深化规范 §5 控制台行），纯 CSS 生成 */
.list-header-main::before {
  content: "DEVICE CONSOLE";
  display: block;
  margin-bottom: var(--space-1);
  font-size: var(--fs-micro);
  font-weight: 600;
  line-height: var(--lh-micro);
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* 页面主标题：h1 阶梯 22px / 700 / lh 1.25 / -0.01em（深化规范 §2） */
.list-header h2 {
  font-size: var(--fs-h1);
  font-weight: 700;
  line-height: var(--lh-h1);
  color: var(--text-main);
  margin: 0;
  letter-spacing: var(--ls-h1);
}

.list-header .subtitle {
  margin-top: var(--space-1);
}

/* 在线/离线统计：caption 口径 12px / 600 / tabular-nums（深化规范 §1/§2） */
.device-online-stats {
  font-size: var(--fs-caption);
  font-weight: 600;
  margin-left: var(--space-2);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* 状态文字统一用 -text 可读变体（规范 §2） */
.device-online-stats .stat-online {
  color: var(--success-text);
}

.device-online-stats .stat-offline {
  color: var(--danger-text);
}

.device-online-stats .stat-sep {
  color: var(--text-muted);
  font-weight: 400;
}

/* ---- 标题右侧工具区（list-head-side）----
   单行静音布局：统计（label + 数字）与视图切换同行右对齐，随标题行垂直居中。
   原「三行 HUD（渐变大数字 + 说明行）」在宽屏会和分组控制条挤在一行、视觉过重，
   按用户反馈收敛：统计降为一枚安静的前缀徽标，说明行收进 title 悬浮提示。
   .dashboard-stats 类名保留（app.js 快照门禁 + 结构复用）。 */
.list-head-side {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-3);
  margin-left: auto;
}

.list-head-side .dashboard-stats.head-stat {
  margin: 0;
  /* width:auto 抵消全局 .dashboard-stats 的 width:100%（行内布局会挤压视图切换） */
  width: auto;
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  overflow: visible;
  cursor: default;
}

/* 统计压成一行：label 与数字基线对齐（HTML 已删说明行，信息进 title） */
.list-head-side .core-data {
  flex-direction: row;
  align-items: baseline;
  gap: 6px;
}

/* 渐变主数字整体退场：改为 14px 主色实色——右上角是导航区不是数据大屏，
   静音优先（页面招牌 ≤3 处口径：渐变字不再占用此处名额） */
.list-head-side .core-data-value {
  font-size: var(--font-size-md);
  font-weight: 650;
  background: none;
  -webkit-background-clip: initial;
  background-clip: initial;
  color: var(--primary);
  -webkit-text-fill-color: currentColor;
  letter-spacing: 0;
}

/* 15s 刷新进度线：贴 list-header 底缘（原独立统计卡下缘的效果随块迁移） */
.list-header .head-refresh {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  margin: 0;
}

/* ---- 视图切换（设备列表 / 手机墙） ---- */
.view-switch {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--bg-input);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-pill);
}

.view-switch-btn {
  min-height: 28px;
  padding: 4px var(--space-3);
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--text-muted);
  background: transparent;
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  white-space: nowrap; /* 行内布局被挤压时按钮文字禁止竖排折行 */
  transition: color var(--transition), background var(--transition);
}

.view-switch-btn:hover {
  color: var(--text-regular);
}

.view-switch-btn.is-active {
  color: var(--text-on-primary);
  background: var(--primary);
}

/* ---- 分组过滤 + 群控操作条 ----
   恒占整行（flex-basis 100%）：标题行只留「标题 + 统计 + 视图切换」，
   宽屏不再把分组控制条挤到视图切换旁边（用户反馈的对齐问题） */
.group-control-bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  flex: 0 0 100%;
}

.group-filter {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.group-filter select {
  width: auto;
  min-height: 36px;
  padding: 6px 28px 6px 10px;
  cursor: pointer;
}

/* ---- 按分组执行的两个下拉 ----
   这两个动作各自指定「对哪个分组执行」，与左侧「分组筛选」（只管列表显示）
   不是一回事，所以各带一个下拉，外观与分组筛选保持一致：
   用户看到同样的控件就知道是同类选择，不用先理解「筛选」和「目标」的区别。
   选完立即执行并把选项复位回占位项——它是一次性动作，不是常驻模式。 */
.group-scope-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-left: var(--space-3);
  border-left: 1px solid var(--panel-border);
  margin-left: var(--space-1);
}

.scope-select {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  white-space: nowrap;
}

.scope-select__label {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  user-select: none;
}

.scope-select:hover .scope-select__label {
  color: var(--text-primary);
}

.scope-select__input {
  width: auto;
  min-width: 96px;
  min-height: 32px;
  padding: 4px 26px 4px 10px;
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  background: var(--bg-input);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color 0.15s var(--ease-standard);
}

.scope-select__input:hover {
  border-color: var(--panel-border-strong);
}

.scope-select__input:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

/* ---- 卡片上的分组下拉 ----
   取代原来的「设置」按钮 + prompt 手打组名：直接列出所有已建分组（含空组） */
.group-select {
  min-width: 88px;
  max-width: 130px;
  min-height: 26px;
  padding: 2px 22px 2px 8px;
  font-size: var(--font-size-xs);
  font-family: inherit;
  color: var(--text-primary);
  background: var(--bg-elevated);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-overflow: ellipsis;
}

.group-select:hover {
  border-color: var(--panel-border-strong);
}

.group-select:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

.group-select.group-text--ungrouped {
  color: var(--text-muted);
}

/* ---- 管理分组弹窗 ---- */
.group-create-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.group-create-row .form-input {
  flex: 1 1 auto;
}

.group-manage-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-height: 46vh;
  overflow-y: auto;
  margin-top: var(--space-3);
}

.group-manage-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-sunken);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-sm);
}

.group-manage-name {
  flex: 1 1 auto;
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.group-manage-count {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.group-manage-del {
  color: var(--danger);
}

.group-manage-empty {
  margin: var(--space-3) 0 0;
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  text-align: center;
}

/* ---- 搜索条（2025-11：移入 group-control-bar 首位，不再独占一行） ---- */
.device-search-bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 0 1 300px;   /* 固定合理宽度，随行内空间收缩，不再 flex-basis 100% */
  min-width: 180px;
  margin-top: 0;
}

.device-search-input {
  flex: 1;
  min-width: 0;
  min-height: 36px;
}

/* --------------------------------------------------------------------------
   5. 设备网格（虚拟滚动：视口由 app.js 计算 padding-top 占位）
   -------------------------------------------------------------------------- */
.grid-virtual-viewport {
  max-height: calc(100vh - 420px);
  min-height: 360px;
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: 6px;
  scroll-behavior: auto;
}

.grid-layout {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  /* gap 保持 14px：app.js getVirtualCols() 以 VIRTUAL_GAP=14（app.js:232）推算列数，
     CSS 实际列数必须与之一致，虚拟滚动的窗口切片才不错位——间距节奏让位于功能契约 */
  gap: 14px;
}

.virtual-scroll-bar {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  border-top: 1px solid var(--border-faint);
}

/* --------------------------------------------------------------------------
   6. 设备卡片（DOM 结构由 app.js buildDeviceCardHtml 生成，类名不可改）
   三态对齐深化规范 §4「卡片（可点）」：hover 抬升 + 辉光 / 按下 scale(.995) /
   focus-visible 外圈环；选中态主色描边 + 品牌光晕双重表达。
   -------------------------------------------------------------------------- */
.device-card {
  position: relative;
  display: flex;
  flex-direction: column;
  /* 虚拟滚动硬性等高：app.js 以 VIRTUAL_ROW_HEIGHT（= 本高度 450 + 行距 14 = 464，
     两处必须同步修改）推算占位 padding-top/minHeight，卡片实际高度必须与之严格
     一致——不一致时滚动重渲染会让内容总高度塌缩。V2 紧凑卡：元信息收敛为
     「标题+分组 / ID·账号 / 备注 / 分辨率」四行，操作按钮压成 30px 单行，
     高度余量由 .device-card-image 的 flex 填充吸收（root 实测 body+actions ≈ 190；
     卡片从 370 加高到 450 是为了缩略图 contain 后整页竖屏有可读尺寸） */
  height: 450px;
  background: var(--bg-elevated);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  cursor: pointer;
  box-shadow: var(--shadow-sm), var(--card-highlight);
  transition: border-color var(--transition), background var(--transition),
    box-shadow var(--transition), transform var(--transition);
}

/* hover：抬升 + 外层辉光（深化规范 §3.2 / §4 卡片行） */
.device-card:hover {
  border-color: var(--panel-border-strong);
  box-shadow: var(--glow-ring), var(--glow-halo), var(--card-highlight);
  transform: translateY(-2px);
}

/* 按下：轻缩放确认（深化规范 §4 卡片行 scale(.995)） */
.device-card:active {
  transform: translateY(0) scale(0.995);
}

/* 键盘聚焦：统一焦点环（卡片由 JS 委托点击，保留可达性钩子） */
.device-card:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* 选中态：主色描边 + 1px 辉光环 + 品牌投影（声明在 :hover 之后，
   同特异性下后命中，保证选中卡悬浮时仍保持选中表达） */
.device-card.selected {
  border-color: var(--primary);
  box-shadow: var(--glow-ring), var(--shadow-primary), var(--card-highlight);
}

/* ---- 截图/视频区：圆角内嵌（深化规范 §5「视频区圆角内嵌」/ 基础规范 §5
   「截图缩略图区圆角内嵌 --bg-sunken」）——8px 内缩 + 8px 圆角与卡片
   16px 圆角同心，空态给一层径向渐变，不再是死黑一块 ----
   截图必须完整呈现手机整页：缩略图区约 264×255，1080×2400 竖屏按 contain
   完整显示约 115×255（旧 cover 顶部裁切只能露出约 1/4 页面，已废弃）；
   左右留白由下层径向渐变补底，视觉上是刻意的相框而非裁切。 */
.device-card-image {
  position: relative;
  margin: var(--space-2) var(--space-2) 0;
  border: 1px solid var(--border-faint);
  border-radius: var(--radius-sm);
  background:
    radial-gradient(ellipse at 50% 42%, var(--bg-hover) 0%, var(--bg-sunken) 78%);
  /* 等高卡片的高度余量吸收器（见 .device-card 注释）：body 固定高度，
     图片区 flex 撑满剩余空间 */
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* contain：任何分辨率（手机竖屏 / 平板横屏）都整页可见，绝不裁切 */
.device-card-image img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}

.placeholder {
  color: var(--text-muted);
  font-size: var(--font-size-xs);
  letter-spacing: 0.02em;
  text-align: center;
  padding: 0 var(--space-3);
}

/* ---- 信息区 ---- */
.device-card-body {
  padding: 10px var(--space-3) 0;
  display: flex;
  flex-direction: column;
  flex-grow: 0;
  gap: 5px;
}

.card-title-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

.card-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--text-main);
  margin-bottom: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 标题行内的分组下拉：药丸化紧凑档 */
.card-title-row .group-select {
  flex: 0 0 auto;
  min-width: 0;
  max-width: 96px;
  min-height: 24px;
  padding: 1px 20px 1px 8px;
  border-radius: var(--radius-pill);
  font-size: 11px;
}

.device-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  font-size: 11px;
  color: var(--text-muted);
  font-family: var(--font-mono);
  margin-bottom: 4px;
  /* 虚拟滚动等高约束：deviceId 过长换行会撑高卡片（同 .remark-text 注释） */
  white-space: nowrap;
}

.device-meta .meta-id {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.device-meta .meta-label {
  color: var(--text-muted);
}

/* 绑定账号（root 可见）：meta 行内右对齐常规强调 */
.device-meta .device-owner-name {
  flex: 0 0 auto;
  color: var(--text-regular);
  font-weight: 600;
}

.device-remark-row,
.device-group-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  /* 虚拟滚动等高约束：wrap 会让长备注把「编辑」按钮挤到第二行，
     卡片高度随内容变化 → 行高塌缩 → 滚动抽搐，统一钳成单行 */
  flex-wrap: nowrap;
  margin-bottom: 4px;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  min-width: 0;
}

.remark-label {
  color: var(--text-muted);
  flex: 0 0 auto;
}

.remark-text {
  color: var(--text-main);
  word-break: break-all;
  /* 虚拟滚动等高约束：备注过长换行会撑高个别卡片，破坏 VIRTUAL_ROW_HEIGHT 假设
     （行高塌缩 → 滚动抽搐滑不到底），钳制单行省略号 */
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.group-text {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  padding: 1px 8px;
  font-size: var(--font-size-xs);
  font-family: var(--font-mono);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-pill);
  word-break: break-all;
  /* 虚拟滚动等高约束：长分组名单行省略，不撑高卡片 */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.group-text--named {
  background: var(--primary-soft);
  color: var(--primary);
  border-color: rgba(var(--primary-rgb), 0.32);
  font-weight: 600;
}

.group-text--ungrouped {
  background: var(--bg-sunken);
  color: var(--text-muted);
}

/* ---- 卡片内小按钮（备注 / 分组）：ghost 迷你钮，三态齐全 ---- */
.remark-edit-btn,
.group-edit-btn {
  background: transparent;
  border: 1px solid var(--panel-border);
  color: var(--text-muted);
  padding: 2px 8px;
  cursor: pointer;
  font-size: var(--font-size-xs);
  font-family: var(--font-body);
  border-radius: var(--radius-sm);
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background var(--transition-fast), transform var(--transition-fast),
    box-shadow var(--transition-fast);
}

.remark-edit-btn:hover,
.group-edit-btn:hover {
  color: var(--primary);
  border-color: var(--primary-ring);
  background: var(--primary-soft);
}

.remark-edit-btn:active,
.group-edit-btn:active {
  transform: scale(0.96);
}

/* ---- 在线 / 离线状态标签（V2：悬浮在截图右上角的玻璃胶囊） ---- */
.device-tag {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: fit-content;
  padding: 2px 9px;
  font-size: 11px;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  background: var(--bg-glass);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-variant-numeric: tabular-nums;
}

.device-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--text-muted);
}

.status-online {
  background: var(--success-soft);
  border-color: rgba(var(--success-rgb), 0.35);
}

.status-online .device-status-dot {
  background: var(--success);
}

.status-online .device-status-text {
  color: var(--success-text);
  font-weight: 600;
}

.status-offline {
  background: var(--danger-soft);
  border-color: rgba(var(--danger-rgb), 0.32);
}

.status-offline .device-status-dot {
  background: var(--danger);
}

.status-offline .device-status-text {
  color: var(--danger-text);
}

.device-stats {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-family: var(--font-mono);
  margin-bottom: var(--space-3);
  font-variant-numeric: tabular-nums;
}

/* 设备卡片 App 版本徽标（更新管道 P0-1 前端增量）：v1.4.3 及以下无法自更新标红 */
.device-stats .appver-adb-only {
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 8px;
  font-size: var(--font-size-xs);
  color: var(--danger-text);
  background: var(--danger-soft, rgba(239, 68, 68, 0.15));
}

/* ---- 底部操作区（V2 紧凑）：主操作两列并排 30px，危险操作独占一行 ---- */
.card-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
  margin-top: auto;
  padding: 6px var(--space-3) var(--space-3);
}

.card-actions .button,
.card-actions .btn,
.card-actions .device-delete-btn {
  min-height: 30px;
}

/* 注入的「运行脚本」主按钮在紧凑卡内降为软底次级（避免每卡一个渐变大钮） */
.card-actions .btn.device-run-btn {
  background: var(--primary-soft);
  color: var(--primary);
  border-color: transparent;
  box-shadow: none;
  font-size: var(--font-size-xs);
}

.card-actions .btn.device-run-btn:hover {
  background: rgba(var(--primary-rgb), 0.22);
  box-shadow: none;
  transform: none;
  filter: none;
}

/* 进入控制中心（app.js 生成的 <a class="button">）：主色软底，hover 加深 */
.card-actions .button {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;
  text-align: center;
  text-decoration: none;
  font-size: var(--font-size-xs);
  font-weight: 550;
  color: var(--primary);
  background: var(--primary-soft);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  transition: background var(--transition-fast), color var(--transition-fast),
    border-color var(--transition-fast), transform var(--transition-fast),
    box-shadow var(--transition-fast);
}

.card-actions .button:hover {
  background: rgba(var(--primary-rgb), 0.22);
  color: var(--primary);
}

.card-actions .button:active {
  transform: scale(0.98);
}

/* 运行脚本按钮（本页内联脚本注入，仅在线设备）：.btn--primary 渐变由 global 提供 */
.device-run-btn {
  width: 100%;
}

/* root 专属：删除设备，危险语义三态 */
.device-delete-btn {
  grid-column: 1 / -1;
  min-height: 32px;
  width: 100%;
  background: transparent;
  border: 1px solid rgba(var(--danger-rgb), 0.4);
  color: var(--danger-text);
  padding: 4px 10px;
  cursor: pointer;
  font-size: var(--font-size-xs);
  font-family: var(--font-body);
  border-radius: var(--radius-sm);
  transition: background var(--transition-fast), border-color var(--transition-fast),
    color var(--transition-fast), transform var(--transition-fast),
    box-shadow var(--transition-fast);
}

.device-delete-btn:hover {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger-text);
}

.device-delete-btn:active {
  background: rgba(var(--danger-rgb), 0.24);
  transform: scale(0.98);
}

/* ---- 多选复选框浮层（V2：悬停 / 聚焦 / 选中才现身，降低常态噪音） ---- */
.device-card-checkbox {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  background: var(--bg-glass);
  border: 1px solid var(--panel-border-strong);
  border-radius: var(--radius-sm);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity var(--transition-fast);
}

.device-card:hover .device-card-checkbox,
.device-card:focus-within .device-card-checkbox,
.device-card.selected .device-card-checkbox,
.device-card:has(.device-checkbox:checked) .device-card-checkbox {
  opacity: 1;
}

.device-checkbox {
  width: 16px;
  height: 16px;
  cursor: pointer;
  accent-color: var(--primary);
}

/* ---- 空状态：凹陷底 + 虚线框，居中灰字 ---- */
.empty-tips {
  grid-column: 1 / -1;
  text-align: center;
  padding: 60px 20px;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
  background: var(--bg-sunken);
  border: 1px dashed var(--panel-border-strong);
  border-radius: var(--radius-lg);
}

/* --------------------------------------------------------------------------
   7. 本页弹窗（运行脚本）：普通面板描边（深化评审 #2：弹窗降回 1px
   --panel-border 普通描边——本页招牌效果已收满 3 处上限，主弹窗描边让位；
   规则保留为显式普通面板口径的占位。adb-tools 弹窗由 adb-tools.css 以 id
   选择器接管，不受影响）
   -------------------------------------------------------------------------- */
.modal-panel {
  border: 1px solid var(--panel-border);
  background: var(--bg-elevated);
}

/* --------------------------------------------------------------------------
   8. 响应式（375px 基线）
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .console-statusbar {
    padding: var(--space-2) var(--space-3);
  }

  /* 登录卡窄屏收一档内边距，给 Aurora 光斑留出呼吸 */
  .login-panel {
    padding: var(--space-5);
  }

  /* 注册设备统计已迁入 .list-header（.list-head-side）：窄屏整行铺满，
     左统计、右视图切换（主布局已是单行，这里只负责占位方向） */
  .list-head-side {
    width: 100%;
    justify-content: space-between;
  }

  .list-card {
    /* 同 .dashboard-stats：width:auto 取代 100%，避免 margin 溢出 */
    width: auto;
    margin: 0 var(--space-3) var(--space-3);
    padding: var(--space-3) var(--space-3) var(--space-4);
  }

  .list-header {
    flex-direction: column;
  }

  .grid-virtual-viewport {
    max-height: calc(100vh - 480px);
    min-height: 280px;
  }
}

/* --------------------------------------------------------------------------
   9. 动效降级：prefers-reduced-motion（深化规范 §0.3：全部动画与位移关闭。
   Aurora 漂移 / 弹窗 / toast / 全局过渡时长由 global.css 的同名媒体查询统一停用，
   这里补齐本页新增的入场动画与 hover 位移）
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .login-panel,
  .login-header,
  .login-form > * {
    animation: none;
  }

  .device-card:hover,
  .device-card:active,
  .login-submit-btn:hover:not(:disabled),
  .login-submit-btn:active:not(:disabled) {
    transform: none;
  }
}

/* --------------------------------------------------------------------------
   10. 手机墙（view-switch 切入的第二视图，phone-wall.js 驱动）
   所有云控手机的实时截图墙：flex 自动换行 = 自动排版；每台手机按自身
   分辨率锁定 aspect-ratio，宽度统一由 --wall-tile-w 控制（默认 192px
   即完整显示整台手机）；页面缩放用 CSS zoom 作用于整个画面区。
   -------------------------------------------------------------------------- */
.list-card.is-wall-view .grid-virtual-viewport,
.list-card.is-wall-view .virtual-scroll-bar,
.list-card.is-wall-view .group-scope-actions,
.list-card.is-wall-view #selectGroupBtn,
.list-card.is-wall-view #syncGroupBtn,
.list-card.is-wall-view #manageGroupsBtn {
  display: none;
}

.phone-wall {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* 手机墙视图裁掉页面横向溢出：瓦片 hover / 主控 1.08 / 放大档 ×2 的
   transform 缩放会在文档右缘周期性撑出又收回横向滚动条 —— 滚动条占位一变
   整页跟着回弹，这就是「选择设备抖动」的另一半根因。
   clip 只裁溢出、不产生滚动容器（页面横向本来就不该滚）。 */
body:has(.list-card.is-wall-view) {
  overflow-x: clip;
}

/* ---- 工具条：手机大小 / 页面缩放 / 仅在线 / 跟随 / 刷新 ---- */
.wall-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-input);
  border: 1px solid var(--border-faint);
  border-radius: var(--radius-md, var(--radius-sm));
}

.wall-tool {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.wall-tool-label {
  font-size: var(--fs-micro, var(--font-size-xs));
  font-weight: 600;
  letter-spacing: var(--ls-micro, 0.04em);
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}

.wall-range {
  width: 120px;
  accent-color: var(--primary);
  cursor: pointer;
}

.wall-tool-value {
  min-width: 44px;
  font-size: var(--font-size-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-regular);
}

.wall-check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--font-size-xs);
  color: var(--text-regular);
  cursor: pointer;
  user-select: none;
}

.wall-check input {
  accent-color: var(--primary);
  cursor: pointer;
}

.wall-toolbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
}

/* 「全部跟随」按钮的跟随中形态：文字由 JS 切成「取消跟随」，
   转警示色表达「再点一次退出」 */
#wallFollowAll.is-following-active {
  color: var(--danger-text);
  border-color: rgba(var(--danger-rgb), 0.55);
  background: var(--danger-soft);
}

/* 跟随状态行内提示：跟随中为 success 色，未选主控为 muted */
.wall-follow-status {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  white-space: nowrap;
}

.wall-follow-status.is-following {
  color: var(--success-text);
}

.wall-follow-status.is-warn {
  color: var(--warning-text);
}

/* 键盘输入目标提示（打字闭环）：与跟随状态行同行展示，点击可重新聚焦输入宿主 */
.wall-typing-status {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}

.wall-typing-status:hover {
  color: var(--text-main);
}

/* ---- 画面区：flex 自动换行排版；zoom 由 JS 写入（页面自定义缩放） ---- */
.wall-canvas {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3);
  min-height: 120px;
  /* 内边距兼做「缩放安全区」：hover 1.045 / 主控 1.08 的 transform 会扩大
     滚动溢出区域，瓦片贴着内容边缘时会把页面撑出又收回滚动条（抖动）。
     12px ≈ 760px 大瓦片 ×2.25% 单侧溢出（17px）− list-card 自身内边距。 */
  padding: 12px;
}

.wall-canvas.is-empty::after {
  content: "没有符合条件的手机：调整筛选或到设置页连接设备";
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  padding: var(--space-4);
}

/* 键盘输入宿主（打字闭环）：真实 textarea 承载输入法组字 —— Chromium 的
   IME 组字只在可编辑元素上启用，焦点在 body/画面上时中文永远打不出来
   （与控制页 text-input.js 同一结论）。视觉隐藏但保持可聚焦；
   pointer-events:none 点不到它，只由 JS 在瓦片单击后聚焦。
   定在视口内（fixed 左下）避免 focus 触发 scrollIntoView 跳页。 */
.wall-type-input {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 1px;
  height: 1px;
  opacity: 0;
  border: 0;
  margin: 0;
  padding: 0;
  resize: none;
  overflow: hidden;
  background: transparent;
  color: transparent;
  caret-color: transparent;
  pointer-events: none;
  z-index: -1;
}

/* ---- 单台手机瓦片 ----
   --wall-tile-w 由工具条写入 .wall-canvas；每台手机 aspect-ratio 取自身
   分辨率（JS 内联），宽度固定、高度自适应 = 默认大小恰好完整显示手机。 */
.wall-tile {
  width: var(--wall-tile-w, 192px);
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.wall-screen {
  position: relative;
  width: 100%;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  overflow: hidden;
  cursor: pointer;
  transition: border-color var(--transition), box-shadow var(--transition);
  /* 直控手势（点击/长按/滑动）在触屏上不被浏览器滚动手势截胡；
     每台手机都可直接操作，不再局限于主控机 */
  touch-action: none;
}

.wall-tile:hover .wall-screen {
  border-color: var(--panel-border-strong);
}

/* 主控机：主色描边 + 光圈 */
.phone-wall .wall-tile.is-master .wall-screen {
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary-ring);
}

/* 正在操作：手势落在哪台机，哪台机亮警示色框（松手后约 1.2s 由 JS 摘除）。
   2025-11 七次调整：去掉 scale(1.08) —— 每次点击「先弹大再缩回」的脉冲
   正是「选择设备抖动」的来源之一；且缩放会扩大滚动溢出区域，瓦片贴近文档
   边缘时反复撑出/收回滚动条，整页跟着回弹。高亮只改边框与阴影（不改几何），
   hover 的轻微缩放保留（见下方 body 溢出裁剪，滚动条不会再跳）。
   .phone-wall 前缀提高特异性，保证压过下方 .wall-tile:hover 的 hover 缩放 */
.phone-wall .wall-tile.is-operating {
  z-index: 3;
}

.wall-tile.is-operating .wall-screen {
  border-color: var(--warning);
  box-shadow: 0 0 0 2px var(--warning-soft), 0 6px 22px rgba(0, 0, 0, 0.35);
}

.wall-screen img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;   /* 指针事件交给 .wall-screen 统一算相对坐标 */
}

.wall-shot-fallback {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-xs);
  color: var(--text-dim);
  background: radial-gradient(ellipse at 50% 42%, var(--bg-hover) 0%, var(--bg-sunken) 78%);
}

/* 跟随触点涟漪：pointerdown 主控时的下发位置反馈 */
.wall-ripple {
  position: absolute;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--primary);
  background: var(--primary-soft);
  pointer-events: none;
  animation: wall-ripple 0.55s ease-out forwards;
}

@keyframes wall-ripple {
  from { transform: scale(0.6); opacity: 1; }
  to { transform: scale(2.4); opacity: 0; }
}

/* 主控角标：跟随中显示在顶部控制条内（勾选框右侧），指示当前主控。
   pointer-events none：纯指示标签 */
.wall-master-badge {
  position: absolute;
  top: 5px;
  left: 34px;   /* 勾选框右侧 */
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  padding: 3px 7px;
  border-radius: var(--radius-pill);
  letter-spacing: 0.05em;
  display: none;
  pointer-events: none;
  color: var(--text-on-primary);
  background: var(--primary);
}

.phone-wall.is-following .wall-tile.is-master .wall-master-badge { display: block; }

/* 跟随勾选框回归（2025-11 五次调整）：.wall-follow-box 重新上墙，
   固定画面左上常显（规则见下方瓦片增强段落），勾选 = 跟随主屏 + 批量下发目标 */

/* 设为主控按钮：外框顶部控制条内（勾选框右侧），仅悬停本瓦片时出现 */
.wall-master-btn {
  position: absolute;
  top: 4px;
  left: 34px;   /* 勾选框右侧 */
  display: block;
  min-height: 20px;
  padding: 2px 9px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text-on-primary);
  background: var(--primary);
  border: 1px solid var(--primary);
  border-radius: var(--radius-pill);
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
}

/* 悬停本瓦片（或按钮自身获得焦点）时出现；勾选框常显不受影响 */
.wall-tile:hover .wall-master-btn,
.wall-tile:focus-within .wall-master-btn {
  opacity: 0.94;
  pointer-events: auto;
}

.wall-tile.is-master .wall-master-btn { display: none; }
.wall-tile.is-offline .wall-master-btn { display: none; }

/* 拖动样式随拖动系统退役（保留类名避免其他引用报错） */
.wall-tile.is-moving-float .wall-master-btn,
.wall-tile.is-moving-float .wall-master-badge,
.wall-tile.is-moving-float .wall-more-btn {
  cursor: pointer;
}

/* 防误触：手势进行期间（画面上按下未抬起）顶部控制条临时「让位」——变淡
   且不可点（控制条在画面外，本就不拦手势，此规则纯视觉降噪：手势时控制条
   不晃眼）。手势结束由 phone-wall.js 摘掉 is-gesturing 恢复 */
.phone-wall.is-gesturing .wall-master-btn,
.phone-wall.is-gesturing .wall-more-wrap,
.phone-wall.is-gesturing .wall-follow-box {
  pointer-events: none;
  opacity: 0.15;
  transition: opacity 0.12s ease;
}

/* 实时视频画布：绝对定位盖在缩略图上（未出画时透明，露出底层截图；
   出画后整面覆盖，流畅度与控制页一致）。手势仍落在 .wall-screen 上 */
.wall-screen canvas.wall-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 2;
  pointer-events: none;
}

/* 视频失联自愈：10s 无新帧的瓦片临时隐藏视频层，露出底下的最新截图，
   避免「画面冻在几秒前 / 只剩一部分」的观感；恢复出帧后自动还原 */
.wall-tile.is-video-stale canvas.wall-video {
  display: none;
}

/* 实时视频画布之上有交互层：手势涟漪必须压过 canvas（z-index 2）。
   2025-11 六次调整：勾选框/主控角标/设为主控/···已移到画面外的 bezel
   顶部控制条（.wall-bezel 直接子元素），不再受 screen 内 canvas 遮挡 */
.wall-screen .wall-ripple {
  z-index: 3;
}

/* 涟漪与息屏遮罩同为 z-index 3，而涟漪是后 append 的（phone-wall.js 的
   showRipple），因此会画在纯黑遮罩之上 —— 黑瓦片被点一下会闪出一个可见的
   涟漪，把「这里其实是活的」暴露出来。新增规则压到遮罩之下（遮罩 3、
   实时视频画布 2）。不改既有 .wall-ripple 声明，只补一条更高特异性的覆盖；
   :has() 不支持的旧浏览器下退回相邻兄弟选择器，再不行就保持原样（仅视觉）。 */
.wall-screen .screen-off-mask ~ .wall-ripple {
  z-index: 2;
}
.wall-screen:has(.screen-off-mask) .wall-ripple {
  z-index: 2;
}

/* ---- 瓦片元信息行 ---- */
.wall-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

.wall-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--text-main);
}

/* 名称即链接：点名称进单机控制中枢（画面本身留给直控手势，不再跳页） */
a.wall-name {
  color: var(--text-main);
  text-decoration: none;
  cursor: pointer;
}

a.wall-name:hover,
a.wall-name:focus-visible {
  color: var(--primary-hover);
  text-decoration: underline;
  outline: none;
}

.wall-state {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  flex: none;
}

.wall-tile.is-online .wall-state { color: var(--success-text); }
.wall-tile.is-offline .wall-state { color: var(--text-muted); }
.wall-tile.is-offline .wall-screen { opacity: 0.55; cursor: default; }

/* ==========================================================================
   手机墙瓦片增强（2025-11，纯增量）：
   1. 手机外框 .wall-bezel：仿实体机圆角边框，宽度 = 瓦片宽、内衬画面；
      颜色承担「跟随」状态——默认描边，勾选跟随 = 绿色，主控 = 主色光圈；
   2. 悬停放大：整瓦片 scale(1.045)（transform 不影响布局，不挤邻居）；
   3. 选中态（2025-11 二次调整）：无勾选框，单击瓦片画面 = 选中（外框变绿）；
   4. 屏内悬浮按钮（2025-11 三次调整）：「设为主控」与「···」挂在画面层内、
      按住可拖动换位（百分比锚点全墙统一 + localStorage 记忆）；
      放大/缩小迁入 ···菜单顶部「画面缩放」组（纯 UI，离线可用）；
   5. 瓦片宽度由 JS 写 style.width（滑块勾选缩放 + 放大档），CSS 变量
      --wall-tile-w 仅作为初始兜底。
   ========================================================================== */

/* ---- 手机外框 ----
   顶部控制条（padding-top 28px）：勾选框/设为主控/···住在画面外的外框里
   （2025-11 六次调整），bezel 即定位锚点 */
.wall-bezel {
  position: relative;   /* 顶部控制条 / ···菜单浮层的定位锚点 */
  border: 2px solid var(--panel-border-strong);
  border-radius: 14px;
  padding: 3px;
  padding-top: 28px;    /* 顶部控制条高度：勾选框/按钮行 */
  background: var(--bg-glass);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

/* 勾选跟随（仅由「全部跟随」按钮驱动，2025-11 回退）：外框变绿 = 已加入跟随 */
.wall-tile.is-follow-checked .wall-bezel {
  border-color: var(--success);
  box-shadow: 0 0 0 1px var(--success-soft);
}

/* 主控：主色描边 + 光圈（优先级高于绿色：主控自身不再勾选框表达） */
.wall-tile.is-master .wall-bezel {
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary-ring);
}

/* 主控机略大于其他屏幕：固定 1.08 倍（跟随模式下徽章显示时） */
.wall-tile.is-master { z-index: 2; }
.phone-wall.is-following .wall-tile.is-master { transform: scale(1.08); }

.wall-tile.is-offline .wall-bezel { opacity: 0.75; }

/* ---- 悬停放大特效 ---- */
.wall-tile {
  transition: transform 0.16s ease;
  transform-origin: center bottom;
}

.wall-tile:hover {
  transform: scale(1.045);
  z-index: 3;
}
/* 主控悬停：叠加不冲突（1.08 基础上再乘 hover 1.045） */
.phone-wall.is-following .wall-tile.is-master:hover { transform: scale(1.128); }

/* 正在操作压过悬停：操作中的瓦片始终 1.08（选择器带 .phone-wall，特异性更高） */

/* ---- 悬浮工具（···菜单）----
   固定外框顶部控制条右侧，仅悬停本瓦片时出现；菜单向下弹出盖在画面上。
   z-index 5 必须高于 .wall-screen（DOM 后置且不透明，无 z-index 也会按
   DOM 序画在 wrap 之上——wrap 悬停时带 opacity 形成层叠上下文，没有
   z-index 的话整个菜单会被画面盖住，表现为「···点了没反应」） */
.wall-more-wrap {
  position: absolute;
  top: 4px;
  right: var(--space-1);
  z-index: 5;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.wall-tile:hover .wall-more-wrap,
.wall-tile:focus-within .wall-more-wrap {
  opacity: 0.94;
  pointer-events: auto;
}

.wall-more-wrap:hover { opacity: 1; }

/* 拖动锚点方案退役：不再使用 translate 对齐 */
.wall-more-wrap.is-anchored { right: var(--space-1); }

.wall-more-btn {
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  color: var(--text-main);
  background: var(--bg-glass);
  border: 1px solid var(--panel-border-strong);
  border-radius: var(--radius-pill);
  cursor: pointer;
  backdrop-filter: blur(var(--backdrop-blur));
  transition: opacity 0.15s ease, border-color 0.15s ease;
}

.wall-more-btn { letter-spacing: 0.08em; }

.wall-more-btn:hover {
  border-color: var(--primary);
  color: var(--text-main);
}

/* ---- 跟随勾选框（外框顶部控制条左端，常显）----
   勾选 = 加入跟随集合（外框变绿），可多选；勾选集同时是批量文件下发/
   启动app/安装APK 的目标。离线机不显示（无法跟随）。 */
.wall-follow-box {
  position: absolute;
  top: 4px;
  left: var(--space-1);
  z-index: 4;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  background: var(--bg-glass);
  border: 1px solid var(--panel-border-strong);
  backdrop-filter: blur(var(--backdrop-blur));
  cursor: pointer;
}

.wall-tile.is-offline .wall-follow-box { display: none; }

.wall-follow-check {
  width: 14px;
  height: 14px;
  margin: 0;
  accent-color: var(--success);
  cursor: pointer;
}

/* ---- ···操作菜单面板（含画面缩放组）----
   按钮固定右上角，默认向下弹出（朝屏内展开，不会被 screen 的
   overflow:hidden 裁掉）；仅显式加 .menu-up 才向上弹（预留） */
.wall-more-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 132px;
  max-height: 300px;
  overflow-y: auto;
  padding: var(--space-1);
  background: var(--bg-glass);
  border: 1px solid var(--panel-border-strong);
  border-radius: var(--radius-sm, 8px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(var(--backdrop-blur));
  z-index: 6;
  display: flex;
  flex-direction: column;
}

/* 显式向上弹出（预留分支）；max-height 收敛到按钮上方实际空间
   （--menu-up-max 由 JS 写成合法 CSS 长度，如 "248px"，直接生效） */
.wall-more-menu.menu-up {
  top: auto;
  bottom: calc(100% + 6px);
  max-height: var(--menu-up-max, 300px);
}

.wall-more-menu.hidden { display: none; }

.wall-menu-group {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  padding: 6px 8px 3px;
  text-transform: uppercase;
}

.wall-menu-item {
  border: 0;
  background: transparent;
  text-align: left;
  font-size: var(--font-size-xs);
  color: var(--text-main);
  padding: 6px 10px;
  border-radius: 6px;
  cursor: pointer;
}

.wall-menu-item:hover {
  background: var(--bg-hover);
  color: var(--text-main);
}

/* 防误触（重复规则段，保留兼容）：手势进行期间屏内悬浮按钮让位 */
.phone-wall.is-gesturing .wall-master-btn,
.phone-wall.is-gesturing .wall-master-badge,
.phone-wall.is-gesturing .wall-more-wrap {
  pointer-events: none;
  opacity: 0.15;
  transition: opacity 0.12s ease;
}

.wall-hint {
  margin: 0;
  font-size: var(--fs-caption, var(--font-size-xs));
  line-height: var(--lh-caption);
  color: var(--text-muted);
}

/* 窄屏：工具条换行铺满，缩放滑杆占满一行 */
@media (max-width: 640px) {
  .wall-toolbar {
    align-items: flex-start;
    flex-direction: column;
    gap: var(--space-2);
  }

  .wall-toolbar-actions {
    margin-left: 0;
  }

  .wall-range {
    flex: 1 1 auto;
    width: auto;
  }

  .wall-tool {
    width: 100%;
  }
}

/* 动效降级：涟漪停用（规范 §0.3） */
@media (prefers-reduced-motion: reduce) {
  .wall-ripple {
    animation: none;
  }
}

/* ==========================================================================
   纯增量：批量应用指令（app_cmd：批量启动 / 批量安装 APK）
   --------------------------------------------------------------------------
   应用选择弹窗（.apps-*）与批量任务逐台进度面板（.batch-cmd-*）。
   颜色全部取自 global.css 既有令牌，不改上方任何既有规则；新增类名统一
   apps- / batch-cmd- 前缀，不与既有选择器冲突。
   ========================================================================== */

/* 应用选择弹窗的三态容器：加载骨架 / 空 / 失败（互斥，app.js 切换） */
.batch-app-select-state {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.batch-app-select-state:empty {
  display: none;
}

/* 应用图标网格（点击项 = 对全部勾选设备批量启动该应用） */
.apps-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: var(--space-3);
  max-height: 46vh;
  overflow-y: auto;
}

.apps-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  color: var(--text-main);
  background: var(--bg-elevated);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast),
    transform var(--transition-fast);
}

.apps-item:hover {
  background: var(--bg-hover);
  border-color: var(--panel-border-strong);
}

.apps-item:active {
  transform: scale(0.97);
}

.apps-item:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.apps-item img {
  width: 48px;
  height: 48px;
  object-fit: contain;
  border-radius: var(--radius);
  background: var(--bg-hover);
}

.apps-item-name {
  max-width: 100%;
  font-weight: 550;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.apps-item-ver {
  font-size: 11px;
  color: var(--text-muted);
}

/* 图标缺失 / 加载失败的首字母占位（尺寸与 .apps-item img 对齐） */
.apps-item-fallback {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  font-size: var(--font-size-lg);
  font-weight: 650;
  color: var(--text-muted);
  background: var(--bg-hover);
  border-radius: var(--radius);
}

/* 加载骨架 */
.apps-skeleton-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: var(--space-3);
}

.apps-skeleton-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-2);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
}

.apps-skeleton-item i,
.apps-skeleton-item span {
  display: block;
  background: var(--bg-hover);
  border-radius: var(--radius-sm);
  animation: apps-skeleton-pulse 1.2s ease-in-out infinite;
}

.apps-skeleton-item i {
  width: 48px;
  height: 48px;
}

.apps-skeleton-item span {
  width: 70%;
  height: 10px;
}

@keyframes apps-skeleton-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
}

@media (prefers-reduced-motion: reduce) {
  .apps-skeleton-item i,
  .apps-skeleton-item span {
    animation: none;
  }
}

/* 批量任务逐台进度：设备名 | 状态 | 详情 | 重试 */
.batch-cmd-summary {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  padding: var(--space-2) var(--space-3);
  border: 1px dashed var(--panel-border);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-3);
}

.batch-cmd-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-height: 46vh;
  overflow-y: auto;
}

.batch-cmd-row {
  display: grid;
  grid-template-columns: minmax(110px, 1.1fr) minmax(150px, 1.3fr) minmax(80px, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
}

.batch-cmd-name {
  font-size: var(--font-size-sm);
  font-weight: 550;
  color: var(--text-main);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.batch-cmd-status {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

.batch-cmd-status.is-wait {
  color: var(--text-muted);
}

.batch-cmd-status.is-run {
  color: var(--text-main);
}

.batch-cmd-status.is-ok {
  color: var(--success-text);
}

/* 需手动确认（final:false 过程态）：黄色，等 final ack 收敛为成功/失败 */
.batch-cmd-status.is-warn {
  color: var(--warning-text);
}

.batch-cmd-status.is-fail {
  color: var(--danger-text);
}

.batch-cmd-detail {
  font-size: 11px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 窄屏：状态行改两行布局（名称+状态 / 详情+重试铺满） */
@media (max-width: 640px) {
  .batch-cmd-row {
    grid-template-columns: 1fr auto;
    grid-auto-rows: auto;
  }

  .batch-cmd-detail {
    grid-column: 1 / -1;
    white-space: normal;
  }
}

/* ==================== 2025-11 增量区（只新增类，不改上方既有规则） ==================== */

/* ---- 1. 在线徽标青色系：结构沿用既有 .device-tag 胶囊（圆点+文字），
   配色从绿色系（.status-online）换为青色令牌 --primary（深色 #22d3ee / 浅色 #0e7490）。
   既有 .status-online 规则保留不动，这里用更高特异性的 .device-tag.status-online 覆盖 ---- */
.device-tag.status-online {
  background: var(--primary-soft);
  border-color: rgba(var(--primary-rgb), 0.38);
}

.device-tag.status-online .device-status-dot {
  background: var(--primary);
}

.device-tag.status-online .device-status-text {
  color: var(--primary);
  font-weight: 600;
}

/* ---- 2. 设备列表卡片三态（画面区） ----
   a) 离线：有缓存帧 → .device-card-image.is-disconnected 压暗 + 左下「已断开」角标
      （卡片左上角被勾选框占用，角标放左下）；无帧 → 整块 .device-offline-placeholder 占位文字 */
.device-card-image.is-disconnected img {
  opacity: 0.38;
  filter: grayscale(0.35) brightness(0.75);
}

.device-disconnect-tag {
  position: absolute;
  left: 8px;
  bottom: 8px;
  z-index: 5;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 550;
  color: var(--danger-text);
  background: var(--bg-glass);
  border: 1px solid rgba(var(--danger-rgb), 0.32);
  border-radius: var(--radius-pill);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}

/* 离线且无帧的整块占位文字（独立类，不借用既有 .placeholder） */
.device-offline-placeholder {
  color: var(--text-muted);
  font-size: var(--font-size-xs);
  letter-spacing: 0.02em;
  text-align: center;
  padding: 0 var(--space-3);
}

/* b) 在线且息屏（screenOn === false）：整块纯黑，不显示最后一帧也不加文字。
   卡片与手机墙瓦片共用本类；z-index 3 盖住瓦片实时视频画布（canvas.wall-video，z-index 2），
   卡片右上角 .device-tag（z-index 5）仍在遮罩之上保持可读 */
.screen-off-mask {
  position: absolute;
  inset: 0;
  z-index: 3;
  background: #000;
  pointer-events: none;
}

/* 手机墙瓦片：离线且有缓存帧时的「已断开」角标（左下，同卡片；
   整屏已被既有 .wall-tile.is-offline .wall-screen 的 opacity 压暗，角标随屏变暗） */
.wall-disconnect-tag {
  position: absolute;
  left: 6px;
  bottom: 6px;
  z-index: 4;
  padding: 2px 7px;
  font-size: 11px;
  font-weight: 550;
  color: var(--danger-text);
  background: var(--bg-glass);
  border: 1px solid rgba(var(--danger-rgb), 0.32);
  border-radius: var(--radius-pill);
  pointer-events: none;   /* 不拦瓦片手势 */
  font-variant-numeric: tabular-nums;
}

/* ---- 3. 批量解锁 / 锁屏按钮组：设备列表工具条与手机墙工具条共用 ---- */
.screen-power-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* ==========================================================================
   素材库卡片网格增量（workspace.html 素材库 tab 改版专用）
   只增不改：既有类规则零改动；新增类一律带 mat- 前缀，仅素材卡片 DOM 使用
   （workspace.js renderMaterials() 生成，.mat-card 是脚本寻址契约）。
   现状说明：workspace.html 原本未链接本文件，本区随 index.css 一起被链入
   （已核对全部选择器与 workspace 页无类名/ID/动画名冲突）。
   令牌全部引用 global.css :root / :root[data-theme=light]，浅色主题自动适配。
   ========================================================================== */

/* ---- 1. 卡片网格：自适应列宽，窄屏自动降列 ---- */
.mat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-4);
}

/* 单卡：圆角/描边/阴影复用卡片令牌，hover 轻抬（既有 .btn 同款过渡曲线） */
.mat-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: box-shadow var(--transition), transform var(--transition), border-color var(--transition);
}

.mat-card:hover {
  transform: translateY(-2px);
  border-color: var(--panel-border-strong);
  box-shadow: var(--shadow-lg), var(--card-highlight);
}

/* ---- 2. 封面：固定高裁切，图片 contain 完整可见；非图片显示大号类型图标 ---- */
.mat-card__cover {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 120px;
  background: var(--bg-sunken);
  border-bottom: 1px solid var(--card-border);
}

.mat-card__img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.mat-card__icon {
  font-size: 40px;
  line-height: 1;
  opacity: 0.85;
}

/* ---- 3. 名称与元信息：名称单行截断，元信息小字排开、归属者用主题色强调 ---- */
.mat-card__name {
  margin: var(--space-2) var(--space-3) 0;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--text-main);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mat-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  margin: var(--space-1) var(--space-3) 0;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.mat-card__meta-item {
  font-variant-numeric: tabular-nums;
}

.mat-card__owner {
  color: var(--text-secondary);
  font-weight: 550;
}

/* ---- 4. 操作行：推送/下载/删除横排收尾，顶部细线与元信息分隔 ---- */
.mat-card__ops {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--card-border);
}

/* ---- 5. 空态：图标 + 主句 + 引导文案，整块居中 ---- */
.mat-empty {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--space-6) var(--space-4);
  border: 1px dashed var(--card-border);
  border-radius: var(--radius-lg);
}

.mat-empty__icon {
  font-size: 40px;
  line-height: 1;
  margin-bottom: var(--space-3);
  opacity: 0.8;
}

.mat-empty__title {
  font-size: var(--font-size-md);
  font-weight: 600;
  color: var(--text-main);
  margin-bottom: var(--space-2);
}

.mat-empty__desc {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  line-height: 1.6;
}

/* ==========================================================================
   工作空间 · 四板块增量区（workspace.html 专用）
   文本库（ws-textlib-*）/ 素材多选推送（ws-devpick-* / ws-push-result）/
   相册导入（album-*）/ 设备文件管理（ws-files-*）/ 脚本变量提示（ws-var-hint）。
   类名全部带 ws- / album- 前缀，不与既有页面冲突；变量沿用 global.css 令牌。
   ========================================================================== */

/* ---- 脚本 tab 编辑器下方的变量提示行 ---- */
.ws-var-hint {
  margin-top: var(--space-1);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.ws-var-hint code {
  padding: 1px 6px;
  border: 1px solid var(--panel-border-strong);
  border-radius: var(--radius-pill, 999px);
  background: var(--bg-hover);
  color: var(--primary);
  white-space: nowrap;
}

/* ---- 文本库（TXT）：区头 + 组行列表 ---- */
.ws-textlib {
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  border: 1px dashed var(--panel-border-strong);
  border-radius: var(--radius);
  background: var(--bg-hover);
}

.ws-textlib-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.ws-textlib-title {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--text-main);
}

.ws-textlib-sub {
  flex: 1 1 280px;
  min-width: 0;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.ws-textlib-sub code,
.ws-textlib-name-var {
  color: var(--primary);
}

.ws-textlib-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.ws-textlib-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  background: var(--bg-base, transparent);
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.ws-textlib-row:hover {
  background: var(--bg-hover);
}

.ws-textlib-name {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--text-main);
  word-break: break-all;
}

.ws-textlib-meta {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* 变量 chip：点一下复制 */
.ws-textlib-var {
  padding: 1px 8px;
  border: 1px solid var(--panel-border-strong);
  border-radius: var(--radius-pill, 999px);
  font-size: var(--font-size-xs);
  font-family: var(--font-mono, monospace);
  color: var(--primary);
  background: var(--primary-soft);
  cursor: pointer;
  user-select: all;
  white-space: nowrap;
}

.ws-textlib-var:hover {
  border-color: var(--primary);
}

.ws-textlib-ops {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-left: auto;
  flex: 0 0 auto;
}

.ws-textlib-empty {
  padding: var(--space-3);
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

/* 编辑模态里的大 textarea：一行一条的正文区 */
.ws-textlib-content {
  width: 100%;
  resize: vertical;
  font-family: var(--font-mono, monospace);
  font-size: var(--font-size-sm);
  line-height: 1.7;
}

/* ---- 素材多选推送：弹层复选框列表 ---- */
.ws-devpick {
  position: relative;
  display: inline-flex;
}

.ws-devpick-pop {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  width: min(320px, 86vw);
  padding: var(--space-3);
  border: 1px solid var(--panel-border-strong);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}

.ws-devpick-bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.ws-devpick-count {
  margin-left: auto;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.ws-devpick-items {
  max-height: 220px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.ws-devpick-foot {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--space-2);
}

/* 批量推送逐台结果 */
.ws-push-result {
  margin-bottom: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--panel-border-strong);
  border-radius: var(--radius);
  font-size: var(--font-size-xs);
}

.ws-push-result__head {
  margin-bottom: var(--space-1);
  font-weight: 600;
  color: var(--text-main);
}

.ws-push-result__row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 2px 0;
}

.ws-push-result__row--ok span:last-child {
  color: var(--success-text, var(--success));
}

.ws-push-result__row--fail span:first-child {
  color: var(--danger-text, var(--danger));
}

.ws-push-result__row--fail span:last-child {
  color: var(--danger-text, var(--danger));
  word-break: break-all;
}

/* 素材来源徽标：source=device */
.mat-card__src--device {
  padding: 0 8px;
  border: 1px solid var(--primary);
  border-radius: var(--radius-pill, 999px);
  background: var(--primary-soft);
  color: var(--primary);
  font-weight: 600;
}

/* ---- 相册导入模态 ---- */
.album-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

.album-toolbar .form-select {
  flex: 0 1 220px;
  min-width: 0;
}

.album-toolbar .badge {
  flex: 0 0 auto;
}

.album-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: var(--space-2);
  max-height: 46vh;
  overflow-y: auto;
  padding: var(--space-1);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
}

.album-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--space-1);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color var(--transition-fast);
}

.album-cell:hover {
  border-color: var(--panel-border-strong);
}

.album-cell:has(input:checked) {
  border-color: var(--primary);
  background: var(--primary-soft);
}

.album-cell input[type="checkbox"] {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 1;
  width: 16px;
  height: 16px;
  cursor: pointer;
}

.album-cell__img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: calc(var(--radius) - 2px);
  background: var(--bg-sunken);
}

.album-cell__name {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.album-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.album-page-info {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.album-progress {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.album-progress.hidden {
  display: none;
}

.album-progress-bar {
  flex: 1 1 auto;
  height: 8px;
  border-radius: var(--radius-pill, 999px);
  background: var(--bg-sunken);
  overflow: hidden;
}

.album-progress-bar span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--primary);
  transition: width 0.3s ease;
}

.album-progress-text {
  flex: 0 0 auto;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- 设备文件管理面板 ---- */
.ws-files-form {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.ws-files-form .form-select {
  flex: 0 1 240px;
  min-width: 0;
}

.ws-files-crumbs {
  flex: 1 1 240px;
  min-width: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px;
  padding: 4px 10px;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  background: var(--bg-hover);
  font-size: var(--font-size-sm);
}

.ws-files-crumb {
  padding: 1px 6px;
  border: none;
  border-radius: var(--radius-pill, 999px);
  background: transparent;
  font-size: var(--font-size-sm);
  font-family: var(--font-mono, monospace);
  color: var(--primary);
  cursor: pointer;
}

.ws-files-crumb:hover {
  background: var(--primary-soft);
}

.ws-files-crumb--cur {
  color: var(--text-main);
  cursor: default;
}

.ws-files-crumb-sep {
  color: var(--text-muted);
  font-family: var(--font-mono, monospace);
}

.ws-files-status {
  margin-bottom: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--panel-border-strong);
  border-radius: var(--radius);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.ws-files-status.hidden {
  display: none;
}

.ws-files-table-wrap {
  max-height: 56vh;
  overflow-y: auto;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
}

.ws-files-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-sm);
}

.ws-files-table th,
.ws-files-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--panel-border);
  text-align: left;
  vertical-align: middle;
}

.ws-files-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-elevated);
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--text-muted);
}

.ws-files-table tbody tr:hover {
  background: var(--bg-hover);
}

.ws-files-table td:nth-child(2),
.ws-files-table td:nth-child(3),
.ws-files-table td:nth-child(4) {
  white-space: nowrap;
  color: var(--text-regular);
  font-variant-numeric: tabular-nums;
}

.ws-files-name {
  max-width: 380px;
}

.ws-files-dir-icon {
  margin-right: 6px;
}

/* 文件名链接：目录可点进入；文件不可点（下载走右侧操作列） */
.ws-files-link {
  padding: 0;
  border: none;
  background: transparent;
  font-size: var(--font-size-sm);
  color: var(--primary);
  cursor: pointer;
  word-break: break-all;
}

.ws-files-link:hover {
  text-decoration: underline;
}

.ws-files-link:disabled {
  color: var(--text-main);
  cursor: default;
}

.ws-files-link:disabled:hover {
  text-decoration: none;
}

.ws-files-ops {
  display: flex;
  gap: var(--space-1);
  white-space: nowrap;
}

.ws-files-ops .btn {
  min-height: 26px;
  padding: 2px 10px;
}
