/* ============================================================================
   TimeCard Web — 视觉语言：Apple Design Language
   ----------------------------------------------------------------------------
   设计依据（apple-design skill）：
     · 原则 15 排版     —— 系统字体栈、字距随字号变化、行高与字号反向、粗细建层级
     · 原则 12 材质     —— 顶栏/底栏用半透明毛玻璃（backdrop-filter），材质重量编码层级
     · 原则 1  即时反馈 —— :active 在 pointer-down 就缩放，不等 click
     · 原则 3/4 弹簧    —— 统一用 --spring 缓动模拟临界阻尼弹簧，可中断、不过冲
     · 原则 14 无障碍   —— prefers-reduced-motion / -transparency / -contrast 三路降级
     · 原则 16 工艺     —— 颜色随明暗主题自适应，间距/圆角/时长全部有据可循
   内部传值（1-15 / 16-31 / 1-31）与类名一律不动，仅重绘外观。
   ============================================================================ */

/* ---------------------------------------------------------------- tokens */
:root {
  /* 系统字体栈：优先 SF，中文回落到苹方，再落到雅黑 */
  --font-ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
             "Helvetica Neue", "PingFang SC", "Hiragino Sans GB",
             "Microsoft YaHei UI", "Microsoft YaHei", system-ui, sans-serif;
  --font-num: ui-rounded, "SF Pro Rounded", -apple-system, "Segoe UI", system-ui, sans-serif;

  /* iOS 系统色 */
  --blue:   #007AFF;
  --green:  #248A3D;
  --orange: #C93400;
  --purple: #8944AB;
  --red:    #D70015;
  --teal:   #0071A4;

  /* 表面与分隔 */
  --bg:        #F2F2F7;                 /* systemGroupedBackground */
  --bg-elev:   #FFFFFF;                 /* 卡片 */
  --bg-sunken: #F2F2F7;
  --sep:        rgba(60,60,67,.12);
  --sep-soft:   rgba(60,60,67,.07);

  /* 文字层级（对应 iOS label / secondaryLabel / tertiaryLabel） */
  --label:   #1C1C1E;
  --label-2: rgba(60,60,67,.72);
  --label-3: rgba(60,60,67,.42);

  /* 填充层（fill） */
  --fill-1:  rgba(120,120,128,.10);
  --fill-2:  rgba(120,120,128,.16);

  /* 材质（毛玻璃） */
  --mat-bar:   rgba(249,249,249,.76);
  --mat-toast: rgba(250,250,252,.88);   /* 通知横幅要压在任意内容之上，比工具条更实 */
  --mat-hair:  rgba(255,255,255,.65);

  /* 阴影：越大的面越重 */
  --sh-1: 0 1px 2px rgba(0,0,0,.035), 0 1px 6px rgba(0,0,0,.035);
  --sh-2: 0 1px 2px rgba(0,0,0,.04),  0 6px 20px rgba(0,0,0,.055);
  --sh-3: 0 2px 6px rgba(0,0,0,.06),  0 16px 40px rgba(0,0,0,.10);

  /* 圆角（连续圆角观感） */
  --r-xs: 7px;
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 26px;
  --r-pill: 999px;

  /* 动效：临界阻尼弹簧近似（无过冲），可中断 */
  --spring:      cubic-bezier(.32,.72,0,1);
  --spring-soft: cubic-bezier(.22,.61,.36,1);
  --dur-1: .16s;
  --dur-2: .28s;
  --dur-3: .45s;

  color-scheme: light dark;
}

/* 深色模式：同语义，换值 */
@media (prefers-color-scheme: dark) {
  :root {
    --blue:   #0A84FF;
    --green:  #30D158;
    --orange: #FF9F0A;
    --purple: #BF5AF2;
    --red:    #FF453A;
    --teal:   #40C8E0;

    --bg:        #000000;
    --bg-elev:   #1C1C1E;
    --bg-sunken: #1C1C1E;
    --sep:        rgba(84,84,88,.48);
    --sep-soft:   rgba(84,84,88,.28);

    --label:   #FFFFFF;
    --label-2: rgba(235,235,245,.62);
    --label-3: rgba(235,235,245,.34);

    --fill-1:  rgba(120,120,128,.22);
    --fill-2:  rgba(120,120,128,.32);

    --mat-bar:   rgba(28,28,30,.74);
    --mat-toast: rgba(44,44,46,.90);
    --mat-hair:  rgba(255,255,255,.10);

    --sh-1: 0 1px 2px rgba(0,0,0,.5);
    --sh-2: 0 1px 2px rgba(0,0,0,.5), 0 8px 24px rgba(0,0,0,.5);
    --sh-3: 0 2px 8px rgba(0,0,0,.6), 0 20px 48px rgba(0,0,0,.6);
  }
}

/* ---------------------------------------------------------------- reset */
* { box-sizing: border-box; margin: 0; padding: 0; }

html { font-size: 100%; -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font-ui);
  font-optical-sizing: auto;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--label);
  background-color: var(--bg);
  /* 极淡的氛围光：给顶部一点纵深，不干扰阅读 */
  background-image:
    radial-gradient(1100px 420px at 14% -6%, rgba(0,122,255,.10), transparent 68%),
    radial-gradient(880px 360px at 88% -12%, rgba(137,68,171,.075), transparent 68%);
  background-repeat: no-repeat;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  padding-bottom: env(safe-area-inset-bottom);
}

::selection { background: color-mix(in srgb, var(--blue) 24%, transparent); }

