/* 工地记工系统 · 全局样式
   设计令牌严格按需求文档第六节：主色 #2563eb / 成功 #16a34a / 异常 #dc2626 / 待审 #f59e0b
   圆角：卡片 12px，按钮 8px；间距：卡片内 14px，卡片间 10px；正文 15px，辅助 13px */

:root {
  --primary: #2563eb;
  --primary-dark: #1d4ed8;
  --primary-soft: #eff6ff;
  --success: #16a34a;
  --success-soft: #f0fdf4;
  --danger: #dc2626;
  --danger-soft: #fef2f2;
  --warn: #f59e0b;
  --warn-soft: #fffbeb;
  /* 待审/待确认的**文字**色。
     #f59e0b 当文字用只有 2.07:1（白/浅黄底），工地强光下等于看不见 ——
     「待处理」看不清就是漏事，所以文字单独用一个深色，底色/描边仍用 --warn 保持告警的辨识度。 */
  --warn-ink: #92400e;

  --text: #111827;
  --text-2: #4b5563;
  /* 次要文字。
     原值 #9ca3af 在白底只有 2.45:1（WCAG AA 正文要 ≥4.5:1），被 .muted/.li-sub/.stat-label/
     底部 tab 大量复用 —— 工头在工地强光下基本看不清。改成这套灰阶里仍算「次要」但能看清的档位：
     白底 5.14:1 / #fafbfc 4.96:1 / 页面灰底 #f5f6f8 4.76:1，三处都过线。 */
  --text-3: #676e79;
  --bg: #f5f6f8;
  --surface: #ffffff;
  --surface-2: #fafbfc;
  --border: #e5e7eb;
  --border-2: #f1f3f5;

  --radius-card: 12px;
  --radius-btn: 8px;
  --pad-card: 14px;
  --gap-card: 10px;
  --fs-body: 15px;
  --fs-sub: 13px;

  --shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 1px 3px rgba(16, 24, 40, .06);
  --shadow-lg: 0 10px 24px rgba(16, 24, 40, .12);
  --header-h: 52px;
  --tabbar-h: 58px;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Segoe UI", Roboto, sans-serif;
  font-size: var(--fs-body);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

a { color: var(--primary); text-decoration: none; }
h1, h2, h3, h4 { margin: 0; font-size: var(--fs-body); font-weight: 700; }
p { margin: 0 0 8px; }
.hide { display: none !important; }

/* ---------------- 布局 ---------------- */

.appbar {
  position: sticky; top: 0; z-index: 40;
  height: var(--header-h);
  display: flex; align-items: center; gap: 10px;
  padding: 0 12px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.appbar-title { font-weight: 700; font-size: 16px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.appbar-right { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sub); color: var(--text-2); }

.page { padding: var(--gap-card); max-width: 1080px; margin: 0 auto; }
.page.worker { padding-bottom: calc(var(--tabbar-h) + 16px); }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--pad-card);
  margin-bottom: var(--gap-card);
  box-shadow: var(--shadow);
}
.card.tight { padding: 10px 12px; }
.card-flat { background: var(--surface-2); box-shadow: none; }
.card-title {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-weight: 700; font-size: var(--fs-body); margin-bottom: 10px;
}
.card-title .muted { font-weight: 400; font-size: var(--fs-sub); color: var(--text-3); }

