/* hkchart — shadcn/ui 风格设计系统 */
:root {
  --background: 0 0% 100%;
  --foreground: 240 10% 3.9%;
  --card: 0 0% 100%;
  --muted: 240 4.8% 95.9%;
  --muted-foreground: 240 3.8% 46.1%;
  --border: 240 5.9% 90%;
  --input: 240 5.9% 90%;
  --primary: 240 5.9% 10%;
  --primary-foreground: 0 0% 98%;
  --accent: 240 4.8% 95.9%;
  --destructive: 0 84.2% 60.2%;
  --success: 142 71% 45%;
  --warning: 38 92% 50%;
  --info: 217 91% 60%;
  --radius: 0.5rem;
}
:root[data-theme="dark"] {
  --background: 240 10% 3.9%;
  --foreground: 0 0% 98%;
  --card: 240 10% 5.9%;
  --muted: 240 3.7% 15.9%;
  --muted-foreground: 240 5% 64.9%;
  --border: 240 3.7% 15.9%;
  --input: 240 3.7% 15.9%;
  --primary: 0 0% 98%;
  --primary-foreground: 240 5.9% 10%;
  --accent: 240 3.7% 15.9%;
  --success: 142 71% 45%;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
body {
  font-family: Inter, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
  background: hsl(var(--background)); color: hsl(var(--foreground));
  font-size: 14px; line-height: 1.5; -webkit-font-smoothing: antialiased;
}
.hidden { display: none !important; }
a { color: inherit; text-decoration: none; }

/* ---------- 登录 ---------- */
.login-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 24px; }
.login-card { width: 100%; max-width: 380px; border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card)); padding: 32px; box-shadow: 0 1px 3px rgba(0,0,0,.06); }
.login-card h1 { font-size: 22px; font-weight: 600; letter-spacing: -.02em; }
.login-card .sub { color: hsl(var(--muted-foreground)); font-size: 13px; margin: 6px 0 24px; }
.login-card label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 6px; }
.login-card .field { margin-bottom: 16px; }

/* ---------- 基础控件 ---------- */
.input, input[type=text], input[type=password], input[type=date], input[type=search], input[type=number], select {
  height: 36px; width: 100%; padding: 0 12px; font-size: 13px; font-family: inherit;
  color: hsl(var(--foreground)); background: hsl(var(--background));
  border: 1px solid hsl(var(--input)); border-radius: calc(var(--radius) - 2px); outline: none;
}
.input:focus, input:focus, select:focus { border-color: hsl(var(--foreground) / .4); box-shadow: 0 0 0 3px hsl(var(--foreground) / .06); }
/* 值越界／冲突的输入框。只描边不改底色：这一格仍然要能读清自己填了什么 */
input.bad, input.bad:focus { border-color: hsl(var(--destructive)); box-shadow: 0 0 0 3px hsl(var(--destructive) / .15); }
select { cursor: pointer; }
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 36px; padding: 0 14px; font-size: 13px; font-weight: 500; font-family: inherit;
  border-radius: calc(var(--radius) - 2px); border: 1px solid transparent; cursor: pointer; white-space: nowrap;
  background: hsl(var(--primary)); color: hsl(var(--primary-foreground));
}
.btn:hover { opacity: .9; }
.btn.outline { background: transparent; border-color: hsl(var(--border)); color: hsl(var(--foreground)); }
.btn.outline:hover { background: hsl(var(--accent)); opacity: 1; }
.btn.ghost { background: transparent; color: hsl(var(--muted-foreground)); }
.btn.ghost:hover { background: hsl(var(--accent)); color: hsl(var(--foreground)); }
.btn.sm { height: 30px; padding: 0 10px; font-size: 12px; }
.btn.danger { background: transparent; border-color: hsl(var(--border)); color: hsl(var(--destructive)); }
.btn.danger:hover { background: hsl(var(--destructive) / .08); opacity: 1; }
.btn.block { width: 100%; }
.err { color: hsl(var(--destructive)); font-size: 13px; margin-top: 10px; min-height: 18px; }