/* 细滚动条（覆盖式，不抢视线） */
* { scrollbar-width: thin; scrollbar-color: var(--label-3) transparent; }
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--label-3); border-radius: var(--r-pill);
  border: 2px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--label-2); background-clip: content-box; }

:focus-visible {
  outline: 3px solid color-mix(in srgb, var(--blue) 55%, transparent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

/* ================================================================ header */
/* 原则 12：顶栏是"浮动功能层"——半透明材质 + 内容在其下方滚动，而不是实心色块 */
header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--mat-bar);
  -webkit-backdrop-filter: saturate(180%) blur(24px);
  backdrop-filter: saturate(180%) blur(24px);
  border-bottom: 1px solid var(--sep);
  padding: 0 24px;
  padding-top: env(safe-area-inset-top);
}
.topbar-inner {
  max-width: 1240px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 0;
}

/* App 图标：圆角方（squircle 观感）+ 卡片图形，替代原 emoji（跨平台字形不一致） */
.app-icon { flex: none; line-height: 0; }
.app-icon svg {
  width: 38px; height: 38px; display: block;
  border-radius: 11px;
  box-shadow: var(--sh-1), inset 0 1px 0 rgba(255,255,255,.28);
}
@media (prefers-color-scheme: dark) {
  .app-icon svg { box-shadow: var(--sh-1), inset 0 1px 0 rgba(255,255,255,.10); }
}

header h1 {
  font-size: 1.3125rem;          /* 21px */
  font-weight: 700;
  letter-spacing: -.022em;       /* 原则 15：大字号收紧字距 */
  line-height: 1.18;
  color: var(--label);
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
}
header h1 .sub {
  font-size: .6875rem;           /* 11px */
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--label-2);
  background: var(--fill-1);
  padding: 3px 9px;
  border-radius: var(--r-pill);
  line-height: 1.35;
}
.tagline {
  font-size: .8125rem;           /* 13px */
  line-height: 1.45;
  color: var(--label-2);
  margin-top: 2px;
  letter-spacing: 0;             /* 小字号不再收紧 */
  /* 注意：ch 是"0"字形宽度，中文环境下 70ch ≈ 只有 35 个汉字，会把这句话挤成两行。
     这里按实际像素给宽度，刚好容纳整句。 */
  max-width: 660px;
}

.engine-box {
  margin-left: auto;
  flex: none;
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--label-2);
  background: var(--fill-1);
  border: 1px solid var(--sep-soft);
  padding: 7px 12px;
  border-radius: var(--r-pill);
  max-width: 280px;
  text-align: right;
  line-height: 1.45;
  word-break: break-all;
}
.engine-box.err {
  color: var(--red);
  background: color-mix(in srgb, var(--red) 11%, transparent);
  border-color: color-mix(in srgb, var(--red) 26%, transparent);
}

/* 双模型就绪 / 缺一个：与 err 同一套色阶，只是换成绿与橙 */
.engine-box.ok {
  color: var(--green);
  background: color-mix(in srgb, var(--green) 11%, transparent);
  border-color: color-mix(in srgb, var(--green) 26%, transparent);
}

.engine-box.warn {
  color: var(--orange);
  background: color-mix(in srgb, var(--orange) 12%, transparent);
  border-color: color-mix(in srgb, var(--orange) 28%, transparent);
}

/* PWA：「添加到主屏」胶囊（pwa.js 动态插入）在底部下载条出现时让位，
   否则会浮在下载按钮上面把它挡住。:has() 需 Chrome 105+ / Safari 15.4+，
   不支持时最多是重叠，不影响任何功能。 */
body:has(#downloadBar:not(.hidden)) #pwaInstall { display: none !important; }

/* 顶栏文字块：占满中间空间，把右侧的引擎框与语言切换都推到最右。
   （引擎框检测成功后是 display:none，若不撑开，语言切换会跑到标题旁边） */
.topbar-text { flex: 1; min-width: 0; }

/* 语言切换：iOS 分段控件观感，材质与引擎框同一套
   （选中态用卡片底色 + 轻投影，未选中态嵌在 fill 里） */
.lang-switch {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border-radius: var(--r-pill);
  background: var(--fill-1);
  border: 1px solid var(--sep-soft);
}
.lang-switch button {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--label-2);
  font-family: inherit;
  font-size: .75rem;             /* 12px */
  font-weight: 600;
  letter-spacing: .01em;
  padding: 5px 11px;
  border-radius: var(--r-pill);
  cursor: pointer;
  line-height: 1.35;
  transition: background .18s ease, color .18s ease;
}
.lang-switch button:hover { color: var(--label); }
.lang-switch button.on {
  background: var(--bg-elev);
  color: var(--label);
  box-shadow: var(--sh-1);
}

/* ================================================================ layout */
main {
  flex: 1;
  width: 100%;
  max-width: 1240px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(340px, 396px) 1fr;
  gap: 20px;
  padding: 22px 24px 34px;
  align-items: start;
}

.panel {
  background: var(--bg-elev);
  border: 1px solid var(--sep-soft);
  border-radius: var(--r-lg);
  padding: 18px;
  box-shadow: var(--sh-2);
}

