/* 本体流动图谱。动效全部 CSS 声明式；类名一律 kf- 前缀，不影响旧图谱页。 */

/* 开场白收成一行：读法折进「读法」里。说明文字一长，没人读，还把画布往下挤。 */
.kf-hero{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap;margin:8px 0 8px;padding:8px 14px;
  border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:9px;
  background:linear-gradient(100deg,var(--accent-soft) 0%,var(--panel) 62%)}
.kf-heroline{font-size:13px;line-height:1.6;color:var(--ink)}
.kf-heroline b{font-weight:600}
.kf-howto{font-size:12px;color:var(--muted)}
.kf-howto>summary{cursor:pointer;color:var(--info);list-style:none}
.kf-howto>summary::-webkit-details-marker{display:none}
.kf-howto>p{margin:6px 0 2px;max-width:760px;line-height:1.75}
.kf-howto em{font-style:normal;color:var(--accent)}

.kf-bar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:8px 12px;background:var(--panel);
  border:1px solid var(--line);border-radius:9px;margin:0 0 8px}
.kf-bar .inline{display:flex;gap:5px;align-items:center;font-size:12px;color:var(--muted);white-space:nowrap}
.kf-bar input[type=search],.kf-bar input[type=text],.kf-bar input[type=number]{padding:5px 8px;border:1px solid var(--line);
  border-radius:6px;background:#fff;font:inherit;font-size:12.5px}
.kf-bar button{padding:5px 11px;font-size:12.5px}
.kf-sep{width:1px;height:20px;background:var(--line2)}
.kf-grow{flex:1 1 auto}
.kf-group{display:flex;gap:6px;align-items:center;flex-wrap:nowrap}

.kf-fold{padding:10px 12px;margin:0 0 8px;border:1px solid var(--line);border-radius:9px;background:var(--panel2)}
.kf-fold .subtle{margin:8px 0 0;font-size:11.5px;line-height:1.7}

.kf-legend{display:flex;gap:14px;flex-wrap:wrap}
.kf-lgrp{display:flex;gap:4px;align-items:center;flex-wrap:wrap}
.kf-lname{font:10.5px var(--mono);color:var(--faint);margin-right:2px}
.kf-chip{display:inline-flex;align-items:center;gap:4px;font-size:11.5px;padding:2px 8px;border:1px solid var(--line);
  border-radius:999px;background:var(--panel);cursor:pointer;color:var(--muted)}
.kf-chip:hover{border-color:var(--muted)}
.kf-chip.off{opacity:.4;text-decoration:line-through}
.kf-chip i{width:9px;height:9px;border-radius:50%;display:inline-block}
.kf-chip span{font:10px var(--mono);color:var(--faint)}

/* 画布独占整幅宽度；选中条目后右边才滑出详情 */
.kf-wrap{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;align-items:start}
.kf-wrap.has-detail{grid-template-columns:minmax(0,1fr) 340px}
.kf-wrap:not(.has-detail) .kf-detail{display:none}
/* 画框高度跟着画布走、横向放不下就滚动：以前固定高度 + svg 100% + viewBox meet，把整幅图等比塞进画框，
   13px 的字在屏幕上只剩 3.5–8px（评审实测）。现在 svg 的像素宽高由脚本按世界尺寸 × 比例（不低于 0.86）写死。 */
.kf-box{position:relative;min-height:220px;border:1px solid var(--line);border-radius:12px;
  background:radial-gradient(120% 90% at 12% 0%,#fbfcff 0%,var(--canvas) 70%);overflow-x:auto;overflow-y:hidden;
  overscroll-behavior-x:contain;box-shadow:inset 0 1px 0 #fff,0 1px 2px rgba(23,32,51,.05)}
.kf-box .empty{padding:60px 20px;text-align:center;color:var(--faint)}
.kf-svg{display:block;max-width:none;cursor:grab;touch-action:pan-x pan-y}
.kf-svg.dragging{cursor:grabbing}

/* 聚焦条：说清现在只画了哪一小片，给一个回全貌的按钮 */
.kf-focusbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:0 0 8px;padding:7px 12px;
  border:1px solid var(--line);border-left:3px solid var(--info);border-radius:9px;background:var(--panel);font-size:13px;color:var(--ink)}
.kf-focusbar[hidden]{display:none}
.kf-focusbar button{padding:4px 11px;font-size:12.5px}
.kf-focustext{flex:1 1 280px;line-height:1.6}

/* 列：一条彩色顶边 + 极淡的底色带，既分得开又不喧宾夺主 */
.kf-col{fill:var(--panel);opacity:.66;stroke:var(--line2);stroke-width:1}
.kf-coltint{opacity:.05}
.kf-coltop{opacity:.85}
/* 画布上所有字都 ≥ 13px（画布最多缩到 0.86 倍，屏幕上仍 ≥ 11px）；人把画布缩小时按 --kf-ts 反算放大 */
.kf-colname{font-family:-apple-system,"PingFang SC",sans-serif;font-weight:600;font-size:calc(16px * var(--kf-ts,1));fill:var(--ink)}
.kf-colcount{font-family:-apple-system,"PingFang SC",sans-serif;font-size:calc(13px * var(--kf-ts,1));fill:var(--faint)}
.kf-colverb{font-family:-apple-system,"PingFang SC",sans-serif;font-size:13px;fill:var(--muted)}
.kf-arrow{stroke:var(--tan);stroke-width:1.4;fill:none;opacity:.55}
/* 空列不消失：列头、计数、抽屉都留着，再写一句为什么空 */
.kf-colempty{font-family:-apple-system,"PingFang SC",sans-serif;font-size:calc(13px * var(--kf-ts,1));fill:var(--faint)}
.kf-flowverb{font-family:-apple-system,"PingFang SC",sans-serif;font-size:13px;fill:var(--faint)}
/* 缩得很小时只留列名和计数：说明文字再放大就会压到牌子上 */
.kf-svg.far .kf-colverb,.kf-svg.far .kf-flowverb{display:none}

/* 边：三层材质。缓动统一用 M3 的 emphasized 曲线，前两成时间走完八成距离，看起来利落不拖沓。 */
.kf-edge{fill:none;stroke:var(--tan);stroke-width:1.2;opacity:.55;stroke-linecap:round;
  transition:opacity .28s cubic-bezier(.2,0,0,1),stroke-width .28s cubic-bezier(.2,0,0,1)}
/* 弱边只做底噪：单条几乎看不见，密集处靠叠色自然变深，不抢结构 */
.kf-edges.kf-weak{mix-blend-mode:multiply}
.kf-edges.kf-weak .kf-edge{opacity:.16;stroke-width:1}
.kf-edges.kf-struct .kf-edge{opacity:.62;stroke-width:1.4}
.kf-edge.weak{stroke-dasharray:4 4}
.kf-edge.dim{opacity:.17}
.kf-edge.hot{stroke:var(--accent);stroke-width:2;opacity:.9}
.kf-edge.path{stroke:var(--info);stroke-width:2.6;opacity:1}
.kf-edge.sel{stroke:var(--accent);stroke-width:3;opacity:1}
/* 有焦点时，非焦点的两层整体退到背景：景深是层级工具，不是装饰 */
.kf-box.focused .kf-edges.kf-weak{opacity:.45}
.kf-box.focused .kf-edges.kf-struct{opacity:.5}
/* 透明宽线专门接点击：细线本身点不中 */
.kf-hit{fill:none;stroke:transparent;stroke-width:12;cursor:pointer;pointer-events:stroke}
.kf-hits{pointer-events:visibleStroke}
.kf-edges.kf-hot{filter:url(#kfglow)}
.kf-particle{fill:var(--accent);opacity:.95}
.kf-edges.kf-hot .kf-edge.hot,.kf-edges.kf-hot .kf-edge.path{stroke-dasharray:9 7;animation:kfdash 1.1s linear infinite}
@keyframes kfdash{to{stroke-dashoffset:-32}}
.kf-box.noflow .kf-edge{animation:none!important;stroke-dasharray:none}
.kf-box.noflow .kf-edge.weak{stroke-dasharray:4 4}
.kf-box.noflow .kf-particle{display:none}

/* 画布下方一行：左边统计，右边「本体走到哪一步了」 */
.kf-foot{display:flex;gap:16px;align-items:center;flex-wrap:wrap;margin:9px 0 0}
.kf-foot .subtle{margin:0;flex:1 1 320px;font-size:11.5px}
.kf-maturity{display:flex;align-items:stretch;gap:3px;flex-wrap:wrap}
.kf-mlabel{font:10.5px/22px var(--mono);color:var(--faint);margin-right:6px;flex:none}
.kf-mseg{display:flex;align-items:center;justify-content:center;gap:4px;min-width:64px;padding:2px 9px;
  font-size:11px;color:var(--muted);border:1px solid var(--line);border-radius:999px;background:var(--panel);white-space:nowrap}
.kf-mseg b{font-size:12.5px;color:var(--ink)}
.kf-mseg.st-proposal{border-style:dashed}
.kf-mseg.st-candidate{border-color:var(--info);color:var(--info)}
.kf-mseg.st-candidate b{color:var(--info)}
.kf-mseg.st-canonical{border-color:var(--ok);background:#eef6f1;color:var(--ok)}
.kf-mseg.st-canonical b{color:var(--ok)}
.kf-mnote{font-size:11px;color:var(--faint);align-self:center;flex:1 1 100%;margin-top:3px}

/* 条目 */
.kf-node{cursor:pointer;transition:opacity .28s cubic-bezier(.2,0,0,1)}
.kf-pill{fill:var(--panel);stroke:var(--line);stroke-width:1}
/* 状态直接画在框上：提案虚线、候选实线蓝、已签署实底绿 */
.kf-pill.st-proposal{stroke-dasharray:3 3}
.kf-pill.st-candidate{stroke:var(--info)}
.kf-pill.st-canonical{fill:#eef6f1;stroke:var(--ok)}
.kf-node:hover .kf-pill{stroke:var(--muted)}
.kf-node.sel .kf-pill{stroke:var(--accent);stroke-width:2.2}
.kf-node.hot .kf-pill{stroke:var(--accent)}
.kf-node.path .kf-pill{stroke:var(--info);stroke-width:2}
/* 淡出是为了让重点跳出来，不是让其余内容消失：太淡会让人以为图坏了 */
.kf-node.dim{opacity:.42}
/* 焦点晕轮：核心形状不变，只在外面套一圈光 */
.kf-halo{fill:var(--accent);opacity:.1;stroke:var(--accent);stroke-opacity:.3;stroke-width:1;pointer-events:none}
.kf-node.path .kf-halo{fill:var(--info);stroke:var(--info)}
/* 牌子两行：第一行业务名加粗，第二行一句解释灰色小字，编码最小最淡（只给工程师对表用） */
.kf-label{font:600 14px -apple-system,"PingFang SC",sans-serif;fill:var(--ink);pointer-events:none}
.kf-sub{font:13px -apple-system,"PingFang SC",sans-serif;fill:var(--muted);pointer-events:none}
.kf-code{font:13px var(--mono);fill:var(--faint);pointer-events:none}
/* 每层一个小图形：形状区分层，颜色只是辅助 */
.kf-glyph{pointer-events:none}
.kf-gshape{fill-opacity:.16;stroke-width:1.3}
.kf-gline{fill:none;stroke-width:1;opacity:.8}
.kf-gring{fill:none;stroke-width:1;stroke-dasharray:1.6 1.4}
.kf-degbox{fill:var(--panel2);stroke:var(--line2);stroke-width:1;pointer-events:none}
.kf-degbox.weakonly{fill:transparent;stroke-dasharray:2 2}
.kf-deg{font:13px var(--mono);fill:var(--muted);pointer-events:none}
.kf-dot{pointer-events:none}
.kf-node:focus-visible .kf-pill{stroke:var(--info);stroke-width:2}
.kf-node:focus{outline:none}
/* 呼吸：只在被追溯到的条目上，按链路层级依次起搏，看得出「信息一级级传过来」 */
.kf-node.breathe .kf-pill{animation:kfbreathe 2.4s ease-in-out infinite;animation-delay:var(--d,0s)}
@keyframes kfbreathe{0%,100%{opacity:1}50%{opacity:.7}}

/* 被追溯链路临时拉上台的条目：灰边角标，明说它平时不在列上 */
.kf-tempbox{fill:var(--panel2);stroke:var(--line2);stroke-width:1;stroke-dasharray:2 2;pointer-events:none}
.kf-temptext{font:13px -apple-system,"PingFang SC",sans-serif;fill:var(--faint)}
/* 缩小看全貌时角标不画，临时上台的牌子改成灰底，一眼仍分得出「平时不在列上」 */
.kf-svg.far .kf-node.temp .kf-pill{fill:var(--panel2)}

.kf-more{cursor:pointer}
.kf-more rect{fill:var(--panel2);stroke:var(--line2);stroke-width:1}
.kf-more:hover rect{stroke:var(--muted);fill:var(--accent-soft)}
.kf-more text{font-family:-apple-system,"PingFang SC",sans-serif;font-size:calc(13px * var(--kf-ts,1));fill:var(--muted)}
/* 列底那条「另有 N 条收起来了」：虚线框，和「展开」区分开 */
.kf-golist rect{fill:transparent;stroke-dasharray:3 3}
.kf-golist text{fill:var(--faint)}

/* 右侧详情 */
/* 画框现在跟着画布一样高：详情栏吸在视口顶上，往下翻画布时它还在眼前 */
.kf-detail{position:sticky;top:8px;border:1px solid var(--line);border-radius:12px;background:var(--panel);padding:14px 16px;
  max-height:min(86vh,820px);overflow:auto;box-shadow:0 1px 2px rgba(23,32,51,.05)}
.kf-dhead{display:flex;gap:10px;align-items:flex-start;margin-bottom:10px}
.kf-dtype{width:10px;height:10px;border-radius:50%;margin-top:6px;flex:none}
.kf-dhead h3{margin:0;font-size:15px;line-height:1.4;word-break:break-all}
.kf-dsub{margin:2px 0 3px;font-size:12.5px;line-height:1.6;color:var(--muted)}
/* 结构化字段：左边中文字段名，右边中文取值；条目原文不上详情（内部枚举会漏出来），原话看「原话（逐字）」 */
.kf-facts{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;margin:0;font-size:12.5px;line-height:1.65}
.kf-facts dt{color:var(--faint);white-space:nowrap}
.kf-facts dd{margin:0;color:var(--ink);word-break:break-word}
.kf-dsec{margin:12px 0;padding-top:10px;border-top:1px solid var(--line2)}
.kf-dsec h4{margin:0 0 6px;font-size:12px;color:var(--muted);font-weight:600}
.kf-nb{font-size:12.5px;line-height:1.8}
.kf-nb a{color:var(--info);text-decoration:underline dotted;cursor:pointer}
.kf-nb a:hover{color:var(--accent)}
.kf-quote{border-left:2px solid var(--line);margin:6px 0;padding:4px 10px;font-size:12px;line-height:1.7;
  background:var(--panel2);border-radius:0 5px 5px 0}
.kf-quote footer{font-size:11.5px;color:var(--faint);margin-top:3px}
/* 下游的口径说明、机器猜的邻居：小一号、灰，和正经的上下游分开 */
.kf-note{font-size:11.5px;line-height:1.6;color:var(--faint);margin-top:4px}
.kf-guess{margin-top:4px}
/* 选中一条连线时的两端卡 */
.kf-edgecard{display:flex;flex-direction:column;gap:6px}
.kf-edgecard a{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--line);border-radius:8px;
  background:var(--panel2);cursor:pointer;color:var(--ink);font-size:13px;text-decoration:none}
.kf-edgecard a:hover{border-color:var(--accent)}
.kf-edgecard a i{width:9px;height:9px;border-radius:50%;flex:none}
.kf-edgecard a small{margin-left:auto;font:10.5px var(--mono);color:var(--faint)}
.kf-edgearrow{font-size:11.5px;color:var(--muted);padding-left:12px}
.kf-detail code{font:11px var(--mono);word-break:break-all}

/* 新建表单 */
.kf-form label{display:block;margin:9px 0}
.kf-form label>span{display:block;font-size:12px;color:var(--muted);margin-bottom:3px}
.kf-form input,.kf-form select,.kf-form textarea{width:100%;padding:7px 9px;border:1px solid var(--line);
  border-radius:6px;background:#fff;font:inherit}
.kf-src{border:1px solid var(--line2);border-radius:7px;padding:4px 12px 10px;margin:12px 0}
.kf-src legend{font-size:11.5px;color:var(--faint);padding:0 5px}
.kf-err{color:var(--err);font-size:12.5px;min-height:18px;margin:6px 0}

@media (max-width:1100px){.kf-wrap.has-detail{grid-template-columns:1fr}.kf-detail{max-height:none;position:static}}
/* 手机宽度下「起点 · 终点 · 查一下」一行放不下，按钮会顶出工具条：允许折行 */
@media (max-width:560px){.kf-group{flex-wrap:wrap}}
@media (prefers-reduced-motion:reduce){
  .kf-edge,.kf-node .kf-pill{animation:none!important}
  .kf-particle{display:none}
}