/* ---------- 布局 ---------- */
.app { display: flex; min-height: 100vh; }
.side {
  width: 240px; flex-shrink: 0; border-right: 1px solid hsl(var(--border));
  background: hsl(var(--card)); position: fixed; top: 0; bottom: 0; display: flex; flex-direction: column;
}
.brand { display: flex; align-items: center; gap: 10px; padding: 18px 20px; border-bottom: 1px solid hsl(var(--border)); }
.brand .logo { width: 30px; height: 30px; border-radius: 7px; background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); display: grid; place-items: center; font-weight: 700; font-size: 14px; }
.brand .txt b { display: block; font-size: 14px; font-weight: 600; letter-spacing: -.01em; }
.brand .txt span { font-size: 11px; color: hsl(var(--muted-foreground)); }
.app-switch { padding: 12px 12px 4px; }
.app-switch select { width: 100%; height: 34px; font-weight: 500; }
.side nav { padding: 8px 10px 12px; flex: 1; }
.side .nav-label { font-size: 11px; font-weight: 500; color: hsl(var(--muted-foreground)); padding: 8px 10px 4px; text-transform: uppercase; letter-spacing: .04em; }
.side nav a {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; margin-bottom: 2px;
  border-radius: calc(var(--radius) - 2px); font-size: 13.5px; font-weight: 500; color: hsl(var(--muted-foreground));
}
.side nav a:hover { background: hsl(var(--accent)); color: hsl(var(--foreground)); }
.side nav a.active { background: hsl(var(--accent)); color: hsl(var(--foreground)); }
.side nav a svg { width: 16px; height: 16px; flex-shrink: 0; }
.side-foot { padding: 12px; border-top: 1px solid hsl(var(--border)); display: flex; gap: 8px; }

.main { flex: 1; margin-left: 240px; min-width: 0; }
.topbar {
  height: 56px; border-bottom: 1px solid hsl(var(--border)); display: flex; align-items: center;
  justify-content: space-between; padding: 0 24px; position: sticky; top: 0; background: hsl(var(--background) / .85);
  backdrop-filter: blur(8px); z-index: 10;
}
.topbar h1 { font-size: 15px; font-weight: 600; letter-spacing: -.01em; }
.content { padding: 24px; }
.page-head { margin-bottom: 20px; }
.page-head h2 { font-size: 22px; font-weight: 600; letter-spacing: -.02em; }
.page-head p { color: hsl(var(--muted-foreground)); font-size: 13px; margin-top: 3px; }

/* ---------- 卡片 ---------- */
.card { border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card)); }
.card-head { padding: 16px 20px 0; }
.card-head .t { font-size: 13px; font-weight: 500; color: hsl(var(--muted-foreground)); display: flex; justify-content: space-between; align-items: center; }
.card-body { padding: 8px 20px 18px; }
.card-body .v { font-size: 26px; font-weight: 700; letter-spacing: -.02em; }
.card-body .d { font-size: 12px; color: hsl(var(--muted-foreground)); margin-top: 3px; }
.delta { font-weight: 600; margin-left: 2px; }
.delta.up { color: hsl(var(--success)); }
.delta.down { color: hsl(var(--destructive)); }
.delta.flat { color: hsl(var(--muted-foreground)); }
.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; align-items: start; }
.grid-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; margin-bottom: 20px; }
.panel { border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card)); padding: 20px; margin-bottom: 20px; }
.panel > h3 { font-size: 15px; font-weight: 600; margin-bottom: 4px; }
.panel > .desc { font-size: 12.5px; color: hsl(var(--muted-foreground)); margin-bottom: 14px; }
#trend-chart, #active-chart, #paid-chart, #transcribe-chart,
#task-active-chart, #task-trend-chart { width: 100%; height: 300px; }
/* 积分只有一条线，不需要和上面那些同高 */
#task-credit-chart { width: 100%; height: 220px; }

/* ---------- 状态灯 ---------- */
.lights { display: flex; gap: 10px; flex-wrap: wrap; }
.lights + .lights { margin-top: 10px; }
.svc-row .light { flex: 1 1 100%; }
.svc-row .meta { text-align: right; word-break: break-all; }
.light { display: flex; align-items: center; gap: 8px; border: 1px solid hsl(var(--border)); border-radius: calc(var(--radius) - 2px); padding: 8px 12px; flex: 1; min-width: 180px; }
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.dot.ok { background: hsl(var(--success)); box-shadow: 0 0 0 3px hsl(var(--success) / .15); }
.dot.bad { background: hsl(var(--destructive)); box-shadow: 0 0 0 3px hsl(var(--destructive) / .15); }
.light .lt { font-size: 13px; font-weight: 500; }
.light .meta { font-size: 11.5px; color: hsl(var(--muted-foreground)); margin-left: auto; }

/* ---------- 筛选栏 ---------- */
.toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.toolbar .input, .toolbar select { width: auto; min-width: 120px; }
.toolbar .grow { flex: 1; }

