/* =============================================================
   五岔 · 云匣子 · 通用组件（v4 石墨极简）
   -------------------------------------------------------------
   按钮 / 动作栏 / 分段控件 / 菜单 / 模态 / 表单 / 复选 / Toast /
   上传面板 / 拖拽遮罩 / 空状态
   -------------------------------------------------------------
   · 描边一律 --line（#d8dbe2）起步，hover 转 --line-strong，轮廓清晰；
   · 主按钮 = 纯石墨；按下即时回压；禁用明确降权；
   · 「动作栏」仍是唯一的文件操作入口（去重原则不变）。
   ============================================================= */

/* =============================================================
   按钮
   ============================================================= */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 34px;
  padding: 0 15px;
  border-radius: var(--r-s);
  font-size: 13px;
  font-weight: 650;
  color: var(--text-1);
  background: var(--surface);
  border: 1px solid var(--line);
  white-space: nowrap;
  overflow: hidden;
  transition: transform var(--dur-2) var(--ease), background var(--dur-1) var(--ease),
    color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
    box-shadow var(--dur-2) var(--ease);
}

.btn svg {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  transition: transform var(--dur-2) var(--ease);
}

.btn:hover {
  background: var(--surface-2);
  border-color: var(--line-strong);
  transform: translateY(-1px);
  box-shadow: var(--shadow-xs);
}

.btn:hover svg {
  transform: scale(1.06);
}

.btn:active {
  transform: translateY(0) scale(0.97);
  transition-duration: var(--dur-instant);
}

.btn[disabled] {
  opacity: 0.45;
  color: var(--text-4);
  background: var(--surface);
  border-color: var(--line);
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}

.btn[disabled]:hover,
.btn[disabled]:active {
  transform: none;
  color: var(--text-4);
  background: var(--surface);
  box-shadow: none;
}

.btn[disabled]:hover svg {
  transform: none;
}

/* 主按钮：纯石墨 + 光泽扫过 */
.btn-primary {
  color: #fff;
  background: var(--grad-brand);
  border-color: transparent;
  box-shadow: var(--brand-glow);
}

.btn-primary:hover {
  color: #fff;
  background: var(--grad-brand);
  border-color: transparent;
  box-shadow: 0 10px 26px rgba(139, 92, 246, 0.4);
  transform: translateY(-2px);
}

/* 2026-10-23 修复：光泽扫过原来 transition:left（违反"动画只动 transform/opacity"红线）。
   改为 transform 位移：光带宽取按钮宽的 30%（配合 .btn 的 overflow:hidden 裁剪），
   起点 left:-35%（整条在按钮外），悬停 translateX(460%) = 走 138% 按钮宽 →
   终点光带左边缘约 103%，与原来扫到 calc(100% + 20px) 出右边界观感一致。 */
.btn-primary::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 30%;
  left: -35%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.22), transparent);
  transform: skewX(-18deg);
  transition: transform var(--dur-5) var(--ease);
}

.btn-primary:hover::after {
  transform: translateX(460%) skewX(-18deg);
}

.btn-danger {
  color: var(--danger);
  background: var(--surface);
  border-color: rgba(192, 80, 78, 0.35);
}

.btn-danger:hover {
  color: var(--danger);
  background: var(--danger-soft);
  border-color: var(--danger);
  box-shadow: var(--shadow-xs);
}

.btn-ghost {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}

.btn-ghost:hover {
  background: var(--surface-2);
  border-color: var(--line);
  box-shadow: none;
}

.btn-sm {
  height: 28px;
  padding: 0 10px;
  font-size: 12.2px;
  border-radius: var(--r-xs);
}

.btn-sm svg {
  width: 13px;
  height: 13px;
}

.btn-block {
  width: 100%;
}

/* =============================================================
   动作栏（唯一操作入口）—— 空闲态 ↔ 选中态整组切换
   ============================================================= */
.toolbar {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px;
  border-bottom: 1px solid var(--line);
  min-height: var(--toolbar-h);
  flex-wrap: nowrap;
  /* ★★ 这里必须用 hidden，**不能用 auto**。
     上一版写成 `overflow-x: auto` 想"挤不下时能横向滚动"，结果：
     勾选/取消勾选时排序那一组有淡入淡出（带 translateX 位移），
     动画期间内容比容器宽 → 浏览器**立刻显示一条横向滚动条**，
     动画结束又消失 → 用户看到的就是"闪现 1 秒的横向滚动条"。
     overflow:hidden 永远不会出现滚动条；配合 .ab-meta 不收缩 + 勾选时隐藏排序，
     正常宽度下也不会真的溢出。 */
  overflow: hidden;
}

