/* croprix 操作台样式。一份手写 CSS，不引任何 CDN、字体与框架（内网可能不通外网）。
 *
 * 分三层，改样式前先认层：
 *   1. 原色（--c-*）——浅色阶，只在本文件顶部出现，别处一律不直接引用；
 *   2. 语义 token（--bg / --ink / --primary / --ok …）——组件只认这层，
 *      每个 token 一行 light-dark(浅, 暗)，暗色取值也只写这一次；
 *   3. 组件规则——只用第 2 层。
 *
 * 同一个东西只描一遍：输入控件的底盘在「表单」一节，各处只补自己的宽度与内边距；
 * 主题切换只改 color-scheme，不重写 token。
 *
 * 布局是「品牌侧栏 + 内容列」：侧栏在两种主题下都是深 navy（--sb-* 一族），
 * 品牌绿只做点睛（active 指示条、logo、成功态），蓝负责一切可交互。
 */

/* ================================================================ 1. 原色 */
:root {
  /* 品牌两色，取自 logo 位图实测值 */
  --c-navy-900: #00223a;
  --c-navy-800: #002b49;
  --c-navy-700: #003459;        /* logo 蓝本体 */
  --c-navy-600: #0a4670;
  --c-navy-500: #175d8e;
  --c-navy-200: #b6cddd;
  --c-navy-100: #dbe8f1;
  --c-navy-50:  #eef4f9;

  --c-green-700: #3f7a00;
  --c-green-600: #4a8f00;
  --c-green-500: #55a400;       /* logo 绿本体 */
  --c-green-200: #c3e39a;
  --c-green-50:  #eef7e4;

  /* 中性阶：偏冷，配蓝不打架 */
  --c-gray-950: #0b1219;
  --c-gray-900: #131c26;
  --c-gray-800: #1c2733;
  --c-gray-700: #2b3846;
  --c-gray-600: #4a5866;
  --c-gray-500: #6b7885;
  --c-gray-400: #98a3ae;
  --c-gray-300: #c7ced5;
  --c-gray-200: #e0e5ea;
  --c-gray-100: #eef1f4;
  --c-gray-75:  #f2f4f7;
  --c-gray-50:  #f6f8fa;
  --c-white:    #ffffff;

  --c-red-600:  #b3261e;
  --c-red-500:  #d3453c;
  --c-red-200:  #f0bcb8;
  --c-red-50:   #fdefee;

  --c-amber-700: #8a5a00;
  --c-amber-600: #9a6400;
  --c-amber-200: #ecd39f;
  --c-amber-50:  #fdf6e8;
}

/* ====================================== 2. 语义 token（浅色 / 暗色成对写） */
/* 每个 token 一行 `light-dark(浅, 暗)`。暗色以前是把整份 token 再抄一遍——跟随系统
   一份、手动切换又一份，共三处；改个颜色漏改一处，两种入口下就不一致。
   现在取值只出现一次，切主题只改 color-scheme，组件规则照旧一行不动。

   暗色那一侧的取值直接写在第二个参数里：它们不成阶（暗底上把浅色阶反着用会发灰
   或刺眼，是单独调的），所以不进第 1 层，但同样只出现这一次。

   `light-dark()` 要 2024 年前后的浏览器；本文件本来就用了 `:has()`，同一档基线。 */