/* ---------- 表格 ---------- */
.table-wrap { border: 1px solid hsl(var(--border)); border-radius: var(--radius); overflow: hidden; background: hsl(var(--card)); }
.table-scroll { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th { text-align: left; padding: 10px 14px; font-weight: 500; color: hsl(var(--muted-foreground)); font-size: 12px; border-bottom: 1px solid hsl(var(--border)); white-space: nowrap; }
.tbl td { padding: 11px 14px; border-bottom: 1px solid hsl(var(--border)); vertical-align: middle; }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover { background: hsl(var(--muted) / .5); }
.tbl .link { color: hsl(var(--foreground)); font-weight: 500; cursor: pointer; }
.tbl .link:hover { text-decoration: underline; }
.muted { color: hsl(var(--muted-foreground)); font-size: 12px; }
.nowrap { white-space: nowrap; }

/* 时间列拆成两行（见 app.js 的 atCell）。行高收一点，两行叠起来的总高才不比
   原来的单行高出一截——那会把每一行都撑高，抵消掉省下来的宽度。 */
.tbl td.at { line-height: 1.4; }
.tbl td.at div + div { opacity: .72; }

/* 主要文本列的宽度下限。
   任务表有十四列，除了「任务」全是 nowrap，谁都不肯让宽度；而 auto layout 只会
   去挤唯一能换行的那一列，于是提示词被压成一字一行，一行任务拉出一屏高。
   用 min-width 而不是 width：width 在 auto layout 里只是建议，照样会被挤掉。
   宽度不够时由 .table-scroll 横向滚动——横着拖一下，好过竖着读一列汉字。 */
.tbl th.main, .tbl td.main { min-width: 340px; }
.empty { padding: 40px; text-align: center; color: hsl(var(--muted-foreground)); }

/* ---------- Badge ---------- */
.badge { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 9999px; font-size: 11.5px; font-weight: 500; border: 1px solid transparent; white-space: nowrap; }
.badge.outline { border-color: hsl(var(--border)); color: hsl(var(--muted-foreground)); }
.badge.live { background: hsl(var(--info) / .12); color: hsl(var(--info)); border-color: hsl(var(--info) / .25); }
.badge.file { background: hsl(280 65% 60% / .12); color: hsl(280 65% 55%); border-color: hsl(280 65% 60% / .25); }
.badge.rec { background: hsl(var(--warning) / .12); color: hsl(38 92% 42%); border-color: hsl(var(--warning) / .25); }
.badge.paid { background: hsl(var(--warning) / .12); color: hsl(38 92% 42%); border-color: hsl(var(--warning) / .25); }
.badge.free { background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); }
.badge.banned { background: hsl(var(--destructive) / .12); color: hsl(var(--destructive)); border-color: hsl(var(--destructive) / .25); }
/* 后台赠送的付费来源。刻意不用 .paid 那个暖色——它不是收入，
   在一排付费徽章里要能一眼挑出来。 */
.badge.grant { background: hsl(var(--info) / .12); color: hsl(var(--info)); border-color: hsl(var(--info) / .25); }
.badge.ghost { background: hsl(var(--destructive) / .12); color: hsl(var(--destructive)); border-color: hsl(var(--destructive) / .25); }
/* AI 抓取器分类：AI 引擎用主强调色（最该先看），搜索索引次之，训练语料弱化 */
.badge.ai { background: hsl(var(--success) / .12); color: hsl(var(--success)); border-color: hsl(var(--success) / .25); }
.badge.search { background: hsl(var(--info) / .12); color: hsl(var(--info)); border-color: hsl(var(--info) / .25); }
.badge.train { background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); border-color: hsl(var(--border)); }

/* ---------- 状态链 ---------- */
.chain { display: inline-flex; align-items: center; white-space: nowrap; }
.chain .step { display: inline-flex; align-items: center; gap: 2px; padding: 2px 7px; white-space: nowrap; border-radius: 9999px; font-size: 11px; font-weight: 500; }
.chain .step.ok { background: hsl(var(--success) / .12); color: hsl(142 71% 35%); }
.chain .step.fail { background: hsl(var(--destructive) / .12); color: hsl(var(--destructive)); }
.chain .step.pending { background: hsl(var(--warning) / .14); color: hsl(38 92% 40%); }
.chain .step.none { background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); }
.chain .sep { width: 8px; height: 1px; background: hsl(var(--border)); }

