/* ===== 表格 ===== */

/* 滚动容器：只有它滚动，粘性表头才挂得住。
   border-collapse 必须留在 separate —— collapse 下 th 的边框会跟着内容滚走。
   max-height 用 svh 而不是 dvh：这是个用户会在里面滚的内滚区，
   dvh 会随地址栏收展改高度，滚到一半盒子忽然变高变矮比"永远矮一点"难受得多。 */
.table-wrap { overflow: auto; max-height: 72vh; max-height: 72svh; }
table { width: 100%; border-collapse: separate; border-spacing: 0; }

th, td {
  text-align: left;
  /* 行高比原来松一点：8px 上下 + 13px 字号让每行都贴着下一行，
     一页 30 行读起来是一片。12px 给数字和状态胶囊留出呼吸。 */
  padding: 11px 12px;
  border-bottom: 1px solid var(--line-soft);
  font-size: 13px;
  vertical-align: middle;
  /* 全表统一等宽数字：只影响数字字形，中文与拉丁字母照常走各自字体 */
  font-variant-numeric: tabular-nums;
}
th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: #f1f5f9;
  color: var(--muted);
  font-weight: 600;
  font-size: 12px;
  white-space: nowrap;
}
/* 数字列右对齐。等宽数字 + 右对齐之后，"0 / 12 / 1380" 的末位才对得齐，
   一列数字扫下来能直接比大小；左对齐时它们各自起始、位数不同就散了。 */
th.num, td.num { text-align: right; }

/* ---------- 窄容器下的列压缩 ---------- */
/* 表格挤窄时中文会在任意字符间断行（td 没有 nowrap，只有 th 有），
   "微信小程序端" 会逐字折成六行，整行被撑得老高。渠道列改成截断成
   "前两个汉字 + …"，行高就稳住了。配合 core/dom.js 的 clippedCell。
   用容器查询而不是媒体查询：判断依据是**表格自己的宽度**，而正文可用宽度
   在 980px 处是**非单调**的——侧栏在那里从常驻 216px 变成覆盖式抽屉，
   窗口再窄一点正文反而更宽。媒体查询表达不了这种跳变。
   container-type 只上在这一张表上（命名容器）：别的表格没有 .cell-clip，
   但容器声明是白加的，不该无谓地给它们套一层 contain。 */
#userListView .table-wrap { container: user-table / inline-size; }
/* 985px 是实测值，不是估的：九列全不折行时表格要 987px（无头 Chrome 里
   把 table 改成 width:max-content 量出来的），压缩后只要 953px。
   门槛取在 987 略下沿，压缩就正好在表格被挤的那一刻生效——比它高会提前
   截掉还放得下的"微信小程序端"，比它低则会出现"没压缩 + 已经在挤"的夹缝，
   渠道又折回多行。整库 212 名玩家的 openid 都是定长 28 字符、昵称最长 7 字，
   所以 987 这个数对当前数据是稳的；将来昵称普遍变长会把它推高，
   届时这个门槛要跟着调。 */
@container user-table (max-width: 985px) {
  /* 3em = 两个汉字，加省略号是这个宽度下的理论下限；多给的 0.4em 是余量——
     U+2026 在不同字体里宽度不一样，正好卡在 3em 时可能只剩一个汉字。
     display:block 而不是 inline-block：单元格里只有这一个元素，
     块级就不必再操心 vertical-align 把文字顶偏。 */
  .cell-clip {
    display: block;
    max-width: 3.4em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

tbody tr[data-id] { cursor: pointer; transition: background .15s; }
tbody tr[data-id]:hover { background: #f8fafc; }
/* 从详情返回时标出刚打开的那一行（features/users/detail.js 的 markSelectedRow）。
   用左侧竖条而不是整行底色：整行染色会和 .row-failed 的失败行撞在一起，
   也盖掉了 hover 态，看不出鼠标在哪。 */
tbody tr.selected { background: var(--primary-soft); }
tbody tr.selected:hover { background: var(--primary-soft); }
tbody tr.selected td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
tbody tr:last-child td { border-bottom: 0; }
/* 发送失败的通知记录整行浅红，扫一眼就能定位 */
tbody tr.row-failed td { background: #fff1f2; }

td.muted { color: var(--hint); }

/* ---------- 玩家单元格 ---------- */
.player-cell { display: flex; align-items: center; gap: 10px; }
/* 昵称下面那行写的是渠道 openid（features/users/list.js 里造的），
   是不含空格的长串，而 .player-cell 是 flex、文字块默认 min-width:auto。
   必须用 anywhere——break-word 只影响换行不影响内在尺寸计算，挡不住它顶宽表格。 */
.player-cell > div { min-width: 0; }
.player-cell .muted { overflow-wrap: anywhere; }
/* 主副标题的层次：昵称吃正文色而不是标题色，openid 那行再压一档。
   两行同色同重时，一列看下去分不清哪行是名字。 */
.player-cell > div > div:first-child { color: var(--ink); font-weight: 500; }
.avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
  background: var(--neutral-soft);
  border: 1px solid var(--line);
}
.avatar.large { width: 52px; height: 52px; }

/* ---------- 兑换码 ---------- */
.gift-code {
  font-family: Consolas, "Courier New", monospace;
  font-size: 13px;
  letter-spacing: 1px;
  background: #f1f5f9;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 2px 6px;
  margin-right: 6px;
  word-break: break-all;
}

/* ---------- 告警级别着色 ---------- */
.alert-level-critical { color: var(--danger-ink); font-weight: 600; }
.alert-level-error { color: #c2410c; }
.alert-level-warning { color: var(--warning-ink); }

/* ---------- 广告流水状态着色 ---------- */
/* 用文字着色而不是 .status 胶囊：这四个不是「正常/异常」的二元态，
   而是一列里并排出现的四种结局，形状（胶囊）会把注意力从颜色上拖走。
   「中途退出」与「未能播放」必须是两个明显不同的颜色——它们是这张表里
   最容易被合成一句「没看完」的两个数，而含义正好相反。 */
.ad-status-completed { color: var(--success-ink); font-weight: 600; }
.ad-status-aborted { color: var(--warning-ink); }
.ad-status-failed { color: var(--danger-ink); }
.ad-status-pending { color: var(--hint); }

/* ---------- 未登记的广告位 ---------- */
/* 只标「广告位」那一格，不整行染色：同一行里页面、状态、时间都是正常数据，
   整行红底会让人以为这条记录本身出了问题（.row-failed 才是那个语义）。
   要的效果是「这一列里有一个我不认识的名字」，视线扫过那一列时自己跳出来。 */
.ad-scene-unregistered { color: var(--danger-ink); font-weight: 600; }

/* 汇总表上方的提示条。用告警色而不是 .muted：上面两行 muted 是口径说明
   （读之前要知道的事），这一条是「有东西需要你去处理」，不是一类东西。 */
.ad-unknown-note {
  color: var(--danger-ink);
  font-weight: 600;
  background: #fff1f2;
  border-radius: 6px;
  padding: 8px 12px;
}