/* ============================================================== dropzone */
.dropzone {
  position: relative;
  border: 1.5px dashed var(--sep);
  border-radius: var(--r-lg);
  padding: 28px 18px 24px;
  text-align: center;
  background: var(--fill-1);
  cursor: pointer;
  transition: border-color var(--dur-2) var(--spring),
              background var(--dur-2) var(--spring),
              transform var(--dur-2) var(--spring),
              box-shadow var(--dur-2) var(--spring);
}
.dropzone:hover,
.dropzone.drag {
  border-color: var(--blue);
  background: color-mix(in srgb, var(--blue) 8%, transparent);
}
.dropzone.drag {
  transform: scale(1.012);                       /* 原则 8：中间帧指向结果 */
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--blue) 16%, transparent);
}
.dropzone:active { transform: scale(.995); }     /* 原则 1：按下即反馈 */

.dz-icon {
  width: 46px; height: 46px;
  margin: 0 auto 10px;
  display: grid; place-items: center;
  border-radius: var(--r-pill);
  color: var(--blue);
  background: color-mix(in srgb, var(--blue) 11%, transparent);
  transition: transform var(--dur-2) var(--spring);
}
.dropzone:hover .dz-icon,
.dropzone.drag .dz-icon { transform: translateY(-2px) scale(1.05); }

.dz-main {
  font-size: .9375rem;
  font-weight: 600;
  letter-spacing: -.005em;
  color: var(--label);
}
.dz-main u {
  color: var(--blue);
  text-decoration: none;
  border-bottom: 1.5px solid color-mix(in srgb, var(--blue) 40%, transparent);
  padding-bottom: 1px;
}
.dz-sub {
  font-size: .75rem;
  color: var(--label-2);
  margin-top: 7px;
  line-height: 1.65;
}
.dz-warn {
  font-size: .75rem;
  color: var(--label-2);
  margin-top: 12px;
  line-height: 1.7;
  text-align: left;
  background: color-mix(in srgb, var(--orange) 8%, transparent);
  border-left: 3px solid color-mix(in srgb, var(--orange) 55%, transparent);
  padding: 9px 12px;
  border-radius: var(--r-sm);
}
.dz-warn b { color: var(--label); font-weight: 600; }

/* ============================================================ config row */
.cfg-row { margin-top: 16px; }
.cfg-row label {
  display: block;
  font-size: .75rem;
  font-weight: 600;
  color: var(--label-2);
  margin-bottom: 7px;
  letter-spacing: .005em;
}
.cfg-controls { display: flex; gap: 9px; }
.cfg-controls select {
  flex: 1;
  min-width: 0;
  padding: 10px 34px 10px 13px;
  font-family: inherit;
  font-size: .875rem;
  font-weight: 500;
  color: var(--label);
  background-color: var(--fill-1);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  transition: background-color var(--dur-2) var(--spring),
              border-color var(--dur-2) var(--spring),
              transform var(--dur-1) var(--spring);
  /* 自绘下拉箭头（系统箭头在深色模式下不可控） */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 4.6 6 8l3.4-3.4' fill='none' stroke='%238E8E93' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
}
.cfg-controls select:hover { background-color: var(--fill-2); }
.cfg-controls select:active { transform: scale(.985); }

/* ============================================================= file list */
.file-list { margin-top: 14px; max-height: 236px; overflow-y: auto; overscroll-behavior: contain; }
.file-list:empty { display: none; }

.fitem {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 10px;
  border: 1px solid var(--sep-soft);
  border-radius: var(--r-md);
  margin-bottom: 7px;
  background: var(--bg-elev);
  font-size: .8125rem;
  animation: riseIn var(--dur-2) var(--spring) both;
}
.fitem:last-child { margin-bottom: 0; }
.fitem img {
  width: 42px; height: 42px;
  object-fit: cover;
  border-radius: var(--r-xs);
  border: 1px solid var(--sep-soft);
  background: var(--fill-1);
  flex: none;
}
.fitem .fname { flex: 1; min-width: 0; }
.fitem .fname .n {
  display: block;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-weight: 600;
  letter-spacing: -.005em;
  color: var(--label);
}
.fitem .fname .s { display: block; color: var(--label-3); font-size: .6875rem; margin-top: 1px; }
.fitem .x {
  cursor: pointer;
  color: var(--label-3);
  font-weight: 600;
  font-size: .8125rem;
  line-height: 1;
  padding: 6px 7px;
  border-radius: var(--r-pill);
  transition: background var(--dur-1) var(--spring), color var(--dur-1) var(--spring),
              transform var(--dur-1) var(--spring);
}
.fitem .x:hover { background: color-mix(in srgb, var(--red) 13%, transparent); color: var(--red); }
.fitem .x:active { transform: scale(.9); }

/* 状态胶囊 */
.chip {
  padding: 3px 10px;
  border-radius: var(--r-pill);
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .01em;
  white-space: nowrap;
  transition: background var(--dur-2) var(--spring), color var(--dur-2) var(--spring);
}
.chip.queue { background: var(--fill-1); color: var(--label-2); }
/* 上传中：比「识别中」更轻的灰蓝，且**不呼吸** —— 上传是本地行为，
   用静态色与后端阶段区分开，用户一眼能看出"还没轮到后端" */
.chip.uploading { background: var(--fill-1); color: var(--label-2); }
.chip.proc  { background: color-mix(in srgb, var(--blue) 14%, transparent); color: var(--blue);
              animation: breathe 1.6s var(--spring) infinite; }
.chip.done  { background: color-mix(in srgb, var(--green) 15%, transparent); color: var(--green); }
.chip.fail  { background: color-mix(in srgb, var(--red) 14%, transparent); color: var(--red); }