/* ---------- 弹窗 ---------- */
.overlay { position: fixed; inset: 0; background: rgba(0,0,0,.5); display: flex; align-items: center; justify-content: center; padding: 24px; z-index: 100; }
.modal { background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: var(--radius); width: 100%; max-width: 760px; max-height: 86vh; display: flex; flex-direction: column; box-shadow: 0 20px 50px rgba(0,0,0,.25); }
.modal-head { padding: 20px 24px; border-bottom: 1px solid hsl(var(--border)); display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; }
.modal-head h3 { font-size: 17px; font-weight: 600; letter-spacing: -.01em; }
.modal-head .sub { font-size: 12.5px; color: hsl(var(--muted-foreground)); margin-top: 5px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.modal-body { padding: 20px 24px; overflow-y: auto; }
.modal-body h4 { font-size: 13px; font-weight: 600; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
.modal-body section { margin-bottom: 22px; }
.modal-body section:last-child { margin-bottom: 0; }
.prose { font-size: 13.5px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; color: hsl(var(--foreground)); background: hsl(var(--muted) / .5); border-radius: calc(var(--radius) - 2px); padding: 14px 16px; }
.transcript { max-height: 320px; overflow-y: auto; border: 1px solid hsl(var(--border)); border-radius: calc(var(--radius) - 2px); }
.utt { display: flex; gap: 12px; padding: 9px 14px; border-bottom: 1px solid hsl(var(--border)); font-size: 13px; }
.utt:last-child { border-bottom: 0; }
.utt .ts { color: hsl(var(--muted-foreground)); font-size: 11.5px; font-variant-numeric: tabular-nums; flex-shrink: 0; padding-top: 2px; }

/* ---------- 用户详情 ---------- */
.profile { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card)); padding: 20px; margin-bottom: 20px; }
.avatar { width: 46px; height: 46px; border-radius: 50%; background: hsl(var(--muted)); display: grid; place-items: center; font-weight: 600; font-size: 16px; color: hsl(var(--muted-foreground)); }
.profile .who b { font-size: 16px; font-weight: 600; }
.profile .who div { font-size: 12px; color: hsl(var(--muted-foreground)); margin-top: 2px; }
.profile .stats { display: flex; gap: 26px; margin-left: auto; flex-wrap: wrap; }
.profile .stats .s .k { font-size: 11.5px; color: hsl(var(--muted-foreground)); }
.profile .stats .s .v { font-size: 15px; font-weight: 600; margin-top: 2px; }
.profile .stats .s .d { font-size: 11px; margin-top: 2px; }

/* .profile 原本是「头像 + 名字 + 右侧统计」的三段式，.stats 靠 margin-left:auto
   贴右。任务详情页没有左边那两段，统计就会整块飘到右边、左侧空一大片。
   .bare 去掉那个 auto，让统计从左边起排。 */
.profile.bare { align-items: stretch; }
.profile.bare .stats { margin-left: 0; gap: 30px; }
.ops { display: flex; gap: 6px; flex-wrap: wrap; }
/* 旋钮的名字不能被挤断行：「思考深度」折成两行会把整排按钮撑歪 */
.ops .chk { white-space: nowrap; }

.pager { display: flex; gap: 8px; align-items: center; justify-content: flex-end; padding: 12px 14px; border-top: 1px solid hsl(var(--border)); }
.pager .info { margin-right: auto; font-size: 12.5px; color: hsl(var(--muted-foreground)); }

@media (max-width: 900px) {
  .side { transform: translateX(-100%); }
  .main { margin-left: 0; }
}

/* 权益单元格：一列里叠「档位徽章 / 到期 / 用量」三行。
   两种权益（会议转录包、LLM 档位）各占一列，分开呈现——它们是分开售卖的，
   合成一列会让人以为买一个就带另一个。 */
