/* ══════════════════════════ TOKENS ══════════════════════════ */
:root{
  --canvas:#fbfbfa; --surface:#fff; --surface-2:#f6f6f4; --surface-3:#efefec;
  --ink:#18181b; --ink-2:#52525b; --ink-3:#7c7c85;
  --line:#e6e6e1; --line-2:#d8d8d2;
  --accent:#0e6e62; --accent-soft:#e9f2f0; --accent-line:#b9d6d0; --accent-ink:#0b574d;
  --ochre:#96620a; --ochre-soft:#fbf3e2; --ochre-line:#ecd9a8;
  --brick:#a93226; --brick-soft:#fbedeb; --brick-line:#f0cdc7;
  --solid:#18181b; --solid-ink:#fbfbfa; --solid-hover:#000;
  --ring:rgba(14,110,98,.28);
  --lift:0 1px 2px rgba(24,24,27,.05);
  --pop:0 4px 6px -2px rgba(24,24,27,.06),0 12px 28px -8px rgba(24,24,27,.14);
  --r:6px; --r-md:8px; --r-lg:10px;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB",
         "Microsoft YaHei","Source Han Sans SC","Noto Sans CJK SC",sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",
         "PingFang SC",monospace;
  --ease:cubic-bezier(.16,1,.3,1);
}
:root[data-theme="dark"]{
  --canvas:#0c0c0d; --surface:#131315; --surface-2:#17171a; --surface-3:#1f1f23;
  --ink:#f4f4f2; --ink-2:#a1a1a8; --ink-3:#7a7a82;
  --line:#232327; --line-2:#2e2e34;
  --accent:#4cc0a8; --accent-soft:#0f2320; --accent-line:#1f4a41; --accent-ink:#6fd4be;
  --ochre:#d9a441; --ochre-soft:#231b0c; --ochre-line:#453413;
  --brick:#e0776b; --brick-soft:#25120f; --brick-line:#492420;
  --solid:#f4f4f2; --solid-ink:#131315; --solid-hover:#fff;
  --ring:rgba(76,192,168,.32);
  --lift:0 1px 2px rgba(0,0,0,.4);
  --pop:0 4px 8px -2px rgba(0,0,0,.5),0 16px 40px -12px rgba(0,0,0,.7);
}
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{font-family:var(--sans);background:var(--canvas);color:var(--ink);font-size:13.5px;
  line-height:1.6;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
button,input,select,textarea{font:inherit;color:inherit}
button{background:none;border:none;cursor:pointer;text-align:left}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}
::selection{background:var(--accent-soft);color:var(--accent-ink)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}

/* ══════════════════════════ TYPE ══════════════════════════ */
.display{font-size:40px;font-weight:600;letter-spacing:-.032em;line-height:1.08}
.h2{font-size:15px;font-weight:600;letter-spacing:-.012em}
.h3{font-size:13.5px;font-weight:600}
.lede{font-size:13.5px;color:var(--ink-2);line-height:1.65;max-width:64ch}
.micro{font-size:10.5px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:12px;letter-spacing:-.01em}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums}
.dim{color:var(--ink-3)}
.tls{font-size:11.5px;color:var(--ink-3);line-height:1.55}

/* ══════════════════════════ SHELL ══════════════════════════ */
.app{display:grid;grid-template-columns:216px minmax(0,1fr);min-height:100dvh}
.rail{border-right:1px solid var(--line);background:var(--surface);position:sticky;top:0;
  height:100dvh;overflow-y:auto;display:flex;flex-direction:column;padding:14px 10px 12px}
/* 窄屏：左栏变抽屉，#menu-btn 拉出、#scrim 收回。以前这里是 display:none ——
   页面上不剩任何一个能换页的东西。宽屏下同一颗按钮把左栏收起／放回来。 */
#menu-btn{display:grid}
/* 收起来的时候那一栏连格子都不占。只在宽屏生效 —— 窄屏它已经是抽屉了（下面
   那条 media），再叠一层 display:none 就等于把抽屉也锁死。 */
@media(min-width:1001px){
  html[data-rail="off"] .app{grid-template-columns:minmax(0,1fr)}
  html[data-rail="off"] .rail{display:none}
}
.scrim{position:fixed;inset:0;z-index:60;background:rgba(24,24,27,.34)}
@media(max-width:1000px){
  .app{grid-template-columns:1fr}
  #menu-btn{display:grid}
  .rail{position:fixed;left:0;top:0;bottom:0;z-index:70;width:246px;
    transform:translateX(-102%);transition:transform .18s var(--ease)}
  .rail.open{transform:none;box-shadow:var(--pop)}
}
.mark{display:flex;align-items:center;gap:9px;padding:4px 8px 14px}
/* 主动作。导航行都是素的，这一颗有边框有底 —— 它是「做一件事」，不是「去一页看看」 */
.rail-new{display:flex;align-items:center;gap:9px;width:100%;padding:6px 8px;margin-bottom:2px;
  text-decoration:none;cursor:pointer;
  border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);
  color:var(--ink);font-size:13px;font-weight:550;box-shadow:var(--lift);
  transition:background .13s var(--ease),border-color .13s var(--ease)}
.rail-new svg{width:15px;height:15px;flex:none;opacity:.72}
.rail-new:hover{background:var(--surface-2)}
.rail-new[aria-current="page"]{background:var(--surface-3);border-color:transparent;
  box-shadow:none;font-weight:600}
.rail-new[aria-current="page"] svg{opacity:1}
.mark-sq{width:22px;height:22px;border-radius:5px;background:var(--solid);flex:none;
  display:grid;place-items:center;color:var(--solid-ink)}
.mark-sq svg{width:12px;height:12px}
.mark b{font-size:13px;font-weight:600;letter-spacing:-.015em}
.rail-sec{padding:13px 8px 5px}
/* 导航行是 <a>（各自有地址，见 30-state.js 的 VIEW_PATH）—— 但它读起来是一行
   导航，不是正文里的一条链接，所以不带下划线。#rest-btn 还是 <button>，两种
   标签穿同一件衣服。 */
.nav{display:flex;align-items:center;gap:9px;width:100%;padding:6px 8px;border-radius:var(--r);
  color:var(--ink-2);font-size:13px;font-weight:500;text-decoration:none;cursor:pointer;
  transition:background .13s var(--ease),color .13s var(--ease)}
.nav svg{width:15px;height:15px;flex:none;opacity:.62}
.nav:hover{background:var(--surface-2);color:var(--ink)}
.nav[aria-current="page"]{background:var(--surface-3);color:var(--ink);font-weight:600}
.nav[aria-current="page"] svg{opacity:1}
.nav .n{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--ink-3);
  font-variant-numeric:tabular-nums}
.recent{display:grid;gap:1px;padding:2px 0}
.recent button{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:var(--r);
  font-size:12.5px;color:var(--ink-2);width:100%;transition:background .13s var(--ease)}
.recent button:hover{background:var(--surface-2);color:var(--ink)}
.recent .st{width:5px;height:5px;border-radius:99px;flex:none;background:var(--line-2)}
.recent .st.live{background:var(--accent)}
.rail-foot{margin-top:auto;border-top:1px solid var(--line);padding-top:8px}
.figma-account-dot{width:6px;height:6px;margin-left:auto;border-radius:50%;background:var(--accent)}
.figma-account-entry{display:grid;gap:3px;text-align:left;min-width:0}
.figma-account-entry small{font-size:10.5px;font-weight:400;line-height:1.5;color:var(--ink-3)}
.figma-account-quota{padding:14px;border:1px solid var(--line);border-radius:var(--r-md);display:grid;gap:10px}
.figma-quota-title,.figma-quota-local,.figma-quota-plan{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;font-size:12px}
.figma-quota-title{align-items:center;font-size:13px}
.figma-quota-title b{font-size:18px;font-weight:600}
.figma-quota-plan{color:var(--ink-2)}
.figma-quota-memberships{display:grid;gap:10px}
.figma-quota-membership-title{font-size:12px;font-weight:600}
.figma-quota-membership{display:grid;gap:5px;padding-top:8px;border-top:1px solid var(--line)}
.figma-quota-membership>b{font-size:12px;font-weight:500;overflow-wrap:anywhere}
.figma-quota-note{font-size:11px;color:var(--ink-3);line-height:1.7;overflow-wrap:anywhere}
.figma-quota-note a{color:var(--ink-2);text-underline-offset:3px}
.figma-account .figma-quota-warning,.figma-account-entry .figma-quota-warning{font-size:11px;color:var(--brick);line-height:1.6}
.figma-account{width:min(440px,calc(100vw - 32px));max-height:calc(100dvh - 48px);margin:auto;
  padding:0;border:1px solid var(--line);border-radius:14px;background:var(--surface);color:var(--ink);
  box-shadow:var(--pop);overflow:auto}
.figma-account::backdrop{background:rgba(20,20,24,.28)}
.figma-account-head{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;
  padding:22px 24px 18px;border-bottom:1px solid var(--line)}
.figma-account-head h2{font-size:17px;font-weight:600;letter-spacing:-.02em}
.figma-account-head p{font-size:12.5px;color:var(--ink-3);margin-top:4px}
.figma-account-head>.btn{padding:0 6px;flex:none}
.figma-account-body{padding:22px 24px 24px;display:grid;gap:16px}
.figma-account-person{display:flex;align-items:center;gap:12px;min-width:0}
.figma-account-person>div{min-width:0;overflow-wrap:anywhere}
.figma-account-person b{font-size:14px;font-weight:600}
.figma-account-person p{font-size:12px;color:var(--ink-3);margin-top:2px}
.figma-account-avatar{width:40px;height:40px;flex:none;display:grid;place-items:center;
  border:1px solid var(--line);border-radius:11px;background:var(--surface-2);font-size:17px}
.figma-account-avatar svg{width:19px;height:19px;color:var(--ink-2)}
.figma-account-status{display:grid;gap:10px;padding:14px;border:1px solid var(--line);border-radius:var(--r-md)}
.figma-account-status>div{display:flex;justify-content:space-between;align-items:center;gap:16px;font-size:12.5px}
.figma-account-status b{font-weight:500}
.figma-account-status b.is-down{color:var(--brick)}
.figma-account-muted{color:var(--ink-3)}
.figma-account-help{font-size:12px;line-height:1.7;color:var(--ink-2)}
.figma-account-info,.figma-account-error{padding:11px 13px;border-radius:var(--r-md);font-size:12px;line-height:1.7}
.figma-account-info{background:var(--surface-2);color:var(--ink-2)}
.figma-account-error{background:var(--brick-soft);color:var(--brick)}
/* Agent 账号（63b-agent-account.js）：两张卡，各一行状态 + 登录步骤，沿用 Figma 账号面板的外壳。 */
.agent-acct-card{display:grid;gap:12px;padding:14px;border:1px solid var(--line);border-radius:var(--r-md)}
.agent-acct-row{display:flex;align-items:center;justify-content:space-between;gap:14px;min-width:0}
.agent-acct-row>div{min-width:0;overflow-wrap:anywhere}
.agent-acct-row b{font-size:14px;font-weight:600}
.agent-acct-row p{font-size:12px;color:var(--ink-3);margin-top:2px}
.agent-acct-state{flex:none;font-size:11.5px;padding:3px 9px;border-radius:99px;background:var(--surface-2);color:var(--ink-3)}
.agent-acct-state.is-on{background:var(--accent-soft);color:var(--accent-ink)}
.agent-acct-steps{display:grid;gap:10px;margin:0;padding-left:18px;font-size:12.5px;line-height:1.7;color:var(--ink-2)}
.agent-acct-steps a{display:inline-block;margin-left:4px}
.agent-acct-code{margin-left:4px;padding:3px 10px;border:1px dashed var(--line-2);border-radius:var(--r);
  font-family:var(--mono);font-size:15px;font-weight:600;letter-spacing:.08em;color:var(--ink);cursor:copy}
.agent-acct-codeform{display:flex;gap:8px;margin-top:6px}
.agent-acct-codeform input{flex:1;min-width:0;padding:7px 10px;border:1px solid var(--line-2);border-radius:var(--r);
  font:inherit;font-family:var(--mono);font-size:12px;background:var(--surface);color:var(--ink)}
.agent-acct-actions{display:flex;flex-wrap:wrap;gap:8px}
#agent-btn{font-family:'OPPO Sans Web',var(--sans)}
.figma-account-actions{display:flex;gap:8px;flex-wrap:wrap;padding-top:2px}
.figma-account-actions .btn{font-size:12px;padding:0 11px}
/* 新用户引导（64-welcome.js）：新登记名字后打开一次，三步。 */
.welcome{width:min(460px,calc(100vw - 32px));max-height:calc(100dvh - 48px);margin:auto;padding:0;
  border:1px solid var(--line);border-radius:16px;background:var(--surface);color:var(--ink);
  box-shadow:var(--pop);overflow:auto;font-family:'OPPO Sans Web',var(--sans)}
.welcome[open]{animation:wl-in .32s var(--ease) both}
.welcome::backdrop{background:rgba(20,20,24,.32);backdrop-filter:blur(2px)}
.wl-x{position:absolute;top:14px;right:14px;width:30px;padding:0}
.wl-pane{padding:52px 28px 6px;display:grid;gap:12px;animation:wl-step .34s var(--ease) both}
.wl-pane h2{margin:0;font-size:20px;font-weight:650;letter-spacing:-.2px}
.wl-lede{margin:0;font-size:13.5px;line-height:1.65;color:var(--ink-2)}
.wl-note{margin:0;font-size:12px;line-height:1.6;color:var(--ink-3)}
.wl-points{list-style:none;margin:2px 0 0;padding:0;display:grid;gap:10px}
.wl-points li{display:grid;grid-template-columns:30px 1fr;gap:10px;align-items:start;
  animation:wl-step .4s var(--ease) both}
.wl-points li:nth-child(2){animation-delay:.06s}.wl-points li:nth-child(3){animation-delay:.12s}
.wl-points svg{width:30px;height:30px;padding:6px;border-radius:9px;background:var(--accent-soft);color:var(--accent-ink)}
.wl-points b{display:block;font-size:13px;font-weight:600}
.wl-points span{display:block;font-size:12.5px;line-height:1.55;color:var(--ink-2)}
.wl-tips svg{background:var(--surface-2);color:var(--ink-2)}
.wl-tips li{align-items:center}
.wl-status{display:flex;align-items:center;gap:9px;padding:10px 12px;border-radius:10px;
  border:1px solid var(--line);background:var(--surface-2);font-size:13px;font-weight:550}
.wl-status-dot{width:8px;height:8px;border-radius:50%;background:var(--ink-3);flex:none}
.wl-status.is-on{border-color:var(--accent-line);background:var(--accent-soft);color:var(--accent-ink)}
.wl-status.is-on .wl-status-dot{background:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}
.wl-status.is-wait .wl-status-dot{animation:wl-pulse 1.2s ease-in-out infinite}
.wl-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:18px 28px 22px}
.wl-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}
.wl-dots{display:flex;gap:6px}
.wl-dots span{width:6px;height:6px;border-radius:99px;background:var(--line-2);transition:width .25s var(--ease),background .25s}
.wl-dots span.done{background:var(--accent-line)}
.wl-dots span.on{width:18px;background:var(--accent)}
.wl-art{width:100%;height:auto;display:block;border-radius:12px;background:var(--surface-2)}
.wl-art text{fill:var(--ink-2);font-family:inherit}
.wl-bubble{fill:var(--surface);stroke:var(--line-2)}
.wl-dot{fill:var(--ink-3);animation:wl-blink 1.2s ease-in-out infinite}
.wl-dot.d2{animation-delay:.15s}.wl-dot.d3{animation-delay:.3s}
.wl-flow{stroke:var(--accent);stroke-width:2;stroke-dasharray:4 5;fill:none;stroke-linecap:round;animation:wl-dash 1s linear infinite}
.wl-phone{fill:var(--surface);stroke:var(--line-2);stroke-width:1.5}
.wl-row{fill:var(--surface-3);transform-box:fill-box;transform-origin:left center;animation:wl-grow 4s var(--ease) infinite}
.wl-row.r2{animation-delay:.25s}.wl-row.r3{animation-delay:.5s}.wl-row.r4{animation-delay:.75s}.wl-row.r5{animation-delay:1s}
.wl-row.wl-accent{fill:var(--accent)}
.wl-tile{fill:var(--surface);stroke:var(--line-2);stroke-width:1.5}
.wl-wire{stroke:var(--line-2);stroke-width:2;stroke-dasharray:5 6;fill:none;stroke-linecap:round;animation:wl-dash 1.4s linear infinite}
.wl-knot{fill:var(--surface);stroke:var(--line-2);stroke-width:1.5}
.wl-knot-tick{fill:none;stroke:transparent;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.wl-art-link.is-on .wl-wire{stroke:var(--accent);animation-duration:.8s}
.wl-art-link.is-on .wl-knot{fill:var(--accent);stroke:var(--accent)}
.wl-art-link.is-on .wl-knot-tick{stroke:var(--surface)}
.wl-input{fill:var(--surface);stroke:var(--line-2);stroke-width:1.5}
.wl-type-mask{animation:wl-type 3.6s steps(22) infinite}
.wl-caret{fill:var(--accent);animation:wl-caret 3.6s steps(22) infinite,wl-blink 1s steps(1) infinite}
.wl-send{fill:var(--accent)}
.wl-send-arrow{fill:none;stroke:var(--surface);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
/* ② 手动复制链接的三幕动画：10.5 s 一轮，三幕与三行说明共用一条时间线。
   关掉动效时（全局 reduced-motion 规则把动画全停）停在第 ② 幕。 */
.wl-art-cb text{fill:var(--ink)}
.wl-win{fill:var(--surface);stroke:var(--line-2);stroke-width:1.2}
.wl-win-bar{fill:var(--surface-3)}
.wl-lights circle{fill:var(--line-2)}
.wl-url{fill:var(--surface);stroke:var(--line-2)}
.wl-url-hot{stroke:var(--accent);stroke-width:1.5}
.wl-art-cb .wl-url-t{fill:var(--ink-2);font-family:var(--mono)}
.wl-art-cb .wl-dim{fill:var(--ink-3)}
.wl-dim-s{fill:none;stroke:var(--ink-3);stroke-width:1.3}
.wl-art-cb .wl-ok{fill:var(--accent-ink);font-weight:600}
.wl-allow{fill:var(--solid)}
.wl-art-cb .wl-allow-t,.wl-art-cb .wl-verify-t{fill:var(--solid-ink)}
.wl-verify{fill:var(--solid)}
.wl-field{fill:var(--surface);stroke:var(--accent);stroke-width:1.3}
.wl-select{fill:var(--accent-line);opacity:.75;transform-box:fill-box;transform-origin:left center}
.wl-copied rect{fill:var(--accent)}.wl-art-cb .wl-copied text{fill:var(--surface)}
.wl-keys rect{fill:var(--surface-3)}.wl-art-cb .wl-keys text{fill:var(--ink-2);font-family:var(--mono)}
.wl-done circle{fill:var(--accent)}.wl-done path{fill:none;stroke:var(--surface);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.wl-art-cb .wl-done text{fill:var(--accent-ink)}
.wl-cursor path{fill:var(--ink);stroke:var(--surface);stroke-width:1}
.wl-paste{transform-box:fill-box;transform-origin:left center}
/* 默认（无动画）= 第 ② 幕、复制完成的样子 */
.wl-sc1,.wl-sc3,.wl-keys{opacity:0}
.wl-cursor1{transform:translate(160px,106px)}.wl-cursor3{transform:translate(108px,106px)}
.wl-sc{animation:wl-sc 10.5s linear infinite}
.wl-sc2{animation-delay:-7s}.wl-sc3{animation-delay:-3.5s}
.wl-cursor1{animation:wl-cur1 10.5s var(--ease) infinite}
.wl-allow{animation:wl-press1 10.5s linear infinite}
.wl-sc2 .wl-select{animation:wl-sel 10.5s var(--ease) infinite -7s}
.wl-keys{animation:wl-keys 10.5s linear infinite -7s}
.wl-copied{animation:wl-copied 10.5s var(--ease) infinite -7s}
.wl-paste{animation:wl-paste 10.5s steps(1) infinite -3.5s}
.wl-cursor3{animation:wl-cur3 10.5s var(--ease) infinite -3.5s}
.wl-verify{animation:wl-press3 10.5s linear infinite -3.5s}
.wl-done{animation:wl-done 10.5s var(--ease) infinite -3.5s}
@keyframes wl-sc{0%,31%{opacity:1}33.3%,97%{opacity:0}100%{opacity:1}}
@keyframes wl-cur1{0%{transform:translate(250px,135px)}14%,100%{transform:translate(160px,106px)}}
@keyframes wl-press1{0%,15%{opacity:1}16.5%{opacity:.6}18%,100%{opacity:1}}
@keyframes wl-sel{0%,5%{transform:scaleX(0)}14%,100%{transform:scaleX(1)}}
@keyframes wl-keys{0%,3%{opacity:0}5%,15%{opacity:1}17%,100%{opacity:0}}
@keyframes wl-copied{0%,16%{opacity:0;transform:translateY(4px)}19%,100%{opacity:1;transform:none}}
@keyframes wl-paste{0%{transform:scaleX(0)}9%,100%{transform:scaleX(1)}}
@keyframes wl-cur3{0%{transform:translate(200px,135px)}8%{transform:translate(160px,78px)}15%,100%{transform:translate(108px,106px)}}
@keyframes wl-press3{0%,16%{opacity:1}17.5%{opacity:.6}19%,100%{opacity:1}}
@keyframes wl-done{0%,19%{opacity:0;transform:translateX(-4px)}22%,100%{opacity:1;transform:none}}
.wl-cb-steps{list-style:none;margin:0;padding:0;display:grid;gap:4px}
.wl-cb-steps li{display:grid;grid-template-columns:22px 1fr;gap:9px;align-items:start;padding:7px 9px;border-radius:9px;
  font-size:12.5px;line-height:1.6;color:var(--ink-2);animation:wl-li 10.5s linear infinite}
.wl-cb-steps li:nth-child(2){animation-delay:-7s;background:var(--accent-soft)}
.wl-cb-steps li:nth-child(3){animation-delay:-3.5s}
.wl-cb-steps b{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;font-size:11.5px;
  background:var(--surface-3);color:var(--ink-2)}
.wl-cb-steps em{font-style:normal;font-weight:600;color:var(--ink)}
.wl-cb-steps kbd{font:11px var(--mono);padding:1px 5px;border:1px solid var(--line-2);border-bottom-width:2px;border-radius:4px;background:var(--surface)}
.wl-lede b{color:var(--ink);font-weight:600}
@keyframes wl-li{0%,31%{background:var(--accent-soft)}33.3%,100%{background:transparent}}
@keyframes wl-in{from{opacity:0;transform:translateY(10px) scale(.98)}}
@keyframes wl-step{from{opacity:0;transform:translateX(10px)}}
@keyframes wl-pulse{50%{opacity:.35}}
@keyframes wl-blink{50%{opacity:.25}}
@keyframes wl-dash{to{stroke-dashoffset:-11}}
@keyframes wl-grow{0%{transform:scaleX(0);opacity:0}14%,82%{transform:scaleX(1);opacity:1}100%{opacity:0}}
@keyframes wl-type{0%{width:0}70%,100%{width:200px}}
@keyframes wl-caret{0%{transform:translateX(0)}70%,100%{transform:translateX(200px)}}
@media (max-width:480px){.wl-pane{padding:50px 18px 4px}.wl-foot{padding:16px 18px 18px;flex-direction:column-reverse;align-items:stretch}
  .wl-actions{justify-content:stretch}.wl-actions .btn{flex:1}.wl-dots{justify-content:center}}
/* Connection prompts share the bundled variable font and follow confirmed MCP state. */
#rest-btn,.figma-account{font-family:'OPPO Sans Web',var(--sans)}
#rest-btn.figma-needs-connection{position:relative;color:var(--accent-ink);background:var(--accent-soft)}
#rest-btn.figma-needs-connection>svg{opacity:1}
#rest-btn.figma-needs-connection::after{content:'';width:6px;height:6px;margin-left:auto;margin-right:4px;
  border-top:1.5px solid currentColor;border-right:1.5px solid currentColor;transform:rotate(45deg)}
#rest-btn.figma-needs-connection::before{content:'';position:absolute;inset:1px;border:1px solid var(--accent);
  border-radius:inherit;pointer-events:none;opacity:.2;animation:figma-entry-breathe 3.6s ease-in-out infinite}
.figma-account-connect-hint{display:flex;align-items:center;gap:6px;flex-basis:100%;margin:0 0 3px;
  color:var(--accent-ink);font-size:12px;line-height:1.6}
.figma-account-connect-hint>span{display:flex;animation:figma-connect-point 3.6s ease-in-out infinite}
.figma-account-connect-hint svg{width:16px;height:16px}
.figma-account.figma-needs-connection [data-figma-action="connect"]{position:relative;
  background:var(--accent);border-color:var(--accent);color:var(--canvas)}
.figma-account.figma-needs-connection [data-figma-action="connect"]::after{content:'';position:absolute;inset:-4px;
  border:1px solid var(--accent);border-radius:calc(var(--r) + 4px);pointer-events:none;opacity:.3;
  animation:figma-connect-ring 3.6s ease-in-out infinite}
.figma-account.figma-needs-connection [data-figma-action="connect"]:hover::after,
.figma-account.figma-needs-connection [data-figma-action="connect"]:focus-visible::after{animation:none;opacity:.5}
@keyframes figma-entry-breathe{0%,100%{opacity:.12}40%{opacity:.55}70%{opacity:.12}}
@keyframes figma-connect-point{0%,60%,100%{transform:translateY(0)}30%{transform:translateY(3px)}}
@keyframes figma-connect-ring{0%,100%{transform:scale(1);opacity:.2}40%{transform:scale(1.035,1.1);opacity:.5}70%{transform:scale(1);opacity:.2}}
.figma-account-progress{display:flex;gap:10px;align-items:flex-start;padding:12px 14px;
  border:1px solid var(--accent-line);border-radius:var(--r-md);background:var(--accent-soft)}
.figma-account-progress .bead{width:7px;height:7px;flex:none;margin-top:7px;background:var(--accent);border-radius:50%}
.figma-account-progress b{font-size:13px;font-weight:600;color:var(--accent-ink)}
.figma-account-progress p{font-size:12px;line-height:1.65;color:var(--ink-2);margin-top:3px}
.figma-account-steps{list-style:none;display:grid;gap:20px;margin:0;padding:0}
.figma-account-steps>li{display:grid;grid-template-columns:24px minmax(0,1fr);gap:11px;align-items:start}
.figma-account-step{display:grid;place-items:center;width:24px;height:24px;border:1px solid var(--line-2);
  border-radius:50%;font-size:12px;font-weight:600;background:var(--surface-2);color:var(--ink-2)}
.figma-account-steps h3,.figma-account-steps .figma-account-complete label{font-size:13px;line-height:24px;font-weight:600}
.figma-account-steps p{font-size:12px;line-height:1.7;color:var(--ink-2);margin-top:5px;overflow-wrap:anywhere}
.figma-account-auth-link{display:inline-block;margin-top:6px;color:var(--accent-ink);font-size:12px;text-underline-offset:3px}
.figma-account-steps .figma-account-callback-note{padding:9px 11px;margin:8px 0;border:1px solid var(--ochre-line);
  border-radius:var(--r);background:var(--ochre-soft);color:var(--ochre)}
.figma-account-shortcut kbd{font:11px var(--mono);padding:2px 4px;border:1px solid var(--line);border-radius:3px;background:var(--surface-2)}
.figma-account-copy-figure{margin:10px 0 0;user-select:none}
.figma-account-copy-figure svg{display:block;width:100%;height:auto}
.figma-account-copy-figure text{font-family:var(--sans)}
.figma-account-complete{display:grid;gap:8px}
.figma-account-complete input{width:100%;min-width:0;padding:10px;border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);font-size:12px}
.figma-account-complete input:disabled{color:var(--ink-3);background:var(--surface-2)}
.figma-account-complete .btn{justify-self:start;font-size:12px}
@media(pointer:coarse){.figma-account-shortcut{display:none}}
/* 语言 / 旧版 / 主题：三个低频动作收进一行，只露图标，话在 title 里 */
.rail-tools{display:flex;gap:2px;margin-top:2px}
.rail-tools .nav{flex:1;width:auto;justify-content:center;padding:6px 0}
.rail-tools .nav span{display:none}

.main{min-width:0;display:flex;flex-direction:column}
.top{display:flex;align-items:center;gap:14px;height:49px;padding:0 20px;flex:none;
  border-bottom:1px solid var(--line);background:var(--surface);position:sticky;top:0;z-index:30}
.bc{display:flex;align-items:center;gap:8px;font-size:13px;min-width:0}
.bc b{font-weight:600}.bc .s{color:var(--line-2)}.bc .p{color:var(--ink-3)}
.cmdk{margin:0 auto;display:flex;align-items:center;gap:8px;width:min(300px,32vw);height:29px;
  padding:0 9px;border:1px solid var(--line);border-radius:var(--r);background:var(--surface-2);
  color:var(--ink-3);font-size:12.5px;transition:border-color .13s var(--ease),background .13s var(--ease)}
.cmdk:hover{border-color:var(--line-2);background:var(--surface)}
.cmdk svg{width:13px;height:13px;flex:none}
.cmdk .kbd{margin-left:auto;font-family:var(--mono);font-size:10.5px;padding:1px 5px;
  border:1px solid var(--line);border-radius:4px;background:var(--surface);color:var(--ink-3)}
@media(max-width:860px){.cmdk{display:none}}
.top-r{margin-left:auto;display:flex;align-items:center;gap:5px}
.iconbtn{width:29px;height:29px;border-radius:var(--r);display:grid;place-items:center;
  color:var(--ink-3);transition:background .13s var(--ease),color .13s var(--ease)}
.iconbtn:hover{background:var(--surface-2);color:var(--ink)}
.iconbtn svg{width:15px;height:15px}
.who{display:flex;align-items:center;gap:7px;height:29px;padding:0 9px 0 5px;border-radius:var(--r);
  font-size:12.5px;color:var(--ink-2);transition:background .13s var(--ease)}
.who:hover{background:var(--surface-2)}
/* CoFlow 有更新（36-version.js）：顶栏里的一行，不是横幅 */
.upd{display:flex;align-items:center;gap:8px;margin-right:6px;font-size:12.5px;color:var(--ink-2);white-space:nowrap}
.upd[hidden]{display:none}
.upd::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--accent);flex:none}
@media(max-width:640px){.upd-t{display:none}}
.upd-say{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.who .av{width:19px;height:19px;border-radius:5px;flex:none;display:grid;place-items:center;
  background:var(--accent-soft);color:var(--accent-ink);font-size:10.5px;font-weight:700;
  border:1px solid var(--accent-line)}

.view{padding:28px 26px 76px;max-width:1240px;width:100%}
/* Native `[hidden]` loses to any author rule that sets display (grid/flex/block).
   A partial or interrupted workspace render must never leave its loading shell
   visible beside the real details, so hidden is an unconditional invariant. */
[hidden]{display:none!important}
/* 换页是一次转场，不是一次跳变：进来的那一页 6px 上浮 + 淡入。190ms —— 再长
   点击就开始「黏」。@media (prefers-reduced-motion) 那条总规则会把它整条关掉。 */
@keyframes vin{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.view.vin,.app.vin{animation:vin .19s var(--ease) both}
/* 进场屏那一帧：主界面先别画。它马上会被进场屏整块盖住。 */
:root[data-boot="gate"] .app{visibility:hidden}
.vhead{display:flex;align-items:flex-start;gap:20px;margin-bottom:22px;flex-wrap:wrap}
.vhead .g{flex:1;min-width:260px}
.vhead .display{margin-bottom:7px}

/* ══════════════════════════ ATOMS ══════════════════════════ */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 13px;
  border-radius:var(--r);font-size:13px;font-weight:550;border:1px solid transparent;white-space:nowrap;
  transition:background .13s var(--ease),border-color .13s var(--ease),color .13s var(--ease),transform .09s var(--ease)}
.btn svg{width:14px;height:14px;flex:none}
.btn:active{transform:translateY(1px)}
.btn.solid{background:var(--solid);color:var(--solid-ink)}
.btn.solid:hover{background:var(--solid-hover)}
.btn.line{border-color:var(--line-2);background:var(--surface);color:var(--ink)}
.btn.line:hover{border-color:var(--ink-3);background:var(--surface-2)}
/* 失败不能长得跟另一个可选动作一样：写回失败那一颗是「点开看原因」，
   不是「再试一次」，颜色得说清它是坏消息。 */
.btn.line.bad{border-color:var(--brick-line);background:var(--brick-soft);color:var(--brick)}
.btn.line.bad:hover{border-color:var(--brick);background:var(--brick-soft)}
.btn.acc{background:var(--accent);color:#fff}
:root[data-theme="dark"] .btn.acc{color:#08201c}
.btn.acc:hover{filter:brightness(1.08)}
.btn:disabled{opacity:.4;pointer-events:none}
/* 真正在做事的 disabled 和「条件不满足」不是同一种灰。保留完整对比度、转圈和
   progress 光标，让读取／提交看起来像进行中，而不是一个坏掉的按钮。 */
.btn.is-busy:disabled{opacity:1;cursor:progress}
.btn.is-busy svg{animation:igspin .9s linear infinite}
/* 一个 <a class="btn"> 关不掉：:disabled 对它无效，而「不给 href」的写法让它跟
   能用的按钮长得一模一样 —— 点下去无声无息，看着就是这个功能坏了。这条规则一次
   管住所有链接式按钮：aria-disabled 的必须看得出来是关着的。 */
.btn[aria-disabled="true"]{opacity:.4;pointer-events:none;cursor:not-allowed}
.btn.sm{height:27px;padding:0 10px;font-size:12.5px}

.pill{display:inline-flex;align-items:center;gap:5px;height:19px;padding:0 7px;border-radius:4px;
  font-size:11px;font-weight:600;background:var(--surface-3);color:var(--ink-2);white-space:nowrap}
.pill.acc{background:var(--accent-soft);color:var(--accent-ink);box-shadow:inset 0 0 0 1px var(--accent-line)}
.pill.och{background:var(--ochre-soft);color:var(--ochre);box-shadow:inset 0 0 0 1px var(--ochre-line)}
.pill.bad{background:var(--brick-soft);color:var(--brick);box-shadow:inset 0 0 0 1px var(--brick-line)}
.pill.plan{background:transparent;color:var(--ink-3);box-shadow:inset 0 0 0 1px var(--line-2)}
.pill.mono{font-family:var(--mono);font-weight:500}
.bead{width:5px;height:5px;border-radius:99px;background:currentColor;flex:none}
.bead.beat{animation:beat 1.8s var(--ease) infinite}
@keyframes beat{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.82)}}
.api{font-family:var(--mono);font-size:11px;color:var(--ink-3);background:var(--surface-2);
  border:1px solid var(--line);border-radius:4px;padding:1px 6px;white-space:nowrap}
.stack>*+*{border-top:1px solid var(--line)}

/* — 提示条 — */
.banner{display:flex;gap:11px;padding:11px 13px;border:1px solid var(--line);border-radius:var(--r-md);
  font-size:12.5px;line-height:1.6;background:var(--surface);margin-bottom:16px;align-items:flex-start}
.banner svg{width:15px;height:15px;flex:none;margin-top:2px}
.banner.acc{border-color:var(--accent-line);background:var(--accent-soft);color:var(--accent-ink)}
.banner.och{border-color:var(--ochre-line);background:var(--ochre-soft);color:var(--ochre)}
.banner.bad{border-color:var(--brick-line);background:var(--brick-soft);color:var(--brick)}
.banner b{font-weight:600}
.banner code{font-family:var(--mono);font-size:11.5px;padding:0 4px;border-radius:3px;
  background:var(--surface);border:1px solid currentColor;opacity:.9}
.banner .x{margin-left:auto;flex:none}

/* — 步骤栏格 — */
.step{display:grid;grid-template-columns:138px minmax(0,1fr)}
.step-l{padding:20px 20px 20px 0;border-right:1px solid var(--line)}
.step-l .idx{font-family:var(--mono);font-size:11px;color:var(--ink-3);display:block;margin-bottom:3px}
.step-l .t{font-size:13px;font-weight:600;letter-spacing:-.01em}
.step-l .d{font-size:11.5px;color:var(--ink-3);margin-top:5px;line-height:1.5}
.step-r{padding:20px 0 20px 22px;min-width:0}
@media(max-width:760px){.step{grid-template-columns:1fr}
  .step-l{border-right:0;border-bottom:1px solid var(--line);padding:16px 0 12px}
  .step-r{padding:14px 0 20px}}

/* — 可选行 — */
.opt{display:flex;align-items:flex-start;gap:11px;width:100%;padding:11px 12px;
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);position:relative;
  transition:border-color .13s var(--ease),background .13s var(--ease)}