/* =============================================================== buttons */
.actions { display: flex; gap: 9px; margin-top: 14px; }

.btn {
  border: none;
  border-radius: var(--r-md);
  padding: 11px 16px;
  font-family: inherit;
  font-size: .875rem;
  font-weight: 600;
  letter-spacing: -.005em;
  white-space: nowrap;                 /* 按钮文字绝不拆行 */
  cursor: pointer;
  transition: background-color var(--dur-2) var(--spring),
              color var(--dur-2) var(--spring),
              transform var(--dur-1) var(--spring),
              box-shadow var(--dur-2) var(--spring),
              opacity var(--dur-2) var(--spring);
}
/* 原则 1：按下瞬间（pointer-down）就反馈，不等 click */
.btn:active:not(:disabled) { transform: scale(.97); }
.btn:disabled { cursor: not-allowed; opacity: .45; }

.btn.primary {
  background: var(--blue);
  color: #fff;
  flex: 1;
  box-shadow: 0 1px 3px color-mix(in srgb, var(--blue) 34%, transparent);
}
.btn.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--blue) 88%, #000); }
.btn.primary:disabled { background: var(--blue); }

.btn.accent {
  background: var(--green);
  color: #fff;
  box-shadow: 0 1px 3px color-mix(in srgb, var(--green) 32%, transparent);
}
.btn.accent:hover:not(:disabled) { background: color-mix(in srgb, var(--green) 88%, #000); }

.btn.danger {
  background: var(--red);
  color: #fff;
  flex: 1;
  box-shadow: 0 1px 3px color-mix(in srgb, var(--red) 32%, transparent);
}
.btn.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--red) 88%, #000); }

.btn.ghost {
  background: var(--fill-1);
  color: var(--label);
}
.btn.ghost:hover:not(:disabled) { background: var(--fill-2); }

.btn.small { padding: 6px 14px; font-size: .8125rem; border-radius: var(--r-sm); }

/* ============================================================== progress */
.progress { margin-top: 14px; }
.prog-text { font-size: .75rem; color: var(--label-2); margin-bottom: 7px; letter-spacing: .005em; }
.prog-bar {
  height: 7px;
  background: var(--fill-1);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.prog-fill {
  height: 100%;
  width: 0;
  background: var(--blue);
  border-radius: var(--r-pill);
  transition: width var(--dur-3) var(--spring);
}

/* =============================================================== results */
.result-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.result-head h2 {
  font-size: 1.0625rem;              /* 17px */
  font-weight: 700;
  letter-spacing: -.018em;
  line-height: 1.25;
  color: var(--label);
}
.result-meta { font-size: .75rem; color: var(--label-2); letter-spacing: .005em; }

.result-list { display: flex; flex-direction: column; gap: 12px; }
.empty-hint { color: var(--label-3); font-size: .875rem; }

.empty {
  text-align: center;
  padding: 52px 24px;
  line-height: 1.85;
  color: var(--label-3);
  font-size: .875rem;
}
.empty .empty-icon {
  display: block;
  margin: 0 auto 14px;
  width: 48px; height: 48px;
  color: var(--label-3);
  opacity: .7;
}

/* 单张照片的结果卡 */
.rcard {
  border: 1px solid var(--sep-soft);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--bg-elev);
  box-shadow: var(--sh-1);
  animation: riseIn var(--dur-3) var(--spring) both;
  animation-delay: calc(var(--i, 0) * 45ms);   /* 原则 8：错峰入场，轨迹可预判 */
}
.rcard .rhead {
  background: var(--fill-1);
  padding: 10px 15px;
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--sep-soft);
  font-size: .75rem;
  color: var(--label-2);
}
.rcard .rhead .fname {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  font-size: .8125rem;
  color: var(--label);
  letter-spacing: -.005em;
}
.rcard .rhead .fname svg { flex: none; color: var(--label-3); }
.rcard .rhead .time {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-size: .75rem;
  color: var(--label-2);
  background: var(--bg-elev);
  border-radius: var(--r-pill);
  padding: 2px 9px;
}
/* token / 图片体积徽章（.tok / .imgsz）已按用户要求从识别页撤下（2026-09-10）：
   这些成本信息仍在后台 /admin.html 与历史记录里，识别页只留识别结果本身。 */
.rcard .rbody { padding: 12px 15px 14px; display: flex; flex-direction: column; gap: 10px; }
/* 单个工人卡 */
.wcard {
  border: 1px solid var(--sep-soft);
  border-left: 3px solid var(--blue);
  border-radius: var(--r-md);
  padding: 12px 14px;
  background: var(--bg-elev);
  transition: box-shadow var(--dur-2) var(--spring), transform var(--dur-2) var(--spring);
}
.wcard:hover { box-shadow: var(--sh-1); }
.wcard.ok   { border-left-color: var(--green); }
.wcard.warn {
  border-left-color: var(--orange);
  background: color-mix(in srgb, var(--orange) 6%, var(--bg-elev));
}