:root {
  /* 主题的唯一开关：light-dark() 认的就是它。 */
  color-scheme: light dark;

  --bg: light-dark(var(--c-gray-75), var(--c-gray-950));
  --surface: light-dark(var(--c-white), var(--c-gray-900));
  --surface-2: light-dark(var(--c-gray-50), var(--c-gray-800));   /* 卡片内的次级底：表头、代码条 */
  --surface-sunken: light-dark(var(--c-gray-100), var(--c-gray-800));

  --ink: light-dark(var(--c-gray-900), #e6ebf0);
  --ink-muted: light-dark(var(--c-gray-500), var(--c-gray-400));
  --ink-subtle: light-dark(var(--c-gray-400), var(--c-gray-500));

  --line: light-dark(var(--c-gray-200), #26323f);
  --line-strong: light-dark(var(--c-gray-300), #35434f);

  /* 暗底上 logo 蓝对比度不足，提亮 */
  --primary: light-dark(var(--c-navy-700), #4d90c4);
  --primary-hover: light-dark(var(--c-navy-600), #63a3d4);
  --primary-ink: light-dark(var(--c-white), var(--c-gray-950));
  --primary-weak: light-dark(var(--c-navy-50), #122938);
  --primary-weak-line: light-dark(var(--c-navy-200), #1d3f56);

  /* 品牌绿：active 指示、点睛，不做大面积底色 */
  --accent: light-dark(var(--c-green-500), #6cbc17);

  --ok: light-dark(var(--c-green-700), #7cc02a);
  --ok-weak: light-dark(var(--c-green-50), #16240c);
  --ok-line: light-dark(var(--c-green-200), #2c4715);
  --bad: light-dark(var(--c-red-600), #e8756c);
  --bad-weak: light-dark(var(--c-red-50), #2a1412);
  --bad-line: light-dark(var(--c-red-200), #4d211e);
  --warn: light-dark(var(--c-amber-700), #d9a441);
  --warn-weak: light-dark(var(--c-amber-50), #261c09);
  --warn-line: light-dark(var(--c-amber-200), #453313);
  --run: light-dark(var(--c-navy-600), #63a3d4);
  --run-weak: light-dark(var(--c-navy-50), #122938);
  --run-line: light-dark(var(--c-navy-200), #1d3f56);

  /* 侧栏一族：两种主题下都是深 navy 底，暗色只把它压得更沉 */
  --sb-mid-at: 62%;                     /* 中间色标的位置——不是颜色，见下面那条 */
  --sb-bg: linear-gradient(180deg,
    light-dark(#013055, #012743) 0%,
    light-dark(var(--c-navy-900), #001b2f) var(--sb-mid-at),
    light-dark(#001b2f, #001222) 100%);
  --sb-ink: rgba(255, 255, 255, 0.68);
  --sb-ink-strong: #ffffff;
  --sb-line: light-dark(rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.07));
  --sb-hover: rgba(255, 255, 255, 0.06);
  --sb-active: rgba(255, 255, 255, 0.1);
  --sb-group: rgba(255, 255, 255, 0.38);

  /* 日志/JSON 的暗底在两种模式下都是暗的——终端观感是刻意保留的 */
  --term-bg: #0d1520;
  --term-ink: #cfd8e3;

  /* 间距刻度，4px 基。组件只用这些值，不写裸数字 */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;

  --r-sm: 6px; --r-md: 8px; --r-lg: 12px; --r-full: 999px;

  /* 阴影分级：静态卡片只配 sh-1，浮层才用 sh-3。
     暗色那边只有一层，所以第二层在暗色下取 transparent。 */
  --sh-1: 0 1px 2px light-dark(rgba(16, 24, 40, 0.04), rgba(0, 0, 0, 0.4)),
          0 1px 3px light-dark(rgba(16, 24, 40, 0.05), transparent);
  --sh-2: 0 4px 12px light-dark(rgba(16, 24, 40, 0.08), rgba(0, 0, 0, 0.45)),
          0 1px 3px light-dark(rgba(16, 24, 40, 0.05), transparent);
  --sh-3: 0 12px 32px light-dark(rgba(16, 24, 40, 0.18), rgba(0, 0, 0, 0.6));

  --ring: 0 0 0 3px light-dark(rgba(0, 52, 89, 0.16), rgba(77, 144, 196, 0.3));
  --ring-bad: 0 0 0 3px rgba(179, 38, 30, 0.14);

  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  --fs-xs: 12px; --fs-sm: 13px; --fs-base: 14px;
  --fs-md: 15px; --fs-lg: 16px; --fs-xl: 24px;

  --sidebar-w: 232px;
  --page-w: 1140px;
  --tap: 34px;                          /* 控件最小高度，保证点得中 */
}

/* 手动切主题（侧栏开关写 data-theme）。整份 token 不用重来，钉死 color-scheme 即可。 */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* 唯一还要写两个入口的一条：色标位置不是颜色，light-dark() 只吃颜色。 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --sb-mid-at: 58%; }
}
:root[data-theme="dark"] { --sb-mid-at: 58%; }

/* ================================================================ 3. 基础 */
* { box-sizing: border-box; }

/* [hidden] 的浏览器默认值是 display:none，但任何带 display 的类规则都能压过它
   （.banner{display:flex} 就干过这事，害得空的错误条常驻在页顶）。这里钉死。 */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: var(--fs-base)/1.65 -apple-system, BlinkMacSystemFont, "PingFang SC",
        "Microsoft YaHei", "Noto Sans CJK SC", "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  display: flex;
  min-height: 100vh;
}

code, pre, .mono, .num, .card-tag { font-family: var(--font-mono); }
/* 等宽数字对齐：表格内外都要，所以挂在 .num 上而不是表格规则里 */
.num { font-variant-numeric: tabular-nums; }
code {
  font-size: 0.9em; background: var(--surface-sunken); color: var(--ink);
  padding: 1px 5px; border-radius: var(--r-sm);
}
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

/* 键盘可达性：只给键盘焦点画环，鼠标点击不画（:focus-visible 的意义所在） */
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
a:focus-visible, .btn:focus-visible { outline-offset: 3px; }
.nav-item:focus-visible { outline-color: var(--sb-ink-strong); outline-offset: 0; }

h1, h2, h3 { line-height: 1.35; }

/* ================================================================ 侧栏 */
.sidebar {
  width: var(--sidebar-w); flex-shrink: 0;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
  display: flex; flex-direction: column;
  background: var(--sb-bg);
  padding: var(--sp-5) var(--sp-3) var(--sp-4);
  scrollbar-width: thin;
}

.brand {
  display: flex; align-items: baseline; gap: var(--sp-2);
  padding: 0 var(--sp-2) var(--sp-5);
  color: var(--sb-ink-strong);
}
.brand:hover { text-decoration: none; }
.brand img { height: 24px; width: auto; display: block; align-self: center; }
.brand .brand-sub {
  color: var(--sb-ink); font-weight: 500; font-size: var(--fs-sm);
  letter-spacing: 0.06em;
}

.nav { display: flex; flex-direction: column; gap: 2px; flex: 1; }
.nav-group {
  margin: var(--sp-5) var(--sp-2) var(--sp-1);
  font-size: 11px; font-weight: 600; letter-spacing: 0.12em;
  color: var(--sb-group); user-select: none;
}
.nav-item {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  padding: 7px var(--sp-2) 7px var(--sp-3);
  border-radius: var(--r-md);
  color: var(--sb-ink); font-size: var(--fs-base); line-height: 1.5;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.nav-item .icon { flex-shrink: 0; opacity: 0.8; }
.nav-item:hover { background: var(--sb-hover); color: var(--sb-ink-strong); text-decoration: none; }
.nav-item:hover .icon { opacity: 1; }
.nav-item.is-active {
  background: var(--sb-active); color: var(--sb-ink-strong); font-weight: 600;
}
.nav-item.is-active .icon { opacity: 1; }
/* active 指示条：品牌绿，贴条目左缘 */
.nav-item.is-active::before {
  content: ""; position: absolute; left: 0; top: 50%; translate: 0 -50%;
  width: 3px; height: 16px; border-radius: 0 2px 2px 0;
  background: var(--accent);
}

.sidebar-foot {
  margin-top: var(--sp-5); padding-top: var(--sp-4);
  border-top: 1px solid var(--sb-line);
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.ext-links { display: flex; flex-direction: column; gap: 2px; font-size: var(--fs-sm); }
.ext-links a {
  display: flex; align-items: center; justify-content: space-between;
  padding: 5px var(--sp-3); border-radius: var(--r-md); color: var(--sb-ink);
}
.ext-links a:hover { color: var(--sb-ink-strong); background: var(--sb-hover); text-decoration: none; }
.ext-links .ext-arrow { color: var(--sb-group); font-size: var(--fs-xs); }
.ext-links a:hover .ext-arrow { color: var(--sb-ink); }

.sidebar-tools {
  display: flex; align-items: center; gap: var(--sp-3); padding: 0 var(--sp-2);
}
.inline { display: inline-flex; gap: var(--sp-2); align-items: center; }
/* 内联小表单（文件页扩展名过滤、审核页标签根）的输入框宽度自定，
   边框/焦点等底盘走「输入控件」那一节，不在这里重描一遍。 */
.inline input[type=text] { width: 260px; max-width: 100%; }
.linkish {
  background: none; border: none; padding: 0; color: var(--sb-ink);
  cursor: pointer; font: inherit; font-size: var(--fs-sm);
}
.linkish:hover { color: var(--sb-ink-strong); text-decoration: underline; }

/* 主题开关：住在深色侧栏上，边框用侧栏一族的线色 */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0;
  border: 1px solid var(--sb-line); border-radius: var(--r-md);
  background: transparent; color: var(--sb-ink); cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.theme-toggle:hover { background: var(--sb-hover); color: var(--sb-ink-strong); }
.theme-toggle svg { width: 15px; height: 15px; }

/* 主题图标的显隐。三种情况都要覆盖：跟随系统、手动 dark、手动 light。 */
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .icon-moon { display: none; }
  :root:not([data-theme="light"]) .icon-sun { display: block; }
}
:root[data-theme="dark"] .icon-moon { display: none; }
:root[data-theme="dark"] .icon-sun { display: block; }

/* ================================================================ 版面 */
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.page {
  width: 100%; max-width: var(--page-w); flex: 1;
  padding: var(--sp-8) var(--sp-10) var(--sp-10);
}
.page-head { margin-bottom: var(--sp-6); }
.page-head h1 {
  font-size: var(--fs-xl); font-weight: 700; letter-spacing: -0.015em;
  margin: 0 0 var(--sp-1);
}
.subtitle { color: var(--ink-muted); margin: 0; font-size: var(--fs-base); }

/* 页面导语：目标/表单页顶部那段 doc。是导语不是内容主体，不占一张卡 */
.page-desc {
  color: var(--ink-muted); max-width: 76ch; line-height: 1.7;
  margin: calc(-1 * var(--sp-3)) 0 var(--sp-5);
}
.page-desc strong { color: var(--ink); }

/* 窄列：表单类页面收进一条舒适的阅读/填写宽度，别让输入框拉满 1140 */
.col-narrow { max-width: 840px; }

.footer {
  max-width: var(--page-w);
  padding: var(--sp-4) var(--sp-10) var(--sp-6);
  color: var(--ink-subtle); font-size: var(--fs-xs);
  display: flex; gap: var(--sp-5); flex-wrap: wrap;
}
.footer code { background: none; color: var(--ink-muted); padding: 0; font-size: inherit; }

/* ================================================================ 卡片 */
.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: var(--sp-5); margin-bottom: var(--sp-4); box-shadow: var(--sh-1);
}
.card-narrow { max-width: 440px; margin-inline: auto; margin-top: 12vh; }
.card h2 { font-size: var(--fs-md); font-weight: 650; margin: 0 0 var(--sp-4); letter-spacing: 0.01em; }
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-3);
}
.card-head h2 { margin: 0; }

.cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(256px, 1fr));
  gap: var(--sp-4); margin-bottom: var(--sp-5);
}
/* 入口卡：整块可点，hover 抬起。图标块是视觉锚点，hover 时反色 */
.card-link {
  display: flex; flex-direction: column; gap: var(--sp-2);
  color: inherit; margin-bottom: 0;
  transition: border-color 0.14s ease, box-shadow 0.14s ease, transform 0.14s ease;
}
.card-link:hover {
  border-color: var(--primary-weak-line); box-shadow: var(--sh-2);
  transform: translateY(-2px); text-decoration: none;
}
.card-link:active { transform: none; }
.card-ico {
  width: 36px; height: 36px; border-radius: 10px; margin-bottom: var(--sp-1);
  display: grid; place-items: center;
  background: var(--primary-weak); color: var(--primary);
  transition: background-color 0.14s ease, color 0.14s ease;
}
.card-link:hover .card-ico { background: var(--primary); color: var(--primary-ink); }
.card-link h2, .card-link h3 {
  margin: 0; font-size: var(--fs-md); font-weight: 650;
  display: flex; align-items: center; gap: var(--sp-2);
}
.card-link p {
  color: var(--ink-muted); margin: 0; font-size: var(--fs-sm); line-height: 1.6;
}
/* 卡片上的说明截三行——整段 doc 会把一排卡片撑成参差不齐的高矮墙。
   全文在目标页顶部，鼠标悬停也有 title 兜底。 */
.card-desc {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  line-clamp: 3; overflow: hidden;
}
/* 卡片里的一行等宽小字（如 `make inspect`），做成标签而不是正文 */
.card-tag {
  display: inline-block; font-size: var(--fs-xs); color: var(--ink-muted);
  background: var(--surface-sunken); border-radius: var(--r-sm); padding: 2px 6px;
  align-self: flex-start;
}

/* 两列并排（首页的最近作业 × 服务状态）。窄屏在文件尾回落单列 */
.cols-2 {
  display: grid; grid-template-columns: 3fr 2fr; gap: var(--sp-4);
  align-items: start;
}
.cols-2 > .card { margin-bottom: 0; }

/* 分组小节：一组入口卡的组标题（运行页）。比卡片标题淡、比正文小，
   拿掉了原来的「卡片套卡片」——组不是卡，是节。 */
.section-label {
  font-size: var(--fs-xs); font-weight: 650; letter-spacing: 0.1em;
  color: var(--ink-muted); margin: var(--sp-6) 0 var(--sp-3);
}
.section-label:first-child { margin-top: 0; }

/* ================================================================ 提示条 */
.banner {
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md);
  margin-bottom: var(--sp-4); font-size: var(--fs-base);
  border: 1px solid; display: flex; gap: var(--sp-2); align-items: flex-start;
}
.banner-ok { background: var(--ok-weak); border-color: var(--ok-line); color: var(--ok); }
.banner-err { background: var(--bad-weak); border-color: var(--bad-line); color: var(--bad); }
.banner-warn { background: var(--warn-weak); border-color: var(--warn-line); color: var(--warn); }

/* 可折叠说明条：文档不该常驻在操作路径上，默认收起，需要时展开 */
.note {
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); margin-bottom: var(--sp-4); font-size: var(--fs-base);
}
.note > summary {
  cursor: pointer; padding: var(--sp-2) var(--sp-4); color: var(--ink-muted);
  list-style: none; display: flex; align-items: center; gap: var(--sp-2);
  border-radius: var(--r-md); font-size: var(--fs-sm);
}
.note > summary::-webkit-details-marker { display: none; }
.note > summary::before {
  content: "›"; display: inline-block; transition: transform 0.15s ease;
  font-size: 15px; line-height: 1; color: var(--ink-subtle);
}
.note[open] > summary::before { transform: rotate(90deg); }
.note[open] > summary { border-bottom: 1px solid var(--line); border-radius: var(--r-md) var(--r-md) 0 0; }
.note > summary:hover { color: var(--ink); background: var(--surface-2); }
.note-body { padding: var(--sp-3) var(--sp-4) var(--sp-4); color: var(--ink-muted); line-height: 1.7; font-size: var(--fs-sm); }
.note-body > :first-child { margin-top: 0; }
.note-body > :last-child { margin-bottom: 0; }
.note-body strong { color: var(--ink); }

/* ================================================================ 表格 */
table.grid { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }
table.grid th, table.grid td {
  text-align: left; padding: 9px var(--sp-3); border-bottom: 1px solid var(--line);
}
table.grid th {
  color: var(--ink-muted); font-weight: 600; font-size: var(--fs-xs);
  letter-spacing: 0.05em; white-space: nowrap;
  border-bottom-color: var(--line-strong);
}
table.grid tbody tr:last-child td { border-bottom: none; }
table.grid tbody tr:hover td { background: var(--surface-2); }
table.grid td.num, table.grid th.num { text-align: right; }

/* 卡片里的表格贴边：行 hover 铺满卡宽，首尾列的内边距顶到卡片内缘 */
.card > table.grid {
  width: calc(100% + 2 * var(--sp-5));
  margin-inline: calc(-1 * var(--sp-5));
}
.card > table.grid th:first-child, .card > table.grid td:first-child { padding-left: var(--sp-5); }
.card > table.grid th:last-child, .card > table.grid td:last-child { padding-right: var(--sp-5); }
.card > table.grid:last-child { margin-bottom: calc(-1 * var(--sp-5)); }
.card > table.grid:last-child tbody tr:last-child td { padding-bottom: var(--sp-4); }
.card > table.grid:last-child tbody tr:last-child td:first-child { border-bottom-left-radius: calc(var(--r-lg) - 1px); }
.card > table.grid:last-child tbody tr:last-child td:last-child { border-bottom-right-radius: calc(var(--r-lg) - 1px); }

/* 列多到放不下时横向滚动这一块，而不是把整页撑宽（话题表有 7 列） */
.table-scroll { overflow-x: auto; }

table.kv { border-collapse: collapse; font-size: var(--fs-base); }
table.kv th {
  text-align: left; color: var(--ink-muted); font-weight: 500;
  padding: var(--sp-1) var(--sp-5) var(--sp-1) 0; vertical-align: top; white-space: nowrap;
}
table.kv td { padding: var(--sp-1) 0; }
/* 多列版：每组 key/value 之间要留出间距，否则上一组的值和下一组的键会粘在一起 */
table.kv-multi td { padding-right: var(--sp-8); }
table.kv-multi td:last-child { padding-right: 0; }
.muted { color: var(--ink-muted); }

/* 空状态：不留白板，给下一步的落点 */
.empty {
  text-align: center; padding: var(--sp-8) var(--sp-5); color: var(--ink-muted);
}
.empty-title { color: var(--ink); font-weight: 600; margin: 0 0 var(--sp-1); }
.empty p { margin: 0 0 var(--sp-4); font-size: var(--fs-base); }

/* ================================================================ 统计 */
/* 统计块不再各自套框——左侧一根语义色竖条 + 大数字，一眼扫过去是仪表盘不是货架 */
.stat-row {
  display: flex; column-gap: var(--sp-8); row-gap: var(--sp-4); flex-wrap: wrap;
}
.stat {
  display: flex; flex-direction: column; gap: 2px;
  padding: 2px 0 2px var(--sp-3); min-width: 84px;
  border-left: 3px solid var(--line-strong);
}
.stat-label { color: var(--ink-muted); font-size: var(--fs-xs); }
.stat-value {
  font-size: var(--fs-xl); font-variant-numeric: tabular-nums; line-height: 1.2;
  font-weight: 650; letter-spacing: -0.01em;
}
.stat.is-zero { border-left-color: var(--line); }
.stat.is-zero .stat-value { color: var(--ink-subtle); font-weight: 500; }
.stat-ok { border-left-color: var(--ok); }
.stat-ok .stat-value { color: var(--ok); }
.stat-bad { border-left-color: var(--bad); }
.stat-bad .stat-value { color: var(--bad); }
.stat-run { border-left-color: var(--run); }
.stat-run .stat-value { color: var(--run); }
/* 值为 0 的语义色块同样让位：竖条与数字一起回落中性 */
.stat-ok.is-zero, .stat-bad.is-zero, .stat-run.is-zero { border-left-color: var(--line); }
.stat-ok.is-zero .stat-value, .stat-bad.is-zero .stat-value, .stat-run.is-zero .stat-value {
  color: var(--ink-subtle);
}

/* ================================================================ 徽章 */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: var(--r-full);
  font-size: var(--fs-xs); font-weight: 500; border: 1px solid; white-space: nowrap;
}
.badge::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor;
}
.badge-ok { color: var(--ok); border-color: var(--ok-line); background: var(--ok-weak); }
.badge-bad { color: var(--bad); border-color: var(--bad-line); background: var(--bad-weak); }
.badge-running { color: var(--run); border-color: var(--run-line); background: var(--run-weak); }
.badge-queued { color: var(--warn); border-color: var(--warn-line); background: var(--warn-weak); }
.badge-muted { color: var(--ink-muted); border-color: var(--line); background: var(--surface-sunken); }
/* 运行中的点会呼吸；prefers-reduced-motion 下自动静止 */
.badge-running::before { animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* ================================================================ 表单 */
/* 输入控件底盘：边框、圆角、高度、焦点环。全站的输入框/下拉/文本域都长一个样，
   所以只描一遍——此前 .field / .inline / .filters 各抄了一份，改个焦点色要改三处。
   各处的差异（宽度、内边距）在各自的规则里补，别再复制底盘。 */
.field input[type=text], .field input[type=password], .field input[type=number],
.field select, .field textarea,
.inline input[type=text],
.filters select, .filters input[type=text] {
  padding: 6px 10px; min-height: var(--tap);
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--surface); color: var(--ink); font: inherit; font-size: var(--fs-base);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.field input:focus, .field select:focus, .field textarea:focus,
.inline input[type=text]:focus,
.filters select:focus, .filters input:focus {
  outline: none; border-color: var(--primary); box-shadow: var(--ring);
}

.field { margin-bottom: var(--sp-5); }
.field > label {
  display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap;
  font-weight: 600; margin-bottom: var(--sp-2); font-size: var(--fs-base);
}
/* 变量名徽章：告诉你这个字段对应哪个 make 变量 / YAML 键，是本操作台的核心线索 */
.field .varname {
  font-weight: 400; color: var(--ink-muted); font-size: var(--fs-xs);
  background: var(--surface-sunken); padding: 1px 6px; border-radius: var(--r-sm);
}
.field .req { color: var(--bad); font-weight: 400; }

/* 表单里的控件拉满栏宽，纵向也比内联版松一格 */
.field input[type=text], .field input[type=password], .field input[type=number],
.field select, .field textarea {
  width: 100%; padding: 7px 10px;
}
.field input::placeholder, .field textarea::placeholder { color: var(--ink-subtle); }
/* 数字框按内容定宽——fps=15 不该占满整行 */
.field input[type=number] { max-width: 180px; }
.field textarea { resize: vertical; min-height: 76px; line-height: 1.55; }
.field select { max-width: 420px; cursor: pointer; }

.field .help { color: var(--ink-muted); font-size: var(--fs-sm); margin: var(--sp-2) 0 0; line-height: 1.55; }
.field .error { color: var(--bad); font-size: var(--fs-sm); margin: var(--sp-2) 0 0; }
.field.has-error input, .field.has-error textarea, .field.has-error select {
  border-color: var(--bad);
}
.field.has-error input:focus, .field.has-error textarea:focus, .field.has-error select:focus {
  box-shadow: var(--ring-bad);
}

/* 勾选项：整行可点，点中范围比 12px 的方块大得多 */
.checkline {
  display: flex; gap: var(--sp-2); align-items: center; font-weight: 400;
  padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); cursor: pointer; min-height: var(--tap);
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.checkline:hover { border-color: var(--line-strong); background: var(--surface-2); }
.checkline input { width: auto; margin: 0; cursor: pointer; accent-color: var(--primary); }
.checkline:has(input:checked) { border-color: var(--primary-weak-line); background: var(--primary-weak); }

.path-input { display: flex; gap: var(--sp-2); align-items: flex-start; }
.path-input input, .path-input textarea { flex: 1; }

/* 表单分组。fieldset 的默认边框样式各浏览器不一，全部重置后自己画 */
fieldset {
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--sp-4); margin: 0 0 var(--sp-5); min-width: 0;
  background: var(--surface);
}
fieldset > legend {
  padding: 0 var(--sp-2); font-weight: 600; font-size: var(--fs-base); color: var(--ink);
}
fieldset .field:last-child { margin-bottom: 0; }

/* 表单底部操作条：主按钮永远在最左，返回/取消跟在后面，全站一致。
   与 .actions 是两回事——.actions 是页面里的一组横排控件（如作业页的徽章+中止），
   没有上分隔线，不表示「表单到此为止」。 */
.form-actions {
  display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap;
  padding-top: var(--sp-4); border-top: 1px solid var(--line); margin-top: var(--sp-5);
}
.actions { display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap; }

/* 筛选条：一排 label 套 select，label 自己是竖排的小标题 */
.filters { display: flex; gap: var(--sp-4); align-items: flex-end; flex-wrap: wrap; }
.filters > label {
  display: flex; flex-direction: column; gap: var(--sp-1);
  font-size: var(--fs-xs); color: var(--ink-muted); font-weight: 500;
}

/* 可增删的重复行组（转换配置的话题表）。选择器跟着 app.js 认的那套 data-* 走，
   别再用类名——类名和行为对不上时，样式会安静地失效。 */
[data-rows-item] td { vertical-align: top; }
[data-rows-del] { color: var(--ink-subtle); padding-inline: var(--sp-2); }
[data-rows-del]:hover { color: var(--bad); border-color: var(--bad-line); background: var(--bad-weak); }

/* ================================================================ 按钮 */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 6px var(--sp-4); min-height: var(--tap);
  border-radius: var(--r-md); border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink); cursor: pointer;
  font: inherit; font-size: var(--fs-base); font-weight: 500; white-space: nowrap;
  transition: background-color 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.btn:hover { background: var(--surface-2); text-decoration: none; }
.btn:active { transform: translateY(0.5px); }

.btn-primary {
  background: var(--primary); border-color: var(--primary); color: var(--primary-ink);
  box-shadow: var(--sh-1);
}
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }

.btn-danger { color: var(--bad); border-color: var(--bad-line); }
.btn-danger:hover { background: var(--bad-weak); border-color: var(--bad); }

.btn-ghost { color: var(--ink-muted); border-color: var(--line); background: transparent; }
.btn-ghost:hover { color: var(--ink); background: var(--surface-2); }

.btn-sm { padding: 3px var(--sp-3); min-height: 28px; font-size: var(--fs-sm); }

.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.btn[disabled]:active { transform: none; }

/* 提交中：按钮转圈并锁住，用户能确认「点上了」 */
.btn.is-loading { color: transparent !important; position: relative; pointer-events: none; }
.btn.is-loading::after {
  content: ""; position: absolute; width: 14px; height: 14px;
  border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
  color: var(--primary-ink); animation: spin 0.6s linear infinite;
}
.btn-ghost.is-loading::after, .btn:not(.btn-primary).is-loading::after { color: var(--ink-muted); }
@keyframes spin { to { transform: rotate(360deg); } }

/* ================================================================ 日志与原文 */
pre.log, .json-block pre {
  background: var(--term-bg); color: var(--term-ink);
  padding: var(--sp-4); border-radius: var(--r-md); border: 1px solid var(--line);
  overflow: auto; max-height: 60vh; font-size: var(--fs-sm); line-height: 1.5;
  white-space: pre-wrap; word-break: break-all; margin: 0;
}
/* 空日志别塌成一条黑杠：留出「等内容」的场地 */
pre.log { min-height: 96px; }
.json-block { margin-bottom: var(--sp-3); }
.json-block summary {
  cursor: pointer; color: var(--ink-muted); font-size: var(--fs-base);
  padding: var(--sp-1) 0;
}
.json-block summary:hover { color: var(--ink); }
.json-block[open] summary { margin-bottom: var(--sp-2); }

.crumbs {
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  font-size: var(--fs-base); color: var(--ink-muted);
}
.crumbs .sep { color: var(--ink-subtle); }
.copyable { cursor: pointer; border-radius: var(--r-sm); transition: background-color 0.15s ease; }
/* 按钮自己有 hover 态；再压一层沉底色会把它洗成一块看不出可点的灰。 */
.copyable:not(.btn):hover { background: var(--surface-sunken); }
.copyable.copied { background: var(--ok-weak); color: var(--ok); }

.linklist { list-style: none; padding: 0; margin: 0; }
.linklist li {
  padding: var(--sp-2) 0; display: flex; gap: var(--sp-3); align-items: baseline;
  border-bottom: 1px solid var(--line);
}
.linklist li:last-child { border-bottom: none; }

/* ================================================================ 文件选择器 */
.picker {
  position: fixed; inset: 0; background: rgba(11, 18, 25, 0.5);
  display: flex; align-items: center; justify-content: center; padding: var(--sp-6); z-index: 50;
  backdrop-filter: blur(2px);
}
.picker-panel {
  background: var(--surface); border-radius: var(--r-lg); width: min(720px, 100%);
  max-height: 80vh; display: flex; flex-direction: column; overflow: hidden;
  box-shadow: var(--sh-3); border: 1px solid var(--line);
}
.picker-head, .picker-foot {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line);
}
.picker-head { font-weight: 600; }
.picker-foot {
  border-bottom: none; border-top: 1px solid var(--line);
  justify-content: space-between; background: var(--surface-2);
}
.picker-hint { color: var(--ink-muted); font-size: var(--fs-sm); flex: 1; }
.picker-crumbs {
  padding: var(--sp-2) var(--sp-4); border-bottom: 1px solid var(--line);
  font-size: var(--fs-base); background: var(--surface-2);
}
.picker-list { list-style: none; margin: 0; padding: var(--sp-1) 0; overflow: auto; flex: 1; }
.picker-list li {
  display: flex; justify-content: space-between; gap: var(--sp-3);
  padding: 6px var(--sp-4); cursor: pointer;
}
.picker-list li:hover { background: var(--surface-2); }
.picker-list .is-dir { font-weight: 600; color: var(--primary); }

/* ================================================================ 无障碍 */
/* 只给读屏用的文本 */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .card-link:hover { transform: none; }
}

/* ================================================================ 窄屏 */
/* 侧栏折叠成深色顶栏：品牌行 + 一行横滚导航；外链让位（要用去宽屏） */
@media (max-width: 940px) {
  body { flex-direction: column; }
  .sidebar {
    position: static; width: auto; height: auto; overflow: visible;
    flex-direction: row; flex-wrap: wrap; align-items: center;
    gap: var(--sp-2); padding: var(--sp-2) var(--sp-4);
  }
  .brand { padding: 0; margin-right: var(--sp-2); }
  .sidebar-foot {
    margin: 0 0 0 auto; padding: 0; border-top: none; order: 2;
    flex-direction: row; align-items: center;
  }
  .ext-links { display: none; }
  .nav {
    order: 3; flex-basis: 100%; flex-direction: row; gap: 2px;
    overflow-x: auto; padding-bottom: 2px; scrollbar-width: none;
  }
  .nav-group { display: none; }
  .nav-item { padding: 5px var(--sp-3); flex-shrink: 0; }
  .nav-item.is-active::before {
    left: 50%; top: auto; bottom: 0; translate: -50% 0;
    width: 16px; height: 3px; border-radius: 2px 2px 0 0;
  }

  .page { padding: var(--sp-5) var(--sp-4) var(--sp-8); }
  .footer { padding: var(--sp-3) var(--sp-4) var(--sp-6); }
  .cards { grid-template-columns: 1fr; }
  .cols-2 { grid-template-columns: 1fr; }
  .cols-2 > .card { margin-bottom: var(--sp-4); }
  .field input[type=number] { max-width: none; }
  .path-input { flex-direction: column; }
  .path-input .btn { width: 100%; }
  .stat-row { column-gap: var(--sp-6); }
}