.ab-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  animation: fade-rise-in var(--dur-2) var(--ease) backwards;
}

.ab-actions .sep {
  width: 1px;
  height: 18px;
  margin: 0 2px;
  background: var(--line);
  flex: 0 0 auto;
}

.ab-meta {
  /* ★ `flex: 0 0 auto`（不是 0 1 auto）——**绝不允许被压缩**。
     原先写成可收缩 + min-width:0，于是勾选文件后左右两侧的固定宽度按钮
     把它挤成 0 宽，"已选中 N 项 · 大小"整块消失（用户报的"被排序挡住"）。
     现在改为不收缩；万一真的放不下，由 .toolbar 横向滚动兜底，而不是让它消失。 */
  flex: 0 0 auto;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding-left: 10px;
  border-left: 1px solid var(--line);
  font-size: 12.5px;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ab-meta.is-plain {
  border-left: 0;
  padding-left: 0;
}

.ab-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-1);
}

.ab-sel {
  font-weight: 650;
  color: var(--text-1);
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.ab-sel .dot {
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: var(--r-full);
  background: var(--ink);
  color: #fff;
  font-size: 11px;
  display: grid;
  place-items: center;
  font-variant-numeric: tabular-nums;
  transition: transform var(--dur-2) var(--ease-back);
}

.toolbar:hover .ab-sel .dot {
  transform: scale(1.08);
}

.toolbar .grow {
  flex: 1 1 auto;
}

.ab-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

/* 排序 + 视图切换：勾选文件时淡出、取消勾选时淡入（用户要求"有过渡效果"） */
.ab-tools-inner {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

.tools-fade-enter-active,
.tools-fade-leave-active {
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease);
}

.tools-fade-enter-from,
.tools-fade-leave-to {
  opacity: 0;
  transform: translateX(8px) scale(0.96);
}

/* =============================================================
   ★ 窄屏触摸目标（按**宽度**判定，不依赖 @media (hover:none)）
   ---------------------------------------------------------------
   为什么按宽度而不是按 hover：
     `@media (hover: none)` 只有真触屏设备才命中 —— 窄窗口的桌面、
     以及无头浏览器的布局审计都命中不到，导致"改了但验不到"。
     手机一定是窄屏，所以**窄屏就放大**更稳、也可被自检验证。
   目标：主要操作 ≥44px（有的系统建议值），次要/紧凑控件 ≥36px。
   ============================================================= */
@media (max-width: 860px) {
  /* ⚠ 每个选择器都加 .shell  前缀提高优先级：
     本文件里的 .seg-btn 基础定义在**这段之后**，同优先级下"后定义胜"，
     不加前缀的话这里的 height 会被基础规则覆盖掉（踩过）。 */
  .shell .icon-btn {
    width: 44px;
    height: 44px;
  }

  .shell .row-act,
  .shell .row-actions .icon-btn {
    min-width: 44px;
    min-height: 44px;
  }

  .shell .toolbar .btn,
  .shell .toolbar .icon-btn {
    min-height: 44px;
  }

  /* 视图切换 / 面包屑：紧凑控件，视觉高度给到 36px
     （再高会把顶栏撑变形）。 */
  .shell .seg-btn {
    height: 36px;
    padding: 0 12px;
    font-size: 12.5px;
  }

  .shell .crumb-chip {
    height: 36px;
  }

  /* ★★ 2026-10-07：按 44px 标准重扫发现顶栏这三个胶囊只有 34~36px。
     直接加高会撑变形顶栏，所以用**透明 ::after 扩大可点区域** ——
     视觉尺寸不变、手指点得准，而且审计能验（它会把 ::after 算进去）。
     这是本项目已有的手法，勾选框/卡片勾选也是这么做的。 */
  .shell .seg-btn,
  .shell .crumb-chip,
  .shell .user-chip {
    position: relative;
  }

  .shell .seg-btn::after,
  .shell .crumb-chip::after,
  .shell .user-chip::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 44px;
    height: 44px;
  }

  /* 网格/瀑布流卡片上的勾选框：同样用 ::after 扩大可点区域 */
  .shell .card-check::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }

  /* ★ 勾选框：视觉保持 17px（放大会破坏列表对齐），
     但用一个透明的 ::after 把**可点区域扩到 44px**。
     另外列表模式下整行都可点（单击=勾选），所以这里更多是"手指按上去稳"。 */
  .check::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }
}
/* =============================================================
   ★ 窄屏（手机）下的工具栏
   ---------------------------------------------------------------
   工具栏平时是 `flex-wrap: nowrap` + `overflow: hidden`
   （nowrap 是为了修"勾选时闪现横向滚动条"那个 bug）。
   但手机屏幕只有 375px 宽，7 个按钮根本排不下 ——
   nowrap + hidden 会让**后面的按钮直接被裁掉、既看不到也点不到**。
   所以窄屏改成允许换行（工具栏变高、内容区相应变矮），
   并且恢复 overflow:visible 以免换行时又出现滚动条。
   ============================================================= */