.wtop { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.wtop .nm {
  font-size: .9375rem;
  font-weight: 700;
  letter-spacing: -.012em;
  color: var(--label);
}
.wtop .id {
  font-size: .8125rem;
  font-weight: 600;
  color: var(--blue);
  font-variant-numeric: tabular-nums;
}
.wtop .tag {
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .01em;
  background: var(--fill-1);
  color: var(--label-2);
  padding: 2px 9px;
  border-radius: var(--r-pill);
}
/* 卡面周期徽章：半月=中性灰，整月=绿（一眼区分），虚框=按出勤天数推断 */
.wtop .tag.period { background: var(--fill-1); color: var(--label-2); }
.wtop .tag.period.full {
  background: color-mix(in srgb, var(--green) 15%, transparent);
  color: var(--green);
  font-weight: 700;
}
.wtop .tag.period.guessed {
  border: 1px dashed currentColor;
  background: transparent;
}
.wtop .site { color: var(--label-2); font-size: .75rem; }
.wtop .act { margin-left: auto; font-size: .75rem; }
.wtop .act a {
  color: var(--blue);
  text-decoration: none;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--blue) 9%, transparent);
  transition: background var(--dur-2) var(--spring), transform var(--dur-1) var(--spring);
  display: inline-block;
}
.wtop .act a:hover { background: color-mix(in srgb, var(--blue) 17%, transparent); }
.wtop .act a:active { transform: scale(.96); }

/* 统计胶囊组 */
.stats { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 10px; }
.st {
  background: var(--fill-1);
  border-radius: var(--r-sm);
  padding: 7px 11px;
  min-width: 68px;
  text-align: center;
  font-size: .6875rem;
  transition: background var(--dur-2) var(--spring);
}
.st:hover { background: var(--fill-2); }
.st b {
  display: block;
  font-family: var(--font-num);
  font-size: 1.0625rem;              /* 17px */
  font-weight: 700;
  letter-spacing: -.022em;           /* 原则 15：数字放大后收紧字距 */
  line-height: 1.25;
  font-feature-settings: 'tnum' 1;
  font-variant-numeric: tabular-nums;
  color: var(--label);
}
.st .lbl {
  display: block;
  margin-top: 1px;
  color: var(--label-2);
  font-size: .625rem;                /* 10px */
  font-weight: 500;
  letter-spacing: .03em;             /* 原则 15：小字号给正字距保可读 */
}
.st .lbl i { font-style: normal; opacity: .8; }

/* 语义配色：一眼分辨指标类别 */
.stats .st:nth-child(1) b,
.stats .st:nth-child(2) b,
.stats .st:nth-child(3) b { color: var(--blue); }
.stats .st.ot b { color: var(--green); }
.stats .st:nth-child(5) b { color: var(--orange); }
.stats .st:nth-child(6) b { color: var(--purple); }

.warn-line {
  font-size: .75rem;
  line-height: 1.7;
  color: var(--label-2);
  background: color-mix(in srgb, var(--orange) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--orange) 22%, transparent);
  border-radius: var(--r-sm);
  padding: 8px 11px;
  margin-top: 9px;
}
.warn-line b { color: var(--label); }

/* ========================================================== download bar */
/* 原则 12：底栏同样是浮动材质层，压在内容之上而非把内容挤走 */
.download-bar {
  position: sticky;
  bottom: 0;
  z-index: 30;
  background: var(--mat-bar);
  -webkit-backdrop-filter: saturate(180%) blur(24px);
  backdrop-filter: saturate(180%) blur(24px);
  border-top: 1px solid var(--sep);
  padding: 13px 24px;
  padding-bottom: calc(13px + env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 16px;
  box-shadow: 0 -8px 28px rgba(0,0,0,.05);
  animation: barUp var(--dur-3) var(--spring) both;
}
.topbar-inner > * { min-width: 0; }
/* 左下角那行统计（卡数/工人数/月份/耗时）已按用户要求撤下（2026-09-10）——
   同样三个数字在识别结果头部已经有了，没必要在页脚重复一遍。
   元素仍留在 DOM 里（只是隐藏），下载按钮因此改为靠右。 */
.dl-note { display: none; }
.dl-btns { display: flex; gap: 10px; flex-wrap: wrap; margin-left: auto; }

/* ================================================================ toast */
/* iOS 通知样式：毛玻璃胶囊，从顶部弹簧落下 */
.toast {
  position: fixed;
  top: calc(16px + env(safe-area-inset-top));
  left: 50%;
  z-index: 99;
  transform: translateX(-50%);
  max-width: min(84vw, 520px);
  padding: 12px 20px;
  border-radius: var(--r-lg);
  font-size: .8125rem;
  font-weight: 500;
  line-height: 1.5;
  color: var(--label);
  background: var(--mat-toast);
  -webkit-backdrop-filter: saturate(180%) blur(26px);
  backdrop-filter: saturate(180%) blur(26px);
  border: 1px solid var(--mat-hair);
  box-shadow: var(--sh-3);
  animation: toastIn var(--dur-3) var(--spring) both;
}
.toast.err {
  color: var(--red);
  background: color-mix(in srgb, var(--red) 14%, var(--mat-toast));
  border-color: color-mix(in srgb, var(--red) 30%, transparent);
}

.hidden { display: none !important; }
footer { margin-top: auto; }

/* ====================================================== merge hint 合并提示 */
.merge-bar { display: flex; flex-direction: column; gap: 8px; }
.merge-hint {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: .8125rem;
  color: var(--label);
  background: color-mix(in srgb, var(--blue) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--blue) 20%, transparent);
  border-radius: var(--r-md);
  padding: 10px 14px;
  margin-bottom: 1px;
  animation: riseIn var(--dur-2) var(--spring) both;
}
.merge-hint b { font-weight: 700; }
.merged-badge {
  background: color-mix(in srgb, var(--green) 15%, transparent);
  color: var(--green);
  border-radius: var(--r-pill);
  padding: 2px 9px;
  font-size: .6875rem;
  font-weight: 600;
}
.unmerge-link {
  font-size: .75rem;
  color: var(--blue);
  text-decoration: none;
  font-weight: 600;
  margin-left: 2px;
}
.unmerge-link:hover { text-decoration: underline; }

