/* ============================================================================
   智能播种管理系统 · 管理后台主题
   ============================================================================
   加载顺序（页面里必须按这个顺序）：
       <link rel="stylesheet" href="_legacy-<page>.css">   旧样式（安全网，只为回退保留）
       <link rel="stylesheet" href="theme.css">            本文件 —— 同选择器后者胜

   本文件做三件事，缺一件都会让"改了没生效"重现：
     1. **重映射全部旧变量名**（56 个，散落在 4 个页面里各有 2~3 种取值）。
        旧规则里写的是 var(--text-muted) 这类名字，只要在这里把名字指向新色值，
        那些没被单独覆盖的旧规则会自动跟着变新 —— 这是本次改动风险最低的一点。
     2. **定义新令牌层**（--ink-* / --line-* / --brand-* / --t-* / --s* 等），新规则只用它们。
     3. **重绘关键组件**（面板/表格/徽标/按钮/表单/分页/侧边栏/顶栏/详情页/首页卡片）。

   ⚠️ 三条不能碰的约束（改这个文件前先读 MEMORY.md）：
     · 所有 id= 与 JS 查询用的 class 名**零变更** —— 10 套前端测试的断言基础
     · 四个重渲染容器的 overflow-anchor: none 与 .page-content 的 auto 必须在
       （本文件末尾重新声明了一遍，这样将来删掉 legacy 文件也不会复现滚动跳动）
     · 数字一律 Fira Code + tnum，单位走 .home-summary-unit / <small>，不要拼进数值

   根字号断点 = 大屏与笔记本兼顾的实现方式：所有新规则用 rem，改一处根字号全站等比放大。
   ============================================================================ */

/* ── 字体：自托管，不用 Google Fonts（@import 是渲染阻塞，展厅/内网会卡首屏）────── */
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:400;font-display:swap;
  src:url('../fonts/fira-sans-latin-400-normal.woff2') format('woff2');}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:500;font-display:swap;
  src:url('../fonts/fira-sans-latin-500-normal.woff2') format('woff2');}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:600;font-display:swap;
  src:url('../fonts/fira-sans-latin-600-normal.woff2') format('woff2');}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:700;font-display:swap;
  src:url('../fonts/fira-sans-latin-700-normal.woff2') format('woff2');}
@font-face{font-family:'Fira Code';font-style:normal;font-weight:300 700;font-display:swap;
  src:url('../fonts/FiraCode-VF.woff2') format('woff2-variations');}

/* ── 原始层：色值本身，只在这里出现一次 ──────────────────────────────────────── */
:root{
  /* 墨色（绿相中性，chroma .012）—— 括号内是白卡上的实测 WCAG 对比度 */
  --ink-900:#16241C;   /* 16.11 标题、核心数字 */
  --ink-700:#33443A;   /* 10.35 正文 */
  --ink-500:#5A6B60;   /*  5.67 次要文字 */
  --ink-400:#68796E;   /*  4.62 元信息：时间戳、单位、注释 */
  --ink-300:#8B9A90;   /*  仅用于禁用态、非文本装饰 */
  /* 线与面 */
  --line-1:#DCE5DA;    /*  1.29 装饰性分隔线（密集表格内） */
  --line-2:#C6D2C4;    /*  1.57 面板轮廓 */
  --line-3:#879A84;    /*  3.01 控件边界（输入框，达 AA） */
  --surface-page:#EFF3EC;
  --surface-panel:#FFFFFF;
  --surface-soft:#F1F5EE;   /* 表头底、次级面 */
  --shell:#16241C;          /* 机壳：侧边栏与顶栏 */
  --shell-2:#1E3026;
  --shell-text:#C9D6CE;
  --shell-text-strong:#F2F7F4;
  --shell-line:rgba(255,255,255,.10);
  /* 品牌 */
  --brand-700:#276B4A; /* 6.39 链接、强调文字 */
  --brand-600:#2F8158; /* 4.77 按钮底（配白字 4.77） */
  --brand-500:#3E9A6A;
  --brand-100:#DCEBE1;
  --brand-050:#F0F6F2;
  /* 语义 */
  --ok:#1F7A4D;     --ok-bg:#E7F3EC;
  --warn:#9A5B12;   --warn-bg:#F8EEDD;
  --danger:#B0392F; --danger-bg:#F8E8E6;
  --info:#2A5FA8;   --info-bg:#E6EEF9;
  /* 单体序号色阶：序号是**有序量**，用连续色阶而不是分类色 ——
     分类色会让"第 3 个 vs 第 4 个"看起来是两个种类，而且红色会和报警语义打架 */
  --seq-1:#4FA277; --seq-2:#33905F; --seq-3:#25804F;
  --seq-4:#1A6B45; --seq-5:#135737; --seq-6:#0E452C;
}

/* ── 语义层 ─────────────────────────────────────────────────────────────────── */
:root{
  --font-sans:'Fira Sans','Microsoft YaHei','PingFang SC','Hiragino Sans GB',system-ui,sans-serif;
  --font-mono:'Fira Code','Consolas','Courier New',monospace;
  /* 字号：固定 rem 刻度，10 档收敛为 6 档（原来 11~16px 六档只差 1px，等于没层级） */
  --t-cap:.8rem;      /* 12px 只给时间戳、坐标这种可略读的角标 */
  --t-meta:.875rem;   /* 13px 表头、徽标、辅助说明 */
  --t-base:1rem;      /* 15px 正文与表格单元格 */
  --t-lead:1.125rem;  /* 17px 引导句、卡片标题 */
  --t-h3:1.25rem;     /* 19px */
  --t-h2:1.5rem;      /* 22px 面板标题 */
  --t-h1:2rem;        /* 30px 页面主标题 */
  --t-num:2.75rem;    /* 41px 展示型数字 */
  /* 间距：4pt 基数 */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px; --s8:64px;
  /* 圆角与阴影 */
  --r1:6px; --r2:10px; --r3:14px;
  --shadow-1:0 1px 2px rgba(22,36,28,.06), 0 1px 1px rgba(22,36,28,.04);
  --shadow-2:0 1px 2px rgba(22,36,28,.05), 0 10px 24px -10px rgba(22,36,28,.14);
  --shadow-3:0 2px 4px rgba(22,36,28,.06), 0 24px 48px -16px rgba(22,36,28,.24);
  --ease-out-quart:cubic-bezier(.25,1,.5,1);
  --layout-gap:var(--s5);
}