.row { display: flex; align-items: center; gap: 8px; }
.row.wrap { flex-wrap: wrap; }
.row.between { justify-content: space-between; }
.col { display: flex; flex-direction: column; gap: 8px; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.grid4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.grow { flex: 1; min-width: 0; }
.muted { color: var(--text-3); font-size: var(--fs-sub); }
.sub { color: var(--text-2); font-size: var(--fs-sub); }
.mt6 { margin-top: 6px; }
.mt10 { margin-top: 10px; }
.mt14 { margin-top: 14px; }
.right { text-align: right; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.num { font-variant-numeric: tabular-nums; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; }

/* ---------------- 文字与状态 ---------------- */

.big-num { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.stat-value { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.2; }
.stat-label { font-size: var(--fs-sub); color: var(--text-3); margin-top: 2px; }

.text-primary { color: var(--primary); }
.text-success { color: var(--success); }
.text-danger { color: var(--danger); }
/* 待审/待确认的**文字**：不用 --warn 的黄（#f59e0b 当文字只有 2.07:1，强光下看不见），
   换成同色系的深棕 --warn-ink（浅黄底 7.2:1）。底色仍旧用 --warn-soft，辨识度不变。 */
.text-warn { color: var(--warn-ink); }

.tag {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 7px; border-radius: 20px;
  font-size: 12px; line-height: 18px; white-space: nowrap;
  background: var(--border-2); color: var(--text-2);
}
.tag-primary { background: var(--primary-soft); color: var(--primary); }
.tag-success { background: var(--success-soft); color: var(--success); }
.tag-danger { background: var(--danger-soft); color: var(--danger); }
.tag-warn { background: var(--warn-soft); color: var(--warn-ink); }
.tag-admin { background: #f5f3ff; color: #7c3aed; }

/* ---------------- 按钮 ---------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 44px; padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-body);
  font-family: inherit;
  cursor: pointer;
  transition: background .15s, border-color .15s, opacity .15s;
  user-select: none;
}
.btn:active { background: var(--border-2); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn-primary:active { background: var(--primary-dark); }
.btn-success { background: var(--success); border-color: var(--success); color: #fff; }
.btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
/* 黄底白字只有 2.2:1，按钮又是「点一下就生效」的操作，所以底色压深到 #b45309（白字 6.4:1）。 */
.btn-warn { background: #b45309; border-color: #b45309; color: #fff; }
.btn-ghost { background: transparent; border-color: transparent; color: var(--primary); }
.btn-outline-primary { border-color: var(--primary); color: var(--primary); background: var(--surface); }
.btn-block { width: 100%; }
.btn-sm { min-height: 34px; padding: 0 10px; font-size: var(--fs-sub); border-radius: var(--radius-btn); }
.btn-xs { min-height: 28px; padding: 0 8px; font-size: 12px; border-radius: 6px; }
.btn-lg { min-height: 54px; font-size: 17px; font-weight: 600; }
.btn-icon { min-height: 34px; min-width: 34px; padding: 0 8px; font-size: var(--fs-sub); }

/* 打卡三大按钮 */
.action-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.action-btn {
  min-height: 76px; border-radius: var(--radius-card);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  border: 1px solid var(--border); background: var(--surface);
  font-size: 16px; font-weight: 600; color: var(--text); cursor: pointer;
  font-family: inherit;
}
.action-btn .ico { font-size: 22px; line-height: 1; }
.action-btn.on-work { border-color: var(--primary); background: var(--primary-soft); color: var(--primary); }
.action-btn.on-rest { border-color: var(--success); background: var(--success-soft); color: var(--success); }
.action-btn.on-leave { border-color: var(--warn); background: var(--warn-soft); color: var(--warn-ink); }
.action-btn:active { transform: scale(.985); }

/* ---------------- 表单 ---------------- */

label.field { display: block; margin-bottom: 10px; }
.label-text { display: block; font-size: var(--fs-sub); color: var(--text-2); margin-bottom: 4px; }
.input, .select, .textarea {
  width: 100%; min-height: 44px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: var(--radius-btn);
  background: var(--surface); color: var(--text);
  font-size: var(--fs-body); font-family: inherit;
  outline: none; transition: border-color .15s, box-shadow .15s;
}
.input:focus, .select:focus, .textarea:focus {
  border-color: var(--primary); box-shadow: 0 0 0 3px rgba(37, 99, 235, .12);
}
.textarea { min-height: 72px; resize: vertical; }
.input-sm { min-height: 36px; font-size: var(--fs-sub); }
.select { appearance: none; background-image: none; padding-right: 28px; }
input[type="date"], input[type="month"] { min-height: 44px; }

.quick-hours { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.chip {
  min-height: 34px; padding: 0 10px; border-radius: 20px;
  border: 1px solid var(--border); background: var(--surface);
  font-size: var(--fs-sub); color: var(--text-2); cursor: pointer; font-family: inherit;
}
.chip:active { background: var(--primary-soft); border-color: var(--primary); color: var(--primary); }
.chip.active { background: var(--primary); border-color: var(--primary); color: #fff; }

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

/* ---------------- 列表 ---------------- */

.list { border-radius: var(--radius-card); overflow: hidden; }
.list-item {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 0; border-bottom: 1px solid var(--border-2);
}
.list-item:last-child { border-bottom: none; }
.list-item .li-main { flex: 1; min-width: 0; }
.li-title { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-weight: 600; }
.li-sub { font-size: var(--fs-sub); color: var(--text-2); margin-top: 2px; word-break: break-all; }
/* 结构化明细（换水表 · 1234.5 m³ · 3号楼 2层东）：每条记录最先看到的一行 */
.li-detail { font-size: var(--fs-body, 14px); font-weight: 700; color: var(--text); margin-top: 2px; word-break: break-all; }
.li-actions { display: flex; gap: 6px; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }

.day-group { border-bottom: 1px solid var(--border-2); padding: 10px 0; }
.day-group:last-child { border-bottom: none; }
.day-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }

.empty { padding: 26px 12px; text-align: center; color: var(--text-3); font-size: var(--fs-sub); }

/* ---------------- 表格 ---------------- */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 -3px; }
table.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-sub); min-width: 560px; }
table.tbl th, table.tbl td { padding: 9px 7px; text-align: left; border-bottom: 1px solid var(--border-2); white-space: nowrap; }
table.tbl th { color: var(--text-3); font-weight: 600; background: var(--surface-2); position: sticky; top: 0; }
table.tbl tfoot td { font-weight: 700; background: var(--surface-2); }
table.tbl td.num, table.tbl th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.tbl tr.abnormal td { background: var(--danger-soft); }
table.tbl tr.total-row td { background: var(--primary-soft); font-weight: 700; }

/* ---------------- 底部导航（工人端） ---------------- */

.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  display: flex; background: var(--surface); border-top: 1px solid var(--border);
}
.tabbar button {
  flex: 1; border: none; background: none; font-family: inherit;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  /* 13px：原来 12px、窄屏再压到 11px —— 低于 13px 正文线，工地上看着费劲。
     5 个 tab 在 390px 上每人 78px，13px 的「考勤/报销」只有 26px 宽，塞得下不会换行。 */
  font-size: 13px; color: var(--text-3); cursor: pointer; position: relative;
  min-height: var(--tabbar-h);
}
.tabbar button .ico { font-size: 19px; line-height: 1; }
.tabbar button.active { color: var(--primary); font-weight: 600; }
.dot {
  position: absolute; top: 8px; right: 50%; transform: translateX(16px);
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 9px;
  background: var(--danger); color: #fff; font-size: 11px; line-height: 16px; text-align: center;
}

/* ---------------- 管理端导航 ---------------- */

.admin-shell { display: flex; min-height: 100vh; }
.sidebar {
  width: 200px; flex-shrink: 0; background: var(--surface);
  border-right: 1px solid var(--border); padding: 10px 8px;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.sidebar .brand { padding: 8px 10px 12px; font-weight: 700; font-size: 16px; }
.sidebar .brand small { display: block; font-weight: 400; color: var(--text-3); font-size: 12px; }
.sidebar button {
  display: flex; align-items: center; gap: 8px; width: 100%;
  min-height: 40px; padding: 0 10px; margin-bottom: 2px;
  border: none; border-radius: var(--radius-btn); background: none;
  font-family: inherit; font-size: 14px; color: var(--text-2);
  cursor: pointer; text-align: left;
}
.sidebar button:hover { background: var(--border-2); }
.sidebar button.active { background: var(--primary-soft); color: var(--primary); font-weight: 600; }
.sidebar .badge {
  margin-left: auto; background: var(--danger); color: #fff;
  border-radius: 9px; font-size: 11px; line-height: 16px; padding: 0 5px; min-width: 16px; text-align: center;
}
.admin-main { flex: 1; min-width: 0; }
.admin-topbar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 8px;
  height: var(--header-h); padding: 0 12px;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.admin-topbar .ttl { font-weight: 700; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 桌面端不出现：手机底部栏只在 ≤767px 显示（见下面的响应式块） */
.mobnav { display: none; }

/* ---------------- 今日总览 · 概览条 ---------------- */

/* 「3 秒看清」的一条：已报/应到 + 百分比 + 总工时 + 总工数。
   桌面端排成一横条（左宽右两格），手机上换成两行（见响应式块）。 */
.ov-strip {
  display: flex; align-items: stretch; gap: 12px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-card);
  padding: 12px 14px; margin-bottom: var(--gap-card); box-shadow: var(--shadow);
}
.ov-main { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 6px; }
.ov-line { display: flex; align-items: baseline; gap: 5px; }
.ov-line b { font-size: 24px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.ov-line span { font-size: var(--fs-sub); color: var(--text-2); }
.ov-line em { margin-left: auto; font-style: normal; font-weight: 700; font-size: 15px; color: var(--primary); font-variant-numeric: tabular-nums; }
.ov-bar { height: 6px; border-radius: 3px; background: var(--border-2); overflow: hidden; }
.ov-bar .ov-fill { display: block; height: 100%; border-radius: 3px; background: var(--primary); }
.ov-cell {
  flex: 0 0 auto; min-width: 96px; padding-left: 12px; border-left: 1px solid var(--border-2);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.ov-cell b { font-size: 20px; font-weight: 700; line-height: 1.2; font-variant-numeric: tabular-nums; }
.ov-cell span { font-size: 12px; color: var(--text-3); }

/* 日期行：.input 是 width:100%，在 flex 行里会把「日期」两个字挤成竖排（桌面端也一样） */
.today-date-row { flex: 1; min-width: 0; }
.today-date-row .label-text { white-space: nowrap; }
.today-date { width: auto; max-width: 150px; }

/* ---------------- 「更多」弹层（手机底部栏的二级菜单） ---------------- */

.moresheet { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.ms-item {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 78px; padding: 10px 4px;
  border: 1px solid var(--border); border-radius: var(--radius-card); background: var(--surface);
  font-family: inherit; font-size: 13px; color: var(--text-2); cursor: pointer;
}
.ms-item:active { background: var(--primary-soft); border-color: var(--primary); }
.ms-item.active { border-color: var(--primary); color: var(--primary); font-weight: 600; }
.ms-ico { font-size: 22px; line-height: 1; }
.ms-lb { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ms-item .badge {
  position: absolute; top: 5px; right: 7px;
  min-width: 16px; padding: 0 5px; border-radius: 9px;
  background: var(--danger); color: #fff; font-size: 11px; line-height: 16px; text-align: center;
}

/* ---------------- 弹窗 / 提示 ---------------- */

.modal-mask {
  position: fixed; inset: 0; z-index: 100; background: rgba(17, 24, 39, .5);
  display: flex; align-items: flex-end; justify-content: center;
}
.modal {
  background: var(--surface); width: 100%; max-width: 520px;
  border-radius: 14px 14px 0 0; max-height: 92vh; display: flex; flex-direction: column;
  animation: slideUp .18s ease-out;
}
@keyframes slideUp { from { transform: translateY(16px); opacity: .6; } to { transform: none; opacity: 1; } }
.modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--border); font-weight: 700;
}
.modal-body { padding: 14px; overflow-y: auto; flex: 1; }
.modal-foot { padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--border); display: flex; gap: 8px; }
.modal-foot .btn { flex: 1; }

#toast-root { position: fixed; left: 0; right: 0; bottom: calc(var(--tabbar-h) + 18px); z-index: 200; display: flex; flex-direction: column; align-items: center; gap: 6px; pointer-events: none; }
.toast {
  max-width: 88vw; padding: 9px 14px; border-radius: 20px;
  background: rgba(17, 24, 39, .9); color: #fff; font-size: var(--fs-sub);
  box-shadow: var(--shadow-lg); animation: fadeIn .15s;
}
.toast.err { background: rgba(220, 38, 38, .95); }
.toast.ok { background: rgba(22, 163, 74, .95); }
@keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

.banner {
  border-radius: var(--radius-card); padding: 10px 12px; margin-bottom: var(--gap-card);
  font-size: var(--fs-sub); border: 1px solid;
}
.banner-info { background: var(--primary-soft); border-color: #bfdbfe; color: var(--primary-dark); }
.banner-warn { background: var(--warn-soft); border-color: #fde68a; color: #92400e; }
.banner-danger { background: var(--danger-soft); border-color: #fecaca; color: #991b1b; }
.banner-success { background: var(--success-soft); border-color: #bbf7d0; color: #166534; }

.thumb {
  width: 62px; height: 62px; object-fit: cover; border-radius: 8px;
  border: 1px solid var(--border); background: var(--border-2); cursor: pointer;
}
.thumb-lg { width: 100%; max-height: 70vh; object-fit: contain; border-radius: 8px; }

/* ---------------- 登录页 ---------------- */

.auth-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 18px; }
.auth-card { width: 100%; max-width: 380px; }
.auth-logo { text-align: center; margin-bottom: 18px; }
.auth-logo .mark {
  width: 56px; height: 56px; margin: 0 auto 10px; border-radius: 14px;
  background: var(--primary); color: #fff; display: flex; align-items: center; justify-content: center;
  font-size: 26px; font-weight: 700;
}
.auth-logo h1 { font-size: 19px; }
.auth-tabs { display: flex; background: var(--border-2); border-radius: var(--radius-btn); padding: 3px; margin-bottom: 14px; }
.auth-tabs button {
  flex: 1; min-height: 38px; border: none; border-radius: 6px; background: none;
  font-family: inherit; font-size: 14px; color: var(--text-2); cursor: pointer;
}
.auth-tabs button.active { background: var(--surface); color: var(--primary); font-weight: 600; box-shadow: var(--shadow); }

/* ---------------- 可下钻元素（点一下看明细） ---------------- */

.drill { cursor: pointer; border-radius: 8px; padding: 6px 2px; transition: background .15s; }
.drill:hover { background: var(--primary-soft); }
.drill:active { background: #dbeafe; }
.drill .stat-value { border-bottom: 2px dotted transparent; display: inline-block; }
.drill:hover .stat-value { border-bottom-color: currentColor; }

.name-link {
  cursor: pointer; font-weight: 600; color: var(--text);
  border-bottom: 1px dashed var(--border); padding-bottom: 1px;
}
.name-link:hover { color: var(--primary); border-bottom-color: var(--primary); }

.warn-mark { cursor: pointer; color: var(--danger); font-weight: 600; }
.warn-mark:hover { text-decoration: underline; }

/* ---------------- 进度条 ---------------- */

.progress {
  height: 10px; border-radius: 6px; background: var(--border-2);
  overflow: hidden; display: flex;
}
.progress span { display: block; height: 100%; transition: width .3s; }
.progress .p-ok { background: var(--success); }
.progress .p-rest { background: #94a3b8; }
.progress .p-bad { background: var(--danger); }
.progress .p-new { background: #93c5fd; }

/* ---------------- 人员卡片网格 ---------------- */

.people-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.person-card {
  border: 1px solid var(--border); border-radius: var(--radius-card);
  padding: 9px 10px; background: var(--surface); cursor: pointer; transition: border-color .15s, box-shadow .15s;
}
.person-card:hover { border-color: var(--primary); box-shadow: var(--shadow); }
.person-card .pc-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.person-card .pc-name { font-weight: 600; font-size: 14px; }
.person-card .pc-bottom { margin-top: 4px; font-size: 12px; color: var(--text-2); }
.person-card.bad { border-color: #fecaca; background: var(--danger-soft); }
.person-card.bad .pc-name { color: var(--danger); }
.person-card.rest { background: var(--surface-2); }

/* ---------------- 端别开关：表格 vs 卡片 ----------------
   手机上「一人一卡 / 一天一卡」和桌面上「一人一行」是两套完全不同的排版，
   硬用 CSS 把 <table> 改造成卡片要靠伪元素拼标签、易碎又难读，
   所以直接渲染两份 DOM，用这两个类各自隐藏一份。
   ⚠️ 桌面的那份写在前面：老测试用 page.$() 取第一个匹配，取到的就是桌面上看得见的那份。 */
.m-only { display: none; }
.d-only { display: block; }

/* ---------------- 记工明细 · 按人看 ---------------- */

/* 手机端的人员名单：整块可点（≥44px），不塞别的按钮 */
.rec-person {
  display: block; width: 100%; text-align: left; font-family: inherit;
  min-height: 44px; padding: 11px 12px; margin-bottom: 8px;
  border: 1px solid var(--border); border-radius: var(--radius-card);
  background: var(--surface); color: var(--text); cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
}
.rec-person:last-child { margin-bottom: 0; }
.rec-person:hover { border-color: var(--primary); box-shadow: var(--shadow); }
.rec-person:active { background: var(--primary-soft); }
.rec-person .rp-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.rec-person .rp-name { font-weight: 700; font-size: 16px; }
.rec-person .rp-nums { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-top: 8px; }
.rec-person .rp-nums > span { display: flex; flex-direction: column; align-items: center; }
.rec-person .rp-nums b { font-size: 17px; line-height: 1.2; }
.rec-person .rp-nums i { font-style: normal; font-size: 12px; color: var(--text-3); margin-top: 2px; }
.rec-person .rp-go { display: block; margin-top: 8px; font-size: 13px; color: var(--primary); }

/* 手机端的按天明细：一天一张卡，操作按钮平铺在卡片底部 */
.rec-day {
  border: 1px solid var(--border); border-radius: var(--radius-card);
  padding: 10px 12px; margin-bottom: 8px; background: var(--surface);
}
.rec-day:last-child { margin-bottom: 0; }
.rec-day.bad { border-color: #fecaca; background: var(--danger-soft); }
.rec-day-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.rec-day-h { font-weight: 700; white-space: nowrap; }
.rec-day-sub { margin-top: 4px; font-size: 13px; color: var(--text-2); }
.rec-day .rec-acts {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border-2);
}
.rec-day .rec-acts .btn { flex: 0 0 auto; }
.rec-who { font-size: 17px; font-weight: 700; }

/* ---------------- 月度汇总 · 手机端一人一卡 ---------------- */

.sum-person {
  border: 1px solid var(--border); border-radius: var(--radius-card);
  padding: 10px 12px; margin-bottom: 8px; background: var(--surface); cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
}
.sum-person:last-child { margin-bottom: 0; }
.sum-person:hover { border-color: var(--primary); box-shadow: var(--shadow); }
.sum-person:active { background: var(--primary-soft); }
.sum-person .sp-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sum-person .sp-name { font-weight: 700; font-size: 16px; }
.sum-person .sp-nums { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-top: 8px; }
.sum-person .sp-nums > span { display: flex; flex-direction: column; align-items: center; }
.sum-person .sp-nums b { font-size: 17px; line-height: 1.2; }
.sum-person .sp-nums i { font-style: normal; font-size: 12px; color: var(--text-3); margin-top: 2px; }
.sum-person .sp-remark { margin-top: 6px; font-size: 13px; color: var(--text-2); }
.sum-person .sp-acts { display: flex; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border-2); }

/* ---------------- 人员管理 · 手机端一人一卡 ---------------- */

.wk-card {
  border: 1px solid var(--border); border-radius: var(--radius-card);
  padding: 10px 12px; margin-bottom: 8px; background: var(--surface);
}
.wk-card:last-child { margin-bottom: 0; }
.wk-card.abnormal { border-color: #fecaca; background: var(--danger-soft); }
.wk-card .wk-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.wk-card .wk-name { font-weight: 700; font-size: 16px; }
.wk-card .wk-meta { margin-top: 8px; font-size: 15px; }
.wk-card .wk-alias { margin-top: 2px; font-size: 13px; color: var(--text-3); }
.wk-card .wk-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border-2); }

/* ---------------- 现场照片 · 拍照提醒入口 ---------------- */

/* 「拍一张，提醒某人」是这一页最要做的事，不能长得跟筛选控件一样。
   加高到 56px、字重加大，手机上一眼就能看见、一按就中（≥44px 的可点高度）。 */
.safety-big { min-height: 56px; font-size: 16px; font-weight: 700; }

/* ---------------- AI 助手 · 对话气泡 ---------------- */

/* 对话卡自己就是一根竖着的 flex：标题/快捷提问/输入框固定，消息区吃掉剩下的高度。
   用户原话："这个页面不好看。它占了屏幕的上半部分，下半部分没有。" */
.chat-card { display: flex; flex-direction: column; }
.chat-card > .card-title,
.chat-card > .chat-qchips,
.chat-card > .chat-input { flex: 0 0 auto; }
.chat-thread { display: flex; flex-direction: column; gap: 8px; padding: 2px 0; flex: 1 1 auto; min-height: 0; }
/* 消息少的时候从底部往上堆（新消息永远在下面），多了就正常从上往下滚。
   用 :first-child 的 margin-top:auto 而不是 justify-content:flex-end —— 后者在
   内容超高时 Chrome 里会把顶部几条滚不到（老坑）。 */
.chat-thread > .chat-msg:first-child { margin-top: auto; }
.chat-msg { display: flex; }
.chat-msg.me { justify-content: flex-end; }
.chat-msg.ai { justify-content: flex-start; }
/* 连着两条同一边的消息贴近一点：像微信那样成一组，而不是每句都隔开一大截 */
.chat-msg.ai + .chat-msg.ai,
.chat-msg.me + .chat-msg.me { margin-top: -3px; }
.chat-msg .bubble {
  max-width: 78%; padding: 8px 11px; border-radius: 12px;
  font-size: var(--fs-body); line-height: 1.5;
  white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere;
}
.chat-msg.me .bubble { background: var(--primary); color: #fff; border-bottom-right-radius: 4px; }
.chat-msg.ai .bubble { background: var(--surface-2); border: 1px solid var(--border); border-bottom-left-radius: 4px; }
.chat-msg.ai .bubble p:last-child { margin-bottom: 0; }
.chat-msg .bubble .banner { font-size: var(--fs-sub); }

/* 快捷提问：窄屏上横滑，不把输入框挤到屏幕外 */
.chat-qchips {
  display: flex; gap: 6px; margin-top: 10px; padding-bottom: 2px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.chat-qchips .chip { flex: 0 0 auto; }

.chat-input { display: flex; gap: 8px; margin-top: 10px; }

/* ---------------- 待办卡 ---------------- */

.todo-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
.todo-item {
  border: 1px solid var(--border); border-radius: var(--radius-card);
  padding: 10px; background: var(--surface); cursor: pointer; transition: border-color .15s;
}
.todo-item:hover { border-color: var(--primary); }
.todo-item .ti-value { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }
.todo-item .ti-label { font-size: 12px; color: var(--text-3); margin-top: 2px; }
.todo-item.todo-warn { border-color: #fde68a; background: var(--warn-soft); }
.todo-item.todo-danger { border-color: #fecaca; background: var(--danger-soft); }
.todo-item.todo-clear { opacity: .6; }

.todo-item.todo-warn { border-color: #fde68a; background: var(--warn-soft); }
.todo-item.todo-danger { border-color: #fecaca; background: var(--danger-soft); }
.todo-item.todo-clear { opacity: .6; }

/* ---------------- 弹窗内容里的紧凑列表 ---------------- */

.modal-body .day-group:first-child { padding-top: 0; }
.modal-body .card-flat { border: 1px solid var(--border-2); }
.d-table { width: 100%; font-size: var(--fs-sub); border-collapse: collapse; }
.d-table th, .d-table td { padding: 5px 4px; text-align: left; border-bottom: 1px solid var(--border-2); }
.d-table td.num, .d-table th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------------- 响应式 ---------------- */

@media (min-width: 768px) {
  .modal-mask { align-items: center; }
  .modal { border-radius: 14px; max-width: 560px; }
  #toast-root { bottom: 24px; }
  /* 桌面上对话区也要有像样的高度，不能只是页面顶上的一小条 */
  .chat-thread { min-height: 46vh; max-height: 62vh; overflow-y: auto; }
}

@media (max-width: 767px) {
  .sidebar { display: none; }
  .admin-shell { display: block; }
  /* 手机底部导航：固定在底部，占满宽度，4~5 个等宽标签（不再是顶部横滑的胶囊条） */
  .mobnav {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    height: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--surface); border-top: 1px solid var(--border);
  }
  .mobnav button {
    flex: 1; min-width: 0; min-height: var(--tabbar-h);
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    padding: 4px 2px; border: none; background: none; position: relative;
    font-family: inherit; font-size: 12px; color: var(--text-3); cursor: pointer;
  }
  .mobnav button .bn-ico { font-size: 19px; line-height: 1; }
  .mobnav button .bn-lb { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mobnav button.active { color: var(--primary); font-weight: 700; }
  .mobnav .badge {
    position: absolute; top: 5px; right: 50%; transform: translateX(14px);
    min-width: 16px; padding: 0 5px; border-radius: 9px;
    background: var(--danger); color: #fff; font-size: 11px; line-height: 16px; text-align: center;
  }
  /* 内容给底部栏让位，否则最后一张卡会被压在栏底下点不到 */
  .page { padding: 10px; padding-bottom: calc(var(--tabbar-h) + 20px + env(safe-area-inset-bottom, 0px)); }
  .grid4 { grid-template-columns: 1fr 1fr; }

  /* 手机上收紧留白、待办改 2 列：原来卡片叠卡片，翻两屏还看不到关键数字 */
  .card { padding: 12px; margin-bottom: 8px; }
  .card.tight { padding: 9px 11px; }
  .card-title { margin-bottom: 8px; }
  .todo-grid { grid-template-columns: 1fr 1fr; gap: 8px; }
  /* 手机上看的是「谁、在哪、多少工」这些正文，不能按桌面辅助字号压到 12px */
  .todo-item .ti-label,
  .person-card .pc-bottom { font-size: 13px; }

  /* 概览条：一行放不下「已报/应到 + 百分比 + 两个数」，窄屏上会横溢，所以改两行 —— 上行进度、下行两个格 */
  .ov-strip { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 6px; padding: 10px 12px; }
  .ov-main { grid-column: 1 / -1; }
  .ov-cell { min-width: 0; padding: 8px 0 0; border-left: none; border-top: 1px solid var(--border-2); }
  /* 「今日填报进度」卡里那行「已报 X/N · P%」和顶部概览条重复，手机上藏掉，桌面端不动 */
  .ov-dup { display: none; }
  .moresheet { grid-template-columns: repeat(3, 1fr); gap: 8px; }

  /* 手机端才显示卡片那份 DOM（见上面「端别开关」） */
  .m-only { display: block; }
  .d-only { display: none; }

  /* 手机上操作按钮就是手指的目标，不能按桌面 28px 的 btn-xs 来 —— 一天一卡里那几个
     「修改/通过/驳回/删除」挨在一起，太小的按钮必然误触，误触就是删错记录。 */
  .rec-day .rec-acts .btn { min-height: 44px; padding: 0 12px; font-size: 13px; }
  .sum-person .sp-acts .btn,
  .wk-card .wk-acts .btn { min-height: 44px; padding: 0 12px; font-size: 13px; }

  /* 月度汇总的 4 格概览：手机上一行放 4 个，别折成两行显得没排好；
     数字收到 18px，标签仍是 13px（辅助字号不能为了塞进去压到看不清）。 */
  .sum-ov { grid-template-columns: repeat(4, 1fr); gap: 4px; }
  .sum-ov .stat-value { font-size: 18px; }

  /* AI 页：配置卡 / 看图模型提示 / 资料库标签都是管理功能，手机上只留对话 */
  .ai-desk-only { display: none !important; }

  /*
   * AI 页撑满整屏。
   *
   * 用户原话："这个对话，这个页面不好看。它占了屏幕的上半部分，下半部分没有。"
   * 原来消息区写死 max-height:54vh，下面半屏就是灰底 —— 现在把 #view 变成一根
   * 固定高度的竖 flex（顶栏以下、底部栏以上全给它），对话卡 flex:1 撑开，
   * 消息区再 flex:1 自己滚，输入框自然落在卡片最下面。
   * 100vh 在手机浏览器里会被地址栏算进去，所以有 dvh 就优先用 dvh（旧内核自动忽略这行）。
   */
  #view.ai-fill {
    display: flex; flex-direction: column;
    height: calc(100vh - var(--header-h) - var(--tabbar-h));
    padding-bottom: 10px;          /* 底部栏已经让出来了，不用再让一次 */
    overflow: hidden;
  }
  #view.ai-fill > .banner { flex: 0 0 auto; margin-bottom: 8px; }
  #view.ai-fill > .chat-card { flex: 1 1 auto; min-height: 0; margin-bottom: 0; }
  #view.ai-fill .chat-thread { max-height: none; min-height: 0; overflow-y: auto; }
  .chat-input { position: static; padding-top: 8px; }
  /* 快捷提问本身也是可点目标，手机上按 44px 起；窄屏横滑、不许把输入框挤下去 */
  .chat-qchips .chip { min-height: 44px; }
  .chat-qchips { flex: 0 0 auto; margin-top: 8px; }
  /* 顶栏加了一个 🤖。宽度原来压到 36px 是为了给标题腾地方，但 36×34 的手指目标
     太小（审计实测点十次错三次），统一到 44×44 —— 顶栏本来就是 52px 高，放得下。
     标题用 flex:1 + ellipsis 兜底，不会因为按钮变宽而换行。 */
  #btnAdminAi { min-width: 44px; min-height: 44px; padding: 0 6px; }
}

/* 手机浏览器地址栏会算进 100vh，能认 dvh 就用 dvh，免得输入框被顶到屏幕外 */
@supports (height: 100dvh) {
  @media (max-width: 767px) {
    #view.ai-fill { height: calc(100dvh - var(--header-h) - var(--tabbar-h)); }
  }
}

@media (min-width: 768px) and (max-width: 1023px) {
  .sidebar { width: 168px; }
}

/* ---------------- 现场拍照记录（工人端） ---------------- */

/* 底部栏现在有 5 个 tab，窄屏上别换行、别挤：文案单行、图标略小。
   注意：**文案字号不要在这里往下压**。审计实测 390px 宽下「现场/考勤/消息」被压到 11px，
   工地强光下等于没有字。字号统一在 .tabbar button 上给 13px，窄屏只缩图标不缩字。 */
.tabbar button { padding: 0 2px; white-space: nowrap; }
@media (max-width: 400px) {
  .tabbar button .ico { font-size: 18px; }
}

/* 大拍照按钮：整块可点，微信里也能正常唤起「拍照 / 相册」选择 */
.capture-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-height: 118px; padding: 14px 12px; cursor: pointer; text-align: center;
  border: 2px dashed var(--primary); border-radius: var(--radius-card);
  background: var(--primary-soft); color: var(--primary);
}
.capture-btn:active { transform: scale(.99); }
.capture-btn .cap-ico { font-size: 34px; line-height: 1.15; }
.capture-btn .cap-main { font-size: 17px; font-weight: 700; }
.capture-btn .cap-sub { font-size: var(--fs-sub); color: var(--text-2); font-weight: 400; }
.file-hidden { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }

.photo-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.photo-cell { position: relative; display: inline-block; }
.photo-del {
  position: absolute; top: -7px; right: -7px; width: 22px; height: 22px; padding: 0;
  border: none; border-radius: 50%; background: var(--danger); color: #fff;
  font-size: 14px; line-height: 22px; text-align: center; cursor: pointer; font-family: inherit;
}

.geo-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.geo-status { font-size: var(--fs-sub); color: var(--text-2); }
.geo-status.ok { color: var(--success); font-weight: 600; }
/* 「定位没拿到」也是要工人**读**的提示，用深棕文字而不是黄色（黄字白底 2.07:1 看不清）。 */
.geo-status.warn { color: var(--warn-ink); }

/* ---------------- 公开只读分享页 ---------------- */

.share-body .page { max-width: 560px; padding-bottom: 0; }
.share-foot { text-align: center; color: var(--text-3); font-size: var(--fs-sub); padding: 6px 12px 30px; }
.share-meta { border-top: 1px solid var(--border-2); }
/* 谁转发的：分享页免登录，只有链接上带的 ?by= 知道是谁发的；
   用户要的就是"我一看就知道该点哪一条"，所以放在标题上面、主色加粗 */
.share-by { margin: 6px 0 2px; font-size: var(--fs-sub); color: var(--text-2); }
.share-by b { color: var(--shr-accent, var(--primary)); }
.share-row { display: flex; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border-2); font-size: var(--fs-sub); }
.share-row .k { color: var(--text-3); flex-shrink: 0; min-width: 64px; }
.share-row .v { color: var(--text); flex: 1; min-width: 0; word-break: break-all; }
.share-note {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--radius-card);
  background: var(--surface-2); font-size: var(--fs-sub);
  white-space: pre-wrap; word-break: break-word;
}
.share-photos { display: grid; grid-template-columns: 1fr; gap: 8px; }
.share-photos img {
  display: block; width: 100%; border-radius: 10px;
  border: 1px solid var(--border); background: var(--border-2); cursor: pointer;
}

/* ---------------- 品牌（公司名 / logo） ---------------- */

.brand-name { vertical-align: middle; }
.brand-logo {
  display: inline-block; height: 22px; width: auto; max-width: 60px;
  vertical-align: middle; margin-right: 6px; border-radius: 5px;
  object-fit: contain; background: var(--border-2);
}
.sidebar .brand .brand-logo { height: 24px; max-width: 100%; }
.mark-img { width: 100%; height: 100%; object-fit: contain; border-radius: inherit; }
.brand-preview {
  display: inline-flex; align-items: center; justify-content: center;
  width: 64px; height: 64px; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--border); background: var(--surface-2); flex-shrink: 0;
}
.brand-preview img { width: 100%; height: 100%; object-fit: contain; }
.brand-preview .muted { font-size: 11px; text-align: center; padding: 0 4px; }
.foot-logo {
  display: inline-block; height: 18px; width: auto; max-width: 48px;
  vertical-align: middle; margin-right: 6px; border-radius: 4px; object-fit: contain;
}
.appbar-head { display: flex; flex-direction: column; justify-content: center; min-width: 0; flex: 1; }
.appbar-brand { display: flex; align-items: center; font-size: 12px; color: var(--text-3); line-height: 1.2; }
.appbar-brand .brand-logo { height: 14px; margin-right: 4px; }
.appbar-brand .brand-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.appbar-avatar { display: flex; align-items: center; flex-shrink: 0; }

/* ---------------- 头像（纯 CSS 圆形 + 姓名首字） ---------------- */

.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%;
  color: #fff; font-weight: 700; font-size: 17px; line-height: 1;
  flex-shrink: 0; user-select: none; overflow: hidden;
}
.avatar-sm { width: 32px; height: 32px; font-size: 15px; }
.avatar-md { width: 44px; height: 44px; font-size: 19px; }

/* ---------------- 工人端问候语 ---------------- */

.greet { display: flex; align-items: center; gap: 10px; padding: 2px 2px 12px; }
.greet-text { min-width: 0; }
.greet-hello { font-size: 17px; font-weight: 700; line-height: 1.25; }
.greet-date { font-size: var(--fs-sub); color: var(--text-2); margin-top: 1px; }

/* ---------------- 累计卡（比本月更醒目） ---------------- */

.lifetime-card {
  border-color: var(--primary);
  background: linear-gradient(180deg, var(--primary-soft), var(--surface));
}
.lifetime-card .card-title { color: var(--primary); }
.lifetime-num {
  font-size: 26px; font-weight: 800; line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

/* ---------------- 整月考勤日历（上班/休息/请假 一眼看全） ---------------- */

.att-legend { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; font-size: 12px; color: var(--text-2); }
.att-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 4px; vertical-align: -1px; }
.att-legend .lg-work { background: var(--primary); }
.att-legend .lg-rest { background: #cbd5e1; }
.att-legend .lg-leave { background: var(--warn); }
.att-legend .lg-none { background: var(--surface); border: 1px dashed var(--border); }

.att-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-top: 10px; }
.att-wk { text-align: center; font-size: 11px; color: var(--text-3); padding-bottom: 2px; }
.att-cell {
  position: relative;
  min-height: 40px;
  border-radius: 8px;
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-size: 12px; line-height: 1.1; color: var(--text-2);
}
.att-cell .att-day { font-weight: 700; font-variant-numeric: tabular-nums; }
.att-cell .att-tag { font-size: 10px; margin-top: 1px; }
.att-blank { background: transparent; border: none; }
.att-work { background: var(--primary-soft); border-color: #bfdbfe; color: var(--primary-dark); }
/* 休息格：原 #64748b 在 #f1f5f9 底上只有 4.34:1，差一点过不了 AA —— 格子里的字本来就只有 10~12px，
   更不能踩线，所以加深一档（#475569 → 6.92:1）。 */
.att-rest { background: #f1f5f9; border-color: #e2e8f0; color: #475569; }
.att-leave { background: var(--warn-soft); border-color: #fde68a; color: #92400e; }
.att-future { background: transparent; border: 1px dashed var(--border-2); color: var(--text-3); }
.att-cell.att-today { box-shadow: 0 0 0 2px var(--primary) inset; }
/* 可点的那天（过去/今天）→ 小手 + 轻按反馈；未来天不可点 */
.att-cell[data-att-date] { cursor: pointer; }
.att-cell[data-att-date]:hover { filter: brightness(.96); }
.att-cell[data-att-date]:active { transform: scale(.96); }
.att-sum { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; font-size: 13px; }
.att-sum b { font-variant-numeric: tabular-nums; }
.att-sum .s-work { color: var(--primary-dark); }
.att-sum .s-rest { color: #64748b; }
.att-sum .s-leave { color: #92400e; }
.att-sum .s-none { color: var(--text-3); }

/* ---------------- 加班待确认（今日总览最上面那张卡） ---------------- */

.ot-alert { border-color: #f59e0b; background: #fffbeb; }
.ot-alert .card-title { color: #92400e; }
.ot-row { cursor: pointer; }
.ot-row:hover { background: var(--warn-soft); }
.ot-big { border-color: #fde68a; }
.ot-big .num { font-size: 17px; }

/* ---------------- 弱网待上传条（工人端，顶部悬浮） ---------------- */

.offline-bar {
  position: fixed;
  left: 8px; right: 8px;
  bottom: 68px;                 /* 抬到 tabbar 上面，不挡住切页 */
  z-index: 60;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 12px;
  border: 1px solid #93c5fd;
  border-radius: 10px;
  background: #eff6ff;
  color: #1e40af;
  font-size: 13px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
}
.offline-bar .btn { flex: 0 0 auto; }

/* ---------------- AI 设置页（密钥状态 / 测试连接） ---------------- */

.ai-key-state { font-size: 12px; }
.ai-test-ok { color: var(--success); font-size: 13px; }
.ai-test-bad { color: var(--danger); font-size: 13px; }
.ai-test-hint { margin-top: 4px; font-size: 13px; font-weight: 700; }

/* ---------------- 问工地 AI（工人端抽屉，手机优先：字大、按钮好点） ---------------- */

.ai-mask {
  position: fixed; inset: 0; z-index: 150;
  background: rgba(17, 24, 39, .5);
  display: flex; align-items: flex-end; justify-content: center;
}
.ai-panel {
  background: var(--surface); width: 100%; max-width: 560px; height: 92vh;
  border-radius: 14px 14px 0 0; display: flex; flex-direction: column;
  animation: slideUp .18s ease-out;
}
.ai-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--border); font-size: 16px;
}
.ai-tabs { display: flex; gap: 8px; padding: 10px 14px 0; }
.ai-tabs button {
  flex: 1; min-height: 42px; border: 1px solid var(--border); background: var(--surface);
  border-radius: var(--radius-btn); font-size: 16px; font-family: inherit;
  color: var(--text-2); cursor: pointer;
}
.ai-tabs button.active { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.ai-body { flex: 1; overflow-y: auto; padding: 12px 14px; }
.ai-foot {
  border-top: 1px solid var(--border); background: var(--surface-2);
  padding: 8px 14px calc(8px + env(safe-area-inset-bottom, 0px));
}
.ai-note {
  font-size: 13px; line-height: 1.45; color: #92400e;
  background: var(--warn-soft); border: 1px solid #fde68a;
  border-radius: 8px; padding: 6px 8px; margin-bottom: 8px;
}
.ai-hints { margin-bottom: 10px; }
.ai-q { font-size: 16px; font-weight: 700; margin: 12px 0 6px; word-break: break-all; }
.ai-a { background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px; padding: 10px; }
.ai-src { margin-bottom: 6px; }
.ai-text { white-space: pre-wrap; word-break: break-word; font-size: 16px; line-height: 1.6; }
.ai-cat {
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
  padding: 8px 10px; margin-bottom: 8px;
}
.ai-cat > summary { font-size: 16px; font-weight: 700; cursor: pointer; }
.ai-entry { border-top: 1px dashed var(--border); margin-top: 8px; padding-top: 8px; }
.ai-entry > summary { font-size: 15px; color: var(--primary); cursor: pointer; }
.ai-entry .ai-text { margin-top: 6px; font-size: 15px; }
.ai-input { font-size: 16px; }   /* 16px 以下 iOS 聚焦时会自动放大页面 */

/* 「去设置」滚到目标卡片时闪一下，让人知道该看哪 */
.flash { animation: flashBg 1.4s ease-out; }
@keyframes flashBg {
  0%, 35% { background: var(--primary-soft); border-color: var(--primary); }
  100% { background: var(--surface); }
}

/* ---------------- 表格/单据（拍照 → 自动识别 → 核对 → 导出） ---------------- *//* 识别要等 20 秒，必须让人看见「它在干活」，不然会以为卡死点了又点 */
.spin {
  display: inline-block; width: 13px; height: 13px; vertical-align: -2px;
  border: 2px solid rgba(255, 255, 255, .45); border-top-color: #fff;
  border-radius: 50%; animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* 浅色按钮上的转圈要用深色，白圈在白底上看不见 */
.spin-dark { border-color: rgba(0, 0, 0, .2); border-top-color: var(--primary); }

/* AI「说一句自动填」写进去的格子：浅黄底，让工人一眼看出哪些是机器填的、
   需要自己核对一下；手指一碰键盘（input 事件）就恢复原样，表示已确认 */
.auto-filled { background: #fffbe6 !important; border-color: #f0c33c !important; }

/* ---------------- 切页反馈 ---------------- */

/* 点消息/待办跳页时，以前只弹一句 toast，页面瞬间就换了 —— 用户容易以为是
   「点错了」或「没反应」。给刚切过来的那一页一个 0.26s 的淡入。
   只由切页动作加这个类，不在每次重渲染时加：否则列表一刷新就一直闪。 */
.page-enter { animation: pageIn .26s ease-out; }
@keyframes pageIn {
  from { opacity: .35; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .page-enter { animation: none; } }

/* ---------------- 弱化的破坏性按钮 ---------------- */

/* 「改成休息/请假」会**删掉当天已记的记录**，但它和「重新填写」以前并排、长得一模一样，
   手指一滑就误触。所以给它描边弱化 + 单独一行，视觉权重明显低于普通按钮。 */
.btn-quiet-danger { border-color: var(--border); color: var(--danger); background: var(--surface); }
.btn-quiet-danger:active { background: var(--danger-soft); border-color: var(--danger); }

/* 「追加 / 替换 / 取消」这种必须由工人自己拿主意的选择框：默认焦点在「取消」上。
   按钮本身不加颜色（不加颜色 = 不暗示任何一边更好），只给焦点一个看得见的圈，
   让人一眼知道"不点就默认不是追加"。 */
.btn-default-choice:focus,
.btn-default-choice:focus-visible {
  outline: 3px solid var(--primary);
  outline-offset: 2px;
}

/* ---------------- 我的账单：待确认支款（要工人动手的那张卡） ---------------- */

.need-pay-card { border-color: var(--primary); background: var(--primary-soft); }
.need-pay-card .need-pay-sum {
  font-size: 20px; font-weight: 700; color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.textarea.auto-filled { background: #fffbe6 !important; }

.doc-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.doc-table { border-collapse: collapse; min-width: 100%; }
.doc-table th, .doc-table td { padding: 3px; vertical-align: middle; }
.doc-table th { background: var(--surface-2); position: sticky; top: 0; z-index: 1; }
.doc-table .doc-no {
  width: 30px; min-width: 30px; text-align: center;
  color: var(--text-3); font-size: 12px; font-variant-numeric: tabular-nums;
}
.doc-table input { min-width: 96px; }
.doc-table td:last-child { width: 34px; }
.doc-th { display: flex; align-items: center; gap: 2px; }
.doc-th input { min-width: 88px; }
/* 选中的那张照片 = 识别用哪张，边框要一眼看得出来 */
.photo-cell.doc-pick img { outline: 3px solid var(--primary); outline-offset: 1px; }
/* 识别时看不清的格子填的是 "?"，标黄提醒要补 */
.doc-table input[data-q] { background: var(--warn-soft); border-color: #fde68a; }
.doc-peek {
  font-size: 13px; color: var(--text-2); background: var(--surface-2);
  border-radius: 8px; padding: 8px; margin-top: 6px; word-break: break-all;
  font-variant-numeric: tabular-nums;
}

/* ---------------- 工人端「我手上的表」 ---------------- */

/* 手机上表格比屏幕宽是常态（列多），横向滚动比把字挤成竖条好读；
   行尾那三个「做完 / 没做 / 有问题」按钮不能被压没，所以给它们独立一格。 */
.doc-list-table { font-size: var(--fs-sub); }
.doc-list-table th, .doc-list-table td {
  padding: 6px 8px; white-space: nowrap; border-bottom: 1px solid var(--border);
}
.doc-list-table th {
  position: static; color: var(--text-2); font-weight: 600; background: var(--surface-2);
}
.doc-list-table td:last-child { width: auto; }
.doc-mark-cell { white-space: normal; min-width: 132px; }
/* 标了「有问题」的那行，备注单独占一行显示，别挤在格子里 */
.doc-note-row td { color: var(--danger); white-space: normal; }
.doc-note-row td:first-child { border-bottom: none; }
/* 「我手上的表」这张卡里的表头说明：小屏上换行更好读 */
.doc-list-table .input-sm { min-width: 96px; }

/* =========================================================
 * 手机端触控目标与折叠（独立一段，避免改动上面桌面的排版）
 *
 * 为什么单独放一段、而不是塞进上面那个 @media 块里：
 *   · 上面的块里有大量「桌面视觉不动、手机收紧留白」的老规则，混在一起改动风险大；
 *   · 这一段只解决「手指点不准」——尺寸、间距、折叠，全部在 ≤767px 生效，
 *     桌面端一行都不受影响（后面有专门的桌面断言守着）。
 * ========================================================= */

@media (max-width: 767px) {
  /*
   * 一、把「手指要点的东西」统一到 ≥44px。
   *
   * 现状：.btn-sm 34px / .btn-xs 28px / .chip 34px / .input-sm 36px —— 这些是
   * 列表里的「修改/通过/驳回/删除/看明细」和数据导入的筛选控件，工头在工地单手点，
   * 28px 必然误触，而这些按钮**涉及钱**（删错、驳错都是事故）。
   */
  .btn-sm,
  .btn-xs,
  .btn-icon,
  .chip,
  .input-sm { min-height: 44px; }
  /* 顶栏那三个图标按钮（🤖 / 改密 / 退出）原来只有 34×34，单手点十次错三次。
     宽度也一起给到 44px，别只加高不加宽（手指是按面积的）。 */
  .btn-icon { min-width: 44px; }

  /* 列表行里的操作按钮：除了加高，还要和正文拉开 8px，别挤在一起 */
  .li-actions { gap: 8px; }
  .li-actions .btn { min-height: 44px; padding: 0 12px; font-size: 13px; }

  /* 今日总览的人员卡上也有按钮（看明细 / 照片N张）。它以前被 .btn-xs 的 28px 盖住，
     审计特意点名「.person-card .btn 漏网」—— 这里补上。 */
  .person-card .btn { min-height: 44px; padding: 0 10px; }

  /*
   * 二、审核中心：手机上按钮必须换行，而且不能被压扁。
   *
   * 实测修前：卡片内 3 个按钮**每个只有 42px 宽**、水平间距 6px —— 通过/驳回/删除
   * 挤成一排，点错一个就是钱的事。这里让按钮独占一行并按 40% 排两列：
   * 每个 ≥140px 宽、44px 高、彼此 8px。桌面上这一套不生效（只在 ≤767px）。
   */
  .review-list .list-item { flex-wrap: wrap; row-gap: 8px; }
  .review-list .li-actions {
    flex: 1 1 100%; flex-wrap: wrap; justify-content: flex-start; gap: 8px;
  }
  .review-list .li-actions .btn { flex: 1 1 40%; min-height: 44px; font-size: 14px; }
  .review-list .li-actions .thumb { flex: 0 0 auto; }

  /*
   * 三、考勤日历：格子 40px、「漏」字 10px → 手指点不准、字看不清。
   *     390px 宽下 7 列每格 46px，所以高度给到 44px 也不会撑破。
   */
  .att-cell { min-height: 44px; font-size: 13px; }
  .att-cell .att-tag { font-size: 11px; }
  .att-wk { font-size: 12px; }

  /*
   * 四、新建表格弹窗的竖排（真正的排版在 admin.js 里，见 paintGrid 的 isNarrow 分支）。
   *     这里只把「一行一张卡」的观感补齐，让列名/格子之间的关系一眼看得出来。
   */
  .doc-vcol { margin-bottom: 8px; }
  .doc-vrow { margin-bottom: 10px; }
  .doc-vrow .field:last-child { margin-bottom: 0; }

  /*
   * 五、行内文字入口（人名 / 可点数字）的**透明扩展点击区**。
   *
   * 为什么不能像按钮那样直接 min-height:44px：.name-link / .drill 都是 display:inline
   * 的 <span>（.drill 也常挂在 div.center 上，但同样不该被撑高），
   * 给它们撑高会把整段的行高/格子高度顶起来 —— 排版就变了，桌面虽然不受影响，
   * 但手机上「一屏能看到几个人」是要紧事。所以：**视觉一个像素都不动**，
   * 只在下面挂一层透明的 ::before，把**手指能点到的那块面积**上下各扩一点。
   *
   * 为什么左右不扩（left/right 都是 0）：同一行里紧跟其后的往往是标签或别的人名，
   * 左右一扩就会把别人的点击吃过来。上下扩是安全的 —— 上下相邻的通常是纯文字行。
   *
   * 为什么把 `.tag[data-worker]` 也算进来（「今日异常」「代填」这两个标签）：
   * 它们和 `.name-link` 挂的是**同一个 data-worker**，admin.js 的委托是
   * `e.target.closest('[data-worker]') → openWorker(id, month)` —— 点标签和点人名
   * 是同一件事，用户根本分不出来哪个是哪个。只放人名、不放标签，等于同一个入口
   * 一半好点一半难点。注意选择器写的是 `[data-worker]` 而不是裸 `.tag`：
   * `已激活` / `未激活` / `启用` / `停用` 这些标签没有动作，**不能**给它们加。
   *
   * 为什么不用 z-index:-1 去"避免抢点击"：负层级会把 ::before 压到祖先的行盒之下，
   * 那样连它自己扩出来的区域都会被祖先行盒接走，等于白扩（实测 elementFromPoint
   * 会返回外层 div）。所以这里让它正常处在定位层，再用「不越界 + 不左右扩」把风险压住，
   * 并且脚本里有一条专门的断言守着「原来能点的按钮没被抢走」。
   *
   * 为什么**不加** min-height:100%（加过，实测是错的）：加了以后元素比 44px 高时
   * 盒子被撑到元素那么高，而 top:50% + margin-top:-22px 就不再居中 —— 实测
   * admin/today 那个 60px 高的 .drill 卡片，可点区变成"上 31 + 下 37"，整体往下偏 8px，
   * 白白多侵占下一行 8px。不加 min-height 时盒子恒为 44px 且始终居中：
   *   · 元素 <44px → 上下各补 (44-h)/2，正是要的效果；
   *   · 元素 ≥44px → 44px 的盒子整个落在元素自己的盒子里，**一点都没往外侵**。
   * 不会因此让点击区变小：::before 属于元素自己，命中它返回的还是这个元素，
   * 所以元素原有的那一块永远算数（脚本里"只增不减"那条断言就是守这个的）。
   */
  .name-link,
  .drill,
  .tag[data-worker] { position: relative; }
  .name-link::before,
  .drill::before,
  .tag[data-worker]::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    margin-top: -22px;
    height: 44px;
  }
}

/* 手机端筛选区默认收起（点「筛选」才展开）——用的是通用 .hide，见 admin.js renderRecords。
   这里不需要额外样式：收起/展开靠 .hide，按钮本身由 .btn-sm / .btn 兜底尺寸。 */

/* =========================================================
 * 工人端「把工地记工放到手机桌面」引导（一次性）
 *
 * 为什么是**「今日」页最上面的一张内联卡片，而不是浮层/弹窗**：
 *   最早做的是 fixed 贴底浮层（当时想的是"盖不到顶栏、压不住底部栏"），
 *   结果被 browser-check 抓出来：**fixed 就必然盖住一块内容**——
 *   工人要点的东西（快捷工时、休息、支出页的「添加」）正好落在浮层底下时，
 *   他点下去是点在浮层上，**看着点了却没反应**。
 *   这跟"关掉之后照样上工"是两回事：关掉之前的那几秒，活儿已经被挡了。
 *   改成内联卡片后它跟内容一起滚，物理上不可能盖住任何东西；
 *   而且它就在「今日」页第一张，一进来第一眼就看见 —— 引导效果反而更好。
 *
 * 桌面端不出现：只对手机有意义（"放到手机桌面"在电脑上不成立）。
 * ========================================================= */
.home-tip { border-color: var(--primary); }
.home-tip-head { display: flex; align-items: center; gap: 8px; }
.home-tip-head b { flex: 1; min-width: 0; font-size: 15px; line-height: 1.4; }
/* 「关掉」也是手指要点的东西，一样按 44px 给（靠 padding 撑，不去动字号） */
.home-tip-close {
  flex: 0 0 auto; border: none; background: none; font-family: inherit;
  color: var(--text-3); font-size: 14px; cursor: pointer;
  padding: 12px 10px; min-height: 44px; min-width: 44px;
}
.home-tip-sub { margin: 6px 0 0; color: var(--text-2); font-size: var(--fs-sub); line-height: 1.5; }
/* 行高 1.6 而不是 1.7：卡片现在长在首屏里，省下的这几像素直接决定
   「今天上工 / 休息」按钮还看不看得见（a11y-fix-check 有这条硬断言）。 */
.home-tip-steps { margin: 8px 0 0; padding-left: 22px; color: var(--text); font-size: var(--fs-sub); line-height: 1.6; }
.home-tip-act { display: flex; gap: 8px; margin-top: 10px; }
.home-tip-act .btn { flex: 1 1 auto; }
/* 桌面端不出现：这一条只对手机有意义（"放到手机桌面"本身就不成立） */
@media (min-width: 768px) { .home-tip { display: none; } }

/* =========================================================
 * 施工记录（管理端派活 / 进度总表 · 工人端写进度）
 *
 * 类名前缀：
 *   · 管理端用 `wlog-`（`wk-` 已经被「人员管理」的工人卡占了，重名会把别人的卡带歪）
 *   · 工人端那张"派给我干的活"卡用 `wk-work` / `wk-urge`（和上面的 `wk-card` 不是一套）
 * ========================================================= */

/* 总览表里"一个活一行"整行可点：鼠标移上去要有"这里能点"的反馈 */
.wlog-row { cursor: pointer; }
.wlog-row:hover td { background: var(--primary-soft); }
/* 进度条塞在 flex 行里：不给 flex 它会按内容塌成一条线 */
.wlog-bar { flex: 1; min-width: 60px; }

/* 派活：一行 = 一个人。窄屏上几个框会换行，给一点纵向间距免得糊成一片 */
.wlog-arow { margin-top: 8px; }
.wlog-arow:first-child { margin-top: 0; }

/* 活详情 · 按天明细（手机端） */
.wlog-day { border-bottom: 1px solid var(--border-2); padding: 10px 0; }
.wlog-day:last-child { border-bottom: none; }
.wlog-day-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.wlog-item { margin-top: 8px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
.wlog-item-top { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.wlog-user { padding: 8px 0; border-bottom: 1px solid var(--border-2); }
.wlog-user:last-child { border-bottom: none; }

/* 工人端「派给我干的活」 */
.wk-work { padding: 10px 0; border-bottom: 1px solid var(--border-2); }
.wk-work:last-child { border-bottom: none; }
/* 提示工人写一句 —— 只有活上开了「要求回报」才有这一段（没开的一个字都不催） */
.wk-urge {
  margin-top: 6px; padding: 7px 9px; border-radius: 8px;
  background: var(--warn-soft); border: 1px solid #fde68a; color: #92400e;
  font-size: var(--fs-sub); line-height: 1.5;
}

@media (max-width: 767px) {
  /* 派活页的输入框/按钮就是手指的目标，桌面的 34px 在工地太容易点错 */
  .wlog-arow .btn,
  .wlog-arow .select,
  .wlog-arow .input { min-height: 44px; }
  /* 工人端"写进度"是这一页最主要的动作，按 44px 给 */
  .wk-work .btn { min-height: 44px; padding: 0 14px; font-size: 14px; }
}

/* =========================================================
 * v12 工人端首屏：先回答"我现在该干什么"
 *
 * 生产库上只有 5 条记工记录 —— 功能做得再多，新人打开不知道点哪儿就等于没有。
 * 所以「今日」页第一张卡只干一件事：
 *   · 今天还没记工 → 一个独占一整行的大按钮「今天上工」+ 一句人话；
 *     休息 / 请假留着（工人确实需要），但降级成小一号的描边按钮，天生不跟主按钮抢。
 *   · 今天已经记过 → 「✅ 记好了 · 今天 N 个工（M 小时）」+「改一笔」。
 *
 * 为什么主按钮要 64px：工地上是单手、戴手套、屏幕在阳光下反光，54px 的通用
 * .btn-lg 在 390px 宽的手机上仍然偏小；这一颗是一天要按一次的主入口，给足。
 * 为什么不用 .action-btn（原来那三个大方块）：48px 的格子并排三个，
 * 新人看不出哪个是"最重要的那一个" —— 问题恰恰出在"三个一样大"。
 * ========================================================= */
.hero-work { border-color: var(--primary); }
.hero-work-hint { color: var(--text-2); font-size: 15px; line-height: 1.4; margin-bottom: 10px; }
/* 借 .action-grid 这个类名是有意的：a11y-fix-check 有一条硬断言按它量
   「默认首屏就能看到今天上工/休息按钮」，这里把它的排布改成"主按钮独占一行" */
.hero-work-act { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.hero-work-act .hero-work-main { grid-column: 1 / -1; min-height: 64px; font-size: 20px; }
/* 次要动作：小一号 + 灰字描边，一眼就知道"没上面那个重要" */
.hero-work-act .hero-work-minor { min-height: 44px; font-size: 14px; color: var(--text-2); }

/* 「记好了」：用 banner-success 那支深绿（#166534 在白底 5.9:1），
   不用 --success #16a34a（白底只有 3.0:1，工地强光下看不清）。 */
.hero-done { border-color: #bbf7d0; background: var(--success-soft); }
.hero-done-head { color: #166534; font-size: 16px; font-weight: 700; }
.hero-done-num { color: var(--text); font-size: 20px; font-weight: 700; margin-top: 6px; line-height: 1.35; }
.hero-done-num .num { font-variant-numeric: tabular-nums; }

/* 第一次进来那句话的引导：**内联卡片**（跟内容一起滚），绝不 fixed —— 见 worker.js 第 ⑩ 段 */
.work-tip { border-color: var(--primary); background: var(--primary-soft); }
.work-tip b { font-size: 15px; }
.work-tip .sub { font-size: var(--fs-sub); line-height: 1.5; }