.opt+.opt{margin-top:6px}
.opt:hover{border-color:var(--line-2);background:var(--surface-2)}
.opt[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-soft);
  box-shadow:inset 0 0 0 1px var(--accent)}
.opt[aria-disabled="true"]{border-style:dashed;background:transparent;cursor:not-allowed;opacity:.72}
.opt[aria-disabled="true"]:hover{border-color:var(--line);background:transparent}
.opt-tick{width:15px;height:15px;border-radius:99px;flex:none;margin-top:2px;border:1px solid var(--line-2);
  display:grid;place-items:center;background:var(--surface)}
.opt[aria-pressed="true"] .opt-tick{background:var(--accent);border-color:var(--accent);color:#fff}
:root[data-theme="dark"] .opt[aria-pressed="true"] .opt-tick{color:#08201c}
.opt[aria-disabled="true"] .opt-tick{border-style:dashed;background:transparent}
.opt-tick svg{width:9px;height:9px;opacity:0}
.opt[aria-pressed="true"] .opt-tick svg{opacity:1}
.opt-b{min-width:0;flex:1}
.opt-t{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;font-weight:600}
.opt-h{font-size:12px;color:var(--ink-3);line-height:1.55;margin-top:3px}
.opt-m{display:flex;gap:7px;flex-wrap:wrap;margin-top:8px;align-items:center}
.opt-lim{font-size:11px;color:var(--ink-3);margin-top:6px;font-family:var(--mono)}

.chips{display:flex;flex-wrap:wrap;gap:6px}
.chip{display:inline-flex;align-items:center;gap:7px;height:29px;padding:0 11px;border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface);font-size:12.5px;color:var(--ink-2);transition:.13s var(--ease)}
.chip:hover{border-color:var(--line-2);color:var(--ink)}
.chip[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink);font-weight:600}
.chip[aria-disabled="true"]{border-style:dashed;opacity:.6;cursor:not-allowed}
.chip[aria-disabled="true"]:hover{border-color:var(--line);color:var(--ink-2)}
.chip .bud{font-family:var(--mono);font-size:10.5px;color:var(--ink-3)}

.field{display:grid;gap:5px;margin-bottom:16px}
.field:last-child{margin-bottom:0}
.field .lbl{font-size:12px;font-weight:600}
.field .hlp{font-size:11.5px;color:var(--ink-3);line-height:1.5}
.seg{display:inline-flex;padding:2px;background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r);gap:2px}
.seg button{padding:4px 11px;border-radius:4px;font-size:12.5px;color:var(--ink-3);transition:.13s var(--ease)}
.seg button:hover{color:var(--ink)}
.seg button[aria-pressed="true"]{background:var(--surface);color:var(--ink);font-weight:600;box-shadow:var(--lift)}
.seg button:disabled{opacity:.4;pointer-events:none}
/* 跟 .btn.sm 一样高（27 px），这样它能跟一排小按钮站在同一条基线上 */
.seg.sm button{height:21px;padding:0 9px;font-size:12px;display:inline-flex;align-items:center}
.swrow{display:flex;align-items:flex-start;gap:9px;padding:7px 0;cursor:pointer}
.swrow+.swrow{border-top:1px solid var(--line)}
.sw{width:30px;height:17px;border-radius:99px;background:var(--line-2);flex:none;position:relative;
  transition:background .16s var(--ease);margin-top:1px}
.sw::after{content:"";position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:99px;
  background:#fff;box-shadow:var(--lift);transition:transform .16s var(--ease)}
.swrow[aria-pressed="true"] .sw{background:var(--accent)}
.swrow[aria-pressed="true"] .sw::after{transform:translateX(13px)}
select{height:32px;padding:0 30px 0 10px;border:1px solid var(--line-2);border-radius:var(--r);
  background:var(--surface);font-size:13px;outline:none;appearance:none;width:100%;color:var(--ink);
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 15px) 14px,calc(100% - 11px) 14px;background-size:4px 4px;background-repeat:no-repeat}
select:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}

/* 请求预览 */
.req{border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;background:var(--surface)}
.req-h{display:flex;align-items:center;gap:9px;padding:8px 12px;background:var(--surface-2);
  border-bottom:1px solid var(--line)}
.req-b{font-family:var(--mono);font-size:11.5px;line-height:1.75;padding:11px 13px;
  white-space:pre;overflow-x:auto;color:var(--ink-2)}
.req-b .k{color:var(--accent-ink)} .req-b .s{color:var(--ink)}
.req-b .c{color:var(--ink-3)}

/* ══════════════════════════ 项目表 ══════════════════════════ */
.tbl{border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;background:var(--surface)}
.tbl-h,.tbl-r{display:grid;grid-template-columns:minmax(170px,2.1fr) 1fr 52px 122px 116px;gap:14px;
  padding:0 15px;align-items:center}
.tbl-h{height:33px;background:var(--surface-2);border-bottom:1px solid var(--line)}
.tbl-h span{font-size:10.5px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}
.tbl-r{height:53px;width:100%;border-top:1px solid var(--line);transition:background .12s var(--ease)}
.tbl-r:first-of-type{border-top:0}
.tbl-r:hover{background:var(--surface-2)}
.tbl-n{display:flex;align-items:center;gap:10px;min-width:0}
.tbl-n>span:last-child{min-width:0;flex:1;overflow:hidden}
.glyph{width:26px;height:26px;border-radius:var(--r);flex:none;border:1px solid var(--line);
  background:var(--surface-2);display:flex;align-items:center;justify-content:center;gap:1.5px;padding:4px}
.glyph i{display:block;width:3px;border-radius:1px;background:var(--ink-3);opacity:.5}
.glyph i:nth-child(1){height:8px}.glyph i:nth-child(2){height:14px}.glyph i:nth-child(3){height:11px}
.tbl-r:hover .glyph i{background:var(--accent);opacity:.75}
.tbl-nm{display:block;max-width:100%;font-size:13.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tbl-sub{font-size:11.5px;color:var(--ink-3)}
.right{text-align:right;justify-self:end}
/* 窄屏：三行 —— [名字][时间] / [状态][操作] / [产物]。「用哪套」是唯一能省的一格；
   产物和中断/重新运行不能省，它们正是来这一页要做的事。 */
@media(max-width:900px){.tbl-h{display:none}
  .tbl-r{grid-template-columns:1fr auto;height:auto;padding:13px 14px;gap:8px 10px}
  .tbl-r>*:nth-child(2){display:none}
  .tbl-r>*:nth-child(1){grid-area:1/1}
  .tbl-r>*:nth-child(6){grid-area:1/2;justify-self:end}
  .tbl-r>*:nth-child(3){grid-area:2/1}
  .tbl-r>*:nth-child(5){grid-area:2/2;justify-self:end}
  .tbl-r>*:nth-child(4){grid-area:3/1/auto/-1}}

/* ══════════════════════════ 工作台 ══════════════════════════ */
.ws{display:grid;grid-template-columns:minmax(0,1fr) 300px;border:1px solid var(--line);
  border-radius:var(--r-lg);overflow:hidden;background:var(--surface)}
@media(max-width:1120px){.ws{grid-template-columns:1fr}}
.utabs{display:flex;border-bottom:1px solid var(--line);padding:0 6px;overflow-x:auto;background:var(--surface-2)}
.utab{display:flex;align-items:center;gap:7px;padding:10px 12px 9px;font-size:12.5px;color:var(--ink-3);
  font-weight:500;white-space:nowrap;border-bottom:2px solid transparent;margin-bottom:-1px;
  transition:color .13s var(--ease)}
.utab:hover{color:var(--ink-2)}
.utab[aria-selected="true"]{color:var(--ink);font-weight:600;border-bottom-color:var(--ink)}
.utab .st{width:5px;height:5px;border-radius:99px;background:var(--accent);flex:none}
.utab .st.idle{background:var(--line-2)}
.utab .st.pend{background:var(--ochre)}
/* 「可玩原型」页签前的一小格真截图（爬虫存的第一屏，见 90-workspace.js）。
   占位色和圆点一样先垫上，图迟到或没有都不会把这颗页签的宽度或页签栏的
   高度扯动一下——图加载失败时脚本会把 <img> 摘掉，这块底色就是它的空态。 */
.utab-th{width:18px;height:18px;border-radius:4px;overflow:hidden;flex:none;background:var(--surface-3)}
.utab-th img{display:block;width:100%;height:100%;object-fit:cover}
.stage{min-height:456px;display:grid;place-items:center;padding:32px 20px;position:relative;
  background:linear-gradient(var(--line) 1px,transparent 1px) 0 0/100% 28px,
             linear-gradient(90deg,var(--line) 1px,transparent 1px) 0 0/28px 100%,var(--surface-2)}
/* .stage 自己声明了 display:grid，会盖掉 [hidden] 的 display:none —— 少了这行，
   交付物出来之后那块「Discovering screens — 29 found so far」的骨架屏还挂在预览
   下面，一次跑完的运行看起来像还在爬。同 .step / .pop 的老问题。 */
.stage[hidden]{display:none}
.stage::before{content:"";position:absolute;inset:0;
  background:radial-gradient(70% 60% at 50% 40%,transparent,var(--surface-2) 96%)}
.device{width:230px;border-radius:22px;background:var(--surface);border:1px solid var(--line-2);
  box-shadow:var(--pop);padding:9px;position:relative;z-index:1}
.dev-scr{border-radius:15px;overflow:hidden;background:var(--surface-2);height:386px;display:flex;flex-direction:column}
.dev-bar{height:26px;display:flex;align-items:center;justify-content:space-between;padding:0 12px;flex:none}
.dev-bar .t{font-family:var(--mono);font-size:9.5px;color:var(--ink-3)}
.dev-bar .r{display:flex;gap:3px}
.dev-bar .r i{width:3px;height:3px;border-radius:99px;background:var(--ink-3);opacity:.5;display:block}
.dev-hd{padding:4px 14px 12px}
.dev-hd .k{font-size:9.5px;color:var(--ink-3);letter-spacing:.06em;text-transform:uppercase;font-weight:600}
.dev-hd .h{font-size:17px;font-weight:600;letter-spacing:-.02em;margin-top:2px}
.dev-body{flex:1;padding:0 12px 12px;display:grid;gap:7px;align-content:start}
.dev-card{border:1px solid var(--line);border-radius:9px;background:var(--surface);padding:9px 10px;display:grid;gap:6px}
.dev-card .a{height:7px;border-radius:3px;background:var(--surface-3);width:64%}
.dev-card .b{height:5px;border-radius:3px;background:var(--surface-3);width:88%;opacity:.7}
.dev-hero{height:84px;border-radius:9px;background:var(--surface-3);border:1px solid var(--line);position:relative;overflow:hidden}
.dev-hero::after{content:"";position:absolute;inset:0;background:linear-gradient(135deg,var(--accent-soft),transparent 62%)}
.dev-cta{height:30px;border-radius:8px;background:var(--accent);opacity:.92}
.stage-cap{position:absolute;bottom:14px;left:50%;transform:translateX(-50%);z-index:2;display:flex;
  align-items:center;gap:9px;height:26px;padding:0 11px;border:1px solid var(--line);border-radius:99px;
  background:var(--surface);box-shadow:var(--lift);font-size:11.5px;color:var(--ink-2);white-space:nowrap}
.skel{width:230px;display:grid;gap:9px;z-index:1}
.sk{border-radius:var(--r-md);background:var(--surface-3);position:relative;overflow:hidden}
.sk::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.55),transparent);animation:shim 1.5s infinite}
:root[data-theme="dark"] .sk::after{background:linear-gradient(90deg,transparent,rgba(255,255,255,.07),transparent)}
@keyframes shim{100%{transform:translateX(100%)}}
.empty{text-align:center;max-width:320px;z-index:1;display:grid;gap:9px;justify-items:center}
.empty .ic{width:34px;height:34px;border-radius:var(--r-md);border:1px dashed var(--line-2);
  display:grid;place-items:center;color:var(--ink-3)}
.empty .ic svg{width:16px;height:16px}

.insp{border-left:1px solid var(--line);background:var(--surface);display:grid;align-content:start}
@media(max-width:1120px){.insp{border-left:0;border-top:1px solid var(--line)}}
.ins{border-bottom:1px solid var(--line)}
.ins:last-child{border-bottom:0}
.ins-h{display:flex;align-items:center;gap:8px;padding:11px 14px 9px}
.ins-h .t{font-size:10.5px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}
.ins-h .x{margin-left:auto}
.ins-b{padding:0 14px 14px}
/* 阶段时间线：一条 skill 的脊柱，走过的节点实心、当前节点在跳、没到的节点是空心。
   竖排是因为它住在最窄的一栏里 —— 四个横排节点在 250px 宽度下只能把每个阶段名
   截成两个字。 */
.tl{display:grid;margin:2px 0 13px}
.tli{display:grid;grid-template-columns:13px minmax(0,1fr);gap:10px;padding-bottom:12px;position:relative}
.tli:last-child{padding-bottom:0}
.tli::before{content:"";position:absolute;left:6px;top:13px;bottom:0;width:1px;background:var(--line)}
.tli.done::before{background:var(--accent-line)}
.tli:last-child::before{display:none}
.tld{width:7px;height:7px;border-radius:99px;background:var(--surface);margin:4px 0 0 3px;z-index:1;
  box-shadow:0 0 0 1.5px var(--line-2),0 0 0 3px var(--surface)}
.tld.done{background:var(--accent);box-shadow:0 0 0 1.5px var(--accent),0 0 0 3px var(--surface)}
.tld.now{background:var(--accent);box-shadow:0 0 0 1.5px var(--accent),0 0 0 3px var(--surface),
  0 0 0 6px var(--accent-soft);animation:beat 1.8s var(--ease) infinite}
/* 受阻（figma_pending）：走到了这一节但没走完。不跳 —— 它没有在进行。 */
.tld.stuck{background:var(--ochre);box-shadow:0 0 0 1.5px var(--ochre),0 0 0 3px var(--surface),
  0 0 0 6px var(--ochre-soft)}
.tlt{font-size:12.5px;color:var(--ink-3);line-height:1.45}
.tli.done .tlt{color:var(--ink-2)}
.tli.now .tlt{color:var(--ink);font-weight:650}
.tli.stuck .tlt{color:var(--ochre);font-weight:650}
/* 当前节点底下那行：服务端的活口播报（“Discovering screens — 2 found so far”），
   数字会变。它挂在节点上，而不是单独一行「阶段」—— 那样读的人得自己把两者对上。 */
.tln{font-size:11.5px;color:var(--ink-3);line-height:1.5;margin-top:2px}
/* 每一步花掉的时间，跟在步名后面：读的是「哪一步在吃时间」，所以它必须小、灰、
   不抢步名，而不是自成一列。 */
.apk-logd{margin-top:10px}
.apk-logd>summary{cursor:pointer;list-style:none}
.apk-logd>summary::-webkit-details-marker{display:none}
.apk-logd>summary::before{content:"▸ ";color:var(--ink-3)}
.apk-logd[open]>summary::before{content:"▾ "}
.tlm{font-size:11px;color:var(--ink-3);font-weight:400;margin-left:8px;font-variant-numeric:tabular-nums}
.tli.now .tlm{color:var(--ink-2)}
.kv{display:flex;align-items:baseline;gap:10px;padding:6px 0;font-size:12.5px}
.kv+.kv{border-top:1px solid var(--line)}
.kv .k{color:var(--ink-3);font-size:12px}
.kv .v{margin-left:auto;font-weight:600;text-align:right}
.kv .v.mono{font-weight:500}

/* ══════════════════════════ 同步中心 ══════════════════════════ */
.tally{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);
  border-radius:var(--r-lg);overflow:hidden;background:var(--surface);margin-bottom:16px}
@media(max-width:700px){.tally{grid-template-columns:repeat(2,1fr)}}
.tal{padding:13px 16px;border-left:1px solid var(--line)}
.tal:first-child{border-left:0}
.tal .n{font-family:var(--mono);font-size:26px;font-weight:600;letter-spacing:-.03em;line-height:1.1;
  font-variant-numeric:tabular-nums}
.tal .l{font-size:11.5px;color:var(--ink-3);margin-top:3px}
.tal.a .n{color:var(--accent)}.tal.o .n{color:var(--ochre)}.tal.b .n{color:var(--brick)}
.unit{border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);overflow:hidden}
.unit+.unit{margin-top:6px}
.unit.o{border-color:var(--ochre-line)}.unit.b{border-color:var(--brick-line)}
.unit-h{display:flex;align-items:center;gap:11px;width:100%;padding:11px 13px;transition:background .12s var(--ease)}
.unit-h:hover{background:var(--surface-2)}
.unit-h .car{width:12px;height:12px;color:var(--ink-3);flex:none;transition:transform .16s var(--ease)}
.unit[data-open="1"] .car{transform:rotate(90deg)}
.unit-h .nm{font-size:13px;font-weight:600}
.unit-h .id{font-family:var(--mono);font-size:11.5px;color:var(--ink-3)}
.unit-h .r{margin-left:auto;display:flex;align-items:center;gap:9px}
.unit-b{display:none;border-top:1px solid var(--line)}
.unit[data-open="1"] .unit-b{display:block}
.diff{font-family:var(--mono);font-size:12px;line-height:1.75;background:var(--surface-2)}
.dl{display:grid;grid-template-columns:20px 150px minmax(0,1fr);align-items:baseline;
  padding:2px 13px 2px 0;font-variant-numeric:tabular-nums}
.dl .g{text-align:center;color:var(--ink-3);user-select:none;font-weight:600}
.dl .f{color:var(--ink-3);padding-right:12px;font-family:var(--sans);font-size:11.5px}
.dl.minus{background:var(--brick-soft)}
.dl.minus .g,.dl.minus .v{color:var(--brick)}
.dl.plus{background:var(--accent-soft)}
.dl.plus .g{color:var(--accent-ink)}
.dl.plus .v{color:var(--accent-ink);font-weight:600}
.dl .v{display:flex;align-items:center;gap:7px;min-width:0}
.sw16{width:11px;height:11px;border-radius:3px;border:1px solid rgba(0,0,0,.16);flex:none}
.dsep{height:1px;background:var(--line)}
.regen{padding:10px 13px;background:var(--surface-2);border-top:1px solid var(--line);
  font-size:12px;color:var(--ink-2);display:flex;gap:9px;align-items:flex-start}
.regen svg{width:14px;height:14px;flex:none;margin-top:2px;color:var(--ink-3)}
.resolve{display:flex;align-items:center;gap:14px;padding:11px 13px;background:var(--ochre-soft);
  border-top:1px solid var(--ochre-line);flex-wrap:wrap}
.resolve .q{font-size:12.5px;font-weight:600;color:var(--ochre)}
.rad{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;cursor:pointer;color:var(--ink-2)}
.rad i{width:13px;height:13px;border-radius:99px;border:1px solid var(--line-2);background:var(--surface);
  display:inline-grid;place-items:center;flex:none}
.rad[aria-checked="true"]{color:var(--ink);font-weight:600}
.rad[aria-checked="true"] i{border-color:var(--ochre)}
.rad[aria-checked="true"] i::after{content:"";width:6px;height:6px;border-radius:99px;background:var(--ochre)}
.actbar{display:flex;align-items:center;gap:12px;margin-top:18px;padding-top:16px;
  border-top:1px solid var(--line);flex-wrap:wrap}

/* ══════════════════════════ 能力库 ══════════════════════════ */
/* 窄下来时这一排整条横向滚，不让某一档的名字断成两行 ——「图标生成」断成
   「图标 / 生成」之后，一排档位高低不齐，也看不出哪几个字是一档。 */
.ftabs{display:flex;gap:2px;border-bottom:1px solid var(--line);overflow-x:auto;
  scrollbar-width:none}
.ftabs::-webkit-scrollbar{display:none}
.ftab{padding:8px 12px;font-size:12.5px;color:var(--ink-3);font-weight:500;border-bottom:2px solid transparent;
  margin-bottom:-1px;transition:color .13s var(--ease);white-space:nowrap;flex:none}
.ftab:hover{color:var(--ink-2)}
.ftab[aria-selected="true"]{color:var(--ink);font-weight:600;border-bottom-color:var(--ink)}
.ftab .c{font-family:var(--mono);font-size:11px;color:var(--ink-3);margin-left:5px}
/* 四栏：字形 / 是什么 / 一句话 / 对照表。最后一栏此前写死 190px，右边一整列
   值全被压成三四行，卡片被撑高，左边两栏就空一大块 —— 所以它跟着可用宽度走，
   和中间那栏一起分。align-items:start 让四栏各自从顶上开始，不去拉齐底边。 */
.spec{display:grid;grid-template-columns:34px minmax(0,1.05fr) minmax(0,1.35fr) minmax(0,1.15fr);
  gap:20px;padding:17px 4px;align-items:start;border-top:1px solid var(--line)}
.spec[data-plan="1"]{opacity:.66}
/* 窄下来先并成三栏（对照表整条挪到第二行），再并成两栏。两栏时说明和对照表都
   得回到第二列，否则说明会掉到字形底下那一格去。 */
@media(max-width:1140px){.spec{grid-template-columns:34px minmax(0,1fr) minmax(0,1fr);gap:16px}
  .spec .sp-io{grid-column:2/-1}}
@media(max-width:900px){.spec{grid-template-columns:32px minmax(0,1fr);gap:14px}
  .spec .sp-h,.spec .sp-io{grid-column:2}}
.sp-ic{width:34px;height:34px;border-radius:var(--r-md);border:1px solid var(--line);
  background:var(--surface-2);color:var(--ink-2);display:grid;place-items:center;margin-top:1px}
.sp-ic svg{width:17px;height:17px}
.spec[data-plan="1"] .sp-ic{border-style:dashed}
.sp-t{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13.5px;font-weight:600;
  letter-spacing:-.012em;line-height:1.35}
.sp-id{font-family:var(--mono);font-size:11px;color:var(--ink-3);margin-top:4px}
.sp-h{font-size:12.5px;color:var(--ink-2);line-height:1.6;max-width:58ch}
.sp-tags{display:flex;gap:5px;flex-wrap:wrap;margin-top:9px}
.sp-tags:empty{display:none}
/* 一张卡上可能有两个动作（打开那个部署 / 把它画完的原型转成 Figma），所以这
   一行会换行，而不是把第二个按钮挤出卡片。只有一个按钮时表现和以前一样。 */
.sp-cta{margin-top:11px;display:flex;gap:8px;flex-wrap:wrap}
/* OpenDesign 的原型清单：点开才存在的一段，整条横跨卡片四栏。一行一个原型，
   不是一块面板 —— 这件事从头到尾只有「挑一个」这一个选择。 */
.od-list{grid-column:1/-1;margin-top:4px;border-top:1px dashed var(--line)}
.od-row{display:flex;align-items:center;gap:10px;padding:9px 0;font-size:12.5px;
  border-bottom:1px solid var(--line)}
.od-row:last-child{border-bottom:0}
.od-row .n{font-weight:600;min-width:0;overflow:hidden;white-space:nowrap;
  text-overflow:ellipsis}
.od-row .t{margin-left:auto;flex:none}
/* 标签与值平铺成两列，所以每一行的值都从同一条竖线起。标签列按语言给宽度：
   中文最长是「生成方式」，英文最长是 Directory —— 都是量出来的，写窄一格就会
   压到值上面。 */
.sp-io{display:grid;grid-template-columns:52px minmax(0,1fr);gap:6px 12px;
  font-size:11.5px;line-height:1.55;align-content:start}
html:lang(en) .sp-io{grid-template-columns:66px minmax(0,1fr)}
.sp-io b{color:var(--ink-3);font-weight:600;white-space:nowrap}
.sp-io span{color:var(--ink-2);min-width:0;overflow-wrap:anywhere}
.sp-io .mono{font-size:11px}
/* 「路径矩阵」（.mtx*）随「路径」页一并移除 —— 2026-07-29。
   能力卡上那块底纹（.fxp 及 glow / grain / glass / motion / os17 / brd 六个
   变体）随之移除 —— 2026-08-26。它是「以后给每档能力画一块材质」的占位，实际
   六张卡一直共用同一块 .brd 网格，等于每档能力都没有图标；现在卡片印的是
   10-icons.js 里按 kind 取的字形，这套底纹再没有人用。 */

/* ══════════════════════════ 悬浮 ══════════════════════════ */
.dock{position:fixed;right:18px;bottom:18px;z-index:60;display:grid;gap:8px;justify-items:end;max-width:314px}
.dock-c{border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);box-shadow:var(--pop);
  padding:12px 13px;font-size:11.5px;color:var(--ink-2);line-height:1.6}
.dock-c b{display:block;color:var(--ink);font-size:12px;margin-bottom:3px;font-weight:600}
.dock-c code{font-family:var(--mono);font-size:11px;background:var(--surface-2);border:1px solid var(--line);
  padding:0 4px;border-radius:3px}
.toast{position:fixed;left:50%;bottom:22px;transform:translate(-50%,64px);z-index:80;display:flex;
  align-items:center;gap:8px;padding:9px 15px;border-radius:var(--r);background:var(--solid);
  color:var(--solid-ink);font-size:12.5px;font-weight:550;box-shadow:var(--pop);opacity:0;pointer-events:none;
  transition:transform .26s var(--ease),opacity .26s var(--ease)}