/* ── 旧变量名重映射：让没被单独覆盖的旧规则也自动变新（本次改动最关键的一段）────
   右列是旧值 → 左列是问题。特别是 --text-muted 2.81:1、--color-accent 2.40:1、
   --border-soft 1.29:1 —— 旧值在投影仪上都看不见。 */
:root{
  --surface-panel-solid:#FFFFFF;
  --text-strong:var(--ink-900);      /* 旧 #17221b */
  --text:var(--ink-700);             /* 旧 #2d3931 */
  --text-secondary:var(--ink-500);   /* 旧 #66736b（4.56，几乎没余量） */
  --text-muted:var(--ink-400);       /* 旧 #8b9690 → 2.81:1 不达标，现 4.62 */
  --border-soft:var(--line-1);
  --border-strong:var(--line-2);
  --success:var(--ok);
  --warning:var(--warn);
  --danger:var(--danger);
  --primary:var(--brand-700);
  --primary-dark:#0B1510;
  --bg:var(--surface-page);
  --card-bg:var(--surface-panel-solid);
  --color-primary:var(--shell);      /* 旧 #1E293B 冷蓝灰 —— 和暖绿中性色打在一起是三个色系 */
  --color-on-primary:#FFFFFF;
  --color-secondary:var(--ink-700);
  --color-on-secondary:#FFFFFF;
  --color-accent:var(--brand-600);   /* 旧 #d9912b 琥珀 → 2.40:1；现为按钮/选中态的品牌绿 */
  --color-on-accent:#FFFFFF;         /* 旧 #1f170b 近黑（配琥珀）；配绿必须换白字 */
  --color-background:var(--shell);
  --color-foreground:var(--shell-text-strong);
  --color-card:var(--surface-panel);
  --color-card-foreground:var(--ink-900);
  --color-muted:var(--surface-soft);
  --color-muted-foreground:var(--ink-400);
  --color-border:var(--line-2);
  --color-destructive:var(--danger);
  --color-ring:var(--brand-500);
  /* 旧间距/圆角族（4pt 重排） */
  --space-xs:2px; --space-sm:4px; --space-md:8px; --space-lg:12px;
  --space-xl:16px; --space-2xl:24px; --space-3xl:32px;
  --radius-sm:6px; --radius-md:10px; --radius-lg:14px;
  --shadow-sm:var(--shadow-1);
  --shadow-md:var(--shadow-2);
  --shadow-lg:var(--shadow-3);
  /* 布局尺寸：三个页面原本各写一套（200/224/248 与 60/68/72），统一 */
  --sidebar-w:248px;
  --header-h:68px;
  /* monitor.html 自己的一套通用名（只它用，已核实另外三个页面不出现）。
     不重映射的话，那个页面会一直是旧的蓝色系，和主站两个色系。 */
  --ink:var(--ink-900);
  --muted:var(--ink-500);
  --line:var(--line-2);
  --page:var(--surface-page);
  --surface:var(--surface-panel);
  --blue:var(--brand-700);    /* 它把 --blue 当强调色用（标题标、按钮、链接）→ 收敛到品牌绿 */
  --green:var(--ok);
  --red:var(--danger);
  --amber:var(--warn);
}

/* ── 基础 ───────────────────────────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box;}
html{font-size:15px;}                      /* 根字号：大屏断点在文件末尾 */
body{
  margin:0; min-height:100vh;
  background:var(--surface-page);
  color:var(--ink-700);
  font-family:var(--font-sans);
  font-size:var(--t-base); line-height:1.6;
  font-kerning:normal;
  -webkit-font-smoothing:antialiased;
}
::selection{background:var(--brand-100);color:var(--ink-900);}
a{color:var(--brand-700);text-decoration:none;}
a:hover{text-decoration:underline;}
h1,h2,h3,h4{color:var(--ink-900);font-weight:600;letter-spacing:-.012em;line-height:1.3;}
:focus-visible{outline:2px solid var(--brand-500);outline-offset:2px;}
/* 数字统一等宽 + 表格数字，且打开 cv11（唯一纯空心零）。
   ⚠️ 这条要写在 font: 简写**之后**才能生效 —— font 简写会重置 font-feature-settings */
.num,.mono,td.n,.home-summary-value,.basic-gauge-core strong,
.info-grid .info-item .value,.stat-card .sc-num{
  font-family:var(--font-mono);
  font-variant-numeric:lining-nums tabular-nums;
  font-feature-settings:'cv11' 1,'zero' 0,'tnum' 1;
}

/* ── 布局壳：深色机壳 + 浅色读数区 ─────────────────────────────────────────── */
.sidebar{
  position:fixed;top:0;left:0;bottom:0;width:var(--sidebar-w);z-index:60;
  background:var(--shell);                 /* 去掉旧的 slate 渐变：仪器感来自确定性 */
  color:var(--shell-text);
  display:flex;flex-direction:column;
  transition:left .28s var(--ease-out-quart);
}
.sidebar.collapsed{left:calc(var(--sidebar-w) * -1);}
.sidebar-logo{
  padding:0 var(--s5);height:var(--header-h);display:flex;align-items:center;
  font-family:var(--font-sans);font-size:var(--t-lead);font-weight:600;
  letter-spacing:-.01em;color:var(--shell-text-strong);
  border-bottom:1px solid var(--shell-line);
}
.sidebar-logo .dot{
  width:9px;height:9px;margin-right:var(--s3);border-radius:50%;
  background:var(--brand-500);box-shadow:0 0 0 5px rgba(62,154,106,.14);
}
.sidebar nav{padding:var(--s3) var(--s3);flex:1;overflow-y:auto;}
.sidebar .nav-item{
  display:flex;align-items:center;gap:var(--s3);
  padding:var(--s3) var(--s3);margin-bottom:2px;
  border-radius:var(--r1);border-left:2px solid transparent;
  color:var(--shell-text);font-size:var(--t-base);
  cursor:pointer;transition:background .16s ease,color .16s ease;
}
.sidebar .nav-item .icon{
  width:1.4em;text-align:center;opacity:.72;font-size:var(--t-meta);
}
.sidebar .nav-item:hover{
  background:rgba(255,255,255,.06);color:var(--shell-text-strong);transform:none;
}
.sidebar .nav-item.active{
  background:rgba(62,154,106,.14);       /* 旧的实心琥珀底 + 黑字，对比不足 */
  border-left-color:var(--brand-500);
  color:var(--shell-text-strong);font-weight:500;
}
.sidebar .nav-item.active .icon{opacity:1;}
.sidebar-footer{padding:var(--s3);border-top:1px solid var(--shell-line);}
.sidebar-footer button{
  width:100%;padding:var(--s3);border-radius:var(--r1);
  border:1px solid var(--shell-line);background:transparent;
  color:var(--shell-text);font-size:var(--t-meta);cursor:pointer;
  transition:background .16s ease,color .16s ease;
}
.sidebar-footer button:hover{background:rgba(255,255,255,.07);color:var(--shell-text-strong);}