.ent { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.ent .sub { font-size: 11.5px; line-height: 1.45; color: hsl(var(--muted-foreground)); white-space: nowrap; font-variant-numeric: tabular-nums; }
.ent .sub.muted { opacity: .65; }

/* 赠送会员对话框。用原生 dialog 会带来一堆浏览器默认样式要覆盖，
   这里就是一个遮罩 + 面板，够用且可控。 */
.modal-mask { position: fixed; inset: 0; background: rgb(0 0 0 / .45); display: grid; place-items: center; z-index: 200; padding: 20px; }
.modal { background: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px solid hsl(var(--border)); border-radius: 12px; padding: 22px 24px; width: min(420px, 100%); box-shadow: 0 18px 48px rgb(0 0 0 / .28); }
.modal h3 { margin: 0; font-size: 17px; font-weight: 600; }
.modal .fld { display: flex; flex-direction: column; gap: 6px; margin-top: 14px; }
.modal .fld > span { font-size: 12.5px; color: hsl(var(--muted-foreground)); }
.modal .fld select, .modal .fld input { width: 100%; padding: 9px 11px; border: 1px solid hsl(var(--border)); border-radius: 8px; background: hsl(var(--background)); color: inherit; font-size: 14px; line-height: 1.5; }
/* 原生 <select> 在 macOS 上不按我们给的 padding/line-height 排版：它内部有一个
   UA 自己的文本盒，高度由控件样式决定，中文字形比拉丁字母高，于是「会议包」
   这几个字上下被裁掉一截——运营看到的就是「文字显示不全」。
   （只加 line-height 没用，那个内部盒子根本不听。）
   appearance:none 关掉原生外观，盒模型才真正回到 CSS 手里；代价是箭头也一起
   没了，所以用一个内联 SVG 背景补回来，并留出 padding-right 免得长标签压住它。
   箭头用 currentColor 画不了（背景图取不到 currentColor），这里跟随主题各写一份。 */
.modal .fld select {
  -webkit-appearance: none; appearance: none;
  /* 纵向 padding 必须归零。
     根因是两条规则打架：基础控件那条（.input, select…）给的是
     `height: 36px; padding: 0 12px` —— 固定高度、不留纵向内边距；上面那条
     .modal .fld 又把 padding 覆盖成 9px 11px，高度却仍是 36px。于是内容盒被
     要求 21px 行高 + 18px 内边距 = 39px，塞不进 36px，WebKit 解决冲突的办法
     是把文字整体往下推——运营看到的就是「会议包那行底部被遮挡」。
     2026-08-26 用 WebKit 在 1.75× 缩放下实测（与报告截图的几何一致）：
       现状  盒高 65px 设备像素，文字偏下 2.0px
       归零  盒高 65px 不变，偏下 −0.5px，即居中
     只改 select：同一条 padding 也压在旁边的 <input type=date> 上，但对它做
     同样处理会从 +2.0 变成 +3.0（日期输入框的子字段自有一套排版），实测更差。 */
  padding-top: 0;
  padding-bottom: 0;
  padding-right: 32px;
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 11px 8px;
}
.modal-ops { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; }

/* 数字列右对齐 + 等宽数字。
   .num 在 app.js 里早已用了 30 处，但样式表一直没有对应规则——所以既有表格的
   数字列其实都是左对齐、非等宽的，一眼看不出量级差异。补这条同时修好旧页面。 */
.tbl th.num, .tbl td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 「按模型」标题里那个天数下拉，跟标题同排 */
/* 面板标题里的小选择器。
   width/height 必须显式给 auto：基础控件那条规则（.input, input…, select）
   给的是 `height: 36px; width: 100%`，只改字号和 padding 压不住它——选择器会
   占满整行、掉到标题下面去，看着像个独立的筛选栏而不是标题的一部分。 */
.panel h3 select.sm { margin-left: 10px; width: auto; height: auto; font-size: 12px; padding: 2px 6px; vertical-align: middle; }

/* 复选 + 标签同行。整个标签可点，不必对准 13px 的小方块。 */
.chk { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.chk input[type=checkbox] { width: 15px; height: 15px; accent-color: hsl(var(--foreground)); cursor: pointer; }
/* 表格里的输入框不该撑满整列 —— 门槛只有两位数，占比是 0~1。 */
.tbl input[type=number], .tbl select { width: auto; min-width: 78px; height: 30px; }
/* 操作行里的数字框（如轮数上限）不该撑满整行——通用规则是 width:100%。 */
.ops input[type=number] { width: 84px; }

/* 详情页的标题限成一行。
 *
 * 提示词上限 2026-08-26 提到 10000 字之后，一份上万字的会议记录会被整段渲染
 * 成 <h2>，一屏全是大字，下面的失败原因、模型调用、步骤流全被挤到几屏之外。
 * 后端已改成只返回前 80 字当标题，这里再兜一层：即便将来某个接口又回传了
 * 全文，版面也不会塌。完整原文在下面「完整提示词」那一块，title 属性里也有。 */
.page-head h2.one-line {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 提示词原文：保留换行、长行自动折，别把面板撑出横向滚动。 */
pre.wrap-pre {
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  font-size: 12.5px;
  line-height: 1.7;
  max-height: 42vh;
  overflow-y: auto;
}

/* 任务详情的用户上传附件。
   图给固定高的方框、object-fit:contain——附件的长宽比什么都有，裁掉一半
   反而看不出那是什么图；这里的用途是"认出他传了什么"，不是看图。 */
.uploads { display: flex; flex-wrap: wrap; gap: 12px; }
.uploads .up {
  display: block; width: 132px; padding: 8px; text-decoration: none; color: inherit;
  border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card));
}
.uploads a.up:hover { border-color: hsl(var(--muted-foreground)); }
.uploads .up img {
  width: 100%; height: 96px; object-fit: contain; display: block;
  background: hsl(var(--muted)); border-radius: 4px; margin-bottom: 6px;
}
.uploads .up .ext {
  height: 96px; display: flex; align-items: center; justify-content: center;
  background: hsl(var(--muted)); border-radius: 4px; margin-bottom: 6px;
  font-size: 13px; font-weight: 600; letter-spacing: .5px; color: hsl(var(--muted-foreground));
}
/* 文件名一行截断：附件名常常很长，撑开格子会让整排对不齐。全名在 title 里。 */
.uploads .up .nm { font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.uploads .up .sz { font-size: 11px; margin-top: 2px; }
/* 任务列表里的附件标记。跟在标题后面，不占单独一列——它只是个提示，
   为它加一列会把本来就有 14 列的表挤得更窄。 */
.tbl .clip { font-size: 11px; white-space: nowrap; }

/* ---------- 技能与品类 ---------- */
/* 两段之间要有呼吸：它们是两张互不相干的表，挨在一起会读成一张。 */
#skz .card + .card { margin-top: 20px; }
#skz .card-head { padding: 16px 20px 12px; display: flex; flex-direction: column; gap: 4px; }
#skz .card-head b { font-size: 14px; }
#skz .card-head .muted { font-size: 12px; line-height: 1.5; }
#skz .card-foot { padding: 14px 20px; border-top: 1px solid hsl(var(--border)); display: flex; align-items: center; gap: 12px; }
#skz .card-foot .muted, #skz .card-foot .err { font-size: 12px; }

/* 四语言一格。竖着排而不是四列：横排会把表撑到要横向滚，而语言标签本身
   （繁/简/EN/日）比输入框窄得多，竖排反而更好扫。 */
.lang-cell { min-width: 260px; }
.lang-in { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.lang-in:last-child { margin-bottom: 0; }
.lang-in > span { width: 22px; flex: none; font-size: 11px; color: hsl(var(--muted-foreground)); }
.lang-in > input, .lang-in > textarea { flex: 1; min-width: 0; }
.lang-in.tall { align-items: flex-start; margin-top: 6px; }
.lang-in.tall > span { padding-top: 6px; }
.lang-in textarea { font-family: inherit; line-height: 1.5; padding: 6px 8px; resize: vertical; }

/* 代码里已经没有的技能：显示但压暗。藏起来的话运营会以为自己配过的东西
   凭空消失了，而那一行还带着四语言文案。 */
.tbl tr.dim td { opacity: .5; }
.tbl tr.dim code { text-decoration: line-through; }

#skz details summary { cursor: pointer; font-size: 12px; }
#skz details[open] summary { margin-bottom: 4px; }

/* 邀请码新建表单。横向流式排布而不是做成表格：字段只有八个，
   摊成表格会让每一格都很窄，而这里最长的「投放位」需要写得下
   hku-main-library-b1 这种。 */
.iv-new {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px 14px;
  padding: 14px 16px;
}
.iv-new label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: hsl(var(--muted-foreground));
}
.iv-new .input { width: 150px; }
/* 码本身要显眼一点：它是这个表单里唯一建好就改不了的字段。 */
.iv-new #iv-n-display { width: 120px; font-weight: 600; letter-spacing: .06em; }
.iv-new #iv-n-credits, .iv-new #iv-n-maxbind { width: 88px; }

/* 邀请码表格。来源三层竖排进一格——摊成三列会把这张表撑到要横向滚，
   而「渠道/活动/投放位」的标签本身比输入框窄得多。 */
/* 210 是量出来的：再宽这张表在 1500 视口下就装不下要横滚了（其余列合计
   约 1045px）。「2026-fall-orientation」这种最长的活动名仍然放得下。 */
.iv-src { min-width: 210px; }
.iv-in { display: flex; align-items: center; gap: 6px; margin: 2px 0; }
.iv-in > span {
  width: 40px; flex: none; font-size: 11px; text-align: right;
  color: hsl(var(--muted-foreground));
}
.iv-in .input { flex: 1; min-width: 0; }
/* 码是这一列的主角，比下面那个可改的「海报写法」重一档。 */
.iv-code { font-weight: 600; letter-spacing: .06em; margin-right: 6px; }
.tbl .f-display { width: 104px; margin-top: 4px; }
.tbl .f-credits, .tbl .f-maxbind { width: 62px; }
.tbl .f-validto { width: 140px; }
/* 状态与操作合成一列：两者都是「对这一行做什么」，各占一列会让本来就
   紧张的横向空间再少一截。 */
.iv-act { white-space: nowrap; }
.iv-act .btn { margin-left: 8px; }

/* ---------- 用例墙 ---------- */
/* 一条案例一张卡，不用表格：正文是一大段 markdown，塞进单元格没法看。 */
#swz .sw-card + .sw-card { margin-top: 16px; }
#swz .card-head { padding: 14px 18px 10px; display: flex; flex-direction: column; gap: 4px; }
#swz .card-head b { font-size: 14px; }
#swz .card-head .muted { font-size: 12px; line-height: 1.7; }
#swz .card-foot {
  padding: 12px 18px; border-top: 1px solid hsl(var(--border));
  display: flex; align-items: center; gap: 10px;
}
#swz .card-foot .muted, #swz .card-foot .err { font-size: 12px; }

.sw-body { display: flex; gap: 18px; padding: 4px 18px 14px; align-items: flex-start; }
@media (max-width: 900px) { .sw-body { flex-direction: column; } }

.sw-cover { flex: none; width: 190px; display: flex; flex-direction: column; gap: 8px; }
/* 与首页卡片同一个比例：后台看到的构图就是访客看到的构图。 */
.sw-thumb {
  aspect-ratio: 4 / 3; border: 1px solid hsl(var(--border));
  background: hsl(var(--muted)); display: grid; place-items: center; overflow: hidden;
}
.sw-thumb img { width: 100%; height: 100%; object-fit: cover; }
.sw-auto { font-size: 11px; text-align: center; line-height: 1.6; padding: 0 8px; }
.sw-up { text-align: center; cursor: pointer; }

.sw-fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.sw-row { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: 4px; }
.sw-sort { width: 150px; margin-bottom: 0; }

/* 用例墙的删除按钮进入「确定删除？」那一步时的样子。
   做成实心红：这一步已经不是"可以看看"，下一次点击就真删了。 */
.sw-armed {
  background: hsl(var(--destructive));
  border-color: hsl(var(--destructive));
  color: #fff;
}

/* 用例卡底部现在是两行：上「类别 / 语言」，下「等级 / 上架 / 上首页 / 排序」。
   两行之间给一点间距，否则两排控件贴在一起看不出是两组。 */
#swz .sw-row + .sw-row { margin-top: 10px; }

/* 类别输入框。不给它 flex-grow——它是一个短词（「游戏」「题目讲解」），
   撑满会让右边的语言勾选被挤到看不见。 */
.sw-cat { flex: none; }
.sw-cat .input { width: 160px; }

/* 那排语言勾选。标签与勾选之间给一点呼吸。 */
.sw-langs { display: inline-flex; align-items: center; gap: 10px; }
.sw-langs-label { font-size: 12px; }

/* 从统计表跳过来时高亮那张卡两秒。
   用描边而不是改背景：卡片本来就有底色，换底色在深色主题下几乎看不出来，
   而一圈亮边在两种主题下都成立。 */
#swz .sw-card.sw-hit {
  outline: 2px solid hsl(var(--primary));
  outline-offset: 2px;
}
/* 统计表最后一列是「编辑」按钮，不该被内容撑开。 */
.sw-rank td:last-child { width: 1%; white-space: nowrap; }

