/* AC Service Engineer App — zero-build mobile-first styles (no framework, no CDN) */

:root {
  --primary: #175cd3;
  --primary-dark: #12469f;
  --primary-soft: #eaf1fd;
  --bg: #f2f5fa;
  --surface: #ffffff;
  --text: #17213c;
  --text-2: #5b6577;
  --text-3: #8a93a5;
  --line: #e4e8f0;
  --success: #0e9f6e;
  --success-soft: #e6f7f0;
  --warn: #c2780b;
  --warn-soft: #fdf3e2;
  --danger: #d92d20;
  --danger-soft: #fdeceb;
  --info: #175cd3;
  --info-soft: #eaf1fd;
  --neutral: #6b7280;
  --neutral-soft: #eef0f4;
  --radius: 14px;
  --radius-s: 10px;
  --shadow: 0 1px 2px rgba(23, 33, 60, .05), 0 4px 14px rgba(23, 33, 60, .06);
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Consolas, "Courier New", monospace;
  /* 布局几何（单一来源，供运行期让位量计算与回归测试读取；见 frontend/js/layout.js）：
     --tabbar-min：底栏高度下限 = 6+6 内边距 + 图标行 22 + 文字行 ~19（不含 env(safe-area-inset-bottom)）
     --toasts-gap：.toasts 浮层**底边**距视口的偏移；死区口径是浮层**顶边** = 本值 + 一条 toast 的高度
     （layout.js TOAST_STACK_HEIGHT_FALLBACK），主操作按钮须整颗在顶边之上——只按底边算会让按钮
     落进浮层实际占据的带里（R18 S6 收口：按钮底 108 < 浮层顶 141，被盖住约 33px/44px） */
  --tabbar-min: 64px;
  --toasts-gap: 92px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
button { font-family: inherit; }
input, select, textarea { font-family: inherit; font-size: 15px; }
a { color: var(--primary); text-decoration: none; }

/* ---- App shell ----
 * padding-bottom = 底部固定栏的让位量（第三轮确认项 B2）：原先写死 76px，而栏高 = 6+6 内边距 +
 * 图标行 22 + 文字行 ~19 + `env(safe-area-inset-bottom)`，工单详情接单后页高增长时"预约"主按钮
 * 整颗落进栏下（实测 430×932 按钮 887~931 对栏顶 871，中心命中 tabbar 链接、点击跳页 POST=0）。
 * 运行期由 ui.js syncTabbarClearance() 按**实测**栏高 + `.toasts` 死区写入 --tabbar-clearance；
 * 变量未写入时退回 80px 兜底（比旧的 76px 硬编码多 4px，且不再随栏高漂移）。 */
.app-root {
  max-width: 480px; margin: 0 auto; min-height: 100vh;
  padding-bottom: max(80px, var(--tabbar-clearance, 80px));
}
/* 驱动台（桌面后管，设计约定 §2.5）无底栏（.tabbar 被隐藏），唯一遮挡物是 .toasts 浮层，
 * 其底边偏移在驱动台下是 24px（见下方 body.driver-mode .toasts）。原先此处写死 24px，
 * 等于把浮层当一条线看：比同一公式的值（max(24, 死区 24+49=73) + 净空 16 → 89px）小一条栈高，
 * App 侧（上面 .app-root 规则）与驱动台两处让位量口径分裂（R18 评审 low-6）。
 * 现统一为与 App 同源：让位量一律经 --tabbar-clearance（ui.syncTabbarClearance 按实测写入），
 * 24px 只作"脚本未跑且变量缺失"时的底线（与浮层底边同值），兜底 80px 与 layout.MIN_APP_BOTTOM_PADDING 同值。 */
body.driver-mode .app-root { max-width: 1160px; padding-bottom: max(24px, var(--tabbar-clearance, 80px)); }

.topbar {
  position: sticky; top: 0; z-index: 20;
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: #fff;
  padding: 14px 16px;
  border-radius: 0 0 18px 18px;
}
.topbar h1 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: .2px; }
.topbar .sub { margin-top: 2px; font-size: 12.5px; opacity: .85; }
.topbar .topbar-row { display: flex; align-items: center; gap: 10px; }
.topbar .back-btn {
  background: rgba(255,255,255,.16); border: none; color: #fff; width: 32px; height: 32px;
  border-radius: 50%; font-size: 18px; line-height: 1; cursor: pointer; flex: 0 0 auto;
}
.page { padding: 14px 14px 8px; }

