/* ============================================================
   静家居数字化工厂驾驶舱 - 大屏视觉
   科技大屏 / 霓虹发光 / 玻璃拟态 / HUD 装饰
   ============================================================ */
/* 主题基色：值写成 "r,g,b"，所以描边/光晕能直接 rgba(var(--rgb-main),.3)。
   下面只是兜底默认（科技蓝），正常情况下由 inc/theme.php 注入同名变量覆盖。 */
:root{
  --rgb-main:34,211,238;
  --rgb-sub:59,130,246;
  --rgb-accent:167,139,250;
  --bg:#03060f;
  --bg2:#071026;
  --panel:#0b1730;
  --deep:#081026;
  --text:#e8f1ff;
  --text2:#cdd9ef;
  --muted:#7f97c4;
  --ok:#34d399;
  --warn:#f59e0b;
  --danger:#fb7185;
  --rgb-ok:52,211,153;
  --rgb-warn:245,158,11;
  --rgb-danger:251,113,133;
  /* —— 以下由基色派生，换主题时自动跟随，无需单独配 —— */
  --line:rgba(var(--rgb-sub),.28);
  --line-strong:rgba(var(--rgb-main),.55);
  --title:rgba(var(--rgb-main),.86);
  --glow-cyan:0 0 18px rgba(var(--rgb-main),.45);
  --glow-blue:0 0 22px rgba(var(--rgb-sub),.4);
  /* 旧变量名兼容 */
  --panel-solid:var(--panel);
  --cyan:rgb(var(--rgb-main));
  --blue:rgb(var(--rgb-sub));
  --green:var(--ok);
  --orange:var(--warn);
  --red:var(--danger);
  --purple:rgb(var(--rgb-accent));
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  font-family:-apple-system,"Microsoft YaHei","Segoe UI",sans-serif;
  color:var(--text);
  background:var(--bg);
  overflow:hidden;
  -webkit-font-smoothing:antialiased;
  transition:background .6s ease,color .6s ease;
}

