* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: -apple-system, "Helvetica Neue", Helvetica, Arial,
               "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  background: #f5f6f9;
  color: #1d1d1f;
  font-size: 13px;
  /* 整个页面是一个 flex 列: header + layout 各占一行, 总高 = 100vh */
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}
code { font-family: ui-monospace, "Menlo", monospace; font-size: 11.5px; color: #555; }

header {
  flex: 0 0 auto;
  padding: 10px 18px;
  background: #ffffff;
  border-bottom: 1px solid #e3e4e8;
  display: flex;
  align-items: baseline;
  gap: 18px;
}
header h1 { margin: 0; font-size: 18px; font-weight: 600; }
header .hint { font-size: 12px; color: #6b6b70; }

.layout {
  flex: 1 1 auto;
  min-height: 0;          /* 关键: flex 子元素默认 min-height = content, 会撑大父级阻止滚动 */
  display: flex;
}

/* ============== sidebar ============== */
.sidebar {
  width: 340px;
  background: #ffffff;
  border-right: 1px solid #e3e4e8;
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}
.tabs {
  display: flex;
  border-bottom: 1px solid #e3e4e8;
}
.tab {
  flex: 1;
  padding: 10px;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  font-size: 13px;
  cursor: pointer;
  color: #555;
}
.tab:hover { background: #f5f6f9; }
.tab.active {
  border-bottom-color: #0a5dff;
  color: #0a5dff;
  font-weight: 600;
}
.panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 10px;
  gap: 10px;
  overflow: hidden;
}
.panel.hidden { display: none; }
.panel .row { display: flex; gap: 8px; }
.panel .row > * { font-size: 12px; }
.panel button {
  padding: 6px 12px;
  border-radius: 5px;
  border: 1px solid #d3d4d8;
  background: #ffffff;
  cursor: pointer;
}
.panel button:hover { background: #f0f1f4; }
.panel input[type=search] {
  flex: 1;
  padding: 6px 9px;
  border-radius: 5px;
  border: 1px solid #d3d4d8;
  background: #ffffff;
}

.src-list {
  list-style: none;
  padding: 0;
  margin: 0;
  overflow-y: auto;
  flex: 1;
}
.src-list li {
  padding: 7px 9px;
  border-radius: 5px;
  cursor: pointer;
  border: 1px solid transparent;
  margin-bottom: 2px;
}
.src-list li:hover { background: #eef3ff; border-color: #0a5dff44; }
.src-list li.hint { color: #999; cursor: default; }
.src-list li.hint:hover { background: transparent; border-color: transparent; }
.src-list .name { font-weight: 500; font-size: 12px; word-break: break-all; }
/* 文件名行: 前缀是手机型号 (Xiaomi_.../HONOR_...), 用等宽 + 主色凸显出来 */
.src-list .fname {
  font-size: 11px;
  color: #0a5dff;
  margin-top: 2px;
  word-break: break-all;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.src-list .sub  { font-size: 11px; color: #888; margin-top: 2px; }

.drop {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border: 2px dashed #c5c8d0;
  border-radius: 10px;
  padding: 32px 14px;
  cursor: pointer;
  color: #6b6b70;
  background: #fafbfc;
}
.drop:hover { background: #f0f1f4; border-color: #a3a5ad; }
.drop.dragover { background: #eef3ff; border-color: #0a5dff; color: #0a5dff; }

/* ============== main ============== */
.main {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.toolbar {
  flex: 0 0 auto;
  background: #ffffff;
  padding: 8px 16px;
  border-bottom: 1px solid #e3e4e8;
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 12px;
}
.toolbar .grow { flex: 1; }
.toolbar .sep { color: #ccc; }
.toolbar .opts-group { display: flex; align-items: center; gap: 10px; color: #555; }
.toolbar .opt { display: flex; align-items: center; gap: 4px; color: #555; }
.toolbar button {
  padding: 5px 10px;
  border-radius: 4px;
  border: 1px solid #d3d4d8;
  background: #fff;
  cursor: pointer;
}
.toolbar button:hover { background: #f0f1f4; }
.toolbar button.primary {
  border-color: #0a5dff;
  background: #0a5dff;
  color: #fff;
}
.toolbar button.primary:hover { background: #004fe0; }
.toolbar button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.toolbar button:disabled:hover { background: #fff; }
.toolbar button.primary:disabled:hover { background: #0a5dff; }

.cards-grid {
  flex: 1 1 auto;
  min-height: 0;          /* 关键: 让 overflow-y: auto 真的能滚 */
  overflow-y: auto;
  overflow-x: hidden;
  padding: 12px;
  display: grid;
  gap: 12px;
  grid-template-columns: 1fr;
  align-content: start;
}
.cards-grid.compact { grid-template-columns: 1fr 1fr; }

.empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #999;
  text-align: center;
  padding: 40px;
}
.empty .hint { font-size: 12px; margin-top: 6px; }
.cards-grid:not(:empty) ~ .empty { display: none; }

/* ============== card ============== */
.card {
  background: #ffffff;
  border: 1px solid #e3e4e8;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  /* 默认高度跟着 plot 内容走 (无白边); 用户拖右下角可改尺寸 */
  resize: vertical;
  overflow: hidden;
  width: 100%;
  height: auto;
  min-height: 300px;
  display: flex;
  flex-direction: column;
}
.card-head {
  flex: 0 0 auto;          /* 头部不参与挤压, 始终钉在卡顶 */
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-bottom: 1px solid #eef0f3;
  background: #fafbfc;
  cursor: grab;            /* 视觉提示: 卡头可拖 */
  user-select: none;
}
.card-head:active { cursor: grabbing; }
.card-head .drag-handle {
  color: #b8bcc4;
  font-size: 13px;
  letter-spacing: -2px;
  margin-right: 2px;
}
.card-head .actions { cursor: default; }
.card-head .actions button { cursor: pointer; }

/* 拖动中: 半透明 ghost; 鼠标移到的卡片显示 drop 指示线 */
.card.dragging { opacity: 0.4; }
.card.drop-above { box-shadow: 0 -3px 0 0 #0a5dff, 0 1px 2px rgba(0,0,0,0.04); }
.card.drop-below { box-shadow: 0 3px 0 0 #0a5dff, 0 1px 2px rgba(0,0,0,0.04); }

/* 「同一文件再次点击」时已有卡片闪一下提示 */
@keyframes card-flash {
  0%   { box-shadow: 0 0 0 0 rgba(10, 93, 255, 0.0); }
  20%  { box-shadow: 0 0 0 4px rgba(10, 93, 255, 0.40); }
  100% { box-shadow: 0 0 0 0 rgba(10, 93, 255, 0.0); }
}
.card.flash { animation: card-flash 0.9s ease-out; }
.card-head .title {
  flex: 1;
  min-width: 0;            /* 允许子元素在 flex 里换行/省略 */
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.card-head .title .title-name {
  font-weight: 600;
  font-size: 13px;
  word-break: break-all;
}
/* 文件名 (含手机型号): 等宽 + 主色, 标明这条曲线来自哪台手机 */
.card-head .title .title-file {
  font-weight: 400;
  font-size: 11px;
  color: #0a5dff;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  word-break: break-all;
}
.card-head .src-tag {
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 3px;
  background: #eef3ff;
  color: #0a5dff;
}
.card-head .src-tag.cloud { background: #fff0e6; color: #d97706; }
.card-head .src-tag.upload { background: #e8f6ee; color: #047a4d; }
.card-head .src-tag.compare { background: #f0ecff; color: #5b35d5; }
.card-head .compare-opt {
  display: flex;
  align-items: center;
  gap: 3px;
  color: #6b6b70;
  font-size: 11px;
  cursor: pointer;
  white-space: nowrap;
}
.card-head .compare-opt input { margin: 0; }
.card-head .actions button {
  background: transparent;
  border: none;
  padding: 4px 8px;
  cursor: pointer;
  color: #6b6b70;
  font-size: 14px;
}
.card-head .actions button.export {
  font-size: 12px;
  color: #0a5dff;
}
.card-head .actions button.export:hover { color: #004fe0; }
.card-head .actions button.close:hover { color: #c00; }
.card-head .actions button.reload:hover { color: #0a5dff; }

.compare-card {
  border-color: #d8d0ff;
  box-shadow: 0 2px 10px rgba(91,53,213,0.08);
}

.card-body {
  flex: 1 1 auto;
  min-height: 0;           /* 关键: 让 overflow-y: auto 真的能滚 (跟外层 flex chain 一个原理) */
  overflow-y: auto;
  overflow-x: hidden;
  padding: 8px;
}
.card-figure, .plot {
  width: 100%;
  /* min-height 给一个保底, 不让 plotly 把 plot 缩到 0 */
  min-height: 320px;
}
.plot { display: block; }
.plot .js-plotly-plot { width: 100%; }
.card-figure img {
  max-width: 100%;
  height: auto;
  display: block;
}
.spinner {
  color: #888;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 200px;
}
.spinner::before {
  content: "";
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid #ccc;
  border-top-color: #0a5dff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.card.err .plot { color: #c00; }
.card.err .spinner::before { display: none; }

.card-meta {
  margin-top: 10px;
  font-size: 11.5px;
  border-top: 1px dashed #eef0f3;
  padding-top: 8px;
  display: none;
}
.card-meta.on { display: block; }
.card-meta dl {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr max-content 1fr;
  gap: 2px 10px;
}
.card-meta dt { color: #6b6b70; }
.card-meta dd { margin: 0; font-family: ui-monospace, Menlo, monospace; }

.card-meta .chirps {
  margin-top: 8px;
  max-height: 220px;
  overflow-y: auto;
}
.card-meta .chirps table {
  border-collapse: collapse;
  width: 100%;
  font-size: 11px;
  font-family: ui-monospace, Menlo, monospace;
}
.card-meta .chirps th, .card-meta .chirps td {
  padding: 2px 6px;
  border-bottom: 1px solid #eef0f3;
  text-align: right;
}
.card-meta .chirps th:first-child, .card-meta .chirps td:first-child {
  text-align: left;
}
.card-meta .chirps thead { position: sticky; top: 0; background: #fff; }

.compare-note {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 2px 2px;
  border-top: 1px dashed #eef0f3;
}
.compare-note:empty { display: none; }
.record-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 440px;
  padding: 3px 8px;
  border: 1px solid #e6e7ea;
  border-radius: 999px;
  background: #fafbfc;
  font-size: 11px;
  color: #444;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.record-chip .chip-label { flex: 0 0 auto; }
/* 文件名 (含手机型号): 等宽 + 主色, 用细分隔线和录音名分开 */
.record-chip .chip-file {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: #0a5dff;
  padding-left: 6px;
  border-left: 1px solid #e0e1e4;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chip-swatch {
  width: 9px;
  height: 9px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--chip-color);
}

/* ====================================================================
   云端树状分类筛选
   ==================================================================== */
.cloud-tree {
  flex: 0 0 auto;
  max-height: 34vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 4px;
  border: 1px solid #e6e7ea;
  border-radius: 6px;
  background: #fafbfc;
}
.cloud-tree:empty { display: none; }
/* 覆盖 .panel button 的默认样式, 让分类行更紧凑 */
.panel .cloud-tree button.ctree-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 100%;
  padding: 4px 8px;
  border: 1px solid transparent;
  border-radius: 5px;
  background: transparent;
  font-size: 12px;
  text-align: left;
  cursor: pointer;
}
.panel .cloud-tree button.ctree-row:hover { background: #eef3ff; }
.panel .cloud-tree button.ctree-row.active {
  background: #e7efff;
  border-color: #0a5dff66;
  font-weight: 600;
}
.cloud-tree .ct-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cloud-tree .ct-count {
  flex: 0 0 auto;
  font-size: 11px;
  color: #888;
  background: #ffffff;
  border: 1px solid #e0e1e4;
  border-radius: 999px;
  padding: 0 7px;
  min-width: 20px;
  text-align: center;
}

/* ====================================================================
   后台批量预计算 (任务 + 进度)
   ==================================================================== */
.panel button.primary {
  border-color: #0a5dff;
  background: #0a5dff;
  color: #fff;
}
.panel button.primary:hover { background: #004fe0; }
.panel button:disabled { opacity: 0.45; cursor: not-allowed; }
.panel button.primary:disabled:hover { background: #0a5dff; }

/* tab 上的运行中任务数角标 */
.tab .badge {
  display: inline-block;
  min-width: 16px;
  padding: 0 4px;
  margin-left: 3px;
  font-size: 10px;
  line-height: 16px;
  text-align: center;
  color: #fff;
  background: #0a5dff;
  border-radius: 999px;
  vertical-align: middle;
}
.tab .badge.hidden { display: none; }

.jobs-cores { align-items: center; gap: 8px; flex-wrap: wrap; }
.jobs-cores label { display: flex; align-items: center; gap: 5px; color: #555; }
.jobs-cores input[type=number] {
  width: 56px;
  padding: 5px 7px;
  border-radius: 5px;
  border: 1px solid #d3d4d8;
  background: #fff;
}
.jobs-cores .grow { flex: 1; }

.jobs-list {
  overflow-y: auto;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.jobs-list .hint { color: #999; }

.job {
  border: 1px solid #e6e7ea;
  border-radius: 8px;
  background: #fafbfc;
  padding: 8px 10px;
}
.job.running { border-color: #0a5dff55; background: #f4f8ff; }
.job.has-err { border-color: #e0a3a3; }
.job-head {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 12px;
}
.job-arrow { color: #b8bcc4; width: 10px; }
.job-state {
  font-size: 10px;
  padding: 1px 7px;
  border-radius: 999px;
  background: #eef1f5;
  color: #666;
}
.job-state.st-running { background: #e7efff; color: #0a5dff; }
.job-state.st-done { background: #e8f6ee; color: #047a4d; }
.job-state.st-cancelled { background: #f1eef0; color: #998; }
.job-frac { font-family: ui-monospace, Menlo, monospace; font-weight: 600; }
.job-cores { color: #888; font-size: 11px; }
.job-head .grow { flex: 1; }
.job-cancel {
  padding: 2px 9px;
  font-size: 11px;
  border-radius: 4px;
  border: 1px solid #d3d4d8;
  background: #fff;
  cursor: pointer;
  color: #c0392b;
}
.job-cancel:hover { background: #fdecea; }

.job-bar {
  height: 7px;
  margin: 7px 0 5px;
  background: #e7e9ee;
  border-radius: 999px;
  overflow: hidden;
}
.job-bar-fill {
  height: 100%;
  background: #0a5dff;
  border-radius: 999px;
  transition: width 0.4s ease;
}
.job.has-err .job-bar-fill { background: #d97706; }
.job-bar-fill.anim {
  background-image: linear-gradient(45deg,
    rgba(255,255,255,0.28) 25%, transparent 25%,
    transparent 50%, rgba(255,255,255,0.28) 50%,
    rgba(255,255,255,0.28) 75%, transparent 75%, transparent);
  background-size: 18px 18px;
  animation: job-stripes 0.7s linear infinite;
}
@keyframes job-stripes { to { background-position: 18px 0; } }

.job-sub { font-size: 11px; color: #777; }
.job-sub .bad { color: #c0392b; font-weight: 600; }

.job-records {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 8px;
}
.job-records:empty { display: none; }
.rec {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 7px;
  border-radius: 5px;
  border: 1px solid #ebecef;
  background: #fff;
  font-size: 11px;
  color: #555;
}
.rec.open { cursor: pointer; }
.rec.open:hover { background: #eef3ff; border-color: #0a5dff55; }
.rec-dot {
  flex: 0 0 auto;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: #c5c8d0;
}
.rec-pending .rec-dot { background: #c5c8d0; }
.rec-done .rec-dot { background: #047a4d; }
.rec-cached .rec-dot { background: #0a5dff; }
.rec-error .rec-dot { background: #c0392b; }
.rec-cancelled .rec-dot { background: #b0a8ac; }
.rec-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rec-st { flex: 0 0 auto; color: #999; font-size: 10px; }
.rec-error { color: #c0392b; }
.rec-error .rec-st { color: #c0392b; }

/* 正在计算的录音: 蓝点呼吸 */
.rec-running .rec-dot { background: #0a5dff; animation: rec-pulse 1s ease-in-out infinite; }
.rec-running .rec-st { color: #0a5dff; }
@keyframes rec-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
.job-sub .run { color: #0a5dff; font-weight: 600; }