.main-content{
  margin-left:var(--sidebar-w);min-height:100vh;
  display:flex;flex-direction:column;
  transition:margin-left .28s var(--ease-out-quart);
}
.sidebar.collapsed + .main-content{margin-left:0;}
/* ⚠️ 选择器用 `.main-content > .topbar` 而不是 `.topbar`：
   monitor.html 里的 .topbar 是它自己的页头（非固定、不贴顶），
   写成裸 .topbar 会把那个页面也变成 sticky 顶栏 —— 属于跨页面污染。 */
.main-content > .topbar{
  position:sticky;top:0;z-index:50;height:var(--header-h);
  display:flex;align-items:center;gap:var(--s4);
  padding:0 var(--s5);
  background:var(--surface-panel);         /* 旧的 rgba(255,255,255,.88) 半透明：对比度随底层浮动 */
  border-bottom:1px solid var(--line-2);
}
.topbar .icon-button{
  width:34px;height:34px;border-radius:var(--r1);
  border:1px solid var(--line-3);background:var(--surface-panel);
  color:var(--ink-700);font-size:var(--t-lead);line-height:1;cursor:pointer;
  transition:background .16s ease;
}
.topbar .icon-button:hover{background:var(--surface-soft);}
.breadcrumb{font-size:var(--t-meta);color:var(--ink-500);}
.breadcrumb span{color:var(--ink-900);font-weight:500;}
.breadcrumb a{color:var(--brand-700);}
.page-content{padding:var(--layout-gap);flex:1;}

/* ── 面板：白卡 + 1px 轮廓（不靠重阴影，投影上阴影会糊）───────────────────── */
.panel{
  background:var(--surface-panel);
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  box-shadow:var(--shadow-1);
  margin-bottom:var(--layout-gap);
  overflow:hidden;
}
.panel-header{
  padding:var(--s4) var(--s5);
  border-bottom:1px solid var(--line-1);
  font-size:var(--t-lead);font-weight:600;color:var(--ink-900);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s4);
  flex-wrap:wrap;
}
.panel-body{padding:var(--s5);}
/* 面板内不再套卡片 —— 层次用间距与分隔线表达 */

/* ── 表格：删竖线，分组靠表头底色 ─────────────────────────────────────────── */
table{width:100%;border-collapse:collapse;font-size:var(--t-base);}
th{
  background:var(--surface-soft);
  color:var(--ink-900);font-weight:500;font-size:var(--t-meta);
  text-align:left;padding:var(--s3) var(--s4);
  border-bottom:1px solid var(--line-2);
  white-space:nowrap;
}
td{
  padding:var(--s3) var(--s4);
  border-bottom:1px solid var(--line-1);
  color:var(--ink-700);
  vertical-align:middle;
}
tr:last-child td{border-bottom:none;}
tbody tr{transition:background .12s ease;}
tbody tr:hover{background:var(--brand-050);}
td.n,th.n{text-align:right;}
.empty-row td{color:var(--ink-400);text-align:center;padding:var(--s6);}