/* ---------- App 冷启动配置 ----------
   四段内容共用一套卡片。与用例墙、技能页一样把 card-head / card-foot 的排版
   放在页面作用域里（#acz），基础的 .card-head 只有 padding，没有横向排布。 */
#acz .ac-sec { margin-bottom: 30px; }
#acz .ac-sec-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
#acz .ac-sec-head h3 { font-size: 15px; font-weight: 600; }
#acz .ac-sec-head .muted { font-size: 12px; line-height: 1.6; }
#acz .ac-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 10px; }
#acz .card-head { padding: 14px 18px 0; display: flex; flex-direction: column; gap: 4px; }
#acz .card-head b { font-size: 14px; }
#acz .card-head .muted { font-size: 12px; line-height: 1.6; }
#acz .ac-body { padding: 12px 18px 4px; display: flex; flex-direction: column; gap: 6px; }
#acz .card-foot { padding: 12px 18px; border-top: 1px solid hsl(var(--border)); display: flex; align-items: center; gap: 10px; }
#acz .card-foot .muted, #acz .card-foot .err { font-size: 12px; }
#acz .ac-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin: 2px 0; }

/* 标签比用例墙那边长（"下载地址"/"更新说明"），22px 放不下 */
#acz .lang-in > span { width: 60px; }
#acz .ac-row .lang-in { margin-bottom: 0; }
#acz .ac-row .lang-in > input, #acz .ac-row .lang-in > select { flex: none; width: 150px; }
#acz .ac-row .lang-in .f-build { width: 84px; }
/* 时间框比日期框窄。与日期分开是刻意的——datetime-local 在时间那半为空时
   整个 .value 就是空串，运营填了日期也存不进去，而且毫无提示。 */
