/* ================= 顶级打磨层 v3 =================
   目标：把「工业仪表盘」做出质感、焦点、呼吸感——深色不平、读数像真仪表、
   动效只在入场 / 进行中 / 交互时出现。全部尊重 prefers-reduced-motion。
   这一层只做视觉叠加，不改任何结构与交互逻辑。 */

/* ---------- 1. 环境光：顶部落一束暖光，四角压暗收视线 ---------- */
body::after {
  content: "";
  position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background:
    radial-gradient(1100px 600px at 50% -6%, rgba(227, 164, 78, .08), transparent 60%),
    radial-gradient(1500px 1000px at 50% 130%, rgba(0, 0, 0, .45), transparent 62%);
}

/* ---------- 2. 主仪表盘：一排格子 → 一台主读数台 ---------- */
.board {
  position: relative; isolation: isolate;
  background:
    radial-gradient(130% 170% at 50% -55%, rgba(227, 164, 78, .11), transparent 58%),
    linear-gradient(180deg, #17212c, #111924);
  border-color: #2f3d4f;
  box-shadow:
    0 34px 80px -50px #000,
    inset 0 1px 0 #ffffff18,
    inset 0 0 0 1px #ffffff06,
    0 22px 72px -34px rgba(227, 164, 78, .13);
}
/* 顶部状态光谱 */
.board::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 2px; z-index: 2;
  background: linear-gradient(90deg, transparent 4%, var(--amber) 50%, transparent 96%);
  opacity: .6;
}
/* 极慢的扫描光，暗示这台仪表在跑（很淡，不抢内容） */
.board::after {
  content: "";
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(100deg, transparent 47%, rgba(255, 255, 255, .055) 50%, transparent 53%);
  transform: translateX(-120%);
  animation: board-scan 11s cubic-bezier(.55, 0, .45, 1) infinite;
  opacity: 0;
}
@keyframes board-scan {
  0%   { transform: translateX(-120%); opacity: 0; }
  6%   { opacity: 1; }
  40%  { transform: translateX(120%); opacity: 0; }
  100% { transform: translateX(120%); opacity: 0; }
}

/* 读数格：悬停点亮跟随光 */
.cell { position: relative; transition: background .3s ease; }
.cell:hover {
  background: radial-gradient(360px circle at 50% 0%, rgba(227, 164, 78, .12), transparent 72%);
}
.cell-label { display: inline-flex; align-items: center; gap: 8px; }