@media (max-width: 860px) {
  .toolbar {
    flex-wrap: wrap;
    row-gap: 6px;
    height: auto;
    min-height: var(--toolbar-h);
    padding: 8px 10px;
    overflow: visible;
  }

  /* 换行后 .grow 会把右侧控件顶到下一行，窄屏直接去掉它 */
  .toolbar .grow {
    display: none;
  }

  .ab-meta {
    border-left: 0;
    padding-left: 0;
    width: 100%;
    order: 9;
  }

  .ab-actions,
  .ab-tools {
    flex-wrap: wrap;
    row-gap: 6px;
  }

  /* 触摸目标：手机上按钮要够大好点 */
  .toolbar .btn,
  .toolbar .icon-btn {
    min-height: 38px;
  }
}

/* =============================================================
   分段控件（视图切换）
   ============================================================= */
.seg {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 2px;
  border-radius: var(--r-s);
  background: var(--surface-2);
  border: 1px solid var(--line);
  transition: border-color var(--dur-1) var(--ease);
}

.seg:hover {
  border-color: var(--line-strong);
}

.seg-btn {
  position: relative;
  z-index: 1;
  height: 26px;
  padding: 0 10px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 650;
  color: var(--text-3);
  white-space: nowrap;
  transition: color var(--dur-1) var(--ease), transform var(--dur-2) var(--ease),
    background var(--dur-1) var(--ease), box-shadow var(--dur-2) var(--ease);
}

.seg-btn:hover {
  color: var(--text-1);
  background: var(--surface);
}

.seg-btn:active {
  transform: scale(0.95);
  transition-duration: var(--dur-instant);
}

.seg-btn svg {
  width: 14px;
  height: 14px;
  transition: transform var(--dur-2) var(--ease);
}

.seg-btn:hover svg {
  transform: scale(1.08);
}

.seg-btn.is-active {
  color: var(--text-1);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-xs);
}

/* =============================================================
   下拉菜单 / 右键菜单
   ============================================================= */
.menu {
  position: fixed;
  z-index: 220;
  min-width: 168px;
  padding: 5px;
  border-radius: var(--r-m);
  transform-origin: top right;
  /* ⚠ 必须用纯 transition（animation:both 会把最终帧钉住，淡出失效） */
}

.menu-enter-active,
.menu-leave-active {
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease-back);
}

.menu-enter-from,
.menu-leave-to {
  opacity: 0;
  transform: scale(0.96) translateY(-5px);
}

.menu-enter-to,
.menu-leave-from {
  opacity: 1;
  transform: scale(1) translateY(0);
}

.menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 32px;
  padding: 0 10px;
  border-radius: var(--r-xs);
  font-size: 12.8px;
  font-weight: 550;
  color: var(--text-2);
  text-align: left;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease),
    transform var(--dur-1) var(--ease);
}

.menu-item svg {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  color: var(--text-3);
  transition: color var(--dur-1) var(--ease), transform var(--dur-2) var(--ease);
}

.menu-item:hover {
  background: var(--surface-2);
  color: var(--text-1);
  transform: translateX(2px);
}

.menu-item:hover svg {
  color: var(--text-1);
  transform: scale(1.06);
}

.menu-item:active {
  transform: translateX(2px) scale(0.98);
  transition-duration: var(--dur-instant);
}

.menu-item.is-danger {
  color: var(--danger);
}