/* ── 徽标：圆点 + 文字，13px 不加粗（旧的 11px 加粗胶囊在后排看不见）──────── */
.badge{
  display:inline-flex;align-items:center;gap:var(--s2);
  padding:2px var(--s3);border-radius:999px;
  font-size:var(--t-meta);font-weight:500;line-height:1.5;
  border:1px solid transparent;white-space:nowrap;
}
.badge::before{
  content:'';width:6px;height:6px;border-radius:50%;
  background:currentColor;flex:0 0 auto;
}
.badge-online,.badge-bound{background:var(--ok-bg);color:var(--ok);border-color:var(--brand-100);}
.badge-offline{background:var(--surface-soft);color:var(--ink-500);border-color:var(--line-1);}
.badge-unbound{background:var(--warn-bg);color:var(--warn);border-color:#EFDCBE;}
.badge-danger,.badge-alarm{background:var(--danger-bg);color:var(--danger);border-color:#EED2CF;}
.badge-info{background:var(--info-bg);color:var(--info);border-color:#D3E0F2;}
/* 无圆点的纯文字徽标（保留原 class，仅改色值） */
.badge-plain::before{display:none;}

/* ── 按钮：主操作唯一，绿底白字 ───────────────────────────────────────────── */
.btn,button{font-family:inherit;font-size:var(--t-base);cursor:pointer;}
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);
  padding:var(--s2) var(--s4);border-radius:var(--r1);
  border:1px solid var(--line-3);background:var(--surface-panel);color:var(--ink-700);
  font-weight:500;line-height:1.5;
  transition:background .16s ease,border-color .16s ease,color .16s ease;
}
.btn:hover{background:var(--surface-soft);border-color:var(--ink-300);}
.btn-primary,.btn-primary:hover{
  background:var(--brand-600);border-color:var(--brand-600);color:#fff;font-weight:500;
  box-shadow:none;                        /* 去掉旧的彩色发光阴影 */
}
.btn-primary:hover{background:var(--brand-700);border-color:var(--brand-700);}
.btn-danger{background:var(--danger);border-color:var(--danger);color:#fff;}
.btn:disabled,.btn[disabled]{opacity:.5;cursor:not-allowed;}
.icon-button,.back-btn{
  border:1px solid var(--line-3);background:var(--surface-panel);color:var(--ink-700);
  border-radius:var(--r1);transition:background .16s ease;
}
.icon-button:hover,.back-btn:hover{background:var(--surface-soft);}

/* ── 分页 ───────────────────────────────────────────────────────────────────── */
.pagination{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;}
.pagination button{
  min-width:32px;height:32px;padding:0 var(--s2);
  border:1px solid var(--line-3);border-radius:var(--r1);
  background:var(--surface-panel);color:var(--ink-700);
  font-family:var(--font-mono);font-size:var(--t-meta);
  transition:background .16s ease,border-color .16s ease;
}
.pagination button:hover:not(:disabled){background:var(--surface-soft);}
.pagination button.active{
  background:var(--brand-600);border-color:var(--brand-600);color:#fff;font-weight:500;
}
.pagination button:disabled{opacity:.45;cursor:not-allowed;}

/* ── 表单控件：边界用 3:1 的 --line-3（AA 要求 UI 边界 3:1）───────────────── */
input,select,textarea{
  font-family:inherit;font-size:var(--t-base);color:var(--ink-700);
  background:var(--surface-panel);
  border:1px solid var(--line-3);border-radius:var(--r1);
  padding:var(--s2) var(--s3);
  transition:border-color .16s ease,box-shadow .16s ease;
}
input::placeholder,textarea::placeholder{color:var(--ink-400);}  /* 占位文字也要 4.5:1 */
input:focus,select:focus,textarea:focus{
  outline:none;border-color:var(--brand-600);
  box-shadow:0 0 0 3px rgba(47,129,88,.16);
}
label{font-size:var(--t-meta);color:var(--ink-500);}
.search-wrap input{padding-left:var(--s4);}

/* ── 首页数据卡：数字是主角，单位退居次要 ─────────────────────────────────── */
.home-summary{background:transparent;}
.home-summary-card,
.home-summary-carousel-card,
.home-summary-card-inner{
  background:var(--surface-panel);
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  box-shadow:var(--shadow-1);
}
.home-summary-card.alarm{border-color:#EED2CF;}
.home-summary-card.online{border-color:var(--brand-100);}
.home-summary-label{
  font-size:var(--t-meta);color:var(--ink-400);font-weight:400;letter-spacing:0;
}
.home-summary-value{
  color:var(--ink-900);font-size:var(--t-num);font-weight:600;
  line-height:1;letter-spacing:-.02em;
}
/* 单位必须独立成 span（不要把单位拼进 value 字符串）—— 单独设字号与颜色 */
.home-summary-value .home-summary-unit{
  font-family:var(--font-sans);font-size:.32em;font-weight:500;
  color:var(--ink-400);margin-left:.35em;letter-spacing:0;
}
.home-summary-meta{font-size:var(--t-meta);color:var(--ink-400);}
.home-summary-dot{background:var(--line-2);transition:background .2s ease,width .2s ease;}
.home-summary-dot.active{background:var(--brand-600);}

/* ── 设备详情页：基础数据栏 ───────────────────────────────────────────────── */
.device-basic-header{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);flex-wrap:wrap;
}
.info-grid .info-item .value{
  font-family:var(--font-mono);font-size:var(--t-base);font-weight:600;color:var(--ink-900);
}
.info-grid .info-item .label,.info-grid .info-item span{color:var(--ink-400);}
.stat-card{background:var(--surface-panel);border:1px solid var(--line-2);border-radius:var(--r2);}
.stat-card .sc-num{color:var(--ink-900);font-weight:600;}
.stat-card .sc-icon{color:var(--brand-600);}
.param-card .p-value{font-family:var(--font-mono);color:var(--brand-700);font-weight:600;}

/* ── 详情页折叠区（每 2 秒被重渲染，改这块前先读 MEMORY 的滚动锚点一节）────── */
.detail-collapse-header{
  background:var(--surface-soft);
  border:1px solid var(--line-1);border-radius:var(--r1);
  color:var(--ink-900);font-weight:500;font-size:var(--t-base);
  transition:background .16s ease;
}
.detail-collapse-header:hover{background:var(--brand-050);}
.detail-collapse-icon{color:var(--ink-400);}
.detail-group-card{background:transparent;}
.detail-group-title{
  font-size:var(--t-meta);font-weight:500;color:var(--ink-400);
  letter-spacing:.02em;
}

/* ── 登录页 ─────────────────────────────────────────────────────────────────── */
#loginPage{background:var(--shell);}
.login-box{
  background:var(--surface-panel);border-radius:var(--r3);
  box-shadow:var(--shadow-3);padding:var(--s8) var(--s7);
}
.login-box h2{
  font-family:var(--font-sans);font-size:var(--t-h2);font-weight:600;
  letter-spacing:-.018em;color:var(--ink-900);
}
.login-box .sub{color:var(--ink-400);font-size:var(--t-meta);}
.login-box input{border-color:var(--line-3);padding:var(--s3) var(--s4);}
.login-box button{
  width:100%;min-height:44px;padding:var(--s3);
  background:var(--brand-600);color:#fff;border:none;border-radius:var(--r1);
  font-size:var(--t-base);font-weight:500;
  transition:background .16s ease;
}
.login-box button:hover{background:var(--brand-700);}

/* ── 弹窗 / 照片查看器 ─────────────────────────────────────────────────────── */
.photo-dialog,.modal-backdrop{
  background:rgba(11,21,16,.62);           /* 显式遮罩色，不用通用半透明 */
}
.photo-card{background:var(--surface-panel);border-radius:var(--r2);}
.photo-folder{background:var(--surface-soft);border-radius:var(--r1);}

/* ── 地图与占位 ─────────────────────────────────────────────────────────────── */
.map-wrap{background:var(--surface-panel);border:1px solid var(--line-2);border-radius:var(--r2);}
.map-placeholder{background:var(--surface-soft);color:var(--ink-400);border-radius:var(--r1);}
.map-wrap .map-title a{color:var(--brand-700);}
.location-info span{color:var(--ink-400);font-size:var(--t-meta);}

/* ── 动效：只做状态过渡，用指数缓出；不做布局属性动画 ─────────────────────── */
@media (prefers-reduced-motion: no-preference){
  .home-summary-value,.stat-card .sc-num{
    transition:color .3s var(--ease-out-quart);
  }
  tbody tr,.panel,.badge,.btn{
    transition:background-color .16s ease,border-color .16s ease,color .16s ease;
  }
}
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.01ms !important;transition-duration:.01ms !important;}
}