#acz .ac-row .lang-in > input.ac-time { width: 104px; }
#acz .sm-hint { font-size: 11.5px; }
#acz .ac-plats { display: inline-flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* 语言页签。四种语言各占一块，不并排四列——公告有三个字段，
   并排就是十二个输入框挤在一张卡里。 */
.ac-langs { border: 1px solid hsl(var(--border)); border-radius: var(--radius); margin-top: 6px; }
.ac-tabs { display: flex; gap: 2px; padding: 6px 6px 0; border-bottom: 1px solid hsl(var(--border)); flex-wrap: wrap; }
.ac-tab {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 12px; border: 0; background: transparent; cursor: pointer;
  font-size: 12.5px; color: hsl(var(--muted-foreground));
  border-radius: var(--radius) var(--radius) 0 0; border-bottom: 2px solid transparent;
}
.ac-tab:hover { background: hsl(var(--accent)); color: hsl(var(--foreground)); }
.ac-tab.on { color: hsl(var(--foreground)); font-weight: 500; border-bottom-color: hsl(var(--foreground)); }
.ac-tab em { font-style: normal; color: hsl(var(--warning)); font-weight: 700; }
/* 「这个语言填了东西」。不然要看某条内容缺哪几种语言，只能一个个点开 */
.ac-dot { width: 5px; height: 5px; border-radius: 50%; background: transparent; }
.ac-dot.on { background: hsl(var(--success)); }
.ac-pane { padding: 10px 12px; }
.ac-pane .lang-in:last-child { margin-bottom: 0; }