.menu-item.is-danger svg {
  color: var(--danger);
}

.menu-item.is-danger:hover {
  background: var(--danger-soft);
  color: var(--danger);
}

.menu-item[disabled] {
  opacity: 0.4;
  pointer-events: none;
}

/* =============================================================
   遮罩 / 模态框
   ============================================================= */
.overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(20, 22, 28, 0.32);
  animation: fade-in var(--dur-2) var(--ease) backwards;
}

.overlay.is-leaving {
  animation: fade-in var(--dur-2) var(--ease) reverse both;
}

.modal {
  width: min(520px, 100%);
  max-height: min(80vh, 720px);
  display: flex;
  flex-direction: column;
  border-radius: var(--r-l);
  overflow: hidden;
  animation: modal-in var(--dur-3) var(--ease-back) backwards;
}

.modal.is-wide {
  width: min(720px, 100%);
}

@keyframes modal-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.modal-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 16px 18px 12px;
}

.modal-head .m-ico {
  width: 32px;
  height: 32px;
  border-radius: var(--r-s);
  display: grid;
  place-items: center;
  background: var(--surface-3);
  border: 1px solid var(--line);
  color: var(--text-2);
  flex: 0 0 auto;
  transition: transform var(--dur-2) var(--ease-back);
}

.modal:hover .modal-head .m-ico {
  transform: scale(1.05);
}

.modal-head .m-ico.is-danger {
  background: var(--danger-soft);
  border-color: rgba(192, 80, 78, 0.3);
  color: var(--danger);
}

.modal-head .m-ico svg {
  width: 17px;
  height: 17px;
}

.modal-title {
  flex: 1 1 auto;
  font-size: 15px;
  font-weight: 700;
  color: var(--text-1);
}

.modal-desc {
  font-size: 12.2px;
  color: var(--text-3);
  margin-top: 2px;
  font-weight: 450;
}

.modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 18px 6px;
}

.modal-foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px 16px;
  border-top: 1px solid var(--line-soft);
}

/* =============================================================
   表单
   ============================================================= */
.field {
  margin-bottom: 14px;
}

.field-label {
  display: block;
  margin-bottom: 6px;
  font-size: 12.2px;
  font-weight: 650;
  color: var(--text-2);
}

.input {
  width: 100%;
  min-height: 40px;
  padding: 8px 12px;
  border-radius: var(--r-s);
  background: var(--surface-2);
  border: 1px solid var(--line);
  font-size: 13px;
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
    box-shadow var(--dur-2) var(--ease);
}

/* 多行输入（分享描述用）：可换行，长到约三行就出滚动条 ——
   弹层高度稳定，不会被长描述撑变形。分享页那边是完整展开（不滚动）。 */
.ta-3 {
  min-height: 40px;
  max-height: calc(1.55em * 3 + 18px);
  line-height: 1.55;
  resize: vertical;
  overflow-y: auto;
  font-family: inherit;
}

.input:hover {
  border-color: var(--line-strong);}

.input:focus {
  outline: none;
  background: var(--surface);
  border-color: var(--text-2);
  box-shadow: 0 0 0 3px rgba(23, 24, 28, 0.08);
}

.input.is-error {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px var(--danger-soft);
}

.hint {
  margin-top: 6px;
  font-size: 11.5px;
  color: var(--text-3);
  display: flex;
  align-items: center;
  gap: 5px;
}

.hint svg {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
}

.hint.is-error {
  color: var(--danger);
}

.hint.is-warn {
  color: var(--warn);
}

/* =============================================================
   自定义复选框
   ============================================================= */
.check {
  position: relative;
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  border-radius: 5px;
  /* 加深描边：白底上也要一眼看得见 */
  border: 1.6px solid rgba(30, 34, 48, 0.42);
  background: var(--surface);
  box-shadow: inset 0 1px 2px rgba(30, 34, 48, 0.08);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
    transform var(--dur-2) var(--ease-back), box-shadow var(--dur-2) var(--ease);
}

.check:hover {
  border-color: var(--brand-1);
  box-shadow: 0 0 0 3px rgba(91, 108, 255, 0.18);
  transform: scale(1.1);
}

.check:active {
  transform: scale(0.92);
  transition-duration: var(--dur-instant);
}

.check svg {
  width: 11px;
  height: 11px;
  color: #fff;
  opacity: 0;
  transform: scale(0.4) rotate(-14deg);
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease-back);
}