/* ── 大屏适配：改一处根字号，全站等比放大（这就是"大屏与笔记本兼顾"的解法）──
   刻意不用 clamp() 流体字号 —— 数据密集后台用固定刻度 + 断点，
   否则"这个宽度能放几列"不可预测，而演示最怕临场折行。
   ⚠️ 只有 rem 写法的规则会跟着变；legacy 里的 px 规则不受影响（它们是安全网）。 */
@media (min-width:1440px){ html{font-size:16px;} }
@media (min-width:1680px){ html{font-size:17px;} }
@media (min-width:1920px){ html{font-size:18px;} }

/* ── 窄屏：侧边栏让位 ──────────────────────────────────────────────────────── */
@media (max-width:900px){
  .main-content{margin-left:0;}
  .sidebar{box-shadow:var(--shadow-3);}
}

/* ============================================================================
   关键不变量 —— 在这里重申一遍（legacy 里也有，两份都留着）
   理由：将来删掉 legacy 文件时，这两条必须仍在，否则滚动跳动会原地复发。
   ============================================================================ */
/* 这四个容器每 2 秒被 innerHTML='' 整块替换，浏览器滚动锚点会随之消失 →
   scrollY 跳 2400px+（用户看到"展开单体数据后被强制往上滑"）。关闭锚点。
   注意：只关"整块替换"的容器，父级 .page-content 保持 auto。 */
#deviceBasicData,
#deviceInfoCards,
#deviceUnitTables,
#deviceUnitChart{overflow-anchor:none;}
.page-content{overflow-anchor:auto;}

/* ============================================================================
   设备详情页 · 基础数据区（2026-09-20 第三版：状态前置）
   ============================================================================
   信息层级按"读这一屏的顺序"排：
     ① 顶栏   任务名称 / 作业时长 / 下种量 / 口肥量 / 底肥量
     ② 状态带 系统工作状态（底色=状态）+ 机具/开关/GPS/肥箱 chip
     ③ 主区   成果卡（播种面积，主数字） | 工况区（130px 圆环 + 5 个读数格）

   三版演进（每版都是被具体问题推着走的，别再走回头路）：
     v1 圆环 480px 独占主位 —— 12 个数据点里它只占 1 个，却吃 60% 面积（用户："设计不合理"）
     v2 6 个读数竖排一列去撑满圆环高度 —— 空洞没了，但圆环仍是主角、左列变 6 张竖卡
     v3 圆环缩到 130px 挪进工况区最左；状态从右列提到第一行（用户选定"方案一"）

   ⚠️⚠️ 本文件整份排在 _legacy-index.css **之后**，会压掉 legacy 里同特异度的
         **所有规则，包括 legacy 的媒体查询**。所以窄屏重排必须在本文件里
         逐级重新声明，且**由宽到窄排列**（同特异度时后写的胜）——
         这是 v1 就踩过、写进 MEMORY 的坑。
   ============================================================================ */

/* ── 状态带 ────────────────────────────────────────────────────────────────── */
.basic-statebar{
  display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;
  padding:11px 18px;
  border-bottom:1px solid var(--line-2);
  background:var(--surface-soft);           /* 未知态：中性，不报警 */
}
.basic-statebar.ok{background:var(--ok-bg);}
.basic-statebar.danger{background:var(--danger-bg);}
.basic-state-head{display:flex;align-items:baseline;gap:var(--s2);min-width:0;flex-wrap:wrap;}
.basic-state-ico{font-size:1rem;line-height:1;color:var(--ink-400);}
.basic-statebar.ok .basic-state-ico{color:var(--ok);}
.basic-statebar.danger .basic-state-ico{color:var(--danger);}
.basic-state-head strong{font-size:var(--t-lead);font-weight:700;color:var(--ink-900);white-space:nowrap;}
.basic-statebar.ok .basic-state-head strong{color:var(--ok);}
.basic-statebar.danger .basic-state-head strong{color:var(--danger);}
.basic-state-head em{font-style:normal;font-size:var(--t-cap);font-weight:700;color:var(--ink-500);white-space:nowrap;}
.basic-chips{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;margin-left:auto;}
.basic-chip{
  display:inline-flex;align-items:baseline;gap:5px;
  padding:4px 10px;border:1px solid var(--line-2);border-radius:999px;
  background:var(--surface-panel);
  color:var(--ink-500);font-size:var(--t-cap);white-space:nowrap;
}
.basic-chip b{font-weight:700;color:var(--ink-900);}
/* warn 只染值不染底：一排 chip 全染红会变成"哪儿都报警"，分不出轻重 */
.basic-chip.is-warn b{color:var(--warn);}

/* ── 主区：成果卡 | 工况区 ───────────────────────────────────────────────────
   ⚠️ 成果卡这一列是 **fit-content 贴内容**，不是固定比例（原为 .9fr）。
      原因（2026-10-08，用户截图）：比例分配下成果卡宽 557px，而它的内容
      （「34.87 亩」+ 备注，最宽的是大数字）只需 277px —— **卡内右侧空出 301px 死区**，
      而右邻的 5 个读数格被挤到每格 165px。左边一大块空白、右边挤，视觉上完全失衡。
      fit-content 让成果卡"够用即止"，省下的宽度全给工况区；工况区内部是
      `auto + repeat(5,1fr)`，于是这份宽度**被 5 个读数格等分** —— 这正是
      "把空白平均掉"的意思：不是消掉，是从"卡里一大块"变成"每格均摊一点"。
   ⚠️ `fit-content(24rem)` 的上限是**防线不是主力**：正常情况下永不生效
      （内容宽 277px < 24rem=408px@17px根字号）。它挡的是"面积值异常长"时
      成果卡无限变胖、把读数格挤到 0 的情况。worked_area 走 `fmtNum(v,2)`，
      正常是 5~7 字符（约 277~355px），离上限还有余量。
      真超了会由 `.basic-yield{overflow:hidden}` 在卡内裁掉，不会压到工况区。
   ⚠️ 1520px 以下本列被单列规则覆盖（见下方媒体查询），这条只在两列时生效。 */
