/* ═══════════════════════════════════════════════════════════════════
   技能广场 / 专家广场 / 连接器 —— 统一能力中心（迁移 081 重设计）
   docs/skill-marketplace-redesign.md

   这个文件承载三件事：
     1) `.zx-*`  标签三层视觉系统（分类 / 标签 / 状态），替换掉此前四套并行的
        badge 样式（sp-item-badge / pm-tag-badge / adm-tag-badge / sp-mkt-tag）
     2) `.skn-*` 左侧主导航 —— 把“Skills / 专家”从一对不显眼的顶栏 tab
        提升为一等的并列导航项
     3) `.mkt-*` 广场页（原技能市场弹窗）—— 从 modal 变成整页

   配色一律走 styles.css 的 ZhiHe 设计令牌，明暗主题自动跟随。
   ═══════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════
   1. 标签三层视觉系统
   ───────────────────────────────────────────────────────────────────
   以前所有徽章长得一样大、一样重，于是"诉讼"（分类）、"官方推荐"（运营标签）、
   "预置"（状态）在卡片上互相打架，用户读不出哪个才是主要的。现在按信息层级
   分成三档，视觉重量依次递减：

     .zx-cat    分类  实心浅底 + 分类色文字 + 圆点  ← 唯一主轴，最显眼
     .zx-tag    标签  中性描边 + 彩色圆点           ← 运营修饰，克制
     .zx-state  状态  极轻中性块，10px             ← 系统态，几乎隐形

   都用同一个基座 `.zx-chip` 保证圆角/行高/字重一致，只在颜色与描边上区分。
   ═══════════════════════════════════════════════════════════════════ */
.zx-chip{display:inline-flex;align-items:center;gap:4px;border-radius:6px;
  font-weight:600;line-height:1.5;white-space:nowrap;vertical-align:middle;
  border:1px solid transparent;box-sizing:border-box}

/* ── 分类（主轴）── */
.zx-cat{padding:1px 8px;font-size:11px;
  background:var(--zx-c-bg,var(--acc-m));color:var(--zx-c-fg,var(--acc))}
.zx-cat .zx-dot{width:5px;height:5px;border-radius:50%;background:currentColor;
  flex:none;opacity:.85}
.zx-cat .zx-emo{font-size:11px;line-height:1}
/* 未分类：不给颜色，只给一个"待补"的弱提示。刻意不隐藏 —— 作者需要看见 */
.zx-cat.none{background:transparent;color:var(--tx3);border-color:var(--brd);
  font-weight:500}

/* ── 运营标签（次要）── */
.zx-tag{padding:1px 7px;font-size:10.5px;font-weight:500;
  background:var(--bg1);color:var(--tx2);border-color:var(--brd)}
.zx-tag .zx-dot{width:5px;height:5px;border-radius:50%;flex:none}
/* 超出展示上限时的 "+N"，点它展开全部 */
.zx-tag-more{padding:1px 6px;font-size:10.5px;font-weight:600;
  background:transparent;color:var(--tx3);border-color:var(--brd);cursor:pointer}
.zx-tag-more:hover{color:var(--acc);border-color:var(--acc-b)}

/* ── 状态徽章（几乎隐形）── */
.zx-state{padding:0 6px;font-size:10px;font-weight:600;
  background:var(--bg3);color:var(--tx3)}
.zx-state.official{background:var(--acc-m);color:var(--acc)}
.zx-state.community{background:var(--purple-m);color:var(--purple)}
.zx-state.builtin{background:var(--ok-m);color:var(--ok)}
.zx-state.manual{background:var(--warn-m);color:var(--warn)}
.zx-state.pending{background:var(--warn-m);color:var(--warn)}
.zx-state.rejected{background:var(--err-m);color:var(--err)}
.zx-state.approved{background:var(--ok-m);color:var(--ok)}
.zx-state.open-source{background:var(--ok-m);color:var(--ok);text-decoration:none}
.zx-state.open-source:hover{text-decoration:underline}

/* 开源公告保持紧凑，源码/下载不需要先安装技能，也不打断当前工作。 */
.skill-source-banner{display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:13px 16px;border:1px solid var(--acc-b);border-radius:12px;
  background:var(--acc-m);color:var(--tx1);flex:none}
.skill-source-market{margin:0 26px 12px}
.skill-source-copy{min-width:0}
.skill-source-copy strong{font-size:15px;line-height:1.5}
.skill-source-eyebrow{display:inline-block;margin-right:10px;font-size:10px;
  font-weight:700;letter-spacing:.08em;color:var(--acc)}
.skill-source-copy p{margin:4px 0 0;font-size:12px;line-height:1.6;color:var(--tx2)}
.skill-source-actions{display:flex;flex-wrap:wrap;gap:8px;flex:none}
.skill-source-link{display:inline-flex;align-items:center;justify-content:center;
  min-height:36px;padding:7px 11px;border:1px solid var(--acc-b);border-radius:8px;
  background:var(--bg2);color:var(--acc);text-decoration:none;font-size:12px;font-weight:600;
  line-height:1.5;white-space:nowrap}