/* ---- Bottom tab bar ---- */
.tabbar {
  position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
  width: 100%; max-width: 480px; z-index: 30;
  display: flex; background: var(--surface);
  border-top: 1px solid var(--line);
  padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
}
.tabbar[hidden] { display: none; } /* author display beats the UA [hidden] rule — restate it */
body.driver-mode .tabbar { display: none; }
.tabbar a {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  color: var(--text-3); font-size: 10.5px; text-decoration: none; padding: 4px 0; border-radius: 10px;
}
.tabbar a svg { width: 22px; height: 22px; }
.tabbar a.active { color: var(--primary); font-weight: 600; }

/* ---- Cards & sections ---- */
.card {
  background: var(--surface); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 14px 16px; margin-bottom: 12px;
}
.card h3 { margin: 0 0 10px; font-size: 13px; text-transform: uppercase; letter-spacing: .6px; color: var(--text-3); }
/* 标签-值行（我的页/返利/积分等）：**必须能换行**。
   R31 用户实测（外文界面 "Today's check-in" / "Location access" 过宽）：原 `.k { flex: 0 0 auto }`
   （标签不收缩）+ 值里的 `.badge { white-space: nowrap }`（徽章不换行）→ 两侧都压不动，
   360px 下整行挤出卡片、整页横向滚动（实测文档宽 en 376 / bn 442 / fr 502，而中文与乌尔都语因字符串短而侥幸不炸）。
   现在：行可换行（值不挤在标签旁边时落到第二行）、两侧都可收缩、值换行后仍靠右（`margin-inline-start:auto`
   是逻辑属性，ur/ar 的 RTL 下同样成立）。 */
.card .row { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 12px; padding: 5px 0; }
.card .row .k { color: var(--text-2); flex: 0 1 auto; min-width: 0; overflow-wrap: anywhere; }
.card .row .v { flex: 0 1 auto; min-width: 0; margin-inline-start: auto; text-align: right; overflow-wrap: anywhere; font-weight: 500; }
.mono { font-family: var(--mono); font-size: 13px; }

.section-title { margin: 18px 2px 8px; font-size: 13px; font-weight: 700; color: var(--text-2); }