.basic-main{
  display:grid;grid-template-columns:fit-content(24rem) minmax(0,1fr);
  gap:14px;padding:18px 20px 20px;align-items:stretch;
}
.basic-yield{
  position:relative;overflow:hidden;
  display:flex;flex-direction:column;justify-content:center;
  padding:16px 22px;
  border:1px solid var(--line-2);border-radius:var(--r2);
  background:
    radial-gradient(130% 150% at 0% 0%, var(--brand-050), transparent 62%),
    var(--surface-panel);
}
/* 左侧 2px 强调线：不靠颜色也能认出"这是本次作业成果" */
.basic-yield::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--brand-600);}
.basic-yield-label{display:flex;align-items:center;gap:6px;color:var(--ink-400);font-size:var(--t-meta);font-weight:700;}
.basic-yield-num{
  display:flex;align-items:baseline;gap:.3rem;margin-top:6px;white-space:nowrap;
  color:var(--brand-700);
  font-family:var(--font-mono);font-size:4rem;font-weight:700;line-height:1;
  font-variant-numeric:tabular-nums lining-nums;
  font-feature-settings:'cv11' 1,'zero' 0,'tnum' 1;
}
.basic-yield-num small{color:var(--ink-400);font-family:var(--font-sans);font-size:var(--t-lead);font-weight:700;}
.basic-yield-note{margin-top:var(--s2);color:var(--ink-400);font-size:var(--t-cap);font-style:normal;}

/* ── 工况区：圆环 + 5 个读数格 ─────────────────────────────────────────────── */
.basic-cells{
  min-width:0;display:grid;align-items:center;
  grid-template-columns:auto repeat(5,minmax(0,1fr));
  border:1px solid var(--line-2);border-radius:var(--r2);
  background:var(--surface-panel);overflow:hidden;
  container-type:inline-size;              /* 供下面按容器宽反算字号 */
}
.basic-gauge-cell{
  display:flex;flex-direction:column;align-items:center;gap:6px;
  padding:10px 16px;border-right:1px solid var(--line-1);
}
/* 圆环下方原来那一行「量程 0—15」的样式（.basic-gauge-scale）已随该行删除 ——
   留着会是"有规则但没元素"的死规则：测试若断言它，就会把死代码一直锁住。 */
/* ── 参数图标（2026-09-20 新增）─────────────────────────────────────────────
   内联 SVG，描边走 currentColor —— 与所在标签同色（贴标签行时即 --ink-400），
   不额外引入颜色，也就不会出现"标签灰、图标绿"的层级割裂。
   flex:none 必加：标签行是 nowrap，图标若可压缩会被挤成一条缝。
   标签行随之从 block 改成 flex（见 .basic-cell > span / .basic-yield-label），
   否则图标会与文字基线错位。 */
.basic-ico{flex:none;}
/* overflow:hidden 是**溢出防线**，不是根治 —— 根治是 index.html 的 fmtNum() 把显示精度收住。
   2026-09-23：终端报上来一个 double 全精度的速度（0.014410000294446945），
   读数原样渲染后比格子宽得多，在 nowrap 下直接盖到隔壁格上（GPS速度 盖住了 雷达速度 整段）。
   外层 .basic-cells 的 overflow:hidden 拦不住这种"格内溢出到隔壁" ——
   它只裁整排的外框，格子里溢出的内容照样压在相邻格上。必须每格自己裁。 */
.basic-cell{min-width:0;overflow:hidden;padding:10px 13px;border-left:1px solid var(--line-1);}
.basic-cell:first-child{border-left:0;}
.basic-cell > span{
  display:flex;align-items:center;gap:5px;
  color:var(--ink-400);font-size:var(--t-cap);font-weight:700;white-space:nowrap;
}
.basic-cell > strong{
  display:flex;align-items:baseline;gap:.25rem;margin-top:5px;white-space:nowrap;
  color:var(--ink-900);
  font-family:var(--font-mono);font-size:2.25rem;font-weight:600;
  font-variant-numeric:tabular-nums lining-nums;
  font-feature-settings:'cv11' 1,'zero' 0,'tnum' 1;
}
/* ⚠️ 字号必须按**容器宽**反算，不能按视口写死。
   5 格横排时每格可用宽 = (100cqw − 162(圆环格+) − 5(分隔线)) / 5 − 26(padding)
                        = 20cqw − 59.4px；
   最长读数是速度「36.29 km/h」5 字符；留一位余量按 **6 字符** 算：
     数字 6 × 0.6em = 3.6F ＋ 间隙 .25rem(4.25px) ＋ 单位「km/h」(0.58em 字号 × ≈2.1em) ≈ 1.22F
     ⇒ 4.82F ≤ 20cqw − 59.4 ⇒ **font-size ≤ (20cqw − 64px) / 4.82**
   ⚠️ 上限 2.4rem（根字号 17px 时 ≈ 40.8px，是播种面积主数字 4rem 的 60%）。
      2026-10-08 从 22px 提到 2.4rem：成果卡改成贴内容后工况区变宽（1900 视口 991→1269px），
      每格 165→221px，而 22px 的读数只占 105px —— **每格空 ~90px**，比成果卡那 301px 死区只是
      换了个地方摆。放大到 ~40px 后典型读数占 ~170px（填 88%），余量留在"6 字符"那一档。
      上限用 rem 不用 px：≥1920 根字号变 18px，主数字跟着长，读数不跟就会被反衬成小字。
      ⚠️ 这一条**上限比 22px 时代宽松得多**，所以底下那句 `overflow:hidden`（.basic-cell）
        不再只是理论防线：真出现 7 字符的异常读数会被裁。这是有意的取舍 —— 靠 `fmtNum(v,2)`
        保证常规 5 字符、留一位余量，比为了不裁而把读数一直缩着更划算。
   实测（2026-10-08 `_basic_layout_probe.js`）：1600 视口 → 29.3px / 1900 → 39.5px，
   两处典型读数都填到 88%，横向溢出均为 0。 */