/* ---------- 3. 读数排版：大数字暖白金属渐变 ---------- */
.cell-value:not([data-tone]),
.version b {
  background: linear-gradient(180deg, #ffffff 8%, #c2cbd7 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
/* 带状态的读数保持纯色 + 微光，不被渐变吃掉 */
.cell-value[data-tone] {
  background: none;
  -webkit-text-fill-color: currentColor;
}
.version b { font-weight: 650; letter-spacing: -.02em; }
.cell-value { font-weight: 650; letter-spacing: -.015em; }

/* ---------- 4. 服务卡片：更厚的玻璃与更明显的悬停层次 ---------- */
.card {
  background:
    radial-gradient(560px circle at var(--mx, 50%) var(--my, -10%), rgba(227, 164, 78, .09), transparent 46%),
    linear-gradient(180deg, #141d28, #10171f);
  border-color: #243040;
}
.card:hover {
  border-color: #40536a;
  box-shadow:
    0 36px 76px -46px #000,
    inset 0 1px 0 #ffffff16,
    0 0 0 1px rgba(227, 164, 78, .10);
}

/* ---------- 5. 主按钮与登录页：更立体、更亮 ---------- */
.btn.primary {
  background: linear-gradient(180deg, #f3b867, #d68b28);
  box-shadow: 0 10px 24px -10px #e3a44e66, inset 0 1px 0 #fff4;
}
.btn.primary:hover:not(:disabled) { filter: brightness(1.07); transform: translateY(-1px); }

.login-brand h1 { font-weight: 700; text-shadow: 0 0 32px rgba(227, 164, 78, .16); }
.login-card {
  background: linear-gradient(180deg, #131c27, #0c1219);
  border-color: #263243;
  box-shadow: 0 40px 90px -40px #000, inset 0 1px 0 #ffffff0e;
}

/* ---------- 6. 入场动效只在「首次进入」时放 ----------
   60s 自动刷新会重渲染读数格，动画不能每 60s 重放一次（否则会一直淡入）。
   由 renderBoard 在首帧给 #board 加 .enter、1.1s 后移除。 */
.cell { animation: none; }
.board.enter .cell { animation: cell-in .5s cubic-bezier(.2, .7, .3, 1) both; }

/* ---------- 7. 发布窗口：时钟 + 呼吸灯，提醒避开 :00 / :30 ---------- */
.window-pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px 6px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: linear-gradient(180deg, #131c27, #0d141c);
  font-size: 12px; color: var(--muted);
  white-space: nowrap;
  transition: border-color .25s, background .25s;
}
.window-pill .mono { font-size: 13px; color: var(--text); letter-spacing: .04em; }
.window-hint { font-size: 11px; letter-spacing: .02em; color: var(--dim); }
.window-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 8px var(--green);
  animation: window-breathe 2.4s ease-in-out infinite;
}
/* 临近整点/半点：转琥珀警示 */
.window-pill[data-window="warn"] {
  border-color: #e3a44e55;
  background: linear-gradient(180deg, #241d12, #14100a);
}
.window-pill[data-window="warn"] .window-dot {
  background: var(--amber); box-shadow: 0 0 10px var(--amber);
  animation-duration: 1.1s;
}
.window-pill[data-window="warn"] .mono,
.window-pill[data-window="warn"] .window-hint { color: var(--amber); }
@keyframes window-breathe {
  0%, 100% { opacity: .45; transform: scale(.85); }
  50%      { opacity: 1; transform: scale(1.15); }
}

/* ---------- 8. v4：读数收紧 + 仪表刻度 ---------- */

/* 版本对照：两个读数紧凑靠左，同步徽标推到最右，把宽度用满 */
.versions {
  display: flex; align-items: flex-end; gap: 34px; margin-top: 14px;
}
.version { flex: 0 0 auto; }

/* 同步状态徽标（替代原来底部那句"落后于仓库最新"） */
.sync-badge {
  margin-left: auto; align-self: center;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 13px; border-radius: 999px;
  font-size: 12px; letter-spacing: .02em;
  border: 1px solid var(--line); color: var(--muted);
  white-space: nowrap;
}
.sync-badge::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; box-shadow: 0 0 8px currentColor;
}
.sync-badge[data-state="ok"]   { color: var(--green); background: #4fb28612; border-color: #4fb28633; }
.sync-badge[data-state="warn"] { color: var(--amber); background: #e3a44e12; border-color: #e3a44e33; }
.sync-badge[data-state="idle"] { color: var(--dim); }

/* 读数格底部：一小段刻度尺，像真仪表 */
.cell::after {
  content: ""; position: absolute; left: 18px; right: 18px; bottom: 0; height: 7px;
  background-image: repeating-linear-gradient(90deg, #2b3849 0 1px, transparent 1px 9px);
  opacity: .5; pointer-events: none;
}

/* 主读数再重一档 */
.cell-value { font-size: 19px; }
.cell-value.mono { font-size: 18px; }

@media (max-width: 700px) {
  .versions { flex-wrap: wrap; gap: 22px; }
}

/* ---------- 9. 版本号呼吸灯 ----------
   读数缓慢呼吸一层暖光，像仪表在"活着"。两个读数错开相位，不同步更自然。 */
@keyframes version-glow {
  0%, 100% { filter: drop-shadow(0 0 0 rgba(227, 164, 78, 0)); }
  50%      { filter: drop-shadow(0 0 8px rgba(227, 164, 78, .42)); }
}
.cell-value.mono,
.version b { animation: version-glow 4.4s ease-in-out infinite; }
.version + .version b { animation-delay: 2.2s; }

/* ---------- 10. 动效层 v5：环境极光 + 光谱呼吸 ---------- */

/* 主内容背后：一片缓慢漂移的暖色极光，只做空气感，不抢内容 */
main::before {
  content: ""; position: fixed; left: 50%; top: -18%;
  width: 1100px; height: 680px; z-index: -1; pointer-events: none;
  transform: translateX(-50%);
  background:
    radial-gradient(closest-side at 34% 42%, rgba(227, 164, 78, .11), transparent 72%),
    radial-gradient(closest-side at 68% 58%, rgba(88, 138, 208, .07), transparent 72%);
  filter: blur(34px);
  animation: aurora-drift 26s ease-in-out infinite alternate;
}
@keyframes aurora-drift {
  0%   { transform: translateX(-52%) translateY(0) scale(1); }
  100% { transform: translateX(-48%) translateY(26px) scale(1.07); }
}

/* 主仪表盘顶部光谱缓慢呼吸，像设备在待命 */
.board::before { animation: spectrum-breathe 4.2s ease-in-out infinite; }
@keyframes spectrum-breathe {
  0%, 100% { opacity: .42; }
  50%      { opacity: .9; }
}

/* ================= 动效层 v6：拉满 ================= */

/* 1) 主按钮：呼吸发光（活跃感）+ 点击涟漪（由 JS 注入 .ripple） */
.btn.primary { animation: btn-breathe 2.8s ease-in-out infinite; }
@keyframes btn-breathe {
  0%, 100% { box-shadow: 0 10px 24px -10px #e3a44e66, inset 0 1px 0 #fff4; }
  50%      { box-shadow: 0 13px 30px -8px #e3a44eaa, inset 0 1px 0 #fff4, 0 0 0 2px #e3a44e14; }
}
.ripple {
  position: absolute; border-radius: 50%; pointer-events: none;
  background: #ffffff66; transform: translate(-50%, -50%) scale(0);
  animation: ripple .6s ease-out forwards;
}
@keyframes ripple {
  to { transform: translate(-50%, -50%) scale(2.8); opacity: 0; }
}

/* 2) 读数格：hover 时一道扫描光掠过 */
.cell::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background: linear-gradient(105deg, transparent 40%, rgba(227, 164, 78, .14) 50%, transparent 60%);
  transform: translateX(-100%); opacity: 0;
}
.cell:hover::before { animation: cell-scan .85s ease; }
@keyframes cell-scan {
  0%   { transform: translateX(-100%); opacity: 0; }
  30%  { opacity: 1; }
  100% { transform: translateX(100%); opacity: 0; }
}

/* 3) 表格行：hover 左侧滑出一条琥珀竖条 */
tbody tr td:first-child { position: relative; }
tbody tr td:first-child::before {
  content: ""; position: absolute; left: 0; top: 22%; bottom: 22%; width: 2px;
  border-radius: 2px; background: var(--amber);
  transform: scaleY(0); transition: transform .18s ease;
}
tbody tr:hover td:first-child::before { transform: scaleY(1); }

/* 4) 落后服务的「发布」键：呼吸提醒环，把注意力拉到该发的服务上 */
.card[data-state="drift"] .btn.primary { animation: attention 2s ease-in-out infinite; }
@keyframes attention {
  0%, 100% { box-shadow: 0 0 0 0 #e3a44e00, 0 10px 24px -10px #e3a44e66; }
  50%      { box-shadow: 0 0 0 5px #e3a44e24, 0 13px 30px -8px #e3a44eaa; }
}

/* 5) 状态胶囊的点：呼吸更明显 */
.pill::before { animation: dot-breathe 2.6s ease-in-out infinite; }
@keyframes dot-breathe {
  0%, 100% { opacity: .5; transform: scale(.9); }
  50%      { opacity: 1; transform: scale(1.15); }
}

/* 6) 数值变化：闪一下暖光（JS 在值变化时加 .flash） */
@keyframes value-flash {
  0%   { filter: drop-shadow(0 0 14px rgba(227, 164, 78, .85)); transform: translateY(-1.5px); }
  100% { filter: drop-shadow(0 0 0 rgba(227, 164, 78, 0)); transform: none; }
}
.flash { animation: value-flash .9s ease-out; }

/* 7) 发布窗口临近整点：整颗胶囊脉冲警示 */
.window-pill[data-window="warn"] { animation: window-warn 1.5s ease-in-out infinite; }
@keyframes window-warn {
  0%, 100% { box-shadow: 0 0 0 0 #e3a44e00; }
  50%      { box-shadow: 0 0 0 4px #e3a44e1f; }
}

/* 8) 顶部品牌 → 摇杆式微动：每 6s 轻轻挑一下，像设备自检 */
.brand .mark span { animation: breathe 3.2s ease-in-out infinite; }

/* 9) 读数格数值变化：整格闪一圈暖光（JS 检测到变化时加 .flash） */
@keyframes cell-flash {
  0%   { box-shadow: inset 0 0 0 1px #e3a44e66, inset 0 0 26px -6px #e3a44e44; }
  100% { box-shadow: inset 0 0 0 1px #e3a44e00, inset 0 0 26px -6px #e3a44e00; }
}
.cell.flash { animation: cell-flash .9s ease-out; }

/* ================= 10. 发布详情：状态头 ================= */
.detail-panel { position: relative; overflow: hidden; }
.detail-panel::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--amber), transparent);
  opacity: .45;
}
.detail-hero {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding-bottom: 16px; margin-bottom: 18px;
  border-bottom: 1px solid var(--line-soft);
}
.detail-hero-main { display: flex; align-items: center; gap: 14px; min-width: 0; }
.detail-hero-text { min-width: 0; }
.detail-title { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: -.01em; }
.detail-title .mono { font-size: 18px; }
.detail-hero .hint { margin: 5px 0 0; }

/* 状态光环：琥珀=进行中(呼吸)，成功绿 / 失败红 / 回滚琥珀 / 忙碌蓝 */
.detail-orb {
  width: 13px; height: 13px; border-radius: 50%; flex: 0 0 auto;
  background: var(--amber);
  box-shadow: 0 0 0 4px #e3a44e22, 0 0 14px #e3a44e99;
  animation: orb-breathe 1.8s ease-in-out infinite;
}
@keyframes orb-breathe {
  0%, 100% { transform: scale(.85); opacity: .75; }
  50%      { transform: scale(1.12); opacity: 1; }
}
.detail-hero[data-state="ok"]   .detail-orb { background: var(--green); box-shadow: 0 0 0 4px #4fb28622, 0 0 14px #4fb28699; animation: none; }
.detail-hero[data-state="bad"]  .detail-orb { background: var(--red);   box-shadow: 0 0 0 4px #d2604a22, 0 0 14px #d2604a99; animation: none; }
.detail-hero[data-state="warn"] .detail-orb { background: var(--amber); box-shadow: 0 0 0 4px #e3a44e22, 0 0 14px #e3a44e99; animation: none; }
.detail-hero[data-state="busy"] .detail-orb { background: var(--blue);  box-shadow: 0 0 0 4px #6e9be022, 0 0 14px #6e9be099; }

/* 详情里的步骤轨道紧凑一点 */
.detail-panel .rail { margin-top: 0; }

/* ---------- 10b. 发布过程动效：进度条 / 流光 / 逐行日志 / 入场 ---------- */

/* 进度条：已完成步骤占比，宽度平滑填充 */
.detail-progress {
  height: 3px; border-radius: 3px; margin: 0 0 20px;
  background: #ffffff0a; overflow: hidden;
}
.detail-progress span {
  display: block; height: 100%; width: 0; border-radius: 3px;
  background: linear-gradient(90deg, #e3a44e, #f0b45f);
  box-shadow: 0 0 10px #e3a44e66;
  transition: width .6s cubic-bezier(.2, .7, .3, 1), background .4s, box-shadow .4s;
}
.detail-progress span[data-state="ok"]   { background: linear-gradient(90deg, #3f9e72, #4fb286); box-shadow: 0 0 10px #4fb28666; }
.detail-progress span[data-state="bad"]  { background: linear-gradient(90deg, #c1523d, #d2604a); box-shadow: 0 0 10px #d2604a66; }
/* 进行中：进度条上有一道流光，暗示"还在跑" */
.detail-progress span[data-state="busy"] {
  background: linear-gradient(90deg, #5a86c8, #8fb3ea, #5a86c8);
  background-size: 200% 100%;
  box-shadow: 0 0 12px #6e9be088;
  animation: progress-flow 1.4s linear infinite;
}
@keyframes progress-flow { to { background-position: 200% 0; } }

/* 正在执行的步骤：名字下方一道流动下划线 */
.detail-panel .rail li.running .name { color: var(--amber); }
.detail-panel .rail li.running .name::after {
  content: ""; display: block; height: 2px; width: 72%; margin: 4px auto 0;
  border-radius: 2px;
  background: repeating-linear-gradient(90deg, var(--amber) 0 8px, transparent 8px 16px);
  background-size: 16px 2px;
  animation: step-flow 1s linear infinite;
}
@keyframes step-flow { to { background-position: 16px 0; } }

/* 详情面板入场 */
#detail:not(.hidden) { animation: detail-in .45s cubic-bezier(.2, .7, .3, 1); }
@keyframes detail-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* ================= 11. 发布过程：终端质感（真终端级） ================= */

/* 日志面板：极淡扫描线 + 顶亮底暗渐变，有玻璃质感 */
.detail-panel .log {
  background:
    repeating-linear-gradient(0deg, transparent 0 3px, rgba(255, 255, 255, .012) 3px 4px),
    linear-gradient(180deg, #0a0f15, #070b10);
  color: #c3ccd8;
  border-color: #1b2633;
  box-shadow: inset 0 1px 0 #ffffff0a, inset 0 0 46px -22px #000;
}

/* 每行：左侧一条彩色光轨（按 ▶/✓/✗ 着色）+ 悬停微亮 */
.log-line {
  position: relative; display: flex; gap: 12px; align-items: baseline;
  padding: 1px 8px 1px 13px; border-radius: 4px;
  animation: log-in .36s ease-out;
  transition: background .15s;
}
.log-line::before {
  content: ""; position: absolute; left: 2px; top: 2px; bottom: 2px; width: 3px;
  border-radius: 2px; background: transparent;
  transition: background .3s, box-shadow .3s;
}
.log-line:has(.log-msg.is-step)::before { background: var(--amber); box-shadow: 0 0 10px #e3a44ecc, 0 0 4px #e3a44eaa; }
.log-line:has(.log-msg.is-ok)::before   { background: var(--green); box-shadow: 0 0 10px #4fb286cc, 0 0 4px #4fb286aa; }
.log-line:has(.log-msg.is-bad)::before  { background: var(--red);   box-shadow: 0 0 10px #d2604acc, 0 0 4px #d2604aaa; }
/* 整行左缘内发光，像终端里被点亮的一行 */
.log-line:has(.log-msg.is-step) { box-shadow: inset 3px 0 16px -6px #e3a44eaa; }
.log-line:has(.log-msg.is-ok)   { box-shadow: inset 3px 0 16px -6px #4fb286aa; }
.log-line:has(.log-msg.is-bad)  { box-shadow: inset 3px 0 16px -6px #d2604aaa; }
.log-line:hover { background: #ffffff07; }

.log-time { flex: 0 0 auto; color: #4d5a68; font-size: 11.5px; font-variant-numeric: tabular-nums; }
.log-msg { flex: 1; min-width: 0; white-space: pre-wrap; word-break: break-all; }
.log-msg.is-step { color: var(--amber); font-weight: 600; }
.log-msg.is-ok   { color: var(--green); font-weight: 500; }
.log-msg.is-bad  { color: var(--red); font-weight: 500; }
.log-msg.typing { text-shadow: 0 0 10px currentColor; }

/* 新行：从左侧滑入，带一道琥珀余辉淡出，像终端"刷"出新行 */
@keyframes log-in {
  0%   { opacity: 0; transform: translateX(-8px); background: #e3a44e2a; }
  55%  { background: #e3a44e10; }
  100% { opacity: 1; transform: none; background: transparent; }
}

/* 闪烁光标：琥珀 */
.log-cursor {
  display: inline-block; color: var(--amber);
  text-shadow: 0 0 9px #e3a44e88;
  animation: cursor-blink 1.1s steps(1) infinite;
}
@keyframes cursor-blink { 50% { opacity: 0; } }

/* 进行中：整个面板缓慢扫过一道绿光 */
.detail-panel { position: relative; overflow: hidden; }
.detail-panel::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: linear-gradient(100deg, transparent 46%, rgba(79, 178, 134, .08) 50%, transparent 54%);
  transform: translateX(-120%); opacity: 0;
}
.detail-panel[data-state="busy"]::after { animation: detail-scan 5.5s cubic-bezier(.4, 0, .2, 1) infinite; }
@keyframes detail-scan {
  0%   { transform: translateX(-120%); opacity: 0; }
  10%  { opacity: 1; }
  46%  { transform: translateX(120%); opacity: 0; }
  100% { transform: translateX(120%); opacity: 0; }
}

/* ================= 12. 发布流水线：竖排时间线 ================= */
.pipeline {
  list-style: none; margin: 0 0 20px; padding: 0;
  display: flex; flex-direction: column;
}
.pipeline li {
  position: relative;
  display: flex; align-items: flex-start; gap: 14px;
  padding: 8px 0;
}
/* 左侧竖线：完成段点亮成绿，其余灰 */
.pipeline li::before {
  content: ""; position: absolute; left: 12px; top: 0; bottom: 0; width: 2px;
  background: var(--line-soft); transition: background .4s;
}
.pipeline li:first-child::before { top: 50%; }
.pipeline li:last-child::before { bottom: 50%; }
.pipeline li.succeeded::before { background: linear-gradient(180deg, #4fb286, #4fb28655); }

/* 节点圆圈 */
.pipeline li .step-node {
  position: relative; z-index: 1;
  width: 25px; height: 25px; flex: 0 0 auto;
  border-radius: 50%; display: grid; place-items: center;
  background: var(--ink-2); border: 1.5px solid var(--line);
  color: var(--dim); font-size: 12px; line-height: 1;
  transition: background .35s, border-color .35s, box-shadow .35s, color .35s;
}
.pipeline li.pending .step-node::before   { content: "○"; }
.pipeline li.succeeded .step-node::before { content: "✓"; }
.pipeline li.failed .step-node::before    { content: "✕"; }
.pipeline li.skipped .step-node::before   { content: "–"; }

/* 完成：绿光 */
.pipeline li.succeeded .step-node {
  border-color: var(--green); color: var(--green);
  background: #4fb2861f; box-shadow: 0 0 12px #4fb28666, 0 0 0 3px #4fb2861a;
}
/* 失败：红光 */
.pipeline li.failed .step-node {
  border-color: var(--red); color: var(--red);
  background: #d2604a1f; box-shadow: 0 0 12px #d2604a66;
}
/* 执行中：琥珀 + 转圈环 */
.pipeline li.running .step-node {
  border-color: var(--amber); color: var(--amber);
  background: #e3a44e1f; box-shadow: 0 0 12px #e3a44e88, 0 0 0 3px #e3a44e1a;
}
.pipeline li.running .step-node::before { content: ""; }
.pipeline li.running .step-node::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 13px; height: 13px; margin: -6.5px 0 0 -6.5px;
  border-radius: 50%;
  border: 2px solid #e3a44e33; border-top-color: var(--amber);
  animation: node-spin .8s linear infinite;
}
@keyframes node-spin { to { transform: rotate(360deg); } }

/* 主体：名字 + 用时 + 状态 */
.step-body { flex: 1; min-width: 0; padding-top: 2px; }
.step-head { display: flex; align-items: baseline; justify-content: flex-start; gap: 10px; }
.step-name { font-family: var(--mono); font-size: 13px; letter-spacing: .02em; color: var(--muted); transition: color .3s; }
.step-time { font-family: var(--mono); font-size: 11px; color: var(--dim); }
.step-state { display: block; font-size: 11.5px; color: var(--dim); margin-top: 1px; transition: color .3s; }

.pipeline li.succeeded .step-name { color: #d9ece1; }
.pipeline li.succeeded .step-state { color: #4fb286cc; }
.pipeline li.running .step-name,
.pipeline li.running .step-state { color: var(--amber); }
.pipeline li.failed .step-name,
.pipeline li.failed .step-state { color: var(--red); }

/* 详情两栏：左流水线（窄）+ 右终端日志（大），两栏等高 */
.detail-split {
  display: grid;
  grid-template-columns: 210px minmax(0, 1fr);
  gap: 14px;
  align-items: stretch;
  margin-bottom: 6px;
}
.detail-split .pipeline { margin: 0; }
.detail-split .log { margin: 0; max-height: 720px; }

@media (max-width: 720px) {
  .detail-split { grid-template-columns: 1fr; }
  .detail-split .log { max-height: 340px; }
}