.toast[data-on="1"]{transform:translate(-50%,0);opacity:1}
.toast svg{width:14px;height:14px}.toast[data-kind="err"] svg{color:#f5a524}
@media(max-width:640px){.dock{left:14px;right:14px;max-width:none}}
  /* servicev2 追加：原型里没有的表单元素 + 运行页 iframe 容器 */
  textarea,input[type=text],input[type=url]{
    width:100%;padding:9px 11px;border:1px solid var(--line-2);border-radius:var(--r);
    background:var(--surface);font-size:13px;outline:none;color:var(--ink);
    font-family:var(--mono);line-height:1.6;resize:vertical;
  }
  textarea:focus,input[type=text]:focus,input[type=url]:focus{
    border-color:var(--accent);box-shadow:0 0 0 3px var(--ring);
  }
  .req-b{white-space:pre-wrap}
  a.btn{text-decoration:none}

  /* .step 自己声明了 display:grid，会盖掉 [hidden] 的 display:none */
  .step[hidden]{display:none}

  /* 高级折叠区：默认这一整块是收起的，简单流程一眼到底 */
  .adv>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:8px;
    font-size:12.5px;font-weight:600;color:var(--ink-2);padding:2px 0}
  .adv>summary:hover{color:var(--ink)}
  .adv>summary::-webkit-details-marker{display:none}
  .adv>summary::before{content:"";width:0;height:0;flex:none;
    border:4.5px solid transparent;border-left-color:currentColor;transition:transform .15s var(--ease)}
  .adv[open]>summary::before{transform:rotate(90deg) translate(1px,1px)}
  .adv-body{margin-top:18px;display:grid;gap:20px}
  .adv-h{display:flex;align-items:center;gap:8px;margin-bottom:9px}

  /* 内容嗅探结果 */
  .snf{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11.5px;color:var(--ink-3)}
  .snf b{color:var(--ink-2);font-weight:600}

  /* 名字面板：这个服务对「你是谁」的全部理解就是一个名字 */
  .top-r{position:relative}
  .pop{position:absolute;top:36px;right:0;z-index:70;width:268px;padding:12px;
    border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);
    box-shadow:var(--pop);display:grid;gap:9px}
  .pop[hidden]{display:none}
  .pop .row{display:flex;gap:6px}
  .pop .row input{flex:1;font-family:var(--sans)}
  .pop .nl{display:grid;gap:2px;max-height:212px;overflow:auto}
  .pop .nm{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:var(--r);
    font-size:13px;color:var(--ink-2);width:100%}
  .pop .nm:hover{background:var(--surface-2);color:var(--ink)}
  .pop .nm[aria-pressed="true"]{background:var(--accent-soft);color:var(--accent-ink);font-weight:600}
  .pop .nm svg{width:14px;height:14px;flex:none;color:var(--ink-3)}

  /* 运行表：名字 + 能力 + 状态 + 产物/操作 + 时间。id 不配一整列。 */
  /* 运行 | 用哪套 | 状态 | 产物 | 操作 | 时间 —— 产物和操作各占一格，不混在一起
     （用户：「产物和操作不能放在一起」）。 */
  .tbl-h,.tbl-r{grid-template-columns:minmax(176px,2fr) minmax(0,1fr) 74px
    minmax(0,190px) 70px 100px}
  /* 「用哪套」这一格必须单行：换行会把整行撑高，一列里高矮不齐比省几个字更糟。 */
  .tbl-sk{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
  /* 状态一列要读成一条竖带：三种胶囊字数不同（已完成 / 失败 / 运行中，后者还多一个
     圆点），不给同一个宽度就会左对齐成锯齿。 */
  .tbl-r .pill{min-width:60px;justify-content:center}
  /* 整行可点（外层 div 带 data-act），格子里的链接和按钮各管自己 —— 事件派发
     取最内层的 data-act，`<a href>` 在派发之前就被放行。 */
  .tbl-r{cursor:pointer}
  .tbl-n{text-align:left}
  .project-name{min-width:0;flex:1;text-align:left}
  .project-toggle{width:25px;height:32px;flex:none;color:var(--ink-2);font-size:17px;border-radius:5px}
  .project-toggle:hover{background:var(--accent-soft)}
  .tbl-history{background:var(--surface-2)}
  .tbl-history .tbl-n{padding-left:28px}
  /* 产物：文字链接（去看东西）。操作：描边按钮（对这次运行动手）。两种形状不同，
     扫一眼就分得开。 */
  .rowarts{display:flex;gap:11px;align-items:center;min-width:0;overflow:hidden}
  .rlink{font-size:11.5px;color:var(--ink-2);white-space:nowrap;
    text-decoration:underline;text-decoration-color:var(--line-2);text-underline-offset:2.5px;
    transition:color .12s var(--ease),text-decoration-color .12s var(--ease)}
  .rlink:hover{color:var(--accent);text-decoration-color:var(--accent-line)}
  /* 产物缩略图：只给「本来就是图片」的交付物用（图标 SVG、图像生成的预览图）——
     可玩原型/海报/对照页/回灌产物全是活的 HTML，服务端从没为它们存过一张现成的
     静态截图，硬塞一张就得现搭一条截图流水线，那不是这一处该做的事，所以那些
     依旧是纯文字链接，原样不动。尺寸照抄 .glyph（运行名前面那颗方块）：这张表
     已经在用「小方块 + 文字」，缩略图跟着用，不是另起一套。
     宽高写死在 .rthumb 这层容器上，图片本身铺满它——不管图到没到、多久到，
     格子从第一帧起就是这个尺寸，不会因为一张图迟到而让整行跳一下。加载失败就
     整个摘掉 <img>，容器的底色留着当占位，不会露出浏览器那个破图角标。
     没有加任何入场过渡：app.css 顶上那条全局 reduced-motion 规则把 transition
     一并 !important 关掉，图能到位是靠「加载完成就是这个样子」这件事本身，
     不是靠一段可能被关掉的动画收尾——省了过渡也就没有「关了动画等于关了图」
     这条坑。 */
  .rlink-img{display:inline-flex;align-items:center;gap:6px}
  .rthumb{width:26px;height:26px;flex:none;border-radius:var(--r);overflow:hidden;
    background:var(--surface-2);border:1px solid var(--line)}
  .rthumb img{display:block;width:100%;height:100%;object-fit:contain}
  .rowacts{display:flex;align-items:center}
  .ract{font-size:11.5px;line-height:1;padding:5px 8px;border-radius:var(--r);
    border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
    white-space:nowrap;transition:border-color .12s var(--ease),color .12s var(--ease)}
  .ract:hover{border-color:var(--line-2);color:var(--ink);background:var(--surface-2)}
  .ract.stop{color:var(--brick);border-color:var(--brick-line)}
  .ract.stop:hover{color:var(--brick);background:var(--brick-soft);border-color:var(--brick)}

  /* 预览：缩放 + 稳定的 iframe（不随轮询重建，否则每次刷新都闪一下） */
  .pvw{border:1px solid var(--line);border-top:0;border-radius:0 0 var(--r-md) var(--r-md);
    overflow:auto;background:var(--surface);resize:vertical;height:560px;min-height:220px}
  .pvw-in{transform-origin:0 0;border:0;display:block;background:var(--surface)}
  .zoom{display:flex;align-items:center;gap:6px;margin-left:auto}
  .zoom .lv{font-family:var(--mono);font-size:11.5px;color:var(--ink-3);min-width:38px;text-align:center}

  /* 上传：拖进来 / 选文件 / 已选文件卡片 */
  .upl{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:9px}
  #drop{position:relative}
  #drop[data-over="1"]::after{content:"释放以上传";position:absolute;inset:14px 0 14px 22px;
    display:flex;align-items:center;justify-content:center;border:2px dashed var(--accent);
    border-radius:var(--r-md);background:var(--accent-soft);color:var(--accent-ink);
    font-size:13px;font-weight:600;pointer-events:none}
  .fcard{display:flex;align-items:center;gap:11px;padding:12px 13px;border:1px solid var(--accent-line);
    background:var(--accent-soft);border-radius:var(--r-md)}
  .fcard svg{width:16px;height:16px;flex:none;color:var(--accent)}
  .fcard .nm{font-size:13px;font-weight:600;word-break:break-all}
  .fcard .mt{font-size:11.5px;color:var(--ink-2);margin-top:2px}
  .fcard .x{margin-left:auto;flex:none}

  /* 导出：一件产物一行，动作就在旁边，不用去别处找 */
  .art{padding:10px 0;border-top:1px solid var(--line)}
  .art:first-child{border-top:0;padding-top:2px}
  .art-h{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-bottom:7px}
  .art-h b{font-size:12.5px;font-weight:640}
  .art-a{display:flex;gap:6px;flex-wrap:wrap}
  .art-a .btn{height:26px;padding:0 9px;font-size:12px}
  /* 字标：中文是主名，英文只是小尾巴，不跟中文抢分量 */
  .mark-en{font-family:var(--mono);font-size:9.5px;font-weight:600;letter-spacing:.13em;
    text-transform:uppercase;color:var(--ink-3);padding-top:1px}
  /* 小尾巴是 AI NATIVE（2026-10-13 起主名 CoFlow 中英一样），英文界面也显示。 */

  /* 进场屏：没有名字之前先问一次「你是谁」。
     一屏只干一件事 —— 点一个已有的名字，或写一个新的。跳过也行，但只问这一次。 */
  .gate{position:fixed;inset:0;z-index:78;display:grid;place-items:center;
    padding:32px 24px;overflow:auto;background:var(--canvas);
    transition:opacity .22s var(--ease)}
  .gate[hidden]{display:none}
  /* 名字选完之后淡出，主界面从底下淡进来 —— 从「你是谁」到主界面是一次转场 */
  .gate[data-out="1"]{opacity:0;pointer-events:none}
  /* 已登记的名称要等服务端。先占住这一行的高度，否则它到了就把整张卡片往上顶。 */
  .gate-wait{display:grid;gap:8px;justify-items:center}
  .gate-c{width:100%;max-width:412px;display:grid;gap:16px;justify-items:center;text-align:center}
  .gate .mark{padding:0;gap:10px}
  .gate .mark-sq{width:29px;height:29px;border-radius:7px}
  .gate .mark-sq svg{width:16px;height:16px}
  .gate .mark b{font-size:19px;letter-spacing:-.02em}
  .gate .mark-en{font-size:10.5px}
  .gate .lede{text-align:center}
  .gate-row{display:flex;gap:7px;width:100%;margin-top:2px}
  .gate-row input{flex:1;text-align:center;font-family:var(--sans);font-size:13.5px}
  .gate-names{display:flex;flex-wrap:wrap;gap:6px;justify-content:center}
  .gate-nm{display:inline-flex;align-items:center;gap:6px;padding:5px 11px;border-radius:99px;
    border:1px solid var(--line-2);background:var(--surface);font-size:12.5px;color:var(--ink-2);
    transition:.13s var(--ease)}
  .gate-nm:hover{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink)}
  .gate-nm svg{width:13px;height:13px;flex:none;opacity:.66}
  .gate-nm:hover svg{opacity:1}
  .gate-skip{font-size:11.5px;color:var(--ink-3);padding:4px 8px;border-radius:var(--r)}
  .gate-skip:hover{color:var(--ink-2);background:var(--surface-2)}

  /* 访问码屏。借进场屏的整套样式（对使用者来说这两屏是同一种东西：进门前的
     一句问话），只多两件事 —— 盖在它上面，以及一行错误提示。 */
  .lock{z-index:79}
  .lock-err{font-size:12.5px;color:var(--brick);margin:-6px 0 0}

  /* 屏级跳转：整板缩略图里找不到的东西，用名字点 */
  .screens{display:flex;flex-wrap:wrap;gap:5px}
  .scr-j{display:inline-flex;align-items:center;gap:5px;padding:3px 8px;
    border:1px solid var(--line-2);border-radius:6px;background:var(--surface);
    font-size:12px;color:var(--ink-2);text-decoration:none;transition:.13s var(--ease)}
  .scr-j:hover{border-color:var(--accent);color:var(--accent-ink);background:var(--accent-soft)}
  .scr-j .go{font-size:10px;opacity:.6}
  .scr-j.off{opacity:.55;border-style:dashed;cursor:default}
  .scr-j.off:hover{border-color:var(--line-2);color:var(--ink-2);background:var(--surface)}

  /* 屏级留言（见 95g-notes.js）：一屏一行，行尾一个留言数，点开才有那段对话。
     没有面板、没有页签 —— 这件事本身只值一行，展开的时候才值更多。 */
  .scr-list{display:grid;gap:1px}
  .scr-r{display:flex;align-items:center;gap:3px}
  .scr-b{flex:1;min-width:0;display:flex;align-items:center;gap:8px;padding:5px 7px;
    border:1px solid transparent;border-radius:var(--r);background:none;text-align:left;
    font-size:12px;color:var(--ink-2);transition:.13s var(--ease)}
  .scr-b:hover,.scr-b[aria-expanded="true"]{border-color:var(--line-2);background:var(--surface-2)}
  .scr-b .mono{flex:none}
  /* 屏名是主的，标题是补充说明：一行放不下时省略的是标题，不是名字 */
  .scr-b .ti{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
    color:var(--ink-3)}
  .scr-n{flex:none;display:inline-flex;align-items:center;gap:4px;font-size:11px;
    font-family:var(--mono);color:var(--ink-3)}
  .scr-n svg{width:13px;height:13px;opacity:.55}
  .scr-b:hover .scr-n svg{opacity:.9}
  /* 有人说过话、而且还没处理完 —— 这一行值得先看 */
  .scr-n.has{color:var(--accent-ink)}.scr-n.has svg{opacity:1}
  .scr-th{margin:1px 0 7px;padding:9px 10px;border:1px solid var(--line-2);
    border-radius:var(--r-md);background:var(--surface-2);display:grid;gap:8px}
  .scr-th textarea{font-family:var(--sans);font-size:12.5px;min-height:54px;padding:7px 9px}
  .scr-nt{display:grid;gap:3px}
  .scr-nt .tls{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
  .scr-nt .tls b{color:var(--ink-2)}
  /* 标为已处理的留言淡下去，但一个字都不少 —— 「已处理」是标记，不是删除 */
  .scr-nt.done{opacity:.55}
  .scr-nt p{margin:0;font-size:12.5px;color:var(--ink);line-height:1.55;
    white-space:pre-wrap;overflow-wrap:anywhere}
  .scr-nt .chip{height:22px;padding:0 8px;font-size:11.5px}
  .scr-send{display:flex;align-items:center;gap:9px;justify-content:space-between;flex-wrap:wrap}

  /* OS 17 Icon Generator：真实库证据 → 语义请求 → 候选与小尺寸检查。 */
  .ig-layout{display:grid;grid-template-columns:minmax(300px,.82fr) minmax(480px,1.35fr);gap:18px;align-items:start}
  .ig-panel{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);overflow:hidden}
  /* ── PRD 资源生成：一个输入框、三行设置、一张清单 ──────────────────── */
  #prd-drop{transition:border-color .16s,box-shadow .16s,background .16s}
  #prd-drop.drag{border-color:var(--accent);box-shadow:0 0 0 3px var(--ring);background:var(--accent-soft)}
  .prd-compose textarea,.prd-style textarea{font-family:var(--sans)}.prd-compose textarea{min-height:128px}
  .prd-compose .ig-compose-actions{align-items:center;flex-wrap:wrap}
  .prd-attach{margin:0;padding:0 12px 10px}.prd-attach:empty{display:none}
  .prd-attach img{width:20px;height:20px;border-radius:4px;object-fit:cover}
  .chips .prd-attach{display:contents}
  /* 三行共用一列标签：一张 grid，行只是语义分组（display:contents），所以最宽的
     那个标签决定列宽，别的行不会各自折行。 */
  .prd-rows{display:grid;grid-template-columns:minmax(72px,auto) minmax(0,1fr);gap:10px 14px;align-items:center;margin-top:14px}
  .prd-row{display:contents}
  .prd-k{font-size:11.5px;font-weight:600;color:var(--ink-2);white-space:nowrap}.prd-k .tls{font-weight:400}
  .prd-out{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.prd-out .seg{margin-right:8px}
  .prd-counts{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
  .prd-counts label{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;color:var(--ink-2)}
  .prd-counts input{width:54px;height:27px;padding:0 8px;border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);color:var(--ink);font:500 12px var(--mono)}
  .prd-style{display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:start;margin-bottom:14px}
  .prd-style label{font-size:11.5px;font-weight:600;color:var(--ink-2);padding-top:9px;white-space:nowrap}.prd-style label .tls{font-weight:400}
  .prd-style textarea{font-size:12px;line-height:1.5;field-sizing:content;min-height:3lh;max-height:9lh}  /* 那一句六七十个词，两行框里看不全 */
  .prd-group{display:grid;gap:6px}.prd-group+.prd-group{margin-top:14px}
  .prd-group-h{display:flex;align-items:baseline;gap:6px;font-size:11.5px;font-weight:600;color:var(--ink-2)}
  .prd-item{display:grid;grid-template-columns:16px 40px minmax(0,1fr) auto;gap:10px;align-items:center;width:100%;text-align:left;padding:8px 10px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);color:var(--ink);cursor:pointer;transition:.13s var(--ease)}
  .prd-item:hover{border-color:var(--line-2);box-shadow:var(--lift)}
  .prd-item[aria-pressed="false"]{opacity:.5}
  .prd-tick{width:16px;height:16px;border:1px solid var(--line-2);border-radius:4px;display:grid;place-items:center;font-size:10px;font-weight:800;color:#fff;background:var(--surface)}
  .prd-item[aria-pressed="true"] .prd-tick{background:var(--accent);border-color:var(--accent)}
  .prd-kind{display:inline-flex;align-items:center;justify-content:center;width:40px;height:22px;border-radius:6px;background:var(--surface-3);color:var(--ink-3);font:700 10px/1 var(--mono);letter-spacing:.04em}
  .prd-kind.icon{background:var(--accent-soft);color:var(--accent-ink)}
  .prd-kind.image{background:var(--ochre-soft);color:var(--ochre)}
  .prd-item-b{min-width:0;display:grid;gap:1px}.prd-item-b b{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.prd-item-b .tls{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .prd-fmt{display:flex;align-items:center;gap:8px;color:var(--ink-3);font:500 10.5px/1 var(--mono);white-space:nowrap}
  .prd-fmt img{width:26px;height:26px;padding:3px;border-radius:6px;background:var(--surface-2);object-fit:contain}
  .prd-overall{display:grid;gap:8px;padding:12px 14px;margin-bottom:10px;border-radius:var(--r-md);background:var(--surface-2)}.prd-overall>div{display:flex;justify-content:space-between;font-size:12.5px}
  .prd-bar{height:4px;overflow:hidden;border-radius:99px;background:var(--surface-3)}.prd-bar i{display:block;height:100%;border-radius:inherit;background:var(--accent);transition:width .3s var(--ease)}
  .prd-jobs{display:grid;gap:6px}
  .prd-job{display:grid;gap:10px;padding:8px 10px;border:1px solid var(--line);border-radius:var(--r-md)}
  .prd-job-body{display:grid;grid-template-columns:40px minmax(0,1fr) auto;align-items:center;gap:10px}
  .prd-job-side{display:grid;gap:6px;justify-items:end}
  .prd-job-refine:empty{display:none}
  .prd-refine .ig-compose-bar{flex-wrap:wrap}
  .prd-refine-base{display:inline-flex;align-items:center;gap:7px;min-width:0;font-size:11.5px;color:var(--ink-3)}.prd-refine-base img{width:26px;height:26px;padding:2px;border:1px solid var(--line);border-radius:5px;background:var(--surface);object-fit:contain}
  .prd-job-main{min-width:0;display:grid;gap:5px}.prd-job-main b{font-size:12.5px}
  .prd-job-main .tls{display:flex;gap:8px;min-width:0}.prd-job-main .tls span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .prd-output-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));gap:8px;margin-top:4px}
  .prd-tile{position:relative;overflow:hidden;aspect-ratio:1;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);cursor:pointer;transition:.13s var(--ease)}
  .prd-tile img{width:100%;height:100%;display:block;object-fit:contain;transition:opacity .13s}
  .prd-tile[aria-pressed="true"]{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent-line)}
  .prd-tile[aria-pressed="false"] img{opacity:.4}
  .prd-tile:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
  .prd-tile-tick{position:absolute;left:6px;top:6px;width:18px;height:18px;display:grid;place-items:center;border:1px solid var(--line-2);border-radius:5px;background:var(--surface);color:transparent;font-size:11px;font-weight:800}
  .prd-tile[aria-pressed="true"] .prd-tile-tick{background:var(--accent);border-color:var(--accent);color:#fff}
  .prd-tile-acts{position:absolute;right:6px;bottom:6px;display:flex;gap:4px;opacity:0;transition:opacity .13s}
  .prd-tile:hover .prd-tile-acts,.prd-tile:focus-within .prd-tile-acts{opacity:1}
  .prd-tile-act{width:22px;height:22px;display:grid;place-items:center;border:0;border-radius:5px;background:rgba(0,0,0,.6);color:#fff;font-size:11px;font-weight:600;text-decoration:none;cursor:pointer}
  .prd-tile-act:hover{background:rgba(0,0,0,.82)}
  @media(max-width:620px){.prd-rows,.prd-style{grid-template-columns:1fr;gap:6px}.prd-rows>.prd-row+.prd-row>.prd-k{margin-top:6px}.prd-fmt{display:none}}
  .ig-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:15px 16px;border-bottom:1px solid var(--line)}
  .ig-body{padding:16px}.ig-grid{display:grid;grid-template-columns:1fr 1fr;gap:11px}
  .ig-field{display:grid;gap:6px}.ig-field.full{grid-column:1/-1}.ig-field label{font-size:11.5px;font-weight:600;color:var(--ink-2)}
  .ig-field select{height:36px;border:1px solid var(--line-2);border-radius:var(--r);padding:0 10px;background:var(--surface);outline:none}
  .ig-field select:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
  .ig-analysis{display:grid;gap:10px;padding:12px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface-2)}
  .ig-analysis-top{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
  .ig-analysis-top .sep{color:var(--ink-4)}
  .ig-analysis-row{display:grid;grid-template-columns:72px minmax(0,1fr);gap:10px;
    align-items:start;font-size:11.5px;line-height:1.55}
  .ig-analysis-row .k{color:var(--ink-3)}
  .ig-analysis-row .v{color:var(--ink-2);min-width:0;overflow-wrap:anywhere}
  .ig-analysis-row .clamp{display:-webkit-box;-webkit-box-orient:vertical;
    -webkit-line-clamp:2;overflow:hidden}
  .ig-analysis-row .clamp3{display:-webkit-box;-webkit-box-orient:vertical;
    -webkit-line-clamp:3;overflow:hidden}
  .ig-evidence-analysis{display:grid;gap:8px;margin-bottom:14px;padding-bottom:14px;
    border-bottom:1px solid var(--line)}
  .ig-prompt{min-height:112px;font-family:var(--sans)}
  .ig-actions{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:14px}
  .ig-refbar{display:flex;gap:12px;align-items:flex-start;justify-content:space-between;margin-bottom:12px}.ig-refbar .h3{margin-bottom:3px}.ig-refbar input{font-family:var(--sans)}
  .ig-refs{display:grid;grid-template-columns:repeat(6,minmax(58px,1fr));gap:8px}
  .ig-ref{border:1px solid var(--line);border-radius:var(--r-md);padding:9px 5px 6px;min-width:0;text-align:center;background:var(--surface);transition:.13s var(--ease)}
  .ig-ref:hover{border-color:var(--line-2);background:var(--surface-2)}.ig-ref[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 1px var(--accent-line)}
  .ig-ref img{display:block;width:28px;height:28px;object-fit:contain;margin:0 auto 6px;filter:var(--ig-filter,none)}
  :root[data-theme="dark"] .ig-ref img{--ig-filter:invert(1)}
  .ig-ref span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10px;color:var(--ink-3)}
  .ig-dna{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--line);margin-top:14px}
  .ig-stat{padding:11px 12px;border-right:1px solid var(--line)}.ig-stat:last-child{border-right:0}.ig-stat b{display:block;font-family:var(--mono);font-size:13px}.ig-stat span{font-size:10.5px;color:var(--ink-3)}
  .ig-results{margin-top:18px}.ig-cards{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
  .ig-card{border:1px solid var(--line);border-radius:var(--r-lg);padding:10px;background:var(--surface);position:relative;transition:.13s var(--ease)}
  .ig-card:hover{transform:translateY(-1px);box-shadow:var(--lift)}.ig-card[aria-selected="true"]{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent-line)}
  .ig-rank{font-family:var(--mono);font-size:10px;color:var(--ink-3);display:flex;justify-content:space-between;gap:4px}.ig-rank b{color:var(--accent-ink)}
  .ig-hero{height:116px;margin:9px 0;border-radius:var(--r-md);background:var(--surface-2);display:grid;place-items:center}.ig-hero img{width:64px;height:64px;object-fit:contain;filter:var(--ig-filter,none)}
  :root[data-theme="dark"] .ig-hero img{--ig-filter:invert(1)}
  .ig-card h3{font-size:11.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ig-score{font-size:11px;line-height:1.45;color:var(--ink-3);margin-top:3px;overflow-wrap:anywhere;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}
  .ig-score .k{color:var(--ink-2);font-weight:500}
  .ig-preview{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:14px}.ig-stage{border:1px solid var(--line);border-radius:var(--r-md);padding:12px;background:#fff;color:#111}.ig-stage.dark{background:#111;color:#fff;border-color:#2b2b2b}
  .ig-sizes{display:flex;align-items:end;justify-content:center;gap:22px;height:70px}.ig-size{display:grid;gap:5px;justify-items:center;font-family:var(--mono);font-size:9px;color:inherit;opacity:.92}.ig-size img{object-fit:contain}.ig-stage.dark img{filter:invert(1)}
  .ig-empty{padding:48px 20px;text-align:center;color:var(--ink-3);border:1px dashed var(--line-2);border-radius:var(--r-lg);background:var(--surface-2)}
  .ig-loading{padding:24px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
  .ig-loading-head{display:flex;align-items:center;gap:11px;margin-bottom:18px}.ig-loading-head p{margin-top:3px;color:var(--ink-3);font-size:12px}
  .ig-elapsed{margin-left:auto;align-self:flex-start;white-space:nowrap;font-family:var(--mono);font-size:11px;color:var(--ink-2);background:var(--surface-2);border:1px solid var(--line);border-radius:99px;padding:5px 8px}
  .ig-vhead-r{display:flex;align-items:center;justify-content:flex-end;gap:10px;flex-wrap:wrap}
  .ig-main-grid{display:block}
  .ig-main-content{min-width:0}.ig-main-content>.ig-results{margin-top:18px}
  .ig-evidence-col{display:grid;gap:12px;min-width:0}
  .ig-timeline{width:100%;padding:14px 15px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
  .ig-timeline-top{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:13px;font-size:10.5px;color:var(--ink-3)}
  .ig-timeline-top b{color:var(--ink-2);font-weight:600}.ig-timeline-track{display:grid;gap:0;margin-left:3px}
  .ig-job-link{display:inline-flex;align-items:center;gap:5px;color:var(--accent-ink);font-family:var(--mono);text-decoration:none}.ig-job-link:hover{text-decoration:underline}
  .ig-time-step{position:relative;min-height:35px;padding:0 0 10px 27px;color:var(--ink-4)}
  .ig-time-step:not(:last-child):before{content:"";position:absolute;left:5px;top:12px;bottom:-1px;width:1px;background:var(--line-2)}
  .ig-time-step:after{content:"";position:absolute;left:0;top:2px;width:9px;height:9px;border:2px solid var(--line-2);border-radius:50%;background:var(--surface)}
  .ig-time-step b{display:block;font-size:11.5px;font-weight:500}.ig-time-step span{display:block;font-family:var(--mono);font-size:9.5px;margin-top:2px;color:var(--ink-3)}
  .ig-time-step.done{color:var(--ink-2)}.ig-time-step.done:after{border-color:var(--accent);background:var(--accent)}
  .ig-time-step.done:not(:last-child):before{background:var(--accent-line)}
  .ig-time-step.active{color:var(--ink)}.ig-time-step.active b{font-weight:700}.ig-time-step.active:after{border-color:var(--accent);background:var(--surface);box-shadow:inset 0 0 0 2px var(--surface),0 0 0 1px var(--accent-line)}
  .ig-time-step.failed{color:var(--brick)}.ig-time-step.failed b{font-weight:700}.ig-time-step.failed:after{border-color:var(--brick);background:var(--brick)}
  .ig-timeline-meta{display:grid;gap:0;margin-top:4px;border-top:1px solid var(--line)}
  .ig-timeline-row{display:flex;justify-content:space-between;gap:14px;padding:9px 0;border-bottom:1px solid var(--line);font-size:11px;color:var(--ink-3)}
  .ig-timeline-row b{color:var(--ink);font-weight:600}.ig-timeline-now{padding-top:10px;font-family:var(--mono);font-size:10px;line-height:1.5;color:var(--ink-3)}
  @media(max-width:900px){.ig-vhead-r{justify-content:flex-start}}
  .ig-spinner{width:28px;height:28px;flex:0 0 28px;border:2px solid var(--line-2);border-top-color:var(--accent);border-radius:50%;animation:igspin .9s linear infinite}
  .ig-loading-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.ig-loading-card{height:154px;border:1px solid var(--line);border-radius:var(--r-lg);padding:10px;background:var(--surface-2)}
  .ig-loading-card .sk:first-child{height:104px}.ig-loading-card .sk:last-child{height:8px;width:58%;margin-top:12px}@keyframes igspin{to{transform:rotate(360deg)}}
  .ig-sketch{display:grid;grid-template-columns:160px minmax(0,1fr);gap:16px;align-items:center;margin-bottom:14px;padding:12px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
  .ig-sketch img{display:block;width:160px;height:160px;object-fit:contain;border:1px solid var(--line);border-radius:12px;background:#fff}
  .ig-sketch-copy{display:grid;gap:6px}.ig-sketch-copy p{margin:0;color:var(--ink-3);font-size:12px;line-height:1.6}
  @media(max-width:620px){.ig-sketch{grid-template-columns:1fr}.ig-sketch img{width:100%;height:auto;aspect-ratio:1}}
  .ig-compose{border:1px solid var(--line-2);border-radius:var(--r-lg);background:var(--surface);overflow:hidden;transition:.15s var(--ease)}
  .ig-compose:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}.ig-compose textarea{width:100%;min-height:72px;padding:12px 13px;border:0!important;border-radius:0!important;box-shadow:none!important;resize:vertical;background:transparent}
  .ig-compose-bar{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px;border-top:1px solid var(--line);background:var(--surface-2)}.ig-compose-actions{display:flex;gap:6px}
  .ig-chat{display:flex;gap:7px;flex-wrap:wrap;margin-bottom:9px}.ig-msg{display:flex;align-items:center;gap:7px;max-width:100%;padding:6px 8px;border:1px solid var(--line);border-radius:99px;background:var(--surface-2);font-size:11px}.ig-msg b{color:var(--accent-ink)}.ig-msg span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:260px}.ig-msg .x{width:18px;height:18px}
  .ig-compare{margin-top:14px;border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden}.ig-compare-head{display:grid;grid-template-columns:1fr auto 18px;gap:10px;align-items:center;padding:10px 12px;background:var(--surface-2);cursor:pointer;list-style:none}.ig-compare-title{display:flex;align-items:baseline;gap:8px;min-width:0}.ig-compare-title .micro{white-space:nowrap}.ig-compare-head::-webkit-details-marker{display:none}.ig-compare-head::after{content:'›';font-size:18px;color:var(--ink-3);transform:rotate(90deg);transition:.15s var(--ease)}.ig-compare:not([open]) .ig-compare-head::after{transform:rotate(0)}.ig-compare[open] .ig-compare-head{border-bottom:1px solid var(--line)}
  .ig-compare-grid{display:grid;grid-template-columns:1fr 3fr}.ig-compare-side{padding:14px;border-right:1px solid var(--line)}.ig-compare-side:last-child{border-right:0}.ig-compare-items{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}.ig-compare-item{min-width:0;text-align:center}.ig-compare-box{height:96px;border:1px solid var(--line);border-radius:var(--r-md);display:grid;place-items:center;background:#fff}.ig-compare-box img{width:64px;height:64px;object-fit:contain}.ig-compare-item span{display:block;margin-top:6px;font-size:10px;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* 调优对话 = 一张图的版本轨道。一轮一行：左侧脊线与点沿用右边进度面板的
     语言（.ig-time-step），中间是那一轮真正产出的 SVG，右边是那一句要求。
     每行不再是一张卡 —— 三张一模一样的描边卡片只会把节奏切碎，而这里真正要
     被看见的是图本身。整行可点，点了回到那一版；当前那一轮只用底色和一个
     「当前」小标记标出，不再套一圈粗高亮。 */
  .ig-thread{display:grid;margin:2px 0 20px}
  /* 脊线和点画在左边的内边距里，不占格子 —— 行本身只有「图」和「话」两列。 */
  .ig-turn{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;
    align-items:center;padding:13px 14px 15px 26px;border-radius:var(--r-md);transition:.13s var(--ease)}
  .ig-turn:not(:last-child):before{content:"";position:absolute;left:6px;top:36px;bottom:-2px;width:1px;background:var(--line-2)}
  .ig-turn:after{content:"";position:absolute;left:2px;top:21px;width:9px;height:9px;
    border:2px solid var(--line-2);border-radius:50%;background:var(--surface)}
  /* 导航那一半：占满一行的宽度，所以视觉上仍然是「整行可点」 */
  .ig-turn-open{display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:start;
    width:100%;padding:0;border:0;background:transparent;text-align:left;font:inherit;color:inherit;border-radius:var(--r-md)}
  button.ig-turn-open{cursor:pointer}
  button.ig-turn-open:hover .ig-turn-plate{border-color:var(--accent)}
  .ig-turn:has(button.ig-turn-open:hover){background:var(--surface-2)}
  .ig-turn-cmp{flex:none;padding:4px 10px;border:1px solid var(--line-2);border-radius:99px;
    background:var(--surface);color:var(--ink-3);font-size:10.5px;cursor:pointer;transition:.13s var(--ease)}
  .ig-turn-cmp:hover{border-color:var(--accent);color:var(--accent-ink)}
  .ig-turn-cmp[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-line);color:var(--accent-ink);font-weight:600}
  .ig-turn[data-now="1"]{background:var(--surface-2);cursor:default}
  .ig-turn[data-now="1"]:after{border-color:var(--accent);background:var(--accent)}
  .ig-turn[data-kind="request"]:after{border-color:var(--ink-4);background:var(--surface)}
  .ig-turn-art{display:flex;gap:7px;flex-wrap:wrap}
  /* 图标是黑色填充：底板固定白色，暗色主题下才看得见（和 .ig-compare-box 同理） */
  .ig-turn-plate{display:grid;place-items:center;width:54px;height:54px;border:1px solid var(--line);
    border-radius:var(--r-md);background:#fff;transition:.13s var(--ease)}
  .ig-turn-plate img{width:32px;height:32px;object-fit:contain}
  .ig-turn-plate.empty{background:var(--surface-2);border-style:dashed}
  .ig-turn-body{display:grid;gap:5px;min-width:0;padding-top:2px}
  .ig-turn-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:10.5px;color:var(--ink-3)}
  .ig-turn-meta b{font-size:10.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-2)}
  .ig-turn-meta time{font-family:var(--mono);font-size:10px}
  .ig-turn-now{padding:1px 6px;border-radius:99px;background:var(--accent-line);color:var(--accent-ink);font-size:9.5px;letter-spacing:.04em}
  .ig-turn-ask{display:block;font-size:13.5px;line-height:1.55;color:var(--ink)}
  .ig-turn-state{display:flex;align-items:center;gap:7px;font-size:11.5px;color:var(--ink-3)}
  .ig-turn-state.bad{color:var(--brick)}
  .ig-turn-state .ig-spinner{width:13px;height:13px;flex:0 0 13px;border-width:2px}
  .ig-turn-refs{display:flex;gap:5px;flex-wrap:wrap;margin-top:1px}
  .ig-turn-refs img{width:24px;height:24px;border:1px solid var(--line);border-radius:5px;object-fit:cover}
  .ig-turn-refs .pill{font-size:9.5px;padding:1px 7px}
  /* 换成手改的 SVG —— 一行，三条路（选文件 / 拖进来 / 粘贴源码） */
  .ig-swap{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:12px;padding:9px 12px;
    border:1px dashed var(--line-2);border-radius:var(--r-md);font-size:11.5px;color:var(--ink-3);
    transition:.13s var(--ease)}
  .ig-swap b{font-weight:600;color:var(--ink-2)}
  .ig-swap[data-over="1"]{border-color:var(--accent);border-style:solid;background:var(--surface-2)}
  .ig-swap .ochre{color:var(--ochre)}
  /* 版本并排。明暗各一块 + 三个小尺寸，和 .ig-compare 一套语言 */
  .ig-vs{margin:4px 0 16px;border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden}
  .ig-vs-head{display:flex;align-items:center;gap:9px;padding:9px 12px;border-bottom:1px solid var(--line);background:var(--surface-2)}
  .ig-vs-head b{flex:1;font-size:12.5px;font-weight:600;color:var(--ink)}
  .ig-vs-items{display:flex;gap:12px;padding:14px 12px;overflow-x:auto}
  .ig-vs-item{flex:0 0 116px;min-width:0;display:grid;gap:6px}
  .ig-vs-box{height:92px;border:1px solid var(--line);border-radius:var(--r-md);display:grid;place-items:center;background:#fff}
  /* 明暗两块和 .ig-stage / .ig-stage.dark 用同一套值（#fff / #111 + invert） */
  .ig-vs-box.dark{height:56px;background:#111;border-color:#2b2b2b}
  .ig-vs-box img{width:64px;height:64px;object-fit:contain}
  .ig-vs-box.dark img{width:36px;height:36px;filter:invert(1)}
  .ig-vs-small{display:flex;align-items:flex-end;justify-content:space-between;gap:6px;padding:7px 8px 5px;
    border:1px solid var(--line);border-radius:var(--r-md);background:#fff}
  .ig-vs-small span{display:grid;justify-items:center;gap:3px}
  .ig-vs-small i{font-family:var(--mono);font-style:normal;font-size:8.5px;color:#71717a}
  .ig-vs-name{font-size:11px;font-weight:600;color:var(--ink-2)}
  .ig-vs-ask{font-size:10.5px;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* 画笔标注：画布固定白底（图标是黑色填充），旁边一列说明和三颗小键 */
  .ig-annot{display:grid;grid-template-columns:260px minmax(0,1fr);gap:14px;align-items:start;
    padding:12px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2)}
  .ig-annot canvas{width:260px;height:260px;border:1px solid var(--line);border-radius:var(--r-md);
    background:#fff;cursor:crosshair;touch-action:none}
  .ig-annot-side{display:grid;gap:9px}
  .ig-annot-tools{display:flex;gap:6px;flex-wrap:wrap}
  @media(max-width:620px){.ig-annot{grid-template-columns:1fr}.ig-annot canvas{width:100%;height:auto;aspect-ratio:1}}
  .ig-refine{display:grid;gap:9px;padding-top:15px;border-top:1px solid var(--line)}
  .ig-refine-base{display:flex;align-items:center;gap:7px;min-width:0;font-size:11px;color:var(--ink-3)}
  .ig-refine-base img{width:22px;height:22px;flex:none;object-fit:contain;padding:2px;border:1px solid var(--line);border-radius:5px;background:#fff}
  .ig-refine-base b{color:var(--ink-2);font-weight:600}
  @media(max-width:620px){.ig-turn{grid-template-columns:minmax(0,1fr);row-gap:10px}}
  @media(max-width:980px){.ig-layout{grid-template-columns:1fr}.ig-refs{grid-template-columns:repeat(8,1fr)}}
  @media(max-width:760px){.ig-refs{grid-template-columns:repeat(4,1fr)}.ig-cards{grid-template-columns:1fr 1fr}.ig-dna{grid-template-columns:1fr 1fr}.ig-stat:nth-child(2){border-right:0}.ig-stat:nth-child(-n+2){border-bottom:1px solid var(--line)}}

/* ══════════════════════════ 资源标注 ══════════════════════════
   圈是网站画的，不是人画的：iframe 里的 asset_probe.js 量出每个图标/图片的真实
   位置，自己把圈画上去，人只负责点选哪些要重做。
   下面这两个颜色必须和 asset_probe.js 里 .ring 的 --c 保持一致 —— 侧栏的圆点
   和页面上的圈是同一套指认关系，颜色对不上这套关系就断了。 */
  :root{--as-icon:#3b6cf6;--as-image:#d2751a}
  .as-bar{display:flex;align-items:center;gap:10px 14px;flex-wrap:wrap;
    justify-content:space-between;padding:10px 13px;
    border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);margin-bottom:14px}
  /* 第一遍自动遍历还没走完：横条与清单只报进度，走完一次性亮出全部结果。 */
  .as-load{display:flex;align-items:center;gap:10px;font-size:12.5px;color:var(--ink-3)}
  .as-load-spin{width:14px;height:14px;flex:0 0 14px;border-radius:99px;
    border:2px solid var(--line-2);border-top-color:var(--accent);
    animation:rdspin .7s linear infinite}
  .as-list-load{display:grid;justify-items:center;gap:12px;padding:44px 22px;
    text-align:center;font-size:12px;color:var(--ink-3);line-height:1.6}
  .as-list-load .as-load-spin{width:22px;height:22px;flex:0 0 22px}
  .as-bar .sp{margin-left:auto}
  /* 折行只发生在左半里。右半（已选 N + 送去重新设计）永远贴右 —— 它掉到第二行
     最左边的时候，一屏里最该突出的那一颗按钮站在了最不起眼的位置。 */
  .as-bar-l{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0;flex:1 1 auto}
  .as-bar-go{margin-left:auto;flex:none}
  /* 四组：筛清单的 / 改怎么看的 / 重新取一份的 / 拿选中的往下走的。组内 5 px，
     组间一条细线 —— 十一个按钮排成一条时没人分得清哪个是要花钱的那一个。 */
  .as-bar-g{display:flex;align-items:center;gap:5px;flex-wrap:wrap}
  .as-bar-d{width:1px;height:18px;background:var(--line);flex:none}
  .as-bar-go{gap:7px}
  .as-bar-go .tls{white-space:nowrap}
  .as-bar-go .tls b{font-size:12.5px;font-weight:650;color:var(--ink)}
  @media(max-width:820px){.as-bar-d{display:none}}
  .as-tally{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--ink-2);flex-wrap:wrap}
  .as-tally b{font-size:15px;font-weight:650;color:var(--ink);letter-spacing:-.01em}
  .as-k{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;color:var(--ink-3)}
  .as-k i{width:7px;height:7px;border-radius:99px;display:block;flex:none}
  .as-k.icon i{background:var(--as-icon)} .as-k.image i{background:var(--as-image)}
  .as-grid{display:grid;grid-template-columns:minmax(0,1fr) 336px;gap:14px;align-items:start}
  /* 在「重新设计」这一页，右边才是正在干活的地方：一屏六组选项挤在 336px 里，
     每一行 chip 都要换行，而左边那台手机大半是空的灰色。所以这一页把宽度让给
     右栏 —— demo 仍然看得见（要对着它判断换得对不对），只是不再占大头。 */
  .as-grid.wide-side{grid-template-columns:minmax(0,1fr) 496px}
  @media(max-width:1080px){.as-grid,.as-grid.wide-side{grid-template-columns:1fr}}
  .as-stage{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);
    padding:16px;display:grid;place-items:start center;overflow:auto;position:relative;min-height:520px}
  .as-frame{width:390px;height:780px;max-width:100%;border:1px solid var(--line-2);border-radius:var(--r-md);
    background:#fff;box-shadow:var(--pop);display:block;transition:width .18s var(--ease)}
  .as-frame.wide{width:100%}
  /* 台下那一份不销毁只隐身：display:none 会把探针量到的几何全变成 0（圈和
     「本屏 N」全部归零），visibility 保得住布局，demo 也不用重新启动。 */
  .as-frame.off{visibility:hidden;position:absolute;top:16px;left:16px;pointer-events:none}
  /* 头/列表/脚三行，中间那行 minmax(0,1fr) —— 少了这个 0，网格行会按内容撑到
     47 条的自然高度，.as-list 永远不滚，被 .as-side 的 overflow 直接裁掉，
     后面三十几个资源谁也够不着。 */
  .as-side{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);
    display:grid;grid-template-rows:auto auto auto minmax(0,1fr) auto;overflow:hidden;
    position:sticky;top:14px;height:calc(100vh - 28px)}
  .as-side-h{display:flex;align-items:center;gap:8px;padding:11px 13px 9px;border-bottom:1px solid var(--line)}
  /* 清单和重新设计共用同一格：一次只显示一个，各自管自己的滚动 —— 套一层公共
     滚动容器会把清单的滚动位置吃掉（重扫描很频繁，那是要保住的）。 */
  .as-body{display:grid;min-height:0}
  .as-tabs{display:flex;gap:2px;padding:7px 9px;border-bottom:1px solid var(--line);
    background:var(--surface-2)}
  .as-tab{flex:1;height:27px;border-radius:7px;border:0;background:transparent;cursor:pointer;
    font-size:12px;font-weight:550;color:var(--ink-3);display:inline-flex;align-items:center;
    justify-content:center;gap:5px;transition:background .12s var(--ease),color .12s var(--ease)}
  .as-tab:hover:not([disabled]){color:var(--ink-2)}
  .as-tab[aria-selected="true"]{background:var(--surface);color:var(--ink);box-shadow:var(--pop)}
  .as-tab[disabled]{opacity:.45;cursor:default}
  .as-tab b{font-family:var(--mono);font-size:10px;font-weight:600}
  .as-filter{display:flex;gap:6px;padding:8px 9px;border-bottom:1px solid var(--line);
    background:var(--surface)}
  .as-filter input,.as-filter select{height:27px;border:1px solid var(--line);
    border-radius:7px;background:var(--surface-2);color:var(--ink);font-size:11.5px;
    font-family:inherit;padding:0 8px;min-width:0}
  .as-filter input{flex:1;-webkit-appearance:none}
  .as-filter input::-webkit-search-cancel-button{-webkit-appearance:none;width:11px;height:11px;
    background:var(--ink-3);cursor:pointer;
    -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 5l14 14M19 5L5 19' stroke='black' stroke-width='3'/%3E%3C/svg%3E") center/contain no-repeat}
  .as-filter select{flex:none;max-width:44%;cursor:pointer}
  .as-filter input:focus,.as-filter select:focus{outline:none;border-color:var(--accent);
    box-shadow:0 0 0 3px var(--ring)}
  .as-filter[data-on="1"] select{border-color:var(--accent-line);background:var(--accent-soft);
    color:var(--accent-ink);font-weight:600}
  .as-list,.as-pane{overflow-y:auto;min-height:0}
  /* 单栏时侧栏跟着页面走，不再自己占满一屏 */
  @media(max-width:1080px){.as-side{position:static;height:auto;max-height:none}
    .as-list,.as-pane{max-height:60vh}}
  .as-row{display:grid;grid-template-columns:22px 34px minmax(0,1fr) auto;gap:9px;align-items:center;
    padding:8px 12px;border-bottom:1px solid var(--line);cursor:pointer;text-align:left;
    background:transparent;transition:background .12s var(--ease)}
  .as-row:last-child{border-bottom:0}
  .as-row:hover,.as-row.hot{background:var(--surface-2)}
  .as-row[aria-pressed="true"]{background:var(--accent-soft)}
  :root[data-theme="dark"] .as-row[aria-pressed="true"]{background:var(--accent-soft)}
  .as-n{width:20px;height:20px;border-radius:5px;display:grid;place-items:center;flex:none;
    font-family:var(--mono);font-size:10px;font-weight:600;color:#fff;background:var(--as-icon)}
  .as-row[data-kind="image"] .as-n{background:var(--as-image)}
  .as-th{width:34px;height:34px;border-radius:6px;border:1px solid var(--line);background:var(--surface-2);
    display:grid;place-items:center;overflow:hidden;flex:none}
  .as-th img{max-width:26px;max-height:26px;object-fit:contain;display:block}
  .as-th.off{border-style:dashed}
  .as-meta{min-width:0;display:grid;gap:2px}
  .as-meta .t{font-size:12.5px;font-weight:550;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .as-meta .d{font-family:var(--mono);font-size:10px;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .as-pick{width:19px;height:19px;border-radius:5px;border:1.5px solid var(--line-2);display:grid;
    place-items:center;color:transparent;flex:none;background:var(--surface);cursor:pointer;
    box-shadow:0 0 0 6px transparent}   /* 透明外圈把点击区域放大到 31px */
  .as-pick:hover{border-color:var(--accent);box-shadow:0 0 0 6px var(--surface-3)}
  .as-row[aria-pressed="true"] .as-pick{background:var(--accent);border-color:var(--accent);color:#fff}
  .as-row[aria-pressed="true"] .as-pick svg{width:11px;height:11px}
  .as-note{padding:10px 13px;font-size:11.5px;color:var(--ink-3);line-height:1.6;border-top:1px solid var(--line)}
  /* ── 重新设计。点「送去重新设计」之后就在同一页往下长出这一段 —— 不弹窗、
     不换页：原来长什么样全在右边那份清单和下面那个 demo 里，跳走了就只能靠
     记忆去对照。风格来源是复选，因为一次重设常常同时受制于两三样东西
     （这份 demo 的长相 + 一份 PRD + 一张参考图）。 */
  /* 插件宽度（336 px）。这里每一条都是「窄栏里怎么放得下」的结果：标签在字段
     上面而不是左边，chip 自己换行，结果卡片单列。 */
  /* 这一栏在这一页有 496 px（.wide-side），但在 Figma 插件里只有 336 px —— 用
     容器查询而不是媒体查询：决定标签能不能挪到左边的是这个盒子的宽度，跟视口
     多宽没关系（<1080 px 时它反而变成整页宽）。 */
  .rd{display:grid;gap:11px;padding:11px 12px 14px;container-type:inline-size}
  .rd-h{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
  .rd-h .t{font-size:12.5px;font-weight:600;color:var(--ink);letter-spacing:-.01em}
  .rd-h .sp{margin-left:auto}
  /* 六个小节，每个都是「一行大写灰标签 + 一堆 chip + 一段灰字」竖着摞 —— 十八行
     里有六行只是标签。放得下就把标签挪到左边：垂直高度掉三分之一，而且六个小节
     的 chip 从同一条竖线起排，一眼就看得出这是同一张表单的六个字段。 */
  .rd-row{display:grid;gap:6px}
  .rd-row>.k{font-size:10.5px;font-weight:550;color:var(--ink-3);
    text-transform:uppercase;letter-spacing:.04em}
  .rd-row>.rd-row-b{display:grid;gap:6px;min-width:0}
  @container (min-width:400px){
    .rd-row{grid-template-columns:96px minmax(0,1fr);gap:6px 12px;align-items:start}
    .rd-row>.k{padding-top:6px;line-height:1.35}
  }
  /* 结果头。以前这里是四行等重的按钮，其中两个还是同一个控件的两份。现在两行：
     上面只说状态，下面只管动手 —— 而且它自己有个盒子，跟下面那张表单分得开。 */
  .rd-res{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);
    display:grid;gap:8px;padding:9px 10px}
  .rd-res-h{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0}
  .rd-res-h .sp{margin-left:auto}
  .rd-res-h .t{font-size:12.5px;font-weight:600;color:var(--ink);letter-spacing:-.01em;
    min-width:0;overflow:hidden;text-overflow:ellipsis}
  /* 状态用一个点说，不用一句话说 —— 完成是绿的，在跑是转的，没成是琥珀色。 */
  .rd-dot{width:7px;height:7px;border-radius:99px;flex:none;background:var(--line-2);
    display:grid;place-items:center}
  .rd-res[data-state="done"] .rd-dot{background:var(--accent)}
  .rd-res[data-state="failed"] .rd-dot{background:var(--ochre)}
  .rd-res[data-state="running"] .rd-dot{width:11px;height:11px;background:none}
  .rd-tally{font-size:11.5px;color:var(--ink-3);flex:none}
  /* 选中的那几个，摆在这一页最上面：它是这一页的输入。缩略图 30 px 一颗，够
     认出「是它」，不够到抢结果卡的戏 —— 这一页的主角始终是那对「原来 → 现在」。 */
  .rd-picked{display:grid;gap:7px;padding-bottom:2px}
  .rd-picked .rd-h .t{font-size:12px;font-weight:600;color:var(--ink)}
  .rd-pk-strip{display:flex;gap:5px;flex-wrap:wrap}
  .rd-pk{width:30px;height:30px;padding:2px;border-radius:6px;border:1px solid var(--line);
    background:var(--surface-2);cursor:pointer;display:grid;place-items:center;overflow:hidden}
  .rd-pk:hover{border-color:var(--ochre)}
  .rd-pk img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;display:block}
  .rd-pk i{font-style:normal;font-size:8px;color:var(--ink-3)}
  /* 每张卡上的勾：这一个换不换进 HTML。它跟标题同一行，因为它是这张卡的属性，
     不是一条独立的操作 —— 给它单开一行，四张卡就多出四条横杠。 */
  .rd-use{width:17px;height:17px;flex:none;border-radius:5px;padding:0;
    border:1.5px solid var(--line-2);background:var(--surface);cursor:pointer;
    display:grid;place-items:center;color:transparent;
    transition:background .12s var(--ease),border-color .12s var(--ease)}
  .rd-use svg{width:11px;height:11px}
  .rd-use:hover{border-color:var(--accent)}
  .rd-use[aria-checked="true"]{background:var(--accent);border-color:var(--accent);color:#fff}
  /* 问一句要改什么。以前是一个光秃秃的输入框加一行灰字；批量那一颗要花 N 次
     生成，所以问题本身要看得见，「走」在有字之前是灰的。 */
  .rd-ask{display:grid;gap:6px;padding:9px 10px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface)}
  .rd-ask>label{font-size:11px;line-height:1.55;color:var(--ink-3)}
  .rd-ask>label b{font-size:12px;font-weight:600;color:var(--ink);margin-right:4px}
  .rd-ask input{width:100%;font-size:12.5px;padding:7px 9px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface-2);color:var(--ink)}
  .rd-ask input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
  /* 读到的那一句：两行封顶，全文在 title 里。它是证据，不是正文 —— 占三行就成了
     每次轮询都要重新扫一遍的东西。 */
  .rd-read{font-size:10.5px;line-height:1.55;color:var(--ink-3);margin:0;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .rd-read b{font-weight:600;color:var(--ink-2)}
  .rd-read .d{color:var(--ink-3)}
  .rd-chips{display:flex;gap:5px;flex-wrap:wrap}
  .rd-chip{display:inline-flex;align-items:center;gap:5px;padding:5px 9px;border-radius:99px;
    border:1px solid var(--line);background:var(--surface);cursor:pointer;font-size:11.5px;
    color:var(--ink-2);transition:border-color .12s var(--ease),background .12s var(--ease)}
  .rd-chip:hover{border-color:var(--line-2);background:var(--surface-2)}
  .rd-chip[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-soft);color:var(--ink)}
  .rd-chip i{width:12px;height:12px;border-radius:4px;border:1.5px solid var(--line-2);
    display:grid;place-items:center;flex:none;font-style:normal;font-size:8px;color:transparent}
  .rd-chip[aria-pressed="true"] i{background:var(--accent);border-color:var(--accent);color:#fff}
  .rd-fields{display:grid;gap:8px;margin-top:7px}
  .rd-field{display:grid;gap:4px}
  .rd-field>label{font-size:10.5px;color:var(--ink-3)}
  .rd-field textarea,.rd-field input{width:100%;font-size:12px;padding:7px 9px;
    border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);
    color:var(--ink);resize:vertical}
  .rd-field textarea{font-family:var(--mono);font-size:11.5px;line-height:1.55}
  .rd-field textarea:focus,.rd-field input:focus{outline:none;border-color:var(--accent);
    box-shadow:0 0 0 3px var(--ring)}
  .rd-note{font-size:10.5px;color:var(--ink-3);line-height:1.55}
  .rd-refs{display:flex;gap:5px;flex-wrap:wrap;align-items:center}
  .rd-refs .th{width:34px;height:34px;border-radius:6px;border:1px solid var(--line);
    overflow:hidden;display:grid;place-items:center;background:var(--surface-2)}
  .rd-refs img{width:100%;height:100%;object-fit:cover;display:block}
  .rd-sep{border-top:1px solid var(--line);margin:1px -12px 0}
  /* 结果是成对的：左边原来那个，右边新的。并排是这一屏唯一要回答的问题
     ——「换了之后是变好了吗」，隔开就没法回答。窄栏里所以单列。 */
  .rd-cards{display:grid;gap:7px}
  .rd-card{border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);
    padding:8px;display:grid;gap:7px;align-content:start}
  .rd-card[data-state="failed"]{border-color:var(--ochre)}
  /* 12 px 的箭头列夹在两张图中间，细得像个排版事故。改成一个圆片压在两半之间：
     两半等宽、贴着，方向由中间那一片说 —— 两张图挨得更近，也就更好比。 */
  .rd-pair{display:grid;grid-template-columns:1fr 1fr;gap:0;align-items:center;
    position:relative}
  .rd-shot{height:132px;border:1px solid var(--line);background:var(--surface-2);
    display:grid;place-items:center;overflow:hidden;padding:6px;
    cursor:zoom-in;width:100%}
  .rd-shot.empty{cursor:default}
  button.rd-shot:hover{border-color:var(--line-2)}
  .rd-pair>.rd-shot:first-child{border-radius:7px 0 0 7px;border-right:0}
  .rd-pair>.rd-shot:last-child{border-radius:0 7px 7px 0}
  /* 尽量大，但一定是整张。`width/height:100%` 撑得更满，可它在这个居中的格子里
     解不出高度：一张 128×85 的缩略图算出 215×143，比 86 px 的格子高出一半，于是
     两张图都被切成一条横带 —— 一条横带跟另一条横带比不出「变好了吗」，而这是
     这一排唯一要回答的问题。max- 两边收口，比例自己保。 */
  .rd-shot img{max-width:100%;max-height:118px;width:auto;height:auto;
    object-fit:contain;display:block}   /* 118 = 132 − 2×6 padding − 2 border */
  .rd-shot.empty{border-style:dashed;font-size:10px;color:var(--ink-3);text-align:center;
    padding:4px;line-height:1.4}
  .rd-arrow{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
    width:20px;height:20px;border-radius:99px;border:1px solid var(--line);
    background:var(--surface);color:var(--ink-3);font-size:10px;display:grid;
    place-items:center;box-shadow:var(--lift);z-index:1}
  /* 候选条：缩略图挨着放，选中的那个描边。编号压在角上而不是另起一行 —— 这一条
     的任务是「哪个更好」，编号只是选中之后说得出口。 */
  .rd-cands{display:flex;gap:5px;flex-wrap:wrap}
  .rd-cand{position:relative;width:52px;height:52px;border-radius:7px;padding:3px;
    border:1px solid var(--line);background:var(--surface-2);cursor:pointer;
    display:grid;place-items:center;overflow:hidden;
    transition:border-color .12s var(--ease),box-shadow .12s var(--ease)}
  .rd-cand:hover{border-color:var(--line-2)}
  .rd-cand.on{border-color:var(--accent);box-shadow:0 0 0 2px var(--ring)}
  .rd-cand img{width:100%;height:100%;object-fit:contain;display:block}
  .rd-cand b{position:absolute;right:2px;bottom:1px;font-family:var(--mono);
    font-size:8.5px;font-weight:600;color:var(--ink-3);background:var(--surface);
    border-radius:3px;padding:0 2px;line-height:1.4}
  .rd-cand.on b{color:#fff;background:var(--accent)}
  .rd-card .t{font-size:11.5px;font-weight:550;color:var(--ink);white-space:nowrap;overflow:hidden;
    text-overflow:ellipsis}
  .rd-card .d{font-size:10px;color:var(--ink-3);line-height:1.5}
  .rd-vers{flex:none}
  .rd-vers button{min-width:30px;text-align:center;font-family:var(--mono);
    font-size:11px;letter-spacing:.02em}
  /* 取走这一份：下载在左，再来一轮在右。它在卡片下面，因为它是看完之后才做的
     事 —— 摆在结果上面就是让人先路过两个还不能点的键。 */
  .rd-take{display:flex;align-items:center;gap:7px;flex-wrap:wrap;
    padding-top:9px;border-top:1px solid var(--line)}
  .rd-take .sp{margin-left:auto}
  /* 提交那一颗通栏 —— 它是这张表单唯一的出口，跟上面每个字段左边的标签对不齐
     也没关系：对齐是给「可以扫读的东西」用的，这一颗要的是拦不住。 */
  .rd-go{width:100%;margin-top:2px}
  .rd-foot{display:flex;align-items:center;gap:7px;min-width:0}
  .rd-foot .t{flex:1;min-width:0}
  .rd-foot .d{flex:none;white-space:nowrap}
  /* 格式当标签用：点之前就知道拿到的是 SVG 还是 PNG */
  .rd-dl{flex:none;height:22px;padding:0 8px;font-family:var(--mono);font-size:10px;
    letter-spacing:.03em}
  .rd-detail{margin-top:-2px}
  /* 放大看一张。整屏压在上面，背景点一下就关 —— 它不是一个"视图"，它是看一眼。 */
  #zoom{position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:24px}
  .zoom-back{position:absolute;inset:0;background:rgba(10,10,12,.72);cursor:zoom-out}
  .zoom-fig{position:relative;margin:0;display:grid;gap:10px;max-width:min(1100px,92vw);
    max-height:92vh;justify-items:center}
  .zoom-fig img{max-width:100%;max-height:calc(92vh - 60px);object-fit:contain;
    border-radius:var(--r-md);background:var(--surface);box-shadow:var(--lift)}
  .zoom-fig figcaption{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
    width:100%;padding:8px 10px;border-radius:var(--r-md);background:var(--surface);
    border:1px solid var(--line)}
  .zoom-fig figcaption .t{font-size:12.5px;font-weight:550;color:var(--ink);
    min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .zoom-fig figcaption .sp{margin-left:auto}
  .zoom-fig figcaption .dim{font-size:10.5px;color:var(--ink-3);flex:none}
  /* 小图不放大：一张 128 px 的缩略图拉到全屏只会糊，而 dim 已经说清它就这么大。
     大图（生成出来的那一份）自然会被 max- 收在屏幕里。 */
  .rd-card[data-state="failed"] .d{color:var(--ochre)}
  .rd-spin{width:11px;height:11px;border-radius:99px;border:2px solid var(--line-2);
    border-top-color:var(--accent);animation:rdspin .7s linear infinite;display:inline-block;
    vertical-align:-1px}
  @keyframes rdspin{to{transform:rotate(360deg)}}
  /* 进场页。一个投放区 + 两组卡片，没有编号步骤，也没有面板套面板 —— 这一页
     只有一件事要做。 */
  .as-intake{max-width:880px;display:grid;gap:26px}
  .as-drop{border:1.5px dashed var(--line-2);border-radius:14px;background:var(--surface);
    padding:38px 24px 32px;text-align:center;display:grid;justify-items:center;gap:0;
    transition:border-color .15s var(--ease),background .15s var(--ease)}
  .as-drop[data-over="1"]{border-color:var(--accent);background:var(--accent-soft)}
  .as-drop-ic{width:42px;height:42px;border-radius:12px;display:grid;place-items:center;
    background:var(--surface-2);color:var(--ink-3);margin-bottom:14px}
  .as-drop-ic svg{width:21px;height:21px}
  .as-drop[data-over="1"] .as-drop-ic{background:var(--surface);color:var(--accent)}
  .as-drop-t{font-size:16px;font-weight:600;letter-spacing:-.015em;color:var(--ink)}
  .as-drop-s{font-size:12.5px;color:var(--ink-3);margin-top:6px;max-width:400px;line-height:1.6}
  .as-drop-b{display:flex;gap:8px;margin-top:17px}
  .as-paste{width:100%;max-width:560px;margin-top:18px;display:grid;gap:9px;justify-items:end}
  .as-paste textarea{width:100%;font-family:var(--mono);font-size:11.5px;padding:10px 12px;
    border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);
    color:var(--ink);resize:vertical}
  .as-paste textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
  .as-sec{display:grid;gap:11px}
  .as-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(212px,1fr));gap:9px}
  /* 交付文件常常同名（三个 Pet Almanac.html），能把它们分开的是时间，不是 id ——
     所以时间在标题正下方，id 退到它旁边的小字。 */
  .as-card{position:relative;display:grid;gap:5px;padding:12px 13px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface);cursor:pointer;text-align:left;
    transition:border-color .13s var(--ease),background .13s var(--ease),transform .13s var(--ease)}
  .as-card:hover{border-color:var(--line-2);background:var(--surface-2);transform:translateY(-1px)}
  .as-card .t{font-size:12.5px;font-weight:550;color:var(--ink);line-height:1.4;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  /* 角标是绝对定位的，标题得给它让出位置，否则两行标题会从它底下穿过去 */
  .as-card.tagged .t{padding-right:52px}
  .as-card .m{font-size:10.5px;color:var(--ink-3);display:flex;align-items:center;gap:5px;flex-wrap:wrap}
  .as-card .m i{font-style:normal;opacity:.5}
  .as-card .g{position:absolute;top:9px;right:9px;height:16px;padding:0 6px;border-radius:4px;
    background:var(--accent-soft);color:var(--accent-ink);font-size:10px;font-weight:600;
    display:grid;place-items:center}

  /* ══════════════════ 图像生成 / 图标动效 ══════════════════
     两页共用一套：候选与结果的呈现方式不同（一个是照片，一个是会动的矢量），
     但「一列历史 + 一块结果」的骨架是同一个，所以行、缩略图、投放区只写一遍。
     .ig-hero 不能复用到图片候选上 —— 它是 64px 定宽并在暗色下 invert，那是
     给单色图标的处理，套到照片上会把整张图反相。 */
  .img-shot{display:grid;place-items:center;min-height:150px;margin:9px 0;
    border-radius:var(--r-md);background:var(--surface-2);overflow:hidden}
  .img-shot img{width:100%;height:auto;max-height:260px;object-fit:contain;display:block}
  /* 选中的那一张放大，明暗两个底各一块。和 .ig-preview / .ig-stage 是同一套
     语言，但**不能**直接复用它们：那一套在暗色格子里对 img 做 invert(1)，那是
     给单色图标的处理，套到照片上是把整张图反过来。 */
  .img-view{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:14px}
  .img-stage{border:1px solid var(--line);border-radius:var(--r-md);padding:12px;
    background:#fff;color:#111;display:grid;gap:9px;align-content:start}
  .img-stage.dark{background:#111;color:#fff;border-color:#2b2b2b}
  .img-hero{display:grid;place-items:center;min-height:170px}
  .img-hero img{max-width:100%;max-height:290px;object-fit:contain;
    border-radius:var(--r);display:block}
  /* 并排比较的格子按图片的比例放大 —— 64px 见方是给图标的。上限写成像素而不是
     100%：这些格子是 place-items:center 的网格，居中的项目没有确定的可解百分比
     高度，height:100% 和 max-height:100% 都解不出来，图片于是按自己的长宽比长出
     格子并被 overflow:hidden 齐刷刷切掉 —— 量出来是 132px 的格子里装着 184px
     的图，截图上就是一排被拦腰切断的图片。 */
  .ig-compare-box.pic{height:132px;padding:6px}
  .ig-compare-box.pic img{width:auto;height:auto;max-width:100%;max-height:120px}
  .ig-vs-box.pic{height:118px;padding:6px}
  .ig-vs-box.pic img{width:auto;height:auto;max-width:100%;max-height:106px}
  .ig-vs-box.pic.dark{height:118px}
  .ig-vs-box.pic.dark img{width:auto;height:auto;max-width:100%;max-height:106px;filter:none}
  @media(max-width:760px){.img-view{grid-template-columns:1fr}
    .img-hero{min-height:130px}.img-hero img{max-height:220px}}
  /* ── Logo 生成 ──────────────────────────────────────────────────────────
     暗底格子里对 img 做 invert(1)。第一版写的是「把格子的 color 设成白，
     currentColor 自己就跟着变白」—— 那句话是错的，截图上是黑底上一块几乎看不
     见的黑：**经 <img> 加载的 SVG 是一份独立文档，不继承宿主页的任何 CSS**，
     currentColor 只能解析成它自己的初始值（黑）。
     所以这里和图标页一样用反相，而这在这一页是成立的：标记单色是有网关强制的
     （logo_job.clean_svg 拒绝任何固定颜色），反相一个纯黑图形得到的就是纯白。
     图像页和动效页不能这么做 —— 那两页的产物是照片和有颜色的标记。 */
  .lg-shot{display:grid;place-items:center;min-height:150px;margin:9px 0;padding:14px;
    border-radius:var(--r-md);background:var(--surface-2);overflow:hidden}
  /* `.mark` 是一枚单色 SVG，`.pic` 是一张自带背景的展示图 —— 只有前者可以反相。
     两者摆在同一种牌里，不分开就只有两个选择：让深色主题下的标记消失，或者把
     展示图整张翻过来。 */
  .lg-shot.pic.dark{background:#111}
  .lg-shot img{width:100%;height:auto;max-height:190px;object-fit:contain;display:block}
  :root[data-theme="dark"] .lg-shot.mark img{filter:invert(1)}
  /* 明暗两块放大格。`.img-stage` 自带 #fff / #111，与站点主题无关，所以这里只
     有暗的那一块需要反相。 */
  .lg-hero{display:grid;place-items:center;min-height:200px}
  .lg-hero img{max-width:100%;max-height:250px;object-fit:contain;display:block}
  .lg-hero.dark img{filter:invert(1)}
  /* 展示图已经自带背景，占满一整行而不是明暗两块 —— 16:9 的效果图并排缩到
     一半宽，那些 6pt 的微排版就彻底看不见了。 */
  .lg-hero-wide{margin-top:14px;border:1px solid var(--line);border-radius:var(--r-md);
    overflow:hidden;background:var(--surface-2)}
  .lg-hero-wide img{width:100%;height:auto;display:block}
  /* 缩到最小还认不认得出，是标记独有的检查 —— 摆成一排真实尺寸，而不是写一句
     「支持任意缩放」。 */
  .lg-sizes{margin-top:14px;padding:10px 12px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface-2);display:grid;gap:8px}
  .lg-size-row{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap}
  .lg-size{display:grid;justify-items:center;gap:4px}
  :root[data-theme="dark"] .lg-size img{filter:invert(1)}
  .lg-size i{font-family:var(--mono);font-style:normal;font-size:8.5px;color:#71717a}
  .lg-why{margin-top:12px;padding:10px 12px;border-left:2px solid var(--line);display:grid;gap:5px}
  .lg-why p{margin:0;font-size:12.5px;line-height:1.55;color:var(--ink-2)}
  /* 背景是多选，所以是一组开关而不是分段控件。每一个带自己的深/浅小样 ——
     「绝对虚空」「瑞士扁平」这样的名字，不给底色是认不出来的。 */
  .lg-styles{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
  .lg-style{display:flex;align-items:center;gap:8px;padding:7px 9px;text-align:left;
    border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);
    cursor:pointer;font:inherit;color:inherit}
  .lg-style:hover{border-color:var(--ink-3)}
  .lg-style[aria-pressed="true"]{border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
  .lg-style-swatch{width:18px;height:18px;border-radius:5px;flex:0 0 auto;
    border:1px solid var(--line);background:#f4f4f5}
  .lg-style.dark .lg-style-swatch{background:#111;border-color:#2b2b2b}
  .lg-style-n{font-size:12px;line-height:1.3}
  @media(max-width:760px){.lg-styles{grid-template-columns:1fr 1fr}
    .lg-hero{min-height:150px}.lg-hero img{max-height:200px}}
  .gen-list{display:grid;gap:6px}
  .gen-row{display:grid;grid-template-columns:38px 1fr auto;gap:10px;align-items:center;
    width:100%;text-align:left;padding:8px 10px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface);transition:.13s var(--ease)}
  .gen-row:hover{border-color:var(--line-2);box-shadow:var(--lift)}
  .gen-row[aria-current="true"]{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent-line)}
  .gen-thumb{width:38px;height:38px;border-radius:var(--r);object-fit:contain;
    background:var(--surface-2);display:block}
  .gen-b{min-width:0;display:grid;gap:2px}
  .gen-t{font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .mtn-drop{border:1.5px dashed var(--line-2);border-radius:14px;background:var(--surface);
    padding:34px 20px;display:grid;gap:6px;justify-items:center;text-align:center;
    cursor:pointer;transition:.13s var(--ease)}
  .mtn-drop[data-over="1"]{border-color:var(--accent);background:var(--accent-soft)}
  .mtn-source{display:grid;grid-template-columns:96px 1fr;gap:14px;align-items:center}
  .mtn-shot{width:96px;height:96px;border-radius:var(--r-md);background:var(--surface-2);
    display:grid;place-items:center}
  .mtn-shot img{width:64px;height:64px;object-fit:contain}
  /* 动效页是别人（Codex）写的一整页 HTML，带自己的背景、自己的 hero 尺寸。按
     原尺寸嵌进来，一个 420px 的标记压满整块面板还拖出滚动条，页尾的原子动效和
     控件全看不见。所以按比例缩成一块预览牌：整页一眼看全，要看大的那里有
     「打开动效页」。 */
  .mtn-view{--mtn-s:.42;position:relative;overflow:hidden;margin:0 auto 14px;max-width:100%;
    width:calc(1100px*var(--mtn-s));height:calc(820px*var(--mtn-s));
    border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2)}
  .mtn-frame{position:absolute;top:0;left:0;width:1100px;height:820px;border:0;display:block;
    transform:scale(var(--mtn-s));transform-origin:top left}
  @media(max-width:760px){.mtn-source{grid-template-columns:72px 1fr}
    .mtn-shot{width:72px;height:72px}.mtn-shot img{width:48px;height:48px}
    .mtn-view{--mtn-s:.3}}

  /* ── 资源库选择器 ─────────────────────────────────────────────────────
     两页共用。展开在原位，不是一个弹层：要选的东西和选完要填的那一行必须
     同时看得见 —— 盖住半个表单去选一张参考图，回来还得再想一遍刚才填到哪。 */
  .pick{margin-top:10px;border:1px solid var(--line);border-radius:var(--r-lg);
    background:var(--surface-2);padding:10px}
  .pick-h{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:9px}
  .pick-h input{flex:1;min-width:120px;height:28px;padding:0 9px;font-size:12px;
    border:1px solid var(--line);border-radius:var(--r);background:var(--surface);
    color:var(--ink)}
  .pick-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(92px,1fr));
    gap:8px;max-height:296px;overflow-y:auto}
  .pick-a{display:grid;gap:3px;padding:6px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface);text-align:left;
    transition:.13s var(--ease)}
  .pick-a:hover{border-color:var(--accent);box-shadow:var(--lift)}
  .pick-s{height:60px;border-radius:var(--r);background:var(--surface-2);
    display:grid;place-items:center;overflow:hidden}
  .pick-s img{max-width:100%;max-height:56px;object-fit:contain}
  .pick-n{font-size:10.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;
    white-space:nowrap}
  .pick-m{font-size:9.5px;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;
    white-space:nowrap}

  /* ── 使用场景：看得见的四个节奏 ────────────────────────────────────────
     「悬停必须在中途被打断时也读得懂」是一句话，不是一个能看见的东西。四格各自
     用当前选中的标记按自己的区间在动，所以选的是节奏而不是一个词。每个循环里
     动作只占前一段、后一段留住不动 —— 四格同时无停顿地动就是四个互相打架的
     小广告，什么也读不出来。 */
  .mtn-uses{display:grid;grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:8px}
  .mtn-use{display:grid;gap:4px;padding:8px 6px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface);transition:.13s var(--ease)}
  .mtn-use:hover{border-color:var(--line-2)}
  .mtn-use[aria-pressed="true"]{border-color:var(--accent);
    box-shadow:0 0 0 1px var(--accent-line);background:var(--accent-soft)}
  .mtn-use-stage{height:56px;display:grid;place-items:center;overflow:hidden}
  .mtn-use-t{font-size:11px;font-weight:600;line-height:1.3}
  .mtn-use-b{font-size:9.5px;color:var(--ink-3)}
  .mtn-demo{width:38px;height:38px;object-fit:contain}
  /* 时长条。同一个标记贴四遍、只有动画速度不同，静止时四格一模一样 —— 条子按
     各自区间的长短画出来，开场和悬停一眼就不是一件事。 */
  .mtn-bar{display:block;height:3px;border-radius:2px;background:var(--line);overflow:hidden}
  .mtn-bar i{display:block;height:100%;border-radius:2px;background:var(--accent);opacity:.8}
  .mtn-bar[data-motion="splash"] i{width:100%}
  .mtn-bar[data-motion="header"] i{width:42%}
  .mtn-bar[data-motion="hover"] i{width:16%}
  /* 循环没有长度，画满没有意义 —— 画成一段接一段的重复。 */
  .mtn-bar[data-motion="loading"] i{width:100%;
    background:repeating-linear-gradient(90deg,var(--accent) 0 8px,transparent 8px 15px)}
  @keyframes mtn-splash{
    0%{opacity:0;transform:scale(.66)}
    38%{opacity:1;transform:scale(1.05)}
    52%,100%{opacity:1;transform:scale(1)}}
  @keyframes mtn-header{
    0%{opacity:0;transform:translateY(9px)}
    26%,100%{opacity:1;transform:translateY(0)}}
  @keyframes mtn-loading{to{transform:rotate(360deg)}}
  @keyframes mtn-hover{
    0%,100%{transform:scale(1)}
    9%{transform:scale(1.14)}
    20%{transform:scale(1)}}
  .mtn-demo[data-motion="splash"]{animation:mtn-splash 3.2s var(--ease) infinite}
  .mtn-demo[data-motion="header"]{animation:mtn-header 2.2s var(--ease) infinite}
  .mtn-demo[data-motion="loading"]{animation:mtn-loading 1.5s linear infinite}
  .mtn-demo[data-motion="hover"]{animation:mtn-hover 1.8s var(--ease) infinite}
  /* 一屏上四个不停在动的东西，对前庭敏感的人是实打实的问题。系统说了不要动，
     就一格也不动 —— 静止的那一帧仍然是它的最终状态，信息没有少。 */
  @media(prefers-reduced-motion:reduce){.mtn-demo{animation:none!important}}

/* ══════════════════════════ 无边画布 ══════════════════════════
   两条硬约束决定了这一节的写法：

   ① 只有 #cv-world 被 transform。平移和缩放搬动整块世界，节点自己不重排，
      于是拖动的代价和板子上有多少东西无关 —— 这是「无边」能成立的前提。
   ② 把手不跟着缩。它们用 --cvz（= 1/缩放）反向缩放：300% 时把手是一块砖，
      20% 时小得抓不住，两头都没法用。

   网格是 background-image，位置和尺寸由 JS 跟着视口写。少了这一条，缩放时
   格子不动，看起来是内容在格子上滑，而不是画布在动。 */
.view.flat{padding:0;max-width:none}
.cv-shell{display:flex;flex-direction:column;height:calc(100dvh - 49px)}
.cv-body{flex:1;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 302px}
@media(max-width:1080px){.cv-body{grid-template-columns:minmax(0,1fr)}
  .cv-side{display:none}}

/* ── 顶栏 ── */
.cv-top{display:flex;align-items:center;gap:12px;height:47px;flex:none;padding:0 14px;
  border-bottom:1px solid var(--line);background:var(--surface)}
.cv-top-l{display:flex;align-items:center;gap:8px;min-width:0;flex:1}
.cv-top-l .btn svg{transform:rotate(180deg)}
.cv-title{border:1px solid transparent;background:none;border-radius:var(--r);
  padding:4px 7px;font-size:13.5px;font-weight:600;min-width:0;max-width:230px;
  letter-spacing:-.012em}
.cv-title:hover{border-color:var(--line)}
.cv-title:focus{border-color:var(--accent);outline:none;background:var(--surface-2)}
.cv-state{font-size:11px;color:var(--ink-3);white-space:nowrap}
.cv-state.live{color:var(--ochre)}
.cv-state.bad{color:var(--brick)}
.cv-tools{display:flex;align-items:center;gap:2px}
.cv-tool{display:grid;place-items:center;width:29px;height:29px;border-radius:var(--r);
  color:var(--ink-2);transition:background .13s var(--ease),color .13s var(--ease),
  transform .1s var(--ease),opacity .13s var(--ease)}
.cv-tool svg{width:16px;height:16px}
.cv-tool:hover{background:var(--surface-2);color:var(--ink)}
/* 按下去要有一下。这一排是这块板子上唯一的按钮，点了没反应的时候人的下一个
   动作是再点一次 —— 而「便签」再点一次就是第二张便签。 */
.cv-tool:active{transform:scale(.9)}
.cv-tool[aria-pressed="true"]{background:var(--solid);color:var(--solid-ink)}
/* 撤销栈空着的时候按钮是灰的。不灰的话「撤销」是一个按下去只会弹一句「没有可
   撤销的操作」的按钮，而那句话本身就是这个按钮不该亮的证据。 */
.cv-tool:disabled{opacity:.32;cursor:default}
.cv-tool:disabled:hover{background:none;color:var(--ink-2)}
.cv-tool:disabled:active{transform:none}
.cv-tool.flip svg{transform:scaleX(-1)}
/* 带字的那两个：把东西弄上板子是第一步，它们要看起来就是入口。 */
.cv-tool.wide{width:auto;grid-auto-flow:column;gap:5px;padding:0 9px;
  font-size:12px;font-weight:600;color:var(--ink)}
.cv-tool.wide svg{width:14.5px;height:14.5px;color:var(--ink-3)}
.cv-tool.wide:hover svg{color:var(--accent)}
.cv-sep{width:1px;height:17px;background:var(--line);margin:0 5px}
.cv-top-r{display:flex;align-items:center;gap:7px;flex:1;justify-content:flex-end}
.cv-zoomv{font-size:11.5px;color:var(--ink-3);padding:3px 5px;border-radius:var(--r);
  min-width:44px;text-align:center;transition:background .13s var(--ease),color .13s var(--ease)}
.cv-zoomv:hover{background:var(--surface-2);color:var(--ink)}
@media(max-width:1180px){.cv-tool.wide span{display:none}
  .cv-tool.wide{width:29px;padding:0}}

/* ── 舞台 ── */
.cv-stage{position:relative;overflow:hidden;background:var(--canvas);touch-action:none;
  background-image:radial-gradient(circle at 1px 1px,var(--line-2) 1px,transparent 0)}
.cv-stage.over{box-shadow:inset 0 0 0 2px var(--accent)}
body.cv-grab .cv-stage{cursor:grab}
body.cv-grabbing .cv-stage{cursor:grabbing}
.cv-world{position:absolute;inset:0;transform-origin:0 0;will-change:transform}
.cv-edges{position:absolute;overflow:visible;left:0;top:0;width:1px;height:1px;
  pointer-events:none}
.cv-edges path{fill:none;stroke:var(--accent-line);stroke-width:1.6;
  stroke-dasharray:5 4}
/* 连到「生成中」的那根线自己在流。一次生成要几十秒到十几分钟，这段时间里板子
   上必须有一处在动 —— 静止的板子和卡死的板子在屏幕上是同一幅画，而分不清的时
   候人会再按一次「开始」。 */
.cv-edges path.live{stroke:var(--accent);stroke-width:1.9;
  animation:cv-flow 1s linear infinite}
@keyframes cv-flow{to{stroke-dashoffset:-18}}
/* 描一圈底色再画字，否则线上的字和背后的网格叠在一起，两者都读不出来。 */
.cv-edges text{fill:var(--ink-3);font-size:11px;font-family:var(--mono);text-anchor:middle;
  paint-order:stroke;stroke:var(--canvas);stroke-width:3.5px;stroke-linejoin:round}
.cv-nodes{position:absolute;left:0;top:0;--cvz:1}

/* 一个节点。绝对定位 + transform 定位：改 transform 不触发重排，拖动因此是
   合成器的事；改 left/top 会让每一帧都重新布局整层。 */
.cv-n{position:absolute;left:0;top:0;border-radius:var(--r-md);
  box-shadow:0 0 0 1px var(--line);background:var(--surface);cursor:default;
  transition:box-shadow .13s var(--ease),transform .17s var(--ease),
    width .17s var(--ease),height .17s var(--ease)}
/* 手里正在拖的时候关掉过渡。过渡会让节点落在指针后面十几帧 —— 那不是动效，
   那是「这个画布反应慢」。方向键微调、面板里改坐标、撤销，走的都是有过渡的
   那条路，因为那些时候看清「它从哪儿挪到哪儿」才是重点。 */
.cv-nodes.dragging .cv-n{transition:box-shadow .13s var(--ease)}
.cv-n[data-type="frame"]{box-shadow:none;background:none}
.cv-n:hover{box-shadow:0 0 0 1px var(--line-2)}
.cv-n.sel{box-shadow:0 0 0 2px var(--accent)}
.cv-n.locked{cursor:not-allowed}

/* ── 进场、退场、落地 ──
   凭空出现和凭空消失都会让人回头找「刚才我按到了什么」。一次生成落三张图的
   时候尤其：三张一起淡进来，看得出是这一批；直接出现，就只是板子上忽然多了
   三张，和原有的分不开。 */
/* 节点自己的 transform 是它的坐标（JS 每帧写的那个），所以进场动画一个字都不
   能碰 transform —— 碰了就是「新节点从世界原点飞过来」，板子越大飞得越远。
   缩放放在里面那一层：它没有内联 transform，动它是安全的。 */
@keyframes cv-in{from{opacity:0}}
@keyframes cv-in-b{from{transform:scale(.94)}}
.cv-n.cv-in{animation:cv-in .24s var(--ease)}
.cv-n.cv-in>:first-child{animation:cv-in-b .2s var(--ease)}
.cv-n.cv-out{opacity:0;transform-origin:50% 50%;pointer-events:none;
  transition:opacity .15s var(--ease)}
/* 占位节点变成产物的那一下。这是板子上唯一一次内容自己变了的时刻。 */
@keyframes cv-pop{
  0%{box-shadow:0 0 0 2px var(--accent),0 0 0 0 var(--ring)}
  55%{box-shadow:0 0 0 2px var(--accent),0 0 0 13px transparent}
  100%{box-shadow:0 0 0 1px var(--line)}}
.cv-n.cv-pop{animation:cv-pop .62s var(--ease)}

.cv-media{position:absolute;inset:0;overflow:hidden;border-radius:var(--r-md)}
.cv-media img{width:100%;height:100%;object-fit:contain;display:block;
  -webkit-user-drag:none;user-select:none;background:var(--surface-2)}
/* 图标是透明底的矢量，而这台服务画的标记都是深色描边（OS 17 那套系统图标、
   动效拟合出来的矢量）。所以底板两个主题下都用浅色，不跟着 --surface 走 ——
   跟着走的话暗色主题里深描边落在 #131315 上，图标等于不存在，而「图上什么都
   没有」和「这个节点坏了」人是分不出来的。 */
.cv-n[data-type="icon"] .cv-media{background:#f7f7f5}
.cv-n[data-type="icon"] .cv-media img{padding:11%;background:none}

/* 图取不回来的时候说出来。一个 404 的 <img> 是一个白框，和空便签、和没画完的
   占位一模一样，于是「这张图呢」变成一个查不出所以然的问题。 */
.cv-broken{display:none}
.cv-n.bad .cv-media img{visibility:hidden}
.cv-n.bad .cv-broken{position:absolute;inset:0;display:grid;place-content:center;
  justify-items:center;gap:5px;padding:8px;text-align:center;
  background:var(--brick-soft);color:var(--brick);font-size:calc(11px * var(--cvz))}
.cv-n.bad .cv-broken svg{width:calc(17px * var(--cvz));height:calc(17px * var(--cvz))}

/* 「会动」。动效跑完落在板子上的是一张矢量静图，真正会动的那份在 href 上 ——
   节点上不说，那十来分钟的产出就等于跑完即丢。 */
.cv-live{position:absolute;right:calc(6px * var(--cvz));top:calc(6px * var(--cvz));
  display:flex;align-items:center;gap:calc(4px * var(--cvz));
  padding:calc(3px * var(--cvz)) calc(7px * var(--cvz));border-radius:99px;
  background:var(--accent);color:var(--solid-ink);font-size:calc(10px * var(--cvz));
  font-weight:600;box-shadow:var(--lift);opacity:0;
  transition:opacity .14s var(--ease)}
:root[data-theme="dark"] .cv-live{color:#08201c}
.cv-live svg{width:calc(11px * var(--cvz));height:calc(11px * var(--cvz))}
.cv-n:hover .cv-live,.cv-n.sel .cv-live{opacity:1}
.cv-live:hover{filter:brightness(1.12)}

/* 类型牌。图标和图片以前在板子上是同一个矩形里一张图，而它们能做的事不一样。
   只在悬停和选中时露出来 —— 二十张图各挂一个常驻标签就是二十条抢视线的字。 */
.cv-badge{position:absolute;left:calc(6px * var(--cvz));top:calc(6px * var(--cvz));
  display:flex;align-items:center;gap:calc(4px * var(--cvz));
  padding:calc(2px * var(--cvz)) calc(6px * var(--cvz));border-radius:99px;
  background:var(--surface);box-shadow:0 0 0 1px var(--line),var(--lift);
  color:var(--ink-2);font-size:calc(10px * var(--cvz));font-weight:600;
  white-space:nowrap;opacity:0;pointer-events:none;
  transition:opacity .14s var(--ease)}
.cv-badge svg{width:calc(11px * var(--cvz));height:calc(11px * var(--cvz));
  color:var(--accent)}
.cv-n:hover .cv-badge,.cv-n.sel .cv-badge{opacity:1}
/* 牌子本来就跟着 --cvz 缩，缩小时它按屏幕尺寸一起变小，不需要额外的门槛。
   （这里曾经写过 [style*="width: 4"] 想挡住小节点 —— 它同时匹配 40px、400px、
   420px，也就是把绝大多数正常图片的牌子一起挡掉了。） */
.cv-lab{position:absolute;left:0;right:0;bottom:calc(-19px * var(--cvz));
  font-size:calc(11px * var(--cvz));color:var(--ink-3);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;transform-origin:0 0}
.cv-note{position:absolute;inset:0;background:var(--ochre-soft);border-radius:var(--r-md);
  padding:12px 13px;overflow:auto}
.cv-t{font-size:13px;line-height:1.55;white-space:pre-wrap;word-break:break-word;
  min-height:100%;outline:none}
.cv-t.editing{cursor:text}
.cv-frame{position:absolute;inset:0;border:1.5px dashed var(--line-2);
  border-radius:var(--r-lg);background:rgba(127,127,127,.03);
  transition:border-color .13s var(--ease)}
/* 分区是背景，不是盖子。它的 620×420 以前整块吃指针事件：人在一片看起来空的
   地方按下去拖，以为在框选，实际是把一个几乎看不见的大方框拖走了 —— 而框选是
   这块板子上选东西的主要办法。所以分区只由它的名字牌抓取，身体让路。 */
.cv-n[data-type="frame"]{pointer-events:none}
.cv-n[data-type="frame"] .cv-frame-lab,
.cv-n[data-type="frame"] .cv-hs{pointer-events:auto}
.cv-n[data-type="frame"].sel .cv-frame{border-color:var(--accent)}
.cv-frame-lab{position:absolute;left:0;top:calc(-22px * var(--cvz));
  font-size:calc(11.5px * var(--cvz));font-weight:600;color:var(--ink-3);
  padding:calc(1px * var(--cvz)) calc(6px * var(--cvz));border-radius:var(--r);
  cursor:move;transition:background .13s var(--ease),color .13s var(--ease)}
.cv-frame-lab:hover{background:var(--surface-2);color:var(--ink)}
.cv-file,.cv-job{position:absolute;inset:0;display:flex;gap:10px;padding:13px 14px;
  align-items:flex-start;overflow:hidden}
.cv-file svg{width:19px;height:19px;flex:none;color:var(--ink-3);margin-top:1px}
.cv-file-b{min-width:0}
.cv-file-b .h3{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cv-job{align-items:center;background:var(--accent-soft);border-radius:var(--r-md)}
/* 「生成中」这四个字自己不会动，一个不动的进度提示看起来就是卡住了。底下一条
   来回走的线是这里唯一说明「还活着」的东西 —— 不假装它知道进度：来回走，不是
   从左到右一次走完（那会在 90% 处停十分钟，比没有更让人不信）。 */
.cv-job-bar{position:absolute;left:0;right:0;bottom:0;height:2px;overflow:hidden;
  border-radius:0 0 var(--r-md) var(--r-md)}
.cv-job-bar::after{content:"";position:absolute;top:0;bottom:0;width:38%;
  background:var(--accent);animation:cv-crawl 1.5s var(--ease) infinite}
@keyframes cv-crawl{0%{left:-38%}100%{left:100%}}

/* 把手：只在「只选中了这一个」时出现。多选时它们会互相盖住，而且缩放本来
   就只对单个节点有定义。 */
.cv-hs{display:none}
.cv-n.only .cv-hs{display:block}
.cv-h{position:absolute;width:calc(9px * var(--cvz));height:calc(9px * var(--cvz));
  background:var(--surface);border:1.5px solid var(--accent);border-radius:2px}
.cv-h[data-h="nw"]{left:calc(-5px * var(--cvz));top:calc(-5px * var(--cvz));cursor:nwse-resize}
.cv-h[data-h="ne"]{right:calc(-5px * var(--cvz));top:calc(-5px * var(--cvz));cursor:nesw-resize}
.cv-h[data-h="sw"]{left:calc(-5px * var(--cvz));bottom:calc(-5px * var(--cvz));cursor:nesw-resize}
.cv-h[data-h="se"]{right:calc(-5px * var(--cvz));bottom:calc(-5px * var(--cvz));cursor:nwse-resize}
.cv-h[data-h="n"]{left:50%;top:calc(-5px * var(--cvz));margin-left:calc(-4.5px * var(--cvz));cursor:ns-resize}
.cv-h[data-h="s"]{left:50%;bottom:calc(-5px * var(--cvz));margin-left:calc(-4.5px * var(--cvz));cursor:ns-resize}
.cv-h[data-h="w"]{top:50%;left:calc(-5px * var(--cvz));margin-top:calc(-4.5px * var(--cvz));cursor:ew-resize}
.cv-h[data-h="e"]{top:50%;right:calc(-5px * var(--cvz));margin-top:calc(-4.5px * var(--cvz));cursor:ew-resize}

.cv-marquee{position:absolute;left:0;top:0;border:1px solid var(--accent);
  background:var(--accent-soft);opacity:.4;pointer-events:none;border-radius:2px}
.cv-guides{position:absolute;inset:0;pointer-events:none}
.cv-guides i{position:absolute;background:var(--brick);opacity:.75}
.cv-hint{position:absolute;left:50%;bottom:16px;transform:translateX(-50%);
  font-size:11.5px;color:var(--ink-3);background:var(--surface);padding:5px 11px;
  border:1px solid var(--line);border-radius:99px;pointer-events:none}

/* ── 右键菜单 ──
   position:fixed，坐标直接用指针的 clientX/clientY —— 舞台是 overflow:hidden
   的，贴着边缘右键时，定位在舞台里的菜单会被裁掉一半，而被裁掉的那一半正是最
   下面那几条（删除就在那儿）。 */
.cv-menu{position:fixed;z-index:60;min-width:206px;padding:5px;
  border-radius:var(--r-lg);background:var(--surface);
  box-shadow:0 0 0 1px var(--line),var(--pop);
  opacity:0;transform:scale(.97);transform-origin:top left;
  transition:opacity .1s var(--ease),transform .1s var(--ease)}
.cv-menu.in{opacity:1;transform:scale(1)}
.cv-menu-i{display:flex;align-items:center;gap:9px;width:100%;padding:6px 9px;
  border-radius:var(--r);font-size:12.5px;color:var(--ink-2);
  transition:background .1s var(--ease),color .1s var(--ease)}
.cv-menu-i svg{width:15px;height:15px;flex:none;color:var(--ink-3)}
.cv-menu-i span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cv-menu-i em{font-style:normal;font-size:10.5px;font-family:var(--mono);color:var(--ink-3);
  flex:none}
.cv-menu-i:hover{background:var(--surface-2);color:var(--ink)}
.cv-menu-i:hover svg{color:var(--ink-2)}
/* 能力排在最前面，所以它们自己一个颜色 —— 一张十条的菜单里，人要一眼分出
   「拿它能做什么」和「怎么摆它」。 */
.cv-menu-i.run{color:var(--ink);font-weight:600}
.cv-menu-i.run svg{color:var(--accent)}
.cv-menu-i.run:hover{background:var(--accent-soft)}
.cv-menu-i.bad{color:var(--brick)}
.cv-menu-i.bad svg{color:var(--brick)}
.cv-menu-i.bad:hover{background:var(--brick-soft);color:var(--brick)}
.cv-menu-cut{display:block;height:1px;margin:4px 8px;background:var(--line)}

/* ── 画面外还有多少东西 ──
   一块无边的板子上，「板子是空的」和「东西都在视野外一千像素」在屏幕上是同一
   幅画。而后者是生成结果落位之后的常态（结果落在源节点右边 90px 处）。这一枚
   小牌子是这块板子唯一说得出「还有」的地方，点它就把镜头带过去。 */
.cv-off{position:absolute;right:14px;bottom:14px;display:flex;align-items:center;gap:9px;
  padding:6px 8px 6px 12px;border-radius:99px;background:var(--surface);
  box-shadow:0 0 0 1px var(--line),var(--pop);font-size:11.5px;color:var(--ink-2);
  animation:cv-rise .22s var(--ease);transition:transform .13s var(--ease)}
.cv-off:hover{transform:translateY(-1px)}
.cv-off b{font-size:11px;color:var(--accent);background:var(--accent-soft);
  padding:3px 8px;border-radius:99px}
@keyframes cv-rise{from{opacity:0;transform:translateY(7px)}}

/* ── 上传进度 ──
   一份 PRD 十几兆，一次拖进来五个就是好几秒。这段时间舞台上不说话，人会以为
   拖放没生效，于是再拖一次 —— 板子上从此有两份。 */
.cv-up{position:absolute;left:50%;bottom:16px;transform:translateX(-50%);
  display:flex;align-items:center;gap:10px;min-width:250px;padding:9px 13px;
  border-radius:var(--r-lg);background:var(--surface);
  box-shadow:0 0 0 1px var(--line),var(--pop);animation:cv-rise .22s var(--ease)}
.cv-up div{min-width:0;flex:1;display:grid;gap:1px}
.cv-up b{font-size:12px}
.cv-up>div i{font-style:normal;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  display:block;max-width:210px}
.cv-up-t{position:absolute;left:13px;right:13px;bottom:5px;height:2px;
  background:var(--surface-3);border-radius:2px;overflow:hidden}
.cv-up-t i{display:block;height:100%;background:var(--accent);
  transition:width .25s var(--ease)}

/* ── 右侧面板 ── */
.cv-side{border-left:1px solid var(--line);background:var(--surface);overflow-y:auto;
  display:flex;flex-direction:column}
.cv-side-h{padding:13px 15px 11px;border-bottom:1px solid var(--line)}
.cv-side-b{padding:13px 15px;display:grid;gap:11px;border-bottom:1px solid var(--line)}
.cv-insp{display:grid;gap:8px}
.cv-insp input{width:100%;padding:6px 9px;border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface-2);font-size:12.5px}
.cv-insp input:focus{border-color:var(--accent);outline:none;background:var(--surface)}
/* 「这张被压扁了」。只在框和图真的不符时出现，所以它带一句话，不是一个图标 ——
   一个只在少数时候出现的按钮，人没有机会学会它的字形。 */
.cv-aspect{display:flex;gap:8px;align-items:flex-start;width:100%;padding:8px 10px;
  border:1px solid var(--ochre-line);border-radius:var(--r-md);background:var(--ochre-soft);
  font-size:11.5px;line-height:1.45;color:var(--ochre);
  transition:background .13s var(--ease),border-color .13s var(--ease)}
.cv-aspect:hover{border-color:var(--ochre);background:var(--surface-2)}
.cv-aspect svg{width:14px;height:14px;flex:none;margin-top:2px}
.cv-xy{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.cv-xy label{display:grid;gap:3px}
.cv-acts{display:flex;flex-wrap:wrap;gap:6px}
.cv-keys{display:grid;gap:5px}
.cv-keys div{display:flex;justify-content:space-between;gap:10px;font-size:11.5px;
  color:var(--ink-2)}
.cv-keys .mono{color:var(--ink-3)}
.cv-run{gap:7px}
.cv-run-i{border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden}
.cv-run-i.open{border-color:var(--accent-line)}
.cv-run-h{display:flex;gap:9px;align-items:flex-start;width:100%;padding:9px 11px;
  transition:background .13s var(--ease)}
.cv-run-h:hover{background:var(--surface-2)}
.cv-run-h svg{width:15px;height:15px;flex:none;color:var(--accent);margin-top:2px}
.cv-run-h span{display:grid;gap:2px;min-width:0}
.cv-run-h b{font-size:12.5px}
.cv-run-h i{font-style:normal}
.cv-run-b{padding:0 11px 11px;display:grid;gap:8px}
.cv-run-f{display:grid;gap:4px}
.cv-run-f textarea,.cv-run-f select{width:100%;padding:7px 9px;border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface-2);font-size:12.5px}
.cv-run-f textarea{resize:vertical}
.cv-run-f select{cursor:pointer;appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--ink-3) 50%),
    linear-gradient(135deg,var(--ink-3) 50%,transparent 50%);
  background-position:calc(100% - 14px) 12px,calc(100% - 9px) 12px;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:26px}
.cv-run-f textarea:focus,.cv-run-f select:focus{border-color:var(--accent);outline:none;
  background-color:var(--surface)}
/* 这次会拿什么当参考。看不见这一句的话，「选中三样东西按开始」和「选中一样
   按开始」在屏幕上完全一样，人无从知道另外两样算没算数。 */
.cv-run-refs{display:flex;gap:7px;align-items:flex-start;padding:7px 9px;
  border-radius:var(--r);background:var(--accent-soft);color:var(--accent-ink);
  font-size:11.5px;line-height:1.45}
.cv-run-refs.warn{background:var(--ochre-soft);color:var(--ochre)}
.cv-run-refs svg{width:14px;height:14px;flex:none;margin-top:2px}
.cv-run-live{display:flex;gap:9px;align-items:center;padding:9px 11px;
  background:var(--accent-soft);border-radius:var(--r-md)}
.cv-run-live div{min-width:0;flex:1}

/* ── 板子列表 ── */
#cv-home{padding:28px 26px 76px;max-width:1240px;width:100%}
.cv-home-head{display:flex;gap:20px;align-items:flex-start;justify-content:space-between;
  padding-bottom:22px;margin-bottom:20px;border-bottom:1px solid var(--line)}
.cv-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(212px,1fr));gap:12px}
.cv-card{position:relative;border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--surface);overflow:hidden;transition:.13s var(--ease)}
.cv-card:hover{border-color:var(--line-2);box-shadow:var(--lift)}
.cv-card-open{display:grid;gap:5px;padding:11px 12px 13px;width:100%}
.cv-card-thumb{height:92px;border-radius:var(--r-md);background:var(--surface-2);
  display:grid;place-items:center;overflow:hidden;margin-bottom:5px;color:var(--ink-3)}
.cv-card-thumb img{width:100%;height:100%;object-fit:cover}
.cv-card-thumb svg{width:26px;height:26px}
.cv-card-t{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.cv-card-x{position:absolute;right:7px;top:7px;width:25px;height:25px;border-radius:var(--r);
  display:grid;place-items:center;color:var(--ink-3);background:var(--surface);
  opacity:0;transition:.13s var(--ease)}
.cv-card:hover .cv-card-x{opacity:1}
.cv-card-x:hover{color:var(--brick);background:var(--brick-soft)}
.cv-card-x svg{width:14px;height:14px}
.cv-blank{display:grid;gap:9px;justify-items:center;text-align:center;padding:58px 20px;
  border:1px dashed var(--line-2);border-radius:var(--r-lg);color:var(--ink-3)}
.cv-blank svg{width:30px;height:30px}

  /* ── 编排方式：技能自己的编排库，一格一个能看见的示例 ──────────────────
     示例演的是**通用示意图形**，不是你的标记：一个没有描边骨架的标记演不出
     「描边绘出」，硬演出来就是一句假话。所以这里演的是这种编排本身长什么样，
     选完由技能在真几何上判断能不能用（用不了会写进 motion_spec.md）。 */
  /* 编排方式那一栏默认只有这一行：现在会用哪一种、为什么，右边一颗「自己指定」。
     七块一样大的牌摊在表单里，是在请人替技能猜（见 86-motion.js 里那段）。 */
  .mtn-free{font-size:11.5px;line-height:1.55;color:var(--ink-3);
    margin:-2px 0 13px;padding:8px 11px;border-radius:var(--r-md);
    background:var(--surface-2);border:1px solid var(--line)}
  .mtn-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:11px;
    align-items:center;padding:8px 10px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface)}
  .mtn-row-b{display:grid;gap:2px;min-width:0}
  .mtn-row-b b{font-size:12.5px}
  .mtn-row-b .tls{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;
    overflow:hidden;line-height:1.45}
  .mtn-row .mtn-pat-stage{width:52px;height:34px}
  .mtn-row .mtn-pat-stage svg{width:52px;height:34px}
  /* 展开后每块牌自己带一句「适合什么」。它以前只在 title 里 —— 藏在悬停提示里的
     判断依据，对正在决定的人等于不存在。 */
  .mtn-pats{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:8px}
  .mtn-pat-for{font-size:10px;line-height:1.45;color:var(--ink-3);
    display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}
  .mtn-pat[aria-pressed="true"] .mtn-pat-for{color:var(--ink-2)}
  .mtn-pat{display:grid;gap:4px;padding:7px 6px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface);transition:.13s var(--ease)}
  .mtn-pat:hover{border-color:var(--line-2)}
  .mtn-pat[aria-pressed="true"]{border-color:var(--accent);
    box-shadow:0 0 0 1px var(--accent-line);background:var(--accent-soft)}
  .mtn-pat-stage{height:42px;display:grid;place-items:center}
  .mtn-pat-stage svg{width:64px;height:42px;overflow:visible}
  .mtn-pat-stage path,.mtn-pat-stage rect{fill:none;stroke:var(--ink-2);stroke-width:2.6;
    stroke-linecap:round;stroke-linejoin:round}
  .mtn-pat-stage circle{fill:var(--accent);stroke:none}
  .mtn-pat[aria-pressed="true"] .mtn-pat-stage path,
  .mtn-pat[aria-pressed="true"] .mtn-pat-stage rect{stroke:var(--accent-ink)}
  /* 描边绘出：真的在描边。pathLength=1 用不了（这是 CSS 不是 SVG 属性），
     所以给一个够长的 dasharray，视觉上等价。 */
  [data-pat="draw"] path{stroke-dasharray:40;stroke-dashoffset:40;
    animation:p2m-draw 2.4s var(--ease) infinite}
  [data-pat="draw"] circle{animation:p2m-late 2.4s var(--ease) infinite}
  @keyframes p2m-draw{0%{stroke-dashoffset:40}55%,100%{stroke-dashoffset:0}}
  @keyframes p2m-late{0%,50%{opacity:0}70%,100%{opacity:1}}
  /* 分部到位：各部件依次到位，起止互相错开 */
  [data-pat="assemble"] .p1{animation:p2m-arrive 2.4s var(--ease) infinite}
  [data-pat="assemble"] .p2{animation:p2m-arrive 2.4s var(--ease) .12s infinite}
  [data-pat="assemble"] .p3{animation:p2m-arrive 2.4s var(--ease) .24s infinite}
  [data-pat="assemble"] .p4{animation:p2m-arrive 2.4s var(--ease) .36s infinite}
  @keyframes p2m-arrive{0%{opacity:0;transform:translateY(7px)}
    30%,100%{opacity:1;transform:translateY(0)}}
  /* 弹跳登场：预备（缩）→ 冲过 → 落定 */
  [data-pat="pop"] svg{transform-origin:center;
    animation:p2m-pop 2.4s var(--ease) infinite}
  @keyframes p2m-pop{0%{opacity:0;transform:scale(.72)}18%{transform:scale(.66)}
    42%{opacity:1;transform:scale(1.1)}56%,100%{opacity:1;transform:scale(1)}}
  /* 遮罩擦出：几何不变形，只是被逐渐露出来 */
  [data-pat="wipe"] svg{animation:p2m-wipe 2.4s var(--ease) infinite}
  @keyframes p2m-wipe{0%{clip-path:inset(0 100% 0 0)}
    55%,100%{clip-path:inset(0 0 0 0)}}
  /* 由基本形变形：先把基本形（那个圆）亮住一拍，再交给整体 */
  [data-pat="morph"] circle{animation:p2m-prim 2.4s var(--ease) infinite}
  [data-pat="morph"] path{animation:p2m-after 2.4s var(--ease) infinite}
  @keyframes p2m-prim{0%{transform:scale(1.9);opacity:1}
    30%{transform:scale(1.9)}55%,100%{transform:scale(1);opacity:1}}
  /* 目标形从来不是全透明的：动画在 0% 停一拍，而截图、reduced-motion 和还没
     注意到它在动的那一秒看到的都是那一拍 —— 只剩一个绿圆点，读不出「由基本
     形变成什么」。 */
  @keyframes p2m-after{0%,24%{opacity:.24}58%,100%{opacity:1}}
  /* 逐字上浮：间隔更密、每一笔更短 */
  [data-pat="cascade"] .p1{animation:p2m-rise 2.4s var(--ease) infinite}
  [data-pat="cascade"] .p2{animation:p2m-rise 2.4s var(--ease) .07s infinite}
  [data-pat="cascade"] .p3{animation:p2m-rise 2.4s var(--ease) .14s infinite}
  [data-pat="cascade"] .p4{animation:p2m-rise 2.4s var(--ease) .21s infinite}
  @keyframes p2m-rise{0%{opacity:0;transform:translateY(11px)}
    22%,100%{opacity:1;transform:translateY(0)}}
  .mtn-pat-stage [class^="p"]{transform-box:fill-box;transform-origin:center}
  .mtn-preset{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
  /* QA 证据：技能自己截的那几张全尺寸渲染图。计数是数字，图才是证据 —— 但证据
     以缩略图出场，点开才是全尺寸；把一张 1600px 的 PNG 铺满整块面板，看的人要
     滚三屏才能回到下一行字。 */
  .mtn-ev{margin-top:14px;display:grid;gap:8px}
  .mtn-evs{display:grid;grid-template-columns:repeat(auto-fill,minmax(146px,1fr));gap:10px}
  .mtn-ev figure{margin:0;display:grid;gap:5px;min-width:0}
  .mtn-ev a{display:block;height:104px;border:1px solid var(--line);
    border-radius:var(--r-md);background:var(--surface-2);overflow:hidden;
    transition:.13s var(--ease)}
  .mtn-ev a:hover{border-color:var(--line-2)}
  .mtn-ev img{display:block;width:100%;height:100%;object-fit:contain}
  .mtn-ev figcaption{font-size:9.5px;color:var(--ink-3);overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
  @media(prefers-reduced-motion:reduce){
    .mtn-pat-stage *,.mtn-pat-stage svg{animation:none!important;
      stroke-dashoffset:0!important;clip-path:none!important}}

/* ══════════════════════════ 留言板 ══════════════════════════
   一块板子：上面一个框，下面所有人说过的话。刻意窄 —— 这一页从头到尾都是
   正文，而 1240px 宽的一行正文读起来要来回甩头。 */
  .fb-wrap{max-width:720px}
  .fb-count{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
    margin:26px 2px 4px;min-height:15px}
  .fb-list{display:grid}
  .fb-i{display:grid;grid-template-columns:26px minmax(0,1fr);gap:11px;padding:14px 2px}
  .fb-i+.fb-i{border-top:1px solid var(--line)}
  /* 首字母那颗方块跟右上角的人用的是同一块（.who .av）—— 同一个人在两处必须
     长得一样。没署名的那些落回素色：匿名是一种状态，不是一个人。 */
  .fb-av{width:26px;height:26px;border-radius:7px;flex:none;display:grid;place-items:center;
    background:var(--accent-soft);color:var(--accent-ink);border:1px solid var(--accent-line);
    font-size:12px;font-weight:700}
  .fb-av[data-anon]{background:var(--surface-2);color:var(--ink-3);border-color:var(--line)}
  .fb-h{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px;min-height:19px}
  .fb-h b{font-size:12.5px;font-weight:600}
  .fb-h time{font-family:var(--mono);font-size:10.5px}
  /* pre-wrap：人分的段就是人的意思。anywhere：一条没有空格的长链接不该把
     整块板子撑宽。 */
  .fb-x{margin:0;font-size:13px;line-height:1.68;color:var(--ink);
    white-space:pre-wrap;overflow-wrap:anywhere}
  .fb-empty{display:grid;place-items:center;padding:38px 4px}

  /* ══════════════════════════ 翻译 Figma 页面 ══════════════════════════
     普查那一条是这一页的主角，所以它是唯一一块有自己排版的东西；其余全部
     借现成的（.chip 选语言、.tl 画阶段、.gen-row 摆历史、.seg.sm 切判定）。 */
  .tr-workbench{box-shadow:0 1px 0 rgba(20,25,30,.02),0 14px 40px rgba(20,25,30,.035)}
  .tr-rules{margin-bottom:18px;border:1px solid var(--line);border-radius:var(--r-lg);
    background:var(--surface);font-family:'OPPO Sans Web',var(--sans);overflow:hidden}
  .tr-rules>summary{display:flex;align-items:center;justify-content:space-between;gap:16px;
    padding:14px 18px;list-style:none;cursor:pointer;background:var(--surface-2)}
  .tr-rules>summary::-webkit-details-marker{display:none}
  .tr-rules>summary:hover{background:var(--surface-3)}
  .tr-rules>summary:focus-visible{outline-offset:-3px}
  .tr-rules-heading{display:grid;gap:2px;min-width:0}
  .tr-rules-heading strong{font-size:14px;font-weight:650}
  .tr-rules-heading>span{font-size:12px;color:var(--ink-2)}
  .tr-rules-toggle{display:flex;align-items:center;gap:8px;flex:none;font-size:12px;color:var(--accent-ink)}
  .tr-rules-toggle i{font-style:normal;font-size:18px;transition:transform .16s var(--ease)}
  .tr-rules-less,.tr-rules[open] .tr-rules-more{display:none}
  .tr-rules[open] .tr-rules-less{display:inline}
  .tr-rules[open] .tr-rules-toggle i{transform:rotate(180deg)}
  .tr-rules-body{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:28px;
    padding:20px 18px;border-top:1px solid var(--line);font-size:13px;line-height:1.7;overflow-wrap:anywhere}
  .tr-rules-body h2{margin-bottom:10px;font-size:14px;font-weight:650}
  .tr-rules-list{display:grid;gap:13px;list-style:none}
  .tr-rules-list li{display:grid;gap:2px}.tr-rules-list b{font-weight:600}
  .tr-rules-list span,.tr-rules-intro,.tr-rules-scenarios dd{color:var(--ink-2)}
  .tr-rules-scenarios{margin-top:10px;display:grid;gap:8px}
  .tr-rules-scenarios>div{padding:9px 12px;border-left:2px solid var(--accent-line);background:var(--accent-soft)}
  .tr-rules-scenarios dt{font-weight:600;color:var(--accent-ink)}
  .tr-rules-note{margin-top:10px;font-size:12px;color:var(--ink-2)}
  .tr-rules-examples{margin-top:14px;padding:12px;border:1px solid var(--line);border-radius:var(--r);
    background:var(--surface-2)}
  .tr-rules-examples h3{font-size:12px;font-weight:600;margin-bottom:8px}
  .tr-rules-examples p{display:grid;grid-template-columns:70px minmax(0,1fr);gap:8px;font-size:12px}
  .tr-rules-examples p+p{margin-top:7px}.tr-rules-examples p>span{color:var(--ink-2)}
  .tr-rules-examples q::before,.tr-rules-examples q::after{content:none}
  .tr-rules-scope{padding:12px 18px;border-top:1px solid var(--line);color:var(--ink-2);
    font-size:12px;line-height:1.65;background:var(--surface-2)}
  @media(max-width:800px){.tr-rules-body{grid-template-columns:1fr;gap:22px}}
  @media(max-width:480px){.tr-rules>summary{padding:12px;gap:10px}
    .tr-rules-body{padding:16px 12px}.tr-rules-scope{padding:12px}
    .tr-rules-heading>span{font-size:11.5px}.tr-rules-toggle{font-size:11.5px}}
  .tr-workbench>.ig-body{padding:0}
  .tr-source{padding:20px}
  .tr-section-head,.tr-history-head{display:flex;align-items:center;justify-content:space-between;
    gap:16px;margin-bottom:14px}
  .tr-section-head>div,.tr-history-head>div{display:grid;gap:3px}
  .tr-step{font:700 10px/1 var(--mono);letter-spacing:.09em;color:var(--accent-ink)}
  .tr-section-head .h2,.tr-history-head .h2{font-size:16px;letter-spacing:-.01em}
  .tr-readonly,.tr-setup-note,.tr-history-head>span{display:inline-flex;align-items:center;
    min-height:25px;padding:0 9px;border:1px solid var(--line);border-radius:99px;
    background:var(--surface-2);color:var(--ink-3);font-size:10.5px;white-space:nowrap}
  .tr-readonly::before{content:"";width:6px;height:6px;margin-right:6px;border-radius:99px;
    background:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
  .tr-bar{display:flex;gap:8px;align-items:center}
  .tr-bar input{flex:1;min-width:0;height:42px;border:1px solid var(--line-2);
    border-radius:var(--r-md);padding:0 13px;background:var(--surface);
    font-size:13px;color:var(--ink);outline:none;box-shadow:0 1px 2px rgba(20,25,30,.025)}
  .tr-bar>.btn{height:40px;padding-inline:16px}
  .tr-bar input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
  .tr-sheet-panel{margin-top:10px;border:1px solid var(--line);border-radius:var(--r-md);
    background:var(--surface);overflow:hidden;box-shadow:var(--lift)}
  .tr-sheet-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
    padding:12px 14px 10px}
  .tr-sheet-head>div{display:grid;gap:4px}.tr-sheet-head b{font-size:13.5px}
  .tr-sheet-head>button{width:28px;height:28px;border:1px solid var(--line);border-radius:99px;
    background:var(--surface);color:var(--ink-3);font-size:18px;cursor:pointer}
  .tr-sheet-head>button:hover{background:var(--surface-2);color:var(--ink)}
  .tr-sheet-tabs{display:flex;gap:4px;padding:0 14px 10px;border-bottom:1px solid var(--line)}
  .tr-sheet-tabs button{height:29px;padding:0 10px;border:1px solid transparent;border-radius:var(--r);
    background:transparent;color:var(--ink-3);font-size:11.5px;font-weight:600;cursor:pointer}
  .tr-sheet-tabs button:hover{color:var(--ink);background:var(--surface-2)}
  .tr-sheet-tabs button[aria-selected="true"]{border-color:var(--line-2);background:var(--surface-2);color:var(--ink)}
  .tr-sheet-manual{display:grid;gap:7px;padding:13px 14px}
  .tr-sheet-manual>label{font-size:11px;font-weight:650;color:var(--ink-2)}
  .tr-sheet-manual textarea{width:100%;min-height:112px;resize:vertical;padding:9px 10px;
    border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface-2);
    color:var(--ink);font:12px/1.6 var(--mono);outline:none}
  .tr-sheet-manual textarea:focus{border-color:var(--accent);background:var(--surface);box-shadow:0 0 0 3px var(--ring)}
  .tr-sheet-foot{display:flex;align-items:center;justify-content:space-between;gap:12px}
  .tr-sheet-foot>span{font-size:10.5px;color:var(--ink-3)}
  .tr-sheet-upload{display:grid;grid-template-columns:42px minmax(0,1fr) auto;align-items:center;
    gap:11px;margin:13px 14px;padding:14px;border:1px dashed var(--line-2);border-radius:var(--r-md);
    background:var(--surface-2)}
  .tr-sheet-fileicon{width:42px;height:42px;display:grid;place-items:center;border-radius:9px;
    background:var(--accent-soft);color:var(--accent-ink);font:700 10px/1 var(--mono)}
  .tr-sheet-upload>div:nth-child(2){display:grid;gap:3px}.tr-sheet-upload b{font-size:12.5px}
  .tr-sheet-upload span{font-size:10.5px;line-height:1.45;color:var(--ink-3)}
  .tr-sheet-status{display:flex;align-items:center;gap:7px;margin-top:9px;padding:9px 11px;
    border-radius:var(--r);background:var(--surface-2);font-size:11.5px;color:var(--ink-2)}
  .tr-sheet-status svg{width:14px;height:14px}.tr-sheet-status.bad{color:var(--brick);background:var(--brick-soft)}
  .tr-sheet-loaded{display:flex;align-items:center;justify-content:space-between;gap:12px;
    margin-top:9px;padding:9px 11px;border:1px solid var(--accent-line);border-radius:var(--r-md);
    background:var(--accent-soft)}
  .tr-sheet-loaded>div{display:flex;align-items:center;gap:9px}.tr-sheet-loaded>div:first-child>span{
    width:24px;height:24px;display:grid;place-items:center;border-radius:99px;background:var(--accent);color:#fff}
  .tr-sheet-loaded svg{width:13px;height:13px}.tr-sheet-loaded b,.tr-sheet-loaded small{display:block}
  .tr-sheet-loaded b{font-size:11.5px}.tr-sheet-loaded small{margin-top:2px;font-size:10.5px;color:var(--accent-ink)}
  .tr-sheet-loaded button{border:0;background:transparent;color:var(--accent-ink);font-size:10.5px;cursor:pointer}
  .tr-sheet-preview{display:grid;gap:5px;margin:0 14px 13px;padding-top:11px;border-top:1px solid var(--line)}
  .tr-sheet-preview>b{font-size:11px}.tr-sheet-preview>span{display:grid;grid-template-columns:22px 1fr;
    gap:7px;font-size:11px;color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tr-sheet-preview i{font:10px/1.5 var(--mono);font-style:normal;color:var(--ink-3);text-align:right}
  .tr-acc{display:flex;align-items:center;gap:7px;margin-bottom:10px;
    font-size:12px;color:var(--ink-3)}
  .tr-acc svg{width:15px;height:15px;flex:none}
  .tr-acc.ok{color:var(--accent-ink)}
  .tr-acc-a{display:inline-flex;gap:6px;margin-left:8px;vertical-align:middle}
  .tr-setup{padding:18px 20px 20px;border-top:1px solid var(--line);background:var(--surface-2)}
  .tr-setup .tr-section-head{margin-bottom:12px}
  .tr-options{display:grid;grid-template-columns:minmax(220px,.7fr) minmax(420px,2fr);gap:12px;
    align-items:stretch}
  .tr-option{min-width:0;padding:14px;border:1px solid var(--line);border-radius:var(--r-md);
    background:var(--surface)}
  .tr-option-head{display:grid;gap:3px;margin-bottom:12px}
  .tr-option-head .prd-k{font-size:12.5px;color:var(--ink)}
  .tr-option-head small{font-size:10.5px;line-height:1.45;color:var(--ink-3)}
  .tr-option-product{display:flex;flex-direction:column}
  .tr-option-product .chips{align-content:flex-start;height:100%}
  .tr-product-state{flex-basis:100%;align-self:flex-end;margin-top:auto;padding-top:16px;
    font-size:10.5px;line-height:1.45;color:var(--ink-3)}
  .tr-product-state::before{content:"";display:inline-block;width:5px;height:5px;margin:0 6px 1px 0;
    border-radius:99px;background:var(--accent)}
  .tr-lw{display:grid;gap:9px;min-width:0}
  .tr-lw>input{width:100%;height:36px;padding:0 11px;border:1px solid var(--line-2);
    border-radius:var(--r);background:var(--surface-2);color:var(--ink);font-size:12.5px;outline:none}
  .tr-lw>input:focus{border-color:var(--accent);background:var(--surface);box-shadow:0 0 0 3px var(--ring)}
  #tr-langs{display:grid;gap:9px}
  .tr-lang-summary{display:flex;align-items:center;justify-content:space-between;gap:12px;
    min-height:27px;color:var(--ink-3);font-size:10.5px}
  .tr-lang-summary>span b{font-family:var(--mono);font-size:11px;color:var(--ink)}
  .tr-lang-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap;justify-content:flex-end}
  .tr-lang-action{height:27px;padding:0 9px;border:1px solid var(--line);border-radius:var(--r);
    background:var(--surface);color:var(--ink-2);font-size:11px;font-weight:550;cursor:pointer}
  .tr-lang-action:hover{border-color:var(--line-2);background:var(--surface-2);color:var(--ink)}
  .tr-lang-action.tr-all{border-color:var(--accent-line);background:var(--accent-soft);color:var(--accent-ink)}
  .tr-lang-action.tr-all[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:#fff}
  .tr-lang-picked,.tr-lang-rest{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-height:29px}
  .tr-lang-rest{padding-top:9px;border-top:1px solid var(--line)}
  .tr-lang-empty{font-size:11px;color:var(--ink-3)}
  .tr-finish{padding:15px 20px 18px;border-top:1px solid var(--line);background:var(--surface)}
  .tr-actions{margin-top:0}
  .tr-actions #tr-run{min-width:112px}
  .tr-history-head{margin:22px 0 10px}
  .tr-history-list{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
  .tr-history-list .gen-row{min-height:62px;padding:10px 12px;border-radius:var(--r-md)}
  .tr-history-list .tr-th{height:40px;width:44px;display:grid;place-items:center;align-content:center;
    gap:2px;border-radius:9px;background:var(--surface-2)}
  .tr-history-list .tr-th b{font:650 12px/1 var(--mono);color:var(--ink-2)}
  .tr-history-list .tr-th small{font-size:8.5px;color:var(--ink-3);line-height:1}
  @media(max-width:860px){
    .tr-options,.tr-history-list{grid-template-columns:1fr}
    .tr-section-head{align-items:flex-start}
  }
  @media(max-width:620px){
    .tr-source,.tr-setup{padding:15px}.tr-finish{padding:13px 15px 15px}
    .tr-section-head{display:grid}.tr-readonly,.tr-setup-note{justify-self:start}
    .tr-bar{display:grid;grid-template-columns:1fr 1fr}.tr-bar input{grid-column:1/-1}
    .tr-bar>.btn{width:100%}
    .tr-lang-summary{align-items:flex-start;flex-direction:column}
    .tr-lang-actions{justify-content:flex-start}
    .tr-sheet-foot{align-items:stretch;flex-direction:column}.tr-sheet-foot>.btn{width:100%}
    .tr-sheet-upload{grid-template-columns:42px 1fr}.tr-sheet-upload>.btn{grid-column:1/-1;width:100%}
    .tr-sheet-loaded{align-items:flex-start;flex-direction:column}
  }
  /* 五格：会翻译 / 自动 / 换行 / 固定 / 需人工。auto-fit 让它窄屏自己折行，
     不必再写一条断点。 */
  .tr-cen{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1px;
    border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;
    background:var(--line);margin:14px 0 8px}
  .tr-c{--tr-tone:var(--ink-2);padding:14px 15px 12px;min-width:0;min-height:132px;
    background:var(--surface);position:relative;
    transition:background .13s var(--ease),box-shadow .13s var(--ease),transform .13s var(--ease)}
  .tr-c:hover{background:var(--surface-2);z-index:1}
  .tr-c[aria-expanded="true"]{background:var(--surface-2);
    box-shadow:inset 0 -3px 0 var(--tr-tone);z-index:2}
  .tr-c:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
  .tr-cn{display:flex;align-items:center;justify-content:space-between;gap:8px}
  .tr-c b{font-family:var(--mono);font-size:26px;font-weight:650;color:var(--tr-tone);
    letter-spacing:-.03em;line-height:1.1;font-variant-numeric:tabular-nums}
  .tr-cchev{width:22px;height:22px;border-radius:99px;display:grid;place-items:center;
    color:var(--ink-3);background:var(--surface-2);font-size:16px;line-height:1;
    transition:transform .16s var(--ease),background .13s var(--ease),color .13s var(--ease)}
  .tr-c:hover .tr-cchev{background:var(--surface-3);color:var(--ink)}
  .tr-c[aria-expanded="true"] .tr-cchev{transform:rotate(180deg);background:var(--tr-tone);color:var(--solid-ink)}
  .tr-cl{display:block;font-size:13px;font-weight:650;margin-top:7px;color:var(--ink)}
  .tr-c i{display:block;font-style:normal;font-size:11px;color:var(--ink-3);
    margin-top:3px;line-height:1.45;min-height:31px}
  .tr-c em{display:block;font-style:normal;font-size:10.5px;color:var(--tr-tone);
    margin-top:8px;font-weight:650}
  .tr-c.a{--tr-tone:var(--accent)}.tr-c.o{--tr-tone:var(--ochre)}.tr-c.b{--tr-tone:var(--brick)}
  .tr-cd{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);
    margin:0 0 9px;overflow:hidden;box-shadow:var(--lift)}
  .tr-cdh{display:flex;align-items:center;justify-content:space-between;gap:12px;
    padding:12px 14px 10px;background:var(--surface)}
  .tr-cdh>div{display:grid;gap:2px}.tr-cdh span{font-size:10.5px;color:var(--ink-3);
    text-transform:uppercase;letter-spacing:.08em;font-weight:650}
  .tr-cdh b{font-size:14px}.tr-cdx{width:28px;height:28px;display:grid;place-items:center;
    border:1px solid var(--line);border-radius:99px;color:var(--ink-3);font-size:18px;line-height:1}
  .tr-cdx:hover{background:var(--surface-2);color:var(--ink);border-color:var(--line-2)}
  .tr-cdt{display:grid;grid-template-columns:minmax(180px,1fr) auto auto;gap:10px;
    align-items:center;padding:0 14px 11px;border-bottom:1px solid var(--line)}
  .tr-cdt input{height:30px;min-width:0;border:1px solid var(--line-2);border-radius:var(--r);
    background:var(--surface-2);padding:0 10px;font-size:12px;color:var(--ink);outline:none}
  .tr-cdt input:focus{border-color:var(--accent);background:var(--surface);box-shadow:0 0 0 3px var(--ring)}
  .tr-cdt span,.tr-cde{font-size:11.5px;color:var(--ink-3)}
  #tr-census-shown{font-family:var(--mono);color:var(--ink-2);white-space:nowrap}
  .tr-cde{padding:13px 14px}
  .tr-cdl{max-height:360px;overflow:auto;overscroll-behavior:contain}
  .tr-ci{display:grid;grid-template-columns:32px minmax(0,1fr) auto auto 18px;
    gap:10px;align-items:center;padding:9px 13px;border-bottom:1px solid var(--line);
    color:inherit;text-decoration:none;transition:background .12s var(--ease)}
  .tr-ci:last-of-type{border-bottom:0}.tr-ci:hover{background:var(--surface-2)}
  .tr-cino{font-family:var(--mono);font-size:10.5px;color:var(--ink-3);text-align:right}
  .tr-cit{display:grid;gap:2px;min-width:0}.tr-cit b{font-size:12.5px;font-weight:550;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tr-cit small{font-size:10.5px;color:var(--ink-3);white-space:nowrap;overflow:hidden;
    text-overflow:ellipsis}
  .tr-cilang{font-size:10.5px;color:var(--ink-3);white-space:nowrap}
  .tr-cir{font-size:10.5px;color:var(--ink-2);padding:2px 6px;border-radius:4px;
    background:var(--surface-3);white-space:nowrap}
  .tr-ciout{font-size:12px;color:var(--ink-3);opacity:.35}.tr-ci:hover .tr-ciout{opacity:1;color:var(--accent-ink)}
  @media(max-width:720px){
    .tr-cen{grid-template-columns:repeat(2,minmax(0,1fr))}
    .tr-c{min-height:124px;padding:12px}
    .tr-cdt{grid-template-columns:1fr auto}.tr-cdt span:last-child{display:none}
    .tr-ci{grid-template-columns:24px minmax(0,1fr) 18px}.tr-cilang,.tr-cir{display:none}
  }
  .tr-sub{margin-bottom:2px}
  /* 普查的用法示例。用 details 是刻意的：第一次打开就看得见，学会之后一按收起，
     不把帮助内容永久塞在任务流里。 */
  .tr-cex{margin-top:10px;border:1px solid var(--line);border-radius:var(--r-lg);
    background:var(--surface);overflow:hidden}
  .tr-cex>summary{list-style:none;display:flex;align-items:center;justify-content:space-between;
    gap:12px;padding:10px 13px;cursor:pointer;user-select:none;background:var(--surface-2)}
  .tr-cex>summary::-webkit-details-marker{display:none}
  .tr-cex>summary>span{display:flex;align-items:baseline;gap:8px}
  .tr-cex>summary b{font-size:12.5px}.tr-cex>summary small{font-size:11px;color:var(--ink-3)}
  .tr-cex>summary>i{font-style:normal;font-size:16px;color:var(--ink-3);
    transition:transform .16s var(--ease)}
  .tr-cex[open]>summary>i{transform:rotate(180deg)}
  .tr-cexg{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;background:var(--line)}
  .tr-ce{--tr-example:var(--accent);padding:12px 13px;background:var(--surface);min-width:0}
  .tr-ce.watch{--tr-example:var(--ochre)}.tr-ce.risk{--tr-example:var(--brick)}
  .tr-ceh{display:flex;align-items:center;gap:7px}.tr-ceh>span{font-family:var(--mono);
    font-size:10px;color:var(--tr-example);font-weight:700}.tr-ceh b{font-size:12px}
  .tr-cecopy{display:flex;align-items:center;gap:7px;margin:9px 0 6px;min-width:0}
  .tr-cecopy span{padding:3px 7px;border-radius:4px;background:var(--surface-2);font-size:11px;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tr-cecopy i{font-style:normal;color:var(--ink-3)}
  .tr-ce p{font-size:11px;line-height:1.5;color:var(--ink-3);min-height:33px}
  .tr-ce strong{display:block;margin-top:7px;font-size:10.5px;color:var(--tr-example)}
  @media(max-width:760px){.tr-cexg{grid-template-columns:1fr}.tr-ce p{min-height:0}}
  /* ── 页目录 ────────────────────────────────────────────────────────────
     一页一行，自上而下读，和 Figma 自己的页面面板同构 —— 这个人整天看的就是
     那一栏。二十二个长短不一的中文页名摊成一片药丸没有阅读顺序，而「译成哪几
     种语言」用的正是药丸：两个完全不同的问题不能长成同一个形状。
     行的样子借资源清单那一栏（.as-row）—— 不新造一套视觉。 */
  /* 高度截断在一行的中间是故意的 —— 半行是「下面还有」唯一不用多画一个东西
     就能说出口的写法。 */
  .tr-pl{margin-top:10px;max-height:min(46vh,322px);overflow-y:auto;
    background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md)}
  .tr-pl:empty{display:none}
  .tr-p{display:grid;grid-template-columns:19px minmax(0,1fr) auto;gap:10px;
    align-items:center;width:100%;padding:7px 11px;text-align:left;
    background:transparent;border-bottom:1px solid var(--line);
    transition:background .12s var(--ease)}
  .tr-p:last-child{border-bottom:0}
  .tr-p:hover{background:var(--surface-2)}
  .tr-p[aria-checked="true"]{background:var(--accent-soft)}
  /* 正在看的那一页。它比「勾上了」更重要，所以标记也得更重 —— 一条 3px 的
     竖杠加一块底色，眯着眼也认得出。 */
  .tr-p[aria-current="true"]{background:var(--surface-2);
    box-shadow:inset 3px 0 0 var(--accent)}
  .tr-pn{font-size:12.5px;color:var(--ink);white-space:nowrap;overflow:hidden;
    text-overflow:ellipsis}
  .tr-p[aria-checked="true"] .tr-pn{font-weight:600;color:var(--accent-ink)}
  .tr-p[aria-current="true"] .tr-pn{font-weight:600}
  .tr-pt{font-family:var(--mono);font-size:10.5px;color:var(--ink-3);white-space:nowrap}
  .tr-pt.q{color:var(--accent-ink);font-weight:600}
  /* 左边那一格：能勾的才画成勾选框，已经在手上的画一个对勾（那是状态，不是
     控件），其余留空 —— 一个点了什么都不做的方框比没有方框更难懂。 */
  .tr-pk{width:19px;height:19px;display:grid;place-items:center;color:transparent}
  .tr-pk svg{width:11px;height:11px}
  .tr-pk.box{border:1.5px solid var(--line-2);border-radius:5px;background:var(--surface)}
  .tr-p:hover .tr-pk.box{border-color:var(--accent)}
  .tr-p[aria-checked="true"] .tr-pk.box{background:var(--accent);border-color:var(--accent);color:#fff}
  .tr-pk.on{color:var(--accent)}
  /* 设计师在页目录里打的分隔条（————————————）。它不是一页 —— 画成一小段
     凹进去的空带，而不是第三条细线挤在上下两行的边框中间。 */
  .tr-pd{height:9px;background:var(--surface-2);border-bottom:1px solid var(--line)}
  /* 一栏空目录只说一句话，不是一个空状态插画 —— 它要让「没有」看起来像一个
     答案，而不是像一次加载失败。 */
  .tr-pe{padding:11px 2px;font-size:12px;color:var(--ink-3)}
  /* 取回进度：一行字，跟按钮占同一个位置 —— 它替换按钮，不是挤在旁边。 */
  .tr-fx{display:inline-flex;align-items:center;gap:6px;font-size:12px;
    color:var(--ink-2)}
  .tr-fx svg{width:13px;height:13px;opacity:.7}
  /* 页队列：一行，一颗小按钮加一句话 —— 不是一个带标题的区块。 */
  .tr-q{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:9px}
  .tr-q:empty{display:none}
  /* ── 交付 ──────────────────────────────────────────────────────────
     退回过三次的那一块。前两版的毛病是同一个：这些类名一个都没有样式，所以
     「输入 <key> 输出 <状态>」是贴着左边的裸文字，而四个同样粗细的按钮挤在
     行尾，看不出哪个才是这一行的目的。

     每种语言单独一张卡：语言与进度、文件交付、复核入口分行排列。
     Excel 是带截图的交付入口，纯文本格式折叠在「其他格式」中。 */
  .tr-src{display:grid;gap:4px;padding:13px 14px;margin-top:12px;
    border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);
    color:inherit;text-decoration:none;transition:border-color .13s var(--ease),background .13s var(--ease),box-shadow .13s var(--ease)}
  a.tr-src{cursor:pointer}a.tr-src:hover{border-color:var(--line-2);background:var(--surface);box-shadow:var(--lift)}
  a.tr-src:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  .tr-src-top{display:flex;align-items:center;justify-content:space-between;gap:14px}
  .tr-srct{font-size:13.5px;font-weight:600;color:var(--ink);min-width:0;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .tr-srcopen{flex:none;display:inline-flex;align-items:center;min-height:28px;padding:0 10px;
    border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);
    color:var(--ink-2);font-size:11px;font-weight:600}
  a.tr-src:hover .tr-srcopen{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink)}
  .tr-srcf{font-size:12px;color:var(--ink-2)}
  .tr-srcw{font-size:11.5px;color:var(--ink-3)}
  .tr-src-sheet{border-color:var(--accent-line);background:var(--accent-soft)}
  .tr-src-sheet .tr-srcopen{border-color:var(--accent-line);background:var(--surface);color:var(--accent-ink)}
  .tr-rows{display:grid;gap:12px;margin-top:10px}
  .tr-d{display:flex;flex-direction:column;gap:14px;padding:18px;
    border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);
    font-family:'OPPO Sans Web',var(--sans)}
  .tr-dhead{display:flex;align-items:center;justify-content:space-between;gap:12px}
  .tr-dinfo{min-width:0;display:grid;gap:5px}
  /* 一次写回没成，不代表这一种语言译坏了 —— 译文是好的，能下载、能重写。
     所以整块不染红，只有那一句原因是红的。 */
  .tr-d.bad{border-color:var(--brick-line)}
  .tr-dn{font-size:15px;font-weight:600;display:inline-flex;
    align-items:center;gap:6px;min-width:0}
  .tr-da{display:flex;align-items:center;justify-content:flex-end;gap:9px;flex-wrap:wrap}
  .tr-da:empty{display:none}
  /* 「重写一页」是备选，不是主动作 —— 那一页已经在文件里了，多数时候人要的是
     打开它。所以它是一条文字链接，不是第二颗按钮。 */
  .tr-again{font-size:11.5px;color:var(--ink-3);border-bottom:1px solid transparent}
  .tr-again:hover{color:var(--ink-2);border-bottom-color:var(--line-2)}
  .tr-dm{font-size:12px;color:var(--ink-2);min-width:0;
    display:flex;align-items:center;gap:7px;flex-wrap:wrap}
  .tr-dms{min-width:0;overflow-wrap:anywhere}
  .tr-dexports{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
    padding-top:14px;border-top:1px solid var(--line)}
  .tr-dexports>.btn{min-height:36px;font-size:12.5px}
  .tr-export-note{flex-basis:100%;margin:0;font-size:12px;color:var(--ink-3);line-height:1.6}
  .tr-formats{margin-left:auto;color:var(--ink-2);font-size:12px}
  .tr-formats summary{padding:8px 0;cursor:pointer}
  .tr-format-links{display:flex;gap:14px;flex-wrap:wrap;padding:4px 0}
  .tr-dj{font-size:12px;color:var(--ink-2);text-decoration:none;
    border-bottom:1px solid transparent}
  .tr-dj:hover{color:var(--ink-2);border-bottom-color:var(--line-2)}
  .tr-dw{font-size:12px;color:var(--ink-3)}
  .tr-dchecks{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
  .tr-dchecks>.tr-again{font-size:12px;padding:5px 0;color:var(--ink-2)}
  .tr-dchecks>.tr-look{margin:0}
  /* 一次好几页时，每一页一颗小片。写好的能点，没写的灰着，失败的是砖红 ——
     这一排同时就是进度：写回一页要两分钟，人需要看得见它走到哪了。 */
  /* 「要看一眼的那些」。和事实那一行分开，因为它们性质不同：一个是「做成了
     多少」，一个是「我动了什么 / 哪里可能不对」，后者需要人去看。所以它是一颗
     可点的东西而不是灰字，而且不抢主按钮 —— 主按钮是「打开 Figma」。 */
  .tr-look{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:6px;margin-top:7px;
    padding:0;background:none;border:0;cursor:pointer;text-align:left}
  .tr-look>span{padding:2px 8px;border-radius:var(--r);font-size:11.5px;
    color:var(--ink-2);background:var(--surface-2);
    border:1px solid var(--line)}
  .tr-look:hover>span{border-color:var(--line-2);color:var(--ink-1)}
  .tr-look:focus-visible{outline:2px solid var(--line-2);outline-offset:2px}
  .tr-dp{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:5px;margin-top:7px}
  .tr-pg{padding:2px 8px;border-radius:var(--r);font-size:11.5px;
    border:1px solid var(--line);color:var(--ink-3);background:var(--surface-2);
    text-decoration:none;max-width:180px;overflow:hidden;text-overflow:ellipsis;
    white-space:nowrap}
  a.tr-pg.on{border-color:var(--line-2);background:var(--surface);color:var(--ink)}
  a.tr-pg.on:hover{border-color:var(--ink-3)}
  .tr-pg.bad{border-color:var(--brick-line);color:var(--brick);
    background:var(--brick-soft)}
  /* 失败原因是一整句话，所以它占满整块的宽度并且换行 —— 它以前是按钮旁边
     一个 title="…"，等于没说。 */
  .tr-de{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:8px 16px;
    align-items:baseline;margin-top:0;padding-top:12px;
    border-top:1px solid var(--brick-line);font-size:12px;color:var(--brick);
    line-height:1.5}
  .tr-de summary{cursor:pointer}.tr-de details{min-width:0}
  .tr-de details[open]{flex-basis:100%}.tr-de p{margin:8px 0 0;overflow-wrap:anywhere}
  /* ── 还可以再看看 ──────────────────────────────────────────────────
     三个数。它们从来不挡交付，所以上面先有一句话说明这件事，再让每个数带上
     自己的单位 —— 「术语 0/40」读的人根本不知道那是什么。 */
  .tr-todo{margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
  .tr-review-heading{display:block;margin-bottom:6px;font-size:13px;font-weight:600}
  .tr-todk{font-size:11.5px;color:var(--ink-3);margin-bottom:8px}
  .tr-tabs{display:flex;gap:7px;flex-wrap:wrap}
  .tr-tt{display:inline-flex;align-items:center;gap:7px;height:29px;padding:0 11px;
    border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);
    font-size:12.5px;color:var(--ink-2);transition:.13s var(--ease)}
  .tr-tt:hover{border-color:var(--ink-3);color:var(--ink)}
  .tr-tt b{font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
  .tr-tt.on{border-color:var(--accent);background:var(--accent-soft);
    color:var(--accent-ink)}
  .tr-tt.on b{color:var(--accent-ink)}
  /* 术语表：两列，右边可改。 */
  .tr-gl{display:grid;grid-template-columns:minmax(78px,auto) minmax(0,1fr);
    gap:5px 10px;align-items:center;margin-top:14px;max-height:240px;overflow-y:auto}
  .tr-gt{font-size:12px;color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;
    white-space:nowrap}
  .tr-gv{height:27px;border:1px solid var(--line);border-radius:var(--r);
    padding:0 8px;background:var(--surface);font-size:12.5px;color:var(--ink);outline:none}
  .tr-gv:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
  /* 一行右边是输入框加一颗「不翻」。钉住原词时输入框自己也要看得出来 —— 光
     靠按钮的按下态，扫一眼四十行是分不出哪些定了的。 */
  .tr-gr{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px;align-items:center}
  .tr-gv.kept{color:var(--ink-3);font-style:italic}
  .tr-gk{height:27px;padding:0 9px;border:1px solid var(--line-2);border-radius:var(--r);
    font-size:11.5px;color:var(--ink-3);background:var(--surface);white-space:nowrap}
  .tr-gk:hover{border-color:var(--ink-3);color:var(--ink-2)}
  .tr-gk.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink)}
  /* 这条词是谁定的：全局 / 产品。不标出来人会把上层定好的再打一遍。 */
  .tr-gsrc{margin-left:6px;padding:1px 5px;border-radius:4px;font-size:10px;
    background:var(--surface-3);color:var(--ink-3)}
  .tr-gsave{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:10px}
  /* 判定不确定的那些，一条一行 */
  .tr-unsure{display:grid;gap:4px;margin-top:12px;max-height:240px;overflow-y:auto}
  .tr-u{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto;
    gap:10px;align-items:center;padding:5px 9px;border:1px solid var(--line);
    border-radius:var(--r)}
  .tr-ut{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* 会撑破盒子的那些 */
  .tr-risk{display:grid;gap:4px;margin-top:14px;max-height:300px;overflow-y:auto}
  .tr-rk{display:grid;grid-template-columns:40px minmax(0,1fr) auto;gap:10px;
    align-items:center;padding:5px 9px;border-top:1px solid var(--line)}
  .tr-rt{min-width:0;display:grid;gap:1px}
  .tr-rt b{font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;
    white-space:nowrap}
  /* 该怎么办，跟在「哪里挤」后面。没有这一句，人会先去改文案 —— 那是最贵的
     那一档，而且改出来的常常不是话（"Gen Image"）。 */
  .tr-fix2{grid-column:1/-1;font-size:11.5px;color:var(--ink-2);
    margin-top:3px;padding-left:50px}
  .tr-th{display:grid;place-items:center;font:600 11px/1 var(--mono);color:var(--ink-3)}
  @media(max-width:700px){
    /* 窄屏：主动作换到自己一行、撑满 —— 挤在名字右边它会缩成一条看不清的
       东西，而它是这一块里唯一要按的地方。 */
    .tr-d{padding:14px}
    .tr-dhead{align-items:flex-start;flex-direction:column}
    .tr-da{justify-content:flex-start}
    .tr-dexports>.btn{flex:1 1 180px}
    .tr-formats{margin-left:0;flex-basis:100%}
    .tr-u{grid-template-columns:1fr auto}.tr-u>.mono{display:none}}

/* ══════════════════════════ 对话框插件 ══════════════════════════
   每一页右下角的同一个框（见 76/77/78-chat*.js）。

   收起来时是一颗按钮，不是一块面板 —— 这套界面已经因为「一个简单动作占掉一整
   块地方」被退回过三次，而这个框在大多数时刻是不该说话的那一个。

   z-index 挑在 70：盖得住内容和窄屏抽屉的幕布（60），压在提示条（80）和两道
   门屏之下 —— 一个浮在访问码屏上面的对话框，问的是还不存在的那一页。 */
.chat{position:fixed;right:18px;bottom:18px;z-index:70;display:flex;
  flex-direction:column;align-items:flex-end;gap:10px;pointer-events:none}
.chat>*{pointer-events:auto}
/* 画布那一页右边是一条 302px 的固定栏（.cv-body），压在它上面就把节点属性挡住
   了。1080 以下那条栏本来就不显示，所以让位也一起撤掉。 */
@media(min-width:1081px){.chat[data-view="canvas"]{right:320px}}

.chat-fab{width:46px;height:46px;flex:none;border-radius:50%;display:grid;
  place-items:center;background:var(--solid);color:var(--solid-ink);
  box-shadow:var(--pop);border:1px solid transparent;
  transition:transform .18s var(--ease),background .18s var(--ease)}
.chat-fab:hover{background:var(--solid-hover);transform:translateY(-1px)}
.chat-fab svg{width:21px;height:21px}
.chat-fab[aria-expanded="true"]{background:var(--surface);color:var(--ink);
  border-color:var(--line-2);box-shadow:var(--lift)}

.chat-p{width:min(378px,calc(100vw - 36px));max-height:min(560px,calc(100dvh - 108px));
  display:flex;flex-direction:column;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--pop);
  overflow:hidden}
.chat-p[data-over="1"]{border-color:var(--accent);box-shadow:0 0 0 3px var(--ring),var(--pop)}
.chat-h{display:flex;align-items:center;gap:6px;padding:9px 8px 9px 13px;
  border-bottom:1px solid var(--line);background:var(--surface-2)}
.chat-h-t{display:grid;gap:1px;min-width:0;flex:1}
.chat-h-t b{font-size:12.5px;font-weight:600;letter-spacing:-.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chat-h-t .tls{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.chat-l{flex:1;min-height:132px;overflow-y:auto;padding:13px;display:grid;
  gap:12px;align-content:start}
.chat-empty{display:flex;flex-wrap:wrap;gap:7px}
.chat-empty p{width:100%;margin-bottom:2px}
.chat-r{display:grid;grid-template-columns:22px minmax(0,1fr);gap:9px;align-items:start}
.chat-av{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;
  background:var(--surface-3);color:var(--ink-2);font-size:10.5px;font-weight:600}
.chat-av svg{width:13px;height:13px}
.chat-r.me .chat-av{background:var(--accent-soft);color:var(--accent-ink)}
.chat-x{display:grid;gap:6px;min-width:0;font-size:12.5px;line-height:1.6}
.chat-x p{white-space:pre-wrap;overflow-wrap:anywhere}
.chat-r.me .chat-x p{color:var(--ink-2)}
.chat-imgs{display:flex;gap:6px;flex-wrap:wrap}
.chat-imgs img{width:52px;height:52px;object-fit:cover;border-radius:var(--r);
  border:1px solid var(--line)}
.chat-x .ig-spinner{width:14px;height:14px;flex:0 0 14px;border-width:2px;
  display:inline-block;vertical-align:-2px;margin-right:6px}

/* 一颗动作。做过的那一颗留在原地并且按不动 —— 拿掉它，这一轮读起来就像什么都
   没发生；留一颗还能再按的，就是同一件事会被做第二遍。 */
.chat-acts{display:flex;gap:6px;flex-wrap:wrap;margin-top:1px}
.chat-act{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;
  border:1px solid var(--line-2);border-radius:99px;background:var(--surface);
  font-size:11.5px;line-height:1.5;color:var(--ink)}
button.chat-act:hover{border-color:var(--accent-line);background:var(--accent-soft);
  color:var(--accent-ink)}
button.chat-act[disabled]{opacity:.45;cursor:default}
.chat-act[data-state="done"]{border-style:dashed;color:var(--ink-3);background:none}
.chat-act[data-state="failed"]{border-color:var(--ochre-line);color:var(--ochre);
  background:var(--ochre-soft)}

.chat-c{border-top:1px solid var(--line);padding:9px 10px 10px;display:grid;gap:8px}
/* 「正在看：…」。一行小字加一颗关掉它的小按钮 —— 不是一块面板：它说的是这个
   框以为你在指哪一个，那是一句话的分量，不是一个区域的分量。长了就省略号，
   永远不换行（一行变两行，输入框就往下跳）。 */
.chat-lens{display:flex;align-items:center;gap:4px;min-width:0;
  font-size:11px;line-height:1.5;color:var(--ink-3)}
.chat-lens-t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chat-lens button{flex:none;display:grid;place-items:center;height:17px;min-width:17px;
  padding:0 4px;border-radius:99px;color:var(--ink-3);font-size:11px}
.chat-lens button:hover{background:var(--surface-3);color:var(--ink)}
.chat-lens button svg{width:9px;height:9px}
.chat-lens[data-off="1"] .chat-lens-t{font-style:italic}
.chat-atts{display:flex;gap:6px;flex-wrap:wrap}
.chat-att{position:relative;display:inline-flex}
.chat-att img{width:44px;height:44px;object-fit:cover;border-radius:var(--r);
  border:1px solid var(--line-2)}
.chat-att button{position:absolute;top:-5px;right:-5px;width:17px;height:17px;
  border-radius:50%;display:grid;place-items:center;background:var(--solid);
  color:var(--solid-ink);box-shadow:var(--lift)}
.chat-att button svg{width:9px;height:9px}
#chat-text{width:100%;resize:none;border:1px solid var(--line);border-radius:var(--r);
  background:var(--surface-2);padding:8px 10px;font-size:12.5px;line-height:1.55;
  max-height:132px}
#chat-text:focus{outline:none;border-color:var(--accent-line);box-shadow:0 0 0 3px var(--ring)}
.chat-c-r{display:flex;align-items:center;gap:7px}
.chat-c-r .sp{margin-left:auto}
.chat-send{display:inline-flex;align-items:center;gap:6px}
.chat-send svg{width:13px;height:13px}

/* 窄屏：铺满宽度，但不铺满高度 —— 一个占满整屏的对话框，等于把它正在改的那
   一页藏起来了，而这个框存在的意义就是「看着它变」。 */
@media(max-width:520px){
  .chat{left:12px;right:12px;bottom:12px;align-items:stretch}
  .chat-fab{align-self:flex-end}
  .chat-p{width:100%;max-height:min(64dvh,470px)}
}

/* ── 装进手机（85d-apk.js）：一行输入，一张二维码 ───────────────────────── */
.apk-compose{padding:8px}
.apk-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.apk-bar .apk-attach{margin:0;flex-basis:100%}.apk-bar .apk-attach:empty{display:none}
.apk-select,#apk-name{height:27px;border:1px solid var(--line-2);border-radius:var(--r-md);background:var(--surface);
  color:var(--ink);font-size:12.5px;padding:0 9px}
.apk-select{max-width:260px}#apk-name{width:210px;flex:1;min-width:140px}
.apk-select:focus,#apk-name:focus{outline:0;border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
.apk-card{display:grid;grid-template-columns:auto minmax(0,1fr);gap:18px;align-items:start}
.apk-qr{width:148px;height:148px;border:1px solid var(--line);border-radius:var(--r-md);background:#fff;padding:6px;box-sizing:border-box;display:block}
.apk-qr-x{display:grid;place-items:center;color:var(--ochre);background:var(--surface-2)}
.art-a.apk-art{align-items:center;gap:12px}.art-a.apk-art .apk-qr{width:96px;height:96px;padding:4px}
.apk-art-b{display:flex;gap:6px;flex-wrap:wrap}
.apk-why{margin:4px 0 0;padding-left:18px;overflow-wrap:anywhere}.apk-why>li+li{margin-top:2px}
.apk-why-more>summary{cursor:pointer;margin-top:4px;color:var(--ink-3)}
@media(max-width:520px){.apk-card{grid-template-columns:1fr}.apk-qr{width:180px;height:180px}}

/* ── 产品（85e-product.js）：一行新建，三块面板，静态输入框 ─────────────── */
.pd-in{height:27px;border:1px solid var(--line-2);border-radius:var(--r-md);background:var(--surface);color:var(--ink);font-size:12.5px;padding:0 9px;min-width:160px}
.pd-in.grow{flex:1;min-width:200px}.pd-in.wide{width:100%;margin-top:8px}
.pd-in:focus{outline:0;border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
.pd-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:11px}
.pd-head{display:flex;justify-content:space-between;gap:14px;align-items:flex-start;flex-wrap:wrap}
.pd-members{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.pd-mem{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:10px;align-items:start;padding:9px 0;border-top:1px solid var(--line)}
.pd-mem:first-child{border-top:0;padding-top:0}
.pd-mem-b{display:grid;gap:3px;font-size:13px;line-height:1.5;word-break:break-word;white-space:pre-wrap}
.pd-mem .x,.pd-members .x{width:18px;height:18px;border-radius:99px;color:var(--ink-3)}
.pd-mem .x:hover,.pd-members .x:hover{background:var(--surface-2);color:var(--ink)}
#pd-direction{width:100%;min-height:72px;margin-top:10px;padding:10px 12px;border:1px solid var(--line-2);border-radius:var(--r-md);background:var(--surface);color:var(--ink);font:inherit;font-size:13px;line-height:1.5;resize:vertical;box-sizing:border-box}
#pd-direction:focus{outline:0;border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}

/* 产品：改一条记录、风格参考链接 */
.pd-refs{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}.pd-refs:empty{display:none}
.pd-refs .ig-msg a{color:var(--accent-ink);text-decoration:none;max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pd-mem-acts{display:flex;gap:2px}
.pd-mem.editing{grid-template-columns:auto minmax(0,1fr) 0;align-items:start}
#pd-edit-text{width:100%;min-height:64px;padding:8px 10px;border:1px solid var(--accent);border-radius:var(--r-md);background:var(--surface);color:var(--ink);font:inherit;font-size:13px;line-height:1.5;resize:vertical;box-sizing:border-box}

/* 原生 Java 的进度日志、预览设备面板（85d / 85f） */
.apk-log{margin:8px 0 0;padding:8px 10px;max-height:120px;overflow:auto;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);font-family:var(--mono);font-size:11px;line-height:1.45;white-space:pre-wrap;word-break:break-all;color:var(--ink-2)}
.dev-wrap{display:grid;grid-template-columns:auto minmax(0,1fr);gap:20px;align-items:start}
.dev-phone{position:relative;width:272px;padding:11px;border-radius:26px;background:#141416;box-shadow:var(--lift)}
.dev-phone img{display:block;width:250px;min-height:556px;border-radius:16px;background:#000;user-select:none}
.dev-phone canvas{display:block;width:250px;min-height:556px;border-radius:16px;background:#000}
.dev-overlay{position:absolute;inset:11px;border-radius:16px;cursor:crosshair}
.dev-side{display:grid;gap:10px;align-content:start}
.dev-keys{display:flex;gap:6px;flex-wrap:wrap}
/* 调样式（85f-device.js tweakView）：一栏表单，色块按用得多少排 */
.dev-tweaks{display:grid;gap:8px;padding-top:10px;border-top:1px solid var(--line)}
.tw-head{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap}
.tw-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12px}
.tw-row>span:first-child{min-width:72px;color:var(--ink-3)}
.tw-row input[type=range]{flex:1;min-width:120px}
.tw-row select{font-size:12px;padding:3px 6px;border:1px solid var(--line);border-radius:6px;background:var(--paper,#fff)}
.tw-swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:6px}
.tw-sw{display:flex;align-items:center;gap:6px;font-size:11px;cursor:pointer}
.tw-sw input[type=color]{width:28px;height:22px;padding:0;border:1px solid var(--line);border-radius:6px;background:none;cursor:pointer}
@media(max-width:640px){.dev-wrap{grid-template-columns:1fr}.dev-phone{width:222px}.dev-phone img{width:200px;min-height:444px}}

/* ── 控件库（95c-widgets.js）：一页图册，不是一张报表 ──────────────────────
   控件的第一身份是长什么样，所以卡片最大的一块是设计稿里的真实裁图，代码点开
   才有。展开的那一张横跨整行（grid-column:1/-1）并分成两栏：左边还是图和事实，
   右边是代码。不做浮层 —— 这一页上的动作只有「看看它」，一个盖住整屏的对话框
   对这件事太重了。 */
/* 目录一栏 + 卡片墙。884 个控件（其中 825 个是 ColorOS 控件库）没法挂在一排
   档位条上 —— 那一排只剩「它做什么」，「它是什么」搬到左边这一栏的目录里。
   窄屏把目录放回上面：两栏各 106px 的时候，两边都读不了。 */
.wg-page{display:grid;grid-template-columns:214px minmax(0,1fr);gap:20px;align-items:start}
.wg-index{position:sticky;top:61px;max-height:calc(100vh - 92px);overflow:auto;
  display:grid;gap:1px;align-content:start;margin-top:16px;padding-right:2px}
.wg-index::-webkit-scrollbar{width:6px}
.wg-index::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:3px}
.wg-dir{display:flex;align-items:center;gap:6px;width:100%;padding:4px 7px;border:0;
  border-radius:5px;background:transparent;color:var(--ink-2);font-size:12px;line-height:1.35;
  text-align:left;cursor:pointer;min-width:0;font-family:inherit}
.wg-dir:hover{background:var(--surface-2);color:var(--ink)}
.wg-dir[aria-current="true"]{background:var(--surface-2);color:var(--ink);font-weight:600}
.wg-dir .tw{flex:none;width:9px;font-size:8px;color:var(--ink-3);line-height:1}
.wg-dir .n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wg-dir .c{flex:none;font-family:var(--mono);font-size:10.5px;color:var(--ink-3)}
.wg-dir.d1{padding-left:18px}
.wg-dir.d2{padding-left:30px;font-size:11.5px;color:var(--ink-3)}
.wg-more{color:var(--ink-3);font-size:11px;text-decoration:underline;text-underline-offset:2px}
.wg-dir.d2[aria-current="true"],.wg-dir.d2:hover{color:var(--ink)}
@media(max-width:900px){
  .wg-page{grid-template-columns:1fr}
  .wg-index{position:static;max-height:190px;border:1px solid var(--line);
    border-radius:var(--r-md);padding:6px;margin-bottom:2px}
}
/* 结论、判据，以及判据的出处。一行三段，不是一块面板 —— 弹窗里本来就只有一个
   控件，说清它能不能点不需要给它一个自己的区。 */
.wg-does{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:9px 11px;
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2)}
.wg-does-w{font-size:11.5px;color:var(--ink-2)}
.wg-chain{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10px}
.wg-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(196px,1fr));gap:10px;margin-top:16px}
.wg-card{display:grid;gap:8px;align-content:start;padding:11px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);min-width:0;transition:border-color .13s var(--ease)}
.wg-card:hover{border-color:var(--line-2)}
/* 卡片不再就地展开（详情走弹窗），只留一个「这张是打开的那张」的记号。 */
.wg-card.is-open{border-color:var(--ink-3)}
/* .wg-head 没了（卡片不再分「头/身」两段），间距由 .wg-card 自己的 grid 给。 */
.wg-cta{margin-top:auto}
/* 裁图本身就是「看代码」那颗按钮。底色用页面的第二层而不是白：白底的控件放在
   白卡片上看不出自己的边界。 */
/* 媒体区是固定高度，不是 min-height —— 这一页是一面瓦片墙，一张 40px 的裁图和一张
   148px 的裁图各自决定卡片多高，整排就参差不齐，再加上会自己长高的实例框，就是
   「整块都很丑」的来源。高度写死，里面的东西居中并 contain，墙就齐了。 */
.wg-shot{display:grid;place-items:center;gap:6px;width:100%;height:136px;padding:10px;border:0;border-radius:var(--r-md);background:var(--surface-2);cursor:pointer;overflow:hidden}
.wg-img{display:block;max-width:100%;max-height:100%;object-fit:contain;border-radius:3px}
.wg-shape{display:block;max-width:100%}
.wg-noshot{font-size:10px;line-height:1.4;color:var(--ink-3);text-align:center}
.wg-t{font-size:12.5px;font-weight:600;color:var(--ink)}
.wg-facts{font-size:10.5px;color:var(--ink-3)}
.wg-facts i{font-style:normal;opacity:.5}
.wg-facts .mono{font-size:10.5px}
/* 判据摆在卡片上，不只在悬停里：「不能点的为啥不能点」问的就是这一句。两行封顶
   —— 它是个说明，不是一段文档。 */
.wg-why{font-size:10.5px;line-height:1.45;color:var(--ink-3);display:-webkit-box;
  -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.wg-words{display:flex;gap:4px;flex-wrap:wrap;min-width:0}
.wg-words span{max-width:100%;padding:1px 6px;border-radius:4px;background:var(--surface-2);color:var(--ink-3);font-size:10.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wg-body{display:grid;gap:8px;min-width:0;align-content:start}
.wg-code-h{display:flex;gap:9px;align-items:center;flex-wrap:wrap}
.wg-code{margin:0;padding:10px 12px;max-height:360px;overflow:auto;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);font-family:var(--mono);font-size:11px;line-height:1.55;white-space:pre;color:var(--ink-2)}
/* 设计系统那一侧：卡片上一行（叫什么），展开后两档（何时用、画成什么样）。
   一行而不是一块 —— 控件库这一页的主体是图，多长一个面板出来就把图挤走了。 */
.wg-bridge{font-size:10.5px;color:var(--ink-3);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wg-bridge b{color:var(--ink-2);font-weight:600}
.wg-bridge i{font-style:normal;opacity:.5}
.wg-bridge .mono{font-size:10px}
.wg-doc{display:grid;gap:9px;min-width:0}
.wg-when{margin:0;padding:0;list-style:none;display:grid;gap:6px}
.wg-when li{position:relative;padding-left:13px;font-size:12px;line-height:1.6;color:var(--ink-2)}
.wg-when li::before{content:"";position:absolute;left:0;top:.62em;width:5px;height:5px;border-radius:50%;background:var(--line-2)}
/* 设计导出图落在棋盘上：ColorOS 的控件大多带透明背景，纯白底上看不出边界。 */
.wg-figma{max-width:100%;height:auto;display:block;border:1px solid var(--line);border-radius:var(--r-md);
  background-color:var(--surface);background-image:
    linear-gradient(45deg,var(--surface-2) 25%,transparent 25%,transparent 75%,var(--surface-2) 75%),
    linear-gradient(45deg,var(--surface-2) 25%,transparent 25%,transparent 75%,var(--surface-2) 75%);
  background-size:12px 12px;background-position:0 0,6px 6px}
/* 可交互的实例：ColorOS kit 的标记跑在自己的 CSS 世界里，所以是 iframe。
   高度由里面报回来（95c-widgets.js 的 message 监听），先给一个不难看的起手高度。 */
/* 卡片左格里的实例：高度由里面报回来，但先给一个和裁图差不多的起手高度，
   否则 884 张卡在 iframe 报回来之前会集体抖一次。 */
/* 弹窗：点开一个控件不该把整面墙重排。 */
#wg-modal-host:empty{display:none}
.wg-modal{position:fixed;inset:0;z-index:90;display:grid;place-items:center;padding:24px}
.wg-modal-bg{position:absolute;inset:0;background:rgba(15,20,27,.34);backdrop-filter:blur(2px);cursor:pointer}
.wg-modal-c{position:relative;display:grid;gap:0;width:min(760px,100%);max-height:min(86vh,760px);
  background:var(--surface);border:1px solid var(--line);border-radius:14px;overflow:hidden;
  box-shadow:0 18px 48px rgba(15,20,27,.18)}
.wg-modal-h{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;
  padding:14px 16px;border-bottom:1px solid var(--rule-2)}
.wg-modal-t{display:grid;gap:3px;min-width:0}
.wg-modal-t b{font-size:14.5px}
.wg-modal-b{display:grid;gap:12px;padding:14px 16px 16px;overflow:auto;align-content:start}
/* 台面：弹窗里那个能点的控件，四周留白，底色和瓦片一致。 */
.wg-stage{display:grid;gap:7px;padding:14px;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface-2);justify-items:center}
.wg-stage .wg-live{background:transparent;border:0;width:100%}
.wg-stage .tls{text-align:center}

/* 实例框和裁图共用同一个媒体区，所以外观、尺寸都一致；它在卡片正面不自适应高度
   （父页只给 .wg-live-doc 改高），否则每张卡各长各的，墙又散了。 */
.wg-shot.is-live{cursor:default}
.wg-live-card{width:100%;height:100%;border:0;border-radius:calc(var(--r-md) - 2px);background:transparent}
/* 真机裁图退成一行小图 —— 它仍然是这一页的承诺，只是不再抢主位。 */
.wg-crop{display:flex;align-items:center;gap:7px;min-width:0}
.wg-crop img{max-height:26px;max-width:58%;border-radius:4px;border:1px solid var(--line);background:var(--surface-2)}
.wg-crop .tls{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wg-live{width:100%;height:96px;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);display:block;transition:height .16s var(--ease)}
.wg-fig{display:grid;gap:3px;padding:8px 10px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2)}
.wg-fig-n{font-size:12px;color:var(--ink)}
.wg-fig-n .mono{font-size:10.5px;color:var(--ink-3)}
.wg-axes{display:flex;gap:5px;flex-wrap:wrap;margin-top:2px}
.wg-axes span{padding:1px 6px;border-radius:4px;background:var(--surface);border:1px solid var(--line);font-size:10px;color:var(--ink-3)}
.wg-axes i{font-style:normal;color:var(--ink-2);margin-left:4px}

/* ── 动效库（95e-motion.js）：一页会动的图册 ───────────────────────────────
   控件库那一页最大的一块是裁图，因为控件的第一身份是长什么样；这一页最大的一块
   是台面（.fx-stage），因为动效的第一身份是怎么动。台面里跑的 markup 和 CSS 都
   是服务端从同一组数字生成的（GET /api/effects 的 web），这里只负责把那些形状
   摆好看 —— 所以形状类名全带 fxs- 前缀（effects_routes.WEB_PREFIX）：站里已经
   有 .pill / .box / .ring / .scrim，不加前缀就会互相染色。
   demo 自己的动画规则由 #fx-style 注入，这里一条都不写。 */
.fx-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(316px,1fr));gap:12px;margin-top:16px}
.fx-card{display:grid;gap:10px;padding:12px;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);min-width:0;transition:border-color .13s var(--ease)}
.fx-card:hover{border-color:var(--line-2)}
.fx-card.open{grid-column:1/-1;grid-template-columns:minmax(0,340px) minmax(0,1fr);
  column-gap:18px;border-color:var(--ink-3)}
.fx-head{display:flex;flex-direction:column;gap:9px;min-width:0}
.fx-cta{margin-top:auto}
.fx-card.open .fx-head{align-self:start}
.fx-card.open .fx-cta{margin-top:0}
/* 台面就是「再播一次」那颗按钮：整块都能点，不用先找到一个小三角。 */
.fx-stage{display:grid;place-items:center;width:100%;height:132px;padding:12px;border:0;
  border-radius:var(--r-md);background:var(--surface-2);overflow:hidden;position:relative;cursor:pointer}
.fx-card.open .fx-stage{height:176px}
.fx-recording,.fx-card.open .fx-recording{height:auto;aspect-ratio:8/5;padding:0;background:#11151f}
.fx-recording img{width:100%;height:100%;object-fit:cover;display:block}
.fx-recording-label{position:absolute;bottom:8px;left:8px;display:flex;gap:5px;align-items:center;
  padding:4px 7px;border-radius:5px;background:rgba(0,0,0,.65);color:#fff;font-size:10px}
.fx-recording-label svg{width:11px;height:11px}
.fx-interactive .fx-what{margin:0;color:var(--ink-3)}
.fx-interactive .fx-cta{display:flex;gap:7px;flex-wrap:wrap}
.fx-card.fx-interactive.open{grid-template-columns:1fr}
.fx-interactive.open .fx-head{display:grid;grid-template-columns:minmax(160px,260px) 1fr;column-gap:18px}
.fx-interactive.open .fx-recording{grid-row:1/6}
.fx-interactive-body{margin-top:8px}
.fx-interactive-frame{width:100%;height:740px;max-height:85vh;border:1px solid var(--line);
  border-radius:var(--r-md);background:#11151f}
.fx-source-links{display:flex;gap:14px;align-items:center;flex-wrap:wrap;font-size:12px}
.fx-source-links a{color:var(--accent-ink);text-decoration:underline}
@media(max-width:640px){.fx-interactive.open .fx-head{display:flex}.fx-interactive-frame{height:720px}}
.fx-t{font-size:12.5px;font-weight:600;color:var(--ink);display:flex;gap:6px;align-items:baseline;flex-wrap:wrap}
.fx-t span{font-weight:400;font-size:11px;color:var(--ink-3)}
.fx-facts{font-size:10.5px;color:var(--ink-3)}
.fx-facts i{font-style:normal;opacity:.5}
/* ColorOS 自己的那个数。和左边的语料默认同一行、同一字号 —— 它不是另一个话题，
   是同一条动效的第二个来源；所以只用颜色区分，不另起一行也不做成表。 */
.fx-coui{color:var(--acc);font-variant-numeric:tabular-nums}
/* 物理动画那一块：卡片下面的一段参考，不是第二张卡册。字号跟着卡片上的事实行走，
   「还不知道」那一列故意留着，灰一点但不藏起来。 */
.fx-phys{margin:18px 2px 0;padding-top:14px;border-top:1px solid var(--line)}
.fx-phys h3{font-size:13px;margin:0 0 4px}
.fx-phys .tls{margin:0 0 10px;max-width:70ch}
.fx-phys-row{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap;
  font-size:10.5px;color:var(--ink-3);padding:5px 0;border-top:1px dashed var(--line)}
.fx-phys-row b{color:var(--ink-1);font-size:11.5px}
.fx-phys-row .d{flex:1 1 260px}
.fx-phys-row .unk{opacity:.72;font-style:italic}
.fx-phys-grid{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
.fx-phys-grid span{font-size:10px;color:var(--ink-3);background:var(--bg-2);
  border-radius:8px;padding:5px 8px;font-variant-numeric:tabular-nums}
.fx-phys-grid b{display:block;color:var(--ink-1)}
/* 状态机那一行（.fx-mach）和链那一行（.fx-chain）。两行都长在原来那张卡里，和
   上面那行事实同一个字号：一条动效的状态不是另一个话题，它就是这条动效的事实之
   一。状态做成小药丸，箭头是灰的 —— 一眼看过去先看见几个状态名，再看清是什么把
   它推过去的。窄卡片上会换行，不横着溢出。 */
.fx-mach,.fx-chain{display:flex;gap:4px;align-items:center;flex-wrap:wrap;
  font-size:10.5px;color:var(--ink-3);min-width:0}
.fx-mn{font-size:9.5px;color:var(--ink-3);opacity:.8;margin-right:1px}
.fx-st{font-weight:600;font-size:10px;color:var(--ink-2);background:var(--surface-3);
  border-radius:4px;padding:1px 6px;white-space:nowrap}
.fx-st.now{background:var(--accent-soft);color:var(--accent-ink)}
.fx-hold{font-size:9.5px;opacity:.8}
.fx-arr{font-style:normal;display:inline-flex;gap:4px;align-items:center;
  font-family:var(--mono);font-size:9.5px;color:var(--ink-3);white-space:nowrap}
.fx-arr::before{content:'→';opacity:.5}
.fx-arr em{font-style:normal;opacity:.65}
.fx-brk{width:6px}
/* 缺的那一段：虚线加一个不同的颜色。它不是一个错误，它是这个库此刻的边界 ——
   所以用琥珀色提示，不用红色报警。 */
.fx-gap{font-style:normal;display:inline-flex;align-items:center;gap:3px;padding:0 5px;
  font-family:var(--mono);font-size:9px;color:var(--ochre);white-space:nowrap;
  border-top:1px dashed currentColor;border-bottom:1px dashed currentColor;opacity:.85}
.fx-int{font-size:11.5px;line-height:1.6;color:var(--ink-2);
  border-left:2px solid var(--line-2);padding-left:9px}
.fx-tags{display:flex;gap:7px;align-items:center;flex-wrap:wrap}
/* 出现频率：一条细线加一个数。它是这一页的分量信息，不是一张报表里的一格。 */
.fx-pct{display:inline-flex;align-items:center;gap:5px;font-family:var(--mono);font-size:10.5px;color:var(--ink-3)}
.fx-bar{display:block;width:34px;height:3px;border-radius:2px;background:var(--surface-3);overflow:hidden}
.fx-bar i{display:block;height:100%;background:var(--accent);opacity:.75}
/* 两端：一条曲线，网页的写法在上，原生的写法在下 —— 同一条单位三次贝塞尔。 */
.fx-bridge{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;align-items:center;
  padding:8px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.fx-mid{display:grid;justify-items:center;gap:3px;width:46px}
.fx-mid span{font-size:9px;line-height:1.2;color:var(--ink-3);text-align:center}
.fx-curve{width:40px;height:40px;overflow:visible}
.fx-curve .fx-axis{fill:none;stroke:var(--line-2);stroke-width:4;stroke-linecap:round}
.fx-curve .fx-trace{fill:none;stroke:var(--accent);stroke-width:7;stroke-linecap:round}
.fx-nocurve{font-size:22px;line-height:40px;color:var(--ink-3)}
.fx-ends{display:grid;gap:5px;min-width:0}
.fx-end+.fx-end{border-top:1px dashed var(--line);padding-top:5px}
.fx-end{display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:8px;row-gap:0;min-width:0}
.fx-end .k{grid-row:1/3;align-self:center;font-size:10px;font-weight:600;color:var(--ink-3)}
.fx-end code{font-family:var(--mono);font-size:10.5px;color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fx-end .d{font-size:10.5px;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 「为什么是这个 API」是这个库里最值钱的一句话，但它是一段话 —— 卡片上给两行，
   全文在展开里。 */
.fx-why{font-size:10.5px;line-height:1.5;color:var(--ink-3);border-left:2px solid var(--line);padding-left:8px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.fx-body{display:grid;gap:9px;min-width:0;align-content:start}
.fx-what{font-size:12px;line-height:1.6;color:var(--ink-2)}
.fx-why-full{font-size:11.5px;line-height:1.6;color:var(--ink-2);border-left:2px solid var(--accent-line);padding-left:9px}
.fx-nums{display:flex;gap:5px;flex-wrap:wrap}
.fx-nums span{display:inline-flex;gap:5px;align-items:baseline;padding:2px 7px;border-radius:4px;
  background:var(--surface-2);font-family:var(--mono);font-size:10.5px;color:var(--ink-2);max-width:100%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fx-nums i{font-style:normal;color:var(--ink-3)}
.fx-code-h{display:flex;gap:9px;align-items:center;flex-wrap:wrap}
.fx-code{margin:0;padding:10px 12px;max-height:300px;overflow:auto;border:1px solid var(--line);
  border-radius:var(--r-md);background:var(--surface-2);font-family:var(--mono);font-size:11px;
  line-height:1.55;white-space:pre;color:var(--ink-2)}
/* 档位那一行右端的一颗开关，不是一块面板。 */
.fx-all{margin-left:auto;display:inline-flex;align-items:center;gap:5px;padding:0 10px;height:26px;
  align-self:center;border-radius:999px;border:1px solid var(--line-2);background:var(--surface);
  font-size:11.5px;color:var(--ink-3);white-space:nowrap}
.fx-all svg{width:11px;height:11px}
.fx-all:hover{color:var(--ink);border-color:var(--ink-3)}
.fx-all[aria-pressed="true"]{background:var(--accent-soft);border-color:var(--accent-line);color:var(--accent-ink)}
.fx-still{margin-left:auto;align-self:center;padding-left:14px;font-size:10.5px;color:var(--ink-3);max-width:46ch}
/* 再播一次之前那一瞬间：过渡和动画全关掉，好让"退回起点"是一次瞬间的复位，
   而不是倒着放一遍（见 95e-motion.js 的 fxPlay）。 */
.fx-card.fx-reset .fx-stage,.fx-card.fx-reset .fx-stage *,
.fx-card.fx-reset .fx-stage *::after{transition:none!important;animation:none!important}
/* 播放时曲线按这一条真实的时长匀速画出来：匀速才看得出进度被曲线拉快拉慢。 */
@keyframes fx-draw{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}
.fx-card.fx-run .fx-trace{stroke-dasharray:1;animation:fx-draw var(--fx-ms) linear}
@media(max-width:640px){.fx-card.open{grid-template-columns:1fr}}

/* demo 里那些形状。服务端给的 markup 带 fxs- 前缀，站里同名的 .pill / .box /
   .ring / .scrim 因此碰不到它们。颜色全走站里的 token：这一页在暗色下也要成立。 */
/* demo 的**动作**由服务端生成，它的**颜色**归这一页 —— 生成的 CSS 里只有两处
   颜色，都留成了变量（apk_effects_demo.css_for）。状态换色要是换成和台面一样的
   灰，那张卡就是一个什么也没发生的演示。 */
#v-effects{--fx-on-bg:var(--ink);--fx-on-ink:var(--canvas);--fx-ripple:var(--ink)}
.fxs-box{width:74px;height:74px;border-radius:14px;background:var(--accent)}
.fxs-pill{display:grid;place-items:center;padding:9px 22px;border-radius:999px;background:var(--accent);
  color:var(--solid-ink);font-weight:600;font-size:12px}
.fxs-sheet-host{width:164px;height:114px;background:var(--surface-3);border-radius:10px;position:relative;overflow:hidden}
.fxs-scrim{position:absolute;inset:0;background:#000;opacity:0}
.fxs-sheet{position:absolute;left:0;right:0;bottom:0;height:58px;background:var(--accent);border-radius:10px 10px 0 0}
.fxs-skel{width:176px;height:20px;border-radius:5px;background:var(--surface-3);position:relative;overflow:hidden}
.fxs-band{position:absolute;top:0;bottom:0;width:100%;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.75),transparent)}
.fxs-ring{width:48px;height:48px;border-radius:50%;border:5px solid var(--surface-3);border-top-color:var(--accent)}
.fxs-rowlist{display:flex;flex-direction:column;gap:7px;width:176px}
.fxs-rowlist div{height:22px;border-radius:5px;background:var(--surface-3)}
.fxs-acc{width:176px}
.fxs-acc .fxs-head{height:26px;border-radius:5px;background:var(--surface-3)}
.fxs-acc .fxs-body{overflow:hidden;height:0;background:var(--accent);border-radius:5px;margin-top:5px}
.fxs-blurred{position:absolute;inset:0;background:repeating-linear-gradient(45deg,
  var(--accent),var(--accent) 12px,var(--surface-3) 12px,var(--surface-3) 24px)}
.fxs-glass{position:relative;width:124px;height:64px;border-radius:12px;background:rgba(255,255,255,.34);
  display:grid;place-items:center;color:#fff;font-size:11px;font-weight:600}

/* ── demo to apk：这条流水线的原理，一张会动的条 ────────────────────────────
   页面上原来只有一段文字说"通常 10–30 分钟"，没说这十几分钟在干什么。六个站，一个
   亮点一站一站地停过去，一圈 12 秒 —— 和真实的六步一一对应。纯 CSS：没有 JS，没有
   图片，跟着主题走。文案用 data-en（静态标记，staticI18n 走 DOM 时翻译）。 */
/* 和 .ig-panel 同宽同圆角同边框 —— 这一页原来有三种右边缘（lede 64ch、这条 920px、
   面板 1240px），一层层错开，页面就是这么变丑的。它比面板安静一档（surface-2），
   因为它是背景说明，不是工具本身。 */
.apkflow{margin:0 0 16px;padding:15px 18px 14px;border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--surface-2);width:100%}
.apkflow-h{display:flex;align-items:baseline;gap:10px;margin-bottom:14px;flex-wrap:wrap}
.apkflow-h b{font-size:12.5px;font-weight:650}
.apkflow-row{display:grid;grid-template-columns:repeat(6,1fr);gap:0;position:relative}
.apkflow-line{position:absolute;left:8.33%;right:8.33%;top:13px;height:2px;background:var(--line);border-radius:2px}
/* the marker: it STOPS on each station, so the eye reads "this step is running now" */
.apkflow-dot{position:absolute;top:7px;width:14px;height:14px;margin-left:-7px;border-radius:50%;
  background:var(--accent);box-shadow:0 0 0 4px var(--accent-soft);
  animation:apkflow-run 12s cubic-bezier(.65,0,.35,1) infinite}
@keyframes apkflow-run{
  0%,12%{left:8.33%}   16.6%,29%{left:25%}    33.3%,45.6%{left:41.67%}
  50%,62.3%{left:58.33%} 66.6%,79%{left:75%}  83.3%,100%{left:91.67%}}
.apkflow-step{position:relative;padding-top:30px;text-align:center;
  background:none;border:0;font:inherit;cursor:pointer;border-radius:8px;padding-bottom:6px;
  -webkit-tap-highlight-color:transparent}
.apkflow-step:hover{background:var(--surface-2)}
.apkflow-step.on{background:var(--accent-soft)}
.apkflow-step.on b{color:var(--accent-ink);animation:none}
.apkflow-step.on::before{border-color:var(--accent);background:var(--accent)}
/* 有一步被点开时，跑马灯停 —— 人在读字，屏幕上就不该同时有两个亮点在抢 */
.apkflow.open .apkflow-dot,.apkflow.open .apkflow-step b{animation-play-state:paused}
.apkflow.open .apkflow-dot{opacity:.25}
.apkflow-step::before{content:"";position:absolute;top:9px;left:50%;margin-left:-5px;width:10px;height:10px;
  border-radius:50%;background:var(--surface);border:2px solid var(--line-2)}
.apkflow-step b{display:block;font-size:11.5px;font-weight:650;color:var(--ink-3);
  animation:apkflow-lit 12s linear infinite}
.apkflow-step:nth-child(3) b{animation-delay:-12s}
.apkflow-step:nth-child(4) b{animation-delay:-10s}
.apkflow-step:nth-child(5) b{animation-delay:-8s}
.apkflow-step:nth-child(6) b{animation-delay:-6s}
.apkflow-step:nth-child(7) b{animation-delay:-4s}
.apkflow-step:nth-child(8) b{animation-delay:-2s}
@keyframes apkflow-lit{0%,14%{color:var(--accent-ink)} 20%,100%{color:var(--ink-3)}}
.apkflow-step span{display:block;font-size:10.5px;color:var(--ink-3);line-height:1.5;margin-top:3px;
  padding:0 8px;min-height:2.9em}
/* 点开的那一步：说明就落在这条线底下，不弹窗、不跳页 */
.apkflow-why{margin-top:14px;padding:13px 15px;border:1px solid var(--accent-line);border-radius:var(--r-md);
  background:var(--accent-soft);animation:vin .18s ease both}
.apkflow-why b{display:block;font-size:12px;font-weight:650;color:var(--accent-ink);margin-bottom:4px}
.apkflow-why p{margin:0;font-size:11.5px;line-height:1.75;color:var(--ink-2);max-width:68ch}
.apkflow-note{margin-top:13px;padding-top:11px;border-top:1px solid var(--line);
  font-size:11.5px;color:var(--ink-2);line-height:1.65;max-width:74ch}
.apkflow-note b{color:var(--ink);font-weight:650}
@media (max-width:860px){.apkflow-row{grid-template-columns:repeat(3,1fr);row-gap:14px}
  .apkflow-line,.apkflow-dot{display:none}.apkflow-step b{animation:none;color:var(--ink)}}
@media (prefers-reduced-motion:reduce){.apkflow-dot{animation:none}.apkflow-step b{animation:none;color:var(--ink)}}

/* ── 六步各自的小动画 ───────────────────────────────────────────────────────
   说明文字旁边一张 240×112 的 SVG，画的就是那一步在干的事：探索一屏一屏地找、
   量测给每个元素套上尺子、编译把量出来的行变成布局、agent 只刷外观、走查挨个点、
   对照逐元素比。纯 CSS 动画，没有 JS、没有图片；SVG 里一个字都没有，所以不牵扯翻译。
   `currentColor` 和 var(--…) 让它跟着主题走；reduced-motion 下全部定格。 */
.apkflow-why{display:flex;gap:16px;align-items:center;flex-wrap:wrap}
.apkflow-why .apkflow-txt{flex:1 1 320px;min-width:0}

/* APK 生成过程：长任务不是一根总进度条，而是每一屏各自有证据的账本。 */
/* Same view embedded in OpenDesign: no nested navigation or onboarding. The
   access-code lock is deliberately left intact; embedding never grants access. */
:root[data-embed="apk-progress"] .app{display:block;visibility:visible}
:root[data-embed="apk-progress"] .rail,
:root[data-embed="apk-progress"] .top,
:root[data-embed="apk-progress"] #scrim,
:root[data-embed="apk-progress"] #gate,
:root[data-embed="apk-progress"] #chat{display:none!important}
:root[data-embed="apk-progress"] #v-apkprogress{max-width:none;padding:20px}
.apk-prog-summary{display:flex;align-items:center;justify-content:space-between;gap:18px;margin-bottom:16px;
  padding:16px 18px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);
  position:relative;overflow:hidden;isolation:isolate}
.apk-prog-summary::before{content:"";position:absolute;inset:-90px 0;z-index:-1;pointer-events:none;
  background:radial-gradient(ellipse at 15% 40%,color-mix(in srgb,var(--accent) 16%,transparent),transparent 55%),
  radial-gradient(ellipse at 85% 65%,color-mix(in srgb,var(--green) 12%,transparent),transparent 55%)}
.apk-prog-summary.is-running::before{animation:apk-aurora 7s ease-in-out infinite alternate}
.apk-prog-summary h2{font-size:18px;margin:3px 0}.apk-prog-summary p{margin:0}
.apk-prog-facts{display:flex;gap:8px;flex-wrap:wrap}.apk-prog-facts>span{min-width:70px;padding:8px 10px;
  border:1px solid var(--line);border-radius:var(--r-md);color:var(--ink-3);font-size:10px}
.apk-prog-facts b{display:block;color:var(--ink);font:650 17px/1.1 var(--mono)}
.apk-prog-facts .same b{color:var(--green)}.apk-prog-facts .differs b{color:var(--ochre)}
.apk-prog-screen{margin:0 0 18px;padding:18px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
.apk-prog-title{display:flex;align-items:center;justify-content:space-between;gap:12px}.apk-prog-title>div{display:flex;align-items:baseline;gap:10px}
.apk-prog-title h2{margin:0;font-size:16px}.apk-prog-title>.pill{white-space:nowrap}
.apk-prog-stages{display:grid;grid-template-columns:repeat(6,1fr);margin:15px 0 17px;position:relative}
.apk-prog-stages::before{content:"";position:absolute;left:8%;right:8%;top:6px;height:1px;background:var(--line)}
.apk-prog-stages span{position:relative;padding-top:18px;text-align:center;color:var(--ink-3);font-size:10px}
.apk-prog-stages i{position:absolute;top:2px;left:50%;width:9px;height:9px;margin-left:-5px;border-radius:50%;
  border:1px solid var(--line-2);background:var(--surface)}
.apk-prog-stages .done{color:var(--ink)}.apk-prog-stages .done i{border-color:var(--green);background:var(--green)}
.apk-prog-stages .now i{border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
.apk-prog-stages .now i::after{content:"";position:absolute;inset:-6px;border:1px solid var(--accent);
  border-radius:50%;animation:apk-beacon 1.9s ease-out infinite}
.apk-prog-stages .done i{box-shadow:0 0 10px color-mix(in srgb,var(--green) 30%,transparent)}
.apk-prog-elements button{transition:background .2s,border-color .2s,transform .25s var(--ease)}
.apk-prog-elements button:hover{transform:translateY(-2px)}
.apk-prog-elements button:active{transform:scale(.96)}
.apk-prog-elements button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
@keyframes apk-aurora{to{transform:translate3d(8%,10%,0) scale(1.12)}}
@keyframes apk-beacon{0%{transform:scale(.55);opacity:.75}85%,100%{transform:scale(1.6);opacity:0}}
@media(prefers-reduced-motion:reduce){
  .apk-prog-summary.is-running::before,.apk-prog-stages .now i::after{animation:none}
  .apk-prog-stages .now i::after{opacity:.3}
  .apk-prog-elements button{transition:none;transform:none!important}
}
.apk-prog-pair{display:grid;grid-template-columns:1fr 1fr;gap:14px}.apk-prog-pair .micro{margin-bottom:7px}
.apk-prog-image,.apk-prog-empty{position:relative;min-height:260px;border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;background:var(--surface-2)}
.apk-prog-image{display:flex;justify-content:center}.apk-prog-image img{display:block;width:auto;max-width:100%;height:auto;max-height:560px;object-fit:contain}
.apk-prog-image.shaped{width:100%;min-height:0;margin:0 auto}.apk-prog-image.shaped img{width:100%;height:100%;max-height:none}
.apk-prog-empty{display:grid;place-items:center;color:var(--ink-3);font-size:11px}
.apk-prog-mark{position:absolute;border:2px solid var(--accent);background:color-mix(in srgb,var(--accent) 15%,transparent);
  box-shadow:0 0 0 1px rgba(255,255,255,.65) inset;pointer-events:none;box-sizing:border-box}
.apk-prog-elements{display:flex;gap:6px;overflow:auto;padding:12px 0 9px;scrollbar-width:thin}
.apk-prog-elements button{flex:0 0 auto;max-width:190px;padding:5px 8px;border:1px solid var(--line);border-radius:999px;
  background:var(--surface);color:var(--ink-2);font:inherit;font-size:10.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
.apk-prog-elements button.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink)}
.apk-prog-elements button .mono{margin-right:5px;color:var(--ink-3)}
.apk-prog-detail{padding:12px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2)}
.apk-prog-detail-h{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}.apk-prog-detail-h>div:first-child{display:flex;gap:8px;align-items:baseline}
.apk-axis-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.apk-axis{padding:9px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface)}
.apk-axis>div{display:flex;justify-content:space-between;gap:6px}.apk-axis b{font-size:11px}.apk-axis>div span{font-size:9px;color:var(--ink-3)}
.apk-axis.same{border-color:color-mix(in srgb,var(--green) 45%,var(--line))}.apk-axis.differs{border-color:color-mix(in srgb,var(--ochre) 55%,var(--line))}
.apk-axis p{display:grid;grid-template-columns:36px 1fr;gap:4px;margin:6px 0 0;font:10px/1.45 var(--mono);word-break:break-word}
.apk-axis em{font:9px/1.45 var(--sans);font-style:normal;color:var(--ink-3)}
.apk-agent-diff{display:flex;gap:7px;align-items:center;flex-wrap:wrap;margin-top:9px;font-size:10px;color:var(--ink-3)}
.apk-agent-diff b{color:var(--ink-2)}.apk-agent-diff span{padding:3px 6px;border-radius:4px;background:var(--surface)}
.apk-prog-findings{margin-top:10px;border-left:2px solid var(--ochre);padding-left:10px}.apk-prog-findings summary{cursor:pointer;font-size:11px;color:var(--ochre)}
.apk-prog-findings p{margin:6px 0;font-size:10.5px;color:var(--ink-2)}
.apk-src-scrim{position:fixed;z-index:90;inset:0;display:grid;place-items:center;padding:24px;background:rgba(0,0,0,.42)}
.apk-src{width:min(900px,94vw);max-height:84vh;overflow:auto;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);box-shadow:var(--shadow-lg)}
.apk-src header{position:sticky;top:0;z-index:1;display:flex;justify-content:space-between;align-items:center;padding:11px 14px;border-bottom:1px solid var(--line);background:var(--surface)}
.apk-src pre{margin:0;padding:10px 0;overflow:auto;background:var(--surface-2);font:11px/1.55 var(--mono)}
.apk-src pre span{display:block;white-space:pre;padding:0 14px}.apk-src pre span.hit{background:var(--accent-soft)}
.apk-src pre i{display:inline-block;width:42px;margin-right:12px;text-align:right;color:var(--ink-3);font-style:normal;user-select:none}
@media(max-width:800px){.apk-prog-summary{align-items:flex-start;flex-direction:column}.apk-prog-pair{grid-template-columns:1fr}.apk-axis-grid{grid-template-columns:1fr 1fr}}
@media(max-width:520px){.apk-prog-screen{padding:12px}.apk-prog-stages span{font-size:8px}.apk-axis-grid{grid-template-columns:1fr}.apk-prog-detail-h{align-items:flex-start;flex-direction:column}}
/* Compact build workbench; inspect just the selected screen. */
#v-apkprogress{max-width:none;padding:22px 26px}
#v-apkprogress .apk-work-head{align-items:center;margin-bottom:18px;gap:16px}
#v-apkprogress .apk-work-head h1{font-size:24px;letter-spacing:-.7px;line-height:1.2;margin:4px 0 0}
#v-apkprogress .apk-work-head .micro{font-size:9px;letter-spacing:1.7px}
#v-apkprogress .apk-prog-summary{padding:0 0 18px;margin-bottom:0;border:0;border-radius:0;background:none;overflow:visible}
#v-apkprogress .apk-prog-summary::before{display:none}
.apk-job-title{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
#v-apkprogress .apk-job-title h2{font-size:15px;line-height:1.3;margin:0 0 4px}
.apk-build-state{display:inline-flex;align-items:center;gap:6px;font-size:10px;color:var(--ink-2);padding:3px 8px;border-radius:20px;background:var(--surface-2)}
.apk-build-state i{width:5px;height:5px;border-radius:50%;background:var(--ink-3)}
.apk-build-state.is-running i{background:var(--green);box-shadow:0 0 0 3px color-mix(in srgb,var(--green) 12%,transparent)}
#v-apkprogress .apk-prog-summary .tls{font-size:11px;margin-top:3px}.apk-prog-summary .mono{color:var(--ink-3)}
#v-apkprogress .apk-prog-facts{gap:22px;flex-shrink:0}
#v-apkprogress .apk-prog-facts>span{border:0;padding:0;min-width:0;font-size:10px}
#v-apkprogress .apk-prog-facts b{font-size:17px;margin-bottom:3px}
.apk-workspace{display:grid;grid-template-columns:184px minmax(0,1fr);border:1px solid var(--line);border-radius:12px;background:var(--surface);overflow:hidden}
#apk-progress-nav{padding:16px 8px;background:var(--surface-2);border-right:1px solid var(--line);max-height:calc(100dvh - 210px);overflow:auto}
.apk-nav-head{display:flex;justify-content:space-between;align-items:center;margin:0 10px 14px;font-size:9px;letter-spacing:1px;color:var(--ink-3)}
.apk-nav-head span{font:10px var(--mono);letter-spacing:0}
.apk-screen-nav{display:flex;align-items:center;gap:9px;width:100%;text-align:left;border:1px solid transparent;background:none;color:var(--ink-2);border-radius:8px;padding:11px 9px;cursor:pointer;margin-bottom:3px}
.apk-screen-nav.on{background:var(--surface);border-color:var(--line);color:var(--ink);box-shadow:0 2px 4px #00000004}.apk-screen-nav:hover{background:var(--surface)}
.apk-screen-number{font:10px var(--mono);color:var(--ink-3)}
.apk-screen-nav>span:nth-child(2){min-width:0;flex:1}.apk-screen-nav b{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:11px;font-weight:600}
.apk-screen-nav small{display:block;margin-top:4px;font-size:9px;color:var(--ink-3)}
.apk-screen-nav>i{width:5px;height:5px;border:1px solid var(--line-2);border-radius:50%;flex-shrink:0}.apk-screen-nav>i.done{background:var(--green);border-color:var(--green)}
#apk-progress-screens{min-width:0}#v-apkprogress .apk-prog-screen{margin:0;border:0;border-radius:0;padding:18px 22px;background:transparent}
#v-apkprogress .apk-prog-title h2{font-size:14px}.apk-prog-title .mono{font-size:10px;color:var(--ink-3)}
#v-apkprogress .apk-prog-stages{margin:18px 0 22px}#v-apkprogress .apk-prog-stages span{font-size:9px}#v-apkprogress .apk-prog-stages .done i{box-shadow:none}
#v-apkprogress .apk-prog-pair{gap:18px;grid-template-columns:repeat(2,minmax(0,1fr))}
.apk-canvas-label{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;font-size:11px}
.apk-canvas-label b{font-weight:500}.apk-canvas-label span{font:9px var(--mono);color:var(--ink-3);padding:2px 5px;border:1px solid var(--line);border-radius:4px}
.apk-preview-stage{height:clamp(320px,calc(100dvh - 310px),570px);display:flex;align-items:center;justify-content:center;overflow:hidden;border:1px solid var(--line);border-radius:10px;padding:16px;background:var(--surface-2);background-image:radial-gradient(var(--line) .6px,transparent .6px);background-size:12px 12px}
#v-apkprogress .apk-prog-image{display:block;position:relative;min-height:0;height:100%;max-height:100%;max-width:100%;width:auto;border-radius:10px;border:0;flex:none;background:var(--surface);box-shadow:0 8px 24px #0000000d;overflow:hidden}
#v-apkprogress .apk-prog-image img{width:100%;height:100%;max-height:100%;object-fit:contain;display:block}
#v-apkprogress .apk-prog-empty{min-height:0;background:none;border:0;display:flex;flex-direction:column;align-items:center;gap:10px;padding:18px;text-align:center;max-width:260px;font-size:11px}
.apk-prog-empty>b{font-weight:500;color:var(--ink-2)}.apk-prog-empty>p{font-size:10px;line-height:1.5;margin:0;color:var(--ink-3)}
.apk-device-ghost{width:88px;height:150px;border:1px dashed var(--line-2);border-radius:16px;padding:20px 10px;position:relative;margin-bottom:8px}
.apk-device-ghost i{position:absolute;top:7px;left:35%;width:30%;height:3px;border-radius:3px;background:var(--line)}
.apk-device-ghost b,.apk-device-ghost span{display:block;height:5px;border-radius:3px;background:var(--line);margin:7px 0}.apk-device-ghost b{height:45px;opacity:.5}.apk-device-ghost span:last-child{width:65%}
.apk-prog-inspector{margin-top:16px}.apk-element-label{display:flex;align-items:center;gap:14px;font-size:11px;color:var(--ink-2);margin:0 0 10px}
.apk-element-label select{min-width:0;max-width:430px;flex:1;padding:7px 10px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink);font:11px var(--sans)}
.apk-prog-notice{border:1px solid var(--line);background:var(--surface-2);padding:10px 12px;margin-bottom:12px;border-radius:8px;font-size:11px;color:var(--ochre);overflow-wrap:anywhere}
.apk-prog-notice summary{cursor:pointer}.apk-prog-notice p{max-height:160px;overflow:auto;margin:10px 0;white-space:pre-wrap}
:root[data-embed="apk-progress"] #v-apkprogress{padding:18px 20px}:root[data-embed="apk-progress"] .apk-work-head h1{font-size:18px}:root[data-embed="apk-progress"] .apk-work-head .micro{display:none}
@media(max-width:900px){.apk-workspace{grid-template-columns:142px minmax(0,1fr)}#v-apkprogress .apk-prog-screen{padding:16px 14px}#v-apkprogress .apk-prog-pair{gap:10px}.apk-preview-stage{padding:9px}.apk-screen-number{display:none}}
@media(max-width:620px){.apk-workspace{grid-template-columns:1fr}#apk-progress-nav{display:flex;gap:5px;max-height:none;border-right:0;border-bottom:1px solid var(--line);padding:8px;overflow:auto}.apk-nav-head{display:none}.apk-screen-nav{width:130px;flex:none;margin:0;padding:8px}.apk-preview-stage{height:310px}.apk-prog-facts{gap:15px!important}#v-apkprogress{padding:14px}.apk-prog-title .btn{font-size:10px}.apk-element-label{flex-wrap:wrap}}
/* Source-first storyboard. Phone dimensions belong to the design, not the
   card: JS applies ONE uniform scale to both axes after the asset loads. */
#v-apkprogress .apk-prog-image{height:auto;max-height:none;max-width:none;flex-shrink:0;border-radius:0;background:transparent;box-shadow:none}
#v-apkprogress .apk-prog-image img{width:100%;height:100%;max-width:none;max-height:none;object-fit:contain}
#v-apkprogress .apk-html-preview iframe{position:absolute;top:0;left:0;border:0;max-width:none;max-height:none;transform-origin:0 0;pointer-events:none;background:white}
.apk-storyboard{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,510px),1fr));align-items:start;gap:1px;background:var(--line)}
#v-apkprogress .apk-storyboard .apk-prog-screen{background:var(--surface);padding:20px}
.apk-storyboard .apk-preview-stage{height:370px;padding:16px;background-image:none}
.apk-storyboard:has(>.apk-screen-card:only-child) .apk-preview-stage{height:clamp(380px,calc(100dvh - 250px),700px)}
#v-apkprogress .apk-prog-title h2{max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#v-apkprogress .apk-screen-card .apk-prog-title .mono{display:none}
.apk-native-slot{display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px;padding:14px;max-width:240px;color:var(--ink-3)}
.apk-native-slot>b{color:var(--ink-2);font-size:12px;font-weight:500}.apk-native-slot>p{font-size:11px;line-height:1.65;margin:0;max-width:190px}
.apk-build-orbit{width:62px;height:62px;position:relative;display:grid;place-items:center;margin-bottom:4px}
.apk-build-orbit>span{width:42px;height:42px;display:grid;place-items:center;border:1px solid var(--line);border-radius:12px;background:var(--surface);box-shadow:0 3px 10px #00000005;color:var(--ink-3)}
.apk-build-orbit>i{position:absolute;inset:0;border:1px solid var(--line);border-radius:50%}
.is-building .apk-build-orbit>i{border-top-color:var(--accent);animation:apk-slot-orbit 3s linear infinite}
.apk-slot-steps{display:flex;gap:5px;margin-top:4px}.apk-slot-steps>i{width:18px;height:3px;background:var(--line);border-radius:4px;transition:background .2s}
.apk-slot-steps>i.done{background:var(--accent)}
:root[data-embed="apk-progress"] .apk-work-head .g{display:none}
:root[data-embed="apk-progress"] .apk-work-head:has(#apk-progress-actions:empty){display:none}
@keyframes apk-slot-orbit{to{transform:rotate(360deg)}}
@media(prefers-reduced-motion:reduce){.is-building .apk-build-orbit>i{animation:none}.apk-slot-steps>i{transition:none}}
@media(max-width:620px){.apk-storyboard .apk-preview-stage{height:310px;padding:8px}.apk-native-slot{padding:6px}.apk-native-slot>p{font-size:10px}.apk-storyboard:has(>.apk-screen-card:only-child) .apk-preview-stage{height:370px}}
.fx{flex:0 0 232px;width:232px;height:108px;border-radius:var(--r-md);background:var(--surface);
  border:1px solid var(--accent-line)}
.fx .ph{fill:none;stroke:var(--line-2);stroke-width:1.5;rx:4}
.fx .bx{fill:var(--surface-3)}
.fx .hot{fill:var(--accent)}
.fx .ink{fill:var(--ink-3)}
.fx .ln{stroke:var(--accent);stroke-width:1.5;fill:none}
.fx .gd{stroke:var(--accent-line);stroke-width:1;fill:none;stroke-dasharray:2 2}
/* 1 探索：一屏点开下一屏 */
.fx-find .s2{opacity:0;animation:fx-pop 4s ease infinite .9s}
.fx-find .s3{opacity:0;animation:fx-pop 4s ease infinite 1.9s}
.fx-find .tap{animation:fx-tap 4s ease infinite}
@keyframes fx-pop{0%,10%{opacity:0;transform:translateX(-6px)}22%,100%{opacity:1;transform:none}}
@keyframes fx-tap{0%,12%{opacity:1;r:4}18%{opacity:.25;r:9}22%,100%{opacity:0;r:9}}
/* 2 量测：尺子扫过，元素一个个被框住 */
.fx-measure .scan{animation:fx-scan 4s linear infinite}
.fx-measure .m1{opacity:0;animation:fx-mark 4s linear infinite .6s}
.fx-measure .m2{opacity:0;animation:fx-mark 4s linear infinite 1.4s}
.fx-measure .m3{opacity:0;animation:fx-mark 4s linear infinite 2.2s}
@keyframes fx-scan{0%{transform:translateY(0)}80%,100%{transform:translateY(72px)}}
@keyframes fx-mark{0%{opacity:0}8%,100%{opacity:1}}
/* 3 编译：量出来的行变成布局里的块 */
.fx-build .r1{animation:fx-fly 4s ease infinite}
.fx-build .r2{animation:fx-fly 4s ease infinite .5s}
.fx-build .r3{animation:fx-fly 4s ease infinite 1s}
.fx-build .wire{opacity:0;animation:fx-wire 4s ease infinite 2s}
@keyframes fx-fly{0%,6%{transform:translateX(0);opacity:1}40%,100%{transform:translateX(96px);opacity:.28}}
@keyframes fx-wire{0%,45%{opacity:0;stroke-dashoffset:40}60%,100%{opacity:1;stroke-dashoffset:0}}
/* 4 写外观：右边那台把颜色和字重刷成左边那台的样子 */
.fx-look .p1{animation:fx-paint 4s ease infinite}
.fx-look .p2{animation:fx-paint 4s ease infinite .4s}
.fx-look .p3{animation:fx-paint 4s ease infinite .8s}
@keyframes fx-paint{0%,20%{fill:var(--surface-3)}45%,100%{fill:var(--accent)}}
/* 5 走查：手指挨个点，点到的变实心，有一个一直不亮 */
.fx-walk .t1{animation:fx-hit 4.8s ease infinite}
.fx-walk .t2{animation:fx-hit 4.8s ease infinite 1.1s}
.fx-walk .t4{animation:fx-hit 4.8s ease infinite 3.3s}
.fx-walk .miss{animation:fx-miss 4.8s ease infinite 2.2s}
.fx-walk .finger{animation:fx-finger 4.8s ease infinite}
@keyframes fx-hit{0%,8%{fill:var(--surface-3)}14%,100%{fill:var(--accent)}}
@keyframes fx-miss{0%,44%{stroke:var(--line-2)}50%,70%{stroke:var(--ochre)}76%,100%{stroke:var(--line-2)}}
@keyframes fx-finger{0%,8%{transform:translateY(0)}20%,30%{transform:translateY(20px)}
  42%,52%{transform:translateY(40px)}64%,74%{transform:translateY(60px)}86%,100%{transform:translateY(0)}}
/* 6 对照：扫描线过去，对得上的变绿，对不上的标出来 */
.fx-diff .sweep{animation:fx-sweep 4s linear infinite}
.fx-diff .ok1{opacity:0;animation:fx-ok 4s linear infinite .5s}
.fx-diff .ok2{opacity:0;animation:fx-ok 4s linear infinite 1.2s}
.fx-diff .bad{opacity:0;animation:fx-bad 4s linear infinite 2s}
@keyframes fx-sweep{0%{transform:translateX(0)}85%,100%{transform:translateX(104px)}}
@keyframes fx-ok{0%{opacity:0}6%,100%{opacity:1}}
@keyframes fx-bad{0%{opacity:0}6%,100%{opacity:1}}
@media (prefers-reduced-motion:reduce){.fx *{animation:none!important;opacity:1!important}}
@media (max-width:700px){.fx{flex:0 0 auto;width:100%}}

/* ── 设计生成（见 85g-studio.js）──────────────────────────────────────
   框里是 OpenDesign 那个应用本身，所以这里只有两条规则：它要占满整块视图
   （.view.flat 已经把内边距去掉了），以及它用不了的时候那一行字长什么样。
   49px 是顶栏的高度 —— 和无边画布的 .cv-shell 取的是同一个数。 */
.ods-app{display:block;width:100%;height:calc(100dvh - 49px);border:0;
  background:var(--surface)}
.ods-gate{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:20px 26px}
/* 它能用的时候这一格是空的 —— 空的 <div> 带着内边距，会把下面那个框往下
   顶 40px，而框的高度是按整屏算的，于是底下被切掉一条。 */
#od-gate:empty{display:none}

/* 某一种语言的「换过行的那些」。和 .tr-risk 同属一张清单，所以留同样的留白，
   但它是我们主动改过的布局而不是一个风险，所以底色是中性的。 */
.tr-wrapped{margin:8px 0 10px;padding:10px 12px;border-radius:10px;
  background:var(--s2,#f6f6f5);border:1px solid var(--ln,#e7e7e4)}
.tr-wrapped .tr-dp{margin-top:6px}

/* 产品自己的词表。译前给，所以它长得像其它「开跑之前」的块，而不是像交付面板
   里的东西 —— 它和「专有名词」是同一层的事。 */
.tr-book{margin:10px 0 2px}
.tr-bookb{margin-top:8px;display:grid;gap:8px}
.tr-bookt{width:100%;font:12px/1.6 var(--mono,ui-monospace,monospace);
  padding:9px 11px;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);color:var(--ink-1);resize:vertical}
.tr-bookt:focus{outline:none;border-color:var(--line-2)}
.tr-bookt:disabled{opacity:.55;cursor:not-allowed}
.tr-booka{display:flex;align-items:center;gap:10px;flex-wrap:wrap}

/* 复核清单。抄 Figma 的 replace：一行两个候选并排，选中的那个高亮，「保留我们
   的」和「换成另一个」是同一种点击 —— 一个选择必须看起来像一个选择，而不是
   「点了」和「没点」。 */
.tr-rv{margin-top:12px;display:grid;gap:10px}
.tr-rvl{display:grid;gap:6px}
.tr-rvr{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr) minmax(0,1fr);
  gap:8px;align-items:stretch;padding:7px 9px;border:1px solid var(--line);
  border-radius:var(--r-md);background:var(--surface)}
.tr-rvr.on{border-color:var(--line-2)}
.tr-rvs{font-size:12.5px;color:var(--ink-1);min-width:0;align-self:center;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tr-rvo{display:grid;gap:2px;text-align:left;padding:6px 9px;font-size:12.5px;
  color:var(--ink-2);background:var(--surface-2);border:1px solid transparent;
  border-radius:var(--r);cursor:pointer;min-width:0;overflow:hidden}
.tr-rvo>.tls{font-size:10.5px}
.tr-rvo.on{background:var(--surface);border-color:var(--ink-1);color:var(--ink-1)}
.tr-rvo:focus-visible{outline:2px solid var(--line-2);outline-offset:2px}
@media (max-width:720px){.tr-rvr{grid-template-columns:minmax(0,1fr)}}

/* ── 方案生成 ─────────────────────────────────────────────────────────────
   一行控件，一块板子，一排卡片。板子本身是一个自足的 HTML（真控件、真样式），
   所以这里用 iframe 原样嵌 —— 同源、不加 sandbox：加了 cookie 就没了，而它要
   从 /option-jobs/<id>/board.html 取自己的东西（见 sandboxed-iframe 那一条）。 */
.opt-form{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:0 0 18px}
.opt-form label.micro{display:inline-flex;align-items:center;gap:6px;color:var(--ink-3)}
.opt-form select{height:32px;border-radius:var(--r-sm);border:1px solid var(--line-2);
  background:var(--surface);color:var(--ink);padding:0 8px;font:inherit}
.opt-sum{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:0 0 14px}
.opt-q{font-size:19px;line-height:1.45;font-weight:600;margin:0 0 10px;max-width:70ch}
.opt-open{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 20px}
.opt-empty{color:var(--ink-3);margin:0;text-align:left}
.opt-cards{display:grid;gap:12px;margin:16px 0 0;
  grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}
.opt-card{border:1px solid var(--line);border-radius:var(--r-md);padding:12px 13px;
  background:var(--surface)}
.opt-card-head{display:flex;align-items:baseline;gap:8px;margin:0 0 4px}
.opt-card p{margin:0 0 6px}
/* 四个产物一行放得下：以前是四颗 .btn 换行成两排，右边永远参差不齐。 */
.opt-files{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:10px}
.opt-files .btn{width:100%;padding:0 8px;font-size:12px}
.opt-notes{margin:18px 0 0}
.opt-notes summary{cursor:pointer;color:var(--ink-3)}
.opt-notes ul{margin:8px 0 0;padding-left:18px}
.opt-list{display:flex;flex-direction:column;gap:6px}
.opt-row{display:flex;align-items:center;gap:12px;padding:10px 13px;text-decoration:none;
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);color:var(--ink)}
.opt-row:hover{border-color:var(--line-2)}
.opt-row .micro{margin-left:auto}

/* 磁盘告警（95z3-storage.js）：管理员页顶上一行，正常时不出现。 */
.sa-row{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;margin:0 0 14px;padding:8px 12px;
  border:1px solid var(--ochre-line);background:var(--ochre-soft);color:var(--ochre);border-radius:var(--r-md);font-size:13px}
.sa-row[data-level="critical"]{border-color:var(--brick-line);background:var(--brick-soft);color:var(--brick)}
.sa-row span{color:var(--ink);flex:1 1 auto;min-width:0}
.sa-row a{color:inherit;text-underline-offset:3px}