/* ---- Badges ---- */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
.badge.b-blue { background: var(--info-soft); color: var(--info); }
.badge.b-green { background: var(--success-soft); color: var(--success); }
.badge.b-amber { background: var(--warn-soft); color: var(--warn); }
.badge.b-red { background: var(--danger-soft); color: var(--danger); }
.badge.b-gray { background: var(--neutral-soft); color: var(--neutral); }
.badge.b-cyan { background: #e3f5fb; color: #0c7a95; }
.badge.b-violet { background: #efe9fd; color: #6941c6; }

/* ---- Chips (filters) ---- */
.chips { display: flex; gap: 8px; overflow-x: auto; padding: 2px; margin-bottom: 12px; scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }
.chip {
  border: 1px solid var(--line); background: var(--surface); color: var(--text-2);
  padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 500; cursor: pointer; white-space: nowrap;
}
.chip.active { background: var(--primary); border-color: var(--primary); color: #fff; }

/* ---- Order cards ---- */
.order-card { cursor: pointer; transition: transform .06s ease; }
.order-card:active { transform: scale(.985); }
.order-card .head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.order-card .no { font-family: var(--mono); font-size: 13px; color: var(--text-2); font-weight: 600; }
.order-card .user { font-weight: 600; }
.order-card .meta { color: var(--text-2); font-size: 13px; margin-top: 2px; }
.order-card .badges { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }

/* ---- Buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--primary); color: #fff; border: none; border-radius: 12px;
  padding: 12px 18px; font-size: 15px; font-weight: 600; cursor: pointer; width: 100%;
}
.btn:active { background: var(--primary-dark); }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn.secondary { background: var(--primary-soft); color: var(--primary); }
.btn.danger { background: var(--danger); }
.btn.ghost { background: transparent; border: 1.5px solid var(--line); color: var(--text-2); }
.btn.sm { width: auto; padding: 7px 14px; font-size: 13px; border-radius: 9px; }
.btn-row { display: flex; gap: 10px; margin-top: 10px; }

/* ---- Forms ---- */
.field { margin-bottom: 12px; }
.field label { display: block; font-size: 12.5px; font-weight: 600; color: var(--text-2); margin-bottom: 5px; }
.field input[type=text], .field input[type=tel], .field input[type=number],
.field input[type=datetime-local], .field select, .field textarea {
  width: 100%; padding: 11px 12px; border: 1.5px solid var(--line); border-radius: 10px;
  background: #fbfcfe; color: var(--text); outline: none;
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--primary); background: #fff; }
.field .hint { font-size: 12px; color: var(--text-3); margin-top: 4px; }
.field input[type=range] { width: 100%; accent-color: var(--primary); }

.switch { display: flex; align-items: center; gap: 10px; padding: 8px 0; }
.switch input { width: 20px; height: 20px; accent-color: var(--primary); }

/* ---- Login ---- */
.login-wrap { min-height: 100vh; display: flex; flex-direction: column; justify-content: center; padding: 28px 22px; max-width: 480px; margin: 0 auto; }
.login-logo { text-align: center; margin-bottom: 18px; }
.login-logo .mark { width: 64px; height: 64px; border-radius: 20px; background: linear-gradient(135deg, var(--primary), var(--primary-dark)); display: inline-flex; align-items: center; justify-content: center; box-shadow: var(--shadow); }
.login-logo .mark svg { width: 34px; height: 34px; }
.login-logo h1 { margin: 10px 0 2px; font-size: 21px; }
.login-logo p { margin: 0; color: var(--text-3); font-size: 13px; }
.demo-accounts { margin-top: 18px; }
.demo-accounts .acc {
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  background: var(--surface); border: 1.5px solid var(--line); border-radius: 12px;
  padding: 10px 14px; margin-bottom: 8px; cursor: pointer; width: 100%;
  /* 逻辑属性 start（R18 S6 收口）：原来写死 text-align:left，ur/ar 下按钮里的
     账号说明仍左对齐、与镜像后的布局相悖。start/end 随 html[dir] 自动翻转，
     不需要在 RTL 块里再补一条覆盖规则。 */
  text-align: start;
}
.demo-accounts .acc:active { border-color: var(--primary); }
.demo-accounts .acc .who { font-weight: 600; font-size: 14px; }
.demo-accounts .acc .note { font-size: 12px; color: var(--text-3); }

/* ---- Empty state ---- */
.empty { text-align: center; color: var(--text-3); padding: 38px 10px; }
.empty svg { width: 46px; height: 46px; opacity: .45; margin-bottom: 8px; }
.empty p { margin: 0; font-size: 14px; }

/* ---- Toasts ----
 * pointer-events:none 是浮层纪律（本轮确认项 B2）：.toasts 是 position:fixed 的整宽浮层
 * （width: min(440px, 100vw - 28px)），默认 pointer-events:auto 会让这块透明矩形吞掉其下所有点击
 * ——真机上"工单详情"主按钮中心命中 div.toast、点击网络请求数为 0，清空浮层后同坐标才 POST 成功。
 * 浮层本身不接收事件，其子元素（.toast）也不需要点击（3.8s 自动消失），故一并置 none。 */
.toasts { position: fixed; left: 50%; transform: translateX(-50%); bottom: var(--toasts-gap); z-index: 90; width: min(440px, calc(100vw - 28px)); pointer-events: none; }
body.driver-mode .toasts { bottom: 24px; }
.toast {
  background: #101828; color: #fff; border-radius: 12px; padding: 11px 16px; font-size: 13.5px;
  margin-top: 8px; box-shadow: 0 8px 24px rgba(16,24,40,.25); word-break: break-word;
  animation: toast-in .18s ease; pointer-events: none;
}
.toast.err { background: #b42318; }
.toast.ok { background: #067647; }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ---- Photo slots & exam panel ---- */
.photo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 10px 0; }
.photo-slot {
  aspect-ratio: 1; border: 1.5px dashed var(--line); border-radius: 10px; background: #fbfcfe;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  font-size: 11.5px; color: var(--text-3); cursor: pointer; text-align: center; padding: 4px;
}
.photo-slot.taken { border-style: solid; border-color: var(--success); background: var(--success-soft); color: var(--success); font-weight: 600; }

.webview-mock {
  border: 1.5px solid var(--line); border-radius: 12px; overflow: hidden; margin-top: 10px; background: #fff;
}
.webview-mock .bar { background: #f4f6fa; padding: 8px 12px; font-size: 12px; color: var(--text-2); display: flex; gap: 6px; align-items: center; border-bottom: 1px solid var(--line); }
.webview-mock .bar .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line); }
.webview-mock .bar .dot.r { background: #fda29b; } .webview-mock .bar .dot.y { background: #fec84b; } .webview-mock .bar .dot.g { background: #75e0a7; }
.webview-mock .body { padding: 16px; }
.webview-mock .url { font-family: var(--mono); font-size: 11.5px; color: var(--text-3); word-break: break-all; }

/* ---- Attendance result / points ---- */
.result-hero { text-align: center; padding: 18px 6px 6px; }
.result-hero .big { font-size: 40px; line-height: 1; }
.result-hero .title { font-weight: 700; font-size: 16px; margin-top: 10px; }
.result-hero .sub { color: var(--text-2); font-size: 13px; }
.points-hero { text-align: center; padding: 22px 6px; }
.points-hero .num { font-size: 44px; font-weight: 800; color: var(--primary); line-height: 1.1; }
.points-hero .cap { color: var(--text-2); font-size: 13px; }

/* ---- Driver console (desktop) ---- */
.driver-head {
  background: linear-gradient(120deg, #101828, #253047); color: #fff;
  padding: 18px 26px; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}
.driver-head h1 { margin: 0; font-size: 20px; }
.driver-head .sub { color: #b6bfce; font-size: 13px; margin-top: 3px; }
/* 三栏栅格（R19 #4 修正）：中栏给最小宽 + 1fr。原 `360px 1fr 340px` 下中栏在 1440px 视口只得
 * 396px，而②预约池表 7 列实宽 542px——表格按内容撑开、溢出白色卡片，压到③候选栏下面
 * （实测表格 right=1090 > 面板 right=928，③ 面板起点 944）。修法：面板 min-width:0 已在，
 * 表宽交给滚动容器 + 列合并（见 driver.js 的②表），并把中栏最小宽提到 520px——否则预约单号
 * （BK-NG-YYYYMMDD-NNNN，13px 等宽约 150px）仍会被折成三行。 */
.driver-cols { display: grid; grid-template-columns: 320px minmax(0, 1fr) 320px; gap: 16px; padding: 16px; align-items: start; }
/* R57：中栏不再锁 520px 最小宽——app-root 上限 1160px 时 320+520+320+间隙已超过容器，
   1440 视口仍会横向滚动。宽表在 .table-scroll 里滚动即可，页面本身不得溢出。
   901–1150 是"两栏 + 第三栏整行"档：既不是手机单列，也塞不下三栏。 */
@media (min-width: 901px) and (max-width: 1150px) {
  .driver-cols { grid-template-columns: 280px minmax(0, 1fr); }
  .driver-cols > .driver-panel:nth-child(3) { grid-column: 1 / -1; }
}
/* R23：窄视口（手机/竖屏平板）必须降为单列——否则 1160px 的桌面栅格塞进 393px 视口，
   Chrome 会把整页"缩到能塞下"（实测 window.innerHeight 从 615 变成 1893 CSS px、字全变小、
   观感就是"尺寸错乱、页面过长"）。降列后按面板顺序纵向排列，宽表仍走 .table-scroll。 */
@media (max-width: 900px) {
  .driver-cols { grid-template-columns: minmax(0, 1fr); gap: 12px; padding: 12px; }
  body.driver-mode .app-root { max-width: 100%; }
}
/* 手机窄屏：驱动台提示"这是桌面后管页"（信息真实，不假装适配得很好） */
.driver-narrow-hint { display: none; }
@media (max-width: 900px) {
  .driver-narrow-hint { display: block; margin: 0 12px 10px; padding: 8px 10px; border-radius: 10px;
    background: var(--primary-soft); color: var(--primary); font-size: 12.5px; }
}

.driver-panel { background: var(--surface); border-radius: var(--radius); box-shadow: var(--shadow); padding: 16px; min-width: 0; }
.driver-panel h3 { margin: 0 0 12px; font-size: 14px; }
/* 表格滚动容器：宽表在窄栏里横向滚动，绝不越出卡片（越出即"方框没包住内容"） */
.table-scroll { overflow-x: auto; }
/* 驱动台内的多列表单栅格（原先写在各面板的内联 style 里）：窄屏一律降为单列。
   缺陷（R29 用户实测）：≤900px 时 ⑦「合规档案」所在的两列栅格仍按 1fr 1fr 分栏，
   左格里那张 5 列表格只剩 ~140px（再去掉卡片化后的 92px 标签沟），"暂无合规档案…"
   被逐字折行成一根竖条（一行一个字）。降为单列后表格拿满整行。 */
.driver-grid { display: grid; gap: 10px; align-items: start; }
.driver-grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.driver-grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.driver-grid.cols-2-wide { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.driver-grid.gap-lg { gap: 18px; }
@media (max-width: 900px) {
  .driver-grid.cols-2, .driver-grid.cols-3, .driver-grid.cols-2-wide { grid-template-columns: minmax(0, 1fr); }
}
/* 表单控件不得撑破所在栏/卡片（R29 用户实测：⑩ 维修单"指派工程师"下拉 225px 落在
   手机上只有 ~190px 可用宽的卡片里，直接顶出卡片边框到屏幕边缘）。
   <select> 作 flex 项时 min-width:auto 会锁在内在宽度（最宽选项）上，必须显式放开。 */
.driver-panel select, .driver-panel input { max-width: 100%; min-width: 0; }
.driver-panel .driver-cell { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; min-width: 0; }
.driver-panel .driver-cell select { flex: 0 1 auto; }
.driver-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.driver-table th { text-align: left; color: var(--text-3); font-size: 11.5px; text-transform: uppercase; letter-spacing: .5px; padding: 6px 8px; border-bottom: 1px solid var(--line); }
.driver-table td { padding: 8px; border-bottom: 1px solid var(--line); vertical-align: top; word-break: break-word; }
.driver-table tr.clickable { cursor: pointer; }
.driver-table tr.clickable:hover { background: #f7f9fd; }
.driver-table tr.selected { background: var(--primary-soft); }
.candidate {
  border: 1.5px solid var(--line); border-radius: 12px; padding: 10px 12px; margin-bottom: 8px; cursor: pointer;
}
.candidate.selected { border-color: var(--primary); background: var(--primary-soft); }
.candidate .top { display: flex; justify-content: space-between; align-items: center; gap: 6px; }
.candidate .name { font-weight: 600; }
.candidate .meta { font-size: 12px; color: var(--text-2); margin-top: 3px; }
.warn-box { background: var(--warn-soft); color: var(--warn); border-radius: 10px; padding: 10px 12px; font-size: 12.5px; margin: 10px 0; }
.driver-task { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--line); }
.driver-task:last-child { border-bottom: none; }
.driver-task .who { font-size: 13px; }
.driver-task .who .t { color: var(--text-3); font-size: 11.5px; }
/* 返利工作台 SLA 倒计时：逾期标红（04 §3.3 审核时效 / §4.3-4 打款时效） */
.driver-table .sla-overdue { color: var(--danger); font-weight: 600; }
/* R63 对账报表：差异行整行标红（汇总/明细/差异清单共用） */
.driver-table tr.diff-row td { background: var(--danger-soft); color: var(--danger); }
.driver-table tr.diff-row td:first-child { box-shadow: inset 3px 0 0 var(--danger); }

/* ---- Language selector ---- */
.lang-row { display: flex; align-items: center; justify-content: center; gap: 10px; margin: 14px 0 4px; }
.lang-select {
  padding: 8px 12px; border: 1.5px solid var(--line); border-radius: 999px; background: var(--surface);
  color: var(--text); font-size: 13.5px; cursor: pointer; outline: none;
}
.lang-select:focus { border-color: var(--primary); }

/* ---- RTL (ur / ar) ---- */
[dir="rtl"] .card .row .v { text-align: left; }
[dir="rtl"] .toasts { direction: ltr; } /* toast payloads often carry latin/codes */

/* ---- Utility ---- */
.muted { color: var(--text-3); font-size: 12.5px; }
.spacer { flex: 1; }
.hidden { display: none !important; }
.skeleton { height: 84px; border-radius: var(--radius); background: linear-gradient(90deg, #eef1f6 25%, #f7f9fc 50%, #eef1f6 75%); background-size: 200% 100%; animation: sk 1.1s infinite; margin-bottom: 12px; }
@keyframes sk { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* 驱动台窄屏卡片化（R25）——**必须放在本节最后**：
   它要覆盖上面 .driver-table td { padding: 8px; word-break: break-word } 等基础规则；
   若写在基础规则之前，同特异度下后者胜出，标签会压在内容上（实测踩到：paddingLeft 仍是 8px）。 */
/* R25：窄屏把驱动台表格**卡片化**——每格独占整行并带列名（data-label 由 driver.js 打），
   否则等宽单号（RB-NG-2026-10-000001 / BK-NG-20261016-0001）在窄列里被 word-break 逐字符断行，
   一行只容纳一个字母、拉成长条（用户实测反馈）。宽屏保持原表格形态不变。 */
@media (max-width: 900px) {
  .driver-table, .driver-table tbody, .driver-table tr, .driver-table td { display: block; width: 100%; }
  .driver-table tr:first-child { display: none; }        /* 表头行：改由每格的 data-label 承载 */
  .driver-table tr { border: 1px solid var(--line); border-radius: 12px; background: #fff;
    padding: 10px 12px; margin-bottom: 10px; }
  .driver-table tr:last-child { margin-bottom: 0; }
  /* 标签用绝对定位而不是 flex 项：单元格里常有多个子元素（徽章 + 时间 + 按钮），
     flex 会把剩余宽度平摊给每个子元素，按钮被压到 ~30px 后一字一行（实测踩到）。
     绝对定位把标签移出流，内容区拿满整行宽度，按正常流换行。 */
  .driver-table td { position: relative; padding: 3px 0 3px 92px; border-bottom: none;
    word-break: normal; overflow-wrap: anywhere; }
  /* 整行格（colspan：空态/错误行/详情行）没有"列名"可言，driver.js 不给它打 data-label：
     一律去标签沟、按整行渲染——否则空态文案被 92px 沟槽挤成一根竖条（R29 用户实测）。 */
  .driver-table td:not([data-label]) { padding-left: 0; }
  .driver-table td::before { content: attr(data-label); position: absolute; left: 0; top: 3px;
    width: 84px; color: var(--text-3); font-size: 11.5px; line-height: 1.6; }
  .driver-table td:empty { display: none; }
  .driver-table td:first-child { font-weight: 600; }
  .driver-table td .btn { width: auto; }                 /* 卡片内按钮按内容宽，不撑满也不被压扁 */
  .driver-table .mono, .driver-table code { white-space: nowrap; }
  .table-scroll { overflow-x: visible; }                  /* 卡片化后不需要横向滚动 */
}