.check.is-on {
  background: var(--grad-brand-flat);
  border-color: transparent;
  box-shadow: var(--shadow-accent);
}

.check.is-on svg {
  opacity: 1;
  transform: scale(1) rotate(0);
}

.check.is-partial {
  background: var(--grad-brand-flat);
  border-color: transparent;
}

.check.is-partial::after {
  content: "";
  width: 8px;
  height: 2px;
  border-radius: 2px;
  background: #fff;
}

.check.is-partial svg {
  display: none;
}

/* =============================================================
   登录门 / 权限行（账号面板）
   ============================================================= */
/* ---------- 常用设备管理（2026-10-16） ---------- */
.dev-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 4px;
  border-bottom: 1px solid var(--line);
}

.dev-row:last-child {
  border-bottom: 0;
}

.dev-main {
  flex: 1 1 auto;
  min-width: 0;
}

.dev-name {
  font-size: 13.5px;
  font-weight: 650;
  color: var(--text-1);
  display: flex;
  align-items: center;
  gap: 8px;
}

.dev-cur {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--brand-ink);
  background: var(--brand-soft);
  border-radius: 999px;
  padding: 2px 8px;
}

.dev-sub {
  font-size: 11.5px;
  color: var(--text-4);
  margin-top: 2px;
}

.dev-wx {
  margin-top: 14px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  background: var(--surface-2);
}

.dev-code {
  margin-top: 10px;
  font-size: 30px;
  font-weight: 750;
  letter-spacing: 10px;
  text-align: center;
  color: var(--brand-ink);
  background: var(--surface);
  border-radius: var(--r-s);
  padding: 12px 8px;
}

.perm-limits {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  padding: 10px 12px;
}

.perm-limits-title {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--text-4);
  letter-spacing: 0.4px;
  margin-bottom: 6px;
}

.perm-limit-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 4px 0;
  font-size: 12.8px;
}

.perm-limit-row .pl-label {
  flex: 0 0 auto;
  min-width: 118px;
  color: var(--text-3);
}

.perm-limit-row .pl-value {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--text-1);
  word-break: break-word;
}

.perm-note {
  margin-top: 10px;
  padding: 8px 10px;
  border-radius: var(--r-s);
  background: var(--brand-soft);
  /* ⚠ 网盘前端的品牌色变量是 --brand-1（管理后台那边才叫 --brand），
     写错变量名 CSS 不报错、只是颜色静默失效 —— 由类名/变量自检兜住。 */
  color: var(--brand-1);
  font-size: 12.3px;
  line-height: 1.6;
}

/* =============================================================
   轻提示 Toast（石墨实底）
   ============================================================= */
.toasts {
  position: fixed;
  top: 18px;
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 460px;
  padding: 9px 14px 9px 11px;
  border-radius: var(--r-m);
  background: var(--ink);
  border: 1px solid var(--ink-3);
  box-shadow: var(--shadow-l);
  font-size: 12.8px;
  font-weight: 550;
  color: var(--ink-text);
  pointer-events: auto;
  cursor: pointer;
  animation: toast-in var(--dur-3) var(--ease-back) backwards;
  transition: transform var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}

.toast:hover {
  transform: translateY(-2px) scale(1.01);
  box-shadow: var(--shadow-xl);
}

