
/* 系统字体的本地声明（与控制台、公开站同款）：local() 只命中本机已装的字体，
   让可变字重在各平台都生效。不加载任何网络字体——苹果的字体就是操作系统，
   国内网络也不必再等 Google。 */
@font-face{font-family:'PingFang SC';src:local('PingFang SC');font-weight:100 900;font-display:swap}
@font-face{font-family:'HarmonyOS Sans SC';src:local('HarmonyOS Sans SC');font-weight:100 900;font-display:swap}
@font-face{font-family:'MiSans';src:local('MiSans');font-weight:100 900;font-display:swap}
@font-face{font-family:'Microsoft YaHei';src:local('Microsoft YaHei');font-weight:100 900;font-display:swap}
/* ============================================================
   AIProxy 交换台 · 设计系统：Apple Human Interface Guidelines（2025，Liquid Glass 一代）
   与控制台、公开站同一套语言，参照 developer.apple.com/design：

   · 颜色：单色墨承担品牌——浅色按钮黑 #1d1d1f，深色云白 #f5f5f7；彩色只留给
     语义（成功 / 警示 / 错误）和数据。系统色取 HIG 2025 那张表：浅色用
     「提高对比度」那一列，因为这些令牌也当文字色用，普通那列的绿在白底上
     只有 2.2:1；深色用默认列。点、开关这类不承载文字的地方用 --*-fill 鲜艳版。
   · 层级：分组背景 #f5f5f7 上放白卡片；深色是纯黑底 + #1c1c1e 卡片（iOS 深色
     分组表的做法）。文字三级：label / secondaryLabel / tertiaryLabel。
   · 材质：只有导航层（侧栏、顶栏、页签条、弹层、提示）用玻璃，内容层不用——
     HIG：「Don't use Liquid Glass in the content layer.」
   · 形状：控件一律胶囊，卡片 18px，弹层 24px 以上，内外圆角同心。
   · 字体：系统字体（SF Pro / 苹方），字重只用 400/500/600/700，不用细体。
   · 动效：弹簧曲线；尊重「减弱动态效果」「降低透明度」「增强对比度」三个系统设置。
   · 焦点：键盘焦点一律一圈蓝环（macOS 的 keyboardFocusIndicator），鼠标点击不出环。

   令牌名一个都没改（JS 里几十处内联样式引用它们），改的是取值。旧版「机箱恒为
   墨色」那条不再成立：侧栏、提示条跟着主题走，和 macOS 的侧栏一样。
   派生色（--x-aNN / --x-NN-on-y）按「名字即算式」整体重算。
   ============================================================ */
:root{
  color-scheme:dark;
  /* 台面：纯黑底 + 抬升一级的卡片 */
  --bg:#000000; --surface:#1c1c1e; --surface-2:#232325; --surface-3:#2c2c2e;
  --line:#38383a; --line-soft:#2a2a2c;
  --text:#f5f5f7; --dim:#a1a1a6; --faint:#7c7c80;
  --brand:#f5f5f7; --brand-2:#f5f5f7; --brand-dim:rgba(245,245,247,.1);
  --ok:#30d158; --warn:#ff9230; --bad:#ff4245;
  /* 状态网格专用的两格颜色。--ok-quiet 是「有记录、这一格没人调用」——灰会被读成
     「那时候挂了」，而没人调用不是一句关于可用性的话；--or-coral 是 80–95% 那一档，
     它得比 --warn 更重、又还不到 --bad，四档才排得开。 */
  --ok-quiet:rgba(48,209,88,.34); --or-coral:#ff6b3d;
  --ac:#ff9a6b;  /* anthropic */ --oa:#00dac3; /* openai */ --gm:#0091ff; /* gemini */ --im:#db34f2; /* image */ --ts:#ffd600; /* typesafe */
  /* 座位色环：只表示「车上的哪一个人」，不表示「多严重」。绿 / 黄 / 红整块板都
     归油表用（还剩多少油），所以这一环刻意避开它们，取 HIG 的冷色到紫一段：
     靛 / 青 / 紫 / 蓝绿 / 蓝 / 淡靛。 */
  --s1:#6d7cff; --s2:#3cd3fe; --s3:#db34f2; --s4:#00d2e0; --s5:#0091ff; --s6:#a7aaff;
  /* 充值分析的两条序列：老客（靛）/ 本期新客（青），两套主题都过了色盲分辨检查
     （dataviz validate_palette，深色对 #1c1c1e）。--ta-h1..h5 是首充批次的单色阶，
     只用来画「有几成人还在充」，不和别的语义共用。 */
  --ta-old:#6d7cff; --ta-new:#1aa3b8; --ta-prev:#636366; --ta-new-bg:rgba(26,163,184,.18); --ta-new-ink:#4fd0e3;
  /* 续费日历的收入条：已入账实心、预计续费斜纹、全部续上描边，同一个靛色（--ta-old）。 */
  --rc-paid:#6d7cff; --rc-exp:rgba(109,124,255,.34); --rc-weekend:rgba(255,255,255,.015);
  --ta-gray:#48484a; --ta-mid:#8e8e93; --ta-h1:#23263a; --ta-h2:#2f3660; --ta-h3:#3f4a92; --ta-h4:#5363d1; --ta-h5:#6d7cff;
  --sp-lane:rgba(245,245,247,.08); --sp-rest:#48484a; --sp-rest-line:rgba(124,124,128,.55);
  --btn-ink:#1d1d1f;               /* 实心按钮上的文字：墨底配白字、白底配墨字 */
  --input-bg:#232325; --input-line:#48484a;
  --card-grad:#1c1c1e;
  --card-shadow:none;              /* 深色靠明度分层，卡片不投影 */
  --glass:rgba(22,22,23,.72);      /* 导航层玻璃：顶栏、页签条 */
  --glass-thick:rgba(44,44,46,.9); /* 厚玻璃：弹层、菜单、确认框、⌘K——上面字多，要更不透明 */
  --scrim:rgba(0,0,0,.5);          /* 抽屉/弹层遮罩 */
  --shadow-1:0 24px 64px rgba(0,0,0,.6),0 0 0 .5px rgba(255,255,255,.1);
  --shadow-2:0 32px 90px rgba(0,0,0,.66),0 0 0 .5px rgba(255,255,255,.1);
  --shadow-3:0 12px 32px rgba(0,0,0,.5),0 0 0 .5px rgba(255,255,255,.1);
  /* 信号灯光晕归零：HIG 里状态点就是一个实心点，不发光。旧组件里写死的
     box-shadow 光晕因此自动塌成扁平。 */
  --halo-blur:0px; --halo-spread:0px; --halo-alpha:0%;
  /* 分段控件的选中块：深色是一块抬起的灰（iOS 深色 segmented 的 #636366） */
  --ctl-on-bg:#636366; --ctl-on-ring:0 1px 3px rgba(0,0,0,.32);
  /* 侧栏：浮在台面上的一块玻璃（macOS 26 的侧栏），跟主题走 */
  --rail-bg:rgba(36,36,38,.72); --rail-line:rgba(255,255,255,.09); --rail-hover:rgba(255,255,255,.06);
  --rail-text:#f5f5f7; --rail-dim:#a1a1a6; --rail-faint:#7c7c80;
  --rail-on-bg:rgba(255,255,255,.12); --rail-on-text:#f5f5f7;
  --toast-bg:rgba(44,44,46,.86); --toast-text:#f5f5f7; --toast-line:rgba(255,255,255,.1);
  --sans:-apple-system,BlinkMacSystemFont,'SF Pro Text','Helvetica Neue','PingFang SC','Hiragino Sans GB','HarmonyOS Sans SC','MiSans','Segoe UI','Microsoft YaHei',system-ui,sans-serif;
  --disp:-apple-system,BlinkMacSystemFont,'SF Pro Display','Helvetica Neue','PingFang SC','Hiragino Sans GB','HarmonyOS Sans SC','MiSans','Segoe UI','Microsoft YaHei',system-ui,sans-serif;
  --mono:ui-monospace,'SF Mono',SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono',monospace;
  --rail:212px;
  /* HIG 新增：链接蓝、焦点环、系统填充四级、鲜艳语义色、玻璃边光、圆角与曲线 */
  --link:#2997ff; --focus:#0a84ff; --focus-ring:0 0 0 3px rgba(10,132,255,.5);
  --fill-1:rgba(120,120,128,.36); --fill-2:rgba(120,120,128,.32); --fill-3:rgba(118,118,128,.24); --fill-4:rgba(116,116,128,.18);
  --ok-fill:#30d158; --warn-fill:#ff9230; --bad-fill:#ff4245;
  --btn-hover:#ffffff; --btn-press:#e3e3e8;   /* 实心按钮的悬停 / 按下：白底再亮一点、按下压暗一点 */
  --brand-a12:rgba(245,245,247,.12); --bad-a6:rgba(255,66,69,.06);  /* 页面里有引用、以前没定义过的两个派生色 */
  --sheen:inset 0 1px 0 rgba(255,255,255,.07); --glass-edge:rgba(255,255,255,.1);
  --r-xs:6px; --r-sm:8px; --r-md:10px; --r-lg:14px; --r-xl:18px; --r-2xl:26px; --r-pill:980px;
  --ease:cubic-bezier(.25,.1,.25,1); --ease-out:cubic-bezier(.22,1,.36,1); --spring:cubic-bezier(.32,.72,0,1);
  --blur:saturate(180%) blur(20px);
  /* 派生色：命名即算式，两套主题各算一份。--brand-a40 = 品牌色 40% 不透明，
     --brand-55-on-line = 品牌色 55% 掺线色。只有颜色要到运行时才知道的
     （--pv/--pc/--led/--vol）还留着 color-mix，那几处前面都补了静态兜底声明。 */
  --ac-a12:rgba(255,154,107,.12); --ac-a14:rgba(255,154,107,.14); --bad-42-on-line:#8c3c3f;
  --bad-45-on-line:#923c3f; --bad-60-on-surface:#a43335; --bad-a10:rgba(255,66,69,.1);
  --bad-a11:rgba(255,66,69,.11); --bad-a12:rgba(255,66,69,.12); --bad-a16:rgba(255,66,69,.16);
  --bad-a18:rgba(255,66,69,.18); --bad-a25:rgba(255,66,69,.25); --bad-a26:rgba(255,66,69,.26);
  --bad-a32:rgba(255,66,69,.32); --bad-a40:rgba(255,66,69,.4); --bad-a42:rgba(255,66,69,.42);
  --bad-a45:rgba(255,66,69,.45); --bad-a5:rgba(255,66,69,.05); --bad-a55:rgba(255,66,69,.55);
  --bad-a60:rgba(255,66,69,.6); --bad-a7:rgba(255,66,69,.07); --bad-a9:rgba(255,66,69,.09);
  --bad-ahalo:rgba(255,66,69,0); --black-a22:rgba(0,0,0,.22); --brand-15-on-surface-3:#4a4a4c;
  --brand-16-on-surface:#3f3f41; --brand-2-28-on-white:#fcfcfd; --brand-2-72-on-dim:#dddde0;
  --brand-2-ahalo:rgba(245,245,247,0); --brand-30-on-surface-3:#68686a; --brand-32-on-line:#747476;
  --brand-34-on-line:#78787a; --brand-38-on-line:#808082; --brand-40-on-input-line:#8d8d8f;
  --brand-40-on-line:#848486; --brand-40-on-line-soft:#7b7b7d; --brand-42-on-line:#878789;
  --brand-45-on-line:#8d8d8f; --brand-50-on-line:#969698; --brand-55-on-line:#a0a0a2;
  --brand-55-on-surface-3:#9b9b9d; --brand-65-on-line:#b3b3b5; --brand-70-on-line:#bcbcbe;
  --brand-78-on-dim:#e3e3e5; --brand-8-on-surface-2:#343436; --brand-80-on-surface-3:#cdcdcf;
  --brand-82-on-surface-3:#d1d1d3; --brand-9-on-surface:#303032; --brand-a13:rgba(245,245,247,.13);
  --brand-a14:rgba(245,245,247,.14); --brand-a15:rgba(245,245,247,.15); --brand-a16:rgba(245,245,247,.16);
  --brand-a18:rgba(245,245,247,.18); --brand-a20:rgba(245,245,247,.2); --brand-a26:rgba(245,245,247,.26);
  --brand-a30:rgba(245,245,247,.3); --brand-a35:rgba(245,245,247,.35); --brand-a40:rgba(245,245,247,.4);
  --brand-a45:rgba(245,245,247,.45); --brand-a46:rgba(245,245,247,.46); --brand-a5:rgba(245,245,247,.05);
  --brand-a50:rgba(245,245,247,.5); --brand-a55:rgba(245,245,247,.55); --brand-a56:rgba(245,245,247,.56);
  --brand-a60:rgba(245,245,247,.6); --brand-a70:rgba(245,245,247,.7); --brand-a72:rgba(245,245,247,.72);
  --brand-a78:rgba(245,245,247,.78); --brand-a8:rgba(245,245,247,.08); --brand-a85:rgba(245,245,247,.85);
  --brand-ahalo:rgba(245,245,247,0); --brand-ahalohalf:rgba(245,245,247,0); --btn-ink-a35:rgba(29,29,31,.35);
  --faint-a12:rgba(124,124,128,.12); --faint-a14:rgba(124,124,128,.14); --faint-a22:rgba(124,124,128,.22);
  --faint-a28:rgba(124,124,128,.28); --faint-a30:rgba(124,124,128,.3); --faint-a34:rgba(124,124,128,.34);
  --faint-a38:rgba(124,124,128,.38); --faint-a42:rgba(124,124,128,.42); --faint-a5:rgba(124,124,128,.05);
  --faint-a52:rgba(124,124,128,.52); --faint-a55:rgba(124,124,128,.55); --faint-a60:rgba(124,124,128,.6);
  --faint-a90:rgba(124,124,128,.9); --faint-a95:rgba(124,124,128,.95); --gm-a12:rgba(0,145,255,.12);
  --gm-a14:rgba(0,145,255,.14); --im-a12:rgba(219,52,242,.12); --im-a14:rgba(219,52,242,.14);
  --ts-a12:rgba(255,214,0,.12); --ts-a14:rgba(255,214,0,.14); --line-a70:rgba(56,56,58,.7);
  --oa-a12:rgba(0,218,195,.12); --oa-a14:rgba(0,218,195,.14); --ok-40-on-line:#357546;
  --ok-55-on-surface:#27803e; --ok-8-on-surface:#1e2a23; --ok-a12:rgba(48,209,88,.12);
  --ok-a13:rgba(48,209,88,.13); --ok-a26:rgba(48,209,88,.26); --ok-a32:rgba(48,209,88,.32);
  --ok-a40:rgba(48,209,88,.4); --ok-a45:rgba(48,209,88,.45); --ok-a52:rgba(48,209,88,.52);
  --ok-a60:rgba(48,209,88,.6); --ok-ahalo:rgba(48,209,88,0); --rail-on-text-a60:rgba(245,245,247,.6);
  --rail-on-text-a70:rgba(245,245,247,.7); --surface-3-a45:rgba(44,44,46,.45); --text-45-on-surface-3:#868688;
  --text-a16:rgba(245,245,247,.16); --text-a17:rgba(245,245,247,.17); --text-a46:rgba(245,245,247,.46);
  --text-a60:rgba(245,245,247,.6); --text-a8:rgba(245,245,247,.08); --warn-36-on-line:#805836;
  --warn-50-on-line:#9c6535; --warn-55-on-line:#a56a34; --warn-a12:rgba(255,146,48,.12);
  --warn-a13:rgba(255,146,48,.13); --warn-a16:rgba(255,146,48,.16); --warn-a20:rgba(255,146,48,.2);
  --warn-a22:rgba(255,146,48,.22); --warn-a26:rgba(255,146,48,.26); --warn-a30:rgba(255,146,48,.3);
  --warn-a40:rgba(255,146,48,.4); --warn-a45:rgba(255,146,48,.45); --warn-a70:rgba(255,146,48,.7);
  --warn-ahalo:rgba(255,146,48,0); --warn-a5:rgba(255,146,48,.05); --warn-a9:rgba(255,146,48,.09);
}
/* 浅色：分组背景 + 白卡片，墨色字，按钮黑 */
[data-theme=light]{
  color-scheme:light;
  --bg:#f5f5f7; --surface:#ffffff; --surface-2:#f9f9fb; --surface-3:#f2f2f7;
  --line:#e3e3e8; --line-soft:#ededf0;
  --text:#1d1d1f; --dim:#6e6e73; --faint:#86868b;
  --brand:#1d1d1f; --brand-2:#1d1d1f; --brand-dim:rgba(0,0,0,.05);
  --ok:#008932; --warn:#c55300; --bad:#e9152d;
  --ok-quiet:rgba(52,199,89,.34); --or-coral:#e5501e;
  --ac:#c4561f; --oa:#008575; --gm:#1e6ef4; --im:#b02fc2; --ts:#a16a00;
  --s1:#564ade; --s2:#007eae; --s3:#b02fc2; --s4:#008198; --s5:#1e6ef4; --s6:#8a34d6;
  --ta-old:#3f5bd8; --ta-new:#0f9bb4; --ta-prev:#aeaeb2; --ta-new-bg:rgba(15,155,180,.12); --ta-new-ink:#00788c;
  --rc-paid:#3f5bd8; --rc-exp:rgba(63,91,216,.26); --rc-weekend:rgba(0,0,0,.012);
  --ta-gray:#d1d1d6; --ta-mid:#8e8e93; --ta-h1:#eef0fc; --ta-h2:#d3d9f7; --ta-h3:#a8b4ef; --ta-h4:#7187e3; --ta-h5:#3f5bd8;
  --sp-lane:rgba(0,0,0,.05); --sp-rest:#c7c7cc; --sp-rest-line:rgba(134,134,139,.6);
  --btn-ink:#ffffff;
  --input-bg:#ffffff; --input-line:#d2d2d7;
  --card-grad:#ffffff;
  --card-shadow:0 1px 2px rgba(0,0,0,.04),0 2px 8px rgba(0,0,0,.03);
  --glass:rgba(250,250,252,.78);
  --glass-thick:rgba(255,255,255,.92);
  --scrim:rgba(0,0,0,.24);
  --shadow-1:0 20px 56px rgba(0,0,0,.16),0 2px 8px rgba(0,0,0,.05),0 0 0 .5px rgba(0,0,0,.07);
  --shadow-2:0 30px 90px rgba(0,0,0,.2),0 4px 14px rgba(0,0,0,.06),0 0 0 .5px rgba(0,0,0,.07);
  --shadow-3:0 10px 30px rgba(0,0,0,.13),0 1px 4px rgba(0,0,0,.05),0 0 0 .5px rgba(0,0,0,.07);
  --halo-blur:0px; --halo-spread:0px; --halo-alpha:0%;
  /* 分段控件的选中块：浅色是一块白、带一层很浅的投影（iOS segmented） */
  --ctl-on-bg:#ffffff;
  --ctl-on-ring:0 3px 8px rgba(0,0,0,.12),0 1px 1px rgba(0,0,0,.04),0 0 0 .5px rgba(0,0,0,.04);
  --rail-bg:rgba(255,255,255,.66); --rail-line:rgba(0,0,0,.07); --rail-hover:rgba(0,0,0,.04);
  --rail-text:#1d1d1f; --rail-dim:#6e6e73; --rail-faint:#86868b;
  --rail-on-bg:rgba(0,0,0,.07); --rail-on-text:#1d1d1f;
  --toast-bg:rgba(255,255,255,.86); --toast-text:#1d1d1f; --toast-line:rgba(0,0,0,.08);
  --link:#0066cc; --focus:#0071e3; --focus-ring:0 0 0 3px rgba(0,113,227,.3);
  --fill-1:rgba(120,120,128,.2); --fill-2:rgba(120,120,128,.16); --fill-3:rgba(118,118,128,.12); --fill-4:rgba(116,116,128,.08);
  --ok-fill:#34c759; --warn-fill:#ff8d28; --bad-fill:#ff383c;
  --btn-hover:#333336; --btn-press:#000000;
  --brand-a12:rgba(29,29,31,.12); --bad-a6:rgba(233,21,45,.06);
  --sheen:inset 0 1px 0 rgba(255,255,255,.85); --glass-edge:rgba(255,255,255,.7);
  --ac-a12:rgba(196,86,31,.12); --ac-a14:rgba(196,86,31,.14); --bad-42-on-line:#e68c99;
  --bad-45-on-line:#e68694; --bad-60-on-surface:#f27381; --bad-a10:rgba(233,21,45,.1);
  --bad-a11:rgba(233,21,45,.11); --bad-a12:rgba(233,21,45,.12); --bad-a16:rgba(233,21,45,.16);
  --bad-a18:rgba(233,21,45,.18); --bad-a25:rgba(233,21,45,.25); --bad-a26:rgba(233,21,45,.26);
  --bad-a32:rgba(233,21,45,.32); --bad-a40:rgba(233,21,45,.4); --bad-a42:rgba(233,21,45,.42);
  --bad-a45:rgba(233,21,45,.45); --bad-a5:rgba(233,21,45,.05); --bad-a55:rgba(233,21,45,.55);
  --bad-a60:rgba(233,21,45,.6); --bad-a7:rgba(233,21,45,.07); --bad-a9:rgba(233,21,45,.09);
  --bad-ahalo:rgba(233,21,45,0); --black-a22:rgba(0,0,0,.22); --brand-15-on-surface-3:#d2d2d7;
  --brand-16-on-surface:#dbdbdb; --brand-2-28-on-white:#c0c0c0; --brand-2-72-on-dim:#343437;
  --brand-2-ahalo:rgba(29,29,31,0); --brand-30-on-surface-3:#b2b2b6; --brand-32-on-line:#a4a4a8;
  --brand-34-on-line:#a0a0a4; --brand-38-on-line:#98989c; --brand-40-on-input-line:#8a8a8d;
  --brand-40-on-line:#949498; --brand-40-on-line-soft:#9a9a9c; --brand-42-on-line:#909094;
  --brand-45-on-line:#8a8a8e; --brand-50-on-line:#808084; --brand-55-on-line:#767679;
  --brand-55-on-surface-3:#7d7d80; --brand-65-on-line:#626265; --brand-70-on-line:#58585b;
  --brand-78-on-dim:#2f2f31; --brand-8-on-surface-2:#e7e7e9; --brand-80-on-surface-3:#48484a;
  --brand-82-on-surface-3:#434346; --brand-9-on-surface:#ebebeb; --brand-a13:rgba(29,29,31,.13);
  --brand-a14:rgba(29,29,31,.14); --brand-a15:rgba(29,29,31,.15); --brand-a16:rgba(29,29,31,.16);
  --brand-a18:rgba(29,29,31,.18); --brand-a20:rgba(29,29,31,.2); --brand-a26:rgba(29,29,31,.26);
  --brand-a30:rgba(29,29,31,.3); --brand-a35:rgba(29,29,31,.35); --brand-a40:rgba(29,29,31,.4);
  --brand-a45:rgba(29,29,31,.45); --brand-a46:rgba(29,29,31,.46); --brand-a5:rgba(29,29,31,.05);
  --brand-a50:rgba(29,29,31,.5); --brand-a55:rgba(29,29,31,.55); --brand-a56:rgba(29,29,31,.56);
  --brand-a60:rgba(29,29,31,.6); --brand-a70:rgba(29,29,31,.7); --brand-a72:rgba(29,29,31,.72);
  --brand-a78:rgba(29,29,31,.78); --brand-a8:rgba(29,29,31,.08); --brand-a85:rgba(29,29,31,.85);
  --brand-ahalo:rgba(29,29,31,0); --brand-ahalohalf:rgba(29,29,31,0); --btn-ink-a35:rgba(255,255,255,.35);
  --faint-a12:rgba(134,134,139,.12); --faint-a14:rgba(134,134,139,.14); --faint-a22:rgba(134,134,139,.22);
  --faint-a28:rgba(134,134,139,.28); --faint-a30:rgba(134,134,139,.3); --faint-a34:rgba(134,134,139,.34);
  --faint-a38:rgba(134,134,139,.38); --faint-a42:rgba(134,134,139,.42); --faint-a5:rgba(134,134,139,.05);
  --faint-a52:rgba(134,134,139,.52); --faint-a55:rgba(134,134,139,.55); --faint-a60:rgba(134,134,139,.6);
  --faint-a90:rgba(134,134,139,.9); --faint-a95:rgba(134,134,139,.95); --gm-a12:rgba(30,110,244,.12);
  --gm-a14:rgba(30,110,244,.14); --im-a12:rgba(176,47,194,.12); --im-a14:rgba(176,47,194,.14);
  --ts-a12:rgba(161,106,0,.12); --ts-a14:rgba(161,106,0,.14); --line-a70:rgba(227,227,232,.7);
  --oa-a12:rgba(0,133,117,.12); --oa-a14:rgba(0,133,117,.14); --ok-40-on-line:#88bf9f;
  --ok-55-on-surface:#73be8e; --ok-8-on-surface:#ebf6ef; --ok-a12:rgba(0,137,50,.12);
  --ok-a13:rgba(0,137,50,.13); --ok-a26:rgba(0,137,50,.26); --ok-a32:rgba(0,137,50,.32);
  --ok-a40:rgba(0,137,50,.4); --ok-a45:rgba(0,137,50,.45); --ok-a52:rgba(0,137,50,.52);
  --ok-a60:rgba(0,137,50,.6); --ok-ahalo:rgba(0,137,50,0); --rail-on-text-a60:rgba(29,29,31,.6);
  --rail-on-text-a70:rgba(29,29,31,.7); --surface-3-a45:rgba(242,242,247,.45); --text-45-on-surface-3:#929296;
  --text-a16:rgba(29,29,31,.16); --text-a17:rgba(29,29,31,.17); --text-a46:rgba(29,29,31,.46);
  --text-a60:rgba(29,29,31,.6); --text-a8:rgba(29,29,31,.08); --warn-36-on-line:#d8af94;
  --warn-50-on-line:#d49b74; --warn-55-on-line:#d29468; --warn-a12:rgba(197,83,0,.12);
  --warn-a13:rgba(197,83,0,.13); --warn-a16:rgba(197,83,0,.16); --warn-a20:rgba(197,83,0,.2);
  --warn-a22:rgba(197,83,0,.22); --warn-a26:rgba(197,83,0,.26); --warn-a30:rgba(197,83,0,.3);
  --warn-a40:rgba(197,83,0,.4); --warn-a45:rgba(197,83,0,.45); --warn-a70:rgba(197,83,0,.7);
  --warn-ahalo:rgba(197,83,0,0); --warn-a5:rgba(197,83,0,.05); --warn-a9:rgba(197,83,0,.09);
}
/* 主题切换瞬间的整页渐变（置于组件规则之前，不覆盖各自的 transition） */
html.theme-fade,html.theme-fade *{transition:background-color .25s ease,color .25s ease,border-color .25s ease,box-shadow .25s ease}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{background:var(--bg);color:var(--text);font-family:var(--sans);font-size:14px;line-height:1.5;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}
/* 选中文字用系统的淡蓝（macOS selectedTextBackgroundColor），两套主题通用 */
::selection{background:rgba(0,122,255,.24)}
/* 文字链接是 HIG 的 link 色（蓝）：它是全页唯一「点了会走」的正文，得和正文分开 */
a{color:var(--link);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:2px}
button{font-family:inherit;cursor:pointer;border:none;font-size:14px;color:inherit}
/* 文本框：白底、一根发丝边、10px 圆角（apple.com 表单的样子）。聚焦时边变蓝、
   外面一圈淡蓝环——输入框的焦点鼠标点进去也给，「光标现在在哪一格」总是有用的。 */
input,select,textarea{font-family:inherit;font-size:14px;color:var(--text);background:var(--input-bg);border:1px solid var(--input-line);border-radius:var(--r-md);padding:9px 12px;width:100%;outline:none;
  transition:border-color .16s var(--ease),box-shadow .16s var(--ease),background-color .16s var(--ease)}
input:focus,textarea:focus,select:focus{border-color:var(--focus);box-shadow:var(--focus-ring)}
/* 下拉框画成 macOS 的弹出按钮：右端一对上下尖角，替掉各浏览器各不相同的原生箭头 */
select{-webkit-appearance:none;appearance:none;padding-right:30px;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 16' fill='none' stroke='%2386868b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 6l3-3 3 3M2 10l3 3 3-3'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 11px center;background-size:8px 13px}
select[multiple]{background-image:none;padding-right:12px}
/* 勾选框不是输入框。上面那条 width:100% 落到它身上，它在弹性行里就会把整行宽度吃光，
   同一行里 flex:1 的名字被压成零宽——看上去是一列没有名字的空勾选框（拼车池的账号
   列表这么消失过一次）。已经有两处各自加内联 width:auto 绕开，在这里堵一次。
   自绘成开关/卡片的那几处（.toggle、.chan-pick）在后面把它藏起来，不受影响。 */
input[type=checkbox],input[type=radio]{width:auto;flex:none;accent-color:var(--brand)}
input[type=checkbox]:focus,input[type=radio]:focus{box-shadow:none}
input[type=checkbox]:focus-visible,input[type=radio]:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
input::placeholder,textarea::placeholder{color:var(--faint)}
textarea{resize:vertical;min-height:88px;font-family:var(--mono);font-size:12.5px;line-height:1.7}
.mono{font-family:var(--mono)}
.hide{display:none!important}
.dim{color:var(--dim)} .faint{color:var(--faint)}
/* 滚动条：macOS 的细滑块，跟主题走 */
*{scrollbar-width:thin;scrollbar-color:var(--faint-a42) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--faint-a38);border-radius:6px;border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:var(--faint-a60);border:3px solid transparent;background-clip:content-box}

/* 按钮：HIG 的几种样式——实心（主操作，墨底）、灰底（次要，.ghost）、危险（.danger
   灰底红字 / .solid-bad 红底白字）。一律胶囊形，按下缩到 0.97。
   次要按钮不再是描边盒：一排描边盒子在表格里会读成一排输入框。边框留一圈透明的，
   让实心和灰底两种按钮等高。 */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;background:var(--brand);color:var(--btn-ink);
  font-weight:500;font-size:13.5px;letter-spacing:-.005em;border:1px solid transparent;border-radius:var(--r-pill);padding:7px 16px;white-space:nowrap;
  transition:transform .2s var(--spring),background-color .16s var(--ease),color .16s var(--ease),opacity .16s var(--ease);box-shadow:none}
.btn:hover{background:var(--btn-hover)}
.btn:active{transform:scale(.97);background:var(--btn-press)}
.btn:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.btn.sm{padding:4px 12px;font-size:12.5px;border-radius:var(--r-pill)}
.btn.ghost{background:var(--fill-3);color:var(--text);box-shadow:none}
.btn.ghost:hover{background:var(--fill-2);color:var(--text)}
.btn.ghost:active{background:var(--fill-1)}
.btn.danger{background:var(--fill-3);color:var(--bad);box-shadow:none}
.btn.danger:hover{background:var(--bad-a12)}
.btn.danger:active{background:var(--bad-a18)}
.btn.solid-bad{background:var(--bad-fill);color:#fff;box-shadow:none}
.btn.solid-bad:hover{background:var(--bad)}
.btn.solid-bad:active{background:var(--bad)}
.btn:disabled,.btn[disabled]{opacity:.4;pointer-events:none}
/* 按钮忙碌态：指针收起，转一枚小针 */
.btn.busy{opacity:.75;pointer-events:none}
.btn.busy::before{content:"";width:13px;height:13px;flex:none;border-radius:50%;
  border:2px solid var(--btn-ink-a35);border-top-color:var(--btn-ink);animation:pspin .7s linear infinite}
.btn.ghost.busy::before,.btn.danger.busy::before{border-color:var(--faint-a38);border-top-color:var(--text)}

/* ---------- LOGIN ---------- */
/* 登录：一张居中的表单卡，顶上一枚大号 app 图标（Apple 账户登录页的构图）。
   背景就是分组灰，不放动画——登录页只有一件事要做。 */
#login{min-height:100vh;min-height:100dvh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;
  padding:24px;background:var(--bg)}
.patchfield{display:none}
.login-card{width:380px;max-width:100%;background:var(--card-grad);position:relative;z-index:1;text-align:center;
  border:1px solid var(--line);border-radius:var(--r-2xl);padding:40px 36px 30px;
  box-shadow:var(--shadow-1);animation:rise .5s var(--ease-out) both}
.login-mark{display:flex;flex-direction:column;align-items:center;gap:12px;margin-bottom:20px}
.login-mark .appmark{width:64px;height:64px;border-radius:15px}
.login-mark .appmark svg{width:38px;height:38px}
.login-mark .name{font-family:var(--disp);font-weight:600;font-size:14px;letter-spacing:-.005em;color:var(--dim)}
.login-card h1{font-family:var(--disp);font-size:26px;font-weight:600;margin:0 0 6px;letter-spacing:-.022em}
.login-card .sub{color:var(--dim);font-size:14px;margin:0 0 26px}
.login-card .field{text-align:left}
.field{margin-bottom:13px}
/* 字段标题：句子写法、次级文字色，不再全大写加字距（中文里那是噪音，英文里是喊话） */
.field label{display:block;font-size:12.5px;font-weight:500;color:var(--dim);margin-bottom:6px}
#login input{padding:11px 14px;font-size:15px;border-radius:12px}
#login .btn{width:100%;justify-content:center;margin-top:10px;padding:10px;font-size:15px}
.err{color:var(--bad);font-size:13px;min-height:18px;margin-top:12px;text-align:center}
/* 两步验证：登录卡第二步和「账户与安全」抽屉共用。二维码自带白底和留白，深色主题里照样能扫。 */
.lg-note{font-size:13px;line-height:1.6;color:var(--dim);text-align:left;margin:0 0 16px}
.tf-qr{display:block;width:172px;height:172px;margin:0 auto 12px;border-radius:12px;border:1px solid var(--line)}
.tf-key{font-size:12px;line-height:1.6;color:var(--dim);margin:0 0 18px}
.tf-key code{display:block;margin-top:4px;font-family:var(--mono);font-size:12px;color:var(--text);
  user-select:all;overflow-wrap:anywhere}
.tf-code{font-family:var(--mono);font-size:20px;letter-spacing:.32em;text-align:center}
.tf-code::placeholder{font-family:var(--sans);font-size:15px;letter-spacing:0}
#login .tf-code{font-size:20px}
.lg-back{display:inline-block;margin-top:14px;padding:4px 8px;border:none;background:none;border-radius:8px;
  font:inherit;font-size:13px;color:var(--dim);cursor:pointer}
.lg-back:hover{color:var(--text)}
.lg-back:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* ---------- SHELL ---------- */
/* 窗口 = 台面 + 一块浮起来的玻璃侧栏（macOS 26 的侧栏不再贴边：四周留 10px，
   圆角和窗口同心）。侧栏本身仍是 --rail 宽，外侧那 10px 算进这一列。 */
#app{display:grid;grid-template-columns:calc(var(--rail) + 10px) minmax(0,1fr);min-height:100vh;min-height:100dvh}
.rail{position:sticky;top:10px;height:calc(100vh - 20px);height:calc(100dvh - 20px);margin:10px 0 10px 10px;
  background:var(--rail-bg);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  border:1px solid var(--rail-line);border-radius:var(--r-2xl);box-shadow:var(--sheen),var(--card-shadow);
  display:flex;flex-direction:column;padding:14px 10px 10px;z-index:6}
/* 品牌：app 图标 + 名字；副题是一行小字，不再全大写加字距 */
.appmark{display:inline-grid;place-items:center;flex:none;width:30px;height:30px;border-radius:8px;
  background:linear-gradient(180deg,#4a4a4f,#1d1d1f);
  box-shadow:inset 0 0 0 .5px rgba(255,255,255,.16),0 1px 2px rgba(0,0,0,.16),0 3px 8px rgba(0,0,0,.1)}
.appmark svg{width:19px;height:19px}
.brand{display:flex;align-items:center;gap:10px;padding:4px 8px 18px}
.brand .name{font-family:var(--disp);font-weight:600;font-size:15px;letter-spacing:-.012em;color:var(--rail-text);line-height:1.2}
.brand .tag{font-size:11px;color:var(--rail-faint);letter-spacing:.02em;text-transform:none;line-height:1.3}
/* 导航：品牌头与脚注钉死，菜单放不下时在自己的区域里滚动，不裁掉尾部 */
.nav{display:flex;flex-direction:column;gap:2px;flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
/* 一项 = 图标 + 名字，选中是一块圆角的浅填充（macOS 侧栏的选中态），名字加粗一档 */
.nav-item{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:var(--r-md);color:var(--rail-text);
  font-size:13.5px;font-weight:450;letter-spacing:-.005em;transition:background-color .16s var(--ease),color .16s var(--ease);
  text-align:left;background:transparent;width:100%;min-width:0}
.nav-item svg{width:17px;height:17px;flex:none;color:var(--rail-dim);transition:color .16s var(--ease)}
.nav-item span{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nav-item:hover{background:var(--rail-hover)}
.nav-item:active{background:var(--rail-on-bg)}
/* 当前模块是一枚实心墨色药丸（品牌单色：浅色黑底白字、深色白底黑字），换页时侧栏只换这一枚 */
.nav-item.on{background:var(--brand);color:var(--btn-ink);font-weight:600;box-shadow:0 1px 2px rgba(0,0,0,.12)}
.nav-item.on svg{color:var(--btn-ink)}
.nav-item.on:hover,.nav-item.on:active{background:var(--brand)}
.nav-item:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
/* 侧栏只放十个一级模块，一列排下（以前 27 个入口分五组、双列才挤得进一屏）。
   当前模块整格点亮；模块里的具体页面在顶栏下面的模块页签条上。 */
.nav-item.nav-more{display:none}
.rail-foot{margin-top:auto;padding:10px 10px 4px;border-top:1px solid var(--rail-line);font-size:12px;color:var(--rail-faint);flex:none}
/* 矮窗口 / 高缩放：侧栏整体收紧一档，尽量让全部模块免滚呈现 */
@media (max-height:820px){
  .rail{padding:10px 10px 8px}
  .brand{padding-bottom:12px}
  .rail-foot{padding-top:8px}
}
/* 13 寸笔记本 + 一堆浏览器工具栏：再收一档，仍要全部模块免滚 */
@media (max-height:720px){
  .rail{padding:8px 8px 8px}
  .brand{padding-bottom:8px}
  .nav-item{padding:6px 9px}
}
.rail-foot .live{display:inline-flex;align-items:center;gap:7px;color:var(--rail-dim)}
.pulse-dot{width:7px;height:7px;border-radius:50%;background:var(--ok-fill);box-shadow:0 0 0 0 rgba(52,199,89,.5);animation:pulse 2.4s infinite}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(52,199,89,.45)}70%{box-shadow:0 0 0 7px rgba(52,199,89,0)}100%{box-shadow:0 0 0 0 rgba(52,199,89,0)}}

.main{min-width:0}
/* 顶栏：导航层的玻璃（半透明 + 模糊 + 提饱和），内容从它底下滚过去 */
.topbar{display:flex;align-items:center;justify-content:space-between;padding:16px 28px;border-bottom:1px solid var(--line-soft);position:sticky;top:0;
  background:var(--glass);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);z-index:5}
.topbar .title{font-family:var(--disp);font-size:21px;font-weight:600;letter-spacing:-.022em;line-height:1.25}
.topbar .crumb{font-size:12.5px;color:var(--dim);margin-top:2px}
.nav-read-status{font-size:11px;color:var(--dim);margin-top:2px}
.nav-read-status:empty{display:none}
.topbar .right{display:flex;align-items:center;gap:6px;flex:none;white-space:nowrap}
.topbar>div:first-child{min-width:0;flex:1 1 auto;padding-right:16px}
.who{font-size:13px;color:inherit}
.account{display:inline-flex;align-items:center;gap:7px;background:var(--fill-4);border:1px solid transparent;color:var(--text);font-weight:500;font-size:13px;
  border-radius:var(--r-pill);padding:5px 6px 5px 12px;transition:background-color .16s var(--ease)}
.account:hover{background:var(--fill-3)}
.account:active{background:var(--fill-2)}
.account:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.account svg{width:14px;height:14px;opacity:.7;flex:none}
/* 顶栏右侧是一排工具栏按钮：平时只有字，悬停才出现胶囊底——一排灰盒子会抢标题的戏 */
.topbar .btn.ghost{background:transparent;color:var(--text)}
.topbar .btn.ghost:hover{background:var(--fill-3);color:var(--text)}
/* 刷新 / 退出：桌面上是文字，窄屏收成两颗圆形图标钮，把位置让给页面标题 */
.tb-act .tb-ico{display:none;width:17px;height:17px;flex:none}
/* 全局搜索钮画成一截搜索栏：灰底胶囊、放大镜 + 占位字 + ⌘K，一看就知道能在这里搜 */
.topbar .tb-search{background:var(--fill-4);color:var(--dim);font-weight:400;min-width:150px;justify-content:flex-start;padding:5px 7px 5px 12px}
@media (max-width:1280px){.topbar .tb-search{min-width:0}}
.topbar .tb-search:hover{background:var(--fill-3);color:var(--text)}
.topbar .tb-search .pal-kbd{margin-left:auto}
/* 掌上搜索框默认不占位。展开规则只在 ≤768px 里，桌面这条一直是 display:none。 */
.tb-qbox{display:none}

/* 主题切换：圆形图标钮。深色时亮出太阳（点了变亮），浅色时亮出月亮 */
.theme-btn{position:relative;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex:none;padding:0;
  background:transparent;border:1px solid transparent;border-radius:50%;color:var(--dim);overflow:hidden;
  transition:color .16s var(--ease),background-color .16s var(--ease)}
.theme-btn:hover{color:var(--text);background:var(--fill-3)}
.theme-btn:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.theme-btn svg{position:absolute;width:16px;height:16px;transition:transform .45s var(--spring),opacity .2s ease}
.theme-btn .i-sun{opacity:1;transform:none}
.theme-btn .i-moon{opacity:0;transform:rotate(90deg) scale(.4)}
[data-theme=light] .theme-btn .i-sun{opacity:0;transform:rotate(-90deg) scale(.4)}
[data-theme=light] .theme-btn .i-moon{opacity:1;transform:none}
.theme-btn.float{position:fixed;top:18px;right:18px;z-index:30;background:var(--surface);border-color:var(--line);box-shadow:var(--card-shadow)}

/* 密码强度：四格，最强一格是实心墨色 */
.pw-strength{display:flex;align-items:center;gap:10px;margin:-2px 0 14px}
.pw-bars{display:flex;gap:4px;flex:1}
.pw-bar{height:4px;flex:1;border-radius:2px;background:var(--fill-3);transition:background-color .2s}
.pw-bar.t1{background:var(--bad-fill)} .pw-bar.t2{background:var(--warn-fill)} .pw-bar.t3{background:var(--ok-fill)}
.pw-bar.t4{background:var(--ok)}
.pw-label{font-size:11.5px;font-weight:600;color:var(--dim);min-width:30px;text-align:right}
/* 页面顶部留白：模块页签条的下边线到页面第一块内容，全站统一 16px（掌上 12px）。
   规范见 docs/admin-layout.md。页面里第一块东西不要再自带上外边距或上内边距；
   钉住的工具带（订阅分配的 .cpd、报表中心的 .rp-catalog）用负外边距抵掉这段，
   让带子贴着页签条、带子里的控件仍落在同一条线上。 */
.view{--view-top:16px;padding:var(--view-top) 28px 60px}
@keyframes rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
/* 换页过渡（ptLeave / ptEnter）：只在导航换了页面或页签的那一刻播一次。
   侧栏不参与；旧内容的残影（.pt-ghost，克隆出来、不可交互）淡出 120ms 并上移 4px；
   新页的顶层模块（.pt-m，只有导航那一刻捕获到的前 8 个错开）从下方 10px 淡入。
   最后一个模块的开始时间封顶在 100 + 140ms，淡入 160ms，整段不超过 400ms：
   模块多时由 JS 把 --pt-step 从 45ms 往下压。只动 opacity 和 transform。
   数据到了重画插进来的新节点不带 .pt-m，不会再播一遍。 */
:root{--pt-out:120ms;--pt-lead:100ms;--pt-in:160ms;--pt-step:45ms;--pt-ease:cubic-bezier(.22,.61,.36,1)}
/* 内容长短变化时滚动条出现 / 消失不再把整页横向推 15px */
html{scrollbar-gutter:stable}
.pt-ghost{position:fixed!important;z-index:3;margin:0!important;pointer-events:none;animation:pt-out var(--pt-out) var(--pt-ease) both}
@keyframes pt-out{from{opacity:1;transform:none}to{opacity:0;transform:translateY(-4px)}}
.pt-m{animation:pt-in var(--pt-in) var(--pt-ease) both;animation-delay:calc(var(--pt-lead) + var(--i,0) * var(--pt-step))}
@keyframes pt-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes pt-fade{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .pt-ghost{display:none}
  .pt-m{animation:pt-fade 90ms linear both;animation-delay:0s}
}
/* 首次读数的占位：进页时还空着的区块（带 id、等数据来填的那些）画成灰色块，
   页面一进来就是最终的骨架。表头里那一行判断句画成一条短线。只在 .is-loading
   期间生效，数据一到 navEnterView 就把类撤掉。 */
.view.is-loading div[id]:empty:not(.hide):not(.tb-lead *):not(.tb-roll){min-height:72px;margin-bottom:16px;border-radius:18px;background:linear-gradient(90deg,var(--fill-4) 25%,var(--fill-3) 50%,var(--fill-4) 75%);background-size:200% 100%;animation:skel 1.4s ease-in-out infinite}
.view.is-loading .tb-say:empty{display:block;height:18px;width:min(360px,60%);border-radius:9px;background:var(--fill-3)}
/* 表格：表头先画出来，表体还空时在表头下面垫三行高的占位。 */
.view.is-loading .panel:has(>table>tbody:empty)::after{content:"";display:block;height:96px;margin:12px 16px 16px;border-radius:12px;background:linear-gradient(90deg,var(--fill-4) 25%,var(--fill-3) 50%,var(--fill-4) 75%);background-size:200% 100%;animation:skel 1.4s ease-in-out infinite}
/* 概览两块大板按最终高度占位，数据到了不再把下面整页往下推。 */
.view.is-loading #tiles:empty{min-height:122px}
.view.is-loading #ov-hero:empty{min-height:280px}
@keyframes skel{from{background-position:100% 0}to{background-position:-100% 0}}
@media (prefers-reduced-motion:reduce){.view.is-loading div[id]:empty,.view.is-loading .panel::after{animation:none}}
/* 顶栏标题随切页轻落 */
.tswap{animation:tswap .32s var(--ease-out)}
@keyframes tswap{from{opacity:0;transform:translateY(-4px)}}
/* 区块导语：一行次级文字。以前是 11px 全大写加字距，而这一行里多半是一整句中文说明 */
.eyebrow{font-size:12.5px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--dim);margin:0 0 12px;line-height:1.55}
.sec{margin-bottom:32px}

/* tiles：读数卡。标题一行次级字，数字用 SF Pro Display 的大字号紧字距 */
.tiles{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:28px}
#ku-tiles{grid-template-columns:repeat(6,1fr)} /* 用量页 6 块：钱三块 + token 两块 + 计量覆盖率 */
#ku-tiles .tile .v{font-size:24px}
.tile{background:var(--card-grad);border:1px solid var(--line);
  border-radius:var(--r-xl);padding:17px 19px;position:relative;overflow:hidden;box-shadow:var(--card-shadow)}
.tile .k{font-size:12.5px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--dim)}
.tile .v{font-family:var(--disp);font-size:30px;font-weight:600;letter-spacing:-.025em;margin-top:10px;line-height:1.05;font-variant-numeric:tabular-nums}
.tile .v small{font-size:14px;color:var(--dim);font-weight:500;letter-spacing:0}
.tile .sub{font-size:12.5px;color:var(--dim);margin-top:8px}
.tile .sub .tile-bad{color:var(--bad);font-weight:600}
.tile .sub .tile-warn{color:var(--warn);font-weight:600}
.tile .sub .tile-split{margin-top:4px;display:flex;flex-wrap:wrap;gap:0 10px}
.tile .sub .tile-split span{display:inline-block;white-space:nowrap}
.tile .sub .tile-split b{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
/* 余额进账瓦片：大字只是申请充值的到账，下面四行各自独立，避免和实收、补记挤成一句。 */
.tile-lines{display:flex;flex-direction:column;gap:6px;margin-top:12px}
.tile-lines div{display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:4px 12px;
  font-size:12.5px;line-height:1.4;color:var(--dim)}
.tile-lines span{min-width:0}
.tile-lines b{font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums;color:var(--text);white-space:nowrap}
.tile-lines .down b{color:var(--bad)}
/* 重点读数卡不再挂顶边彩线：HIG 用位置和字号分主次，不用装饰线 */
.tile.accent{border-color:var(--line)}

/* ---------- SIGNATURE: routing signal paths ---------- */
/* 通路板：干净台面，只留行分隔——旧版 30px 横纹在浅色下与行界错位成"错觉线"，已拆除 */
.routes{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);padding:8px 4px;box-shadow:var(--card-shadow)}
.route{display:grid;grid-template-columns:230px 1fr 150px;gap:18px;align-items:center;padding:15px 20px;border-bottom:1px solid var(--line-soft)}
.route:last-child{border-bottom:none}
.route-meta{display:flex;flex-direction:column;gap:6px;min-width:0}
.route-model{font-family:var(--mono);font-size:13px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.path{display:flex;align-items:center;flex-wrap:wrap;gap:2px;row-gap:10px}
.source{width:11px;height:11px;border-radius:50%;background:var(--brand);flex:none;
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--brand-ahalo)}
.wire{height:2px;width:30px;flex:none;background:repeating-linear-gradient(90deg,var(--faint-a55) 0 4px,transparent 4px 8px);border-radius:2px}
.wire.live{background:linear-gradient(90deg,var(--brand),var(--brand-2));background-size:200% 100%;animation:flow 1.8s linear infinite}
@keyframes flow{from{background-position:200% 0}to{background-position:0 0}}
.hop{display:inline-flex;align-items:center;gap:8px;background:var(--surface-3);border:1px solid var(--line);
  border-radius:999px;padding:6px 13px 6px 11px;flex:none}
.hop.active{border-color:var(--brand-a70);background:var(--brand-9-on-surface);
  box-shadow:0 2px 10px -4px var(--brand-a45)}
.hop-name{font-size:12.5px;font-weight:500}
/* 均衡组的连线不该画成「接不住才往下」的虚线：这两条线是同时在用的。实心一条、
   压上一个 ⇉，看板才不会把一组并联的账号画成一串备胎。 */
.wire.peer{background:var(--brand-a55);height:2px;position:relative;width:22px}
.wire.peer::after{content:"⇉";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  font-size:10px;line-height:1;color:var(--brand-a85);
  background:var(--surface);padding:0 2px}
.hop-load{font-family:var(--mono);font-size:10.5px;font-variant-numeric:tabular-nums;
  color:var(--faint);padding-left:6px;margin-left:2px;border-left:1px solid var(--line)}
.hop-load.hot{color:var(--warn)} .hop-load.full{color:var(--bad);font-weight:600}
.hop-alias{font-family:var(--mono);font-size:11px;color:var(--dim)}
.lamp{width:9px;height:9px;border-radius:50%;flex:none;background:var(--faint)}
.lamp-closed{background:var(--ok);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--ok-ahalo)}
.lamp-open{background:var(--bad);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--bad-ahalo)}
.lamp-half_open{background:var(--warn);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--warn-ahalo);animation:blink 1s steps(2,start) infinite}
.lamp-unknown{background:var(--faint)}
/* 停靠：琥珀但不闪。闪的是「正在试探、随时会变」，停靠恰恰相反——日子已经定死，
   在那之前什么都不会发生，所以它该是一盏稳定的灯。 */
.lamp-parked{background:var(--warn);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--warn-ahalo)}
@keyframes blink{50%{opacity:.3}}

/* ---------- 活线 / LIVE FLOW -----------------------------------------
   通路板一直只画「线怎么接」：谁排第一、谁熔断了。可每天打开这一页要问的第一句
   其实是「此刻电流在哪根线上过」，而那句话以前得翻到健康页去问，还问不到规则这一层
   ——健康页按模型统计，通路板按规则接线，两边对不上号。日志里的 route_rule 正好是
   两者之间那把钥匙，于是把电流画回线上。

   三条通道，各说一件互不重复的事：
     色相     说种类 —— 紫是流量、红是失败。颜色永远不表示「多少」。
     运动     说此刻 —— 线上跑不跑电荷 = 最近 5 分钟有没有真实请求；越密越快越忙。
     右侧读数 说总账 —— 24 小时的次数、逐时纸带，和一行只在出事时才出现的例外。

   所以 .live 不再自己动了。它原本挂着一段与流量无关的循环扫光：满屏的线都在动，
   动就不再是信号，只是装饰。现在动只有一个含义——真的有请求正从这儿过。 */
.wire.live,.wire.flowed{background:linear-gradient(90deg,var(--brand),var(--brand-2));
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--brand) calc(var(--vol,1) * 100%),transparent),
    color-mix(in srgb,var(--brand-2) calc(var(--vol,1) * 100%),transparent));
  animation:none}
/* .flowed 是「窗口内有流量从这儿过」，.live 是「断路器认为链走到这儿」——两件事
   常常不重合，而不重合处正是要看的。--vol 决定它有多实：一段线承载得越少越淡，
   一次也没走过就退回灰色虚线。于是首选跳没接住时，第二段会和第一段一样实，
   静默故障切换不必点开就看得见。 */
/* 电荷是线上最亮的那一点，不是最暗的：往白里调，深浅两套主题下它都是「亮」，
   而底色仍然只由 --vol 决定。这样线的明度始终单调地跟着流量走——一条正忙的线
   永远不会看起来比一条闲着的线更淡。 */
.wire.charged{height:3px;
  background-image:
    repeating-linear-gradient(90deg,transparent 0 calc(var(--gap,14px) - var(--dash,5px)),
      var(--brand-2-28-on-white) calc(var(--gap,14px) - var(--dash,5px)) var(--gap,14px)),
    linear-gradient(90deg,
      color-mix(in srgb,var(--brand) calc(var(--vol,1) * 100%),transparent),
      color-mix(in srgb,var(--brand-2) calc(var(--vol,1) * 100%),transparent));
  background-size:var(--gap,14px) 100%,100% 100%;
  background-repeat:repeat-x,no-repeat;
  animation:charge var(--spd,2s) linear infinite;animation-delay:var(--dly,0s)}
@keyframes charge{to{background-position:var(--gap,14px) 0,0 0}}
/* 请求源那颗点：这条泳道 24 小时里一次调用都没有时压暗。一列泳道扫下来，
   左边这排点就是「哪几条线真的在被用」。它不闪——动只留给线，一件事一种表达；
   给源头再配一圈脉冲，只是把线已经说过的话大声说了第二遍。 */
.source.cold{background:var(--faint-a52);box-shadow:none}

/* 泳道读数：这条规则 24 小时接了多少。从上到下是「多少 → 什么时候 → 出没出事」，
   最后一行是例外行：没有失败也没有改道时它根本不出现。一条一切正常的泳道就该
   只剩一个数字——报警的位置平时空着，它出现时才有分量。 */
.lane-read{display:flex;flex-direction:column;align-items:flex-end;gap:5px;min-width:0}
.lane-n{font-family:var(--mono);font-size:16px;font-weight:600;line-height:1;
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;color:var(--text)}
.lane-n .u{font-family:var(--sans);font-size:10.5px;font-weight:500;color:var(--faint);
  margin-left:4px;letter-spacing:0}
.lane-read.idle .lane-n{color:var(--faint);font-weight:500;font-size:14px}
.lane-quiet{font-size:10.5px;color:var(--faint)}
/* 纸带：一格一小时，和健康页同一种读法（高度是量、红头是失败），缩到一行的尺寸。
   高度按本泳道自己的峰值归一化，因为它回答的是「这条线什么时候在跑」；
   「跑了多少」左边那个数字已经答过一遍，不必再答。 */
.lane-tape{display:flex;align-items:flex-end;gap:1px;height:15px}
.lane-tape i{width:3px;border-radius:1px;min-height:1px;
  background:linear-gradient(to top,
    var(--brand-a56) 0 calc(100% - var(--e,0%)),
    var(--bad) calc(100% - var(--e,0%)) 100%)}
.lane-tape i.nil{height:1px;background:var(--faint-a34)}
.lane-flag{display:flex;align-items:center;gap:6px;font-family:var(--mono);font-size:10.5px;
  font-variant-numeric:tabular-nums;white-space:nowrap}
.lane-flag .e{color:var(--bad)} .lane-flag .o{color:var(--warn)} .lane-flag .sep{color:var(--faint)}
/* 图例只讲那两个要学一次的读法，断路器的灯色不在这儿重复解释——那套灯全站通用 */
.flow-key{display:flex;flex-wrap:wrap;gap:6px 20px;margin:-8px 0 13px;padding:0 2px;
  font-size:11px;color:var(--faint)}
.flow-key span{display:inline-flex;align-items:center;gap:7px}
.flow-key i{display:inline-block;flex:none;border-radius:1px}
.flow-key i.chg{width:24px;height:2px;
  background:repeating-linear-gradient(90deg,transparent 0 6px,var(--brand-2) 6px 9px)}
.flow-key i.tp{width:13px;height:10px;border-radius:1px;
  background:linear-gradient(to top,var(--brand-a46) 0 72%,var(--bad) 72% 100%)}
.flow-key .miss{color:var(--warn)}

/* ---------- 订阅节点 / SUBSCRIPTION JACK ----------
   每个原生 OAuth 上游就是一个账号。节点在名字后显示该账号自己的额度窗口，
   账号之间仍由普通路由链组合；读不到就是未知，不把不同供应商的额度硬凑成均值。 */
.hop-pool{display:inline-flex;align-items:center;gap:7px;padding-left:9px;margin-left:1px;
  border-left:1px solid var(--line);cursor:pointer}
.hop-bank{display:inline-flex;gap:2px;align-items:flex-end}
/* 健康的那几格压到六成：五个号全绿时这排灯不该抢戏，红黄才是要被一眼看见的。
   一排等亮度的红黄绿看起来像圣诞灯，反而把例外藏进了图案里。 */
.hop-bank i{width:3px;height:10px;border-radius:1px;background:var(--faint);opacity:.4}
.hop-bank i.ok{background:var(--ok);opacity:.6}
.hop-bank i.warn{background:var(--warn);opacity:1}
.hop-bank i.bad{background:var(--bad);opacity:1}
.hop-read{font-family:var(--mono);font-size:10.5px;font-variant-numeric:tabular-nums;
  color:var(--dim);white-space:nowrap;letter-spacing:-.01em}
.hop-read.warn{color:var(--warn)} .hop-read.bad{color:var(--bad)}
.hop-read .when{color:var(--faint);margin-left:5px}
.hop-pool:hover .hop-read{color:var(--text)}
.hop-more-n{font-family:var(--mono);font-size:10px;color:var(--faint)}

/* ---- 号池汇流排 / ACCOUNT-POOL BUS ------------------------------------
   一个号池在链上占一跳，池里 N 路同时在用。收起时只报三件事——几路、几路通、
   均余多少；点开才是每个账号。池内不给拖拽：能拖的地方就会有人以为顺序有用，
   而池内的顺序恰恰是唯一没有意义的那个。
   前缀一律 .ap-。.hop-pool 早被「这个账号的额度读数」占了，新组件用同名 class
   会被另一处规则整个套上，症状是多出一个框而看代码看不出来。 */
.ap-bus{display:inline-flex;flex-direction:column;flex:none;max-width:100%;
  background:var(--brand-9-on-surface);border:1px solid var(--brand-a70);border-radius:var(--r-xl)}
/* 展开的池自己占一行：十一路要横着摊开才读得出谁在扛，挤在一跳的宽度里
   就又变回一根竖着的清单——正是要拆掉的那个形状。 */
.ap-bus.open{flex:1 1 100%;width:100%}
.ap-bus.alarm{border-color:var(--bad);background:var(--bad-a7)}
.ap-top{display:flex;align-items:center;gap:9px;padding:7px 12px 7px 11px;cursor:pointer;
  background:none;border:0;color:inherit;font:inherit;text-align:left;width:100%;border-radius:13px}
.ap-top:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.ap-mark{font-size:12px;color:var(--brand-2);line-height:1}
.ap-bus.alarm .ap-mark{color:var(--bad)}
.ap-nm{font-size:12.5px;font-weight:600;white-space:nowrap}
.ap-ways{font-family:var(--mono);font-size:10.5px;color:var(--brand-2);
  background:var(--brand-a12);border-radius:5px;padding:1px 6px;white-space:nowrap}
.ap-bus.alarm .ap-ways{color:var(--bad);background:var(--bad-a12)}
.ap-meta{font-family:var(--mono);font-size:10.5px;color:var(--dim);white-space:nowrap;
  padding-left:9px;border-left:1px solid var(--line)}
.ap-meta.warn{color:var(--warn)} .ap-meta.bad{color:var(--bad)}
.ap-caret{margin-left:2px;color:var(--faint);font-size:9px;transition:transform .16s}
.ap-bus.open .ap-caret{transform:rotate(90deg)}
.ap-body{display:none;padding:0 12px 11px}
.ap-bus.open .ap-body{display:block}
/* 分流条：最近这段时间，这个池把流量摊成了什么形状。一段一个账号，宽度就是
   它接了多少。这是唯一能证明号池在工作的读数——配置本身证明不了任何事。 */
.ap-split{display:flex;gap:2px;height:9px;border-radius:3px;overflow:hidden;
  background:var(--surface-3);margin:2px 0 6px}
.ap-split i{background:var(--brand);opacity:.75;min-width:2px;border-radius:2px}
.ap-split i.hot{background:var(--warn);opacity:1}
/* 没跑过的窗口不画成「完美均匀」：空态是未接线，和这块板别处一个规矩。 */
.ap-split.cold i{background:var(--faint);opacity:.3}
.ap-splitcap{display:flex;justify-content:space-between;gap:12px;
  font-family:var(--mono);font-size:10px;color:var(--faint);margin-bottom:9px}
.ap-splitcap b{color:var(--dim);font-weight:500}
.ap-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(126px,1fr));gap:5px}
.ap-cell{display:flex;flex-direction:column;gap:4px;padding:6px 8px;border-radius:8px;
  background:var(--surface);border:1px solid var(--line-soft)}
.ap-cell.off{opacity:.55;border-style:dashed}
.ap-cellhd{display:flex;align-items:center;gap:6px}
.ap-cellnm{font-family:var(--mono);font-size:11px;color:var(--text);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.ap-cellpc{font-family:var(--mono);font-size:10.5px;color:var(--dim);margin-left:auto}
.ap-cellpc.warn{color:var(--warn)} .ap-cellpc.bad{color:var(--bad)}
.ap-bar{height:3px;border-radius:2px;background:var(--surface-3);overflow:hidden}
.ap-bar i{display:block;height:100%;background:var(--ok);border-radius:2px}
.ap-bar i.warn{background:var(--warn)} .ap-bar i.bad{background:var(--bad)}
.ap-share{font-family:var(--mono);font-size:9.5px;color:var(--faint)}
/* 收拢提示条：板上认出一串对等的跳还串着时，由板自己提出这件事。 */
.ap-nudge{grid-column:1/-1;display:flex;align-items:center;gap:11px;flex-wrap:wrap;
  margin:2px 0 0;padding:8px 12px;border:1px dashed var(--brand-a45);border-radius:10px;
  background:var(--brand-a5);font-size:12px;color:var(--dim)}
.ap-nudge b{color:var(--brand-2)}
.ap-nudge .btn{margin-left:auto}

/* 池详单：点开一个池节点，把里面 N 个号摊平。和供应商页那块面板同一套读法，
   只是窄了一号——同一件事在两个地方长得不一样，是要人重新学一遍。 */
.jack-pop{position:fixed;z-index:70;width:384px;max-width:calc(100vw - 24px);
  background:var(--glass-thick);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  border:1px solid var(--rail-line);border-radius:var(--r-lg);
  box-shadow:var(--sheen),var(--shadow-3);padding:12px 14px 10px;
  animation:jackin .28s var(--spring)}
@keyframes jackin{from{opacity:0;transform:translateY(-4px) scale(.98)}}
.jack-pop h4{margin:0 0 2px;font-size:12.5px;font-weight:600;display:flex;align-items:center;gap:7px}
.jack-pop h4 .n{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--dim);font-weight:500}
.jack-pop h4 .n b{color:var(--ok)} .jack-pop h4 .n b.thin{color:var(--warn)} .jack-pop h4 .n b.out{color:var(--bad)}
.jack-when{font-size:10.5px;color:var(--faint);margin:0 0 7px}
.jack-note{font-size:11px;color:var(--warn);padding:3px 0 5px}
/* 两行一个号，不是一行：状态和重置时刻挤在名字后面时，第一个被省略号吃掉的正是
   它们——而"几时回来"恰好是点开这张单子唯一要问的事。名字长短不该决定它出不出现。 */
.jack-row{display:grid;grid-template-columns:9px minmax(0,1fr) 108px;gap:10px;align-items:center;
  padding:8px 0;border-top:1px solid var(--line-soft)}
.jack-row .lamp{margin-top:3px;align-self:start}
.jack-row .who{display:flex;flex-direction:column;gap:3px;min-width:0}
.jack-row .nm{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 「当前」排在名字之前：名字长度没有上限，跟在后面的东西迟早被省略号吃掉，
   而"现在轮到谁"正是这张单子上最不能丢的一个字。 */
.jack-row .nm .serve{font-size:10px;font-weight:600;color:var(--brand);margin-right:7px;
  padding:1px 5px;border-radius:5px;background:var(--brand-a13)}
.jack-row .st{font-size:10.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jack-row .st.ok{color:var(--ok)} .jack-row .st.warn{color:var(--warn)} .jack-row .st.bad{color:var(--bad)}
.jack-row .st b{font-weight:500;color:var(--dim);font-family:var(--mono);font-size:10.5px}
.jack-meter{display:flex;flex-direction:column;gap:3px;align-items:flex-end}
.jack-bar{width:100%;height:4px;border-radius:2px;background:var(--faint-a22);overflow:hidden}
.jack-bar i{display:block;height:100%;border-radius:2px;background:var(--ok)}
.jack-meter.warn .jack-bar i{background:var(--warn)} .jack-meter.bad .jack-bar i{background:var(--bad)}
.jack-num{font-family:var(--mono);font-size:10.5px;font-variant-numeric:tabular-nums;color:var(--dim);white-space:nowrap}
.jack-meter.warn .jack-num{color:var(--warn)} .jack-meter.bad .jack-num{color:var(--bad)}
/* 订阅号有两口井（5 小时 / 周），所以这一格摞两条。颜色跟着各自那条走：一条
   见底另一条宽裕是常态，用一个颜色概括两条等于把先见底的那条藏起来。 */
.jack-meter.multi{gap:7px}
.jack-w{display:flex;flex-direction:column;gap:3px;align-items:flex-end;width:100%}
.jack-w.warn .jack-bar i{background:var(--warn)} .jack-w.warn .jack-num{color:var(--warn)}
.jack-w.bad .jack-bar i{background:var(--bad)} .jack-w.bad .jack-num{color:var(--bad)}
.jack-foot{display:flex;justify-content:space-between;align-items:center;gap:8px;
  margin-top:8px;padding-top:8px;border-top:1px solid var(--line-soft);font-size:11px;color:var(--faint)}
.jack-foot button{background:none;border:none;padding:0;font:inherit;color:var(--brand-2);cursor:pointer}
.jack-foot button:hover{text-decoration:underline}

/* ---------- 改线 / REPATCH ----------
   顺序就是故障切换顺序，改它本来要开一次编辑抽屉、点四下、保存。但这件事在版面上
   本来就是空间性的——「谁排前面」肉眼可见。所以把它做成拔插：捏起一个插头，接线
   之间的每一段线变成敞开的插孔，松手落进哪个孔，就是排到那个位置。
   落点是线段而不是节点：位置在两个节点「之间」，插孔画在那儿才没有歧义。 */
.hop[draggable="true"]{cursor:grab}
.hop[draggable="true"]:active{cursor:grabbing}
.hop.lifting{opacity:.34;transform:scale(.97)}
.hop:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* 末尾那个插孔平时是零宽的：一段悬在最后一个节点后面、不通向任何地方的线，
   会被读成"链还没完"。它只在拔插时才需要存在——拖到队尾总得有处落。 */
.wire.tail{width:0;margin:0}
.path.patching .wire{width:34px;background:none;height:16px;border-radius:3px;
  border:1px dashed var(--brand-a55);
  background-color:var(--brand-a8);
  transition:width .13s ease,background-color .13s ease}
.path.patching .wire.hot{width:52px;border-style:solid;
  background-color:var(--brand-a26);
  box-shadow:0 0 0 3px var(--brand-a14)}
/* 拔插时每一段线都是敞开的插孔，不再是线：电荷停下，免得插孔看起来还在导通 */
.path.patching .wire.live,.path.patching .wire.charged{animation:none}
.path.saving{opacity:.55;pointer-events:none}
@media (prefers-reduced-motion:reduce){
  .path.patching .wire{transition:none}
  .jack-pop{animation:none}
}

/* tags */
.tag{display:inline-flex;align-items:center;font-size:11px;font-weight:600;letter-spacing:.02em;padding:2px 9px;border-radius:6px;width:fit-content;white-space:nowrap}
.tag-anthropic{color:var(--ac);background:var(--ac-a12)}
.tag-openai{color:var(--oa);background:var(--oa-a12)}
.tag-gemini{color:var(--gm);background:var(--gm-a12)}
.tag-image{color:var(--im);background:var(--im-a12)}
.tag-typesafe{color:var(--ts);background:var(--ts-a12)}
/* 图标版协议标签：宽度只剩一枚图标，原文在 title 里（protoTag）。text 版图标后跟原文。 */
.tag.tag-ic{gap:3px;padding:2px 5px}
.tag.tag-ic svg{width:12px;height:12px;flex:none}
.tag.tag-ic b{font-weight:700;line-height:1}
.tag.tag-ict{gap:5px;padding:2px 9px 2px 6px}
.ptag>svg{width:12px;height:12px;flex:none}
/* 共享档位的饼图：每人份额，独享整圆、双人一半、四人四分之一。 */
.tier-ic{display:inline-flex;align-items:center;justify-content:center;flex:none;width:14px;height:14px;color:var(--dim);vertical-align:-2px}
.tier-ic svg{width:12px;height:12px;display:block}
/* 两层标记：外层协议家族，内层是这个家族里真正能调的 API。斜体那一格是
   桥接——上游不会说，网关在进程里翻译，那是我们的能力不是对方的。 */
.ptag{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;letter-spacing:0;
  padding:2px 5px 2px 9px;border-radius:var(--r-pill);width:fit-content;white-space:nowrap}
.ptag i{font-style:normal;font-weight:500;font-family:var(--mono);font-size:10.5px;
  padding:1px 6px;border-radius:var(--r-pill);background:var(--surface);opacity:.95}
.ptag i.bridged{opacity:.75;border:1px dashed currentColor;padding:0 5px}
.ptag.tag-anthropic{color:var(--ac);background:var(--ac-a12)}
.ptag.tag-openai{color:var(--oa);background:var(--oa-a12)}
.ptag.tag-gemini{color:var(--gm);background:var(--gm-a12)}
.ptag.tag-image{color:var(--im);background:var(--im-a12)}
.ptag.tag-typesafe{color:var(--ts);background:var(--ts-a12)}
.badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:3px 10px;border-radius:999px;white-space:nowrap}
.badge.closed{color:var(--ok);background:var(--ok-a12)}
.badge.open{color:var(--bad);background:var(--bad-a11)}
.badge.half_open{color:var(--warn);background:var(--warn-a12)}
.badge.parked{color:var(--warn);background:var(--warn-a12)}
.badge.off{color:var(--faint);background:var(--faint-a12)}
.badge.on{color:var(--ok);background:var(--ok-a12)}
.badge.expired{color:var(--warn);background:var(--warn-a12)}
.badge.reauth{color:var(--bad);background:var(--bad-a11)}
/* 治理表和账号授权提示用 ok / bad / unknown 三个通用值，以前没有样式，印成无色。 */
.badge.ok{color:var(--ok);background:var(--ok-a12)}
.badge.bad{color:var(--bad);background:var(--bad-a11)}
.badge.unknown{color:var(--faint);background:var(--faint-a12)}
/* In-service switch: the rack breaker for one line. Reads as the state badge it
   replaces, but the miniature track makes it obvious it can be thrown — taking a
   dead upstream out of service is an incident action, not an edit. */
.sw{display:inline-flex;align-items:center;gap:7px;font:inherit;font-size:12px;font-weight:600;
  padding:3px 10px 3px 5px;border-radius:999px;border:1px solid transparent;background:transparent;
  cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}
/* 里面那枚是一只迷你 iOS 开关：开是绿色轨道、关是灰色轨道，白色圆钮弹簧滑过去。
   （圆钮以前引用了一个不存在的 --surface-1，一直是透明的，整只开关读起来像一块色块。） */
.sw i{position:relative;width:26px;height:16px;border-radius:999px;flex:none;transition:background-color .25s var(--ease)}
.sw i::before{content:"";position:absolute;width:12px;height:12px;top:2px;left:2px;border-radius:50%;
  background:#fff;transition:transform .3s var(--spring);box-shadow:0 1px 2px rgba(0,0,0,.25),0 0 0 .5px rgba(0,0,0,.04)}
.sw.on{color:var(--ok);background:var(--ok-a12)}
.sw.on i{background:var(--ok-fill)}
.sw.on i::before{transform:translateX(10px)}
.sw.off{color:var(--dim);background:var(--fill-4)}
.sw.off i{background:var(--fill-1)}
.sw:hover{border-color:color-mix(in srgb,currentColor 35%,transparent)}
.sw:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.sw[disabled]{cursor:progress;opacity:.55}

/* ---- OAuth pairing: a subscription tap, not a static patch cable. The device
   code shows on an LED-style hardware readout; a live signal pathway conveys the
   handshake in progress. All colors derive from the existing rack tokens. ---- */
.oauth-tag{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:11px;font-weight:600;
  color:var(--oa);background:var(--oa-a12);padding:2px 8px;border-radius:999px;white-space:nowrap}
/* ---------- PROVIDERS · 端口铭牌 / PORT NAMEPLATE ----------
   一条上游 = 配线架上的一个端口。铭牌要在不点开任何东西的前提下说清「这是谁」：
   刻字方块（记号认供应商、底色认协议）+ 线名 + 下面一行身份。
   那一行身份对订阅线是账号邮箱，对密钥线是端点地址——同一条规则，两种载荷：
   「什么东西能把这条线和它的同类区分开」。五条 Kiro 之前印着五串一模一样的
   q.us-east-1.amazonaws.com，占着整版最宽的一栏，却没有一个字回答这个问题。 */
.pv-plate{display:grid;grid-template-columns:28px minmax(0,1fr);gap:10px;align-items:start;min-width:0}
/* 掌上把测试 / 编辑 / 删除挂在铭牌下面。操作列在横向滑轨最右端，390px 上等于
   不存在；display:none 的这一排不占格子，桌面仍用右侧那三个图标。 */
.pv-touch-act{display:none}
.pv-mark{width:28px;height:28px;border-radius:9px;display:grid;place-items:center;flex:none;margin-top:1px;
  font-size:13px;line-height:1;color:var(--pc,var(--faint));
  background:var(--surface-3);background:color-mix(in srgb,var(--pc,var(--faint)) 12%,transparent);
  box-shadow:inset 0 0 0 1px var(--line);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--pc,var(--faint)) 22%,transparent)}
.pv-mark svg{width:14px;height:14px}
.pv-id{display:flex;flex-direction:column;gap:3px;min-width:0}
/* 名字行不换行：协议标签不缩，名字截省略号（全名在 title 里）。换行会让这一格四行高。 */
.pv-id-top{display:flex;align-items:center;gap:7px;min-width:0;flex-wrap:nowrap}
.pv-id-top .tag,.pv-id-top .ptag{flex:none}
/* 身份和标签同一行，但**谁让步是定死的**：邮箱 `flex:1 1 auto;min-width:0` 会截断，
   钢印 `flex:none` 永远不动。最早它们挤在一个可换行 flex 里，邮箱长一点就把档位
   挤到第三行，整列像随机错位；接着各占一轨，错位没了，代价是每一行都多一行高。
   两个毛病的根都是「谁让步没定死」——定死之后一行装得下，长邮箱自己收省略号。 */
/* 邮箱收到 10.5px：这一行要装下的是「完整的账号」加一两枚钢印，而截一半的邮箱
   认不出任何东西——同一家的几个号正是靠它分开的。小一号换来的是不截。 */
.pv-who{display:flex;align-items:baseline;gap:7px;min-width:0;
  font-size:10.5px;line-height:1.5}
.pv-who-main{flex:1 1 auto;display:block;min-width:0;overflow:hidden}
/* 钢印（档位 / 重复 / 月费）自己占第三行：和邮箱同一行时，两者只能有一个完整，
   而铭牌格的三行正好一行一件事——名字、邮箱、钢印。空着就收起（密钥线没有钢印）。 */
.pv-labels{flex:none;display:flex;align-items:center;gap:5px;white-space:nowrap;min-width:0;max-width:100%;overflow:hidden}
.pv-labels:empty{display:none}
.pv-mail{font-family:var(--mono);color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  display:block;min-width:0;max-width:100%}
.pv-mail .at{color:var(--faint)}
.pv-who .none{color:var(--faint)}
/* 档位钢印：邮箱之后，第二个能把同一家的几个号分开的东西 */
.pv-plan{flex:none;font-size:10px;font-weight:600;letter-spacing:.03em;padding:1px 6px;border-radius:5px;
  color:var(--dim);background:var(--surface-3);box-shadow:inset 0 0 0 1px var(--line);white-space:nowrap}
/* 走代理的线极少，所以它值得一个记号：这条线出网走的是另一扇门 */
.pv-px{flex:none;font-size:10px;font-weight:600;padding:1px 6px;border-radius:5px;white-space:nowrap;
  color:var(--brand-2);background:var(--brand-dim);box-shadow:inset 0 0 0 1px var(--brand-a30)}
/* Claude 节点的出口由节点自己上报。「直连」要显形：一列里没配代理的节点和
   没上报的节点不能长得一样，那正是要找的那几台。代理和直连测到同一个地址时，
   代理没起作用，用警示色。 */
.pv-px.direct{color:var(--dim);background:var(--surface-3);box-shadow:inset 0 0 0 1px var(--line)}
.pv-px.na{color:var(--faint);background:transparent;box-shadow:inset 0 0 0 1px var(--line-soft)}
.pv-px.warn{color:var(--warn);background:var(--warn-a12);box-shadow:inset 0 0 0 1px var(--warn-a40)}
/* 代理 / 直连只印方向图标：代理是绕行的箭头，直连是直箭头；两种告警仍印字。 */
.pv-px.ic{display:inline-flex;align-items:center;padding:1px 4px}
.pv-px.ic svg{width:11px;height:11px;display:block}
/* 代理地址和「查看 / 复制」同一行：地址截省略号，入口不缩。竖着摞是两行，
   而这一格的预算只有三行（芯片 / 代理 / 绑定）。 */
.pv-proxy-open{display:flex;align-items:baseline;gap:6px;max-width:100%;min-width:0;padding:0;line-height:1.35;
  border:0;background:none;color:var(--dim);font-size:10.5px;cursor:pointer;text-align:left}
.pv-proxy-open .mono{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-proxy-open .action{flex:none;color:var(--brand-2)}
.proxy-copy-row{display:flex;align-items:center;gap:8px}
.proxy-copy-row input{flex:1;min-width:0}
.proxy-copy-row .btn{flex:none}
.proxy-readout{margin:16px 0}
.proxy-readout .field{margin-bottom:16px}
/* ---- 「用途 · 号池」这一列 ---------------------------------------------
   这三枚芯片（用途 / 号池 / 出口）原本挂在铭牌的账号行后面，和邮箱抢同一行的宽度。
   两个后果：一是最后进场的那枚被挤没了形——`.ap-cellbtn` 没有 flex 下限，于是
   「+ 入池」被压成一个竖着排字的圆点；二是更贵的那个，**用途钢印在每一行的横坐标
   都不一样**，因为它跟在长短不一的邮箱后面。一整列没法竖着扫，而这一页最要紧的
   那句话——哪几个号 API 和拼车都够得着（2026-08-16 在 923 上真出过事）——恰恰只有
   竖着扫才看得见。

   所以拆成一列。铭牌只答「这是谁」（找行时读），这一列只答「它怎么接进产品」
   （决定流量怎么走时读）。两个问题不在同一个时刻问，就不该在同一格里挤。
   列名照抄上面筛选轨的两组芯片：那两组筛的正是这一列。

   左边那根 2px 的线是这一格的读法：颜色 = 用途，和用途筛片同一套色。芯片的底色
   在 10px 上扫不出来，一根定在同一个横坐标上的竖线可以——灰的是常态，有颜色的
   才要停下来看。 */
/* `minmax(0,1fr)` 不是装饰：默认的 auto 轨道按最宽的那枚芯片撑开，一个长池名
   就能把整格顶到隔壁「额度余量」上去。有了下限 0，芯片才收得回来、`.t` 的省略号
   才轮得到出场。 */
.pv-wire{display:grid;grid-template-columns:minmax(0,1fr);gap:4px;justify-items:start;
  min-width:0;padding-left:9px;position:relative}
.pv-wire::before{content:"";position:absolute;left:0;top:1px;bottom:1px;width:2px;border-radius:2px;
  background:var(--wc,var(--line))}
.pv-wire>*{max-width:100%}
/* 号池和出口并排站一行：两者都是「它接在哪儿」，而用途是「它给谁干活」，
   分两行正好把这一格分成两句话。挤不下时代理自己掉下去，不拖着号池一起换行。 */
.pv-riders{display:flex;flex-direction:column;gap:3px;min-width:0;max-width:100%;font-size:12px;margin-top:1px}
.pv-rider{display:flex;align-items:baseline;gap:4px;min-width:0;max-width:100%;line-height:1.35}
.pv-rider-prefix{color:var(--dim);font-size:11px;flex:none;user-select:none}
.pv-rider-primary{flex:0 1 auto;min-width:0;font-weight:500;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-rider-sub{flex:1 1 30px;min-width:0;font-size:10.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-riders-empty{font-size:11px;color:var(--faint)}
/* 多个绑定并成一行，只印名字：名字已经认得出人，四个人的邮箱一行装不下。
   整行截省略号，全名单在 title 里。 */
.pv-rider.many{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-rider.many .pv-rider-primary{display:inline;overflow:visible}
.pv-rider-sep{color:var(--faint);margin:0 4px}
.pv-rider-lend{font-style:normal;font-size:10px;color:var(--warn);margin-left:2px}
/* 芯片行不换行：长的两枚（用途、号池）收省略号，短的（节点 / 出口）不缩。换行会把
   这一格顶到四行，而整张表的行高就是最高那一格定的。 */
.pv-wire-r{display:flex;align-items:center;gap:5px;flex-wrap:nowrap;min-width:0;max-width:100%}
.pv-wire-r .pv-car,.pv-wire-r .pv-cross{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
.pv-wire-r .ap-cellbtn{flex:0 1 auto;min-width:0}
/* 纯 API 不盖钢印——那是一条上游的默认身份，每行都盖等于没盖。但一列如果留白，
   「默认」和「没数据」就分不开了，所以它在这里出现，只是不带底色：认得出，不喊。 */
.pv-api{font-size:10px;font-weight:600;letter-spacing:.06em;color:var(--faint);white-space:nowrap}
.pv-api.na{letter-spacing:0}
/* 用途钢印：拼车那枚印的是车名——骑手买的就是那辆车；交叉那枚印的是交叉本身
   （「API ↔ 四人车」），因为这一格要回答的不是「它属于谁」而是「哪两门生意
   在这个号上撞了」。 */
.pv-car{flex:none;font-size:10px;font-weight:600;padding:1px 6px;border-radius:5px;white-space:nowrap;
  color:var(--brand-2);background:var(--brand-dim);box-shadow:inset 0 0 0 1px var(--brand-a30)}
.pv-car.alt{color:var(--dim);background:var(--surface-3);box-shadow:inset 0 0 0 1px var(--line)}
.pv-cross{flex:none;font-size:10px;font-weight:600;padding:1px 6px;border-radius:5px;white-space:nowrap;
  color:var(--warn);background:color-mix(in srgb,var(--warn) 12%,transparent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--warn) 30%,transparent)}
/* ---- 供应商页的号池分组 ------------------------------------------------
   分组是一次真划分：一个账号只属于一个号池，所以每一行恰好落在一个组里，
   最后那一组「未入池」把余数收干净。组头本身就是这个池的操作台。 */
tr.ap-grouphd>td{background:var(--brand-a5);border-top:1px solid var(--brand-a45);
  padding:9px 14px}
tr.ap-grouphd.loose>td{background:var(--surface-2);border-top:1px solid var(--line)}
/* 档位分组的组头用中性底：它只是把表切开，不像号池组头那样是一个可编辑的对象。 */
tr.ap-grouphd.pv-tierhd>td{background:var(--surface-2);border-top:1px solid var(--line)}
.pv-tier-ic{display:inline-flex;color:var(--dim)}
.pv-tier-ic svg{width:14px;height:14px}
button.ap-tag{cursor:pointer;font:inherit;font-size:11px}
.ap-gh{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.ap-gh .nm{font-family:var(--disp);font-size:13px;font-weight:600}
.ap-gh .n{font-family:var(--mono);font-size:11px;color:var(--dim)}
.ap-gh .right{margin-left:auto;display:flex;gap:7px;align-items:center}
.ap-tag{display:inline-flex;align-items:center;gap:5px;font-size:11px;padding:1px 8px;
  border-radius:6px;background:var(--surface-3);color:var(--dim);border:1px solid var(--line-soft)}
.ap-tag.on{background:var(--brand-a12);color:var(--brand-2);border-color:var(--brand-a45)}
.ap-tag.warn{background:var(--warn-a12);color:var(--warn);border-color:var(--warn-a30)}
/* 「号池」那一格本身就是操作：点开换池，不必进抽屉。
   `flex:none` + `nowrap` 是这一枚上花了最久才找到的两个字：它以前既没有伸缩下限
   也不禁止换行，所以在一行芯片里它总是第一个被压垮的——压到比「入池」两个字还窄，
   浏览器就把它们竖着排，一枚胶囊渲染成一个圆点。要截也该截池名，不该截控件本身。 */
.ap-cellbtn{display:inline-flex;align-items:center;gap:5px;font:inherit;font-size:11.5px;
  padding:2px 9px;border-radius:999px;background:var(--brand-a12);color:var(--brand-2);
  border:1px solid var(--brand-a45);cursor:pointer;max-width:100%;flex:none;white-space:nowrap}
/* 池名长了就在名字里打省略号（全名在 title 里），胶囊的形状不参与让步 */
.ap-cellbtn .t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.ap-cellbtn .k{flex:none}
.ap-cellbtn.none{background:transparent;color:var(--faint);border:1px dashed var(--line)}
.ap-cellbtn:hover{border-color:var(--brand)}
.ap-cellbtn:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* 建池 / 调成员的抽屉：左候选、右池内，点一下就过去。 */
.ap-xfer{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.ap-xcol{border:1px solid var(--line);border-radius:10px;background:var(--surface-2);overflow:hidden}
.ap-xh{padding:7px 11px;font-size:11px;color:var(--faint);border-bottom:1px solid var(--line-soft);
  display:flex;justify-content:space-between;gap:8px}
.ap-xl{padding:7px;display:flex;flex-direction:column;gap:4px;max-height:max(260px,46dvh);overflow:auto}
.ap-mem{display:flex;align-items:center;gap:7px;padding:5px 8px;border-radius:7px;
  background:var(--surface);border:1px solid var(--line-soft);font-family:var(--mono);
  font-size:11.5px;cursor:pointer;text-align:left;color:var(--text);width:100%}
.ap-mem:hover{border-color:var(--brand-a45)}
.ap-mem[disabled]{cursor:not-allowed;opacity:.5}
.ap-mem .tail{margin-left:auto;font-size:10.5px;color:var(--faint);font-family:var(--sans)}
.ap-mem .tail.taken{color:var(--warn)}
.ap-xempty{padding:14px 8px;text-align:center;font-size:11.5px;color:var(--faint)}
.ap-xempty a{color:var(--text);text-decoration:underline}
.ap-xl .cj-h{font-size:10.5px;color:var(--faint);padding:6px 8px 2px;letter-spacing:.02em}
.ap-xl .cj-h:not(:first-child){margin-top:4px;border-top:1px solid var(--line-soft);padding-top:8px}
.ap-mem .ap-mark .tier-ic{display:inline-flex;vertical-align:middle}
@media (max-width:820px){.ap-xfer{grid-template-columns:1fr}}
/* 三条分流策略折起来：十个池里九个跟随全局，摊开是三段产品课压在名单底下。
   折叠条上印着当前取值，折着也看得见有没有人改过。 */
.ap-fold{margin:4px 0 14px;border:1px solid var(--line-soft);border-radius:10px;background:var(--surface-2)}
.ap-fold>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:10px;padding:10px 12px;
  font-size:12px;font-weight:600;color:var(--dim)}
.ap-fold>summary::-webkit-details-marker{display:none}
.ap-fold>summary .v{margin-left:auto;font-weight:500;font-size:11.5px;color:var(--faint);text-align:right;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ap-fold>summary .v b{font-weight:600;color:var(--brand-2)}
.ap-fold>summary::after{content:"▾";flex:none;font-size:10px;color:var(--faint);transition:transform .16s}
.ap-fold[open]>summary::after{transform:rotate(180deg)}
.ap-fold>summary:hover{color:var(--text)}
.ap-fold>summary:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.ap-fold>.ap-foldbody{padding:4px 12px 4px;border-top:1px solid var(--line-soft)}
.ap-fold>.ap-foldbody .field{margin:12px 0 4px}

/* 凭证：这条线拿什么进门，此刻进不进得去。和「线路」是两件事——令牌有效的线
   照样可能正在熔断，所以两栏各说各的，谁也不替谁下结论。 */
.pv-cred{display:flex;flex-direction:column;gap:4px;align-items:flex-start;min-width:118px}
.pv-cred-kind{flex:1 1 auto;min-width:0;font-size:11px;color:var(--faint);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.pv-cred-kind .mono{font-size:10.5px;color:var(--dim)}

/* 余量：一条额度窗口一根量条，一根都不省——周额度宽裕的号照样可能撞上 Fable 的
   模型墙，把它折进绿灯里正是这一格要消灭的含混。
   条子给足宽度：42px 的轨只有「有色 / 没色」两档，读不出刻度，而「谁快见底」
   恰好是这一栏唯一要回答的事。 */
.pv-meters{display:flex;flex-direction:column;gap:5px;min-width:0}
/* 四列定宽是为了上下行的数字对齐（见下）。收窄时的让步顺序也定死在这里，按
   「这一列没了还答不答得上话」排：
     窗口名   —— 先让。三行的次序永远是 5 小时 / 周 / 限定，截了还认得出，全名在
                 title 里；所以它是 minmax(0,…)，可以一路收到 0。
     量条     —— 有硬下限。它就是这一格存在的理由，收成一截色块等于这一列白开。
     读数和几时回来 —— 不让。它们是答案，截掉「重置 / 见底」那两个字，剩下的
                 数字就又变回一个不知道该不该等的东西。 */
.pv-m{display:grid;grid-template-columns:minmax(0,92px) minmax(56px,1fr) 84px 64px 96px;align-items:center;gap:10px;
  font-size:11px;color:var(--dim);font-variant-numeric:tabular-nums}
.pv-m .ml{color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-m.scoped .ml::before{content:"◇ ";color:var(--brand-2)}
.pv-m.scoped{opacity:.78}
.pv-m .mb{position:relative;height:5px;max-width:220px;border-radius:3px;background:var(--surface-3);overflow:hidden;
  box-shadow:inset 0 0 0 1px var(--line-a70)}
.pv-m .mb i{display:block;height:100%;width:var(--left);border-radius:3px;background:var(--ok);transition:width .5s ease}
.pv-m .mn{font-family:var(--mono);font-size:10.5px;white-space:nowrap;color:var(--dim);text-align:right}
.pv-m.warn .mb i{background:var(--warn)} .pv-m.warn .mn,.pv-m.warn .ml{color:var(--warn)}
.pv-m.out .mb i{background:var(--bad)} .pv-m.out .mn,.pv-m.out .ml{color:var(--bad)}
/* 这一格经本平台跑了多少 token（输入 + 输出），斜杠后面是按百分比推出来的满额。
   满额是拿来横着比的数：同档位的号应该差不多，所以它和左边的余量一样右对齐。
   偏低的整格换成警示色，原因在 title 里。 */
.pv-m .mtk{font-family:var(--mono);font-size:10.5px;white-space:nowrap;color:var(--dim);text-align:right;
  overflow:hidden;text-overflow:ellipsis;cursor:help}
.pv-m .mtk .mtk-f{color:var(--faint)}
.pv-m .mtk.low,.pv-m .mtk.low .mtk-f{color:var(--warn)}
/* 重置时刻：一根条子一句，跟着它自己的窗口走。以前整格只说最早的那一个——可
   「最早」通常是 5 小时窗口，而卡住人的往往是那条要等到周四的周额度，于是这一
   格答的正好不是要问的那件事。四列定宽是为了上下行的数字对齐：两个右缘参差的
   百分比，人会先去比长度，再发现比错了。 */
.pv-mnote{font-size:10.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  text-align:right;font-variant-numeric:tabular-nums}
.pv-mnote.dry{color:var(--bad)}
/* 读不到就是读不到：一根虚轨，不冒充 0 也不冒充满 */
.pv-mnone{display:grid;grid-template-columns:minmax(0,92px) minmax(56px,1fr);align-items:center;gap:10px;
  font-size:11px;color:var(--faint)}
.pv-mnone i{height:5px;max-width:220px;border-radius:3px;grid-column:2;
  background:repeating-linear-gradient(90deg,var(--faint-a34) 0 3px,transparent 3px 7px)}
.pv-mnone b{grid-column:1;font-weight:400;white-space:nowrap}
/* 压根没有额度这回事的线（密钥线按量计费）：一格静默的横杠，不是「读不到」 */
.pv-mna{color:var(--faint);opacity:.45;font-size:12px}
.readout{background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:18px 16px;text-align:center;
  box-shadow:inset 0 1px 3px var(--black-a22)}
.readout .rd-lbl{font-size:11px;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);margin-bottom:9px}
.readout .rd-code{font-family:var(--mono);font-size:30px;font-weight:700;letter-spacing:.02em;color:var(--brand-2);
  text-shadow:0 0 18px var(--brand-a40);word-break:break-all;line-height:1.15}
.readout .rd-sub{font-size:12px;color:var(--dim);margin-top:10px}
.pair-flow{display:flex;align-items:center;justify-content:center;gap:9px;margin:16px 0 4px}
.pair-flow .hop{padding:6px 12px}
.pair-note{font-size:12.5px;color:var(--dim);line-height:1.55}
/* Codex has no pairing code to read out, so its wizard anchors on the three
   steps instead — same readout frame, numbered rungs in place of the digits. */
.rd-steps{display:flex;flex-direction:column;gap:9px;text-align:left;counter-reset:rdstep}
.rd-steps li{display:flex;align-items:baseline;gap:10px;font-size:13px;color:var(--dim);list-style:none}
.rd-steps li::before{counter-increment:rdstep;content:counter(rdstep);flex:none;width:20px;height:20px;
  border-radius:50%;border:1px solid var(--line);font-family:var(--mono);font-size:11px;font-weight:700;
  color:var(--brand-2);display:flex;align-items:center;justify-content:center;transform:translateY(2px)}
.rd-steps li b{color:var(--text);font-weight:600}
/* ---- 授权链接读出屏 / AUTH LINK READOUT ----------------------------------
   配对码一直是"物"——30px 等宽、能念出来、能对着屏幕核对。链接却藏在 hidden
   input 里，只有两个按钮露在外面，其中"打开"还是实心主按钮。于是这一屏读起
   来就是"点这里打开浏览器"，而对同时管着十几个订阅号的人来说，当前浏览器登
   录的往往并不是要接的那个号——跨浏览器才是常态，不是退路。
   所以链接也进读出屏：可见、可选中、可整条复制；下面两个动作**等权**，各自
   带一句"什么时候用它"。两条并列的路，而不是主按钮加一个次要出口。 ---- */
.lnk{width:100%;font-family:var(--mono);font-size:12px;color:var(--brand-2);background:var(--surface-3);
  border:1px solid var(--line);border-radius:9px;padding:10px 11px;text-align:left;cursor:text}
.lnk:focus{border-color:var(--brand-a55);
  box-shadow:0 0 0 3px var(--brand-a16)}
/* 按钮和它的说明是一个单元——窄屏堆叠时必须一起走，否则两句说明会挤在两个
   按钮下面，读者没法知道哪句配哪个。所以按单元建网格，而不是按行。 */
.lnk-acts{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px}
.lnk-act{display:flex;flex-direction:column;gap:7px}
.lnk-act .btn{width:100%}
.lnk-act span{font-size:11.5px;line-height:1.45;color:var(--faint);text-align:center}
.rd-div{height:1px;background:var(--line);margin:16px -16px 14px}
@media (max-width:480px){
  .lnk-acts{grid-template-columns:1fr;gap:13px}
  .lnk-act span{text-align:left}
}

/* ---- 代理粘贴回执与候选选择器 / PROXY PASTE RECEIPT + PICKER --------------
   粘贴解析是这一屏唯一"有魔法"的动作，而魔法的风险是用户不知道刚才发生了
   什么。所以每次解析都留一张回执：认出的是哪一条、填成了什么；并且一键可
   撤销回粘贴前的值。有回执、可撤销的魔法才是好魔法。 ---- */
.px-lib{margin:0 0 8px}
.px-lib:empty{display:none}
.px-libsel{width:100%;min-width:0}
.px-rcpt{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-top:8px;font-size:12px;color:var(--ok)}
.px-rcpt .v{font-family:var(--mono);color:var(--dim)}
.px-rcpt button{background:none;border:0;padding:0;color:var(--faint);font-size:12px;
  text-decoration:underline;text-underline-offset:2px;cursor:pointer}
.px-rcpt button:hover{color:var(--text)}
.px-pick{margin-top:9px;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--surface-3)}
.px-pick .hd{display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:8px 11px;font-size:12px;color:var(--dim);border-bottom:1px solid var(--line-soft)}
.px-pick .hd button{background:none;border:0;color:var(--faint);font-size:16px;line-height:1;cursor:pointer;padding:0 2px}
.px-pick .hd button:hover{color:var(--text)}
.px-opt{display:flex;align-items:center;gap:10px;width:100%;padding:9px 11px;background:none;border:0;
  border-top:1px solid var(--line-soft);text-align:left;cursor:pointer;transition:background .12s}
.px-opt:first-of-type{border-top:0}
.px-opt:hover{background:var(--brand-dim)}
.px-opt:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.px-opt .nm{flex:0 0 auto;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:12.5px;font-weight:600;color:var(--text)}
.px-opt .ad{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:var(--mono);font-size:11.5px;color:var(--dim)}
.px-opt .tg{flex:none;font-size:11px;padding:2px 7px;border-radius:5px;background:var(--surface);color:var(--faint)}
.px-opt.no{cursor:not-allowed;opacity:.55}
.px-opt.no:hover{background:none}
.px-opt.no .tg{color:var(--bad)}

/* ---- 出口占用读数 / SHARED EXIT OCCUPANCY --------------------------------
   一条出口上已经挂着谁。灯 + 地址 + 名单三段，读法沿用「池详单」那张摊平
   的号单（.jack-row）——同一件事在两个地方长得不一样，是要人重新学一遍。
   琥珀取自确认小牌（.cfm h4 .lamp）：这是提醒，不是拦截，红色留给不可逆。
   名字前那颗灯按协议着色，因为「跟谁共用」这件事里，是不是同一家上游，比
   共用本身更要紧——两个 Claude 号挤一个出口，和一个 Claude 一个 Codex 挤
   一个出口，风险差着一个量级，而颜色一眼就说完了。 ---- */
.px-dup{margin-top:9px;border:1px solid var(--warn-a30);border-radius:10px;overflow:hidden;
  background:var(--warn-a5);animation:jackin .16s cubic-bezier(.2,.8,.3,1)}
.px-dup .hd{display:flex;align-items:center;gap:8px;padding:8px 11px;font-size:12px;font-weight:600;color:var(--warn)}
.px-dup .hd .lamp{background:var(--warn);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--warn-ahalo)}
.px-dup .hd .ad{margin-left:auto;min-width:0;font-family:var(--mono);font-size:11px;font-weight:500;
  color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.px-dup .ln{display:flex;align-items:center;gap:9px;padding:7px 11px;border-top:1px solid var(--warn-a20)}
.px-dup .ln .lamp{width:8px;height:8px;background:var(--pc,var(--faint))}
.px-dup .ln .nm{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--text);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.px-dup .ln .tg{flex:none;font-size:11px;padding:2px 7px;border-radius:5px;background:var(--surface);color:var(--faint)}
.px-dup .ln .tg.off{opacity:.62}
.px-dup .ln.more{font-size:11.5px;color:var(--faint);padding-left:28px}
.px-dup .why{padding:8px 11px;border-top:1px solid var(--warn-a20);font-size:11.5px;line-height:1.55;color:var(--dim)}
.px-dup .why+.why{border-top-color:var(--line-soft);color:var(--faint)}
/* 弱档：同一台机器、另一组凭证。住宅代理按用户名分会话是常态，点亮琥珀会
   让这块屏天天喊狼来了——所以它只是一段灰字，连边框都退到普通面板色。 */
.px-dup.soft{border-color:var(--line);background:var(--surface-3)}
.px-dup.soft .hd{color:var(--dim)}
.px-dup.soft .hd .lamp{background:var(--faint);box-shadow:none}
.px-dup.soft .why{border-top:0;padding-top:0;color:var(--faint)}
@media (prefers-reduced-motion:reduce){.px-dup{animation:none}}

.pair-done{display:flex;flex-direction:column;align-items:center;gap:10px;padding:8px 0 4px;text-align:center}
.pair-done .chk{width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  color:#fff;background:var(--ok);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--ok-ahalo)}
.tok-line{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--dim);
  background:var(--surface-3);border:1px solid var(--line);border-radius:9px;padding:9px 12px}

/* ---- protocol ports: a provider is one credential feeding N peer protocol
   jacks. Each port is a card tinted by its protocol accent (--pc), with a lamp,
   the protocol selector, and its own Base URL — no privileged "primary". ---- */
.ports-head{display:flex;align-items:center;justify-content:space-between}
.ports{display:flex;flex-direction:column;gap:10px}
.port{position:relative;background:var(--surface-3);border:1px solid var(--line);
  border-left:3px solid var(--line);border-left:3px solid color-mix(in srgb,var(--pc,var(--faint)) 80%,var(--line));border-radius:11px;padding:11px 12px}
.port-top{display:flex;align-items:center;gap:9px;margin-bottom:9px}
.port-top .lamp{width:9px;height:9px;background:var(--pc,var(--faint));
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) color-mix(in srgb,var(--pc,var(--faint)) var(--halo-alpha),transparent)}
.port-proto{flex:1;font-weight:600;color:var(--text)}
.port-x{width:28px;height:28px;flex:none;border-radius:7px;background:transparent;border:1px solid transparent;
  color:var(--faint);font-size:13px;transition:.14s}
.port-x:hover{color:var(--bad);border-color:var(--bad-a40);background:var(--bad-a9)}
.port input{margin-bottom:7px}
.port input:last-child{margin-bottom:0}
.port .pp{font-size:12.5px;color:var(--dim)}
/* 端口下的格式勾选。只有 openai 会出现——它一个协议名下面是两种线协议。 */
.port-fmts{margin-top:9px;padding-top:9px;border-top:1px dashed var(--line)}
.pf-head{display:flex;align-items:center;gap:7px;font-size:11.5px;font-weight:600;color:var(--dim);margin-bottom:7px}
.pf-auto,.pf-manual{font-size:10.5px;font-weight:600;padding:1px 7px;border-radius:5px}
.pf-auto{color:var(--dim);background:var(--surface)}
.pf-manual{color:var(--warn);background:var(--warn-a12)}
/* `.field label` 是块级 + 全大写的表单标签样式，这些格子是 label 但不是标签，
   所以用同等特异性的选择器（且写在后面）把它按回行内芯片。 */
.port .pf-chip{display:inline-flex;align-items:center;gap:7px;margin:0 7px 6px 0;padding:5px 10px;cursor:pointer;
  text-transform:none;letter-spacing:normal;border:1px solid var(--line);border-radius:9px;
  background:var(--surface);transition:.14s}
.port .pf-chip.on{border-color:color-mix(in srgb,var(--pc,var(--faint)) 55%,var(--line));background:var(--surface-2)}
.port .pf-chip input{width:14px;height:14px;margin:0;flex:none}
.port .pf-chip b{font-size:12px;font-weight:600;color:var(--text)}
.port .pf-chip .mono{font-size:11px;color:var(--faint)}

/* ---- 出站代理输入组 / PROXY FIELD GROUP ----------------------------------
   一条代理 URL 拆成五个格子。手拼 URL 的失败模式很单一也很贵：代理密码里
   出现 / # ? 空格就整条解析不出来，percent 编码漏一次就是一条连不上的线，
   而症状要到第一次真实调用才现形。分栏后编码归前端做，操作员只填自己知道的。
   两行：协议 / 主机 / 端口，然后 用户名 / 密码。 ---- */
.pxrow{display:flex;gap:8px;align-items:center}
.pxrow+.pxrow{margin-top:8px}
.pxrow>*{min-width:0}
.px-proto{flex:0 0 112px;font-weight:600}
.px-host{flex:1 1 auto}
.px-port{flex:0 0 96px}
.px-user{flex:1 1 0}
.px-pw{flex:1 1 0;position:relative;display:flex}
.px-pw input{width:100%;padding-right:54px}
.px-eye{position:absolute;right:5px;top:50%;transform:translateY(-50%);padding:5px 8px;border-radius:7px;
  background:transparent;border:0;color:var(--faint);font-size:12px;cursor:pointer;transition:color .14s,background .14s}
.px-eye:hover{color:var(--text);background:var(--text-a8)}
/* 窄屏：最长的值（主机）独占一行，协议和端口并排。gap 同时是行距，不另加 margin。 */
@media (max-width:520px){
  .pxrow{flex-wrap:wrap}
  .px-host{flex:0 0 100%;order:-1}
  .px-proto{flex:1 1 auto}
  .px-port{flex:0 0 116px}
  .px-user,.px-pw{flex:0 0 100%}
}

/* cards grid */
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:14px}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);padding:16px;box-shadow:var(--card-shadow)}
.card .top{display:flex;align-items:center;justify-content:space-between;gap:8px}
.card .nm{font-family:var(--disp);font-weight:600;font-size:15px}
.card .url{font-family:var(--mono);font-size:11.5px;color:var(--dim);margin-top:9px;word-break:break-all}
/* 事故时钟：只有不通的线才有这一行，所以它一出现就是要读的 */
.card-when{margin-top:9px;font-size:11.5px;color:var(--bad);font-variant-numeric:tabular-nums}
.card .stats{display:flex;gap:16px;margin-top:13px;font-size:12.5px;color:var(--dim)}
.card .stats b{color:var(--text);font-weight:600;font-family:var(--mono)}

/* table */
/* ---- 模型目录：配线架上的价目板 ------------------------------------------
   这一页要同时回答两个问题——「调什么、多少钱」和「谁在跑、赚不赚」。所以它不是
   一张 CRUD 表：标识符、价格、流量三者各有自己的排版语言。

   · 标识符走等宽，是这一行的锚点；
   · 所有数字 tabular-nums 且不折行，右对齐成一把尺——比价靠的是小数点对齐，
     不是读数；
   · 官方价一条泳道，不再和已退役的售价 / 成本并列互抢注意力；
   · 厂商与上下文窗口折进模型格当微标签，省下的两列宽度还给价格与流量。 */
.cat-rail{display:flex;flex-wrap:wrap;align-items:center;gap:0 18px;margin:0 0 14px;font-size:12px;color:var(--dim)}
.cat-rail b{font-family:var(--mono);color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.cat-rail span+span{padding-left:18px;border-left:1px solid var(--line)}
.cat-bar{display:flex;flex-wrap:wrap;gap:9px;align-items:center;margin:0 0 11px}
.cat-search{flex:1 1 220px;min-width:170px;position:relative}
.cat-search input{width:100%;padding:8px 12px 8px 31px;border-radius:9px;border:1px solid var(--line);
  background:var(--surface-2);color:var(--text);font-size:13px;font-family:inherit}
.cat-search input:focus{outline:none;border-color:var(--brand-55-on-line)}
.cat-search::before{content:'';position:absolute;left:11px;top:50%;width:12px;height:12px;transform:translateY(-50%);
  border:1.6px solid var(--faint);border-radius:50%;box-shadow:5px 5px 0 -4.2px var(--faint)}
.chips{display:flex;flex-wrap:wrap;gap:5px}
.chip{padding:4px 10px;border-radius:999px;border:1px solid var(--line);background:transparent;
  color:var(--dim);font-size:11.5px;cursor:pointer;white-space:nowrap;font-family:inherit}
.chip:hover{color:var(--text);border-color:var(--brand-40-on-line)}
.chip.on{color:var(--text);background:var(--brand-a15);
  border-color:var(--brand-50-on-line)}
.chip .n{color:var(--faint);margin-left:5px;font-family:var(--mono);font-size:10.5px}
/* 发券台（增长 → 优惠券）：工具条一行、状态芯片一行、下面一张表。按人视图一人一行，
   点行展开到每一张券（.cd-sub）。 */
.cd-head{padding:14px 16px 12px;display:grid;gap:10px}
.cd-tools{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.cd-tools input[type=search]{flex:1 1 220px;min-width:0;width:auto;padding:7px 12px;border-radius:var(--r-pill)}
.cd-tools .seg{margin-left:auto}
.cd-tools .seg button{flex:none;padding:6px 12px}
.cd-user{display:inline-flex;align-items:center;gap:4px;padding:4px 6px 4px 12px;border-radius:var(--r-pill);background:var(--brand-a15);font-size:12.5px}
.cd-user button{border:0;background:transparent;color:var(--dim);cursor:pointer;font-size:15px;line-height:1;padding:2px 6px;border-radius:var(--r-pill);font-family:inherit}
.cd-user button:hover{color:var(--text)}
.cd-who{display:inline-grid;line-height:1.3;vertical-align:middle}
.cd-who small{color:var(--faint);font-size:11.5px}
.cd-row{cursor:pointer}
.cd-row .cd-car{display:inline-block;width:1em;color:var(--faint);transition:transform .15s;margin-right:4px;vertical-align:middle}
.cd-row[aria-expanded="true"] .cd-car{transform:rotate(90deg)}
.cd-sub td{background:var(--brand-a5);padding-top:7px;padding-bottom:7px;font-size:13px}
.cd-sub td:first-child{padding-left:38px}
.cd-soon{color:var(--warn)}
.cd-num{background:transparent;border:0;padding:0;font:inherit;font-weight:600;color:var(--text);cursor:pointer;
  text-decoration:underline;text-decoration-color:var(--line);text-underline-offset:3px}
.cd-num:hover{color:var(--brand-2);text-decoration-color:currentColor}
.cd-num:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.cd-foot{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;color:var(--faint);font-size:12px;padding:10px 16px}
@media (prefers-reduced-motion:reduce){.cd-row .cd-car{transition:none}}
.cat-sort{flex:0 0 auto;width:auto;padding:8px 30px 8px 13px;border-radius:var(--r-pill);border:1px solid transparent;
  background-color:var(--fill-4);color:var(--text);font-size:12.5px;font-family:inherit}
.cat-count{font-size:11.5px;color:var(--faint);margin-left:auto;font-family:var(--mono)}

#acat-tbl{table-layout:fixed;width:100%}
#acat-tbl thead th{position:sticky;top:0;z-index:2;background:var(--surface);
  font-size:11px;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);font-weight:600;
  padding:9px 14px;border-bottom:1px solid var(--line);white-space:nowrap}
#acat-tbl td{padding:9px 14px;border-bottom:1px solid var(--line-soft);vertical-align:middle}
#acat-tbl tbody tr:last-child td{border-bottom:none}
#acat-tbl tbody tr{transition:background .12s}
#acat-tbl tbody tr:hover{background:var(--brand-a5)}
#acat-tbl th.r,#acat-tbl td.r{text-align:right}
#acat-tbl td.fig{font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12.5px}
/* 状态灯做成左轨：一行的健康度先于它的数字被看到，且不占一列宽度 */
#acat-tbl td.who{border-left:2px solid transparent;position:relative}
#acat-tbl tr.st-healthy td.who{border-left-color:var(--ok)}
#acat-tbl tr.st-degraded td.who{border-left-color:var(--warn)}
#acat-tbl tr.st-down td.who{border-left-color:var(--bad)}
#acat-tbl tr.st-unverified td.who{border-left-color:var(--line)}
.acat-id{font-family:var(--mono);font-size:13.5px;font-weight:600;letter-spacing:-.01em;cursor:pointer;
  color:var(--text);display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}
.acat-id:hover{color:var(--brand-2)}
/* 有简介和没简介的行等高，否则整页的节奏会跟着上游文案的有无起伏 */
.idcell{min-height:46px;display:flex;flex-direction:column;justify-content:center}
.acat-desc{font-size:11.5px;color:var(--dim);margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:440px}
.acat-tags{display:flex;align-items:center;gap:7px;margin-top:5px;flex-wrap:wrap}
.acat-vendor{font-size:11px;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);font-weight:600}
.acat-ctx{font-family:var(--mono);font-size:10.5px;color:var(--dim);border:1px solid var(--line);
  border-radius:5px;padding:1px 5px;line-height:1.5}
.acat-hidden{font-size:10px;color:var(--warn);border:1px solid var(--warn-a40);
  border-radius:5px;padding:1px 5px}
/* 重名：不是错误，是「客户会看见两行长得一样」。用点线区别于隐藏那种实边框——
   隐藏是已经做出的决定，重名是还等着人做决定。 */
.acat-twin{font-size:10px;color:var(--dim);border:1px dashed var(--line);
  border-radius:5px;padding:1px 5px;cursor:help}
/* 门户范围条：这一页看得见全部，客户看得见的是它的子集。差额印在页顶而不是
   逐行找，因为「少了什么」是一个总数问题——挨行看隐藏标记回答不了。 */
.cat-scope{display:flex;align-items:center;gap:11px;flex-wrap:wrap;margin-bottom:12px;
  padding:9px 13px;border:1px solid var(--line);border-radius:9px;font-size:12px;color:var(--dim)}
.cat-scope b{color:var(--text);font-weight:600}
.cat-scope .off,.cat-scope .off b{color:var(--warn)}
/* 分隔点：三段说的是三件事（范围 / 差额 / 影响面），挤在一行里没有边界就读成一句话 */
.cat-scope>span+span::before{content:'·';color:var(--faint);margin-right:11px}
.cat-scope .btn{margin-left:auto;padding:4px 11px;font-size:11.5px}
.scope-pick{display:flex;flex-direction:column;gap:2px;margin:10px 0 0}
.scope-pick label{display:flex;align-items:center;gap:9px;padding:7px 2px;font-size:13px;
  border-bottom:1px solid var(--line);cursor:pointer}
.scope-pick label:last-child{border-bottom:0}
.scope-pick input{width:auto;margin:0}
.scope-pick .n{margin-left:auto;color:var(--faint);font-size:11.5px;font-family:var(--mono)}
/* 官方价：入价在前、出价在后，币种符号只写一次。从前这里是「售价在上、成本在下」
   两行，两条泳道随人民币计量一起退役，剩下的一条就不必再分层了。 */
.px-sell{font-size:13px;color:var(--text);font-weight:600}
.px-sell i{font-style:normal;color:var(--faint);font-weight:400;padding:0 3px}
.px-sell .cur{font-size:11px;color:var(--faint);font-weight:400;margin-right:1px}
.px-out{font-weight:400;color:var(--dim)}
.px-none{color:var(--faint);font-size:12px;font-family:var(--sans)}
/* 电流条：一行的 30 天流量占比。对数刻度——0 次和 1 次的差别（这条线到底通不通
   电）比 200 次和 400 次的差别更值得占像素，泳道流量那边同理。 */
.ampwrap{display:flex;align-items:center;gap:11px}
.amp{flex:1;min-width:40px;height:4px;border-radius:2px;background:var(--line-soft);overflow:hidden}
.amp>span{display:block;height:100%;background:linear-gradient(90deg,var(--brand-a55),var(--brand));border-radius:2px}
.ampn{flex:none;width:52px;text-align:right;font-family:var(--mono);font-size:12.5px;font-variant-numeric:tabular-nums}
.cat-legend{display:flex;flex-wrap:wrap;gap:7px 16px;margin:0 0 13px;font-size:11.5px;color:var(--faint)}
.cat-legend span{display:inline-flex;align-items:center;gap:6px}
.cat-legend .stq{width:3px;height:12px;border-radius:2px;background:var(--line);display:inline-block;flex:none}
.cat-legend .stq.ok{background:var(--ok)} .cat-legend .stq.warn{background:var(--warn)} .cat-legend .stq.bad{background:var(--bad)}
.cat-legend .hintq{color:var(--faint);opacity:.8}
.mg-quiet{color:var(--faint)}
.mg-pos{color:var(--ok)} .mg-neg{color:var(--bad)}
.serve{font-size:11.5px;color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.serve b{font-family:var(--mono);font-weight:600;color:var(--text);font-size:11.5px}
.serve .more{color:var(--faint)}
#acat-tbl td.mute{color:var(--faint)}
@media (max-width:1180px){
  #acat-tbl .col-cost,#acat-tbl .col-mg{display:none}
}
.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);overflow:hidden;box-shadow:var(--card-shadow)}
/* .panel 自己不带内边距——它装表格的次数远多于装别的，而表格要贴边。装一段文字或
   表单时用这两块给它抬头和留白。以前这两个类只出现在 HTML 里、CSS 里根本没有，
   于是标题和输入框直接压在边框上（窗口份额判据、抽屉里的拼车额度都是这么长的）。 */
.p-hd{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;padding:15px 20px;
  border-bottom:1px solid var(--line-soft);font-size:12.5px;color:var(--dim)}
.p-hd b{font-family:var(--disp);font-size:15px;font-weight:600;letter-spacing:-.012em;color:var(--text)}
.p-hd .mono{font-size:14px;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
/* 抬头右端留给「什么时候的事」——到期日、时间戳这一类跟在标题后面读不通、
   但又必须和标题同一行的东西。 */
.p-hd .faint{margin-left:auto;font-size:12px}
.p-bd{padding:16px 20px 20px}
.p-bd>.field:last-of-type{margin-bottom:0}

/* ---------- SETTINGS ---------- */
.set-panel{padding:20px 22px}
.set-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px 20px}
.set-grid .field{margin-bottom:0}
.set-grid label{display:flex;align-items:center;gap:8px}
.set-hint{font-size:11.5px;color:var(--faint);margin-top:6px;line-height:1.5}
.set-tag{font-size:10px;font-weight:600;letter-spacing:.04em;color:var(--warn);background:var(--warn-a12);border:1px solid var(--warn-a30);border-radius:6px;padding:1px 6px;text-transform:none}
.set-foot{display:flex;align-items:center;gap:14px;margin-top:20px;padding-top:18px;border-top:1px solid var(--line-soft);flex-wrap:wrap}
.set-foot-note{font-size:12px;color:var(--dim)}
.set-secret{display:flex;align-items:center;justify-content:space-between;gap:18px;flex-wrap:wrap}
.set-secret-state{display:flex;align-items:center;gap:8px;font-weight:600;font-size:14px;margin-bottom:6px}
@media(max-width:720px){.set-grid{grid-template-columns:1fr}}
/* 站点域名：主域名一行只读、备用域名一格输入、每个域名一张小卡。卡片的内边距、
   卡与卡之间的间距照抄 set-panel 里其他分组（16 / 12），代码块用等宽小字。 */
.dmn-primary{margin-bottom:16px}
.dmn-line{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 16px;margin:0;font-size:13px}
.dmn-kv{display:inline-flex;gap:6px;align-items:baseline}
.dmn-kv em{font-style:normal;color:var(--faint);font-size:12px}
.dmn-primary code,.dmn-zone code{font-family:var(--mono);font-size:12px}
.dmn-zones{display:grid;gap:12px;margin-top:16px}
.dmn-zones:empty{display:none}
.dmn-zone{border:1px solid var(--line-soft);border-radius:var(--r-md);padding:14px 16px;background:var(--surface-2)}
.dmn-zone-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.dmn-acts{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
.dmn-hosts{margin:10px 0 0;font-size:12px;line-height:1.9;display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline}
.dmn-res{list-style:none;margin:12px 0 0;padding:12px 0 0;border-top:1px solid var(--line-soft);display:grid;gap:6px;font-size:12px}
.dmn-res li{display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px}
.dmn-res li.bad span:last-child{color:var(--bad)}
.dmn-res.bad{color:var(--bad)}
.dmn-dot{width:7px;height:7px;border-radius:50%;background:var(--ok-fill);flex:none}
.dmn-res li.bad .dmn-dot{background:var(--bad-fill)}
/* 返佣三级比例。桌面和起付线 / 计酬期同一行宽，三格并排、沟仍是 16/20。
   手机上这张表单本来就是一列（截图里 起付线 下面就是 计酬期），比例也
   各占一整行，插在计酬期和挂账期之间，不要挤成三列小格。 */
.rb-rates{grid-column:1 / -1;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px 20px}
@media(max-width:720px){.rb-rates{grid-template-columns:1fr}}

/* ---------- 设置页 · 开关柜的分格 ----------------------------------------
   这一页原本是十组参数首尾相连的一根五千像素长条，找一个开关的代价等于把
   全部读一遍。分格之后代价换成了另一个问题——「看不见的那几格里有没有我该
   知道的事」——所以这条轨同时承担导航和状态：琥珀点 = 那一格有开关正关着
   （关着的开关是这套系统最贵的一类静默故障），玫红点 = 那一格此刻正在坏，
   紫环 = 那一格有改了没存的字段。三种记号都直接从已加载的读数算，不另存状态。 */
/* 模块页签条：钉在顶栏 + 实时带下面（--topbar-lv-h 由 sxTopbarH 量出来），
   全站只有这一条。按钮沿用原来设置页页签的样子（.sx-tab），记号也还是那三种：
   琥珀 = 那一页有开关关着，玫红 = 那一页正在坏，紫环 = 那一页有没保存的改动。
   窄屏时页签放不下就横向滑动，两端的渐隐告诉人那边还有。 */
.mt-bar{position:sticky;top:var(--topbar-lv-h,74px);z-index:4;display:flex;align-items:center;
  padding:7px 22px;background:var(--glass);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);border-bottom:1px solid var(--line-soft)}
.mt-list{display:flex;align-items:center;gap:4px;min-width:0;flex:1;overflow-x:auto;scrollbar-width:none}
.mt-list::-webkit-scrollbar{display:none}
.mt-bar.fade-r .mt-list{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(90deg,#000 calc(100% - 36px),transparent)}
.mt-bar.fade-l .mt-list{-webkit-mask-image:linear-gradient(90deg,transparent,#000 36px);mask-image:linear-gradient(90deg,transparent,#000 36px)}
.mt-bar.fade-l.fade-r .mt-list{-webkit-mask-image:linear-gradient(90deg,transparent,#000 36px,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(90deg,transparent,#000 36px,#000 calc(100% - 36px),transparent)}
/* 有没保存改动的页签：标签后面一个紫环（设置页的四格由 sxMark 自己画，不走这里） */
.sx-tab.has-dirty::before{content:'';order:9;width:8px;height:8px;border-radius:50%;flex:none;
  border:1px solid var(--brand-a40);box-shadow:var(--shadow-1)}
/* 页签：胶囊。选中的那一个是一块浅填充、字加粗一档（macOS 26 工具栏里的页签），
   不再用底部那根下划线——下划线在玻璃条上读起来像一道裂缝。 */
.sx-tab{position:relative;display:inline-flex;align-items:center;gap:8px;height:32px;padding:0 14px;flex:none;
  background:none;border-radius:var(--r-pill);font-family:var(--sans);font-size:13.5px;font-weight:500;letter-spacing:-.005em;color:var(--dim);
  white-space:nowrap;transition:color .16s var(--ease),background-color .16s var(--ease)}
.sx-tab:hover{color:var(--text);background:var(--fill-4)}
.sx-tab:active{background:var(--fill-3)}
.sx-tab.on{color:var(--text);font-weight:600;background:var(--fill-3)}
.sx-tab.on::after{content:none}
.sx-tab:focus-visible{outline:2px solid var(--focus);outline-offset:-2px;border-radius:var(--r-pill)}
.sx-mark{width:6px;height:6px;border-radius:50%;flex:none;background:currentColor;
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) color-mix(in srgb,currentColor var(--halo-alpha),transparent)}
/* 记号插槽：JS 往里塞 .sx-mark 的落点。display:contents 让点直接成为页签的
   flex 子项——嵌在普通 inline span 里的 6px 宽高会被整个无视，点就消失了。 */
.sx-markslot{display:contents}
.sx-mark.warn{color:var(--warn)} .sx-mark.bad{color:var(--bad)}
.sx-mark.dirty{width:8px;height:8px;color:var(--brand);background:none;
  border:2px solid currentColor;box-shadow:none}
.sx-pane>.sec:last-child{margin-bottom:0}

/* 区标题：一个真正的中号标题 + 一行注解 + 一枚生效标记。原来这三件事挤在同一行
   11px 全大写小字里，于是一张三行的表和一组十个阈值在版面上重量完全相同。 */
/* 子选择器写死到那一层：注解里会嵌 .mono 之类的行内 span，一条 .sx-head span
   的后代规则会把它们也变成块，于是「sail login 签发的 Key」断成两行。 */
.sx-head{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:14px 16px;margin:0 0 13px}
/* 两栏放不下就换行，别硬挤在一行里。动作栏是 flex:none 永不收缩的，所以一行装
   不下时收缩的只能是注解那一栏——而中文的 min-content 是「一个字」，注解会被
   压成一列竖排的字，同时把动作栏顶出容器造成整页横向溢出。手机上的收款页就是
   这么坏的（#topup-sec 在 760px 以下让搜索框占满一行，注解那栏当场只剩一个字宽）。
   min-width:0 是给不可断的长串（Key、URL）留的：那种内容 min-content 比容器还宽，
   不解开就换成动作栏被挤扁。 */
.sx-head>div:first-child{flex:1 1 auto;min-width:0}
.sx-head>div:first-child>b{display:block;font-family:var(--disp);font-size:15px;font-weight:600;letter-spacing:-.005em}
.sx-head>div:first-child>span{display:block;font-size:12px;color:var(--faint);margin-top:5px;line-height:1.6;max-width:84ch}
.sx-act{display:flex;align-items:center;flex-wrap:wrap;gap:10px 12px;flex:none;max-width:100%}
/* 生效标记。这一页上真正会伤人的从来不是填错一个数，是不知道这个数什么时候开始
   作用于线上流量——所以它单独成一枚记号，颜色沿用信号灯：绿=此刻就变、
   琥珀=要重启、玫红=按下去有人被踢下线。 */
.sx-when{flex:none;margin-top:1px;font-size:10.5px;font-weight:600;letter-spacing:.04em;
  padding:3px 10px;border-radius:999px;white-space:nowrap;
  color:var(--ok);background:var(--ok-a12);border:1px solid var(--ok-a26)}
.sx-when.warn{color:var(--warn);background:var(--warn-a12);border-color:var(--warn-a30)}
.sx-when.bad{color:var(--bad);background:var(--bad-a12);border-color:var(--bad-a26)}
.sx-when.mute{color:var(--faint);background:var(--surface-2);border-color:var(--line)}

/* 直接坐在面板上的说明是段落，不是字段注释：它没有栅格替它收边，于是在宽屏上
   一行能拉到一百五十个字，读到行尾就找不回下一行的行首。字段里的那些不用管，
   两列栅格已经把它们收在一个能读的宽度里了。 */
.set-panel>.set-hint,.set-panel>.sx-why{max-width:92ch}
.set-panel .toggle+.dim{max-width:92ch}
.sx-pane>.sec>.hint{max-width:92ch}

/* 面板里的小节题。一条从标签延出去的细线——分组是这条线画的，不是靠空行。 */
.sx-grp{display:flex;align-items:center;gap:12px;font-size:11px;font-weight:600;letter-spacing:.02em;
  text-transform:uppercase;color:var(--dim);margin:24px 0 13px}
.sx-grp:first-child{margin-top:0}
.sx-grp::after{content:'';flex:1;height:1px;background:var(--line-soft)}

/* 折起来的是「它当初为什么会存在」，不是「你现在需要知道什么」。后者一律留在
   外面——把操作事实折进抽屉，等于把这一页的注释写成了装饰。 */
.sx-why{margin-top:10px;font-size:11.5px;color:var(--faint);line-height:1.65}
.sx-why>summary{cursor:pointer;color:var(--dim);font-weight:500;list-style:none;
  display:inline-flex;align-items:center;gap:7px;padding:2px 0}
.sx-why>summary::-webkit-details-marker{display:none}
.sx-why>summary::before{content:'';width:5px;height:5px;flex:none;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(-45deg);transition:transform .18s ease}
.sx-why[open]>summary::before{transform:rotate(45deg)}
.sx-why>summary:hover{color:var(--text)}
.sx-why>summary:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:4px}
.sx-why p{margin:9px 0 0;max-width:92ch}

/* 保存条。一个 POST 装着两格的字段，所以保存键不能待在任何一格里面——它浮在页
   底，只在真有改动时出现，并且说出这一次会送走哪几格。 */
.sx-save{position:sticky;bottom:14px;z-index:3;display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  margin-top:10px;padding:13px 16px;border-radius:var(--r-xl);background:var(--surface);
  border:1px solid var(--brand-a40);box-shadow:var(--shadow-1)}
body.is-viewer .sx-save .btn:not(.ghost){opacity:.45;pointer-events:none}
@media(prefers-reduced-motion:no-preference){
  .sx-save{animation:rise .28s cubic-bezier(.22,.61,.25,1) both}
}
@media (max-width:860px){
  .mt-bar{padding:2px 8px 0}
  .sx-tab{padding:0 12px;font-size:13px;min-height:44px}
  .sx-save{bottom:calc(14px + 74px + env(safe-area-inset-bottom))}
}

/* ---------- 日志归档 ----------
   三段流水读作一条链：盘上压着什么 → 昨夜送走了什么 → 元数据还留多久。
   刻意不是三个并列的统计格：中间那两个箭头是这一格存在的理由——正文必须
   离开本机，一个只报「已上传 N 个」的面板回答不了「盘要满了吗」。 */
.arc-flow{display:grid;grid-template-columns:1fr auto 1fr auto 1fr;align-items:stretch;gap:0;
  min-width:0;border:1px solid var(--line);border-radius:12px;overflow:hidden;background:var(--surface-2)}
.arc-stage{padding:15px 17px;display:flex;flex-direction:column;gap:7px;min-width:0}
.arc-stage+.arc-stage{border-left:1px solid var(--line-soft)}
.arc-k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);
  display:flex;align-items:center;gap:7px}
.arc-v{font-family:var(--disp);font-size:23px;font-weight:600;letter-spacing:-.02em;line-height:1.05;
  font-variant-numeric:tabular-nums;color:var(--text)}
.arc-v small{font-size:12.5px;color:var(--dim);font-weight:500;letter-spacing:0}
.arc-sub{font-size:11.5px;color:var(--dim);line-height:1.5;overflow-wrap:anywhere}
.arc-sub b{color:var(--text);font-weight:600}
.arc-sub .warnv{color:var(--warn);font-weight:600}
.arc-sub .badv{color:var(--bad);font-weight:600}
.arc-sub .okv{color:var(--ok);font-weight:600}
/* 箭头指向数据流动的方向，不是装饰：正文只会往桶里走，不会回来。 */
.arc-arrow{display:flex;align-items:center;justify-content:center;padding:0 3px;color:var(--faint);
  background:var(--surface-3);border-left:1px solid var(--line-soft);border-right:1px solid var(--line-soft)}
.arc-arrow svg{width:15px;height:15px}
.arc-off .arc-v,.arc-off .arc-sub{color:var(--faint)}
.arc-sep{height:1px;background:var(--line-soft);margin:22px 0 18px}
.arc-fail{margin-top:12px;border:1px solid var(--bad-a32);background:var(--bad-a12);border-radius:10px;
  padding:11px 14px;font-size:12px;color:var(--text);line-height:1.6}
.arc-fail b{color:var(--bad)}
.arc-days{margin-top:12px;display:flex;flex-direction:column;gap:6px}
.arc-day{display:flex;align-items:center;gap:10px;font-size:12px;padding:7px 12px;border-radius:9px;
  background:var(--surface-2);border:1px solid var(--line-soft)}
.arc-day .mono{font-size:11.5px;color:var(--dim)}
.arc-day .grow{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media(max-width:820px){
  .arc-flow{grid-template-columns:1fr}
  .arc-stage+.arc-stage{border-left:none;border-top:1px solid var(--line-soft)}
  /* 竖排时只把箭头图标转下来，格子仍是一条横分隔。整格和卡片一样宽，
     整格转 90° 会立成一根竖条，盖住每段正文的后半句。 */
  .arc-arrow{border-left:none;border-right:none;border-top:1px solid var(--line-soft);padding:5px 0;background:transparent}
  .arc-arrow svg{transform:rotate(90deg)}
}

/* ---------- 主机资源 / 资源告警 ----------
   左边一根条回答「用了多少」，右边三格回答「会不会用完」。条上的细刻度是提醒
   档位：看得见下一次飞书会在哪里响。 */
.hr-top{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,2fr);gap:0;
  border:1px solid var(--line);border-radius:12px;overflow:hidden;background:var(--surface-2)}
.hr-main{padding:16px 18px;display:flex;flex-direction:column;gap:8px;min-width:0}
.hr-src{margin-left:auto;font-size:10.5px;font-weight:500;text-transform:none;letter-spacing:0;color:var(--faint)}
.hr-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border-left:1px solid var(--line-soft)}
.hr-stats>div{padding:16px 17px;display:flex;flex-direction:column;gap:7px;min-width:0}
.hr-stats>div+div{border-left:1px solid var(--line-soft)}
.hr-sv{font-family:var(--disp);font-size:19px;font-weight:600;letter-spacing:-.01em;font-variant-numeric:tabular-nums;color:var(--text)}
.hr-sv small{font-size:12px;color:var(--dim);font-weight:500}
.hr-sv .badv{color:var(--bad)}
.hr-bar{position:relative;display:block;height:8px;border-radius:4px;background:var(--faint-a14);overflow:hidden}
.hr-bar>span{display:block;height:100%;border-radius:4px;background:var(--brand-a60);transition:width .5s cubic-bezier(.22,.61,.36,1)}
.hr-bar>span.ok{background:var(--ok-fill)} .hr-bar>span.warn{background:var(--warn-fill)} .hr-bar>span.bad{background:var(--bad-fill)}
.hr-bar>i{position:absolute;top:0;bottom:0;width:1.5px;background:var(--surface-2);opacity:.9}
.hr-bar.sm{height:5px;width:120px;flex:none}
.hr-days{display:flex;align-items:flex-end;gap:3px;height:56px;margin-top:16px;padding:0 2px}
.hr-days>span{flex:1;max-width:44px;height:100%;display:flex;align-items:flex-end;min-width:0}
.hr-days>span>i{display:block;width:100%;border-radius:3px 3px 1px 1px;background:var(--brand-a60)}
.hr-days>span:hover>i{background:var(--text)}
.hr-days-ax{display:flex;justify-content:space-between;margin-top:6px;font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.hr-warn{margin-top:12px;border:1px solid var(--warn-a40);background:var(--warn-a12);border-radius:10px;
  padding:11px 14px;font-size:12px;color:var(--text);line-height:1.6}
.hr-warn b{color:var(--warn)}
.hr-cal{display:flex;flex-direction:column;gap:2px}
/* ---------- 源站 IP ----------
   沿用主机资源那一套读数块（hr-top / hr-days），柱子上加一条告警线。DNS 每个
   站点域名一块面板，表格和出口状态页同一种（ei-tbl）。 */
#oip-alerts{display:flex;flex-direction:column;gap:12px}
#oip-alerts:not(:empty){margin-bottom:24px}
#oip-alerts>.hr-warn{margin:0}
.oip-ip{font-family:var(--mono);font-size:19px;line-height:1.35;letter-spacing:0}
.oip-stats-1{grid-template-columns:minmax(0,1fr)}
.hr-sv .warnv{color:var(--warn)}
.oip-ports{font-family:var(--mono);font-size:15px}
.oip-bars{position:relative}
.oip-bars>span>i.bad{background:var(--bad-fill)}
.oip-line{position:absolute;left:0;right:0;height:0;border-top:1px dashed var(--bad-a40);pointer-events:none}
#oip-dns{display:flex;flex-direction:column;gap:16px}
#oip-dns>.hr-warn{margin:0}
.oip-act{display:flex;align-items:center;gap:12px 16px;flex-wrap:wrap}
.oip-act>div:first-child{flex:1 1 320px;min-width:0}
.oip-act-r{display:flex;align-items:center;gap:10px;flex:none}
.oip-zone-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 14px}
.oip-zone-head>b{font-family:var(--disp);font-size:14px;font-weight:600}
.oip-ssl{margin-left:auto;font-size:12px;color:var(--dim)}
.oip-ssl.bad{color:var(--bad);font-weight:600}
/* 两个站点域名各一张表，列宽定死才上下对齐。窄屏由 ei-tblwrap 横向滚动。 */
.oip-tbl{table-layout:fixed;min-width:880px}
.oip-tbl td{vertical-align:middle}
.oip-c-role{width:96px} .oip-c-st{width:108px} .oip-c-cloud{width:80px} .oip-c-act{width:200px}
.oip-tbl td>code{overflow-wrap:anywhere}
.oip-perm{margin-top:14px}
.oip-to{font-family:var(--mono);font-size:12px;overflow-wrap:anywhere}
.oip-acts{text-align:right;white-space:nowrap}
.oip-acts>*+*{margin-left:6px}
.oip-acts>.faint{font-size:12px}
.oip-after{margin-top:-8px;font-size:12.5px;color:var(--dim)}
.oip-after>summary{cursor:pointer;color:var(--text);font-weight:500}
.oip-after>ul{margin:10px 0 0;padding-left:20px;line-height:1.75}
/* 资源告警页顶上那一行本月用量：和运行节点页用同一个 sdHostTraffic，这里它是
   面板的第一行，不要那条分隔线。 */
.hr-now>.sd-host-traffic{margin-top:0;padding-top:0;border-top:0}
/* 日志归档设置页顶上那一行现状（完整流水在主机资源页）。 */
.arc-now{display:flex;align-items:center;gap:12px;flex-wrap:wrap;font-size:12.5px;color:var(--text)}
.sd-gauges.hr-gauges{margin-top:0;padding-top:0;border-top:0}
.sd-host-traffic{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:14px;padding-top:13px;
  border-top:1px solid var(--line-soft);font-size:12px}
.sd-ht-v{font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums;color:var(--text)}
.sd-ht-sub{color:var(--dim);flex:1;min-width:0}
.sd-ht-sub .badv{color:var(--bad);font-weight:600} .sd-ht-sub .warnv{color:var(--warn);font-weight:600}
/* 节点流量：一台服务器一格，铺成自适应的网格。每格只有这台自己的数；全体相同的话
   （合计、重置时间、未设额度、读数偏低）在页眉页脚各说一次。整格可点，打开设置。 */
.sd-nt{padding-bottom:16px}
.sd-nt .sd-deck-hd{align-items:center;margin-bottom:12px}
.sd-nt-meta{display:flex;align-items:center;gap:6px;min-width:0}
.sd-nt-meta .sd-deck-r{white-space:normal}
.sd-nt-meta .sd-deck-r span{white-space:nowrap}
.sd-nt-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(184px,1fr));gap:8px}
.sd-nt-tile{display:flex;flex-direction:column;gap:6px;min-width:0;padding:10px 12px;border-radius:12px;text-align:left;
  background:var(--fill-4);border:1px solid transparent;font-size:12px;color:var(--text);
  transition:background .14s ease,border-color .14s ease}
.sd-nt-tile:hover{background:var(--fill-3)}
.sd-nt-tile:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.sd-nt-tile.warn{border-color:var(--warn-a40)} .sd-nt-tile.bad{border-color:var(--bad-a40)}
.sd-nt-l1{display:flex;align-items:baseline;gap:6px;min-width:0}
.sd-nt-k{flex:0 1 auto;min-width:0;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sd-nt-k .fl{margin-right:5px}
.sd-nt-more{margin-left:4px;font-size:11px;font-weight:500;color:var(--dim)}
.sd-nt-dot{flex:none;align-self:center;width:6px;height:6px;border-radius:50%;background:var(--warn-fill)}
.sd-nt-v{flex:none;margin-left:auto;font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums}
.sd-nt-l2{display:flex;align-items:center;gap:8px}
.sd-nt-l2 .hr-bar{flex:1;height:5px}
.sd-nt-l2 em{font-style:normal;font-family:var(--mono);font-size:11px;color:var(--dim);font-variant-numeric:tabular-nums}
.sd-nt-l3{display:flex;flex-wrap:wrap;justify-content:space-between;gap:0 8px;font-size:11px;color:var(--dim);
  font-variant-numeric:tabular-nums;line-height:1.45}
.sd-nt-l3 span{white-space:nowrap}
.sd-nt-l3 .badv{color:var(--bad);font-weight:600}
.sd-nt-foot{display:flex;flex-wrap:wrap;gap:4px 16px;margin-top:12px;font-size:11.5px;color:var(--faint);line-height:1.5}
.sd-nt-foot .warnv{color:var(--warn)}
/* 网络连通：一个节点一行，列照请求走的路排（节点自身 | 出口）。行和表头都用 subgrid
   共享同一组列，竖着扫一列能看出是不是全队同一段坏了。窄屏换成一台一张小卡。 */
.sd-nx{padding-bottom:16px}
.sd-nx .sd-deck-hd{align-items:center;margin-bottom:12px}
.sd-nx-meta{white-space:normal;text-align:right}
.sd-nx-meta span{white-space:nowrap}
.sd-nx-meta .okv{color:var(--ok);font-weight:600}
.sd-nx-meta .badv{color:var(--bad);font-weight:600}
.sd-nx-meta .warnv{color:var(--warn);font-weight:600}
.sd-nx-cause{display:flex;align-items:center;gap:10px;padding:9px 13px;margin-bottom:12px;border-radius:12px;
  border:1px solid var(--bad-a40);background:var(--bad-a7);font-size:12.5px;line-height:1.5;color:var(--text)}
.sd-nx-cause.warn{border-color:var(--warn-a40);background:var(--warn-a5)}
.sd-nx-cause.warn .sd-todo-i{background:var(--warn-a12);color:var(--warn)}
.sd-nx-g{display:grid;font-size:12px;
  grid-template-columns:minmax(108px,1fr) minmax(132px,1.1fr) repeat(3,minmax(80px,.78fr)) minmax(104px,.9fr) minmax(172px,1.45fr) minmax(88px,.82fr)}
.sd-nx-grp,.sd-nx-hd,.sd-nx-r{display:grid;grid-column:1/-1;grid-template-columns:subgrid;align-items:center}
.sd-nx-grp span{font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--dim);padding:0 8px 5px;
  border-bottom:1.5px solid var(--line)}
.sd-nx-grp span:first-child{border-bottom:0}
.sd-nx-grp .a{grid-column:2/6;margin-right:6px}
.sd-nx-grp .b{grid-column:6/9;margin-left:6px}
.sd-nx-h{padding:7px 8px 7px;font-size:11px;font-weight:500;color:var(--faint);white-space:nowrap;min-width:0;
  overflow:hidden;text-overflow:ellipsis;cursor:default}
.sd-nx-h em{font-style:normal;font-family:var(--mono);font-size:10.5px;margin-left:6px;color:var(--dim);
  font-variant-numeric:tabular-nums}
.sd-nx-h em.badv{color:var(--bad);font-weight:600} .sd-nx-h em.warnv{color:var(--warn);font-weight:600}
.sd-nx-r{border-top:1px solid var(--line-soft);border-radius:8px;cursor:pointer;transition:background .14s ease}
.sd-nx-r:hover{background:var(--fill-4)}
.sd-nx-r:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.sd-nx-r>*{padding:7px 8px;min-width:0}
/* 一台多账号服务器的几行装在一个浅底框里（和下面的卡片框同一个意思）：框头一行写服务器名、
   共用的地址和这台的连通情况，框里的行地址只印端口。框也是 subgrid，列和框外的行对齐；
   框的边框让名字那一列往里缩一点，正好表示「属于这台」。 */
.sd-nx-box{display:grid;grid-column:1/-1;grid-template-columns:subgrid;margin:6px 0;
  border:1px solid var(--line);border-radius:12px;background:var(--faint-a5)}
.sd-nx-box+.sd-nx-r{border-top:0}
.sd-nx-bh{display:grid;grid-column:1/-1;grid-template-columns:subgrid;align-items:center;cursor:pointer;
  border-radius:11px 11px 0 0;transition:background .14s ease}
.sd-nx-bh:hover{background:var(--fill-4)}
.sd-nx-bh:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.sd-nx-bh>*{padding:8px 8px;min-width:0}
.sd-nx-bh .sd-nx-n b{font-weight:700}
.sd-nx-ic{display:inline-flex;flex:none;width:15px;height:15px;color:var(--dim)}
.sd-nx-ic svg{width:100%;height:100%}
.sd-nx-bs{grid-column:3/-1;display:flex;flex-wrap:wrap;gap:2px 0;font-size:11.5px;color:var(--dim)}
.sd-nx-bs>span+span::before{content:"·";color:var(--faint);margin:0 7px}
.sd-nx-bs .okv{color:var(--ok);font-weight:600} .sd-nx-bs .badv{color:var(--bad);font-weight:600}
.sd-nx-bs .warnv{color:var(--warn);font-weight:600}
.sd-nx-box>.sd-nx-r:last-child{border-radius:0 0 11px 11px}
/* 框里的账号名往里缩到框头名字下面（和框外各行名字左边对齐），一眼看出它们挂在这台下面。 */
.sd-nx-box .sd-nx-r>.sd-nx-n{padding-left:27px}
/* 节点自身和出口之间那条竖线：表头、每一行都画在出口的第一格左边。 */
.sd-nx-hd>:nth-child(6),.sd-nx-r>:nth-child(6){box-shadow:inset 1px 0 var(--line-soft);padding-left:12px}
.sd-nx-r.bad{background:var(--bad-a7)}
.sd-nx-r.bad:hover{background:var(--bad-a12)}
.sd-nx-r.off{opacity:.5}
.sd-nx-n{display:flex;align-items:center;gap:6px;min-width:0}
.sd-nx-n b{font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sd-nx-n .fl{flex:none}
.sd-nx-addr{display:flex;align-items:baseline;gap:1px;white-space:nowrap;overflow:hidden}
.sd-nx-addr code,.sd-nx-ipc code{font-family:var(--mono);font-size:11.5px;color:var(--text);font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis}
.sd-nx-addr em,.sd-nx-c em{font-style:normal;font-family:var(--mono);font-size:10.5px;color:var(--faint)}
.sd-nx-addr .sd-nx-tag{margin-right:6px}
.sd-nx-c{display:flex;align-items:center;gap:6px;white-space:nowrap;font-family:var(--mono);font-size:11.5px;
  font-variant-numeric:tabular-nums;color:var(--text)}
.sd-nx-c i{flex:none;width:7px;height:7px;border-radius:50%;background:var(--faint)}
.sd-nx-c b{font-weight:500;min-width:0;overflow:hidden;text-overflow:ellipsis}
.sd-nx-c.ok i{background:var(--ok-fill)}
.sd-nx-c.warn i{background:var(--warn-fill)}
.sd-nx-c.warn b{color:var(--warn)}
.sd-nx-c.bad i{background:var(--bad-fill)}
.sd-nx-c.bad b{color:var(--bad);font-weight:600}
.sd-nx-c.stale i{background:transparent;box-shadow:inset 0 0 0 1.5px var(--faint)}
.sd-nx-c.stale b,.sd-nx-c.stale code{color:var(--faint)}
.sd-nx-c.none{font-family:var(--sans);color:var(--faint)}
.sd-nx-c.none i{display:none}
.sd-nx-ipc code{cursor:copy;padding:1px 4px;margin-left:-4px;border-radius:5px}
.sd-nx-ipc code:hover{background:var(--fill-3)}
.sd-nx-tag{flex:none;font-family:var(--sans);font-size:10.5px;font-weight:600;line-height:1.5;padding:0 6px;
  border-radius:999px;background:var(--fill-3);color:var(--dim)}
.sd-nx-tag.warn{background:var(--warn-a12);color:var(--warn)}
.sd-nx-why{grid-column:2/-1;padding:0 8px 8px!important;margin-top:-4px;font-size:11.5px;line-height:1.5;color:var(--dim);
  white-space:normal}
.sd-nx-why::before{content:'↳';margin-right:6px;color:var(--faint)}
.sd-nx-why.bad{color:var(--bad)} .sd-nx-why.warn{color:var(--warn)}
.sd-nx-more{grid-column:1/-1;justify-self:start;margin:2px 0 0;padding:8px 8px;border:0;border-top:1px solid var(--line-soft);
  width:100%;text-align:left;background:none;font-size:11.5px;color:var(--dim);cursor:pointer;border-radius:0}
.sd-nx-more:hover{color:var(--text)}
.sd-nx-foot{margin-top:10px}
.sd-nx-lg{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
.sd-nx-lg i{width:7px;height:7px;border-radius:50%;margin-left:7px}
.sd-nx-lg i:first-child{margin-left:0}
.sd-nx-lg i.ok{background:var(--ok-fill)} .sd-nx-lg i.warn{background:var(--warn-fill)}
.sd-nx-lg i.bad{background:var(--bad-fill)} .sd-nx-lg i.stale{box-shadow:inset 0 0 0 1.5px var(--faint)}
@media(max-width:900px){
  .sd-nx .sd-deck-hd{align-items:flex-start}
  .sd-nx-meta{text-align:left}
  .sd-nx-grp,.sd-nx-hd{display:none}
  .sd-nx-g{display:block}
  .sd-nx-r{grid-template-columns:repeat(3,minmax(0,1fr));gap:9px 10px;padding:11px 6px}
  .sd-nx-r>*{padding:0}
  .sd-nx-r>:nth-child(6){box-shadow:none;padding-left:0}
  .sd-nx-n{grid-column:1/3}
  .sd-nx-addr{grid-column:3;justify-content:flex-end}
  .sd-nx-c{flex-wrap:wrap;row-gap:2px}
  .sd-nx-c::before{content:attr(data-k);flex:none;width:100%;font-family:var(--sans);font-size:10.5px;color:var(--faint)}
  .sd-nx-why{grid-column:1/-1;margin-top:0;padding:0!important}
  .sd-nx-box{display:block;margin:8px 0;padding:0 6px}
  .sd-nx-bh{display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px;padding:10px 6px}
  .sd-nx-bh>*{padding:0}
  .sd-nx-box .sd-nx-r>.sd-nx-n{padding-left:0}
}
/* 设置抽屉：一台一块，四个输入排一行。 */
.ntm-batch,.ntm-m{padding:14px 12px;margin:0 -12px;border-top:1px solid var(--line-soft)}
.ntm-batch{margin-top:16px;margin-bottom:6px;border:0;border-radius:12px;background:var(--fill-4)}
.ntm-batch+.ntm-m{border-top:0}
.ntm-m.on{border-radius:12px;background:var(--brand-dim);border-top-color:transparent}
.ntm-m.on+.ntm-m{border-top-color:transparent}
.ntm-hd{display:flex;align-items:baseline;justify-content:space-between;gap:4px 12px;flex-wrap:wrap;margin-bottom:10px}
.ntm-hd b{font-size:13px;font-weight:600;color:var(--text);min-width:0;overflow-wrap:anywhere}
.ntm-hd b .fl{margin-right:5px}
.ntm-hd span{font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums}
.ntm-g{display:grid;grid-template-columns:1fr 76px 1.15fr 1fr;gap:8px}
.ntm-g label{display:flex;flex-direction:column;gap:5px;min-width:0;font-size:11px;color:var(--faint)}
.ntm-g input,.ntm-g select{padding:7px 10px;font-size:13px}
.ntm-g select{padding-right:26px}
.ntm-g .btn{justify-content:center;padding:7px 10px;font-size:13px}
.ntm-warn,.ntm-sub,.ntm-err{font-size:11.5px;line-height:1.5;margin-top:8px}
.ntm-warn{color:var(--warn)} .ntm-sub{color:var(--faint)} .ntm-err{color:var(--bad)} .ntm-err:empty{display:none}
.ntm-clear{display:flex;align-items:center;gap:8px;margin-top:10px;font-size:12.5px;color:var(--dim)}
@media(max-width:820px){
  /* 窄屏上说明独占一行，否则会被挤成一列两三个字宽。 */
  .sd-host-traffic .sd-ht-sub{flex:none;width:100%;order:5}
  .sd-host-traffic .btn{margin-left:auto}
}
@media(max-width:900px){
  /* 页眉在这个宽度竖排（.sd-deck-hd）：合计靠左，设置入口靠右。 */
  .sd-nt .sd-deck-hd{align-items:stretch}
  .sd-nt-meta{justify-content:space-between}
}
@media(max-width:560px){
  .sd-nt-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .sd-nt-tile{padding:10px}
  .ntm-g{grid-template-columns:1fr 1fr}
}
@media(max-width:820px){
  .hr-top{grid-template-columns:1fr}
  .hr-stats{border-left:none;border-top:1px solid var(--line-soft);grid-template-columns:1fr}
  .hr-stats>div+div{border-left:none;border-top:1px solid var(--line-soft)}
}

/* ---------- 管理员 · 名册与权限档位 ----------------------------------------
   原来这一格是一张五列的表：一行人、四个列头、右边一片没有列头的按钮，宽屏上
   四百多像素是空的。表是用来比数的，这一格比的是权限——所以列头全部撤掉，名册
   摊成号单（读法沿用池详单的 .jack-row：灯 + 人 + 右侧读数），省下的宽度还给
   「这个档能做什么」那一句，于是一行人自己就说得清，不用再去下面对图例。

   下面那块原本是一段四行的灰色小字，是这一格最值钱、排版上却最轻的东西。它现在
   按后端真正的切法摆成三栏（roles.go 是按爆炸半径切的：运营一张白名单，白名单
   之外一律归所有者）。三档只有一根轴——伸得多深——所以用同一支紫的三级台阶表示，
   不另发三种颜色：发了颜色等于说这是三类东西，而它们是一条线上的三段。

   运营那一栏比另外两栏多一块「碰不到」。权限面板通常只印 can；而真要把运营档
   交出去的人，问的是 cannot。三栏因此不等高，最重的一栏正好是实际最常发的那档。 */
.aa-rack{display:flex;flex-direction:column}
/* 档位、最近登录、操作三个轨定宽，名字吃掉全部富余：这一行的主语是人，右边那一
   簇是对这个人能做的事，两者之间空着是对的。原来那张表空的是最后一列的右边——
   一列没有列头的按钮后面还挂着四百像素，那不是留白，是版面裂了。 */
.aa-row{display:grid;grid-template-columns:10px minmax(0,1fr) 156px 112px 300px;
  gap:0 16px;align-items:center;padding:14px 18px;border-top:1px solid var(--line-soft);
  transition:background .14s ease}
.aa-row:first-child{border-top:none}
.aa-row:hover{background:var(--surface-2)}
.aa-who{display:flex;align-items:center;gap:8px;min-width:0}
.aa-who>b{font-family:var(--disp);font-size:14.5px;font-weight:600;letter-spacing:-.005em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aa-me{flex:none;font-size:10.5px;font-weight:600;letter-spacing:.03em;padding:2px 7px;border-radius:999px;
  color:var(--brand-2);background:var(--brand-dim);border:1px solid var(--brand-a30)}
.aa-off{flex:none;font-size:10.5px;font-weight:600;letter-spacing:.03em;padding:2px 7px;border-radius:999px;
  color:var(--faint);background:var(--faint-a12);border:1px solid var(--faint-a22)}
.aa-tf{flex:none;font-size:10.5px;font-weight:600;letter-spacing:.03em;padding:2px 7px;border-radius:999px;
  color:var(--warn);background:var(--warn-a12);border:1px solid var(--warn-a22)}
.aa-tf.on{color:var(--ok);background:var(--ok-a12);border-color:var(--ok-a26)}
/* 「要求两步验证」是对整张名册生效的一条规则，所以放在名册上面，不混进某一行。 */
.aa-req{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:14px 18px;margin:0 0 12px}
.aa-req-t{min-width:0}
.aa-req-t b{display:block;font-size:13.5px;font-weight:600}
.aa-req-t span{display:block;font-size:12px;line-height:1.5;color:var(--faint);margin-top:3px}
/* 账户与安全抽屉：上半段两步验证，下半段改密码。 */
.tf-sec{padding:0 0 18px;margin:0 0 18px;border-bottom:1px solid var(--line-soft)}
.tf-head{display:flex;align-items:center;gap:8px;font-family:var(--disp);font-size:14px;font-weight:600;margin:0 0 12px}
.tf-row{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
.tf-row .tf-head{margin:0}
.tf-row .hint{margin:6px 0 0;line-height:1.55}
.tf-state{font-family:var(--sans);font-size:10.5px;font-weight:600;letter-spacing:.03em;padding:2px 7px;border-radius:999px;
  color:var(--faint);background:var(--faint-a12)}
.tf-state.on{color:var(--ok);background:var(--ok-a12)}
.tf-acts{display:flex;justify-content:flex-end;gap:8px}
/* 贴在名字后面的一句是这一档的理由，不是它的清单——清单在下面三栏里，抄两遍
   只会让人以为那是两件不同的事。 */
.aa-reach{min-width:0;font-size:12px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aa-seen{font-family:var(--mono);font-size:11.5px;font-variant-numeric:tabular-nums;color:var(--dim);white-space:nowrap}
.aa-seen.never{color:var(--faint)}
.aa-acts{text-align:right;white-space:nowrap;font-size:0}
.aa-acts>*{margin-left:6px}
/* 停用的号压暗但不撤走：撤走之后「他还在不在名册上」只能去数据库里问。 */
.aa-row.off .aa-who>b,.aa-row.off .aa-reach,.aa-row.off .aa-seen{color:var(--faint)}

/* 档位选择器。原来它是一个裸 select 坐在 110px 的表格列里，「所有者」被裁成两个
   半字——一个决定权限的控件读不全，比难看贵得多。 */
.aa-sel{position:relative;display:inline-flex;align-items:center;gap:9px;height:34px;padding-left:11px;
  background:var(--input-bg);border:1px solid var(--input-line);border-radius:10px;
  transition:border-color .14s ease,background .14s ease}
.aa-sel:hover{border-color:var(--brand-40-on-input-line)}
.aa-sel:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-dim)}
.aa-sel select{appearance:none;-webkit-appearance:none;border:none;background:transparent;box-shadow:none;
  height:32px;width:100%;padding:0 24px 0 0;font-size:12.5px;font-weight:600;color:var(--text);cursor:pointer}
.aa-sel select:focus{outline:none;box-shadow:none}
.aa-sel::after{content:'';position:absolute;right:11px;top:50%;width:5.5px;height:5.5px;margin-top:-4.5px;
  border-right:1.6px solid var(--faint);border-bottom:1.6px solid var(--faint);transform:rotate(45deg);pointer-events:none}
/* 自己那一档改不动：虚线边 + 没有小箭头，是「这里本来能拨」和「这一个拨不了」的区别。 */
.aa-sel.locked{background:var(--surface-2);border-style:dashed}
.aa-sel.locked::after{display:none}
.aa-sel select:disabled{opacity:1;color:var(--dim);cursor:default}

/* 台阶：三格递增，亮起几格就是这一档伸到多深。名册和下面的档位卡共用同一枚，
   所以那三栏同时就是这份名册的图例，不用再写一行「紫色表示……」。 */
.aa-deep{display:inline-flex;align-items:flex-end;gap:2px;flex:none;height:12px}
.aa-deep i{width:3px;border-radius:1.5px;background:var(--faint-a30)}
.aa-deep i:nth-child(1){height:5px} .aa-deep i:nth-child(2){height:8.5px} .aa-deep i:nth-child(3){height:12px}
.aa-deep.d2 i:nth-child(-n+2),.aa-deep.d3 i{background:var(--brand)}
.aa-deep.d1 i:nth-child(1){background:var(--faint)}

/* start 而不是 stretch：三栏本来就不一样重，拉平之后只读那栏会空出半张卡，
   看着像少了点什么——而它确实没有别的可说。 */
.aa-tiers{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;margin-top:18px;align-items:start}
.aa-tier{border:1px solid var(--line);border-radius:var(--r-xl);padding:15px 16px 16px;background:var(--surface)}
.aa-tier.own{border-color:var(--brand-38-on-line);background:var(--brand-a5)}
.aa-tk{display:flex;align-items:center;gap:10px;margin-bottom:8px}
.aa-tk>b{font-family:var(--disp);font-size:13.5px;font-weight:600;letter-spacing:-.005em}
.aa-tier.own .aa-tk>b{color:var(--brand-2)}
.aa-tw{margin:0 0 12px;font-size:12px;color:var(--dim);line-height:1.7}
/* 域名单是一串小牌子，不是竖着的 bullet：竖列会让「运营能碰八件事」在版面上重过
   「所有者能碰七件事」，而这两串本来一样重。也不用间隔点——折行时那个点会掉到
   下一行的行首，读起来就成了一个凭空出现的项目符号。 */
.aa-list{display:flex;flex-wrap:wrap;gap:6px}
.aa-list span{white-space:nowrap;font-size:12px;padding:3px 9px;border-radius:7px;
  background:var(--surface-2);border:1px solid var(--line-soft);color:var(--text)}
.aa-tier.own .aa-list span{background:var(--surface);border-color:var(--brand-32-on-line)}
/* 「碰不到」用虚线，不用红。红在这套系统里是「此刻正在坏」；权限的边界不是故障，
   它是一条画好的线。虚线沿用这一页别处「没接上」的写法（.ptag i.bridged / .tr-blank）。 */
.aa-no{margin-top:13px;padding-top:12px;border-top:1px dashed var(--line)}
.aa-no>b{display:block;font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;
  color:var(--dim);margin-bottom:6px}
.aa-no .aa-list span{background:transparent;border:1px dashed var(--faint-a42);color:var(--faint)}
.aa-foot{margin-top:15px;font-size:12px;color:var(--faint);line-height:1.75;max-width:92ch}

/* 抽屉里发档位：三张牌摞着，不是一个下拉。发号是这三档唯一一次被真正选中的时刻，
   而下拉把选项藏在点开之后——藏起来的东西没法在按下之前被比较。 */
.aa-picks{display:flex;flex-direction:column;gap:7px}
.aa-pick{display:flex;align-items:center;gap:11px;width:100%;text-align:left;padding:11px 13px;
  border:1px solid var(--line);border-radius:11px;background:var(--surface-2);color:var(--dim);
  font:inherit;cursor:pointer;transition:color .14s ease,background .14s ease,border-color .14s ease}
.aa-pick:hover{color:var(--text);border-color:var(--brand-40-on-line)}
.aa-pick.on{color:var(--text);background:var(--brand-dim);border-color:var(--brand-a50)}
.aa-pick:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.aa-pick>b{flex:none;min-width:42px;font-family:var(--disp);font-size:13px;font-weight:600}
.aa-pick>span{min-width:0;font-size:11.5px;line-height:1.55}

@media(max-width:1080px){
  /* 先撤名字后面那句——它是这一行里唯一可以由下面三栏代答的东西。 */
  .aa-reach{display:none}
}
@media(max-width:900px){
  .aa-tiers{grid-template-columns:1fr}
  .aa-row{grid-template-columns:10px minmax(0,1fr) auto;gap:11px 12px;padding:15px 14px}
  .aa-who{grid-column:2/4}
  .aa-sel{grid-column:2;justify-self:start}
  .aa-seen{grid-column:3;text-align:right}
  .aa-acts{grid-column:2/4;text-align:left}
  .aa-acts>*{margin:0 6px 0 0}
}

table{width:100%;border-collapse:collapse}
th,td{text-align:left;padding:11px 16px;border-bottom:1px solid var(--line-soft);font-size:13px;vertical-align:middle}
/* 表头：一行次级小字——不全大写、不加字距、不铺底色，只靠下面那根线和上面的
   内容分开（macOS 表格的列头）。底色给成台面色而不是透明：吸顶的表头要盖住
   从它底下滚过去的行。 */
th{font-size:12px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--dim);background:var(--surface);white-space:nowrap;border-bottom-color:var(--line)}
td{font-variant-numeric:tabular-nums}
tr:last-child td{border-bottom:none}
tbody tr{transition:background-color .12s var(--ease)}
tbody tr:hover{background:var(--fill-4)}
/* 操作列保持真正的表格单元格：display:flex 会把它挤出表格布局，行底色只铺到
   倒数第二列就断掉——平时看不出来，给行上色（如熔断行）时那道白缝很扎眼。
   font-size:0 吃掉按钮间的空白文本节点，按钮各自的字号不受影响。 */
.row-actions{text-align:right;white-space:nowrap;font-size:0}
.row-actions>*{margin-left:6px}
.row-actions>*:first-child{margin-left:0}
/* compact icon actions — labels live in title/aria, so the column never crushes a CJK word into a vertical stack */
.row-actions .btn,.line-act .btn{flex:none}
.btn.icon{width:30px;height:30px;padding:0;justify-content:center;border-radius:50%}
/* 图标不参与伸缩：按页面调过 .btn.sm 内边距的规则（比如触屏块的 8px 13px）会
   落到 .sm.icon 上，36px 的按钮只剩 8px 内容宽，图标被压成 8px 的一个点。 */
.btn.icon svg{width:15px;height:15px;flex:none}
.btn.ghost.icon{color:var(--dim);background:transparent;border-color:transparent}
.btn.ghost.icon:hover{color:var(--text);background:var(--fill-3)}
.btn.danger.icon{color:var(--dim);background:transparent;border-color:transparent}        /* delete stays quiet until you reach for it */
.btn.danger.icon:hover{color:var(--bad);background:var(--bad-a12)}
/* 空态：一枚托盘图标 + 一句话（HIG 的空态：说清楚这里为什么是空的、下一步做什么） */
.empty{padding:36px 24px;text-align:center;color:var(--dim);font-size:13px;line-height:1.6}
.empty::before{content:"";display:block;width:30px;height:30px;margin:0 auto 10px;background:var(--faint);opacity:.7;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M3.5 13.5 6.1 5.6A2.3 2.3 0 0 1 8.3 4h7.4a2.3 2.3 0 0 1 2.2 1.6l2.6 7.9'/%3E%3Cpath d='M3.5 13.5V18a2 2 0 0 0 2 2h13a2 2 0 0 0 2-2v-4.5h-4.8a2.7 2.7 0 0 1-5.4 0z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M3.5 13.5 6.1 5.6A2.3 2.3 0 0 1 8.3 4h7.4a2.3 2.3 0 0 1 2.2 1.6l2.6 7.9'/%3E%3Cpath d='M3.5 13.5V18a2 2 0 0 0 2 2h13a2 2 0 0 0 2-2v-4.5h-4.8a2.7 2.7 0 0 1-5.4 0z'/%3E%3C/svg%3E") center/contain no-repeat}
/* 状态码：胶囊形的着色小章 */
.status-pill{font-family:var(--mono);font-size:12px;font-weight:600;padding:2px 9px;border-radius:var(--r-pill)}
.s2xx{color:var(--ok);background:var(--ok-a12)}
.s4xx{color:var(--warn);background:var(--warn-a12)}
.s5xx,.s0{color:var(--bad);background:var(--bad-a11)}

/* toggle：iOS 的开关——开是绿色、关是灰色轨道，白色圆钮带一层投影，弹簧滑过去 */
.toggle{position:relative;display:inline-block;width:44px;height:26px;flex:none}
.toggle input{opacity:0;width:0;height:0;position:absolute}
.toggle .slide{position:absolute;inset:0;background:var(--fill-2);border:0;border-radius:999px;cursor:pointer;transition:background-color .25s var(--ease)}
.toggle .slide::before{content:"";position:absolute;width:22px;height:22px;left:2px;top:2px;border-radius:50%;background:#fff;
  box-shadow:0 3px 8px rgba(0,0,0,.15),0 1px 1px rgba(0,0,0,.16),0 0 0 .5px rgba(0,0,0,.04);transition:transform .32s var(--spring)}
.toggle input:checked + .slide{background:var(--ok-fill)}
.toggle input:checked + .slide::before{transform:translateX(18px)}
.toggle input:focus-visible + .slide{outline:2px solid var(--focus);outline-offset:2px}
.toggle input:disabled + .slide{opacity:.45;cursor:default}

/* segmented：分段控件——一条灰色胶囊轨道，选中的那段是一块抬起来的白（深色是灰） */
.seg{display:flex;gap:2px;padding:2px;border-radius:var(--r-pill);background:var(--fill-3)}
.seg button{flex:1;padding:7px 9px;border-radius:var(--r-pill);background:transparent;border:0;color:var(--text);font-weight:500;font-size:13px;
  transition:background-color .2s var(--ease),box-shadow .2s var(--ease),color .2s var(--ease)}
.seg button:hover{background:var(--fill-4)}
.seg button.on{background:var(--ctl-on-bg);box-shadow:var(--ctl-on-ring);color:var(--text);font-weight:600}
.seg button:focus-visible{outline:2px solid var(--focus);outline-offset:1px}

/* ---------- ROUTING BOARD (the patchbay) ---------- */
.rgroup{margin-bottom:24px}
.rgroup-head{display:flex;align-items:center;gap:11px;padding:0 2px 11px}
.rgroup-head .lbl{font-size:12.5px;color:var(--dim)}
.rgroup-head .count{font-size:11px;color:var(--faint);margin-left:auto}
.lines{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);overflow:hidden;box-shadow:var(--card-shadow)}
.line{display:grid;grid-template-columns:208px 1fr 128px auto;gap:16px;align-items:center;
  padding:14px 18px;border-bottom:1px solid var(--line-soft)}
.line:last-child{border-bottom:none}
.line:hover{background:var(--surface-2)}
.line.add{cursor:pointer;color:var(--faint)}
.line.add:hover{color:var(--brand-2)}
.line-jack{display:flex;flex-direction:column;gap:7px;min-width:0}
.kind-chip{display:inline-flex;align-items:center;font-size:11px;font-weight:600;letter-spacing:0;
  text-transform:none;padding:2px 9px;border-radius:var(--r-pill);width:fit-content}
.kind-default{color:var(--brand-2);background:var(--brand-dim)}
.kind-model{color:var(--dim);background:var(--surface-3)}
.kind-pattern{color:var(--warn);background:var(--warn-a12)}
.line-match{font-family:var(--mono);font-size:12.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.line-match.muted{color:var(--faint)}
.line-act{display:flex;gap:7px;justify-content:flex-end}
/* 窄屏先收窄读数柱，再让它整条落到通路下面横过来——最后被牺牲的是纸带，
   因为「多少」和「出没出事」是要带走的结论，「什么时候」是可以回头再看的。
   两条规则都排在 .route / .line 的定义之后，靠源码顺序生效。 */
@media (max-width:1240px){
  .route{grid-template-columns:190px 1fr 132px;gap:14px}
  .line{grid-template-columns:176px 1fr 116px auto;gap:12px}
}
@media (max-width:1000px){
  .route,.line{grid-template-columns:1fr}
  .lane-read{flex-direction:row;align-items:center;gap:12px}
  .lane-read .lane-tape{display:none}
}
.hop.hop-missing{border-color:var(--bad);border-style:dashed;background:transparent}
.hop.hop-missing .hop-name{color:var(--bad)}
/* 停用的上游：灰灯 + 压暗 + 划掉名字。和「已不存在」区分开——那是错误（红），
   这是有意为之（静音），但同样都不是能接住流量的一跳。 */
.hop.hop-off{border-style:dashed;background:transparent;opacity:.62}
.hop.hop-off .hop-name{color:var(--faint);text-decoration:line-through;text-decoration-color:var(--faint)}
.hop.hop-off .hop-alias{color:var(--faint)}
.hop-off-flag{font-size:10.5px;font-weight:600;letter-spacing:.02em;color:var(--faint);
  border:1px solid var(--line);border-radius:5px;padding:1px 5px}
.path-empty{color:var(--faint);font-size:12.5px}

.chain-hint{font-size:11.5px;color:var(--faint);margin:0 0 6px}
/* 复制来的规则：抽屉顶上引一条边，说清这份表单是从哪抄来的。定义排在 .chain-hint
   之后，同权重后来居上——两个类同时挂在一个 <p> 上，靠的就是这个顺序。 */
.copy-from{font-size:12px;color:var(--dim);background:var(--brand-dim);
  border-left:2px solid var(--brand-2);border-radius:0 8px 8px 0;
  padding:9px 11px;margin:0 0 14px;line-height:1.6}
.copy-from b{color:var(--text);font-family:var(--mono);font-size:11.5px;font-weight:600}

/* ---------- 故障切换链编辑器 · 配线架的正面 ----------
   顺序就是优先级，所以顺序必须看得见：每一跳挂在一条竖直母线上，1 号是点亮的
   实心插孔（主选），往下是空心备用；两跳之间那截线上写着「接不住 ↓」——这是
   串行的失败下切，不是并联。母线末端明写「都接不住 → 请求被拒」，因为
   chain_exhausted 是日志里真会出现的结局，配线的时候就该看见它。
   每跳一张卡：上排上游占满整行（长模型名不再被截成半个词），下排是从属的
   「传输方式 · 改写模型名」——不填就该像没填，填了才亮。 */
.chain-rows{display:flex;flex-direction:column;gap:28px}
.hoprow{position:relative;display:grid;grid-template-columns:22px minmax(0,1fr);gap:10px;align-items:start}
/* 往下的那截线和那句话由「上一跳」自己拥有——拖拽重排后依然对。最后一跳也画线：
   它下面接的是「添加下一跳」那个虚线插孔，母线不该断在半路。 */
.hoprow::before{content:"";position:absolute;left:10px;top:27px;bottom:-28px;width:2px;border-radius:2px;
  background:var(--brand-a45)}
.hoprow:not(:last-child)::after{content:"失败时 ↓";position:absolute;left:32px;bottom:-25px;height:22px;
  display:flex;align-items:center;font-size:10.5px;letter-spacing:.06em;color:var(--faint)}
.hop-jack{width:22px;height:22px;border-radius:50%;flex:none;margin-top:8px;
  display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:11px;font-weight:600;
  background:var(--surface);border:1.5px solid var(--brand-38-on-line);color:var(--dim)}
.hoprow:first-child .hop-jack{background:var(--brand);border-color:var(--brand);color:var(--btn-ink);
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--brand-ahalo)}
.hop-card{min-width:0;display:flex;flex-direction:column;gap:6px;padding:9px 10px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:12px;transition:border-color .14s}
.hop-card:focus-within{border-color:var(--brand-55-on-line)}
.hop-top{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center}
.hop-sel{padding:8px 10px;border-radius:8px;font-size:13px;font-weight:500}
.hop-tools{display:flex;gap:4px;flex:none}
.hop-tools button,.hop-grip{width:26px;height:30px;border-radius:7px;flex:none;background:var(--surface-3);
  border:1px solid var(--line);color:var(--dim);display:flex;align-items:center;justify-content:center;font-size:13px;transition:.12s}
.hop-tools button:hover{color:var(--text);border-color:var(--brand)}
.hop-grip{cursor:grab;user-select:none;font-size:12px;letter-spacing:-1px}
.hop-grip:hover{color:var(--text);border-color:var(--brand)}
.hop-grip:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.hop-del:hover{color:var(--bad)!important;border-color:var(--bad)!important}
/* 均衡组：并联的几跳不是「接不住才轮到」，而是同时在用。所以这段母线换成实心的
   一条粗线、连接词从「接不住 ↓」换成「同组分流」——一条链画成上下相接的顺序表，
   对一段并联的成员来说是一句谎话，而看着这张图配线的人会照着谎话去排优先级。 */
.hoprow.peer-next::after{content:"同组分流 ⇉";color:var(--brand-78-on-dim)}
.hoprow.peer-next::before{background:var(--brand-a78);width:3px;left:9.5px}
.hoprow.peer .hop-jack{background:var(--brand-16-on-surface);
  border-color:var(--brand-70-on-line);color:var(--brand);font-size:12px}
.hoprow.peer .hop-card{border-color:var(--brand-34-on-line)}
.hop-grp{width:26px;height:30px;border-radius:7px;flex:none;background:var(--surface-3);
  border:1px solid var(--line);color:var(--faint);display:flex;align-items:center;justify-content:center;
  font-size:13px;transition:.12s}
.hop-grp:hover{color:var(--text);border-color:var(--brand)}
.hop-grp.on{background:var(--brand-15-on-surface-3);
  border-color:var(--brand-65-on-line);color:var(--brand)}
.hoprow.dragging{opacity:.45}
.hoprow.dragging .hop-grip{cursor:grabbing}
/* 从属行：默认状态（透传 + 不改写）只留一句浅字，点开才换出真控件——
   一条链上四跳大多是默认，四行灰底空控件比四个浅字吵得多。 */
.hop-more{align-self:flex-start;background:transparent;border:none;padding:1px 0;font-size:11px;
  color:var(--faint);border-bottom:1px dashed transparent;transition:color .13s,border-color .13s}
.hop-more:hover{color:var(--brand-2);border-bottom-color:var(--brand-a45)}
.hop-more:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px}
.hop-sub{display:flex;align-items:center;gap:6px}
.hop-sub>*{background:transparent;border:1px solid transparent;border-radius:7px;padding:4px 6px;font-size:11.5px;color:var(--dim)}
.hop-tf{width:auto;flex:none;cursor:pointer}
.hop-tf:hover{background:var(--surface-3)}
.hop-tf.on{color:var(--brand-2);background:var(--brand-dim);border-color:var(--brand-a35);font-weight:600}
.hop-rw{flex:1;min-width:0;font-family:var(--mono);border-bottom-color:var(--line)}
.hop-rw:hover{border-bottom-color:var(--faint)}
.hop-rw.on{color:var(--text)}
.hop-rw:focus{background:var(--input-bg);border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-dim)}
/* 母线的下端：先接一个虚线插孔（加一跳），再落到终止端子（都接不住的结局） */
.chain-add,.chain-end{position:relative;display:grid;grid-template-columns:22px minmax(0,1fr);gap:10px;align-items:center}
.chain-add{margin-top:28px}
.chain-add .hop-jack{margin-top:0;border-style:dashed;color:var(--faint)}
.chain-add .btn{justify-self:start}
.chain-end{margin-top:22px}
.chain-end::before{content:"";position:absolute;left:10px;top:-22px;height:22px;width:2px;
  background:repeating-linear-gradient(180deg,var(--line) 0 3px,transparent 3px 7px)}
.chain-end-cap{width:22px;height:2px;background:var(--faint);border-radius:2px;opacity:.7}
.chain-end-txt{font-size:11px;color:var(--faint)}
/* 抬头与「怎么写」：正文一行说清顺序语义，写法细则收进可展开的注释卡 */
.chain-lead{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;margin:18px 0 9px}
.chain-lead label{margin-bottom:0}
.chain-lead .sub{font-size:11.5px;color:var(--faint)}
.chain-doc{margin:0 0 18px;border:1px solid transparent;border-radius:10px;overflow:hidden;transition:border-color .16s,background .16s}
.chain-doc[open]{border-color:var(--line-soft);background:var(--surface-2)}
.chain-doc>summary{list-style:none;cursor:pointer;width:fit-content;padding:5px 9px 5px 7px;border-radius:8px;
  font-size:11.5px;color:var(--faint);display:flex;align-items:center;gap:7px}
.chain-doc[open]>summary{width:auto;border-radius:0}
.chain-doc>summary::-webkit-details-marker{display:none}
.chain-doc>summary::before{content:"?";width:15px;height:15px;border-radius:50%;flex:none;font-size:10px;font-weight:700;
  display:flex;align-items:center;justify-content:center;background:var(--surface-3);color:var(--faint)}
.chain-doc>summary::after{content:"▾";margin-left:8px;font-size:10px;color:var(--faint);transition:transform .16s}
.chain-doc[open]>summary::after{margin-left:auto}
.chain-doc>summary:hover{color:var(--text);background:var(--surface-2)}
.chain-doc>summary:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.chain-doc[open]>summary{color:var(--text);border-bottom:1px solid var(--line-soft)}
.chain-doc[open]>summary::after{transform:rotate(180deg)}
.chain-doc dl{margin:0;padding:11px 12px 13px;font-size:11.5px;line-height:1.65;color:var(--faint)}
.chain-doc dt{color:var(--text);font-weight:600;margin-top:10px}
.chain-doc dt:first-child{margin-top:0}
.chain-doc dd{margin:2px 0 0}
.chain-doc code{font-family:var(--mono);font-size:11px;background:var(--surface-3);border-radius:4px;padding:1px 4px;color:var(--dim)}

/* ---------- ROUTING CHANNELS (专属通道 · 配线架上的另一层跳线) ----------
   同一台机箱、同一批上游，只是换一层跳线：所以通道选择器做成配线架的物理卡口，
   选中的那格向下咬住工作区（底边与面板同色，缺口连成一体）。
   通道视图里，标准路由的规则以「幽灵线」虚线呈现——看得见继承，才敢改。 */
.chan-bay{display:flex;align-items:center;gap:5px;flex-wrap:wrap;
  padding-bottom:11px;margin-bottom:20px;border-bottom:1px solid var(--line)}
.chan-tab{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;border-radius:9px;
  background:var(--surface-2);border:1px solid transparent;
  color:var(--dim);font-size:12.5px;font-weight:500;transition:color .14s,background .14s,border-color .14s}
.chan-tab:hover{color:var(--text);background:var(--surface-3)}
.chan-tab.on{background:var(--brand-dim);color:var(--brand-2);font-weight:600;
  border-color:var(--brand-a45)}
.chan-tab:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.chan-tab .chan-n{font-family:var(--mono);font-size:10.5px;color:var(--faint)}
.chan-tab.on .chan-n{color:var(--brand-2);opacity:.8}
.chan-tab.off{opacity:.5}
.chan-tab.off .chan-name{text-decoration:line-through;text-decoration-thickness:1px}
.chan-add{padding:8px 13px;border-radius:9px;background:transparent;border:1px dashed var(--line);
  color:var(--faint);font-size:12.5px;transition:.14s;margin-left:5px}
.chan-add:hover{color:var(--brand-2);border-color:var(--brand-50-on-line)}
.chan-add:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* 选中通道的抬头：它是谁、怎么落线、谁在用 */
.chan-head{background:var(--card-grad);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:16px 18px;margin-bottom:22px;box-shadow:var(--card-shadow)}
.chan-head-top{display:flex;align-items:center;gap:11px;flex-wrap:wrap}
.chan-title{font-family:var(--disp);font-size:15px;font-weight:600;color:var(--text)}
.chan-head-act{margin-left:auto;display:flex;gap:7px}
.chan-bind{font-size:11.5px;color:var(--dim)}
.chan-bind b{font-family:var(--mono);font-weight:600;color:var(--text)}
.chan-note{font-size:12px;color:var(--faint);margin:9px 0 0;line-height:1.55}
.chan-modes{display:flex;align-items:center;gap:6px;margin-top:14px;flex-wrap:wrap}
.chan-modes .seg{flex:none}
.chan-modes .seg button{flex:none;padding:6px 14px;font-size:12px;border-radius:8px}
/* 选中态直接穿上该模式的颜色，和标题旁的徽章说同一件事：切断了多少流量 */
.seg button.m-prefer.on{color:var(--ok);background:var(--ok-a13);
  border-color:var(--ok-a45)}
.seg button.m-override.on{color:var(--warn);background:var(--warn-a13);
  border-color:var(--warn-a45)}
.seg button.m-only.on{color:var(--bad);background:var(--bad-a12);
  border-color:var(--bad-a45)}
.mode-why{font-size:11.5px;color:var(--dim);line-height:1.55;margin:10px 0 0;max-width:70ch}
/* 模式徽章的颜色就是它切断了多少流量：绿=不切、琥珀=部分、红=全切 */
.mode-chip{display:inline-flex;align-items:center;font-size:10.5px;font-weight:600;letter-spacing:.06em;
  padding:2px 8px;border-radius:6px;white-space:nowrap}
.mode-prefer{color:var(--ok);background:var(--ok-a12)}
.mode-override{color:var(--warn);background:var(--warn-a12)}
.mode-only{color:var(--bad);background:var(--bad-a11)}

/* 幽灵线：这条规则不属于本通道，是从标准路由继承下来的 */
.lines.ghosted .line.inherited{background:repeating-linear-gradient(135deg,
  transparent 0 7px,var(--faint-a5) 7px 14px)}
.line.inherited .line-match,.line.inherited .kind-chip{opacity:.72}
.line.inherited .hop{border-style:dashed;background:transparent}
.line.inherited .wire{opacity:.6}
.line.cut{opacity:.5}
.line.cut .path{text-decoration:line-through;text-decoration-color:var(--bad);text-decoration-thickness:1px}
/* 停用的规则：行还在（链、顺序、备注都留着），但解析时当它不存在——所以压暗，
   不划掉：划掉是「被切断」的读法，那是模式决定的，不是这个开关扳的。 */
.line.off .line-jack,.line.off .path{opacity:.42}
.inherit-flag{display:inline-flex;align-items:center;gap:5px;font-size:10px;font-weight:600;letter-spacing:.06em;
  color:var(--faint);white-space:nowrap}
.inherit-flag::before{content:"";width:12px;height:1px;flex:none;
  background:repeating-linear-gradient(90deg,var(--faint) 0 3px,transparent 3px 6px)}
.line.cut .inherit-flag{color:var(--bad)}

/* 通道在密钥 / 用户表里的印记 */
.chan-chip{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;
  padding:3px 9px;border-radius:999px;color:var(--brand-2);background:var(--brand-dim);white-space:nowrap}
.chan-chip.inherit{color:var(--dim);background:var(--surface-3);font-weight:500}
.chan-chip.inherit::before{content:"↳";font-weight:600;color:var(--faint)}

/* 绑定通道的选择器：把横着的配线架竖过来，一行一个卡口。
   下拉框把通道压成一行没有形状的字，看不见落线方式、规则数和停用状态，
   一个通道都没有时更是只剩孤零零一个默认项——像坏了。这里全部摊开。 */
.chan-pick{display:flex;flex-direction:column;gap:6px}
/* position:relative 让藏起来的单选框以这一格为定位参照。没有它，单选框的参照是
   整个抽屉，点一个靠下的选项时浏览器把焦点滚进视野，滚走的是抽屉本身，
   标题和正文被推出去、只剩一片白（2026-10-24）。 */
.chan-pick .cp-opt{position:relative;display:flex;align-items:center;gap:11px;padding:11px 13px;border-radius:10px;
  background:var(--surface-2);border:1px solid var(--line);cursor:pointer;
  transition:background .14s,border-color .14s}
.chan-pick .cp-opt:hover{background:var(--surface-3);border-color:var(--brand-32-on-line)}
.chan-pick input{position:absolute;width:1px;height:1px;opacity:0;margin:0}
/* 插上的那一格：左沿亮起一根跳线头 */
.chan-pick .cp-opt::before{content:"";flex:none;width:3px;align-self:stretch;border-radius:2px;
  background:transparent;transition:background .14s}
.chan-pick .cp-opt:has(input:checked){background:var(--brand-dim);
  border-color:var(--brand-a50)}
.chan-pick .cp-opt:has(input:checked)::before{background:var(--brand-2)}
.chan-pick .cp-opt:has(input:focus-visible){outline:2px solid var(--focus);outline-offset:2px}
.cp-body{flex:1;min-width:0}
.cp-name{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;font-weight:600;color:var(--text)}
.chan-pick .cp-opt:has(input:checked) .cp-name{color:var(--brand-2)}
.cp-sub{font-size:11.5px;color:var(--faint);line-height:1.5;margin-top:3px}
.cp-n{flex:none;font-family:var(--mono);font-size:10.5px;color:var(--faint);white-space:nowrap}
/* 分组头：API 模式和拼车模式不是并列的两个选项，是两种生意——一个按官方价从
   额度扣，一个按月买座位。摊在同一张平表里选，等于让人在「哪张路由表」和「几人
   车」之间二选一，而这两个问题根本不在一个层面上。 */
.cp-grp{display:flex;align-items:baseline;gap:8px;margin:14px 3px 2px;font-size:11.5px;
  font-weight:700;color:var(--dim)}
.cp-grp:first-child{margin-top:0}
.cp-grp span{font-weight:500;font-size:11px;color:var(--faint)}
/* 车型是算出来的，通道自己的名字降一级挂在后面 */
.cp-alias{font-size:11px;font-weight:500;color:var(--faint)}
/* 拼车那一格不是选项，是事实：他买了什么、坐在哪、到什么时候。所以它长得
   不像单选框——没有卡口、没有左沿跳线头，一眼看出来这里不接受输入。 */
.cp-seatcard{border:1px solid var(--line);border-radius:10px;padding:12px 13px;background:var(--surface-2)}
.cp-seatcard.none{border-style:dashed;background:transparent}
.cp-seatcard.warn{border-color:var(--warn-a50,var(--line));background:var(--surface-2)}
.cp-seatcard .cs-t{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;color:var(--text)}
.cp-seatcard.none .cs-t{color:var(--dim)}
.cp-seatcard.warn .cs-t{color:var(--warn)}
.cp-seatcard .cs-s{font-size:11.5px;color:var(--faint);line-height:1.55;margin-top:4px}
.cp-seatcard .cs-chip{font-size:10px;font-weight:600;padding:2px 6px;border-radius:5px;
  color:var(--ok);background:var(--ok-dim,var(--surface-3))}
.cp-seatcard .btn{margin-top:9px}
/* 用户表里的通道列：拼车的人显示车型，和手工绑的通道分得开 */
.chan-chip.car{color:var(--brand-2);background:var(--brand-dim)}
.cp-opt.off .cp-name span:first-child{text-decoration:line-through;text-decoration-thickness:1px}
.cp-opt.off{opacity:.62}
/* 一个通道都没有：这不是错误，是还没开始 */
.chan-empty{border:1px dashed var(--line);border-radius:12px;padding:16px 17px;background:var(--surface-2)}
.chan-empty .ce-t{font-size:13px;font-weight:600;color:var(--text);margin:0}
.chan-empty .ce-b{font-size:12px;color:var(--faint);line-height:1.6;margin:7px 0 13px;max-width:52ch}
@media (max-width:860px){
  .chan-bay{gap:4px;padding-bottom:9px;margin-bottom:15px}
  .chan-head{padding:14px}
  .chan-head-act{margin-left:0;width:100%}
  .chan-bind{width:100%}
  .line.inherited .inherit-flag{margin-top:-2px}
}

/* ---------- MODEL HEALTH ---------- */
.hbar-row{display:flex;align-items:center;justify-content:space-between;gap:14px 22px;flex-wrap:wrap;margin-bottom:18px}
.hlegend{display:flex;gap:14px;flex-wrap:wrap;font-size:11.5px;color:var(--dim)}
.hlegend i{display:inline-block;width:9px;height:9px;border-radius:2px;margin-right:5px;vertical-align:-1px}
/* time-window selector — segments of one continuous look-back axis; the active
   segment lifts off the track instead of glowing (light mode used to smear) */
.hwin{display:flex;align-items:center;gap:12px;min-width:0}
/* 标签就是标签 —— 这里曾经挂着一个发光圆点，长得像选中的 radio 却不可点 */
.hwin-lbl{display:inline-flex;align-items:center;font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);flex:none}
/* 时间窗 / 结果：分段控件（灰色胶囊轨道 + 抬起来的选中块） */
.winsel{display:inline-flex;align-items:stretch;background:var(--fill-3);border:0;border-radius:var(--r-pill);padding:2px}
.winsel button{position:relative;padding:6px 15px;border-radius:var(--r-pill);background:transparent;color:var(--text);font-weight:500;font-size:12.5px;white-space:nowrap;
  transition:color .2s var(--ease),background-color .2s var(--ease),box-shadow .2s var(--ease)}
.winsel button+button{margin-left:2px}
.winsel button:not(.on):hover{background:var(--fill-4)}
.winsel button.on{color:var(--text);font-weight:600;background:var(--ctl-on-bg);box-shadow:var(--ctl-on-ring)}
.winsel button:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.htiles{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin-bottom:22px}
.hcards{display:grid;grid-template-columns:repeat(auto-fill,minmax(345px,1fr));gap:14px}
.hcard{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);padding:15px 16px;display:flex;flex-direction:column;gap:12px;box-shadow:var(--card-shadow)}
.hcard.attn{border-color:var(--bad-42-on-line)}
.hcard.warn{border-color:var(--warn-36-on-line)}
.hc-head{display:flex;align-items:center;gap:9px;min-width:0}
.hc-dot{width:9px;height:9px;border-radius:50%;flex:none;background:var(--faint)}
.dot-healthy{background:var(--ok);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--ok-ahalo)}
.dot-degraded{background:var(--warn);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--warn-ahalo)}
.dot-down{background:var(--bad);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--bad-ahalo);animation:blink 1s steps(2,start) infinite}
.dot-idle{background:var(--faint)}
.hc-name{font-family:var(--disp);font-weight:600;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.hbar{display:flex;height:8px;border-radius:5px;overflow:hidden;background:var(--surface-3)}
.hbar>span{display:block;height:100%;min-width:1px}
.seg-ok{background:var(--ok)} .seg-rl{background:var(--warn)} .seg-auth{background:#fb923c}
.seg-5xx{background:var(--bad)} .seg-tx{background:#9f1239} .seg-brk{background:#c2410c} .seg-cl{background:var(--faint)}
[data-theme=light] .seg-auth{background:#ea7317}

/* ---------- 模型健康 · 走纸记录仪 (strip chart) ----------
   这屏只回答三件事：谁不可信 / 什么时候开始 / 证据是什么。所以版面按风险分配，
   不按模型数量分配 —— 出事的展开成证据，正常的收成一行，24h 无流量的折成一句话。
   两条纪律：
   1) 板上没有绿色。健康是无色的，颜色出现本身就是警报；品牌紫只表达「选中」，
      永不表达数据（旧版迹线用品牌紫画成功、可用条用绿画成功，同一概念两种色）。
   2) 纸带全表同刻度 —— 每张卡各自归一化时，n=0 的柱子会比 n=47 的还高。 */
.tb{--ink:var(--text-a46);
    --id-w:198px;--rate-w:74px;--lat-w:70px;--note-w:190px;--pad:16px;
    max-width:1560px}
.tb-lead{display:flex;align-items:flex-end;justify-content:space-between;gap:12px 30px;flex-wrap:wrap;margin:0 0 18px}
.tb-say{font-family:var(--disp);font-size:19px;font-weight:600;letter-spacing:-.022em;line-height:1.4;margin:0;color:var(--text);min-width:0}
.tb-say.calm{font-weight:500;color:var(--dim)}
.tb-say b{font-weight:700}
.tb-say .mk{border-bottom:1px solid var(--line);cursor:pointer}
.tb-say .mk:hover{border-bottom-color:currentColor}
.tb-say .sv{font-weight:700}
.tb-say.bad .sv{color:var(--bad)} .tb-say.warn .sv{color:var(--warn)}
.ov-quick{display:flex;flex-wrap:wrap;gap:8px;margin:-4px 0 20px}
@media (max-width:420px){.topbar .who{display:none}}
/* 窄屏的模块面板：从底部升起，两列大按钮，每格至少 48px 高，拇指点得准。 */
.mod-scrim{position:fixed;inset:0;z-index:60;background:rgba(6,8,14,.46);opacity:0;pointer-events:none;transition:opacity .18s ease}
.mod-scrim.show{opacity:1;pointer-events:auto}
.mod-sheet{position:fixed;left:0;right:0;bottom:0;z-index:61;background:var(--surface);border-top:1px solid var(--line);
  border-radius:var(--r-2xl) var(--r-2xl) 0 0;padding:14px 16px calc(18px + env(safe-area-inset-bottom));box-shadow:0 -18px 48px -18px rgba(0,0,0,.5);
  transform:translateY(105%);transition:transform .22s cubic-bezier(.22,.61,.25,1);visibility:hidden}
.mod-sheet.show{transform:none;visibility:visible}
.mod-sheet-hd{display:flex;align-items:center;justify-content:space-between;margin:0 0 12px}
.mod-sheet-hd b{font-family:var(--disp);font-size:15px;font-weight:600}
.mod-sheet-x{width:36px;height:36px;display:grid;place-items:center;border-radius:50%;background:var(--fill-3);color:var(--dim);font-size:19px}
.mod-sheet-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.mod-sheet-list button{display:flex;align-items:center;gap:10px;min-height:48px;padding:10px 12px;border-radius:var(--r-lg);
  background:var(--fill-4);border:1px solid transparent;color:var(--text);font-weight:500;text-align:left}
.mod-sheet-list button svg{width:18px;height:18px;flex:none;opacity:.8}
.mod-sheet-list button.on{color:var(--text);font-weight:600;background:var(--fill-2)}
.mod-sheet-list button:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
@media(prefers-reduced-motion:reduce){.mod-sheet,.mod-scrim{transition:none}}
.tb-lead-r{display:flex;align-items:center;gap:10px 22px;flex-wrap:wrap;min-width:0}
.tb-roll{display:flex;gap:16px;flex-wrap:wrap;font-family:var(--mono);font-size:11.5px;color:var(--faint);font-variant-numeric:tabular-nums}
.tb-roll b{color:var(--text);font-weight:600}
.tb-key{display:flex;gap:14px;flex-wrap:wrap;font-size:10.5px;letter-spacing:.02em;color:var(--faint);margin:0 0 9px}
.tb-key i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:5px;vertical-align:-1px}
.tb-key i.ink{background:var(--ink)} .tb-key i.up{background:var(--bad)}

.tb-board{border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface);box-shadow:var(--card-shadow);overflow:hidden}
.tb-axis{display:flex;align-items:flex-end;gap:14px;padding:12px var(--pad) 8px;border-bottom:1px solid var(--line)}
.tb-ax-h{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);flex:none}
.tb-ax-h.id{width:var(--id-w)} .tb-ax-h.rate{width:var(--rate-w);text-align:right}
.tb-ax-h.lat{width:var(--lat-w);text-align:right} .tb-ax-h.note{width:var(--note-w)}
.tb-ax-t{flex:1;min-width:150px;position:relative;height:15px}
.tb-ax-t .s{position:absolute;bottom:4px;font-family:var(--mono);font-size:9.5px;color:var(--faint);white-space:nowrap}
.tb-ax-t .s::before{content:"";position:absolute;left:0;bottom:-4px;width:1px;height:3px;background:var(--line)}
.tb-ax-t .s.now{right:0;color:var(--dim)}
.tb-ax-t .s.now::before{display:none}
/* 统计窗口不是一个孤立的分段控件：它就是纸带上被点亮的那一段 */
.tb-ax-t .win{position:absolute;right:0;bottom:0;height:2px;min-width:3px;border-radius:2px;background:var(--brand);transition:width .22s cubic-bezier(.2,.7,.3,1)}

.tbr{display:flex;align-items:center;gap:14px;padding:10px var(--pad);border-top:1px solid var(--line-soft);cursor:pointer;position:relative;transition:background .13s ease}
.tbr:hover,.tbr.on{background:var(--surface-2)}
.tbr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.tbr::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:transparent}
.tbr.sev-down::before{background:var(--bad)}
.tbr.sev-degraded::before{background:var(--warn)}
.tb-id{width:var(--id-w);flex:none;display:flex;align-items:center;gap:8px;min-width:0}
.tb-nm{font-family:var(--disp);font-size:13px;font-weight:600;letter-spacing:-.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.tb-tr{flex:1;min-width:150px}
/* 纸带：基线以下＝正常调用量（全表同刻度），基线以上＝失败次数（自己的敏感刻度，
   一次失败就看得见 —— 失败是事件，不是要跟流量比大小的量） */
.trc{display:block;width:100%;height:46px;overflow:visible}
.trc .base{stroke:var(--line);stroke-width:1}
/* 统计窗口从哪儿起算，就在纸带上划一刀；窗口外压一档 —— 压掉的是强调，不是历史本身 */
.trc .edge{stroke:var(--brand);stroke-width:1;stroke-dasharray:3 3;opacity:.5}
.trc .vol{fill:var(--ink)}
.trc .sp{fill:var(--bad)}
.trc .dim{opacity:.62}
.trc .hit{fill:transparent}
.trc .hit:hover{fill:var(--brand-a18)}
/* 上电走纸：整板迹线从基线一次性拉起来，只在首绘和换窗口时放一次 */
.trace.ink .trc .vol{transform-box:fill-box;transform-origin:top;animation:tbfeed .42s cubic-bezier(.2,.75,.3,1) both}
.trace.ink .trc .sp{transform-box:fill-box;transform-origin:bottom;animation:tbfeed .42s cubic-bezier(.2,.75,.3,1) .1s both}
@keyframes tbfeed{from{transform:scaleY(0)}to{transform:scaleY(1)}}
@media (prefers-reduced-motion:reduce){.trace.ink .trc .vol,.trace.ink .trc .sp{animation:none}}
.tb-rate{width:var(--rate-w);flex:none;text-align:right;font-family:var(--mono);font-size:13px;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;line-height:1.25}
.tb-rate .n{display:block;font-size:9.5px;font-weight:500;color:var(--faint);margin-top:4px}
.tb-rate.nil{color:var(--faint);font-weight:500}
/* 样本不足：读数虚化，n=1 的 100% 不许和 n=47 的 100% 长成一个样 */
.tb-rate.thin{color:var(--dim);font-weight:500}
.tb-rate.thin .v{text-decoration:underline dotted;text-underline-offset:3px;text-decoration-color:var(--faint)}
.tb-rate.bad{color:var(--bad)} .tb-rate.warn{color:var(--warn)}
.tb-lat{width:var(--lat-w);flex:none;text-align:right;font-family:var(--mono);font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums}
.tb-note{width:var(--note-w);flex:none;font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tb-note.bad{color:var(--bad)}
.tb-note em{font-style:normal;color:var(--dim)}

.tbx{background:var(--surface-2);border-top:1px dashed var(--line);padding:16px var(--pad) 18px;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(215px,1fr));gap:18px 28px}
.tbx h5{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);margin:0 0 10px}
/* 成因条里"成功"也不给绿：这条的工作是说明出了什么错 */
.tbx .seg-ok{background:var(--ink)}
.tbx-lg{display:flex;flex-wrap:wrap;gap:5px 13px;margin-top:9px;font-family:var(--mono);font-size:10.5px;color:var(--dim)}
.tbx-lg i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:5px;vertical-align:-1px}
/* 时延标尺：TTFB 与端到端共用一条刻度，生成占了多少一眼可见 */
.rul{display:grid;grid-template-columns:30px 1fr 46px;gap:5px 9px;align-items:center;font-family:var(--mono);font-size:10px;color:var(--faint)}
.rul .g{grid-column:1/-1;font-size:11px;letter-spacing:.02em;text-transform:uppercase;margin-top:7px}
.rul .g:first-child{margin-top:0}
.rul .t{height:5px;border-radius:3px;background:var(--surface-3);overflow:hidden}
.rul .t i{display:block;height:100%;background:var(--ink);border-radius:3px}
.rul .t.e i{background:var(--text-a17)}
.rul .v{text-align:right;color:var(--dim);font-variant-numeric:tabular-nums}
.ush{display:grid;grid-template-columns:1fr auto;gap:4px 10px;align-items:center;font-size:11.5px}
.ush .n{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dim)}
.ush .p{font-family:var(--mono);font-size:11px;color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.ush .b{grid-column:1/-1;height:4px;border-radius:2px;background:var(--surface-3);overflow:hidden;margin-bottom:5px}
.ush .b i{display:block;height:100%;background:var(--ink)}
.ush .miss{color:var(--bad)}
.tbx-err{font-size:12px;line-height:1.65;color:var(--text);word-break:break-word;margin:0}
.tbx-err.dim{color:var(--dim)}
.tbx-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:9px;font-family:var(--mono);font-size:10.5px;color:var(--faint)}
.rq{display:inline-flex;align-items:center;gap:6px;background:var(--surface-3);border:1px solid var(--line);border-radius:6px;padding:2px 8px;
  font-family:var(--mono);font-size:10.5px;color:var(--dim);cursor:pointer;transition:border-color .13s,color .13s}
.rq:hover{border-color:var(--brand);color:var(--text)}
.tbx-note{grid-column:1/-1;font-size:11.5px;color:var(--faint);line-height:1.6;margin:0;border-top:1px solid var(--line-soft);padding-top:12px}

.tb-fold{display:flex;align-items:center;gap:10px;width:100%;padding:11px var(--pad);border-top:1px solid var(--line-soft);
  background:var(--surface-2);font-size:11.5px;color:var(--faint);cursor:pointer;text-align:left}
.tb-fold:hover{color:var(--dim)}
.tb-fold .cv{margin-left:auto;transition:transform .2s ease}
.tb-fold.on .cv{transform:rotate(90deg)}
.tb-dead{display:flex;flex-wrap:wrap;gap:6px;padding:2px var(--pad) 14px;background:var(--surface-2)}
.tb-dead span{font-family:var(--mono);font-size:11px;color:var(--faint);background:var(--surface-3);border-radius:6px;padding:3px 8px}
.tb-none{padding:44px var(--pad);text-align:center;color:var(--faint);font-size:12.5px}
@media (max-width:1380px){.tb-ax-h.note,.tb-note{display:none}}
@media (max-width:1200px){.tb-ax-t .s.alt{display:none}}
@media (max-width:1020px){.tb{--id-w:150px}}

/* ---------- 效率 / EFFICIENCY (what to change) ----------
   健康页问「通不通」，计费页问「赚不赚」。这一页只问一句：我现在该改什么。
   凡是不服务这句话的部件都不该在这页上——包括那些「看起来很仪表」的部件。

   所以版面是三层递降，字号和宽度一路收窄：
     结论    一整段话，整页唯一的大字，句子里直接点名最重的那个东西；
     该改什么 一本台账，一行 = 一个「要修的东西」，不是一条规则——同一个模型
             触发的几条规则收在同一行里展开，因为操作的人修的是模型，不是规则；
     凭据    全局读数 + 三张原始表收进一个带页签的面板，默认只露一张。

   签名件是台账右缘那列读数牌：所有行的数字右对齐成一竖列、按严重度上色，
   不读字也能从上往下扫出今天值得动的是哪几行。整页只有这一处敢用颜色。 */

/* 结论：一段话。旧版把它拆成五块瓦片，读者得自己把结论拼回来。 */
/* 分页：一页装不下两种阅读姿势。「该改什么」是站着扫的清单，「原始数据」是
   坐下来核的表——把它们叠在一条时间线上，就是逼人滚过一屏结论去找一张表。
   页签自己带徽标：不在这一页时也知道待办有几项、里面有没有高优先的。 */
.ef-nav{display:inline-flex;align-items:stretch;gap:3px;background:var(--surface-2);
  border:1px solid var(--line);border-radius:13px;padding:3px}
.ef-nav button{display:inline-flex;align-items:center;gap:9px;padding:9px 17px;border-radius:10px;
  background:transparent;color:var(--dim);font-family:var(--disp);font-weight:600;font-size:13.5px;
  white-space:nowrap;transition:color .16s ease,background .16s ease,box-shadow .16s ease}
.ef-nav button:not(.on):hover{color:var(--text);background:var(--surface-3)}
.ef-nav button.on{color:var(--brand-2);background:var(--ctl-on-bg);box-shadow:var(--ctl-on-ring)}
.ef-nav button:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.ef-nav b{display:inline-flex;align-items:center;justify-content:center;min-width:19px;height:19px;
  padding:0 5px;border-radius:6px;font-family:var(--sans);font-size:11px;font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--btn-ink);background:var(--faint)}
.ef-nav b.high{background:var(--bad)} .ef-nav b.mid{background:var(--warn)} .ef-nav b.ok{background:var(--ok)}

/* 结论和台账是读的，收在一个阅读宽度里，别跟着 27 寸屏一起长。
   下面的表才要宽——那是数据面，两者右边缘不齐是有意的。 */
.ef-say{max-width:1060px;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  box-shadow:var(--card-shadow);padding:21px 24px 17px;margin-bottom:26px}
.ef-say-k{font-size:10.5px;font-weight:600;letter-spacing:.02em;color:var(--faint);margin-bottom:12px}
.ef-say-h{font-family:var(--disp);font-size:25px;font-weight:600;letter-spacing:-.02em;line-height:1.44;margin:0}
.ef-say-h b{font-weight:700;font-variant-numeric:tabular-nums}
.ef-say-h b.loss{color:var(--bad)} .ef-say-h b.gain{color:var(--ok)} .ef-say-h b.cnt{color:var(--brand-2)}
.ef-say-s{font-size:13.5px;line-height:1.78;color:var(--dim);margin:10px 0 0;max-width:76ch}
.ef-say-s b{color:var(--text);font-weight:600}
.ef-say-s code{font-family:var(--mono);font-size:12.5px;color:var(--text);
  background:var(--surface-3);border-radius:6px;padding:1.5px 7px}
.ef-say-f{display:flex;flex-wrap:wrap;gap:5px 20px;margin-top:16px;padding-top:13px;
  border-top:1px solid var(--line-soft);font-size:11.5px;color:var(--faint)}
.ef-say-f b{font-family:var(--mono);font-weight:500;color:var(--dim);font-variant-numeric:tabular-nums}

/* 台账：一行 = 一个要修的东西。行是折叠的，展开才给依据和动作。 */
.ef-rail{display:flex;align-items:baseline;gap:11px;flex-wrap:wrap;margin:0 0 13px}
.ef-rail b{font-family:var(--disp);font-size:15px;font-weight:600;letter-spacing:-.005em}
.ef-rail span{font-size:12px;color:var(--faint)}
.dks{max-width:1060px}
.dk{--sv:var(--faint);position:relative;border-bottom:1px solid var(--line-soft)}
.dk:last-child{border-bottom:none}
.dk.high{--sv:var(--bad)} .dk.mid{--sv:var(--warn)} .dk.low{--sv:var(--brand)}
.dk-sum{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto 18px;
  align-items:center;gap:3px 18px;padding:13px 18px 14px 22px;cursor:pointer;list-style:none;
  transition:background .14s ease}
.dk-sum::-webkit-details-marker{display:none}
.dk-sum::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--sv)}
.dk-sum:hover{background:var(--surface-2)}
.dk-sum:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.dk[open]>.dk-sum{background:var(--surface-2)}
.dk-id{grid-column:1;grid-row:1;display:flex;align-items:baseline;gap:9px;min-width:0}
.dk-scope{font-size:10px;font-weight:700;letter-spacing:.1em;color:var(--sv);flex:none}
.dk-tgt{font-family:var(--mono);font-size:13px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dk-n{font-size:11px;color:var(--faint);flex:none;white-space:nowrap}
.dk-claim{grid-column:1;grid-row:2;font-size:12.5px;line-height:1.5;color:var(--dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 读数牌：一竖列右对齐的数字，这一页的签名件 */
.dk-plate{grid-column:2;grid-row:1/3;text-align:right;min-width:92px}
.dk-plate b{display:block;font-family:var(--disp);font-size:19px;font-weight:650;letter-spacing:-.025em;
  line-height:1.15;font-variant-numeric:tabular-nums;color:var(--sv);overflow-wrap:anywhere}
.dk.low .dk-plate b{color:var(--text)}
.dk-plate i{display:block;font-style:normal;font-size:10.5px;color:var(--faint);margin-top:3px}
.dk-cv{grid-column:3;grid-row:1/3;width:16px;height:16px;color:var(--faint);flex:none;transition:transform .2s ease}
.dk[open] .dk-cv{transform:rotate(90deg)}
.dk-body{padding:0 22px 18px 22px;background:var(--surface-2)}
.dk-it{padding-top:14px;margin-top:14px;border-top:1px dashed var(--line)}
.dk-it:first-child{padding-top:2px;margin-top:0;border-top:none}
.dk-ih{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-bottom:7px}
.dk-ih h4{font-family:var(--disp);font-size:14px;font-weight:600;letter-spacing:-.005em;margin:0}
.dk-im{font-family:var(--mono);font-size:11.5px;color:var(--faint);font-variant-numeric:tabular-nums;margin-left:auto}
.dk-d{font-size:12.5px;line-height:1.78;color:var(--dim);margin:0;max-width:74ch}
.dk-a{display:flex;gap:9px;font-size:12.5px;line-height:1.72;color:var(--text);margin:9px 0 0;max-width:74ch}
.dk-a::before{content:"→";flex:none;color:var(--sv);font-weight:700}
.dk-go{margin-top:15px}
.dk-ok{padding:32px 28px;text-align:center;color:var(--faint);font-size:12.5px;line-height:1.9}

/* 全局读数：五个比率并列，数字保持墨色，好坏交给信号灯——机箱一直是这么说话的。 */
.ef-reads{display:grid;grid-template-columns:repeat(5,minmax(0,1fr))}
.ef-read{padding:14px 18px 16px;min-width:0}
.ef-read+.ef-read{border-left:1px solid var(--line-soft)}
.ef-read .k{display:flex;align-items:center;gap:7px;font-size:10.5px;font-weight:600;letter-spacing:.1em;color:var(--faint)}
.ef-read .k i{width:7px;height:7px;border-radius:50%;flex:none;background:currentColor;
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) color-mix(in srgb,currentColor var(--halo-alpha),transparent)}
.ef-read .v{font-family:var(--disp);font-size:20px;font-weight:600;letter-spacing:-.02em;line-height:1;
  margin-top:9px;font-variant-numeric:tabular-nums;color:var(--text)}
.ef-read .n{font-size:11.5px;color:var(--faint);margin-top:8px;line-height:1.55}
.lv-ok{color:var(--ok)} .lv-warn{color:var(--warn)} .lv-bad{color:var(--bad)} .lv-idle{color:var(--faint)}
/* 首字节前：一根三段条，颜色分的是责任——灰是上游的，橙是白等的，紫是我们自己的。 */
.ef-split{display:flex;height:6px;border-radius:3px;background:var(--surface-3);overflow:hidden;margin-top:9px}
.ef-split i{display:block;height:100%;flex:none;width:0;transition:width .62s cubic-bezier(.22,.61,.25,1)}
.ef-split i.up{background:var(--text-45-on-surface-3)}
.ef-split i.fo{background:var(--warn)}
.ef-split i.gw{background:var(--brand)}
.ef-split+.n{margin-top:7px}

/* 凭据面板：三张表挤在一个面板里轮流出场，不是三段各自占一屏。 */
.ef-tabs{display:flex;align-items:stretch;gap:2px;padding:8px 10px 0;background:var(--surface-2);border-bottom:1px solid var(--line)}
.ef-tabs button{position:relative;padding:9px 15px 10px;border-radius:9px 9px 0 0;background:transparent;
  color:var(--faint);font-weight:600;font-size:12.5px;white-space:nowrap;transition:color .15s ease,background .15s ease}
.ef-tabs button:not(.on):hover{color:var(--dim);background:var(--surface-3)}
.ef-tabs button.on{color:var(--text);background:var(--surface)}
.ef-tabs button.on::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;background:var(--surface)}
.ef-tabs button i{font-style:normal;margin-left:6px;font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.ef-tabs button:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.ef-tabnote{padding:12px 18px 13px;font-size:11.5px;color:var(--faint);line-height:1.7;
  border-bottom:1px solid var(--line-soft);max-width:96ch}
/* 面板是 overflow:hidden 的（圆角要裁），所以窄屏上表会被切掉右边几列而不是
   变得可滚——给表自己一条横轨，切掉的列滑得回来。 */
.ef-tw{overflow-x:auto}
/* 数字列右对齐 + 等宽——一列数要能上下对位比大小，不然表就只是排版整齐的噪音。
   （旧版这三张表写了 class="num" 却没人给它定过样式，所有读数一直左贴着列头。） */
.ef-t th.num,.ef-t td.num{text-align:right}
.ef-t td.num{font-family:var(--mono);font-size:12.5px;font-variant-numeric:tabular-nums}
.ef-t td.mono{font-size:12.5px}
.ef-more{display:flex;align-items:center;gap:9px;width:100%;padding:11px 18px;border-top:1px solid var(--line-soft);
  background:var(--surface-2);font-size:11.5px;color:var(--faint);cursor:pointer;text-align:left}
.ef-more:hover{color:var(--dim)}
/* 表里的行如果正被上面的台账点名，就在名字前点一盏同色的灯——凭据和结论对得上号。 */
.ef-dot{display:inline-block;width:6px;height:6px;border-radius:50%;margin-right:8px;vertical-align:1px;background:currentColor;
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) color-mix(in srgb,currentColor var(--halo-alpha),transparent)}
.ef-dot.high{color:var(--bad)} .ef-dot.mid{color:var(--warn)} .ef-dot.low{color:var(--brand)}
/* 表里的「无后备」是风险标记，就该长得像风险标记，不是中性小胶囊 */
.ef-tag{font-size:10px;font-weight:600;letter-spacing:.06em;padding:2px 7px;border-radius:999px;white-space:nowrap;
  color:var(--warn);background:var(--warn-a12);border:1px solid var(--warn-a30)}
/* 区标题：一个真正的中号标题 + 一行注解，而不是两句同样大小的方法论。 */
.ef-h{margin:0 0 12px}
.ef-h b{display:block;font-family:var(--disp);font-size:15px;font-weight:600;letter-spacing:-.005em}
.ef-h span{display:block;font-size:12px;color:var(--faint);margin-top:5px;line-height:1.6;max-width:80ch}
.ef-cov{font-size:11.5px;color:var(--faint);margin-top:11px;line-height:1.7;max-width:84ch}
@media(max-width:1080px){
  .ef-reads{grid-template-columns:repeat(3,minmax(0,1fr))}
  .ef-read:nth-child(3n+1){border-left:none}
  .ef-read:nth-child(n+4){border-top:1px solid var(--line-soft)}
}
.bar-cell{display:flex;align-items:center;gap:8px;min-width:130px}
.bar-cell .bar{flex:1;height:6px;border-radius:3px;background:var(--line-soft);overflow:hidden;min-width:44px}
.bar-cell .bar i{display:block;height:100%;background:var(--brand)}
.bar-cell .bar i.warn{background:var(--warn)} .bar-cell .bar i.bad{background:var(--bad)}
.bar-cell b{font-variant-numeric:tabular-nums;font-weight:600;font-size:12px;min-width:44px;text-align:right}

/* ---------- 体检 / PROBE (active synthetic ping) ----------
   Signature: a probe fires a pulse down the gateway→upstream wire and waits for
   the echo. Pending = a dot travelling the line; resolved = the lamp snaps
   green/red with the round-trip stamped. Reuses the overview's wire/lamp idiom,
   but makes it active. */
.probe-bar{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:20px}
.probe-scope{min-width:0}
.ps-main{display:flex;align-items:center;gap:10px}
.ps-k{font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dim)}
.ps-v{font-family:var(--disp);font-size:16px;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:46vw}
.ps-clear{background:var(--surface-3);border:1px solid var(--line);color:var(--dim);font-size:11.5px;padding:3px 9px;border-radius:7px;flex:none}
.ps-clear:hover{color:var(--brand-2);border-color:var(--brand)}
.ps-sub{font-size:12px;color:var(--faint);margin-top:5px}
.probe-act{display:flex;align-items:center;gap:14px}
.probe-last{font-size:12px;color:var(--faint)}
#pb-run svg{width:15px;height:15px}
#pb-run.running{opacity:.65;cursor:default}

.pcard{gap:13px}
.pcard .hc-name{flex:1}
.pwire-row{display:flex;align-items:center}
.psource{width:10px;height:10px;border-radius:50%;background:var(--brand);flex:none;
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--brand-ahalo)}
.pwire{position:relative;height:2px;flex:1;margin:0 9px;border-radius:2px;background:repeating-linear-gradient(90deg,var(--line) 0 5px,transparent 5px 10px)}
.pwire.live{background:linear-gradient(90deg,var(--ok),var(--ok-55-on-surface))}
.pwire.dead{background:linear-gradient(90deg,var(--bad),var(--bad-60-on-surface))}
.pwire.firing{background:var(--brand-dim)}
.pwire .pdot{position:absolute;top:50%;left:0;width:7px;height:7px;border-radius:50%;margin-top:-3.5px;opacity:0}
.pwire.firing .pdot{opacity:1;background:var(--brand-2);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--brand-2-ahalo);animation:ptravel 1.05s cubic-bezier(.55,0,.45,1) infinite}
@keyframes ptravel{0%{left:0}100%{left:calc(100% - 7px)}}
.ptarget{display:inline-flex;align-items:center;gap:7px;background:var(--surface-3);border:1px solid var(--line);border-radius:999px;padding:5px 12px;font-size:12.5px;font-weight:500;flex:0 1 auto;min-width:0;max-width:52%}
.ptarget.on{border-color:var(--ok-40-on-line)}
.ptarget.off{border-color:var(--bad-45-on-line)}
.pt-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.lamp-pending{background:var(--brand-2);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--brand-2-ahalo);animation:blink 1s steps(2,start) infinite}

.pverdict{display:flex;align-items:center;gap:10px}
.pv-stat{display:inline-flex;align-items:center;gap:8px;font-family:var(--disp);font-size:17px;font-weight:600;letter-spacing:-.01em}
.pv-ok{color:var(--ok)} .pv-bad{color:var(--bad)} .pv-pending{color:var(--dim);font-size:15px}
.pv-rtt{font-family:var(--mono);font-size:12.5px;color:var(--dim);margin-left:auto}
.pspin{width:13px;height:13px;border-radius:50%;border:2px solid var(--line);border-top-color:var(--brand-2);display:inline-block;animation:pspin .7s linear infinite}
@keyframes pspin{to{transform:rotate(360deg)}}
.pdetail{font-size:12px;color:var(--dim);font-family:var(--mono);line-height:1.55;background:var(--surface-2);border:1px solid var(--line-soft);border-radius:8px;padding:8px 10px;word-break:break-word}
.pdetail.err{color:var(--bad);background:var(--bad-a7);border-color:var(--bad-a18)}
/* 试过哪几种格式。只有真的试过一种以上才画——一条线只开一种格式时，
   这行字会把「本来就没得选」说成「我们挑了挑」。 */
.ptried{margin-top:7px;font-size:11.5px;color:var(--faint);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.pt-fmt{font-family:var(--mono);font-size:11px;padding:1px 7px;border-radius:5px;border:1px solid var(--line)}
.pt-fmt.ok{color:var(--ok);border-color:var(--ok-a40);background:var(--ok-a12)}
.pt-fmt.bad{color:var(--bad);border-color:var(--bad-a18);background:var(--bad-a7)}
.ptried-why{flex-basis:100%;color:var(--faint);opacity:.85}
.pc-eff{font-family:var(--mono);font-size:11px;color:var(--brand-2);flex:none}
.lamp-none{background:var(--faint)}
/* probe-model manager (drawer) */
.pm-list{display:flex;flex-direction:column;gap:14px;margin-top:8px}
.pm-group{display:flex;flex-direction:column;gap:6px}
.pm-row{display:flex;align-items:center;justify-content:space-between;gap:10px;background:var(--surface-2);border:1px solid var(--line-soft);border-radius:8px;padding:7px 11px}
.pm-row .mono{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pm-x{background:transparent;color:var(--faint);font-size:16px;line-height:1;padding:1px 8px;border-radius:6px;flex:none}
.pm-x:hover{color:var(--bad);background:var(--bad-a9)}
/* test-model picker (drawer, opened from a provider's 测试 button) */
.tm-list{display:flex;flex-direction:column;gap:6px;margin:8px 0 2px;max-height:max(340px,48dvh);overflow-y:auto}
.tm-row{display:flex;align-items:center;gap:10px;background:var(--surface-2);border:1px solid var(--line-soft);border-radius:8px;padding:8px 11px;cursor:pointer;text-align:left;width:100%;color:inherit}
.tm-row:hover{border-color:var(--brand-40-on-line-soft)}
.tm-row.on{border-color:var(--brand);background:var(--brand-8-on-surface-2)}
.tm-row .mono{font-size:12.5px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tm-dot{width:9px;height:9px;border-radius:50%;border:2px solid var(--faint);flex:none}
.tm-row.on .tm-dot{border-color:var(--brand);background:var(--brand)}
.tm-src{font-size:10.5px;padding:1px 7px;border-radius:999px;flex:none;background:var(--surface-3,var(--surface-2));color:var(--faint);border:1px solid var(--line-soft)}
.tm-src.bound{color:var(--brand);border-color:var(--brand-a35)}
.tm-src.traffic{color:var(--ok);border-color:var(--ok-a32)}

/* ---------- 用量看板 / USAGE ----------
   条件和维度是两个旋钮，不是一个。上面的配线盘（与日志页同一套：粗调一排、
   细调一排、拧过的留一张条件牌）决定「算哪些调用」；账本自己的导轨决定
   「按什么摊开」——同一批调用，换个维度就是另一种读法，不是另一块板。

   签名件是分账条：每行一条按窗口内最大花费归一化的横条，暗的一段是付给
   上游的成本、亮的一段是留下的毛利。竖着看，这一列就是花费直方图；横着看，
   这笔钱有多少出了门。没配成本价时它退回 token 进出比例——同一个部件、
   两种读法，而不是两个部件。 */
.ku-filters{margin-bottom:18px}
.ku-panel{overflow:hidden}
.ku-scroll{overflow-x:auto}
/* 账本导轨：一句话读下来——「按 [维度] 汇总」，右边挂着这批数字的出口 */
.ku-rail{display:flex;align-items:center;gap:8px 11px;flex-wrap:wrap;padding:10px 14px;border-bottom:1px solid var(--line-soft)}
.ku-rail-k{font-size:12px;color:var(--faint);flex:none}
.ku-rail-n{font-size:12px;color:var(--faint);font-variant-numeric:tabular-nums}
.ku-rail-acts{display:flex;align-items:center;gap:4px;margin-left:auto}
.ku-act{font-size:12px;font-weight:600;color:var(--dim);background:none;border-radius:7px;padding:5px 9px;
  white-space:nowrap;transition:color .14s,background .14s}
.ku-act:hover{color:var(--brand-2);background:var(--brand-dim)}
.ku-act:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.ku-act[disabled]{color:var(--faint);pointer-events:none}

.ku-table th.num,.ku-table td.num{text-align:right;font-variant-numeric:tabular-nums}
.ku-table th.sortable{cursor:pointer;user-select:none;white-space:nowrap}
.ku-table th.sortable:hover{color:var(--brand-2)}
.ku-sort{color:var(--brand-2);margin-left:3px;font-size:10px}
.ku-table td.num{font-family:var(--mono);font-size:12.5px;color:var(--text)}
.ku-table td.zero{color:var(--faint)}
.ku-tot{font-weight:600}
.ku-id{display:flex;align-items:baseline;gap:9px;min-width:0}
.ku-name{font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ku-name.gone{color:var(--faint);font-style:italic}
button.ku-name{background:transparent;padding:0;text-align:left;cursor:pointer;transition:color .14s}
button.ku-name:hover{color:var(--brand-2)}
button.ku-name:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.ku-prefix{font-family:var(--mono);font-size:11px;color:var(--faint);flex:none}
/* 运营备注跟在显示名后面、不跟用户名抢等宽字体：这块板按人摊花费，微信号写在
   用户页却不出现在这一列，找人就得再翻一页。没有备注时这一段不占位。 */
.ku-memo{font-family:var(--sans);font-size:11.5px;color:var(--dim);font-weight:500;flex:none;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px}
.ku-board{font-size:11px;color:var(--faint);background:none;padding:0;flex:none;opacity:0;transition:opacity .14s,color .14s}
tr:hover .ku-board,.ku-board:focus-visible{opacity:1}
.ku-board:hover{color:var(--brand-2)}
.ku-req{display:flex;align-items:baseline;gap:7px;justify-content:flex-end}
.ku-err{color:var(--bad);font-size:11px;font-family:var(--sans);font-weight:600}
/* 展开箭头：这一行还能往里拆一层 */
.ku-caret{display:flex;align-items:center;justify-content:center;width:18px;height:18px;flex:none;margin-right:-2px;
  border-radius:5px;background:none;color:var(--faint);transition:color .14s,background .14s,transform .16s ease}
.ku-caret:hover{color:var(--brand-2);background:var(--brand-dim)}
.ku-caret:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.ku-caret.open{transform:rotate(90deg);color:var(--brand-2)}
.ku-caret svg{width:11px;height:11px}
/* 分账条 */
.ku-split{display:flex;align-items:center;gap:11px;min-width:210px}
.ku-bar{height:9px;flex:1;min-width:130px;border-radius:5px;overflow:hidden;background:var(--surface-3)}
.ku-bar .fill{display:flex;height:100%;border-radius:5px;overflow:hidden;min-width:3px;
  animation:kugrow .34s cubic-bezier(.2,.8,.3,1) both}
.ku-bar .fill>span{display:block;height:100%;min-width:2px}
@keyframes kugrow{from{transform:scaleX(0);transform-origin:left}to{transform:none}}
/* 成本一段要比空槽亮出一档才看得出分界，又不能亮到跟毛利抢眼 */
.ku-bar .cost{background:var(--faint-a90)}
.ku-bar .keep{background:var(--brand)}
.ku-bar .loss{background:var(--bad)}
.ku-bar .in{background:var(--gm)} .ku-bar .out{background:var(--brand)}
.ku-pct{font-family:var(--mono);font-size:11px;color:var(--faint);flex:none;width:56px;text-align:right;letter-spacing:.02em}
.ku-pct.loss{color:var(--bad)}
/* 拆开的一层：贴在母行下面的一张小表，缩进一格表示从属 */
/* 直接子选择器：这条只管母行下面那一格，不许穿透到内层表把它的留白也抹平 */
.ku-sub>td{background:var(--surface-2);padding:0!important}
.ku-subwrap{padding:11px 14px 13px 30px;border-left:2px solid var(--brand-a45)}
.ku-sub-k{font-size:11px;font-weight:600;letter-spacing:.06em;color:var(--faint);margin-bottom:8px;display:block}
/* 内层表是一张紧凑的清单，不是第二张主表：宽度收住，别让它横跨整页 */
table.ku-subt{width:100%;max-width:660px;font-size:12.5px;table-layout:fixed}
table.ku-subt td{padding:5px 0;border:none;background:none}
table.ku-subt td+td{padding-left:16px}
table.ku-subt td:last-child{padding-left:20px}   /* 数字与条之间要留一口气 */
table.ku-subt .ku-bar{min-width:0}               /* 内层的条按列宽走，不吃主账本那条的下限 */
table.ku-subt td.num{text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums}
table.ku-subt td:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
table.ku-subt col.c-n{width:auto} table.ku-subt col.c-x{width:72px}
table.ku-subt col.c-t{width:78px} table.ku-subt col.c-y{width:86px} table.ku-subt col.c-b{width:132px}
.ku-subt-more{font-size:11.5px;color:var(--faint);padding-top:6px}
/* 收窄时的让位顺序：先把六块表盘折成两排，再收起缓存与最近两列——
   钱和总量任何宽度下都不让位 */
@media (max-width:1260px){#ku-tiles{grid-template-columns:repeat(3,1fr)}}
@media (max-width:1180px){.ku-table th.opt,.ku-table td.opt{display:none}}
@media (max-width:720px){#ku-tiles{grid-template-columns:repeat(2,1fr)}}

/* ---------- 余额 · 纸带 (BALANCE TAPE) ----------------------------------
   一个客户的余额从来不是一个数。现金和赠金是两种钱——赠金会过期、提不出来、
   永远先花——而人民币现在只有两个出口：换成美元额度（单向、按官方价再扣），
   和付一个月的拼车租金。按 token 扣人民币已经停了，旧流水还在「旧口径计量」
   里，不混进这两口。四个事实压在一个「余额 ¥」列里，于是没人答得出「这人
   还能撑几天」和「这个车位卖亏了没有」——这一屏就是为了把它们拆开。

   所以这一列不是数字，是一条纸带：中间一条基准线，线上是他手上的钱，线下是
   这段时间花掉的钱。全表共用一把尺子，行与行才可比；而「下面比上面长」这件
   事本身就是警报，读的人不用再算第二遍。

   板上只有两种颜色：赠金用琥珀（它有一口在走的钟），见底用红。别的全是墨色，
   计量和席位靠质地分——计量是细齿（它本来就是几千口小咬），席位是实块（它
   一次一整口）。改用两种颜色区分也行，但这块板上每多一种颜色，警报就少一分。 */
.bt-key{display:flex;gap:6px 16px;flex-wrap:wrap;align-items:center;font-size:11px;color:var(--faint);margin:0 0 11px;letter-spacing:.02em}
.bt-key i{display:inline-block;width:15px;height:7px;border-radius:2px;margin-right:6px;vertical-align:-1px}
.bt-key .grp{font-weight:600;letter-spacing:.1em;text-transform:uppercase;font-size:11px;color:var(--dim)}
.bt-key .grp+span{margin-left:-6px}
.tp-cash{background:var(--text-a60)}
.tp-gift{background:var(--warn)}
.tp-meter{background:repeating-linear-gradient(90deg,var(--text-a46) 0 2px,transparent 2px 4px)}
.tp-rent{background:var(--text-a60)}

/* 基准线是刻度，不是数据：它必须比任何一条数据都轻，否则空行看起来像满行。

   类名带 bl- 前缀不是洁癖：审计页那条走带（.tape / .tape-row / .tape-day，
   本文件下方）也叫 tape，两个毫不相干的组件抢同一个裸类名，CSS 是全局的，
   于是两边同时坏——审计走带里的每个 <i>（操作者徽章、日期分隔线）被这里的
   `.tape i{position:absolute}` 抽出文档流，这里的每一行被那边的 `.tape::before`
   在 107px 处画一道竖发丝线。谁后写谁吃亏，而两边都后写过。 */
.bl-tape{position:relative;height:24px;min-width:120px}
.bl-tape .datum{position:absolute;left:0;right:0;top:11.5px;height:1px;background:var(--line)}
.bl-tape i{position:absolute;display:block;height:8px;min-width:2px;
        animation:kugrow .34s cubic-bezier(.2,.8,.3,1) both}
.bl-tape i.hold{bottom:14px;border-radius:2px 2px 0 0}
.bl-tape i.spend{top:14px;border-radius:0 0 2px 2px}
/* 见底：这个人的现金撑不到下一次有人看这块板。整条消耗染红，因为该改的不是
   某一段，是「他在花的比他有的多」这件事。 */
.bl-tape.short i.spend{background:var(--bad-a55)}
.bl-tape.short i.tp-meter{background:repeating-linear-gradient(90deg,var(--bad-a55) 0 2px,transparent 2px 4px)}
/* 一分钱没有、一分钱没花：只留基准线。空账号和「刚好花了 0」是两件事，
   而前者本来就该读起来像一条什么都没发生的空线。 */
.bl-tape.idle .datum{background:var(--line-soft)}

/* 到期列：这一列回答的不是「什么时候到期」，是「什么时候该管这个人」——
   计量客户会见底，拼车客户会到期，两者取更早的那个。都是日期，才排得出先后。 */
.bl-due{display:flex;flex-direction:column;align-items:flex-end;gap:1px;line-height:1.25}
.bl-due b{font-family:var(--mono);font-size:12.5px;font-weight:600;font-variant-numeric:tabular-nums}
.bl-due span{font-size:10.5px;color:var(--faint)}
.bl-due.soon b{color:var(--bad)} .bl-due.near b{color:var(--warn)}
.bl-seat{display:inline-flex;align-items:center;gap:5px;padding:2px 8px;border-radius:999px;
  border:1px solid var(--line);font-size:11px;color:var(--dim);white-space:nowrap}
.bl-seat b{font-weight:600;color:var(--text)}
.bl-seat .tier-ic{color:var(--text)}
.bl-name{display:flex;flex-direction:column;gap:3px;align-items:flex-start;min-width:0}
.bl-sub{font-size:11px;font-weight:400;color:var(--faint);margin-top:2px;white-space:nowrap}
/* 数字列里的可点项：pv-link 前面那颗圆点是给名字用的锚，摆进金额列会破掉
   右对齐——一列数字读的是尾数在不在一条线上。这里只留下划线。 */
.bl-numlink{background:transparent;padding:0;font:inherit;color:inherit;
  border-bottom:1px dashed var(--faint-a55);transition:color .14s,border-color .14s}
.bl-numlink:hover{color:var(--brand-2);border-bottom-color:var(--brand-2)}
.bl-numlink:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px}
.bl-gift{color:var(--warn)}
/* 美元额度那一行：$ 桶给自己的颜色，和 ¥ 一眼分得开；用完/到期褪成灰。 */
.bl-usd{color:var(--brand-2);font-weight:600}
.bl-usd.zero{color:var(--faint);font-weight:500}
.bl-zero{color:var(--faint)}
.bl-bar{display:flex;align-items:center;gap:12px 16px;flex-wrap:wrap;margin:0 0 12px}
.bl-note{font-size:11.5px;color:var(--faint);margin:10px 0 0}
/* 可排序表头：整个标题是一颗按钮，键盘也能排。箭头只画在当前那一列上。 */
.bl-th{all:unset;cursor:pointer;display:inline-flex;align-items:center;gap:2px;white-space:nowrap;border-radius:4px}
.bl-th:hover{color:var(--text)}
.bl-th:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.bl-table th[aria-sort] .bl-th{color:var(--text)}
.bl-allow{display:flex;flex-direction:column;align-items:flex-end;gap:1px}
.bl-allow .bl-sub{margin-top:0}

/* 本时段对账：期初 + 每一类进出 = 现在。两张卡各管一种币，¥ 和 $ 不进同一把尺子。
   条形是瀑布：每一行从上一行的累计值画到这一行的累计值，所以条形首尾相接，
   最后一行的右端必然落在「现在」那一条的右端上。 */
#bl-tiles{margin-bottom:14px}
.bl-bridges{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin:0 0 28px}
.bl-bridge{background:var(--card-grad);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:17px 19px 18px;box-shadow:var(--card-shadow);min-width:0}
.bl-bridge-hd{display:flex;align-items:baseline;justify-content:space-between;gap:8px 12px;flex-wrap:wrap;margin-bottom:14px}
.bl-bridge-hd .k{font-size:12.5px;font-weight:600;color:var(--dim)}
.bl-bridge-hd .st{font-size:11.5px;color:var(--faint)}
.bl-bridge-hd .st.ok{color:var(--ok)}
.bl-bridge-hd .st.bad{color:var(--bad)}
.bl-wf{display:grid;grid-template-columns:minmax(76px,auto) minmax(60px,1fr) auto;gap:7px 12px;align-items:center;font-size:12.5px}
.bl-wf .lbl{color:var(--dim);white-space:nowrap}
.bl-wf .amt{font-family:var(--mono);font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.bl-wf .trk{position:relative;height:10px}
.bl-wf .trk i{position:absolute;top:0;bottom:0;min-width:2px;border-radius:2px;
  animation:kugrow .34s cubic-bezier(.2,.8,.3,1) both}
.bl-wf .trk .zero{position:absolute;top:-3px;bottom:-3px;width:1px;background:var(--line)}
.bl-wf .wf-in{background:var(--ok-a60)}
.bl-wf .wf-out{background:var(--text-a46)}
.bl-wf .wf-gift{background:var(--warn)}
.bl-wf .wf-bad{background:var(--bad-a55)}
.bl-wf .wf-sum{background:var(--text-a60)}
.bl-wf .sum{font-weight:600;color:var(--text)}
.bl-wf .sum.amt{font-size:13.5px}
.bl-wf .rule{grid-column:1/-1;height:1px;background:var(--line-soft);margin:1px 0}
.bl-bridge-ft{display:flex;flex-wrap:wrap;gap:4px 16px;margin-top:14px;padding-top:12px;border-top:1px solid var(--line-soft);
  font-size:11.5px;color:var(--faint)}
.bl-bridge-ft b{font-family:var(--mono);font-weight:600;color:var(--dim);font-variant-numeric:tabular-nums}
@media (max-width:960px){.bl-bridges{grid-template-columns:1fr}}

/* ---------- 用户 · 值机台 ------------------------------------------------
   「余额」和「用户」是同一批人的两种读法：那边问钱，这边问「他现在怎么样」。
   一行答排查一个客户时先问的三句：他的请求会落到哪辆车（在开）、他走的是
   哪扇门（线路）、他最近有没有撞墙（近 24 小时）。

   门户账号的生命线（建号 → 验邮箱 → 登录 → 建密钥 → 调用）曾经画成一列五格
   刻度，2026-09-18 撤掉：22 个人里 20 个五格全满，246px 换来的是「他早就上路
   了」这一句废话。判定还在（usRows.steps / stalled），只有卡住的人在名字下得到
   一枚芯片，五步明细在它的悬停里。 */

/* 卡住：停在生命线同一步超过 7 天。7 天用的是本仓库既有的 BurnWindow 口径——
   一周是这块控制台衡量「习惯」的单位，不新发明一个阈值。整页只有它是一条待办，
   所以只有它拿黄色。 */
.us-chip.stall{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 40%,transparent);background:color-mix(in srgb,var(--warn) 8%,transparent)}

/* 身份格：一个人只出现一次。第一行是他的把手（用户名），第二行是关于他的话
   ——姓名、邮箱、备注、状态。两行之间给 5px，比行内 6px 的芯片间距略大，
   「哪些是一组」才不用猜。 */
.us-name{display:flex;flex-direction:column;gap:5px;align-items:flex-start;min-width:176px;padding:1px 0}
.us-id{display:inline-flex;align-items:center;gap:5px;min-width:0;max-width:100%}
.us-id .pv-link{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.us-id .us-vfy,.us-id .us-memo{flex:none}
/* 邮箱就是用户名时，它唯一还没说过的话是「验没验过」——所以它缩成用户名旁边的
   一枚勾，而不是把同一个串再印一遍。 */
.us-vfy{display:inline-flex;flex:none;color:var(--ok)}
.us-vfy svg{width:12px;height:12px}
.us-vfy.unver{color:var(--warn)}
.us-sub{display:flex;align-items:center;gap:5px 6px;flex-wrap:wrap;font-size:11px;color:var(--faint);min-height:19px}
.us-disp{all:unset;cursor:pointer;font-size:11.5px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px}
.us-disp:hover{color:var(--brand-2);text-decoration:underline}
.us-mail{display:inline-flex;align-items:center;gap:4px;max-width:100%;min-width:0;
  font-family:var(--mono);font-size:11px;color:var(--dim)}
.us-mail b{font-weight:400;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.us-mail svg{width:11px;height:11px;flex:none;color:var(--ok)}
.us-mail.unver svg{color:var(--warn)}
/* 备注是别人写在这个人身上的一句话，不是他的第二个名字。上一版它和姓名同款同色
   紧挨着，「测试账号002 上善若水🌟」读起来像一个人有两个名字——所以给它自己的
   容器：一枚便签图标 + 淡底 + 圆角，一眼就和名字分得开。 */
.us-memo{all:unset;box-sizing:border-box;display:inline-flex;align-items:center;gap:5px;max-width:230px;
  padding:2px 9px 2px 7px;border-radius:999px;border:1px solid var(--line);background:var(--line-soft);
  cursor:pointer;font-size:11px;color:var(--dim);line-height:1.45}
.us-memo>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.us-memo svg{width:12px;height:12px;flex:none;color:var(--faint)}
.us-memo:hover{color:var(--text);border-color:var(--brand-a35)}
.us-memo:hover svg{color:var(--brand-2)}
.us-memo:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* 没有备注时不占版面。多数行永远不需要备注，而常驻的「＋备注」把这件事印在了
   每一行上——一列 22 行、22 个都在喊同一句话，等于没喊。停在这一行上（或键盘
   走到它上面）才浮出来；触屏没有悬停，那里给一个常驻的淡记号。 */
.us-memo.add{width:20px;height:20px;padding:0;justify-content:center;border-style:dashed;background:none;
  opacity:0;transition:opacity .12s ease}
.us-memo.add svg{width:13px;height:13px}
tbody tr:hover .us-memo.add,.us-memo.add:hover,.us-memo.add:focus-visible{opacity:1}
@media (hover:none){.us-memo.add{opacity:.55}}
.us-chip{display:inline-flex;align-items:center;padding:1px 7px;border-radius:999px;
  border:1px solid var(--line);font-size:10.5px;font-weight:600;color:var(--faint);letter-spacing:.02em}
.us-chip.off{color:var(--bad);border-color:var(--bad-a40)}
.us-chip.test{color:var(--brand-2);border-color:var(--brand-a35);background:var(--brand-a5)}
.us-chip.test.expired{color:var(--bad);border-color:var(--bad-a40);background:var(--bad-a6)}
/* Claude Desktop 钢印：所有者已为该用户开通 Desktop（desktop_users 有行）。只是一个
   显示器图标，悬停说明开通时间；点用户列表和客户工作台上的钢印打开 Desktop 抽屉。
   凡是印用户名字的地方都盖这一枚（dkStamp）。 */
.dk-stamp{display:inline-flex;align-items:center;gap:3px;flex:none;height:16px;padding:0 5px;border-radius:5px;
  border:1px solid var(--brand-a35);background:var(--brand-a8);color:var(--brand-2);
  font-family:var(--sans);font-size:10px;font-weight:700;letter-spacing:.04em;line-height:1;vertical-align:middle;white-space:nowrap}
.dk-stamp svg{width:11px;height:11px;flex:none}
button.dk-stamp{cursor:pointer;margin:0}
button.dk-stamp:hover{border-color:var(--brand-a55)}
.us-id .dk-stamp,.bl-name .dk-stamp,.tp-who .dk-stamp,.feed-who .dk-stamp{margin-left:6px}
.cp-seat .dk-stamp,.cp-lendchip .dk-stamp,.sp-nm .dk-stamp,.sp-gonename .dk-stamp,.ap-ppl .nm .dk-stamp{height:14px;padding:0 3px;margin-left:4px;align-self:center}
/* 份额行的名字格只有 76px（和上面油表同一把尺子，不能加宽）：这里的钢印去掉边框和
   底色，只留 11px 的图标，四个字的备注名（结构人生）还能完整印下。 */
.sp-nm .dk-stamp{margin-left:-2px;height:12px;padding:0;border:0;background:transparent}
.cp-seat .dk-stamp svg,.cp-lendchip .dk-stamp svg,.sp-gonename .dk-stamp svg,.ap-ppl .nm .dk-stamp svg{width:10px;height:10px}
.sp-nm .dk-stamp svg{width:11px;height:11px}
.cp-seat .sid .dk-stamp{flex:none}
#cwb-title .dk-stamp,.pv-node .dk-stamp{margin-left:8px;height:18px;font-size:11px}
/* 车位的期限：芯片说他坐哪种车，这一行说这辆车还能坐到几时。日期与「剩多久」
   分列两端，一竖列扫下去比较的是同一个位置的两个数。近了变黄、进最后一天变红
   ——续费这件事只有在还来得及的时候提醒才有意义。
   间距交给 .us-ridecell 的 gap：一格里三行各自再加一个 margin，三行就有三种行距。 */
.us-seatterm{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  width:100%;max-width:208px;font-size:10.5px;line-height:1.2;color:var(--faint)}
.us-seatterm .d{white-space:nowrap;font-variant-numeric:tabular-nums}
.us-seatterm .l{white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--dim);margin-left:auto}
.us-seatterm .us-chip{padding:0 6px;line-height:1.35;flex:none}
.us-seatterm.warn .l{color:var(--warn)}
.us-seatterm.bad .l{color:var(--bad);font-weight:600}
/* 车上的油：拼车乘客的通道格下面挂整车额度窗口的条子。答的是这一页反复被问
   的那句「他是不是把车开到没油了」——以前这个答案只在车队页和用户档案抽屉里，
   认出一个「其实是额度用尽」的人要跳两页。条子和车队页油表同一个数据源
   （seat-pools）、同一把尺（占整窗的 %）；他自己吃掉的份额进 title。 */
/* 两根表并排，不再一根一行。竖着摞的那版把这一格撑到九十多像素高，而隔壁
   「线路」「近 24 小时」都只有三四行——整张表的行高于是由最不紧要的那半格决定。
   横过来之后标签只剩两三个字的位置，留下的是「哪个窗口」（5h / 周），全名进 title。
   两根表把这一行填满（flex:1 1 auto），所以第二根的右缘落在固定的一处——期限行的
   「剩 N 天」用同一个宽度靠右，两行右缘对齐，一竖列扫下去比较的是同一个位置。
   条子那一格吃掉多余的宽（1fr），数字才不会跟着行宽浮动；用尽的那根写
   「3h20 回血」比别的宽，允许它换行——那是唯一值得多占一行的状态。 */
.us-fuel{display:flex;flex-wrap:wrap;align-items:center;gap:3px 12px;width:100%;max-width:208px}
.us-fuel .fr{flex:1 1 auto;min-width:0;display:grid;grid-template-columns:auto minmax(20px,1fr) auto;gap:5px;
  align-items:center;font-size:10.5px;color:var(--faint);line-height:1.2}
.us-fuel .fr.note{grid-template-columns:1fr;max-width:100%}
.us-fuel .fl{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.us-fuel .fn{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dim)}
.us-fuel .fr.warn .fn{color:var(--warn)}
.us-fuel .fr.warn .qrail i.fill{background:var(--warn)}
.us-fuel .fr.bad .fn{color:var(--bad);font-weight:600}
.us-fuel .fr.bad .qrail i.fill{background:var(--bad)}
/* 在开：一张车牌三行——身份（车型 + 号）、合约（到期 · 还剩）、油表。
   车型和车号以前各占一行，可它们是同一句话的两半；分开写只是把这一格摞高了
   一行，而这一格的高度就是整张表的行高。三行行距收成同一个值（gap:3px），
   扫下来是一条带子，不是一摞卡片。 */
.us-ridecell{display:flex;flex-direction:column;gap:3px;align-items:flex-start;min-width:264px}
.us-ride{display:flex;align-items:center;gap:6px;flex-wrap:wrap;max-width:100%;min-width:0}
.us-ride.sub{margin-top:2px}
/* 车型芯片挨着车号站，不再自己占一行，所以收一号：它是限定词，车号才是名字。 */
.us-ride .chan-chip{font-size:11px;padding:2px 8px}
.us-car{all:unset;box-sizing:border-box;display:inline-block;flex:0 1 auto;min-width:0;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;font-size:11.5px;color:var(--text);cursor:pointer;line-height:1.3}
.us-car:hover{color:var(--brand-2);text-decoration:underline}
.us-car:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px}
.us-car.none{cursor:default;color:var(--faint);font-size:11px}
.us-car.none:hover{color:var(--faint);text-decoration:none}
.us-car.none.bad{color:var(--bad);font-weight:600;font-size:11.5px}
.us-car.none.bad:hover{color:var(--bad)}
.us-chip.ride{color:var(--dim);border-color:var(--line)}
.us-chip.warn{color:var(--warn);border-color:var(--warn-a40,color-mix(in srgb,var(--warn) 40%,transparent))}
.us-chip.flag{all:unset;box-sizing:border-box;display:inline-flex;align-items:center;padding:1px 7px;border-radius:999px;
  font-size:10.5px;line-height:1.5;border:1px solid var(--brand-a35);color:var(--brand-2);background:var(--brand-a5);cursor:pointer}
.us-chip.flag.warn{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 40%,transparent);background:color-mix(in srgb,var(--warn) 8%,transparent)}
.us-chip.flag:hover{border-color:var(--brand)}
.us-chip.alert{color:var(--bad);border-color:var(--bad-a40);background:var(--bad-a6);font-weight:600}
/* 线路：两三行「什么 · 怎么样」，和油表同一套字号与行距。 */
/* min-width 是给表格自动布局看的：不写，这一列会被两侧的宽列挤到六十像素，值全被截成「开 · …」。 */
/* 线路：第一行模式名（和当前账号格的账号名同一号字），第二行近 24 小时实际入口占比。 */
.us-line{display:grid;gap:3px;width:100%;min-width:150px;max-width:160px}
.us-line .usl-top{display:flex;align-items:center;flex-wrap:wrap;gap:3px 6px;min-width:0}
.us-line .usl-mode{all:unset;box-sizing:border-box;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;font-size:11.5px;line-height:1.3;color:var(--text);cursor:pointer}
.us-line .usl-mode:hover{color:var(--brand-2);text-decoration:underline}
.us-line .usl-mode:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px}
.us-line .us-chip{flex:none}
.us-line .usl-v{font-size:10.5px;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--dim);font-variant-numeric:tabular-nums}
.us-line .usl-v.ok{color:var(--ok)}
.us-line .usl-v.warn{color:var(--warn);font-weight:600}
.us-line .usl-v.dim{color:var(--faint)}
/* 近 24 小时：整格是一个按钮，开客户工作台的请求页签。 */
.us-pulse{all:unset;box-sizing:border-box;display:flex;flex-direction:column;gap:3px;align-items:flex-start;
  width:100%;min-width:156px;max-width:176px;padding:3px 6px;margin:-3px -6px;border-radius:8px;cursor:pointer;line-height:1.25}
.us-pulse:hover{background:var(--surface-3)}
.us-pulse:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.us-pulse .usp-n{font-size:11.5px;color:var(--dim);white-space:nowrap;font-variant-numeric:tabular-nums}
.us-pulse .usp-n b{font-size:12.5px;font-weight:600;color:var(--text)}
.us-pulse .usp-n em{font-style:normal;color:var(--bad);font-weight:600}
.us-pulse .usp-r{font-size:10.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.us-pulse .usp-r.gate{color:var(--warn)}
.us-pulse .usp-r.up{color:var(--bad)}
.us-pulse .usp-r.dim{color:var(--faint)}
.us-pulse .usp-r em{font-style:normal;color:var(--bad);font-weight:600}
.us-pulse .usp-c{color:var(--faint)}
.us-pulse .usp-c.low{color:var(--warn);font-weight:600}
.us-pulse .rp-spark{height:18px;min-width:56px;width:100%;max-width:120px;margin-top:2px}
.us-pulse.none,.us-pulse.quiet{cursor:default}
.us-pulse.none .usp-n,.us-pulse.quiet .usp-n{color:var(--faint);font-size:11px}
.us-pulse.quiet{cursor:pointer}
.us-pulse.none:hover{background:none}
/* 「更多」按钮在有非默认开关时点亮，不然它和旁边五个灰图标长得一样。 */
.us-acts .us-more-on{color:var(--brand-2)}
/* 这张表的格子都是两三行的小读数，自带行距；上下内边距比通用表格收 2px，
   左右照旧 16px，列与列之间的缝不变。 */
.us-table td{padding-top:9px;padding-bottom:9px}
/* 七列的最小宽度：放不下时面板自己横滑。≤860px 换成卡片（见本节末尾），不再横滑。 */
.us-panel{overflow-x:auto}
/* 九个筛选排成一行要 780px：放不下时这一条自己横滑，不把整页撑出横向滚动
   （861–1020px 之间侧栏还在，内容区只有 580px 上下）。 */
#us-lens{max-width:100%;overflow-x:auto;scrollbar-width:none}
#us-lens::-webkit-scrollbar{display:none}
#us-lens button{flex:none}
.us-table{min-width:1224px}
.us-table th,.us-table td{padding-left:12px;padding-right:12px}
/* 有事的行左缘一道细红边，扫一眼就能停在它上面。 */
tbody tr[data-alert] td:first-child{box-shadow:inset 3px 0 0 var(--bad)}
.tile.tile-alert .v{color:var(--bad)}
/* ---- API Key 页：类型芯片、按用户分组 ---------------------------------------
   类型芯片是这一页最常扫的一列，颜色只分「怎么付钱」：订阅蓝、API 额度青、
   无法调用红，手动建的 API Key 和管理员 Key 用中性灰。文字保留：这是计费归类，
   只印图标就得悬停才分得清。 */
.ky-panel{overflow-x:auto}
.ky-table{min-width:1080px}
.ky-table th,.ky-table td{padding-left:12px;padding-right:12px}
.ky-table td{vertical-align:middle;white-space:nowrap}
.ky-table td.row-actions{width:1%}
/* 设备 Key 没有「换通道」：留一个看不见的占位，混排时各行的图标仍对齐 */
.ky-act-gap{visibility:hidden}
.ky-own{max-width:200px;overflow:hidden;text-overflow:ellipsis}
.ky-tier{margin-top:6px}
.ky-type{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;padding:2px 9px;
  border-radius:var(--r-pill);white-space:nowrap;width:fit-content;color:var(--dim);background:var(--faint-a12)}
.ky-type::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.ky-type.sub{color:var(--gm);background:var(--gm-a12)}
.ky-type.allow{color:var(--oa);background:var(--oa-a12)}
.ky-type.none{color:var(--bad);background:var(--bad-a12)}
.ky-type.api{color:var(--text);background:var(--brand-a12)}
.ky-sub{display:block;margin-top:4px;font-size:11.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:240px}
.ky-name{font-weight:600}
.ky-pfx{display:block;margin-top:3px;font-family:var(--mono);font-size:11px;color:var(--faint)}
.ky-use{font-variant-numeric:tabular-nums;white-space:nowrap}
.ky-use .ky-err{color:var(--bad);font-weight:600}
.ky-st select{padding:7px 30px 7px 12px;font-size:12.5px}
.ky-foot{margin:10px 2px 0;font-size:12px;color:var(--faint)}
.ky-foot:empty{display:none}
/* 按用户：一行一个人，点开就地展开他名下的 Key */
.ky-grp{cursor:pointer}
.ky-grp:hover td{background:var(--fill-4)}
.ky-grp .chev{color:var(--faint);transition:transform .18s;display:inline-block}
.ky-grp.open .chev{transform:rotate(90deg)}
.ky-grp td.ky-who{white-space:nowrap}
.ky-mix{display:flex;gap:6px;flex-wrap:wrap}
.ky-mix .ky-type{padding:2px 8px}
tr.ky-inner>td{padding:0 12px 14px 40px;background:var(--fill-4)}
.ky-inner table{width:100%;background:var(--surface);border:1px solid var(--line-soft);border-radius:12px;overflow:hidden}
.ky-inner table td{padding-top:8px;padding-bottom:8px}
/* 更多抽屉：一行一颗开关，说明在左、开关在右，四边留足。 */
.us-more{display:flex;flex-direction:column;gap:14px}
.us-more-row{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:14px 16px;
  border:1px solid var(--line);border-radius:10px;background:var(--surface-2)}
.us-more-row .t{display:flex;flex-direction:column;gap:5px;min-width:0}
.us-more-row .t b{font-size:13px;font-weight:600;color:var(--text)}
.us-more-row .t span{font-size:11.5px;line-height:1.6;color:var(--dim)}
.us-more-row .sw{flex:none;margin-top:2px}
.us-more-row.ro{background:transparent}
.us-more-row .us-chip{flex:none;white-space:nowrap;margin-top:3px}
/* 抽屉里把「运营的开关」和「客户自己的线路偏好」分成两组：后者替他改会写审计，
   他下次打开门户就看得到，所以标题要先把这一点说出来。 */
.us-more-sub{margin:6px 0 -6px;font-size:11.5px;line-height:1.6;color:var(--faint)}
.us-more-row .t span em{font-style:normal;color:var(--warn)}
/* 最后出现：运维问的从来不是「他上次登录的时间戳」，是「他多久没来了」。 */
.us-last{display:flex;flex-direction:column;align-items:flex-end;gap:1px;line-height:1.25}
.us-last b,.us-last span{white-space:nowrap}
.us-last b{font-size:12.5px;font-weight:600;font-variant-numeric:tabular-nums}
.us-last span{font-size:10.5px;color:var(--faint)}
.us-last.never b{color:var(--faint);font-weight:500}
/* 停用的行整行退到背景里：这比第七列一个开关更快地说出「这个人现在用不了」。 */
tbody tr.us-off td{color:var(--faint)}
tbody tr.us-off .pv-link,tbody tr.us-off .us-last b{color:var(--faint)}
tbody tr.us-expired td{color:var(--faint)}
tbody tr.us-expired .pv-link,tbody tr.us-expired .us-last b{color:var(--faint)}
/* 动作分三组：看 · 改 · 毁。十枚一样灰、一样大的图标并排要 246px，其中大半每天
   点不到一次，而且「打开档案」和「永久删除」手感相同。行上只留天天做的三件
   （启停、代客登录、充值），其余进 ⋯ 菜单，分组做成菜单里的小标题。
   代客登录留在行上是因为运营天天用它；只有所有者看得到，停用、到期、归档不画。 */
/* 这一格是 <td>：给它 display:flex 会把它从表格布局里摘出去（行分隔线会断在
   前一格的右缘，列宽也不再听 colgroup 的）。所以仍按 row-actions 那套行内排。 */
.us-acts>*{margin-left:7px;vertical-align:middle}
.us-kebab[aria-expanded="true"]{color:var(--brand-2);background:var(--brand-dim)}
/* 窄屏（≤860px，和底部导航同一档）：一行一张卡片，不再横滑。
   七列要 1224px，手机上一屏只装得下账号和半列，排查一个人得来回滑三屏。
   卡片从上到下：名字 + 最后出现 → 当前账号 → 线路 | 近 24 小时 → 余额 + 动作。
   <tr> 是一条可换行的 flex：两个伪元素各占一整行，一个把名字行和读数隔开，
   一个是动作行上面那条线；行距写在各格的 margin-top 上，不用 row-gap
   （零高的换行占位也会吃一份 row-gap，名字下面就多出一倍的空）。
   列名印在格子上方（data-th），表头整行收起。 */
@media (max-width:860px){
  #us-account-pane .us-panel{overflow:hidden;background:var(--surface)}
  #us-account-pane .us-table{min-width:0}
  #us-account-pane .us-table,#us-account-pane .us-table tbody{display:block}
  #us-account-pane .us-table thead,#us-account-pane .us-table colgroup{display:none}
  #us-account-pane .us-table tr{display:flex;flex-wrap:wrap;align-items:flex-start;column-gap:16px;
    padding:14px 14px 12px;border-bottom:1px solid var(--line-soft)}
  #us-account-pane .us-table tr:last-child{border-bottom:0}
  #us-account-pane .us-table td{display:block;min-width:0;max-width:none;padding:0;border:0;
    white-space:normal;overflow:visible;text-overflow:clip;text-align:left}
  #us-account-pane .us-table tr::before{content:"";order:3;flex:0 0 100%;height:0}
  #us-account-pane .us-table tr::after{content:"";order:7;flex:0 0 100%;height:0;margin-top:12px;border-top:1px solid var(--line-soft)}
  #us-account-pane .us-table tr.us-none::before,#us-account-pane .us-table tr.us-none::after{display:none}
  #us-account-pane .us-table tr.us-none{padding:0}
  #us-account-pane .us-table td.empty{flex:1 1 100%;padding:32px 20px;text-align:center}
  /* 有事的红边从第一格挪到整张卡片的左缘 */
  #us-account-pane .us-table tr[data-alert]{box-shadow:inset 3px 0 0 var(--bad)}
  #us-account-pane .us-table tr[data-alert] td:first-child{box-shadow:none}
  #us-account-pane .us-c-name{order:1;flex:1 1 0}
  #us-account-pane .us-c-last{order:2;flex:none;padding-top:1px}
  #us-account-pane .us-c-ride{order:4;flex:1 1 100%;margin-top:12px}
  #us-account-pane .us-c-line{order:5;flex:1 1 118px;margin-top:12px}
  #us-account-pane .us-c-pulse{order:6;flex:1 1 118px;margin-top:12px}
  /* 余额至少留 88px：360px 宽的手机上所有者的四个动作还排得进同一行；再窄动作就自己
     换到下一行靠右，不把余额挤成一个字一行。 */
  #us-account-pane .us-table td.us-c-bal{order:8;flex:1 1 0;min-width:88px;align-self:center;margin-top:10px;
    display:flex;flex-wrap:wrap;align-items:baseline;gap:3px 10px}
  #us-account-pane .us-acts{order:9;flex:none;align-self:center;margin-top:10px;margin-left:auto}
  #us-account-pane .us-table td[data-th]::before{content:attr(data-th);display:block;margin-bottom:6px;
    font-family:var(--sans);font-size:11px;font-weight:600;line-height:1.2;color:var(--faint)}
  #us-account-pane .us-table td.us-c-bal::before{margin-bottom:0}
  /* 名字是这张卡片的标题：放大一号；点的范围上下各多 5px，手指才点得准。 */
  #us-account-pane .us-name{min-width:0;gap:7px;padding:0}
  #us-account-pane .us-id{gap:7px}
  #us-account-pane .us-id .pv-link{font-size:14px;padding:5px 0;margin:-5px 0}
  #us-account-pane .us-memo.add{width:26px;height:26px;margin:-3px 0}
  #us-account-pane .us-sub{font-size:11.5px;gap:6px}
  #us-account-pane .us-disp,#us-account-pane .us-memo{max-width:100%}
  /* 桌面上给表格自动布局写的最小 / 最大宽度，在卡片里都撤掉：格子跟着卡片走。 */
  #us-account-pane .us-ridecell{min-width:0;gap:5px}
  #us-account-pane .us-seatterm,#us-account-pane .us-fuel{max-width:420px;font-size:11.5px}
  #us-account-pane .us-fuel .fr{font-size:11.5px}
  #us-account-pane .us-car{font-size:12.5px}
  #us-account-pane .us-line{min-width:0;max-width:none;gap:4px}
  #us-account-pane .us-line .usl-mode{font-size:12.5px}
  #us-account-pane .us-line .usl-v{font-size:11.5px;white-space:normal}
  #us-account-pane .us-pulse{min-width:0;max-width:none;margin:0;padding:0;gap:4px}
  #us-account-pane .us-pulse:hover{background:none}
  #us-account-pane .us-pulse .usp-n{white-space:normal}
  #us-account-pane .us-pulse .usp-r{font-size:11.5px;white-space:normal}
  #us-account-pane .us-bal{display:flex;flex-wrap:wrap;align-items:baseline;gap:3px 10px;min-width:0}
  #us-account-pane .us-bal>.bl-numlink{font-size:14px;font-weight:600}
  #us-account-pane .us-bal .bl-sub{margin-top:0;font-size:11.5px}
  #us-account-pane .us-acts>*{margin-left:4px}
  #us-account-pane .us-acts .btn.icon{width:36px;height:36px}
  #us-account-pane .us-acts>*:first-child{margin-left:0}
  #us-account-pane .us-acts .sw{padding:7px 12px 7px 8px}
  /* 小字链接和芯片看起来不变，可点的范围向外扩几像素（相邻两个之间仍留着缝，不会点串）。 */
  #us-account-pane .us-car,#us-account-pane .us-line .usl-mode{padding:5px 0;margin:-5px 0}
  #us-account-pane .us-bal .bl-numlink,#us-account-pane .us-memo:not(.add),
  #us-account-pane .us-sub button.us-chip{position:relative}
  #us-account-pane .us-bal .bl-numlink::after,#us-account-pane .us-memo:not(.add)::after,
  #us-account-pane .us-sub button.us-chip::after{content:"";position:absolute;inset:-7px -3px}
  #us-account-pane .us-bal .bl-numlink::after{inset:-9px -8px}
  /* 四块读数在手机上已经占掉半屏，和筛选条之间不再留桌面那 28px */
  #us-tiles{margin-bottom:14px}
  /* 平板竖屏放得下三格并排：当前账号、线路、近 24 小时同一行 */
  @media (min-width:640px){
    #us-account-pane .us-c-ride{flex:1.6 1 264px}
    #us-account-pane .us-c-line,#us-account-pane .us-c-pulse{flex:1 1 150px}
  }
}
.rowmenu-scrim{position:fixed;inset:0;z-index:38}
.rowmenu-scrim.sheet{background:var(--scrim);
  touch-action:none;overscroll-behavior:contain}
/* 行尾菜单：macOS 的上下文菜单——厚玻璃、圆角，行是圆角高亮 */
.rowmenu{position:fixed;z-index:39;min-width:208px;width:max-content;max-width:calc(100vw - 16px);
  padding:5px;background:var(--glass-thick);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  border:1px solid var(--rail-line);border-radius:var(--r-lg);
  box-shadow:var(--sheen),var(--shadow-3)}
.rowmenu:focus{outline:none}
.rm-head{display:none}
.rm-who{margin:2px 9px 6px;padding-bottom:7px;border-bottom:1px solid var(--line);
  font-size:11px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rm-grp{margin:9px 9px 3px;font-size:11.5px;font-weight:600;letter-spacing:0;color:var(--dim)}
/* 「毁」那一组先隔一条线再开始。 */
.rm-grp.cut{margin-top:9px;padding-top:10px;border-top:1px solid var(--line)}
.rm-item{display:flex;align-items:center;gap:10px;width:100%;padding:7px 9px;border-radius:var(--r-sm);
  background:none;color:var(--text);text-align:left;cursor:pointer;transition:background .12s,color .12s}
.rm-item i{display:flex;flex:none;color:var(--dim);transition:color .12s}
.rm-item i svg{width:15px;height:15px}
.rm-item .n{flex:1;min-width:0;font-size:12.5px;font-weight:600;white-space:nowrap}
.rm-item .c{flex:none;font-size:10.5px;font-weight:600;color:var(--brand-2)}
.rm-item:hover,.rm-item:focus-visible{background:var(--fill-3)}
.rm-item:hover i{color:var(--text)}
.rm-item:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.rm-item.danger:hover{background:var(--bad-a16);color:var(--bad)}
.rm-item.danger:hover i{color:var(--bad)}
/* 删除在静止状态就是红的：一个只在悬停时才变红的删除，扫视时和它上面那九条
   长得一模一样。归档不红——它收得回来。 */
.rm-item.gone .n{color:var(--bad)}
.rm-item.gone i{color:var(--bad-a60)}

/* 用户页第二张表是处置台，不是账号表的又一列。把窗口、判定依据、设备和动作
   放在同一平面上，运营才能从“有 12% non-cc”落到“该核对哪一个人哪台机器”。 */
.cr-bar{margin-bottom:12px}
.cr-panel th,.cr-panel td{padding-left:11px;padding-right:11px}
.cr-panel td{vertical-align:top}
.cr-who{display:flex;flex-direction:column;gap:3px;min-width:170px}
.cr-who b{font-size:12.5px}.cr-who span{font-size:11px;color:var(--faint)}
.cr-who .memo{max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--brand-2);font-weight:600}
.cr-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 9px;border-radius:999px;
  border:1px solid var(--line);font-size:11.5px;white-space:nowrap}
.cr-pill::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--faint)}
.cr-pill.high{color:var(--bad);border-color:var(--bad-a40);background:var(--bad-a9)}
.cr-pill.high::before{background:var(--bad-fill)}
.cr-pill.review{color:var(--warn);border-color:var(--warn-a40);background:var(--warn-a9)}
.cr-pill.review::before{background:var(--warn)}
.cr-pill.recovered{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 40%,transparent);background:color-mix(in srgb,var(--ok) 9%,transparent)}
.cr-pill.recovered::before{background:var(--ok)}
.cr-pill.observe{color:var(--dim)}
.cr-pill.disabled{color:var(--faint);border-style:dashed}.cr-pill.disabled::before{background:var(--line)}
.cr-count{display:flex;flex-direction:column;align-items:flex-end;gap:2px;white-space:nowrap}
.cr-count b{font-family:var(--mono);font-size:13px}.cr-count span{font-size:10.5px;color:var(--faint)}
.cr-rate{display:grid;grid-template-columns:50px 60px;align-items:center;gap:7px;min-width:117px}
.cr-rate b{font-family:var(--mono);font-size:12.5px;text-align:right}
.cr-rate i{display:block;height:5px;border-radius:999px;background:var(--line);overflow:hidden}
.cr-rate i::after{content:"";display:block;width:var(--p);height:100%;background:var(--rc,var(--warn));border-radius:inherit}
.cr-marks,.cr-ups{display:flex;gap:5px;flex-wrap:wrap;max-width:185px}
.cr-mark{padding:2px 6px;border:1px solid var(--line-soft);border-radius:5px;background:var(--surface-2);
  font-family:var(--mono);font-size:10.5px;white-space:nowrap}
.cr-marks .cr-mark{white-space:normal;line-height:1.4}
.cr-devs{display:flex;flex-direction:column;gap:5px;min-width:210px}
.cr-dev{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 10px;padding:6px 7px;
  border:1px solid var(--line-soft);border-radius:7px;background:var(--surface-2);font-size:11px}
.cr-dev .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cr-dev .meta{grid-column:1/-1;color:var(--faint);font-family:var(--mono);font-size:9.5px}
.cr-dev b{font-family:var(--mono);font-size:10.5px;white-space:nowrap;color:var(--dim)}
.cr-dev.live .meta{color:var(--ok)}
.cr-dev.retired{opacity:.72}
.cr-devs>.cr-ups{margin-top:3px}
.cr-action{display:flex;justify-content:flex-end;gap:6px;white-space:nowrap}
.cr-panel tr.cr-high td:first-child{box-shadow:inset 3px 0 0 var(--bad)}
/* 七列的最小宽度：放不下时面板自己横滑。≤860px 换成卡片，和用户管理页同一套做法。 */
.cr-panel{overflow-x:auto}
.cr-table{min-width:1120px}
/* 窄屏（≤860px）：一个用户一张卡片。从上到下：是谁 + 判定 → 判定请求 | non-cc 占比 →
   缺失特征 → 设备 Key 和最终线路 → 动作。<tr> 是可换行的 flex，两个伪元素各占一整行
   （名字行下面的换行、动作行上面那条线），行距写在各格的 margin-top 上。 */
@media (max-width:860px){
  #us-risk-pane .cr-panel{overflow:hidden;background:var(--surface)}
  #us-risk-pane .cr-table{min-width:0}
  #us-risk-pane .cr-table,#us-risk-pane .cr-table tbody{display:block}
  #us-risk-pane .cr-table thead{display:none}
  #us-risk-pane .cr-table tr{display:flex;flex-wrap:wrap;align-items:flex-start;column-gap:16px;
    padding:14px 14px 12px;border-bottom:1px solid var(--line-soft)}
  #us-risk-pane .cr-table tr:last-child{border-bottom:0}
  #us-risk-pane .cr-table td{display:block;min-width:0;max-width:none;padding:0;border:0;
    white-space:normal;overflow:visible;text-overflow:clip;text-align:left}
  #us-risk-pane .cr-table tr::before{content:"";order:3;flex:0 0 100%;height:0}
  #us-risk-pane .cr-table tr::after{content:"";order:8;flex:0 0 100%;height:0;margin-top:12px;border-top:1px solid var(--line-soft)}
  #us-risk-pane .cr-table tr.cr-none::before,#us-risk-pane .cr-table tr.cr-none::after{display:none}
  #us-risk-pane .cr-table tr.cr-none{padding:0}
  #us-risk-pane .cr-table td.empty{flex:1 1 100%;padding:32px 20px;text-align:center}
  #us-risk-pane .cr-table tr.cr-high{box-shadow:inset 3px 0 0 var(--bad)}
  #us-risk-pane .cr-table tr.cr-high td:first-child{box-shadow:none}
  #us-risk-pane .cr-c-who{order:1;flex:1 1 0}
  #us-risk-pane .cr-c-level{order:2;flex:none}
  #us-risk-pane .cr-c-count{order:4;flex:1 1 118px;margin-top:12px}
  #us-risk-pane .cr-c-rate{order:5;flex:1 1 118px;margin-top:12px}
  #us-risk-pane .cr-c-marks{order:6;flex:1 1 100%;margin-top:12px}
  #us-risk-pane .cr-c-devs{order:7;flex:1 1 100%;margin-top:12px}
  #us-risk-pane .cr-c-act{order:9;flex:none;margin-top:10px;margin-left:auto}
  #us-risk-pane .cr-table td[data-th]::before{content:attr(data-th);display:block;margin-bottom:6px;
    font-family:var(--sans);font-size:11px;font-weight:600;line-height:1.2;color:var(--faint)}
  /* 桌面上给列宽写的最小 / 最大宽度在卡片里撤掉，小字放大半号。 */
  #us-risk-pane .cr-who{min-width:0;gap:4px}
  #us-risk-pane .cr-who b{font-size:14px}
  #us-risk-pane .cr-who span{font-size:11.5px}
  #us-risk-pane .cr-who .memo{max-width:100%}
  #us-risk-pane .cr-count{align-items:flex-start;gap:4px;white-space:normal}
  #us-risk-pane .cr-count b{font-size:14px}
  #us-risk-pane .cr-count span{font-size:11.5px}
  #us-risk-pane .cr-rate{grid-template-columns:auto minmax(40px,96px);min-width:0;justify-content:start;gap:9px}
  #us-risk-pane .cr-rate b{font-size:14px;text-align:left}
  #us-risk-pane .cr-marks,#us-risk-pane .cr-ups{max-width:none;gap:6px}
  #us-risk-pane .cr-mark{font-size:11.5px}
  #us-risk-pane .cr-ups .cr-mark{white-space:normal;overflow-wrap:anywhere}
  #us-risk-pane .cr-devs{min-width:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:6px}
  #us-risk-pane .cr-devs>.cr-ups{grid-column:1/-1;margin-top:2px}
  /* 并排的两块等高，内容靠上：不让矮的那块把自己两行字撑开 */
  #us-risk-pane .cr-dev{padding:8px 10px;font-size:12px;align-content:start}
  /* 触屏没有悬停，设备名不截断，整个印出来 */
  #us-risk-pane .cr-dev .nm{white-space:normal;overflow-wrap:anywhere}
  #us-risk-pane .cr-dev b{font-size:11.5px}
  #us-risk-pane .cr-dev .meta{font-size:11px}
  #us-risk-pane #cr-tiles{margin-bottom:14px}
  /* 平板竖屏：判定请求、占比、缺失特征三格并排 */
  @media (min-width:640px){
    #us-risk-pane .cr-c-count,#us-risk-pane .cr-c-rate{flex:0 1 150px}
    #us-risk-pane .cr-c-marks{flex:1 1 240px}
  }
}

/* ---------- 余额档案（抽屉） ----------
   一次请求答完一个人的钱：他有多少、这段时间进出了什么、车位卖得值不值、
   每天怎么花的、以及背后那本流水。以前这里是两次请求两块面板，于是同一块
   屏幕上能同时摆出两个时刻的数字——客服照着念，客户听出两个答案。 */
.bd-hold{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:12px;overflow:hidden;margin-bottom:16px}
.bd-cell{background:var(--surface);padding:12px 13px}
.bd-cell .k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.bd-cell .v{font-family:var(--mono);font-size:19px;font-weight:600;margin-top:6px;font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.bd-cell .s{font-size:11px;color:var(--faint);margin-top:4px}
.bd-cell.gift .v{color:var(--warn)}
.bd-cell.bad .v{color:var(--bad)}
.bd-sec{margin:20px 0 0}
.bd-sec>.eyebrow{margin-bottom:9px}
.bd-lines{display:flex;flex-direction:column;gap:7px}
.bd-line{display:flex;align-items:center;gap:11px;font-size:12.5px}
.bd-line .lbl{width:76px;flex:none;color:var(--dim)}
.bd-line .amt{font-family:var(--mono);font-size:12.5px;font-variant-numeric:tabular-nums;width:96px;text-align:right;flex:none}
/* bd-rail, not rail: 侧栏机架也叫 .rail，它的 padding 会漏进来，把每条槽按
   自己的说明文字长短截短一截——于是这四条本该同底的槽，长度不再可比。 */
.bd-line .bd-rail{flex:1;min-width:60px;height:8px;padding:0;border-radius:2px;
  background:var(--surface-3);overflow:hidden}
.bd-line .bd-rail i{display:block;height:100%;min-width:2px;animation:kugrow .34s cubic-bezier(.2,.8,.3,1) both}
/* 说明文字定宽：它是注解，不是内容，让它去挤槽的长度就等于让注解决定读数 */
.bd-line .why{width:132px;flex:none;font-size:11px;color:var(--faint);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* 每日：柱高是当天花掉的钱，尺子只有一把，量的只有一件事。
   进账不画成柱：一次充值动辄是一个月的量，画进同一把尺子里，剩下 29 天的消耗
   就全被压成一条线——而这张图存在的理由恰恰是那 29 天。所以钱进来那天在基准
   线上点一个记号：充值是一次事件，不是一个可以和日耗比长短的量。 */
.bd-strip{position:relative;height:82px;display:flex;align-items:stretch;gap:2px;
  padding:0 3px;border:1px solid var(--line);border-radius:12px;background:var(--surface);overflow:hidden}
.bd-strip .datum{position:absolute;left:0;right:0;top:15px;height:1px;background:var(--line)}
.bd-day{position:relative;flex:1;min-width:3px}
.bd-day span{position:absolute;left:0;right:0;display:block;min-height:2px}
/* 实心。细齿在纸带上是用来把计量和席位分开的，而这排柱子只量一件事——
   没有第二样东西要分，细齿就只剩下装饰，宽一点的柱子上还读成一架梯子。 */
.bd-day .dn{border-radius:0 0 2px 2px;background:var(--text-a46)}
/* 记号：一颗贴在基准线上的点。进来的钱用绿 / 琥珀，出去的整笔（席位租金）用墨色，
   和柱子同色——它和柱子是同一种钱，只是没法和日耗比长短。 */
.bd-day .mk{top:8px;height:6px;border-radius:2px;background:var(--ok)}
.bd-day .mk.gift{background:var(--warn)}
.bd-day .mk.rent{top:8px;background:var(--text-a60)}
.mk-k{font-weight:600;letter-spacing:0;text-transform:none}
.mk-k::before{content:"";display:inline-block;width:9px;height:6px;border-radius:2px;margin-right:4px;vertical-align:0}
.mk-k.in::before{background:var(--ok)}
.mk-k.gift::before{background:var(--warn)}
.mk-k.rent::before{background:var(--text-a60)}
.bd-day:hover::after{content:"";position:absolute;inset:-2px -1px;border-radius:3px;background:var(--brand-a13)}
.bd-days-x{display:flex;justify-content:space-between;font-family:var(--mono);font-size:10px;color:var(--faint);margin-top:6px}
.bd-seatcard{border:1px solid var(--line);border-radius:12px;padding:12px 14px;background:var(--surface);
  display:flex;flex-wrap:wrap;gap:8px 22px;align-items:baseline}
.bd-seatcard+.bd-seatcard{margin-top:8px}
.bd-seatcard .t{font-family:var(--disp);font-weight:600;font-size:14px}
.bd-seatcard .f{font-size:12px;color:var(--dim)}
.bd-seatcard .f b{font-family:var(--mono);color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.bd-seatcard .verdict{font-family:var(--mono);font-size:12.5px;font-weight:600;margin-left:auto;
  font-variant-numeric:tabular-nums;color:var(--text)}
/* 抽屉里的流水表：类型和金额是两个不该折行的短词，折了就要多读一眼才知道
   这一行说的是什么钱 */
.bd-sec table td:nth-child(2),.bd-sec table td:nth-child(3){white-space:nowrap}
@media (max-width:960px){#bl-tiles{grid-template-columns:repeat(2,1fr)}}
@media (max-width:720px){#bl-tiles{grid-template-columns:1fr}}
/* 收款页一行四块：到账 / 待处理 / 待开票 / 已开票未送达。到账是税后实收，
   入账（含税实付）、笔数、退款跟在下面一行小字。窄屏先两列再一列：
   四块挤在 900px 上每块只剩 200px，那句解释会折成三行。 */
#tp-tiles{grid-template-columns:minmax(0,1.3fr) repeat(3,minmax(0,1fr));margin-bottom:18px}
#tp-tiles .tp-recon .v{white-space:nowrap}
#tp-tiles .tp-recon-sub b{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
#tp-tiles .tp-recon-sub .tile-warn b{color:var(--warn)}
/* 到账按收款渠道切开：细线下面一行一个渠道，渠道 / 金额 / 笔数三列对齐（subgrid），
   金额右对齐，两行的小数点落在一条线上。每一行也是筛选（tpChannel）：悬停浅底，
   选中时品牌色底，和三块待办瓦片「点一下只看这一格」是同一个手势。
   一行要 ~210px，四块一排时到账那块给宽一点（1.3fr），1280 宽也放得下。 */
#tp-tiles .tp-chs{display:grid;grid-template-columns:auto auto auto;justify-content:start;column-gap:12px;row-gap:2px;
  margin-top:12px;padding-top:10px;border-top:1px solid var(--line-soft)}
.tp-ch{display:grid;grid-column:1/-1;grid-template-columns:subgrid;align-items:baseline;margin:0 -8px;padding:4px 8px;
  border:0;border-radius:var(--r-sm);background:transparent;color:var(--dim);font-family:inherit;font-size:12.5px;
  text-align:left;white-space:nowrap;cursor:pointer;transition:background-color .14s var(--ease)}
.tp-ch b{justify-self:end;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
.tp-ch .n{color:var(--faint);font-size:11.5px;font-variant-numeric:tabular-nums}
.tp-ch:hover{background:var(--fill-3);color:var(--text)}
.tp-ch.on{background:var(--brand-a12);color:var(--text)}
.tp-ch:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.tp-ch-x,.tp-ch-x:hover{cursor:default;background:transparent;color:var(--dim)}
/* 三块待办同时是筛选：点一下队列只剩这一格，再点一下回到全部。和下面那排芯片
   是同一个状态，选中时两边一起亮。 */
#tp-tiles .tp-todo{cursor:pointer;transition:border-color .14s var(--ease),background-color .14s var(--ease)}
#tp-tiles .tp-todo:hover{border-color:var(--brand-40-on-line)}
#tp-tiles .tp-todo.on{border-color:var(--brand-50-on-line);background:var(--brand-a12)}
#tp-tiles .tp-todo:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
#tp-tiles .tpt-s{display:none}
#tp-q{width:230px}
#tp-filter{margin-bottom:14px}
.tp-more{display:flex;justify-content:center;align-items:center;gap:12px;margin-top:14px;font-size:12px}
.tp-more[hidden]{display:none}
/* 批量标记已开票：只在「待开票」这一格出现。勾选列和表格上方这一条一起出现、一起消失；
   选了几笔、开票金额、按钮都在这一条里，窄屏卡片没有表头，全选也放在这里。 */
.tp-batch{display:flex;align-items:center;gap:10px 14px;flex-wrap:wrap;margin-bottom:12px;padding:10px 12px 10px 16px;
  border:1px solid var(--line);border-radius:12px;background:var(--surface-2);font-size:12.5px;color:var(--dim)}
.tp-batch[hidden]{display:none}
.tp-batch.on{border-color:var(--brand-50-on-line);background:var(--brand-a12);color:var(--text)}
.tp-batch .tpb-say{flex:1 1 260px;min-width:0;line-height:1.5}
.tp-batch .tpb-say b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.tp-batch .tpb-act{display:flex;gap:8px;flex:none}
#topup-sec:not(.tp-sel) .tp-pick{display:none}
#topup-sec th.tp-pick,#topups td.tp-pick{width:30px;padding-right:0}
.tp-pick input{margin:0;width:16px;height:16px;vertical-align:middle;accent-color:var(--brand);cursor:pointer}
.tp-pk{display:inline-flex;vertical-align:middle;padding:6px;margin:-6px;cursor:pointer}
#topups tr.tp-picked td{background:var(--brand-a8)}
#topups{transition:opacity .14s var(--ease)}
#topups.tp-busy{opacity:.55}
/* 对账（2026-10-21）：标题行里一个「对账」按钮；选了窗口变成一枚胶囊（写着哪一段、按哪个时间），
   旁边一个 ✕ 退出。到账瓦片的标题、渠道合计、待办和列表都跟着窗口走。 */
.tpr-ctl{display:flex;align-items:center;gap:6px;flex:none}
.tpr-ctl .btn{gap:6px}
.tpr-ctl .btn svg{width:14px;height:14px}
.tpr-ctl .tpr-pill{background:var(--brand);color:var(--btn-ink)}
.tpr-ctl .tpr-pill .b{opacity:.72;font-weight:400}
.tpr-ctl .tpr-x{width:28px;height:28px;padding:0;justify-content:center;font-size:13px}
/* .cfm 的规则写在后面，宽度和抬头间距要用两个类才压得住。 */
.cfm.tpr{width:min(580px,94vw);max-height:92vh;overflow-y:auto;overscroll-behavior:contain}
.cfm.tpr h4{margin-bottom:14px}
.tpr-top{display:flex;align-items:center;justify-content:space-between;gap:10px 12px;flex-wrap:wrap}
.tpr-top .winsel button{padding:5px 12px}
.tpr-basis{width:auto;flex:none;padding:5px 30px 5px 12px;font-size:12.5px;border-radius:var(--r-pill);border:1px solid var(--line);background-color:var(--fill-3);color:var(--text);font-family:inherit}
.tpr-nav{display:flex;align-items:center;justify-content:space-between;margin-top:16px}
.tpr-nav b{font-family:var(--disp);font-size:14.5px;font-weight:600;letter-spacing:-.01em}
.tpr-nav .btn.icon{width:30px;height:30px;padding:0;justify-content:center;font-size:17px;line-height:1}
.tpr-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:4px;margin-top:10px}
.tpr-grid .h{font-size:11.5px;color:var(--faint);text-align:center;padding:2px 0 4px}
.tpr-d{position:relative;display:flex;flex-direction:column;gap:3px;min-height:56px;min-width:0;padding:6px 6px 5px;
  border:1px solid transparent;border-radius:var(--r-md);background:var(--fill-3);color:var(--text);font-family:inherit;
  text-align:left;cursor:pointer;transition:background-color .14s var(--ease),border-color .14s var(--ease)}
.tpr-d .n{font-size:12.5px;font-weight:600;line-height:1;font-variant-numeric:tabular-nums}
/* 金额两套写法。桌面 .a-full 是「2,971.06」，格子不够宽才省略。手机 .a-short 是去掉小数的
   ¥2971，从 10 万起写成 12.8万：约 50px 的格子里完整金额会被裁成残段，所以窄屏曾经把
   .a 整段藏掉，格子里只剩日期。悬停标题仍是精确到分的含税实付。 */
.tpr-d .a{display:block;min-width:0;color:var(--dim);font-variant-numeric:tabular-nums}
.tpr-d .a-full{display:block;font-size:10.5px;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}
.tpr-d .a-short{display:none}
.tpr-d.zero .a{color:var(--faint)}
.tpr-d .dots{display:flex;gap:3px;margin-top:auto;min-height:5px}
.tpr-d .dot{width:5px;height:5px;border-radius:50%}
.tpr-d .dot.p{background:var(--warn-fill)}
.tpr-d .dot.r{background:var(--bad-fill)}
.tpr-d:hover{border-color:var(--brand-40-on-line)}
.tpr-d.in{background:var(--brand-a12);border-color:var(--brand-50-on-line)}
.tpr-d.today .n{text-decoration:underline;text-underline-offset:3px}
.tpr-d.future{opacity:.42;cursor:default}
.tpr-d.future:hover{border-color:transparent}
.tpr-d:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.tpr-d.pad{background:transparent;cursor:default;border-color:transparent}
.tpr-sum{margin-top:16px;padding:14px 16px;border-radius:var(--r-lg);background:var(--fill-3)}
.tpr-sum .t{font-size:12.5px;color:var(--dim)}
.tpr-sum .t b{color:var(--text);font-weight:600}
.tpr-sum .row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 16px;margin-top:10px}
.tpr-sum .c .k{font-size:12px;color:var(--dim)}
.tpr-sum .c .v{font-family:var(--disp);font-size:22px;font-weight:600;letter-spacing:-.02em;line-height:1.1;margin-top:3px;font-variant-numeric:tabular-nums}
.tpr-sum .c .s{font-size:12px;color:var(--faint);margin-top:3px;font-variant-numeric:tabular-nums}
.tpr-sum .foot{display:flex;flex-wrap:wrap;gap:4px 14px;margin-top:10px;padding-top:9px;border-top:1px solid var(--line-soft);font-size:12.5px;color:var(--dim)}
.tpr-sum .foot b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.tpr-sum .foot .tile-warn b{color:var(--warn)}
.tpr-sum.busy{opacity:.55}
.tpr-hint{margin:12px 0 18px;font-size:12px;color:var(--faint);line-height:1.6}
.tpr-hint-btn{display:none}
.tp-late{display:inline-block;margin-left:6px;padding:1px 6px;border-radius:999px;font-size:11px;color:var(--warn);background:var(--warn-a12);white-space:nowrap}
/* 手机（≤560px）：对账卡按可视高度封顶，两个按钮留在卡片底部，不跟内容滚走。
   以前 max-height:92vh 再垂直居中。Safari 的 vh 按工具栏收起的大视口算，按钮会落到
   底部工具栏下面；日历金额又被 display:none 整段藏掉。这里改用 100dvh，说明收成一行，
   日历格压矮，汇总改回并排，腾出的高度刚好放下每天的金额。桌面不进这一段。 */
@media (max-width:560px){
  .cfm.tpr{width:min(580px,calc(100vw - 12px));max-height:100dvh;
    padding:12px 10px calc(8px + env(safe-area-inset-bottom));
    display:flex;flex-direction:column;overflow:hidden}
  .cfm.tpr .tpr-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}
  .cfm.tpr h4{margin-bottom:8px}
  .cfm.tpr .cfm-foot{flex:none;margin-top:8px}
  .cfm.tpr .cfm-foot .btn{min-height:44px}
  .tpr-top{gap:6px}
  .tpr-nav{margin-top:8px}
  .tpr-grid{gap:2px;margin-top:6px}
  .tpr-grid .h{padding:0 0 2px}
  .tpr-d{min-height:0;gap:1px;padding:3px 1px 2px}
  .tpr-d .n{font-size:12px}
  .tpr-d .a{display:block;min-height:11px}
  .tpr-d .a-full{display:none}
  .tpr-d .a-short{display:block;font-size:9px;letter-spacing:-.045em;line-height:1.05;white-space:nowrap}
  .tpr-d .dots{position:absolute;top:3px;right:2px;margin:0;min-height:0;gap:2px}
  .tpr-d .dot{width:4px;height:4px}
  .tpr-sum{margin-top:8px;padding:8px 10px}
  .tpr-sum .t{font-size:12px;line-height:1.35}
  .tpr-sum .row{grid-template-columns:repeat(2,minmax(0,1fr));gap:4px 8px;margin-top:6px}
  .tpr-sum .c .k{font-size:11px}
  .tpr-sum .c .v{font-size:17px;margin-top:1px}
  .tpr-sum .c .s{font-size:10.5px;line-height:1.25;margin-top:1px}
  .tpr-sum .foot{margin-top:6px;padding-top:6px;gap:2px 8px;font-size:11.5px}
  .tpr-hint{margin:6px 0 0}
  .tpr-hint-btn{display:inline-flex;align-items:center;gap:5px;margin:0;padding:2px 0;border:0;background:none;color:var(--dim);font-size:12px;font-weight:500;line-height:1.3}
  .tpr-hint-btn:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
  .tpr-hint-mk{width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:5px solid var(--faint)}
  .tpr-hint.open .tpr-hint-mk{transform:rotate(90deg)}
  .tpr-hint:not(.open) #tpr-hint{display:none}
  .tpr-hint.open #tpr-hint{margin-top:4px}
  .tpr-ctl .tpr-pill .b{display:none}
}
@media (max-width:1100px){#tp-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* 窄屏（≤860px，和底部导航同一档）：手机上打开这一页多半是飞书来了一笔充值，
   要做的是核对金额、点通过。所以先把队列顶到第一屏：
   页头只留搜索框（「充值收款」顶栏和页签上已经各写了一遍，待处理笔数在瓦片上，
   待付款的 20 分钟规则印在那张卡片上）；
   到账一整行，三块待办并成一排，每块只留标题、笔数和金额（四块竖排要两屏）；
   芯片排成一行横滑，两端渐隐提示还有。 */
@media (max-width:860px){
  #topup-sec .sx-head{margin-bottom:12px}
  #topup-sec .sx-head>div:first-child{display:none}
  #topup-sec .sx-act{width:100%}
  #tp-q{width:100%}
  #tp-tiles{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-bottom:12px}
  #tp-tiles .tp-recon{grid-column:1/-1;padding:14px 16px 13px}
  #tp-tiles .tp-recon .v{font-size:26px;margin-top:6px}
  #tp-tiles .tp-recon .sub{margin-top:6px}
  #tp-tiles .tp-chs{margin-top:10px;padding-top:8px}
  .tp-ch{padding:7px 8px}
  #tp-tiles .tp-todo{padding:11px 12px 12px;min-width:0}
  #tp-tiles .tp-todo .k{font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  #tp-tiles .tp-todo .v{font-size:24px;margin-top:6px}
  #tp-tiles .tp-todo .sub{margin-top:4px;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
  #tp-tiles .tp-todo.tp-zero:not(.on) .v{color:var(--faint)}
  #tp-tiles .tpt-l{display:none}
  #tp-tiles div.tpt-s{display:block}
  #tp-tiles span.tpt-s{display:inline}
  #tp-filter{flex-wrap:nowrap;gap:6px;overflow-x:auto;scrollbar-width:none;overscroll-behavior-x:contain;
    margin:0 -12px 12px;padding:2px 12px}
  #tp-filter::-webkit-scrollbar{display:none}
  #tp-filter.fade-r{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent)}
  #tp-filter.fade-l{-webkit-mask-image:linear-gradient(90deg,transparent,#000 28px);mask-image:linear-gradient(90deg,transparent,#000 28px)}
  #tp-filter.fade-l.fade-r{-webkit-mask-image:linear-gradient(90deg,transparent,#000 28px,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(90deg,transparent,#000 28px,#000 calc(100% - 28px),transparent)}
  #tp-filter .chip{flex:none;padding:7px 13px;font-size:13px}
  #tp-filter .chip .n{font-size:11.5px}
}

/* ---------- 充值自动确认穿梭框 ---------- */
.shuttle-wrap{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:6px}
@media (max-width:760px){.shuttle-wrap{grid-template-columns:1fr}}
.shuttle-pane{background:var(--surface);border:1px solid var(--line);border-radius:10px;display:flex;flex-direction:column;height:290px;overflow:hidden}
.shuttle-head{padding:8px 12px;background:var(--surface-2);border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;font-size:12px;font-weight:600;color:var(--text)}
.shuttle-search{padding:6px 10px;border-bottom:1px solid var(--line);background:var(--surface)}
.shuttle-search input{width:100%!important;padding:5px 8px;font-size:12px;border:1px solid var(--line);border-radius:6px;background:var(--surface-2);color:var(--text);outline:none;box-sizing:border-box}
.shuttle-search input:focus{border-color:var(--brand)}
.shuttle-list{flex:1;overflow-y:auto;padding:2px 0}
.shuttle-item{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 10px;border-bottom:1px solid var(--line-a12, rgba(0,0,0,0.05));cursor:pointer;transition:background .12s}
.shuttle-item:hover{background:var(--surface-2)}
.shuttle-uinfo{min-width:0;flex:1}
.shuttle-uname{font-size:12.5px;font-weight:600;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:6px}
.shuttle-uname .uid{font-family:var(--mono);font-size:11px;color:var(--faint);font-weight:normal}
.shuttle-meta{font-size:11px;color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:6px;margin-top:2px}
.shuttle-note{background:rgba(99,102,241,0.12);color:var(--brand-2, #6366f1);padding:1px 5px;border-radius:4px;font-weight:500;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.shuttle-item .btn{flex-shrink:0;padding:2px 8px;font-size:11px;height:24px;line-height:20px}
.shuttle-empty{padding:40px 12px;text-align:center;color:var(--faint);font-size:12px}
/* 名单行里的金额格（按客户设置在用）：等宽数字、右对齐，下面一行和默认值对比。
   行本身不可点：格子里有输入框，整行点击移除会让「点进去改金额」变成「把人移出名单」。 */
.shu-count{font-weight:500;color:var(--dim);font-variant-numeric:tabular-nums;margin-left:4px}
.shu-count.on{color:var(--brand-2)}
.shu-row{cursor:default;gap:12px;padding:8px 8px 8px 12px;box-shadow:inset 2px 0 0 transparent}
.shu-row:hover{background:transparent}
.shu-amt{position:relative;display:block;margin:0}
.shu-amt>span{position:absolute;left:9px;top:50%;transform:translateY(-50%);font-family:var(--mono);font-size:12px;color:var(--faint);pointer-events:none}
.shu-amt input{width:112px;height:30px;padding:4px 9px 4px 22px;border-radius:7px;font-family:var(--mono);font-size:13px;text-align:right;font-variant-numeric:tabular-nums;-moz-appearance:textfield;appearance:textfield}
.shu-amt input::-webkit-outer-spin-button,.shu-amt input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.shu-cap{font-size:11px;line-height:1.3;color:var(--faint);white-space:nowrap}
.shu-x{flex:none;width:26px;height:26px;border-radius:7px;background:transparent;color:var(--faint);font-size:12px;line-height:26px;text-align:center}
.shu-x:hover{background:var(--bad-a12);color:var(--bad)}
.shu-x:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.shu-foot{margin-top:8px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;font-size:11.5px;color:var(--dim)}
.shu-foot a{color:var(--dim);text-decoration:underline}
@media (max-width:760px){
  .shu-row .shuttle-meta{flex-wrap:wrap;white-space:normal;row-gap:3px}
  .shu-amt input{width:96px}
}
/* 触屏上输入框小于 16px，iOS 对焦时会整页放大 */
@media (pointer:coarse){.shu-amt input{font-size:16px}}

/* ---------- 收款设置 · 按客户设置 ----------
   免审额度和普票税点合成一张名单，一人一行：运营想的是「这个客户有什么特殊条款」。
   两格各自一个输入框和一行对比默认值的小字；和默认不同的格子上品牌色，调严的上琥珀。
   左栏每人两个加入按钮，加进来就开免审 / 光标落在税点格，不用先加人再找格子。 */
.cu-defaults{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px 20px;margin-bottom:18px}
.cu-defaults .field{margin-bottom:0}
.cu-defaults input{max-width:240px}
.shuttle-wrap.cu-wrap{grid-template-columns:minmax(0,2fr) minmax(0,3.4fr);margin-top:0}
.cu-wrap .shuttle-pane{height:420px}
.cu-filter{display:flex;gap:2px;margin:-3px 0}
.cu-filter button{padding:3px 10px;border-radius:999px;background:none;font-size:12px;font-weight:500;color:var(--dim)}
.cu-filter button:hover{color:var(--text)}
.cu-filter button.on{background:var(--surface);color:var(--text);font-weight:600;box-shadow:0 0 0 1px var(--line)}
.cu-filter button.on .shu-count{color:var(--brand-2)}
.cu-filter button:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.cu-cols{display:grid;grid-template-columns:minmax(0,1fr) 150px 104px 26px;gap:12px;padding:6px 8px 6px 12px;
  font-size:11px;color:var(--faint);border-bottom:1px solid var(--line);background:var(--surface)}
.shu-row.cu-row{display:grid;grid-template-columns:minmax(0,1fr) 150px 104px 26px;gap:12px;align-items:start;box-shadow:none}
.cu-row .shuttle-uinfo{padding-top:0}
.cu-row.cu-blank{background:var(--surface-2)}
.cu-cell{display:flex;flex-direction:column;align-items:flex-start;gap:3px;min-width:0}
.cu-in{display:flex;align-items:center;gap:7px}
.cu-in input[type=checkbox]{margin:0;width:16px;height:16px;flex:none;accent-color:var(--brand)}
.cu-lim .shu-amt input{width:118px}
.cu-pct input{width:104px;padding:4px 24px 4px 9px}
.cu-pct>span{left:auto;right:9px}
.cu-lim.off .shu-amt input{opacity:.45}
.cu-cell.raised .shu-amt input,.cu-cell.open .shu-amt input{border-color:var(--brand-a45);color:var(--brand-2)}
.cu-cell.raised .shu-cap,.cu-cell.open .shu-cap{color:var(--brand-2)}
.cu-cell.lowered .shu-cap{color:var(--warn)}
.cu-lim .shu-cap{padding-left:23px}
.cu-row .shu-x{margin-top:2px}
.cu-warn{color:var(--warn)}
.cu-pick{cursor:default}
.cu-pick:hover{background:transparent}
.cu-add{display:flex;gap:6px;flex:none}
.cu-raw{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px 20px;margin-top:14px}
.cu-raw .field{margin-bottom:0}
.cu-raw textarea{font-family:var(--mono);font-size:12px}
/* 开关行：开关在左，名字和说明在右，和系统设置里的开关同一种排法 */
.cu-switch{display:flex;align-items:flex-start;gap:12px}
.cu-switch .toggle{margin-top:1px}
.cu-switch b{font-size:13px;font-weight:600}
.cu-switch .set-hint{margin-top:3px;max-width:80ch}
.cu-readout{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;min-height:36px;padding-top:6px}
.cu-readout b{font-family:var(--mono);font-size:15px;font-weight:600;font-variant-numeric:tabular-nums}
.cu-readout span{font-size:11.5px;color:var(--faint)}
.cu-pay{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);gap:16px 20px}
.cu-pay .field{margin-bottom:0}
.cu-qr{display:flex;align-items:flex-start;gap:14px;margin-top:2px}
.cu-qr img,.cu-qr-empty{width:104px;height:104px;flex:none;border-radius:12px;border:1px solid var(--line)}
.cu-qr img{object-fit:contain;background:#fff;padding:6px;box-sizing:border-box}
.cu-qr-empty{display:flex;align-items:center;justify-content:center;font-size:11.5px;color:var(--faint);
  background:var(--surface-2);border-style:dashed}
.cu-qr-act{display:flex;flex-direction:column;gap:8px;min-width:0}
.cu-qr-act .set-hint{margin-top:0}
.wg-qr img,.wg-qr .cu-qr-empty{width:132px;height:auto;min-height:132px;aspect-ratio:auto}
.wg-qr img{max-height:210px}
.wg-meta{font-size:13px;color:var(--dim);line-height:1.6}
.wg-meta b{color:var(--ink,inherit);font-weight:600}
.wg-meta .bad{color:var(--bad,#c0392b)}
.set-grid.cu-corp{grid-template-columns:repeat(3,minmax(0,1fr))}
@media (max-width:900px){
  .shuttle-wrap.cu-wrap{grid-template-columns:1fr}
  .cu-pay{grid-template-columns:1fr}
  .set-grid.cu-corp{grid-template-columns:1fr}
}
@media (max-width:720px){
  .cu-defaults,.cu-raw{grid-template-columns:1fr}
  .cu-defaults input{max-width:none}
  .cu-cols{display:none}
  .shu-row.cu-row{grid-template-columns:auto minmax(0,1fr) 26px;row-gap:8px}
  .cu-row .shuttle-uinfo{grid-column:1/3;grid-row:1}
  .cu-row .shu-x{grid-column:3;grid-row:1}
  .cu-row .cu-lim{grid-column:1;grid-row:2}
  .cu-row .cu-tax{grid-column:2/4;grid-row:2}
  .cu-pick{flex-wrap:wrap}
}
@media (pointer:coarse){.cu-pct input{font-size:16px}}

/* ---------- PROVIDERS · 凭证分栏 ----------
   订阅令牌和 API 密钥挤在一张表里时，谁都没配好：订阅线要的是账号、档位、额度
   窗口和重置时刻，密钥线这四格全是「—」；密钥线要的是完整端点地址，可它被挤成
   了省略号，因为 32% 的版面让给了一列它永远填不满的余量条。
   所以这一层不是筛选器，是分栏：它换的是列，不只是行。三段各有各的差事——
   「全部」是货架（唯一能拖拽排序的地方），另两段是各自的工作台。
   视觉上它必须比下面那条筛选轨重一档，否则读起来又是一排平级的筛选按钮：
   高一截、坐在一条底色带上、选中的那段下沿有一道实指示线。
   底色带现在由 .pv-face 承担——那条带子上除了分栏，右边还嵌着本栏的读数窗。 */
.pv-face{display:flex;align-items:stretch;gap:16px;background:var(--surface-2);
  border-bottom:1px solid var(--line)}
.pv-tabs{display:flex;align-items:stretch;gap:2px;padding:0 8px;min-width:0}
.pv-tab{position:relative;display:inline-flex;align-items:center;gap:7px;height:40px;padding:0 13px;
  background:none;font-size:13px;font-weight:500;color:var(--dim);transition:color .14s}
.pv-tab:hover{color:var(--text)}
.pv-tab.on{color:var(--text);font-weight:600}
.pv-tab.on::after{content:'';position:absolute;left:11px;right:11px;bottom:-1px;height:2px;
  background:var(--brand);border-radius:2px 2px 0 0}
/* Claude 那一段带着它在这一页别处的同一枚记号（铭牌方块、家族筛片、页顶授权项
   用的都是 Claude 标志，TAG_IC.claude）。分栏条上多一个符号是有代价的，付得起是因为它认的是同一件事。 */
.pv-tab .mk{display:inline-flex;margin-right:1px;color:var(--ac)}
.pv-tab .mk svg{width:12px;height:12px;display:block}
.pv-tab:focus-visible{outline:2px solid var(--focus);outline-offset:-3px;border-radius:6px}
.pv-tab .n{font-family:var(--mono);font-size:11.5px;font-weight:600;color:var(--faint);font-variant-numeric:tabular-nums}
.pv-tab.on .n{color:var(--brand-2)}
/* 实况窗跟分栏同一条带子，但自己带名字：它筛的是通断，不是账号种类。
   选择器挂在 #v-providers 上：透视板后面还有一条同名 .pv-live，不挂的话
   它会把这一条的 margin 和 display 盖掉，实况窗就不再靠右。 */
#v-providers .pv-face>.pv-live{display:flex;align-items:center;gap:8px;margin-left:auto;padding-right:14px;min-width:0;font-size:inherit;color:inherit}
.pv-k{flex:none;font-size:11px;font-weight:600;color:var(--faint);white-space:nowrap}
button.pv-k{padding:0;background:none;font-size:11px;font-weight:600;color:var(--faint)}
button.pv-k.hot{color:var(--brand-2)}
button.pv-k:hover{color:var(--text)}
button.pv-k:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:4px}
/* 订阅家族筛片：五个 Kiro 号之间要分的从来不是协议，是「这是哪一家的订阅」。
   记号沿用页顶授权条那一套（vendorIcon），不另起一套图形。 */
.pv-chip.fam b{display:inline-flex;line-height:1;color:var(--pc,var(--dim))}
.pv-chip.fam b svg,.pv-chip b.ic svg{width:12px;height:12px;display:block}
.pv-chip b.ic{display:inline-flex;margin-right:-1px;color:var(--ac)}
.pv-chip.fam .n{font-family:var(--mono);font-size:11px;font-weight:600;color:var(--faint);font-variant-numeric:tabular-nums}
.pv-chip.fam.on .n{color:inherit}

/* ---------- PROVIDERS · filter rail + drag-to-reorder ---------- */
/* Filters sit on one slim rail inside the ledger panel — table furniture,
   not a second content block. Protocols read as patch-bay LED dots that
   pick up the theme's halo (glow at night, ring by day). */
.pv-rail{display:flex;align-items:center;gap:8px 12px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--line-soft)}
/* 筛选轨上每一组自带名字（执行 / 用途 / 档位 / 账号）。名字不是又一颗芯片：
   没筛时它是这组的轴，筛过之后变色，点它回到全部。 */
.pv-filters{align-items:center;gap:8px 22px;padding:10px 14px}
.pv-filters[hidden],.pv-grp[hidden],.pv-read[hidden]{display:none}
.pv-grp{display:inline-flex;align-items:center;gap:8px;max-width:100%;min-width:0}
.pv-chips{display:inline-flex;align-items:center;gap:2px;flex-wrap:wrap;min-width:0}
.pv-read{display:flex;align-items:center;gap:14px;min-width:0;padding:9px 14px;border-bottom:1px solid var(--line-soft)}
.pv-tools{gap:10px 14px;padding:10px 14px}
/* 透视板的排序箭头也叫 .pv-sort，写在后面。挂在工具条上，字号和颜色才不会被它改成 10px。 */
#v-providers .pv-tools>.pv-sort{display:inline-flex;align-items:center;gap:8px;flex:none;margin-left:0;font-size:12px;color:var(--dim)}
#v-providers .pv-tools>.pv-sort select{width:auto;height:28px;padding:0 28px 0 9px;font-size:12px}
.pv-pools{display:inline-flex;align-items:center;gap:6px;min-width:0}
.pv-seg{display:inline-flex;flex:none;height:28px;border:1px solid var(--line);border-radius:8px;overflow:hidden}
.pv-seg button{padding:0 11px;font-size:12px;font-weight:500;color:var(--dim);background:none;transition:color .14s,background .14s}
.pv-seg button+button{border-left:1px solid var(--line-soft)}
.pv-seg button:hover{color:var(--text)}
.pv-seg button.on{background:var(--brand-dim);color:var(--brand-2);font-weight:600}
.pv-seg button:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.pv-protos{display:flex;align-items:center;gap:2px;flex-wrap:wrap}
.pv-chip{display:inline-flex;align-items:center;gap:6px;height:24px;padding:0 9px;border-radius:999px;border:1px solid transparent;background:none;font-size:12px;font-weight:500;color:var(--dim);transition:color .14s,border-color .14s,background .14s}
.pv-chip:hover{color:var(--text);background:var(--surface-3)}
.pv-chip.on{border-color:var(--brand-a40);background:var(--brand-dim);color:var(--brand-2)}
/* 「全部X」选中 = 什么都没筛。它以前和真筛片长得一样，于是一进页面轨上就亮着
   两颗紫色药丸，而它们表示的恰恰是「没有筛任何东西」。让它安静下来之后，这条轨
   上只剩一条规则：亮着的就是你缩小了范围；不亮的时候，这两个词正好当轴的标签读。 */
.pv-chip.all.on{border-color:transparent;background:none;color:var(--dim);font-weight:600}
.pv-chip.all.on:hover{background:var(--surface-3);color:var(--text)}
.pv-chip:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.pv-chip i{width:6px;height:6px;border-radius:50%;flex:none;background:var(--led,var(--faint))}
.pv-chip.on i{box-shadow:0 0 var(--halo-blur) var(--halo-spread) color-mix(in srgb,var(--led,var(--faint)) var(--halo-alpha),transparent)}
.pv-chip .n{font-family:var(--mono);font-size:11px;font-weight:600;color:var(--faint);font-variant-numeric:tabular-nums}
.pv-chip.on .n{color:inherit}
/* 「两边都在」有数才着色，和状态格里熔断那一格同一套语法：没有的东西不该
   一直亮着，亮起来就说明有事要处理。 */
.pv-chip.warn{color:var(--warn)}
.pv-chip.warn.on{border-color:color-mix(in srgb,var(--warn) 40%,transparent);
  background:color-mix(in srgb,var(--warn) 12%,transparent);color:var(--warn)}
.pv-count{margin-left:auto;font-size:11.5px;color:var(--faint);white-space:nowrap;font-variant-numeric:tabular-nums}
.pv-count b{color:var(--dim);font-weight:600}
.pv-lock{color:var(--faint);display:inline-flex;align-items:center;gap:5px}
.pv-find{display:inline-flex;align-items:center;gap:6px;flex:none;height:28px;padding:0 10px;border:1px solid var(--line);border-radius:8px;color:var(--faint);transition:border-color .14s,box-shadow .14s}
.pv-find:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-dim);color:var(--dim)}
.pv-find input{width:168px;padding:0;border:none;border-radius:0;background:none;font-size:12.5px;transition:width .18s ease}
.pv-find input:focus{border:none;box-shadow:none;width:240px}
.pv-find input::placeholder{color:var(--faint)}
/* drag grip: a patch-port handle that only lights up when ordering is live */
th.pv-grip-h{width:34px;padding-left:14px}
td.pv-grip-cell{width:34px;padding-left:14px;color:var(--faint)}
.pv-grip{display:inline-flex;align-items:center;justify-content:center;width:22px;height:24px;border-radius:6px;cursor:grab;color:var(--faint);transition:color .12s,background .12s}
tbody tr:hover .pv-grip{color:var(--dim)}
.pv-grip:hover{color:var(--brand-2);background:var(--brand-dim)}
.pv-grip:active{cursor:grabbing}
.pv-grip.locked{cursor:not-allowed;color:var(--line)}
.pv-grip.locked:hover{color:var(--line);background:none}
tbody tr.dragging{opacity:.45}
tbody tr.dragging td{background:var(--brand-dim)}
tbody tr.drop-above td{box-shadow:inset 0 2px 0 var(--brand)}
tbody tr.drop-below td{box-shadow:inset 0 -2px 0 var(--brand)}
.pv-empty-row td{text-align:center;color:var(--faint);padding:22px}

/* ---------- PROVIDERS · 线路实况 / LIVE LINE STATE ----------
   「启用」是操作意图，「线路」是此刻的通断——两件正交的事，过去挤在一列里，
   于是熔断的上游照样写着「启用」，答的不是运维在问的问题。这里只画后者。
   熔断读数有三个数：断了多久（trippedAt）、还有多久自动试探（openUntil）、
   冷却退避到第几档（cooldown，翻倍到上限就是「已经断很久了」）。
   重合闸引信 .fuse 是全页唯一自己会动的东西——因为它确实每秒都在变。 */
.pv-state{display:flex;flex-direction:column;gap:4px;min-width:132px}
.pv-st{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;font-weight:600;white-space:nowrap}
.pv-st.up{color:var(--ok)} .pv-st.trip{color:var(--bad)} .pv-st.probing{color:var(--warn)}
.pv-st.park{color:var(--warn)}
.pv-st.reauth{color:var(--bad)}
.pv-st.idle{color:var(--faint);font-weight:500}
.pv-st .age{font-family:var(--mono);font-weight:500;font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums}
/* 引信：冷却条走完 = 网关放一次试探。宽度按剩余/冷却总长，每秒重画 */
.fuse{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--faint);white-space:nowrap}
.fuse-bar{position:relative;width:56px;height:3px;flex:none;border-radius:2px;background:var(--bad-a18);overflow:hidden}
.fuse-bar i{position:absolute;inset:0 auto 0 0;background:var(--bad);border-radius:2px;transition:width .95s linear}
.fuse .t{font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--dim)}
/* 冷却走完 = 引信满格待发：整条转琥珀并呼吸，等网关放那一次试探 */
.fuse.due .t{color:var(--warn)}
.fuse.due .fuse-bar{background:var(--warn-a20)}
.fuse.due .fuse-bar i{width:100%!important;background:var(--warn);animation:blink 1.4s steps(2,start) infinite}
/* 熔断行整行示警：左沿一道红边 + 极淡底色，扫一眼就能落到出事的那条线上 */
tbody tr.pv-trip{background:var(--bad-a5)}
tbody tr.pv-trip:hover{background:var(--bad-a9)}
tbody tr.pv-trip td:first-child{box-shadow:inset 3px 0 0 var(--bad)}
.panel>table tbody tr.pv-trip:hover:not(.dragging):not(.drop-above):not(.drop-below) td:first-child{box-shadow:inset 3px 0 0 var(--bad)}
/* 熔断行的「测试」= 不必等冷却，立刻试一次。只把图标点亮，不加框：
   这一行的重点是引信，不是按钮 */
tr.pv-trip .btn.ghost.icon.probe{color:var(--bad)}
tr.pv-trip .btn.ghost.icon.probe:hover{color:var(--bad);border-color:var(--bad);background:var(--bad-a10)}
/* 停靠行：同样整行示警，但染琥珀不染红——这条线没坏，只是今天不再接单了。
   两种色一眼分得开「要去修」和「等日子」。 */
tbody tr.pv-park{background:var(--warn-a5)}
tbody tr.pv-park:hover{background:var(--warn-a9)}
tbody tr.pv-park td:first-child{box-shadow:inset 3px 0 0 var(--warn)}
.panel>table tbody tr.pv-park:hover:not(.dragging):not(.drop-above):not(.drop-below) td:first-child{box-shadow:inset 3px 0 0 var(--warn)}
/* 停靠的放行时刻是准信，不是猜的倒计时：给它引信的位置，但不画那根会烧完的条 */
.fuse.park .t{color:var(--warn)}
/* 掉授权行：染红，因为它和熔断一样是「要去修」。但它没有引信——冷却走完也不会
   有任何事发生，refresh token 死了就是死了，只有人能救。所以引信那一格改放
   去向导的那一步；「测试」按钮也不点亮，试探对它毫无意义。 */
tbody tr.pv-reauth{background:var(--bad-a5)}
tbody tr.pv-reauth:hover{background:var(--bad-a9)}
tbody tr.pv-reauth td:first-child{box-shadow:inset 3px 0 0 var(--bad)}
.panel>table tbody tr.pv-reauth:hover:not(.dragging):not(.drop-above):not(.drop-below) td:first-child{box-shadow:inset 3px 0 0 var(--bad)}
.fuse .fix{padding:0;background:none;color:var(--brand-2);font-size:11px;font-weight:600;white-space:nowrap}
.fuse .fix:hover{text-decoration:underline}
/* 熔断 / 停靠的计数现在长在抬头的读数窗里（.pv-gauge），不在筛选轨上 */
/* ---- 列宽：定宽表格 ------------------------------------------------------
   `table-layout:fixed` 是这一版最要紧的一行。在自动布局下，**一行的内容能决定
   整列的宽度**：Sidecar 那一行的令牌牌子上印着 40 个十六进制字符的修订号，于是
   「令牌」这一列被撑到三百多像素——而那三百像素是从铭牌身上抢的，剩下的七行只好
   把邮箱截成 `rui…`、把「+ 入池」压成一个圆点。修订号我们截短了（shortRev），
   但下一个长字符串迟早还会来。定宽把这条路整个封死：列宽只由这里的百分比决定，
   内容长了就在自己的格子里打省略号，谁也挤不着谁。

   于是断点的职责也变了——它不再是「抢在表撑破面板之前撤掉一列」，而只是
   「这个宽度下这一列还读得出来吗」。

   定宽有一条必须自己守住的账：**每一档、每一栏的百分比都要正好加到 100**。
   加多了表就伸到面板外面，而面板是 overflow:hidden 的——右端那三个图标按钮
   会被整整齐齐地切掉，看上去像它们不存在。所以下面每一组后面都写着它的和。 */
#v-providers table{table-layout:fixed}
/* ---- 一档：>1560，七列全开 ---- */
#v-providers th.pv-grip-h{width:3%}
#v-providers th.pv-c-name{width:20%}
#v-providers th.pv-c-wire{width:18%}
#v-providers th.pv-c-cred{width:10%}
#v-providers th.pv-c-meter{width:31%}
#v-providers th.pv-c-ops{width:18%}                      /* 全部栏 3+20+18+10+31+18 = 100 */
/* 分栏之后，比例按各自那一栏真正装着什么重排：
   订阅栏——余量是打开这一栏的理由，让它再宽一档，「几时回来」那句话不再被挤掉；
   密钥栏——余量列整列撤掉（那一列在这里全是「—」），26% 的版面还给端点地址，
   于是 ai-gateway-qa.gaiaworkforce.com/shine-a… 那截省略号消失。 */
#v-providers[data-pvkind="sub"] th.pv-c-wire{width:18%}
#v-providers[data-pvkind="sub"] th.pv-c-meter{width:32%}
#v-providers[data-pvkind="sub"] th.pv-c-ops{width:17%}   /* 订阅栏 3+20+18+10+32+17 = 100 */
/* Claude 栏——这一页默认落在这里，所以它比别的栏多一列：订阅期限。开号日 / 到期 /
   周限相位这三个事实原本只在铭牌上以「14 天内才冒头」的红标露一次面，而「下一个
   要续的是谁」是每天都要问的一句。版面从余量和操作两列各借一点：余量在 29% 时
   已经宽裕，操作那一列装的是三颗图标按钮。 */
/* 读数与动作并成一格。三列各装一盏灯、一个开关、三颗图标时，每一列都左对齐
   在自己那 8% 里，于是灯和开关之间、开关和图标之间各空着一大截——浪费的不是
   某一列，是列与列之间那两道缝。并成一格靠右站，缝就没了。
   读数在左、动作在右：能扳的和只能读的分在两边，别混着排。停靠 / 熔断那一行
   带一句「几时放行」，装不下时整块读数自己换行占满一行（flex-wrap），而不是
   横着溢出去压住隔壁——那一句是这一格最该看清的时候说的话。 */
/* 2026-10-06 改成竖排三行：令牌 / 线路 / 开关 + 图标。横着并排时这一列要 22% 才装得下
   一行，而其余列都是三行高，并排省下的那一行高度谁也用不上；竖排之后这一列只要
   装得下「开关 + 两颗图标」那一行（约 175px），省出的宽度给用途和余量。 */
.pv-ops{display:flex;flex-direction:column;align-items:flex-start;gap:3px;min-width:0}
/* 徽标和开关在这一格里各占一行，壳收薄一圈：三行加起来要和铭牌、用途那两格一样高，
   否则整张表的行高就由这一格的壳定。 */
.pv-ops .badge{padding:1px 8px;font-size:11.5px}
.pv-ops .sw{padding:1px 8px 1px 4px}
/* 两盏灯上下站，不并排：并排时能不能站得下取决于「令牌有效」这枚徽标当下有多宽，
   于是同一张表里深色全换行、浅色前两行不换——同一列出现两种排法比哪一种都差。
   并进来的令牌格自己横着站：Claude 栏里它的种类只剩「边车」两个字，竖着摞就是
   拿一行去装两个字。别的栏里它照旧竖排——那里的种类是「Claude 订阅」这种整句。 */
.pv-ops-read{min-width:0;max-width:100%;display:flex;flex-direction:column;align-items:flex-start;gap:3px}
.pv-ops-read .pv-cred{flex-direction:row;align-items:center;gap:6px;min-width:0}
/* 线路那一行：灯和它后面那句（失败率 / 几时放行）并排，装不下再换行。 */
#v-providers .pv-state{flex-direction:row;flex-wrap:wrap;align-items:center;gap:3px 8px;max-width:100%}
.pv-ops-do{flex:none;display:flex;align-items:center;gap:6px;max-width:100%}
/* 图标按钮收到 26px：它和开关同一行，30px 的壳会让这一行比铭牌的任何一行都高。 */
#v-providers .pv-ops-acts .btn.icon{width:26px;height:26px}
/* 「近 40 次 · 失败 5%」这类附注跟在灯后面，剩不到 60px 才换行；换行后独占整行。 */
#v-providers .pv-state .fuse{flex:1 1 60px}
.pv-ops-acts{display:inline-flex;align-items:center;gap:6px}
/* 断路器那句「还有多久自动试探 / 几时放行」允许收省略号：它比这一格宽的时候，
   以前靠面板 overflow:hidden 切、或者干脆横着压住隔壁列。 */
.pv-ops .fuse{min-width:0;max-width:100%}
.pv-ops .fuse .t{min-width:0;overflow:hidden;text-overflow:ellipsis}
.pv-kebab[aria-expanded="true"]{color:var(--brand-2);background:var(--brand-dim)}
/* Claude 栏里令牌与线路并成一格。两盏灯答的是两个问题——凭证（登没登录）和
   断路器（这条线通不通）——而 `pvFleetHTML` 的注释早就写着那句关键的话：
   **一个没登录的号，断路器照样报「通」**。并排站着，那个对照第一次在行上看得见；
   原来它们隔着两列，读的人要自己把两格拼起来。顺带把 ≤1560 整列撤掉令牌那条
   规矩也解了：那一档以前是干脆没有凭证灯。
   宽度的账也因此重排：各格从「一摞标签」收成一两行之后，用途里那半截邮箱、
   期限里的周限都在截字，而线路 / 启用 / 动作三列装的是一盏灯、一个开关和三颗
   图标——行高 140px 时它们还算匀称，87px 之后就是空气。 */
#v-providers[data-pvkind="claude"] th.pv-c-cred,
#v-providers[data-pvkind="claude"] td.pv-c-cred{display:none}
#v-providers[data-pvkind="claude"] th.pv-c-name{width:19%}
#v-providers[data-pvkind="claude"] th.pv-c-wire{width:22%}
#v-providers[data-pvkind="claude"] th.pv-c-meter{width:28%}
#v-providers[data-pvkind="claude"] th.pv-c-term{width:14%}
/* 用途是这一页信息最多的一格（芯片 / 代理地址 / 绑定）。余量格多了一列 token 数
   （约 70px），这一截从用途里让：用途的芯片一行在 1600px 上占不到 260px。动作格竖排
   三行之后只要装下「开关 + 两颗图标」那一行（约 175px），14% 在 1561px 上是 196px。 */
#v-providers[data-pvkind="claude"] th.pv-c-ops{width:14%} /* Claude 栏无令牌列 3+19+22+28+14+14 = 100 */
/* 有「订阅期限」这一列的时候，铭牌上那枚到期红标自己让位：同一句话印两遍，
   而铭牌那一行正缺宽度装完整的邮箱。撤掉期限列的那一档（≤1120）再放它回来。 */
#v-providers[data-pvkind="claude"] .pv-plan.pv-due{display:none}
/* 多一列就得有人让出宽度，让的是量条那一格的刻度而不是它的条子：窗口名的头两个
   字就够认（三行的次序是固定的），而条子和右边那句「几时回来」少一个字都读不出。
   这条规则窄屏那两档已经写过一遍，Claude 栏只是把它提前到宽屏。 */
/* Claude 的窗口只报百分比，「余 100%」实测约 36px，所以读数那一列收到 40px，
   省下的给 token 数（「3.1M/≈9.4M」实测 63px）。 */
#v-providers[data-pvkind="claude"] .pv-m{grid-template-columns:minmax(0,72px) minmax(40px,1fr) 40px 64px 96px;gap:6px}
#v-providers[data-pvkind="claude"] .pv-mnone{grid-template-columns:minmax(0,72px) minmax(40px,1fr);gap:6px}
/* 期限列只在 Claude 栏出现。别的栏里它整列填不满：密钥线没有订阅期，另外三家
   订阅的台账还没补录——摆一列全是「—」的格子，是拿版面换一个「我们不知道」。 */
#v-providers:not([data-pvkind="claude"]) th.pv-c-term,
#v-providers:not([data-pvkind="claude"]) td.pv-c-term{display:none}
#v-providers[data-pvkind="key"] th.pv-c-meter,#v-providers[data-pvkind="key"] td.pv-c-meter{display:none}
#v-providers[data-pvkind="key"] th.pv-c-name{width:31%}
#v-providers[data-pvkind="key"] th.pv-c-wire{width:14%}
#v-providers[data-pvkind="key"] th.pv-c-cred{width:21%}
#v-providers[data-pvkind="key"] th.pv-c-ops{width:31%}   /* 密钥栏无余量列 3+31+14+21+31 = 100 */
/* 密钥栏的铭牌不必再让位给余量：地址整条摊开，两行都不打省略号 */
#v-providers[data-pvkind="key"] .pv-mail{white-space:normal;word-break:break-all;min-width:0}
/* 订阅期限格：三行同一个形状——标签 + 值，值走等宽，扫一列时日期自己排成一队。
   只有「到期」那行会变色（过期红、七天内黄），另外两行永远是灰的：开号日不会变，
   周限相位是机器读来的观测，两者都不是要人今天动手的事。 */
.pv-term{display:flex;flex-direction:column;gap:3px;min-width:0}
.pv-term .r{display:flex;align-items:baseline;gap:6px;min-width:0;line-height:1.35}
/* 到期那一行右端的「剩 N 天 / 已过 N 天」：和日期分列两端，一竖列扫下去比较的是
   同一个位置的两个数。近了变黄、过了变红——和左边的日期同一套变色。 */
.pv-term .r .l{margin-left:auto;flex:0 1 auto;min-width:0;padding-left:8px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums;color:var(--dim)}
.pv-term .r.due .l{color:var(--warn);font-weight:600}
.pv-term .r.over .l{color:var(--bad);font-weight:600}
/* 末行：周限靠左、登记入口靠右，共用一行。入口是一句链接不是一颗按钮——带壳的
   按钮在这一格要 64px，而这一列最缺的就是宽度。 */
.pv-term .r.foot{align-items:baseline;justify-content:space-between;gap:8px;margin-top:1px}
.pv-term .r.foot>.r{flex:1 1 auto;min-width:0}
.pv-termlink{flex:none;background:none;border:none;padding:0;font-size:11px;line-height:1.35;
  color:var(--brand-2);cursor:pointer;white-space:nowrap}
.pv-termlink:hover{text-decoration:underline}
.pv-termlink:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.pv-term .k{flex:none;font-size:11px;color:var(--faint)}
/* 日期永不让步：它是这一列的台账事实，截一半就不是日期了。要让的是它右边
   那句「剩 N 天」——那句缩了还读得出，日期缩了什么都读不出。 */
.pv-term .v{flex:none;font-family:var(--mono);font-size:11.5px;color:var(--dim);
  font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pv-term .v.none{color:var(--faint)}
.pv-term .r.due .v{color:var(--warn)}
.pv-term .r.over .v{color:var(--bad)}
.pv-term .r.stale .v{color:var(--faint)}

/* 车队读数单独占一行，不跟筛选芯片站在一起。三个数按不动就没有意义，所以号名
   是一处跳转：点它会清掉当前筛选，在表里只留这个账号。数字本身仍然只是读数。 */
.pv-fleet{display:inline-flex;align-items:center;gap:13px;min-width:0;flex-wrap:wrap}
.pv-fleet .c{display:inline-flex;align-items:baseline;gap:6px;min-width:0}
.pv-fleet .k{flex:none;font-size:11px;color:var(--faint);white-space:nowrap}
.pv-fleet .v{font-family:var(--mono);font-size:12.5px;font-weight:600;color:var(--dim);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.pv-fleet .w{min-width:0;max-width:160px;font-size:11px;color:var(--faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pv-fleet button.w{padding:0;background:none;color:var(--brand-2);text-align:left;cursor:pointer}
.pv-fleet button.w:hover{text-decoration:underline}
.pv-fleet button.w:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.pv-fleet .c.ok .v{color:var(--ok)}
.pv-fleet .c.warn .v{color:var(--warn)}
.pv-fleet .c.bad .v{color:var(--bad)}
.pv-fleet .sep{flex:none;width:1px;height:15px;background:var(--line)}

/* 定宽之后，每一格自己负责收边：外面不会再有人替它加宽。
   `.pv-state` 漏了这一条：它自带 min-width:132px，而线路列从来没有 132px，
   于是每一行的这一格都比格子宽，靠面板的 overflow:hidden 切掉右边那一截
   ——「冷却还剩 2m」这类话就是这么被切没的。 */
#v-providers .pv-cred,#v-providers .pv-state{min-width:0}
#v-providers .pv-cred-kind{max-width:100%;overflow:hidden;text-overflow:ellipsis}
#v-providers td{vertical-align:middle;padding-top:9px;padding-bottom:9px}
#v-providers td:first-child{padding-right:0}
/* 铭牌上的线名：刻字方块已经在左边点着这一行，那颗悬停小圆点就是多余的第二个记号
   （用户表里没有方块，那颗点照旧留着）。线名不折行，长名字打省略号——折行会让
   同一列里高矮不齐，扫起来比一截省略号更费劲（全名在 title 里）。 */
#v-providers .pv-link{font-family:var(--disp);font-size:13.5px;letter-spacing:-.01em;white-space:nowrap;gap:0;
  overflow:hidden;text-overflow:ellipsis;max-width:100%;min-width:0;flex:0 1 auto;display:block;text-align:left}
#v-providers .pv-link::before{content:none}
/* ---- 二档：≤1560，撤「令牌」------------------------------------------------
   撤的顺序只按一条：这一列没了，别处还找不找得到。令牌状态在抽屉里和授权向导里
   都还在；铭牌、用途和余量撤掉就没有别处可看。
   密钥栏是例外——它的余量列本来就不在，令牌是这一栏的第二支柱，撤掉只剩名字和
   一盏灯，所以它多扛一档。
   「用途 · 号池」任何一档都不撤：它是这一页唯一说得出「这个号在给谁干活」的地方，
   撤掉它，2026-08-16 那种「API 和拼车撞在同一个号上」就又变回看不见的事。 */
@media (max-width:1560px){
  #v-providers td.pv-c-cred,#v-providers th.pv-c-cred{display:none}
  #v-providers th.pv-c-name{width:22%}
  #v-providers th.pv-c-wire{width:20%}
  #v-providers th.pv-c-meter{width:34%}
  #v-providers th.pv-c-ops{width:21%}                    /* 3+22+20+34+21 = 100 */
  #v-providers[data-pvkind="sub"] th.pv-c-name{width:22%}
  #v-providers[data-pvkind="sub"] th.pv-c-wire{width:20%}
  #v-providers[data-pvkind="sub"] th.pv-c-meter{width:36%}
  #v-providers[data-pvkind="sub"] th.pv-c-ops{width:19%} /* 3+22+20+36+19 = 100 */
  #v-providers[data-pvkind="claude"] th.pv-c-name{width:20%}
  #v-providers[data-pvkind="claude"] th.pv-c-wire{width:21%}
  #v-providers[data-pvkind="claude"] th.pv-c-meter{width:27%}
  #v-providers[data-pvkind="claude"] th.pv-c-term{width:15%}
  /* 这一档的面板约 1230px（窗口减侧栏）：铭牌 20% 装得下「claude-side-r12-a07 + 协议标签」，
     用途 21% 装得下「接入 + 两枚芯片」，余量 27% 才在 token 数之外留得住窗口名的头几个字；动作格竖排后只要装下「开关 + 两颗图标」
     那一行（约 136px），14% 是 172px。 */
  #v-providers[data-pvkind="claude"] th.pv-c-ops{width:14%} /* 3+20+21+27+15+14 = 100 */
  /* 乘客那半截邮箱在这一档退场：名字已经认得出人，四个字符的邮箱认不出任何东西，全文在 title 里。 */
  #v-providers .pv-rider-sub{display:none}
  #v-providers[data-pvkind="key"] td.pv-c-cred,#v-providers[data-pvkind="key"] th.pv-c-cred{display:table-cell}
}
/* ---- 三档：≤1400，连内边距和按钮一起收 ---- */
@media (max-width:1400px){
  /* 八列 × 左右各 16px = 256px 的内边距，在这一档比任何一列都贵：定宽表里它不会
     把表撑宽，而是把每一格的内容往外挤，于是最右边那三个图标被面板的
     overflow:hidden 整整齐齐切掉——被切掉的按钮和不存在的按钮长得一样。 */
  #v-providers th,#v-providers td{padding-left:11px;padding-right:11px}
  #v-providers .pv-ops-acts .btn.icon{width:27px;height:27px}
  #v-providers .pv-ops-acts{gap:4px}
  #v-providers th.pv-c-name{width:21%}
  #v-providers th.pv-c-wire{width:20%}
  #v-providers th.pv-c-meter{width:35%}
  #v-providers th.pv-c-ops{width:21%}                    /* 3+21+20+35+21 = 100 */
  #v-providers[data-pvkind="sub"] th.pv-c-name{width:20%}
  #v-providers[data-pvkind="sub"] th.pv-c-meter{width:37%}
  #v-providers[data-pvkind="sub"] th.pv-c-ops{width:20%} /* 3+20+20+37+20 = 100 */
  #v-providers[data-pvkind="claude"] th.pv-c-name{width:20%}
  #v-providers[data-pvkind="claude"] th.pv-c-wire{width:22%}
  #v-providers[data-pvkind="claude"] th.pv-c-meter{width:25%}
  #v-providers[data-pvkind="claude"] th.pv-c-term{width:16%}
  /* 这一档图标收到 27px，「开关 + 两颗图标」约 164px；14% 在 1121px 上是 157px 减内边距
     后仍装得下（内边距 11px）。 */
  #v-providers[data-pvkind="claude"] th.pv-c-ops{width:14%} /* 3+20+22+25+16+14 = 100 */
  #v-providers[data-pvkind="key"] th.pv-c-name{width:29%}
  #v-providers[data-pvkind="key"] th.pv-c-wire{width:16%}
  #v-providers[data-pvkind="key"] th.pv-c-cred{width:22%}
  #v-providers[data-pvkind="key"] th.pv-c-ops{width:30%} /* 3+29+16+22+30 = 100 */
  /* 芯片自己也收一圈壳：这一档里池名比壳值钱得多。 */
  #v-providers .ap-cellbtn{padding:2px 7px;gap:4px}
  /* 量条那四列跟着收：窗口名先让（三行的次序是固定的，头两个字就够认），
     量条留住下限，右边两句不动。 */
  #v-providers .pv-m{grid-template-columns:minmax(0,44px) minmax(40px,1fr) 64px 60px 96px;gap:5px}
  #v-providers .pv-mnone{grid-template-columns:minmax(0,44px) minmax(40px,1fr);gap:5px}
  /* Claude 栏这一档多装一列 token 数，窗口名整列让掉（它在这里只剩不到 1px，
     露出半个字比不露更难认；次序固定，全名在 title 里），量条下限再让 4px，
     换「用途」那一格的「接入」按钮不被截。 */
  #v-providers[data-pvkind="claude"] .pv-m{grid-template-columns:minmax(32px,1fr) 40px 64px 94px;gap:4px}
  #v-providers[data-pvkind="claude"] .pv-m .ml{display:none}
  #v-providers[data-pvkind="claude"] .pv-mnone{grid-template-columns:minmax(0,44px) minmax(32px,1fr);gap:4px}
  /* 协议标签收成信号灯：筛选条上的协议已经用同一套颜色教过一遍，窄屏下这两个
     词占掉近三分之一的铭牌宽度，换成两颗灯，行高照旧两行（词在 title 里）。 */
  #v-providers .pv-id-top .tag,
  #v-providers .pv-id-top .ptag{width:7px;height:7px;padding:0;border-radius:50%;font-size:0;
    background:currentColor;margin-left:0!important;overflow:hidden}
  #v-providers .pv-id-top .ptag i{display:none}
  #v-providers .pv-id-top .tag+.tag{margin-left:4px!important}
}
/* ---- 四档：≤1120 ---- */
@media (max-width:1120px){
  /* 抬头装不下「分栏 + 读数窗」时，读数窗自己换行——它是仪表，不是导航，
     可以坐第二行；把分栏挤成滚动条才是真的读不出层级。 */
  .pv-face{flex-wrap:wrap;gap:0}
  #v-providers .pv-face>.pv-live{flex:1 1 100%;margin:0 0 8px;padding:0 10px}
  .pv-read,.pv-filters,.pv-tools{padding-left:10px;padding-right:10px}
  #v-providers th,#v-providers td{padding-left:9px;padding-right:9px}
  #v-providers .pv-ops-acts .btn.icon{width:25px;height:25px}
  #v-providers th.pv-c-name{width:21%}
  #v-providers th.pv-c-wire{width:19%}
  #v-providers th.pv-c-meter{width:36%}
  #v-providers th.pv-c-ops{width:21%}                    /* 3+21+19+36+21 = 100 */
  #v-providers[data-pvkind="sub"] th.pv-c-name{width:20%}
  #v-providers[data-pvkind="sub"] th.pv-c-meter{width:37%}
  #v-providers[data-pvkind="sub"] th.pv-c-ops{width:21%} /* 3+20+19+37+21 = 100 */
  /* 这一档撤掉期限列。它是「今天要不要动手」那一类信息，撤掉之后铭牌上那枚
     14 天红标仍然会替它开口；而留着它，余量条和「用途 · 号池」两列——这一页
     真正不能撤的两列——就要各让出一截。 */
  #v-providers[data-pvkind="claude"] th.pv-c-term,
  #v-providers[data-pvkind="claude"] td.pv-c-term{display:none}
  #v-providers[data-pvkind="claude"] th.pv-c-name{width:20%}
  #v-providers[data-pvkind="claude"] th.pv-c-wire{width:28%}
  #v-providers[data-pvkind="claude"] th.pv-c-meter{width:32%}
  #v-providers[data-pvkind="claude"] th.pv-c-ops{width:17%} /* 3+20+28+32+17 = 100 */
  /* 期限列撤了，「还有几天」就没有别处可看——铭牌上那枚红标在这一档回来。 */
  #v-providers[data-pvkind="claude"] .pv-plan.pv-due{display:inline-flex}
  #v-providers[data-pvkind="key"] th.pv-c-name{width:30%}
  #v-providers[data-pvkind="key"] th.pv-c-wire{width:16%}
  #v-providers[data-pvkind="key"] th.pv-c-cred{width:23%}
  #v-providers[data-pvkind="key"] th.pv-c-ops{width:28%} /* 3+30+16+23+28 = 100 */
  /* 窄屏收的是列宽，不是列数：把「几时回来」整列挤掉，剩下的余量读数就又变回一个
     不知道该不该等的数字。 */
  #v-providers .pv-m{grid-template-columns:minmax(0,40px) minmax(36px,1fr) 58px 92px;gap:4px}
  /* token 数是第一个让位的：它在这一行的 title 里还在，而「几时回来」没有别处可去。
     它是 display:none，所以 Claude 栏那条更具体的五列规则在这一档也得收成四列，
     否则「几时回来」会落进 token 那一列的宽度里被截掉。 */
  #v-providers .pv-m .mtk{display:none}
  #v-providers .pv-mnone{grid-template-columns:minmax(0,40px) minmax(36px,1fr);gap:4px}
  #v-providers[data-pvkind="claude"] .pv-m{grid-template-columns:minmax(0,40px) minmax(36px,1fr) 40px 92px;gap:4px}
  #v-providers[data-pvkind="claude"] .pv-m .ml{display:block}
  #v-providers[data-pvkind="claude"] .pv-mnone{grid-template-columns:minmax(0,40px) minmax(36px,1fr);gap:4px}
  /* 这一格已经是竖排的了，芯片里的字再折一行不会打乱任何东西——「API ↔ 双人车」
     是这一页最不该被截掉的一句话。 */
  #v-providers .pv-wire .pv-car,#v-providers .pv-wire .pv-cross{white-space:normal}
}

/* ---------- PROVIDERS · 页顶动作条 + 读数窗 ----------
   这一页顶上原本并排站着九颗按钮：四家订阅授权、两个 Sidecar 舰队操作、一个
   「添加供应商」，外加一条按分栏把其中两个藏起来的规则。它们长得平级，可它们
   根本不是一类事——「授权 Claude」和「对比 native / sidecar」唯一的共同点，
   是都被放在了右上角；而「对比 / 更新」夹在 Claude 和 Codex 中间，把那串家族
   名单拦腰截断，读起来像两家没听说过的订阅供应商。

   重排只按一条：这颗按钮作用在谁身上。
     作用在整张表 → 页顶两颗按钮各带一张菜单（加一条线 / 管整支舰队）。
       家族不再是五颗页面级按钮，而是「添加」这一个动作里的五个选项——它们本来
       就是同一件事的分支，摊在顶上只是让主位从一个变成六个。
     作用在本栏的实况 → 面板抬头右边那格读数窗（通 / 熔断 / 停靠 / 停用）。
     作用在行 → 抬头下面那条轨（用途、家族、搜索）。
   于是顶上永远只有一颗实心按钮，而唯一会自己变色的地方，是「几条线现在不接单」。 */
.pv-top{display:flex;align-items:center;justify-content:space-between;gap:18px;margin-bottom:14px}
.pv-top .eyebrow{margin:0;min-width:0}
/* 和供应商透视板的 .pv-acts 同名：那一条带 margin-top:12px，会把页顶按钮压低一截。 */
.pv-top .pv-acts{display:flex;align-items:center;gap:8px;flex:none;margin-top:0}
.pv-caret{width:0;height:0;margin-left:2px;border:4px solid transparent;border-bottom:0;
  border-top-color:currentColor;opacity:.7}
.pv-menu{position:relative}
.pv-pop{position:absolute;z-index:30;top:calc(100% + 7px);right:0;width:290px;padding:5px;
  background:var(--glass-thick);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  border:1px solid var(--rail-line);border-radius:var(--r-lg);box-shadow:var(--sheen),var(--shadow-3);
  opacity:0;transform:translateY(-4px) scale(.98);transform-origin:top right;pointer-events:none;transition:opacity .16s var(--ease),transform .28s var(--spring)}
.pv-menu.open .pv-pop{opacity:1;transform:none;pointer-events:auto}
.pv-pop-h{margin:7px 9px 4px;font-size:11.5px;font-weight:600;letter-spacing:0;color:var(--dim)}
.pv-pop button{display:flex;width:100%;align-items:flex-start;gap:10px;padding:8px 9px;border-radius:8px;
  background:none;color:var(--text);text-align:left;transition:background .12s}
.pv-pop button:hover{background:var(--fill-3)}
.pv-pop button:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.pv-pop button b{flex:none;width:15px;margin-top:1px;text-align:center;font-size:13px;font-weight:600;color:var(--pc,var(--faint))}
.pv-pop button b svg{display:block;width:14px;height:14px;margin:0 auto}
/* 每一项都带一句「它到底是什么」：这些名字（Sidecar、Kiro）光看名字选不出来。
   说明压在名字下面而不是并排——并排会把两个字的名字挤成三行。 */
.pv-pop .t{display:flex;flex-direction:column;gap:2px;min-width:0}
.pv-pop .t .l{font-size:13px;line-height:1.35;color:var(--text)}
.pv-pop .t .sub{font-size:11.5px;line-height:1.45;color:var(--faint)}
.pv-pop hr{border:0;border-top:1px solid var(--line-soft);margin:6px 4px}

/* 读数窗：本栏此刻的四种去向，一格一盏灯，坐在抬头右边。
   两条规矩让它读起来是仪表而不是又一排筛选按钮：
     数字比标签大一档、用等宽表格数字——先读数，再读它是什么；
     零是熄的（空心圈 + 灰数字），非零才点灯。于是「熔断 2」自己会跳出来，
     而全线在岗的日子这一格安静得像没有东西。
   颜色只上在读数和灯上，标签始终中性：仪表的刻字不跟着示数变色。
   窗底用 --bg（两套主题里都比台面暗/浅一档），像一块嵌进面板的玻璃。 */
.pv-gauge{display:flex;align-items:center;gap:1px;flex:none;align-self:center;margin:0;
  padding:2px;background:var(--bg);border:1px solid var(--line);border-radius:10px;transition:border-color .2s}
/* 白昼台面上 --bg 和抬头那条底色只差一度，压不出「嵌进去」的样子；换深一档的纸面 */
[data-theme=light] .pv-gauge{background:var(--surface-3)}
.pv-gauge.alarm{border-color:var(--bad-a40)}
.pv-gauge.warn{border-color:var(--warn-a40)}
.pv-gauge button{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 9px;border-radius:8px;
  background:none;transition:background .14s,box-shadow .14s}
.pv-gauge button:hover{background:var(--surface-2)}
.pv-gauge button.on{background:var(--surface-3);box-shadow:inset 0 0 0 1px var(--line)}
/* 和筛选轨同一条规则：「全部」被选中＝没筛任何东西，所以它不点亮，只当那个
   能按回去的词待着（.pv-chip.all.on 那里写着为什么）。 */
.pv-gauge .all.on{background:none;box-shadow:none}
[data-theme=light] .pv-gauge button.on{background:var(--surface)}
.pv-gauge button:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.pv-gauge .l{font-size:11.5px;color:var(--faint);white-space:nowrap}
.pv-gauge button.on .l{color:var(--text)}
.pv-gauge .n{font-family:var(--mono);font-size:13px;font-weight:600;color:var(--dim);font-variant-numeric:tabular-nums}
.pv-gauge i{width:6px;height:6px;flex:none;border-radius:50%;box-shadow:inset 0 0 0 1.5px var(--line)}
.pv-gauge button.lit i{background:var(--lc,var(--faint));box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--lc,var(--faint))}
.pv-gauge button.lit i{box-shadow:0 0 var(--halo-blur) var(--halo-spread) color-mix(in srgb,var(--lc,var(--faint)) var(--halo-alpha),transparent)}
.pv-gauge button.lit .n{color:var(--lc,var(--dim))}
/* 「全部」是复位键不是读数：不给灯，也不给数字（分栏条上那个数就是它） */
.pv-gauge .all .l{color:var(--dim)}
.pv-gauge .sep{width:1px;height:16px;flex:none;margin:0 3px;background:var(--line)}

/* 提示条：筛到「两边都在」时，筛出来的这堆东西本身就是待办。
   过去这句话是把导语原地换掉——同样的灰色小字、同一个位置，说的却是要动手的事。
   现在它是表上方一条带左轨的条子，末尾直接给出那一步动作。 */
.pv-notice{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:0;padding:11px 15px;
  font-size:12px;color:var(--dim);border-bottom:1px solid var(--line-soft);
  background:var(--warn-a9);box-shadow:inset 3px 0 0 var(--warn)}
.pv-notice[hidden]{display:none}
.pv-notice.info{background:var(--brand-a8);box-shadow:inset 3px 0 0 var(--brand)}
.pv-notice b{color:var(--text);font-weight:650}
.pv-notice button{margin-left:auto;padding:0;background:none;color:var(--brand-2);font-size:12px;font-weight:600;white-space:nowrap}
.pv-notice button:hover{text-decoration:underline}
.pv-plan.fee-miss{color:var(--warn);background:var(--warn-a12);box-shadow:inset 0 0 0 1px var(--warn-a40);cursor:pointer}
.pv-plan.fee-miss:hover{background:var(--warn-a20)}
/* Claude 档位：点钢印就能改。人标的优先；没标时显示 Claude 账号资料接口返回的档位。 */
button.pv-plan{cursor:pointer;font:inherit;font-size:10px;font-weight:600;letter-spacing:.03em;line-height:inherit}
button.pv-plan:hover{filter:brightness(1.12)}
button.pv-plan:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.pv-plan.p20{color:var(--ac);background:var(--ac-a14);box-shadow:inset 0 0 0 1px var(--ac)}
.pv-plan.p5{color:var(--ac);background:var(--ac-a12);box-shadow:inset 0 0 0 1px var(--ac-a14)}
.pv-plan.ptm{color:var(--brand-2);background:var(--brand-dim);box-shadow:inset 0 0 0 1px var(--brand-a40)}
/* 档位图标：Max 是仪表加倍数，Team 是两个人（planIconBody）。 */
.pv-plan.pi{display:inline-flex;align-items:center;gap:2px;padding:1px 4px;font-variant-numeric:tabular-nums;line-height:1.35}
.pv-plan.pi svg{width:11px;height:11px;flex:none}
.pv-plan.pi.miss{padding:0 4px}
.pv-plan.miss{color:var(--faint);background:transparent;box-shadow:none;
  border:1px dashed var(--line);padding:0 6px}
.pv-plan.miss:hover{color:var(--dim);border-color:var(--dim)}
.pv-plan.diff{box-shadow:inset 0 0 0 1px var(--warn)}
.jack-pop.plan-pop{width:min(280px,calc(100vw - 24px));z-index:82;padding:12px 13px;
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow-2)}
.plan-pop h4{margin:0 0 10px;padding-bottom:8px;border-bottom:1px solid var(--line-soft)}
.plan-opts{display:flex;flex-direction:column;gap:6px}
.plan-opt{display:flex;align-items:center;gap:8px;width:100%;padding:8px 10px;border-radius:8px;
  background:var(--surface-2);border:1px solid var(--line);color:var(--text);text-align:left;
  font-size:13px;font-weight:600}
.plan-opt:hover{border-color:var(--brand-a45);background:var(--surface-3)}
.plan-opt.on{border-color:var(--brand-a50);background:var(--brand-dim);color:var(--brand-2)}
.plan-opt .sub{margin-left:auto;font-size:11px;font-weight:500;color:var(--faint)}
.plan-custom{display:flex;gap:6px;margin-top:10px;align-items:center}
.plan-custom input{flex:1;min-width:0;padding:8px 10px;font-size:12.5px}
.plan-custom .btn{flex:none}
.plan-clear{display:block;margin:8px 0 0;padding:0;background:none;color:var(--faint);font-size:12px}
.plan-clear:hover{color:var(--text);text-decoration:underline}
.plan-hint{margin:8px 0 0;font-size:11.5px;color:var(--faint);line-height:1.5}
.plan-chips{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 8px}
.plan-chip{height:26px;padding:0 10px;border-radius:999px;border:1px solid var(--line);
  background:var(--surface-2);font-size:12px;font-weight:600;color:var(--dim)}
.plan-chip:hover{color:var(--text);border-color:var(--brand-a40)}
.plan-chip.on{border-color:var(--brand-a50);background:var(--brand-dim);color:var(--brand-2)}
.plan-chip:focus-visible{outline:2px solid var(--focus);outline-offset:1px}

/* 弹层打开期间锁住身后的底屏。手指落在弹层里任何一块不滚动的地方（标题、设置区、
   页脚），浏览器会顺着祖先链找到 body 去滚——弹层纹丝不动，滚的是底屏。
   body{overflow:hidden} 在 iOS Safari 上拦不住这一手，只有 position:fixed 拦得住，
   代价是滚动位置要自己记下来、解锁时放回去（见 lockBodyScroll）。 */
body.body-locked{position:fixed;left:0;right:0;width:100%;overflow:hidden}

/* ---------- DRAWER ---------- */
.scrim{position:fixed;inset:0;background:var(--scrim);opacity:0;pointer-events:none;transition:opacity .3s var(--ease);z-index:40}
.scrim.show{opacity:1;pointer-events:auto}
/* 抽屉 = 一张从右侧滑进来的浮动面板（macOS 26 的 inspector / iPad 的 sheet）：
   离窗口边 10px，24px 圆角，弹簧曲线滑入。收起时多移出 24px，连投影一起离场。 */
.drawer{position:fixed;top:10px;right:10px;bottom:10px;width:440px;max-width:calc(100vw - 20px);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-2xl);
  transform:translateX(calc(100% + 24px));transition:transform .44s var(--spring);z-index:41;display:flex;flex-direction:column;box-shadow:none;overflow:clip}
/* 移出屏幕的抽屉仍会把投影画回视口，多个隐藏抽屉叠成整页右缘的暗带。
   投影只属于真正打开的层级，隐藏态不应在台面上留下任何痕迹。
   overflow 同样：页脚比抽屉宽时，多出来的按钮从盒子左边溢出去。抽屉用
   translateX(100%) 收起，盒子走了，溢出去的那一截还留在视口里——乘客抽屉
   的「退订」关了之后仍压在车卡右下角，就是这一截。裁在盒子里，开着不压页面，
   收起也不留在屏幕上。用 clip 不用 hidden：hidden 的盒子仍能被程序滚动，
   焦点或 scrollIntoView 会把整个抽屉卷走；滚动只归 .drawer-body。 */
.drawer.show{transform:none;box-shadow:var(--shadow-2)}
/* 供应商看板（z 50/51）之上再叠一层：订阅授权是从额度面板里发起的，
   把看板压掉就等于让人在看不见那池账号的情况下往里加账号。 */
.scrim.atop{z-index:52} .drawer.atop{z-index:53}
/* 配线抽屉宽一档：切换链要同时读得下上游全名和它的改写，430px 会把模型名截半 */
.drawer.wide{width:500px}
/* 再宽一档：一份 Sail 诊断要并排读客户机器的检查结果和网关同一段时间的请求表 */
.drawer.xl{width:920px}
/* ---- 家宽出口（#v-egresses，egressinv.go）----
   汇总块本身就是筛选；出口按状态分组成卡片，一张卡先说连通（网关测 / 节点测分开），再说
   谁在用，最后一行是属性和本月流量费。详情在通用抽屉里，照 ip.cx 的样子一项一行。 */
#v-egresses .ei-bar{display:flex;align-items:center;gap:10px 12px;flex-wrap:wrap}
#v-egresses .ei-search{flex:0 1 280px;min-width:0;display:flex;align-items:center;gap:8px;padding:0 14px;height:32px;
  border:1px solid var(--input-line);border-radius:var(--r-pill);background:var(--input-bg);color:var(--faint)}
#v-egresses .ei-search input{flex:1;min-width:0;border:0;background:transparent;outline:0;font:inherit;font-size:13px;color:var(--text);padding:0}
/* 读数一行到底，放不下就收省略号（全文在悬停说明里），不把右边的按钮挤到第二行 */
#v-egresses .ei-meta{flex:1 1 0;min-width:0;font-size:12.5px;color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#v-egresses .ei-acts{display:flex;gap:8px;margin-left:auto}
#v-egresses .ei-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(128px,1fr));gap:10px;margin-top:16px}
#v-egresses .ei-tile{display:flex;flex-direction:column;align-items:flex-start;gap:2px;padding:12px 14px;border:1px solid var(--line-soft);
  border-radius:var(--r-lg);background:var(--surface);box-shadow:var(--card-shadow);font:inherit;text-align:left;color:var(--text);cursor:pointer;min-width:0}
#v-egresses .ei-tile:hover{border-color:var(--line)}
#v-egresses .ei-tile.on{border-color:var(--text);box-shadow:inset 0 0 0 1px var(--text)}
#v-egresses .ei-tile.static{cursor:default}
#v-egresses .ei-tile .k{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--dim)}
#v-egresses .ei-tile .v{font-family:var(--disp);font-size:22px;font-weight:700;letter-spacing:-.01em;font-variant-numeric:tabular-nums}
#v-egresses .ei-tile .s{font-size:11.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
#v-egresses .ei-tile.bad .v{color:var(--bad)} #v-egresses .ei-tile.warn .v{color:var(--warn)}
#v-egresses .ei-unreg{margin-top:16px;padding:12px 16px;border-radius:var(--r-lg);background:var(--warn-a12);font-size:13px;line-height:1.6;
  display:flex;gap:10px 16px;align-items:center;flex-wrap:wrap}
#v-egresses .ei-unreg>div{flex:1 1 360px;min-width:0}
#v-egresses .ei-unreg .mono{font-size:12px}
#v-egresses .ei-group{margin-top:28px}
#v-egresses .ei-group>h3{display:flex;align-items:center;gap:8px;margin:0 0 12px;font-size:13px;font-weight:600}
#v-egresses .ei-group>h3 .c{color:var(--faint);font-weight:500;font-variant-numeric:tabular-nums}
#v-egresses .ei-group>h3 .h{color:var(--dim);font-weight:400;font-size:12px}
#v-egresses .ei-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,340px),1fr));gap:12px}
#v-egresses .ei-card{display:flex;flex-direction:column;gap:10px;min-width:0;padding:14px 16px 12px;border:1px solid var(--line-soft);
  border-radius:var(--r-xl);background:var(--surface);box-shadow:var(--card-shadow);cursor:pointer}
#v-egresses .ei-card:hover{border-color:var(--line)}
/* 屏幕外的卡片先不排版（几百张卡一次排版要两百多毫秒，这样四十多毫秒），滚到附近才画 */
#v-egresses .ei-card{content-visibility:auto;contain-intrinsic-size:auto 240px}
#v-egresses .ei-card:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
#v-egresses .ei-card.h-down{border-color:var(--bad-a40)}
#v-egresses .ei-hd{display:flex;align-items:flex-start;gap:10px;min-width:0}
#v-egresses .ei-hd .lamp{margin-top:6px}
#v-egresses .ei-hd>div{min-width:0;flex:1}
#v-egresses .ei-ip{font-family:var(--mono);font-size:15px;font-weight:600;letter-spacing:-.01em;overflow-wrap:anywhere}
#v-egresses .ei-sub{display:flex;flex-wrap:wrap;gap:0 6px;margin-top:1px;font-size:12px;color:var(--dim)}
#v-egresses .ei-rows{display:grid;grid-template-columns:auto minmax(0,1fr);gap:6px 12px;align-items:center;font-size:12.5px}
#v-egresses .ei-rows>.l{font-size:12px;color:var(--faint);white-space:nowrap}
#v-egresses .ei-ft{display:flex;align-items:center;gap:8px;padding-top:10px;border-top:1px solid var(--line-soft)}
#v-egresses .ei-ft .meta{flex:1;min-width:0;font-size:11.5px;color:var(--faint)}
/* 显示方式：卡片 / 表格，两颗图标按钮。窄屏只有卡片（宽表到了手机上就是卡片），开关收起。 */
#v-egresses .ei-viewsel button{display:inline-flex;align-items:center;padding:6px 11px}
#v-egresses .ei-viewsel svg{width:15px;height:15px}
/* 表格视图：一行一个出口，每格最多两行（主读数 + 一行灰字），列头可排序。
   表头钉在模块页签条下面（--topbar-h）：外框用 overflow:clip 收圆角，它不是滚动容器，
   表头才钉得住整页滚动；外框同时是尺寸容器（eit），列按表格自己的宽度从次要的开始收起，
   1024 宽的窗口里也不横滑。容器本身就是一个层叠上下文，表头的 z-index 只在表里比大小，
   不会压到模块页签条上。 */
#v-egresses .ei-twrap{margin-top:20px;border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface);
  box-shadow:var(--card-shadow);overflow:clip;container:eit/inline-size}
#v-egresses .ei-t{width:100%;table-layout:fixed;border-collapse:separate;border-spacing:0;font-size:13px}
#v-egresses .ei-t th,#v-egresses .ei-t td{padding:10px 12px;border-bottom:1px solid var(--line-soft);vertical-align:middle;
  overflow:hidden;white-space:nowrap}
#v-egresses .ei-t th:first-child,#v-egresses .ei-t td:first-child{padding-left:16px}
#v-egresses .ei-t th:last-child,#v-egresses .ei-t td:last-child{padding-right:12px}
#v-egresses .ei-t thead th{position:sticky;top:var(--topbar-h,120px);z-index:2;padding-top:9px;padding-bottom:9px;
  background:var(--surface);border-bottom-color:var(--line);font-size:12px}
#v-egresses .ei-t th[aria-sort] .bl-th{color:var(--text)}
#v-egresses .ei-t .num{text-align:right}
#v-egresses .ei-t tbody:last-child tr:last-child td{border-bottom:0}
/* 列宽：账号一列不定宽，吃剩下的；其余定宽。表变窄时依次收起 本月 → 近 1 小时 → 位置 → 近 24 小时，
   每一档都给账号留够两个名字（≥ 206px），最后一档留一个名字加「+N」。近 7 天不收：挑出口主要看它。 */
#v-egresses .ei-t .c-st{width:146px} #v-egresses .ei-t .c-ip{width:160px} #v-egresses .ei-t .c-geo{width:176px}
#v-egresses .ei-t .c-ms{width:96px} #v-egresses .ei-t .c-up{width:124px} #v-egresses .ei-t .c-wk{width:112px}
#v-egresses .ei-t .c-req{width:88px} #v-egresses .ei-t .c-cost{width:88px} #v-egresses .ei-t .c-act{width:76px}
@container eit (min-width:1432px){#v-egresses .ei-t .c-geo{width:220px}}
@container eit (max-width:1269px){#v-egresses .ei-t .c-cost{display:none}}
@container eit (max-width:1181px){#v-egresses .ei-t .c-req{display:none}}
@container eit (max-width:1093px){#v-egresses .ei-t .c-geo{display:none}}
@container eit (max-width:851px){#v-egresses .ei-t .c-up{display:none}}
#v-egresses .ei-t tbody tr[data-id]{cursor:pointer}
#v-egresses .ei-t tbody tr[data-id]:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
/* 有事的行在左缘留一道色条，和出口状态页同一个记号 */
#v-egresses .ei-t tr.h-down td:first-child{box-shadow:inset 3px 0 0 var(--bad-fill)}
#v-egresses .ei-t tr.h-warn td:first-child{box-shadow:inset 3px 0 0 var(--warn-fill)}
#v-egresses .ei-t tr.h-held td{color:var(--dim)}
/* 分组抬头：一行灰底小字，和卡片视图的分组标题同一句话。这一行也按列出格子（同样的类名），
   不用 colspan：列被收起时 colspan 会凭空多出几列、把别的列挤窄。字写在第一格里、越过后面的空格。 */
#v-egresses .ei-t tr.ei-tg>*{padding-top:8px;padding-bottom:8px;background:var(--fill-4);border-bottom:1px solid var(--line-soft)}
#v-egresses .ei-t tr.ei-tg th{overflow:visible;font-size:12.5px;font-weight:600;color:var(--text);text-align:left}
#v-egresses .ei-t tr.ei-tg th>span{display:inline-flex;align-items:center;gap:8px}
#v-egresses .ei-t tr.ei-tg .c{color:var(--faint);font-weight:500;font-variant-numeric:tabular-nums}
#v-egresses .ei-t tr.ei-tg .h{color:var(--dim);font-weight:400;font-size:12px}
#v-egresses .ei-l1{overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
#v-egresses .ei-l2{margin-top:2px;overflow:hidden;text-overflow:ellipsis;font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums}
#v-egresses .ei-l1.ip{font-family:var(--mono);font-size:13px;font-weight:600;letter-spacing:-.01em}
#v-egresses .ei-l2 .bad,#v-egresses .ei-l1 .bad,#v-egresses .ei-l1.bad{color:var(--bad)}
#v-egresses .ei-l2 .warn,#v-egresses .ei-l1 .warn,#v-egresses .ei-l1.warn{color:var(--warn)}
#v-egresses .ei-l2 .ok{color:var(--ok)}
#v-egresses .ei-t .faint{color:var(--faint)}
#v-egresses .ei-t .ei-pill{max-width:100%;overflow:hidden;text-overflow:ellipsis}
/* 近 24 小时：一格一小时，整条是一块渐变（不是 24 个元素），格缝由遮罩切出来。
   定宽 96px = 24 格 × 4px（3px 色块 + 1px 缝），落在整像素上才不发虚。 */
.ei-strip{display:block;width:96px;height:13px;margin:2px 0 1px;border-radius:2px;background:var(--fill-2);
  -webkit-mask:repeating-linear-gradient(90deg,#000 0 3px,transparent 3px 4px);mask:repeating-linear-gradient(90deg,#000 0 3px,transparent 3px 4px)}
#v-egresses .ei-tac{display:flex;align-items:center;gap:4px;min-width:0}
#v-egresses .ei-tac .ei-acct{flex:0 1 auto;min-width:0;display:block}
#v-egresses .ei-tac .more{flex:none}
#v-egresses .ei-tac .more{font-size:12px;color:var(--dim)}
#v-egresses .ei-t td.c-act{text-align:right;font-size:0}
#v-egresses .ei-t td.c-act .btn+.btn{margin-left:2px}
#v-egresses .ei-t td.c-act .btn.icon.bad{color:var(--bad)}
#v-egresses .ei-t td.c-act .btn.icon.bad:hover{background:var(--bad-a12)}
/* 断流（egressdrops.go）：一行一个出口 IP。列头只说明、不排序，顺序由后端排好（常断的在前）。
   在用账号一列吃剩下的宽度；表变窄时依次收起 一起断 → 断开的请求 → 近 7 天 → 结论和账号，
   手机上留 IP、单独断和「换出口」。 */
#v-egresses .ei-dnote{margin-top:16px;padding:10px 14px;border-radius:var(--r-lg);background:var(--fill-4);font-size:12.5px;line-height:1.6;color:var(--dim)}
#v-egresses .ei-dnote b{font-weight:600;color:var(--text)}
#v-egresses .ei-dt thead th{font-weight:500;color:var(--dim);text-align:left}
#v-egresses .ei-dt thead th.num{text-align:right}
#v-egresses .ei-dt .c-dlv{width:112px} #v-egresses .ei-dt .c-ip{width:188px} #v-egresses .ei-dt .c-solo{width:112px}
#v-egresses .ei-dt .c-dtr{width:196px} #v-egresses .ei-dt .c-shared{width:80px} #v-egresses .ei-dt .c-str{width:108px}
#v-egresses .ei-dt .c-act{width:96px}
#v-egresses .ei-dt td.c-act{font-size:13px}
#v-egresses .ei-dt tbody tr[data-k]{cursor:pointer}
#v-egresses .ei-dt tbody tr[data-k]:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
@container eit (max-width:1080px){#v-egresses .ei-dt .c-shared{display:none}}
@container eit (max-width:980px){#v-egresses .ei-dt .c-str{display:none}}
@container eit (max-width:820px){#v-egresses .ei-dt .c-dtr{display:none}}
@container eit (max-width:620px){#v-egresses .ei-dt .c-dlv,#v-egresses .ei-dt .c-acct{display:none}#v-egresses .ei-dt .c-ip{width:auto}}
/* 近 7 天的断流轴：168px = 7 天 × 24px，一格一天；一次断开一道 2px 的竖线 */
.ei-dtrack{position:relative;display:block;width:168px;height:14px;border-radius:3px;background-color:var(--fill-4);
  background-image:repeating-linear-gradient(90deg,transparent 0 23px,var(--line-soft) 23px 24px)}
.ei-dtrack.wide{width:100%;height:22px;background-image:repeating-linear-gradient(90deg,transparent 0 calc(100%/7 - 1px),var(--line-soft) calc(100%/7 - 1px) calc(100%/7))}
.ei-dtrack i{position:absolute;top:2px;bottom:2px;width:2px;margin-left:-1px;border-radius:1px;background:var(--bad-fill)}
.ei-dtrack i.g{background:var(--faint);opacity:.55}
.ei-pill{display:inline-flex;align-items:center;gap:5px;flex:none;padding:2px 9px;border-radius:var(--r-pill);font-size:11.5px;font-weight:600;white-space:nowrap}
.ei-pill.ok{background:var(--ok-a12);color:var(--ok)} .ei-pill.warn{background:var(--warn-a12);color:var(--warn)}
.ei-pill.bad{background:var(--bad-a12);color:var(--bad)} .ei-pill.mute{background:var(--fill-3);color:var(--dim)}
.ei-pill.info{background:var(--gm-a12);color:var(--gm)}
.ei-dot{display:inline-block;flex:none;width:7px;height:7px;border-radius:50%;background:var(--faint)}
.ei-dot.ok{background:var(--ok-fill)} .ei-dot.warn{background:var(--warn-fill)} .ei-dot.bad{background:var(--bad-fill)}
.ei-dot.idle{background:transparent;box-shadow:inset 0 0 0 1.5px var(--ok-fill)}
.ei-vant{display:flex;flex-wrap:wrap;gap:4px 12px}
.ei-vant>span{display:inline-flex;align-items:center;gap:5px;white-space:nowrap;font-variant-numeric:tabular-nums}
.ei-vant .bad{color:var(--bad)} .ei-vant .warn{color:var(--warn)} .ei-vant .faint{color:var(--faint)}
.ei-bars{display:flex;align-items:stretch;gap:1.5px;height:16px;min-width:0;flex:1}
.ei-bars i{flex:1;min-width:0;border-radius:2px;background:var(--ok-fill);opacity:.85}
.ei-bars i.w{background:var(--warn-fill)} .ei-bars i.b{background:var(--bad-fill)} .ei-bars i.n{background:var(--fill-2);opacity:1}
.ei-barline{display:flex;align-items:center;gap:8px;min-width:0}
.ei-barline .pct{font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap}
.ei-accts{display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;min-width:0}
.ei-acct{display:inline-flex;align-items:center;gap:4px;max-width:100%;padding:1px 8px;border-radius:6px;background:var(--fill-4);font-size:12px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ei-acct.off{color:var(--faint);text-decoration:line-through}
.ei-acct.via{background:var(--warn-a12)}
.ei-why{padding:8px 12px;border-radius:var(--r-md);background:var(--fill-4);font-size:12.5px;line-height:1.55;color:var(--dim)}
.ei-why.bad{background:var(--bad-a10);color:var(--text)} .ei-why.warn{background:var(--warn-a12);color:var(--text)}
/* 详情抽屉：ip.cx 式的属性表，标签右对齐 */
.ei-sec{margin-top:22px}
.ei-sec:first-child{margin-top:4px}
.ei-sec>h4{display:flex;align-items:center;gap:8px;margin:0 0 10px;font-size:13px;font-weight:600}
.ei-sec>h4 .r{margin-left:auto;font-size:12px;font-weight:400;color:var(--faint)}
.ei-kv{border:1px solid var(--line-soft);border-radius:var(--r-lg);overflow:hidden}
.ei-kv>div{display:grid;grid-template-columns:100px minmax(0,1fr);gap:14px;align-items:center;padding:9px 14px;border-top:1px solid var(--line-soft);font-size:13px}
.ei-kv>div:first-child{border-top:0}
.ei-kv>div>span:first-child{text-align:right;font-size:12.5px;color:var(--dim)}
.ei-kv>div>span:last-child{min-width:0;overflow-wrap:anywhere}
.ei-kv .n{display:block;margin-top:2px;font-size:11.5px;color:var(--faint)}
.ei-score{display:flex;align-items:center;gap:10px}
.ei-score .t{flex:0 1 160px;height:7px;border-radius:4px;background:var(--fill-3);overflow:hidden}
.ei-score .t i{display:block;height:100%;border-radius:4px}
.ei-spark{display:block;width:100%;height:56px}
.ei-box{padding:12px 14px;border:1px solid var(--line-soft);border-radius:var(--r-lg)}
.ei-box .ei-bars{height:18px}
.ei-axis{display:flex;justify-content:space-between;gap:8px;margin-top:4px;font-size:11.5px;color:var(--faint);font-variant-numeric:tabular-nums}
.ei-sub-h{margin:16px 0 8px;font-size:12px;font-weight:500;color:var(--dim)}
.ei-acts-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:6px}
.ei-tbl{width:100%;border-collapse:collapse;font-size:12.5px}
.ei-tbl th{padding:8px 12px;border-bottom:1px solid var(--line-soft);text-align:left;font-size:11.5px;font-weight:500;color:var(--faint);white-space:nowrap}
.ei-tbl td{padding:9px 12px;border-top:1px solid var(--line-soft);vertical-align:top}
.ei-tbl tr:first-child td{border-top:0}
.ei-tbl .num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.ei-tblwrap{border:1px solid var(--line-soft);border-radius:var(--r-lg);overflow-x:auto}
.ei-add textarea{width:100%;min-height:160px;font-family:var(--mono);font-size:12px;white-space:pre;overflow-wrap:normal;overflow-x:auto}
.ei-res{font-variant-numeric:tabular-nums;white-space:nowrap}
.ei-res div{white-space:normal}
.ei-range{display:grid;grid-template-columns:2fr 1fr 1fr;gap:8px}
.ei-pair{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ei-acctbox:empty{display:none}
.ei-acctbox{margin-top:18px;padding-top:16px;border-top:1px solid var(--line-soft)}
.ei-acctbox h4{margin:0 0 10px;font-size:13px;font-weight:600}
.ei-acctbox .row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px}
.ei-acctbox .acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.ei-swlist{display:flex;flex-direction:column;gap:10px}
.ei-swrow{display:flex;flex-direction:column;gap:8px;padding:12px 14px;border:1px solid var(--line-soft);border-radius:var(--r-lg)}
.ei-swrow.off{opacity:.55}
.ei-swhd{display:flex;align-items:flex-start;gap:10px;cursor:pointer;font-size:13px}
.ei-swhd input{margin-top:3px}
.ei-swhd>span{display:flex;flex-direction:column;gap:2px;min-width:0}
.ei-swhd .n{font-size:12px;color:var(--faint)}
.ei-swrow select{width:100%;min-width:0;font-size:13px;font-variant-numeric:tabular-nums}
.ei-swdet{margin-top:-2px;font-size:12px;color:var(--faint)}
.ei-swwarn{font-size:12px;color:var(--warn)}
.ei-swok{margin-top:6px;font-size:12.5px;color:var(--ok)}
.ei-check{display:flex;align-items:flex-start;gap:8px;margin-top:14px;font-size:13px;cursor:pointer}
.ei-check input{margin-top:3px}
.ei-swwhy{width:100%;margin-top:8px}
.ei-steps{display:flex;flex-direction:column;gap:5px}
.ei-steps:empty{display:none}
.ei-step{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;color:var(--faint)}
.ei-step .ic{flex:none;width:16px;height:16px;margin-top:1px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;box-shadow:inset 0 0 0 1.5px var(--fill-1)}
.ei-step .n{color:var(--faint)}
.ei-step.run{color:var(--text)} .ei-step.run .ic{box-shadow:none;border:2px solid var(--fill-2);border-top-color:var(--text);animation:pspin .8s linear infinite}
.ei-step.done{color:var(--text)} .ei-step.done .ic{background:var(--ok-fill);box-shadow:none;color:var(--btn-ink)}
.ei-step.fail{color:var(--bad)} .ei-step.fail .ic{background:var(--bad-fill);box-shadow:none;color:#fff}
.px-pickwrap{display:flex;flex-direction:column;gap:6px;margin-bottom:8px}
.px-recbar{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 11px;border-radius:var(--r-md);border:1px solid var(--line-soft);background:var(--fill-4);font-size:12px;line-height:1.45}
.px-recbar .txt{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;min-width:0;flex:1}
.px-recbar .tag{display:inline-flex;align-items:center;padding:2px 7px;border-radius:var(--r-pill);font-weight:600;font-size:11px;white-space:nowrap}
.px-recbar .tag.pair{background:var(--ok-a12);color:var(--ok)}
.px-recbar .tag.team{background:var(--gm-a12);color:var(--gm)}
.px-recbar .tag.idle{background:var(--fill-3);color:var(--text)}
.px-recbar .btn{flex:none;white-space:nowrap}
.px-trig{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;padding:9px 12px;border:1px solid var(--input-line);border-radius:var(--r-md);background:var(--input-bg);color:var(--text);font-size:12.5px;text-align:left;cursor:pointer;transition:border-color .15s}
.px-trig:hover,.px-trig.open{border-color:var(--text)}
.px-trig-main{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;min-width:0;flex:1}
.px-trig-main .ip{font-family:var(--mono);font-weight:600;color:var(--text)}
.px-trig-main .sub{font-size:12px;color:var(--dim)}
.px-trig-caret{flex:none;font-size:12px;color:var(--dim);transition:transform .15s}
.px-trig.open .px-trig-caret{transform:rotate(180deg)}
.px-panel{display:none;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);box-shadow:0 8px 28px rgba(0,0,0,.18);overflow:hidden;margin-top:2px}
.px-panel.open{display:block}
.px-top{display:flex;flex-direction:column;gap:8px;padding:10px 12px;border-bottom:1px solid var(--line-soft);background:var(--fill-4)}
.px-q{width:100%;height:34px;box-sizing:border-box;padding:6px 11px;font-size:12.5px;border:1px solid var(--input-line);border-radius:var(--r-md);background:var(--input-bg);color:var(--text)}
.px-tabs{display:flex;align-items:center;flex-wrap:wrap;gap:5px}
.px-tab{display:inline-flex;align-items:center;gap:4px;padding:3px 9px;border-radius:var(--r-pill);border:1px solid transparent;background:var(--fill-3);color:var(--dim);font-size:11.5px;cursor:pointer;white-space:nowrap}
.px-tab .cnt{font-family:var(--mono);font-size:10.5px;opacity:.85}
.px-tab.on{background:var(--text);color:var(--bg);font-weight:600}
.px-regs{display:flex;flex-wrap:wrap;gap:5px;max-height:66px;overflow-y:auto;padding:2px 0}
.px-reg{padding:2px 8px;border-radius:var(--r-pill);border:1px solid var(--line-soft);background:var(--surface);color:var(--dim);font-size:11px;cursor:pointer;white-space:nowrap}
.px-reg.on{border-color:var(--text);color:var(--text);font-weight:600;background:var(--fill-3)}
.px-list{max-height:320px;overflow-y:auto}
.px-empty{padding:18px 12px;text-align:center;font-size:12px;color:var(--dim)}
.px-ghd{position:sticky;top:0;z-index:1;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:5px 12px;background:var(--surface-2);border-top:1px solid var(--line-soft);border-bottom:1px solid var(--line-soft);font-size:11.5px;font-weight:600;color:var(--dim)}
.px-ghd:first-child{border-top:0}
.px-item{display:flex;flex-direction:column;gap:4px;width:100%;padding:8px 12px;border:0;border-top:1px solid var(--line-soft);background:transparent;color:var(--text);text-align:left;cursor:pointer;font-size:12.5px;transition:background .12s}
.px-item:first-child{border-top:0}
.px-item:hover{background:var(--fill-4)}
.px-item.on{background:var(--ok-a12)}
.px-item:disabled{opacity:.48;cursor:not-allowed}
.px-it1{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;width:100%}
.px-itl{display:inline-flex;align-items:center;gap:6px;min-width:0;flex-wrap:wrap}
.px-itl .ip{font-family:var(--mono);font-weight:600;font-size:12.5px;color:var(--text)}
.px-itl .ep{font-family:var(--mono);font-size:11.5px;color:var(--faint)}
.px-itl .geo{font-size:12px;color:var(--dim)}
.px-itr{display:inline-flex;align-items:center;gap:7px;font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums;margin-left:auto}
.px-itr .ms.warn{color:var(--warn)} .px-itr .ms.bad{color:var(--bad)}
.px-it2{display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;padding-left:14px}
.px-uchip{display:inline-flex;align-items:center;gap:4px;padding:1px 7px;border-radius:5px;background:var(--fill-3);font-size:11.5px;color:var(--text)}
.px-uchip.same{background:var(--ok-a12)}
.px-uchip .pl{padding:0 4px;border-radius:3px;background:var(--fill-2);font-size:10.5px;font-weight:600}
.px-uchip .em{color:var(--dim);font-family:var(--mono);font-size:11px}
.sc-egcard{margin-top:8px;padding:10px 12px;border:1px solid var(--line-soft);border-radius:var(--r-lg);background:var(--fill-4);font-size:12.5px}
.sc-egcard:empty{display:none}
.sc-eghd{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;flex-wrap:wrap}
.sc-egmeta{display:flex;align-items:center;flex-wrap:wrap;gap:6px 9px;font-size:12.5px;color:var(--dim)}
.sc-egmeta .ip{font-family:var(--mono);font-weight:600;color:var(--text);font-size:13px}
.sc-egacts{display:flex;align-items:center;gap:6px;flex-wrap:wrap;flex:none}
.sc-egswap{display:none;margin-top:10px;padding:12px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
.sc-egswap.open{display:block}
.sc-egswap:empty{display:none}
.sc-egswap-hd{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;font-size:13px}
@media (prefers-reduced-motion:reduce){.ei-step.run .ic{animation:none}}
@media (max-width:860px){
  #v-egresses .ei-search{flex:1 1 100%}
  #v-egresses .ei-acts{margin-left:0}
  #v-egresses .ei-viewsel{display:none}
  .ei-kv>div{grid-template-columns:84px minmax(0,1fr);gap:10px}
  .ei-range{grid-template-columns:1fr 1fr}
}
/* 客户工作台：一个人的四面（总览 / 余额 / 额度 / 请求）。z 刻意压在通用抽屉
   (40/41) 之下——充值、座位、档案这些动作都从工作台里发起、叠在它上面，做完
   回来人还在原地；这正是它存在的理由：上下文不再跟着页面切换丢掉。 */
.cwb-scrim{position:fixed;inset:0;background:var(--scrim);opacity:0;pointer-events:none;transition:opacity .3s var(--ease);z-index:36}
.cwb-scrim.show{opacity:1;pointer-events:auto}
.drawer.cwb{width:840px;max-width:96vw;z-index:37}
.cwb-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  padding:12px 22px;border-bottom:1px solid var(--line-soft)}
.cwb-sub{font-size:12px;color:var(--faint)}
.cwb-head-acts{display:flex;align-items:center;gap:10px;flex-shrink:0}
.drawer.cwb .drawer-head h3{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cwb-actrow{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 16px}
.cwb-facts{display:flex;gap:8px 18px;flex-wrap:wrap;font-size:12.5px;color:var(--dim);margin:0 0 18px}
.cwb-facts b{color:var(--text);font-weight:600}
/* 计费未结：一条一行，说明文字与列表、列表与输入框之间都留出一档间距 */
.cwb-unsettled{margin:10px 0 14px;padding-left:18px;font-size:12.5px;color:var(--dim);line-height:1.7}
.cwb-unsettled b{color:var(--text);font-weight:600}
.cwb-actrow input#cwb-settle-reason{flex:1;min-width:220px}
/* 借用他人订阅：一份订阅一行，左边是谁的哪一份、用了多少，右边一颗收回。
   行与行、说明与列表、列表与按钮之间各留一档间距，和计费未结同一套数。 */
.cwb-borrows{display:flex;flex-direction:column;gap:8px;margin:10px 0 14px}
.cwb-borrow{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--line);
  border-radius:var(--r-md);background:var(--surface-2)}
.cwb-borrow.off{opacity:.72}
.cwb-borrow-main{flex:1;min-width:0}
.cwb-borrow-main .t{font-size:13px;color:var(--text);line-height:1.5}
.cwb-borrow-main .t .upk-pill{margin-left:8px;vertical-align:1px}
.cwb-borrow-main .s{font-size:12px;color:var(--dim);line-height:1.6;margin-top:3px}
.cwb-borrow-main .s b{color:var(--text);font-weight:600}
/* 添加借用的选择抽屉：搜索框、单选列表、选中后的说明，三段各自留白 */
.brw-search{width:100%;margin:0 0 10px}
.brw-opts{display:flex;flex-direction:column;gap:6px;max-height:46vh;overflow:auto;padding:2px}
.brw-opt{display:flex;gap:10px;align-items:flex-start;padding:11px 12px;border:1px solid var(--line);
  border-radius:var(--r-md);background:var(--surface);cursor:pointer}
.brw-opt.on{border-color:var(--focus);box-shadow:var(--focus-ring)}
.brw-opt input{margin-top:3px;flex:none}
.brw-opt .t{display:block;font-size:13px;color:var(--text);line-height:1.5}
.brw-opt .s{display:block;font-size:12px;color:var(--dim);line-height:1.5;margin-top:2px}
.brw-sum{margin:14px 0 0;padding:12px 14px;border-radius:var(--r-md);background:var(--surface-2);
  font-size:12.5px;line-height:1.7;color:var(--dim)}
.brw-sum b{color:var(--text);font-weight:600}
/* 车辆工作台：客户工作台的镜像。z 在它上一档、仍在通用抽屉之下；两台工作台
   互相跳转时先关对方——一个人和一辆车同时摊开只会互相挡住。 */
.carb-scrim{position:fixed;inset:0;background:var(--scrim);opacity:0;pointer-events:none;transition:opacity .3s var(--ease);z-index:38}
.carb-scrim.show{opacity:1;pointer-events:auto}
.drawer.carb{width:840px;max-width:96vw;z-index:39}
/* 把车名从死文本变成门：套在原来的排版类外面，只负责「是个链接」这件事 */
.carb-link{background:none;border:none;padding:0;margin:0;color:inherit;font:inherit;text-align:left;cursor:pointer}
.carb-link:hover{color:var(--brand-2)}
.carb-seatlink{color:var(--brand-2);white-space:nowrap}
.carb-link:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
/* 全局搜索（⌘K）：菜单负责巡视，这里负责「我知道我要找谁」。z 在 jack(70)
   之上、确认卡(80) 之下——从任何叠层里都按得出来、也压不住确认。 */
.pal-scrim{position:fixed;inset:0;background:var(--scrim);opacity:0;pointer-events:none;transition:opacity .18s;z-index:74}
.pal-scrim.show{opacity:1;pointer-events:auto}
/* ⌘K：Spotlight 的样子——一块厚玻璃，顶上一个大号搜索框，结果是圆角行 */
.pal{position:fixed;top:14%;left:50%;transform:translate(-50%,-10px) scale(.98);opacity:0;pointer-events:none;
  width:600px;max-width:92vw;background:var(--glass-thick);-webkit-backdrop-filter:blur(40px) saturate(180%);backdrop-filter:blur(40px) saturate(180%);
  border:1px solid var(--rail-line);border-radius:var(--r-2xl);
  box-shadow:var(--sheen),var(--shadow-2);z-index:75;overflow:hidden;transition:opacity .2s var(--ease),transform .32s var(--spring)}
.pal.show{opacity:1;pointer-events:auto;transform:translate(-50%,0)}
.pal-find{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--line-soft)}
.pal-find svg{width:19px;height:19px;color:var(--dim);flex:none}
.pal-find input{flex:1;background:transparent;border:none;outline:none;box-shadow:none;padding:0;font:inherit;font-size:19px;letter-spacing:-.015em;color:var(--text)}
.pal-find input:focus{box-shadow:none}
.pal-list{max-height:56vh;overflow-y:auto;padding:8px}
.pal-g{font-size:12px;letter-spacing:0;color:var(--dim);padding:10px 12px 4px;font-weight:600}
.pal-row{display:flex;align-items:baseline;gap:10px;width:100%;padding:9px 12px;border-radius:var(--r-md);background:transparent;text-align:left}
.pal-row .lb{font-weight:600;color:var(--text);flex:none}
.pal-row .sb{font-size:12px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}
.pal-row.on,.pal-row:hover{background:var(--fill-3)}
.pal-hint{padding:18px 14px;color:var(--faint);font-size:12.5px}
.pal-kbd{font-family:var(--sans);font-size:11px;font-weight:500;color:var(--dim);background:var(--fill-4);border:0;border-radius:5px;padding:1px 6px;margin-left:2px}
@media (max-width:860px){.pal{top:8%}.pal-kbd{display:none}}
.drawer-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:18px 18px 16px 24px;border-bottom:1px solid var(--line-soft)}
.drawer-head h3{font-family:var(--disp);font-size:19px;font-weight:600;letter-spacing:-.02em;margin:0}
.drawer-body{padding:20px 24px;overflow-y:auto;flex:1;min-height:0}
.drawer-foot{padding:14px 20px 16px 24px;border-top:1px solid var(--line-soft);display:flex;flex-wrap:wrap;gap:10px;justify-content:flex-end;align-items:center}
/* 乘客抽屉的动作不是一排同类按钮。上面一行是离开：退订、待补缴、放开座位，
   都不读表单。下面一行才提交抽屉里改过的车型和有效期。430px 里排成一行必然
   从左边溢出去，所以按这个分界折成两行，行内再放不下就换行，不缩字。 */
.seat-acts{display:flex;flex-direction:column;gap:10px;width:100%;min-width:0}
.seat-acts-quiet,.seat-acts-go{display:flex;flex-wrap:wrap;gap:8px 10px;align-items:center;min-width:0;max-width:100%}
.seat-acts-quiet{justify-content:flex-start}
.seat-acts-go{justify-content:flex-end}
.seat-acts .btn{flex:0 0 auto;max-width:100%}
.fee-lead{font-size:13px;color:var(--dim);line-height:1.6;margin:0 0 16px}
/* ---------- 用户选择器（开单抽屉） ----------
   原生下拉在用户上百之后找不到人：不能打字，一行一个长串，备注、用户名、当前订阅挤成
   一句。这里换成抽屉里内嵌的搜索列表：打字即筛（备注 / 邮箱 / 用户名 / ID / 账号名），
   每人两行，右侧只放和这次开单有关的事实（续期、当前档位、新注册）。选中后折成一张卡。
   不做浮层：抽屉在手机上是整屏，浮层要跟锚点、躲软键盘；内嵌列表两种屏幕都不用。 */
.upk{display:flex;flex-direction:column;gap:10px}
.upk-find{display:flex;align-items:center;gap:9px;height:42px;padding:0 5px 0 13px;
  background:var(--input-bg);border:1px solid var(--input-line);border-radius:var(--r-md);color:var(--faint);
  transition:border-color .16s var(--ease),box-shadow .16s var(--ease)}
.upk-find:focus-within{border-color:var(--focus);box-shadow:var(--focus-ring);color:var(--dim)}
.upk-find svg{flex:none}
.upk-find input{flex:1;min-width:0;height:100%;padding:0;border:0;border-radius:0;background:transparent;box-shadow:none;font-size:14px}
.upk-find input:focus{box-shadow:none}
.upk-find input::-webkit-search-cancel-button{-webkit-appearance:none;display:none}
.upk-x{display:flex;align-items:center;justify-content:center;flex:none;width:30px;height:30px;border-radius:var(--r-sm);
  background:none;color:var(--faint);font-size:17px;line-height:1;padding:0 0 2px}
.upk-x:hover{background:var(--fill-4);color:var(--text)}
.upk-x:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.upk-segs{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;margin:0 -2px;padding:0 2px}
.upk-segs::-webkit-scrollbar{display:none}
.upk-seg{flex:none;display:inline-flex;align-items:center;gap:5px;height:28px;padding:0 11px;border-radius:var(--r-pill);
  background:var(--fill-4);font-size:12px;font-weight:500;color:var(--dim);transition:background-color .14s,color .14s}
.upk-seg:hover{background:var(--fill-3);color:var(--text)}
.upk-seg b{font-weight:600;font-variant-numeric:tabular-nums;color:var(--faint)}
.upk-seg.on{background:var(--brand);color:var(--btn-ink)}
.upk-seg.on b{color:var(--btn-ink);opacity:.66}
.upk-seg:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.upk-box{border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;background:var(--surface)}
.upk-list{max-height:400px;overflow-y:auto;overscroll-behavior:contain;padding:4px}
.upk-grp{display:flex;align-items:baseline;justify-content:space-between;gap:10px;padding:10px 10px 5px;
  font-size:11.5px;font-weight:600;color:var(--dim)}
.upk-grp:first-child{padding-top:6px}
.upk-grp span{font-weight:500;color:var(--faint)}
.upk-row{display:flex;align-items:center;gap:11px;width:100%;min-height:52px;padding:7px 10px;border-radius:var(--r-md);
  background:none;color:var(--text);text-align:left;cursor:pointer;transition:background-color .1s}
.upk-row.hi{background:var(--fill-3)}
.upk-av{flex:none;display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;
  background:var(--fill-4);color:var(--dim);font-size:13px;font-weight:600;line-height:1;text-transform:uppercase}
.upk-who{display:block;flex:1;min-width:0}
.upk-nm{display:flex;align-items:center;gap:6px;min-width:0;font-size:13.5px;font-weight:600;line-height:1.35}
.upk-nm .t{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.upk-nm .dk-stamp{height:14px;padding:0 3px}
.upk-sub{display:block;margin-top:2px;font-size:12px;line-height:1.35;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.upk-id{font-family:var(--mono);font-size:11px}
.upk-side{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:3px;max-width:46%;min-width:0;text-align:right}
.upk-tier{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:500;color:var(--dim);white-space:nowrap}
.upk-meta{max-width:100%;font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.upk-meta.mono{font-family:var(--mono);font-size:11px}
.upk-pill{display:inline-flex;align-items:center;height:20px;padding:0 8px;border-radius:var(--r-pill);
  font-size:11px;font-weight:600;white-space:nowrap}
.upk-pill.renew{background:var(--brand-dim);color:var(--text);box-shadow:inset 0 0 0 1px var(--line)}
.upk-pill.new{background:var(--ok-a12);color:var(--ok)}
.upk-none{font-size:12px;color:var(--faint);white-space:nowrap}
.upk-row mark,.upk-ident mark{background:var(--fill-2);color:inherit;border-radius:3px;padding:0 1px;margin:0 -1px}
.upk-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 12px;
  border-top:1px solid var(--line-soft);background:var(--surface-2);font-size:11.5px;color:var(--faint)}
.upk-keys{display:flex;align-items:center;gap:10px;white-space:nowrap}
.upk-keys kbd{font-family:var(--sans);font-size:10.5px;font-weight:500;color:var(--dim);background:var(--fill-4);
  border-radius:4px;padding:1px 5px;margin-right:3px}
.upk-empty{padding:30px 16px 28px;text-align:center}
.upk-empty b{display:block;font-size:13px;font-weight:600;color:var(--text);margin-bottom:6px;word-break:break-all}
.upk-empty span{font-size:12px;color:var(--faint);line-height:1.6}
.upk-more{padding:10px 12px 8px;text-align:center;font-size:11.5px;color:var(--faint)}
.upk.closed .upk-segs,.upk.closed .upk-box{display:none}
.upk-av.blank{background:none;box-shadow:inset 0 0 0 1.5px var(--line);color:var(--faint)}
.upk-cancel{flex:none;height:30px;padding:0 11px;border-radius:var(--r-sm);background:none;
  font-size:12.5px;font-weight:500;color:var(--dim)}
.upk-cancel:hover{background:var(--fill-4);color:var(--text)}
.upk-cancel:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.upk-pill.warn{background:var(--warn-a12);color:var(--warn)}
.upk-pill.mute{background:var(--fill-4);color:var(--dim)}
.upk-tier.warn{color:var(--warn)}
.upk-meta .warn{color:var(--warn)}
/* 列表下方的额外动作（「新建用户并安排」）：虚线框，和行同一个排法，读得出是「另一条路」 */
.upk-new{display:flex;align-items:center;gap:11px;width:100%;min-height:52px;padding:8px 10px;
  border:1px dashed var(--line);border-radius:var(--r-lg);background:none;color:var(--text);text-align:left;cursor:pointer}
.upk-new:hover{background:var(--fill-4)}
.upk-new:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.upk-new .upk-av{font-size:17px;font-weight:500}
/* 身份块在别的容器里（「按客户设置」名单）：头像 + 两行，和列表行同一个样子 */
.upk-ident{display:flex;align-items:center;gap:11px;min-width:0}
.upk-ident .upk-nm{font-size:13px}
.upk-ident .upk-pill{height:18px;padding:0 6px;font-size:10.5px}
/* 成员表：身份列吃掉剩余宽度、邮箱省略，不把「移除」挤出抽屉 */
table.gov-mem td:first-child{width:100%;max-width:0}
table.gov-mem td:not(:first-child){white-space:nowrap}
.shuttle-search .upk-find{height:34px;padding:0 4px 0 11px;gap:8px}
.shuttle-search .upk-find input{width:auto!important;padding:0;border:0;border-radius:0;background:transparent;font-size:13px}
/* 选中后：一张卡。名字一行、身份一行，下面三格是开单前要看的三件事 */
.upk-card{padding:14px 16px 15px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2)}
.upk-card-hd{display:flex;align-items:center;gap:12px}
.upk-card .upk-av{width:40px;height:40px;font-size:16px;background:var(--fill-3);color:var(--text)}
.upk-card .upk-nm{font-size:15px}
.upk-card .upk-sub{white-space:normal;overflow-wrap:anywhere}
.upk-card-hd .btn{flex:none}
.upk-facts{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr) minmax(0,1fr);gap:12px 14px;
  margin-top:14px;padding-top:13px;border-top:1px solid var(--line-soft)}
.upk-fact{min-width:0}
.upk-fact .k{font-size:11px;color:var(--faint)}
.upk-fact .v{display:flex;align-items:center;gap:5px;margin-top:4px;font-size:13px;font-weight:600;
  font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.upk-fact .v .tier-ic{color:var(--text)}
.upk-fact .v.warn{color:var(--warn)}
.upk-fact .v.dim{color:var(--dim);font-weight:500}
.upk-fact .s{margin-top:3px;font-size:11.5px;color:var(--faint);line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.upk-fact .s.mono{font-family:var(--mono);font-size:11px}
@media (max-width:720px){
  .upk-facts{grid-template-columns:repeat(2,minmax(0,1fr))}
  .upk-fact.wide{grid-column:1/-1}
  .upk-keys{display:none}
}
/* 触屏：输入框 16px 防止 iOS 对焦放大；行和筛选片加高到手指够得着 */
@media (pointer:coarse){
  .upk-find{height:46px}
  .upk-find input{font-size:16px}
  .upk-seg{height:32px;padding:0 11px;font-size:12.5px}
  .upk-row{min-height:58px;padding:9px 10px}
  .upk-av{width:36px;height:36px;font-size:14px}
  .upk-nm{font-size:14.5px}
  .upk-sub{font-size:12.5px}
  .upk-keys{display:none}
  .upk-x{width:36px;height:36px}
  .upk-new{min-height:58px}
  .upk-cancel{height:36px}
  .shuttle-search .upk-find input{font-size:16px}
}
@media (prefers-reduced-motion:reduce){.upk-find,.upk-row,.upk-seg{transition:none}}
.fee-list{display:flex;flex-direction:column;gap:12px}
.fee-card{padding:14px 16px;border:1px solid var(--line);border-radius:12px;background:var(--surface-2)}
.fee-card .who{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 12px;margin-bottom:12px}
.fee-card .who b{font-size:13.5px}
.fee-card .who .pool{font-size:12px;color:var(--dim)}
.fee-card .pair{display:flex;flex-direction:column;gap:12px}
.fee-card .field{margin-bottom:0}
.fee-card .field label{margin-bottom:6px}
/* 批量补录抽屉。间距节奏抄的上面那组（卡片 14/16、块间 12、说明与控件之间 6），
   刻意不另起一套更挤的数值。预览表是这个抽屉的主角：粘进去之后先看「哪一行
   对上了哪个号、日期读成了哪天」，确认过再写库。 */
.trm-lead{font-size:13px;color:var(--dim);line-height:1.6;margin:0 0 16px}
.trm-box{width:100%;min-height:170px;padding:12px 14px;border:1px solid var(--line);border-radius:12px;
  background:var(--surface-2);color:var(--text);font-family:var(--mono);font-size:12.5px;line-height:1.7;resize:vertical}
.trm-note{font-size:12px;color:var(--faint);line-height:1.7;margin:10px 0 0}
.trm-res{margin-top:20px}
.trm-res h4{margin:0 0 12px;font-size:12.5px;font-weight:600;color:var(--text)}
/* 一行一块，不是一张五列表：抽屉只有 460 出头，五列会把账号名和错误原因都
   折成两三行，而这两样恰恰是这一步要读的东西。 */
.trm-line{padding:10px 0;border-top:1px solid var(--line)}
.trm-line:first-of-type{border-top:0}
.trm-raw{display:block;font-family:var(--mono);font-size:11.5px;color:var(--faint);
  line-height:1.6;word-break:break-all}
.trm-to{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;margin-top:6px;font-size:12.5px}
.trm-to .who{font-family:var(--mono);color:var(--text)}
.trm-to .day{color:var(--dim)}
.trm-to .tag{font-size:11px;color:var(--faint)}
.trm-line.bad .trm-to{color:var(--warn)}
#bl-accounts tr.unpriced td:first-child{box-shadow:inset 3px 0 0 var(--warn)}
/* 关闭钮：一枚灰色小圆（iOS / macOS 26 面板右上角那颗） */
.x{display:inline-grid;place-items:center;flex:none;width:30px;height:30px;padding:0;background:var(--fill-3);color:var(--dim);
  font-size:17px;line-height:1;border-radius:50%;transition:background-color .16s var(--ease),color .16s var(--ease)}
.x:hover{color:var(--text);background:var(--fill-2)}
.x:active{background:var(--fill-1)}
.x:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.x svg{width:15px;height:15px}
/* ---- 用量上限编辑器 ----------------------------------------------------
   一条上限一块，输入框下面永远跟着一条尺子：今天已用画成进度，近 14 天最重的
   一天画成一根刻度线。这把尺子是这组控件存在的理由——单看一个空输入框，填
   什么都像是对的；把真实用量摆在同一条轴上，填低了是一眼能看出来的错。
   （生产实测：单把 Key 单日额度消耗中位数 $12、p95 $184、峰值 230 请求/分。）
   间距节奏抄的 .fee-card：卡片 14/16、块间 14、标签与控件之间 6-8。 */
.cap-list{display:flex;flex-direction:column;gap:14px}
.cap-row{padding:14px 16px;border:1px solid var(--line);border-radius:12px;background:var(--surface-2)}
.cap-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;margin-bottom:10px}
.cap-head b{font-size:13.5px;font-weight:600}
.cap-head .cap-why{font-size:12px;color:var(--faint)}
.cap-in{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.cap-in select{width:auto;min-width:132px}
.cap-in input{width:120px}
.cap-in .unit{font-size:12.5px;color:var(--faint)}
.cap-meter{position:relative;height:6px;margin-top:14px;border-radius:999px;background:var(--line)}
.cap-meter .fill{position:absolute;left:0;top:0;bottom:0;border-radius:999px;background:var(--brand);transition:width .2s}
.cap-meter.hot .fill{background:var(--warn)}
.cap-meter.full .fill{background:var(--bad)}
/* 没有上限时轨道本身是虚的：一条画不满的进度条会读成「还早着呢」，而实际上
   它根本没有终点。 */
.cap-meter.open{background:repeating-linear-gradient(90deg,var(--line) 0 5px,transparent 5px 10px)}
.cap-peak{position:absolute;top:-5px;bottom:-5px;width:2px;background:var(--faint-a42);border-radius:1px}
.cap-foot{display:flex;flex-wrap:wrap;gap:4px 14px;margin-top:10px;font-size:12px;color:var(--faint);line-height:1.6}
.cap-foot .now{color:var(--dim)}
.cap-foot .over{color:var(--bad)}
.hint{font-size:12px;color:var(--faint);margin-top:6px}
.reveal{background:var(--surface-2);border:1px dashed var(--brand);border-radius:10px;padding:13px;font-family:var(--mono);font-size:12.5px;word-break:break-all;margin:10px 0;color:var(--brand-2)}

/* 推荐网络谱系图（返佣抽屉）。三个层级色从品牌色按饱和度递减派生——颜色的浓淡
   就是比例的高低（离得越近返得越多），图例和连接线共用同一套色，不需要看两遍。 */
.rbn{--rb1:var(--brand);
     --rb2:color-mix(in srgb,var(--brand) 55%,var(--faint));
     --rb3:color-mix(in srgb,var(--brand) 28%,var(--faint))}
.rbn-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px}
.rbn-stats>div{background:var(--surface-2);border:1px solid var(--line-soft);border-radius:10px;padding:8px 11px;min-width:0}
.rbn-stats i{font-style:normal;font-size:11px;color:var(--faint);display:block;margin-bottom:2px}
.rbn-stats b{font-family:var(--mono);font-size:15px;font-weight:600;white-space:nowrap}
.rbn-legend{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:16px;font-size:12px;color:var(--dim)}
.rbn-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;border:1px solid var(--line);background:var(--surface-2)}
.rbn-chip b{font-family:var(--mono);font-weight:600;color:var(--text)}
.rbn-dot{display:inline-block;width:9px;height:9px;border-radius:50%;flex:none}
.rbn .c1{background:var(--rb1)}.rbn .c2{background:var(--rb2)}.rbn .c3{background:var(--rb3)}
.rbn-root{display:flex;align-items:center;gap:9px;padding:10px 13px;border:1px solid var(--brand);border-radius:11px;background:var(--brand-dim);font-weight:700;font-size:13.5px}
.rbn-root .dim{font-weight:400;font-size:11.5px;margin-left:auto}
.rbn-tree{list-style:none;margin:0;padding:0 0 0 13px}
.rbn-tree li{position:relative;padding-left:16px}
.rbn-tree li::before{content:'';position:absolute;left:0;top:-5px;bottom:0;width:1.5px;background:var(--rbc,var(--line))}
.rbn-tree li:last-child::before{bottom:auto;height:26px}
.rbn-tree li::after{content:'';position:absolute;left:0;top:21px;width:13px;height:1.5px;background:var(--rbc,var(--line))}
.rbn li.lv1{--rbc:var(--rb1)}.rbn li.lv2{--rbc:var(--rb2)}.rbn li.lv3{--rbc:var(--rb3)}
.rbn-node{display:flex;align-items:center;gap:8px;padding:8px 11px;margin:5px 0;border:1px solid var(--line-soft);border-radius:9px;background:var(--surface-2);font-size:12.5px;min-width:0}
.rbn-name{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rbn-money{margin-left:auto;font-family:var(--mono);font-size:11.5px;white-space:nowrap;display:flex;gap:6px;align-items:center;color:var(--dim)}
.rbn-money .arr{color:var(--faint)}
.rbn-money .gain{color:var(--ok);font-weight:600}
.rbn-money .none{color:var(--faint)}
.rbn-node.miss{border-color:color-mix(in srgb,var(--warn) 45%,var(--line));background:color-mix(in srgb,var(--warn) 8%,var(--surface-2))}
.rbn-node.miss .gain{color:var(--warn)}
.rbn-empty{padding:26px 10px;text-align:center;color:var(--faint);font-size:12.5px;line-height:1.8}
.rbn-link{color:var(--brand-2);cursor:pointer;border-bottom:1px dashed color-mix(in srgb,var(--brand) 45%,transparent)}
.rbn-link:hover{border-bottom-style:solid}
@media (prefers-reduced-motion:no-preference){
  .rbn-tree .rbn-node{animation:rbnIn .2s ease both}
  @keyframes rbnIn{from{opacity:0;transform:translateX(-4px)}to{opacity:1;transform:none}}
}

/* ---------- 停摆台账 / OUTAGE LEDGER ----------
   每一条停着的线，右边都要回答「什么时候回来」。难点在于时间尺度差四个数量级：
   断路器的退避是 40 秒，月配额停靠是 3 天。同一根进度条画这两件事必然有一头是
   假的——3 天的条每秒走 0.0004%，看着就是不动。
   所以不按一张表排，按「等的是什么」分三舱：
     有准信 — 供应商自己说了日子。主角是那个日期，不画条（画了也不走）。
     在试探 — 断路器的自家计时器，秒级、真的在走：直接复用 .fuse 引信，
              连 data-until/data-cd 契约都不改，pvTick 那根秒针顺手就带上了。
     等你处理 — 没有钟。没有钟本身就是信号，所以这一舱给的是动作不是时间，
              左边用虚线轨（和 .hop-off 同一套「已停用」语汇）。
   分舱也正好答了操作者真正的问题：要不要我现在动手。 */
.og-bay{margin-bottom:28px}
.og-bay:last-child{margin-bottom:0}
.og-bay-hd{display:flex;align-items:baseline;gap:9px;margin:0 0 11px;flex-wrap:wrap}
.og-bay-hd h3{font-family:var(--disp);font-size:14px;font-weight:600;margin:0;letter-spacing:-.01em}
.og-bay-hd .n{font-family:var(--mono);font-size:11.5px;font-weight:600;color:var(--faint);
  background:var(--surface-2);border:1px solid var(--line-soft);border-radius:999px;padding:1px 8px}
.og-bay-hd .say{font-size:12px;color:var(--faint);margin-left:auto;text-align:right}
.og-list{display:flex;flex-direction:column;gap:8px}
.og-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 20px;align-items:center;
  background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--line);
  border-radius:12px;padding:12px 15px}
/* 左轨的颜色就是这一舱的可信度：品牌色＝有据，琥珀＝在猜，虚线＝没人管就一直这样 */
.og-bay.told .og-row{border-left-color:var(--brand)}
.og-bay.probing .og-row{border-left-color:var(--warn)}
.og-bay.hand .og-row{border-left-style:dashed;border-left-color:var(--faint)}
.og-who{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0}
.og-up{font-family:var(--disp);font-weight:600;font-size:14px;color:var(--text)}
.og-sep{color:var(--faint);font-size:12px}
.og-slot{font-family:var(--mono);font-size:12px;color:var(--dim);background:var(--surface-2);
  border-radius:6px;padding:1px 6px}
.og-sub{grid-column:1;display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--dim);min-width:0}
.og-id{font-family:var(--mono);font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
.og-why{color:var(--dim);min-width:0;overflow-wrap:anywhere}
.pv-fail{display:block;flex-basis:100%;min-width:0;font-size:12px;line-height:1.4;color:var(--dim);overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* 「机器」角标：这一行的熔断说的是出口，不是账号。存在的理由就是拦住那个最贵的
   误判——看到订阅号红了就去重新授权，而刷新令牌一转就换不回来了 */
.og-tag-mc{flex:0 0 auto;font-size:10.5px;line-height:1.6;padding:0 6px;border-radius:4px;
  color:var(--warn);background:var(--warn-a20);letter-spacing:.02em}
.og-time{grid-row:1/span 2;grid-column:2;text-align:right;white-space:nowrap}
.og-abs{display:block;font-family:var(--mono);font-size:16px;font-weight:600;color:var(--text);
  font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.og-left{display:block;font-size:11.5px;color:var(--brand-2);margin-top:3px;font-variant-numeric:tabular-nums}
.og-time .fuse{justify-content:flex-end}
/* 引信在这一舱里跟着轨走琥珀：这一舱说的是「它自己在重试」，不是「出事了」 */
#v-outages .fuse-bar{background:var(--warn-a20)}
#v-outages .fuse-bar i{background:var(--warn)}
/* ---- 出口状态 · 一个出口一行 ----
   左边一道色条说这一行的状态，和停摆台账、客户端兼容性同一种画法；格子里第一行是读数，
   第二行是灰字的依据或时间。宽表在窄屏换成卡片（docs/admin-layout.md §5）。 */
#v-egress .eg-hosts{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 16px}
#v-egress .eg-host{display:inline-flex;align-items:center;gap:10px;padding:6px 12px;border:1px solid var(--line);
  border-radius:var(--r-pill);background:var(--surface);font-size:12.5px;color:var(--dim);font-variant-numeric:tabular-nums}
#v-egress .eg-host b{font-family:var(--mono);font-weight:600;color:var(--text)}
#v-egress .eg-host .n-bad{color:var(--bad);font-weight:600}
#v-egress .eg-host .n-warn{color:var(--warn);font-weight:600}
#v-egress .eg-notes{display:grid;gap:8px;margin:0 0 16px}
#v-egress .eg-notes:empty{display:none}
#v-egress .eg-note{display:flex;align-items:center;gap:8px 16px;flex-wrap:wrap;padding:12px 16px;border-radius:var(--r-lg);
  background:var(--fill-1);font-size:13px;line-height:1.6;color:var(--text)}
#v-egress .eg-note.bad{background:var(--bad-a10)}
#v-egress .eg-note.warn{background:var(--warn-a12)}
#v-egress .eg-note>span{flex:1 1 320px;min-width:0}
#v-egress .eg-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface)}
#v-egress .eg-table{width:100%;min-width:980px;border-collapse:collapse;font-size:13px}
#v-egress .eg-table th,#v-egress .eg-table td{padding:14px 12px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
#v-egress .eg-table th:first-child,#v-egress .eg-table td:first-child{padding-left:18px}
#v-egress .eg-table th{color:var(--dim);font-size:12px;font-weight:500;white-space:nowrap}
#v-egress .eg-table tr:last-child td{border-bottom:0}
#v-egress .eg-table tr.eg-down td:first-child{box-shadow:inset 3px 0 0 var(--bad)}
#v-egress .eg-table tr.eg-warn td:first-child{box-shadow:inset 3px 0 0 var(--warn)}
#v-egress .eg-cell{display:flex;flex-direction:column;gap:4px;min-width:0}
#v-egress .eg-cell>b{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap}
#v-egress .eg-cell>span{font-size:12px;line-height:1.5;color:var(--dim)}
#v-egress .eg-cell .bad{color:var(--bad)}
#v-egress .eg-cell .warn{color:var(--warn)}
#v-egress .eg-cell .ok{color:var(--ok)}
#v-egress .eg-cell .idle{color:var(--faint);font-weight:500}
/* 探测的第二行和近 7 天那一行都是短读数，折行只会把最后一两个字挤到下一行 */
#v-egress .eg-c-probe .eg-cell>span,#v-egress .eg-hist{white-space:nowrap}
#v-egress .eg-cell>.eg-hist{color:var(--faint)}
#v-egress .eg-exit{display:flex;align-items:flex-start;gap:10px;min-width:170px}
#v-egress .eg-exit .lamp{margin-top:5px}
#v-egress .eg-exit b{font-family:var(--mono);font-size:12.5px}
#v-egress .eg-why{max-width:220px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
#v-egress .eg-ip{font-family:var(--mono);font-size:12.5px;line-height:1.5}
#v-egress .eg-chips{display:flex;flex-wrap:wrap;align-items:center;gap:6px;max-width:250px}
#v-egress .eg-acct{display:inline-flex;align-items:center;gap:4px;max-width:100%;min-width:0}
#v-egress .eg-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;min-width:0;margin:0;padding:3px 10px;
  border:1px solid var(--line);border-radius:var(--r-pill);background:var(--surface-2);font:inherit;font-size:12px;
  color:var(--text);cursor:pointer}
#v-egress .eg-chip:hover{border-color:var(--brand-40-on-line)}
#v-egress .eg-chip .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
#v-egress .eg-chip .k{flex:none;font-size:11px;color:var(--faint)}
#v-egress .eg-fail{flex:none;margin:0;padding:2px 8px;border:0;border-radius:var(--r-pill);background:var(--bad-a10);
  font:inherit;font-size:11px;font-weight:600;color:var(--bad);cursor:pointer}
#v-egress .eg-fail:hover{background:var(--bad-a18)}
#v-egress .eg-more{font-size:12px;color:var(--dim);white-space:nowrap}
#v-egress .eg-link{display:inline;margin:0;padding:0;border:0;border-bottom:1px dotted currentColor;border-radius:0;
  background:none;font:inherit;line-height:inherit;text-align:left;white-space:nowrap;color:var(--text);cursor:pointer}
#v-egress .eg-link:hover{color:var(--brand)}
#v-egress .ucd-method{margin-top:24px}
@media (max-width:860px){
  #v-egress .eg-wrap{overflow:hidden}
  #v-egress .eg-table{min-width:0}
  #v-egress .eg-table,#v-egress .eg-table tbody{display:block}
  #v-egress .eg-table thead{display:none}
  #v-egress .eg-table tr{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 16px;padding:16px 14px;
    border-bottom:1px solid var(--line-soft)}
  #v-egress .eg-table tr:last-child{border-bottom:0}
  #v-egress .eg-table td{display:block;min-width:0;padding:0;border:0}
  #v-egress .eg-table tr.eg-down{box-shadow:inset 3px 0 0 var(--bad)}
  #v-egress .eg-table tr.eg-warn{box-shadow:inset 3px 0 0 var(--warn)}
  #v-egress .eg-table tr.eg-down td:first-child,#v-egress .eg-table tr.eg-warn td:first-child{box-shadow:none}
  #v-egress .eg-table th:first-child,#v-egress .eg-table td:first-child{padding-left:0}
  #v-egress .eg-table td.eg-c-exit,#v-egress .eg-table td.eg-c-state,#v-egress .eg-table td.eg-c-acct,
  #v-egress .eg-table td.empty{grid-column:1/-1}
  /* 两两并排：探测和请求、出口 IP 和用户；账号芯片最宽，放最后单占一行 */
  #v-egress .eg-c-exit{order:1} #v-egress .eg-c-state{order:2} #v-egress .eg-c-probe{order:3} #v-egress .eg-c-req{order:4}
  #v-egress .eg-c-ip{order:5} #v-egress .eg-c-user{order:6} #v-egress .eg-c-acct{order:7}
  #v-egress .eg-table td[data-th]::before{content:attr(data-th);display:block;margin-bottom:6px;
    font-family:var(--sans);font-size:11px;font-weight:600;line-height:1.2;color:var(--faint)}
  #v-egress .eg-exit{min-width:0}
  #v-egress .eg-why,#v-egress .eg-chips{max-width:none}
  #v-egress .eg-why{-webkit-line-clamp:3}
}
.og-none{font-size:12px;color:var(--faint)}
/* 全线在岗是这一页最好的消息，由抬头那句话说；这里只补一句为什么空着。以前这块是
   一张居中的大字报，但上面多了运力架之后，好消息已经被那排满着的条子说过一遍了 */
.og-clear{padding:26px 20px;text-align:center}
.og-clear .small{font-size:13px;color:var(--faint)}
.tb-say.clear{color:var(--ok)}
@media(max-width:640px){
  .og-row{grid-template-columns:minmax(0,1fr)}
  .og-time{grid-row:auto;grid-column:1;text-align:left;margin-top:4px}
  .og-time .fuse{justify-content:flex-start}
  .og-bay-hd .say{margin-left:0;text-align:left;width:100%}
}

/* ---------- 运力架 / CAPACITY RACK ----------
   台账原本只有分子。「3 条停着」在一个五座的池子上是急事，在五十座的池子上是
   零头，而这一页从没说过是哪一种——飞书告警从写下来那天就带着「池内可用 n/N」，
   正是因为光有条数决定不了任何事。

   所以先把整块板的座位画出来：一个上游一格机架，一个账号一根竖条，条子插在同一
   根横轨上。两条编码，各管一件事，互不重复：
     高度 = 这个号自己还剩多少额度。这是全页唯一一个朝前看的数——别的读数都在
            讲已经坏掉的事，而一根只剩两成的条子讲的是下一个要坏的是谁。
     颜色 = 它属于下面哪一舱。绿＝在岗，紫＝有准信，琥珀＝在试探，灰虚＝等你处理。
            于是这排条子同时就是下面三舱的图例，不必另画一份。
   量不到额度的座位（普通线路、读不到余额的号）画成一整根压暗的条：没有刻度，
   就不假装有——「满格」和「不知道」在这块板上必须长得不一样。 */
.og-rack{display:flex;flex-wrap:wrap;margin:0 0 24px}
/* 一格机架就是一条线的入口：按下去开它的实况面板，和供应商页点名字是同一件事 */
.og-line{padding:0 14px 8px;border-bottom:1px solid var(--line);min-width:0;
  background:transparent;text-align:left;border-radius:0;transition:background .13s}
.og-line+.og-line{border-left:1px solid var(--line-soft)}
.og-line:hover{background:var(--surface-2)}
.og-line:hover .nm{color:var(--text)}
.og-line:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.og-seats{display:flex;align-items:flex-end;gap:4px;height:44px;padding-bottom:6px}
/* 空管子＝这个号自己的额度槽。液面是余量，管壁的底色是它归哪一舱——所以一个
   耗尽的号不会因为液面归零就从板上消失，它还在那儿，只是空着且是紫的。 */
.og-seat{position:relative;width:11px;height:38px;border-radius:2.5px;overflow:hidden;
  background:var(--faint-a14)}
.og-seat i{position:absolute;left:0;right:0;bottom:0;height:var(--lv,100%);border-radius:2.5px;
  background:var(--ok);transform-origin:bottom;
  animation:seatup .52s cubic-bezier(.2,.8,.3,1) both;animation-delay:calc(var(--i,0)*26ms)}
@keyframes seatup{from{transform:scaleY(0)}to{transform:scaleY(1)}}
.og-seat.told{background:var(--brand-a20)}
.og-seat.told i{background:var(--brand)}
.og-seat.probe{background:var(--warn-a22)}
.og-seat.probe i{background:var(--warn)}
/* 停用的座位不画液面：它不是空了，是被拔了 —— 和路由图上 .hop-off 同一套虚线语汇 */
.og-seat.hand{background:none;border:1px dashed var(--faint-a55)}
.og-seat.hand i{display:none}
/* 读不到状态：空框。它照样算在「可以被试」的分子里（池子自己就是这么数的），
   但一个我们说不出话的号不配拿一格实心的绿 */
.og-seat.unk{background:none;box-shadow:inset 0 0 0 1px var(--ok-a45)}
.og-seat.unk i{display:none}
.og-seat.flat i{height:100%;opacity:.48}
.og-line-ft{display:flex;align-items:baseline;gap:10px;margin-top:8px}
.og-line-ft .nm{font-size:12.5px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.og-line-ft .n{font-family:var(--mono);font-size:11.5px;color:var(--faint);
  font-variant-numeric:tabular-nums;margin-left:auto;white-space:nowrap}
.og-line-ft .n b{font-weight:600;color:var(--ok)}
.og-line-ft .n b.thin{color:var(--warn)} .og-line-ft .n b.out{color:var(--bad)}
/* 整条线自己停着：里面的号再满也接不到活。斜纹压在管子上，说的是「够不着」，
   不是「空了」——这两件事在这一页上是不同的决定 */
.og-line.blocked .og-seat{
  background-image:repeating-linear-gradient(-45deg,transparent 0 3px,
    var(--faint-a30) 3px 4px)}
.og-line.blocked .og-line-ft .nm{color:var(--faint)}
/* 出口：这条线的流量从哪个地址出去。画在运力架上而不是只在停摆列表里，是因为
   它有一半的价值在没出事的时候——地址昨夜换过、代理其实没生效，这两件事今天
   都不影响接单，等到影响的时候已经查不出是什么时候变的了。
   一行字，默认极淡；只有出问题时才有颜色，否则一排卡片会被十几个 IP 占满。 */
.og-eg{display:flex;align-items:center;gap:5px;margin-top:5px;font-family:var(--mono);
  font-size:10.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.og-eg .pip{width:5px;height:5px;border-radius:50%;background:var(--ok-a60);flex:0 0 auto}
.og-eg.warn{color:var(--warn)} .og-eg.warn .pip{background:var(--warn)}
.og-eg.bad{color:var(--bad)} .og-eg.bad .pip{background:var(--bad)}
/* 读数过期＝守望进程自己出了事。灰掉而不是藏起来：藏起来跟「这条线没配代理」
   看着一模一样，而那是两件完全不同的事 */
.og-eg.stale{color:var(--faint);opacity:.6} .og-eg.stale .pip{background:var(--faint)}

/* ---------- 恢复时刻表 / RECOVERY TIMETABLE ----------
   三行读数各说各的「22.1 小时后」，谁也看不出其中两条其实是同一刻回来的。把它们
   钉到同一根轨上，一眼就答完了这一页真正的问题：今天还要不要管，什么时候一次性
   都回来。

   轴按倍数走，不按等分。这一页的等待横跨四个数量级（断路器退避 40 秒、月配额停靠
   3 天），等分轴上秒级的那一条会紧贴原点，等于没画。对数轴让两头都读得见，代价是
   位置不能当刻度用——所以每根钉子都自带绝对时刻，轴只负责讲疏密。
   没有恢复时间的那几条不硬塞进轴里：它们进右边那格「没有钟」。没有钟本身就是信号。 */
.og-when{margin:0 0 24px}
.og-rail{display:flex;align-items:stretch;gap:14px}
/* --wire 是轨面到顶的距离，由 JS 按牌子实际叠了几层给出：钉子少的时候整块就矮
   下来，不留一片为最坏情况预留的空白 */
.og-track{--wire:36px;position:relative;flex:1;min-width:0;height:calc(var(--wire) + 27px)}
/* 线本身：起点实、越远越淡 —— 越靠右的承诺越不该被当真 */
.og-track::before{content:"";position:absolute;left:0;right:0;top:var(--wire);height:3px;border-radius:2px;
  background:linear-gradient(90deg,var(--brand-a50),
    var(--faint-a30))}
.og-now{position:absolute;left:0;top:calc(var(--wire) - 4px);width:3px;height:13px;border-radius:2px;background:var(--brand)}
.og-now::after{content:"现在";position:absolute;top:15px;left:0;font-size:10px;color:var(--faint);white-space:nowrap}
.og-tick{position:absolute;left:var(--x);top:var(--wire);transform:translateX(-50%)}
.og-tick i{display:block;width:1px;height:8px;margin:0 auto;background:var(--line)}
.og-tick b{display:block;margin-top:3px;font-size:10px;font-weight:400;color:var(--faint);white-space:nowrap}
.og-pin{position:absolute;left:var(--x);top:0;width:0;height:calc(var(--wire) + 6px)}
.og-pin .dot{position:absolute;bottom:0;left:0;width:9px;height:9px;border-radius:50%;
  transform:translateX(-50%);background:var(--brand);border:2px solid var(--surface);
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--brand-ahalo)}
.og-pin .stem{position:absolute;left:0;bottom:6px;width:1px;height:var(--stem,13px);
  background:var(--brand-a45)}
.og-pin .flag{position:absolute;left:0;bottom:calc(6px + var(--stem,13px));transform:translateX(-50%);
  display:flex;align-items:baseline;gap:5px;white-space:nowrap;padding:2px 7px;border-radius:7px;
  background:var(--surface-2);border:1px solid var(--line);cursor:default}
.og-pin.lft .flag{transform:translateX(-11px)} .og-pin.rgt .flag{transform:translateX(calc(-100% + 11px))}
.og-pin .flag b{font-family:var(--mono);font-size:11.5px;font-weight:600;color:var(--text);
  font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.og-pin .flag span{font-size:10.5px;color:var(--faint);max-width:130px;overflow:hidden;text-overflow:ellipsis}
.og-pin.probe .dot{background:var(--warn);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--warn-ahalo)}
.og-pin.probe .stem{background:var(--warn-a45)}
.og-pin:hover .flag,.og-pin.lit .flag{border-color:var(--brand-55-on-line);background:var(--surface-3)}
/* 钉子和下面的清单是同一份数据的两种画法，所以互相点亮 —— 指一个位置，就指到了那几行 */
.og-row.lit{border-color:var(--brand-45-on-line);background:var(--surface-2)}
/* 轴到这里为止，右边这一格是轴外：那几条根本没有时间可画 */
.og-noclock{flex:none;padding:0 0 0 14px;border-left:1px dashed var(--line);
  display:flex;flex-direction:column;justify-content:center;gap:5px}
.og-noclock .hd{font-size:10.5px;color:var(--faint)}
.og-noclock .r{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--dim);white-space:nowrap}
.og-noclock .r i{width:7px;height:7px;border-radius:50%;flex:none;background:var(--faint)}
.og-noclock .r.probe i{background:var(--warn)} .og-noclock .r.told i{background:var(--brand)}
.og-noclock .r b{font-family:var(--mono);font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
/* 窄屏收掉牌子，只留钉子。牌子并排要 150px 一张，挤到一起就互相盖住，那时它既读不出
   时刻也读不出疏密——两样都丢。钉子还在，疏密还在，确切时刻由下面的清单答；
   一颗钉子的 title 仍带着全文，指上去就有 */
@media(max-width:900px){
  .og-rail{flex-direction:column;gap:10px}
  /* 竖过来之后 flex:1 量的是高度，会把轨压成 0 —— 高度交回给 height 那条 calc */
  .og-track{flex:none;--wire:14px!important}
  .og-pin .flag,.og-pin .stem{display:none}
  .og-noclock{align-self:flex-start;border-left:none;border-top:1px dashed var(--line);
    padding:9px 0 0;flex-direction:row;gap:14px;align-items:center}
}

/* ---------- 供应商透视 / PROVIDER SCOPE BOARD ----------
   Tap a provider's name in the table and you patch into that one upstream's
   line to read its meters. It's the transpose of the model-health board — per
   model load carried on this single provider — so it reuses that board's
   availability bar (hbar/seg-*) and token flow meter (flow), and tints the whole
   surface with the provider's protocol accent (--pv) so each board wears its own
   line's identity instead of a generic modal purple. */
.pscrim{position:fixed;inset:0;background:var(--scrim);opacity:0;pointer-events:none;transition:opacity .3s var(--ease);z-index:50}
.pscrim.show{opacity:1;pointer-events:auto}
.pboard{--pv:var(--brand);--pv-dim:var(--brand-dim);
  position:fixed;top:50%;left:50%;width:988px;max-width:94vw;max-height:90vh;
  transform:translate(-50%,-48%) scale(.97);opacity:0;pointer-events:none;
  background:var(--card-grad);border:1px solid var(--line);border-radius:var(--r-2xl);
  box-shadow:var(--shadow-2);z-index:51;display:flex;flex-direction:column;overflow:hidden;
  transition:opacity .22s var(--ease),transform .42s var(--spring)}
.pboard.show{opacity:1;pointer-events:auto;transform:translate(-50%,-50%) scale(1)}
/* head — the line tap: source ●─live─▶ [provider node] */
/* flex:none 是这一行的重点，不是排版偏好。.pboard 是 max-height:90vh 的列向 flex，
   而 overflow:hidden 让抬头也成了滚动容器（min-height:auto 因此解析成 0）——板体一高，
   抬头就会被挤扁，把第二行整排按钮无声裁成一条缝。板体自己会滚，抬头没有理由让位。 */
.pv-head{position:relative;flex:none;padding:19px 22px 17px;border-bottom:1px solid var(--line-soft);overflow:hidden}
.pv-head::after{content:none}
.pv-line{display:flex;align-items:center;gap:0;flex-wrap:wrap;row-gap:9px;position:relative;z-index:1;padding-right:40px}
.pv-tap{width:11px;height:11px;border-radius:50%;background:var(--pv);flex:none;
  box-shadow:0 0 var(--halo-blur) var(--halo-spread) color-mix(in srgb,var(--pv) var(--halo-alpha),transparent)}
.pv-feed{height:2px;width:34px;flex:none;border-radius:2px;margin:0 2px;background:linear-gradient(90deg,var(--pv),var(--pv));background-size:200% 100%;animation:flow 1.8s linear infinite;opacity:.85}
.pv-node{display:inline-flex;align-items:center;gap:9px;background:var(--surface);background:color-mix(in srgb,var(--pv) 9%,var(--surface));
  border:1px solid var(--line);border:1px solid color-mix(in srgb,var(--pv) 55%,transparent);border-radius:999px;padding:7px 15px 7px 12px;flex:none;
  box-shadow:0 3px 12px -5px color-mix(in srgb,var(--pv) 40%,transparent)}
.pv-node .lamp{width:9px;height:9px}
.pv-name{font-family:var(--disp);font-weight:600;font-size:15px;letter-spacing:-.01em;max-width:42vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-meta{display:flex;align-items:center;gap:8px;margin-left:14px;flex-wrap:wrap}
/* 「带你去别处」的按钮和「这是谁」的身份行是两件事，所以它们不挤在同一条线里抢位置：
   身份行长短随客户邮箱变，按钮跟着漂就成了每次都要重新找的控件。给它固定的一行、
   右对齐——上面是关闭按钮、下面是每日/每周/累计，三者共用同一条右边线。
   :empty 收掉外边距，活动看板没有动作时不会凭空多出一段空白。供应商透视现在
   有测试 / 编辑：点名字是手机上够得着的入口，操作列在滑轨尽头够不着。 */
.pv-acts{display:flex;justify-content:flex-end;align-items:center;gap:8px;flex-wrap:wrap;
  margin-top:12px;position:relative;z-index:1}
.pv-acts:empty{display:none;margin-top:0}
.pv-acts .btn svg{width:14px;height:14px;flex:none}
.pv-when{font-size:11.5px;color:var(--bad);font-variant-numeric:tabular-nums}
.pv-url{font-family:var(--mono);font-size:11.5px;color:var(--dim);margin-top:11px;word-break:break-all;position:relative;z-index:1}
.pv-x{position:absolute;top:14px;right:14px;z-index:2}
/* body */
.pv-body{padding:18px 22px 24px;overflow-y:auto;flex:1}
.pv-axis{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-bottom:16px}
.pv-axis .winsel button.on{color:var(--pv);background:var(--surface);background:color-mix(in srgb,var(--pv) 10%,var(--surface));box-shadow:inset 0 0 0 1px var(--line);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--pv) 45%,transparent)}
.pv-live{display:inline-flex;align-items:center;gap:7px;font-size:11.5px;color:var(--faint)}
.pv-tiles{grid-template-columns:repeat(4,1fr);margin-bottom:18px}
.pv-tiles .tile.accent{border-color:color-mix(in srgb,var(--pv) 40%,var(--line))}
.pv-tiles .tile.accent::after{background:linear-gradient(90deg,var(--pv),transparent 92%)}
.pv-tiles .v .pvc{color:var(--pv)}
/* traffic strip — the load on the line over the window */
.pv-strip{background:var(--surface);border:1px solid var(--line);border-radius:13px;padding:13px 15px 11px;margin-bottom:20px}
.pv-strip-head{display:flex;align-items:center;justify-content:space-between;font-size:11px;color:var(--faint);margin-bottom:9px}
.pv-strip-head .lg i{display:inline-block;width:8px;height:8px;border-radius:2px;margin:0 5px 0 12px;vertical-align:-1px}
.pv-strip-head .lg i.n{background:var(--pv)} .pv-strip-head .lg i.e{background:var(--bad)}
.pv-spark{display:flex;align-items:flex-end;gap:2px;height:52px}
.pv-spark .b{flex:1;min-width:1px;height:100%;background:var(--line-soft);border-radius:1px 1px 0 0;display:flex;flex-direction:column;justify-content:flex-end;transition:background .12s}
.pv-spark .b i{display:block;width:100%}
.pv-spark .b i.n{background:var(--pv)} .pv-spark .b i.e{background:var(--bad)}
.pv-spark .b:hover{outline:1px solid var(--pv);outline-offset:0}
.pv-strip-ax{display:flex;justify-content:space-between;font-size:10px;color:var(--faint);font-family:var(--mono);margin-top:7px}
/* account pool — for an upstream that is itself N rotating credentials, the one
   number the line's health hides: how many of them are still carrying load */
.pv-pool{background:var(--surface);border:1px solid var(--line);border-radius:13px;padding:13px 15px 6px;margin-bottom:20px}
.pv-pool-head{display:flex;align-items:center;justify-content:space-between;font-size:11px;color:var(--faint);margin-bottom:4px}
.pv-pool-head b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.pv-pool-head b.thin{color:var(--warn)} .pv-pool-head b.out{color:var(--bad)}
.pv-pool-note{font-size:11.5px;color:var(--warn);padding:5px 0 8px}
.pv-acct{display:flex;align-items:center;gap:10px;padding:7px 8px;margin:0 -8px;border-top:1px solid var(--line-soft);font-size:12px}
.pv-acct .slot{font-family:var(--mono);font-size:11.5px;color:var(--faint);width:54px;flex:none}
.pv-acct .who{color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-acct .st{font-size:11px;width:132px;flex:none;white-space:nowrap}
.pv-acct .st.ready{color:var(--ok)} .pv-acct .st.cooldown,.pv-acct .st.quota_parked{color:var(--bad)}
.pv-acct .st.half_open{color:var(--warn)} .pv-acct .st.disabled,.pv-acct .st.unknown{color:var(--faint)}
.pv-acct .rq{font-family:var(--mono);font-size:11px;color:var(--faint);width:116px;text-align:right;flex:none;white-space:nowrap;flex-wrap:nowrap;justify-content:flex-end}
.pv-acct .rq .e{color:var(--bad)}
.pv-recheck-slot{width:78px;flex:none;text-align:center}
/* 「当前」原来是吊在行尾的一颗药丸，而且只有一行有——那一行就比别人宽，
   整块的右边缘因此是毛的。改成每行都留这一格（不在服务的行留空），右边缘
   就齐了；正在服务的那一行另给一点底色和一枚圆点，一眼扫得到，不需要靠找
   那颗药丸来定位。 */
.pv-acct .now{width:40px;flex:none;text-align:right;font-size:10.5px;font-weight:600;
  color:var(--pv);line-height:1.6;white-space:nowrap}
.pv-acct.serving{background:color-mix(in srgb,var(--pv) 8%,transparent);
  border-radius:9px;box-shadow:inset 2px 0 0 var(--pv)}
.pv-acct.serving .slot{color:var(--pv)}
/* Credit 余量：这一列回答的是「这个号还能干多久」，其余几列说的都是已经发生的事。
   条子从满往空走，所以看的是剩下的那截，不是用掉的那截。 */
.pv-cr{display:flex;align-items:center;gap:8px;width:148px;flex:none}
.pv-crbar{position:relative;flex:1;min-width:44px;height:4px;border-radius:2px;background:var(--faint-a22);overflow:hidden}
.pv-crbar i{position:absolute;inset:0 auto 0 0;border-radius:2px;background:var(--ok)}
.pv-cr.warn .pv-crbar i{background:var(--warn)} .pv-cr.out .pv-crbar i{background:var(--bad)}
.pv-crnum{font-family:var(--mono);font-size:11px;font-variant-numeric:tabular-nums;color:var(--dim);white-space:nowrap}
.pv-cr.warn .pv-crnum{color:var(--warn)} .pv-cr.out .pv-crnum{color:var(--bad)}
.pv-crnone{width:148px;flex:none;font-size:11px;color:var(--faint);text-align:center}
/* 订阅额度：一个账号不再是一行，是一小组——它的每个窗口各占一行。
   两个窗口不是同一个数的两种读法。5 小时的窗口吃顿饭就回来了，周额度一周才
   翻一次篇；同样是 70%，一个是今晚的事，一个是这周的事。压成一个数字（这块
   面板以前就是这么干的）等于把做预算时唯一要用的那个区别扔掉。

   每条窗口条上叠三样东西，回答的都是同一个问题——这个速度撑不撑得到重置：
     实心   = 已用；
     竖刻度 = 此刻的配速线（窗口已经走过多少），实心越过它就是烧超了；
     虚影   = 按当前速度到重置时会走到哪儿，顶到头（红帽）就是撑不到。
   刻度在 5 小时条上走得飞快、在周条上几乎不动，所以同一个记号在两行里自己
   就把两种时间尺度说清楚了，不用再加一句话去解释。 */
.pv-qacct{border-top:1px solid var(--line-soft);padding:9px 0 11px}
.pv-qtop{display:flex;align-items:center;gap:9px;font-size:12px}
.pv-qtop .who{color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-qtop .st{font-size:11px;white-space:nowrap;flex:none}
.pv-qtop .st.ready{color:var(--ok)} .pv-qtop .st.warning{color:var(--warn)}
.pv-qtop .st.quota_parked{color:var(--bad)} .pv-qtop .st.disabled,.pv-qtop .st.unknown{color:var(--faint)}
.pv-qnone{font-size:11px;color:var(--faint);padding:7px 0 1px 19px}
/* 窗口行：标签｜条｜数字｜那句要紧的话。四列固定，是为了上下两行的条子左右
   端对齐——两条对不齐的进度条，人会先去比长度，再发现比错了。 */
.pv-qwin{display:grid;grid-template-columns:112px minmax(56px,1fr) 84px minmax(0,160px);
  gap:11px;align-items:center;margin-top:9px;padding-left:19px}
.pv-qwin .wl{font-size:10.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pv-qwin.bind .wl{color:var(--dim)}
.pv-qwin.scoped{opacity:.66}
.pv-qnum{font-family:var(--mono);font-size:11.5px;font-variant-numeric:tabular-nums;
  text-align:right;color:var(--dim)}
.pv-qwin.warn .pv-qnum{color:var(--warn)} .pv-qwin.bad .pv-qnum{color:var(--bad)}
.pv-qnote{font-size:10.5px;color:var(--faint);text-align:right;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.pv-qnote.dry{color:var(--bad)}
/* 见底 + 重置摞成两行（qWinCell）：上红下浅、右对齐，两个动词落在同一列。行高压到
   1.2，两行 10px 字加起来 25px——只有会见底的那根条子长这么高，其余行照旧。
   b / i 只是两行的记号，字重和字形都还原成正文的。 */
.qn2{display:flex;flex-direction:column;align-items:flex-end;gap:1px;line-height:1.2}
.qn2 b{font-weight:inherit}
.qn2 i{font-style:normal;color:var(--faint)}
/* 三层叠在一根 5px 的轨上。--u 已用、--g 虚影的宽度、--p 配速线的位置，
   全是百分比，由 JS 一次算好——CSS 这边只负责画，不负责判断。 */
.qrail{position:relative;height:5px;border-radius:3px;background:var(--surface-3)}
.qrail i{position:absolute;top:0;bottom:0;display:block}
.qrail i.fill{left:0;width:var(--u);border-radius:3px;background:var(--ok);transition:width .4s ease}
.qrail i.ghost{left:var(--u);width:var(--g);border-radius:0 3px 3px 0;
  background:var(--ok-a26);transition:width .4s ease,left .4s ease}
.pv-qwin.warn .qrail i.fill{background:var(--warn)}
.pv-qwin.warn .qrail i.ghost{background:var(--warn-a26)}
.pv-qwin.bad .qrail i.fill{background:var(--bad)}
.pv-qwin.bad .qrail i.ghost{background:var(--bad-a26)}
/* 配速线出轨两头各 4px，才读得出是一道刻度而不是条子里的一段颜色 */
.qrail b.pace{position:absolute;left:var(--p);top:-4px;bottom:-4px;width:1.5px;
  border-radius:1px;background:var(--faint-a95)}
/* 撑不到重置：虚影转红，右端再封一道。虚影顶到头本身还能读成「刚好用完」，
   这两笔合起来才是「按这个速度会撞墙」——而且只染将来那一截，实心那截仍然
   按当下的水位着色。 */
/* 选择器要压得住上面按水位染色的那两条（同样多的类，写在后面才赢）：预测撞墙
   的虚影必须是红的，哪怕这条窗口此刻只是「接近上限」。 */
.pv-qwin .qrail.dry i.ghost{background:var(--bad-a32)}
.qrail.dry::after{content:"";position:absolute;right:0;top:-4px;bottom:-4px;width:2px;
  border-radius:1px;background:var(--bad)}
@media (prefers-reduced-motion:reduce){.qrail i{transition:none}}
/* 添加订阅账号：面板上唯一一个会往外部服务写东西的入口，所以它不做成主按钮，
   也不抢额度读数的位置——平时几乎不可见，需要时才在那儿。 */
.pv-add{background:none;border:1px solid var(--line);border-radius:999px;color:var(--faint);
  font:inherit;font-size:11px;line-height:1.6;padding:1px 9px;margin-left:9px;cursor:pointer;transition:.15s}
.pv-add:hover{color:var(--pv);border-color:var(--pv)}
/* per-model ledger */
.pv-table th.num,.pv-table td.num{text-align:right;font-variant-numeric:tabular-nums}
.pv-table th.sortable{cursor:pointer;user-select:none;white-space:nowrap}
.pv-table th.sortable:hover{color:var(--pv)}
.pv-sort{color:var(--pv);margin-left:3px;font-size:10px}
.pv-mname{font-family:var(--mono);font-size:12.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:230px}
.pv-mid{display:flex;align-items:center;gap:9px;min-width:0}
.pv-avail{display:flex;align-items:center;gap:10px;min-width:150px}
.pv-avail .hbar{flex:1;min-width:74px}
.pv-rate{font-family:var(--mono);font-size:12px;width:48px;text-align:right;flex:none}
.pv-rate.bad{color:var(--bad)} .pv-rate.warn{color:var(--warn)} .pv-rate.ok{color:var(--ok)}
.pv-lat{font-family:var(--mono);font-size:12px;color:var(--text)}
.pv-lat small{color:var(--faint)}
.pv-req{display:flex;align-items:baseline;gap:7px;justify-content:flex-end}
.pv-req .ku-err{color:var(--bad);font-size:11px;font-weight:600}
.pv-last{font-size:11.5px;color:var(--faint)}
.pv-last .err{color:var(--bad);display:block;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:2px}
.pv-empty{padding:30px;text-align:center;color:var(--faint)}
/* clickable provider name in the table — a patch point into its scope board */
.pv-link{background:transparent;padding:0;font-weight:600;color:var(--text);text-align:left;
  display:inline-flex;align-items:center;gap:7px;transition:color .14s}
.pv-link::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--faint);transition:background .14s,box-shadow .14s;flex:none}
.pv-link:hover{color:var(--brand-2)}
.pv-link:hover::before{background:var(--brand);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--brand-ahalo)}
.pv-link:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}

/* activity board（用户/密钥活动看板）— 与门户同款：统计条 + 53 周格阵三读法。
   色阶从品牌紫向台面色 color-mix，日夜主题自动换算；令牌与 portal.html 保持同值 */
.statstrip{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line-soft);
  border:1px solid var(--line);border-radius:var(--r-xl);overflow:hidden;margin-bottom:14px;box-shadow:var(--card-shadow)}
.statstrip.c3{grid-template-columns:repeat(3,1fr)}
.statstrip .stat{background:var(--surface);padding:13px 16px;min-width:0}
.stat .k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);white-space:nowrap}
.stat .v{font-family:var(--disp);font-size:21px;font-weight:600;letter-spacing:-.02em;margin-top:7px;line-height:1;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stat .v small{font-size:12px;font-weight:500;color:var(--dim);margin-left:3px}
.stat .v.txt{font-size:15.5px;padding-top:3px;font-family:var(--mono);font-weight:600;letter-spacing:0}
.hm-card{background:var(--card-grad);border:1px solid var(--line);border-radius:var(--r-xl);padding:15px 18px 13px;margin-bottom:26px;box-shadow:var(--card-shadow)}
.hm-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:13px}
.hm-cap{font-size:12px;color:var(--dim)}
.hm-scroll{overflow-x:auto;padding-bottom:3px}
.hm-grid{display:grid;grid-auto-flow:column;grid-template-rows:repeat(7,12px);grid-auto-columns:12px;gap:3px;width:max-content}
.hm-cell{width:12px;height:12px;border-radius:3px;transition:background-color .25s ease}
.hm-cell:hover{box-shadow:inset 0 0 0 1px var(--text-a60)}
.hm-cell.off{visibility:hidden}
.hm-cell.today{box-shadow:inset 0 0 0 1.5px var(--brand)}
.hm-c[data-lv="0"]{background:var(--surface-3)}
.hm-c[data-lv="1"]{background:var(--brand-30-on-surface-3)}
.hm-c[data-lv="2"]{background:var(--brand-55-on-surface-3)}
.hm-c[data-lv="3"]{background:var(--brand-80-on-surface-3)}
.hm-c[data-lv="4"]{background:var(--brand-2)}
.hm-c[data-lv="b"]{background:var(--brand-82-on-surface-3)}
.hm-months{display:grid;grid-auto-flow:column;grid-auto-columns:12px;grid-template-rows:14px;gap:3px;width:max-content;margin-top:5px;font-size:10.5px;color:var(--faint)}
.hm-months span{grid-row:1;white-space:nowrap}
.hm-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:9px;font-size:11.5px;color:var(--faint)}
.hm-legend{display:inline-flex;align-items:center;gap:3px}
.hm-legend i{width:10px;height:10px;border-radius:2.5px}
/* ---- 用户档案（活动看板的门户段，ub- 前缀）----
   拼车车况 / SLA / 消耗价值三块与门户同源的数据并进活动看板。车况的窗口条直接
   复用供应商额度板的 .qrail 仪表（实心=已用、竖刻度=配速、虚影=按当前速度到
   重置时的投影、红帽=撑不到重置）——操作员在供应商页已经学会读这套记号，
   同一台仪器出现在用户档案里说的还是同一件事。份额条借同一根轨：刻度线在这里
   改指「应得」，实心越过刻度就是超份额，和配速线「越线=烧超」同一种读法。 */
.ub-duo{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(340px,100%),1fr));gap:14px;margin-top:14px}
.ub-card{background:var(--card-grad);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:15px 18px 16px;box-shadow:var(--card-shadow);min-width:0}
.ub-card>.eyebrow{margin-bottom:11px}
.ub-card .bd-sec{margin-top:15px}
.ub-card .bd-sec>.eyebrow{margin-bottom:8px}
.ub-full{margin-top:14px}
/* 客户机器报回来的诊断（devicereport.go）。日志是等宽的、可能很宽，
   所以它自己横向滚，页面不跟着滚——这条是全站的规矩。 */
.dr-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));
  gap:15px 20px;margin:0 0 16px}
.dr-grid dt{font-size:11px;color:var(--faint);margin:0 0 5px}
.dr-grid dd{margin:0;font-size:13px;color:var(--text);word-break:break-word}
.dr-log{margin:0;padding:12px 14px;background:var(--bg);border:1px solid var(--line);
  border-radius:10px;font-family:var(--mono);font-size:11.5px;line-height:1.7;
  color:var(--dim);max-height:280px;overflow:auto;white-space:pre}
.dr-when{font-size:11.5px;color:var(--faint);margin:0 0 13px}
/* ---- 按诊断编号查（saildiag.go）-----------------------------------------
   客户运行 sail doctor / sail line 后拿到一个编号 sd_…，发给客服。查到的那一份在通用
   抽屉里打开（.drawer.xl，叠在客户工作台之上）：上半是客户机器当时的判断，下半是网关
   同一段时间看到的请求、订阅和设备。认不出的字段折叠成原样 JSON，客户端加了新段落
   也照样能读；被 48 KB 截断、已经不是 JSON 的那一份只给原文。 */
.sdg-find{display:flex;align-items:center;gap:10px 12px;flex-wrap:wrap;padding:16px 20px;margin-bottom:18px;
  border:1px solid var(--line);border-radius:var(--r-xl);background:var(--card-grad);box-shadow:var(--card-shadow)}
.sdg-find-t{margin:0;font-family:var(--disp);font-size:14px;font-weight:600;color:var(--text);white-space:nowrap}
.sdg-find .cat-search{flex:0 1 360px}
.sdg-find .hint{margin:0;flex:1 1 240px;line-height:1.6}
.sdg-list{margin:0 0 20px}
.sdg-list-hd{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0 0 10px}
.sdg-list-hd .eyebrow{margin:0}
.sdg-tbl-wrap{overflow:auto;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
.sdg-tbl-wrap.tall{max-height:380px;overscroll-behavior:contain}
.sdg-tbl{width:100%;border-collapse:collapse;font-size:12.5px}
.sdg-tbl th,.sdg-tbl td{padding:9px 12px;text-align:left;border-bottom:1px solid var(--line-soft);vertical-align:top}
.sdg-tbl th{position:sticky;top:0;z-index:1;background:var(--surface-2);color:var(--dim);font-size:11.5px;font-weight:500;white-space:nowrap}
.sdg-tbl tr:last-child td{border-bottom:0}
.sdg-tbl td.n{font-family:var(--mono);font-size:11.5px;font-variant-numeric:tabular-nums;white-space:nowrap}
.sdg-tbl td.nw{white-space:nowrap}
.sdg-tbl td.id{font-family:var(--mono);font-size:11px;color:var(--dim);max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sdg-tbl td.w{color:var(--dim);min-width:160px;line-height:1.55;word-break:break-word}
.sdg-tbl td.w b{color:var(--text);font-weight:600}
.sdg-tbl tr.bad td.st{color:var(--bad);font-weight:600}
.sdg-tbl tbody tr.go{cursor:pointer}
.sdg-tbl tbody tr.go:hover td{background:var(--surface-2)}
.sdg-tbl tbody tr.go:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.sdg-sec{margin:24px 0 0}
.sdg-sec:first-child{margin-top:0}
.sdg-h{display:flex;align-items:baseline;gap:6px 12px;flex-wrap:wrap;margin:0 0 12px;
  font-family:var(--disp);font-size:15px;font-weight:600;letter-spacing:-.01em;color:var(--text)}
.sdg-h small{font-family:var(--sans);font-size:12px;font-weight:400;letter-spacing:0;color:var(--faint)}
.sdg-chips{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 14px}
.sdg-chips .hint{margin:0 0 0 4px}
.sdg-lv{display:inline-flex;align-items:center;height:20px;padding:0 8px;border-radius:var(--r-pill);
  font-size:11px;font-weight:600;white-space:nowrap;background:var(--fill-4);color:var(--dim)}
.sdg-lv.ok{background:var(--ok-a12);color:var(--ok)}
.sdg-lv.note{background:var(--warn-a12);color:var(--warn)}
.sdg-lv.bad{background:var(--bad-a11);color:var(--bad)}
.sdg-grp{margin:0 0 16px}
.sdg-grp>.eyebrow{margin-bottom:8px}
.sdg-fs{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);overflow:hidden}
.sdg-f{display:grid;grid-template-columns:76px minmax(0,1fr);gap:4px 12px;padding:12px 14px;border-bottom:1px solid var(--line-soft)}
.sdg-f:last-child{border-bottom:0}
.sdg-f>.sdg-lv{justify-self:start;margin-top:1px}
.sdg-f-t{font-size:13px;font-weight:600;color:var(--text);line-height:1.5}
.sdg-f-t small{font-family:var(--mono);font-size:10.5px;font-weight:400;color:var(--faint);margin-left:8px}
.sdg-f.ok .sdg-f-t{font-weight:500;color:var(--dim)}
.sdg-f-d,.sdg-f-a{font-size:12.5px;line-height:1.6;margin-top:4px;white-space:pre-wrap;word-break:break-word}
.sdg-f-d{color:var(--dim)}
.sdg-f-a{color:var(--text)}
.sdg-f-a b{font-weight:600;margin-right:6px}
.sdg-json{margin-top:10px}
.sdg-json summary{cursor:pointer;font-size:12px;color:var(--dim);user-select:none;width:fit-content}
.sdg-json summary:hover{color:var(--text)}
.sdg-json summary:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.sdg-json[open] summary{margin-bottom:8px}
.sdg-json .dr-log{max-height:320px;white-space:pre-wrap;word-break:break-word}
.sdg-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(250px,100%),1fr));gap:10px;margin:0 0 16px}
.sdg-fact{padding:12px 14px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);min-width:0}
.sdg-fact.bad{border-color:var(--bad-a40)}
.sdg-fact .k{font-size:11px;color:var(--faint);margin:0 0 5px}
.sdg-fact .v{font-size:13px;color:var(--text);line-height:1.6;word-break:break-word}
.sdg-fact .s{font-size:12px;color:var(--dim);line-height:1.6;margin-top:3px;word-break:break-word}
.sdg-fact .s+.s{margin-top:2px}
.sdg-code{font-family:var(--mono);font-size:11.5px;color:var(--dim);word-break:break-all}
@media (max-width:860px){
  .sdg-find .cat-search{flex:1 1 100%}
  .sdg-f{grid-template-columns:minmax(0,1fr)}
}
/* ---- Sail 诊断（sailogs.go）--------------------------------------------
   主角是「同刻矩阵」：行 = 坏法，列 = 时段，格高 = 这一格里**几台机器**报了它，
   不是几行。一台断网的机器能在一小时里刷出六百行 DNS 失败，按行数画它会像一次
   全网事故；按机器数画，它只是一排矮格子——而八台机器同一刻齐刷刷顶格，才是
   该去查我们自己（或 Cloudflare）的样子。选中一台机器时它的格子描边，
   「他的故障和别人的重合吗」一眼就答了。 */
.slg-head{display:flex;align-items:flex-end;justify-content:space-between;gap:16px 28px;flex-wrap:wrap;margin-bottom:18px}
.slg-verdict{margin:0;max-width:66ch;font-family:var(--disp);font-size:17px;line-height:1.6;font-weight:500;
  letter-spacing:-.005em;color:var(--text)}
.slg-verdict b{font-weight:600}
.slg-verdict .bad{color:var(--bad)}
.slg-verdict small{display:block;margin-top:6px;font-family:var(--sans);font-size:12.5px;font-weight:400;
  line-height:1.6;color:var(--faint);letter-spacing:0}
.slg-ctl{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.slg-bar{display:flex;gap:10px 14px;align-items:center;flex-wrap:wrap;margin-bottom:16px}
.slg-bar .cat-search{flex:0 1 340px}
.slg-toggle{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;color:var(--dim);cursor:pointer;
  user-select:none;white-space:nowrap}
.slg-toggle input{accent-color:var(--brand);margin:0}
.slg-pins{display:flex;gap:6px;flex-wrap:wrap}
.slg-pin{display:inline-flex;align-items:center;gap:6px;padding:4px 6px 4px 11px;border-radius:999px;
  border:1px solid var(--brand-50-on-line);background:var(--brand-a15);color:var(--text);font-size:11.5px;
  font-family:inherit;cursor:pointer;white-space:nowrap}
.slg-pin span{color:var(--faint)}
.slg-pin b{font-weight:600}
.slg-pin i{font-style:normal;width:16px;height:16px;border-radius:50%;display:grid;place-items:center;
  color:var(--dim);font-size:12px;line-height:1}
.slg-pin:hover i{background:var(--brand-a30);color:var(--text)}
.slg-pin:focus-visible,.slg-lbl:focus-visible,.slg-dev:focus-visible,.slg-who:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

.slg-board{border:1px solid var(--line);border-radius:var(--r-xl);background:var(--card-grad);box-shadow:var(--card-shadow);
  padding:18px 20px 16px;margin-bottom:20px}
.slg-board-hd{display:flex;align-items:baseline;justify-content:space-between;gap:10px 18px;flex-wrap:wrap;margin-bottom:16px}
.slg-board-t{font-family:var(--disp);font-size:14px;font-weight:600;color:var(--text);margin:0}
.slg-legend{display:flex;gap:6px 16px;align-items:center;flex-wrap:wrap;font-size:11.5px;color:var(--faint)}
.slg-legend span{display:inline-flex;align-items:center;gap:6px}
.slg-legend u{display:inline-block;width:6px;border-radius:1.5px 1.5px 0 0;background:var(--bad);text-decoration:none}
.slg-legend .ring{width:9px;height:9px;border-radius:2px;outline:1.5px solid var(--brand-2);outline-offset:1px}
.slg-scroll{overflow-x:auto;overscroll-behavior-x:contain;padding-bottom:2px}
.slg-mx{display:grid;grid-template-columns:132px minmax(0,1fr) 104px;column-gap:14px;min-width:660px}
.slg-row{display:contents}
.slg-row.err{--c:var(--bad)} .slg-row.warn{--c:var(--warn)}
.slg-row.info{--c:var(--faint)} .slg-row.noise{--c:var(--faint-a42)}
.slg-lbl{display:flex;align-items:center;gap:8px;height:30px;padding:0 8px;margin-left:-8px;border:0;border-radius:8px;
  background:none;color:var(--dim);font:inherit;font-size:12.5px;text-align:left;cursor:pointer;white-space:nowrap;min-width:0}
.slg-lbl i{flex:none;width:7px;height:7px;border-radius:2px;background:var(--c)}
.slg-lbl span{overflow:hidden;text-overflow:ellipsis}
.slg-lbl:hover{color:var(--text);background:var(--surface-3)}
.slg-lbl.on{color:var(--text);background:var(--brand-dim);box-shadow:inset 0 0 0 1px var(--brand-a45)}
.slg-lane{display:grid;grid-template-columns:repeat(var(--n),minmax(0,1fr));gap:2px;height:30px;align-items:end;
  padding:4px 0 0;border-bottom:1px solid var(--line-soft)}
.slg-lane i{display:block;height:calc(var(--h) * 100%);border-radius:2px 2px 0 0;background:var(--c);cursor:pointer}
.slg-lane i.z{height:1px;background:transparent;cursor:default}
.slg-lane i.w{box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--bad-a40)}
.slg-row:not(.err) .slg-lane i.w{box-shadow:none}
.slg-lane i:not(.z):hover{filter:brightness(1.25)}
.slg-lane i.bg{opacity:.4;box-shadow:none}
.slg-lane i.mine{outline:1.5px solid var(--brand-2);outline-offset:1px}
.slg-lane i.sel{outline:2px solid var(--text);outline-offset:1px}
.slg-mx.pick .slg-lane i:not(.z):not(.mine){opacity:.32}
.slg-mx.kf .slg-row:not(.on) .slg-lane i{opacity:.3}
.slg-sum{display:flex;align-items:center;justify-content:flex-end;gap:5px;height:30px;font-family:var(--mono);
  font-size:11.5px;color:var(--faint);white-space:nowrap;font-variant-numeric:tabular-nums}
.slg-sum b{color:var(--text);font-weight:600}
.slg-marks{display:grid;grid-template-columns:repeat(var(--n),minmax(0,1fr));gap:2px;height:22px;align-items:end;padding-bottom:4px}
.slg-marks b{justify-self:center;min-width:17px;height:15px;padding:0 3px;border-radius:4px;display:grid;place-items:center;
  font-family:var(--mono);font-size:10px;font-weight:600;color:var(--btn-ink);background:var(--bad)}
.slg-marks-l{font-size:11px;color:var(--faint);align-self:end;padding-bottom:6px;white-space:nowrap}
.slg-axis{display:flex;justify-content:space-between;padding-top:8px;font-family:var(--mono);font-size:10.5px;
  color:var(--faint);font-variant-numeric:tabular-nums}

.slg-split{display:grid;grid-template-columns:minmax(260px,330px) minmax(0,1fr);gap:18px;align-items:start}
.slg-devs{border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface);box-shadow:var(--card-shadow);overflow:hidden;
  position:sticky;top:calc(var(--topbar-h,64px) + 14px);max-height:calc(100vh - var(--topbar-h,64px) - 28px);
  display:flex;flex-direction:column}
.slg-pane-hd{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:13px 16px;
  border-bottom:1px solid var(--line);background:var(--surface-2)}
.slg-pane-t{font-family:var(--disp);font-size:13.5px;font-weight:600;color:var(--text);margin:0}
.slg-pane-t small{font-family:var(--sans);font-weight:400;font-size:12px;color:var(--faint);margin-left:8px}
.slg-devs-list{overflow:auto;overscroll-behavior:contain}
.slg-dev{display:grid;grid-template-columns:8px minmax(0,1fr) auto;gap:3px 11px;width:100%;padding:12px 16px;
  border:0;border-bottom:1px solid var(--line-soft);background:none;color:inherit;font:inherit;text-align:left;cursor:pointer}
.slg-dev:last-child{border-bottom:0}
.slg-dev:hover{background:var(--surface-2)}
.slg-dev.on{background:var(--brand-dim);box-shadow:inset 3px 0 0 var(--brand)}
.slg-led{width:8px;height:8px;min-height:0;border-radius:50%;margin-top:6px;background:var(--faint-a42)}
.slg-led.err{background:var(--bad);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--bad-ahalo)}
.slg-led.warn{background:var(--warn)}
.slg-dev-n{min-width:0;font-size:13px;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.slg-dev-h{display:block;margin-top:2px;font-family:var(--mono);font-size:11px;font-weight:400;color:var(--faint);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.slg-dev-r{text-align:right;font-family:var(--mono);font-size:11px;color:var(--faint);white-space:nowrap;font-variant-numeric:tabular-nums}
.slg-dev-r b{display:block;font-size:13px;font-weight:600;color:var(--bad)}
.slg-dev-r b.q{color:var(--faint);font-weight:500}
.slg-dev-f{grid-column:2/4;display:flex;flex-wrap:wrap;gap:5px 8px;margin-top:5px;font-size:11.5px;color:var(--dim)}
.slg-tag{font-size:10.5px;line-height:1.5;padding:0 7px;border-radius:999px;border:1px solid var(--line);color:var(--dim);white-space:nowrap}
.slg-tag.warn{color:var(--warn);border-color:var(--warn-a40)}
.slg-tag.bad{color:var(--bad);border-color:var(--bad-a40)}

.slg-stream{min-width:0;border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface);box-shadow:var(--card-shadow);overflow:hidden}
.slg-why{padding:12px 16px;border-bottom:1px solid var(--line-soft);font-size:12.5px;line-height:1.65;color:var(--dim)}
.slg-why b{color:var(--text);font-weight:600;margin-right:6px}
.slg-card{padding:16px 18px 2px;border-bottom:1px solid var(--line)}
.slg-card-hd{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:16px}
.slg-card-n{font-family:var(--disp);font-size:15px;font-weight:600;color:var(--text)}
.slg-card-n small{display:block;margin-top:3px;font-family:var(--mono);font-size:11.5px;font-weight:400;color:var(--faint)}
.slg-card-a{display:flex;gap:8px;flex-wrap:wrap}
.slg-ln{--c:var(--faint);display:grid;text-align:left;grid-template-columns:auto 104px minmax(0,132px) minmax(0,1fr);gap:4px 14px;
  align-items:baseline;padding:9px 16px;border-bottom:1px solid var(--line-soft)}
.slg-ln.err{--c:var(--bad)} .slg-ln.warn{--c:var(--warn)} .slg-ln.noise{--c:var(--faint-a42)}
.slg-ln:hover{background:var(--surface-2)}
.slg-t{font-family:var(--mono);font-size:11.5px;color:var(--faint);white-space:nowrap;font-variant-numeric:tabular-nums}
.slg-t.est{cursor:help}
.slg-k{display:inline-flex;align-items:center;gap:7px;font-size:11.5px;color:var(--dim);white-space:nowrap;min-width:0}
.slg-k i{flex:none;width:7px;height:7px;border-radius:2px;background:var(--c)}
.slg-who{min-width:0;padding:0;border:0;background:none;color:var(--dim);font:inherit;font-size:12px;text-align:left;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}
.slg-who:hover{color:var(--brand-2)}
.slg-x{min-width:0;font-family:var(--mono);font-size:12px;line-height:1.65;color:var(--text);overflow-wrap:anywhere}
.slg-x .h{color:var(--brand-2)}
.slg-x .e{color:var(--dim)}
.slg-ln.err .slg-x .e{color:var(--bad)}
.slg-n{font-family:var(--mono);font-size:12px;font-weight:600;color:var(--text);white-space:nowrap;font-variant-numeric:tabular-nums}
.slg-agm{grid-column:3/5;font-size:11.5px;color:var(--faint);margin-top:1px}
.slg-ln.ag{grid-template-columns:auto 104px minmax(0,1fr)}
.slg-ln.ag .slg-x{grid-column:3}
.slg-more{padding:16px;text-align:center;border-top:1px solid var(--line-soft)}
.slg-stream .empty,.slg-devs .empty{padding:30px 20px}
@media (max-width:1100px){
  .slg-split{grid-template-columns:minmax(0,1fr)}
  .slg-devs{position:static;max-height:380px}
}
@media (max-width:700px){
  .slg-verdict{font-size:15.5px}
  .slg-board{padding:16px 14px 14px}
  .slg-ln,.slg-ln.ag{grid-template-columns:auto auto minmax(0,1fr);gap:4px 12px;padding:10px 14px;align-items:center}
  .slg-ln .slg-x,.slg-ln.ag .slg-x,.slg-ln .slg-agm{grid-column:1/-1}
  .slg-ln .slg-who{justify-self:end;max-width:100%}
}
/* 头部座位牌：车型与状态跟着人走，点开就先看到「他是谁、坐哪、还剩多久」 */
.ub-tag{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line);
  border-radius:999px;padding:3px 11px;font-size:11.5px;color:var(--dim);white-space:nowrap}
.ub-tag b{font-weight:600;color:var(--text)}
.ub-tag>svg{width:12px;height:12px;flex:none}
.ub-tag .dot{width:6px;height:6px;border-radius:50%;background:var(--faint);flex:none}
.ub-tag.ok .dot{background:var(--ok)} .ub-tag.warn .dot{background:var(--warn)} .ub-tag.bad .dot{background:var(--bad)}
.ub-tag.warn{color:var(--warn)} .ub-tag.bad{color:var(--bad)}
/* 车况窗口行复用 .pv-qwin/.qrail；去掉供应商板为账号行准备的缩进，数字列收窄
   （乘客视角只有百分比，没有绝对量） */
.ub-qwrap .pv-qwin{padding-left:0;grid-template-columns:104px minmax(48px,1fr) 56px minmax(0,148px)}
.ub-share .qrail i.fill{background:var(--text-a46)}
.ub-share .pv-qwin.over .qrail i.fill{background:var(--warn)}
/* SLA：结论先行——大字是可用率，旁边一句话说明当下是不是正常 */
.ub-hero{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin:1px 0 12px}
.ub-hero .n{font-family:var(--disp);font-size:33px;font-weight:650;letter-spacing:-.02em;line-height:1;font-variant-numeric:tabular-nums}
.ub-hero.bad .n{color:var(--bad)}
.ub-hero .s{font-size:11.5px;color:var(--faint)}
/* 停摆记录：时长画成条，长短相对本月最长的一次——一眼分出「掉了 2 分钟」和
   「掉了 20 分钟」，补没补到在右边那句话里 */
/* 每行各自是一个 grid，尾列必须定宽——minmax(0,auto) 会让每行的轨道长短跟着
   右侧文字走，上下两条对不齐的条子，人会先去比长度，再发现比错了 */
.ub-gap{display:grid;grid-template-columns:88px minmax(36px,1fr) 66px 122px;gap:10px;align-items:center;font-size:12px;margin-top:8px}
.ub-gap .at{color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap}
.ub-gap .trk{height:6px;border-radius:2px;background:var(--surface-3);overflow:hidden}
.ub-gap .trk i{display:block;height:100%;border-radius:2px;background:var(--faint-a22)}
.ub-gap .trk i.hot{background:var(--bad-a32)}
.ub-gap .dur{font-family:var(--mono);font-size:11.5px;font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.ub-gap .why{font-size:10.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ub-gap .why.got{color:var(--ok)}
.ub-gap.live .why{color:var(--bad)}
/* 价值窗口牌：既是读数也是选择器，选中的窗口决定下面逐项拆分看哪一段 */
.ub-value-nav{display:flex;align-items:center;justify-content:space-between;gap:8px 14px;
  flex-wrap:wrap;margin-bottom:11px}
.ub-value-nav .winsel{margin:0}
.ub-value-nav .ub-range{margin-left:auto}
.ub-allow-kind button{font-size:11px}
.ub-wins{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line-soft);
  border:1px solid var(--line);border-radius:12px;overflow:hidden;margin-bottom:11px}
.ub-win{background:var(--surface);padding:11px 14px 12px;text-align:left;cursor:pointer;
  border:0;font:inherit;color:inherit;transition:background .15s;min-width:0}
.ub-win .k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);white-space:nowrap}
.ub-win .v{font-family:var(--disp);font-size:20px;font-weight:600;margin-top:5px;line-height:1.1;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ub-win .s{font-size:10.5px;color:var(--faint);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ub-win.on{box-shadow:inset 0 -2px 0 var(--brand);background:color-mix(in srgb,var(--brand) 6%,var(--surface))}
.ub-win:hover:not(.on){background:color-mix(in srgb,var(--brand) 3%,var(--surface))}
.ub-win:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
/* 近 30 天逐日价值柱：高度=当日官方价值。窗口牌回答「一共多少」，这排柱子回答
   「是匀着用的还是一天烧出来的」——同一个 $203 两种形状，续费对话完全不同 */
.ub-days{display:flex;align-items:flex-end;gap:2px;height:56px;margin-top:2px}
.ub-days .b{flex:1;min-width:2px;height:100%;display:flex;flex-direction:column;justify-content:flex-end;background:var(--line-soft);border-radius:2px 2px 0 0}
.ub-days .b i{display:block;width:100%;border-radius:2px 2px 0 0;background:var(--ok-a60);transition:background .12s}
.ub-days .b:hover i{background:var(--ok)}
.ub-days .b:hover{outline:1px solid var(--ok);outline-offset:0}
.ub-days-ax{display:flex;justify-content:space-between;font-size:10px;color:var(--faint);font-family:var(--mono);margin-top:6px}
/* 逐项拆分：模型名等宽字体带协议色点，条子按窗口内份额，行内右侧是钱和次数 */
.ub-brk{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:15px}
.ub-brk-t{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);display:block}
.ub-row{display:grid;grid-template-columns:minmax(84px,152px) minmax(36px,1fr) 72px 104px;gap:10px;align-items:center;font-size:12px;margin-top:9px;min-width:0}
.ub-row .lbl{display:flex;align-items:center;gap:7px;font-family:var(--mono);font-size:11.5px;color:var(--dim);min-width:0}
.ub-row .lbl .t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ub-row .lbl i{width:6px;height:6px;border-radius:50%;flex:none;background:var(--faint)}
.ub-row .trk{height:6px;border-radius:2px;background:var(--surface-3);overflow:hidden}
.ub-row .trk i{display:block;height:100%;border-radius:2px;background:var(--ok-a60);min-width:2px}
.ub-row .amt{font-family:var(--mono);font-size:12px;font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.ub-row .why{font-size:10.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ub-row .why .e{color:var(--bad);font-weight:600}
@media (max-width:760px){
  .ub-brk{grid-template-columns:1fr}
  .ub-wins{grid-template-columns:1fr}
  .ub-win{display:flex;align-items:baseline;gap:10px}
  .ub-win .v{margin-top:0}
  .ub-qwrap .pv-qwin{grid-template-columns:86px minmax(36px,1fr) 50px minmax(0,104px);gap:8px}
  .ub-gap{grid-template-columns:80px minmax(24px,1fr) 60px 96px;gap:8px}
  .ub-row{grid-template-columns:minmax(72px,120px) minmax(28px,1fr) 64px 88px;gap:8px}
}
#hm-tip{position:fixed;z-index:70;pointer-events:none;background:var(--glass-thick);color:var(--toast-text);
  -webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  border:1px solid var(--toast-line);border-radius:var(--r-md);padding:7px 11px;
  font-size:12px;line-height:1.6;opacity:0;transition:opacity .12s;box-shadow:var(--shadow-3);white-space:nowrap}
#hm-tip.show{opacity:1}
#hm-tip .tt{font-weight:600}
#hm-tip .td{color:var(--dim);font-family:var(--mono);font-size:11.5px}
/* 订阅卡「分配过的人」名单：同一枚浮层，一人一行、点在前、名字加粗、说明 mono。 */
#hm-tip .sh-h{margin-top:6px}
#hm-tip .sh-h:first-child{margin-top:0}
#hm-tip .sh-r{display:flex;align-items:center;gap:7px;padding-left:1px}
#hm-tip .sh-r b{font-weight:600}
#hm-tip .sh-dot{width:7px;height:7px;border-radius:50%;flex:none;border:1.5px solid var(--faint)}
#hm-tip .sh-dot.on{background:var(--ok-fill);border-color:var(--ok-fill)}
#hm-tip .sh-s{color:var(--dim);font-size:11.5px}
#hm-tip .sh-more{margin-top:5px;color:var(--dim);font-size:11.5px}
.cp-foot .fig>span.cpf-hist{cursor:pointer;border-radius:4px}
.cp-foot .fig>span.cpf-hist:hover b,.cp-foot .fig>span.cpf-hist:focus-visible b{color:var(--text)}
.cp-foot .fig>span.cpf-hist:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* toast：底部居中的一枚胶囊 HUD（玻璃），左端一个状态点。跟主题走。 */
#toasts{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;gap:8px;z-index:60}
.toast{display:flex;align-items:center;gap:10px;background:var(--toast-bg);color:var(--toast-text);border:1px solid var(--toast-line);border-radius:var(--r-pill);
  -webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  padding:10px 18px 10px 15px;font-size:13px;font-weight:500;box-shadow:var(--shadow-3);animation:toastin .42s var(--spring) both}
@keyframes toastin{from{opacity:0;transform:translateY(12px) scale(.96)}to{opacity:1;transform:none}}
.toast::before{content:"";width:8px;height:8px;border-radius:50%;flex:none;background:var(--faint)}
.toast.ok::before{background:var(--ok-fill)}
.toast.bad::before{background:var(--bad-fill)}
.toast.out{animation:sink .24s var(--ease) both}
@keyframes sink{to{opacity:0;transform:translateY(8px)}}

/* 确认卡 —— 原生 confirm 驯化成机箱上的一块确认小牌：琥珀 = 需要想一下，红 = 不可逆 */
.cfm-scrim{position:fixed;inset:0;background:var(--scrim);z-index:80;opacity:0;pointer-events:none;transition:opacity .2s var(--ease)}
.cfm-scrim.show{opacity:1;pointer-events:auto}
/* 确认框：macOS 26 的提示框——一块居中的厚玻璃，26px 圆角，弹出时轻轻放大。
   琥珀点 = 需要想一下，红点 = 不可逆。 */
.cfm{position:fixed;left:50%;top:50%;width:min(420px,92vw);z-index:81;overflow:hidden;
  background:var(--glass-thick);-webkit-backdrop-filter:blur(40px) saturate(180%);backdrop-filter:blur(40px) saturate(180%);
  border:1px solid var(--rail-line);border-radius:var(--r-2xl);padding:22px 22px 18px;
  box-shadow:var(--sheen),var(--shadow-1);opacity:0;pointer-events:none;
  transform:translate(-50%,-50%) scale(1.06);transition:opacity .18s var(--ease),transform .34s var(--spring)}
.cfm.show{opacity:1;pointer-events:auto;transform:translate(-50%,-50%) scale(1)}
.cfm h4{font-family:var(--disp);font-size:17px;font-weight:600;letter-spacing:-.015em;margin:0 0 8px;display:flex;align-items:center;gap:9px}
.cfm h4 .lamp{background:var(--warn);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--warn-ahalo)}
.cfm.danger h4 .lamp{background:var(--bad);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--bad-ahalo)}
/* pre-line: ask() writes textContent (never HTML), so a multi-line consequence
   list — which chains go dark — keeps its line breaks. */
.cfm p{margin:0 0 20px;color:var(--dim);font-size:13.5px;line-height:1.65;white-space:pre-line}
.cfm-foot{display:flex;justify-content:flex-end;gap:10px}
.cfm-foot .btn{flex:1 1 0;min-width:0;justify-content:center;padding:9px 16px;font-size:14px}
/* 收款确认窗：和确认框是同一块玻璃（共用 .cfm，降低透明度那一行不用另登记），
   多一张单据摘要和一排常用备注。绿点 = 入账，红点 = 拒绝。 */
.tpd{width:min(460px,92vw);max-height:92vh;overflow-y:auto;overscroll-behavior:contain}
.tpd h4{margin-bottom:14px}
.tpd.ok h4 .lamp{background:var(--ok-fill);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--ok-ahalo)}
.tpd-sum{padding:14px 16px;margin:0 0 18px;border-radius:var(--r-lg);background:var(--fill-3)}
.tpd-who{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--dim);min-width:0}
.tpd-who b{font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.tpd-amt{display:flex;align-items:baseline;gap:8px;margin:8px 0 6px}
.tpd-amt .v{font-family:var(--disp);font-size:28px;font-weight:600;letter-spacing:-.02em;line-height:1.1;font-variant-numeric:tabular-nums}
.tpd-amt .k{font-size:12px;color:var(--dim)}
.tpd-meta{font-size:12px;color:var(--dim);line-height:1.6}
.tpd-note{margin-top:8px;padding-top:8px;border-top:1px solid var(--line-soft);font-size:12.5px;color:var(--text);line-height:1.55;overflow-wrap:anywhere}
.tpd-note span{color:var(--dim)}
.tpd-lbl{display:block;font-size:12px;color:var(--dim);margin:0 0 8px}
.tpd .chips{gap:6px;margin:0 0 10px}
.tpd .chip{padding:6px 12px;font-size:12.5px}
.tpd .chip:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.tpd .tpd-hint{margin:10px 0 20px;font-size:12px;color:var(--dim);line-height:1.55}
/* 窄屏：确认窗改成从底部升起的一张单子，两颗按钮落在拇指够得着的地方。
   触屏上打开时不抢焦点（见 decideTopup）：键盘一弹起来就把「确认入账」盖住了，
   而多数时候备注用默认的「收到」就行。常用备注排成一行横滑，不占四行高。 */
@media (max-width:860px){
  .tpd{left:0;right:0;top:auto;bottom:0;width:auto;max-height:calc(100dvh - 32px);
    border-width:1px 0 0;border-radius:var(--r-2xl) var(--r-2xl) 0 0;
    padding:22px 16px calc(14px + env(safe-area-inset-bottom));transform:translateY(100%)}
  .tpd.show{transform:none}
  .tpd .chips{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;overscroll-behavior-x:contain;margin:0 -16px 10px;padding:0 16px}
  .tpd .chips::-webkit-scrollbar{display:none}
  .tpd .chip{flex:none;padding:8px 14px;font-size:13.5px}
  .tpd .cfm-foot .btn{min-height:46px;font-size:15px}
}

/* ============ 窄屏：底部浮动标签栏 ============
   侧栏化作浮在拇指边的一枚玻璃胶囊（iOS 26 的标签栏）：图标 + 标签逐格可读，
   当前格垫一块浅色胶囊。台账变横向滑轨（两端本色淡出暗示还有内容），
   抽屉满幅、看板成底部抽板。 */
@media (max-width:860px){
  #app{grid-template-columns:1fr}
  .rail{position:fixed;top:auto;bottom:calc(8px + env(safe-area-inset-bottom));left:10px;right:10px;height:auto;margin:0;z-index:30;
    flex-direction:row;align-items:stretch;
    padding:5px;border-radius:30px;
    border:1px solid var(--rail-line);background:var(--glass-thick);
    box-shadow:var(--sheen),var(--shadow-3)}
  .brand,.rail-foot{display:none}
  /* 底部只放四个最常用的模块 + 「更多」，五格平分一排，不横向滑动 */
  .nav{flex-direction:row;align-items:stretch;gap:2px;min-height:auto;overflow:visible}
  .nav-item:not(.dock):not(.nav-more){display:none}
  .nav-item.nav-more{display:flex}
  .nav-item{position:relative;flex-direction:column;justify-content:center;gap:3px;
    padding:7px 4px 6px;width:auto;min-width:0;min-height:48px;flex:1 1 0;border-radius:24px;font-weight:500}
  .nav-item svg{width:20px;height:20px}
  .nav-item span{font-size:10.5px;line-height:1;white-space:nowrap;max-width:100%}
  /* 底部浮条沿用浅填充：一排五格里一块实心墨色太重 */
  .nav-item.on,.nav-item.on:hover,.nav-item.on:active{background:var(--rail-on-bg);color:var(--rail-on-text);font-weight:600;box-shadow:none}
  .nav-item.on svg{color:var(--rail-on-text)}
  .main{padding-bottom:calc(84px + env(safe-area-inset-bottom))}
  /* 顶栏收紧：标题可截断，副题让位给台面 */
  .topbar{padding:12px 14px;gap:8px}
  .topbar>div:first-child{min-width:0}
  .topbar .title{font-size:16.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .topbar .crumb{display:none}
  .topbar .right{gap:2px;flex:none}
  .who{max-width:64px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* 手机宽度下顶栏右侧六个按钮排不下（所有者还多一个角色牌），会把整页撑出横向滚动：
     搜索只留放大镜，用户名让给角色牌（点开「账户与安全」仍能看到是谁）。 */
  .tb-search span,.tb-search .pal-kbd{display:none}
  /* 窄屏的工具栏只留图标：主题跟着系统走，切换钮收起来；刷新 / 退出变成圆形图标钮。
     以前这一排全是字，页面标题被挤得只剩一个字。 */
  .topbar>div:first-child{padding-right:8px}
  .topbar .theme-btn{display:none}
  /* 写成 .topbar .btn.x：触屏那一节的 .btn.sm{padding:8px 13px} 在后面、特指度一样，
     会把 34px 的圆钮撑回去、把里面的图标挤成一个点。 */
  .topbar .btn.tb-search{width:34px;height:34px;min-width:0;padding:0;justify-content:center;border-radius:50%;background:transparent}
  .topbar .btn.tb-search svg{flex:none;width:17px;height:17px}
  .topbar .btn.tb-act{width:34px;height:34px;padding:0;justify-content:center;border-radius:50%}
  .tb-act .tb-ico{display:block}
  .tb-act .tb-txt{display:none}
  .as-launch{padding:6px 9px}
  .view{--view-top:12px;padding:var(--view-top) 12px 32px}
  .sec{margin-bottom:20px}
  .eyebrow{margin:0 0 8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  /* 仪表与卡阵收为双列 / 单列 */
  .tiles,#ku-tiles{grid-template-columns:repeat(2,1fr);gap:8px}
  .tile{padding:12px 12px}
  .tile .v{font-size:22px}
  #ku-tiles .tile .v{font-size:21px}
  .htiles{grid-template-columns:repeat(2,1fr)}
  .hcards{grid-template-columns:1fr}
  /* 纸带板窄屏：轴头收起，每行折成「名字 / 读数 / 整幅纸带」三层 */
  .tb-axis{display:none}
  .tbr{flex-wrap:wrap;gap:8px 14px;padding:12px var(--pad)}
  .tb-id{width:auto;flex:1 1 100%;order:1}
  .tb-rate,.tb-lat{order:2;width:auto;text-align:left}
  .tb-rate .n{display:inline;margin-left:5px}
  .tb-lat::before{content:"TTFB P90 ";letter-spacing:.1em;font-size:9.5px;color:var(--faint)}
  .tb-tr{order:3;flex:1 1 100%}
  .tbx{grid-template-columns:1fr}
  .statstrip,.statstrip.c3{grid-template-columns:repeat(2,1fr)}
  /* 窄屏：结论收字号，台账的读数牌从右缘翻到行底横过来 */
  .ef-say{padding:17px 16px 15px}
  .ef-say-h{font-size:20px;line-height:1.5}
  .ef-reads{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ef-read{padding:12px 15px 14px}
  .ef-read:nth-child(n){border-left:1px solid var(--line-soft);border-top:none}
  .ef-read:nth-child(odd){border-left:none}
  .ef-read:nth-child(n+3){border-top:1px solid var(--line-soft)}
  .dk-sum{grid-template-columns:minmax(0,1fr) 18px;gap:3px 12px;padding:12px 14px 13px 18px}
  .dk-claim{white-space:normal}
  .dk-plate{grid-column:1;grid-row:3;grid-row-end:4;text-align:left;min-width:0;
    display:flex;align-items:baseline;gap:8px;margin-top:7px}
  .dk-plate b{font-size:17px}
  .dk-plate i{margin-top:0}
  .dk-cv{grid-column:2;grid-row:1/4}
  .dk-body{padding:0 15px 16px 18px}
  .dk-im{margin-left:0}
  .ef-tabs{padding:8px 8px 0;overflow-x:auto}
  .route{grid-template-columns:1fr;gap:10px;padding:14px 16px}
  .line{grid-template-columns:1fr;gap:10px}
  .line-act{justify-content:flex-start}
  /* 切换链：卡片本来就是竖排的，窄屏只需把母线收窄、手柄放大到能按 */
  .hoprow,.chain-add,.chain-end{grid-template-columns:20px minmax(0,1fr);gap:9px}
  .hoprow:not(:last-child)::after{left:29px}
  .hop-tools button,.hop-grip{width:32px;height:34px}
  /* 台账变横向滑轨：列不再互相挤压，左右滑读全表；
     两端本色遮罩随内容滚动让开，露出边缘阴影提示"这边还有" */
  .panel{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;
    background:
      linear-gradient(90deg,var(--surface) 38%,transparent) left/32px 100% no-repeat local,
      linear-gradient(270deg,var(--surface) 38%,transparent) right/32px 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%,var(--text-a16),transparent) left/14px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%,var(--text-a16),transparent) right/14px 100% no-repeat scroll,
      var(--surface)}
  .panel th,.panel td{white-space:nowrap}
  .panel td{max-width:min(72vw,340px);overflow:hidden;text-overflow:ellipsis}
  .panel td.empty,.pv-empty-row td{white-space:normal;max-width:none}
  /* 时间窗轴、筛选轨、搜索框：可滑、可伸（标签让位，轴占满整行） */
  .hwin{width:100%;min-width:0}
  .hwin-lbl{display:none}
  .winsel{max-width:100%;overflow-x:auto;scrollbar-width:none}
  .winsel::-webkit-scrollbar{display:none}
  .winsel button{flex:none}
  .ku-rail{padding:9px 10px}
  .ku-rail-acts{margin-left:0;width:100%}
  .pv-rail{padding:9px 10px}
  /* 掌上这张表走的是横向滑轨（上面 .panel 那条规则），滑轨要的是「列按内容撑开、
     整表比屏幕宽」，正好和定宽相反——所以这一档把 table-layout 交回 auto，
     百分比列宽全部作废。八列在 390px 上无论怎么分都读不了，滑着读才读得完整。 */
  #v-providers table{table-layout:auto;width:auto;min-width:100%}
  /* 交回 auto 之后，每一格得自己报出「我至少要这么宽」——上面那几条为定宽写的
     min-width:0 / 省略号，在滑轨里正好是反效果：列会一路缩到只剩三个字母，
     而屏幕右边还空着。这几行就是把那几条按滑轨的规矩改回来。 */
  #v-providers .pv-plate{min-width:236px}
  #v-providers .pv-meters{min-width:300px}
  #v-providers .pv-wire{padding-left:8px;min-width:132px}
  #v-providers .pv-m,#v-providers[data-pvkind="claude"] .pv-m{grid-template-columns:92px minmax(56px,1fr) 84px 64px 96px;gap:10px}
  #v-providers .pv-m .mtk{display:block}
  #v-providers .pv-mnone,#v-providers[data-pvkind="claude"] .pv-mnone{grid-template-columns:92px minmax(56px,1fr);gap:10px}
  #v-providers .pv-mail{min-width:0;max-width:none}
  #v-providers .pv-link{overflow:visible;max-width:none}
  #v-providers .ap-cellbtn{padding:2px 9px;gap:5px}
  #v-providers .ap-cellbtn .t{overflow:visible}
  /* 铭牌格必须能折行：上面 .panel td 的 nowrap + overflow:hidden 会把测试按钮
     和线名挤在同一行再切掉，看起来又像按钮不存在。 */
  #v-providers td.pv-c-name{white-space:normal;max-width:none;overflow:visible}
  .pv-touch-act{display:flex;grid-column:1/-1;align-items:center;gap:6px;flex-wrap:wrap;margin-top:2px}
  .pv-touch-act .btn{padding:8px 12px;min-height:38px;font-size:13px}
  .pv-touch-act .btn svg{width:14px;height:14px}
  /* 右侧那两颗图标藏起来：铭牌下面已经有一套按得到的按钮，同一行两套，窄屏上
     后一套仍然要滑出屏幕才看得到。闸刀和灯留着——它们是读数，不是重复的动作。 */
  #v-providers .pv-ops-acts{display:none}
  /* 掌上：导语和动作各占一行，读数窗横着滑 */
  .pv-top{flex-wrap:wrap;gap:10px}
  .pv-pop{right:auto;left:0;width:min(288px,calc(100vw - 32px))}
  .pv-menu:last-child .pv-pop{right:0;left:auto}
  /* 表比屏幕宽，面板整块横滑。抬头若跟着表走，实况和筛选会滑出屏幕。
     钉在可视区域左侧。实况窗折行，六盏灯都在屏幕里，不藏进一条没有滚动条的横滑。 */
  #v-providers .pv-face,#v-providers .pv-read,#v-providers .pv-filters,#v-providers .pv-tools,#v-providers .pv-notice{
    position:sticky;left:0;z-index:2;box-sizing:border-box;max-width:100%}
  #v-providers .pv-read,#v-providers .pv-filters,#v-providers .pv-tools{background:var(--surface)}
  #v-providers .pv-face>.pv-live{width:auto;max-width:100%;min-width:0;margin:0;padding:0 10px 8px}
  #v-providers .pv-gauge{flex:1 1 auto;width:auto;min-width:0;flex-wrap:wrap;align-content:center;row-gap:2px;overflow:visible}
  .pv-read{flex-wrap:wrap;row-gap:6px}
  #v-providers .pv-tools>.pv-sort{flex:1 1 160px}
  #v-providers .pv-tools>.pv-sort select{flex:1;min-width:0;width:auto}
  .pv-gauge button{flex:none}
  /* 手机上分栏条要能横着滑，三段挤不下时不换行——它是导航，断成两行就读不出层级 */
  .pv-tabs{flex:1 1 100%;padding:0 4px;overflow-x:auto;scrollbar-width:none}
  .pv-tabs::-webkit-scrollbar{display:none}
  .pv-tab{height:38px;padding:0 10px;flex:none;font-size:12.5px}
  .pv-find{flex:1 1 150px}
  .pv-find input,.pv-find input:focus{flex:1;width:auto;min-width:0}
  .probe-bar .probe-act{flex-wrap:wrap}
  .ps-v{max-width:70vw}
  /* 抽屉满幅：表单成为整页的操作台 */
  .drawer,.drawer.wide,.drawer.xl,.drawer.cwb,.drawer.carb{top:0;right:0;bottom:0;width:100vw;max-width:100vw;height:100dvh;border:0;border-radius:0}
  .drawer-body{overscroll-behavior:contain}
  .drawer-foot{padding-bottom:calc(16px + env(safe-area-inset-bottom))}
  .drawer-foot .btn{flex:1;justify-content:center;padding:12px}
  /* 乘客动作按标签宽度折行。上面那条 flex:1 会把五颗 nowrap 按钮的最小宽度
     加在一起，窄屏上仍然比抽屉宽，多出来的又从左边溢到页面上。 */
  .drawer-foot .seat-acts .btn{flex:0 0 auto;min-width:0;padding:12px 14px}
  .pboard .pv-acts .btn{min-height:38px;padding:8px 12px}
  /* 透视 / 活动看板：从底部升起的抽板 */
  .pboard{top:auto;bottom:0;left:0;right:0;width:100%;max-width:100%;
    max-height:calc(100dvh - 42px);border-radius:18px 18px 0 0;
    border-left:none;border-right:none;border-bottom:none;
    transform:translateY(28px)}
  .pboard.show{transform:translateY(0)}
  .pv-head{padding:16px 14px 14px}
  .pv-body{padding:14px 12px calc(18px + env(safe-area-inset-bottom));overscroll-behavior:contain}
  .pv-tiles{grid-template-columns:repeat(2,1fr)}
  .pv-name{max-width:52vw}
  /* 提示条移到机架上方 */
  #toasts{bottom:calc(92px + env(safe-area-inset-bottom));left:14px;right:14px;transform:none;align-items:center}
  .toast{max-width:100%}
  .login-card{width:min(380px,calc(100vw - 28px));padding:26px 18px 20px}
  .login-card .sub{margin-bottom:14px}
  .login-mark{margin-bottom:14px}
  .login-mark .appmark{width:52px;height:52px;border-radius:12px}
  .login-mark .appmark svg{width:31px;height:31px}
  .theme-btn.float{top:calc(14px + env(safe-area-inset-top))}
  .doc-body{padding:18px 16px}
  .doc-body table{display:block;overflow-x:auto}
  .doc-body th,.doc-body td{white-space:nowrap}
}

/* ---- 掌上（≤760px）：上游线路表改成卡片 ----
   手机上整张表横着滑：铭牌占满一屏，余量、期限、开关都在屏幕外，看一个账号要滑
   三次。改成一个账号一张卡，四格竖着排：铭牌 / 用途 / 余量 / 线路。期限格沿用
   ≤1120 那条规则仍然收起（到期红标回到铭牌上，登记续费在「更多」菜单里）。
   这一块排在上面那个 ≤860 横滑块后面，是为了压过那里按横滑写的 min-width。 */
@media (max-width:760px){
  #v-providers table,#v-providers tbody,#v-providers tr,#v-providers td{display:block;width:auto;min-width:0;max-width:none}
  #v-providers thead,#v-providers td.pv-grip-cell,#v-providers .pv-touch-act{display:none}
  #v-providers tbody tr{padding:13px 14px 14px;border-bottom:1px solid var(--line-soft)}
  #v-providers tbody tr:last-child{border-bottom:0}
  #v-providers td{padding:0;border:0;white-space:normal;overflow:visible;vertical-align:top}
  #v-providers td.pv-c-wire,#v-providers td.pv-c-cred,#v-providers td.pv-c-meter,#v-providers td.pv-c-term,#v-providers td.pv-c-ops{margin-top:10px}
  #v-providers tr.ap-grouphd>td{padding:0}
  #v-providers td.ap-xempty,#v-providers .pv-empty-row td{padding:14px 0;text-align:left}
  /* 触屏块给横滑写的最小宽度在卡片里都要撤掉 */
  #v-providers .pv-plate,#v-providers .pv-meters,#v-providers .pv-wire{min-width:0}
  #v-providers .pv-wire{padding-left:9px}
  #v-providers .pv-m{grid-template-columns:minmax(0,60px) minmax(40px,1fr) 64px 60px 92px;gap:6px}
  #v-providers .pv-mnone{grid-template-columns:minmax(0,60px) minmax(40px,1fr);gap:6px}
  #v-providers .pv-m .mb{max-width:none}
  /* 线路格横着排一行：灯在左、开关和图标在右。右侧图标在卡片里回来（铭牌下面那排
     大按钮撤了，它们是同一组动作），按触屏尺寸放到 34px。 */
  #v-providers .pv-ops{flex-direction:row;flex-wrap:wrap;align-items:center;gap:8px 12px}
  #v-providers .pv-ops-read{flex:1 1 auto;flex-direction:row;flex-wrap:wrap;align-items:center;gap:6px 12px}
  #v-providers .pv-ops-do{margin-left:auto}
  #v-providers .pv-ops-acts{display:inline-flex}
  #v-providers .pv-ops-acts .btn.icon{width:34px;height:34px}
  #v-providers .pv-ops .badge{padding:3px 10px;font-size:12px}
  #v-providers .pv-ops .sw{padding:3px 10px 3px 5px}
  /* 筛选轨：每组一行横滑，不再折成四五行把表推到第二屏 */
  #v-providers .pv-filters{flex-direction:column;align-items:stretch;gap:6px;padding:8px 14px}
  #v-providers .pv-grp{display:flex;flex-wrap:nowrap;overflow-x:auto;max-width:100%;padding-bottom:2px;
    scrollbar-width:none;-webkit-overflow-scrolling:touch}
  #v-providers .pv-grp::-webkit-scrollbar{display:none}
  #v-providers .pv-grp>*{flex:none}
  #v-providers .pv-chips{flex-wrap:nowrap}
  #v-providers .pv-read{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none}
  #v-providers .pv-read::-webkit-scrollbar{display:none}
  #v-providers .pv-read>*{flex:none}
  /* 实况读数窗也横滑一行，不折成两行 */
  #v-providers .pv-gauge{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;max-width:100%}
  #v-providers .pv-gauge::-webkit-scrollbar{display:none}
  /* 页顶那句导语和「显示 N / N · 拖拽」的提示在手机上各占一行，只是在把表往下推 */
  #v-providers .pv-top .eyebrow,#v-providers .pv-count{display:none}
  #v-providers .pv-top{margin-bottom:10px}
}
/* 触屏基本功：输入 16px 免 iOS 聚焦放大、靶区加大、悬停才现身的控件常驻 */
@media (pointer:coarse){
  /* !important 是有意的：这条规则要压过每一处按版面调过字号的 id / 类选择器
     （#pv-q、#pal-q、#combo-q、#as-input、.cat-sort… 都比 `input` 更特指，
     实测有 12 处小于 16px）。小于 16px 的输入框一被对焦，iOS 就把整页放大，
     而放大之后吸顶条、机架、弹层的定位全部错位——它不是字号问题，是布局事故。
     写成通配还有一个好处：以后新加的输入框自动落在保护里，不用记得来补一行。 */
  input,select,textarea{font-size:16px!important}
  .btn.sm{padding:8px 13px}
  /* 圆形图标按钮不要上一行的内边距：同特异性、排在后面，.sm.icon 会继承它 */
  .btn.icon{width:38px;height:38px;padding:0}
  .btn.icon svg{width:16px;height:16px}
  .theme-btn{width:36px;height:36px}
  .x{width:36px;height:36px;font-size:19px}
  .hop-tools button,.hop-grip{width:34px;height:38px}
  /* 从属行在触屏上也得够 16px：小于它 iOS 一聚焦就整页放大 */
  .hop-sub>*{font-size:16px;padding:7px 8px}
  .hop-more{font-size:13px;padding:6px 0}
  .pm-x{padding:4px 12px;font-size:18px}
  .winsel button{padding:9px 15px}
  .seg button{padding:10px 9px}
  .ku-act{padding:8px 11px}
  .pv-chip{height:30px}
  .pv-seg{height:32px}
  .ps-clear{padding:6px 11px}
  .doc-body .pre-copy{opacity:.9}
  /* 1120px 档把操作图标收到 25px，特异性压过上面的 .btn.icon{38px}。
     平板横屏仍走桌面那一列，手指点不中 25px。 */
  #v-providers .pv-ops-acts .btn.icon{width:38px;height:38px}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
/* ---------- 接入文档 / DOCS ---------- */
.doc-wrap{padding:0;overflow:hidden}
.doc-body{padding:24px 28px;line-height:1.72;color:var(--dim);font-size:13.5px;max-width:960px}
.doc-body h1{font-size:21px;margin:2px 0 6px;color:var(--text);letter-spacing:-.01em}
.doc-body h2{font-size:16px;margin:28px 0 12px;padding-bottom:8px;border-bottom:1px solid var(--line);color:var(--text)}
.doc-body h3{font-size:13.5px;margin:20px 0 8px;color:var(--brand-2)}
.doc-body p{margin:9px 0}
.doc-body ul{margin:9px 0;padding-left:20px}
.doc-body ul ul{margin:4px 0}
.doc-body li{margin:5px 0}
.doc-body a{color:var(--brand-2)}
.doc-body a:hover{text-decoration:underline}
.doc-body code{font-family:var(--mono);font-size:12px;background:var(--surface-3);padding:1.5px 6px;border-radius:5px;color:var(--brand-2);white-space:nowrap}
.doc-body pre{background:var(--surface-2);border:1px solid var(--line-soft);border-radius:10px;padding:13px 15px;overflow:auto;margin:11px 0}
.doc-body pre code{background:none;padding:0;color:var(--text);font-size:12px;line-height:1.65;display:block;white-space:pre}
.doc-body .codeblock{position:relative;margin:11px 0}
.doc-body .codeblock pre{margin:0}
.doc-body .pre-copy{position:absolute;top:8px;right:8px;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:1px solid var(--line);border-radius:7px;background:var(--surface-3);color:var(--faint);cursor:pointer;opacity:.5;transition:opacity .14s,color .14s,border-color .14s}
.doc-body .codeblock:hover .pre-copy{opacity:1}
.doc-body .pre-copy:hover{color:var(--brand-2);border-color:var(--brand)}
.doc-body .pre-copy.done{color:var(--ok);border-color:var(--ok);opacity:1}
.doc-body blockquote{margin:11px 0;padding:9px 14px;border-left:3px solid var(--brand);background:var(--brand-dim);border-radius:0 8px 8px 0}
.doc-body table{border-collapse:collapse;width:100%;margin:12px 0;font-size:12.5px}
.doc-body th,.doc-body td{border:1px solid var(--line);padding:7px 11px;text-align:left;vertical-align:top}
.doc-body th{background:var(--surface-2);color:var(--text);font-weight:600;white-space:nowrap}
.doc-body hr{border:none;border-top:1px solid var(--line);margin:20px 0}

/* ---------- 呼叫单 / CALL TRACE ----------
   一次调用的接线记录：从客户端要的模型，到这把 Key 的通道、命中的规则、
   按序试过的每一跳，最后落在应答的那家供应商和它的计量。
   版面是一条竖着的信号走线——机箱里那束线立起来：节点是接点，
   线段是电流，打开时电流自上而下跑一遍，跑到哪一跳就点亮哪一跳。
   失败的跳画成断线（缺口 + 红帽），命中的那跳走线常亮流动。
   日志 / 用户 / 密钥三处点开的是同一张单子，只是入口不同。 */
.tscrim{position:fixed;inset:0;background:var(--scrim);opacity:0;pointer-events:none;transition:opacity .3s var(--ease);z-index:60}
.tscrim.show{opacity:1;pointer-events:auto}
/* 与通用抽屉同一形制：离窗口边 10px 的浮动面板，24px 圆角，弹簧滑入 */
.tdrawer{position:fixed;top:10px;right:10px;bottom:10px;width:788px;max-width:calc(100vw - 20px);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-2xl);transform:translateX(calc(100% + 24px));transition:transform .44s var(--spring);
  z-index:61;display:flex;flex-direction:column;box-shadow:none;overflow:hidden}
.tdrawer.show{transform:none;box-shadow:var(--shadow-2)}
.tr-head{display:flex;align-items:flex-start;gap:12px;padding:17px 20px 15px;border-bottom:1px solid var(--line-soft);position:relative}
.tr-head::after{content:none}
.tr-head h3{font-family:var(--disp);font-size:16.5px;font-weight:600;margin:0;letter-spacing:-.01em}
.tr-sub{font-size:12px;color:var(--faint);margin-top:5px;display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.tr-back{font-size:17px;line-height:1;margin-top:2px}
.tr-rid{font-family:var(--mono);font-size:11px;color:var(--dim);background:var(--surface-3);border:1px solid var(--line);
  border-radius:6px;padding:2px 8px;cursor:pointer}
.tr-rid:hover{color:var(--brand-2);border-color:var(--brand)}
.tr-body{padding:18px 20px 26px;overflow-y:auto;flex:1}

/* 走线：左侧接点 + 电流线段，右侧一张卡片 */
.tr-run{display:flex;flex-direction:column}
.tr-stage{display:grid;grid-template-columns:26px 1fr;column-gap:14px;align-items:start;
  animation:rise .34s cubic-bezier(.2,.7,.3,1) both;animation-delay:calc(var(--i)*.07s)}
.tr-lead{position:relative;display:flex;justify-content:center;align-self:stretch;padding-top:5px}
.tr-lead::before{content:"";position:absolute;top:16px;bottom:-4px;left:50%;width:2px;margin-left:-1px;border-radius:2px;
  background:linear-gradient(180deg,var(--brand),var(--brand-a30));
  background-repeat:no-repeat;animation:tcurrent .42s ease-out both;animation-delay:calc(var(--i)*.07s + .12s)}
.tr-stage:last-child .tr-lead::before{display:none}
.tr-node{width:11px;height:11px;border-radius:50%;background:var(--surface-3);border:2px solid var(--brand);z-index:1;
  animation:tlight .3s ease-out both;animation-delay:calc(var(--i)*.07s + .1s)}
.tr-stage.bad .tr-node{border-color:var(--bad)}
.tr-stage.bad .tr-lead::before{background:linear-gradient(180deg,var(--bad),var(--bad-a25))}
@keyframes tcurrent{from{background-size:100% 0}to{background-size:100% 100%}}
@keyframes tlight{from{transform:scale(.4);box-shadow:none}
  to{transform:none;box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--brand-ahalo)}}
.tr-card{padding:0 0 20px;min-width:0}
.tr-k{font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);margin-bottom:8px}
.tr-v{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:13.5px}
.tr-strong{font-family:var(--mono);font-size:14px;font-weight:600;color:var(--text);word-break:break-all}
.tr-note{font-size:12px;color:var(--dim);margin-top:7px;line-height:1.6}
.tr-note b{color:var(--text);font-weight:600}
.tr-node-sec{margin-top:22px;padding:16px 18px 18px;border:1px solid var(--line);border-radius:18px;background:var(--surface)}
.tr-node-sec>.tr-k{margin-top:14px}
.tr-node-sec>.tr-k:first-child{margin-top:0}
.tr-node-body{margin-top:16px;padding-top:14px;border-top:1px solid var(--line)}
.tr-node-body .tr-k{margin-top:14px}
.tr-node-body .tr-k:first-child{margin-top:0}
.tr-node-pre{margin:8px 0 0;padding:12px 14px;border-radius:12px;background:var(--fill-4);font-family:var(--mono);font-size:12px;line-height:1.55;white-space:pre-wrap;word-break:break-word;max-height:280px;overflow:auto}
#tr-node-body-out{margin-top:12px}
#lg-nodeerr{max-width:186px}
.tr-chip{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;padding:3px 10px;border-radius:999px;
  background:var(--surface-3);color:var(--dim);border:1px solid var(--line);white-space:nowrap}
.tr-chip.mono{font-family:var(--mono);font-weight:500}
.tr-chip.on{color:var(--brand-2);background:var(--brand-dim);border-color:var(--brand-a45)}
.tr-chip.warnc{color:var(--warn);background:var(--warn-a12);border-color:var(--warn-a40)}
.tr-chip.badc{color:var(--bad);background:var(--bad-a11);border-color:var(--bad-a40)}

/* 配线架：候选链的每一跳 */
.tr-rack{display:flex;flex-direction:column;gap:7px}
.tr-hop{display:grid;grid-template-columns:20px 1fr auto;column-gap:11px;align-items:center;
  background:var(--surface-2);border:1px solid var(--line);border-radius:11px;padding:9px 12px 9px 10px;position:relative}
.tr-seq{font-family:var(--mono);font-size:11.5px;color:var(--faint);text-align:center}
.tr-hop-main{display:flex;align-items:center;gap:9px;flex-wrap:wrap;min-width:0}
.tr-up{font-weight:600;font-size:13px}
.tr-alias{font-family:var(--mono);font-size:11.5px;color:var(--dim)}
.tr-hop-r{display:flex;align-items:center;gap:9px;white-space:nowrap}
.tr-ms{font-family:var(--mono);font-size:11.5px;color:var(--faint)}
.tr-hop-why{grid-column:2/-1;font-size:11.5px;color:var(--faint);margin-top:5px}
/* 命中：接通的那一跳常亮，左缘一条流动的电流 */
.tr-hop.ok{border-color:var(--ok-a52);background:var(--ok-8-on-surface)}
.tr-hop.ok::before{content:"";position:absolute;left:0;top:9px;bottom:9px;width:3px;border-radius:3px;
  background:linear-gradient(180deg,var(--ok),var(--ok-a40));background-size:100% 200%;
  animation:tflowv 1.9s linear infinite}
@keyframes tflowv{from{background-position:0 200%}to{background-position:0 0}}
.tr-hop.ok .tr-up{color:var(--ok)}
/* 失败 / 跳过：断线——缺口加一顶红帽，读起来就是"这条没接上" */
.tr-hop.fail{border-color:var(--bad-a42)}
.tr-hop.fail::before{content:"";position:absolute;left:0;top:50%;width:3px;height:12px;margin-top:-6px;border-radius:3px;background:var(--bad)}
.tr-hop.fail .tr-up{color:var(--bad)}
.tr-hop.idle{border-style:dashed;background:transparent;opacity:.6}
.tr-hop.idle .tr-up{color:var(--dim);font-weight:500}
.tr-hop.blocked{border-style:dashed;border-color:var(--warn-a45);background:transparent}
.tr-hop.blocked .tr-up{color:var(--warn)}

/* 计量：四条 token 计量条 + 结算 */
.tr-meter{display:grid;grid-template-columns:repeat(auto-fit,minmax(126px,1fr));gap:1px;background:var(--line-soft);
  border:1px solid var(--line);border-radius:12px;overflow:hidden}
.tr-cell{background:var(--surface);padding:11px 13px}
.tr-cell .k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.tr-cell .v{font-family:var(--disp);font-size:19px;font-weight:600;letter-spacing:-.02em;margin-top:6px;font-variant-numeric:tabular-nums}
.tr-cell .v small{font-size:11.5px;font-weight:500;color:var(--dim);margin-left:3px}
.tr-cell.cost .v{color:var(--brand-2)}
.tr-bar{height:4px;border-radius:3px;background:var(--surface-3);margin-top:9px;overflow:hidden}
.tr-bar i{display:block;height:100%;border-radius:3px;background:var(--brand);animation:tgrow .5s cubic-bezier(.2,.7,.3,1) both;animation-delay:.3s}
.tr-bar.out i{background:var(--oa)} .tr-bar.cr i{background:var(--gm)} .tr-bar.cw i{background:var(--im)}
@keyframes tgrow{from{transform:scaleX(0);transform-origin:left}to{transform:none}}
.tr-blank{padding:22px;border:1px dashed var(--line);border-radius:12px;color:var(--faint);font-size:12.5px;line-height:1.7}

/* 当前账号的角色徽标，就贴在用户名后面：交接之后第一件要确认的事是「我现在是谁」 */
.role-chip{margin-left:6px;padding:1px 6px;border-radius:999px;font-size:11px;line-height:1.6;
  border:1px solid var(--line-soft);color:var(--dim);white-space:nowrap}
/* 所有者是紫的，不是绿的。绿在这套灯语里是「通着、没事」，而所有者说的是「这把
   钥匙开得最深」——那是权限的轴，和健康无关。设置页的权限档位用的是同一支紫。 */
.role-chip.role-owner{color:var(--brand-2);border-color:var(--brand-a40)}
.role-chip.role-viewer{color:var(--faint)}
/* 只读账号：把写操作的按钮压暗并挡住点击。它们仍然存在，因为看得见「这里本来有个按钮，
   但我这个角色按不动」比按钮凭空消失更好解释。 */
body.is-viewer .sec .btn:not(.ghost):not(.x){opacity:.45;pointer-events:none}

/* 审计页的两条筛选栏。窄屏时筛选整条落到标题下面而不是把标题挤成竖排 */
.audit-bar{display:flex;justify-content:space-between;align-items:center;gap:14px 18px;flex-wrap:wrap;margin-bottom:14px}
.audit-bar>p{flex:1 1 260px;min-width:0}
/* 全局的 input/select 是 width:100%，在这条工具栏里会把三个下拉撑成三整行。
   这里按内容给宽度，让它们排成一行读得完的一排。 */
.audit-filters{display:flex;gap:8px;align-items:center;flex-wrap:wrap;flex:0 1 auto}
.audit-filters input{width:210px;flex:0 1 210px;min-width:130px}
.audit-filters select{width:auto;flex:0 0 auto;min-width:112px;padding-right:34px}
.audit-check{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--dim);white-space:nowrap}
.audit-check input{width:auto;flex:none;min-width:0;margin:0}
@media (max-width:720px){.audit-bar>p{flex:1 1 100%}.audit-filters{width:100%}
  .audit-filters input,.audit-filters select{flex:1 1 120px;min-width:0;width:auto}}

/* ---------- 内容安全 ----------
   间距全部抄设置页与审计页（.set-panel 20/22、.field 13、.sec 34、.audit-bar），不另起一套。 */
/* 模式三选一：每个按钮自带一句「选了它会怎样」，比按钮下面再摞一段说明更不容易读串行。
   拦截是红、仅记录是黄——和事件表里两种处理的标签同色，一眼对得上。 */
.sf-mode button{display:flex;flex-direction:column;align-items:flex-start;gap:4px;text-align:left;padding:12px 14px;line-height:1.45}
.sf-mode button b{font-size:13.5px;font-weight:600;color:var(--text)}
.sf-mode button span{font-size:11.5px;color:var(--faint);font-weight:400}
.sf-mode button.on b{color:var(--brand-2)}
.sf-mode button.m-observe.on{border-color:var(--warn-a45);background:var(--warn-a12)}
.sf-mode button.m-observe.on b{color:var(--warn)}
.sf-mode button.m-block.on{border-color:var(--bad-a45);background:var(--bad-a11)}
.sf-mode button.m-block.on b{color:var(--bad)}
.sf-mode button.on span{color:var(--dim)}
.sf-grid{margin-top:18px}
.sf-grid input{max-width:320px}
.sf-boardbar{display:flex;align-items:flex-start;justify-content:space-between;gap:16px 24px;flex-wrap:wrap;margin-bottom:18px}
.sf-boardbar .eyebrow{margin:0}
.sf-status{display:flex;align-items:center;gap:8px 14px;flex-wrap:wrap;margin-top:10px;font-size:13px;color:var(--dim)}
.sf-status .mono{font-size:12.5px;color:var(--text)}
.sf-status .bad{color:var(--bad);font-weight:600}
.sf-spend{margin-bottom:14px}
.sf-spend-row{display:flex;align-items:flex-end;justify-content:space-between;gap:18px 32px;flex-wrap:wrap}
.sf-spend .k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.sf-spend .v{font-family:var(--disp);font-size:32px;font-weight:600;letter-spacing:-.03em;line-height:1.1;margin-top:6px;font-variant-numeric:tabular-nums}
.sf-spend .sub{margin-top:8px;font-size:12.5px;color:var(--dim);line-height:1.6}
.sf-spend .side{max-width:46ch;font-size:12.5px;color:var(--dim);line-height:1.7}
@media (max-width:760px){.sf-spend .v{font-size:26px}}
.sf-line{display:flex;align-items:center;gap:9px;flex-wrap:wrap;min-height:41px;padding:0 2px;font-size:13px;color:var(--dim)}
.sf-line .mono{color:var(--text);font-size:12.5px}
.sf-line .dot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--ok-fill)}
.sf-line.bad .dot{background:var(--bad-fill)}
.sf-cats-field{margin-top:18px}
.sf-cats{gap:7px}
.sf-cats .chip{font-size:12px;padding:5px 12px}
.sf-cats .chip.off{color:var(--faint);text-decoration:line-through;text-decoration-color:var(--faint-a55)}
.sf-cat-n{margin-left:8px;font-family:var(--mono);letter-spacing:0;text-transform:none;color:var(--dim);font-weight:500}
.sf-scope{margin:18px 0 0;padding:12px 16px 12px 32px;border-radius:10px;background:var(--surface-2);border:1px solid var(--line-soft);
  font-size:12px;color:var(--dim);line-height:1.65}
.sf-scope li+li{margin-top:4px}
.sf-scope b{color:var(--text);font-weight:600}
/* 趋势与试一句并排：两块都是「读一眼」的东西，竖着摞会把命中记录推到第三屏 */
.sf-duo{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:14px 18px;align-items:start}
@media (max-width:1180px){.sf-duo{grid-template-columns:minmax(0,1fr);gap:34px}}
.sf-legend{display:inline-flex;align-items:center;gap:6px 14px;flex-wrap:wrap;font-size:12px;color:var(--dim)}
.sf-legend>span{display:inline-flex;align-items:center;gap:6px}
.sf-legend i{display:inline-block;width:9px;height:9px;border-radius:3px}
.sf-legend i.b{background:var(--bad)} .sf-legend i.o{background:var(--warn)} .sf-legend i.c{background:var(--faint-a55)}
/* 命中与检测量各用各的尺子：命中往往只有检测量的百分之一，共用一把尺它就是一条贴地的线 */
.sf-trend{display:grid;grid-template-columns:repeat(14,minmax(0,1fr));gap:6px;align-items:end}
.sf-day{display:flex;flex-direction:column;align-items:stretch;gap:5px;min-width:0}
.sf-day .hit{height:78px;display:flex;flex-direction:column;justify-content:flex-end;border-radius:4px;background:var(--surface-2)}
.sf-day .hit>i{display:block;min-height:0}
.sf-day .hit>i.b{background:var(--bad)} .sf-day .hit>i.o{background:var(--warn)}
.sf-day .hit>i:first-child{border-radius:4px 4px 0 0}
.sf-day .chk{height:18px;display:flex;align-items:flex-end;border-radius:3px;background:var(--surface-2)}
.sf-day .chk>i{display:block;width:100%;background:var(--faint-a55);border-radius:3px}
.sf-day .d{font-family:var(--mono);font-size:10px;color:var(--faint);text-align:center;white-space:nowrap;overflow:hidden}
.sf-day.today .d{color:var(--text);font-weight:600}
.sf-rt{display:flex;gap:6px 18px;flex-wrap:wrap;margin-top:16px;padding-top:14px;border-top:1px solid var(--line-soft);font-size:12px;color:var(--dim)}
.sf-rt b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.sf-rt .sf-err{color:var(--bad);flex-basis:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sf-test-bar{display:flex;align-items:center;gap:12px;margin-top:12px;flex-wrap:wrap}
.sf-test-bar .faint{font-size:12px}
.sf-verdict-line{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:18px 0 14px;font-size:13px;color:var(--dim)}
.sf-verdict-line b{color:var(--text)}
/* 各类得分：一行一类，竖线是当前阈值。关掉的类别也画，只是压暗——
   「它其实有 0.91，只是没启用」正是调类别时要看见的东西。 */
.sf-scores{display:flex;flex-direction:column;gap:9px}
.sf-score{display:grid;grid-template-columns:minmax(96px,150px) minmax(0,1fr) 44px;gap:12px;align-items:center;font-size:12.5px}
.sf-score .l{color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sf-score .l small{margin-left:6px;font-size:10.5px;color:var(--faint)}
.sf-score .bar{position:relative;height:8px;border-radius:4px;background:var(--surface-3)}
.sf-score .bar>i{position:absolute;left:0;top:0;bottom:0;border-radius:4px;background:var(--faint-a60)}
.sf-score .bar>u{position:absolute;top:-4px;bottom:-4px;width:2px;margin-left:-1px;border-radius:1px;background:var(--text-a46)}
.sf-score .v{font-family:var(--mono);font-size:12px;text-align:right;color:var(--dim)}
.sf-score.hit .l{color:var(--text);font-weight:600}
.sf-score.hit .bar>i{background:var(--bad)}
.sf-score.hit .v{color:var(--bad);font-weight:600}
.sf-score.over .bar>i{background:var(--warn)}
.sf-score.off{opacity:.62}
/* 事件表：摘录独占弹性列、单行省略；整行可点，点开抽屉 */
.sf-t{table-layout:fixed}
.sf-t tbody tr{cursor:pointer}
.sf-t td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sf-t .who b{display:block;font-weight:600;overflow:hidden;text-overflow:ellipsis}
.sf-t .who small{display:block;margin-top:2px;font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis}
.sf-t .ex{color:var(--dim)}
.sf-t .sc{margin-left:6px;font-family:var(--mono);font-size:11.5px;color:var(--faint)}
.sf-t tr.sf-fp td{opacity:.62}
.tag.sf-blocked,.tag.sf-confirmed{color:var(--bad);background:var(--bad-a11)}
.tag.sf-observed{color:var(--warn);background:var(--warn-a12)}
.tag.sf-retro{color:var(--brand-2);background:var(--brand-a12)}
.tag.sf-fp{color:var(--ok);background:var(--ok-a12)}
.tag.sf-none{color:var(--faint);background:var(--faint-a12)}
.sf-more{display:flex;justify-content:center;margin-top:14px}
.sf-more[hidden]{display:none}
@media (max-width:860px){
  .sf-mode{flex-direction:column}
  .sf-grid input{max-width:none}
  .sf-trend{gap:3px}
  .sf-day .d{font-size:9px}
  .sf-t{table-layout:auto;min-width:760px}
  .sf-t td.ex{max-width:260px}
}
@media (max-width:720px){.audit-filters #sf-q{flex:1 1 100%}}
/* 抽屉：先认人与事，再读原文，最后下判断 */
.sf-dr-tags{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 18px}
.sf-kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:9px 18px;margin:0 0 22px;font-size:12.5px}
.sf-kv dt{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);white-space:nowrap;padding-top:1px}
.sf-kv dd{margin:0;color:var(--text);min-width:0;overflow-wrap:anywhere}
.sf-kv dd .faint{margin-left:6px}
.sf-dr-h{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin:0 0 9px;
  font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.sf-dr-h span{font-weight:500;letter-spacing:0;text-transform:none;font-size:11.5px}
.sf-text{margin:0 0 24px;padding:12px 14px;max-height:300px;overflow:auto;border-radius:10px;background:var(--surface-2);border:1px solid var(--line-soft);
  font-family:var(--mono);font-size:12.5px;line-height:1.7;color:var(--text);white-space:pre-wrap;overflow-wrap:anywhere}
.sf-dr-review{margin-top:24px;padding-top:18px;border-top:1px solid var(--line-soft)}
.sf-dr-review .field{margin-bottom:0}
.sf-dr-by{font-size:12px;color:var(--dim);margin:0 0 16px}

/* ============================================================
   审计走带 / AUDIT TAPE
   ------------------------------------------------------------
   一条审计记录是一个句子——〔谁〕在〔何时〕把〔什么〕从〔A〕改成了〔B〕。
   表格把这句话拆进六个格子，再把最要紧的 A 和 B 藏进弹窗里的 JSON，
   所以这里不用表格：事件是走带上的一格，带子本身记录时间与密度，
   日期在带上打一道横断，机器轮询卷成一段打孔的空带——它们确实发生过，
   但它们不是任何人要读的东西。
   展开就地发生，不弹窗：翻审计的人有一半是在问「这前后还有什么」，
   弹窗恰好切断的就是这个。
   ============================================================ */
.tape{position:relative;padding:6px 0 4px}
/* 走带本体：一条贯穿的发丝线，事件在它上面打点 */
.tape::before{content:"";position:absolute;left:calc(var(--tape-x) - 1px);top:0;bottom:0;width:1px;
  background:linear-gradient(180deg,transparent,var(--line) 22px,var(--line) calc(100% - 22px),transparent)}
.tape{--tape-x:108px}

/* 日期横断：读审计的人按「今天 / 昨天」找，不按行号找 */
.tape-day{position:relative;display:flex;align-items:center;gap:12px;padding:16px 20px 14px calc(var(--tape-x) + 20px);
  font-size:11.5px;font-weight:600;letter-spacing:.1em;color:var(--dim);text-transform:uppercase}
.tape-day::before{content:"";position:absolute;left:calc(var(--tape-x) - 5px);top:50%;width:9px;height:1px;background:var(--line)}
.tape-day b{font-family:var(--disp);font-size:13px;font-weight:600;letter-spacing:.01em;
  text-transform:none;color:var(--dim)}
.tape-day i{flex:1;height:1px;background:var(--line-soft);font-style:normal}
.tape-day span{font-variant-numeric:tabular-nums}

/* 一格 = 一次操作。整格可点（有内容才展开），键盘可达。 */
/* 背景要显式写：全局的 button 复位只清了边框和字体，没清背景，
   留着浏览器默认的 buttonface 会在深色台面上糊出一条灰带。 */
.tape-row{position:relative;display:grid;grid-template-columns:var(--tape-x) 1fr auto;
  align-items:baseline;column-gap:18px;width:100%;text-align:left;background:transparent;
  padding:11px 18px 11px 0;border-radius:0;transition:background .14s ease}
.tape-row:hover{background:var(--brand-a5)}
.tape-row:focus-visible{outline:2px solid var(--focus);outline-offset:-2px;border-radius:10px}
.tape-row[aria-expanded=true]{background:var(--brand-a5)}
/* 打点：落在带子上，压住线。失败是红点，动钱是品牌色，其余保持安静。 */
.tape-row::after{content:"";position:absolute;left:calc(var(--tape-x) - 4px);top:16px;width:7px;height:7px;border-radius:50%;
  background:var(--surface);box-shadow:inset 0 0 0 1.5px var(--faint-a60)}
.tape-row.is-bad::after{background:var(--bad);box-shadow:0 0 0 3px var(--bad-a16)}
.tape-row.is-money::after{background:var(--brand);box-shadow:0 0 0 3px var(--brand-a20)}
.tape-time{font-family:var(--mono);font-size:12px;color:var(--faint);text-align:right;
  padding-right:20px;font-variant-numeric:tabular-nums;white-space:nowrap}
.tape-main{min-width:0}
/* 一行摘要就是一句话，读的是行长而不是可用宽度。宽屏上让它在 80 字左右
   收住，剩下的留白比一条横穿 1300px 的句子好读。 */
.tape-what{font-size:13.5px;line-height:1.55;color:var(--text);max-width:82ch}
.tape-what .mono{font-size:12.5px}
/* 端点名是「这条记录写在有人给它写说明之前」的退路，不是内容，压到底噪 */
.tape-raw{font-family:var(--mono);font-size:12.5px;color:var(--faint)}
.tape-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:5px;
  font-size:12px;color:var(--dim)}
.tape-meta .sep{color:var(--faint);opacity:.5}
.tape-who{display:inline-flex;align-items:center;gap:6px;font-weight:600;color:var(--dim)}
/* 操作者的来源侧：同名的管理员和用户是两个人 */
.tape-who i{font-style:normal;display:inline-flex;align-items:center;justify-content:center;
  width:17px;height:17px;border-radius:5px;font-size:10px;font-weight:700;letter-spacing:0;
  background:var(--ok-a12);color:var(--ok)}
.tape-who.user i{background:var(--brand-a15);color:var(--brand-2)}
.tape-who.sys i{background:var(--faint-a14);color:var(--faint)}
.tape-target{color:var(--dim)}
.tape-target b{color:var(--text);font-weight:600}
.tape-ip{font-family:var(--mono);font-size:11.5px;color:var(--faint)}
/* 成功不着色：一屏 112 个绿色「成功」什么也没说，只有被拒绝的才是消息 */
.tape-fail{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;
  padding:2px 8px;border-radius:999px;color:var(--bad);background:var(--bad-a11)}
.tape-aside{display:flex;align-items:center;gap:10px;align-self:center;white-space:nowrap}
/* ±N = 这次动了几个字段。是读数，不是装饰：点开之前就知道这是拨了一个开关，
   还是把整张表重写了一遍。新建记 +N、删除记 −N——那不是「改了 N 处」。 */
.tape-delta{font-family:var(--mono);font-size:11.5px;font-weight:600;color:var(--brand-2);
  background:var(--brand-dim);border:1px solid var(--brand-a30);border-radius:999px;padding:2px 8px}
.tape-delta.add{color:var(--ok);background:var(--ok-a12);border-color:var(--ok-a40)}
.tape-delta.del{color:var(--bad);background:var(--bad-a11);border-color:var(--bad-a40)}
.tape-caret{width:15px;height:15px;color:var(--faint);opacity:.45;flex:none;transition:transform .18s ease,opacity .14s ease}
.tape-row:hover .tape-caret,.tape-row:focus-visible .tape-caret{opacity:1;color:var(--brand-2)}
.tape-row[aria-expanded=true] .tape-caret{transform:rotate(180deg);opacity:1;color:var(--brand-2)}

/* 卷带：连续的机器轮询。它们发生过，所以不能删；没人读，所以不该占位置。
   打孔的底纹是字面意思——这一段带子上什么也没记。 */
.tape-spool{position:relative;display:grid;grid-template-columns:var(--tape-x) 1fr auto;
  align-items:center;column-gap:18px;width:100%;text-align:left;padding:9px 18px 9px 0;
  background:transparent;color:var(--faint);transition:color .14s ease}
.tape-spool:hover{color:var(--dim)}
.tape-spool::after{content:"";position:absolute;left:calc(var(--tape-x) - 3px);top:0;bottom:0;width:5px;
  background:repeating-linear-gradient(180deg,var(--faint-a38) 0 3px,transparent 3px 8px)}
.tape-spool .tape-time{color:var(--faint);opacity:.7}
.tape-spool-what{font-size:12.5px}
.tape-spool-what b{font-family:var(--mono);font-weight:600;color:var(--dim)}
.tape-spool-act{font-size:12px;font-weight:600;color:var(--brand-2);opacity:.75}
.tape-spool:hover .tape-spool-act{opacity:1}

/* ---- 变更对照 ----------------------------------------------------------
   「改了用量策略」不是审计，「每日请求上限 2,000 → 6,000」才是。
   只列动过的字段：没动的那些是背景，翻审计的人不是来读背景的。 */
/* 展开的部分挂在带子右侧，带子本身继续往下走——一条走到一半断掉的带子
   读起来像是坏了，而这里什么也没坏。 */
.tape-open{position:relative;padding:2px 18px 20px calc(var(--tape-x) + 18px);
  animation:tapeOpen .22s ease both}
.tape-open::before{content:"";position:absolute;left:calc(var(--tape-x) - 1px);top:0;bottom:0;
  width:1px;background:var(--line)}
@keyframes tapeOpen{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
/* 对照表是拿来读的，不是拿来铺满屏幕的：宽到 1300px 之后，「之前」和「之后」
   两栏之间会空出半个屏幕，眼睛得横着走完才对得上。 */
.reg{border:1px solid var(--line);border-radius:12px;background:var(--surface-2);overflow:hidden;max-width:940px}
.reg-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 15px;
  background:var(--surface-3-a45);border-bottom:1px solid var(--line-soft);
  font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.reg-head em{font-style:normal;font-family:var(--mono);letter-spacing:0;text-transform:none;
  color:var(--dim);font-size:11.5px}
.reg-body{padding:4px 0}
/* 三栏：字段名 / 之前 / 之后。之后一栏加重，因为那是现在生效的值。 */
.reg-row{display:grid;grid-template-columns:minmax(130px,var(--reg-label,232px)) 1fr 22px 1fr;
  align-items:baseline;column-gap:12px;padding:8px 15px}
.reg-row+.reg-row{border-top:1px solid var(--line-soft)}
/* 新建与删除只有一侧，没有对照可摆 */
.reg-row.one{grid-template-columns:minmax(130px,232px) 1fr}
.reg-k{font-size:12.5px;color:var(--dim);line-height:1.5}
/* 中文标签是我们的翻译，原始字段名是收据——审计的价值全在「可以复核」，
   所以键名留着，但跟在标签后面，而不是把每一行都撑成两行。 */
.reg-k small{font-family:var(--mono);font-size:10.5px;color:var(--faint);
  letter-spacing:.01em;margin-left:7px;word-break:normal;overflow-wrap:anywhere}
.reg-v{font-family:var(--mono);font-size:12.5px;line-height:1.6;word-break:break-word;color:var(--text)}
.reg-v.was{color:var(--faint)}
.reg-arrow{align-self:center;width:22px;height:1px;background:var(--line);position:relative}
.reg-arrow::after{content:"";position:absolute;right:0;top:-2.5px;border-left:5px solid var(--faint-a60);
  border-top:3px solid transparent;border-bottom:3px solid transparent}
.reg-none{color:var(--faint);font-style:italic;font-family:var(--sans);font-size:12px}
/* 集合型的值（模型白名单、IP 段、故障切换链）按「进出」读，不按整串读 */
.reg-set{display:flex;flex-wrap:wrap;gap:5px}
.reg-tag{font-family:var(--mono);font-size:11.5px;padding:2px 8px;border-radius:6px;
  background:var(--surface-3);border:1px solid var(--line);color:var(--dim);white-space:nowrap}
.reg-tag.add{color:var(--ok);background:var(--ok-a12);border-color:var(--ok-a40)}
.reg-tag.del{color:var(--bad);background:var(--bad-a11);border-color:var(--bad-a40);text-decoration:line-through}
.reg-num{font-variant-numeric:tabular-nums}
/* 涨跌只标方向，不做判断：调高上限和调低上限都可能是对的 */
.reg-trend{font-family:var(--sans);font-size:11px;font-weight:600;margin-left:7px;color:var(--faint)}
.reg-more{display:block;width:100%;text-align:left;padding:9px 15px;border-top:1px solid var(--line-soft);
  background:transparent;font-size:12px;color:var(--faint);transition:color .14s ease}
.reg-more:hover{color:var(--brand-2)}
.reg-foot{display:flex;gap:8px 18px;flex-wrap:wrap;padding:11px 15px 0;font-size:11.5px;color:var(--faint)}
.reg-foot span{display:inline-flex;gap:6px}
.reg-foot b{font-family:var(--mono);font-weight:500;color:var(--dim);word-break:break-all}
.reg-plain{padding:13px 15px;font-size:12.5px;color:var(--dim);line-height:1.65}

/* 资金流水：一列钱必须右对齐、小数点对齐、位数固定，否则「+3000」和「−8.42」
   在同一列里读起来一样长。全局没有 .num 这条规则（它只在别的表里各定义了一份），
   所以这张表一直是左对齐的——一本左对齐的账没法用眼睛加总。 */
.led-t th.num,.led-t td.num{text-align:right}
.led-t td.num{font-family:var(--mono);font-size:12.5px;font-variant-numeric:tabular-nums;
  white-space:nowrap;color:var(--text)}
.led-t td.num.in{color:var(--ok)}
.led-t td.num.out{color:var(--dim)}
.led-t td .tape-who{font-size:12.5px}

@media (max-width:860px){
  .tape{--tape-x:74px}
  .tape-time{padding-right:14px;font-size:11.5px}
  /* 窄屏把三栏叠成三行。顺序必须还是「之前 → 之后」：把新值排在旧值上面，
     再给旧值挂一个向下的箭头，读出来就是反的。 */
  .reg-row,.reg-row.one{grid-template-columns:1fr;row-gap:2px}
  .reg-arrow{width:auto;height:auto;background:none;line-height:1}
  .reg-arrow::after{content:"↓";position:static;border:0;color:var(--faint);font-size:12px}
  .tape-open{padding-left:14px;padding-right:14px}
  .tape-open::before{display:none}
}
/* 变更前 / 变更后的 JSON。横向自己滚，不把抽屉撑开 */
.field pre{background:var(--surface-2);border:1px solid var(--line-soft);border-radius:10px;
  padding:11px 13px;margin:0;overflow-x:auto;font-size:12px;line-height:1.55;white-space:pre}

/* 调用流水：日志页与用户 / 密钥抽屉共用的同一张表 */
.feed-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:13px}
/* 搜索框：灰底胶囊（macOS 的搜索栏），聚焦时变回白底、套上蓝色焦点环 */
.feed-find{display:flex;align-items:center;gap:7px;background:var(--fill-4);border:1px solid transparent;
  border-radius:var(--r-pill);padding:0 13px;color:var(--dim);flex:1;min-width:180px;max-width:340px;
  transition:background-color .16s var(--ease),border-color .16s var(--ease),box-shadow .16s var(--ease)}
.feed-find:hover{background:var(--fill-3)}
.feed-find:focus-within{background:var(--input-bg);border-color:var(--focus);box-shadow:var(--focus-ring)}
.feed-find input{background:transparent;border:none;padding:8px 0;font-size:13px;flex:1;color:var(--text)}
.feed-find input:focus{outline:none;box-shadow:none}
.feed-count{font-size:12px;color:var(--faint);margin-left:auto}
table.feed td{vertical-align:middle;white-space:nowrap}

/* ---------- 日志页的配线盘：两排旋钮 + 一排条件牌 ----------
   上排是每次都会动的粗调（时间窗 / 结果 / 搜索），下排是按需拧的细调
   （模型 / 供应商 / 归属 / 协议 / 慢调用）。拧过的每一项都会在最下面留一张
   可摘的条件牌——面板上插着什么线，一眼看得见，摘一张就松一条。 */
.lg-filters{display:flex;flex-direction:column;gap:9px;margin-bottom:13px}
.lg-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.winsel button i{font-style:normal;margin-left:6px;font-size:11px;font-variant-numeric:tabular-nums;
  color:var(--faint);transition:color .16s ease}
.winsel button.on i{color:inherit;opacity:.62}
.winsel button:not(.on):hover i{color:var(--dim)}
/* 限流是「失败」里的一格，不是它的邻居：一道细线把这层关系说出来，省得
   有人把 失败 135 和 限流 40 读成 175 次出错。 */
#lg-outcome button.sub{margin-left:9px}
#lg-outcome button.sub::before{content:'';position:absolute;left:-5px;top:6px;bottom:6px;
  width:1px;background:var(--line)}
.lg-find{max-width:340px}
.lg-x{display:flex;align-items:center;justify-content:center;width:18px;height:18px;flex:none;border-radius:50%;
  background:var(--faint);color:var(--surface);font-size:13px;font-weight:600;line-height:1;padding:0 0 1px;opacity:.75}
.lg-x:hover{opacity:1}
/* 细调旋钮：左边是刻度名，右边是当前档位 */
/* 筛选钮：灰底胶囊（macOS 工具栏里的弹出按钮）。选中了某一档就整颗反成墨色——
   「这张表现在被什么筛着」要一眼能数出来。 */
.lg-sel{position:relative;display:inline-flex;align-items:center;height:32px;padding-left:12px;
  background:var(--fill-4);border:1px solid transparent;border-radius:var(--r-pill);
  transition:background-color .16s var(--ease),color .16s var(--ease)}
.lg-sel:hover{background:var(--fill-3)}
.lg-sel>span{font-size:12px;font-weight:500;color:var(--dim);white-space:nowrap;letter-spacing:0}
.lg-sel select{appearance:none;-webkit-appearance:none;border:none;background:transparent;box-shadow:none;
  height:32px;width:auto;max-width:186px;padding:0 25px 0 7px;font-size:12.5px;font-weight:600;
  color:var(--text);text-overflow:ellipsis;cursor:pointer}
.lg-sel select:focus{outline:none;box-shadow:none;border:none}
.lg-sel:focus-within{box-shadow:var(--focus-ring)}
.lg-sel::after{content:'';position:absolute;right:12px;top:50%;width:5.5px;height:5.5px;margin-top:-4.5px;
  border-right:1.6px solid var(--dim);border-bottom:1.6px solid var(--dim);transform:rotate(45deg);pointer-events:none}
.lg-sel.on{background:var(--brand);color:var(--btn-ink)}
.lg-sel.on>span{color:var(--btn-ink);opacity:.66}
.lg-sel.on select{color:var(--btn-ink)}
.lg-sel.on::after{border-color:var(--btn-ink)}
/* 可搜索下拉：原生 <select> 不能在选项里打字。供应商一多，找 sidecar
   就要在一长串 claude-oauth* / kiro-acct* 里滚。按钮长得和原来的档位一样，
   点开才是带搜索的列表。 */
.lg-sel.combo{cursor:pointer}
.lg-combo-btn{appearance:none;border:none;background:transparent;box-shadow:none;
  height:32px;width:auto;max-width:186px;padding:0 25px 0 7px;font-size:12.5px;font-weight:600;
  color:var(--text);text-overflow:ellipsis;overflow:hidden;white-space:nowrap;cursor:pointer;text-align:left}
.lg-combo-btn:focus{outline:none}
.lg-combo-btn:disabled{opacity:.45;cursor:default}
.lg-sel.on .lg-combo-btn{color:var(--btn-ink)}
/* 弹出的列表是一块厚玻璃菜单（macOS 的菜单：圆角、半透明、行是圆角高亮） */
.combo-pop{position:fixed;z-index:35;min-width:248px;max-width:min(360px,calc(100vw - 16px));
  padding:6px;background:var(--glass-thick);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  border:1px solid var(--rail-line);border-radius:var(--r-lg);
  box-shadow:var(--sheen),var(--shadow-3)}
.combo-pop:focus{outline:none}
.combo-head{display:none}
/* 触屏上这张菜单不再吊在按钮下面，而是从屏幕底部升起来的一张卡。
   Why：吊着的那版在手机上等于点不开——它靠"页面一滚就关"来跟住锚点，而
   手机上一对焦搜索框，软键盘顶上来、iOS 还会因为输入框字号 <16px 自动放大整页，
   两件事都会滚一下页面，于是菜单在同一次点击里开了又关。底部卡片没有锚点，
   也就没有"跟丢"这回事。 */
.combo-scrim{position:fixed;inset:0;z-index:34;background:var(--scrim);
  touch-action:none;overscroll-behavior:contain}
.combo-find{display:flex;align-items:center;gap:7px;margin:0 0 6px;padding:0 11px;
  background:var(--fill-4);border:1px solid transparent;border-radius:var(--r-md);color:var(--dim)}
.combo-find:focus-within{background:var(--input-bg);border-color:var(--focus);box-shadow:var(--focus-ring)}
.combo-find input{background:transparent;border:none;padding:8px 0;font-size:12.5px;flex:1;color:var(--text)}
.combo-find input:focus{outline:none;box-shadow:none}
.combo-list{max-height:min(280px,calc(100vh - 120px));overflow-y:auto;padding:0 2px 2px}
.combo-grp{margin:8px 9px 4px;font-size:11.5px;font-weight:600;letter-spacing:0;color:var(--dim)}
.combo-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border-radius:var(--r-sm);
  background:none;color:var(--text);text-align:left;cursor:pointer;transition:background-color .12s}
.combo-item:hover,.combo-item.hi{background:var(--fill-3)}
.combo-item.on{background:var(--brand);color:var(--btn-ink)}
.combo-item.on .c{color:var(--btn-ink);opacity:.7}
.combo-item .n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:13px;font-weight:600}
.combo-item .c{flex:none;font-size:11.5px;font-variant-numeric:tabular-nums;color:var(--faint)}
.combo-kind{flex:none;font-size:10.5px;font-weight:600;color:var(--brand-2);background:var(--brand-dim);
  padding:1px 7px;border-radius:999px;letter-spacing:.02em}
.combo-empty{padding:16px 10px;text-align:center;color:var(--faint);font-size:12.5px}
.feed-side{flex:none;font-size:10.5px;font-weight:600;color:var(--brand-2);background:var(--brand-dim);
  padding:1px 7px;border-radius:999px;letter-spacing:.02em}
/* 两个开关是一对：都是「只看这一类调用」，跟前面按维度切的下拉不是一回事。
   宽度不够时它们一起换行，而不是留一个在上面、一个孤零零地掉到下一行。 */
.lg-flags{display:inline-flex;align-items:center;gap:9px;flex:none}
.lg-toggle{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 13px;border-radius:var(--r-pill);
  background:var(--fill-4);border:1px solid transparent;color:var(--text);
  font-size:12.5px;font-weight:500;white-space:nowrap;transition:color .16s var(--ease),background-color .16s var(--ease)}
.lg-toggle:hover{background:var(--fill-3)}
.lg-toggle.on{background:var(--brand);color:var(--btn-ink)}
.lg-toggle:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* 条件牌：面板上插着的线，摘一张松一条 */
.lg-tags{display:flex;align-items:center;gap:7px;flex-wrap:wrap;
  padding-top:9px;border-top:1px dashed var(--line)}
.lg-tags-k{font-size:11px;font-weight:600;color:var(--faint);letter-spacing:.06em;padding-right:1px}
.lg-tag{display:inline-flex;align-items:center;gap:7px;height:26px;padding:0 5px 0 10px;border-radius:8px;
  background:var(--surface-2);border:1px solid var(--line);font-size:12px;color:var(--text)}
.lg-tag em{font-style:normal;color:var(--faint);font-size:11px}
.lg-tag b{font-weight:600;max-width:190px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lg-tag button{display:flex;align-items:center;justify-content:center;width:18px;height:18px;flex:none;
  border-radius:5px;background:none;color:var(--dim);font-size:15px;line-height:1;padding:0 0 2px}
.lg-tag button:hover{background:var(--bad-a16);color:var(--bad)}
.lg-tag button:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.lg-clear{margin-left:2px;font-size:12px;font-weight:600;color:var(--dim);background:none;padding:4px 2px;
  border-bottom:1px solid transparent}
.lg-clear:hover{color:var(--bad);border-bottom-color:currentColor}
/* 空态：没插上线的插口，顺手给一条出路 */
.lg-void{padding:34px 20px;text-align:center;color:var(--faint)}
.lg-void p{margin:0 0 12px;font-size:13px}
.lg-more{display:flex;justify-content:center;padding:11px 0 2px}
@media (max-width:720px){
  .lg-find{max-width:none}
  .lg-sel select,.lg-combo-btn{max-width:118px}
  /* 输入框字号压到 16px 以下，iOS 一对焦就把整页放大，用户还得自己缩回去 */
  .feed-find input{font-size:16px}
  /* 底部升起的那张卡：整宽、按住哪一行都够得着、避开刘海屏的下巴 */
  .combo-pop.sheet{left:0!important;right:0;top:auto!important;bottom:0;width:100%;
    min-width:0!important;max-width:none;border-radius:18px 18px 0 0;border-bottom:none;
    padding:14px 14px calc(14px + env(safe-area-inset-bottom));
    box-shadow:0 -14px 40px rgba(0,0,0,.4)}
  .combo-pop.sheet .combo-head{display:flex;align-items:center;justify-content:space-between;
    gap:12px;padding:0 2px 12px;margin-bottom:2px}
  .combo-pop.sheet .combo-head b{font-size:14px;font-weight:600;color:var(--text)}
  .combo-pop.sheet .combo-head button{display:flex;align-items:center;justify-content:center;
    width:34px;height:34px;flex:none;border-radius:9px;background:var(--surface-2);
    color:var(--dim);font-size:19px;line-height:1;padding:0 0 2px}
  /* 行尾菜单和筛选卡同一个形：吊在按钮下面的那版在手机上靠「一滚就关」跟住锚点，
     而手机上随便一下滚动就能把它在同一次点击里开了又关。 */
  .rowmenu.sheet{left:0!important;right:0;top:auto!important;bottom:0;width:100%;
    min-width:0!important;max-width:none;border-radius:18px 18px 0 0;border-bottom:none;
    padding:14px 14px calc(14px + env(safe-area-inset-bottom));
    /* 实心底：这张卡盖在底部导航上，半透明时导航的图标会从最后一项底下透上来 */
    background:var(--surface);-webkit-backdrop-filter:none;backdrop-filter:none;
    box-shadow:0 -14px 40px rgba(0,0,0,.4)}
  .rowmenu.sheet .rm-head{display:flex;align-items:center;justify-content:space-between;
    gap:12px;padding:0 2px 10px}
  .rowmenu.sheet .rm-head b{font-size:14px;font-weight:600;color:var(--text)}
  .rowmenu.sheet .rm-head button{display:flex;align-items:center;justify-content:center;
    width:34px;height:34px;flex:none;border-radius:9px;background:var(--surface-2);
    color:var(--dim);font-size:19px;line-height:1;padding:0 0 2px}
  .rowmenu.sheet .rm-who{display:none}
  .rowmenu.sheet .rm-item{padding:12px;gap:12px}
  .rowmenu.sheet .rm-item .n{font-size:14.5px}
  .rowmenu.sheet .rm-grp{margin:13px 10px 5px}
  .combo-pop.sheet .combo-find{margin:0 0 12px;padding:0 12px}
  .combo-pop.sheet .combo-find input{padding:11px 0;font-size:16px}
  .combo-pop.sheet .combo-list{max-height:min(48vh,360px);padding:0}
  .combo-pop.sheet .combo-item{padding:13px 12px;gap:10px}
  .combo-pop.sheet .combo-item .n{font-size:14.5px}
  .combo-pop.sheet .combo-grp{margin:12px 10px 6px}
}

.tr-body .panel,#cwb-body .panel{overflow-x:auto}
table.feed.compact{font-size:12px}
table.feed.compact th,table.feed.compact td{padding:9px 8px}
table.feed.compact th:first-child,table.feed.compact td:first-child{padding-left:12px}
table.feed.compact th:last-child,table.feed.compact td:last-child{padding-right:12px}
/* 空态："这里还没有调用" 也要看得出是同一台机器上的空插口 */
#tr-body .empty{padding:26px}
tr.feed-row{cursor:pointer}
tr.feed-row:hover{background:var(--surface-2)}
tr.feed-row:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.feed-path{display:inline-flex;align-items:center;gap:7px;font-size:12px;color:var(--dim);min-width:0}
.feed-path .chan-chip{font-size:10.5px;padding:1px 7px}
.feed-up{font-weight:600;color:var(--text);font-size:12.5px}
.feed-fail{font-family:var(--mono);font-size:11px;color:var(--bad)}
.feed-tok{font-family:var(--mono);font-size:12px;color:var(--dim);white-space:nowrap}
.feed-open{color:var(--faint);font-size:14px;line-height:1}
tr.feed-row:hover .feed-open{color:var(--brand-2)}
/* 「谁在调用」：人在前，他手里的凭证在后，一行读完。凭证走等宽——它是一个被
   起了名字的物件，不是一个人，两种字体让这一格不必再加一道分隔符。
   两个名字都是入口：点谁就只剩谁的调用。这一列只在日志页出现，抽屉是从某个
   用户或某把密钥点进来的，那里这一列恒等于标题，占的是宽度而不是信息。 */
.feed-who{display:inline-flex;align-items:baseline;gap:8px;min-width:0;max-width:280px}
.feed-owner,.feed-cred{background:none;padding:0;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;transition:color .14s ease}
.feed-owner{font-size:12.5px;font-weight:600;color:var(--text);text-align:left}
.feed-owner.sys{font-weight:500;color:var(--faint)}
.feed-memo{font-size:11px;color:var(--dim);font-weight:500;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;max-width:120px;flex:none}
.feed-cred{font-family:var(--mono);font-size:11.5px;color:var(--dim);flex:none;max-width:130px}
.feed-cred.none{color:var(--faint)}
button.feed-owner:hover,button.feed-cred:hover{color:var(--brand-2)}
button.feed-owner:focus-visible,button.feed-cred:focus-visible{outline:2px solid var(--focus);
  outline-offset:2px;border-radius:4px}
/* 多了一列之后表可能比面板宽：让它自己横向滚，而不是把整页撑出一条横条。
   同时把列间距收紧一档 —— 少这几十像素，费用那一列就还留在屏幕上，不用先滚
   一下才知道这次调用花了多少。两头仍按原样留白，表不会贴着面板边。 */
.lg-panel{overflow-x:auto}
.lg-panel table.feed th,.lg-panel table.feed td{padding-left:11px;padding-right:11px}
.lg-panel table.feed th:first-child,.lg-panel table.feed td:first-child{padding-left:16px}
.lg-panel table.feed th:last-child,.lg-panel table.feed td:last-child{padding-right:14px}
@media (max-width:860px){
  .tdrawer{top:0;right:0;bottom:0;width:100vw;max-width:100vw;border:0;border-radius:0}
  .tr-hop{grid-template-columns:18px 1fr;row-gap:6px}
  .tr-hop-r{grid-column:2;justify-content:flex-start}
}
@media (prefers-reduced-motion:reduce){
  .tr-stage,.tr-lead::before,.tr-node,.tr-hop.ok::before,.tr-bar i{animation:none}
  .tr-lead::before{background-size:100% 100%}
}

/* ---------- 拼车位（车道 · 车 · 座）-------------------------------------
   一辆车 = 一个订阅账号。车按车型停进车道，车厢里的座位就摆成这辆车的形状：
   1 人车一格、2 人车一排、4 人车两排两列、8 人车两排四列。于是「这是几人车」
   不用读数字，看形状就知道；「还空几个位」也不用读，数空格就行。

   车厢里每个人名底下压着一道细线 —— 这七天他在这辆车上吃掉的份额。同一张图
   于是回答两件事：谁坐在这儿（座位表），以及这辆车实际上是被谁开的（负载表）。
   一个买了位置却整周没发过请求的人，和一个把四人车一个人开满的人，在旧版的
   「3/4 人」里长得一模一样。

   车厢外面还挂着两样车本身的事实：油表（订阅额度的两个窗口，与供应商页同一
   套读法）和铭牌（账号邮箱 / 档级）。卖的是这辆车的额度，所以板上必须看得见
   它还剩多少。 */
.cp-pool{margin-bottom:26px}

/* 两种模式是两门生意，不是一个筛选器的两档：包月卖「一个账号的 N 分之一，一个月」，
   额度卖「按官方原价计价的一笔用量」。混在一块板上读，那些只对其中一种成立的读数
   （已售 / 可售 / 未定价）会安静地骑到另一种头上。所以分成两个页签。

   页签上挂的是计价单位。¥ 和 $ 在这套系统里从来是两个桶（余额 ¥ 能提现，额度 $
   不能），而把 $2000 读成 ¥2000 的人会按错误的预期规划一整个月——这两个符号是
   两种模式之间最短、也最难读错的那句话。
   页签本身在模块页签条上（「包月订阅 / API 额度」）；调度条 .cpd 里只剩搜人框、
   额度价签、新建和齿轮。 */
/* 搜人框排在调度条最左：车多了之后扫格子找不到「微信张三」，这一格是整块板
   上唯一按乘客/备注筛的入口。外形用同模块用户管理那一格的 .feed-find，别另起一套。 */
.cp-find{flex:none}
.cp-find-miss{display:none;margin:8px 2px 0;font-size:12.5px;color:var(--faint)}
.cp-find-miss.on{display:block}
/* 搜索靠 hidden 藏卡片，而这几种卡片自己写了 display:flex，会压过浏览器给 [hidden] 的默认样式：
   不补这一条，没命中的卡片照样显示，看起来就是搜索不生效。 */
#seat-board .cp-car[hidden],#seat-board .cp-carwrap[hidden],#seat-board .ap-acct[hidden]{display:none}
/* 价签（只有额度那一格有）跟在搜人框后面，不跟在句子里：它是一个会变的数
   （兑换价可以没配），而句子是不会变的定义。 */
.cp-modeprice{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);font-size:11.5px;
  color:var(--dim);background:var(--surface-2);border:1px solid var(--line);border-radius:999px;
  height:32px;box-sizing:border-box;padding:0 13px;font-variant-numeric:tabular-nums;white-space:nowrap;flex:none}
.cp-modeprice em{font-style:normal;font-family:var(--sans);font-size:11px;color:var(--faint)}
.cp-modeprice.dark{color:var(--warn);border-color:var(--warn-a30);background:var(--warn-a12)}
.cp-modeprice.dark em{color:var(--warn-a70)}
/* 模式的那一句话跟着页签换：说的是这个页签卖的是什么，不是拼车总的介绍。 */
.cp-thesis{font-size:12px;color:var(--faint);line-height:1.7;margin:0 0 20px;max-width:92ch}
.cp-thesis b{color:var(--dim);font-weight:600}
.cp-thesis .warn{color:var(--warn)}
/* ── 调度条：钉在顶栏底下的那条仪表带 ────────────────────────────────
   这一页只有一个职责：看车和车上的人。车多了以后一屏装不下第七辆，而模式
   页签、搜人框、新建按钮全留在一千像素之上——想换个页签得先滚回顶。所以控件
   收进一条钉住的带子，跟着人往下走。

   带子固定两层（2026-10-14）：上层一行控件，全部 32px 高；下层一条读数。读数替掉了
   原来那段三行的说明文字：定义值得被读一次，不值得每次进来都占三行；而「现在什么
   状况」每一眼都要。两种模式读的不是同一组数（包月问位子卖掉没有，额度问余量还剩
   多少），所以这一排由 seatVitals() 按模式算，不是一套写死的格子。
   读数以前夹在控件中间，宽度一紧就在中间竖着折成三四行、把齿轮挤到下一行；
   现在它永远单独一行，只在真的放不下时才折。

   读数里「最紧的那辆」带一根 46px 的水位条——它决定这支车队下一个撑不住
   的是谁，也正是滚到第七辆车时最该还看得见的那个数。 */
.cpd{position:sticky;top:var(--topbar-h,74px);z-index:4;display:flex;align-items:center;gap:8px 12px;flex-wrap:wrap;
  margin:-10px -28px 16px;padding:10px 28px 9px;background:var(--bg);border-bottom:1px solid var(--line);
  container:cpd/inline-size}
/* 控件行放不下时价签先让位，排到新建和齿轮后面、自己折到第二行：新建往往是这一页
   唯一的入口，不能被挤下去。880 是额度页签控件行的自然宽度（约 866）加一点余量。 */
@container cpd (max-width:880px){.cpd>.cp-modeprice{order:8}}
.cpd>.cp-find{flex:0 1 240px;min-width:170px;height:32px;box-sizing:border-box}
.cpd>.cp-find input{padding:0;height:30px}
.cpd-add{height:32px;padding:0 14px;flex:none}
/* 齿轮：判据 / 牌价 / 告警线 / 借用余量这些「配完就不动」的格子全在这扇门后面。
   它跟新建按钮同一档尺寸但不抢眼——它开的是设置，不是这一页的活。琥珀点跟侧栏
   同一套语言：门后有开关正关着，而关着的开关是这套系统最贵的一类静默故障。 */
.cpd-gear{position:relative;display:inline-grid;place-items:center;width:32px;height:32px;flex:none;
  border:1px solid var(--line);border-radius:10px;background:var(--surface-2);color:var(--dim);
  transition:color .16s ease,background .16s ease}
.cpd-gear svg{width:16px;height:16px}
.cpd-gear:hover{color:var(--text);background:var(--surface-3)}
.cpd-gear:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.cpd-gear .dot{position:absolute;top:-3px;right:-3px;width:8px;height:8px;border-radius:50%;
  background:var(--warn);box-shadow:0 0 0 2px var(--bg)}
.cpd-vit{order:9;flex:1 0 100%;display:flex;align-items:center;gap:4px 20px;flex-wrap:wrap;min-width:0;min-height:20px;padding:0 2px;
  overflow-x:clip;overflow-clip-margin:5px}
.cpd-m{display:inline-flex;align-items:baseline;gap:6px;white-space:nowrap}
.cpd-m .k{font-size:11.5px;color:var(--faint)}
.cpd-m b{font-family:var(--mono);font-size:13px;font-weight:600;line-height:1;color:var(--text);
  font-variant-numeric:tabular-nums}
.cpd-m .u{font-size:11.5px;color:var(--faint);margin-left:-3px}
/* 竖线画在组的左边、落在列间距正中。读数折行时，折到行首那一组的竖线落在 .cpd-vit
   左边界之外，被 overflow-x:clip 裁掉：不会出现挂在行首或行尾的半截分隔。
   clip-margin 5px 留给按钮的对焦描边（2px + 3px 偏移）。 */
.cpd-grp{position:relative;display:inline-flex;align-items:center}
.cpd-grp::before{content:'';position:absolute;left:-10px;top:50%;width:1px;height:12px;margin-top:-6px;background:var(--line)}
/* 账号名可能是一长串邮箱：截断，全名在悬停提示里。 */
.cpd-vit .who{display:inline-block;max-width:22ch;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom}
.cpd-m.dim b{color:var(--dim)}
.cpd-m.warn b{color:var(--warn)}
.cpd-m.bad b{color:var(--bad)}
/* 订阅页的排序下拉。不能用 .pv-sort：全局那条把字号收成 10px，是透视表的箭头。 */
#v-seats .seat-sort{display:inline-flex;align-items:center;gap:8px;flex:none;font-size:12px;color:var(--dim);white-space:nowrap}
#v-seats .seat-sort select{width:auto;height:32px;padding:0 28px 0 11px;font-size:12.5px;border-radius:10px}
/* 不是手动顺序时描深：这一页正按重置时刻重排，拖动手柄也因此收起来了。 */
#v-seats .seat-sort.on select{border-color:var(--brand-a60);font-weight:600}
button.cpd-m{background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer}
button.cpd-m:hover b{text-decoration:underline;text-underline-offset:2px}
button.cpd-m:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:6px}
.cpd-tight{display:inline-flex;align-items:center;gap:9px;white-space:nowrap}
.cpd-tight .k{font-size:11.5px;color:var(--faint)}
.cpd-tight .up{font-family:var(--mono);font-size:12px;color:var(--dim)}
.cpd-tight .pct{font-family:var(--mono);font-size:13px;font-weight:600;line-height:1;color:var(--text);
  font-variant-numeric:tabular-nums}
.cpd-tight .pct.warn{color:var(--warn)}
.cpd-tight .pct.bad{color:var(--bad)}
.cpd-bar{position:relative;width:46px;height:5px;border-radius:999px;background:var(--surface-3);flex:none}
.cpd-bar i{position:absolute;left:0;top:0;bottom:0;border-radius:999px;background:var(--ok)}
.cpd-bar.warn i{background:var(--warn)}
.cpd-bar.bad i{background:var(--bad)}
/* 「这卖的是什么」折成控件行右端一个小字：读一次就够的定义，不该每次进来都念一遍。
   它的 margin-left:auto 把说明、新建、齿轮推到右边。 */
.cpd-why{display:inline-flex;align-items:center;gap:6px;margin-left:auto;height:32px;
  font-size:11.5px;color:var(--faint);background:none;white-space:nowrap}
.cpd-why:hover{color:var(--dim)}
.cpd-why svg{width:9px;height:9px;transition:transform .2s ease}
.cpd-why.on svg{transform:rotate(90deg)}
.cpd-why:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:6px}
/* ── 订阅分配的表格模式 ─────────────────────────────────────────────
   卡片一张一个账号；表格一行一个名额（包月）或一个用户（API 额度），34px 一行，
   一屏装得下四五十个名额。上面一条工具条：左边是分类筛选（每一类带个数，点一下只看
   这一类），右边是分组、排序、份额按哪个限额算。表头钉在调度条下面（--cpd-h 由 stPinOffset
   量出来）。外框 overflow:clip 收圆角，它不是滚动容器，表头才钉得住整页滚动。
   #stb-root 是尺寸容器（stw），工具条和表都按它的宽度变：721px 以上是表，列按宽度从次要的
   开始收起，每一档都给用户名留出约 200px；720px 及以下（手机、窄窗口）每行改成紧凑记录。 */
.cpd-view{flex:none;box-sizing:border-box;height:32px}
.cpd-view button{display:inline-flex;align-items:center;padding:0 11px}
.cpd-view svg{width:15px;height:15px}
#stb-root{margin:0 0 24px;container:stw/inline-size}
/* 选择框按内容宽，不吃掉一整行：掌上给调度条排序框的 flex:1 1 180px 在这里不适用。 */
#v-seats .stb-ctl .seat-sort{flex:none}
.stb-bar{display:flex;align-items:flex-start;gap:10px 16px;flex-wrap:wrap;margin:0 0 12px}
.stb-chips{display:flex;flex-wrap:wrap;gap:6px;flex:1 1 420px;min-width:0}
.stb-chip{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 11px;border-radius:var(--r-pill);
  background:var(--fill-3);color:var(--dim);font-size:12.5px;font-weight:500;white-space:nowrap;
  transition:background-color .16s ease,color .16s ease}
.stb-chip b{font-family:var(--mono);font-size:12px;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
.stb-chip .u{font-size:11px;color:var(--faint);margin-left:-3px}
.stb-chip.warn b{color:var(--warn)}
.stb-chip.bad b{color:var(--bad)}
.stb-chip:not([disabled]):not(.on):hover{background:var(--fill-4);color:var(--text)}
.stb-chip.on{background:var(--ctl-on-bg);box-shadow:var(--ctl-on-ring);color:var(--text);font-weight:600}
.stb-chip:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.stb-ctl{display:flex;align-items:center;gap:8px 12px;flex-wrap:wrap;flex:none;margin-left:auto}
.stb-dir,.stb-foldall{display:inline-grid;place-items:center;height:32px;padding:0 12px;box-sizing:border-box;flex:none;
  border:1px solid var(--line);border-radius:10px;background:var(--surface-2);color:var(--dim);font-size:12.5px;white-space:nowrap}
.stb-dir{width:32px;padding:0;font-family:var(--mono);font-weight:600;color:var(--text)}
.stb-dir:hover,.stb-foldall:hover{background:var(--surface-3);color:var(--text)}
.stb-dir:focus-visible,.stb-foldall:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

.stb-wrap{border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface);box-shadow:var(--card-shadow);
  overflow:clip}
.stb-t{width:100%;table-layout:fixed;border-collapse:separate;border-spacing:0;font-size:12.5px}
.stb-t th,.stb-t td{height:34px;padding:0 10px;border-bottom:1px solid var(--line-soft);vertical-align:middle;
  overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-variant-numeric:tabular-nums;color:var(--dim)}
.stb-t th:first-child,.stb-t td:first-child{padding-left:16px}
.stb-t th:last-child,.stb-t td:last-child{padding-right:12px}
.stb-t thead th{position:sticky;top:calc(var(--topbar-h,74px) + var(--cpd-h,0px));z-index:2;height:32px;
  background:var(--surface);border-bottom-color:var(--line);font-size:11.5px;font-weight:600;color:var(--faint);text-align:left}
.stb-t .num{text-align:right}
.stb-t .vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.stb-th{display:inline-flex;align-items:center;gap:4px;max-width:100%;background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer}
.stb-th:hover,.stb-th.on{color:var(--text)}
.stb-th .ar{font-family:var(--mono)}
.stb-th:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.stb-t tbody:last-child tr:last-child td{border-bottom:0}
/* 列宽：用户一列不定宽，吃剩下的；其余定宽。 */
.stb-t .c-acct{width:224px} .stb-t .c-tier{width:96px} .stb-t .c-exp{width:104px} .stb-t .c-sh{width:116px}
.stb-t .c-req{width:76px} .stb-t .c-tok{width:84px} .stb-t .c-last{width:96px} .stb-t .c-use{width:104px}
.stb-t .c-st{width:176px} .stb-t .c-op{width:36px}
.stb-t .c-usd{width:96px} .stb-t .c-bal{width:96px} .stb-t .c-left{width:116px} .stb-t .c-d1{width:96px}
/* 表变窄时依次收起次要的列，每一档都按「定宽列之和 + 用户名约 200px」算门槛
   （包月 / 额度、是否按账号分组，四种表的列不一样，门槛各算各的）：
   包月：token → 7 天调用 → 档位 → 账号用量 → 最近调用 → 账号、状态两列收窄；
   按账号分组的包月：token → 7 天调用 → 状态收窄；
   额度：近 24 小时 → 7 天调用 → 最近调用 → 7 天扣额度 → 账号、状态收窄 → 预计用完；
   按账号分组的额度：近 24 小时 → 7 天调用 → 最近调用 → 7 天扣额度 → 状态收窄。
   收成零宽而不是 display:none：组头那一格的 colspan 是写死的列数，少一列它就多出一个空列，
   正文各行会在表格右侧提前结束。只在表的那一段（≥ 721px）收；再窄是紧凑记录，另有规则。
   账号一列收窄到 150px 时不再画档位标记，留给账号名。 */
@container stw (min-width:721px) and (max-width:1333px){.stb-t.a:not(.g-acct) .c-d1{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.a:not(.g-acct) .c-d1>*{display:none}}
@container stw (min-width:721px) and (max-width:1313px){.stb-t.m:not(.g-acct) .c-tok{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.m:not(.g-acct) .c-tok>*{display:none}}
@container stw (min-width:721px) and (max-width:1243px){.stb-t.a:not(.g-acct) .c-req{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.a:not(.g-acct) .c-req>*{display:none}}
@container stw (min-width:721px) and (max-width:1233px){.stb-t.m:not(.g-acct) .c-req{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.m:not(.g-acct) .c-req>*{display:none}}
@container stw (min-width:721px) and (max-width:1163px){.stb-t.a:not(.g-acct) .c-last{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.a:not(.g-acct) .c-last>*{display:none}}
@container stw (min-width:721px) and (max-width:1153px){.stb-t.m:not(.g-acct) .c-tier{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.m:not(.g-acct) .c-tier>*{display:none}}
@container stw (min-width:721px) and (max-width:1109px){.stb-t.a.g-acct .c-d1{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.a.g-acct .c-d1>*{display:none}}
@container stw (min-width:721px) and (max-width:1073px){.stb-t.a:not(.g-acct) .c-usd{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.a:not(.g-acct) .c-usd>*{display:none}}
@container stw (min-width:721px) and (max-width:1063px){.stb-t.m:not(.g-acct) .c-use{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.m:not(.g-acct) .c-use>*{display:none}}
@container stw (min-width:721px) and (max-width:1019px){.stb-t.a.g-acct .c-req{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.a.g-acct .c-req>*{display:none}}
@container stw (min-width:721px) and (max-width:949px){
  .stb-t.m.g-acct .c-tok,.stb-t.a.g-acct .c-last{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}
  .stb-t.m.g-acct .c-tok>*,.stb-t.a.g-acct .c-last>*{display:none}}
@container stw (min-width:721px) and (max-width:973px){
  .stb-t.a:not(.g-acct) .c-acct{width:150px}.stb-t.a:not(.g-acct) .c-st{width:140px}.stb-t.a:not(.g-acct) .c-acct .pv-plan{display:none}}
@container stw (min-width:721px) and (max-width:953px){.stb-t.m:not(.g-acct) .c-last{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}.stb-t.m:not(.g-acct) .c-last>*{display:none}}
@container stw (min-width:721px) and (max-width:859px){
  .stb-t.m.g-acct .c-req,.stb-t.a.g-acct .c-usd,.stb-t.a:not(.g-acct) .c-left{width:0;padding:0;border-bottom-width:0;font-size:0;line-height:0;overflow:hidden}
  .stb-t.m.g-acct .c-req>*,.stb-t.a.g-acct .c-usd>*,.stb-t.a:not(.g-acct) .c-left>*{display:none}}
@container stw (min-width:721px) and (max-width:863px){.stb-t.m:not(.g-acct) .c-acct{width:150px}.stb-t.m:not(.g-acct) .c-st{width:140px}.stb-t.m:not(.g-acct) .c-acct .pv-plan{display:none}
  .stb-t.m:not(.g-acct) .stb-pre{display:none}}
@container stw (min-width:721px) and (max-width:759px){.stb-t.g-acct .c-st{width:140px}}

/* 分组标题行。按账号分组时每一行是同一套网格，几个账号的油表落在同一条竖线上；
   表窄了退回自动折行。 */
.stb-gh td{height:auto;padding:0;background:var(--surface-2);color:var(--text);white-space:normal;overflow:visible}
.stb-t tbody.stb-grp+tbody.stb-grp .stb-gh td{border-top:1px solid var(--line)}
.stb-ghd{display:flex;align-items:center;gap:6px 14px;flex-wrap:wrap;min-height:40px;padding:6px 12px 6px 8px;
  box-sizing:border-box;font-size:12.5px}
.stb-ghd.acct{display:grid;grid-template-columns:22px 9px minmax(160px,1fr) 210px minmax(260px,330px) 200px 28px;gap:6px 12px}
/* 放不下网格时退成一行流：档位和名额紧跟账号名，油表靠右。账号名不再撑满，否则多一格 Fable 油表的那一行
   会被挤成左对齐，和别的组头对不齐。 */
@container stw (max-width:1120px){.stb-ghd.acct{display:flex}.stb-ghd.acct .stb-gt{display:none}
  .stb-ghd.acct .stb-gn{flex:0 1 auto}.stb-ghd.acct .stb-gauges{margin-left:auto}}
/* 按用户分组的组头：7 天调用和 7 天 token 两列可见时，该用户的总计直接对齐在这两列正上方，
   下面的明细行展示各个账号上独立消耗的 7 天量；窄屏收起这两列时退回右侧文字。 */
.stb-gu{display:none;padding:0 10px;text-align:right;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
@container stw (min-width:1234px){
  .stb-ghd.user{display:grid;grid-template-columns:22px minmax(0,1fr) 76px 84px 412px;gap:0;padding:6px 0 6px 8px}
  .stb-ghd.user>.stb-gn{padding:0 10px 0 6px}
  .stb-ghd.user>.stb-gu{display:block}
  .stb-ghd.user>.stb-gf{justify-self:end;padding:0 12px 0 10px;white-space:nowrap}
  .stb-ghd.user .stb-gu-inline{display:none}
}
@container stw (min-width:1234px) and (max-width:1313px){
  .stb-ghd.user{grid-template-columns:22px minmax(0,1fr) 76px 0px 412px}
}
.stb-fold{display:inline-grid;place-items:center;width:22px;height:22px;flex:none;border-radius:6px;background:none;color:var(--faint)}
.stb-fold svg{width:9px;height:9px;transition:transform .2s ease}
.stb-fold[aria-expanded="true"] svg{transform:rotate(90deg)}
.stb-fold:hover{background:var(--fill-3);color:var(--text)}
.stb-fold:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.stb-grp.fold .stb-row{display:none}
.stb-gn{display:inline-flex;align-items:center;gap:8px;min-width:0;flex:1 1 240px}
.stb-gnm{min-width:0;max-width:34ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:none;border:0;padding:0;
  font:inherit;font-weight:600;color:var(--text);cursor:pointer}
.stb-gnm:hover{text-decoration:underline;text-underline-offset:2px}
.stb-gnm:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.stb-gsub{min-width:0;font-family:var(--mono);font-size:11.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stb-gtt{font-weight:600;color:var(--text);white-space:nowrap}
.stb-gf{display:inline-flex;align-items:center;gap:4px 14px;flex-wrap:wrap;min-width:0;color:var(--dim);font-size:12px}
.stb-gf b,.stb-gt b{font-family:var(--mono);font-weight:600;color:var(--text)}
.stb-gt{color:var(--faint);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stb-gt .err{color:var(--bad)}
.stb-gnote{flex:none;font-size:11px;font-weight:600;border-radius:999px;padding:1px 8px;background:var(--surface-3);color:var(--dim);white-space:nowrap}
.stb-gnote.bad{color:var(--bad);background:var(--bad-a12)}
.stb-multi{flex:none;font-size:11px;font-weight:600;border-radius:999px;padding:1px 8px;color:var(--brand-2);background:var(--brand-dim);white-space:nowrap}
.stb-bk{font-weight:600;color:var(--text)}
.stb-bk.warn{color:var(--warn)}
.stb-bk.bad{color:var(--bad)}
.stb-gauges{display:inline-flex;align-items:center;gap:6px 12px;flex-wrap:wrap;min-width:0}
.stb-g{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;white-space:nowrap}
.stb-g em{font-style:normal;color:var(--faint)}
.stb-g i{position:relative;width:40px;height:5px;border-radius:999px;background:var(--surface-3);overflow:hidden;flex:none}
.stb-g i::after{content:'';position:absolute;left:0;top:0;bottom:0;width:var(--u);border-radius:999px;background:var(--ok)}
.stb-g.warn i::after{background:var(--warn)}
.stb-g.bad i::after{background:var(--bad)}
.stb-g b{font-family:var(--mono);font-weight:600;color:var(--text)}
.stb-g.warn b{color:var(--warn)}
.stb-g.bad b{color:var(--bad)}
.stb-g.sc{opacity:.72}
.stb-g .rs{font-family:var(--mono);color:var(--faint)}
.stb-nog{font-size:11.5px;color:var(--faint)}
.stb-more{display:inline-grid;place-items:center;width:28px;height:28px;margin-left:auto;flex:none;border-radius:8px;background:none;color:var(--dim)}
.stb-more:hover{background:var(--fill-3);color:var(--text)}
.stb-more svg{width:16px;height:16px}
.stb-more:focus-visible{outline:2px solid var(--focus);outline-offset:1px}

/* 行：整行可点，动作和卡片里点那一格相同。左沿色条只给需要处理的行。 */
.stb-row{cursor:pointer}
.stb-row:hover td{background:var(--fill-2)}
.stb-row:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.stb-row td:first-child{border-left:2px solid transparent}
.stb-row.warn td:first-child{border-left-color:var(--warn)}
.stb-row.bad td:first-child{border-left-color:var(--bad)}
#seat-board .stb-row[hidden],#seat-board .stb-grp[hidden]{display:none}
.stb-who{display:flex;align-items:center;gap:6px;min-width:0;color:var(--text)}
.stb-who b{min-width:0;flex:0 1 auto;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.stb-who .memo{min-width:0;flex:0 1 auto;font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.stb-who .jls{display:inline-flex;align-items:center;gap:4px;flex:none}
.stb-who .jl{display:inline-flex;align-items:center;gap:3px;height:17px;padding:0 5px;border-radius:999px;flex:none;
  border:1px solid var(--line);background:var(--surface-3);color:var(--dim);font-size:10.5px;line-height:1;white-space:nowrap}
.stb-who .jl svg{width:11px;height:11px;flex:none}
.stb-who .jl[data-go]:hover{border-color:var(--brand);color:var(--brand);background:var(--brand-a20)}
.stb-who .jl.guest{color:var(--brand-2)}
.stb-who .jl.rides.on{color:var(--ok)}
.stb-who .jl.rides.off,.stb-who .jl.borrow{color:var(--faint)}
.stb-who.free{color:var(--faint)}
.stb-add{margin-left:6px;color:var(--brand-2);opacity:0;transition:opacity .16s ease}
.stb-row:hover .stb-add,.stb-row:focus-visible .stb-add{opacity:1}
.stb-row.k-former .stb-who b,.stb-row.k-released .stb-who b{font-weight:500;color:var(--dim)}
.stb-nil{color:var(--faint)}
.stb-tier{color:var(--dim)}
.stb-exp{display:inline-flex;align-items:baseline;gap:6px}
.stb-exp b{font-family:var(--mono);font-size:12px;font-weight:600;color:var(--text)}
.stb-exp span{font-size:11.5px;color:var(--faint)}
.stb-exp.warn b,.stb-exp.warn span{color:var(--warn)}
.stb-exp.bad,.stb-exp.bad b,.stb-exp.bad span{color:var(--bad)}
.stb-sh{display:inline-flex;align-items:center;gap:8px;width:100%}
.stb-sh i{position:relative;flex:1 1 auto;max-width:54px;height:5px;border-radius:999px;background:var(--surface-3);overflow:hidden}
.stb-sh i::after{content:'';position:absolute;left:0;top:0;bottom:0;width:var(--u);border-radius:999px;background:var(--ok)}
.stb-sh b{min-width:4ch;font-family:var(--mono);font-size:12px;font-weight:600;color:var(--text);text-align:right}
.stb-sh.warn i::after{background:var(--warn)}
.stb-sh.warn b{color:var(--warn)}
.stb-sh.bad i::after{background:var(--bad)}
.stb-sh.bad b{color:var(--bad)}
.stb-sh.use i::after{background:var(--dim)}
.stb-sh.use.warn i::after{background:var(--warn)}
.stb-sh.use.bad i::after{background:var(--bad)}
.stb-last.live{color:var(--ok)}
.stb-last.stale{color:var(--warn)}
.stb-bal{font-family:var(--mono);font-size:12.5px;font-weight:600;color:var(--text)}
.stb-bal.warn{color:var(--warn)}
.stb-bal.bad{color:var(--bad)}
.stb-left{display:inline-flex;align-items:baseline;gap:6px}
.stb-left span{font-size:11.5px;color:var(--faint)}
.stb-left.warn{color:var(--warn)}
.stb-left.bad{color:var(--bad)}
.stb-acl{display:flex;align-items:center;gap:7px;min-width:0}
.stb-acn{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:none;border:0;padding:0;font:inherit;color:var(--dim);cursor:pointer}
.stb-acn:hover{color:var(--text);text-decoration:underline;text-underline-offset:2px}
.stb-acl .pv-plan{margin-left:auto}
.stb-pill{display:inline-block;margin-right:4px;font-size:11px;font-weight:600;border-radius:999px;padding:1px 8px;
  background:var(--surface-2);color:var(--dim);white-space:nowrap}
.stb-pill.warn{color:var(--warn);background:var(--warn-a12)}
.stb-pill.bad{color:var(--bad);background:var(--bad-a12)}
.stb-pill.acc{color:var(--brand-2);background:var(--brand-dim)}
.stb-pill.more{color:var(--faint);background:none;padding:1px 2px}
.stb-ok{font-size:11.5px;color:var(--faint)}
.stb-ast{display:none;flex:none;font-size:11.5px;font-weight:600;color:var(--dim)}
.stb-ast.bad{color:var(--bad)}
.stb-go{display:inline-grid;place-items:center;color:var(--faint)}
.stb-go svg{width:12px;height:12px}
.stb-row:hover .stb-go{color:var(--text)}
.stb-t tr.flash td{animation:stFlash 1.6s ease-out}
@keyframes stFlash{0%,40%{background:var(--brand-a20)}100%{background:transparent}}
@media (prefers-reduced-motion:reduce){.stb-t tr.flash td{animation:none;background:var(--brand-a20)}.stb-fold svg{transition:none}}

/* 窄（容器 ≤ 720px：手机，或开着侧栏的窄窗口）：表头收起，每一行改成一条紧凑记录。
   第一行：用户 · 状态胶囊 · 箭头；第二行：账号和档位（按账号分组时组头已经说了，不画）；
   第三行：几格读数，小字列名印在读数前面（data-l）。只留最常看的几格：包月是到期、份额、
   最近调用，额度是余额、够用多久、占限额；按哪一项排序，那一格也画出来（表上的 s-<键>），
   已解绑的人留着在这个账号上扣过多少。没有读数（ST_NONE）的格子整格不画。
   <tr> 是一条可换行的 flex，两个零高的伪元素各占一整行当换行；行距写在格子的 margin-top 上，
   不用 row-gap（零高的换行占位也会吃一份 row-gap）。
   工具条：分类芯片一行横滑，分组 / 排序 / 份额窗口在下面一行铺满。 */
@container stw (max-width:720px){
  .stb-bar{gap:10px;margin-bottom:10px}
  .stb-chips{flex:1 1 100%;flex-wrap:nowrap;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;
    margin:0 -12px;padding:2px 12px}
  .stb-chips::-webkit-scrollbar{display:none}
  .stb-chip{flex:none;height:32px}
  .stb-ctl{flex:1 1 100%;margin-left:0;gap:8px}
  #v-seats .stb-ctl .seat-sort{flex:1 1 128px;min-width:0}
  #v-seats .stb-ctl .seat-sort select{width:100%}

  .stb-t,.stb-t tbody,.stb-t tr,.stb-t td{display:block}
  .stb-t thead{display:none}
  .stb-t td{height:auto}
  .stb-t tr.stb-row{display:flex;flex-wrap:wrap;align-items:center;column-gap:10px;padding:11px 12px 11px 14px;
    border-bottom:1px solid var(--line-soft)}
  .stb-t tbody:last-child tr.stb-row:last-child{border-bottom:0}
  .stb-t tr.stb-row td{display:flex;align-items:center;gap:5px;width:auto;min-width:0;padding:0;border:0;
    background:none;overflow:visible;text-overflow:clip;font-size:12px}
  .stb-row:hover{background:var(--fill-2)}
  .stb-row.warn{box-shadow:inset 3px 0 0 var(--warn)}
  .stb-row.bad{box-shadow:inset 3px 0 0 var(--bad)}
  .stb-t tr.flash{animation:stFlash 1.6s ease-out}
  .stb-t tr.flash td{animation:none}
  @media (prefers-reduced-motion:reduce){.stb-t tr.flash{animation:none;background:var(--brand-a20)}}
  .stb-t tr.stb-row::before{content:"";order:4;flex:0 0 100%;height:0}
  .stb-t tr.stb-row::after{content:"";order:7;flex:0 0 100%;height:0}
  .stb-t.g-acct tr.stb-row::after{display:none}

  .stb-t tr.stb-row td.c-who{order:1;flex:1 1 0;font-size:12.5px}
  .stb-t tr.stb-row td.c-st{order:2;flex:none;max-width:60%;overflow:hidden}
  .stb-t tr.stb-row td.c-op{order:3;flex:none}
  .stb-t tr.stb-row td.c-acct{order:5;flex:0 1 auto;margin-top:6px}
  .stb-t tr.stb-row td.c-tier{order:6;flex:none;margin-top:6px}
  .stb-t tr.stb-row td:is(.c-exp,.c-sh,.c-req,.c-tok,.c-last,.c-use,.c-usd,.c-bal,.c-left,.c-d1){order:8;flex:none;margin-top:6px;margin-right:6px}
  /* 读数的先后：包月先到期、份额；额度先余额、够用多久。 */
  .stb-t.m tr.stb-row td.c-sh{order:9} .stb-t.m tr.stb-row td.c-last{order:10} .stb-t.m tr.stb-row td.c-req{order:11}
  .stb-t.m tr.stb-row td.c-tok{order:12} .stb-t.m tr.stb-row td.c-use{order:13}
  .stb-t.a tr.stb-row td.c-left{order:9} .stb-t.a tr.stb-row td.c-sh{order:10} .stb-t.a tr.stb-row td.c-usd{order:11}
  .stb-t.a tr.stb-row td.c-last{order:12} .stb-t.a tr.stb-row td.c-d1{order:13} .stb-t.a tr.stb-row td.c-req{order:14}
  .stb-t tr.stb-row td[data-l]::before{content:attr(data-l);flex:none;font-size:11px;color:var(--faint)}
  .stb-who b{font-size:13.5px}
  .stb-who .memo{flex:0 100 auto;font-size:11.5px}
  .stb-ast{display:inline}
  .stb-t:not(.g-acct) .stb-pill.f-acct,.stb-t .stb-pre{display:none}
  .stb-add{opacity:1}
  .stb-ok{display:none}
  .stb-sh{width:auto;gap:6px}
  .stb-sh i{flex:none;width:40px}
  .stb-sh b{min-width:0}
  .stb-left span{display:none}
  /* 账号名在紧凑记录里也能单独点（打开账号工作台），点的范围上下各多 6px。 */
  .stb-acn{padding:6px 0;margin:-6px 0;font-size:12px}
  .stb-go svg{width:14px;height:14px}

  .stb-t tr.stb-row :is(td.c-req,td.c-tok,td.c-use,td.c-usd,td.c-d1){display:none}
  .stb-t.a tr.stb-row td.c-last{display:none}
  .stb-t.s-req tr.stb-row td.c-req,.stb-t.s-tok tr.stb-row td.c-tok,.stb-t.s-use tr.stb-row td.c-use,
  .stb-t.s-usd7 tr.stb-row td.c-usd,.stb-t.s-d1 tr.stb-row td.c-d1,.stb-t.s-last tr.stb-row td.c-last,
  .stb-t.a tr.k-former td.c-usd{display:flex}
  .stb-t tr.stb-row td:has(>.stb-nil.none),.stb-t tr.stb-row td.c-left:has(>.stb-left.bad){display:none}
  .stb-t tr.stb-row td.c-exp:has(>.stb-exp.gone)::before{display:none}

  /* 组头：账号那一格折成三行——账号名和菜单；内部名、档位标记、档位与分配；几根限额读数。
     收起按钮和状态灯挂在左边的空白里，三行都和账号名对齐。账号名和分配那两块拆开排
     （display:contents），内部名和「已分配 1 / 1」才排得进同一行。 */
  .stb-ghd{position:relative;padding:8px 10px 8px 40px;gap:4px 10px}
  .stb-ghd>.stb-fold{position:absolute;left:6px;top:6px}
  .stb-fold{width:28px;height:28px}
  .stb-more{width:32px;height:32px}
  .stb-ghd.acct{display:flex;flex-wrap:wrap;align-items:center;position:relative;gap:0 8px;padding:10px 6px 10px 52px}
  .stb-ghd.acct>.stb-fold{top:7px}
  .stb-ghd.acct>.lamp{position:absolute;left:37px;top:17px}
  .stb-ghd.acct>.stb-gn,.stb-ghd.acct>.stb-gf{display:contents}
  .stb-ghd.acct .stb-gnm{order:1;flex:1 1 0;max-width:none;font-size:13px;text-align:left;padding:6px 0;margin:-6px 0}
  .stb-ghd.acct>.stb-more{order:2;margin:-6px 0}
  .stb-ghd.acct::before{content:"";order:3;flex:0 0 100%;height:0}
  .stb-ghd.acct>.stb-gn>:not(.stb-gnm),.stb-ghd.acct>.stb-gf>*{order:4;margin-top:5px}
  .stb-ghd.acct::after{content:"";order:5;flex:0 0 100%;height:0}
  .stb-ghd.acct>.stb-gauges{order:6;flex:1 1 100%;margin:7px 0 0;gap:5px 16px}
}
/* 大屏手机横放、平板分屏（520–720px）：读数那一行还排得下一格，包月补 7 天调用，额度补 7 天扣额度。 */
@container stw (min-width:520px) and (max-width:720px){
  .stb-t.m tr.stb-row td.c-req,.stb-t.a tr.stb-row td.c-usd{display:flex}
  .stb-t tr.stb-row td:has(>.stb-nil.none){display:none}
}

/* ── 折叠块：把二级事实压成一行 ──────────────────────────────────────
   持卡客户不是这一页的活，但不能藏——它说「谁的额度见底了」。所以折叠头
   自己要把答案说完：读完那一行还想看证据的人才展开。真出事时（有人透支）
   它自己弹开并染色，
   因为折叠只准省掉「一切正常」这四个字，不准省掉一次告警。 */
.cpf{margin-bottom:14px}
.cpf-hd{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding-right:16px}
.cpf-tap{display:flex;align-items:center;gap:10px;min-width:0;flex-wrap:wrap;
  padding:14px 4px 14px 18px;background:none;text-align:left;color:var(--dim);font-size:12.5px}
.cpf-tap b{font-family:var(--disp);font-size:14.5px;font-weight:600;letter-spacing:-.01em;color:var(--text)}
.cpf-tap:hover b{color:var(--brand-2)}
.cpf-tap:focus-visible{outline:2px solid var(--focus);outline-offset:-2px;border-radius:8px}
.cpf-tap .cv{width:10px;height:10px;flex:none;color:var(--faint);transition:transform .2s ease}
.cpf.on .cpf-tap .cv{transform:rotate(90deg)}
.cpf.on .cpf-hd{border-bottom:1px solid var(--line-soft)}
.cpf-sum{font-size:12px;color:var(--faint)}
.cpf-sum .n{font-family:var(--mono);font-weight:600;color:var(--dim);font-variant-numeric:tabular-nums}
.cpf-alerts{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.cpf-act{margin-left:auto;display:flex;align-items:center;gap:8px}
.cpf-alert{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;
  font-size:11.5px;font-weight:600}
.cpf-alert.warn{color:var(--warn);background:var(--warn-a12)}
.cpf-alert.bad{color:var(--bad);background:var(--bad-a11)}
.cpf.attn{border-color:var(--bad-42-on-line)}

/* 水位不再是一块板：一池一行，紧跟在调度条下面。它答的是「车里还有没有油」，
   一行就答得完；告警线那格搬进了设置抽屉——一个很少动的数字不值一整块表单。 */
/* 额度账号分配方式面板：内边距与持有额度的用户那块折叠面板一致。 */
.asch-body{padding:16px 18px 18px;display:flex;flex-direction:column;gap:18px}
.asch-modes{max-width:760px;border-radius:14px}
.asch-modes button{display:flex;flex-direction:column;align-items:flex-start;gap:3px;text-align:left;padding:9px 12px;border-radius:11px}
.asch-modes button span{font-size:11.5px;color:var(--dim);font-weight:400;line-height:1.5}
.asch-sec h4{margin:0 0 8px;font-size:12.5px;font-weight:600;color:var(--dim)}
.asch-outlook{display:flex;flex-wrap:wrap;gap:8px}
.asch-outlook+.asch-outlook{margin-top:8px}
.asch-ol{padding:7px 12px;border:1px solid var(--line);border-radius:10px;font-size:12.5px;color:var(--text)}
.asch-ol.warn{border-color:var(--warn-36-on-line);color:var(--warn)}
.asch-ol.bad{border-color:var(--bad-42-on-line);color:var(--bad)}
.asch-scroll{overflow-x:auto}
.asch-tbl th,.asch-tbl td{padding:8px 12px;font-size:12.5px;white-space:nowrap}
.asch-tbl tr.asch-differ td{background:var(--warn-a12)}
.asch-basis summary{cursor:pointer;font-size:12.5px;font-weight:600;color:var(--dim)}
.asch-basis[open] summary{margin-bottom:10px}
.asch-basis .asch-scroll+.asch-scroll{margin-top:12px}
@media (max-width:720px){.asch-modes{flex-direction:column}.asch-body{padding:14px 14px 16px}
  .asch-panel .cpf-alerts{padding:0 0 12px 18px}}

/* ── 运营设置抽屉 ────────────────────────────────────────────────────
   判据 / 牌价 / 告警线 / 借用余量，四样东西原来摞在车队板底下，量过一次占着
   整页近四成高度，而它们都是配一次就不动的格子。搬进这扇门之后仍然各存各的：
   四个独立的决定，一个统一的保存按钮会让改一个数的人顺手把另外三个也提交一遍。 */
.cpo-lead{font-size:12px;color:var(--faint);line-height:1.7;margin:0 0 22px}
.cpo-sec+.cpo-sec{margin-top:26px;padding-top:24px;border-top:1px solid var(--line)}
.cpo-hd{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:10px}
.cpo-hd b{font-family:var(--disp);font-size:14.5px;font-weight:600;letter-spacing:-.01em;color:var(--text)}
.cpo-hd .faint{margin-left:auto;font-size:11.5px;color:var(--faint)}
.cpo-p{font-size:12px;color:var(--faint);line-height:1.7;margin:0 0 16px}
.cpo-p b{color:var(--dim);font-weight:600}
.cpo-sub{margin-top:24px;padding-top:20px;border-top:1px dashed var(--line)}
.cpo-sub .cpo-hd b{font-size:13px}
.cpo-wv{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 0;
  border-top:1px solid var(--line-soft);font-size:12.5px}
.cpo-wv .btn{margin-left:auto}
.cpo-wv.none{color:var(--faint);font-size:12px}
/* 牌价在抽屉里竖起来：¥ 付款 → $ 记账 → $ 额度，一段一段往下读。横排是因为
   原来有一整页宽；500px 里横排会把每一站压成一列挤字。 */
.cpo-stn{display:flex;align-items:baseline;gap:12px;padding:12px 15px;
  border:1px solid var(--line);border-radius:11px;background:var(--surface-2)}
.cpo-stn .l{font-size:11.5px;color:var(--faint);width:3.4em;flex:none}
.cpo-stn .v{font-family:var(--mono);font-size:18px;font-weight:600;color:var(--text);
  font-variant-numeric:tabular-nums;line-height:1.2}
.cpo-stn .s{font-size:11px;color:var(--faint);margin-left:auto;text-align:right;line-height:1.5}
.cpo-stn.on{border-color:var(--brand-a60);background:var(--brand-dim)}
.cpo-stn.on .v{color:var(--brand-2)}
.cpo-hop{position:relative;display:flex;align-items:center;gap:10px;padding:9px 0 9px 24px;min-height:34px}
.cpo-hop::before{content:'';position:absolute;left:11px;top:0;bottom:0;width:1px;background:var(--line)}
.cpo-hop .op{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);font-size:13px;color:var(--dim)}
.cpo-hop .op input{width:70px;padding:6px 10px;font-family:var(--mono);font-size:13px;
  border:1px solid var(--input-line);border-radius:8px;background:var(--input-bg);color:var(--text)}
.cpo-hop .op input:focus{outline:2px solid var(--focus);outline-offset:1px}
.cpo-hop .why{font-size:11px;color:var(--faint)}
.cpo-chips{display:flex;flex-wrap:wrap;gap:9px;margin-top:16px}
/* 页签正文和它底下的判据之间留一格。最后一辆车自己的 26px 就是这一格，所以
   车队里的最后一个池把自己的下边距交出来，免得叠成两格。 */
.cp-tabbody{margin-bottom:26px}
.cp-tabbody>.cp-pool:last-child{margin-bottom:0}

/* 一个池都没有的那一屏：问题不是板是空的，是不知道有两种卖法。所以它是两张
   并排的说明卡，各自带着自己那个建起来的按钮，而不是一句「暂无数据」。 */
.cp-firstrun .cp-fr-t{font-family:var(--disp);font-size:16px;font-weight:600;letter-spacing:-.01em}
.cp-firstrun .cp-fr-s{font-size:12.5px;color:var(--faint);line-height:1.65;margin:6px 0 16px;max-width:70ch}
.cp-fr-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:14px}
.cp-fr-card{border:1px solid var(--line);border-radius:12px;padding:15px 16px 16px;background:var(--surface-2)}
.cp-fr-hd{display:flex;align-items:center;gap:9px;font-size:13.5px;font-weight:600;color:var(--text)}
.cp-fr-hd .u{font-family:var(--mono);font-size:12.5px;font-weight:600;line-height:1;
  width:22px;height:22px;display:inline-grid;place-items:center;border-radius:7px;
  color:var(--brand-2);background:var(--brand-a15)}
.cp-fr-card p{font-size:12px;color:var(--faint);line-height:1.65;margin:9px 0 13px}
/* 号池水位挂在号池名字后面（2026-10-14 前是名字上方单独一块带边框的卡，加一条
   整宽的告警横幅，约 110px 高）：读数、一根带告警刻度的短水位条、可用 / 限流暂停，
   告警是同一行末尾的胶囊。 */
.cp-poolrow{display:flex;align-items:center;gap:8px 18px;flex-wrap:wrap}
.phr{display:inline-flex;align-items:center;gap:8px;white-space:nowrap;font-size:12px;color:var(--dim)}
.phr .k{font-size:11.5px;color:var(--faint)}
.phr b{font-family:var(--mono);font-size:15px;font-weight:600;line-height:1;font-variant-numeric:tabular-nums}
.phr.ok b{color:var(--ok)} .phr.warn b{color:var(--warn)} .phr.bad b{color:var(--bad)}
.phr .of{font-family:var(--mono);font-size:12px;color:var(--faint);margin-left:-3px}
.phr-gauge{position:relative;width:132px;height:6px;border-radius:999px;background:var(--fill-3);flex:none}
.phr-fill{position:absolute;left:0;top:0;bottom:0;border-radius:999px;background:var(--ok);transition:width .3s ease}
.phr.warn .phr-fill{background:var(--warn)} .phr.bad .phr-fill{background:var(--bad)}
.phr-tick{position:absolute;top:-3px;bottom:-3px;width:2px;border-radius:1px;background:var(--dim);opacity:.55}
.phr-facts{font-size:12px;color:var(--faint)}
.phr-pill{display:inline-flex;align-items:center;gap:6px;padding:4px 11px;border-radius:999px;font-size:12px;font-weight:500;line-height:1.35}
.phr-pill::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.phr-pill.warn{color:var(--warn);background:var(--warn-a12)}
.phr-pill.bad{color:var(--bad);background:var(--bad-a11)}
/* 别处（转发测试、飞书事件抽屉）用来放错误句子的提示条。 */
.hr-note{margin:7px 0 3px;padding:8px 12px;border-radius:9px;font-size:12px;line-height:1.55}
.hr-note.bad{color:var(--bad);background:var(--bad-a11)}
.hr-note.warn{color:var(--warn);background:var(--warn-a12)}
/* 对不上的人：只在真有东西的时候出现，所以它长得像一条待办清单而不是一块面板。
   用 warn 而不是 bad —— 这些不是故障，是需要有人做个决定的状态。
   （待办清单也是面板：内容自带内边距，别贴着边框写字。） */
.drift-panel{margin-bottom:24px;border-color:var(--warn-a50,var(--line));padding:15px 18px 7px}
.dr-hd{display:flex;align-items:center;gap:9px;font-size:13.5px;font-weight:600;color:var(--warn)}
.dr-n{font-family:var(--mono);font-size:11px;padding:1px 7px;border-radius:6px;
  color:var(--warn);background:var(--warn-a12,var(--surface-3))}
.dr-b{font-size:12px;color:var(--faint);line-height:1.6;margin:7px 0 11px}
.dr-row{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;flex-wrap:wrap;
  padding:12px 0;border-top:1px solid var(--line);font-size:12.5px;color:var(--dim);line-height:1.6}
.dr-row b{color:var(--text)}
.dr-act{display:flex;gap:7px;flex:none;align-self:center}

/* ---- 发票：抄进税务系统的那四个字段 ---------------------------------------
   这一段全是为了「看得清、抄得动」。开票的人手里是另一个系统的表单，他要把
   抬头、税号、金额、邮箱一个一个搬过去；在队列那一行里它们是三行 11px 的灰
   字挤在备注列，抄错一位就是一张作废的票。所以抽屉里每个值单独一行、等宽、
   14px 起、点一下即复制，行与行之间留够呼吸的空——这一屏的密度是刻意低的。 */
.inv-kv{display:flex;flex-direction:column;gap:10px;margin:0 0 20px}
.inv-kv .r{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:12px 14px;border:1px solid var(--line);border-radius:11px;background:var(--surface-2)}
.inv-kv .r.hi{border-color:var(--brand-32-on-line);background:var(--brand-dim)}
.inv-kv .k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;
  color:var(--dim);flex:none}
.inv-kv .v{font-family:var(--mono);font-size:14px;line-height:1.5;color:var(--text);
  text-align:right;word-break:break-all;user-select:all;cursor:copy}
.inv-kv .v.lg{font-size:17px;font-weight:600}
.inv-kv .v:hover{color:var(--brand-2)}
 /* 「还没填」用 .none 而不是 .empty —— 全局 .empty 是表格的空态插画（一根
    虚线加两个圆点），套在一个 22px 的格子上会撑出一个 147px 高的空盒子。 */
.inv-kv .v.none{color:var(--faint);cursor:default;user-select:none}
/* 这一屏有两个按钮在条件不满足时是禁用的（没选文件、没有文件可发）。全局没有
   禁用态样式，不给一个就会出现一个看起来能点、点了没反应的主按钮。 */
.inv-sec .btn[disabled]{opacity:.45;cursor:not-allowed}
.inv-sec{margin:0 0 22px}
.inv-sec:last-child{margin-bottom:4px}
.inv-file{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:11px 13px;border:1px solid var(--line);border-radius:11px;background:var(--surface-2);margin-bottom:9px}
.inv-file .nm{font-size:12.5px;color:var(--text);word-break:break-all}
.inv-file .mt{font-size:11px;color:var(--faint);margin-top:3px}
.inv-drop{border:1px dashed var(--line);border-radius:12px;padding:18px 16px;text-align:center;
  background:var(--surface-2);transition:.15s;cursor:pointer}
.inv-drop:hover,.inv-drop.over{border-color:var(--brand-a45);background:var(--brand-dim)}
.inv-drop .big{font-size:12.5px;color:var(--text)}
.inv-drop .sm{font-size:11.5px;color:var(--faint);margin-top:6px;line-height:1.6}
.inv-picked{margin-top:12px;font-size:12.5px;color:var(--text);display:flex;align-items:center;
  justify-content:space-between;gap:12px;padding:10px 12px;border-radius:10px;background:var(--surface-3)}
/* 收款队列一行只有一行字高：每一格都是一个短事实，竖着摞只会把行撑到 80px。
   横向挤不下时谁先让，按格定死——
     用户格：备注先缩，名字后缩；
     发票格：备注先缩，抬头后缩；
     状态格：运营备注先缩，徽章不缩。
   宽度封顶是为了让「状态」和「操作」两列永远在同一个位置。 */
#topups td{padding-top:8px;padding-bottom:8px;height:45px}
/* 一行排开的 flex 格会按整段文字宽度向表格要最小宽度，几格加起来就把表撑出面板，
   最右边的退款槽被裁掉。用户格和发票格改成 max-width:0 + 百分比：列宽由表格分配，
   里面的文字按分到的宽度省略，其余几列按内容定宽、永远不让。 */
#topups td.tp-fit{max-width:0}
#topups td.tp-fit-who{width:33%}
#topups td.tp-fit-inv{width:23%}
.tp-who{display:flex;align-items:center;gap:6px;max-width:100%;min-width:0;font-size:12.5px;color:var(--text);white-space:nowrap}
.tp-uname{font-weight:600;cursor:pointer;flex:0 1 auto;min-width:48px;overflow:hidden;text-overflow:ellipsis;transition:color .14s}
.tp-uname:hover{color:var(--brand-2)}
.tp-who .tp-note{flex:0 100 auto;min-width:30px;max-width:160px}
.tp-who .badge{flex:none;padding:1px 7px;font-size:11px}
/* 没有颜色的徽章（对公、专票）补一层浅底，不然像一个漏排的字。桌面、手机卡片和入账确认窗一样。 */
#topups .badge:not(.on):not(.open):not(.half_open),.tpd-who .badge{background:var(--fill-3);color:var(--dim)}
.tp-amt{white-space:nowrap}
.tp-amt .faint{font-size:11px;margin-left:6px}
.tp-inv{display:flex;align-items:baseline;gap:8px;max-width:100%;min-width:0;white-space:nowrap}
.tp-inv .t{font-size:12.5px;color:var(--text);flex:0 1 auto;min-width:60px;overflow:hidden;text-overflow:ellipsis}
.tp-inv .n{font-size:11.5px;color:var(--faint);flex:0 100 auto;min-width:40px;overflow:hidden;text-overflow:ellipsis}
.tp-cut{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tp-st{display:flex;align-items:center;gap:6px;white-space:nowrap}
.tp-st .badge{flex:none}
.tp-memo{font-size:11px;flex:0 1 auto;min-width:24px;max-width:80px;overflow:hidden;text-overflow:ellipsis}
.tp-time{white-space:nowrap}
.tp-rail{display:inline-flex;align-items:center;gap:6px;vertical-align:middle}
.tp-rail .btn.sm{padding:4px 11px;font-size:12.5px}
.tp-slot{display:inline-flex;flex:none;width:28px;height:28px}
.tp-rail .btn.icon{width:28px;height:28px;border-radius:7px}
.tp-rail .btn.icon svg{width:15px;height:15px}
.btn.ghost.icon.tp-inv-need{color:var(--brand-2);background:var(--brand-dim);border-color:var(--brand-a45)}
.btn.ghost.icon.tp-inv-send{color:var(--warn);background:var(--warn-a12);border-color:transparent}
.btn.ghost.icon.tp-refund:hover,.btn.ghost.icon.tp-refund:focus-visible{color:var(--bad);border-color:var(--bad-a55);background:var(--bad-a9)}
@media (pointer:coarse){
  .tp-slot,.tp-rail .btn.icon{width:36px;height:36px}
  .tp-rail .btn.sm{padding:8px 13px}
}
/* 飞书链接带 ?topup= 进来时，那一行要能被一眼认出来。背景打在 td 上：
   有的表格样式会把 tr 的背景盖掉。左边一条色条是窄屏下背景不够显的退路。 */
#topups tr.tp-focus td{background:color-mix(in srgb,var(--warn) 16%,transparent)}
#topups tr.tp-focus td:first-child{box-shadow:inset 3px 0 0 var(--warn)}
/* 只在卡片里出现的几样：金额前的 ¥（桌面列头写着）、按钮上的字（桌面靠悬停提示）、
   待付款那句 20 分钟的规则（桌面在页头说明里）。 */
#topups .tp-cy,#topups .tp-bl,#topups .tp-wait{display:none}
/* 窄屏：一笔一张卡，不横滑。横滑时「通过入账」在屏幕外，而手机上来这一页
   就是为了点它。卡片是一张两列的网格：
     名字      ¥金额
     时间      应付 ¥…
     抬头 · 备注（没有就不占行）
     状态徽章          图标
   待处理、待开票、待发送三种是待办，动作占满最后一行，次要的在左、主要的在右
   （和确认窗里「取消 / 确认」同一个方向），平板宽度下最宽 420px 靠右；
   其余只在状态那行右端留图标。
   行距写在各格的 margin-top 上，不用 row-gap：没内容的行也会吃一份 row-gap。 */
@media (max-width:860px){
  #topup-sec .panel{overflow:hidden;background:var(--surface)}
  #topup-sec .panel>table,#topups{display:block}
  #topup-sec .panel thead{display:none}
  #topups tr{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:14px;align-items:start;
    padding:14px 16px 15px;border-bottom:1px solid var(--line-soft)}
  #topups tr:last-child{border-bottom:0}
  #topups tr:hover{background:none}
  #topups td{display:block;height:auto;min-width:0;max-width:none;width:auto;padding:0;border:0;
    white-space:normal;overflow:visible;text-overflow:clip;text-align:left}
  /* 桌面给这两格写的 max-width:0 + 百分比列宽（特异性更高）在卡片里撤掉 */
  #topups td.tp-fit,#topups td.tp-fit-who,#topups td.tp-fit-inv{max-width:none;width:auto}
  #topups td.tp-fit-who{grid-area:1/1/2/2}
  #topups td.tp-amt{grid-area:1/2/3/3;text-align:right}
  #topups td.tp-time{grid-area:2/1/3/2;margin-top:3px;font-size:12px}
  #topups td.tp-fit-inv{grid-column:1/-1;grid-row:3;margin-top:9px}
  #topups td.tp-fit-inv.tp-empty{display:none}
  #topups td.tp-stc{grid-column:1/2;grid-row:4;margin-top:11px;align-self:center}
  #topups td.row-actions{grid-column:2/3;grid-row:4;margin-top:11px;align-self:center}
  #topups td.empty{grid-column:1/-1;padding:28px 12px;text-align:center}
  /* 勾选框钉在卡片左上角，卡片整体右移让出位置 */
  #topup-sec.tp-sel #topups tr{position:relative;padding-left:50px}
  #topups td.tp-pick{position:absolute;left:16px;top:15px;width:auto}
  .tp-pick input{width:20px;height:20px}
  #topups tr.tp-picked{background:var(--brand-a8)}
  #topups tr.tp-picked td{background:none}
  .tp-batch{padding:10px 12px}
  .tp-batch .tpb-act{width:100%;justify-content:flex-end}
  .tp-batch .tpb-act .btn.sm{min-height:38px;padding:8px 14px;font-size:13.5px}
  /* 名字是卡片的标题；点的范围上下各多 5px，手指才点得准 */
  #topups .tp-who{font-size:15px;gap:7px}
  #topups .tp-uname{padding:5px 0;margin:-5px 0;min-width:0}
  #topups .tp-who .tp-note{max-width:none;font-size:12px}
  #topups .tp-amt{font-family:var(--disp);font-size:17px;font-weight:600;letter-spacing:-.01em;line-height:1.3}
  #topups .tp-cy{display:inline;margin-right:1px;font-size:.82em;font-weight:500;color:var(--dim)}
  #topups .tp-amt .faint{display:block;margin:3px 0 0;font-family:var(--sans);font-size:12px;font-weight:400;letter-spacing:0;line-height:1.4}
  #topups .tp-amt .faint .tp-cy{font-size:1em;font-weight:400;color:inherit;margin:0}
  /* 抬头和备注连成一段，最多两行 */
  #topups .tp-inv{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;white-space:normal;line-height:1.5}
  #topups .tp-inv .t,#topups .tp-inv .n{display:inline;overflow:visible;min-width:0}
  #topups .tp-inv .t{font-size:13.5px}
  #topups .tp-inv .n{font-size:12.5px;margin-left:6px}
  #topups .tp-cut{white-space:normal;font-size:12.5px;line-height:1.5}
  #topups .tp-st{flex-wrap:wrap;gap:6px}
  #topups .tp-memo{max-width:none;font-size:12px;white-space:normal}
  #topups .tp-wait{display:inline;font-size:12px;color:var(--faint)}
  #topups .tp-rail{display:flex;gap:8px;justify-content:flex-end}
  #topups .tp-slot:empty{display:none}
  #topups .tp-st .tp-memo{order:9}
  /* 待处理：拒绝 / 通过入账占满最后一行 */
  #topups tr[data-st="pending"] td.row-actions{grid-column:1/-1;grid-row:5;margin-top:13px;justify-self:end;width:100%;max-width:420px}
  #topups tr[data-st="pending"] .tp-rail .btn.sm{flex:1 1 0;justify-content:center;min-height:42px;padding:9px 14px;font-size:14px}
  #topups tr[data-st="pending"] .tp-rail .btn.sm:not(.ghost){flex-grow:2}
  #topups tr[data-st="pending"] .tp-rail .btn.danger{order:-1}
  /* 待开票、待发送：发票按钮带上字占满最后一行，退款仍是状态行右端的图标。
     两个槽位拆到卡片网格的两处，所以这里把外面两层摊平（display:contents）。 */
  #topups tr[data-st="owed"] td.row-actions,#topups tr[data-st="unsent"] td.row-actions,
  #topups tr[data-st="owed"] .tp-rail,#topups tr[data-st="unsent"] .tp-rail{display:contents}
  #topups tr[data-st="owed"] .tp-slot,#topups tr[data-st="unsent"] .tp-slot{grid-column:2/3;grid-row:4;margin-top:11px;align-self:center;justify-self:end}
  #topups tr[data-st="owed"] .tp-slot:first-child,#topups tr[data-st="unsent"] .tp-slot:first-child{
    grid-column:1/-1;grid-row:5;justify-self:end;width:100%;max-width:420px;height:auto;margin-top:13px}
  #topups tr[data-st="owed"] .tp-slot:first-child .btn.icon,#topups tr[data-st="unsent"] .tp-slot:first-child .btn.icon{
    width:100%;height:42px;gap:7px;border-radius:999px;font-size:14px;font-weight:500}
  #topups tr[data-st="owed"] .tp-slot:first-child .tp-bl,#topups tr[data-st="unsent"] .tp-slot:first-child .tp-bl{display:inline}
  #topups tr[data-st="owed"] .btn.ghost.icon.tp-inv-need{background:var(--brand);color:var(--btn-ink);border-color:transparent}
  #topups tr.tp-focus{background:color-mix(in srgb,var(--warn) 12%,transparent);box-shadow:inset 3px 0 0 var(--warn)}
  #topups tr.tp-focus td{background:none}
  #topups tr.tp-focus td:first-child{box-shadow:none}
}
.cp-poolhd{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;flex-wrap:wrap;
  padding:0 2px 14px;border-bottom:1px solid var(--line);margin-bottom:18px}
/* 号池抬头的左半（名字、水位、读数）。基准 320px：右边的筛选和按钮放不下时整块换到下一行，
   左半占满整行。以前是 flex:1（基准 0），手机上右边那组约 310px 一块不让，左半被挤到 80px，
   每一格读数单独占一行，抬头 370px 高，账号卡被推到屏幕外面（2026-10-28）。 */
.cp-poolmain{flex:1 1 320px;min-width:0}
.cp-poolnm{font-family:var(--disp);font-size:17px;font-weight:600;letter-spacing:-.01em;display:flex;align-items:center;gap:9px}
.cp-facts{display:flex;align-items:center;gap:16px;flex-wrap:wrap;font-size:12.5px;color:var(--dim);margin-top:8px}
.cp-facts b{font-family:var(--mono);color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.cp-facts .warn{color:var(--warn)} .cp-facts .warn b{color:var(--warn)}
.cp-facts .idle{color:var(--faint)}
/* 名片里指向别处的名字（另一份订阅的账号卡、借用人的工作台）：按钮画成文字链接。
   格子本身只有一个点击目标，这些去处全在这里。 */
.cp-facts .go{border:0;background:none;padding:0;margin:0;font:inherit;font-family:var(--mono);font-weight:600;color:var(--brand-2);cursor:pointer;text-decoration:underline;text-underline-offset:2px}
.cp-facts .go:hover{color:var(--text)}
.cp-facts .go:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px}
/* 乘客名片：抽屉一打开先答「这是谁、他在不在开车」，然后才是「要拿他怎么办」。
   右侧那颗「客户工作台」开的是格子上那只眼睛以前开的工作台（余额 / 额度 / 请求 / 代客登录 /
   开通 Desktop，用户档案也在里面），叠在抽屉之上——看完关掉，抽屉还在原处等着动手。尺寸抄 .cp-seatcard。 */
.rc{border:1px solid var(--line);border-radius:10px;background:var(--surface-2);padding:12px 14px;margin:0 0 16px}
.rc-hd{display:flex;align-items:center;justify-content:space-between;gap:12px}
.rc-who{display:flex;align-items:baseline;gap:8px;min-width:0}
.rc-who .lbl{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim);flex:none}
.rc-who .un{font-family:var(--mono);font-size:13px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rc .cp-facts{margin-top:10px;font-size:12px}
.rc .btn.sm{flex:none} .rc .btn.sm svg{width:14px;height:14px}
.cp-act{display:flex;align-items:center;gap:8px;flex:none}

/* 车道：一条车道一种车型。车道的标牌就是那个车型的车厢俯视图 —— 车道与车里
   的座位是同一套图形，所以「这条道停的是四人车」不需要另外说一遍。 */
.cp-lane{margin-bottom:20px}
.cp-lanehd{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:0 2px 11px}
.cp-plate{display:grid;gap:2px;padding:4px;border:1px solid var(--brand-45-on-line);
  border-radius:6px;background:var(--brand-dim);flex:none}
.cp-plate i{width:5px;height:5px;border-radius:1.5px;background:var(--brand-a72)}
.cp-lanenm{font-size:13.5px;font-weight:600;white-space:nowrap}
.cp-lanehint{font-size:12px;color:var(--faint)}
.cp-tag .cp-ownprice{flex:none;margin-left:auto;font-size:11.5px;white-space:nowrap}
/* 道口标牌和道尾的账之间拉一条虚线 —— 中间那段空白原本让两头看着不相干，
   而它们说的是同一种车。顺便，这条线本身就是条车道线。 */
.cp-road{flex:1;min-width:24px;height:1px;border-top:1px dashed var(--line);opacity:.85}
.cp-price{font-family:var(--mono);font-size:12px;color:var(--brand-2);background:var(--brand-dim);
  border:1px solid var(--brand-a30);border-radius:999px;padding:2px 10px;
  cursor:pointer;transition:.15s}
.cp-price:hover{border-color:var(--brand)}
.cp-price.off{color:var(--faint);background:transparent;border-style:dashed;border-color:var(--line)}
.cp-price.off:hover{color:var(--dim);border-color:var(--dim)}
.cp-price.mixed{color:var(--warn);background:transparent;border-color:var(--warn)}
.cp-planwarn{margin:0 0 12px;padding:10px 12px;border-radius:10px;font-size:12.5px;line-height:1.6;
  color:var(--warn);background:var(--surface-2);border:1px solid var(--line)}
.cp-ledger{display:flex;align-items:center;gap:14px;flex-wrap:wrap;font-size:12px;color:var(--dim)}
.cp-ledger b{font-family:var(--mono);color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.cp-ledger .short{color:var(--warn)} .cp-ledger .short b{color:var(--warn)}
.cp-ledger .idle{color:var(--faint)}

/* 车位：卡片网格。8 人车占两格，否则四列座位会挤成一堆缩写 */
.cp-cars{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:14px}
.cp-carwrap{display:flex;flex-direction:column;gap:10px;min-width:0}
.cp-carwrap>.cp-car{flex:1}
/* 同一条道上的车一样高，车尾读数因此在一条线上 —— 卡片高度差本身不携带信息，
   而错开的车尾会让人以为它携带了。 */
.cp-car{background:var(--card-grad);border:1px solid var(--line);border-radius:15px;
  box-shadow:var(--card-shadow);overflow:hidden;position:relative;
  display:flex;flex-direction:column;
  animation:cpin .34s cubic-bezier(.22,.61,.25,1) both;animation-delay:calc(var(--i,0)*45ms)}
@keyframes cpin{from{opacity:0;transform:translateY(6px)}}
.cp-car.wide{grid-column:span 2}
/* 车况只染左沿一道：停靠 / 熔断 / 停用的车，扫一眼车队就找得到，
   而卡片里面的读数照旧是它自己的颜色，不被车况染成一片红。 */
.cp-car.down::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--bad)}
.cp-car.parked::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--warn)}
.cp-car.off{opacity:.62}
.cp-carhd{display:flex;align-items:center;gap:9px;padding:11px 14px 0}
.cp-carnm{font-family:var(--mono);font-size:13px;font-weight:600;flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cp-size{font-size:11px;font-family:var(--mono);color:var(--dim);background:var(--surface-3);
  border:1px solid var(--line);border-radius:999px;padding:2px 8px;cursor:pointer;flex:none;transition:.15s}
.cp-size:hover{color:var(--brand-2);border-color:var(--brand-55-on-line)}
.cp-size:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* 拖动手柄：把整张账号卡拖到另一个订阅池。只放在手柄上，不把整张卡设成可拖——
   卡片里的用户格本来就能拖，两种拖动叠在一张卡上会互相抢。 */
.cp-cargrip{display:inline-flex;align-items:center;justify-content:center;width:22px;height:24px;
  margin:0 -4px 0 -6px;border-radius:6px;color:var(--faint);cursor:grab;flex:none;transition:color .12s,background .12s}
.cp-car:hover .cp-cargrip{color:var(--dim)}
.cp-cargrip:hover{color:var(--text);background:var(--surface-3)}
.cp-cargrip:active{cursor:grabbing}
.cp-car.dragging{opacity:.45}
@media (hover:none){.cp-cargrip{display:none}}
/* 拖着账号卡时，能接住它的订阅池描一圈虚线，指到的那个变实线。 */
body.car-dragging #seat-board section.cp-lane[data-monthly-tier]{outline:1px dashed var(--brand-55-on-line);outline-offset:8px;border-radius:var(--r-xl)}
body.car-dragging #seat-board section.cp-lane.car-drop{outline:2px solid var(--focus);outline-offset:8px;
  background:color-mix(in srgb,var(--brand-2) 5%,transparent)}
/* 插入位置：落在这张卡的前面还是后面 */
body.car-dragging #seat-board .cp-car.car-before{box-shadow:-4px 0 0 0 var(--focus)}
body.car-dragging #seat-board .cp-car.car-after{box-shadow:4px 0 0 0 var(--focus)}
/* 铭牌：账号自己的身份。我们叫它 claude-oauth1，账单上写的是这个邮箱。 */
.cp-tag{display:flex;align-items:center;gap:8px;padding:4px 14px 0;font-size:11.5px;min-width:0}
.cp-tag .em{font-family:var(--mono);color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cp-tag .em.none{color:var(--faint);font-family:var(--sans)}
.cp-account-tools{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;margin-top:8px}
.cp-car .cp-account-tools{margin:8px 14px 0}
.cp-account-tools .badge{white-space:normal}

/* 车厢：框住的是买到的容量，所以超配的座位必须落在框外 */
.cp-cabin{display:grid;gap:6px;margin:11px 14px 0;padding:6px;border:1px solid var(--line-soft);
  border-radius:12px;background:var(--surface-3-a45)}
.cp-seat{position:relative;display:flex;flex-direction:column;justify-content:center;gap:4px;
  min-height:46px;padding:7px 10px;border-radius:9px;text-align:left;overflow:hidden;
  background:var(--brand-dim);border:1px solid var(--brand-42-on-line);
  cursor:pointer;transition:border-color .14s,background .14s}
.cp-seat:hover{border-color:var(--brand)}
.cp-seat:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* 拖动换座。座位能被抓起来，就得先长得像能被抓起来：光标是抓手，抓起时自己压暗，
   合法的落点亮边。不合法的落点（同一辆车里的另一个格子）什么都不做——车厢里没有
   座位号，那次拖动没有意义，静静地不接比弹一句"不能放这里"便宜。 */
.cp-seat[draggable="true"]{cursor:grab}
.cp-seat[draggable="true"]:active{cursor:grabbing}
.cp-seat.dragging{opacity:.4}
.cp-seat.drop{border-style:solid;border-color:var(--brand);background:var(--brand-a20)}
.cp-seat.free.drop .ph{opacity:0}
.cp-seat.free.drop .add{opacity:1}
/* 一格两行：上一行是人（称呼 + 副名），下一行是读数（档位 · 份额 · 次数 … 剩 N 天）。
   副名（邮箱 / 用户名）以前单独占一行，于是有的格子三行、有的两行，同一排卡片的
   格子高低不齐。现在副名跟在称呼后面、字小一号；空间不够时先让副名收成省略号，
   称呼最后才让（flex-shrink 1000 : 1）。全名都在 title 里。 */
.cp-seat .sid,.cp-lendchip .sid{display:flex;align-items:baseline;gap:7px;min-width:0;line-height:1.35}
.cp-seat .nm{flex:0 1 auto;min-width:0;font-size:12.5px;font-weight:500;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cp-seat .memo{flex:1 1 0;min-width:0;font-size:11px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 最近一次请求：钉在名字行右端，不收缩；让位的是副名，然后才是称呼。 */
.cp-seat .lc{flex:none;margin-left:auto;font-family:var(--mono);font-size:10px;color:var(--faint);font-variant-numeric:tabular-nums;white-space:nowrap}
.cp-seat .lc.live{color:var(--ok)}
.cp-seat .lc.stale{color:var(--warn)}
/* 去向标签：名字行右端一排常驻的小标签，和「最近一次请求」并排，格子保持两行。
   借出 = 人 + 外出箭头，后面跟借用人的备注名；借用中 = 出门的箭头；另一份订阅 = 两张
   叠着的卡，在用的绿、备用的淡。每一枚都能点：跳到那张账号卡（seatJumpCar），借用人
   没有自己的账号时打开他的工作台。它和 #778 撤掉的悬停眼睛不是一回事：常驻可见、
   长得像按钮、位置固定；格子其余地方的点击仍只开操作抽屉。
   以前去向写成「→ 账号名」替掉最近请求，借用人另起第三行，同一排格子高低不齐。 */
.cp-seat .jls{display:inline-flex;align-items:center;gap:4px;flex:none;margin-left:auto;align-self:center}
.cp-seat .jls+.lc{margin-left:0}
.cp-seat .jl{display:inline-flex;align-items:center;gap:3px;height:17px;padding:0 5px;border-radius:999px;flex:none;
  border:1px solid var(--line);background:var(--surface-3);color:var(--dim);font-size:10.5px;line-height:1;white-space:nowrap;
  transition:border-color .14s,color .14s,background .14s}
.cp-seat .jl svg{width:11px;height:11px;flex:none}
.cp-seat .jl span{max-width:8em;overflow:hidden;text-overflow:ellipsis;font-weight:500;color:var(--text)}
.cp-seat .jl[data-go]:hover{border-color:var(--brand);color:var(--brand);background:var(--brand-a20)}
.cp-seat .jl.guest{color:var(--brand-2)}
.cp-seat .jl.rides.on{color:var(--ok)}
.cp-seat .jl.rides.off,.cp-seat .jl.borrow{color:var(--faint)}
/* 卡头「+1 借用」：扫一排卡时不用看格子就知道这个账号实际上有几个人在用。 */
.cp-guests{flex:none;font-size:10px;font-family:var(--mono);color:var(--dim);background:var(--surface-3);border:1px solid var(--line);border-radius:999px;padding:1px 6px;white-space:nowrap}
/* 从另一张卡的「→ 账号名」跳过来时闪一下，眼睛才找得到落点。 */
.cp-car.flash{animation:cpFlash 1.6s ease-out}
@keyframes cpFlash{0%,40%{box-shadow:0 0 0 3px var(--brand-a70)}100%{box-shadow:0 0 0 0 transparent}}
@media (prefers-reduced-motion:reduce){.cp-car.flash{animation:none;box-shadow:0 0 0 3px var(--brand-a70)}}
.cp-seat .mt{display:flex;align-items:center;gap:8px;min-width:0;font-family:var(--mono);font-size:10px;
  color:var(--faint);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden}
/* 档位和用量是一串，点号隔开。剩余天数不进这串：多车的「在开 / 备用」把行填满时，
   被裁掉的是次数，天数钉在右端，跟用户页期限行的「剩 N 天」同一个位置。 */
.cp-seat .mt span+span::before{content:"·";margin-right:5px;opacity:.55}
.cp-seat .mt .mt-main{display:flex;align-items:center;gap:5px;min-width:0;flex:0 1 auto;overflow:hidden}
.cp-seat .mt .use{min-width:0;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto}
.cp-seat .mt>.dy{flex:none;margin-left:auto;font-weight:600;color:var(--dim)}
.cp-seat .mt>.dy::before{content:none;margin:0}
.cp-seat .mt>.dy.warn{color:var(--warn)}
.cp-seat .mt>.dy.bad{color:var(--bad)}
.cp-seat .tr{color:var(--brand-2);font-weight:600;flex:none}
/* 一人多辆车：备用那一格的名字退一档，去向在名字行右端的 .jl.rides 标签上。 */
.cp-seat.spare .nm{color:var(--dim)}
/* 负载：这个人这七天吃掉的份额，压在座位底沿。它是这张图的第二层意思，
   所以画得比边框还轻 —— 要看的时候在，不看的时候不抢名字。 */
.cp-seat .ld{position:absolute;left:0;bottom:0;height:2px;width:var(--load,0%);
  background:var(--brand-a70);border-radius:0 2px 0 0;
  transition:width .45s cubic-bezier(.2,.8,.3,1)}
/* 座位左沿那道色：芯片和下面分账表里的那一行靠它连起来，一个人在这张卡上
   只有一种颜色。只在画得出分账的车上出现——没有归因就没有颜色可认。 */
.cp-seat.seated::after{content:"";position:absolute;left:0;top:9px;bottom:9px;width:2.5px;
  border-radius:0 2px 2px 0;background:var(--sc)}
/* 没有生效档位的人：坐在车上但没买票，这是要处理的事，不是要好看的事 */
.cp-seat.nosub{background:var(--warn-a13);
  border-color:var(--warn-50-on-line)}
.cp-seat.nosub .tr{color:var(--warn)}
.cp-seat.nosub .ld{background:var(--warn-a70)}
/* 份额：这个人这个窗口吃掉了自己那一份的百分之几。超过一整份不报错——车上
   没别人在抢的时候本来就该随便开——但它是这块板上唯一先看一眼的数。 */
.cp-seat .sh{color:var(--brand-2);font-weight:600;flex:none}
.cp-seat.hot .sh{color:var(--warn)}
.cp-seat .mt .tk{flex:none}
.cp-seat .mt .use>span::before{content:none;margin:0}
.cp-seat:not(.hot) .mt .sh.ovr{display:none}
.cp-seat.hot .mt .use>.cv{display:none}
.cp-seat.hot .ld{background:var(--warn-a70)}
/* 分账：油表说这辆车用掉了多少，这一块说它是被谁用掉的。
   一根尺子：所有条子都按「占整个窗口的百分之几」画，上面那根是整车，
   下面每一根是车上的一个人，四列逐列对齐，长短可以直接比。以前这里是一行
   小字（已分 / 在座 / 非在座 / 空置），只有总数没有人名——两个人分别用了多少，
   得挨个把鼠标停在座位上看 title。 */
.cp-split{margin:11px 14px 0;padding-top:9px;border-top:1px solid var(--line-soft)}
.sp-hd{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:8px}
.sp-t{font-size:11.5px;color:var(--dim)}
.sp-t b{color:var(--text);font-weight:600;font-family:var(--mono);font-variant-numeric:tabular-nums}
.sp-scale{font-size:10.5px;color:var(--faint);white-space:nowrap}
.sp-scale i{display:inline-block;width:1.5px;height:9px;background:var(--dim);opacity:.75;
  vertical-align:-1px;margin-right:5px}
/* 行是 display:contents，四列跟上面的油表用同一套列宽 */
.sp-rows{display:grid;grid-template-columns:76px minmax(40px,1fr) 50px 94px;gap:6px 9px;
  align-items:center}
.sp-row{display:contents}
.sp-nm{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
/* 名字单独一个 span 承担省略号：76px 放不下时让名字缩成「结构人…」。 */
.sp-nm .nm{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
.sp-nm i.dot{width:7px;height:7px;border-radius:2px;background:var(--sc);flex:none}
/* 名字是这一行唯一能点的东西：直达这个人的客户工作台（那里才有他的细账——
   按天、按模型、按车的消耗价值，和他自己在门户上看到的同一份数）。 */
button.sp-nm{background:none;border:0;padding:0;font:inherit;font-size:11px;
  cursor:pointer;text-align:left;transition:color .14s}
button.sp-nm:hover{color:var(--brand-2)}
button.sp-nm:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.sp-rail{position:relative;height:7px;border-radius:3px;background:var(--surface-3)}
.sp-rail i{position:absolute;top:0;bottom:0;display:block;z-index:1}
.sp-rail i.fill{left:0;width:var(--u);min-width:3px;border-radius:3px;background:var(--sc)}
/* 份额车道：0 到「一份」那一段底色抬亮一点，和门户上客户看到的那条车道
   同一个记号——填满这一段就是正好用完自己那份。 */
.sp-row:not(.rest) .sp-rail::after{content:"";position:absolute;left:0;top:0;bottom:0;
  width:var(--e);border-radius:3px;background:var(--sp-lane)}
/* 超出自己那一份的一截：同一个颜色，斜纹。不报红——车上没别人在抢的时候多开
   本来就是对的（门户也是这么跟客户说的），红色会把一件常态说成故障。 */
.sp-rail i.over{left:var(--e);width:var(--o);border-radius:0 3px 3px 0;
  background:repeating-linear-gradient(115deg,var(--sc) 0 2px,transparent 2px 5px);
  border-right:1.5px solid var(--sc)}
/* 份额线：一份有多宽。一辆车里每个座位一样大，所以它在每一行落在同一个位置，
   上下各探出半个行距，几行连起来就是一条竖线——越过它的人吃掉了不止一份。 */
.sp-rail b.tick{position:absolute;left:var(--e);top:-6px;bottom:-6px;width:1px;z-index:2;
  background:var(--dim);opacity:.42;pointer-events:none}
.sp-rows>.sp-row:first-child .sp-rail b.tick{top:-3px}
.sp-rows>.sp-row:last-child .sp-rail b.tick{bottom:-3px}
.sp-n{font-family:var(--mono);font-size:11px;font-variant-numeric:tabular-nums;
  color:var(--dim);text-align:right}
.sp-s{font-size:10px;color:var(--faint);text-align:right;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
.sp-row.over .sp-s{color:var(--warn)}
/* 借用人一行：挂在原订阅用户下面，名字退一格、颜色同他、条淡一档。 */
.sp-row.guest .sp-nm{padding-left:9px}
.sp-row.guest .sp-rail i.fill{opacity:.55}
.sp-row.none .sp-nm,.sp-row.none .sp-n{color:var(--faint)}
/* 这个窗口一次没用的人：空轨道。min-width 是给「用了一点点」留的下限，
   不能让「一点没用」也长出一个色块——那两件事必须长得不一样。 */
.sp-row.none .sp-rail i.fill{display:none}
/* 剩下的三种份都不属于车上任何一个人：非在座（这个窗口里已经下车的）、
   未归属（本网关解释不了的消耗）、空位（没卖出去的那一份，是浪费不是余量）。
   灰阶加纹理，和有主的行分开。 */
.sp-row.rest .sp-nm,.sp-row.rest .sp-n{color:var(--faint)}
.sp-rail i.fill.unowned{background:repeating-linear-gradient(115deg,var(--faint) 0 2px,transparent 2px 5px);
  box-shadow:inset 0 0 0 1px var(--sp-rest-line)}
.sp-rail i.fill.gone{background:var(--sp-rest)}
.sp-rail i.fill.idle{background:transparent;box-shadow:inset 0 0 0 1px var(--line)}
/* 「非在座」不是匿名损耗：账本保留了窗口内每个旧放置人的份额。名称本身做成
   展开按钮，原来的总条仍留在同一把尺子上；展开层只解释这根条由谁组成，不把
   历史放置画回车厢，也不提供任何会改变绑定的动作。 */
button.sp-rest-toggle{background:none;border:0;padding:0;font:inherit;font-size:11px;color:var(--faint);
  display:flex;align-items:center;gap:5px;min-width:0;cursor:pointer;text-align:left;white-space:nowrap}
button.sp-rest-toggle:hover{color:var(--text)}
button.sp-rest-toggle:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.sp-rest-toggle .arr{font-family:var(--mono);font-size:12px;line-height:1;transform:rotate(0deg);transition:transform .14s}
.sp-rest-toggle[aria-expanded="true"] .arr{transform:rotate(90deg)}
.sp-gone{grid-column:1/-1;margin:0 0 3px;padding:4px 8px;border:1px solid var(--line-soft);
  border-radius:8px;background:var(--surface-3-a45)}
.sp-goneperson{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:5px 12px;align-items:center;padding:6px 0}
.sp-goneperson+.sp-goneperson{border-top:1px solid var(--line-soft)}
.sp-goneid{min-width:0}
.sp-gonename{display:flex;align-items:center;max-width:100%;min-width:0;white-space:nowrap;
  color:var(--text);font-size:11px;font-weight:500}
.sp-gonename .nm{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
span.sp-gonename{display:block;overflow:hidden;text-overflow:ellipsis}
button.sp-gonename{background:none;border:0;padding:0;font:inherit;font-size:11px;font-weight:500;
  color:var(--text);cursor:pointer;text-align:left}
button.sp-gonename:hover{color:var(--brand-2)}
button.sp-gonename:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.sp-gonememo{display:block;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  color:var(--faint);font-family:var(--mono);font-size:9.5px}
.sp-goneuse{text-align:right;white-space:nowrap}
.sp-goneuse b{display:block;color:var(--dim);font-family:var(--mono);font-size:10.5px;font-weight:500}
.sp-goneuse span{display:block;margin-top:2px;color:var(--faint);font-size:9.5px}
.sp-foot{margin:9px 0 0;font-size:10.5px;color:var(--faint);line-height:1.6}
.sp-foot .gap{color:var(--warn)}
/* 额度账号卡的人表（allowPeopleHTML）：一人一行，五列和油表共用 .ap-acct 上的列宽变量，
   油表的百分比和人的百分比落在同一条竖线上。行是 display:contents，表头也是一行。
   份额轨只画自己用掉的（额度客户没有车型份额，不画「一份 = x%」那根线）。 */
.ap-ppl{margin:9px 0 0;padding-top:7px;border-top:1px solid var(--line-soft);
  display:grid;grid-template-columns:var(--c-name) minmax(40px,1fr) var(--c-pct) var(--c-usd) var(--c-note);
  column-gap:var(--c-gap);row-gap:5px;align-items:center;align-content:start}
.ap-ppl>.ap-row{display:contents}
.ap-ppl .ap-th{font-size:10px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;align-self:end;padding-bottom:1px}
.ap-ppl .ap-th b{color:var(--text);font-family:var(--mono);font-weight:600;font-size:11px;font-variant-numeric:tabular-nums}
.ap-ppl .ap-th.r{text-align:right}
.ap-ppl .ap-th.share{display:flex;justify-content:space-between;gap:6px}
.ap-ppl .ap-th.share em{font-style:normal}
.ap-ppl .ap-th.money{grid-column:3/5}
.ap-ppl .ap-nm{display:flex;align-items:center;gap:6px;min-width:0;background:none;border:0;padding:0;
  font:inherit;font-size:11.5px;color:var(--text);white-space:nowrap;overflow:hidden;text-align:left}
.ap-ppl .ap-nm>span{overflow:hidden;text-overflow:ellipsis;min-width:0}
.ap-ppl button.ap-nm{cursor:pointer}
.ap-ppl button.ap-nm[draggable="true"]{cursor:grab}
.ap-ppl button.ap-nm[draggable="true"]:active{cursor:grabbing}
.ap-ppl button.ap-nm.dragging{opacity:.4}
.ap-ppl button.ap-nm:hover{color:var(--brand-2)}
.ap-ppl button.ap-nm:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.ap-ppl .ap-nm .ap-dot{width:7px;height:7px;border-radius:2px;background:var(--sc);flex:none;position:relative}
/* 最近 5 分钟有请求的人：色点外套一圈绿，和芯片上绿色的「3m」同一个意思。 */
.ap-ppl .ap-nm .ap-dot.live::after{content:"";position:absolute;inset:-3px;border-radius:4px;border:1.5px solid var(--ok)}
.ap-ppl .ap-nm .ap-dot.gone{background:var(--sp-rest)}
.ap-ppl .ap-nm .ap-dot.unowned{background:transparent;box-shadow:inset 0 0 0 1px var(--faint)}
.ap-ppl .ap-rail{position:relative;height:7px;border-radius:3px;background:var(--surface-3);overflow:hidden}
.ap-ppl .ap-rail i{display:block;height:100%;width:var(--u);min-width:3px;border-radius:3px;background:var(--sc)}
.ap-ppl .ap-rail i.gone{background:var(--sp-rest)}
.ap-ppl .ap-rail i.unowned{background:repeating-linear-gradient(115deg,var(--faint) 0 2px,transparent 2px 5px)}
.ap-ppl .ap-row.zero .ap-rail i{display:none}
.ap-ppl .ap-pct,.ap-ppl .ap-usd,.ap-ppl .ap-bal{font-family:var(--mono);font-size:11px;text-align:right;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
.ap-ppl .ap-pct{color:var(--dim)}
.ap-ppl .ap-usd{color:var(--text)}
.ap-ppl .ap-usd.nil{color:var(--faint)}
.ap-ppl .ap-bal{color:var(--dim)}
.ap-ppl .ap-bal.warn{color:var(--warn)} .ap-ppl .ap-bal.bad{color:var(--bad)}
.ap-ppl .ap-bal.note{font-family:inherit;font-size:10px;color:var(--faint)}
.ap-ppl .ap-bal.note .sp-rest-toggle{display:inline;color:var(--brand-2);font-size:10px;white-space:nowrap}
.ap-ppl .ap-row.former .ap-nm,.ap-ppl .ap-row.former .ap-bal{color:var(--faint)}
.ap-ppl .ap-row.former .ap-nm .ap-dot,.ap-ppl .ap-row.former .ap-rail i{background:var(--sp-rest)}
.ap-ppl .ap-row.rest .ap-nm,.ap-ppl .ap-row.rest .ap-pct{color:var(--faint)}
.ap-ppl .sp-foot{grid-column:1/-1;margin:2px 0 0}
/* 整周没发过请求：位置占着，车没在开。压暗而不是报警 —— 它是一条销售线索，
   不是一次故障。 */
.cp-seat.cold{background:transparent;border-style:solid;border-color:var(--line)}
.cp-seat.cold .nm{color:var(--dim)}
.cp-seat.free{background:none;border:1px dashed var(--line);align-items:center;justify-content:center;
  color:var(--faint);font-size:12px}
.cp-seat.free .ph{transition:opacity .14s}
.cp-seat.free .ph.lent{max-width:100%;padding:0 10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 「＋ 安排一个人」压在空位上层，不占位——否则每个空位平时都高出一行，
   一辆空车会比一辆坐满的车还高。 */
.cp-seat.free .add{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  padding:0 6px;text-align:center;color:var(--brand-2);opacity:0;transition:opacity .14s}
.cp-seat.free:hover,.cp-seat.free:focus-visible{border-color:var(--brand-55-on-line);border-style:solid}
.cp-seat.free:hover .ph,.cp-seat.free:focus-visible .ph{opacity:0}
.cp-seat.free:hover .add,.cp-seat.free:focus-visible .add{opacity:1}
/* 已买未坐：订阅写了、人还没发过请求。空位是留给他们的，不是还能再卖的。
   虚线边沿把他们和已落座分开，名字必须在——只加「已售 − 在坐」那个数，
   刷新页面的人仍然看不见切换成功。 */
.cp-seat.pending{background:transparent;border:1px dashed var(--brand-42-on-line)}
.cp-seat.pending .nm{color:var(--text)}
.cp-seat.pending .mt{color:var(--brand-2)}
/* 加座：挂在车厢外，自带警示色，但仍按车厢的列宽排 —— 它是「多出来的那一格」，
   不是另一种东西。不是错误，是「挤了，该添一辆车了」 */
.cp-over{display:grid;gap:6px;margin:7px 21px 0;padding-left:0}
.cp-over .cp-seat{background:var(--warn-a16);
  border-color:var(--warn-55-on-line)}
.cp-over .cp-seat .tr{color:var(--warn)}
.cp-overlbl{grid-column:1/-1;font-size:10.5px;letter-spacing:.06em;color:var(--warn)}
/* 放开座位之后人还在待落座名单里，但不能再占空位——空位是拿来安排别人的。
   虚线取自待落座格子，和「还没定下来」同一套形状。内边距跟车厢格子同一档。 */
.cp-hold{margin:12px 0 0;padding:10px 12px;border:1px dashed var(--line);border-radius:12px;background:var(--surface-2)}
.cp-holdlbl{font-size:10.5px;letter-spacing:.06em;color:var(--faint);margin:0 0 8px}
.cp-holdrow{display:flex;flex-wrap:wrap;gap:7px}
.cp-holdchip{display:inline-flex;align-items:center;gap:7px;padding:6px 11px;border:1px dashed var(--brand-42-on-line);
  border-radius:10px;background:transparent;color:var(--text);font-size:12px;cursor:pointer}
.cp-holdchip:hover,.cp-holdchip:focus-visible{border-style:solid;border-color:var(--brand-55-on-line)}
.cp-holdchip .un{font-size:11px;color:var(--faint)}
.cp-holdchip .days{font-family:var(--mono);font-size:10.5px;color:var(--brand-2);font-variant-numeric:tabular-nums}
/* 借乘：虚线跟已放开同一套，不进车厢格子——进了就像这辆车卖了两个座。 */
.cp-lend{margin:11px 14px 0;padding:9px 10px 10px;border:1px dashed var(--brand-42-on-line);border-radius:12px;background:var(--brand-dim)}
.cp-lend .cp-holdlbl{margin:0 0 7px;padding-left:2px}
/* 芯片吃掉整行，两颗动作竖着码在右边。原先三样东西并排在一个 flex 里，
   320px 的卡上放不下就换行，这一块因此比一格座位高出一倍。 */
.cp-lendrow{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:stretch;gap:8px}
/* 借用人也是这辆车上的一个人，所以按座位芯片那一套画：称呼、邮箱、mono 读数。
   份额、次数、天数一个都不能少——虚线框里本来只有名字，那 21% 就会掉进下面的
   「未归属 / 非在座」。左沿的色跟分账表里他那一行对齐。 */
.cp-lendchip{position:relative;display:flex;flex-direction:column;justify-content:center;gap:4px;
  min-width:0;padding:7px 10px 7px 12px;border:1px solid transparent;border-radius:9px;
  background:var(--surface-3-a45);text-align:left;cursor:pointer;overflow:hidden;transition:border-color .14s}
.cp-lendchip::after{content:"";position:absolute;left:0;top:8px;bottom:8px;width:2.5px;
  border-radius:0 2px 2px 0;background:var(--sc)}
.cp-lendchip:hover{border-style:solid;border-color:var(--brand-55-on-line)}
.cp-lendchip:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.cp-lendchip .nm{flex:0 1 auto;min-width:0;font-size:12.5px;font-weight:500;color:var(--text);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cp-lendchip .memo{flex:0 1000 auto;min-width:0;font-size:11px;color:var(--dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cp-lendchip .mt{display:flex;align-items:center;gap:5px;font-family:var(--mono);font-size:10px;
  color:var(--faint);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden}
.cp-lendchip .mt span+span::before{content:"·";margin-right:5px;opacity:.55}
.cp-lendchip .sh{color:var(--brand-2);font-weight:600;flex:none}
.cp-lendchip .mt .warn{color:var(--warn)}
/* 两颗动作：竖着排，跟芯片一样高。这是卡片上最少用到的一对按钮，所以画得比
   车尾那几颗还轻——转正只用文字提亮，不铺底色：一颗实心紫会比账号名还响。 */
.cp-lendact{display:flex;flex-direction:column;justify-content:center;gap:5px;flex:none}
.cp-mini{font-size:11px;line-height:1;font-family:var(--sans);color:var(--dim);
  background:var(--surface-3);border:1px solid var(--line);border-radius:7px;
  padding:6px 10px;cursor:pointer;white-space:nowrap;transition:.14s}
.cp-mini:hover{color:var(--text);border-color:var(--dim)}
.cp-mini:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.cp-mini.go{color:var(--brand-2);border-color:var(--brand-42-on-line);background:transparent}
.cp-mini.go:hover{border-color:var(--brand);background:var(--brand-dim)}

/* 油表：与供应商页同一套条子（实心=已用、竖刻度=配速、虚影=按这个速度到重置
   会走到哪儿）加一句几时翻篇，压成四列塞进卡片。同一个事实，三处一种读法。 */
/* 四列由整块油表统一定宽，每根表用 subgrid 借这四列：右端那句会见底时写成
   「22h33 后见底 · 10-01 19:00 重置」一行，这一列按本卡最长的那句撑开，条子让出
   宽度，几根表的条子仍然上下对齐。卡太窄时右列可以收缩，先让出去的是见底那半句
   （见 .gnote .gd），重置时刻不截。 */
.cp-fuel{margin:11px 14px 0;padding-top:9px;border-top:1px solid var(--line-soft);
  display:grid;grid-template-columns:76px minmax(40px,1fr) max-content minmax(0,max-content);column-gap:9px;row-gap:6px}
.cp-fuel>.cp-nofuel{grid-column:1/-1}
.cp-gauge{grid-column:1/-1;display:grid;grid-template-columns:subgrid;
  align-items:center;position:relative;
  background:none;border:0;padding:0;width:100%;text-align:left;font:inherit;color:inherit}
/* 能分账的车上，油表兼做页签：点哪一根，下面就按哪个窗口拆人。选中的那根
   左边立一根游标、标签提亮，不换配色——配色在这块板上说的是「还剩多少油」。 */
button.cp-gauge{cursor:pointer}
button.cp-gauge:hover .gl{color:var(--dim)}
button.cp-gauge:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:4px}
.cp-gauge.on .gl{color:var(--text);font-weight:600}
.cp-gauge.on::before{content:"";position:absolute;left:-9px;top:50%;transform:translateY(-50%);
  width:3px;height:15px;border-radius:2px;background:var(--brand)}
.cp-gauge .gl{font-size:10.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cp-gauge .gn{font-family:var(--mono);font-size:11px;font-variant-numeric:tabular-nums;color:var(--dim);
  white-space:nowrap;text-align:right}
/* 「几时回来」：一根表一句，和账号详情页同一套说法——一天以内给倒计时（照着它
   决定要不要等），一天以外给日期（周额度的「还有 3 天」远不如「08-06 19:00」
   有用）。这辆车满不满是此刻的事，这句话是「什么时候能再上车」。 */
.cp-gauge .gnote{font-size:10px;color:var(--faint);text-align:right;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
.cp-gauge .gnote.dry{color:var(--bad)}
/* 会见底的那根：见底（红，参考）在前，重置（和别的行同色、同一列右对齐）在后。
   窄卡上见底那半句先收成省略号，重置那半句整段保留。 */
.cp-gauge .gnote.both{display:flex;justify-content:flex-end;align-items:baseline;gap:5px;min-width:0}
.cp-gauge .gnote.both .gd{color:var(--bad);min-width:0;overflow:hidden;text-overflow:ellipsis}
.cp-gauge .gnote.both .gd::after{content:"·";margin-left:5px;color:var(--faint)}
.cp-gauge .gnote.both .gr{flex:none}
/* 窄卡（320 上下）：标签列收窄，会见底那一行的「重置 / 后重置」两个字省掉——时刻
   仍落在和别的行「… 重置」同一列，读得出是重置；腾出来的宽度让见底时刻完整。 */
/* 额度账号卡的容器挂在四段上而不是卡上：卡是 subgrid 项，Chrome 里 container-type 和
   grid-template-rows:subgrid 放在同一个元素上会把 subgrid 丢掉（2026-10-11 实测）。
   四段和卡同宽，查询结果不变。 */
.cp-car,.cpa-acct.ap-acct>*{container:seatcard/inline-size}
@container seatcard (max-width:360px){
  .cp-fuel{grid-template-columns:62px minmax(40px,1fr) max-content minmax(0,max-content);column-gap:8px}
  .ap-acct .cp-fuel,.ap-ppl{--c-name:62px;--c-usd:56px;--c-note:66px}
  .ap-acct .cp-fuel{grid-template-columns:var(--c-name) minmax(40px,1fr) var(--c-pct) var(--c-usd) var(--c-note);column-gap:var(--c-gap)}
  .cp-gauge .gnote.both .gv{display:none}
}
/* 限定模型的窗口（Fable 那道墙）也上表：它停不了整条线，却能把车上某个人挡在
   门外，所以画出来、但压一档——一枚菱形加一点透明度，和供应商页同一个记号。 */
.cp-gauge.scoped{opacity:.72}
.cp-gauge.scoped .gl::before{content:"◇ ";color:var(--brand-2)}
.cp-gauge.warn .gn{color:var(--warn)} .cp-gauge.bad .gn{color:var(--bad)}
.cp-gauge.warn .qrail i.fill{background:var(--warn)}
.cp-gauge.warn .qrail i.ghost{background:var(--warn-a26)}
.cp-gauge.bad .qrail i.fill{background:var(--bad)}
.cp-gauge.bad .qrail i.ghost{background:var(--bad-a26)}
.cp-gauge .qrail.dry i.ghost{background:var(--bad-a32)}
.cp-nofuel{font-size:11px;color:var(--faint);padding-top:2px}

/* 车尾读数：这辆车这七天跑了多少、累计接过多少人、上一次动是什么时候。
   读数排成固定的 2×2：上一行是这周的流量（请求 / token），下一行是历史与近况
   （接过几人 / 最近一次）。以前是一串 span 靠 flex 自己折行，三颗文字按钮占走
   大半宽度后，折出来的行数随卡宽随机变化——同一排两张卡一张三行一张两行。
   字号只有一档：数字 mono、标签 sans，全 11px；失败数只换色不加粗——这一块
   是仪表读数，不是标题，哪个数都不该比邻居大。
   四个动作收成图标：它们的名字本来就活在 title / aria-label 里，文字按钮把
   「重置份额」这四个字印在每张卡上，只是让读数无处可放。 */
.cp-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 12px 10px 14px;
  margin-top:auto;border-top:1px solid var(--line-soft);font-size:11px;color:var(--faint);line-height:1.5}
.cp-foot .fig{display:grid;grid-template-columns:repeat(2,minmax(0,max-content));column-gap:16px;row-gap:2px;
  align-items:start;min-width:0;flex:1}
/* 每格不换行也不截断。唯一放得下的弹性在「请求」那格：一张 320 宽的卡装不下
   「七天 24.6k 次 · 41 失败」时，让「· 41 失败」整段落到格内第二行——它仍在
   请求数正下方、仍然是红的。省略号会先吃掉的恰恰是失败数，那是这一格里最
   不该被藏的数。 */
.cp-foot .fig>span{white-space:nowrap}
.cp-foot .fig>span.cpf-rq{white-space:normal}
.cp-foot .fig>span.cpf-rq>span{white-space:nowrap}
.cp-foot .fig b{font-family:var(--mono);font-size:11px;color:var(--dim);font-weight:600;font-variant-numeric:tabular-nums}
.cp-foot .fig b.err{color:var(--bad)}
/* 图标组：26px 一颗，紧挨着排，作为一组读；hover 时才亮出品牌色。
   触屏下还原到 34px（全站 pointer:coarse 那块把 .btn.icon 放到 38，这里的
   卡片装不下四颗 38，34 是手指能点中的下限）。额度页「持有额度的用户」表的
   行尾动作（.cpa-acts）是同一页的同一种图标组，尺寸一起走这里。 */
.cp-foot .sp,.cpa-acts>div{display:flex;gap:2px;flex:none;align-self:center}
.cpa-acts>div{justify-content:flex-end}
.cp-foot .sp .btn.icon,.cpa-acts .btn.icon{width:26px;height:26px;border-radius:7px}
.cp-foot .sp .btn.icon svg,.cpa-acts .btn.icon svg{width:14px;height:14px}
@media (pointer:coarse){
  .cp-foot .sp,.cpa-acts>div{gap:4px}
  .cp-foot .sp .btn.icon,.cpa-acts .btn.icon{width:34px;height:34px}
  .cp-foot .sp .btn.icon svg,.cpa-acts .btn.icon svg{width:16px;height:16px}
}
.cp-note{padding:8px 14px 0;font-size:11.5px;color:var(--warn);line-height:1.5}
.cp-note.calm{color:var(--faint)}
.cp-empty{padding:34px 18px;text-align:center;color:var(--faint);font-size:13px}
/* 车队页的按钮统一压一档。这一页的主角是图形——油表、分账条、座位芯片，
   而按钮在一张 320px 宽的卡上是按表单的尺寸画的：车尾三颗一排就把七天读数
   挤成两行。压到 12px 之后读数回到一行，省下的宽度全给了条子。
   只压这一页：别处的表单和抽屉仍按全站那一档。 */
#v-seats .btn.sm{padding:5px 10px;font-size:12px;border-radius:7px;gap:6px}
#v-seats .btn.sm svg{width:13px;height:13px}
/* 靶区不跟着缩：手指点不中的按钮，省下来的那几像素一文不值。上面那个总的
   触屏块（@media (pointer:coarse)）在文件里排得比这一段早，同特异性下压不住
   它——所以还原写在规则本体旁边，靠先后顺序生效，而不是靠特异性。 */
@media (pointer:coarse){
  #v-seats .btn.sm{padding:8px 13px;font-size:13px}
  .cp-mini{padding:9px 12px;font-size:12px}
  .cp-size{padding:6px 12px;font-size:12px}
}
/* 价目表的一行 = 一种车型。行首挂着那个车型的俯视图，和板上的车道标牌同一个
   记号 —— 定价定的就是那个形状。 */
/* 一档一张卡：上面「卖不卖 + 叫什么」，下面四个数分两排。
   四个数曾经并排一行，而这个抽屉只有 430px 宽——四组「单位 + 输入框 + 单位」谁也
   放不下：¥358 露出「35」、240 分钟露出「24」、「/月」和「/天」「原价」叠成
   「/天原价」，而被切掉的正是保存前要核对的那几个数。两列之后每格有 170px，
   数字连着单位一次读完；读的顺序也从「四个数」变成「先月后日、先卖价后展示价」。 */
.cp-planrow{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;
  align-items:center;padding:14px 15px;margin-bottom:12px;
  border:1px solid var(--line-soft);border-radius:12px;background:var(--surface-2)}
/* 在售的档位自己亮起来：这一屏最先要回答的是「门户上现在挂着哪几档」 */
.cp-planrow:has(.pl-on:checked){border-color:var(--brand-55-on-line);background:var(--brand-dim)}
.cp-planrow .pl-head{display:flex;align-items:center;gap:8px;min-width:0;
  font-size:13px;color:var(--dim);white-space:nowrap;cursor:pointer}
.cp-planrow .pl-nums{grid-column:1/-1;display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 16px;margin-top:2px}
.cp-planrow .pl-f{display:block;min-width:0}
.cp-planrow .pl-cap{display:block;font-size:11px;color:var(--faint);margin-bottom:7px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cp-planrow .pl-in{display:flex;align-items:center;gap:7px;min-width:0}
.cp-planrow .pl-in input{min-width:0;flex:1;font-variant-numeric:tabular-nums}
.cp-planrow .pl-u{flex:none;font-size:12px;color:var(--faint);white-space:nowrap}
/* 四个输入框要左右对齐成一张表：单位词长短不一（¥ / /月 / /天 / 分钟），不给槽位
   定宽的话每格的输入框各宽各的，四个数字的左右边都对不上，扫一眼像四个不同的字段。
   没有单位的两格补一个空槽（划线原价没有后缀、分钟数没有 ¥），槽宽照最长的词算。 */
.cp-planrow .pl-in .pfx{min-width:11px;text-align:right}
.cp-planrow .pl-in .sfx{min-width:30px}
/* 说明文字和表单之间要有一条明确的界：上面是要填的，下面是填之前要知道的 */
.cp-planhelp{margin-top:20px;padding-top:16px;border-top:1px solid var(--line-soft)}
.cp-planhelp .hint{margin:0 0 12px;line-height:1.7}
.cp-planhelp .hint:last-child{margin-bottom:0}
/* 掉车额外补不是价格，是保障参数：整行独占，自己成一段，而不是去补「五个字段
   除以二」剩下的那个空格。输入框不跟着行宽拉满——一个三位数的框横跨整行，
   看起来像还没填完。 */
.cp-planrow .pl-f.wide{grid-column:1/-1}
.cp-planrow .pl-f.wide .pl-in{max-width:200px}
/* 抽屉最宽 430、最窄 92vw，两列到 360px 都还塞得下一个四位数；真到手机竖屏
   那么窄再摊成一列。 */
@media (max-width:400px){.cp-planrow .pl-nums{grid-template-columns:1fr}}
/* 空位选人：抽屉里的一列人，每行是一个还没上车的人 */
/* 滚动条挂在外层，不挂在每个分组上——两个分组各自滚，找人的时候要来回试
   哪一块能动。列表也不自己滚：它曾经封顶 340px，抽屉再高名单也只露八个人，
   而抽屉本来就是一条从头滚到尾的带子。搜索框吸在抽屉顶上，滚到哪都能改词。 */
.sk-list{margin:0}
.sk-find{position:sticky;top:-20px;z-index:1;background:var(--surface);padding:6px 0 10px;margin:0}
.sk-find input{width:100%}
.cp-pick{display:flex;flex-direction:column;gap:6px}
.cp-pickrow{display:flex;align-items:center;gap:10px;padding:9px 11px;border:1px solid var(--line);
  border-radius:10px;background:transparent;text-align:left;transition:.14s;width:100%}
.cp-pickrow:hover{border-color:var(--brand-55-on-line);background:var(--brand-dim)}
.cp-pickrow:disabled{cursor:not-allowed;opacity:.62}
.cp-pickrow:disabled:hover{border-color:var(--line);background:transparent}
.cp-pickrow .who{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}
.cp-pickrow .un{font-family:var(--mono);font-size:11px;color:var(--faint)}
.cp-pickrow .tr{font-family:var(--mono);font-size:11px;color:var(--brand-2);flex:none}
.cp-pickrow .tr.none{color:var(--warn)}
/* 定向发券的人选。通用 .cp-pickrow .who 是单行省略，窄屏顶栏还有一条
   .who{max-width:64px}——邮箱和备注挤进 64px 就分不清两个相近的号。
   这里改成两行：第一行是认人的把手（备注 / 昵称），第二行是邮箱，ID 跟在后面。
   max-width:none 专门压过那条 64px，不然手机上这张抽屉只剩三个字。 */
.cp-pickrow.gc-row{align-items:flex-start;padding:12px;min-height:48px}
.cp-pickrow.gc-row .gc-id{flex:1;min-width:0;max-width:none;display:flex;flex-direction:column;gap:3px;text-align:left}
.cp-pickrow.gc-row .gc-id b{font-size:13.5px;font-weight:600;line-height:1.35;white-space:normal;overflow-wrap:anywhere;max-width:none}
.cp-pickrow.gc-row .gc-id .sub{font-family:var(--mono);font-size:11.5px;color:var(--faint);line-height:1.45;white-space:normal;overflow-wrap:anywhere}
.cp-pickrow.gc-row.on{border-color:var(--brand-a50);background:var(--brand-dim)}
.cp-pickrow.gc-row:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.gc-promos{margin-top:18px;padding-top:16px;border-top:1px solid var(--line-soft)}
.gc-promos .sk-sec{margin-top:0}
.field.gc-count{margin-top:18px;padding-top:16px;border-top:1px solid var(--line-soft)}
.field.gc-count input{max-width:120px}
.gc-empty{padding:8px 2px 4px;color:var(--faint);font-size:12.5px;line-height:1.6}
#gc-go:disabled{opacity:.45;cursor:not-allowed;filter:none;box-shadow:none}

/* ---------- 拼车池编辑台（spw-） ----------
   独立居中弹窗，不走通用抽屉：这里的主工作区是车队名册，一行要放下
   「车牌 + 身份 + 车况 + 车型」，抽屉的 500px 只够把它们摞成一团。
   z 序停在确认卡（80/81）之下——删除 / 移出里的 ask() 要能盖在它上面。

   布局是「名册占整面、设置靠边站」：改一个池九成的动作发生在名册上，而池的
   七项设置一个月碰不了几次。它们曾经摞在名册头顶——七个格子加三行说明吃掉
   四百多像素，880 高的窗里名册只露得出两行半，最常用的区域反而要在一条小缝里
   滚。现在设置是左侧一根 268px 的窄栏（自己滚），名册从头到脚吃满右边；两边
   各自滚动，页脚的保存钉在底。 */
.spw-scrim{position:fixed;inset:0;background:var(--scrim);z-index:60;opacity:0;pointer-events:none;transition:opacity .18s}
.spw-scrim.show{opacity:1;pointer-events:auto}
.spw{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%) scale(.97);width:min(1080px,94vw);
  height:min(880px,94dvh);display:flex;flex-direction:column;background:var(--surface);
  border:1px solid var(--line);border-radius:18px;box-shadow:var(--shadow-2);z-index:61;
  opacity:0;pointer-events:none;transition:opacity .18s,transform .18s;overflow:hidden}
.spw.show{opacity:1;pointer-events:auto;transform:translate(-50%,-50%) scale(1)}
.spw::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;
  background:linear-gradient(90deg,var(--brand),transparent 70%)}
.spw-head{display:flex;align-items:center;gap:12px;padding:20px 24px 16px;flex:none;border-bottom:1px solid var(--line-soft)}
.spw-head h3{font-family:var(--disp);font-size:17px;font-weight:600;margin:0;flex:1}
.spw-body{flex:1 1 auto;min-height:0;display:flex}
/* 设置栏：七个字段竖排一列，说明压在最底下。只有这一栏滚不动时它才自己滚。 */
.spw-set{flex:0 0 268px;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  padding:18px 20px 22px 24px;border-right:1px solid var(--line-soft);background:var(--surface-2)}
.spw-grid{display:grid;grid-template-columns:1fr;gap:14px}
.spw-grid .field{margin-bottom:0}
.spw-set select,.spw-set input{padding:9px 11px;font-size:13px}
.spw-hints{margin-top:18px;padding-top:14px;border-top:1px dashed var(--line)}
.spw-set .hint{margin:0;line-height:1.65}
.spw-set .hint+.hint:not(:empty){margin-top:10px}
.spw-set .hint.warn{color:var(--warn)}
.spw-hints:not(:has(.hint:not(:empty))){display:none}
/* 名册面：工具行钉在顶上，下面整块是滚动区 */
.spw-main{flex:1 1 auto;min-width:0;min-height:0;display:flex;flex-direction:column}
/* 名册工具行：搜索 + 按订阅商筛（claude / codex / grok / kiro） */
.spw-bar{display:flex;align-items:center;gap:10px;padding:16px 24px 12px;flex:none;flex-wrap:wrap}
.spw-bar input{flex:1 1 180px;min-width:150px;padding:8px 12px;font-size:13px}
.spw-chips{display:flex;gap:6px;flex-wrap:wrap}
.spw-chip{font-size:12px;color:var(--dim);background:transparent;border:1px solid var(--line);
  border-radius:999px;padding:5px 12px;display:inline-flex;align-items:center;gap:6px;transition:.14s}
.spw-chip:hover{color:var(--text);border-color:var(--brand-55-on-line)}
.spw-chip.on{color:var(--brand-2);background:var(--brand-dim);border-color:var(--brand-a45)}
.spw-chip b{font-family:var(--mono);font-weight:600;font-size:11px;opacity:.75}
.spw-chip i{width:7px;height:7px;border-radius:50%;flex:none;font-style:normal}
/* 订阅商的识别色：沿用协议色板（claude=Anthropic 橙、codex=OpenAI 青），
   grok / kiro 各借一格没有归属冲突的（gemini 蓝 / image 品红） */
.spw-c-claude{background:var(--ac)} .spw-c-codex{background:var(--oa)}
.spw-c-grok{background:var(--gm)} .spw-c-kiro{background:var(--im)} .spw-c-x{background:var(--faint)}
/* 名册本体：分「已入池 / 未入池」两组，只有这一层滚动；组头吸顶，滚到哪都知道
   自己在哪一组 */
.spw-roster{flex:1 1 auto;min-height:150px;overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;padding:0 24px 20px}
.spw-gh{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:10px;font-size:11.5px;font-weight:600;
  color:var(--faint);letter-spacing:.04em;padding:12px 0 8px;margin:0;background:var(--surface)}
.spw-gh:first-child{padding-top:4px}
.spw-gh::after{content:"";flex:1;border-top:1px dashed var(--line);opacity:.85}
.spw-row{display:flex;align-items:center;gap:11px;padding:9px 13px;border:1px solid var(--line-soft);
  border-radius:12px;background:var(--surface-2);margin-bottom:7px}
.spw-row.out{background:transparent}
.spw-row.off{opacity:.6}
/* 换账号抽屉复用编辑台名册行：点选一行，而不是一个会把 aws-bedrock 排第一的 <select>。 */
.cp-swap-bar{margin:2px 0 12px}
.cp-swap-bar input{width:100%;padding:8px 12px;font-size:13px}
.cp-swap-list{margin:0 0 8px}
button.spw-row{width:100%;text-align:left;cursor:pointer;font:inherit;color:inherit}
button.spw-row.on{border-color:var(--brand-a45);background:var(--brand-dim)}
button.spw-row:hover{border-color:var(--brand-55-on-line)}
button.spw-row:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
button.spw-row:disabled{cursor:not-allowed}
button.spw-row:disabled:hover{border-color:var(--line-soft)}
/* 候选行右边的额度读数：换账号多半是因为原账号额度用完，余量要一眼看得到 */
.spw-row.cp-swap-row{align-items:flex-start;flex-wrap:nowrap}
/* 编辑台名册在窄屏把 .spw-who 提到行首、独占一行；换账号这一行不跟着变，状态点留在左边 */
.cp-swap-row .spw-who{flex:1;order:0;min-width:0}
.cp-swap-row .lamp{margin-top:5px}
.cp-swap-meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;margin-top:7px}
.cp-swap-note{font-size:11.5px;color:var(--faint);line-height:1.4}
.cp-swap-warn{font-size:11.5px;color:var(--warn);line-height:1.4;margin-top:5px}
.cp-swap-q{display:flex;gap:5px;align-items:center}
.cp-swap-q .q{display:inline-flex;align-items:baseline;gap:4px;padding:2px 8px;border-radius:var(--r-pill);
  background:var(--fill-3);font-family:var(--mono);font-size:11.5px;font-weight:600;color:var(--text);white-space:nowrap}
.cp-swap-q .q i{font-style:normal;font-weight:500;color:var(--faint)}
.cp-swap-q .q.scoped{background:transparent;border:1px solid var(--line-soft)}
.cp-swap-q .q.warn{color:var(--warn);background:var(--warn-a13)}
.cp-swap-q .q.bad{color:var(--bad);background:var(--bad-a12)}
.cp-swap-q .q.none{font-family:inherit;font-weight:500;color:var(--faint)}
/* 选中候选后页脚分两段：上面问原账号去哪，下面是取消和确认 */
.cp-swap-foot{flex:1;display:flex;flex-direction:column;gap:12px;min-width:0}
.cp-swap-dest{display:flex;flex-direction:column;gap:8px}
.cp-swap-lbl{font-size:12px;font-weight:600;color:var(--dim)}
.cp-swap-back button{white-space:nowrap}
.cp-swap-dest .hint{margin:0;line-height:1.5}
.cp-swap-go{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap}
.spw-who{flex:1;min-width:0}
.spw-nm{font-family:var(--mono);font-size:13px;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.spw-sub{font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:1px}
.spw-sub.warn{color:var(--warn)}
.spw-prov{flex:none;display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);
  font-size:10px;font-weight:600;letter-spacing:.06em;color:var(--dim);
  background:var(--surface-3);border:1px solid var(--line-soft);border-radius:999px;padding:3px 9px}
.spw-prov i{width:7px;height:7px;border-radius:50%;font-style:normal}
.spw-row select{flex:0 0 128px;width:128px;padding:6px 9px;font-size:12px}
/* 借用车：车牌上没有座位可画（它一个座都不卖），车型那一格换成一句话。宽度跟
   选择器对齐，否则一行借用车会把整份名册的「移出」按钮挤成锯齿。 */
.spw-lent{flex:0 0 128px;width:128px;font-size:11.5px;color:var(--faint);text-align:center}
.spw-lentplate{flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:23px;height:23px;border:1px dashed var(--line);border-radius:6px;
  font-size:10.5px;color:var(--dim);background:transparent}
.spw-row .btn.sm{flex:none;padding:5px 11px;font-size:12px}
/* 车牌上亮着的点是已坐下的人：编辑时就看得见「移走这辆车会动到谁」 */
.spw-row .cp-plate i.off{background:var(--brand-a26)}
.spw-empty{padding:30px 18px;text-align:center;color:var(--faint);font-size:13px}
.spw-foot{display:flex;align-items:center;gap:10px;padding:14px 24px;border-top:1px solid var(--line-soft);flex:none}
/* 变更摘要贴在删除和保存之间：保存前先把「这一下会发生什么」念一遍 */
.spw-delta{flex:1;font-size:12px;color:var(--dim);line-height:1.5;min-width:0}
.spw-delta b{font-family:var(--mono);font-weight:600}
.spw-delta .mv{color:var(--warn)}
/* 不够宽放两栏的屏（平板竖屏）：设置栏收窄一点，名册行还能一行放下 */
@media (max-width:980px){
  .spw-set{flex-basis:236px;padding:16px 16px 20px 20px}
  /* 订阅商那粒药丸让给名字：顶上的筛选芯片已经说了它是谁家的 */
  .spw-prov{display:none}
  .spw-row select,.spw-lent{flex-basis:124px;width:124px}
  .spw-row{flex-wrap:wrap}
  .spw-who{min-width:150px}
}
/* 装不下这台编辑器的屏幕：窄屏（设置竖排后一屏都不够）和矮屏（手机横过来
   只剩三百来点高）。两栏在这种屏上退成上下两段，整台编辑器一条滚动带，标题钉
   在顶、页脚（保存）钉在底，两头都不会滑走；名册在设置底下，一划就到。 */
@media (max-width:760px),(max-height:640px){
  /* 滚动的是中间那一段（.spw-body），标题和页脚是弹窗自己的两条边，不靠 sticky：
     以前整窗滚动、页脚 sticky，正文被压在固定高度的 flex 格里往外溢，页脚先盖住名册、
     滚到底又跟着滑走。 */
  .spw{height:auto;max-height:min(880px,94dvh)}
  .spw-body{display:block;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
  .spw-set{overflow:visible;border-right:none;border-bottom:1px solid var(--line-soft);padding:16px 24px 18px}
  .spw-grid{grid-template-columns:1fr 1fr;gap:12px 16px}
  .spw-hints{margin-top:14px}
  .spw-roster{flex:1 0 auto;min-height:0;overflow:visible}
  .spw-gh{position:static}
}
@media (max-width:760px){
  .spw{width:100vw;height:100dvh;max-height:100dvh;border-radius:0;border:none}
  .spw-grid{grid-template-columns:1fr}
  .spw-row{flex-wrap:wrap}
  .spw-who{flex:1 1 100%;order:-1}
  .spw-foot{flex-wrap:wrap;padding-bottom:calc(14px + env(safe-area-inset-bottom))}
  .spw-delta{flex:1 1 100%;order:-1}
  .spw-foot .btn{flex:1;justify-content:center;padding:11px}
}
/* ---------- 包月账号管理（#spw.maw-mode） ----------
   左「可添加」、右「包月账号」两栏各自滚动，搜索在标题行、同时筛两栏。左栏是浅底的托盘，
   放还没决定去处的账号；右栏是包月现状，按档位分组，新加的在顶上「待添加」组。
   加入、改档、移出都不用滚动去找：档位在加入时就选好，待添加的永远在右栏顶上。 */
.spw-hsearch{display:none}
.spw.maw-mode{width:min(1180px,94vw)}
.spw.maw-mode .spw-head h3{flex:none}
.spw.maw-mode .spw-hsearch{display:flex;align-items:center;gap:10px;flex:1;justify-content:flex-end;min-width:0}
.maw-q{position:relative;display:flex;align-items:center;flex:0 1 380px;min-width:0;margin:0}
.maw-q svg{position:absolute;left:11px;width:14px;height:14px;fill:none;stroke:var(--faint);stroke-width:1.6;stroke-linecap:round;pointer-events:none}
.maw-q input{width:100%;padding:8px 12px 8px 32px;font-size:13px}
.spw.maw-mode .spw-set{flex:0 0 380px;padding:0;display:flex;flex-direction:column;overflow:hidden}
.maw-ph{display:flex;align-items:baseline;gap:8px;flex:none}
.maw-ph h4{margin:0;font-size:13px;font-weight:600;color:var(--text)}
.maw-ph .n{font-family:var(--mono);font-size:12px;color:var(--faint);font-variant-numeric:tabular-nums}
.spw.maw-mode .spw-set .maw-ph{padding:18px 20px 6px 24px}
.maw-list{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;padding:0 20px 20px 24px}
.spw.maw-mode .spw-set .spw-gh{background:var(--surface-2)}
.maw-cand{display:flex;align-items:center;gap:10px;padding:10px 12px;border:1px solid var(--line-soft);
  border-radius:12px;background:var(--surface);margin-bottom:7px}
.maw-cand.off{opacity:.6}
.maw-cand .spw-who{flex:1;min-width:0}
.maw-flags{display:flex;flex-wrap:wrap;gap:5px;margin-top:6px}
.maw-add{display:inline-flex;align-items:center;gap:5px}
.maw-chev{width:10px;height:10px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;opacity:.7}
.maw-add[aria-expanded="true"]{border-color:var(--brand-a45);background:var(--brand-dim)}
.spw.maw-mode .spw-bar{flex-direction:column;align-items:stretch;gap:10px;padding:18px 24px 4px}
.maw-tabs .spw-chip.none:not(.on){opacity:.55}
.maw-tabs .spw-chip .tier-ic,.spw.maw-mode .spw-gh .tier-ic{color:currentColor}
/* 组头：档位图标 + 名称 · 个数，虚线后面是这一档的月价 */
.maw-gp{order:2;font-family:var(--mono);font-weight:500;letter-spacing:0;color:var(--faint)}
.maw-gh-new{color:var(--text)}
.maw-row.add{border-color:var(--brand-a45);background:var(--brand-dim)}
@keyframes mawflash{0%{box-shadow:0 0 0 3px var(--focus)}100%{box-shadow:0 0 0 3px transparent}}
.maw-flash{animation:mawflash 1.6s var(--ease) both}
@media (prefers-reduced-motion:reduce){.maw-flash{animation:none}}
.maw-pop{width:340px}
.maw-pop-who{font-family:var(--mono);font-size:11.5px;color:var(--dim);margin:3px 0 2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.maw-pop .ap-xl{padding:8px 0 2px}
.maw-pop .ap-mem{padding:8px 10px;font-family:var(--sans);font-size:12.5px}
.maw-pop .ap-mem:focus{outline:none;border-color:var(--brand-a45);background:var(--brand-dim)}
.maw-pop .ap-mem:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.maw-pop .ap-mem .tail{padding-left:12px}
.maw-pop-note{margin:8px 2px 2px;font-size:11.5px;line-height:1.55;color:var(--warn)}
.maw-panesw{display:none}
.maw-panesw b{font-family:var(--mono);font-weight:600;margin-left:4px;opacity:.7}
.maw-panesw i{font-style:normal;font-family:var(--mono);font-size:11px;font-weight:600;margin-left:4px;color:var(--focus)}
@media (max-width:980px){
  .spw.maw-mode .spw-set{flex-basis:320px}
}
/* 手机：两栏放不下，标题下面一个切换，一次只看一栏；搜索照样同时筛两栏 */
@media (max-width:760px){
  .spw.maw-mode .spw-head{flex-wrap:wrap;row-gap:12px}
  .spw.maw-mode .spw-head h3{flex:1}
  .spw.maw-mode .spw-hsearch{order:3;flex:1 1 100%;flex-direction:column;align-items:stretch}
  .maw-q{flex:1 1 auto}
  .maw-panesw{display:flex}
  .spw.maw-mode .spw-set{flex:none;overflow:visible;border-bottom:none;background:var(--surface);padding:0}
  .spw.maw-mode .maw-list{overflow:visible;padding:12px 16px 16px}
  .spw.maw-mode .maw-ph{display:none}
  .spw.maw-mode .spw-set .spw-gh{background:var(--surface)}
  .spw.maw-mode:not(.maw-p-mon) .spw-main{display:none}
  .spw.maw-mode.maw-p-mon .spw-set{display:none}
  .spw.maw-mode .spw-bar{padding:12px 16px 4px}
  .maw-tabs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;margin:0 -16px;padding:0 16px}
  .maw-tabs::-webkit-scrollbar{display:none}
  .maw-tabs .spw-chip{flex:none}
  .spw.maw-mode .spw-roster{padding-left:16px;padding-right:16px}
  .maw-cand .spw-who{flex:1;order:0}
  .maw-row{flex-wrap:wrap;gap:8px;padding:10px 12px}
  .maw-row .cp-plate{display:none}
  .maw-row .spw-who{flex:1 1 96px;order:0;min-width:0}
  .maw-row select{flex:0 0 112px;width:112px}
  .maw-row .btn.sm,.maw-cand .btn.sm{padding:8px 13px}
  /* 状态标签排到第二行，第一行固定是「名字 · 档位 · 按钮」，按钮不会被标签挤下去 */
  .maw-row .cpa-pill{order:3}
}
/* 分组小标题：这一列现在有两种人（有档位的、要顺手开单的），点下去的后果不一样，
   所以它们不能长成同一堆。 */
.sk-sec{font-size:11.5px;color:var(--faint);margin:12px 0 6px;letter-spacing:.02em}
.sk-sec:first-child{margin-top:0}
/* 上车流水线的清单（skc-）：账号 / 设备 key / 档位 / 落座 四节，✓ 齐 / ✗ 缺 / → 将做。 */
.skc-list{border:1px solid var(--line);border-radius:10px;background:var(--card-grad);padding:2px 14px}
.skc{display:flex;gap:10px;align-items:flex-start;padding:10px 0;border-bottom:1px dashed var(--line-soft)}
.skc:last-child{border-bottom:none}
.skc i{flex:none;width:18px;height:18px;border-radius:50%;font:600 11px var(--mono);font-style:normal;display:flex;align-items:center;justify-content:center;margin-top:1px}
.skc.done i{background:rgba(52,211,153,.14);color:var(--ok)}
.skc.todo i{background:rgba(251,191,36,.14);color:var(--warn)}
.skc.next i{background:var(--brand-dim);color:var(--brand-2)}
.skc-t{font-size:13px;font-weight:600;color:var(--text)}
.skc-s{font-size:11.5px;color:var(--dim);line-height:1.7;margin-top:2px}
.skc-s .btn{margin-top:6px}
.skc-sel{width:auto;display:inline-block;padding:3px 8px;font-size:12px}
/* ── 额度页签（cpa-）：牌价三站管道 / 持卡客户表 / 号池。
   号池不画车厢：没有「几人车 / 空几位 / 加座」。一张卡一个 Cloud 账号，
   卡上列此刻钉在这个号上的人（含运营备注）。全部走主题变量。 ── */
.cpa-cust,.cpa-fleet{margin-bottom:14px}
.cpa-pipe{display:flex;align-items:stretch;overflow-x:auto;padding-bottom:4px}
.cpa-stn{flex:1;min-width:148px;background:var(--card-grad);border:1px solid var(--line);border-radius:10px;padding:11px 15px}
.cpa-stn .l{display:block;font-size:10.5px;letter-spacing:.02em;color:var(--faint);margin-bottom:5px}
.cpa-stn .v{display:block;font-family:var(--mono);font-size:23px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--text)}
.cpa-stn .s{display:block;font-size:11px;color:var(--faint);margin-top:4px}
.cpa-stn.on{border-color:var(--brand);background:linear-gradient(180deg,var(--brand-dim),transparent),var(--card-grad)}
.cpa-stn.on .v{color:var(--brand-2)}
.cpa-hop{flex:none;width:106px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;position:relative}
.cpa-hop::before{content:"";position:absolute;left:8px;right:8px;top:50%;border-top:1px dashed var(--line)}
.cpa-hop .op{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:999px;padding:3px 12px;font-family:var(--mono);font-size:12px;font-weight:600;color:var(--text);white-space:nowrap}
.cpa-hop .op input{width:38px;background:none;border:none;border-bottom:1px solid var(--brand);color:var(--brand-2);font:inherit;text-align:center;padding:0;border-radius:0}
.cpa-hop .why{position:relative;font-size:10px;color:var(--faint);background:var(--surface);padding:0 6px;text-align:center}
.cpa-pricefoot{display:flex;gap:10px;margin-top:13px;flex-wrap:wrap;align-items:center;font-size:12px}
.cpa-chip{color:var(--dim);background:var(--surface-2);border:1px solid var(--line);border-radius:999px;padding:4px 12px}
.cpa-chip b{color:var(--text);font-family:var(--mono)}
.cpa-tblwrap{overflow-x:auto}
.cpa-tbl{width:100%;border-collapse:collapse;font-size:12.5px}
.cpa-tbl th{font-size:10.5px;letter-spacing:.02em;color:var(--faint);font-weight:600;text-align:left;padding:9px 16px;border-bottom:1px solid var(--line-soft);white-space:nowrap}
.cpa-tbl td{padding:10px 16px;border-bottom:1px solid var(--line-soft);white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--dim)}
.cpa-tbl td:first-child{color:var(--text);border-left:2px solid transparent}
.cpa-memo{display:block;font-size:11px;color:var(--dim);margin-top:2px;max-width:220px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cpa-tbl tr:last-child td{border-bottom:none}
.cpa-row.ok td:first-child{border-left-color:var(--ok)}
.cpa-row.warn td:first-child{border-left-color:var(--warn)}
.cpa-row.bad td:first-child{border-left-color:var(--bad)}
.cpa-tbl td.mono,.cpa-bal{font-family:var(--mono);font-size:12px}
.cpa-bal{font-size:13.5px;font-weight:600;color:var(--text)}
.cpa-bal.warn{color:var(--warn)}
.cpa-bal.bad{color:var(--bad)}
.cpa-pill{display:inline-block;font-size:11px;font-weight:600;border-radius:999px;padding:2px 10px;white-space:nowrap}
.cpa-pill.ok{color:var(--ok);background:rgba(52,211,153,.12)}
.cpa-pill.warn{color:var(--warn);background:rgba(251,191,36,.12)}
.cpa-pill.bad{color:var(--bad);background:rgba(251,113,133,.13)}
.cpa-fleetbd{display:flex;flex-wrap:wrap;gap:12px;align-items:stretch}
.cpa-acct{flex:1;min-width:250px;max-width:360px;background:var(--card-grad);border:1px solid var(--line);border-radius:10px;padding:10px 13px}
.cpa-acct .hd{display:flex;align-items:center;gap:8px}
/* 只管卡头那个账号名：写成 .cpa-acct .nm 时它会套到卡里每一格用户的 .nm 上，
   名字变成等宽字、flex:1 抢走整行，窄格里先被截断的就成了称呼而不是邮箱。 */
.cpa-acct .hd .nm{font-family:var(--mono);font-weight:600;font-size:12.5px;color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cpa-acct .em{font-size:11px;color:var(--faint);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cpa-acct .cp-fuel{margin:10px 0 0;padding-top:9px}
.cpa-acct .cp-note{margin:8px 0 0}
.cpa-acct .cp-split{margin-left:0;margin-right:0}
/* 额度账号卡整卡只定义一次列宽：油表（cp-fuel）和人表（ap-ppl）都引用它。油表原来是
   「标签 / 轨 / 读数 / 重置时间」四列，这里把它放到五列上：读数落在人表的份额% 那列，
   重置时间横跨后两列右对齐。 */
.cpa-acct.ap-acct{--c-name:82px;--c-pct:40px;--c-usd:60px;--c-note:66px;--c-gap:8px}
.ap-acct .cp-fuel{grid-template-columns:var(--c-name) minmax(40px,1fr) var(--c-pct) var(--c-usd) var(--c-note);column-gap:var(--c-gap)}
.ap-acct .cp-gauge>.gn{grid-column:3}
.ap-acct .cp-gauge>.gnote{grid-column:4/6}
/* 额度号池账号卡：和包月车道同一种自适应（每列至少 320px，油表、分账与扣额明细
   才不会溢出），宽屏封顶四列，窄一点自动落到三列、两列、一列。 */
.ap-pool .cpa-fleetbd{display:grid;gap:14px;
  grid-template-columns:repeat(auto-fill,minmax(max(320px,calc((100% - 42px)/4)),1fr))}
.ap-pool .cpa-fleetbd>.panel{grid-column:1/-1}
.cpa-acct.ap-acct{max-width:none;width:100%;min-width:0;padding:12px 14px 13px}
/* 没人、也没扣过额度的卡：人表那一段只剩一句话（拖人过来时它变成品牌色，是落点提示）。 */
.ap-none{margin:9px 0 0;padding-top:7px;border-top:1px solid var(--line-soft);font-size:11px;color:var(--faint)}
/* 额度账号卡的车尾贴到卡底：卡自己有 14px 内边距，车尾用负外边距顶回卡沿，
   分隔线才和包月车一样通到两边。
   卡与卡之间：卡是四段（卡头 / 油表 / 人表 / 车尾），每张卡在号池的 grid 里占四条行轨
   （subgrid），同一排的卡四段各自等高，人多的卡不会把邻居的油表挤到别的高度。
   人表那一段 align-content:start，行数少的卡从同一条线起画。 */
.cpa-acct.ap-acct{display:grid;grid-template-rows:subgrid;grid-row:span 4}
.cpa-acct.ap-acct>*{min-width:0}
.cpa-acct.ap-acct>.ap-mid{display:flex;flex-direction:column;align-items:stretch}
.cpa-acct.ap-acct>.cp-foot{margin:10px -14px -13px;border-radius:0 0 10px 10px;align-self:end}
.cpa-acct .hd .cpa-pill{flex:none}
.cpa-acct .hd .ap-sw,.stb-ghd .ap-sw{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 8px 0 6px;border-radius:999px;font:600 11px/1 var(--sans);white-space:nowrap;border:1px solid var(--line);background:var(--surface-2);color:var(--dim);cursor:pointer;transition:background .15s,border-color .15s,color .15s}
.stb-ghd .ap-sw{margin-left:0}
.cpa-acct .hd .ap-sw::before,.stb-ghd .ap-sw::before{content:"";width:10px;height:10px;flex:none;border-radius:50%;background:var(--faint);box-shadow:inset 0 0 0 2px var(--surface);transition:background .15s,transform .15s}
.cpa-acct .hd .ap-sw.on,.stb-ghd .ap-sw.on{background:var(--ok-a12);border-color:var(--ok-a32);color:var(--ok)}
.cpa-acct .hd .ap-sw.on::before,.stb-ghd .ap-sw.on::before{background:var(--ok)}
.cpa-acct .hd .ap-sw:hover,.stb-ghd .ap-sw:hover{border-color:var(--brand);color:var(--text)}
.cpa-acct .hd .ap-sw.on:hover,.stb-ghd .ap-sw.on:hover{border-color:var(--ok)}
.cpa-acct .hd .ap-lent-btn{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:4px;height:22px;padding:0 8px;border-radius:999px;font:600 11px/1 var(--sans);white-space:nowrap;border:1px solid var(--brand-a26);background:var(--brand-a12);color:var(--brand-2);cursor:pointer;transition:background .15s,border-color .15s}
.cpa-acct .hd .ap-lent-btn svg{width:12px;height:12px;flex:none;display:block}
.cpa-acct .hd .ap-lent-btn span{white-space:nowrap}
.cpa-acct .hd .ap-lent-btn:hover{border-color:var(--brand);background:var(--brand-a20)}
.cpa-acct .hd .ap-more{flex:none;display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;border-radius:6px;border:1px solid transparent;background:transparent;color:var(--dim);font:600 12px/1 var(--sans);cursor:pointer;transition:background .12s,color .12s,border-color .12s}
.cpa-acct .hd .ap-more:hover{background:var(--surface-3);border-color:var(--line);color:var(--text)}
.cpa-acct.ap-acct.is-idle{border-style:dashed;background:var(--surface);opacity:.78;transition:opacity .15s,border-color .15s,background .15s}
.cpa-acct.ap-acct.is-idle:hover{opacity:1;border-color:var(--brand-38-on-line)}
.dp-node{display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px;margin:8px 0 0;padding:6px 9px;border-radius:7px;background:var(--surface-2);border:1px solid var(--line-soft);font-size:11px;color:var(--dim)}
.dp-node .dp-pill{display:inline-flex;align-items:center;gap:5px;font-weight:600;color:var(--text)}
.dp-node .dp-pill.ok{color:var(--ok)}
.dp-node .dp-pill.warn{color:var(--warn)}
.dp-node .dp-pill.bad{color:var(--bad)}
.dp-node .dp-eg{font-family:var(--mono);font-size:10.5px;color:var(--faint)}
.dp-node .dp-acts{margin-left:auto;display:inline-flex;gap:6px}
.ap-none.ap-idle{display:flex;align-items:center;justify-content:space-between;gap:8px}
.ap-flt{display:inline-flex;align-items:center;gap:4px;padding:2px;border-radius:8px;background:var(--surface-2);border:1px solid var(--line-soft)}
.ap-flt button{height:24px;padding:0 9px;border-radius:6px;border:0;background:transparent;color:var(--dim);font:600 11.5px/1 var(--sans);cursor:pointer;display:inline-flex;align-items:center;gap:4px}
.ap-flt button b{font-family:var(--mono);font-size:11px;font-weight:600;color:var(--faint)}
.ap-flt button.on{background:var(--surface);color:var(--text);box-shadow:var(--ctl-on-ring)}
.ap-flt button.on b{color:var(--brand-2)}
button.cp-guests{cursor:pointer;transition:border-color .15s,color .15s,background .15s}
button.cp-guests.can-lend{border-style:dashed;color:var(--dim);background:transparent}
button.cp-guests.can-lend:hover{border-style:solid;border-color:var(--brand);color:var(--brand-2);background:var(--brand-a12)}
button.cp-guests.is-lent{border-color:var(--brand-a26);background:var(--brand-a12);color:var(--brand-2)}
button.cp-guests.is-lent:hover{border-color:var(--bad-a32);background:var(--bad-a12);color:var(--bad)}
/* 额度芯片上的余额：正常跟读数一个色，快见底黄、用尽 / 透支 / 没额度红。 */
.cp-seat .al{font-weight:600;flex:none;color:var(--dim)}
.cp-seat .al.warn{color:var(--warn)} .cp-seat .al.bad{color:var(--bad)}
.ap-pool{margin-bottom:14px}
/* 号池没有空位格子，整张账号卡就是落点。亮边和座位 .drop 同一套语言。 */
.ap-acct.drop{border-color:var(--brand);background:var(--brand-a20)}
.ap-acct.drop .ap-none{color:var(--brand-2)}
/* 空号卡几乎全是子节点（油表、「换账号」、空号提示）。HTML5 把最后一次
   dragover 调过 preventDefault 的那一层当落点，子节点没调就不会触发 drop。
   拖的时候子节点不接指针，事件落到卡自己身上。 */
body.seat-dragging .ap-acct > *{pointer-events:none}
body.seat-dragging .ap-acct .ap-none{color:var(--brand-2)}
/* 边车铭牌。池里两种号的坏法不一样（原生掉授权 vs 那台机器失联），排障第一步
   是知道手上是哪一种，所以它印在名字旁边而不是藏在备注里。 */
.fb-side{flex:none;font-size:10px;letter-spacing:.03em;color:var(--brand-a45,var(--dim));
  border:1px solid var(--line);border-radius:5px;padding:1px 5px;line-height:1.5}
.fb-foot{margin-top:10px;padding-top:8px;border-top:1px solid var(--line-soft);font-size:11px;color:var(--faint)}
.fb-foot b{font-family:var(--mono);color:var(--dim);font-weight:600;font-variant-numeric:tabular-nums}
.fb-foot .err{color:var(--bad)}
/* ── 额度对账单 ──
   银行流水的形状：头上一条等式（换购 ＋ 调整 − 消耗 − 到期 ＝ 现有）借换购牌价
   那套站点画法——那张卡讲钱怎么变成额度，这条等式讲额度去了哪，同一套视觉语法。
   表格每行带「之后余下」，最新一行必然等于当前持仓，对不上就在头上亮出来。 */
.cpa-cust tbody tr{cursor:pointer}
.cpa-cust tbody tr:hover td{background:var(--surface-2)}
.cpa-cust th.srt{cursor:pointer;user-select:none}
.cpa-cust th.srt:hover,.cpa-cust th.srt.on{color:var(--text)}
/* 行尾三个动作只画图标（对账单 / 充额度 / 转包月），文字在 title 和 aria-label 里，
   图标尺寸和车卡车尾的图标组共用一条规则（见 .cp-foot .sp）。这一列收到图标本身的
   宽度（width:1%），单元格左右留白收到 12px，常见窗口下整行一屏放下。
   窗口再窄、表格要横向滚动时，动作列钉在右边，数据列从它底下滚过去，三个动作始终看得见；
   左侧那道渐变把滚过去的字淡出，表格放得下时它落在状态列的空白上，看不出来。 */
.cpa-cust .cpa-tbl th,.cpa-cust .cpa-tbl td{padding-left:12px;padding-right:12px}
.cpa-cust .cpa-tbl .cpa-acts{width:1%;padding-left:8px;position:sticky;right:0;background:var(--surface)}
.cpa-cust .cpa-tbl .cpa-acts::before{content:"";position:absolute;top:0;bottom:0;left:-12px;width:12px;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--surface))}
.cpa-cust tbody tr:hover td.cpa-acts{background:var(--surface-2)}
.cpa-cust tbody tr:hover td.cpa-acts::before{background:linear-gradient(90deg,transparent,var(--surface-2))}
/* 近 24 小时逐小时走势：每格是一整列高的命中区，柱子画在 ::after 上，
   所以悬停一根矮柱不用瞄准它那几个像素。 */
.cpa-spark{display:inline-flex;align-items:stretch;gap:1px;height:22px;vertical-align:middle}
.cpa-spark i{width:3px;display:flex;align-items:flex-end;position:relative}
.cpa-spark i::after{content:"";width:100%;height:var(--h,0%);min-height:1px;background:var(--faint);border-radius:1px}
.cpa-spark i.z::after{background:var(--line)}
.cpa-spark i.now::after,.cpa-spark i:hover::after{background:var(--text)}
.cpa-spark i.tp::before{content:"";position:absolute;left:-1px;top:0;bottom:0;width:1px;background:var(--ok-fill)}
.cpa-sub{display:block;font-size:11px;color:var(--faint);margin-top:2px;font-family:var(--mono)}
.cpa-trend{margin-left:6px;font-size:11px;color:var(--faint)}
.cpa-trend.up{color:var(--warn);font-weight:600}
.cpa-used{display:block;width:96px;height:4px;border-radius:2px;background:var(--line);margin-top:5px;overflow:hidden}
.cpa-used b{display:block;height:100%;background:var(--faint);border-radius:2px}
.cpa-used.hot b{background:var(--warn-fill)}
/* 对账单页头上的余额走势图。字都放在 HTML 里：SVG 按容器宽度拉伸，里面的字会跟着变形。 */
.cpa-bt{margin-top:16px;border:1px solid var(--line);border-radius:10px;padding:13px 16px 12px}
.cpa-bt-hd{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px;font-size:12.5px}
.cpa-bt-hd .fc{margin-left:auto;color:var(--dim)}
.cpa-bt-hd .fc b{color:var(--text)}
.cpa-bt-hd .fc b.warn{color:var(--warn)}
.cpa-bt-plot{position:relative;height:170px;margin-left:54px}
.cpa-bt-plot svg{display:block;width:100%;height:100%;overflow:visible}
.cpa-bt-plot .ln{fill:none;stroke:var(--text);stroke-width:1.5;vector-effect:non-scaling-stroke;stroke-linejoin:round}
.cpa-bt-plot .ar{fill:var(--brand-dim);stroke:none}
.cpa-bt-plot .ax{stroke:var(--line);stroke-width:1;vector-effect:non-scaling-stroke}
.cpa-bt-plot .nw{stroke:var(--faint);stroke-width:1;stroke-dasharray:2 3;vector-effect:non-scaling-stroke}
.cpa-bt-plot .tp{stroke:var(--ok-fill);stroke-width:1.5;vector-effect:non-scaling-stroke}
.cpa-bt-plot .pj{fill:none;stroke:var(--faint);stroke-width:1.5;stroke-dasharray:5 4;vector-effect:non-scaling-stroke}
.cpa-bt-plot .pj.warn{stroke:var(--warn-fill)}
.cpa-bt-y{position:absolute;right:100%;margin-right:8px;font-family:var(--mono);font-size:10.5px;color:var(--faint);white-space:nowrap;transform:translateY(-50%)}
.cpa-bt-cur{position:absolute;top:0;bottom:0;width:1px;background:var(--dim);pointer-events:none}
.cpa-bt-tip{position:absolute;top:6px;z-index:2;pointer-events:none;background:var(--surface);border:1px solid var(--line);border-radius:8px;padding:6px 10px;font-size:11.5px;white-space:nowrap;color:var(--dim);box-shadow:0 4px 14px rgba(0,0,0,.12)}
.cpa-bt-tip b{display:block;font-family:var(--mono);font-size:13px;color:var(--text)}
.cpa-bt-x{position:relative;height:18px;margin:6px 0 0 54px;font-size:10.5px;color:var(--faint);font-family:var(--mono)}
.cpa-bt-x span{position:absolute;top:0;white-space:nowrap}
.cpa-bt-lg{display:flex;gap:16px;flex-wrap:wrap;margin-top:8px;font-size:11px;color:var(--faint)}
.cpa-bt-lg i{display:inline-block;width:14px;height:0;border-top:2px solid var(--text);vertical-align:middle;margin-right:6px}
.cpa-bt-lg i.tp{border-color:var(--ok-fill)}
.cpa-bt-lg i.pj{border-top-style:dashed;border-color:var(--faint)}
.cpa-stmt-eq .cpa-stn{flex:none;min-width:122px;padding:10px 14px}
.cpa-stmt-eq .cpa-stn .v{font-size:19px}
.cpa-stmt-eq .cpa-hop{width:46px}
.cpa-stmt-eq .cpa-hop .op{padding:2px 10px;font-size:13px}
.cpa-flt{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:13px 0 4px}
.cpa-flt .winsel button .n{opacity:.6;margin-left:5px;font-family:var(--mono);font-weight:500;font-size:11px}
.cpa-pill.dim{color:var(--dim);background:var(--surface-2)}
.cpa-pill.ic-only{display:inline-flex;align-items:center;padding:2px 6px}
.cpa-pill.ic-only svg{width:12px;height:12px;display:block}
.cpa-pill.acc{color:var(--brand-2);background:var(--brand-dim)}
.cpa-amt-in{color:var(--ok)}
.cpa-pager{display:flex;align-items:center;justify-content:center;gap:14px;padding:12px 0 2px;font-size:12px;color:var(--dim)}
.cpa-pager b{font-family:var(--mono);color:var(--text);font-variant-numeric:tabular-nums}
@media (max-width:900px){
  .cp-car.wide{grid-column:span 1}
  .cp-ledger{margin-left:0;width:100%}
  /* 模式栏拆成两行：页签自己占满一行（它本来就能横滑），价签和新建按钮共用
     第二行，而且不再贴右——这一页在掌上本来就比屏幕宽（车队的读数横着排），
     贴右等于贴在屏幕外面，而新建是这一页唯一的入口。 */
  /* 调度条在掌上不钉住：44px 的顶栏加两排读数会吃掉半屏，而这一页在掌上
     本来就得靠滚。两行：页签和价签一行，搜索框撑开第二行、新建和齿轮跟在它后面
     ——贴右等于贴在屏幕外面，而新建是这一页唯一的入口。 */
  .cpd{position:static;margin:calc(0px - var(--view-top)) -12px 16px;padding:10px 12px;gap:10px}
  .cpd>.cp-find{flex:1 1 150px;min-width:130px;width:auto}
  #v-seats .seat-sort{flex:1 1 180px}
  #v-seats .seat-sort select{min-width:0;max-width:100%}
  .cpd-vit{flex-basis:100%;order:9;padding:0}
  .cpf-tap{padding-left:14px}
  /* 号池抬头的读数折成几行时，行距收到 5px：16px 是同一行里格与格之间的距离。 */
  .cp-poolhd .cp-facts{gap:5px 14px}
}
/* 手机宽度的调度条（容器 ≤ 560px）：控件两行。第一行搜索框撑满，视图切换、说明开关、齿轮跟在
   后面；第二行排序、价签、新建。按桌面的顺序排，430px 宽的手机上会折成三行，齿轮和价签
   单独落在第三行。为了 375px 宽也排得下两行：排序框不写「排序」两个字（选项本身就是
   「手动顺序 / …先重置」），宽度跟着剩下的地方走，不再按最长的选项撑开；价签只留兑换价，
   「12 个月到期」在齿轮后的运营设置里。 */
@container cpd (max-width:560px){
  .cpd>.cpd-why,.cpd>.cpd-gear{order:1}
  .cpd>.seat-sort,.cpd>.cp-modeprice,.cpd>.cpd-add{order:2}
  .cpd>.cp-find{flex:1 1 120px;min-width:110px}
  #v-seats .cpd>.seat-sort{flex:1 1 96px;min-width:0}
  #v-seats .cpd>.seat-sort select{width:100%;min-width:0}
  .cpd>.seat-sort .seat-sort-k,.cpd>.cp-modeprice em{display:none}
}
@media (prefers-reduced-motion:reduce){
  .cp-car{animation:none}
  .cp-seat .ld{transition:none}
}

/* ---------- 报表中心 -------------------------------------------------------
   一个渲染器画所有报表，所以这里没有任何一张报表专属的样式：列怎么排、单元格
   怎么写，全由后端发来的 columns[].type 决定。

   报表名称不占左侧一列。名称排在模块页签下面的一条横条上：分组名是安静的
   标签，名字是按钮，当前这一张用和原来目录一样的浅底标明。每张报表的说明以前
   跟在每个名字下面，八张叠起来比名字还占地方；现在只留当前这一张，单独一行，
   超出就省略，完整句子在 title 里。名字放不下时横条自己滚动，两端渐隐。
   整条钉在模块页签底下，长图往下滚时换报表不用先滚回顶部。 */
#v-reports{padding-top:0}
.rp-catalog{position:sticky;top:var(--topbar-h,120px);z-index:3;display:flex;flex-direction:column;
  align-items:stretch;margin:0 -28px 16px;padding:4px 28px 8px;background:var(--bg);
  border-bottom:1px solid var(--line);min-width:0}
.rp-menu{display:flex;align-items:center;gap:16px;min-width:0;overflow-x:auto;
  scrollbar-width:none;padding:3px 0}
.rp-menu::-webkit-scrollbar{display:none}
.rp-catalog.fade-r .rp-menu{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);
  mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent)}
.rp-catalog.fade-l .rp-menu{-webkit-mask-image:linear-gradient(90deg,transparent,#000 28px);
  mask-image:linear-gradient(90deg,transparent,#000 28px)}
.rp-catalog.fade-l.fade-r .rp-menu{-webkit-mask-image:linear-gradient(90deg,transparent,#000 28px,#000 calc(100% - 28px),transparent);
  mask-image:linear-gradient(90deg,transparent,#000 28px,#000 calc(100% - 28px),transparent)}
.rp-grp{display:flex;align-items:center;gap:2px;flex:none}
.rp-menu .grp{flex:none;font-size:12px;font-weight:500;color:var(--faint);padding:0 6px 0 2px;white-space:nowrap}
.rp-menu button{flex:none;height:28px;padding:0 10px;border-radius:var(--r-pill);border:1px solid transparent;
  background:transparent;color:var(--dim);font-size:13px;font-weight:500;white-space:nowrap}
.rp-menu button:hover{background:var(--fill-4);color:var(--text)}
.rp-menu button.on{border-color:transparent;background:var(--fill-3);color:var(--text);font-weight:600}
.rp-menu button:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.rp-blurb{margin:1px 2px 0;font-size:12px;line-height:1.45;color:var(--faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rp-main{min-width:0;display:flex;flex-direction:column;gap:12px}
/* 列开关：默认收起。一张报表可以很宽，但不该一打开就很宽。 */
.rp-cols{display:flex;flex-wrap:wrap;gap:6px;padding:10px 15px;border-top:1px solid var(--line)}
.rp-cols .cg{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-right:10px}
.rp-cols .cgk{font-size:10.5px;color:var(--faint);letter-spacing:.1em}
.rp-cols label{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;color:var(--dim);
  padding:3px 8px;border:1px solid var(--line);border-radius:20px;cursor:pointer}
.rp-cols label.on{border-color:var(--brand-55-on-line);color:var(--text);background:var(--brand-dim)}
.rp-cols input{display:none}
/* 报表表格：锁定表头、首列与滚动容器。
   向下滚动时表头吸顶（两层表头层叠吸顶）；
   向右横滚时首列（智能首列）吸左锁定，两轴相交处（左上角表头、左下角汇总行）层级最高。
   滚动容器自成一个层叠上下文（isolation）：表头格的 z-index 4 / 6 只在表格里面比大小。
   没有这一层时它们和顶栏底下的实时带、模块页签条（都是 z-index 4）同层，整页往下滚、
   表格滑到顶栏底下的那一段，表头会画在那两条带子上面——一块带「分组 / 请求 / 客户端」
   的白色色块盖住页签。 */
.rp-main .ku-scroll{overflow:auto;max-height:calc(100vh - 314px);min-height:260px;overscroll-behavior:contain;isolation:isolate}
.rp-table{border-collapse:separate;border-spacing:0}
.rp-table th,.rp-table td{white-space:nowrap;vertical-align:middle;border-bottom:1px solid var(--line-soft)}
.rp-table thead th{position:sticky;background:var(--surface-2);box-sizing:border-box}
.rp-table thead tr:first-child th{top:0;z-index:4;height:26px;padding:6px 16px 3px;border-bottom:1px solid var(--line-soft)}
.rp-table thead tr:nth-child(2) th{top:26px;z-index:3;height:32px;padding:6px 16px 7px;border-bottom:1px solid var(--line);box-shadow:0 1px 0 var(--line)}
.rp-gh{font-size:11px;letter-spacing:.02em;color:var(--dim);text-transform:uppercase}
.rp-table td.clip{max-width:220px;overflow:hidden;text-overflow:ellipsis}
.rp-table td.num,.rp-table th.num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--mono)}
.rp-table td.mono{font-family:var(--mono);font-size:11.5px}
.rp-table tfoot td{position:sticky;bottom:0;z-index:2;border-top:1px solid var(--line);font-weight:600;color:var(--text);background:var(--surface-2);box-shadow:0 -1px 0 var(--line)}
/* 首列吸左锁定：在横向滚动时固定在最左侧，带竖向阴影作为与后续列的分割线 */
.rp-table .rp-sticky-col{position:sticky;left:0;box-sizing:border-box;min-width:120px;max-width:240px}
/* 表头首列（左上角交叉区）：跨两行吸顶且吸左，需最高层级 z-index:6 */
.rp-table thead th.rp-sticky-col{
  top:0;left:0;z-index:6;height:58px;vertical-align:middle;padding:6px 16px;
  background:var(--surface-2);border-bottom:1px solid var(--line);
  box-shadow:1px 0 0 var(--line), 0 1px 0 var(--line);
}
/* 表体首列：吸左，需高于普通滚动单元格 z-index:2 */
.rp-table tbody td.rp-sticky-col{
  z-index:2;background:var(--surface);font-weight:500;
  box-shadow:1px 0 0 var(--line);
}
.rp-table tbody tr:hover td.rp-sticky-col{background:var(--surface-2)}
/* 汇总行首列（左下角交叉区）：既吸底又吸左，需高于普通汇总单元格 z-index:5 */
.rp-table tfoot td.rp-sticky-col{
  bottom:0;left:0;z-index:5;background:var(--surface-2);
  box-shadow:1px 0 0 var(--line), 0 -1px 0 var(--line);
}
/* 用户报表首列：一张人的卡片，不是 id + 字段。头像色相跟设备授权页
   那条身份条同一套（--who-h），两行字自己截断，单元格不再 nowrap。 */
.rp-table td.rp-who{white-space:normal;max-width:240px;padding-top:7px;padding-bottom:7px}
.rp-cust{display:inline-flex;align-items:center;gap:8px;min-width:0;max-width:100%;
  --who-h:250}
.rp-cust-ava{flex:none;width:28px;height:28px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--disp);font-weight:700;font-size:12px;line-height:1;
  background:hsl(var(--who-h) 40% 20%);color:hsl(var(--who-h) 85% 80%);
  box-shadow:inset 0 0 0 1px hsl(var(--who-h) 40% 30%)}
[data-theme=light] .rp-cust-ava{background:hsl(var(--who-h) 78% 92%);color:hsl(var(--who-h) 52% 34%);
  box-shadow:inset 0 0 0 1px hsl(var(--who-h) 60% 82%)}
.rp-cust-id{min-width:0;display:flex;flex-direction:column;gap:1px;line-height:1.25}
.rp-cust-id b{color:var(--text);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rp-cust-id small{font-size:11px;color:var(--faint);font-weight:400;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rp-chip{display:inline-block;padding:2px 8px;border-radius:20px;font-size:11px;white-space:nowrap;
  border:1px solid var(--line);color:var(--dim)}
.rp-chip.ok{color:var(--ok);border-color:var(--ok-a32,rgba(52,211,153,.32))}
.rp-chip.warn{color:var(--warn);border-color:var(--warn-a32,rgba(251,191,36,.32))}
.rp-chip.bad{color:var(--bad);border-color:var(--bad-a32)}
.rp-none{color:var(--faint)}
/* 代理那两列：地址是主角，协议和「几条线共用」降成小标签跟在后面。截断从右边开始，
   所以先被切掉的一定是标签，不会是地址。 */
/* 比别的截断列宽一点：地址后面还跟着两个小标签，按 220px 裁会正好把标签切掉，
   于是「共用出口」这条只在共用时才出现的提示，恰好在共用时看不见。 */
.rp-table td.clip.px{max-width:330px}
.rp-table td.clip .rp-exit{font-weight:600;color:var(--text)}
.rp-tag{display:inline-block;margin-left:6px;padding:1px 5px;border-radius:4px;font-size:9.5px;
  letter-spacing:.04em;color:var(--faint);border:1px solid var(--line);vertical-align:1px}
/* 共用出口不是毛病，是一件要知道的事——所以用品牌色而不是告警色：一个正常配置
   顶着黄标，第二次看见就没人再当回事了。 */
.rp-tag.sh{color:var(--dim);border-color:var(--brand-55-on-line);background:var(--brand-dim)}
/* 出口读数的健康只占一颗点：这一列要读的是地址，不是状态。 */
.rp-eg{display:inline-flex;align-items:center;gap:5px}
.rp-eg i{flex:none;width:5px;height:5px;border-radius:50%;background:var(--ok)}
.rp-eg.warn i{background:var(--warn)}
.rp-eg.bad i{background:var(--bad)}
.rp-eg.stale i{background:var(--faint)}
.rp-eg.bad,.rp-eg.stale{color:var(--faint)}
/* 走势条：一天一根。画的是同一张日聚合表，不是另取的数。 */
.rp-spark{display:flex;align-items:flex-end;gap:1px;height:20px;min-width:64px}
.rp-spark i{display:block;flex:1;min-width:1px;background:var(--brand-55-on-line);border-radius:1px}
.rp-spark i.err{background:var(--bad)}
.rp-caveats{padding:12px 15px;border:1px solid var(--line);border-radius:10px;background:var(--surface-2);
  font-size:11.5px;color:var(--dim);line-height:1.65}
.rp-caveats b{color:var(--text);font-weight:600}
.rp-caveats li{margin-left:16px}
.rp-empty{padding:34px 18px;text-align:center;color:var(--faint);font-size:13px}
/* 连接速度：一屏两栏。左边是大图（全体三扇门 + 钉上去的人），右边是整张名册。
   每人只占一行：名字、一小段曲线、三扇门的速度并排，悬停才在大图上浮出线，点一下钉住。
   右栏绝对定位在格子里，只拿左栏的高度，名册在里面自己滚。 */
#rp-speed{container-type:inline-size}
.rp-speed{min-width:0;max-width:100%}
.rp-speed-split{display:grid;grid-template-columns:minmax(0,1fr) minmax(340px,400px);align-items:stretch}
.rp-speed-main{min-width:0;display:flex;flex-direction:column;gap:12px;padding:16px 18px 16px}
.rp-speed-side{position:relative;border-left:1px solid var(--line);min-height:460px}
.rp-speed-people{position:absolute;inset:0;display:flex;flex-direction:column;min-height:0}
.rp-speed-stage{position:relative;min-width:0;max-width:100%}
.rp-speed-svg{width:100%;height:auto;display:block}
.rp-speed-svg .ax{fill:var(--dim);font-size:11px;font-family:var(--mono)}
.rp-speed-svg .grid{stroke:var(--line);stroke-width:1}
.rp-speed-svg .ref{stroke:var(--line);stroke-width:1;stroke-dasharray:3 4}
.rp-speed-svg .stuck{stroke:var(--bad);stroke-width:2;stroke-linecap:round}
.rp-speed-tip{position:absolute;z-index:2;min-width:180px;max-width:260px;padding:10px 12px;
  background:var(--surface-3);color:var(--text);border:1px solid var(--line);border-radius:8px;
  font-size:12.5px;line-height:1.45;box-shadow:var(--shadow-3);pointer-events:none}
.rp-speed-tip[hidden]{display:none}
.rp-speed-tip .when{color:var(--dim);margin-bottom:6px}
.rp-speed-tip .row{display:flex;justify-content:space-between;gap:14px;margin-top:3px}
.rp-speed-lede{margin:0 2px}
.rp-speed-legend{display:flex;flex-wrap:wrap;gap:8px 16px;align-items:center;padding:0 2px;font-size:12.5px;color:var(--dim)}
.rp-speed-legend .it{display:inline-flex;align-items:center;white-space:nowrap}
.rp-speed-legend .it span{color:var(--faint);margin-left:6px}
.rp-speed-legend button{font:inherit;color:var(--dim);background:transparent;border:0;padding:0;cursor:pointer}
.rp-speed-legend button:hover{color:var(--text)}
.rp-speed-legend button:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.rp-speed-legend .pin{display:inline-flex;align-items:center;gap:6px;padding:3px 6px 3px 9px;border:1px solid var(--line);border-radius:20px;color:var(--text)}
.rp-speed-legend .pin b{font-weight:500}
.rp-speed-legend .pin small{color:var(--faint);font-size:11.5px}
.rp-speed-legend .pin button{width:18px;height:18px;border-radius:50%;line-height:18px;text-align:center;font-size:13px}
.rp-speed-legend .pin button:hover{background:var(--surface-3)}
.rp-speed-sw{width:18px;height:3px;border-radius:2px;display:inline-block;margin-right:6px;vertical-align:middle;flex:none}

/* 名册：抬头两行（标题+排序，搜索），说明只留一行，超出省略，整句在 title 里。
   每个人一行，曲线缩进中间那一格，速度跟在右边，不再各自占一行。 */
.rp-speed-ph{flex:none;padding:10px 12px 8px;border-bottom:1px solid var(--line-soft);display:flex;flex-direction:column;gap:6px}
.rp-speed-ph .t{display:flex;align-items:center;flex-wrap:wrap;gap:8px;row-gap:6px;min-width:0}
.rp-speed-ph h3{margin:0;font-family:var(--disp);font-size:13px;font-weight:600;color:var(--text);flex:none}
.rp-speed-ph .t .n{flex:none;font-size:12px;color:var(--faint);font-variant-numeric:tabular-nums;white-space:nowrap}
.rp-speed-ph .ctl{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.rp-speed-ph input{flex:1 1 8em;width:auto;min-width:0;padding:5px 8px;font-size:12px;border-radius:7px}
.rp-speed-sort{display:inline-flex;flex:none;background:var(--surface-2);border:1px solid var(--line);border-radius:8px;padding:2px}
.rp-speed-sort button{padding:4px 8px;border-radius:6px;background:transparent;color:var(--dim);font-size:11.5px;font-weight:600;white-space:nowrap}
.rp-speed-sort button.on{background:var(--ctl-on-bg);box-shadow:var(--ctl-on-ring);color:var(--text)}
.rp-speed-sort button:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.rp-speed-ph .note{margin:0;font-size:11px;line-height:1.35;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rp-speed-list{flex:1 1 0;min-height:0;overflow:auto;overscroll-behavior:contain}
.rp-speed-person{display:grid;grid-template-columns:minmax(8.5em,1.2fr) 64px minmax(0,1fr);gap:0 8px;align-items:center;
  width:100%;height:34px;padding:0 10px 0 12px;border:0;border-bottom:1px solid var(--line-soft);border-radius:0;background:transparent;
  color:inherit;font:inherit;text-align:left;cursor:pointer}
.rp-speed-person:hover,.rp-speed-person.hov{background:var(--surface-2)}
.rp-speed-person:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.rp-speed-person.on{background:var(--surface-2)}
.rp-speed-person .who{min-width:0;display:flex;flex-direction:row;align-items:center;gap:6px}
.rp-speed-person .who i{flex:none;width:7px;height:7px;border-radius:50%;background:transparent}
.rp-speed-person.on .who i{box-shadow:0 0 0 2px var(--surface-2)}
.rp-speed-person .who b{min-width:4em;flex:1 1 auto;display:block;font-size:12.5px;font-weight:600;color:var(--text);overflow:hidden;white-space:nowrap}
.rp-speed-person .who b span{display:block;overflow:hidden;text-overflow:ellipsis}
.rp-speed-person .flag{flex:none;font-style:normal;font-size:11px;font-weight:600;color:var(--bad);font-variant-numeric:tabular-nums}
.rp-speed-doors{display:flex;flex-wrap:nowrap;gap:6px;justify-content:flex-start;min-width:0;overflow:hidden;
  font-variant-numeric:tabular-nums;white-space:nowrap}
.rp-speed-doors>span{flex:none;font-family:var(--mono);font-size:11px;color:var(--dim);white-space:nowrap}
.rp-speed-doors span.bad{color:var(--bad)}
.rp-speed-spark{display:block;width:64px;height:18px;overflow:visible}
.rp-speed-spark .band{fill:var(--text-a8)}
.rp-speed-spark .fmed{fill:none;stroke-width:1;stroke-dasharray:3 3;opacity:.55}
.rp-speed-spark .me{fill:none;stroke-width:1.8;stroke-linejoin:round;stroke-linecap:round}
.rp-speed-spark .dot{stroke-width:4.5;stroke-linecap:round}
.rp-speed-spark .stk{fill:var(--bad)}
.rp-speed-none{margin:0;padding:18px 16px;font-size:12.5px;color:var(--faint);line-height:1.6}
/* 拖选放大：整块绘图区是十字光标，横向拖动归图、纵向滑动还给页面（手机上
   拖选不会和滚动打架）。选框是品牌色的浅底，顶上印起止与长度。 */
.rp-speed-svg{touch-action:pan-y}
.rp-speed-svg .hit{cursor:crosshair}
.rp-speed-svg .sel{fill:var(--brand);fill-opacity:.12;stroke:var(--brand);stroke-width:1}
.rp-speed-svg .sel-t{fill:var(--brand-2);font-size:11.5px;font-weight:600;font-family:var(--mono)}
.rp-speed-hint{margin-left:auto;font-size:12px;color:var(--faint);white-space:nowrap}
/* 时间窗那一排：放大后的退回 / 复制链接紧跟在档位后面；「实时」是一颗会呼吸
   的绿点——页面上唯一一处自己在动的东西，意思是「这张图自己会更新」。 */
.ku-act.rp-zoom{padding:7px 10px}
.rp-live{display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:600;color:var(--ok)}
.rp-live i{width:7px;height:7px;border-radius:50%;background:var(--ok);animation:rp-live 2s ease-out infinite}
@keyframes rp-live{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--ok) 55%,transparent)}100%{box-shadow:0 0 0 7px transparent}}
@media (prefers-reduced-motion:reduce){.rp-live i{animation:none}}
/* 导航条：大图下面一根缩略图，横轴和大图的绘图区对齐。亮着的那一截就是大图
   正在看的一段，两边压暗；两端的把手是整块界面里唯一能「抓」的东西，所以只有
   它们用品牌色描边。 */
.rp-speed-nav[hidden]{display:none}
.rp-speed-navsvg{width:100%;height:auto;display:block;touch-action:pan-y;user-select:none;-webkit-user-select:none}
.rp-speed-navsvg .trk{fill:var(--surface-2);stroke:var(--line);stroke-width:1}
.rp-speed-navsvg .grid{stroke:var(--line);stroke-width:1}
.rp-speed-navsvg .ax{fill:var(--faint);font-size:10.5px;font-family:var(--mono)}
.rp-speed-navsvg .cap{fill:var(--dim);font-size:11px;font-family:var(--mono)}
.rp-speed-navsvg .ln{fill:none;stroke-width:1.3;stroke-linejoin:round;stroke-linecap:round}
.rp-speed-navsvg .stk{stroke:var(--bad);stroke-width:1.5}
.rp-speed-navsvg .mask{fill:var(--surface);fill-opacity:.72}
.rp-speed-navsvg .win{fill:var(--brand);fill-opacity:.07;stroke:var(--brand);stroke-opacity:.75;stroke-width:1}
.rp-speed-navsvg .knob{fill:var(--surface-3);stroke:var(--brand);stroke-width:1.2}
.rp-speed-navsvg .grip{stroke:var(--dim);stroke-width:1;stroke-linecap:round}
.rp-speed-navsvg [data-k]{outline:none}
.rp-speed-navsvg [data-k]:hover .knob,.rp-speed-navsvg.drag .knob{fill:color-mix(in srgb,var(--brand) 30%,var(--surface-3))}
.rp-speed-navsvg [data-k]:focus-visible .knob{stroke-width:2.4}
.rp-speed-navsvg [data-k="w"]:focus-visible .win{stroke-width:2;stroke-opacity:1}
.rp-speed-navsvg .lab{fill:var(--faint);font-size:11.5px}
.rp-speed-navsvg .lab.on{fill:var(--brand-2);font-weight:600;font-family:var(--mono)}
/* 松手之后到那一段的数据回来之前，大图先淡下去：知道它在读，也知道现在看的还是旧的。 */
.rp-speed-stage.busy .rp-speed-svg{opacity:.45;transition:opacity .15s}
@container (max-width:720px){
  .rp-speed-split{grid-template-columns:minmax(0,1fr)}
  .rp-speed-side{border-left:0;border-top:1px solid var(--line);min-height:0}
  .rp-speed-people{position:static}
  .rp-speed-list{flex:none;max-height:520px}
}
@media (pointer:coarse){
  .rp-speed-person{height:40px}
}
/* 全屏：这张报表自己占满视口，侧栏和顶栏留在下面。
   连接速度的画布改成绝对定位，高度由舞台决定，不再把图撑出屏幕。 */
body.rp-fill{overflow:hidden}
#rp-full[aria-pressed="true"]{color:var(--text);background:var(--brand-dim)}
#v-reports.rp-fill{
  position:fixed;inset:0;z-index:32;
  display:flex;flex-direction:column;
  width:100%;height:100%;height:100dvh;
  margin:0;padding:0;box-sizing:border-box;
  background:var(--bg);overflow:hidden;
}
#v-reports:fullscreen,
#v-reports:-webkit-full-screen{
  position:relative;inset:auto;z-index:auto;
  display:flex;flex-direction:column;
  width:100%;height:100%;margin:0;padding:0;box-sizing:border-box;
  background:var(--bg);overflow:hidden;
}
#v-reports.rp-fill .rp-catalog,
#v-reports:fullscreen .rp-catalog,
#v-reports:-webkit-full-screen .rp-catalog{
  position:static;top:auto;margin:0;flex:none;
}
#v-reports.rp-fill .rp-blurb,
#v-reports:fullscreen .rp-blurb,
#v-reports:-webkit-full-screen .rp-blurb{display:none}
#v-reports.rp-fill .sec,
#v-reports:fullscreen .sec,
#v-reports:-webkit-full-screen .sec{
  margin:0;flex:1;min-height:0;
  display:flex;flex-direction:column;
  padding:0 16px 12px;
}
#v-reports.rp-fill .rp-main,
#v-reports:fullscreen .rp-main,
#v-reports:-webkit-full-screen .rp-main{flex:1;min-height:0;gap:8px}
#v-reports.rp-fill .lg-filters,
#v-reports:fullscreen .lg-filters,
#v-reports:-webkit-full-screen .lg-filters{margin-bottom:0;flex:none}
#v-reports.rp-fill .panel,
#v-reports:fullscreen .panel,
#v-reports:-webkit-full-screen .panel{
  flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;
}
#v-reports.rp-fill .rp-caveats,
#v-reports:fullscreen .rp-caveats,
#v-reports:-webkit-full-screen .rp-caveats{
  flex:none;max-height:72px;overflow:auto;margin:0;
}
#v-reports.rp-fill #rp-speed:not(.hide),
#v-reports.rp-fill #rp-viz:not(.hide),
#v-reports.rp-fill .ku-scroll:not(.hide),
#v-reports:fullscreen #rp-speed:not(.hide),
#v-reports:fullscreen #rp-viz:not(.hide),
#v-reports:fullscreen .ku-scroll:not(.hide){
  flex:1 1 auto;min-height:0;
}
#v-reports.rp-fill #rp-speed:not(.hide),
#v-reports:fullscreen #rp-speed:not(.hide){
  display:flex;flex-direction:column;overflow:auto;
}
#v-reports.rp-fill #rp-viz:not(.hide),
#v-reports:fullscreen #rp-viz:not(.hide){overflow:auto}
#v-reports.rp-fill .rp-main .ku-scroll:not(.hide),
#v-reports:fullscreen .rp-main .ku-scroll:not(.hide){max-height:none;overflow:auto}
#v-reports.rp-fill .rp-speed-split,
#v-reports:fullscreen .rp-speed-split{
  flex:1;min-height:0;height:100%;
  grid-template-rows:minmax(0,1fr);
}
#v-reports.rp-fill .rp-speed-main,
#v-reports:fullscreen .rp-speed-main{
  height:100%;min-height:0;overflow:hidden;gap:8px;padding:12px 14px 12px;
}
#v-reports.rp-fill .rp-speed-side,
#v-reports:fullscreen .rp-speed-side{min-height:0;height:100%}
#v-reports.rp-fill .rp-speed-stage,
#v-reports:fullscreen .rp-speed-stage{flex:1 1 auto;min-height:0;overflow:hidden}
#v-reports.rp-fill .rp-speed-svg,
#v-reports:fullscreen .rp-speed-svg{position:absolute;inset:0;width:100%;height:100%}
/* 窄屏全屏改成上下排。画布回到文档流，避免绝对定位把图高收成 0。
   这条必须写在上面那组绝对定位之后，两边选择器一样重，后来的才生效。 */
@container (max-width:720px){
  #v-reports.rp-fill .rp-speed-split,
  #v-reports:fullscreen .rp-speed-split{height:auto;grid-template-rows:none}
  #v-reports.rp-fill .rp-speed-main,
  #v-reports:fullscreen .rp-speed-main{height:auto;overflow:visible}
  #v-reports.rp-fill .rp-speed-side,
  #v-reports:fullscreen .rp-speed-side{height:auto}
  #v-reports.rp-fill .rp-speed-stage,
  #v-reports:fullscreen .rp-speed-stage{flex:none;height:auto;overflow:visible}
  #v-reports.rp-fill .rp-speed-svg,
  #v-reports:fullscreen .rp-speed-svg{position:static;height:auto}
}
/* 流量图 / 运营足迹图：报表中心里的两张图。rpv- 前缀，组件内层类名也带前缀
   （看板里 .bar/.rail/.lv/.rq/.tile 早被占了）。
   一张图 = 一句话（.rpv-lede，结论先说）+ 一张画 + 一行口径；
   区块之间用细线分开而不是一张张卡片，读起来是一份报告，不是一面墙。 */
.rpv{display:flex;flex-direction:column;min-width:0;max-width:100%}
.rpv-sec{padding:20px 22px 22px;min-width:0;border-top:1px solid var(--line-soft)}
.rpv-sec:first-child{border-top:0}
.rpv-sec h3{margin:0;font-family:var(--disp);font-size:14px;font-weight:600;color:var(--text);letter-spacing:-.005em}
.rpv-sec .rpv-sub{margin:5px 0 0;font-size:12.5px;line-height:1.6;color:var(--dim);max-width:72ch}
.rpv-sec .rpv-sub b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.rpv-head{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:10px 20px;margin-bottom:16px}
.rpv-head>div{min-width:0}
.rpv-lede{margin:0 0 18px;font-family:var(--disp);font-size:18px;line-height:1.55;font-weight:500;color:var(--text);
  letter-spacing:-.01em;max-width:62ch;text-wrap:pretty}
.rpv-lede em{font-style:normal;color:var(--brand-2);font-variant-numeric:tabular-nums}
.rpv-stage{position:relative;overflow-x:auto;min-width:0;max-width:100%}
.rpv-svg{width:100%;height:auto;display:block}
.rpv-svg text{font-family:var(--sans)}
.rpv-svg .ax{fill:var(--faint);font-size:11px;font-family:var(--mono);font-variant-numeric:tabular-nums}
.rpv-svg .ax-d{fill:var(--dim);font-size:11.5px}
.rpv-svg .grid{stroke:var(--line-soft);stroke-width:1}
.rpv-svg .grid-day{stroke:var(--line);stroke-width:1}
.rpv-pair{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);border-top:1px solid var(--line-soft)}
.rpv-pair>.rpv-sec{border-top:0}
.rpv-pair>.rpv-sec+.rpv-sec{border-left:1px solid var(--line-soft)}
.rpv-legend{display:flex;flex-wrap:wrap;gap:6px 16px;font-size:12px;color:var(--dim);align-items:center}
.rpv-legend span{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.rpv-legend i{display:inline-block;width:9px;height:9px;border-radius:50%;flex:none}
.rpv-legend i.hollow{background:transparent!important;box-shadow:inset 0 0 0 1.6px var(--dim)}
.rpv-legend i.bar{width:14px;height:8px;border-radius:2px}
.rpv-legend i.dash{width:16px;height:0;border-radius:0;border-top:1.5px dashed var(--dim)}
.rpv-tip{position:absolute;z-index:2;min-width:190px;max-width:280px;padding:10px 12px;
  background:var(--surface-3);color:var(--text);border:1px solid var(--line);border-radius:8px;
  font-size:12.5px;line-height:1.5;box-shadow:var(--shadow-3);pointer-events:none}
.rpv-tip[hidden]{display:none}
.rpv-tip .when{color:var(--dim);margin-bottom:5px}
.rpv-tip .kv{display:flex;justify-content:space-between;gap:16px;font-variant-numeric:tabular-nums}
.rpv-tip .kv span{color:var(--dim)}
.rpv-empty{padding:40px 18px;text-align:center;color:var(--faint);font-size:13px;line-height:1.6}

/* 四条腿 */
.rpv-flow .band-up{fill:var(--brand)}
.rpv-flow .band-extra{fill:var(--warn)}
.rpv-flow .band-dn{fill:var(--s2)}
.rpv-flow .node{fill:var(--surface-2);stroke:var(--line);stroke-width:1}
.rpv-flow .node-name{fill:var(--text);font-family:var(--disp);font-size:14px;font-weight:600}
.rpv-flow .node-sub{fill:var(--faint);font-size:11.5px}
.rpv-flow .lab{fill:var(--dim);font-size:12px}
.rpv-flow .lab-v{fill:var(--text);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}

/* 逐小时带宽 */
.rpv-hours .bar-h{fill:var(--brand-a45)}
.rpv-hours .bar-h.pk{fill:var(--brand)}
.rpv-hours .avg{stroke:var(--text-a60);stroke-width:1.2;stroke-dasharray:5 5}
.rpv-hours .avg-lab{fill:var(--text);font-size:11.5px;paint-order:stroke;stroke:var(--surface);stroke-width:4px;stroke-linejoin:round}
.rpv-hours .pk-lab{fill:var(--text);font-size:12px;font-weight:600;paint-order:stroke;stroke:var(--surface);stroke-width:4px;stroke-linejoin:round}
.rpv-hours .cursor{fill:var(--text-a8)}

/* 节奏格阵 */
.rpv-heat{display:grid;grid-template-columns:78px repeat(24,minmax(8px,1fr));gap:3px;max-width:640px;min-width:360px}
.rpv-heat i{display:block;border-radius:3px;background:var(--brand);min-height:0}
.rpv-heat i.none{background:var(--sp-lane);opacity:1!important}
.rpv-heat i.avg,.rpv-heat i.quiet{margin-top:6px}
.rpv-heat i.quiet{outline:2px solid var(--ok);outline-offset:1px}
.rpv-heat span{font-family:var(--mono);font-size:11px;color:var(--faint);white-space:nowrap;line-height:1;align-self:center}
.rpv-heat span.hx{text-align:left;padding-bottom:4px}
.rpv-heat span.dl{padding-right:8px;text-align:right;color:var(--faint)}
.rpv-heat span.dl.now{color:var(--dim)}
.rpv-heat span.dl.avg{color:var(--dim);margin-top:6px}

/* 排行 */
.rpv-rank{display:flex;flex-direction:column;gap:12px;margin:0;padding:0;list-style:none}
.rpv-rank li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:5px 14px;align-items:baseline}
.rpv-rank .who{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--text);font-weight:500}
.rpv-rank .who small{margin-left:8px;font-size:11.5px;color:var(--faint);font-weight:400}
.rpv-rank .amt{font-family:var(--mono);font-size:12px;color:var(--text);font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.rpv-rank .amt span{color:var(--faint);margin-left:8px}
.rpv-rank .trk{grid-column:1/-1;height:6px;border-radius:3px;background:var(--sp-lane);overflow:hidden}
.rpv-rank .trk i{display:block;height:100%;border-radius:3px;background:var(--brand)}
.rpv-rank .meta{grid-column:1/-1;font-size:11.5px;color:var(--faint);display:flex;flex-wrap:wrap;gap:4px 12px}
.rpv-rank .meta .flag{color:var(--warn)}
.rpv-rank li.rest .who{color:var(--dim);font-weight:400}
.rpv-rank li.rest .trk i{background:var(--faint-a55)}

/* 足迹 */
.rpv-foot .span{stroke:var(--line);stroke-width:2;stroke-linecap:round}
.rpv-foot .row-bg{fill:var(--sp-lane)}
.rpv-foot .dot-view{fill:var(--brand)}
.rpv-foot .dot-click{fill:var(--s2)}
.rpv-foot .dot-search{fill:var(--ac)}
.rpv-foot .hollow{fill:var(--surface)!important;stroke-width:1.8}
.rpv-foot .hollow.dot-view{stroke:var(--brand)}
.rpv-foot .hollow.dot-click{stroke:var(--s2)}
.rpv-foot .hollow.dot-search{stroke:var(--ac)}
.rpv-foot .today{fill:var(--text);font-weight:600}
.rpv-foot .cnt{fill:var(--text);font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}
.rpv-foot .cnt-sub{fill:var(--faint);font-size:11px;font-family:var(--mono)}
.rpv-foot .off{fill:var(--faint);font-size:11px}

.rpv-clock{display:grid;grid-template-columns:repeat(24,1fr);gap:3px;height:132px;margin-top:14px;align-items:end}
.rpv-clock .col{position:relative;display:flex;flex-direction:column;justify-content:flex-end;height:100%;padding-bottom:18px}
.rpv-clock .col i{display:block;width:100%;border-radius:2px 2px 0 0;background:var(--brand-a45);min-height:1px}
.rpv-clock .col.pk i{background:var(--brand)}
.rpv-clock .col b{position:absolute;bottom:0;left:0;font-family:var(--mono);font-size:10.5px;font-weight:400;color:var(--faint)}
.rpv-clock .col em{font-style:normal;font-size:11px;color:var(--text);text-align:center;margin-bottom:3px;white-space:nowrap}
/* 页面 → 动作 */
.rpv-pages{columns:2 340px;column-gap:44px;margin:0;padding:0;list-style:none}
.rpv-pages>li{break-inside:avoid;padding-bottom:20px}
.rpv-page-hd{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:baseline}
.rpv-page-hd b{font-size:13.5px;font-weight:600;color:var(--text);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rpv-page-hd span{font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap}
.rpv-page-trk{grid-column:1/-1;display:flex;height:8px;border-radius:4px;overflow:hidden;background:var(--sp-lane)}
.rpv-page-trk i{display:block;height:100%}
.rpv-page-trk i.v{background:var(--brand)}
.rpv-page-trk i.c{background:var(--s2)}
.rpv-funcs{margin:10px 0 0;padding:0 0 0 12px;list-style:none;border-left:2px solid var(--line-soft);display:flex;flex-direction:column;gap:6px}
.rpv-funcs li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 12px;align-items:baseline;font-size:12.5px}
.rpv-funcs .fn{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text)}
.rpv-funcs .fn small{margin-left:8px;color:var(--faint);font-family:var(--mono);font-size:11px}
.rpv-funcs .n{font-family:var(--mono);font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums}
.rpv-funcs li.more .fn{color:var(--faint)}

.rpv-trio{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr) minmax(0,1fr);border-top:1px solid var(--line-soft)}
.rpv-trio>.rpv-sec{border-top:0}
.rpv-trio>.rpv-sec+.rpv-sec{border-left:1px solid var(--line-soft)}
.rpv-terms{display:flex;flex-wrap:wrap;gap:8px;margin:0;padding:0;list-style:none}
.rpv-terms li{font-size:12.5px;padding:4px 10px;border:1px solid var(--line);border-radius:20px;color:var(--text)}
.rpv-terms li span{margin-left:6px;color:var(--faint);font-family:var(--mono);font-size:11.5px}
.rpv-note{margin:0;font-size:12.5px;line-height:1.6;color:var(--faint)}
.rpv-split{display:flex;height:8px;border-radius:4px;overflow:hidden;background:var(--sp-lane);margin-top:10px}
.rpv-split i{display:block;height:100%}
@media (max-width:1100px){
  .rpv-pair{grid-template-columns:1fr}
  .rpv-pair>.rpv-sec+.rpv-sec{border-left:0;border-top:1px solid var(--line-soft)}
  .rpv-trio{grid-template-columns:1fr 1fr}
  .rpv-trio>.rpv-sec:first-child{grid-column:1/-1}
  .rpv-trio>.rpv-sec+.rpv-sec{border-top:1px solid var(--line-soft)}
  .rpv-trio>.rpv-sec:nth-child(2){border-left:0}
}
@media (max-width:640px){
  .rpv-trio{grid-template-columns:1fr}
  .rpv-trio>.rpv-sec:nth-child(3){border-left:0}
}
@media (max-width:900px){
  .rpv-sec{padding:16px 14px 18px}
  .rpv-lede{font-size:16px}
  .rpv-stage .rpv-svg.wide{min-width:780px}
}
/* ---------- 数据分析 · 用户流量分析 (USER TRAFFIC) --------------------------
   回答「网关主机的带宽被谁、什么时候、用什么客户端占掉，能怎么调」。一屏从上到下：
   工具条一行（时间窗 · 读数 · 入口）→ 建议清单 → 逐小时堆叠图（前几位各一色）→
   用户表 → 九块角度面板。所有类名带 utf- 前缀，内层也带。
   主口径是「经网关」：请求体收一遍发一遍、应答收一遍发一遍；节点直连另列一数。 */
.utf-bar{display:grid;grid-template-columns:auto auto minmax(0,1fr) auto;align-items:center;gap:10px 16px;margin-bottom:16px}
.utf-bar .utf-stats{display:flex;flex-wrap:wrap;gap:6px 22px;min-width:0;font-size:12.5px;color:var(--dim)}
.utf-stats b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums;margin-left:5px}
.utf-stats .utf-host{color:var(--faint)}
.utf-acts{display:flex;align-items:center;flex-wrap:wrap;gap:8px;flex:none}
.utf-focus{display:inline-flex;align-items:center;gap:8px;padding:4px 6px 4px 12px;border-radius:999px;background:var(--brand);color:var(--btn-ink);font-size:12.5px;font-weight:600}
.utf-focus small{font-weight:400;opacity:.75}
.utf-focus button{border:0;background:var(--btn-ink);color:var(--brand);width:18px;height:18px;border-radius:50%;font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
.utf-note{margin:0 0 16px;font-size:12.5px;line-height:1.6;color:var(--faint);max-width:92ch}
/* 建议：一条一卡，级别只用左侧色条和一个小标，正文说人、量、原因。 */
.utf-advice{display:flex;flex-direction:column;gap:10px;margin-bottom:28px}
.utf-adv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:4px 14px;padding:14px 18px 14px 16px;border:1px solid var(--line);border-left-width:4px;border-radius:var(--r-lg);background:var(--surface)}
.utf-adv.high{border-left-color:var(--bad-fill)}
.utf-adv.mid{border-left-color:var(--warn-fill)}
.utf-adv.low{border-left-color:var(--brand-a45)}
.utf-adv .lvl{grid-row:1/3;align-self:start;margin-top:2px;font-size:10.5px;font-weight:600;letter-spacing:.04em;padding:3px 9px;border-radius:999px;white-space:nowrap;background:var(--fill-3);color:var(--dim)}
.utf-adv.high .lvl{background:var(--bad-a12);color:var(--bad)}
.utf-adv.mid .lvl{background:var(--warn-a14,rgba(255,146,48,.14));color:var(--warn)}
.utf-adv b{font-size:13.5px;font-weight:600;color:var(--text);line-height:1.5}
.utf-adv p{margin:0;font-size:12.5px;line-height:1.65;color:var(--dim)}
.utf-adv .utf-who{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.utf-adv .utf-who button{font-size:11.5px;padding:3px 10px;border-radius:999px;border:1px solid var(--line);background:transparent;color:var(--text);cursor:pointer}
.utf-adv .utf-who button:hover{background:var(--fill-3)}
.utf-calm{padding:14px 18px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);font-size:13px;color:var(--dim);margin-bottom:28px}
/* 面板通用：标题行 + 内容区，和 rpv-sec 同一组数。 */
.utf-sec{padding:18px 22px 20px;min-width:0}
.utf-sec h3{margin:0;font-family:var(--disp);font-size:14px;font-weight:600;color:var(--text);letter-spacing:-.005em}
.utf-sec .sub{margin:5px 0 0;font-size:12.5px;line-height:1.6;color:var(--dim);max-width:72ch}
.utf-hd{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:8px 20px;margin-bottom:14px}
.utf-hd>div{min-width:0}
.utf-legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--dim)}
.utf-legend span{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.utf-legend i{display:inline-block;width:10px;height:10px;border-radius:3px;flex:none}
.utf-legend i.node{background:repeating-linear-gradient(135deg,var(--s2) 0 2px,transparent 2px 4px);outline:1px solid var(--s2);outline-offset:-1px}
/* 逐小时堆叠图：SVG 宽 960 等比缩放，窄栏横滚。 */
.utf-stage{position:relative;overflow-x:auto;min-width:0}
.utf-svg{width:100%;height:auto;display:block}
.utf-svg text{font-family:var(--sans)}
.utf-svg .ax{fill:var(--faint);font-size:11px;font-family:var(--mono);font-variant-numeric:tabular-nums}
.utf-svg .grid{stroke:var(--line-soft);stroke-width:1}
.utf-svg .grid-day{stroke:var(--line);stroke-width:1}
.utf-svg .seg{shape-rendering:crispEdges}
.utf-svg .seg.rest{fill:var(--faint-a55)}
.utf-svg .seg.node{fill:var(--s2);opacity:.45}
.utf-svg .cursor{fill:var(--text-a8)}
.utf-svg .avg{stroke:var(--text-a60);stroke-width:1.2;stroke-dasharray:5 5}
.utf-svg .avg-lab{fill:var(--text);font-size:11.5px;paint-order:stroke;stroke:var(--surface);stroke-width:4px;stroke-linejoin:round}
/* 用户表：名字列定宽让步，其余列数字右对齐。整行可点进单人视图。 */
.utf-table{width:100%;border-collapse:separate;border-spacing:0;font-size:12.5px}
.utf-table th{text-align:left;font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--faint);padding:8px 12px;border-bottom:1px solid var(--line);white-space:nowrap;background:var(--surface-2);position:sticky;top:0;z-index:1}
.utf-table td{padding:9px 12px;border-bottom:1px solid var(--line-soft);vertical-align:middle;white-space:nowrap}
.utf-table th.num,.utf-table td.num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--mono);font-size:12px}
.utf-table tbody tr{cursor:pointer}
.utf-table tbody tr:hover td{background:var(--fill-3)}
.utf-table tbody tr.on td{background:var(--brand-a5)}
.utf-table td.utf-name{min-width:170px;max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.utf-table td.utf-name b{font-weight:600;color:var(--text)}
.utf-table td.utf-name small{margin-left:8px;color:var(--faint);font-size:11.5px}
.utf-table td.utf-name i{display:inline-block;width:9px;height:9px;border-radius:3px;margin-right:8px;vertical-align:-1px}
.utf-table .trk{display:inline-block;width:72px;height:6px;border-radius:3px;background:var(--sp-lane);vertical-align:middle;margin-right:8px;overflow:hidden}
.utf-table .trk i{display:block;height:100%;border-radius:3px;background:var(--brand)}
.utf-table .flag{color:var(--warn);font-weight:600}
.utf-table .dimc{color:var(--faint)}
.utf-table .mv{color:var(--ok);font-weight:600}
.utf-table tfoot td{border-top:1px solid var(--line);border-bottom:0;font-weight:600;color:var(--text);background:var(--surface-2)}
.utf-more{display:block;width:100%;padding:10px;border:0;background:transparent;color:var(--dim);font-size:12.5px;cursor:pointer}
.utf-more:hover{background:var(--fill-3)}
/* 角度面板：三列网格，每块一张条形清单。 */
.utf-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;margin-top:28px}
.utf-grid .panel{min-width:0}
.utf-list{display:flex;flex-direction:column;gap:9px;margin:0;padding:0;list-style:none}
.utf-list li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 12px;align-items:baseline}
.utf-list .lb{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;color:var(--text)}
.utf-list .lb small{margin-left:7px;color:var(--faint);font-size:11px}
.utf-list .amt{font-family:var(--mono);font-size:11.5px;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap}
.utf-list .amt span{color:var(--faint);margin-left:7px}
.utf-list .trk{grid-column:1/-1;height:5px;border-radius:3px;background:var(--sp-lane);overflow:hidden;display:flex}
.utf-list .trk i{display:block;height:100%;background:var(--brand)}
.utf-list .trk i.node{background:var(--s2);opacity:.55}
.utf-list li.rest .lb{color:var(--dim)}
.utf-list li.rest .trk i{background:var(--faint-a55)}
.utf-cap{margin:16px 0 8px;font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--faint)}
.utf-cap:first-child{margin-top:0}
.utf-empty{padding:18px 0;color:var(--faint);font-size:12.5px;text-align:center}
@media (max-width:1320px){
  .utf-bar{grid-template-columns:auto minmax(0,1fr) auto}
  .utf-bar .utf-stats{grid-column:1/-1;grid-row:2}
}
@media (max-width:1180px){.utf-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:900px){
  .utf-sec{padding:14px 14px 16px}
  .utf-stage .utf-svg{min-width:760px}
  .utf-table th.opt,.utf-table td.opt{display:none}
}
@media (max-width:720px){.utf-grid{grid-template-columns:1fr}}
/* ---------- 数据分析 · 用户价值 (USER VALUE) --------------------------------
   回答「哪些付费用户最值得留住，眼下谁需要有人去联系」。一屏从上到下：工具条一行
   （时间窗 · 付费方式 · 搜索 · 读数）→ 五个总数 → 价值地图 + 四个客群 → 跟进清单 →
   价值名册 → 付费意愿 + 加购机会 → 每月收入 + 集中度 → 留存分群。个人画像走右侧抽屉。
   订阅用 --s1、API 额度用 --s2，两样在每张图里都是同一个颜色。类名全部带 uv- 前缀。 */
.uv-bar{display:grid;grid-template-columns:auto auto minmax(160px,240px) minmax(0,1fr) auto;align-items:center;gap:10px 14px;margin-bottom:12px}
.uv-search{height:30px;padding:0 12px;border:1px solid var(--line);border-radius:var(--r-pill);background:var(--surface);color:var(--text);font:inherit;font-size:12.5px;min-width:0;width:100%}
.uv-meta{min-width:0;font-size:12.5px;color:var(--dim);display:flex;flex-wrap:wrap;gap:4px 18px}
.uv-meta b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums;margin-left:4px}
.uv-acts{display:flex;align-items:center;gap:8px;flex:none}
.uv-note{margin:0 0 18px;font-size:12.5px;line-height:1.65;color:var(--faint);max-width:110ch}
.uv-note b{color:var(--dim);font-weight:600}
/* 五个总数 */
.uv-kpis{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px;margin-bottom:24px}
.uv-kpi{display:flex;flex-direction:column;gap:6px;min-width:0;padding:15px 18px 16px;border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface);box-shadow:var(--card-shadow)}
.uv-kpi .l{font-size:12px;color:var(--dim)}
.uv-kpi .v{font-family:var(--disp);font-size:24px;font-weight:600;letter-spacing:-.02em;line-height:1.15;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.uv-kpi .v small{font-size:13px;font-weight:500;color:var(--dim);margin-left:3px;letter-spacing:0}
.uv-kpi .s{font-size:12px;line-height:1.5;color:var(--faint);font-variant-numeric:tabular-nums}
.uv-kpi .s b{color:var(--text);font-weight:600}
.uv-kpi .s .bad{color:var(--bad);font-weight:600}
.uv-mix{display:flex;gap:2px;height:6px;border-radius:3px;overflow:hidden;background:var(--sp-lane)}
.uv-mix i{display:block;height:100%}
.uv-mix .ms,.uv-key.ms{background:var(--s1)}
.uv-mix .ma,.uv-key.ma{background:var(--s2)}
/* 面板 */
.uv-sec{padding:18px 22px 20px;min-width:0}
.uv-sec h3{margin:0;font-family:var(--disp);font-size:14px;font-weight:600;color:var(--text);letter-spacing:-.005em}
.uv-sec .sub{margin:5px 0 0;font-size:12.5px;line-height:1.6;color:var(--dim);max-width:76ch}
.uv-hd{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:8px 20px;margin-bottom:14px}
.uv-hd>div{min-width:0}
.uv-row{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(0,1fr);gap:14px;margin-bottom:24px}
.uv-row.even{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.uv-row>.panel{min-width:0}
.uv-block{margin-bottom:24px}
.uv-legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--dim)}
.uv-legend span{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.uv-key{display:inline-block;width:10px;height:10px;border-radius:3px;flex:none}
.uv-key.dot{border-radius:50%;background:var(--s1)}
.uv-key.ring0{border-radius:50%;box-shadow:inset 0 0 0 2px var(--bad-fill)}
.uv-key.ring1{border-radius:50%;box-shadow:inset 0 0 0 2px var(--warn-fill)}
.uv-stage{position:relative;min-width:0}
.uv-svg{width:100%;height:auto;display:block;overflow:visible}
.uv-svg text{font-family:var(--sans)}
.uv-svg .ax{fill:var(--faint);font-size:11px;font-variant-numeric:tabular-nums}
.uv-svg .axt{fill:var(--dim);font-size:11.5px}
.uv-svg .gl{stroke:var(--line-soft);stroke-width:1}
.uv-svg .thr{stroke:var(--faint);stroke-width:1;stroke-dasharray:4 4}
.uv-svg .ql{font-size:12px;font-weight:600}
.uv-svg .dl{fill:var(--text);font-size:11px;paint-order:stroke;stroke:var(--surface);stroke-width:3px;stroke-linejoin:round}
.uv-svg .pt{cursor:pointer}
.uv-svg .pt:focus-visible{outline:none}
.uv-svg .pt:focus-visible .dot{stroke:var(--text)}
.uv-empty{padding:28px 0;color:var(--faint);font-size:12.5px;text-align:center;line-height:1.6}
/* 四个客群 */
.uv-segs{display:flex;flex-direction:column;gap:10px}
.uv-sg{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 12px;width:100%;padding:12px 14px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);color:var(--text);text-align:left;font:inherit;cursor:pointer}
.uv-sg:hover{border-color:var(--brand-a26)}
.uv-sg.on{border-color:var(--text);box-shadow:0 0 0 1px var(--text) inset}
.uv-sg .ct{font-family:var(--disp);font-size:17px;font-weight:600;text-align:right;font-variant-numeric:tabular-nums}
.uv-sg .ct small{font-size:12px;font-weight:500;color:var(--dim);margin-left:2px}
.uv-sg .ds{grid-column:1/-1;font-size:12px;line-height:1.55;color:var(--dim)}
.uv-sg .ac{grid-column:1/-1;font-size:12px;line-height:1.55;color:var(--text)}
.uv-sg .ac span{color:var(--faint);margin-right:6px}
.uv-foot{margin:12px 0 0;font-size:12px;line-height:1.6;color:var(--faint)}
/* 小标签：客群、信号、等级 */
.uv-pill{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:var(--r-pill);font-size:11.5px;font-weight:500;line-height:1.5;white-space:nowrap;background:var(--fill-3);color:var(--dim)}
.uv-pill.core{background:var(--brand-a12);color:var(--text)}
.uv-pill.rescue,.uv-pill.warn{background:var(--warn-a12);color:var(--warn)}
.uv-pill.bad{background:var(--bad-a12);color:var(--bad)}
.uv-pill.good{background:var(--ok-a12);color:var(--ok)}
.uv-pill.grow{background:var(--fill-3);color:var(--text)}
.uv-pill.watch,.uv-pill.unpaid,.uv-pill.dim{background:var(--fill-4);color:var(--faint)}
.uv-pills{display:flex;flex-wrap:wrap;gap:4px;align-items:center}
/* 跟进清单 */
.uv-queue{display:flex;flex-direction:column}
.uv-qrow{display:grid;grid-template-columns:4px 88px minmax(130px,190px) minmax(0,1.35fr) minmax(0,1fr) auto;gap:12px 14px;align-items:center;padding:13px 0;border-top:1px solid var(--line-soft)}
.uv-qrow:first-child{border-top:0;padding-top:2px}
.uv-qrow .stripe{align-self:stretch;border-radius:2px;background:var(--line)}
.uv-qrow.P0 .stripe{background:var(--bad-fill)}
.uv-qrow.P1 .stripe{background:var(--warn-fill)}
.uv-qrow .who{display:flex;flex-direction:column;gap:4px;min-width:0}
.uv-qrow .who button{all:unset;cursor:pointer;font-weight:600;color:var(--text);font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.uv-qrow .who button:hover{text-decoration:underline}
.uv-qrow .who button:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.uv-qrow .who small{font-size:12px;color:var(--faint);display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.uv-qrow .why{font-size:13px;line-height:1.55;color:var(--text);min-width:0}
.uv-qrow .why small{display:block;margin-top:3px;font-size:12px;color:var(--faint)}
.uv-qrow .how{font-size:12.5px;line-height:1.55;color:var(--dim);min-width:0}
.uv-qrow .how b{color:var(--ok);font-weight:600;font-variant-numeric:tabular-nums}
.uv-qrow .how b.neg{color:var(--warn)}
.uv-qrow .btns{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
.uv-qmore{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding-top:14px;border-top:1px solid var(--line-soft);font-size:12px;color:var(--faint)}
/* 名册 */
.uv-rhd{padding:18px 22px 14px;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 18px}
.uv-chips{display:flex;flex-wrap:wrap;gap:6px}
.uv-scroll{overflow-x:auto}
.uv-table{width:100%;border-collapse:separate;border-spacing:0;font-size:12.5px}
.uv-table th{text-align:left;font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--faint);padding:8px 10px;border-bottom:1px solid var(--line);white-space:nowrap;background:var(--surface-2);position:sticky;top:0;z-index:1}
.uv-table th.srt{cursor:pointer;user-select:none}
.uv-table th.srt:hover,.uv-table th.srt.on{color:var(--text)}
.uv-table th.srt.on::after{content:" ↓"}
.uv-table th.srt.on.asc::after{content:" ↑"}
.uv-table td{padding:10px;border-bottom:1px solid var(--line-soft);vertical-align:middle;white-space:nowrap}
.uv-table .num{text-align:right;font-variant-numeric:tabular-nums}
.uv-table tbody tr{cursor:pointer}
.uv-table tbody tr:hover td{background:var(--fill-4)}
.uv-table tbody tr.on td{background:var(--brand-a5)}
.uv-table tbody tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.uv-table .n1{font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px;max-width:220px;overflow:hidden;text-overflow:ellipsis}
.uv-table .n2{margin-top:3px;font-size:11.5px;color:var(--faint);max-width:220px;overflow:hidden;text-overflow:ellipsis}
.uv-table .n2 .uv-pill{padding:0 6px;font-size:11px;margin-right:2px}
.uv-table .num .n2{margin-left:auto}
.uv-table .uv-mix{width:96px;margin:5px 0 0 auto}
.uv-table .hot{color:var(--bad);font-weight:600}
.uv-spark{display:block}
.uv-strip{display:inline-grid;grid-template-columns:repeat(30,2px);gap:1px;vertical-align:middle;margin-right:8px}
.uv-strip i{display:block;height:14px;border-radius:1px;background:var(--sp-lane)}
.uv-strip i.a{background:var(--s1)}
.uv-hs{display:inline-flex;align-items:center;gap:6px;font-weight:600;font-variant-numeric:tabular-nums}
.uv-hs i{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--faint)}
.uv-hs.good i{background:var(--ok-fill)}
.uv-hs.low i{background:var(--warn-fill)}
.uv-more{display:block;width:100%;padding:10px;border:0;background:transparent;color:var(--dim);font-size:12.5px;cursor:pointer}
.uv-more:hover{background:var(--fill-3)}
/* 付费意愿阶梯 */
.uv-ladder{display:flex;flex-direction:column;gap:10px}
.uv-step{display:grid;grid-template-columns:104px minmax(0,1fr) 96px;gap:12px;align-items:center;font-size:12.5px}
.uv-step .lb{color:var(--dim)}
.uv-step .bar{height:14px;border-radius:5px;background:var(--sp-lane);overflow:hidden}
.uv-step .bar i{display:block;height:100%;border-radius:5px;background:var(--s1)}
.uv-step .pc{text-align:right;color:var(--dim);font-variant-numeric:tabular-nums}
.uv-step .pc b{color:var(--text);font-weight:600}
.uv-ups{display:flex;flex-direction:column}
.uv-up{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 14px;align-items:baseline;padding:10px 0;border-top:1px solid var(--line-soft);font-size:12.5px}
.uv-up:first-child{border-top:0;padding-top:0}
.uv-up button{all:unset;cursor:pointer;font-weight:600;color:var(--text)}
.uv-up button:hover{text-decoration:underline}
.uv-up button:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.uv-up .what{color:var(--dim);margin-left:6px}
.uv-up .ev{grid-column:1/-1;font-size:12px;color:var(--faint);line-height:1.55}
.uv-up .gain{font-weight:600;font-variant-numeric:tabular-nums;color:var(--ok);white-space:nowrap}
.uv-up .gain.neg{color:var(--warn)}
.uv-up .gain.na{color:var(--faint);font-weight:500}
/* 留存分群 */
.uv-cohort{border-collapse:separate;border-spacing:4px;font-size:12.5px}
.uv-cohort th{font-size:11px;font-weight:600;color:var(--faint);padding:4px 6px;text-align:center;white-space:nowrap}
.uv-cohort th:first-child{text-align:left}
.uv-cohort td{padding:9px 8px;text-align:center;font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:var(--r-xs)}
.uv-cohort td.lb{text-align:left;color:var(--dim);padding-left:2px}
.uv-cohort td.c{min-width:72px;color:var(--text)}
.uv-cohort td.c small{display:block;font-size:10.5px;color:var(--dim);margin-top:1px}
.uv-cohort td.na{color:var(--faint);font-size:11.5px}
/* 个人画像（抽屉里） */
.uv-pf{display:flex;flex-direction:column;gap:18px}
.uv-pf-top{display:flex;flex-direction:column;gap:8px}
.uv-pf-top .idl{font-size:12.5px;color:var(--dim);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.uv-alert{padding:12px 14px;border-radius:var(--r-lg);background:var(--fill-4);font-size:12.5px;line-height:1.6;color:var(--text);display:flex;flex-direction:column;gap:6px}
.uv-alert.P0{background:var(--bad-a10)}
.uv-alert.P1{background:var(--warn-a12)}
.uv-alert div{display:flex;gap:8px;align-items:baseline}
.uv-alert small{color:var(--dim)}
.uv-facts{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}
.uv-fact{padding:11px 13px;border-radius:var(--r-md);background:var(--surface-2);border:1px solid var(--line-soft);min-width:0}
.uv-fact .l{font-size:11.5px;color:var(--faint)}
.uv-fact .v{margin-top:2px;font-size:16px;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
.uv-fact .ft{margin-top:2px;font-size:11.5px;color:var(--faint);font-variant-numeric:tabular-nums}
.uv-pf-cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:18px 22px}
.uv-mh{margin:0 0 8px;font-size:11.5px;font-weight:600;letter-spacing:.02em;color:var(--faint)}
.uv-mh:not(:first-child){margin-top:18px}
.uv-hp{display:grid;grid-template-columns:64px minmax(0,1fr) 54px;gap:10px;align-items:center;font-size:12.5px;margin-bottom:7px}
.uv-hp span:first-child{color:var(--dim)}
.uv-hp .tr{height:6px;border-radius:3px;background:var(--sp-lane);overflow:hidden}
.uv-hp .tr i{display:block;height:100%;border-radius:3px;background:var(--s1)}
.uv-hp .nv{text-align:right;font-variant-numeric:tabular-nums;color:var(--text)}
.uv-subs{display:flex;flex-direction:column;gap:6px}
.uv-subr{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 10px;align-items:center;padding:9px 11px;border-radius:var(--r-md);background:var(--surface-2);border:1px solid var(--line-soft);font-size:12.5px}
.uv-subr .m{grid-column:1/-1;font-size:11.5px;color:var(--faint)}
.uv-kv{display:flex;flex-direction:column;gap:6px;font-size:12.5px;color:var(--dim);line-height:1.55}
.uv-kv b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.uv-fu{display:flex;flex-direction:column;gap:10px;padding:14px;border-radius:var(--r-lg);border:1px solid var(--line)}
.uv-fu select,.uv-fu textarea{font:inherit;font-size:12.5px;color:var(--text);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);padding:7px 10px;width:100%}
.uv-fu textarea{min-height:64px;resize:vertical;line-height:1.55}
.uv-fu .row{display:flex;gap:8px;align-items:center;justify-content:space-between;flex-wrap:wrap}
.uv-fu .row small{font-size:11.5px;color:var(--faint)}
.uv-log{display:flex;flex-direction:column;gap:8px;font-size:12.5px;color:var(--dim);line-height:1.55}
.uv-log div b{color:var(--text);font-weight:600;margin-right:6px}
.uv-log div small{color:var(--faint);margin-left:6px}
@media (max-width:1320px){
  .uv-bar{grid-template-columns:auto auto minmax(140px,1fr) auto}
  .uv-bar .uv-meta{grid-column:1/-1;grid-row:2}
}
@media (max-width:1200px){
  .uv-kpis{grid-template-columns:repeat(3,minmax(0,1fr))}
  .uv-qrow{grid-template-columns:4px 80px minmax(0,1fr) minmax(0,1.3fr);}
  .uv-qrow .how{grid-column:3/5}
  .uv-qrow .btns{grid-column:3/5;justify-content:flex-start}
  .uv-qrow .stripe{grid-row:1/span 3}
}
@media (max-width:1000px){.uv-row,.uv-row.even{grid-template-columns:minmax(0,1fr)}}
@media (max-width:860px){
  .uv-bar{grid-template-columns:minmax(0,1fr)}
  .uv-bar>*{grid-column:auto!important;grid-row:auto!important}
  .uv-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}
  .uv-sec,.uv-rhd{padding-left:14px;padding-right:14px}
  .uv-qrow{grid-template-columns:4px minmax(0,1fr)}
  .uv-qrow>*:not(.stripe){grid-column:2!important}
  .uv-qrow .stripe{grid-row:1/span 5}
  .uv-facts{grid-template-columns:repeat(2,minmax(0,1fr))}
  .uv-pf-cols{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:480px){.uv-kpis{grid-template-columns:minmax(0,1fr)}}
@media (max-width:860px){
  .rp-catalog{margin-left:-12px;margin-right:-12px;padding:4px 12px 6px}
}
@media (pointer:coarse){
  .rp-menu button{height:36px;padding:0 12px}
}
@media (max-width:900px){
  .rp-speed-main{padding:12px 12px 14px}
}

/* ---------- 边车舰队 / SIDECAR FLEET ---------------------------------------
   这一页画的东西和机架上的别处都不一样：这些机器不在这个机柜里。它们在别人的
   IDC，网关只能通过两条细线摸到它们——一条是它们每 30 秒主动打回来的心跳，
   一条是我们存着的 SSH。所以整页的主角是「时间」而不是「此刻」：

     心跳带（.sd-tape）是签名元素。一格 = 10 分钟。有心跳画满格，没心跳画一条
     贴底的短横——不是留白。留白读起来像「这段没数据」，而没数据恰恰就是故障
     本身：一台死掉的机器唯一能留下的证据，就是它什么都没留下。

     全舰队共用一条时间轴。所有节点的带子等宽、同起点，于是「三台同时断」和
     「一台断了三次」在版面上长得完全不同——前者是一根竖着的缺口，后者是一行
     里的三个缺口。这一条是这页存在的理由，别在任何改动里把对齐弄丢。

   颜色只说种类，高度说有无：即使把颜色全抽掉，带子依然读得出来。 */
.sd-deck{border:1px solid var(--line);border-radius:var(--r-xl);background:var(--card-grad);
  box-shadow:var(--card-shadow);padding:16px 20px 12px;margin-bottom:14px}
.sd-deck-hd{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:11px}
.sd-deck-t{font-family:var(--disp);font-size:14px;font-weight:600;letter-spacing:.01em;color:var(--text)}
.sd-deck-t small{font-family:var(--sans);font-weight:500;font-size:11.5px;color:var(--faint);
  letter-spacing:0;margin-left:9px}
.sd-deck-r{font-family:var(--mono);font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums;
  white-space:nowrap}
.sd-deck-r b{color:var(--text);font-weight:600}
/* 舰队脉搏：每格的高度 = 那 10 分钟里在线的节点占比。整格空 = 全舰队同时哑了，
   那是网关侧的问题，不是某一台的问题——这一眼是分不出来也没别的地方能分的。 */
.sd-pulse{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:1px;height:46px;
  align-items:end;background-image:repeating-linear-gradient(90deg,var(--line-soft) 0 1px,transparent 1px 25%);
  border-bottom:1px solid var(--line)}
/* 甲板和逐台的带子用同一套颜色，这样一道竖着的缺口能从上往下对穿——那正是
   「大家一起断的」和「这一台自己断的」唯一分得开的地方。 */
.sd-pulse i{display:block;border-radius:1.5px 1.5px 0 0;background:var(--ok-a60);min-height:1px}
.sd-pulse i.part{background:var(--warn-a70)}
.sd-pulse i.none{background:var(--bad-a45);height:3px!important;border-radius:0}
.sd-pulse i.void{background:var(--faint-a22);height:2px!important;border-radius:0}
/* 时间轴归甲板所有：它是整页共用的那一条，不属于任何一台。 */
/* 舰队此刻的吞吐。挂在时间轴下面而不是标题右边：标题右边那一行答的是「这 24
   小时靠不靠得住」，这一行答的是「此刻在拉多少活」。两个时间尺度挤进同一行，
   读的人得先分辨哪个数属于哪一段时间，而那正是这两行本该替他做的事。 */
.sd-now{display:flex;flex-wrap:wrap;gap:7px 20px;margin-top:13px;padding-top:12px;
  border-top:1px solid var(--line-soft);font-size:11.5px;color:var(--dim)}
.sd-now b{font-family:var(--mono);color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.sd-now .bad{color:var(--bad)}
.sd-axis{display:flex;justify-content:space-between;font-family:var(--mono);font-size:10px;
  letter-spacing:.05em;color:var(--faint);padding-top:6px}
.sd-legend{display:flex;flex-wrap:wrap;gap:7px;align-items:center;margin-top:12px;
  padding-top:11px;border-top:1px solid var(--line-soft)}
.sd-legend .sp{flex:1}
/* 状态计数同时是筛选器。点一下只看这一类；再点回到全部。 */
.sd-cnt{display:inline-flex;align-items:center;gap:7px;padding:4px 11px;border-radius:999px;
  border:1px solid var(--line);background:transparent;font-size:12px;font-weight:600;color:var(--dim);
  transition:border-color .14s ease,color .14s ease,background .14s ease}
.sd-cnt:hover{color:var(--text);border-color:var(--brand-55-on-line);background:var(--brand-dim)}
.sd-cnt.on{color:var(--text);border-color:var(--brand-a60);background:var(--brand-dim)}
.sd-cnt.mute{opacity:.5}
.sd-cnt b{font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--text)}
.sd-cnt i{width:7px;height:7px;border-radius:50%;flex:none;background:var(--faint)}
.sd-cnt.online i{background:var(--ok);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--ok-ahalo)}
.sd-cnt.down i{background:var(--bad);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--bad-ahalo)}
.sd-cnt.nologin i,.sd-cnt.behind i,.sd-cnt.strained i{background:var(--warn);box-shadow:0 0 var(--halo-blur) var(--halo-spread) var(--warn-ahalo)}
.sd-cnt.updating i{background:var(--brand)}

/* 网关主机卡：复用甲板的壳和节点仪表的形状。它排在甲板正下方——先看整体
   脉搏，再看码头本身，然后才轮到逐台的船。 */
.sd-host{padding-bottom:16px}
.sd-host .sd-gauges{margin-top:2px}

/* 待办条：只有真有事时才存在。没有「一切正常」这一条——那种横幅看第二次就没人读了。 */
.sd-todo{display:flex;align-items:center;gap:12px;padding:11px 15px;border-radius:12px;
  border:1px solid var(--bad-a40);background:var(--bad-a7);margin-bottom:12px;flex-wrap:wrap}
.sd-todo.warn{border-color:var(--warn-a40);background:var(--warn-a5)}
.sd-todo-m{display:flex;align-items:flex-start;gap:12px;flex:1;min-width:240px}
.sd-todo-i{flex:none;width:22px;height:22px;border-radius:7px;display:grid;place-items:center;
  background:var(--bad-a16);color:var(--bad);font-weight:700;font-size:13px;margin-top:1px}
.sd-todo.warn .sd-todo-i{background:var(--warn-a16);color:var(--warn)}
.sd-todo-x{flex:1;min-width:0;font-size:12.5px;color:var(--text);line-height:1.55;
  overflow-wrap:anywhere}
.sd-todo-x b{font-family:var(--mono);font-weight:600}
.sd-todo-x span{color:var(--dim)}

/* ---- 一台多账号服务器 = 一个机箱 ----
   同一台服务器上的几张卡装进一个浅底圆角框，框头说这是哪台。框只给多账号的服务器：
   单账号服务器排在框外，一张卡就是一台，套个框反倒像是它们几台是一伙的。
   圆角取卡片圆角加内边距，内外两层圆角同心。 */
.sd-box{border:1px solid var(--line);border-radius:calc(var(--r-xl) + 12px);background:var(--faint-a5);
  padding:14px 12px 12px;margin-bottom:16px}
.sd-box-hd{padding:0 6px 14px}
.sd-box-l1{display:flex;align-items:center;gap:8px 12px;flex-wrap:wrap}
.sd-box-ic{display:inline-flex;width:18px;height:18px;color:var(--dim);flex:none}
.sd-box-ic svg{width:100%;height:100%}
.sd-box-nm{font-family:var(--disp);font-size:17px;font-weight:600;letter-spacing:.01em;color:var(--text)}
.sd-box-mode{font-size:11px;font-weight:600;letter-spacing:.02em;padding:2px 9px;border-radius:6px;
  color:var(--dim);background:var(--faint-a12)}
/* 状态灯：一个账号一格，顺序和框里的卡片相同，像机箱前面板上一排硬盘灯。 */
.sd-bays{display:inline-flex;align-items:center;gap:3px}
.sd-bays i{display:block;width:7px;height:15px;border-radius:2px;background:var(--faint-a42)}
.sd-bays i.st-online{background:var(--ok)} .sd-bays i.st-down{background:var(--bad)}
.sd-bays i.st-nologin,.sd-bays i.st-never{background:var(--warn)} .sd-bays i.st-updating{background:var(--brand)}
.sd-bays i.st-off{background:transparent;box-shadow:inset 0 0 0 1px var(--faint-a42)}
.sd-box-acts{margin-left:auto;display:flex;gap:7px}
.sd-box-meta{display:flex;flex-wrap:wrap;gap:4px 0;margin-top:7px;font-size:12px;color:var(--dim);
  font-variant-numeric:tabular-nums}
.sd-box-meta>span+span::before{content:"·";color:var(--faint);margin:0 8px}
.sd-box-meta .badv{color:var(--bad);font-weight:600} .sd-box-meta .warnv{color:var(--warn);font-weight:600}
.sd-box-hd .sd-gauges{margin-top:13px}
.sd-solo-hd{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:6px 6px 12px}
.sd-solo-t{font-family:var(--disp);font-size:14px;font-weight:600;color:var(--text)}
.sd-solo-hd small{font-size:11.5px;color:var(--faint)}
/* 退役的节点账号：页底收起的一行，展开是名单。 */
.sd-retired{margin:8px 0;border:1px dashed var(--line);border-radius:var(--r-xl);padding:14px 18px}
.sd-retired>summary{cursor:pointer;font-size:13px;font-weight:600;color:var(--dim)}
.sd-retired>summary small{font-size:11.5px;font-weight:400;color:var(--faint);margin-left:10px}
.sd-ret-list{list-style:none;margin:12px 0 2px;padding:0;display:flex;flex-direction:column;gap:8px}
.sd-ret-row{display:flex;align-items:center;gap:8px 14px;flex-wrap:wrap;font-size:12.5px}
.sd-ret-nm{color:var(--text)}
.sd-ret-why{color:var(--faint);flex:1;min-width:0}

/* ---- 一台节点 = 一个机位 ----
   机位按卡片宽度自动排列，一排最多四台，展开的那一台横跨整排。分界是**读法换了**，
   不是宽度够不够：收起来的卡片是拿来互相比的（十一台机器竖着扫一列仪表，哪台不对
   一眼就出来），一排多放几台，一屏能比的台数跟着翻倍；展开之后读的是一台，而那里
   面的三档窗口对照表要的是宽度——它存在的全部理由就是同一个读数在 1 分钟 / 5 分钟
   / 1 小时下的三个值要能横着一眼比出来，塞进半个卡片就只剩横向滚动。

   列数不按窗口宽度写死：侧栏、抽屉和浏览器缩放都会改变这块板实际拿到的宽度。
   一台不窄于 480px（半张卡片还放得下四个仪表和心跳带右边那行读数的地方），
   一排最多四台——所以 1280 到 1680 的屏两列、1920 三列、2300 以上四列封顶，
   窄屏退回一列。
   卡片内部跟着自己的宽度换排法（见本节末尾的 @container），不跟窗口。 */
/* 竖向 11px、横向 16px：两张卡片左右挨着，各自还带一道边框和一层投影，等宽的沟
   会让它们读成一整块（本页两列网格的老规矩，set-grid 16/20、pt-nums 14/16）。 */
/* 同一排的卡片逐行对齐：每张卡是一个 subgrid，七条横轨（名字行、账号行、机器行、
   心跳与流量、紧张判决、两排仪表、展开的详情）由同一排的几张卡共用。哪张卡的
   旗子折了行、哪台的读数长一截，那一条轨对整排一起长高，心跳带和仪表仍落在同
   一条水平线上，竖着扫一列读数才对得上行。没有判决、没展开的卡，那两条轨是
   零高——空着的那一条就是「这台没有判决」。
   栅格自己不设行距：行距会落进卡片内部的每一条轨之间，空轨两侧的那两份没有
   子项能抵掉（Chrome 实测会多出 11px）。排与排之间的 11px 由卡片的 margin-bottom
   给，栅格末尾再收回一份；卡片内部各段之间的距离仍由各段自己的 margin-top 定。 */
.sd-rack{display:grid;gap:0 16px;align-items:stretch;margin-bottom:-11px;
  grid-template-columns:repeat(auto-fill,minmax(min(100%,max(480px,calc((100% - 48px)/4))),1fr))}
.sd-unit.open{grid-column:1/-1}
.sd-unit{position:relative;border:1px solid var(--line);border-radius:var(--r-xl);background:var(--card-grad);
  box-shadow:var(--card-shadow);padding:15px 18px 14px 21px;margin-bottom:11px;overflow:hidden;
  display:grid;grid-template-rows:subgrid;grid-row:span 7;row-gap:0;align-content:start}
/* 卡片上不能再挂 container（inline-size 包含会让 subgrid 退化成 none，Chrome 实测）。
   卡片宽度本来就是栅格按窗口算出来的，窄屏的换排法改按窗口判（见本节末尾的 @media）。 */
/* min-width:0：卡片里每一段都是栅格项，默认的 auto 最小宽度会把一行不折行的长邮箱
   当成这段的最小宽度，整段顶出卡片右边被裁掉，而不是在省略号处停下。 */
.sd-unit>*{min-width:0}
.sd-unit>.sd-hd{grid-row:1} .sd-unit>.sd-acct{grid-row:2} .sd-unit>.sd-addr{grid-row:3}
.sd-unit>.sd-tape-wrap{grid-row:4} .sd-unit>.sd-strain{grid-row:5} .sd-unit>.sd-dash{grid-row:6}
.sd-unit>.sd-more{grid-row:7}
.sd-unit::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--sdc,var(--faint))}
.sd-unit.st-online{--sdc:var(--ok)} .sd-unit.st-down{--sdc:var(--bad)}
.sd-unit.st-nologin{--sdc:var(--warn)} .sd-unit.st-never{--sdc:var(--warn)}
.sd-unit.st-updating{--sdc:var(--brand)} .sd-unit.st-off{--sdc:var(--faint)}
.sd-unit.st-off{opacity:.72}
.sd-hd{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;flex-wrap:wrap}
.sd-nm{display:flex;align-items:center;gap:9px;flex-wrap:wrap;min-width:0;flex:1}
.sd-nm .sd-name{font-family:var(--disp);font-size:15px;font-weight:600;letter-spacing:.015em;color:var(--text)}
.sd-st{font-size:11px;font-weight:600;letter-spacing:.02em;padding:2px 9px;border-radius:6px;
  color:var(--faint);background:var(--faint-a12)}
.sd-unit.st-online .sd-st{color:var(--ok);background:var(--ok-a12)}
.sd-unit.st-down .sd-st{color:var(--bad);background:var(--bad-a12)}
.sd-unit.st-nologin .sd-st,.sd-unit.st-never .sd-st{color:var(--warn);background:var(--warn-a13)}
.sd-unit.st-updating .sd-st{color:var(--brand-2);background:var(--brand-a15)}
.sd-flag{font-size:10.5px;font-weight:600;letter-spacing:.02em;padding:2px 8px;border-radius:6px;
  border:1px solid var(--line);color:var(--faint)}
.sd-flag.up{color:var(--brand-2);border-color:var(--brand-a45);background:var(--brand-dim)}
.sd-flag.hold{color:var(--warn);border-color:var(--warn-a40);background:var(--warn-a9)}
.sd-flag.bad{color:var(--bad);border-color:var(--bad-a45);background:var(--bad-a7)}
/* 地区牌。它是身份，不是告警——所以中性配色，和「待升级」「停靠中」那几个
   会变色的旗子分开：一台机器在东京不是一件需要处理的事。 */
.sd-geo{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;font-weight:600;
  letter-spacing:.02em;padding:2px 8px;border-radius:6px;
  border:1px solid var(--line);color:var(--dim);background:var(--faint-a12)}
.sd-geo .fl{font-size:12px;line-height:1}
/* 地区分栏挂在甲板底部，紧跟状态计数：两行读的是同一批机器，只是切法不同。
   一整行同时变红 = 那个机房的事，这正是分栏存在的理由。 */
.sd-regions{display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  margin-top:13px;padding-top:13px;border-top:1px solid var(--line-soft)}
.sd-regions .k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;
  color:var(--dim);margin-right:3px}
.sd-cnt .fl{font-size:12.5px;line-height:1}
.sd-cnt.geo i{display:none}
.sd-cnt.geo.dark{border-color:var(--bad-a45);color:var(--bad)}
.sd-cnt.geo .dn{color:var(--bad);font-weight:600}
/* 身份区三行，每行都是「左边是谁、右边读数」，三行都横跨整张卡：
     名字行   名字 · 状态 · 地区 · 旗子                    探测 / 详情
     账号行   邮箱 · 档位 · 组织                      已分配 1/1 位订阅用户
     机器行   1 核 · 956 MB · 9.8 GB                   30 秒前上报 · 版本
   左边的那一半可以被省略号吃，右边的永远完整——右边才是会变的读数。账号行和
   机器行不折行：之前它们挤在按钮左边那一半里，「已分配」跟在邮箱后面随缘换行，
   长邮箱的卡就比邻居高出一行。 */
.sd-acct{margin-top:4px;font-size:12.5px;color:var(--dim);display:flex;align-items:baseline;
  justify-content:space-between;gap:14px;white-space:nowrap}
.sd-acct b{color:var(--text);font-weight:500}
.sd-acct-id{min-width:0;overflow:hidden;text-overflow:ellipsis}
.sd-acct-seat{flex:none}
.sd-addr{margin-top:3px;font-family:var(--mono);font-size:11px;color:var(--faint);
  display:flex;align-items:baseline;justify-content:space-between;gap:14px;white-space:nowrap;
  font-variant-numeric:tabular-nums}
.sd-addr-l{min-width:0;overflow:hidden;text-overflow:ellipsis}
.sd-addr-r{flex:none}
.sd-spec{color:var(--dim)}
.sd-acts{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end}

/* 心跳带 */
.sd-tape-wrap{margin-top:11px}
.sd-tape-hd{display:flex;align-items:baseline;justify-content:space-between;gap:14px;margin-bottom:5px}
/* 标题不参与收缩。让它跟着挤，「流量 · 这台自己数的」会折成「…这台自己 / 数的」，
   而右边的读数明明还有余量 —— 一个折行的标题读起来像出了错，读数折行只是读数长。 */
.sd-tape-k{flex:none;font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
/* 读数只占一行：一台转发了一万次的和一台没转发的，右边这串长度差一倍，让它
   折行就是让两张卡的心跳带错开一行。放不下时省略号吃末尾，全串在悬停提示里。 */
.sd-tape-v{font-family:var(--mono);font-size:11px;color:var(--dim);font-variant-numeric:tabular-nums;
  text-align:right;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  display:flex;justify-content:flex-end;align-items:baseline}
.sd-tape-vt{min-width:0;overflow:hidden;text-overflow:ellipsis}
/* 「有失败」是这行颜色的图例，放在省略号外面：读数被吃掉还有悬停提示，图例被吃掉
   那几根红柱子就没人解释了。 */
.sd-tape-vb{flex:none;margin-left:4px}
.sd-tape-vb::before{content:"· ";color:var(--dim)}
.sd-tape-v b{color:var(--text);font-weight:600}
.sd-tape-v .bad{color:var(--bad)}
.sd-tape{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:1px;height:30px;align-items:end;
  background-image:repeating-linear-gradient(90deg,var(--line-soft) 0 1px,transparent 1px 25%)}
.sd-tape i{display:block;height:100%;border-radius:1px;background:var(--ok-a60)}
.sd-tape i.n{background:var(--warn)}
.sd-tape i.u{background:var(--brand-a70)}
.sd-tape i.g{height:26%;background:var(--bad-a55);border-radius:0}
.sd-tape i.v{height:12%;background:var(--faint-a34);border-radius:0}
/* 一次都没上报过的机器没有带子可画。画一条空槽比画一排看不见的短横诚实：
   前者说「这里本来就没有东西」，后者会被读成「这段时间它是好的」。 */
.sd-tape-none{height:30px;display:flex;align-items:center;padding:0 11px;border-radius:6px;
  border:1px dashed var(--line);color:var(--faint);font-size:11.5px}
.sd-fresh .sd-tape{animation:sd-print .75s cubic-bezier(.22,.61,.36,1) both;animation-delay:calc(var(--i,0)*70ms)}
@keyframes sd-print{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
@media (prefers-reduced-motion:reduce){.sd-fresh .sd-tape{animation:none}}

/* 流量泳道：和心跳带共用同一条时间轴，但编码换了一套 —— **高度说量，颜色说
   有没有失败**（心跳带是高度说有无、颜色说种类）。两条上下并排才答得出这一页
   最难答的那个问题：一台机器可以整天心跳齐全、却一个请求都没接住，那种故障在
   只有心跳的带子上跟满载完全一样。
   一格取的是那 10 分钟里**最忙的一分钟**，不是十分钟的总和 —— 心跳每 30 秒
   带一个一分钟窗口回来，把二十个重叠窗口相加会报出二十倍的流量，而那个数字
   全程看起来都很合理。 */
.sd-lane{margin-top:10px}
.sd-flow{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:1px;height:22px;
  align-items:end;background-image:repeating-linear-gradient(90deg,var(--line-soft) 0 1px,transparent 1px 25%);
  border-bottom:1px solid var(--line-soft)}
.sd-flow i{display:block;border-radius:1.5px 1.5px 0 0;background:var(--brand-a60);min-height:1px}
.sd-flow i.warn{background:var(--warn-a70)}
.sd-flow i.bad{background:var(--bad-a55)}
.sd-flow i.z{height:2px!important;background:var(--faint-a22);border-radius:0}
/* 没有流量读数的节点也占着这条泳道的位置（旧版节点不报每分钟请求数、从未上报
   的机器）：空槽配一句说明，让下面两排仪表和邻居的落在同一高度。 */
.sd-flow-none{height:22px;display:flex;align-items:center;padding:0 11px;border-bottom:1px solid var(--line-soft);
  color:var(--faint);font-size:11px}

/* 紧张原因：这块板上唯一的整句文字。所以它不套卡片、不加图标框 —— 一条左侧
   竖线加一行字，读起来像批注，而不是页面上的第 N 个横幅。 */
.sd-strain{margin-top:11px;padding:8px 0 8px 12px;border-left:2px solid var(--warn-a45);
  font-size:12px;line-height:1.6;color:var(--dim)}
.sd-strain.bad{border-left-color:var(--bad-a45)}
.sd-strain b{color:var(--text);font-weight:600}

/* 仪表：同形状的读数横着排，形状一样是为了能竖着扫一列比出哪台不对。节点卡上
   有两块——上面一块是「这台在干活吗、干得好不好」，下面一块是「这台机器还撑得
   住吗」。分组信号就是每一块自带的这道发丝线，不额外加小标题：多一个标签就多
   一行要读的字，而一道线已经说清了两排读数不是一类。 */
.sd-gauges{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-top:14px;
  padding-top:13px;border-top:1px solid var(--line-soft)}
.sd-g-k{font-size:11px;letter-spacing:.02em;color:var(--dim);text-transform:uppercase;font-weight:600}
.sd-g-v{font-family:var(--mono);font-size:13px;font-weight:600;color:var(--text);
  font-variant-numeric:tabular-nums;margin:3px 0 5px}
.sd-g-v small{font-size:10.5px;font-weight:500;color:var(--faint);margin-left:3px}
.sd-g-v.dimv{color:var(--faint);font-weight:500}
.sd-g-bar{height:4px;border-radius:2px;background:var(--faint-a14);overflow:hidden}
.sd-g-bar span{display:block;height:100%;border-radius:2px;background:var(--brand-a60);
  transition:width .5s cubic-bezier(.22,.61,.36,1)}
.sd-g-bar span.warn{background:var(--warn)} .sd-g-bar span.bad{background:var(--bad)}
.sd-g-bar span.ok{background:var(--ok-a60)}

/* 身份行右边的档位牌：包月印档位（饼图 + 名字 + 已分配），额度池印池子和人数。
   它是这个账号卖成什么的事实，所以中性配色，不跟状态旗子抢颜色。 */
.sd-plc{display:inline-flex;align-items:center;gap:5px;margin-left:6px;padding:1px 8px 1px 6px;border-radius:6px;
  font-size:11.5px;color:var(--dim);background:var(--faint-a12);font-variant-numeric:tabular-nums}
.sd-plc:first-child{margin-left:0}
.sd-plc b{color:var(--text);font-weight:600}
.sd-plc .tier-ic{color:var(--text);width:12px;height:12px}
.sd-plc .tier-ic svg{width:11px;height:11px}
.sd-plc.al{padding-left:8px}
.sd-acct .pv-plan{vertical-align:1px}

/* 展开区：三栏（卖给了谁 / 这一小时 / 这台机器），底下一整条运维横栏。栏数按展开区
   自己的宽度换（@container sdmore）：卡片上不能挂 container（会让 subgrid 失效），
   挂在展开区这一层不影响卡片的横轨。 */
.sd-more{margin-top:16px;padding-top:16px;border-top:1px solid var(--line-soft);container:sdmore/inline-size}
.sd-dx{display:grid;grid-template-columns:minmax(0,1fr);gap:22px}
.sd-col{display:flex;flex-direction:column;gap:22px;min-width:0}
.sd-col-c{display:grid;grid-template-columns:minmax(0,1fr);gap:22px;align-content:start}
@container sdmore (min-width:760px){
  .sd-dx{grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);gap:24px 32px}
  .sd-col-c{grid-column:1/-1;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px 32px;
    padding-top:20px;border-top:1px solid var(--line-soft)}
}
@container sdmore (min-width:1240px){
  .sd-dx{grid-template-columns:minmax(0,1fr) minmax(0,1.2fr) minmax(0,1fr);gap:0}
  .sd-col{padding:0 26px}
  .sd-col-a{padding-left:0}
  .sd-col-c{grid-column:auto;display:flex;flex-direction:column;gap:22px;padding:0 0 0 26px;border-top:0}
  .sd-col-b,.sd-col-c{border-left:1px solid var(--line-soft)}
}
.sd-blk{min-width:0}
.sd-blk-hd{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:10px}
.sd-blk-k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.sd-blk-r{font-size:11px;color:var(--faint);white-space:nowrap}
.sd-kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:6px 16px;font-size:12px;align-items:baseline}
/* 标签列给一个下限：右栏「节点上报」和「流量」上下两块的值才落在同一条竖线上。 */
.sd-kv dt{min-width:92px;color:var(--faint);white-space:nowrap}
.sd-kv dd{margin:0;min-width:0;color:var(--text);font-family:var(--mono);font-size:11.5px;overflow-wrap:anywhere;
  font-variant-numeric:tabular-nums}
.sd-kv dd.plain{font-family:var(--sans);font-size:12px}
.sd-kv dd.ell{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;overflow-wrap:normal}
.sd-kv dd .mono{font-family:var(--mono);font-size:11.5px}
.sd-kv dd .dim{font-family:var(--sans);font-size:11.5px;color:var(--faint)}
.sd-kv dd.warnv{color:var(--warn)} .sd-kv dd.badv{color:var(--bad)}
.sd-note{margin-top:12px;font-size:11.5px;line-height:1.6;color:var(--faint)}
.sd-note[title]{cursor:help}
.sd-win{display:flex;flex-direction:column;gap:12px}
.sd-win-r{font-size:11.5px}
.sd-win-t{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:5px}
.sd-win-t span{color:var(--dim)}
.sd-win-t em{font-style:normal;margin-left:6px;font-size:10.5px;color:var(--faint)}
.sd-win-t b{font-family:var(--mono);font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
.sd-win-s{margin-top:4px;font-size:11px;color:var(--faint)}
/* 三档窗口摆成一张小表：同一个读数在 1 分钟 / 5 分钟 / 1 小时下的值并排。
   「刚开始出问题」和「一小时来一直这样」在单个窗口里长得完全一样，并排才分得开。

   它独占展开区中间那一栏（三栏时六百来像素，两栏时五百来像素）：四列数字塞进
   268px 的窄栏会溢出去，压在旁边那一块的文字上。窄屏上让它自己横向滚，而不是
   把 nowrap 去掉：一个折成两行的数字读起来像两个数字。表占满这一栏，从行首的
   「首字 平均」扫到它的 1 小时值不用横穿整张卡。 */
.sd-tel-wrap{overflow-x:auto}
.sd-tel{width:100%;border-collapse:collapse;font-size:11.5px}
.sd-tel th{font-weight:600;font-size:11px;letter-spacing:.02em;color:var(--dim);
  text-transform:uppercase;text-align:right;padding:0 0 8px 14px;white-space:nowrap}
.sd-tel th:first-child{text-align:left;padding-left:0}
.sd-tel td{padding:6px 0 6px 14px;text-align:right;font-family:var(--mono);
  font-variant-numeric:tabular-nums;color:var(--text);border-top:1px solid var(--line-soft);
  white-space:nowrap}
.sd-tel td:first-child{text-align:left;padding-left:0;font-family:var(--sans);color:var(--faint)}
.sd-tel td.z{color:var(--faint)}
.sd-tel td.warnv{color:var(--warn)}
.sd-tel td.badv{color:var(--bad)}
/* 失败去处：只列非零的那几格。列一排 0 会让「哪里在漏」变成在一串数字里找，
   而一串 0 中间的那个 3 恰恰是要一眼看到的东西。 */
.sd-fail{display:flex;flex-wrap:wrap;gap:7px;margin-top:12px}
.sd-fail span{font-size:11px;padding:3px 9px;border-radius:6px;border:1px solid var(--line);
  color:var(--dim)}
.sd-fail span b{font-family:var(--mono);color:var(--text);font-weight:600;margin-left:6px;
  font-variant-numeric:tabular-nums}
.sd-fail span.w{border-color:var(--warn-a40);background:var(--warn-a5);color:var(--warn)}
.sd-fail span.b{border-color:var(--bad-a45);background:var(--bad-a7);color:var(--bad)}
/* 订阅与用户：先是档位那一行（卖成什么、名额），再每人一行（备注 / 邮箱 / 何时分配）。 */
.sd-plcs{display:flex;flex-direction:column;gap:6px;margin-bottom:8px}
.sd-plc-r{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 11px;
  border-radius:10px;background:var(--fill-4);font-size:12px}
.sd-plc-n{display:flex;align-items:center;gap:7px;min-width:0}
.sd-plc-n b{font-weight:600;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sd-plc-n em{font-style:normal;font-size:11px;color:var(--faint);white-space:nowrap}
.sd-plc-n .tier-ic{color:var(--text)}
.sd-plc-v{flex:none;color:var(--dim);font-size:11.5px;font-variant-numeric:tabular-nums}
.sd-plc-v b{font-family:var(--mono);color:var(--text);font-weight:600}
.sd-rds{display:flex;flex-direction:column}
.sd-rd{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:7px 0;
  border-bottom:1px solid var(--line-soft)}
.sd-rd:last-child{border-bottom:0}
.sd-rd-who{display:flex;flex-direction:column;align-items:flex-start;gap:1px;min-width:0;padding:2px 6px;margin:-2px -6px;
  border-radius:7px;background:none;text-align:left;font-size:12.5px;color:var(--text)}
.sd-rd-who:hover{background:var(--fill-3)}
.sd-rd-who:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.sd-rd-n{display:flex;align-items:center;gap:6px;max-width:100%}
.sd-rd-n b{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sd-rd-m{max-width:100%;font-family:var(--mono);font-size:11px;color:var(--faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sd-rd-meta{flex:none;font-size:11px;color:var(--faint);white-space:nowrap}
.sd-none{font-size:12px;color:var(--faint);line-height:1.6}
/* 运维横栏：一行放得下时三组按钮之间一道竖线；放不下就去掉分组、按钮照常折行——
   分组线落到新一行的开头，读起来像缩进。SSH 凭据跟在后面，远程输出在下面铺满整宽。 */
.sd-opsbar{margin-top:20px;padding-top:14px;border-top:1px solid var(--line-soft)}
.sd-ops{display:flex;flex-wrap:wrap;align-items:center;gap:8px 7px}
.sd-ops>.sd-blk-k{margin-right:7px}
.sd-ops-g{display:contents}
@container sdmore (min-width:1180px){
  .sd-ops-g{display:inline-flex;align-items:center;gap:7px}
  .sd-ops-g+.sd-ops-g{margin-left:5px;padding-left:12px;box-shadow:inset 1px 0 var(--line-soft)}
}
.sd-ops-ssh{margin-top:9px;font-size:11.5px;color:var(--faint)}
.sd-ops-ssh .mono{font-family:var(--mono);font-size:11px;color:var(--dim)}
.sd-ops-ssh .btn{margin-left:8px;padding:1px 10px;font-size:11.5px}
.gwk-list{border-top:1px solid var(--line)}
.gwk-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:14px;align-items:center;padding:14px 0;border-bottom:1px solid var(--line)}
.gwk-line{margin-top:4px;font-size:12px;color:var(--dim)}
.gwk-host{font-family:var(--mono);font-size:12.5px;color:var(--text)}
.gwk-acts{display:flex;flex-direction:column;align-items:flex-end;gap:6px}
.gwk-acts .btn{white-space:nowrap}
.gwk-st{font-size:12.5px;font-weight:600}
.gwk-st.ok{color:var(--ok)} .gwk-st.warn{color:var(--warn)}
.gwk-out{margin-top:4px;font-size:11.5px}
.gwk-out.ok{color:var(--ok)} .gwk-out.bad{color:var(--bad)}
.gwk-sub{margin-top:3px;font-size:11.5px;color:var(--faint)}
.sd-ssh{display:grid;grid-template-columns:1fr 78px;gap:8px;margin-bottom:9px}
.sd-ssh input{width:100%}
.sd-ssh.one{grid-template-columns:1fr}
/* 终端读数在两套主题里都是墨色：机箱那一层本来就不随主题翻转，而一段 journalctl
   长得像终端才读得快。 */
.sd-term{background:var(--rail-bg);border:1px solid var(--rail-line);border-radius:10px;
  padding:11px 13px;font-family:var(--mono);font-size:11px;line-height:1.65;color:var(--rail-text);
  white-space:pre-wrap;word-break:break-word;max-height:280px;overflow:auto}
.sd-term:empty{display:none}
.sd-term .ok{color:var(--ok)} .sd-term .bad{color:var(--bad)}
.sd-probe{margin-top:9px;font-family:var(--mono);font-size:11.5px;color:var(--dim)}
.sd-probe.ok{color:var(--ok)} .sd-probe.bad{color:var(--bad)}
.sd-caret{transition:transform .18s ease}
.sd-unit.open .sd-caret{transform:rotate(180deg)}
.sd-empty{border:1px dashed var(--line);border-radius:var(--r-xl);padding:44px 28px;text-align:center;
  background:var(--surface)}
.sd-empty h3{font-family:var(--disp);font-size:16px;font-weight:600;color:var(--text);margin-bottom:8px}
.sd-empty p{font-size:12.5px;color:var(--dim);line-height:1.75;max-width:560px;margin:0 auto 16px}
.sd-empty code{font-family:var(--mono);font-size:11px;color:var(--brand-2);background:var(--brand-dim);
  padding:1px 6px;border-radius:5px}
/* 单列的手机屏：四个仪表并排每格不到 100px，「1.2 GB / 2.0 GB」放不下，降到两列；
   再窄一点心跳带右边那行读数换到标题下面。展开的卡片横跨整排，不受影响。 */
@media (max-width:520px){
  .sd-tape-hd{flex-wrap:wrap;gap:2px 14px}
  .sd-tape-v{display:block;flex-basis:100%;text-align:left;white-space:normal}
  .sd-tape-vt{display:inline;white-space:normal}
  .sd-acct{flex-wrap:wrap;white-space:normal;gap:2px 14px}
  .sd-box{padding:12px 8px 8px}
  .sd-box-hd{padding:0 4px 12px}
}
@media (max-width:900px){
  .sd-gauges{grid-template-columns:repeat(2,1fr)}
  /* 竖排时三个属性一起改，少一个都不行：column + wrap 让容器的交叉尺寸按内容算，
     column + flex-start 让每一项按内容宽度撑——两条都会被一行长地址顶宽，
     然后整块被 .sd-unit 的 overflow 裁掉，看起来像地址「莫名其妙没了」。 */
  .sd-hd{flex-direction:column;align-items:stretch;flex-wrap:nowrap}
  .sd-acts{justify-content:flex-start}
  .sd-deck-hd{flex-direction:column;gap:6px;align-items:stretch}
  .sd-legend .sp{display:none}
  .sd-todo-m{min-width:100%}
}

/* ── 模型价格：匹配阶梯 ──────────────────────────────────────────────
   这张表以前是九列裸数字，三组价格在说三件不同的事（客户付多少、我们付多少、
   不经我们的手要花多少）。前两组随人民币计量一起退役，现在只剩一条泳道：厂商
   自己的官方标价，也是消费扣美元额度的唯一依据。

   剩下的那件最要紧的事没变——**一个模型会命中哪一行**。命中顺序是
   `ORDER BY sort_order, id`，只活在 SQL 里，表面上一个字都没写。

   那不是审美问题，是花过钱的问题：kimi-k2.7-code 曾落在 kimi-k2* 上，按错误的
   一行计了价；gemini-3.7-flash 若不单开一行会落到 gemini-* 上，输出多收两成。
   2026-09-19 的 gemini-3.8-flash 是同一类：官方限时 $0.75/$3.75，落进 gemini-*
   就按 $1.5/$9 扣。
   两次都是"看着都对"。

   所以：
   · 官方价占一条**泳道**，斜杠对齐成一条竖脊，几十行能顺着脊线扫下来；
   · 缓存与按次退到下面的小字，只在非零时出现——它们大多是零，占着列宽却不说话；
   · 顶上是**命中测试**：输入一个模型 ID，命中的那行亮起来，同样匹配但被压在下面的
     行标成"被挡住"，其余淡出。阶梯是这张表的本体，让它自己演一遍。 */
/* 命中测试：这一条是整块的主角，所以给它品牌色描边和自己的底面，
   不跟下面的表格共用 .panel 的灰。 */
.prc-probe{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:14px;
  padding:14px 18px;border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface);box-shadow:var(--card-shadow)}
.prc-probe>.lb{font-size:12.5px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--text);white-space:nowrap}
.prc-probe input{flex:1 1 240px;min-width:0;font-family:var(--mono);font-size:13px;
  background:var(--fill-4);border:1px solid transparent;color:var(--text);
  border-radius:var(--r-pill);padding:7px 14px}
.prc-probe input:focus{outline:none;background:var(--input-bg);border-color:var(--focus);box-shadow:var(--focus-ring)}
.prc-verdict{flex:1 1 100%;font-size:12.5px;color:var(--dim);line-height:1.7;min-height:1px}
.prc-verdict .hit{font-family:var(--mono);color:var(--brand-2)}
.prc-verdict .warn{color:var(--warn)}
.prc-verdict .bad{color:var(--bad)}
.prc-verdict .ok{color:var(--ok)}

.prc-rows{font-size:13px}
/* 三列一次定死，表头和每一行共用，列才对得齐（表头不是 <thead>，是同一套栅格的第一行）。
   从前是五列：售价、成本、官方价各一条泳道，前两条随人民币计量一起退役。
   前两列都封了上限、把富余全给最后一列：一条泳道要是跟着容器一起摊开，宽屏上
   型号和价格会被一整片空白隔在两头，而这张表要的正是横着一眼扫过去。 */
.prc-hd,.prc-row{display:grid;gap:0 14px;align-items:center;padding:9px 16px;
  grid-template-columns:minmax(220px,380px) minmax(175px,230px) minmax(195px,255px) 1fr}
.prc-hd{position:sticky;top:0;z-index:2;background:var(--surface-2);border-bottom:1px solid var(--line);
  font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.prc-hd .u{text-transform:none;letter-spacing:0;font-weight:500;opacity:.72}
.prc-hd .h-lane{text-align:center}
.prc-row{border-bottom:1px solid var(--line-soft);transition:opacity .12s,background .12s}
.prc-row:hover{background:var(--surface-2)}
.prc-row:last-child{border-bottom:none}

/* 家族分带。取值不是装饰：带内按 sort_order 排，带间按各自最小的 sort_order 排，
   所以整张表从上往下读仍然是真正的命中顺序。 */
.prc-band{display:flex;align-items:baseline;gap:10px;padding:14px 16px 7px;
  border-bottom:1px solid var(--line);background:var(--surface-2)}
.prc-band .t{font-family:var(--disp);font-size:12.5px;font-weight:600;color:var(--text);letter-spacing:.02em}
.prc-band .d{font-size:11.5px;color:var(--faint)}
.prc-band .n{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--faint)}

.prc-pat{font-family:var(--mono);font-weight:600;color:var(--text);word-break:break-all}
.prc-tag{display:inline-block;margin-left:7px;padding:1px 6px;border-radius:5px;vertical-align:1px;
  font-size:10px;font-weight:600;letter-spacing:.02em;white-space:nowrap;
  color:var(--dim);background:var(--surface-3)}
.prc-tag.wild{color:var(--brand-2);background:var(--brand-dim)}
.prc-tag.none{color:var(--warn);background:transparent;border:1px dashed var(--warn-a45)}

/* 泳道：[入 · 斜杠 · 出]，入右对齐、出左对齐，斜杠居中——斜杠于是在整列上排成一条线。
   币种贴在每行第一个数字前面，因为价目表按厂商原币记，一行里未必都是美元。 */
.prc-n{display:grid;grid-template-columns:1fr 12px 1fr;align-items:baseline;row-gap:2px;
  font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:12.5px}
.prc-n .c{font-size:11px;color:var(--faint);margin-right:1px}
.prc-n .a{text-align:right}
.prc-n .s{text-align:center;color:var(--faint);opacity:.6}
.prc-n .b{text-align:left}
.prc-n .dash{grid-column:1/-1;text-align:center;color:var(--faint)}
/* 第二行是同一组价格的缓存读/写，第三行是按次。压小压淡，但留在本列——
   谁的缓存价，靠它站在哪一列回答，不靠前缀。 */
.prc-n .lo{font-size:10.5px;color:var(--faint)}
.prc-n .k{margin-right:3px;opacity:.7}
.prc-n .full{grid-column:1/-1;text-align:center}
.prc-offi{color:var(--text)}
/* 未填官方价不再是"没关系"：额度按官方价扣，这一行扣不出钱。所以画成虚线警告，
   不画成 0，也不画成灰"—"。 */
.prc-offi.none{color:var(--warn);opacity:.85}
.prc-act{display:flex;gap:4px;justify-content:flex-end}
.prc-row .prc-act{opacity:0;transition:opacity .12s}
.prc-row:hover .prc-act,.prc-row:focus-within .prc-act{opacity:1}

/* 测试进行中：命中的一行留亮并挂一面旗，同样匹配但排在下面的标成被挡住，其余淡出。
   "被挡住"是这张表唯一说不出口的信息，也是最值钱的那条。 */
.prc-rows.probing .prc-row{opacity:.28}
.prc-rows.probing .prc-row.on,.prc-rows.probing .prc-row.shadow{opacity:1}
.prc-row.on{background:var(--brand-dim);box-shadow:inset 3px 0 0 var(--brand)}
.prc-row.on:hover{background:var(--brand-dim)}
.prc-row.shadow{box-shadow:inset 3px 0 0 var(--line)}
.prc-row.shadow .prc-pat{text-decoration:line-through;text-decoration-color:var(--faint);color:var(--dim)}
.prc-mark{display:inline-block;margin-left:7px;padding:1px 6px;border-radius:5px;vertical-align:1px;
  font-size:10px;font-weight:600;white-space:nowrap}
.prc-mark.on{color:var(--btn-ink);background:var(--brand)}
.prc-mark.shadow{color:var(--faint);background:var(--surface-3)}

@media (max-width:1080px){
  .prc-hd,.prc-row{grid-template-columns:minmax(140px,1.3fr) minmax(150px,1fr) minmax(165px,1.1fr) 72px}
}
@media (max-width:720px){
  .prc-hd{display:none}
  .prc-row{grid-template-columns:1fr auto;gap:6px 12px;padding:12px 14px}
  .prc-row .prc-act{grid-column:2;grid-row:1;opacity:1}
  .prc-offi,.prc-direct{grid-column:1;justify-self:start;min-width:170px}
}

/* ---------- 计费 · 账本条 ----------------------------------------------------
   经营格开门第一件东西：左边花掉（美元官方价）、右边进钱（人民币现金），中间一道
   虚线墙挂着 ≠。「两侧不相减」是这一页最贵的一条规矩（真犯过：拿人民币月租去除
   美元产出），让版式替脚注守着它。 */
.bx-ledger{display:grid;grid-template-columns:1.12fr auto 1fr}
.bx-side{padding:18px 20px 16px;min-width:0}
.bx-side-k{display:flex;align-items:center;gap:9px;margin:0 0 15px;
  font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.bx-side-k .sym{font-style:normal;font-family:var(--mono);font-size:12px;font-weight:700;
  width:20px;height:20px;flex:none;display:inline-flex;align-items:center;justify-content:center;
  border-radius:6px;color:var(--brand-2);background:var(--brand-dim);letter-spacing:0}
.bx-cells{display:grid;grid-template-columns:1fr 1fr;gap:4px 22px}
.bx-cell{min-width:0}
.bx-cell .k{display:block;font-size:12px;font-weight:600;color:var(--dim)}
.bx-cell .v{display:block;margin-top:8px;font-family:var(--disp);font-size:28px;font-weight:600;
  letter-spacing:-.02em;line-height:1;font-variant-numeric:tabular-nums}
.bx-cell .v em{font-style:normal;font-size:14px;font-weight:500;color:var(--dim);margin-right:2px;vertical-align:3px}
.bx-cell .sub{display:block;margin-top:7px;font-size:11.5px;color:var(--faint);line-height:1.6}
.bx-cell .sub.meta{font-family:var(--mono);font-size:10.5px}
.bx-cell .sub.warn{color:var(--warn)}
.bx-note{margin:15px 0 0;padding-top:12px;border-top:1px solid var(--line-soft);
  font-size:11.5px;color:var(--faint);line-height:1.65}
.bx-wall{display:flex;flex-direction:column;align-items:center;padding:16px 0;cursor:help}
.bx-wall i{flex:1;width:0;border-left:1px dashed var(--faint-a55)}
.bx-wall span{flex:none;margin:7px 0;width:28px;height:28px;display:flex;align-items:center;justify-content:center;
  border:1px dashed var(--faint-a55);border-radius:50%;
  font-family:var(--mono);font-size:13px;font-weight:700;color:var(--dim)}
@media (max-width:900px){
  .bx-ledger{grid-template-columns:1fr}
  .bx-wall{flex-direction:row;padding:0 20px}
  .bx-wall i{width:auto;height:0;border-left:none;border-top:1px dashed var(--faint-a55)}
  .bx-wall span{margin:0 10px}
}
@media (max-width:560px){
  .bx-cells{grid-template-columns:1fr;gap:14px}
}

/* 按模型的「构成」列：条长 = 官方价值在本表里的相对大小，双色 = 扣额度（紫）对
   包月覆盖（灰）。原来这道减法要读者对着三列心算，现在瞄一眼。 */
.covth{width:104px}
td.covcell{padding-right:4px}
.covbar{display:flex;height:6px;min-width:3px;border-radius:3px;overflow:hidden;background:var(--faint-a12)}
.covbar i{display:block}
.covbar .ca{background:var(--brand)}
.covbar .cc{background:var(--faint-a55)}
@media (max-width:1080px){
  .covth,td.covcell{display:none} /* 窄屏让位给数字列——构成条是导读，不是数据本体 */
}
/* ============================================================
   概览 · 值机台 / OVERVIEW COCKPIT
   首页只回答三个问题：有什么等我处理（工单）、生意怎么样（钱）、车队行不行
   （运力）。路由信号板搬去了「路由」页；这一页的计量单位是生意，不是请求。
   币种纪律：进钱侧永远 ¥，交付侧永远 $，同一块板上两个币种只并排、不相减。
   ============================================================ */
/* 工单栏：一块面板、一件一行。红点＝不动手就一直坏着，总是摊开；黄点＝该看一眼，
   默认收起。颜色只落在点和数字上，不再整条铺底色——整条红黄底把瓦片挤出了首屏。 */
#ov-todos:empty{display:none}
#ov-todos{margin:-14px 0 24px}
.ov-ibx{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);box-shadow:var(--card-shadow)}
.ov-ibx-hd{display:flex;align-items:center;gap:8px;padding:10px 12px 10px 18px;min-height:44px;box-sizing:border-box}
.ov-ibx-hd b{font-family:var(--disp);font-size:14px;font-weight:600;letter-spacing:-.01em;color:var(--text)}
.ov-ibx-n{min-width:20px;height:20px;padding:0 6px;box-sizing:border-box;border-radius:10px;display:inline-grid;place-items:center;
  font-size:11.5px;font-weight:700;font-variant-numeric:tabular-nums}
.ov-ibx-n.bad{background:var(--bad-a16);color:var(--bad)}
.ov-ibx-n.warn{background:var(--warn-a16);color:var(--warn)}
.ov-ibx-tg{margin-left:auto;display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border-radius:8px;
  background:none;border:0;color:var(--dim);font:inherit;font-size:12px;cursor:pointer}
.ov-ibx-tg:hover{background:var(--faint-a12);color:var(--text)}
.ov-ibx-tg:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.ov-ibx-tg svg{transition:transform .16s ease}
.ov-ibx-tg .ov-ibx-h{display:none}
.ov-ibx.open .ov-ibx-tg .ov-ibx-s{display:none}
.ov-ibx.open .ov-ibx-tg .ov-ibx-h{display:inline}
.ov-ibx.open .ov-ibx-tg svg{transform:rotate(180deg)}
.ov-ibx-list{list-style:none;margin:0;padding:0}
.ov-ibx-warn{display:none}
.ov-ibx.open .ov-ibx-warn{display:block}
.ov-ibx-row{display:flex;align-items:center;gap:12px;min-height:44px;padding:6px 12px 6px 18px;box-sizing:border-box;
  border-top:1px solid var(--line-soft)}
.ov-ibx-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--bad)}
.ov-ibx-row.warn .ov-ibx-dot{background:var(--warn)}
.ov-ibx-x{flex:1;min-width:0;font-size:12.5px;color:var(--text);line-height:1.5;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ov-ibx-x b{font-family:var(--mono);font-weight:600}
.ov-ibx-x span{color:var(--dim)}
.ov-ibx-row .btn{flex:none}
.tb-say .ov-say-w{font-weight:500;color:var(--dim)}
/* 最近消息：发往飞书的通知在看板上留的那几行。和待处理用同一张卡、同一种行，
   所以两块叠在一起时行高、左右留白是对齐的。圆点只分三种：要人处理、已恢复、其余。 */
#ov-feed:empty{display:none}
.ov-pending{grid-column:1/-1;padding:16px 18px;margin-bottom:16px;border:1px solid var(--line-soft);
  border-radius:var(--r-lg);background:var(--surface);color:var(--dim);font-size:12.5px;line-height:1.5}
#ov-feed{margin:-14px 0 24px}
#ov-todos:not(:empty)~#ov-feed,#ov-cash:not(:empty)+#ov-feed{margin-top:0}
/* 今日收款：和待处理、最近消息同一张卡、同一种行，三块叠在一起时左右留白对齐。
   抬头下面一排是今天的合计（按渠道），再下面一笔一行。 */
#ov-cash:empty{display:none}
#ov-cash{margin:-14px 0 24px}
#ov-todos:not(:empty)+#ov-cash{margin-top:0}
.ov-cash-day{font-size:12px;color:var(--dim);white-space:nowrap}
.ov-cash-sum{display:flex;flex-wrap:wrap;align-items:flex-end;gap:14px 32px;padding:12px 18px 16px;border-top:1px solid var(--line-soft)}
.ov-cash-c{min-width:0}
.ov-cash-k{display:block;font-size:11.5px;color:var(--dim);white-space:nowrap}
.ov-cash-v{display:block;margin-top:5px;font-family:var(--mono);font-size:15px;font-weight:600;color:var(--text);
  font-variant-numeric:tabular-nums;white-space:nowrap;line-height:1.15}
.ov-cash-c.big .ov-cash-v{font-family:var(--disp);font-size:26px;letter-spacing:-.02em;line-height:1.05}
.ov-cash-c.warn .ov-cash-v{color:var(--warn)}
.ov-cash-s{display:block;margin-top:4px;font-size:11px;color:var(--faint);white-space:nowrap}
.ov-cash-row .ov-ibx-dot{background:var(--ok)}
.ov-cash-row.pend .ov-ibx-dot{background:var(--warn)}
.ov-cash-row.off .ov-ibx-dot{background:var(--faint-a42)}
.ov-cash-at{flex:none;width:40px;font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums}
.ov-cash-row .ov-ibx-x em{font-style:normal;font-weight:500;color:var(--text)}
.ov-cash-row.off .ov-ibx-x em{color:var(--dim)}
.ov-cash-amt{flex:none;min-width:96px;text-align:right;font-family:var(--mono);font-size:13px;font-weight:600;
  color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap}
.ov-cash-row.off .ov-cash-amt{color:var(--dim);font-weight:500}
.ov-cash-st{flex:none;min-width:60px;box-sizing:border-box;padding:2px 8px;border-radius:999px;text-align:center;
  font-size:11.5px;white-space:nowrap;color:var(--ok);background:var(--ok-a12)}
.ov-cash-st.pend{color:var(--warn);background:var(--warn-a12)}
.ov-cash-st.off{color:var(--dim);background:var(--faint-a12)}
.ov-cash-foot{display:flex;justify-content:center;padding:6px 12px;border-top:1px solid var(--line-soft)}
.ov-cash-foot .ov-ibx-tg{margin:0}
.ov-fd-void,.ov-cash-void{margin:0;padding:14px 18px 16px;border-top:1px solid var(--line-soft);font-size:12.5px;color:var(--dim);line-height:1.6}
.ov-fd-new{height:20px;padding:0 8px;border-radius:10px;display:inline-grid;place-items:center;
  font-size:11.5px;font-weight:600;background:var(--faint-a12);color:var(--text)}
.ov-fd-all{margin-left:auto}
.ov-fd-row .ov-ibx-dot{background:var(--faint-a42)}
.ov-fd-row.act .ov-ibx-dot{background:var(--warn)}
.ov-fd-row.ok .ov-ibx-dot{background:var(--ok)}
.ov-fd-cat{flex:none;width:52px;font-size:11.5px;color:var(--dim);white-space:nowrap}
.ov-fd-row .ov-ibx-x em{font-style:normal;font-weight:500;color:var(--text)}
.ov-fd-row.new .ov-ibx-x em{font-weight:700}
.ov-fd-n,.ov-fd-miss{flex:none;font-size:11.5px;padding:2px 8px;border-radius:999px;white-space:nowrap}
.ov-fd-n{color:var(--dim);background:var(--faint-a12)}
.ov-fd-miss{color:var(--warn);background:var(--warn-a12)}
.ov-fd-at{flex:none;min-width:64px;text-align:right;font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap}
/* 「查看全部」抽屉：一条消息一块，全文照飞书里的分行显示。 */
.ovm-bar{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px}
.ovm-list{list-style:none;margin:0;padding:0}
.ovm-it{padding:14px 0;border-top:1px solid var(--line-soft)}
.ovm-it:first-child{border-top:0;padding-top:0}
.ovm-hd{display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11.5px;color:var(--dim)}
.ovm-hd .ov-ibx-dot{background:var(--faint-a42)}
.ovm-it.act .ov-ibx-dot{background:var(--warn)}
.ovm-it.ok .ov-ibx-dot{background:var(--ok)}
.ovm-hd .ovm-at{margin-left:auto;font-variant-numeric:tabular-nums;white-space:nowrap}
.ovm-t{margin:8px 0 0;font-size:13.5px;font-weight:600;color:var(--text);line-height:1.5;overflow-wrap:anywhere}
.ovm-b{margin:6px 0 0;font-size:12.5px;color:var(--dim);line-height:1.65;white-space:pre-wrap;overflow-wrap:anywhere}
.ovm-go{margin-top:10px}
.ovm-more{display:flex;justify-content:center;padding:16px 0 4px}
.ovm-foot{margin-right:auto;font-size:12px;color:var(--faint)}
@media (max-width:760px){
  .ov-fd-row{flex-wrap:wrap;row-gap:4px}
  .ov-fd-row .ov-ibx-dot{margin-top:6px}
  .ov-fd-row .ov-ibx-x{order:9;flex-basis:100%;padding-left:19px}
  .ov-fd-cat{width:auto;line-height:19px}
  .ov-fd-n,.ov-fd-miss{margin-top:-1px}
  .ov-fd-at{margin-left:auto;min-width:0;line-height:19px}
  .ov-fd-row .btn{display:none}
  .ov-fd-row.go{cursor:pointer}
  /* 今日收款：第一行时间、金额、状态，第二行是谁、哪个渠道；点整行打开那一笔。 */
  .ov-cash-row{flex-wrap:wrap;row-gap:4px;cursor:pointer}
  .ov-cash-row .ov-ibx-x{order:9;flex-basis:100%;padding-left:19px}
  .ov-cash-at{line-height:19px}
  .ov-cash-amt{margin-left:auto;min-width:0;line-height:19px}
  .ov-cash-row .btn{display:none}
  .ov-cash-sum{gap:14px 24px}
  .ov-cash-c.big{flex-basis:100%}
}
@media (prefers-reduced-motion:reduce){.ov-ibx-tg svg{transition:none}}
@media (max-width:760px){
  .ov-ibx-row{align-items:flex-start;padding-top:10px;padding-bottom:10px}
  .ov-ibx-x{white-space:normal}
  .ov-ibx-dot{margin-top:6px}
}
/* 营运走带：柱高＝当天按官方价交付的价值，紫段＝扣额度，灰段＝包月覆盖 */
#ov-hero{margin-bottom:14px}
#ov-hero:empty{display:none}
.ov-hero-bd{display:flex;gap:28px;align-items:stretch}
.ov-tape-wrap{flex:1;min-width:0;display:flex;flex-direction:column;gap:9px}
.ov-tape{display:flex;align-items:flex-end;gap:3px;height:138px}
.ov-day{flex:1;min-width:0;height:100%;display:flex;align-items:flex-end;border-radius:3px 3px 0 0}
.ov-day:hover{background:var(--faint-a12)}
.ov-day.today{background:var(--brand-a8)}
.ov-day.today .ov-bar .drawn{background:var(--brand-2)}
.ov-bar{width:100%;display:flex;flex-direction:column;border-radius:2.5px 2.5px 0 0;overflow:hidden}
.ov-bar .cov{background:var(--faint-a42)}
.ov-bar .drawn{background:var(--brand)}
.ov-bar.nil{height:2px;background:var(--faint-a22);border-radius:1px}
.ov-axis{display:flex;justify-content:space-between;font-family:var(--mono);font-size:10.5px;color:var(--faint)}
.ov-key{display:flex;gap:8px 20px;flex-wrap:wrap;font-size:11px;color:var(--faint);margin-top:2px}
.ov-key i{display:inline-block;width:12px;height:8px;border-radius:2px;margin-right:7px;vertical-align:-1px}
.ov-key .drawn{background:var(--brand)} .ov-key .cov{background:var(--faint-a42)}
.ov-read{flex:none;width:216px;display:flex;flex-direction:column;gap:8px;align-items:flex-end;text-align:right;
  justify-content:center;padding-left:26px;border-left:1px solid var(--line-soft)}
.ov-read-n{font-family:var(--disp);font-size:29px;font-weight:600;letter-spacing:-.02em;line-height:1;
  font-variant-numeric:tabular-nums}
.ov-read-n .u{display:block;font-family:var(--sans);font-size:10.5px;color:var(--faint);font-weight:500;
  margin-top:7px;letter-spacing:.06em}
.ov-read-row{font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums}
.ov-read-row b{color:var(--text);font-family:var(--mono);font-weight:600}
.ov-read-row::before{content:"";display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:7px}
.ov-read-row.drawn::before{background:var(--brand)}
.ov-read-row.cov::before{background:var(--faint-a42)}
.ov-read-sub{font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
/* 订阅账号：一个档位一行的表，账号是一排圆点，经营数字在同一行（ovFleetHTML） */
#ov-fleet{margin-bottom:14px}
#ov-fleet:empty{display:none}
.ov-fx{container:ovfx/inline-size}
.ov-fx .p-hd{align-items:center}
.ov-fx .p-hd .btn:first-of-type{margin-left:auto}
.ov-fx-key{display:inline-flex;flex-wrap:wrap;gap:4px 12px;font-size:11.5px;color:var(--faint)}
.ov-fx-key>span{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
.ov-fx-key i,.ov-fx-dot{--c:var(--ok-fill);width:10px;height:10px;border-radius:50%;flex:none;background:var(--c)}
.ov-fx-key i.warn,.ov-fx-dot.warn{--c:var(--warn)}
.ov-fx-key i.bad,.ov-fx-dot.bad{--c:var(--bad)}
.ov-fx-dot.off{--c:var(--faint)}
.ov-fx-key i.idle,.ov-fx-dot.idle{background:transparent;box-shadow:inset 0 0 0 1.75px var(--c)}
.ov-fx-dot{display:block;padding:0;border:none;cursor:pointer;transition:transform .14s var(--ease)}
.ov-fx-dot:hover{transform:scale(1.35)}
.ov-fx-dot:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.ov-fx-wrap{overflow-x:auto}
.ov-fx-tbl{width:100%;border-collapse:collapse;font-size:12.5px}
.ov-fx-tbl th{font-size:10.5px;letter-spacing:.02em;color:var(--faint);font-weight:600;text-align:left;vertical-align:bottom;
  padding:10px 14px 9px;border-bottom:1px solid var(--line-soft);white-space:nowrap}
.ov-fx-th-s{display:block;margin-top:2px;font-weight:500;letter-spacing:0;color:var(--faint);opacity:.8}
.ov-fx-tbl td{padding:12px 14px;border-bottom:1px solid var(--line-soft);vertical-align:top;color:var(--dim);white-space:nowrap}
.ov-fx-tbl th:first-child,.ov-fx-tbl td:first-child{padding-left:20px}
.ov-fx-tbl th:last-child,.ov-fx-tbl td:last-child{padding-right:20px}
.ov-fx-tbl tbody tr:last-child td{border-bottom:none}
.ov-fx-tbl .num{text-align:right}
.ov-fx-nm{display:flex;align-items:center;gap:7px;color:var(--text);white-space:nowrap}
.ov-fx-nm b{font-size:13.5px;font-weight:600}
.ov-fx-nm .tier-ic{color:var(--dim)}
.ov-fx-n{margin-left:2px;font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.ov-fx-tbl td.ov-fx-name{white-space:normal;width:1%}
.ov-fx-dotrow{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px;width:max-content;max-width:212px}
.ov-fx-odd{display:flex;flex-wrap:wrap;gap:2px 10px;margin-top:7px}
.ov-fx-odd span{white-space:nowrap}
.ov-fx-v{font-family:var(--mono);font-size:13px;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
.ov-fx-v small{font-family:var(--sans);font-size:11px;font-weight:500;color:var(--faint)}
.ov-fx-v.ok{color:var(--ok)} .ov-fx-v.warn{color:var(--warn)} .ov-fx-v.bad{color:var(--bad)}
.ov-fx-s{margin-top:3px;font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.ov-fx-s .warn{color:var(--warn)} .ov-fx-s .bad{color:var(--bad)}
.ov-fx-s>span{white-space:nowrap}
.ov-fx-s .warn[onclick]{cursor:pointer;text-decoration:underline;text-underline-offset:2px}
.ov-fx-nil{color:var(--faint)}
.ov-fx-tbl tfoot td{border-top:1px solid var(--line);border-bottom:none;background:var(--fill-3)}
.ov-fx-bd .ov-econ-foot{margin-top:0;padding:14px 20px 17px}
/* 整张表最窄约 840px，再窄就一个档位一张小卡片。按面板自己的宽度判断。 */
@container ovfx (max-width:849px){
  .ov-fx .p-hd .ov-fx-key{order:3;width:100%}
  .ov-fx-tbl thead{display:none}
  .ov-fx-tbl,.ov-fx-tbl tbody,.ov-fx-tbl tfoot{display:block}
  .ov-fx-tbl tr{display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));gap:12px 14px;padding:14px 16px;border-bottom:1px solid var(--line-soft)}
  .ov-fx-tbl tbody tr:last-child{border-bottom:none}
  .ov-fx-tbl tfoot tr{border-top:1px solid var(--line);background:var(--fill-3)}
  .ov-fx-tbl td,.ov-fx-tbl tfoot td{display:block;padding:0!important;border:none;background:none;white-space:normal}
  .ov-fx-tbl td.ov-fx-name{grid-column:1/-1;width:auto}
  .ov-fx-tbl td:empty{display:none}
  .ov-fx-tbl .num{text-align:left}
  .ov-fx-tbl td:not(.ov-fx-name)::before{content:attr(data-k);display:block;margin-bottom:3px;font-size:10.5px;font-weight:600;color:var(--faint)}
  .ov-fx-dotrow{max-width:none}
  .ov-fx-bd .ov-econ-foot{padding:14px 16px 16px}
}
.ov-econ-foot{display:flex;gap:12px 28px;flex-wrap:wrap;margin-top:15px;padding-top:15px;
  border-top:1px solid var(--line-soft);font-size:12px;color:var(--dim)}
.ov-econ-foot b{font-family:var(--mono);font-weight:600;color:var(--text);font-size:14px}
.ov-econ-foot small{display:block;font-size:11px;color:var(--faint);margin-top:3px}
.ov-econ-foot .bad b{color:var(--bad)}
/* 需关注：包月到期 / 账号续费 / 空闲账号 / API 额度，四签一块面板 */
#ov-cust{margin-bottom:14px}
#ov-cust:empty{display:none}
.ov-watch .p-hd{flex-wrap:wrap;row-gap:10px;align-items:center;padding-top:11px;padding-bottom:11px}
.ov-watch-tabs{margin-left:auto}
.ov-watch-say{margin:0;padding:12px 18px 4px;font-size:12px;color:var(--dim);line-height:1.5}
.ov-watch-note{margin:0;padding:12px 18px 16px;font-size:11.5px;color:var(--faint);line-height:1.6;border-top:1px solid var(--line-soft)}
.ov-watch-note .carb-link{color:var(--brand-2)}
.ov-watch .cp-empty{padding:28px 20px 30px}
.ov-cust-tbl tbody tr{cursor:pointer}
.ov-cust-tbl td.bad{color:var(--bad)}
.ov-watch .cpa-tbl td{vertical-align:top}
.ov-watch .cpa-tbl .cpa-memo{font-family:var(--sans)}
/* 登录邮箱是主名，内部名在下一行。邮箱比 claude-oauth8 长，窄屏要能折行。 */
.carb-link.id-mail,.us-car.id-mail,.cp-carnm.id-mail,.cpa-acct .hd .nm.id-mail,.spw-nm.id-mail,.og-up.id-mail{white-space:normal;overflow-wrap:anywhere;text-overflow:unset;overflow:visible}
/* 调用流水里的邮箱只占一行。流水表其余各列都不折行，overflow-wrap:anywhere 让这一格的最小宽度
   只剩一个字：模型名一长或抽屉一窄，表格只能压这一列，邮箱被拆成一字一行，整行撑到三百多像素高。
   宽度不够时由面板横向滚动；完整邮箱在悬停提示里，点开账号工作台也能看到。 */
table.feed .feed-up.id-mail{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}
.id-sub{display:block;margin-top:2px;font-size:11px;color:var(--dim);overflow-wrap:anywhere;white-space:normal;max-width:240px;line-height:1.35}
.id-sub.inline{display:inline;margin:0 0 0 6px;max-width:none}
.ov-watch .ov-acct-tbl td:first-child,
.ov-watch .ov-idle-tbl td:first-child{white-space:normal;overflow-wrap:anywhere;max-width:280px}
#bl-accounts td.id-mail,.fee-card .who .id-mail{white-space:normal;overflow-wrap:anywhere;max-width:280px}
.ov-t-bad{color:var(--bad)!important}
.ov-t-warn{color:var(--warn)!important}
.ov-watch td.ov-hint{white-space:normal;min-width:150px;font-size:12px;line-height:1.5}
.ov-watch td.ov-act{width:1%;text-align:right}
.ov-watch td.ov-act .btn+.btn{margin-left:6px}
/* 已忽略：收在名单下面，展开后一行一条，可以恢复 */
.ov-watch-note .ov-dis-tg{margin-left:4px}
.ov-watch .ov-dis{border-top:1px solid var(--line-soft)}
.ov-watch .ov-dis-tbl td{color:var(--faint)}
.ov-watch .ov-dis-tbl td:first-child{color:var(--dim)}
@media (max-width:760px){
  .ov-watch-tabs{margin-left:0;width:100%;overflow-x:auto}
}
/* 页脚链路行：技术面的读数和三扇门 */
.ov-foot{display:flex;align-items:center;gap:10px 18px;flex-wrap:wrap;padding:13px 17px;margin-bottom:34px;
  border:1px dashed var(--line);border-radius:12px;font-size:12px;color:var(--faint)}
.ov-foot .mono{font-variant-numeric:tabular-nums;color:var(--dim)}
.ov-foot .mono b{color:var(--text)} .ov-foot .mono b.bad{color:var(--bad)}
.ov-foot .sp{flex:1}
@media (max-width:1080px){
  .ov-hero-bd{flex-direction:column}
  .ov-read{width:auto;border-left:none;padding-left:0;align-items:flex-start;text-align:left;
    border-top:1px solid var(--line-soft);padding-top:14px}
  .ov-tape{height:110px}
}

/* ============================================================
   值机助手 / ASSISTANT · 智能巡检 / INSPECTION
   交换台的值机员：右上角一按，抽屉里是一个能读整台交换机的 agent。
   它做事的过程用「接线轨」画出来——每次工具/skill 调用是轨上的一个插口，
   LED 讲状态（琥珀=执行中 / 绿=成功 / 红=失败），SKILL 用品牌紫点名。
   巡检页复用同一条轨：定时巡查和即时对话是同一个值机员，视觉上也该是。
   ============================================================ */
.as-launch{display:inline-flex;align-items:center;gap:7px;padding:6px 13px;border-radius:var(--r-pill);
  background:var(--fill-4);border:1px solid transparent;color:var(--text);font-size:13px;font-weight:500;
  transition:color .16s var(--ease),background-color .16s var(--ease)}
.as-launch svg{width:15px;height:15px;flex:none}
.as-launch .as-led{width:6px;height:6px;border-radius:50%;background:var(--ok-fill);flex:none}
.as-launch:hover{color:var(--text);background:var(--fill-3)}
.as-launch.busy .as-led{animation:as-blink 1s infinite}
@keyframes as-blink{0%,100%{opacity:1}50%{opacity:.25}}

.as-drawer{width:600px;max-width:96vw}
.as-head-acts{display:flex;align-items:center;gap:2px}
.as-head-acts .x svg{width:16px;height:16px;vertical-align:-3px}
.as-model-tag{font-family:var(--mono);font-size:11px;color:var(--faint);background:var(--surface-2);
  border:1px solid var(--line-soft);border-radius:999px;padding:2px 9px;margin-left:10px;max-width:180px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 配置条：第一次打开（没配模型）时自动展开，其余时候收在小齿轮后面 */
.as-config{padding:14px 22px;border-bottom:1px solid var(--line-soft);background:var(--surface-2);
  display:flex;flex-direction:column;gap:10px}
.as-config .row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.as-config label{font-size:12px;color:var(--dim);min-width:74px}
.as-config select,.as-config input{background-color:var(--input-bg);border:1px solid var(--input-line);color:var(--text);
  border-radius:8px;padding:6px 9px;font-size:12.5px;font-family:var(--mono);min-width:0;flex:1}
.as-config .hint{margin:0}
/* 历史对话面板 */
.as-hist{padding:8px 12px;border-bottom:1px solid var(--line-soft);background:var(--surface-2);
  max-height:40vh;overflow-y:auto}
.as-hist-item{display:flex;align-items:center;gap:9px;width:100%;text-align:left;padding:8px 10px;border-radius:9px;
  background:transparent;color:var(--dim);font-size:12.5px}
.as-hist-item:hover{background:var(--surface-3);color:var(--text)}
.as-hist-item .t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.as-hist-item .d{font-family:var(--mono);font-size:10.5px;color:var(--faint);flex:none}
.as-hist-item .del{color:var(--faint);padding:2px 5px;border-radius:6px;flex:none}
.as-hist-item .del:hover{color:var(--bad);background:var(--bad-a12)}
.as-hist-empty{padding:14px;text-align:center;font-size:12px;color:var(--faint)}

.as-log{display:flex;flex-direction:column;gap:14px;scroll-behavior:smooth}
.as-hello{margin:auto;text-align:center;color:var(--faint);font-size:12.5px;line-height:2;padding:30px 10px}
.as-hello svg{width:34px;height:34px;opacity:.5;margin-bottom:6px}
.as-hello .ex{display:inline-block;margin:2px 4px;padding:4px 11px;border:1px dashed var(--line);border-radius:999px;
  color:var(--dim);font-size:12px;cursor:pointer;background:transparent;transition:border-color .15s,color .15s}
.as-hello .ex:hover{border-color:var(--brand-a40);color:var(--text)}
.as-u{align-self:flex-end;max-width:86%;background:var(--brand-dim);border:1px solid var(--brand-16-on-surface);
  border-radius:13px 13px 4px 13px;padding:9px 13px;font-size:13px;line-height:1.65;white-space:pre-wrap;word-break:break-word}
.as-a{font-size:13.5px;line-height:1.75;word-break:break-word;max-width:100%}
.as-a .cur{display:inline-block;width:7px;height:15px;background:var(--brand);vertical-align:-2px;
  margin-left:2px;border-radius:2px;animation:as-blink .9s infinite}
.as-think{border-left:2px dashed var(--line);padding:2px 0 2px 12px;margin:2px 0}
.as-think summary{font-size:11.5px;color:var(--faint);cursor:pointer;list-style:none;user-select:none}
.as-think summary::before{content:'◦ '}
.as-think .tx{font-size:12px;color:var(--faint);line-height:1.7;white-space:pre-wrap;margin-top:6px;word-break:break-word}
.as-sys{font-size:12px;color:var(--bad);background:var(--bad-a10);border:1px solid var(--bad-a25);
  border-radius:9px;padding:8px 12px}
/* 接线轨：agent 的执行链。左侧一根走线，每个调用是线上一个插口 */
.agsteps{position:relative;display:flex;flex-direction:column;gap:6px;padding-left:17px;margin:2px 0}
.agsteps::before{content:'';position:absolute;left:4px;top:8px;bottom:8px;width:1px;
  background:var(--line);border-radius:1px}
.agstep{position:relative}
.agstep .led{position:absolute;left:-17px;top:8px;width:7px;height:7px;border-radius:50%;
  background:var(--warn-fill);animation:as-blink 1s infinite}
.agstep.ok .led{background:var(--ok-fill);box-shadow:none;animation:none}
.agstep.err .led{background:var(--bad-fill);box-shadow:none;animation:none}
.agstep.skill .led{background:var(--brand);box-shadow:none}
.agstep-hd{display:flex;align-items:baseline;gap:8px;cursor:pointer;padding:4px 8px;margin:0 -8px;border-radius:8px;min-width:0}
.agstep-hd:hover{background:var(--surface-2)}
.agstep-hd .nm{font-family:var(--mono);font-size:12px;font-weight:600;color:var(--text);flex:none}
.agstep.skill .agstep-hd .nm{color:var(--brand-2)}
.agstep-hd .tag{font-family:var(--mono);font-size:9.5px;font-weight:600;letter-spacing:.02em;color:var(--brand-2);
  background:var(--brand-dim);border-radius:5px;padding:1px 5px;flex:none}
.agstep-hd .dg{font-family:var(--mono);font-size:11px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;flex:1;min-width:0;text-align:left}
.agstep-hd .ms{font-family:var(--mono);font-size:10.5px;color:var(--faint);flex:none}
.agstep-out{display:none;font-family:var(--mono);font-size:11px;line-height:1.6;color:var(--dim);
  background:var(--surface-2);border:1px solid var(--line-soft);border-radius:9px;padding:9px 11px;
  margin:4px 0 2px;white-space:pre-wrap;word-break:break-word;max-height:300px;overflow-y:auto}
.agstep.open .agstep-out{display:block}
.as-usage{font-family:var(--mono);font-size:10.5px;color:var(--faint);text-align:right;margin-top:-6px}
.as-foot{padding:13px 16px;border-top:1px solid var(--line-soft);display:flex;gap:9px;align-items:flex-end}
.as-foot textarea{flex:1;background:var(--input-bg);border:1px solid var(--input-line);color:var(--text);
  border-radius:11px;padding:10px 13px;font-size:13px;font-family:var(--sans);line-height:1.55;resize:none;
  max-height:132px;min-height:40px}
.as-foot textarea:focus{outline:none;border-color:var(--brand-a40)}
.as-foot .btn{flex:none}

/* Markdown（助手回答与巡检报告共用）。装在 .md 里，不外泄 */
.md h1,.md h2{font-family:var(--disp);font-size:14.5px;font-weight:600;margin:16px 0 7px;letter-spacing:-.01em}
.md h3,.md h4{font-family:var(--disp);font-size:13px;font-weight:600;margin:13px 0 5px}
.md h1:first-child,.md h2:first-child,.md h3:first-child{margin-top:2px}
.md p{margin:7px 0}
.md ul,.md ol{margin:7px 0;padding-left:22px}
.md li{margin:3px 0}
.md code{font-family:var(--mono);font-size:.92em;background:var(--surface-2);border:1px solid var(--line-soft);
  border-radius:5px;padding:1px 5px}
.md pre{background:var(--surface-2);border:1px solid var(--line-soft);border-radius:9px;padding:10px 13px;
  overflow-x:auto;margin:8px 0}
.md pre code{background:none;border:none;padding:0;font-size:11.5px;line-height:1.6}
.md table{border-collapse:collapse;margin:9px 0;font-size:12px;display:block;overflow-x:auto;max-width:100%}
.md th,.md td{border:1px solid var(--line-soft);padding:5px 10px;text-align:left;white-space:nowrap}
.md th{background:var(--surface-2);font-weight:600;font-family:var(--mono);font-size:11px}
.md hr{border:none;border-top:1px solid var(--line-soft);margin:13px 0}
.md strong{font-weight:600;color:var(--text)}

/* ---------- 智能巡检 / INSPECTION ---------- */
.insp-top{display:flex;align-items:center;gap:14px;flex-wrap:wrap;background:var(--card-grad);
  border:1px solid var(--line);border-radius:var(--r-xl);padding:15px 19px;margin-bottom:16px;box-shadow:var(--card-shadow)}
.insp-cad{display:flex;align-items:center;gap:10px;flex:1;min-width:220px}
.insp-cad .led{width:8px;height:8px;border-radius:50%;background:var(--ok-fill);flex:none}
.insp-cad.running .led{background:var(--warn-fill);animation:as-blink 1s infinite}
.insp-cad.off .led{background:var(--faint);box-shadow:none}
.insp-cad .say{font-size:12.5px;color:var(--dim)}
.insp-cad .say b{color:var(--text);font-weight:600}
.insp-acts{display:flex;align-items:center;gap:9px}
.insp-acts select{background-color:var(--input-bg);border:1px solid var(--input-line);color:var(--text);
  border-radius:8px;padding:6px 9px;font-size:12px;font-family:var(--mono)}
.insp-list{display:flex;flex-direction:column;gap:10px}
.insp-card{background:var(--card-grad);border:1px solid var(--line);border-left:3px solid var(--ok);
  border-radius:13px;box-shadow:var(--card-shadow);overflow:hidden}
.insp-card.st-error{border-left-color:var(--bad)}
.insp-card.st-running{border-left-color:var(--warn)}
.insp-card-hd{display:flex;align-items:center;gap:11px;padding:13px 17px;cursor:pointer;min-width:0}
.insp-card-hd:hover{background:var(--surface-2)}
.insp-card-hd .when{font-family:var(--mono);font-size:12px;color:var(--text);font-weight:600;flex:none}
.insp-card-hd .kind{font-family:var(--mono);font-size:10px;font-weight:600;letter-spacing:.06em;
  border-radius:5px;padding:2px 7px;flex:none;color:var(--dim);background:var(--surface-2);border:1px solid var(--line-soft)}
.insp-card-hd .kind.auto{color:var(--brand-2);background:var(--brand-dim);border-color:transparent}
.insp-card-hd .dur{font-family:var(--mono);font-size:11px;color:var(--faint);flex:none}
.insp-card-hd .chev{margin-left:auto;color:var(--faint);flex:none;transition:transform .18s}
.insp-card.open .insp-card-hd .chev{transform:rotate(90deg)}
.insp-sum{padding:0 17px 13px;font-size:13px;line-height:1.7;color:var(--dim)}
.insp-card.st-running .insp-sum{color:var(--warn)}
.insp-detail{display:none;border-top:1px solid var(--line-soft);padding:15px 17px}
.insp-card.open .insp-detail{display:block}
.insp-detail .md{font-size:13px;line-height:1.75}
.insp-sec-label{font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.1em;color:var(--dim);
  margin:15px 0 8px;text-transform:uppercase}
.insp-sec-label:first-child{margin-top:0}
.insp-err{font-family:var(--mono);font-size:12px;color:var(--bad);background:var(--bad-a10);
  border:1px solid var(--bad-a25);border-radius:9px;padding:9px 12px;margin-top:9px;white-space:pre-wrap}
.insp-empty{border:1px dashed var(--line);border-radius:var(--r-xl);padding:44px 20px;text-align:center;color:var(--faint);
  font-size:13px;line-height:2}
.insp-pg{display:flex;align-items:center;justify-content:flex-end;gap:11px;margin-top:14px}
/* 巡检项：代码判定的检查清单，一行一项，展开看发现 */
.ick{background:var(--card-grad);border:1px solid var(--line);border-radius:var(--r-xl);box-shadow:var(--card-shadow);
  margin-bottom:18px;overflow:hidden}
.ick-hd{display:flex;align-items:center;gap:12px;padding:14px 19px;flex-wrap:wrap}
.ick-hd .ttl{font-size:14px;font-weight:600;color:var(--text);flex:none}
.ick-hd .say{font-size:12.5px;color:var(--dim);flex:1;min-width:180px}
.ick-hd .say b{color:var(--text);font-weight:600}
.ick-list{border-top:1px solid var(--line-soft)}
.ick-row{border-bottom:1px solid var(--line-soft)}
.ick-row:last-child{border-bottom:none}
.ick-row-hd{display:flex;align-items:center;gap:11px;padding:11px 19px;cursor:pointer;min-width:0}
.ick-row-hd:hover{background:var(--surface-2)}
.ick-row-hd .nm{font-size:13px;color:var(--text);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ick-row-hd .area{font-size:11.5px;color:var(--faint);flex:none}
.ick-row-hd .meta{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--faint);flex:none;white-space:nowrap}
.ick-row-hd .chev{color:var(--faint);flex:none;transition:transform .18s}
.ick-row.open .ick-row-hd .chev{transform:rotate(90deg)}
.ick-lv{font-family:var(--mono);font-size:10.5px;font-weight:600;border-radius:5px;padding:2px 7px;flex:none;white-space:nowrap;
  color:var(--dim);background:var(--surface-2);border:1px solid var(--line-soft)}
.ick-lv.p0{color:var(--bad);background:var(--bad-a10);border-color:var(--bad-a25)}
.ick-lv.p1{color:var(--warn);background:var(--warn-a13);border-color:var(--warn-a26)}
.ick-lv.ok{color:var(--ok);background:var(--ok-a12);border-color:transparent}
.ick-lv.err{color:var(--bad);background:transparent;border-color:var(--bad-a25)}
.ick-body{display:none;padding:4px 19px 16px 19px}
.ick-row.open .ick-body{display:block}
.ick-what{font-size:12.5px;line-height:1.7;color:var(--dim)}
.ick-rule{font-size:12px;line-height:1.7;color:var(--faint);margin-top:4px}
.ick-f{display:flex;gap:11px;align-items:flex-start;margin-top:12px;padding:11px 13px;border:1px solid var(--line-soft);
  border-radius:11px;background:var(--surface-2)}
.ick-f.done{opacity:.65}
.ick-f-main{flex:1;min-width:0}
.ick-f-ttl{font-size:13px;color:var(--text);line-height:1.6}
.ick-f-dt{font-size:12px;color:var(--dim);line-height:1.7;margin-top:3px;word-break:break-word}
.ick-f-ts{font-family:var(--mono);font-size:11px;color:var(--faint);margin-top:5px}
.ick-f .btn{flex:none}
.insp-reports-label{margin:0 0 10px}
.ith-lv{font-size:12px;line-height:1.7;color:var(--dim);margin:-2px 0 10px}
.insp-pg .n{font-family:var(--mono);font-size:12px;color:var(--faint)}
@media (max-width:640px){
  .as-drawer{width:100vw;max-width:100vw}
  .insp-top{flex-direction:column;align-items:stretch}
  .insp-acts{justify-content:flex-end}
  .ick-hd,.ick-row-hd{padding-left:15px;padding-right:15px}
  .ick-body{padding-left:15px;padding-right:15px}
  .ick-row-hd .area{display:none}
  .ick-f{flex-direction:column}
  .as-launch span{display:none}
}
@media (prefers-reduced-motion:reduce){
  .as-launch.busy .as-led,.agstep .led,.insp-cad.running .led,.as-a .cur{animation:none}
}

/* ---------- 开放接口（Open API + 事件订阅）--------------------------------
   这一页要能一眼回答三个问题：谁在调、推出去了吗、发了什么。所以三样东西各有
   自己的形状，而不是三张长得一样的表：
   · 客户端是**名册**，最值钱的一列是「最后一次调用」——对面的定时器停了，
     这里立刻看得出来，而错误日志里什么都不会有。
   · 订阅是**管道**，所以画成卡片：地址、落后多少、最近一次的下场，一屏读完。
   · 事件是**流水**，一行一条，点开看载荷和它在每一路上的下场。
   间距节奏抄设置页（.sec 34px / .set-panel 20-22px），不另起一套更挤的数值。 */
.ox-note{font-size:12.5px;color:var(--dim);line-height:1.75;max-width:92ch}
.ox-note b{color:var(--text)}
.ox-note code,.ox-code-inline{font-family:var(--mono);font-size:12px;background:var(--surface-2);
  border:1px solid var(--line-soft);border-radius:5px;padding:1px 6px}

.ox-pre{background:var(--surface-2);border:1px solid var(--line-soft);border-radius:11px;
  padding:16px 18px;overflow:auto;font-family:var(--mono);font-size:12px;line-height:1.85;
  margin:0;white-space:pre;color:var(--text)}
.ox-pre .c{color:var(--faint)}
.ox-pre .k{color:var(--brand-2)}
.ox-copy{display:flex;justify-content:flex-end;margin-top:12px}

/* 端点表：能力位用色点区分，一眼看得出哪扇门归哪把钥匙 */
.ox-scope{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:11px;
  color:var(--dim);white-space:nowrap}
.ox-scope i{width:7px;height:7px;border-radius:50%;flex:none;background:var(--faint);font-style:normal}
.ox-scope[data-s="finance:read"] i{background:var(--brand)}
.ox-scope[data-s="events:read"] i{background:var(--ok)}
.ox-scope[data-s="ops:read"] i{background:var(--warn)}
.ox-ep-path{font-family:var(--mono);font-size:12.5px;color:var(--text)}
.ox-ep-sum{font-weight:600}
.ox-ep-det{font-size:12px;color:var(--faint);line-height:1.65;margin-top:5px;max-width:74ch}
.ox-ep-params{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.ox-ep-params span{font-family:var(--mono);font-size:11px;color:var(--dim);
  background:var(--surface-2);border:1px solid var(--line-soft);border-radius:5px;padding:2px 7px}

/* 订阅卡片 */
.ox-subs{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:18px;align-items:start}
.ox-sub{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:20px 22px 18px;box-shadow:var(--card-shadow);display:flex;flex-direction:column;gap:14px}
.ox-sub.paused{border-color:var(--bad-a45)}
.ox-sub-top{display:flex;align-items:flex-start;justify-content:space-between;gap:14px}
.ox-sub-name{font-weight:600;font-size:14.5px;display:flex;align-items:center;gap:9px}
.ox-sub-url{font-family:var(--mono);font-size:11.5px;color:var(--faint);margin-top:6px;
  overflow-wrap:anywhere;line-height:1.6}
.ox-sub-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;
  border-top:1px solid var(--line-soft);border-bottom:1px solid var(--line-soft);padding:14px 0}
.ox-sub-grid .k{font-size:11px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;
  color:var(--dim);white-space:nowrap}
.ox-sub-grid .v{font-family:var(--mono);font-size:14px;margin-top:6px;font-variant-numeric:tabular-nums}
.ox-sub-grid .v.bad{color:var(--bad)}
.ox-sub-grid .v.ok{color:var(--ok)}
.ox-sub-acts{display:flex;flex-wrap:wrap;gap:8px}
.ox-pause{font-size:12px;color:var(--bad);background:var(--bad-a9);border-radius:9px;
  padding:11px 13px;line-height:1.65}

/* 事件流 */
.ox-ev-type{font-family:var(--mono);font-size:12px;color:var(--text);white-space:nowrap}
.ox-ev-sub{font-family:var(--mono);font-size:12px;color:var(--dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:280px}
.ox-ev-money{color:var(--warn)}
.ox-empty{padding:34px 22px;text-align:center;color:var(--faint);font-size:13px;line-height:1.8}

/* 一次性密钥：只在生成那一刻给一次，之后任何读取都拿不到 */
.ox-secret{background:var(--surface-2);border:1px solid var(--brand-a45);border-radius:11px;
  padding:16px 18px;margin-top:14px}
.ox-secret .v{font-family:var(--mono);font-size:13px;overflow-wrap:anywhere;line-height:1.7;
  margin-top:9px;user-select:all}

@media (max-width:760px){
  .ox-subs{grid-template-columns:1fr}
  .ox-sub-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* ============================================================
   服务状态 · 和门户读的是同一份快照
   ------------------------------------------------------------
   两块东西：顶栏底下那条实时带（整条线现在怎么样）、状态页那张网格（每个模型
   这一段时间怎么样）。两者都读 /admin/api/model-status —— 服务端 5 分钟一刷的
   内存字节，和客户在门户上看到的 /portal/api/status 是同一份。这一页存在的
   全部理由就是这一句：客服和客户不该对着同一时刻给出两个数。

   间距节奏抄自 .panel / .card：卡片 14px 圆角、16-20px 内边距、区块间 24px。
   颜色只用语义令牌（--ok / --warn / --or-coral / --bad），两套主题自动跟随。
   ============================================================ */
/* 实时带钉在顶栏底下。--topbar-only-h 是顶栏自己的高度，--topbar-h 是顶栏加这
   一条（sxTopbarH 一次量两个）—— 设置页那条 .sx-rail 钉的正是后者，于是三层
   自动叠好，谁也不压着谁。带子自己是个 <button>：读到不对劲的数，下一步一定
   是去状态页，别让人再回头找入口。 */
.lv{position:sticky;top:var(--topbar-only-h,74px);z-index:4;
  display:flex;align-items:center;gap:0 20px;width:100%;height:40px;padding:0 28px;
  background:var(--glass);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);border:0;border-bottom:1px solid var(--line-soft);border-radius:0;
  font:inherit;text-align:left;color:var(--dim);cursor:pointer;
  overflow-x:auto;scrollbar-width:none;transition:background .12s}
.lv::-webkit-scrollbar{display:none}
.lv:hover{background:var(--fill-4)}
.lv:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.lv-state{display:inline-flex;align-items:center;gap:8px;flex:none;
  font-size:13px;font-weight:600;color:var(--text)}
/* 点用 currentColor 画呼吸环，状态色只在一处设置——三个状态各写一遍环，迟早
   会有一个忘了跟着改。 */
.lv-dot{position:relative;width:8px;height:8px;border-radius:50%;flex:none;
  background:currentColor;color:var(--ok-fill)}
.lv-dot::after{content:'';position:absolute;inset:-4px;border-radius:50%;
  border:1px solid currentColor;opacity:0;animation:lvPing 2.6s ease-out infinite}
@keyframes lvPing{0%{transform:scale(.6);opacity:.5}70%{transform:scale(1.45);opacity:0}100%{opacity:0}}
.lv.degraded .lv-state{color:var(--warn)} .lv.degraded .lv-dot{color:var(--warn-fill)}
.lv.outage .lv-state{color:var(--bad)} .lv.outage .lv-dot{color:var(--bad-fill)}
/* 线上没有流量时不呼吸：一个对着 0 请求跳动的点，说的是「有事在发生」，
   而那正是此刻唯一不成立的事。 */
.lv.idle .lv-dot::after{display:none}
.lv-metrics{display:flex;align-items:center;gap:0 18px;flex:1 1 auto;min-width:0;white-space:nowrap}
.lv-m{display:inline-flex;align-items:baseline;gap:6px;flex:none}
.lv-m i{font-style:normal;font-size:13px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums}
.lv-m u{text-decoration:none;font-size:12px;color:var(--faint)}
.lv-m.warn i{color:var(--warn)} .lv-m.bad i{color:var(--bad)}
.lv-idle{font-size:13px;color:var(--faint)}
.lv-win{margin-left:auto;flex:none;font-size:12px;color:var(--faint);font-variant-numeric:tabular-nums}

.st-banner{display:flex;align-items:center;gap:12px;flex-wrap:wrap;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-xl);padding:18px 20px;margin-bottom:24px;
  box-shadow:var(--card-shadow)}
.st-banner .st-dot{width:11px;height:11px;border-radius:50%;flex:none;background:var(--ok)}
.st-banner.degraded .st-dot{background:var(--warn)}
.st-banner.outage .st-dot{background:var(--bad)}
.st-banner .st-lead{font-size:16px;font-weight:700;letter-spacing:-.01em;color:var(--ok)}
.st-banner.degraded .st-lead{color:var(--warn)}
.st-banner.outage .st-lead{color:var(--bad)}
.st-banner .st-meta{margin-left:auto;font-size:13px;color:var(--dim);font-variant-numeric:tabular-nums}
/* 一个模型一行，十几行共用一张卡片。名字在左、条在中、三个读数在右；左右两
   头定宽，所以所有行的条左右对齐——对齐是这个版式能成立的前提：同一列像素在
   每一行都是同一段时间。 */
.st-list{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:2px 20px;box-shadow:var(--card-shadow)}
.st-row{display:flex;align-items:center;gap:20px;padding:15px 0;border-top:1px solid var(--line-soft)}
.st-row:first-child{border-top:0}
.st-id{flex:none;width:216px;min-width:0;display:flex;flex-direction:column;gap:3px}
.st-id .st-name{font-family:var(--mono);font-size:13.5px;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.st-id .st-sub{font-size:11.5px;color:var(--faint);font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* 右边三列：首字、生成速度、正常运行。右对齐、列宽固定，所以它们在所有行的同
   一个 x 上——这不是排版洁癖，是这三个数唯一能用的读法：延迟和吞吐单看一行没
   有意义，「81 还是 26」要跟别的模型比才知道好坏。
   两个速度读数用等宽字体，正常运行率用正文字体加粗：**等宽 = 量出来的，正文 =
   判出来的**。前两个是测量值，最后一个是我们对这条线下的结论。 */
.st-meas{flex:none;display:flex;align-items:center;gap:14px}
.st-m{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:3px}
.st-m.c-ttfb{width:62px} .st-m.c-tps{width:78px} .st-m.st-fig{width:64px}
.st-m>b{font-family:var(--mono);font-size:12px;font-weight:500;color:var(--dim);
  font-variant-numeric:tabular-nums;white-space:nowrap}
/* 没有读数时留一个破折号而不是空白：定宽的一列里，空白读起来像「这里坏了」，
   破折号读起来像「这次没有数」——后者才是真的（样本不够）。 */
.st-m>b.st-none{color:var(--faint);font-weight:400}
/* 每格自带的小标题：桌面上由表头统一说一次，所以藏起来；窄屏行会折行、表头对
   不上列，那时候再各说各的。同一份 DOM 两种读法，不用渲染两次。 */
.st-m>u{display:none;text-decoration:none;font-size:11px;color:var(--faint);white-space:nowrap}
/* 正常运行率回正文字体加粗——它是结论，不是读数。颜色留给下面那四条按档位的
   规则去写：在这里写一个 color 会盖掉它们（.st-m>b 只有一个类，输给两个类的
   .st-rate.ok），于是全页的绿/黄/红一起失效。 */
.st-rate{font-family:var(--sans);font-size:14px;font-weight:700}
.st-rate.ok{color:var(--ok)} .st-rate.warn{color:var(--warn)}
.st-rate.mid{color:var(--or-coral)} .st-rate.bad{color:var(--bad)}
/* 表头只盖住右边这三列——左边不写「模型」，因为那一列写的就是模型名；条那一列
   也不标：它底下已经有一根共用坐标轴在说话。 */
.st-head{display:flex;align-items:flex-end;gap:20px;padding:15px 0 9px}
.st-head .st-head-pad{flex:none;width:216px}
.st-head .st-head-band{flex:1 1 0;min-width:0}
.st-head .st-m>u{display:block;letter-spacing:.04em}
.st-band{flex:1 1 0;min-width:0;display:flex;align-items:stretch;gap:3px;height:32px}
.st-band i{flex:1 1 0;min-width:0;display:block;border-radius:2px;background:var(--ok-quiet)}
.st-band i.ok{background:var(--ok)} .st-band i.warn{background:var(--warn)}
.st-band i.mid{background:var(--or-coral)} .st-band i.bad{background:var(--bad)}
.st-band i:hover{filter:brightness(1.12);box-shadow:inset 0 0 0 1px var(--text-a60)}
/* 系列行与它下面的版本行。分隔线画在系列外框上（.st-row:first-child 的规则在
   这里管不到：每个系列里的第一行都是 first-child），版本行之间用同一种细线。
   版本行左边缩进 20px 表明从属；列宽不变（border-box），条和三列读数照旧对齐。 */
.st-fam{border-top:1px solid var(--line-soft)}
.st-fam>.st-row{border-top:0;cursor:pointer}
.st-fam-btn{all:unset;display:flex;align-items:center;gap:6px;min-width:0;cursor:pointer;border-radius:6px}
.st-fam-btn:focus-visible{outline:2px solid var(--focus);outline-offset:3px}
.st-fam-btn .st-name{font-family:var(--sans);font-size:14.5px;font-weight:700}
.st-chev{flex:none;width:14px;height:14px;color:var(--faint);transition:transform .15s ease}
.st-fam.open .st-chev{transform:rotate(90deg)}
.st-famrow:hover .st-chev{color:var(--text)}
.st-id .st-sub.st-low{color:var(--warn)}
.st-vers{display:none;padding-bottom:4px}
.st-fam.open .st-vers{display:block}
.st-vers>.st-row{border-top:1px solid var(--line-soft);padding:11px 0}
.st-vers .st-id{padding-left:20px}
.st-vers .st-id .st-name{font-size:12.5px;font-weight:500;color:var(--dim)}
.st-vers .st-band{height:22px}
@media (prefers-reduced-motion:reduce){.st-chev{transition:none}}
/* 共用坐标轴：左右两个端点 + 中间一句总结，两根细线把它们拉开。
   它对齐到条的那一列（左右的空位和 .st-id / 读数区同宽）。 */
.st-axis{display:flex;align-items:center;gap:20px;padding:2px 20px 0;margin-top:10px}
.st-axis .st-axis-pad{flex:none;width:216px}
/* 右垫片 = 读数区整块的宽度（62 + 14 + 78 + 14 + 64），坐标轴才和条的右端对齐。 */
.st-axis .st-axis-pad.r{width:232px}
.st-axis .st-axis-line{flex:1 1 0;min-width:0;display:flex;align-items:center;gap:10px;
  font-size:11.5px;color:var(--faint);white-space:nowrap}
.st-axis .st-axis-line .rule{flex:1 1 0;min-width:8px;height:0;border-top:1px solid var(--line)}
.st-axis em{font-style:normal;color:var(--dim);font-variant-numeric:tabular-nums}
.st-legend{display:flex;align-items:center;gap:16px;flex-wrap:wrap;font-size:12px;color:var(--faint);
  margin:14px 2px 0}
.st-legend i{width:10px;height:10px;border-radius:3px;display:inline-block;margin-right:5px;vertical-align:-1px}
.st-legend .st-legend-note{margin-left:auto;color:var(--faint)}
.st-foot{font-size:12px;line-height:1.85;color:var(--faint);margin-top:22px;max-width:78ch}
@media (max-width:900px){
  .st-banner .st-meta{margin-left:0;flex-basis:100%}
  /* 窄屏上名字和百分比先占一行，条掉到下面独占整宽：定宽的名字列在窄屏上会把
     条挤成一截。折行之后列就对不上表头了，所以表头收起来，每个读数各自带上
     小标题。 */
  .st-list{padding:2px 14px}
  .st-row{flex-wrap:wrap;gap:8px 12px;padding:13px 0}
  .st-id{flex:1 1 auto;width:auto}
  .st-head{display:none}
  .st-meas{gap:16px}
  .st-m{align-items:flex-end}
  .st-m.c-ttfb,.st-m.c-tps,.st-m.st-fig{width:auto}
  .st-m>u{display:block}
  .st-band{flex-basis:100%;order:3;gap:1.5px;height:26px}
  .st-vers .st-band{height:20px}
  .st-vers .st-id{padding-left:16px}
  .st-axis{padding:2px 14px 0;gap:0}
  .st-axis .st-axis-pad{display:none}
}
@media (max-width:860px){
  .lv{height:40px;padding:0 14px;gap:0 14px}
  .lv-win{display:none}
}

/* 掌上（≤768）：顶栏吸在屏幕最上，实心底，避开刘海。往下滚过一小段就收起实时带
   和模块页签，只留这一条约 44px 的标题栏；往上滚再展开。搜索点开后输入框铺满
   同一条，结果仍走原来的全局搜索。桌面不进这一档。 */
@media (max-width:768px){
  .topbar{
    position:sticky;top:0;z-index:20;
    background:var(--bg);
    -webkit-backdrop-filter:none;backdrop-filter:none;
    padding:0 10px;
    padding-top:env(safe-area-inset-top);
    min-height:calc(44px + env(safe-area-inset-top));
    height:calc(44px + env(safe-area-inset-top));
    gap:4px;
    border-bottom:1px solid var(--line-soft);
  }
  .topbar.m-qopen{z-index:76}
  .topbar.m-qopen>.tb-ident,
  .topbar.m-qopen>.right{display:none}
  .topbar.m-qopen .tb-qbox{
    display:flex;align-items:center;gap:8px;
    flex:1 1 auto;min-width:0;height:36px;
  }
  .tb-qbox input{
    flex:1;min-width:0;width:auto;height:36px;margin:0;
    padding:0 12px;border-radius:var(--r-pill);
    background:var(--fill-4);border:1px solid transparent;
  }
  .tb-qcancel{
    flex:none;background:none;border:0;color:var(--link);
    font-size:16px;font-weight:500;padding:6px 4px;min-height:36px;
  }
  .lv,.mt-bar{
    transition:max-height .22s var(--ease),opacity .18s var(--ease),
      padding .22s var(--ease),border-color .18s var(--ease);
  }
  .lv{max-height:48px}
  .mt-bar{max-height:64px;overflow:hidden}
  body.m-chrome-collapse .lv,
  body.m-chrome-collapse .mt-bar{
    max-height:0;min-height:0;opacity:0;
    padding-top:0;padding-bottom:0;border-bottom-width:0;
    overflow:hidden;pointer-events:none;
  }
  .topbar .btn.tb-search{position:relative}
  .topbar .btn.tb-search.has-q::after{
    content:"";position:absolute;top:4px;right:4px;
    width:7px;height:7px;border-radius:50%;
    background:var(--link);
    box-shadow:0 0 0 2px var(--bg);
  }
  .pal.m-inline{
    top:var(--topbar-only-h,44px);left:0;right:0;width:100%;max-width:none;
    border-radius:0 0 var(--r-xl) var(--r-xl);
    border-left:0;border-right:0;
    transform:none;
    background:var(--surface);
    -webkit-backdrop-filter:none;backdrop-filter:none;
  }
  .pal.m-inline.show{transform:none}
  .pal.m-inline .pal-find{display:none}
  .pal.m-inline .pal-list{max-height:calc(100dvh - var(--topbar-only-h,44px) - 96px)}
}
@media (max-width:768px) and (prefers-reduced-motion:reduce){
  .lv,.mt-bar{transition:none}
}

/* ============================================================
   系统的「降低透明度」「增强对比度」（HIG · Accessibility）
   ------------------------------------------------------------
   玻璃在「降低透明度」下退回实心：开这个设置的人是在说，模糊的背景让他读不清。
   「增强对比度」把分隔线和次级文字各加深一档。「减弱动态效果」在窄屏那一节后面
   已经整页关掉动画。:root 是深色、[data-theme=light] 是浅色，后者必须写在后面——
   两个选择器特指度相同，谁在后谁生效。
   ============================================================ */
@media (prefers-reduced-transparency:reduce){
  :root,[data-theme=light]{--glass:var(--surface);--glass-thick:var(--surface);--rail-bg:var(--surface);--toast-bg:var(--surface)}
  .topbar,.lv,.mt-bar,.rail,.pal,.cfm,.combo-pop,.rowmenu,.pv-pop,.jack-pop,.toast,#hm-tip{-webkit-backdrop-filter:none!important;backdrop-filter:none!important}
}
@media (prefers-contrast:more){
  :root{--line:#636366;--line-soft:#48484a;--dim:#d1d1d6;--faint:#aeaeb2;--input-line:#8e8e93;--rail-line:rgba(255,255,255,.3);--rail-dim:#f5f5f7}
  [data-theme=light]{--line:#aeaeb2;--line-soft:#c7c7cc;--dim:#3a3a3c;--faint:#545458;--input-line:#8e8e93;--rail-line:rgba(0,0,0,.28);--rail-dim:#1d1d1f}
}
/* User connection diagnosis shares the console's spacing and semantic colors. */
.ucd-toolbar{display:flex;align-items:center;gap:10px;flex-wrap:nowrap;overflow-x:auto;padding-bottom:4px}
.ucd-toolbar input,.ucd-toolbar select{width:auto;flex-shrink:0;margin:0}
.ucd-toolbar input[type=search]{width:160px;min-width:120px}
.ucd-toolbar label{display:flex;align-items:center;gap:8px;white-space:nowrap;color:var(--dim);font-size:12px}
.ucd-tools-space{flex:1;min-width:0}
.ucd-toolbar .btn{flex-shrink:0;white-space:nowrap}
.ucd-summary{margin:16px 0;color:var(--dim);font-size:13px;line-height:1.6}
.blk-key{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;word-break:break-all}
.blk-bars{display:inline-flex;align-items:flex-end;gap:1px;height:28px}
.blk-bars i{display:inline-block;width:4px;background:var(--acc,#4f7cff);border-radius:1px;opacity:.75}
.blk-bars i:hover{opacity:1}
.ucd-table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface)}
.ucd-table{width:100%;min-width:1000px;border-collapse:collapse;font-size:13px}
.ucd-table th,.ucd-table td{padding:14px 16px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
.ucd-table th{color:var(--dim);font-size:12px;font-weight:500;white-space:nowrap}
.ucd-table tr:last-child td{border-bottom:0}
.ucd-table td:nth-child(4),.ucd-table td:nth-child(5),.ucd-table td:nth-child(6){font-variant-numeric:tabular-nums;white-space:nowrap}
.ucd-user small,.ucd-mode small{display:block;margin-top:6px;color:var(--dim);font-size:11px}
.ucd-reason{max-width:360px;line-height:1.6}
.ucd-adjust{color:var(--bad);font-weight:600}
.ucd-detail{padding:4px 4px 8px;line-height:1.7}
.ucd-detail p{margin:8px 0 12px;color:var(--dim)}
.ucd-detail h4{font-size:13px;margin:18px 0 8px}
.ucd-detail table{width:100%;font-size:12px}
.ucd-detail td,.ucd-detail th{padding:10px 12px}
.ucd-detail code{overflow-wrap:anywhere;font-size:11px}
.ucd-method{margin-top:24px;border:1px solid var(--line);border-radius:var(--r-xl);padding:16px 20px;line-height:1.7;color:var(--dim);font-size:13px}
.ucd-method summary{cursor:pointer;color:var(--text)}
.ucd-method p{margin:12px 0 0}

/* ---- 充值分析（#bx-analysis）。类名一律 ta- 开头：.lede/.bar/.cell 这类短名全站早被占了。
   卡片、统计块、表格沿用 .panel / .tiles / .chips / .winsel 的数值，不另起一套间距。 */
.ta-head .tiles{margin-bottom:0}
#bx-analysis .sec{transition:opacity .2s var(--ease)}
#bx-analysis.ta-busy .sec{opacity:.55}
.ta-filter{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 16px}
.ta-filter-l,.ta-filter-r{display:flex;flex-wrap:wrap;align-items:center;gap:10px 12px;min-width:0;max-width:100%}
.ta-filter .winsel{max-width:100%;overflow-x:auto;scrollbar-width:none}
.ta-filter .winsel::-webkit-scrollbar{display:none}
.ta-range{font-size:12.5px;color:var(--dim)}
.ta-custom{display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px;margin-top:12px;padding:12px 14px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
.ta-custom.hide{display:none}
.ta-custom label{display:inline-flex;align-items:center;gap:8px;font-size:12.5px;color:var(--dim)}
.ta-custom input{width:auto}
.ta-lede{margin:18px 0;font-family:var(--disp);font-size:18px;line-height:1.6;font-weight:500;letter-spacing:-.01em;max-width:64ch;text-wrap:pretty}
.ta-lede em{font-style:normal;font-weight:600;font-variant-numeric:tabular-nums}
.ta-lede.ta-wait{color:var(--faint)}
#ta-tiles{grid-template-columns:repeat(5,minmax(0,1fr))}
#ta-tiles .tile .v{font-variant-numeric:normal}
.ta-tile-top{display:flex;align-items:flex-end;justify-content:space-between;gap:10px}
.ta-tile-top svg{flex:none;width:76px;height:30px;margin-bottom:2px}
.ta-d{font-size:12.5px;margin-top:8px;font-weight:600}
.ta-d span{color:var(--dim);font-weight:400}
.ta-d.up{color:var(--ok)}.ta-d.down{color:var(--bad)}.ta-d.flat{color:var(--faint);font-weight:500}
.ta-conc{display:flex;height:6px;gap:2px;margin-top:10px;border-radius:3px;overflow:hidden}
.ta-conc i{display:block}
.ta-card{padding:18px 20px}
.ta-cardhead{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:12px 16px}
.ta-cardhead>div:first-child{flex:1 1 220px;min-width:0}
.ta-cardhead b{display:block;font-family:var(--disp);font-size:15px;font-weight:600;letter-spacing:-.005em}
.ta-cardhead span{display:block;font-size:12px;color:var(--faint);margin-top:5px;line-height:1.6}
.ta-cardact{display:flex;flex-wrap:wrap;align-items:center;gap:12px 14px}
.ta-legend{display:flex;flex-wrap:wrap;align-items:center;gap:6px 14px;font-size:12px;color:var(--dim)}
.ta-legend span{display:inline-flex;align-items:center;gap:6px}
.ta-legend i{display:inline-block;width:10px;height:10px;border-radius:3px;flex:none}
.ta-legend i.ln{width:14px;height:2px;border-radius:1px}
.ta-legend-gap{margin-top:14px}
.ta-plotwrap{overflow-x:auto;margin-top:18px}
.ta-plot{min-width:560px;display:grid;grid-template-columns:44px minmax(0,1fr);column-gap:8px}
.ta-yax{position:relative;height:210px}
.ta-yax span{position:absolute;right:0;transform:translateY(50%);font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.ta-area{position:relative;height:210px;border-bottom:1px solid var(--line)}
.ta-grid{position:absolute;left:0;right:0;height:1px;background:var(--line-soft)}
.ta-cols{position:absolute;inset:0;display:grid}
.ta-col{height:210px;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;border-radius:6px 6px 0 0}
.ta-col.hi{background:var(--fill-4)}
.ta-col i{display:block;width:14px;max-width:70%}
.ta-col i.n{background:var(--ta-new)}.ta-col i.o{background:var(--ta-old)}
.ta-xax{display:grid;margin-top:6px}
.ta-xax span{font-size:11px;color:var(--faint);text-align:center;white-space:nowrap;overflow:visible}
.ta-tip{position:absolute;top:6px;width:224px;background:var(--glass-thick);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-3);padding:11px 13px;font-size:12px;pointer-events:none;z-index:1}
.ta-tip-h{display:flex;justify-content:space-between;gap:8px;color:var(--dim)}
.ta-tip-v{font-family:var(--disp);font-size:17px;font-weight:600;margin-top:2px;font-variant-numeric:tabular-nums}
.ta-tip-r{display:flex;align-items:center;gap:7px;margin-top:5px}
.ta-tip-r i{display:inline-block;width:8px;height:8px;border-radius:2px;flex:none}
.ta-tip-r span:nth-child(2){flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ta-tip-r span:last-child{font-variant-numeric:tabular-nums}
.ta-end{position:absolute;right:8px;transform:translateY(-6px);font-size:12px;background:var(--surface);padding:0 4px;font-variant-numeric:tabular-nums;white-space:nowrap}
.ta-svg{position:absolute;inset:0;display:block;width:100%;height:210px}
.ta-empty{padding:28px 0;color:var(--dim);font-size:13px;text-align:center}
.ta-three{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
.ta-bands{display:flex;flex-direction:column;gap:14px;margin-top:14px}
.ta-band{display:grid;grid-template-columns:96px minmax(0,1fr);column-gap:12px;align-items:center}
.ta-band>span{font-size:12.5px;color:var(--dim);white-space:nowrap}
.ta-band-bars{display:grid;grid-template-columns:minmax(0,1fr) 112px;row-gap:4px;column-gap:8px;align-items:center;font-size:11.5px;font-variant-numeric:tabular-nums}
.ta-track{height:8px;position:relative}
.ta-track i{position:absolute;left:0;top:0;bottom:0;min-width:2px;border-radius:0 4px 4px 0}
.ta-foot{font-size:12px;color:var(--dim);margin-top:16px;padding-top:12px;border-top:1px solid var(--line-soft);line-height:1.7}
.ta-foot:empty{display:none}
.ta-stack{display:flex;height:14px;gap:2px;margin-top:18px}
.ta-stack i{display:block;min-width:4px}
.ta-stack i:first-child{border-radius:4px 0 0 4px}.ta-stack i:last-child{border-radius:0 4px 4px 0}
.ta-stack i:only-child{border-radius:4px}
.ta-flows{display:flex;flex-direction:column;gap:10px;margin-top:16px}
.ta-flow{display:flex;align-items:center;gap:8px;font-size:13px}
.ta-flow i{display:inline-block;width:10px;height:10px;border-radius:3px;flex:none}
.ta-flow span:nth-child(2){flex:1}
.ta-flow b{font-weight:600;font-variant-numeric:tabular-nums}
.ta-flow em{font-style:normal;color:var(--faint);width:38px;text-align:right;font-variant-numeric:tabular-nums}
.ta-tier{display:grid;grid-template-columns:96px minmax(0,1fr) 72px;align-items:center;column-gap:10px;font-size:12.5px;margin-top:8px}
.ta-tier i{display:block;height:6px;background:var(--ta-mid);border-radius:0 3px 3px 0}
.ta-tier b{font-weight:500;text-align:right;font-variant-numeric:tabular-nums}
.ta-coh{display:grid;gap:4px;margin-top:16px;font-size:12px}
.ta-coh-h{color:var(--dim);text-align:center;white-space:nowrap}
.ta-coh-l{align-self:center;line-height:1.35}
.ta-coh-l b{font-weight:600;font-size:13px}
.ta-coh-c{height:52px;border-radius:var(--r-sm);display:flex;flex-direction:column;align-items:center;justify-content:center;line-height:1.25;font-variant-numeric:tabular-nums}
.ta-coh-c b{font-size:14px;font-weight:600}
.ta-coh-c small{font-size:11px;opacity:.85}
.ta-due-sum{text-align:right}
.ta-due-sum b{display:block;font-family:var(--disp);font-size:20px;font-weight:600;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.ta-due-sum span{font-size:12px;color:var(--dim)}
.ta-calwrap{overflow-x:auto;margin-top:16px}
.ta-cal{min-width:980px;display:grid;grid-template-columns:1.5fr repeat(14,minmax(0,1fr));gap:6px}
.ta-cal-c{display:flex;flex-direction:column;gap:6px;min-width:0}
.ta-cal-d{font-size:12px;padding:0 2px 6px;border-bottom:2px solid var(--line);white-space:nowrap;color:var(--faint)}
.ta-cal-d small{font-size:inherit;font-weight:400;color:var(--faint)}
.ta-cal-d.has{border-color:var(--warn);color:var(--text);font-weight:600}
.ta-cal-d.over{border-color:var(--bad);color:var(--bad);font-weight:600}
.ta-chipc{display:block;width:100%;text-align:left;border-radius:var(--r-md);padding:7px 9px;background:var(--surface-2);border:1px solid var(--line);font-size:11.5px;line-height:1.35;color:var(--text);cursor:pointer}
.ta-chipc:hover{border-color:var(--ta-mid)}
.ta-chipc b{display:block;font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ta-chipc span{display:block;color:var(--dim)}
.ta-chipc em{display:block;font-style:normal;font-variant-numeric:tabular-nums}
.ta-chipc.over{background:var(--bad-a9);border-color:transparent}
.ta-chipc.ok em{color:var(--ok)}
.ta-chips{margin-bottom:14px}
#ta-q{width:230px}
.ta-rank{overflow-x:auto}
.ta-rank table{min-width:1080px}
.ta-rank th button{font:inherit;color:inherit;padding:0;background:none;white-space:nowrap}
.ta-rank th button.on{color:var(--text)}
.ta-rank th.num,.ta-rank td.num{text-align:right}
#ta-rows tr{cursor:pointer}
#ta-rows tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
#ta-rows td{padding-top:9px;padding-bottom:9px}
.ta-rk{color:var(--faint);font-size:12.5px;width:28px}
.ta-who{display:flex;align-items:center;gap:6px;min-width:0}
.ta-who b{font-weight:600;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ta-who2{display:block;font-size:12px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:220px}
.ta-amt{display:flex;align-items:center;gap:10px;min-width:180px}
.ta-amt b{font-weight:600;width:68px;flex:none;text-align:right;font-variant-numeric:tabular-nums}
.ta-amt .ta-track{flex:1;min-width:40px}
.ta-amt em{font-style:normal;font-size:11.5px;color:var(--faint);width:40px;flex:none;text-align:right;font-variant-numeric:tabular-nums}
.ta-dl{font-weight:600;white-space:nowrap}
.ta-dl.up{color:var(--ok)}.ta-dl.down{color:var(--bad)}.ta-dl.flat{color:var(--faint);font-weight:500}
.ta-two{line-height:1.35;white-space:nowrap}
.ta-two small{display:block;font-size:11.5px;color:var(--faint)}
.ta-spark{display:flex;align-items:flex-end;gap:3px;height:24px}
.ta-spark i{display:block;width:8px;border-radius:2px 2px 0 0;background:var(--ta-mid)}
.ta-spark i.z{background:var(--line)}.ta-spark i.last{background:var(--text)}
.ta-subc{line-height:1.35;min-width:140px}
.ta-subc small{display:block;font-size:11.5px;color:var(--faint)}
.ta-tags{display:flex;flex-wrap:wrap;gap:4px}
.ta-tg-new{color:var(--ta-new-ink);background:var(--ta-new-bg)}
.ta-tg-due{color:var(--warn);background:var(--warn-a12)}
.ta-tg-churn{color:var(--bad);background:var(--bad-a11)}
.ta-tg-down,.ta-tg-idle{color:var(--dim);background:var(--fill-3)}
.ta-tfoot{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;padding:12px 16px;border-top:1px solid var(--line-soft);font-size:12px;color:var(--dim)}
.ta-note{max-width:92ch}
/* 档案抽屉 */
.ta-pf{display:flex;flex-direction:column;gap:26px}
.ta-pf-tags{display:flex;flex-wrap:wrap;gap:6px}
.ta-pf-mini{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
.ta-pf-mini div{background:var(--surface-2);border:1px solid var(--line-soft);border-radius:var(--r-lg);padding:12px 14px;min-width:0}
.ta-pf-mini b{display:block;font-size:12px;color:var(--dim);font-weight:600}
.ta-pf-mini span{display:block;font-family:var(--disp);font-size:22px;font-weight:600;letter-spacing:-.02em;margin-top:4px;white-space:nowrap}
.ta-pf-mini small{display:block;font-size:11.5px;color:var(--faint)}
.ta-pf h4{font-size:13px;font-weight:600;margin:0 0 12px}
.ta-tl{display:grid;grid-template-columns:58px minmax(0,1fr);row-gap:10px;column-gap:10px;align-items:end}
.ta-tl>span{font-size:12px;color:var(--dim)}
.ta-lane{position:relative}
.ta-lane>*{position:absolute}
.ta-lane .ta-seg{top:0;bottom:0;border-radius:6px;font-size:11px;font-weight:600;display:flex;align-items:center;padding-left:6px;white-space:nowrap;overflow:hidden}
.ta-facts{display:flex;flex-direction:column;gap:10px}
.ta-fact{display:grid;grid-template-columns:18px minmax(0,1fr);gap:8px;font-size:13px;line-height:1.55}
.ta-fact i{width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;font-style:normal;margin-top:1px;background:var(--fill-3);color:var(--dim)}
.ta-fact i.w{background:var(--warn-a12);color:var(--warn)}
.ta-recs{border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden}
.ta-rec{display:grid;grid-template-columns:1fr auto;gap:4px 12px;padding:12px 14px;border-top:1px solid var(--line-soft)}
.ta-rec:first-child{border-top:0}
.ta-rec b{font-size:14px;font-weight:600;text-align:right;font-variant-numeric:tabular-nums}
.ta-rec small{font-size:12px;color:var(--dim)}
@media (max-width:1180px){
  #ta-tiles{grid-template-columns:repeat(3,minmax(0,1fr))}
  .ta-three{grid-template-columns:repeat(2,minmax(0,1fr))}
}
/* 窄屏：统计块两列（实付占满一行）、三张小卡一列、排行一人一张卡。
   卡片网格照抄充值收款（#topups）：tr 是两列网格，格子按 grid-area 落位。 */
@media (max-width:860px){
  #ta-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}
  #ta-tiles .tile:first-child{grid-column:1/-1}
  .ta-lede{font-size:16px;margin:14px 0}
  .ta-three{grid-template-columns:1fr}
  .ta-card{padding:16px}
  .ta-plot{min-width:0;grid-template-columns:30px minmax(0,1fr);column-gap:6px}
  .ta-col i{width:6px}
  .ta-xax span{font-size:10.5px}
  .ta-filter-r .ta-range{display:none}
  #ta-rank-sec .sx-act,#ta-q{width:100%}
  .ta-rank{overflow:hidden}
  .ta-rank table{display:block;min-width:0}
  .ta-rank thead{display:none}
  #ta-rows{display:block}
  #ta-rows tr{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:14px;row-gap:8px;align-items:center;padding:14px 16px;border-bottom:1px solid var(--line-soft)}
  #ta-rows tr:last-child{border-bottom:0}
  #ta-rows td{display:block;padding:0;border:0;min-width:0}
  #ta-rows td.ta-c-rk,#ta-rows td.ta-c-cnt,#ta-rows td.ta-c-spark,#ta-rows td.ta-c-life,#ta-rows .ta-two small.f{display:none}
  #ta-rows td.ta-c-who{grid-area:1/1/2/2}
  #ta-rows td.ta-c-dl{grid-area:1/2/2/3;text-align:right}
  #ta-rows td.ta-c-amt{grid-column:1/-1;grid-row:2}
  #ta-rows td.ta-c-sub{grid-column:1/2;grid-row:3}
  #ta-rows td.ta-c-last{grid-column:2/3;grid-row:3;text-align:right;color:var(--dim);align-self:start}
  #ta-rows td.ta-c-tags{grid-column:1/-1;grid-row:4}
  #ta-rows td.ta-c-tags:has(.ta-tags:empty){display:none}
  #ta-rows td.empty{grid-column:1/-1}
  .ta-amt{min-width:0}
  .ta-amt b{width:auto;font-family:var(--disp);font-size:16px}
  .ta-who2{max-width:none}
  .ta-subc{min-width:0}
}

/* ---- 注册管理（#gx-signup）。类名一律 su- 开头；导语、卡片头、柱状图、留存格、条形沿用充值分析的 ta- 类，间距照抄。
   风险只用两种颜色说话：红 = 高风险 / 熔断，橙 = 中风险 / 门口拒绝；其余都是中性灰和品牌蓝。 */
#su-growth .sec,#su-risk .sec{transition:opacity .2s var(--ease)}
#su-growth.ta-busy .sec,#su-risk.ta-busy .sec{opacity:.55}
.su-door{padding:18px 20px}
.su-door-main{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:12px 20px}
.su-door-main>div:first-child{flex:1 1 320px;min-width:0}
.su-door-h{font-family:var(--disp);font-size:15px;font-weight:600;letter-spacing:-.005em;margin-bottom:6px}
.su-guard{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
.su-guard:empty{display:none}
.su-gd{display:inline-flex;align-items:center;gap:7px;padding:5px 12px 5px 10px;border-radius:var(--r-pill);background:var(--fill-4);font-size:12px;color:var(--dim);white-space:nowrap}
.su-gd i{width:7px;height:7px;border-radius:50%;background:var(--faint);flex:none}
.su-gd b{font-weight:600;color:var(--text)}
.su-gd.on i{background:var(--ok-fill)}
.su-gd.bad{background:var(--bad-a11);color:var(--bad)}
.su-gd.bad i{background:var(--bad-fill)}
.su-gd.bad b{color:var(--bad)}
.su-door .su-door-foot{max-width:none;margin:14px 0 0;padding-top:12px;border-top:1px solid var(--line-soft)}
.su-head{margin-bottom:0}
.su-views button{display:inline-flex;align-items:center;gap:6px}
.su-range{font-size:12.5px;color:var(--dim);margin-top:12px}
.su-range:empty{display:none}
.su-tiles{grid-template-columns:repeat(5,minmax(0,1fr))}
.su-tiles .tile .v{font-variant-numeric:normal}
.su-link{background:none;border:0;padding:0;font:inherit;font-size:12.5px;color:var(--link);cursor:pointer}
.su-link:hover{text-decoration:underline;text-underline-offset:2px}
.su-link:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
/* 拓客思路 */
.su-tips{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:16px}
.su-tips>.ta-empty{grid-column:1/-1}
.su-tip{display:grid;grid-template-columns:30px minmax(0,1fr);gap:12px;padding:14px 16px;border-radius:var(--r-lg);background:var(--surface-2);border:1px solid var(--line-soft)}
.su-tip-ic{width:30px;height:30px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:var(--fill-3);color:var(--dim)}
.su-tip-ic svg{width:16px;height:16px}
.su-tip-idea .su-tip-ic{background:var(--ta-new-bg);color:var(--ta-new-ink)}
.su-tip-warn .su-tip-ic{background:var(--warn-a12);color:var(--warn)}
.su-tip-good .su-tip-ic{background:var(--ok-a12);color:var(--ok)}
.su-tip-b{min-width:0}
.su-tip-b b{display:block;font-size:13.5px;font-weight:600;line-height:1.45}
.su-tip-b p{margin:6px 0 0;font-size:12.5px;color:var(--dim);line-height:1.65}
.su-tip-act{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.su-two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
/* 漏斗：左说明 · 中条 · 右转化 */
.su-funnel{display:flex;flex-direction:column;gap:14px;margin-top:18px}
.su-fn{display:grid;grid-template-columns:132px minmax(0,1fr) 128px;column-gap:14px;align-items:center}
.su-fn-l{min-width:0}
.su-fn-l b{display:block;font-size:13px;font-weight:600}
.su-fn-l span{display:block;font-size:11.5px;color:var(--faint);margin-top:2px;line-height:1.4}
.su-fn-bar{display:flex;align-items:center;gap:10px;min-width:0}
.su-fn-bar .ta-track{flex:1;height:14px;min-width:40px}
.su-fn-bar .ta-track i{border-radius:0 5px 5px 0}
.su-fn-n{flex:none;width:68px;text-align:right;line-height:1.2;font-variant-numeric:tabular-nums}
.su-fn-n b{display:block;font-size:15px;font-weight:600}
.su-fn-n em{font-style:normal;font-size:11.5px;color:var(--faint)}
.su-fn-r{display:flex;flex-direction:column;align-items:flex-end;gap:3px;font-size:11.5px;color:var(--dim);text-align:right}
.su-fn-r .su-link{font-size:11.5px}
.su-fn-conv{font-weight:600;color:var(--text)}
/* 来源 */
.su-src-wrap{overflow-x:auto;margin-top:14px}
.su-src{width:100%;min-width:720px}
.su-src th.num,.su-src td.num{text-align:right}
.su-src tbody tr{cursor:pointer}
.su-src tbody tr:hover{background:var(--fill-4)}
.su-src tbody tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.su-src-l{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px}
.su-src-l b{font-weight:600}
.su-src-few{font-size:11.5px}
.su-src-n{min-width:150px}
.su-src-n b{width:44px}
.su-src-n em{width:52px}
.su-sm{display:block;font-size:11.5px;color:var(--faint)}
/* 注册钟点：7 × 24 */
.su-heatwrap{overflow-x:auto;margin-top:18px}
.su-heat{display:grid;grid-template-columns:38px repeat(24,minmax(12px,1fr));gap:3px;min-width:460px;align-items:center}
.su-heat i{display:block;height:20px;border-radius:4px}
.su-heat-h{font-size:10.5px;color:var(--faint);text-align:left;font-variant-numeric:tabular-nums}
.su-heat-l{font-size:11.5px;color:var(--dim)}
/* 推荐人 */
.su-refs{display:flex;flex-direction:column;gap:4px;margin-top:14px}
.su-ref{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(60px,1fr) auto;align-items:center;gap:12px;width:100%;padding:9px 10px;margin:0 -10px;border-radius:var(--r-md);background:none;border:0;text-align:left;color:var(--text);cursor:pointer;box-sizing:content-box}
.su-ref:hover{background:var(--fill-4)}
.su-ref:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.su-ref-who{min-width:0;line-height:1.35}
.su-ref-who b{display:block;font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.su-ref-who small{display:block;font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.su-ref-bar,.su-ref-bar .ta-track{display:block}
.su-ref-bar .ta-track{height:8px}
.su-coh .su-coh-open{outline:1.5px dashed var(--faint);outline-offset:-3px}
.su-ref-n{text-align:right;line-height:1.35;white-space:nowrap}
.su-ref-n b{display:block;font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}
.su-ref-n small{font-size:11.5px;color:var(--faint)}
/* 名单 */
#su-q,#sr-q{width:230px}
.su-users{overflow-x:auto}
.su-users table{min-width:980px}
.su-users th.num,.su-users td.num{text-align:right}
#su-rows tr{cursor:pointer}
#su-rows tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
#su-rows td,#sr-rows td{padding-top:9px;padding-bottom:9px;vertical-align:middle}
.su-c-st{white-space:nowrap}
.su-chip-ids{max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.su-st-paid{color:var(--ok);background:var(--ok-a12)}
.su-st-trial{color:var(--ta-new-ink);background:var(--ta-new-bg)}
.su-st-quiet,.su-st-held{color:var(--warn);background:var(--warn-a12)}
.su-st-idle{color:var(--dim);background:var(--fill-3)}
.su-lv-high{color:var(--bad);background:var(--bad-a11)}
.su-lv-mid{color:var(--warn);background:var(--warn-a12)}
.su-lv-low{color:var(--dim);background:var(--fill-3)}
/* 风险与防护 */
.su-breaker{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px 16px;padding:14px 18px;margin-bottom:6px;border-radius:var(--r-lg);background:var(--bad-a9);border:1px solid var(--bad-a25)}
.su-breaker[hidden]{display:none}
.su-breaker b{display:block;font-size:14px;font-weight:600;color:var(--bad)}
.su-breaker span{display:block;font-size:12.5px;color:var(--dim);margin-top:4px;line-height:1.6}
.su-holds{display:flex;flex-direction:column;margin-top:12px}
.su-holds>.ta-empty{padding:20px 0}
.su-hold{display:grid;grid-template-columns:minmax(150px,1fr) minmax(0,2fr) 76px auto;align-items:center;gap:10px 16px;padding:12px 0;border-top:1px solid var(--line-soft)}
.su-hold:first-child{border-top:0}
.su-hold.done{opacity:.6}
.su-hold-who{min-width:0;line-height:1.35}
.su-hold-who .su-link{font-size:13px;color:var(--text);text-decoration:none}
.su-hold-who small{display:block;font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.su-hold-why{display:flex;flex-wrap:wrap;gap:5px;min-width:0}
.su-hold-amt{text-align:right;font-variant-numeric:tabular-nums}
.su-hold-amt b{font-weight:600}
.su-hold-act{display:flex;gap:8px;justify-content:flex-end}
.su-sig{display:inline-flex;align-items:center;padding:2px 8px;border-radius:6px;background:var(--fill-3);font-size:11px;color:var(--dim);white-space:nowrap}
.su-sigs{display:flex;flex-wrap:wrap;gap:4px;max-width:360px}
.su-clusters{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:14px}
.su-clusters>.su-cl-empty{grid-column:1/-1}
.su-cl{padding:16px 18px;display:flex;flex-direction:column;gap:12px;border-left:3px solid var(--line)}
.su-cl-high{border-left-color:var(--bad-fill)}
.su-cl-medium{border-left-color:var(--warn-fill)}
.su-cl-h{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
.su-cl-h b{font-size:14px;font-weight:600;line-height:1.45;min-width:0}
.su-cl-kinds{display:flex;flex-wrap:wrap;align-items:center;gap:5px 6px;font-size:11.5px}
.su-cl-kinds .faint{margin-left:4px}
.su-cl-stats{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:8px;padding:10px 12px;border-radius:var(--r-md);background:var(--surface-2)}
.su-cl-stats span{display:flex;flex-direction:column;font-size:11px;color:var(--faint);line-height:1.3;min-width:0}
.su-cl-stats b{font-size:14px;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap}
.su-cl-m{display:flex;flex-direction:column}
.su-cl-row{display:grid;grid-template-columns:minmax(0,1fr) auto 44px 64px;align-items:center;gap:10px;padding:6px 8px;margin:0 -8px;border-radius:var(--r-sm);background:none;border:0;font-size:12px;text-align:left;color:var(--text);cursor:pointer}
.su-cl-row:hover{background:var(--fill-4)}
.su-cl-row:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.su-cl-row .mono{font-size:11px}
.su-cl-row .tag{justify-self:end}
.su-cl-n{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.su-cl-more{font-size:11.5px;padding:4px 0 0}
.su-cl-act{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding-top:12px;border-top:1px solid var(--line-soft);margin-top:auto}
.su-cl-act .faint{font-size:12px}
.su-reasons{display:flex;flex-direction:column;gap:10px;margin-top:14px}
.su-reasons>.ta-empty{padding:20px 0}
.su-reasons .ta-band{grid-template-columns:150px minmax(0,1fr)}
.su-reasons .ta-band>span{white-space:normal;line-height:1.35}
.su-rs-g{font-size:11.5px;font-weight:600;color:var(--faint);margin-top:6px}
.su-rs-g:first-child{margin-top:0}
.su-ips{margin-top:12px;overflow-x:auto}
.su-ips>.ta-empty{padding:20px 0}
.su-iptab{width:100%;min-width:420px}
.su-iptab th.num,.su-iptab td.num{text-align:right}
.su-iptab tbody tr{cursor:pointer}
.su-iptab tbody tr:hover{background:var(--fill-4)}
.su-iptab tbody tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.su-score{display:grid;grid-template-columns:auto 64px 24px;align-items:center;gap:8px}
.su-score .ta-track{height:6px}
.su-score b{font-weight:600;text-align:right;font-variant-numeric:tabular-nums}
@media (max-width:1180px){
  .su-tiles{grid-template-columns:repeat(3,minmax(0,1fr))}
  .su-two{grid-template-columns:1fr}
}
@media (max-width:860px){
  .su-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}
  .su-tiles .tile:first-child{grid-column:1/-1}
  .su-tips{grid-template-columns:1fr}
  .su-door{padding:16px}
  .su-door-main .btn{width:100%}
  .su-funnel{gap:18px}
  .su-fn{grid-template-columns:minmax(0,1fr);row-gap:6px}
  .su-fn-r{flex-direction:row;flex-wrap:wrap;align-items:baseline;justify-content:flex-start;gap:4px 12px;text-align:left}
  .su-fn-n{display:flex;align-items:baseline;justify-content:flex-end;gap:6px;width:auto}
  .su-clusters{grid-template-columns:1fr}
  .su-cl-stats{grid-template-columns:repeat(3,minmax(0,1fr))}
  .su-hold{grid-template-columns:minmax(0,1fr) auto}
  .su-hold-why{grid-column:1/-1;grid-row:2}
  .su-hold-act{grid-column:1/-1;justify-content:flex-start}
  .su-reasons .ta-band{grid-template-columns:110px minmax(0,1fr)}
  #su-users-sec .sx-act,#su-q,#sr-q{width:100%}
  /* 新用户名单：一人一张卡（照抄充值分析排行） */
  .su-users{overflow:hidden}
  #su-growth .su-users table{display:block;min-width:0}
  #su-growth .su-users thead{display:none}
  #su-rows{display:block}
  #su-rows tr{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:14px;row-gap:6px;align-items:center;padding:14px 16px;border-bottom:1px solid var(--line-soft)}
  #su-rows tr:last-child{border-bottom:0}
  #su-rows td{display:block;padding:0;border:0;min-width:0;text-align:left}
  #su-rows td.su-c-who{grid-area:1/1/2/2}
  #su-rows td.su-c-st{grid-area:1/2/2/3;text-align:right;white-space:normal}
  #su-rows td.su-c-at{grid-area:2/1/3/2}
  #su-rows td.su-c-pay{grid-area:2/2/3/3;text-align:right}
  #su-rows td.su-c-src{grid-column:1/-1;grid-row:3}
  #su-rows td.su-c-use,#su-rows td.su-c-days,#su-rows td.su-c-rk{display:none}
  #su-rows td.empty{grid-column:1/-1}
  #su-risk .su-users{overflow-x:auto}
}

/* ---- 续费日历（#bx-renewals）。类名一律 rc- 开头；导语、卡片头、筛选条沿用充值分析的 ta- 类，间距照抄。
   每种状态先靠形状分开（实心对勾 / 钟 / 空心圈 / 感叹号 / 删除线 / 虚线框），颜色是第二层：
   色弱、灰度截图里也分得出。今天那格用墨色圆点，和 HIG 日历一致，不发光。 */
#bx-renewals .sec{transition:opacity .2s var(--ease)}
#bx-renewals.ta-busy .sec{opacity:.55}
.rc-bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px 16px}
.rc-bar-l,.rc-bar-r{display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px;min-width:0}
.rc-month{display:flex;align-items:center;gap:6px}
.rc-month b{font-family:var(--disp);font-size:20px;font-weight:600;letter-spacing:-.015em;min-width:136px;text-align:center;font-variant-numeric:tabular-nums}
.rc-nav{width:32px;height:32px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:var(--fill-3);color:var(--text);flex:none}
.rc-nav:hover{background:var(--fill-2)}
.rc-nav svg{width:14px;height:14px}
.rc-nav:focus-visible,.rc-dn:focus-visible,.rc-chip:focus-visible,.rc-more:focus-visible,.rc-row:focus-visible,.rc-lg:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
#rc-q{width:220px}
/* 预测带：大数 · 拆分 · 进度环 三栏 */
.rc-hero{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr) 150px;gap:22px 32px;padding:22px 24px;align-items:center}
.rc-big .k{font-size:12.5px;font-weight:600;color:var(--dim)}
.rc-big .v{font-family:var(--disp);font-size:40px;font-weight:600;letter-spacing:-.03em;line-height:1.05;margin-top:8px;font-variant-numeric:tabular-nums}
.rc-big .v small{font-size:15px;font-weight:500;color:var(--dim);letter-spacing:0;margin-left:8px}
.rc-big .r{font-size:12.5px;color:var(--dim);margin-top:8px;line-height:1.6}
.rc-big .r b{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.rc-meter{position:relative;height:14px;margin-top:20px}
.rc-mtrack{position:absolute;inset:0;border-radius:7px;overflow:hidden;background:var(--fill-4)}
.rc-meter i{position:absolute;top:0;bottom:0;display:block}
.rc-meter .p{background:var(--rc-paid)}
.rc-meter .e{background:repeating-linear-gradient(135deg,var(--rc-paid) 0 3px,var(--rc-exp) 3px 7px)}
.rc-meter .c{box-shadow:inset 0 0 0 1.5px var(--rc-exp)}
.rc-meter .od{background:var(--bad-fill)}
.rc-meter .o{box-shadow:inset 0 0 0 1.5px var(--rc-paid)}
.rc-meter .t{top:-5px;bottom:-5px;width:2px;margin-left:-1px;background:var(--text);border-radius:1px}
.rc-meter .t span{position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:4px;font-size:11px;color:var(--dim);white-space:nowrap;font-style:normal}
.rc-meter.has-t{margin-bottom:22px}
.rc-mk{display:flex;flex-wrap:wrap;gap:6px 16px;margin-top:12px;font-size:12px;color:var(--dim)}
.rc-mk span{display:inline-flex;align-items:center;gap:6px}
.rc-mk i{width:10px;height:10px;border-radius:3px;flex:none}
.rc-mk i.p{background:var(--rc-paid)}
.rc-mk i.e{background:repeating-linear-gradient(135deg,var(--rc-paid) 0 2px,var(--rc-exp) 2px 4px)}
.rc-mk i.c{box-shadow:inset 0 0 0 1.5px var(--rc-exp)}
.rc-mk i.o{box-shadow:inset 0 0 0 1.5px var(--rc-paid)}
.rc-mk i.od{background:var(--bad-fill)}
.rc-brk{display:flex;flex-direction:column;gap:12px;font-size:13px;min-width:0}
.rc-brk-r{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 12px;align-items:baseline}
.rc-brk-r>span{display:inline-flex;align-items:center;gap:7px;min-width:0}
.rc-brk-r b{font-weight:600;font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.rc-brk-r small{grid-column:1/-1;font-size:11.5px;color:var(--faint);line-height:1.55;padding-left:21px}
.rc-ring{text-align:center}
.rc-ring svg{width:116px;height:116px;display:block;margin:0 auto}
.rc-ring .n{font-family:var(--disp);font-size:22px;font-weight:600;fill:var(--text);font-variant-numeric:tabular-nums}
.rc-ring .l{font-size:10.5px;fill:var(--dim)}
.rc-ring small{display:block;font-size:12px;color:var(--dim);margin-top:8px}
/* 图例兼筛选 */
.rc-legend{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:14px}
.rc-lg{display:inline-flex;align-items:center;gap:7px;padding:5px 12px 5px 7px;border-radius:var(--r-pill);border:1px solid var(--line);font-size:12.5px;color:var(--dim);background:transparent;white-space:nowrap}
.rc-lg b{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums}
.rc-lg:not(:disabled):hover{border-color:var(--brand-40-on-line);color:var(--text)}
.rc-lg.on{background:var(--fill-3);border-color:transparent;color:var(--text)}
.rc-lg:disabled{opacity:.42;cursor:default}
.rc-lg.all{padding-left:12px}
/* 状态记号：14px 圆，形状先行 */
.rc-ic{flex:none;width:14px;height:14px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box}
.rc-ic svg{width:12px;height:12px;display:block}
.s-renewed .rc-ic,.s-late .rc-ic{background:var(--ok-fill);color:var(--surface)}
.s-today .rc-ic{background:var(--warn-fill);color:var(--surface)}
.s-soon .rc-ic{box-shadow:inset 0 0 0 1.5px var(--warn-fill);color:var(--warn)}
.s-scheduled .rc-ic{box-shadow:inset 0 0 0 1.5px var(--faint)}
.s-overdue .rc-ic{background:var(--bad-fill);color:var(--surface)}
.s-cancelled .rc-ic,.s-off .rc-ic{box-shadow:inset 0 0 0 1.5px var(--faint);color:var(--faint)}
.s-projected .rc-ic{border:1.5px dashed var(--faint)}
/* 日历 */
.rc-layout{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:16px;align-items:start}
.rc-cal{min-width:0}
.rc-wk,.rc-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr))}
.rc-wk span{padding:11px 12px 9px;font-size:12px;color:var(--dim);font-weight:600;border-bottom:1px solid var(--line)}
.rc-wk span.we{color:var(--faint)}
.rc-day{position:relative;min-height:132px;padding:8px 6px 14px;border-right:1px solid var(--line-soft);border-bottom:1px solid var(--line-soft);display:flex;flex-direction:column;gap:4px;min-width:0}
.rc-day:nth-child(7n){border-right:0}
.rc-grid .rc-day:nth-last-child(-n+7){border-bottom:0}
.rc-day.out{background:var(--fill-4)}
.rc-day.we:not(.out){background:var(--rc-weekend)}
.rc-dh{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-bottom:2px;min-height:26px}
.rc-dn{font-size:13px;font-weight:600;min-width:26px;height:26px;padding:0 6px;border-radius:13px;display:inline-flex;align-items:center;justify-content:center;font-variant-numeric:tabular-nums;background:none;color:var(--text)}
button.rc-dn:hover{background:var(--fill-3)}
.rc-day.past .rc-dn{color:var(--faint)}
.rc-day.today .rc-dn{background:var(--brand);color:var(--btn-ink)}
.rc-dsum{font-size:11px;color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap}
.rc-dsum.r{color:var(--bad);font-weight:600}
/* 一格两行：上行是谁，下行是哪一档和多少钱。下行从图标下面起、占满整格宽度：
   格子在 1440 宽的屏上只有一百出头，档位和金额挤在名字后面就只剩省略号 */
.rc-chip{display:grid;grid-template-columns:14px minmax(0,1fr);column-gap:6px;row-gap:1px;align-items:center;width:100%;min-width:0;padding:5px;border-radius:var(--r-sm);font-size:11.5px;line-height:1.3;text-align:left;border:1px solid transparent;background:var(--fill-4);color:var(--text);cursor:pointer}
.rc-chip b{min-width:0;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rc-cm{grid-column:1/-1;display:flex;align-items:baseline;justify-content:space-between;gap:4px;min-width:0;font-size:10px;color:var(--dim)}
.rc-cm small{min-width:0;font-size:inherit;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rc-cm em{flex:none;font-style:normal;font-variant-numeric:tabular-nums}
.rc-chip:hover{border-color:var(--brand-40-on-line)}
.rc-chip.s-renewed,.rc-chip.s-late{background:var(--ok-a12)}
.rc-chip.s-late{box-shadow:inset 2px 0 0 var(--warn-fill)}
.rc-chip.s-today{background:var(--warn-a16);border-color:var(--warn-fill)}
.rc-chip.s-soon{background:var(--warn-a9)}
.rc-chip.s-scheduled{background:transparent;border-color:var(--line)}
.rc-chip.s-overdue{background:var(--bad-a12)}
.rc-chip.s-overdue em{color:var(--bad)}
.rc-chip.s-cancelled,.rc-chip.s-off{background:transparent;color:var(--faint)}
.rc-chip.s-cancelled b,.rc-chip.s-off b{text-decoration:line-through;text-decoration-color:var(--faint)}
.rc-chip.s-projected{background:transparent;border:1px dashed var(--line);color:var(--dim)}
.rc-chip.rc-out{opacity:.6}
.rc-more{align-self:flex-start;font-size:11.5px;color:var(--link);padding:2px 5px;border-radius:var(--r-xs);background:none}
.rc-more:hover{background:var(--fill-4)}
/* 每格底部一条：这一天到期金额里续上 / 待续 / 过期各占多少，长度按本月最多那一天归一 */
.rc-mix{position:absolute;left:8px;bottom:6px;height:3px;display:flex;gap:1px;border-radius:2px;overflow:hidden;max-width:calc(100% - 16px)}
.rc-mix i{display:block;height:100%;min-width:2px}
.rc-mix .g{background:var(--ok-fill)}.rc-mix .a{background:var(--warn-fill)}
.rc-mix .r{background:var(--bad-fill)}.rc-mix .n{background:var(--fill-1)}
/* 右栏：需要跟进 */
.rc-side{padding:16px 18px}
.rc-side+.rc-side{margin-top:14px}
.rc-sh{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:6px}
.rc-sh b{display:block;font-family:var(--disp);font-size:15px;font-weight:600}
.rc-sh span{display:block;font-size:12px;color:var(--faint);margin-top:4px;line-height:1.5}
.rc-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:2px 10px;padding:11px 0;border-top:1px solid var(--line-soft);align-items:center;text-align:left;width:100%;background:none;color:inherit;border-radius:0;cursor:pointer}
.rc-sh+.rc-row,.rc-sh+div>.rc-row:first-child{border-top:0}
.rc-row:hover b{text-decoration:underline;text-underline-offset:2px}
.rc-row b{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.rc-row small{grid-column:2/3;font-size:11.5px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rc-row em{grid-row:1/3;grid-column:3;font-style:normal;text-align:right;font-size:12.5px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap;line-height:1.35}
.rc-row em small{display:block;font-weight:400;color:var(--faint);font-size:11px}
.rc-row .rc-ic{grid-row:1/3}
.rc-row em.need{color:var(--bad)}
.rc-row em.ok{color:var(--ok)}
.rc-empty{padding:18px 0 6px;font-size:13px;color:var(--dim)}
.rc-tag{display:inline-flex;align-items:center;font-size:10.5px;font-weight:600;padding:1px 6px;border-radius:5px;color:var(--dim);background:var(--fill-3);margin-left:4px;vertical-align:1px}
.rc-tag.w{color:var(--warn);background:var(--warn-a12)}
.rc-tag.b{color:var(--bad);background:var(--bad-a11)}
.rc-tag.g{color:var(--ok);background:var(--ok-a12)}
/* 抽屉里的一件事 */
.rc-evs{display:flex;flex-direction:column;gap:12px}
.rc-ev{border:1px solid var(--line);border-radius:var(--r-lg);padding:14px 16px;display:flex;flex-direction:column;gap:10px}
.rc-ev.hl{border-color:var(--focus);box-shadow:var(--focus-ring)}
.rc-ev-h{display:flex;align-items:center;gap:9px;min-width:0}
.rc-ev-h b{font-size:14px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rc-ev-h span{font-size:12px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.rc-ev-h em{margin-left:auto;font-style:normal;font-family:var(--disp);font-size:17px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.rc-ev-k{display:grid;grid-template-columns:72px minmax(0,1fr);gap:6px 12px;font-size:12.5px;line-height:1.5}
.rc-ev-k>span:nth-child(odd){color:var(--dim)}
.rc-ev-k b{font-weight:600}
.rc-ev-act{display:flex;flex-wrap:wrap;gap:8px}
/* 窄屏：日历换成按天的清单 */
.rc-list{display:none}
.rc-lday{padding:14px 16px 4px}
.rc-lday+.rc-lday{border-top:1px solid var(--line)}
.rc-lday h4{font-size:13px;font-weight:600;margin:0 0 2px;display:flex;justify-content:space-between;gap:8px}
.rc-lday h4 span{font-weight:400;color:var(--dim);font-variant-numeric:tabular-nums}
.rc-lday.today h4{color:var(--warn)}
@media (max-width:1280px){
  .rc-layout{grid-template-columns:1fr}
  .rc-sidewrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px}
  .rc-side+.rc-side{margin-top:0}
}
@media (max-width:1080px){
  .rc-hero{grid-template-columns:minmax(0,1fr) 140px}
  .rc-brk{grid-column:1/-1;grid-row:2}
}
@media (max-width:760px){
  .rc-hero{grid-template-columns:1fr;padding:18px 16px}
  .rc-ring{display:none}
  .rc-big .v{font-size:32px}
  .rc-month b{font-size:18px;min-width:120px}
  .rc-bar-r,#rc-q{width:100%}
  .rc-cal .rc-wk,.rc-cal .rc-grid{display:none}
  .rc-list{display:block}
  .rc-sidewrap{grid-template-columns:1fr}
}

/* ---- 额度兑换（#bx-exchange）。类名一律 ae- 开头；筛选条、统计块、趋势、卡片头沿用充值分析的 ta- 类，
   间距照抄，不另起一套。 */
#bx-exchange .sec{transition:opacity .2s var(--ease)}
#bx-exchange.ta-busy .sec{opacity:.55}
#ae-tiles{grid-template-columns:repeat(5,minmax(0,1fr))}
#ae-tiles .tile .v{font-variant-numeric:normal}
.ae-sumhead{text-align:right}
.ae-sumhead b{display:block;font-family:var(--disp);font-size:20px;font-weight:600;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.ae-sumhead span{font-size:12px;color:var(--dim)}
.ae-srcwrap{overflow-x:auto;margin-top:16px}
.ae-src{width:100%;min-width:640px}
.ae-src th,.ae-src td{padding:10px 12px}
.ae-src th:first-child,.ae-src td:first-child{padding-left:0}
.ae-src th:last-child,.ae-src td:last-child{padding-right:0}
.ae-src th.num,.ae-src td.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.ae-src tr.zero td{color:var(--faint)}
.ae-src tr.rev td{font-weight:600}
.ae-srcname{display:flex;align-items:flex-start;gap:10px}
.ae-srcname i{display:inline-block;width:10px;height:10px;border-radius:3px;flex:none;margin-top:4px}
.ae-srcname b{display:block;font-size:13.5px;font-weight:600}
.ae-srcname small{display:block;font-size:12px;font-weight:400;color:var(--faint);margin-top:2px;line-height:1.5}
.ae-rev{display:inline-block;margin-left:6px;font-size:11px;font-weight:600;padding:1px 6px;border-radius:999px;color:var(--ok);background:var(--ok-a12);vertical-align:1px}
.ae-tbl{overflow-x:auto}
.ae-tbl table{min-width:980px}
.ae-tbl th.num,.ae-tbl td.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.ae-tbl td{padding-top:9px;padding-bottom:9px}
.ae-who{display:flex;align-items:center;gap:6px;min-width:0}
.ae-who button{font:inherit;font-weight:600;font-size:13.5px;color:var(--text);background:none;padding:0;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:220px}
.ae-who button:hover{text-decoration:underline}
.ae-srcs{display:flex;flex-wrap:wrap;gap:4px}
.ae-srcs .tag{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
.ae-srcs .tag i{display:inline-block;width:7px;height:7px;border-radius:2px;flex:none}
.ae-via{font-size:12.5px;color:var(--dim);white-space:nowrap}
.ae-tip-s{font-size:12px;color:var(--dim);margin-top:2px}
.ae-link{background:none;padding:0;font:inherit;font-weight:600;color:var(--text);text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--faint)}
.ae-link:hover{text-decoration-color:currentColor}
.ae-link:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
/* 每一行都有「不计收入」，常亮就是一列一模一样的按钮。能悬停的设备上指到这一行才出现；触屏照常显示。 */
@media (hover:hover){.ae-tbl tr .ae-act{opacity:0;transition:opacity .15s var(--ease)}.ae-tbl tr:hover .ae-act,.ae-tbl .ae-act:focus-visible{opacity:1}}
.ae-testtag{color:var(--dim);background:var(--fill-3)}
.ae-tests{display:flex;flex-wrap:wrap;gap:8px}
.ae-tests:empty{display:none}
.ae-test{display:inline-flex;align-items:center;gap:10px;padding:7px 8px 7px 12px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);font-size:13px}
.ae-test small{color:var(--faint);font-size:12px}
.ae-add{margin-top:14px;max-width:420px}
.ae-tests:empty+.ae-add{margin-top:0}
.ae-none{font-size:13px;color:var(--dim)}
@media (max-width:1180px){#ae-tiles{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (max-width:860px){
  #ae-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}
  #ae-tiles .tile:first-child{grid-column:1/-1}
  #ae-user-sec .sx-act,#ae-ex-sec .sx-act,#ae-uq,#ae-xq{width:100%}
  .ae-add{max-width:none}
}