@supports (font-size: 1cqw){
  .basic-cell > strong{font-size:clamp(14px, calc((20cqw - 64px) / 4.82), 2.4rem);}
}
/* ⚠️ 单位必须跟着读数走（`.58em` 而不是固定 `.75rem`）：
   读数 22px 时代 0.75rem(12.75px) 比例是 0.58；读数涨到 40px 后固定 12.75px 就只剩 0.32，
   单位会缩成读数旁边一行蚊子腿。写成 em 后比例恒定，改上限时不用再动这里。 */
.basic-cell > strong small{color:var(--ink-400);font-family:var(--font-sans);font-size:.58em;font-weight:700;}

/* ── 半圆环（2026-10-10 从"整圆环"改过来）───────────────────────────────────
   两个改动一起做，它们其实是同一件事的两面：

   ① **形状**：整圆 → 上半圆。
   ② **读数位置**：从"环内居中"挪到"弧的下方"。
      原因是内圈孔径 = 半径 − 厚度。130px 宽的环厚 24px 时孔径只有 41px 高，
      装不下「标签 16 + 数字 26 + 单位 14 = 56px」；要装下必须把环加到 170px 宽，
      格子会从 163px 涨到约 202px，右边 5 个读数格各少约 8px —— 收益不值这个代价。
   ③ **不再画进度弧**：原来那条 `conic-gradient(from 210deg, … 0 238deg, …)` 是
      **写死的 238°**，全项目没有任何 JS 改它（grep `conic-gradient` / `--gauge` /
      `setProperty` 零命中）—— 也就是说 0.00 km/h 时它照样画满 66%，是个
      **永远虚报同一个值**的刻度盘。配色上用户也不要那两色
      （绿 #2F8158 刻度 + 橙 #d9912b 弧，后者 theme.css 已标为废弃色）。
      只留刻度纹理后：不再暗示任何数值，且整环只剩墨色一个色系。

   ⚠️ 半圆怎么画：容器 130×65，`border-radius:999px 999px 0 0` 得到**上半圆盘**，
      再用 `mask:radial-gradient(circle at 50% 100%, transparent 0 R, #000 R)` 挖掉内圈。
      整圆时代是用"内圈盖一个白圆"实现的，半圆**没法用白圆盖**（圆盖不住直边），
      所以这里换 mask，`-webkit-mask` 要一起写。
   ⚠️ `container-type:inline-size` 必须留着 —— `.basic-gauge-core strong` 的 `22cqw`
      就是靠它才有参照物（22cqw × 130px = 28.6px）。
   ⚠️ 这条块里的 `width:130px` 被 `_mutate_basic_totals.py` 的变异盯着（改成 480px
      必须让"圆环宽度已缩到 130px"失败），改块内写法时要同步那条变异串。 */
.basic-gauge{
  width:130px;min-width:130px;
  /* 覆盖 legacy 的整圆盘：width min(232px,72%) / aspect-ratio:1 / border-radius:50%
     / 四层 background / box-shadow。只留宽度与容器查询。 */
  display:flex;flex-direction:column;align-items:center;
  aspect-ratio:auto;
  border-radius:0;
  background:none;
  box-shadow:none;
  container-type:inline-size;
}
/* 半圆刻度弧。`::before` 参与 flex 布局：占前 65px 高，下面的 core 自然落到弧下方。
   ⚠️ 只有刻度纹理、**没有进度弧** —— 见上面 ③。 */