.toast.is-leaving {
  animation: toast-out var(--dur-2) var(--ease) both;
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(-14px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes toast-out {
  to { opacity: 0; transform: translateY(-8px) scale(0.98); }
}

.toast .t-ico {
  width: 20px;
  height: 20px;
  border-radius: var(--r-xs);
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  color: #fff;
  transition: transform var(--dur-2) var(--ease-back);
}

.toast:hover .t-ico {
  transform: scale(1.1) rotate(-5deg);
}

.toast .t-ico svg {
  width: 12px;
  height: 12px;
}

.toast.is-ok .t-ico { background: var(--ok); }
.toast.is-warn .t-ico { background: var(--warn); }
.toast.is-error .t-ico { background: var(--danger); }

/* =============================================================
   传输任务行（右栏「任务列表」使用）
   ============================================================= */
.upload-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: var(--r-s);
  transition: background var(--dur-1) var(--ease), transform var(--dur-2) var(--ease);
  animation: rise-in var(--dur-3) var(--ease) backwards;
}

.upload-row:hover {
  background: var(--surface-2);
  transform: translateX(2px);
}

/* =============================================================
   ★ 断点续传：被中断的上传行
   ---------------------------------------------------------------
   这类行不是"正在传"，而是"上次没传完、还留在云端等着"。
   所以要：跟普通行区分开（左侧一条提示色）、显示已传多少、
   显示还有多久会被自动清理、并给出「继续上传 / 删除」两个按钮。
   ============================================================= */
.upload-row.is-paused {
  background: var(--surface-2);
  box-shadow: inset 3px 0 0 var(--warn, #f59e0b);
}

.upload-row.is-paused .upload-thumb {
  opacity: 0.72;
}

.upload-row.is-paused .upload-name {
  color: var(--text-2);
}

.up-warn {
  color: var(--warn, #b45309);
  font-weight: 700;
}

.up-actions {
  display: flex;
  gap: 6px;
  margin-top: 6px;
}

.up-actions .btn {
  height: 26px;
  padding: 0 10px;
  font-size: 11.5px;
}

/* 触屏：续传按钮要大一点，手指才点得准 */
@media (hover: none) {
  .up-actions .btn {
    min-height: 34px;
    padding: 0 14px;
  }
}

.upload-thumb {
  width: 32px;
  height: 32px;
  border-radius: var(--r-s);
  border: 1px solid var(--line);
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  color: var(--text-3);
  overflow: hidden;
}

.upload-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.upload-thumb svg {
  width: 16px;
  height: 16px;
}

.upload-meta {
  flex: 1 1 auto;
  min-width: 0;
}

.upload-name {
  font-size: 12.2px;
  font-weight: 550;
  color: var(--text-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.upload-sub {
  margin-top: 3px;
  font-size: 10.8px;
  color: var(--text-3);
  display: flex;
  align-items: center;
  gap: 6px;
  font-variant-numeric: tabular-nums;
}

.upload-bar {
  margin-top: 5px;
  height: 4px;
  border-radius: var(--r-full);
  background: var(--surface-3);
  border: 1px solid var(--line-soft);
  overflow: hidden;
}

.upload-bar > i {
  display: block;
  height: 100%;
  border-radius: var(--r-full);
  background: var(--brand-1);
  transition: width var(--dur-4) var(--ease);
}

.upload-row.is-error .upload-bar > i {
  background: var(--danger);
}

/* =============================================================
   拖拽上传全屏遮罩
   ============================================================= */
.dropzone {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  place-items: center;
  background: rgba(23, 24, 28, 0.06);
  animation: fade-in var(--dur-2) var(--ease) backwards;
  pointer-events: none;
}

.dropzone-inner {
  padding: 44px 66px;
  border-radius: var(--r-xl);
  border: 2px dashed var(--line-strong);
  background: var(--surface);
  box-shadow: var(--shadow-l);
  text-align: center;
  animation: drop-in var(--dur-3) var(--ease-back) backwards;
}

@keyframes drop-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}

.dropzone-ico {
  width: 64px;
  height: 64px;
  margin: 0 auto 14px;
  border-radius: var(--r-l);
  display: grid;
  place-items: center;
  background: var(--ink);
  color: #fff;
  box-shadow: var(--shadow-m);
  /* 2026-10-23 修复：脉冲光环改由 ::after 用 transform/opacity 实现（不再动画 box-shadow） */
  position: relative;
}

/* 2026-10-23 修复：pulse-ring 改为 transform+opacity 的扩散光环（见 base.css 的 @keyframes） */
.dropzone-ico::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-l);
  border: 3px solid rgba(91, 108, 255, 0.25);
  animation: pulse-ring 1.7s var(--ease) infinite;
  pointer-events: none;
}

.dropzone-ico svg {
  width: 28px;
  height: 28px;
}

.dropzone-title {
  font-size: 17px;
  font-weight: 700;
  color: var(--text-1);
  margin-bottom: 6px;
}

.dropzone-sub {
  font-size: 12.5px;
  color: var(--text-3);
}

/* =============================================================
   空状态
   ============================================================= */
.empty {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 50px 24px;
  text-align: center;
  animation: fade-in var(--dur-3) var(--ease) backwards;
}

.empty-art {
  position: relative;
  width: 104px;
  height: 104px;
  margin-bottom: 12px;
  display: grid;
  place-items: center;
  transition: transform var(--dur-3) var(--ease);
}

.empty:hover .empty-art {
  transform: translateY(-3px);
}

.empty-art::before {
  content: "";
  position: absolute;
  inset: 10px;
  border-radius: 50%;
  background: var(--surface-3);
  border: 1px solid var(--line-soft);
  /* 2026-10-23 修复：动画 box-shadow 违反"只动 transform/opacity"红线 →
     底盘保持静态，脉冲光环移到 ::after（transform+opacity 实现） */
}

/* 2026-10-23 修复：pulse-ring 改为 transform+opacity 的扩散光环（见 base.css 的 @keyframes） */
.empty-art::after {
  content: "";
  position: absolute;
  inset: 10px;
  border-radius: 50%;
  border: 2px solid rgba(91, 108, 255, 0.25);
  animation: pulse-ring 3.2s var(--ease) infinite;
  pointer-events: none;
}

.empty-art svg {
  position: relative;
  width: 44px;
  height: 44px;
  color: var(--text-3);
}

.empty-title {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--text-1);
}

.empty-desc {
  font-size: 12.5px;
  color: var(--text-3);
  max-width: 380px;
}

/* =============================================================
   徽标 / 芯片 / 进度
   ============================================================= */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 20px;
  padding: 0 8px;
  white-space: nowrap;
  flex: 0 0 auto;
  border-radius: var(--r-full);
  font-size: 10.8px;
  font-weight: 650;
  background: var(--surface-3);
  border: 1px solid var(--line-soft);
  color: var(--text-2);
  transition: transform var(--dur-2) var(--ease), background var(--dur-1) var(--ease);
}