/* 新功能介绍的条目 */
#acz .ac-items-head { display: flex; align-items: baseline; gap: 10px; margin-top: 12px; }
#acz .ac-items-head b { font-size: 13px; }
#acz .ac-items-head .muted { font-size: 11.5px; }
#acz .ac-items { display: flex; flex-direction: column; gap: 8px; margin: 6px 0 8px; }
#acz .ac-item { position: relative; border: 1px dashed hsl(var(--border)); border-radius: var(--radius); padding: 10px 40px 10px 12px; }
#acz .ac-item-del { position: absolute; top: 8px; right: 8px; }

/* 开关表 */
#acz .ac-ftbl td { vertical-align: middle; }
#acz .ac-ftbl .input { width: 100%; min-width: 0; }
#acz .ac-ftbl select.f-type { width: 100%; }
#acz .ac-plat-cell { display: table-cell; }
#acz .ac-plat-cell .chk { margin-right: 10px; }
/* flex 放在 td 里面的 div 上，不放在 td 自己身上：给 <td> 设 display:flex
   会把它从表格布局里摘出去，那一列的宽度和边框就跟着乱。 */
#acz .ac-frow-act { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
#acz .ac-frow-act .muted, #acz .ac-frow-act .err { font-size: 11.5px; }

/* 删除的第二段。与用例墙的 .sw-armed 同一个意思：一个一直亮着的危险按钮
   迟早会被误点，所以第一下只是"举起来"，五秒不确认就自己放下。 */
#acz .ac-armed { background: hsl(var(--destructive) / .12); border-color: hsl(var(--destructive) / .4); }

/* 顶部那两条"不在这一页"的说明。用 warning 的底色而不是普通卡片：
   它说的是两件配错了会伤到人的事，不是补充说明。 */
.ac-warn { border-color: hsl(var(--warning) / .35); background: hsl(var(--warning) / .06); margin-bottom: 24px; }
.ac-warn .card-head { padding: 14px 18px 0; }
.ac-warn-body { padding: 6px 18px 16px; display: flex; flex-direction: column; gap: 8px; }
.ac-warn-body p { font-size: 12.5px; line-height: 1.7; color: hsl(var(--muted-foreground)); }
.ac-warn-body b { color: hsl(var(--foreground)); }

/* ---------- 提示词展厅 ---------- */
.pg-bulk { margin-top: -6px; }
.pg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 14px; }
.pg-card { position: relative; display: flex; flex-direction: column; border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card)); overflow: hidden; }
/* 下架的卡片压暗，但不藏：后台就是来看「我配过什么」的。 */
.pg-card.off .pg-thumb img { opacity: .45; filter: grayscale(.6); }
.pg-thumb { display: grid; place-items: center; aspect-ratio: 1 / 1; padding: 0; border: 0; background: hsl(var(--muted)); cursor: pointer; }
/* contain 不 cover：后台要看到整张图，裁掉一截的话看不出这张适不适合上架。 */
.pg-thumb img { width: 100%; height: 100%; object-fit: contain; }
.pg-pick { position: absolute; top: 8px; left: 8px; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 6px; background: hsl(var(--card) / .9); box-shadow: 0 1px 3px rgb(0 0 0 / .2); cursor: pointer; }
.pg-meta { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px 12px; min-width: 0; }
.pg-title, .pg-en { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pg-title { font-size: 13.5px; }
.pg-en { font-size: 12px; }
.pg-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.pg-flags { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin-top: 4px; font-size: 13px; }
.pg-flags .pg-edit { margin-left: auto; }

.modal.pg-modal { width: min(1040px, 100%); max-height: 92vh; display: flex; flex-direction: column; padding: 0; }
.pg-modal-head { display: flex; justify-content: space-between; align-items: center; padding: 16px 22px; border-bottom: 1px solid hsl(var(--border)); }
.pg-modal-head h3 { display: flex; align-items: center; gap: 8px; }
.pg-modal-body { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 22px; padding: 18px 22px; overflow-y: auto; }
.pg-img-col { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.pg-img { display: grid; place-items: center; width: 100%; aspect-ratio: 1 / 1; border-radius: 8px; background: hsl(var(--muted)); overflow: hidden; }
.pg-img img { width: 100%; height: 100%; object-fit: contain; }
.pg-hint { font-size: 12px; }
.pg-form .fld:first-of-type { margin-top: 0; }
.pg-form .fld > span { display: flex; justify-content: space-between; gap: 8px; }
.pg-form .fld em { font-style: normal; }
.pg-form textarea.input { width: 100%; min-height: 220px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; line-height: 1.55; padding: 9px 11px; }
.pg-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.pg-checks { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 16px; }
.pg-modal .modal-ops { align-items: center; margin: 0; padding: 14px 22px; border-top: 1px solid hsl(var(--border)); }
.pg-modal .modal-ops .pg-emsg { margin-right: auto; font-size: 13px; }
@media (max-width: 760px) {
  .pg-modal-body { grid-template-columns: 1fr; }
  .pg-2col { grid-template-columns: 1fr; }
}