.basic-gauge::before{
  content:"";flex:none;width:100%;height:65px;
  border-radius:999px 999px 0 0;
  background:repeating-conic-gradient(from 270deg,
    rgba(23,34,27,.62) 0deg 3.2deg,
    rgba(23,34,27,.10) 3.6deg 7deg,
    transparent 7.4deg 9deg);
  -webkit-mask:radial-gradient(circle at 50% 100%, transparent 0 41px, #000 41px);
          mask:radial-gradient(circle at 50% 100%, transparent 0 41px, #000 41px);
}
/* 读数：去掉 legacy 的"白圆内圈"（74% 宽高 + 圆角 + 边框 + 白底），
   宽度放开到 100% 让数字有位置。 */
.basic-gauge-core{
  width:100%;height:auto;margin-top:6px;
  border:0;border-radius:0;background:transparent;
}
/* 圆环内的「速度 ECU」标签行改成 flex，给前面的芯片图标留位。
   ⚠️ 原注释写「图标取 11px 而不是 13px —— 13px 会把标签挤到折行」，**这条是错的**：
      实测内圈 96px，13px 下标签盒只有 60px（图标 13 + gap 3 + 文字 44），到 16px 都不折行。
      而 11px 让图标笔画跌进亚像素（1.9×11/24 ≈ 0.87 设备像素）→ 糊成一团。
      2026-10-10 已改成 13px，顺便与其它读数格的图标同尺寸。
      复核对折行的量测：node build/harness/_basic_layout_probe.js 1900 →「环内标签」那行。 */
.basic-gauge-core span{display:flex;align-items:center;gap:3px;font-size:10px;}
.basic-gauge-core strong{font-size:clamp(20px, 22cqw, 40px);}   /* 覆盖 legacy 的 @supports 版 */
.basic-gauge-core em{margin-top:5px;font-size:10px;}

/* ── 顶部任务概览栏（v1 引入，v2/v3 沿用）─────────────────────────────────── */

/* ── 顶部任务概览栏 ────────────────────────────────────────────────────────── */
.basic-task-strip{
  display:grid;
  /* 任务名 1.7 份：它是唯一长度不可控的一格（实测 18 字符，理论可更长） */
  grid-template-columns:minmax(0,1.7fr) repeat(4,minmax(0,1fr));
  gap:10px;
  padding:14px 20px;
  border-bottom:1px solid var(--line-2);
  background:linear-gradient(180deg,var(--brand-050),var(--surface-panel) 78%);
}
.basic-task-strip > div{
  min-width:0;
  display:flex;
  flex-direction:column;
  justify-content:center;
  gap:.25rem;
  padding:.5625rem .875rem;
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  background:var(--surface-panel);
}
.basic-task-strip span{
  color:var(--ink-400);
  font-size:var(--t-meta);
  font-weight:700;
  white-space:nowrap;
}
.basic-task-strip strong{
  display:flex;
  align-items:baseline;
  gap:.25rem;
  min-width:0;
  color:var(--ink-900);
  font-family:var(--font-mono);
  font-size:1.5rem;
  font-weight:600;
  line-height:1.2;
  font-variant-numeric:tabular-nums lining-nums;
  font-feature-settings:'cv11' 1,'zero' 0,'tnum' 1;
  white-space:nowrap;
}
/* 单位单独用 <small>，绝不拼进数值 —— 与 basicMetric / readRow 同一个约定 */
.basic-task-strip strong small{
  color:var(--ink-400);
  font-family:var(--font-sans);
  font-size:var(--t-meta);
  font-weight:700;
}
/* 任务名是**标识符不是读数**：换正文字体、字号降一档，与右侧四个数字拉开层级。
   长度不可控，必须 ellipsis 截断（title 属性兜底看全量，见 index.html）。 */
.basic-task-strip .is-name strong{
  display:block;
  font-family:var(--font-sans);
  font-size:1.0625rem;
  font-weight:700;
  letter-spacing:-.01em;
  overflow:hidden;
  text-overflow:ellipsis;
}
/* 下种量沿用主读数绿调（原来在 .basic-read-row.is-primary 上，随下种量一起上移到此） */
.basic-task-strip .is-primary{background:var(--brand-050);border-color:#CFE3D2;}
.basic-task-strip .is-primary strong{color:var(--brand-700);}

/* ── 主区折行 / 图标隐藏：两处断点都是按"工况格能否放下标签"反算的 ─────────────
   工况格标签行 = 图标(13) + gap(5) + 最长标签「播种机角度」5 个汉字。
   --t-cap = .8rem ≈ 12.8px → 5 字 ≈ 64px；加图标后一格需要 108px
   （内容 82 + 左右 padding 26）。
   格宽 = (工况区宽 − 167) / 5（167 = 圆环格 162 + 5 条分隔线），所以：
     · 显示图标   → 工况区 ≥ 5×108 + 167 = 707px
     · 隐藏图标   → 工况区 ≥ 5×90  + 167 = 617px
   ≥1920px 时根字号变 18px（`html{font-size:18px}`），标签涨到约 72px，
   需要工况区 ≥ 747px —— 那时工况区有约 950px，仍然够。

   ① 两列主区里工况区只拿 64% 宽：
      视口 1521 → 面板 1225 → 工况区 692（<710 → 图标隐藏，可用 79 ≥ 64 ✓）
      视口 1520 起改单列，工况区直接拿整宽（1520 → 1184），反而更宽松。
      所以"两列不再划算"的转折点是 1520，而不是按经验拍的 1180。
   ② 图标按**容器**宽隐藏（工况区 <710），不按视口 ——
      决定放不下的是工况区自身宽度，而它与视口的关系还取决于主区是几列。 */
@media (max-width:1520px){
  .basic-main{grid-template-columns:minmax(0,1fr);}
}
@container (max-width:710px){
  .basic-cell .basic-ico{display:none;}
}
/* 5 格并排时任务名只有约 190px，18 字符会被截到看不清 → 让它独占一行，四个读数排一行。
   （任务栏的折行阈值与主区无关，仍是 1180，别跟着上面那条一起改。） */
@media (max-width:1180px){
  .basic-task-strip{grid-template-columns:repeat(4,minmax(0,1fr));}
  .basic-task-strip .is-name{grid-column:1 / -1;}
}
/* 下面三块是本文件的窄屏重排，**必须由宽到窄排列**。
   （legacy 里那几档对应的规则会随旧结构一起删除，所以这里是唯一定义处。） */
@media (max-width:1000px){
  /* 圆环 + 5 格横排在这里必须折成 2 列（圆环独占一行居中，读数格 2+2+1，
     末格跨满整行免得留孤格）。
     阈值从 980 提到 1000：**卡住它的不是数值行而是标签行**。
     901~1000 视口时侧边栏还在（248px），面板只剩 605~704px，
     单列主区下工况区 565~664px → 每格 79.6~99.4px → 可用 53.6~73.4px，
     而「播种机角度」5 个字就要 64px —— 这一段原本就在临界以下（图标已按容器隐藏）。
     1000 起折 2 列后每格 199px 起，两种行都宽松。 */
  .basic-cells{grid-template-columns:repeat(2,minmax(0,1fr));}
  .basic-gauge-cell{grid-column:1 / -1;border-right:0;border-bottom:1px solid var(--line-1);}
  .basic-cell{border-top:1px solid var(--line-1);}
  /* 第 1 个子元素是圆环格，所以 .basic-cell 里"偶序号"（第 2/4/6 个子元素）落在左列 */
  .basic-cell:nth-child(even){border-left:0;}
  .basic-cell:nth-child(-n+3){border-top:0;}   /* 紧跟圆环格的那一行不重复画上边框 */
  .basic-cell:last-child{grid-column:1 / -1;border-left:0;}
}
@media (max-width:620px){
  .basic-task-strip{grid-template-columns:repeat(2,minmax(0,1fr));}
  .basic-task-strip strong{font-size:1.25rem;}
  .basic-yield-num{font-size:3rem;}
  .basic-chips{margin-left:0;width:100%;}
}
@media (max-width:480px){
  .basic-cells{grid-template-columns:minmax(0,1fr);}
  .basic-cell{border-left:0;}
  .basic-cell:last-child{grid-column:auto;}
  .basic-yield-num{font-size:2.5rem;}
}