.badge:hover {
  transform: translateY(-1px);
}

.badge:active {
  transform: translateY(0) scale(0.94);
  transition-duration: var(--dur-instant);
}

.badge.is-brand { background: var(--brand-soft); border-color: var(--line); color: var(--text-1); }
.badge.is-ok { background: var(--ok-soft); border-color: rgba(47, 133, 90, 0.25); color: var(--ok); }
.badge.is-warn { background: var(--warn-soft); border-color: rgba(183, 121, 31, 0.25); color: var(--warn); }
.badge.is-danger { background: var(--danger-soft); border-color: rgba(192, 80, 78, 0.25); color: var(--danger); }

.badge svg {
  width: 11px;
  height: 11px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--r-full);
  font-size: 12px;
  font-weight: 550;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--line);
  transition: all var(--dur-2) var(--ease);
}

.chip:hover {
  background: var(--surface-2);
  border-color: var(--line-strong);
  transform: translateY(-1px);
}

.chip:active {
  transform: translateY(0) scale(0.96);
  transition-duration: var(--dur-instant);
}

.chip.is-on {
  background: var(--ink);
  color: #fff;
  border-color: var(--ink);
}

.spinner {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 2px solid var(--surface-3);
  border-top-color: var(--text-2);
  animation: spin 0.7s linear infinite;
}

/* =============================================================
   视频压缩预览提示条
   ============================================================= */
.quality-tip {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 12px;
  border-radius: var(--r-s);
  background: var(--warn-soft);
  border: 1px solid rgba(183, 121, 31, 0.3);
  font-size: 12px;
  font-weight: 550;
  color: var(--warn);
  transition: transform var(--dur-2) var(--ease);
}

.quality-tip:hover {
  transform: translateY(-1px);
}

.quality-tip svg {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
}

/* =============================================================
   Mock 演示角标（2026-10-23 OPT-16）
   ---------------------------------------------------------------
   useMock 由 hostname 推导（localhost → 内存 Mock 演示数据），
   原来页面上没有任何提示，容易把演示数据当生产数据。
   固定左下角的小胶囊：醒目但 pointer-events:none，绝不挡操作；
   窄屏时抬到底部导航之上。
   ============================================================= */
.mock-badge {
  position: fixed;
  left: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  z-index: 80;
  padding: 5px 12px;
  border-radius: var(--r-full);
  background: var(--warn, #b4791f);
  color: #fff;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  box-shadow: var(--shadow-m);
  pointer-events: none;
  animation: rise-in var(--dur-3) var(--ease) backwards;
}

@media (max-width: 860px) {
  .mock-badge {
    /* 让开底部导航 + iPhone 安全区 */
    bottom: calc(78px + env(safe-area-inset-bottom, 0px));
  }
}