/* ========================================================== global stats */
.global-stats {
  margin-top: 14px;
  padding: 10px 12px;
  border-top: 1px solid var(--sep-soft);
  font-size: .6875rem;
  color: var(--label-3);
  line-height: 1.6;
  text-align: center;
}
.global-stats b { color: var(--label-2); }

/* ================================================================ 动效 */
/* 原则 3/4：统一弹簧缓动、无过冲；入场从"当前值"平滑推进 */
@keyframes riseIn {
  from { opacity: 0; transform: translate3d(0, 10px, 0) scale(.994); }
  to   { opacity: 1; transform: none; }
}
@keyframes toastIn {
  from { opacity: 0; transform: translate(-50%, -18px) scale(.94); }
  to   { opacity: 1; transform: translate(-50%, 0) scale(1); }
}
@keyframes barUp {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: .55; }
}

/* =========================================================== responsive */
@media (max-width: 1080px) {
  main { grid-template-columns: minmax(300px, 360px) 1fr; gap: 18px; padding: 18px 18px 30px; }
  header { padding: 0 18px; }
  .download-bar { padding: 12px 18px; }
}

@media (max-width: 960px) {
  main { grid-template-columns: 1fr; gap: 16px; }
  .engine-box { display: none; }
  .file-list { max-height: 190px; }
}

@media (max-width: 720px) {
  .tagline { display: none; }                 /* 窄屏收起说明，让标题呼吸 */
  header h1 { font-size: 1.125rem; }
  .download-bar {
    flex-direction: column;
    align-items: stretch;
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
    gap: 9px;
  }
  .dl-note { width: 100%; text-align: center; }
  .dl-btns { width: 100%; }
  .dl-btns .btn { flex: 1; min-width: 0; }
  .result-list { gap: 10px; }
  .wtop .act { margin-left: 0; }
}

@media (max-width: 480px) {
  main { padding: 14px 12px 24px; }
  header { padding: 0 12px; }
  .panel { padding: 15px; border-radius: var(--r-md); }
  .download-bar { padding: 9px 12px calc(9px + env(safe-area-inset-bottom)); gap: 7px; }
  .dl-note { font-size: .75rem; }
  .dl-btns { flex-direction: column; }
  .dl-btns .btn { width: 100%; font-size: .875rem; padding: 12px 10px; }
  .stats { gap: 6px; }
  .st { flex: 1 1 calc(33.333% - 4px); min-width: 0; padding: 7px 6px; }
  .st b { font-size: 1rem; }
}

/* ============================================== 原则 14：无障碍三路降级 */
/* ① 减弱动态：滑动/弹簧 → 短促淡入，去掉位移与过冲 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .12s !important;
    animation-delay: 0ms !important;
  }
  .dropzone:hover, .dropzone.drag, .dropzone:active,
  .btn:active:not(:disabled), .fitem .x:active,
  .wtop .act a:active, .cfg-controls select:active { transform: none !important; }
  .rcard, .fitem, .toast, .download-bar, .merge-hint { animation: none !important; opacity: 1; }
  @keyframes riseIn { from { opacity: 0 } to { opacity: 1 } }
}

/* ② 降低透明度：毛玻璃变实心，保证文字对比度 */
@media (prefers-reduced-transparency: reduce) {
  header, .download-bar, .toast {
    background: var(--bg-elev);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  header, .download-bar { border-color: var(--sep); }
}

/* ③ 提高对比度：边框变明确，弱化灰字加深 */
@media (prefers-contrast: more) {
  :root { --label-2: rgba(60,60,67,.92); --label-3: rgba(60,60,67,.72); --sep: rgba(60,60,67,.4); }
  .panel, .rcard, .wcard, .fitem { border-color: var(--sep); }
  .dz-warn, .warn-line, .toast { border-width: 1.5px; }
  header, .download-bar { border-color: var(--sep); }
}
@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
  :root { --label-2: rgba(235,235,245,.92); --label-3: rgba(235,235,245,.7); --sep: rgba(180,180,190,.5); }
}