/* ---------- 背景层 ---------- */
#bgCanvas{position:fixed;inset:0;width:100%;height:100%;z-index:0;opacity:.55;transition:opacity .6s}
.bg-grid{
  position:fixed;inset:-40% -40% -40% -40%;z-index:0;pointer-events:none;
  background-image:
    linear-gradient(rgba(var(--rgb-sub),.10) 1px,transparent 1px),
    linear-gradient(90deg,rgba(var(--rgb-sub),.10) 1px,transparent 1px);
  background-size:52px 52px;
  transform:perspective(600px) rotateX(58deg);
  animation:gridMove 18s linear infinite;
  mask-image:radial-gradient(ellipse at 50% 40%,#000 20%,transparent 72%);
  -webkit-mask-image:radial-gradient(ellipse at 50% 40%,#000 20%,transparent 72%);
}
@keyframes gridMove{from{background-position:0 0}to{background-position:52px 52px}}
.bg-glow{
  position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(1100px 560px at 78% -12%,rgba(var(--rgb-sub),.22),transparent 60%),
    radial-gradient(900px 520px at 6% 108%,rgba(var(--rgb-main),.16),transparent 60%),
    radial-gradient(700px 400px at 50% 50%,rgba(var(--rgb-accent),.08),transparent 70%);
  animation:breathe 12s ease-in-out infinite;
}
@keyframes breathe{0%,100%{opacity:.82}50%{opacity:1}}
.scanline{
  position:fixed;left:0;right:0;top:-20%;height:22%;z-index:1;pointer-events:none;
  background:linear-gradient(180deg,transparent,rgba(var(--rgb-main),.06),rgba(var(--rgb-main),.14),transparent);
  animation:scan 7.5s linear infinite;
}
@keyframes scan{from{top:-25%}to{top:110%}}
.vignette{
  position:fixed;inset:0;z-index:1;pointer-events:none;
  box-shadow:inset 0 0 220px rgba(0,0,0,.85);
}

/* ---------- 布局 ---------- */
#app{
  position:relative;z-index:2;display:flex;flex-direction:column;
  height:100vh;padding:12px 16px;gap:11px;
}
/* 大屏开机：各区块自上而下依次浮现 */
#app > *{animation:riseIn .7s cubic-bezier(.22,1,.36,1) both}
#app > *:nth-child(1){animation-delay:.05s}
#app > *:nth-child(2){animation-delay:.16s}
#app > *:nth-child(3){animation-delay:.26s}
#app > *:nth-child(4){animation-delay:.34s}
@keyframes riseIn{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}

/* ---------- 顶部 ---------- */
/* 三栏用 grid 而不是 flex：1fr auto 1fr 的两侧永远等宽，
   中间大标题就是页面的正中，不用再靠"左右固定等宽"去凑居中。 */
.hud-header{
  position:relative;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  /* 高度让内容说了算：右上角那列的行数（刷新设置 / 干支三柱 / 日期时钟）决定实际高度。
     写死数值的话，再加一行就又会被下面的 overflow:hidden 裁掉。 */
  min-height:88px;padding:0 26px;border-radius:6px;overflow:hidden;
  background:linear-gradient(90deg,rgba(var(--rgb-main),.10),rgba(var(--rgb-sub),.06) 40%,rgba(var(--rgb-accent),.10));
  border:1px solid var(--line);
  box-shadow:inset 0 0 40px rgba(var(--rgb-main),.08),0 6px 30px rgba(0,0,0,.45);
}
.hud-header::before,.hud-header::after{
  content:"";position:absolute;left:6%;right:6%;height:2px;border-radius:2px;
  background:linear-gradient(90deg,transparent,rgb(var(--rgb-main)),transparent);
  filter:drop-shadow(0 0 6px rgb(var(--rgb-main)));
}
.hud-header::before{top:-1px}
.hud-header::after{bottom:-1px;opacity:.6}
/* 顶栏缓慢掠过的整片高光 */
.hud-header .hud-sheen{
  position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(100deg,transparent 35%,rgba(var(--rgb-main),.10) 48%,rgba(var(--rgb-main),.16) 50%,rgba(var(--rgb-main),.10) 52%,transparent 65%);
  transform:translateX(-130%);
  animation:sheen 9s ease-in-out infinite;
}
@keyframes sheen{0%{transform:translateX(-130%)}55%,100%{transform:translateX(130%)}}
.corner{position:absolute;width:16px;height:16px;border:2px solid rgb(var(--rgb-main));opacity:.85;animation:cornerPulse 3.4s ease-in-out infinite}
.corner.tl{left:-1px;top:-1px;border-right:0;border-bottom:0}
.corner.tr{right:-1px;top:-1px;border-left:0;border-bottom:0;animation-delay:.4s}
.corner.bl{left:-1px;bottom:-1px;border-right:0;border-top:0;animation-delay:1.2s}
.corner.br{right:-1px;bottom:-1px;border-left:0;border-top:0;animation-delay:1.8s}
@keyframes cornerPulse{0%,100%{opacity:.5}50%{opacity:1}}

/* 左上角：天气（原来在右上角，用户要求换到左边并放大；自动刷新设置已移到右上角） */
.hud-left{min-width:0;display:flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:6px}
.brand-mark{
  width:38px;height:38px;flex:none;position:relative;border-radius:8px;
  background:conic-gradient(from 0deg,rgba(var(--rgb-main),.25),rgba(var(--rgb-sub),.55),rgba(var(--rgb-accent),.3),rgba(var(--rgb-main),.25));
  box-shadow:var(--glow-cyan);
}
.brand-mark::before{
  content:"";position:absolute;inset:3px;border-radius:6px;background:var(--bg2);
}
.brand-mark::after{
  content:"";position:absolute;inset:11px;border-radius:3px;
  background:linear-gradient(135deg,rgb(var(--rgb-main)),rgb(var(--rgb-sub)));
  box-shadow:var(--glow-cyan);animation:spinPulse 4s ease-in-out infinite;
}
@keyframes spinPulse{0%,100%{transform:rotate(0);opacity:.85}50%{transform:rotate(90deg);opacity:1}}
.brand-mark.mirror::after{animation-direction:reverse}
.brand-mark.mirror{transform:scaleX(-1)}
/* 大标题：渐变字 + 缓慢流光扫过 */
.brand-text h1{
  font-size:42px;letter-spacing:8px;font-weight:800;line-height:1.15;
  background:linear-gradient(100deg,
    rgb(var(--rgb-main)) 0%,
    rgba(var(--rgb-main),.72) 28%,
    rgb(var(--rgb-accent)) 50%,
    rgba(var(--rgb-main),.72) 72%,
    rgb(var(--rgb-main)) 100%);
  background-size:220% 100%;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  filter:drop-shadow(0 0 18px rgba(var(--rgb-main),.55));
  animation:textFlow 8s linear infinite;
}
@keyframes textFlow{from{background-position:0% 50%}to{background-position:220% 50%}}
.brand-text .sub{font-size:12.5px;color:var(--muted);letter-spacing:3.5px;margin-top:5px}

.hud-center{justify-self:center;display:flex;align-items:center;justify-content:center;gap:18px;position:relative;z-index:2}
.hud-center .brand-text{text-align:center}

/* 右上角一列放两行：①自动刷新设置 ②左边「干支三柱 + 日期」一列，右边并排大时钟 */
.hud-right{min-width:0;display:flex;flex-direction:column;align-items:flex-end;justify-content:center;gap:5px}
/* 干支/日期/时间同排：左边那一小列两行右对齐，大时钟在它右侧垂直居中 */
.hud-right .clock-row{display:flex;align-items:center;gap:14px}
.hud-right .date-col{display:flex;flex-direction:column;align-items:flex-end;gap:3px}
.hud-right .clock{
  font-size:32px;font-weight:800;letter-spacing:3px;font-variant-numeric:tabular-nums;
  color:var(--text);text-shadow:0 0 16px rgba(var(--rgb-main),.9),0 0 34px rgba(var(--rgb-main),.5);
}
.hud-right .date{font-size:12.5px;color:var(--muted);letter-spacing:1.5px}

/* 干支三柱（年柱/月柱/日柱）：跟日期同列，压在日期上面一行。
   三柱各一个颜色（年青 / 月紫 / 日金），固定色不跟主题变量走 —— 换色板时也得看得出是「彩色」的；
   亮度比时钟低一档，别抢「时间」的视觉重心。 */
.hud-right .gz-row{
  display:flex;align-items:baseline;gap:13px;
  font-size:14px;font-weight:700;letter-spacing:1.5px;line-height:1.25;
}
.hud-right .gz{white-space:nowrap}
.hud-right .gz i{font-style:normal;font-size:10.5px;font-weight:400;margin-left:1px;opacity:.7}
.hud-right .gz-y{color:#22d3ee;text-shadow:0 0 10px rgba(34,211,238,.55)}
.hud-right .gz-m{color:#c084fc;text-shadow:0 0 10px rgba(192,132,252,.55)}
.hud-right .gz-d{color:#ffc957;text-shadow:0 0 10px rgba(255,201,87,.55)}

/* 左侧天气胶囊：整体放大（工位远处也能看清），原来是右上角的小胶囊 */
.weather{
  display:inline-flex;align-items:center;gap:13px;max-width:100%;
  padding:9px 24px 9px 17px;border-radius:32px;font-size:19px;color:var(--text2);
  background:linear-gradient(90deg,rgba(var(--rgb-main),.16),rgba(var(--rgb-sub),.06));
  border:1px solid rgba(var(--rgb-main),.32);
  box-shadow:inset 0 0 16px rgba(var(--rgb-main),.14),0 2px 10px rgba(0,0,0,.35);
  transition:opacity .3s;
}
.weather.off{opacity:.55}
.weather .w-ico{display:inline-flex;align-items:center;color:rgb(var(--rgb-main));filter:drop-shadow(0 0 6px rgba(var(--rgb-main),.75))}
.weather .w-ico svg{width:30px;height:30px}
.weather .w-temp{font-size:30px;font-weight:800;color:var(--text);letter-spacing:.5px;text-shadow:0 0 14px rgba(var(--rgb-main),.75)}
.weather .w-desc{font-size:19px;color:var(--text2);white-space:nowrap}
.weather .w-range{color:var(--muted);font-size:16px;padding-left:12px;border-left:1px solid rgba(var(--rgb-main),.28);white-space:nowrap}

/* 右上角「自动刷新」间隔下拉 + 上次刷新时间（选完写回后台默认值，见 api/interval.php） */
.hud-refresh{
  display:inline-flex;align-items:center;gap:7px;max-width:100%;
  padding:3px 10px 3px 9px;border-radius:20px;font-size:12px;color:var(--text2);
  background:linear-gradient(90deg,rgba(var(--rgb-sub),.14),rgba(var(--rgb-main),.05));
  border:1px solid rgba(var(--rgb-main),.28);
  box-shadow:inset 0 0 16px rgba(var(--rgb-main),.12),0 2px 10px rgba(0,0,0,.3);
}
.hud-refresh .ico-slot{margin-right:0}
/* 胶囊里的状态灯：绿=取数正常，红=异常（颜色由 cockpit.js 的 setStatus 改） */
.hud-refresh .dot{flex:none;width:8px;height:8px;margin-right:1px}
.hud-refresh .rf-sel{
  flex:none;padding:1px 4px 1px 6px;font:inherit;font-size:12px;letter-spacing:.5px;
  color:var(--bright);cursor:pointer;outline:none;
  background:rgba(var(--rgb-main),.12);border:1px solid var(--line-strong);border-radius:5px;
  box-shadow:0 0 10px rgba(var(--rgb-main),.14) inset;
  transition:border-color .2s,color .2s;
}
.hud-refresh .rf-sel:hover{border-color:rgb(var(--rgb-main));color:var(--text)}
.hud-refresh .rf-sel option{background:var(--deep);color:var(--text2)}
.hud-refresh .rf-last{color:var(--muted);font-size:11.5px;letter-spacing:.4px;white-space:nowrap}
.hud-refresh .rf-last b{color:var(--text2);font-weight:600;font-variant-numeric:tabular-nums}

/* 通用图标挂载点（页头/页脚等静态区域） */
.ico-slot{display:inline-flex;align-items:center;margin-right:6px}
.ico-slot svg{width:14px;height:14px;color:rgb(var(--rgb-main));filter:drop-shadow(0 0 5px rgba(var(--rgb-main),.6))}
.dot{
  width:9px;height:9px;border-radius:50%;background:var(--ok);
  box-shadow:0 0 10px var(--ok);animation:blink 1.6s ease-in-out infinite;
}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.35}}

/* ---------- KPI ---------- */
/* 一排放 5 个：报工 2 张 + 任务状态 3 张正好铺满一整排，不会有落单的空档；
   与下面 ≤1700px 的断点保持一致的节奏（≤1200px 再降到 3 个一行）。 */
.kpi-row{display:grid;gap:14px;grid-template-columns:repeat(5,1fr)}
.kpi{
  position:relative;overflow:hidden;border-radius:11px;padding:16px 20px 14px 22px;
  background:linear-gradient(160deg,rgba(var(--rgb-sub),.34),rgba(var(--bg2),.72));
  border:1px solid var(--line);
  box-shadow:inset 0 0 26px rgba(var(--rgb-main),.06),0 6px 22px rgba(0,0,0,.4);
  transition:transform .25s ease,border-color .25s ease,box-shadow .25s ease;
  animation:kpiIn .6s cubic-bezier(.22,1,.36,1) both;
  animation-delay:calc(var(--i,0) * 70ms + .28s);
}
@keyframes kpiIn{from{opacity:0;transform:translateY(14px) scale(.97)}to{opacity:1;transform:none}}
.kpi::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:4px;
  background:linear-gradient(180deg,var(--kpi-c,rgb(var(--rgb-main))),var(--kpi-c-soft,rgb(var(--rgb-sub))));
  box-shadow:var(--glow-cyan);
}
.kpi::after{
  content:"";position:absolute;right:-36px;top:-36px;width:118px;height:118px;border-radius:50%;
  background:radial-gradient(circle,rgba(var(--rgb-main),.22),transparent 70%);
  opacity:.7;transition:opacity .25s;
}
/* 鼠标划过时横向掠过的光带 */
.kpi .sweep{
  position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(100deg,transparent 40%,rgba(var(--rgb-main),.16) 50%,transparent 60%);
  transform:translateX(-130%);
}
.kpi:hover .sweep{animation:sweepOnce .75s ease}
@keyframes sweepOnce{from{transform:translateX(-130%)}to{transform:translateX(130%)}}
.kpi:hover{
  transform:translateY(-3px);
  border-color:var(--line-strong);
  box-shadow:inset 0 0 32px rgba(var(--rgb-main),.14),0 10px 30px rgba(0,0,0,.55),var(--glow-blue);
}
.kpi:hover::after{opacity:1}
.kpi .ico{
  position:absolute;right:14px;top:14px;width:30px;height:30px;opacity:.85;
  color:rgb(var(--rgb-main));filter:drop-shadow(0 0 6px rgba(var(--rgb-main),.6));
}
.kpi .label{font-size:14.5px;color:var(--muted);letter-spacing:.5px;white-space:nowrap}
.kpi .val{
  display:block;font-size:40px;font-weight:800;margin-top:9px;line-height:1.05;
  font-variant-numeric:tabular-nums;color:#fff;
  text-shadow:0 0 18px var(--kpi-c-glow,rgba(var(--rgb-main),.55));
}
/* 数值刷新时闪一下，远处也能一眼看出「刚变了」 */
.kpi .val.bump{animation:valBump .7s ease}
@keyframes valBump{
  0%{transform:scale(1);text-shadow:0 0 18px var(--kpi-c-glow,rgba(var(--rgb-main),.55))}
  35%{transform:scale(1.09);text-shadow:0 0 30px var(--kpi-c,rgba(var(--rgb-main),.95))}
  100%{transform:scale(1);text-shadow:0 0 18px var(--kpi-c-glow,rgba(var(--rgb-main),.55))}
}
/* 单位：所有 KPI 统一用主题主色（原来跟随各卡片的 --kpi-c，未开始/进行中/已完成三色不一） */
.kpi .unit{font-size:15.5px;color:rgb(var(--rgb-main));margin-left:5px;font-weight:600}
.kpi .tail{font-size:12.5px;color:var(--muted);margin-top:7px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 任务类 KPI：大数字 + 百分比圆环并排；下面一行小字（已报工/计划）；末行是统计口径。
   主色由前端按状态注入（--kpi-c/--kpi-c-soft/--kpi-c-glow），没有注入时回落到主题主色。 */
.kpi .val-row{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:9px}
.kpi .val-box{display:flex;align-items:baseline;gap:5px;min-width:0}
.kpi .val-row .val{display:inline-block;margin-top:0}
.kpi .val-tag{font-size:12.5px;color:var(--muted);font-weight:600;white-space:nowrap}
.kpi .ring{position:relative;width:50px;height:50px;flex:none}
.kpi .ring svg{display:block;width:50px;height:50px;transform:rotate(-90deg)}
.kpi .ring circle{fill:none;stroke-width:5.5}
.kpi .ring .r-bg{stroke:var(--kpi-c-soft,rgba(var(--rgb-main),.16))}
.kpi .ring .r-fg{
  stroke:var(--kpi-c,rgb(var(--rgb-main)));stroke-linecap:round;
  filter:drop-shadow(0 0 6px var(--kpi-c-glow,rgba(var(--rgb-main),.7)));
  transition:stroke-dasharray .9s cubic-bezier(.22,1,.36,1);
}
.kpi .ring .r-txt{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:13px;font-weight:800;color:#fff;font-variant-numeric:tabular-nums;
  text-shadow:0 0 10px var(--kpi-c-glow,rgba(var(--rgb-main),.7));
}
.kpi .sub{font-size:12.5px;color:var(--muted);margin-top:5px;font-variant-numeric:tabular-nums}
.kpi .sub b{font-size:14px;font-weight:700;color:#e9f1ff}
.kpi .sub .su{color:var(--kpi-c,rgb(var(--rgb-main)));margin-left:3px}
.kpi.warn::before{background:linear-gradient(180deg,var(--warn),var(--danger))}
.kpi.warn .val{text-shadow:0 0 16px rgba(var(--rgb-danger),.6)}
.kpi.warn .ico{color:var(--warn)}

/* ---------- 内容区 ---------- */
.content{display:flex;gap:11px;flex:1;min-height:0}
.main{flex:3.05;display:grid;gap:11px;grid-template-columns:repeat(2,1fr);grid-auto-rows:1fr;min-height:0}
.side{flex:1;display:flex;flex-direction:column;gap:11px;min-height:0}
/* 侧栏是一列：卡片按比例分整列高度（明细表 1.6 份，下面的图表各 1 份 → 排行 → 明细 → 占比），
   明细表内部自己滚动。这里必须用 flex-basis:0 —— 用 auto 的话，明细表 50 行的内容高度
   会把下面的图表卡按比例压成一条线，ECharts 没有高度可画，卡片看着像坏了。
   多给高度的按「内容」认（谁带表格谁吃这 1.6 份），不按位置认 ——
   明细表在侧栏里的排序是可调的（默认排在「员工报工排行」下面）。 */
.side > .card{flex:1 1 0}
.side > .card:has(.tbl-wrap){flex:1.6 1 0}

.card{
  position:relative;display:flex;flex-direction:column;min-height:0;border-radius:8px;overflow:hidden;
  background:linear-gradient(165deg,rgba(var(--rgb-sub),.18),rgba(var(--bg2),.7));
  border:1px solid var(--line);
  box-shadow:inset 0 0 30px rgba(var(--rgb-main),.05),0 6px 26px rgba(0,0,0,.4);
  transition:border-color .3s ease,box-shadow .3s ease;
  animation:cardIn .55s cubic-bezier(.22,1,.36,1) both;
  animation-delay:calc(var(--i,0) * 80ms);
}
@keyframes cardIn{from{opacity:0;transform:translateY(18px) scale(.985)}to{opacity:1;transform:none}}
.card:hover{
  border-color:var(--line-strong);
  box-shadow:inset 0 0 34px rgba(var(--rgb-main),.10),0 10px 30px rgba(0,0,0,.5);
}
/* 数据自动刷新时：只做轻微淡入，不重播入场位移，避免每 30 秒整屏闪一下 */
.card.is-refresh,.kpi.is-refresh{animation:dataFade .3s ease both;animation-delay:0s}
@keyframes dataFade{from{opacity:.45}to{opacity:1}}
/* 卡片内缓慢游走的光带：静态画面也有「在动」的感觉，各卡片错开起始时间 */
.card .sweep{
  position:absolute;inset:0;pointer-events:none;z-index:1;
  background:linear-gradient(102deg,transparent 38%,rgba(var(--rgb-main),.07) 48%,rgba(var(--rgb-main),.12) 50%,rgba(var(--rgb-main),.07) 52%,transparent 62%);
  transform:translateX(-130%);
  animation:cardSweep 11s ease-in-out infinite;
  animation-delay:calc(var(--i,0) * 1.6s);
}
@keyframes cardSweep{0%{transform:translateX(-130%)}45%,100%{transform:translateX(130%)}}
.card::before,.card::after{
  content:"";position:absolute;width:14px;height:14px;border:2px solid var(--line-strong);pointer-events:none;z-index:3;
}
.card::before{left:2px;top:2px;border-right:0;border-bottom:0}
.card::after{right:2px;bottom:2px;border-left:0;border-top:0}
.card h3{
  position:relative;z-index:2;display:flex;align-items:center;gap:9px;
  font-size:13.5px;color:var(--title);padding:9px 16px 9px 14px;letter-spacing:1.2px;
  border-bottom:1px solid var(--line);
  background:linear-gradient(90deg,rgba(var(--rgb-main),.12),transparent 70%);
}
.card h3::before{
  content:"";width:4px;height:14px;border-radius:2px;flex:none;
  background:linear-gradient(180deg,rgb(var(--rgb-main)),rgb(var(--rgb-sub)));
  box-shadow:var(--glow-cyan);
}
.card h3 .h3-ico{
  width:16px;height:16px;flex:none;color:rgb(var(--rgb-main));opacity:.92;
  filter:drop-shadow(0 0 6px rgba(var(--rgb-main),.6));
}
.card .body{flex:1;min-height:0;padding:6px 8px 8px;position:relative;z-index:2}
.chart{width:100%;height:100%}
.tag-live{
  margin-left:auto;font-size:10.5px;color:var(--ok);letter-spacing:.5px;
  display:inline-flex;align-items:center;gap:5px;font-weight:500;
}
.tag-live::before{
  content:"";width:6px;height:6px;border-radius:50%;background:var(--ok);
  box-shadow:0 0 8px var(--ok);animation:blink 1.6s infinite;
}
/* 图表标题右侧的时间范围下拉（今日 / 近 7 日 / 近 30 天） */
.card h3 .range-sel{
  flex:none;margin-left:10px;padding:2px 4px 2px 6px;font:inherit;font-size:11.5px;
  letter-spacing:.5px;color:var(--bright);cursor:pointer;outline:none;
  background:rgba(var(--rgb-main),.10);border:1px solid var(--line-strong);border-radius:5px;
  box-shadow:0 0 10px rgba(var(--rgb-main),.16) inset;
  transition:border-color .2s,color .2s;
}
.card h3 .range-sel:hover{border-color:rgb(var(--rgb-main));color:var(--text)}
.card h3 .range-sel option{background:var(--deep);color:var(--text2)}
/* 时间范围写回后台的轻提示：选完在下半屏闪一下，告知已存成默认值 */
.range-toast{
  position:fixed;left:50%;bottom:7vh;z-index:99;
  padding:9px 18px;font-size:12.5px;letter-spacing:.6px;color:var(--text);
  background:var(--panel);border:1px solid var(--line-strong);border-radius:8px;
  box-shadow:0 0 22px rgba(var(--rgb-main),.28);
  opacity:0;pointer-events:none;transform:translate(-50%,12px);
  transition:opacity .24s,transform .24s;
}
.range-toast.show{opacity:1;transform:translate(-50%,0)}

/* ---------- 表格 ----------
   明细表是站在车间里远处看的，字号按大屏放大；默认最近 50 条，
   超出卡片高度时由 .tbl-wrap 滚动（表头 sticky 不会跟着滚走）。
   滚动本身由 cockpit.js 的 autoScrollTables() 以 16px/s 慢速自动推进（鼠标悬停暂停）。 */
.tbl-wrap{overflow:auto;flex:1;min-height:0;overscroll-behavior:contain}
/* 字号按大屏给足；不给换行，日期/工序名被折成两行比塞不下更难看 ——
   万一窄屏真放不下，就由 .tbl-wrap 横向滚动兜底。 */
table.grid{width:100%;border-collapse:collapse;font-size:16px;white-space:nowrap}
table.grid th{
  position:sticky;top:0;z-index:2;text-align:left;font-weight:600;letter-spacing:.5px;
  padding:9px 12px;color:var(--bright);
  /* 表头必须完全不透明：上面那层渐变是半透明色，只给渐变的话，滚到表头底下的行会透上来，
     看着像文字叠在一起。所以在渐变下面再压一层不透明的 --panel 当底色。 */
  background:linear-gradient(180deg,rgba(var(--rgb-sub),.42),var(--panel)),var(--panel);
  border-bottom:1px solid var(--line-strong);
  box-shadow:0 8px 14px -10px rgba(0,0,0,.85);
  text-shadow:0 0 10px rgba(var(--rgb-main),.5);
}
table.grid td{padding:9px 12px;color:var(--text2);border-bottom:1px solid rgba(var(--rgb-sub),.16)}
table.grid tbody tr{transition:background .18s;animation:rowIn .36s ease both;animation-delay:calc(var(--i,0) * 28ms)}
table.grid tbody tr:nth-child(n+14){animation-delay:0s}
@keyframes rowIn{from{opacity:0;transform:translateX(-8px)}to{opacity:1;transform:none}}
table.grid tbody tr:nth-child(even) td{background:rgba(var(--rgb-main),.03)}
table.grid tbody tr:hover td{background:rgba(var(--rgb-main),.14);color:#fff}
.tbl-wrap::-webkit-scrollbar{width:7px;height:7px}
.tbl-wrap::-webkit-scrollbar-thumb{background:linear-gradient(180deg,rgb(var(--rgb-main)),rgb(var(--rgb-sub)));border-radius:6px}

/* ---------- 滚动列表 ---------- */
.scroll-list{overflow:hidden;flex:1;position:relative;mask-image:linear-gradient(180deg,transparent,#000 6%,#000 94%,transparent);-webkit-mask-image:linear-gradient(180deg,transparent,#000 6%,#000 94%,transparent)}
.scroll-list ul{list-style:none;animation:scrollUp 24s linear infinite}
.scroll-list li{
  padding:8px 12px;font-size:13px;color:var(--text2);
  border-bottom:1px dashed rgba(var(--rgb-sub),.28);
  transition:background .2s,color .2s;
  animation:fadeUp .42s ease both;animation-delay:calc(var(--i,0) * 40ms);
}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.scroll-list li:hover{background:rgba(var(--rgb-main),.08);color:#fff}
.scroll-list li b{color:var(--bright)}
.scroll-list:hover ul{animation-play-state:paused}
@keyframes scrollUp{from{transform:translateY(0)}to{transform:translateY(-50%)}}

/* ---------- 空态 ---------- */
.empty{color:var(--muted);font-size:13px;padding:26px;text-align:center;line-height:1.9}
.empty-ico{
  display:block;width:36px;height:36px;margin:0 auto 10px;
  color:rgb(var(--rgb-main));opacity:.45;
  filter:drop-shadow(0 0 10px rgba(var(--rgb-main),.5));
  animation:floatY 3.4s ease-in-out infinite;
}
@keyframes floatY{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}

/* ---------- 底部 ---------- */
.hud-foot{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  height:30px;padding:0 20px;border-radius:6px;font-size:12px;color:var(--muted);
  background:linear-gradient(90deg,rgba(var(--rgb-main),.07),rgba(var(--rgb-sub),.04),rgba(var(--rgb-accent),.07));
  border:1px solid var(--line);
}
/* 左下角：#编号 · 设备号（原来这里是 SYSTEM ONLINE 字样） */
.hud-foot .foot-l{
  color:var(--ok);display:inline-flex;align-items:center;gap:8px;min-width:0;
  font-family:ui-monospace,Consolas,monospace;font-weight:700;letter-spacing:1px;
}
.hud-foot .foot-l #footDev{
  color:var(--ok);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  text-shadow:0 0 10px rgba(var(--rgb-ok),.45);
}
.hud-foot .foot-c{display:inline-flex;align-items:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.hud-foot .foot-r{display:inline-flex;align-items:center;font-variant-numeric:tabular-nums;color:rgb(var(--rgb-main))}
.hud-foot .foot-r .ico-slot svg{width:13px;height:13px}
.pulse{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok);animation:blink 1.6s infinite}

/* ---------- 响应式收缩 ---------- */
/* 阈值 1700：大于它才用 42px 大标题（标题块 550+2×38+2×18 = 662）。
   三栏是 1fr auto 1fr，中间那栏先按内容撑开，两侧不够时才会挤到标题折行，
   所以这里只需要盯住标题本身的高度预算。 */
@media (max-width:1700px){
  .kpi-row{grid-template-columns:repeat(5,1fr);gap:11px}
  .kpi{padding:14px 16px 13px 18px}
  .kpi .val{font-size:32px}
  .kpi .label{font-size:13px}
  .kpi .ring,.kpi .ring svg{width:46px;height:46px}   /* 窄屏卡片变矮，圆环跟着收，别把卡片顶高 */
  .kpi .ring .r-txt{font-size:12px}
  .brand-text h1{font-size:32px;letter-spacing:5px}
  .hud-header{min-height:80px}
  .hud-right .clock{font-size:27px}
  .hud-right .date{font-size:11.5px}
  .weather{font-size:14px;gap:9px;padding:6px 16px 6px 12px;border-radius:24px}
  .weather .w-ico svg{width:23px;height:23px}
  .weather .w-temp{font-size:23px}
  .weather .w-desc{font-size:14px}
  .weather .w-range{font-size:13px;padding-left:9px}
  .hud-refresh .rf-last{display:none}   /* 窄屏优先保住「下拉」本身 */
  /* 明细表不换行（见 table.grid 的注释），侧栏变窄后靠缩字号继续塞得下 */
  table.grid{font-size:14px}
  table.grid th,table.grid td{padding:8px 10px}
}
@media (max-width:1200px){
  .kpi-row{grid-template-columns:repeat(3,1fr)}
  .kpi{padding:12px 14px 11px 16px}
  .kpi .val{font-size:26px}
  .kpi .label{font-size:12.5px}
  .content{flex-direction:column}
  .main{grid-template-columns:1fr}
  .hud-center{gap:10px}
  .brand-mark{display:none}
  .brand-text h1{font-size:26px;letter-spacing:3px}
  .brand-text .sub{display:none}
  .weather{font-size:12.5px;gap:8px;padding:5px 13px 5px 10px;border-radius:20px}
  .weather .w-ico svg{width:20px;height:20px}
  .weather .w-temp{font-size:20px}
  .weather .w-desc{font-size:12.5px}
  .weather .w-range{display:none}
  .hud-right .clock{font-size:22px;letter-spacing:2px}
  .hud-right .date{display:none}
  table.grid{font-size:13px}
  table.grid th,table.grid td{padding:7px 8px}
}

/* ---------- 虚拟车间 · 俯视平面图 ---------- */
.card--scene{grid-column:1 / -1;grid-row:span 2;box-shadow:inset 0 0 40px rgba(var(--rgb-main),.07),0 6px 26px rgba(0,0,0,.4)}
/* 整行显示的图表卡（mapping.wide=true，如「员工报工排行」）：
   主区是两列网格，卡片数为单数时最后一格会空着，铺满一行把这块补上。 */
.card--wide{grid-column:1 / -1}
.card--scene .body{padding:0}
.scene-body{position:relative;overflow:hidden;background:radial-gradient(120% 90% at 28% 0%,rgba(var(--rgb-sub),.30),rgba(var(--bg),.2) 62%)}
.scene-svg{width:100%;height:100%}

/* 传送带流向 */
.fp-belt{stroke-dasharray:10 12;animation:fpFlow 1.1s linear infinite}
@keyframes fpFlow{to{stroke-dashoffset:-22}}
.fp-chev{fill:none;stroke:rgba(var(--rgb-main),.85);stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}

/* 工位设备块 */
.fp-station .fp-box{transition:filter .25s,opacity .25s}
.fp-station.off .fp-box{opacity:.7}
.fp-station:hover .fp-box{filter:drop-shadow(0 0 12px rgba(var(--rgb-main),.45))}
.fp-status circle{animation:fpLed 2.2s ease-in-out infinite}
.fp-station.off .fp-status circle{animation:none;opacity:.85}
@keyframes fpLed{0%,100%{opacity:.45}50%{opacity:1}}
.fp-num{font-variant-numeric:tabular-nums;text-shadow:0 0 14px rgba(var(--rgb-main),.5)}

/* 被前端强调整组替换（切时间范围）时，工位块整体淡入，避免“硬跳”。
   注意：这里只能动 opacity。工位块的位置是 SVG 的 transform="translate(x,y)" 属性，
   而 CSS 动画/过渡的 transform 优先级高于该属性（fill-mode:both 的终态也会一直生效），
   一旦在这里写 transform，属性会被覆盖成 none，所有工位叠在原点、看上去只剩一个。 */
.fp-station{animation:stationIn .5s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--i,0) * 34ms)}
@keyframes stationIn{from{opacity:0}to{opacity:1}}

/* ---------- 设备授权遮罩 ----------
   大屏默认「关闭」：没被管理员同意的设备只看到这一屏 + 一个授权二维码，
   整屏盖住且不拉任何数据。尺寸按 vmin 走，1080p 上二维码约 500px，站远处也能扫。 */
body.gated #app{visibility:hidden}
.gate{
  position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
  /* overflow + 卡片 margin:auto：横屏手机这类矮屏上卡片本来就装不下（二维码按 vmin 撑得很大），
     没有这两句，超出部分会被裁在屏幕外、连扫码都够不着。 */
  padding:3vh 4vw;overflow:auto;
  background:
    radial-gradient(900px 600px at 50% 38%,rgba(var(--rgb-sub),.20),transparent 66%),
    radial-gradient(760px 520px at 50% 106%,rgba(var(--rgb-main),.14),transparent 70%),
    rgba(3,6,15,.95);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  animation:gateIn .4s ease both;
}
.gate[hidden]{display:none}
@keyframes gateIn{from{opacity:0}to{opacity:1}}
.gate-card{
  position:relative;display:flex;flex-direction:column;align-items:center;gap:2vh;margin:auto;
  padding:3vh 4vw 2.4vh;border-radius:12px;text-align:center;
  background:linear-gradient(180deg,rgba(var(--rgb-main),.10),rgba(var(--rgb-sub),.04) 46%,rgba(var(--rgb-accent),.09)),var(--deep);
  border:1px solid var(--line-strong);
  box-shadow:0 26px 80px rgba(0,0,0,.62),inset 0 0 70px rgba(var(--rgb-main),.08);
  max-width:min(94vw,760px);
}
.gate-card::before,.gate-card::after{
  content:"";position:absolute;left:7%;right:7%;height:2px;border-radius:2px;
  background:linear-gradient(90deg,transparent,rgb(var(--rgb-main)),transparent);
  filter:drop-shadow(0 0 6px rgb(var(--rgb-main)));
}
.gate-card::before{top:-1px}
.gate-card::after{bottom:-1px;opacity:.6}

.gate-head{display:flex;align-items:center;gap:14px;text-align:left}
.gate-lock{
  display:inline-flex;align-items:center;justify-content:center;width:46px;height:46px;flex:none;
  border-radius:50%;color:rgb(var(--rgb-main));
  background:rgba(var(--rgb-main),.12);border:1px solid rgba(var(--rgb-main),.45);
  box-shadow:0 0 22px rgba(var(--rgb-main),.35),inset 0 0 16px rgba(var(--rgb-main),.18);
}
.gate-lock svg{width:23px;height:23px}
.gate-title h2{font-size:22px;letter-spacing:2px;color:var(--text);font-weight:700}
.gate-sub{font-size:11px;letter-spacing:2.4px;color:var(--muted);margin-top:5px}

.gate-qr{
  width:min(46vmin,46vh,520px);height:min(46vmin,46vh,520px);
  padding:14px;border-radius:10px;background:#fff;
  box-shadow:0 0 0 1px rgba(var(--rgb-main),.55),0 0 40px rgba(var(--rgb-main),.34);
}
.gate-qr svg{display:block;width:100%;height:100%}
.gate-qr-err{display:flex;align-items:center;justify-content:center;height:100%;color:#b3261e;font-size:14px;line-height:1.7}

.gate-tip{font-size:15px;line-height:1.95;color:var(--text2)}
.gate-tip b{color:rgb(var(--rgb-main))}

.gate-state{
  display:inline-flex;align-items:center;gap:9px;padding:8px 20px;border-radius:24px;
  font-size:14px;color:var(--ok);background:rgba(var(--rgb-ok),.12);border:1px solid rgba(var(--rgb-ok),.34);
}
.gate-state.bad{color:var(--danger);background:rgba(var(--rgb-danger),.12);border-color:rgba(var(--rgb-danger),.38)}

/* 编号 + 设备号：手机扫码后要拿它跟授权页上的两行大字对一下，别扫了隔壁那块屏。
   所以这里不做成小灰字，而是编号做成亮色药丸、设备号加大加发光。 */
.gate-meta{display:flex;flex-direction:column;align-items:center;gap:9px;font-size:12px;color:var(--muted)}
.gate-devno{display:inline-flex;align-items:center;gap:10px;font-size:13px;letter-spacing:2px;color:var(--muted)}
.gate-devno b{
  display:inline-flex;align-items:center;justify-content:center;min-width:52px;padding:2px 14px;border-radius:22px;
  font-family:ui-monospace,Consolas,monospace;font-size:19px;font-weight:800;letter-spacing:1px;
  color:#03121b;background:rgb(var(--rgb-main));box-shadow:0 0 22px rgba(var(--rgb-main),.6);
}
.gate-devid{
  max-width:100%;font-family:ui-monospace,Consolas,monospace;
  font-size:min(2.6vmin,26px);font-weight:800;letter-spacing:1.5px;line-height:1.35;
  color:rgb(var(--rgb-main));word-break:break-all;
  text-shadow:0 0 18px rgba(var(--rgb-main),.75);
}
.gate-screen{font-family:ui-monospace,Consolas,monospace;color:var(--muted)}
.gate-url{
  max-width:100%;font-size:11px;line-height:1.6;word-break:break-all;color:rgba(127,151,196,.75);
  font-family:ui-monospace,Consolas,monospace;
}
/* 演示入口：跟本机授权无关，所以单独压一条分隔线、居中一行，
   既好找又不至于被当成「允许展示」之类的操作按钮。 */
.gate-demo{
  align-self:stretch;display:flex;align-items:center;justify-content:center;gap:9px;
  margin-top:.2vh;padding-top:1.7vh;border-top:1px solid rgba(var(--rgb-main),.22);
  font-size:13.5px;color:var(--muted);text-decoration:none;transition:color .2s;
}
.gate-demo svg{width:17px;height:17px;flex:none;color:rgb(var(--rgb-main))}
.gate-demo b{color:rgb(var(--rgb-main));font-weight:600}
.gate-demo i{font-style:normal;color:rgb(var(--rgb-main))}
.gate-demo:hover{color:var(--text2)}
.gate-demo:hover b{text-decoration:underline}
@media (max-width:900px){
  .gate-card{gap:2.6vh}
  .gate-title h2{font-size:18px}
  .gate-sub{display:none}
  .gate-qr{width:min(72vmin,320px);height:min(72vmin,320px)}
  .gate-tip{font-size:13px}
  .gate-devid{font-size:17px}
  .gate-devno b{font-size:16px;min-width:44px}
}

