/* ============================================================================
 *  微信第三方平台管理控制台 · 样式表
 *
 *  这份样式原本内联在 index.html 的 <style> 里，现按「结构与样式分离」
 *  的要求原样搬出：规则内容、顺序、注释、缩进均未改动，只换了存放位置。
 *
 *  为什么独立成文件：
 *    * index.html 只剩页面结构，改样式不再与 HTML 挤在同一个 diff 里；
 *    * 浏览器可缓存 CSS，与 assets/app.js 走 Startup.cs 里那套缓存策略
 *      （入口页 no-store，其余 max-age=300 + must-revalidate）；
 *    * 编辑器对 .css 的高亮与检查比 HTML 里的 <style> 更完整。
 *
 *  ⚠ 新增样式请写在本文件末尾。不要往 index.html 里再加内联样式块 ——
 *    E2E 有断言守着这一点（样式被搬回去会让「样式独立」重新失效）。
 * ========================================================================== */

  :root{ --brand:#07c160; --ink:#1f2733; --ink2:#5a6675; --line:#e6eaf0; --bg:#f4f6fa; --card:#fff; --danger:#e5484d; --warn:#d97706; }
  *{box-sizing:border-box}
  body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.6 "Segoe UI","Microsoft YaHei",system-ui,sans-serif}
  .header{background:linear-gradient(120deg,#0f9d58 0%,#07c160 60%,#22c07a 100%);color:#fff;padding:20px 24px}
  .header h1{margin:0;font-size:20px}
  .header p{margin:4px 0 0;opacity:.9;font-size:13px}
  .wrap{max-width:1180px;margin:0 auto;padding:20px}
  .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:16px}
  .card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:16px 18px;box-shadow:0 1px 2px rgba(24,39,75,.05)}
  .card h2{margin:0 0 12px;font-size:15px;display:flex;align-items:center;gap:8px}
  .row{display:flex;justify-content:space-between;gap:12px;padding:6px 0;border-bottom:1px dashed var(--line);font-size:13px}
  .row:last-child{border-bottom:0}
  .row .k{color:var(--ink2);flex:0 0 auto}
  .row .v{text-align:right;word-break:break-all;font-weight:600}
  .ok{color:#0f9d58}.bad{color:var(--danger)}.warn{color:var(--warn)}
  .chip{display:inline-block;padding:1px 8px;border-radius:10px;font-size:12px;font-weight:600}
  .chip.ok{background:#e7f7ee;color:#0f7a45}
  .chip.bad{background:#fdeaea;color:#c02626}
  .chip.warn{background:#fdf3e2;color:#a35b06}
  button{font:inherit;padding:8px 16px;border-radius:8px;border:1px solid var(--line);background:#fff;cursor:pointer}
  button.primary{background:var(--brand);border-color:var(--brand);color:#fff}
  button:disabled{opacity:.5;cursor:not-allowed}
  .actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}

  /*
    这里原本是「发起授权」卡片按钮、绿色扫码按钮与绑定弹层的样式（含弹层入场动画），
    已随页面容器一起移除：控制台不再发起绑定，故不留这些「无宿主」的类 ——
    留着只会让下一个人以为还有入口。E2E 有断言守着「弹层样式不许回来」。
    注意 .urlbox 保留：它在「开放平台应填写的地址」卡片里仍在用。
  */
  .urlbox{background:#f7f9fc;border:1px solid var(--line);border-radius:8px;padding:8px 10px;font-family:Consolas,monospace;font-size:12px;word-break:break-all;margin:6px 0}
  .log{max-height:340px;overflow:auto;font-family:Consolas,monospace;font-size:12px}
  .log div{padding:3px 0;border-bottom:1px solid #f0f3f7}
  .log .t{color:var(--ink2);margin-right:6px}
  .empty{color:var(--ink2);text-align:center;padding:24px 0}

  /* ---------- 已授权服务号：标题栏与搜索框 ---------- */
  .auth-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;flex-wrap:wrap}
  .auth-head h2{margin:0}

  /* 搜索框：图标绝对定位在左侧内部，输入框留出左侧内边距避让 */
  .auth-search{position:relative;flex:0 1 180px;min-width:130px}
  .auth-search__icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);
    width:14px;height:14px;color:var(--ink3);pointer-events:none}
  .auth-search input{width:100%;box-sizing:border-box;font:inherit;font-size:13px;
    padding:6px 10px 6px 30px;border:1px solid var(--line);border-radius:6px;
    background:#fff;color:var(--ink);outline:none;transition:border-color .15s}
  .auth-search input:focus{border-color:var(--brand);box-shadow:0 0 0 3px rgba(59,110,246,.08)}
  .auth-search input::placeholder{color:var(--ink3)}

  /* ---------- 已授权服务号：列表容器 ---------- */
  /* 用列表而非表格：信息层级更清晰，且窄屏不会横向滚动 */
  /*
    只限制「容器」高度，不限制卡片本身 —— 每张卡片完整显示
    （图标 / 名称 / 令牌状态 / appId / 介绍 / 授权时间）。
    高度按 3 张完整卡片核算：单卡 = 上下 padding 24 + 内容（4 行文字约 70px，
    比图标 44px 高）= 94px；3 张 + 2 个间距 = 94×3 + 10×2 = 302px。
    取 320px 留余量，避免字体渲染差异导致第 3 张被裁掉半行。
  */
  .auth-list{display:flex;flex-direction:column;gap:10px;max-height:360px;overflow-y:auto;
    padding-right:4px}
  /* 滚动条美化（Chrome/Edge/Safari） */
  .auth-list::-webkit-scrollbar{width:8px}
  .auth-list::-webkit-scrollbar-thumb{background:#d3dbe6;border-radius:4px}
  .auth-list::-webkit-scrollbar-thumb:hover{background:#bcc7d6}
  .auth-list::-webkit-scrollbar-track{background:transparent}
  .auth-item{border:1px solid var(--line);border-radius:10px;background:#fff;transition:border-color .15s}
  .auth-item:hover{border-color:#c8d4e4}
  .auth-item.is-open{border-color:#9fc3ff;box-shadow:0 0 0 3px rgba(59,110,246,.08)}

  /* 折叠区：整块可点击 */
  /* 整块与图标都不可点击；只有右侧箭头是展开开关 */
  .auth-item__summary{display:flex;align-items:flex-start;gap:12px;padding:12px 14px}

  .auth-item__icon{flex:0 0 auto}
  .auth-item__icon img{width:44px;height:44px;border-radius:8px;object-fit:cover;background:#eef2f7;display:block}
  .auth-item__icon-fallback{display:flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:8px;background:#eef2f7;font-size:20px}

  .auth-item__info{flex:1 1 auto;min-width:0}
  .auth-item__title{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
  .auth-item__title strong{font-size:14px;color:var(--ink)}
  .auth-item__appid{font-family:Consolas,monospace;font-size:12px;color:var(--ink3);margin-top:2px;word-break:break-all}
  .auth-item__desc{font-size:12px;color:var(--ink2);margin-top:3px}
  .auth-item__time{font-size:12px;color:var(--ink3);margin-top:2px}
  .auth-error{margin-top:6px;font-size:12px;color:var(--danger);background:#fdeaea;border-radius:6px;padding:5px 8px}

  /* 展开箭头：唯一的操作开关。
     默认就是「右侧 > 图标」，点击后旋转 90° 变蓝，并展开下方操作按钮。 */
  .auth-item__arrow{flex:0 0 auto;align-self:center;cursor:pointer;
    width:26px;height:26px;display:flex;align-items:center;justify-content:center;
    color:var(--ink3);font-size:20px;line-height:1;border-radius:6px;
    transition:transform .18s, color .15s, background .15s}
  .auth-item__arrow:hover{background:#eef2f7;color:var(--brand)}
  .auth-item.is-open .auth-item__arrow{transform:rotate(90deg);color:var(--brand)}

  /* 操作区：默认隐藏（max-height:0，不占高度），展开时显示。
     用 max-height 过渡而不是 display，是为了让展开有高度动画、
     同时保证收起时卡片高度精确回到原值 —— 容器高度因此不会动态变化。 */
  .auth-item__actions{display:flex;gap:8px;flex-wrap:wrap;
    max-height:0;overflow:hidden;padding:0 14px;
    transition:max-height .18s ease, padding .18s ease}
  .auth-item.is-open .auth-item__actions{max-height:70px;padding:0 14px 12px 70px}

  .auth-item__actions button{font:inherit;font-size:13px;padding:6px 14px;border-radius:6px;border:1px solid var(--line);background:#fff;cursor:pointer}
  .auth-item__actions button:hover:not(:disabled){border-color:var(--brand);color:var(--brand)}
  .auth-item__actions button:disabled{opacity:.5;cursor:not-allowed}
  .auth-item__actions button.danger:hover:not(:disabled){border-color:var(--danger);color:var(--danger)}
  /*
    这里原本有一条 #qr（居中放二维码）—— 扫码入口移除后，页面上已没有任何宿主元素，
    故一并删除。二维码自始至终由微信官方页面渲染，本控制台从未自己画过。
  */

  /* ---------- API 密钥（供校易服务端调用） ---------- */
  .apikey-list{display:flex;flex-direction:column;gap:10px;max-height:360px;overflow-y:auto;padding-right:4px}
  .apikey-list::-webkit-scrollbar{width:8px}
  .apikey-list::-webkit-scrollbar-thumb{background:#d3dbe6;border-radius:4px}
  .apikey-list::-webkit-scrollbar-thumb:hover{background:#bcc7d6}
  .apikey-list::-webkit-scrollbar-track{background:transparent}

  .apikey-item{border:1px solid var(--line);border-radius:10px;background:#fff;transition:border-color .15s}
  .apikey-item:hover{border-color:#c8d4e4}
  .apikey-item.is-open{border-color:#9fc3ff;box-shadow:0 0 0 3px rgba(59,110,246,.08)}
  /* 停用的密钥整块变淡：一眼看出哪把不再生效，无需逐个读文字 */
  .apikey-item.is-off{background:#fafbfd;opacity:.72}

  /* 折叠区：左侧信息 + 右侧箭头（与 .auth-item__summary 同一套布局思路） */
  .apikey-item__main{display:flex;align-items:flex-start;gap:12px;padding:11px 13px}
  .apikey-item__info{flex:1 1 auto;min-width:0}

  .apikey-item__title{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
  .apikey-item__title strong{font-size:14px;color:var(--ink);word-break:break-all}
  .apikey-item__meta{font-family:Consolas,monospace;font-size:12px;color:var(--ink2);margin-top:3px;word-break:break-all}
  .apikey-item__time{font-size:12px;color:var(--ink3);margin-top:2px}

  /* 展开箭头：唯一的操作开关，与授权列表相同的视觉与旋转行为 */
  .apikey-item__arrow{flex:0 0 auto;align-self:center;cursor:pointer;
    width:26px;height:26px;display:flex;align-items:center;justify-content:center;
    color:var(--ink3);font-size:20px;line-height:1;border-radius:6px;
    transition:transform .18s, color .15s, background .15s}
  .apikey-item__arrow:hover{background:#eef2f7;color:var(--brand)}
  .apikey-item.is-open .apikey-item__arrow{transform:rotate(90deg);color:var(--brand)}

  /* 操作区：默认 max-height:0 完全隐藏。
     用 max-height 过渡而非 display，是为了展开有动画、收起能精确回到原高度。 */
  .apikey-item__actions{display:flex;gap:8px;flex-wrap:wrap;
    max-height:0;overflow:hidden;padding:0 13px;
    transition:max-height .18s ease, padding .18s ease}
  .apikey-item.is-open .apikey-item__actions{max-height:60px;padding:0 13px 11px 13px}
  .apikey-item__actions button{font:inherit;font-size:13px;padding:5px 12px;border-radius:6px;
    border:1px solid var(--line);background:#fff;cursor:pointer}
  .apikey-item__actions button:hover:not(:disabled){border-color:var(--brand);color:var(--brand)}
  .apikey-item__actions button:disabled{opacity:.5;cursor:not-allowed}
  .apikey-item__actions button.danger:hover:not(:disabled){border-color:var(--danger);color:var(--danger)}

  /* 新建表单：默认隐藏，点「新建密钥」才展开 */
  .apikey-new{margin-top:12px;padding:12px;border:1px solid var(--line);
    border-radius:10px;background:#f7f9fc;display:none}
  .apikey-new.is-open{display:block}
  .apikey-new label{display:block;font-size:12px;color:var(--ink2);margin-bottom:5px}
  .apikey-new input{width:100%;box-sizing:border-box;font:inherit;font-size:13px;
    padding:8px 10px;border:1px solid var(--line);border-radius:8px;
    background:#fff;color:var(--ink);outline:none;transition:border-color .15s}
  .apikey-new input:focus{border-color:var(--brand);box-shadow:0 0 0 3px rgba(7,193,96,.12)}

  /* ---------- 通用遮罩弹层（当前只有「修改密码」在用） ---------- */
  /*
    遮罩 fixed 铺满视口并压住页面；面板居中、宽度随视口自适应但不超 420px。
    字段一律「标签在上、输入框在下」各占一行：密码框看不见内容，
    横排容易填错行，竖排一眼就能核对。
  */
  .modal{position:fixed;inset:0;z-index:1100;display:flex;align-items:center;justify-content:center;padding:20px}
  .modal__mask{position:absolute;inset:0;background:rgba(15,23,42,.45);backdrop-filter:blur(2px)}

  .modal__panel{position:relative;width:100%;max-width:420px;background:#fff;
    border-radius:12px;overflow:hidden;box-shadow:0 20px 60px rgba(15,23,42,.28)}

  /* 绿色标题栏（微信生态视觉习惯） */
  .modal__header{background:var(--brand);padding:16px 52px 16px 20px}
  .modal__header h3{margin:0;color:#fff;font-size:16px;font-weight:600;line-height:1.5}

  /* 右上角关闭按钮：压在标题栏上 */
  .modal__close{position:absolute;top:10px;right:10px;width:28px;height:28px;
    display:flex;align-items:center;justify-content:center;
    font-size:20px;line-height:1;color:#fff;background:rgba(255,255,255,.22);
    border:0;border-radius:50%;cursor:pointer;padding:0;transition:background .15s}
  .modal__close:hover{background:rgba(255,255,255,.38)}

  .modal__body{padding:18px 20px 20px}
  .modal__body label{display:block;font-size:12px;color:var(--ink2);margin:12px 0 5px}
  .modal__body input{width:100%;box-sizing:border-box;font:inherit;font-size:13px;
    padding:8px 10px;border:1px solid var(--line);border-radius:8px;
    background:#fff;color:var(--ink);outline:none;transition:border-color .15s}
  .modal__body input:focus{border-color:var(--brand);box-shadow:0 0 0 3px rgba(7,193,96,.12)}

  /* 主操作靠右、取消在左：弹层里的通用习惯，避免误点主按钮 */
  .modal__actions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}
  /*
    ⚠ 这条规则**只许调字号与内边距**，不要再写 background / border / color。

    本文件靠「同权重看源码顺序」决胜：`button.primary`（第 37 行）在这里前面，
    而 `.modal__actions button` 与它权重相同（都是 0,1,1）。一旦在这里写
    `background:#fff`，就会把主按钮的绿底盖成白底，而 `button.primary` 的
    `color:#fff` 仍然生效 —— 结果是「保存新密码」变成白底白字，看起来是个空按钮。
    这个坑已经踩过一次，E2E 里有断言守着（见「改密码入口在页头…」那条用例）。
  */
  .modal__actions button{font-size:13px;padding:7px 16px}
  /* 次要按钮（取消）悬停变绿；主按钮只做轻微压暗，避免把文字颜色也改掉 */
  .modal__actions button:not(.primary):hover:not(:disabled){border-color:var(--brand);color:var(--brand)}
  .modal__actions button.primary:hover:not(:disabled){filter:brightness(.94)}

  /* 弹层内的提示行：另起一行放在按钮下方。
     为什么不复用页面顶部的提示条 —— 遮罩铺满视口并盖住整页，写在那里看不见。
     三档配色与顶部提示条一致，避免同一类消息在两处长得不一样。 */
  .modal__msg{margin-top:14px;font-size:13px;border-radius:6px;padding:8px 10px;display:none}
  .modal__msg.is-ok{color:#0f7a45;background:#e7f7ee}
  .modal__msg.is-warn{color:#8a5b00;background:#fff8e6}
  .modal__msg.is-bad{color:#a12626;background:#fdeaea}

  /* 明文只显示这一次，因此用醒目告警样式圈起来，降低「以为还能再看」的概率 */
  .apikey-secret{margin-top:12px;border:1px solid #f0d9a0;background:#fff8e6;
    border-radius:10px;padding:12px}
  .apikey-secret__title{font-size:13px;font-weight:600;color:#8a5b00;margin-bottom:6px}
  .apikey-secret__key{font-family:Consolas,monospace;font-size:12px;word-break:break-all;
    background:#fff;border:1px solid #f0d9a0;border-radius:8px;padding:8px 10px;color:var(--ink)}
  .apikey-secret__actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}

  /* ---------- 顶部提示条：全站唯一的操作反馈出口 ---------- */
  /*
    替代浏览器默认弹窗（alert）。三类语义各一套配色：
      is-ok   成功（如「已刷新」「已设为平台代发」）
      is-warn 提醒（缺省配色，兼容不传 kind 的调用）
      is-bad  失败
    基类保持原样，未挂修饰类时与改造前完全一致，老调用不会变样。
    长文案（如「配置未就绪：…」的原因）按行折行，不截断、不设高度上限。
  */
  .banner{background:#fff8e6;border:1px solid #f0d9a0;color:#8a5b00;border-radius:8px;padding:10px 14px;margin-bottom:14px;font-size:13px}
  .banner.is-ok{background:#e7f7ee;border-color:#a8dcbf;color:#0f7a45}
  .banner.is-warn{background:#fff8e6;border-color:#f0d9a0;color:#8a5b00}
  .banner.is-bad{background:#fdeaea;border-color:#f0b0b0;color:#a12626}
  .muted{color:var(--ink2);font-size:12px}

  /* ---------- 登录遮罩 ---------- */
  /* 仅作界面遮挡：真正的权限控制在服务端，绕过它也无法访问任何接口 */
  .login-overlay{position:fixed;inset:0;z-index:1200;display:flex;
    align-items:center;justify-content:center;padding:20px;
    background:rgba(15,23,42,.55);backdrop-filter:blur(3px)}

  .login-box{width:100%;max-width:360px;background:#fff;border-radius:12px;
    padding:28px 26px 24px;box-shadow:0 20px 60px rgba(15,23,42,.3)}

  .login-box__title{margin:0 0 4px;font-size:17px;font-weight:600;color:var(--ink)}
  .login-box__sub{margin:0 0 18px;font-size:12px;color:var(--ink2)}

  .login-box label{display:block;font-size:13px;color:var(--ink2);margin:12px 0 5px}
  .login-box input{width:100%;box-sizing:border-box;font:inherit;font-size:14px;
    padding:9px 12px;border:1px solid var(--line);border-radius:8px;
    background:#fff;color:var(--ink);outline:none;transition:border-color .15s}
  .login-box input:focus{border-color:var(--brand);box-shadow:0 0 0 3px rgba(7,193,96,.12)}

  /* 登录框里的消息条：默认是失败（红）。改密码成功后会强制回到这个界面，
     那时提示必须是绿的 —— 否则「密码已修改」看起来像报错。 */
  .login-box__error{margin-top:12px;font-size:13px;color:var(--danger);
    background:#fdeaea;border-radius:6px;padding:7px 10px;display:none}
  .login-box__error.is-ok{color:#0f7a45;background:#e7f7ee}

  .login-box__submit{width:100%;margin-top:18px;font:inherit;font-size:15px;font-weight:600;
    padding:11px;border-radius:8px;border:1px solid var(--brand);
    background:var(--brand);color:#fff;cursor:pointer;transition:filter .15s}
  .login-box__submit:hover:not(:disabled){filter:brightness(.94)}
  .login-box__submit:disabled{opacity:.6;cursor:not-allowed}

  .header__inner{max-width:1180px;margin:0 auto;display:flex;
    align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
  /* 页头右上角的账号级按钮（修改密码 / 退出登录）：同款浅色描边，间距 8px */
  .header__actions{display:flex;gap:8px;flex-wrap:wrap}
  .header__btn{font:inherit;font-size:13px;padding:6px 14px;border-radius:6px;
    border:1px solid rgba(255,255,255,.5);background:rgba(255,255,255,.15);
    color:#fff;cursor:pointer}
  .header__btn:hover{background:rgba(255,255,255,.28)}