/* 打印：去掉材质与阴影，节省墨水 */
@media print {
  body { background: #fff; background-image: none; }
  header, .download-bar, .upload-panel { display: none !important; }
  .panel, .rcard, .wcard { box-shadow: none; border-color: #ddd; }
  main { grid-template-columns: 1fr; padding: 0; }
}

/* 版本号胶囊（内容来自 /api/health 的 version，可选注入） */
.ver-tag {
  font-size: .6875rem;
  font-weight: 600;
  color: var(--label-2);
  background: var(--fill-1);
  padding: 3px 10px;
  border-radius: var(--r-pill);
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
}

/* ══════════════════════════════════════════════════════ 人工核对面板
   2026-09-19：由 review.html 的页内 <style> 迁入，并把硬编码的深色值全部换成
   主题变量 ⇒ 内联进识别结果页后**跟随系统浅色/深色**（旧版在任何主题下都是深色块，
   嵌进浅色结果页会突兀）。

   同一份样式服务两种挂载方式：
     · 结果卡内联（容器带 .rv-inline）—— 面板随卡片流动，底部栏不吸底
     · review.html 独立页（无 .rv-inline）—— 底部栏吸底、面板居中、预留底部留白
   布局铁律不变：桌面「左图右选项」，≤860px 自动变「上图下选项」（grid 单列）。
   ─────────────────────────────────────────────────────────────────────── */
.rv-wrap { max-width: 1280px; margin: 0 auto; padding: 10px 0 120px; }

.rv-head { padding: 0 0 10px; border-bottom: 1px solid var(--sep); }
.rv-head h1 { font-size: 17px; margin: 0 0 7px; font-weight: 700; color: var(--label); }
.rv-meta { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center;
  font-size: 13px; color: var(--label-2); }
.rv-meta b { color: var(--label); font-weight: 600; }
.rv-prog { display: inline-flex; align-items: center; gap: 8px; }
.rv-bar { width: 150px; height: 7px; border-radius: 4px; background: var(--fill-2);
  overflow: hidden; display: block; }
.rv-bar > i { display: block; height: 100%; width: 0; background: var(--blue);
  transition: width var(--dur-2) var(--spring-soft); }
.rv-ident { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 9px; }
.rv-ident input { background: var(--bg-elev); border: 1px solid var(--sep); color: var(--label);
  border-radius: var(--r-xs); padding: 6px 9px; font-size: 13px; min-width: 130px;
  font-family: inherit; }
/* 姓名通常最长（"KHAN MOHAMMAD LABLU"），窄框会把值视觉截断 —— 单独放宽 */
.rv-ident label:first-child input { min-width: 210px; }
.rv-ident label { font-size: 12px; color: var(--label-2); display: flex; align-items: center; gap: 5px; }
.rv-hint { font-size: 12px; color: var(--label-3); margin: 8px 0 2px; line-height: 1.45; }

/* ── 逐日行：桌面 左图右数 ── */
.rv-row { display: grid; grid-template-columns: minmax(300px, 44%) 1fr; gap: 16px;
  margin: 10px 0; padding: 12px; border-radius: var(--r-md);
  border: 1px solid var(--sep); border-left: 5px solid var(--label-3);
  background: var(--bg-elev); box-shadow: var(--sh-1); }
.rv-row.s-agree    { border-left-color: var(--green);
                     background: color-mix(in srgb, var(--green) 8%, var(--bg-elev)); }
.rv-row.s-disagree { border-left-color: var(--orange);
                     background: color-mix(in srgb, var(--orange) 10%, var(--bg-elev)); }
.rv-row.s-unread   { border-left-color: var(--label-3); }
.rv-row.s-done     { border-left-color: var(--blue); }

.rv-img { position: relative; align-self: start; }
.rv-img img { width: 100%; height: auto; border-radius: var(--r-xs); background: #fff;
  cursor: zoom-in; display: block; }
.rv-img img.zoom { position: fixed; inset: 2vh 2vw; width: 96vw; height: 96vh;
  object-fit: contain; z-index: 999; background: rgba(8,12,22,.97);
  border-radius: var(--r-sm); box-shadow: var(--sh-3); }
/* 日期徽章（压在条带图左上角的「日期水印」）：
   底色 alpha .82 → .41（2026-09-20 用户要求「再透明 50%」）——
   条带最左端就是工卡印刷日期列，底色太重会把原日期整片盖掉。
   文字 alpha 1.00 → .50（2026-10-03 用户要求「日期水印 文字白色 做 50% 透明」）——
   保持纯白 #fff 只把不透明度减半，形状/底色/阴影一律不动，
   否则徽章会从「水印」变回「实心标签」、又盖住卡面。 */
.rv-day { position: absolute; top: 6px; left: 6px; background: rgba(8,14,26,.41);
  color: rgba(255,255,255,.5); font-size: 13px; font-weight: 700; padding: 2px 9px;
  border-radius: 6px;
  text-shadow: 0 1px 3px rgba(0,0,0,.9), 0 0 2px rgba(0,0,0,.7); }
/* 右下角标签（内容 = 工卡印刷体日期 printed_ocr / 无条带提示 rv.noStrip）：
   alpha .72 → .36，与 .rv-day 同一诉求（2026-09-20「右边那个日期标签也要增透明」）。
   它同样压在条带右端的工卡内容上，底色一重就遮字；靠 text-shadow 保可读。 */
.rv-anchor { position: absolute; bottom: 6px; right: 6px; background: rgba(8,14,26,.36);
  color: #cbd5e1; font-size: 11px; padding: 1px 7px; border-radius: 5px;
  text-shadow: 0 1px 3px rgba(0,0,0,.9), 0 0 2px rgba(0,0,0,.7); }
/* 推断行角标（2026-09-20 · 112.jpeg「条带退化为整卡图」事故）：
   内插行（日期栏没 OCR 自证、靠上下邻行行距定位）条带**照裁**，
   但必须让核对的人知道「这行的日期数字是推断的，多看两眼」。
   放**右上角**：避开左上「日期徽章」与右下「印刷体日期 / 无条带」标签，
   且条带右端多为空白列，遮挡最小。 */
.rv-anchor.infer { top: 6px; right: 6px; bottom: auto;
  background: rgba(122,72,6,.55); color: #fde68a; cursor: help; }

/* ── 模型读数卡 ── */
.rv-reads { display: flex; gap: 10px; flex-wrap: wrap; }
.rv-read { flex: 1 1 170px; text-align: left; cursor: pointer; border-radius: var(--r-sm);
  border: 2px solid var(--sep); background: var(--fill-1); color: var(--label);
  padding: 9px 11px; transition: border-color var(--dur-1), transform var(--dur-1); }
.rv-read:hover { transform: translateY(-1px); }
.rv-read.sel { border-color: var(--blue);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--blue) 28%, transparent); }
.rv-read.err { opacity: .75; border-style: dashed; }
.rv-read b { display: block; font-size: 12px; color: var(--label-2); font-weight: 600;
  margin-bottom: 3px; }
.rv-read i { display: block; font-size: 22px; font-weight: 800; font-style: normal;
  letter-spacing: .5px; }
.rv-read i.none { font-size: 15px; color: var(--orange); }
.rv-read small { display: block; font-size: 11px; color: var(--label-3); line-height: 1.35;
  margin-top: 4px; word-break: break-all; }
.rv-read small.raw { color: var(--label-2); }
.rv-read small.flag { color: var(--orange); font-weight: 600; }

.rv-ctl { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 10px; }
.rv-ctl button, .rv-ctl input { font-size: 13px; border-radius: var(--r-xs);
  border: 1px solid var(--sep); background: var(--bg-elev); color: var(--label);
  padding: 6px 10px; font-family: inherit; }
.rv-ctl button { cursor: pointer; }
.rv-ctl button.on { border-color: var(--blue);
  background: color-mix(in srgb, var(--blue) 14%, var(--bg-elev)); }
.rv-ctl input[type=number] { width: 88px; }
.rv-ctl input.memo { flex: 1 1 140px; min-width: 110px; }
.rv-tag { font-size: 11px; padding: 2px 8px; border-radius: var(--r-pill); font-weight: 700; }
.rv-tag.auto { background: color-mix(in srgb, var(--green) 22%, var(--bg-elev)); color: var(--green); }
.rv-tag.man  { background: color-mix(in srgb, var(--blue) 22%, var(--bg-elev));  color: var(--blue); }
.rv-tag.wait { background: color-mix(in srgb, var(--orange) 22%, var(--bg-elev)); color: var(--orange); }

/* ── 底部操作栏：独立页吸底；内联时随卡片流动 ── */
.rv-bottom { position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  background: var(--mat-bar); backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-top: 1px solid var(--sep);
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
  display: flex; gap: 12px; align-items: center; justify-content: space-between;
  flex-wrap: wrap; }
.rv-bottom .rv-txt { font-size: 13px; color: var(--label-2); }
.rv-btn { font-size: 15px; font-weight: 700; padding: 10px 20px; border-radius: var(--r-sm);
  border: 0; background: var(--blue); color: #fff; cursor: pointer; font-family: inherit; }
.rv-btn:disabled { background: var(--fill-2); color: var(--label-3); cursor: not-allowed; }
.rv-btn.sec { background: var(--fill-2); color: var(--label); }

.rv-done-box { margin: 14px 0; padding: 14px 16px; border-radius: var(--r-md);
  background: color-mix(in srgb, var(--green) 10%, var(--bg-elev));
  border: 1px solid color-mix(in srgb, var(--green) 40%, var(--sep)); }
.rv-done-box a { display: inline-block; margin: 8px 8px 0 0; padding: 9px 14px;
  border-radius: var(--r-xs); background: var(--green); color: #fff; text-decoration: none;
  font-weight: 700; font-size: 14px; }

/* 提示条：固定在最上层，一次只可能有一条 ⇒ 用全局单例，挂在 body 上 */
.rv-toast { position: fixed; left: 50%; top: calc(16px + env(safe-area-inset-top, 0px));
  transform: translateX(-50%); z-index: 999; background: var(--red); color: #fff;
  padding: 10px 18px; border-radius: var(--r-sm); font-size: 14px; max-width: 90vw;
  display: none; box-shadow: var(--sh-2); }
.rv-toast.ok { background: var(--green); }

/* ── 内联宿主：结果卡里承载核对面板的那块（app.js 挂载点）── */
.rvinline-host { padding: 12px 14px 14px; }
.rvinline-host.rv-hidden { display: none; }

/* 结果卡右上角的「收起/展开核对」开关（.wtop 是 flex，margin-left:auto 靠右） */
.rv-toggle { margin-left: auto; font-size: 12px; font-weight: 600;
  font-family: inherit; padding: 4px 11px; border-radius: var(--r-pill);
  border: 1px solid var(--sep); background: var(--bg-elev); color: var(--label-2);
  cursor: pointer; transition: color var(--dur-1), border-color var(--dur-1); }
.rv-toggle:hover { color: var(--label); border-color: var(--label-3); }

/* ── 内联模式修正：嵌在结果卡里 ⇒ 不居中、不吸底、不预留底部留白 ── */
.rv-inline .rv-wrap { max-width: none; margin: 0; padding: 0; }
.rv-inline .rv-head { position: static; }
.rv-inline .rv-bottom { position: static; left: auto; right: auto; bottom: auto;
  margin-top: 12px; padding: 10px 12px; border: 1px solid var(--sep);
  border-radius: var(--r-md); backdrop-filter: none; -webkit-backdrop-filter: none;
  background: var(--fill-1); }

@media (max-width: 860px) {
  /* 手机视图：上图下选项（用户 2026-09-19 明确要求） */
  .rv-row { grid-template-columns: 1fr; }
  .rv-head h1 { font-size: 16px; }
  .rv-bar { width: 96px; }
  .rv-bottom .rv-btn { flex: 1 1 auto; }
  .rv-ident input { min-width: 96px; }
}