.skill-source-link.primary{background:var(--acc);border-color:var(--acc);color:#fff}
.skill-source-link:hover{filter:brightness(1.06)}
.skill-source-link:disabled{opacity:.6;cursor:wait}
.enterprise-season{align-items:flex-start}
.enterprise-season .skill-source-eyebrow{display:inline-block}
.enterprise-season-list{margin-top:8px;font-size:12px;line-height:1.6}
.enterprise-season-list summary{cursor:pointer;color:var(--acc);padding:4px 0}
.enterprise-season-list ul,.enterprise-season-errors ul{padding-left:18px;margin:6px 0}
.enterprise-season-list ul{max-height:160px;overflow-y:auto}
.enterprise-season-list a{color:var(--acc)}
.enterprise-season-list li{margin-bottom:8px;overflow-wrap:anywhere}
.enterprise-season-list li span{color:var(--tx3)}
.enterprise-season-errors{font-size:12px;color:var(--err);overflow-wrap:anywhere;max-height:120px;overflow-y:auto}
.enterprise-season .skill-source-actions{flex-wrap:wrap;max-width:100%}
.skill-source-link:focus-visible,.zx-state.open-source:focus-visible{
  outline:2px solid var(--acc);outline-offset:3px}
.skill-source-detail{margin:0 0 20px;flex-wrap:wrap}
.skills-page .login-overlay{overflow-y:auto;align-items:flex-start;padding:20px 0}
.skills-page .login-box{flex-shrink:0;margin:auto 0}
.skill-source-login{margin-top:18px;display:block;text-align:left;padding:12px;
  background:rgba(255,255,255,.06);border-color:rgba(160,190,255,.3);color:#fff}
.skill-source-login .skill-source-copy p{color:#cad9f5}
.skill-source-login .skill-source-eyebrow{display:none}
.skill-source-login .skill-source-actions{margin-top:10px;gap:6px}
.skill-source-login .skill-source-link{padding:6px 8px;font-size:11px;
  color:#e8eefa;background:rgba(255,255,255,.08);border-color:rgba(160,190,255,.3)}
.skill-source-login .skill-source-link.primary{background:var(--acc);color:#fff}
@media(max-width:1200px){
  .skill-source-market{align-items:flex-start;flex-direction:column;gap:10px}
}
@media(max-width:860px){
  .skill-source-market{margin:0 16px 12px}
  .skill-source-eyebrow{display:none}
  .skill-source-link{min-height:40px}
}
/* “新品”（迁移 112）：与官方/社区同一层级的状态徽章，绝不能比分类 pill 更重 ——
   它只是"最近上架"这个事实，不是一个分类轴 */
.zx-state.fresh{background:var(--ok-m);color:var(--ok)}

/* 徽章行：统一行距，避免各处自己写 gap */
.zx-badges{display:flex;flex-wrap:wrap;align-items:center;gap:5px;min-width:0}

/* ═══════════════════════════════════════════════════════════════════
   2. 左侧主导航
   ───────────────────────────────────────────────────────────────────
   原来“Skills / 专家”是顶栏中间的一对小 tab，和旁边七八个按钮混在一起，
   用户根本注意不到还能切到专家。改成左侧固定导航后，两者是**并列的目的地**
   而不是一个需要被发现的开关 —— 顺带把“技能广场”从弹窗提升为同级入口。
   ═══════════════════════════════════════════════════════════════════ */
.skn{position:fixed;left:0;top:0;bottom:0;width:212px;z-index:30;
  background:var(--bg2);border-right:1px solid var(--brd);
  display:flex;flex-direction:column;padding:14px 12px 10px;overflow-y:auto}
.skn-brand{display:flex;align-items:center;gap:9px;padding:2px 6px 14px;
  text-decoration:none}
.skn-brand img{width:26px;height:26px;border-radius:7px;flex:none}
.skn-brand-txt{display:flex;flex-direction:column;min-width:0}
.skn-brand-name{font-size:13px;font-weight:700;color:var(--tx1);line-height:1.3}
.skn-brand-sub{font-size:10px;color:var(--tx3);line-height:1.3}
.skn-group{font-size:10px;color:var(--tx3);padding:14px 8px 5px;letter-spacing:.6px;
  font-weight:600;text-transform:uppercase}
.skn-group:first-of-type{padding-top:4px}
.skn-item{display:flex;align-items:center;gap:9px;width:100%;text-align:left;
  padding:8px 10px;border:0;border-radius:9px;background:transparent;
  color:var(--tx2);font-size:13px;cursor:pointer;font-family:inherit;
  transition:background .14s,color .14s;position:relative}
.skn-item:hover{background:var(--bg1);color:var(--tx1)}
.skn-item.on{background:var(--acc-m);color:var(--acc);font-weight:600}
.skn-ico{width:16px;height:16px;flex:none;display:flex;align-items:center;
  justify-content:center;font-size:14px;line-height:1}
.skn-ico svg{width:16px;height:16px}
.skn-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* 计数 / 红点徽标 */
.skn-count{font-size:10px;font-weight:600;color:var(--tx3);
  background:var(--bg3);border-radius:9px;padding:1px 6px;flex:none}
.skn-item.on .skn-count{background:#fff;color:var(--acc)}
.skn-dot{width:6px;height:6px;border-radius:50%;background:var(--warn);flex:none}
.skn-team{width:100%;margin:2px 0 6px;padding:7px 9px;border-radius:9px;
  border:1px solid var(--brd);background:var(--bg1);color:var(--tx2);font-size:12px;
  font-family:inherit}
.skn-spacer{flex:1}
/* 底部：用户区 + 返回会话 */
.skn-foot{border-top:1px solid var(--brd);padding-top:10px;margin-top:8px;
  display:flex;flex-direction:column;gap:2px}
.skn-user{display:flex;align-items:center;gap:8px;padding:6px 8px;min-width:0}
.skn-user-av{width:26px;height:26px;border-radius:50%;flex:none;object-fit:cover;
  background:var(--grad-card-blue);color:#fff;display:flex;align-items:center;
  justify-content:center;font-size:11px;font-weight:700}
.skn-user-name{flex:1;min-width:0;font-size:12px;color:var(--tx1);font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.skn-link{display:block;padding:6px 10px;color:var(--tx3);font-size:11.5px;
  text-decoration:none;border-radius:8px;background:transparent;border:0;
  cursor:pointer;font-family:inherit;text-align:left;width:100%}
.skn-link:hover{color:var(--acc);background:var(--bg1)}

/* 主区域给左导航让位。用 :has() 是为了不去改每个页面容器的类名 —— 左导航
   不渲染时（编辑器全屏 / 移动端横排）自动回到 margin-left:0。 */
body:has(.skn) .sp-header,
body:has(.skn) .sp-listpage,
body:has(.skn) .sp-detailpage,
body:has(.skn) .sp-roles-page,
body:has(.skn) .mkt,
body:has(.skn) .spx-preview,
body:has(.skn) .spx-review{margin-left:212px}

/* ═══════════════════════════════════════════════════════════════════
   3. 广场页（原“技能市场”弹窗 → 整页）
   ═══════════════════════════════════════════════════════════════════ */
.mkt{display:flex;flex-direction:column;height:100vh;overflow:hidden}
.mkt-head{padding:20px 26px 0;flex:none}
.mkt-title{font-size:20px;font-weight:700;color:var(--tx1);margin:0 0 4px;
  display:flex;align-items:center;gap:10px}
.mkt-sub{font-size:12.5px;color:var(--tx3);line-height:1.6;margin-bottom:14px;
  max-width:760px}

/* ── Top Contributors ──
   贡献榜已并入 .mkt-rank 那条带 tab 的榜单 rail（原本它自己占一条横排，两条几乎
   一样的 rail 叠在一起吃掉近 120px 首屏 —— 而 .mkt 是定高列，头部每多一块，卡片
   网格就少一块）。这里只剩它**行内**还在用的头像与名次徽章。 */
.mkt-cb-av{width:30px;height:30px;flex:none;border-radius:50%;overflow:hidden;display:flex;
  align-items:center;justify-content:center;background:var(--grad-card-blue);
  color:#fff;font-size:12px;font-weight:700}
.mkt-cb-av img{width:100%;height:100%;object-fit:cover}
.mkt-cb-av svg{width:15px;height:15px}
.mkt-cb-rank{position:absolute;right:-3px;bottom:-3px;min-width:14px;height:14px;
  border-radius:7px;background:var(--bg4);color:var(--tx2);font-size:9px;
  font-weight:700;display:flex;align-items:center;justify-content:center;
  border:1.5px solid var(--bg2);padding:0 3px}
.mkt-cb-rank.r1{background:#F7B500;color:#fff}
.mkt-cb-rank.r2{background:#B8C0CC;color:#fff}
.mkt-cb-rank.r3{background:#D08B5B;color:#fff}

/* ── 榜单 rail：热度榜 / 上新榜（迁移 112）──
   位置刻意在 .mkt-split **之外**（与 .mkt-contrib 同族，flex:none），不参与
   下面那条"页面定高 → .mkt-split 吃掉剩余高度 → 左栏与网格各自滚动"的高度链。
   tests/unit/test_marketplace_scroll_chain.py 钉着那条链，别把榜挪进去。 */
.mkt-rank{flex:none;padding:0 26px 14px}
.mkt-rank-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;
  flex-wrap:wrap}
.mkt-rank-tabs{display:flex;gap:8px;flex-wrap:wrap}
.mkt-rank-tab{padding:3px 10px;border-radius:8px;border:1px solid var(--brd);
  background:var(--bg2);color:var(--tx2);font-size:11.5px;font-weight:600;
  font-family:inherit;cursor:pointer;transition:all .15s}
.mkt-rank-tab:hover{border-color:var(--acc-b);color:var(--tx1)}
.mkt-rank-tab.on{background:var(--acc-m);border-color:var(--acc-b);color:var(--acc)}
.mkt-rank-hint{font-size:10.5px;color:var(--tx3)}
.mkt-rank-all{margin-left:auto;padding:0;border:0;background:none;cursor:pointer;
  font-family:inherit;font-size:11px;color:var(--tx3)}
.mkt-rank-all:hover{color:var(--acc)}
.mkt-rank-row{display:flex;gap:8px;overflow-x:auto;padding-bottom:4px;
  scrollbar-width:thin}
.mkt-rank-row::-webkit-scrollbar{height:5px}
.mkt-rank-row::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:3px}
.mkt-rk{display:flex;align-items:center;gap:8px;padding:7px 10px 7px 8px;flex:none;
  border:1px solid var(--brd);border-radius:11px;background:var(--bg2);
  cursor:pointer;transition:border-color .15s,box-shadow .15s}
.mkt-rk:hover{border-color:var(--acc-b);box-shadow:var(--zh-shadow-sm)}
.mkt-rk:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
/* 已学会的条目是惰性的 —— 榜是横向滚动的密集控件，误点一下就卸载技能，
   代价与收益完全不成比例。所以榜里只能装、不能卸。 */
.mkt-rk-a{text-decoration:none}
.mkt-rk.done{cursor:default;opacity:.72}
.mkt-rk.done:hover{border-color:var(--brd);box-shadow:none}
.mkt-rk-avw{position:relative;flex:none}
.mkt-rk-av{width:30px;height:30px;border-radius:9px;overflow:hidden;display:flex;
  align-items:center;justify-content:center;color:#fff;font-size:13px;font-weight:700}
.mkt-rk-av img{width:100%;height:100%;object-fit:cover}
.mkt-rk-info{display:flex;flex-direction:column;min-width:0}
.mkt-rk-name{font-size:12px;font-weight:600;color:var(--tx1);max-width:150px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mkt-rk-stat{font-size:10px;color:var(--tx3);max-width:150px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.mkt-rk-cta{flex:none;font-size:10.5px;font-weight:600;color:var(--acc);
  padding-left:2px}
.mkt-rk.done .mkt-rk-cta{color:var(--tx3)}

/* 近期新增（榜单口径）与累计学会人数并排。两个数字必须长得不一样，
   否则用户读不出"↑12"说的是最近、"🔥 500"说的是历史。 */
.mkt-trend{color:var(--ok);font-weight:600}

/* ── 工具条 ── */
.mkt-tools{display:flex;gap:10px;align-items:center;padding:0 26px 12px;flex:none;
  flex-wrap:wrap}
.mkt-search{position:relative;flex:1;min-width:200px;max-width:420px}
.mkt-search input{width:100%;padding:9px 12px 9px 34px;border-radius:10px;
  border:1px solid var(--brd);background:var(--bg2);color:var(--tx1);font-size:13px;
  font-family:inherit;box-sizing:border-box;transition:border-color .15s}
.mkt-search input:focus{outline:none;border-color:var(--acc);
  box-shadow:0 0 0 3px var(--acc-m)}
.mkt-search-ico{position:absolute;left:11px;top:50%;transform:translateY(-50%);
  color:var(--tx3);display:flex;pointer-events:none}
.mkt-search-ico svg{width:15px;height:15px}
.mkt-sel{padding:9px 10px;border-radius:10px;border:1px solid var(--brd);
  background:var(--bg2);color:var(--tx2);font-size:12.5px;font-family:inherit;
  cursor:pointer}

/* ── 筛选区：分类（主）/ 来源（次）/ 标签（折叠）── */
.mkt-filters{padding:0 26px 12px;flex:none;display:flex;flex-direction:column;gap:8px}
.mkt-frow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-height:28px}
.mkt-flabel{font-size:11px;color:var(--tx3);font-weight:600;flex:none;width:34px}

/* 分类 chip —— 主筛选轴，做得比其它 chip 大一号 */
.mkt-cat{display:inline-flex;align-items:center;gap:6px;padding:6px 13px;
  border-radius:20px;border:1px solid var(--brd);background:var(--bg2);
  color:var(--tx2);font-size:12.5px;font-weight:600;cursor:pointer;
  font-family:inherit;transition:all .15s}
.mkt-cat:hover{border-color:var(--acc-b);color:var(--tx1)}
.mkt-cat.on{background:var(--acc);border-color:var(--acc);color:#fff}
.mkt-cat .n{font-size:11px;opacity:.7;font-weight:500}
.mkt-cat .emo{font-size:13px;line-height:1}

/* 来源 / 标签 chip —— 次级，小一号、更轻 */
.mkt-chip{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;
  border-radius:14px;border:1px solid var(--brd);background:transparent;
  color:var(--tx3);font-size:11.5px;cursor:pointer;font-family:inherit;
  transition:all .15s}
.mkt-chip:hover{color:var(--tx1);border-color:var(--acc-b)}
.mkt-chip.on{background:var(--acc-m);border-color:var(--acc-b);color:var(--acc);
  font-weight:600}
.mkt-chip .zx-dot{width:6px;height:6px;border-radius:50%;flex:none}
.mkt-ftoggle{background:none;border:0;color:var(--tx3);font-size:11.5px;
  cursor:pointer;font-family:inherit;padding:4px 6px;border-radius:6px}
.mkt-ftoggle:hover{color:var(--acc);background:var(--bg1)}

/* ── 卡片网格 ── */
/* grid item：`flex:1` 在这儿是死的，高度由 .mkt-split 的行轨道给，
   自己只需要 min-height:0 把它变成可滚的盒子 */
.mkt-body{min-height:0;min-width:0;overflow-y:auto;padding:0 26px 26px}
.mkt-grid{display:grid;gap:14px;
  grid-template-columns:repeat(auto-fill,minmax(290px,1fr))}

.mkt-card{border:1px solid var(--brd);border-radius:var(--zh-radius-lg);
  background:var(--bg2);padding:15px;display:flex;flex-direction:column;gap:9px;
  transition:border-color .15s,box-shadow .15s;position:relative}
.mkt-card:hover{border-color:var(--acc-b);box-shadow:var(--zh-shadow-sm)}
.mkt-card.installed{border-color:var(--ok-b);background:linear-gradient(
  180deg,var(--ok-m) 0%,var(--bg2) 46px)}

.mkt-card-top{display:flex;gap:11px;align-items:flex-start}
.mkt-card-icon{width:38px;height:38px;border-radius:10px;flex:none;
  display:flex;align-items:center;justify-content:center;color:#fff;
  font-size:16px;font-weight:700;overflow:hidden}
.mkt-card-icon img{width:100%;height:100%;object-fit:cover}
.mkt-card-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}
.mkt-card-name{font-size:14px;font-weight:600;color:var(--tx1);line-height:1.35;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.mkt-card-desc{font-size:12px;color:var(--tx2);line-height:1.6;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  min-height:38px}
.mkt-card-eg{font-size:11px;color:var(--tx3);line-height:1.5;
  background:var(--bg1);border-radius:7px;padding:6px 9px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}

.mkt-card-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  font-size:11px;color:var(--tx3);margin-top:auto;padding-top:2px}
.mkt-hot{color:var(--warn);font-weight:600}
.mkt-author{color:var(--tx3);text-decoration:none;display:inline-flex;
  align-items:center;gap:4px;max-width:150px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
a.mkt-author:hover{color:var(--acc)}

/* 卡片底部操作条 */
.mkt-card-foot{display:flex;align-items:center;gap:8px;padding-top:10px;
  border-top:1px solid var(--zh-border-subtle)}
.mkt-btn{flex:1;padding:7px 12px;border-radius:9px;border:1px solid var(--acc);
  background:var(--acc);color:#fff;font-size:12.5px;font-weight:600;cursor:pointer;
  font-family:inherit;transition:filter .15s}
.mkt-btn:hover{filter:brightness(1.08)}
.mkt-btn:disabled{opacity:.6;cursor:default}
.mkt-btn.ghost{background:transparent;color:var(--tx2);border-color:var(--brd)}
.mkt-btn.ghost:hover{color:var(--err);border-color:var(--err)}
/* 官方技能“下载”：次要动作，不抢“让 AI 学会”的主按钮 */
.mkt-btn.dl{flex:0 0 auto;background:transparent;color:var(--tx2);border-color:var(--brd)}
.mkt-btn.dl:hover{color:var(--acc);border-color:var(--acc);filter:none}
/* 基础技能：不是按钮，是一句陈述。给它按钮的位置但不给按钮的 affordance */
.mkt-static{flex:1;padding:7px 12px;border-radius:9px;font-size:12px;
  text-align:center;background:var(--ok-m);color:var(--ok);font-weight:600}

/* ── 更新态（本次重设计的重点）───────────────────────────────────
   旧版把“有更新 v2→v4”“查看变更”“更新到最新”“锁定此版本”四个
   同等重量的按钮平铺在卡片里，一张卡看起来像一个控制面板。
   现在收敛成一行：状态文字 + 一个主动作，其余进 ⋯ 溢出菜单。
   ──────────────────────────────────────────────────────────────── */
.mkt-upd{display:flex;align-items:center;gap:8px;font-size:11.5px;
  background:var(--warn-m);border-radius:8px;padding:6px 8px 6px 10px;
  color:var(--warn);position:relative}
.mkt-upd .t{flex:1;font-weight:600;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.mkt-upd .go{background:var(--warn);color:#fff;border:0;border-radius:6px;
  padding:3px 10px;font-size:11px;font-weight:600;cursor:pointer;
  font-family:inherit;flex:none}
.mkt-upd .go:hover{filter:brightness(1.06)}
.mkt-upd .more{background:transparent;border:0;color:var(--warn);cursor:pointer;
  font-size:14px;line-height:1;padding:2px 4px;border-radius:5px;flex:none;
  font-family:inherit}
.mkt-upd .more:hover{background:rgba(232,147,12,.18)}
.mkt-upd-menu{position:absolute;right:4px;top:calc(100% + 4px);z-index:20;
  background:var(--bg2);border:1px solid var(--brd);border-radius:9px;
  box-shadow:var(--zh-shadow-popover);padding:4px;min-width:150px;
  display:flex;flex-direction:column}
.mkt-upd-menu button{background:none;border:0;text-align:left;padding:7px 10px;
  font-size:12px;color:var(--tx2);cursor:pointer;border-radius:6px;
  font-family:inherit;white-space:nowrap}
.mkt-upd-menu button:hover{background:var(--bg1);color:var(--tx1)}
/* 锁定 / 强制更新的只读状态：一句灰字，不给任何按钮 */
.mkt-vstate{font-size:11px;color:var(--tx3);display:flex;align-items:center;gap:5px}
.mkt-vstate.locked{color:var(--purple)}

/* ── 空状态 / 加载 ── */
.mkt-state{padding:60px 20px;text-align:center;color:var(--tx3);font-size:13px}
.mkt-skel{border:1px solid var(--brd);border-radius:var(--zh-radius-lg);
  background:var(--bg2);padding:15px;height:168px;
  animation:mkt-pulse 1.5s ease-in-out infinite}
@keyframes mkt-pulse{0%,100%{opacity:.55}50%{opacity:.85}}

/* ═══════════════════════════════════════════════════════════════════
   4. 连接器推荐（/mcp 页的“官方推荐”区）
   ═══════════════════════════════════════════════════════════════════ */
.mcr-sec{margin-bottom:26px}
.mcr-group-t{font-size:12px;font-weight:600;color:var(--tx3);margin:16px 0 9px;
  letter-spacing:.3px}
.mcr-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}
.mcr-card{border:1px solid var(--brd);border-radius:var(--zh-radius-lg);
  background:var(--bg2);padding:14px;display:flex;gap:11px;align-items:flex-start;
  transition:border-color .15s,box-shadow .15s}
.mcr-card:hover{border-color:var(--acc-b);box-shadow:var(--zh-shadow-sm)}
.mcr-ico{width:36px;height:36px;border-radius:10px;flex:none;display:flex;
  align-items:center;justify-content:center;font-size:18px;background:var(--bg1)}
.mcr-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px}
.mcr-name{font-size:13.5px;font-weight:600;color:var(--tx1);display:flex;
  align-items:center;gap:6px;flex-wrap:wrap}
.mcr-desc{font-size:12px;color:var(--tx2);line-height:1.6;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.mcr-foot{display:flex;align-items:center;gap:8px;margin-top:2px}
.mcr-btn{padding:5px 14px;border-radius:8px;border:1px solid var(--acc);
  background:var(--acc);color:#fff;font-size:12px;font-weight:600;cursor:pointer;
  font-family:inherit}
.mcr-btn:hover{filter:brightness(1.08)}
.mcr-btn:disabled{opacity:.55;cursor:default}
.mcr-btn.ghost{background:transparent;color:var(--tx2);border-color:var(--brd)}
.mcr-link{font-size:11.5px;color:var(--tx3);text-decoration:none}
.mcr-link:hover{color:var(--acc)}

/* ── 密钥引导弹窗 ── */
.mcr-key-help{background:var(--bg1);border-radius:10px;padding:12px 14px;
  font-size:12.5px;color:var(--tx2);line-height:1.75;margin:10px 0 14px;
  border-left:3px solid var(--acc)}
.mcr-key-help p{margin:0 0 6px}
.mcr-key-help p:last-child{margin-bottom:0}
.mcr-key-help a{color:var(--acc)}
.mcr-key-help ol,.mcr-key-help ul{margin:4px 0;padding-left:20px}
.mcr-key-help li{margin:3px 0}
.mcr-key-help blockquote{margin:8px 0 0;padding:8px 10px;background:var(--bg2);
  border-radius:7px;color:var(--tx3);font-size:11.5px;border:0}
.mcr-key-input{width:100%;padding:9px 12px;border-radius:9px;
  border:1px solid var(--brd);background:var(--bg2);color:var(--tx1);
  font-size:13px;font-family:var(--mono);box-sizing:border-box}
.mcr-key-input:focus{outline:none;border-color:var(--acc);
  box-shadow:0 0 0 3px var(--acc-m)}

/* ═══════════════════════════════════════════════════════════════════
   5. 分类选择器（编辑器里的必填字段）
   ═══════════════════════════════════════════════════════════════════ */
.cat-pick{display:flex;gap:8px;flex-wrap:wrap}
.cat-opt{display:flex;flex-direction:column;gap:2px;align-items:flex-start;
  padding:9px 13px;border-radius:11px;border:1.5px solid var(--brd);
  background:var(--bg2);cursor:pointer;font-family:inherit;text-align:left;
  transition:all .15s;min-width:132px}
.cat-opt:hover{border-color:var(--acc-b)}
.cat-opt.on{border-color:var(--acc);background:var(--acc-m)}
.cat-opt-t{font-size:13px;font-weight:600;color:var(--tx1);display:flex;
  align-items:center;gap:6px}
.cat-opt.on .cat-opt-t{color:var(--acc)}
.cat-opt-d{font-size:10.5px;color:var(--tx3);line-height:1.4}
/* 必填未选中的红框提示 —— 只在提交被拒后出现，不做实时红字骚扰 */
.cat-pick.invalid .cat-opt{border-color:var(--err)}
.cat-err{font-size:11.5px;color:var(--err);margin-top:6px}

/* ═══════════════════════════════════════════════════════════════════
   6. 窄屏（H5）：左导航横排收敛 + 把滚动条还给整页
   ───────────────────────────────────────────────────────────────────
   线上 H5 上同时踩了两个坑，成因是同一个：`.skills-page` 是**定高列 flex**
   （height:100vh），`.skn` 与 `.mkt` 都是它的直接子项。

     · “部分内容被遮挡”—— 导航收敛成横排后仍是可压缩的 flex 子项
       （flex-shrink 默认 1），兄弟 `.mkt` 一撑，导航就被压到不足一行高；
       又因 `overflow-x:auto` 会把 overflow-y 一并计算成 auto，被裁掉的
       “我的技能 / 我的专家 / 预置技能管理”既看不见、也没有任何可滚提示。
     · “可滑动区域太小”—— 滚动条挂在 `.mkt-body` 上。标题 + 贡献榜 +
       搜索 + 三行筛选在窄屏吃掉近半屏后，真正能滑的卡片区只剩一小条。

   解法两条，缺一不可：
     ① 导航 `flex:none`（永不被压）+ 单行横滑，高度固定一行；
     ② 滚动条从 `.mkt-body` 上提到 `.mkt` —— 标题 / 贡献榜 / 筛选跟着一起
        滑走，卡片区拿到导航以下的**整块**视口。这也是 H5 该有的手感：
        一根手指从上滑到底，而不是在页面里找那块能滑的小窗口。
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width:860px){
  /* ── ① 顶部导航：单行横滑，绝不压缩 ── */
  .skn{position:static;width:auto;flex:none;flex-direction:row;flex-wrap:nowrap;
    align-items:center;gap:6px;padding:7px 10px;border-right:0;
    border-bottom:1px solid var(--brd);overflow-x:auto;overflow-y:hidden;
    -webkit-overflow-scrolling:touch;scroll-snap-type:x proximity}
  /* 细横条：横滑是唯一能拿到后面几项的方式，得给一个诚实的可滚提示 */
  .skn::-webkit-scrollbar{height:3px}
  .skn::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:2px}
  .skn-brand{padding:0 6px 0 0;flex:none}
  .skn-brand-txt{display:none}          /* 只留 logo：它就是"返回会话"的入口 */
  .skn-group{display:none}
  .skn-item{width:auto;flex:none;padding:6px 11px;font-size:12.5px;
    scroll-snap-align:center}
  .skn-label{max-width:none}
  .skn-spacer{display:none}
  .skn-team{width:auto;flex:none;max-width:140px}
  /* 账户区原本整块 display:none —— 于是 H5 上没有“我的主页 / 返回会话 /
     退出登录”的任何入口。改成排在横滑条末尾的紧凑一组。 */
  .skn-foot{flex:none;flex-direction:row;align-items:center;gap:2px;
    border-top:0;margin:0 0 0 6px;padding:0 0 0 8px;
    border-left:1px solid var(--brd)}
  .skn-user{padding:0 2px;gap:0}
  .skn-user-name{display:none}
  .skn-link{width:auto;padding:6px 8px;font-size:11.5px;white-space:nowrap}

  body:has(.skn) .sp-header,
  body:has(.skn) .sp-listpage,
  body:has(.skn) .sp-detailpage,
  body:has(.skn) .sp-roles-page,
  body:has(.skn) .mkt,
  body:has(.skn) .spx-preview,
  body:has(.skn) .spx-review{margin-left:0}

  /* ── ② 广场：滚动条上提到页面本身 ── */
  .mkt{flex:1;min-height:0;height:auto;overflow-y:auto;
    -webkit-overflow-scrolling:touch}
  .mkt-body{flex:none;min-height:auto;overflow:visible}

  .mkt-head,.mkt-tools,.mkt-filters,.mkt-body,.mkt-rank{
    padding-left:14px;padding-right:14px}
  .mkt-head{padding-top:16px}
  .mkt-title{font-size:18px}
  /* 说明文字最多两行 —— 它是一次性读物，不该每次进页面都占三行首屏 */
  .mkt-sub{font-size:12px;line-height:1.55;margin-bottom:12px;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
    overflow:hidden}
  /* 工具条：搜索独占一行，排序与已安装计数并排 */
  .mkt-search{flex:1 1 100%;max-width:none}
  .mkt-search input{font-size:16px}     /* iOS 聚焦时 <16px 会触发页面缩放 */
  .mkt-tools-gap{display:none}
  .mkt-count-inst{margin-left:auto}
  /* 筛选：每组一行横滑，不再折行成三四行把首屏吃光 */
  .mkt-frow{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;
    padding-bottom:2px;scrollbar-width:none}
  .mkt-frow::-webkit-scrollbar{display:none}
  .mkt-frow>*{flex:none}
  .mkt-flabel{width:auto;position:sticky;left:0;background:var(--bg0);
    padding-right:2px;z-index:1}
  .mkt-grid{grid-template-columns:1fr}
}

/* 手机：卡片本身再紧一档，让一屏能完整看到 3 张以上 */
@media (max-width:600px){
  .mkt-grid{gap:10px}
  .mkt-card{padding:13px;gap:8px;border-radius:12px}
  .mkt-card-icon{width:34px;height:34px;border-radius:9px;font-size:15px}
  .mkt-card-name{font-size:13.5px}
  .mkt-card-desc{min-height:0}          /* 占位高度在单列下只是空白 */
  .mkt-card-meta{gap:8px}
  .mkt-card-foot{padding-top:9px}
  .mkt-btn{padding:9px 12px}            /* 更矮但仍满足触控高度 */
  .mkt-rk{padding:6px 10px 6px 6px}
  .mkt-rk-av,.mkt-cb-av{width:26px;height:26px}
  .mkt-rk-name,.mkt-rk-stat{max-width:120px}
}

/* ══════════════════════════════════════════════════════════════════
   §7 两级分类（迁移 103）—— 广场左栏 / 面包屑 / facet 收窄条
   顶部 chip 行放不下 41 个二级分类，所以换控件而不是硬塞。
   ══════════════════════════════════════════════════════════════════ */
/* ⚠️ `.mkt` 是 `height:100vh;overflow:hidden` 的定高列 flex，**它自己永远不滚**，
   滚动条必须挂在某个子级身上。原先那个子级是 `.mkt-body`（`flex:1;min-height:0`
   拿到剩余高度 + `overflow-y:auto`）。左栏把 `.mkt-body` 往下压了一层之后，
   `flex:1` 落在 grid item 上是**死的**，`.mkt-split` 自己又没接住剩余高度 ——
   于是首屏以下的卡片被 `.mkt` 的 overflow:hidden 直接裁掉，整页一点都拉不动。
   现网踩过（“市场里无法下拉滚动”）。

   所以 `flex:1;min-height:0` 这对组合要跟着挪到这一层。`grid-template-rows` 也
   得钉死：单行轨道默认是 auto（按内容撑高），不写成 minmax(0,1fr) 的话两栏拿到
   的仍是内容高度，各自的 overflow-y 依旧没有可滚的空间 —— 高度约束在 grid 里
   必须一路显式传下去，断一环就是静默失效。

   左右两栏各滚各的（而不是整块一起滚）：分类树全展开比一屏高，跟卡片共用一根
   滚动条的话，要看底部的分类得先把卡片划到底。 */
.mkt-split{display:grid;grid-template-columns:212px minmax(0,1fr);gap:20px;
  grid-template-rows:minmax(0,1fr);flex:1;min-height:0;padding-left:26px}
.mkt-split>.mkt-body:only-child{grid-column:1 / -1}
.mkt-rail{min-height:0;overflow-y:auto;scrollbar-width:thin}

/* 窄屏：左栏退化成**横向可滚的 chip 行**，而不是一个吃掉半屏的竖直滚动块。
   一级铺在第一行、展开的二级跟在后面，都靠横滑；这样分类始终只占两行高度，
   下面立刻就是技能卡片 —— 手机上"先看到内容"比"先看全分类"重要得多。 */
@media(max-width:900px){
  .mkt-split{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);
    gap:10px;padding-left:0}
  /* 横排 chip 行自己横滑（.ctr 上的 overflow-x），外层不再当竖向滚动容器 */
  .mkt-rail{overflow-y:visible}
  .mkt-rail .ctr{flex-direction:row;flex-wrap:nowrap;overflow-x:auto;gap:6px;
    padding-bottom:6px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .mkt-rail .ctr::-webkit-scrollbar{display:none}
  .mkt-rail .ctr-group{flex-direction:row;flex-wrap:nowrap;gap:6px}
  .mkt-rail .ctr-row{width:auto;flex:none;border:1px solid var(--brd);
    border-radius:999px;padding:5px 12px;white-space:nowrap}
  .mkt-rail .ctr-l2{padding-left:12px}
  .mkt-rail .ctr-row.on{border-color:var(--acc-b)}
  /* 展开/收起箭头在触屏上是个误点陷阱（挨着标签只有 10px）—— 窄屏直接摘掉，
     点一级本来就会展开它的子类 */
  .mkt-rail .ctr-caret{display:none}
}

/* ⚠️ 这一段必须排在上面 max-width:900px **之后**：≤860 时两段同时命中，特异度
   一样，后写的赢。§6 已经把滚动条上提到 `.mkt` 了，中间这层要是还留着
   `minmax(0,1fr)` 的行轨道，卡片区就被重新框回一屏高 —— 内容靠 overflow:visible
   溢出去看着还能滑，但滚动条归属乱了，触屏上一碰就是两层在抢。 */
@media (max-width:860px){
  .mkt-split{grid-template-rows:auto auto;flex:none;min-height:auto}
}

.ctr{display:flex;flex-direction:column;gap:1px}
.ctr-group{display:flex;flex-direction:column;gap:1px}
.ctr-row{display:flex;align-items:center;gap:7px;width:100%;padding:6px 9px;
  border:0;background:none;border-radius:6px;cursor:pointer;font:inherit;
  font-size:13px;color:var(--tx2);text-align:left;line-height:18px}
.ctr-row:hover{background:var(--bg1);color:var(--tx1)}
.ctr-row.on{background:var(--acc-m);color:var(--acc);font-weight:600}
.ctr-spine{width:3px;height:13px;border-radius:2px;background:var(--c,var(--tx3));flex:none}
.ctr-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ctr-n{font-size:11px;color:var(--tx3);font-variant-numeric:tabular-nums;flex:none}
.ctr-row.on .ctr-n{color:var(--acc)}
.ctr-l2{padding-left:23px;font-size:12.5px;color:var(--tx3)}
.ctr-l2:hover{color:var(--tx1)}
/* 计数为 0 的分类仍然显示（是"这一类还没有技能"的信息），只是压暗。
   ⚠️ 修饰类必须叫 .zero，不能叫 .empty —— styles.css 里有个全局裸 `.empty`
   （首页空态 hero：flex-direction:column + 32px 36px 64px padding + gap:16px）。
   撞上之后这一行会竖成两行、内容居中、从 30px 撑到 55px；窄屏 chip 行的高度由
   最高的 chip 决定，于是一个空分类把整排 chip 一起撑高。同一个坑在
   styles.css 的 .imt-credit.zero 那里已经记过一次了。 */
.ctr-row.zero{opacity:.55}
.ctr-caret{font-size:10px;color:var(--tx3);padding:0 2px;flex:none}
.ctr-caret:hover{color:var(--acc)}

.ctr-crumb{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--tx3);
  margin-bottom:10px;flex-wrap:wrap}
.ctr-crumb button{border:0;background:none;padding:0;font:inherit;font-size:12px;
  color:var(--tx3);cursor:pointer}
.ctr-crumb button:hover{color:var(--acc);text-decoration:underline}
.ctr-crumb b{color:var(--tx1);font-weight:600}
.ctr-crumb .sep{color:var(--tx4,var(--tx3));opacity:.6}

.ctf{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}
.ctf-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.ctf-label{font-size:11px;color:var(--tx3);flex:none;min-width:52px}
.ctf-chip{border:1px solid var(--brd);background:var(--bg0,transparent);color:var(--tx2);
  border-radius:999px;padding:2px 9px;font-size:11.5px;cursor:pointer;font-family:inherit;
  display:inline-flex;align-items:center;gap:5px}
.ctf-chip:hover{border-color:var(--acc-b);color:var(--acc)}
.ctf-chip.on{background:var(--acc-m);border-color:var(--acc-b);color:var(--acc);font-weight:600}
.ctf-chip .n{font-size:10px;color:var(--tx3);font-variant-numeric:tabular-nums}
.ctf-chip.on .n{color:var(--acc)}
.ctf-clear{border:0;background:none;color:var(--tx3);font-size:11px;cursor:pointer;
  font-family:inherit;padding:2px 4px}
.ctf-clear:hover{color:var(--acc)}

/* 编辑器里的两级级联选择器 */
.ctc{display:flex;flex-direction:column;gap:6px}
.ctc-row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.ctc-sel{flex:1;min-width:140px;padding:7px 9px;border:1px solid var(--brd);
  border-radius:6px;background:var(--bg0,#fff);color:var(--tx1);font:inherit;font-size:13px}
.ctc-ai{border:1px solid var(--acc-b);background:var(--acc-m);color:var(--acc);
  border-radius:6px;padding:7px 11px;font:inherit;font-size:12.5px;cursor:pointer;flex:none}
.ctc-ai:disabled{opacity:.6;cursor:default}
.ctc-hint{font-size:11.5px;color:var(--tx3);line-height:17px}
.ctc-hint.warn{color:var(--warn)}
