canopy-engine 0.1.5 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ import{fileURLToPath as __cf,pathToFileURL as __cu}from"node:url";import{dirname as __cd,join as __cj}from"node:path";import{createRequire as __cr}from"node:module";
2
+ const require=__cr(import.meta.url);
3
+ globalThis.__canopyBundle??=__cj(__cd(__cf(import.meta.url)),"canopy.js");
4
+ globalThis.__canopyUrl??=(r)=>__cu(__cj(__cd(__cd(globalThis.__canopyBundle)),r)).href;
5
+ import{b as $,c as le}from"./chunk-ZF5SEDQB.js";import{Parser as Q,Language as de}from"web-tree-sitter";import D from"node:path";import{fileURLToPath as _t}from"node:url";var dn=D.dirname(_t(globalThis.__canopyUrl("src/stage2_parse/initParser.js")));var X=new WeakMap,M=t=>X.get(t)??"javascript",T=t=>M(t)!=="javascript";function q(t){let e=D.extname(t);return e===".tsx"?"tsx":e===".ts"||e===".mts"||e===".cts"?"typescript":"javascript"}var Ft={typescript:"tree-sitter-typescript.wasm",tsx:"tree-sitter-tsx.wasm"};async function pn(t,e){for(let r of new Set(e.map(q))){if(t.grammars.has(r))continue;let o=D.join($("tree-sitter-typescript"),Ft[r]),n=await de.load(o);X.set(n,r);let i=new Q;i.setLanguage(n),t.grammars.set(r,{parser:i,language:n})}return t}async function un(){await Q.init();let t=new Q,e=D.join($("tree-sitter-javascript"),"tree-sitter-javascript.wasm"),r=await de.load(e);return t.setLanguage(r),X.set(r,"javascript"),{parser:t,language:r,grammars:new Map([["javascript",{parser:t,language:r}]])}}import E from"node:fs";import w from"node:path";import gt from"node:os";import ue from"node:crypto";import{threadId as Nt}from"node:worker_threads";import{fileURLToPath as Ct}from"node:url";var wt=w.dirname(Ct(globalThis.__canopyUrl("src/stage2_parse/parseCache.js"))),fe=w.join(wt,"..",".."),ee=process.env.CANOPY_CACHE_DIR||(le()?w.join(gt.homedir(),".canopy-cache"):w.join(fe,".canopy-cache")),pe=t=>ue.createHash("sha256").update(t).digest("hex");function G(t,e){return E.readdirSync(t,{withFileTypes:!0}).flatMap(r=>r.isDirectory()?G(w.join(t,r.name),e):e.some(o=>r.name.endsWith(o))?[w.join(t,r.name)]:[]).sort()}var te=null;function kt(){if(te)return te;let t=w.join(fe,"src"),e=!E.existsSync(t)&&globalThis.__canopyBundle,r=e?E.readdirSync(w.dirname(e)).filter(i=>i.endsWith(".js")).sort().map(i=>w.join(w.dirname(e),i)):[],o=e?[...r,...G(w.join(w.dirname(e),"queries"),[".scm"])]:[...G(w.join(t,"stage2_parse"),[".js",".scm"]),...G(w.join(t,"stage5_entrypoints"),[".js"]).filter(i=>/match/i.test(w.basename(i))),w.join(t,"utils","loadQuery.js")],n=ue.createHash("sha256");for(let i of o)n.update(w.relative(e?w.dirname(e):t,i).split(w.sep).join("/")).update("\0").update(E.readFileSync(i).toString("utf8").replace(/\r\n/g,`
6
+ `)).update("\0");for(let i of["web-tree-sitter","tree-sitter-javascript","tree-sitter-typescript"])try{n.update(i+"@"+JSON.parse(E.readFileSync(w.join($(i),"package.json"),"utf8")).version)}catch{n.update(i+"@?")}return te=n.digest("hex").slice(0,16)}function me({prune:t=!0}={}){let e=kt(),r=w.join(ee,e);if(E.mkdirSync(r,{recursive:!0}),t)for(let o of E.readdirSync(ee,{withFileTypes:!0}))o.isDirectory()&&o.name!==e&&E.rmSync(w.join(ee,o.name),{recursive:!0,force:!0});return{version:e,keyFor:(o,n="javascript")=>pe(n==="javascript"?o:Buffer.concat([Buffer.from(n+"\0"),o])),get(o){try{return JSON.parse(E.readFileSync(w.join(r,o+".json"),"utf8"))}catch{return null}},set(o,n){let i=w.join(r,`${o}.${process.pid}.${Nt}.tmp`);E.writeFileSync(i,JSON.stringify(n)),E.renameSync(i,w.join(r,o+".json"))}}}function he(t){return JSON.parse(JSON.stringify(t,(e,r)=>r&&typeof r=="object"&&r.startPosition&&r.endPosition&&"type"in r&&"tree"in r?void 0:r))}import bt from"node:fs";function Fe(t,e){let r=bt.readFileSync(e,"utf-8"),o=t.parse(r);if(ne(o)||r.includes("\0")){let n=r,i=_e(o);for(let s of St){let a=s.apply(n);if(a===n)continue;let c=t.parse(a),p=_e(c);if(p<i||s.always){try{o.delete()}catch{}n=a,o=c,i=p}else try{c.delete()}catch{}}n!==r&&(o.canopyOriginalSource=r)}return{tree:o,source:r,filePath:e}}var ge=t=>t.replace(/[^\n]/g," "),ye=t=>"any"+ge(t).slice(3),xe=/(?<![&])&(?![&=])/g,St=[{name:"NUL byte",always:!0,apply:t=>t.replace(/\0/g," ")},{name:"export type * from",apply:t=>t.replace(/\bexport\s+type\s+\*\s*(?:as\s+[\w$]+\s+)?from\s*(["'])[^"'\n]*\1\s*;?/g,ge)},{name:"typeof import()",apply:t=>t.replace(/typeof\s+import\s*\(\s*(["'])[^"'\n]*\1\s*\)(?:\s*\.\s*[\w$]+)*/g,ye)},{name:"import().Type",apply:t=>t.replace(/(?<=[:<,|&?]\s*|\breadonly\s+|\bkeyof\s+|\bextends\s+|\bas\s+)import\s*\(\s*(["'])[^"'\n]*\1\s*\)(?:\s*\.\s*[\w$]+)+(?!\s*\()/g,ye)},{name:"generic call signature after a member",apply:t=>t.replace(/(?<=\n[ \t]*)[ ](?=<\s*[A-Za-z_$][\w$]*\s*(?:extends\b|=|,|>\s*\())/g,";")},{name:"& in JSX text",apply:t=>t.replace(/>[^<>{}"'`]*&[^<>{}"'`]*</g,e=>e.replace(xe,"+"))},{name:"& in JSX attribute",apply:t=>t.replace(/(\s[\w:-]+=)"([^"\n]*)"/g,(e,r,o)=>r+'"'+o.replace(xe,"+")+'"')}];function _e(t){let e=0;return(function r(o){if(o.type==="ERROR"){e++;return}for(let n=0;n<o.childCount;n++)r(o.child(n))})(t.rootNode),e}function ne(t){let e=!1;function r(o){if(o.type==="ERROR"){e=!0;return}for(let n=0;n<o.childCount;n++)r(o.child(n))}return r(t.rootNode),e}import re from"node:fs";import B from"node:path";import{fileURLToPath as vt}from"node:url";import{Query as Et}from"web-tree-sitter";var Ne=B.dirname(vt(globalThis.__canopyUrl("src/utils/loadQuery.js"))),Ce=new WeakMap;function k(t,e){let r=Ce.get(t);if(r||Ce.set(t,r=new Map),r.has(e))return r.get(e);let o=B.join(Ne,"..","stage2_parse","queries"),n=re.existsSync(o)?o:B.join(Ne,"..","..","dist","queries"),i=M(t),c=(i==="javascript"?[]:i==="tsx"?["tsx","typescript"]:[i]).map(y=>B.join(n,y,e)).find(y=>re.existsSync(y))??B.join(n,e),p=re.readFileSync(c,"utf-8"),h=new Et(t,p);return r.set(e,h),h}var we="javascript",ke=t=>{we=t};var K=()=>we!=="javascript";var jt=new Set(["non_null_expression","as_expression","satisfies_expression","type_assertion"]);function S(t){for(;t;)if(jt.has(t.type))t=t.type==="type_assertion"?t.namedChild(t.namedChildCount-1):t.namedChild(0);else if(t.type==="parenthesized_expression"&&t.namedChildCount===1){let e=S(t.namedChild(0));if(e===t.namedChild(0))break;t=e}else break;return t}function Pt(t){let e=t;for(;e;){e=S(e);for(let r=0;r<e.childCount;r++)if(e.child(r).type==="optional_chain")return!0;if(e.type==="member_expression"||e.type==="subscript_expression")e=e.childForFieldName("object");else if(e.type==="call_expression")e=e.childForFieldName("function");else break}return!1}function L(t){let e=t.childForFieldName("function"),r=t.childForFieldName("arguments");if(!e)return{kind:"callOfCall",oldShape:!1,nameNode:t};let o=e.type==="identifier",n=e.type==="member_expression"?e.childForFieldName("property"):null,i=e.type==="member_expression"?S(e.childForFieldName("object")):null,s=o||i?.type==="identifier"&&n?.type==="property_identifier",a=n??e;if(K()&&t.parent?.type==="decorator")return{kind:"decorator",oldShape:s,nameNode:a};if(r&&r.type==="template_string")return{kind:"taggedTemplate",oldShape:s,nameNode:a};if(Pt(t))return{kind:"optionalCall",oldShape:s,nameNode:a};if(o)return{kind:"call",oldShape:s,nameNode:a};if(e.type==="subscript_expression")return{kind:"computed",oldShape:!1,nameNode:e};if(e.type==="member_expression")return i.type==="super"?{kind:"super",oldShape:!1,nameNode:a}:i.type==="this"?{kind:"thisCall",oldShape:!1,nameNode:a}:s?{kind:"methodCall",oldShape:!0,nameNode:a}:{kind:"chainedCall",oldShape:!1,nameNode:a};if(e.type==="import")return{kind:"dynamicImport",oldShape:!1,nameNode:e};if(e.type==="super")return{kind:"super",oldShape:!1,nameNode:e};let c=e;for(;c.type==="parenthesized_expression"&&c.namedChildCount===1;)c=c.namedChild(0);return c.type==="arrow_function"||c.type==="function_expression"||c.type==="function"||c.type==="generator_function"?{kind:"iife",oldShape:!1,nameNode:e}:{kind:"callOfCall",oldShape:!1,nameNode:e}}function be(t){let e=t.childForFieldName("name");return e?e.type==="identifier"?/^[A-Z]/.test(e.text)?{kind:"jsx",oldShape:!0,nameNode:e}:null:e.type==="member_expression"||e.type==="nested_identifier"?{kind:"jsxMember",oldShape:!1,nameNode:e}:null:null}function z(t){let e=t;for(;e&&e.type!=="call_expression";)e=e.parent;return e}function Se(t){let e=t.childForFieldName("function"),r=!1;for(;e;)if(e=S(e),e.type==="member_expression"||e.type==="subscript_expression")e=e.childForFieldName("object");else if(e.type==="call_expression")r=!0,e=e.childForFieldName("function");else if(e.type==="await_expression"||e.type==="parenthesized_expression"||e.type==="non_null_expression")e=e.namedChild(0);else break;return e&&e.type==="identifier"?{head:e.text,called:r}:{head:null,called:!1}}function ve(t){let e=t.text.replace(/\s+/g," ");return e.length>120?e.slice(0,117)+"...":e}function je(t,e){let n=k(e,"functionDeclarations.scm").matches(t.rootNode).map(i=>{let s=i.captures.find(p=>p.name==="func_name"),a=i.captures.find(p=>p.name==="func_params"),c=i.captures.find(p=>p.name==="func_declaration");return{name:s.node.text,params:a.node.text,startLine:c.node.startPosition.row+1,endLine:c.node.endPosition.row+1,startIndex:c.node.startIndex,endIndex:c.node.endIndex,node:s.node}});return T(e)&&n.push(...Tt(t,e)),n}function Tt(t,e){return k(e,"defaultExports.scm").matches(t.rootNode).map(r=>{let o=r.captures.find(i=>i.name==="func_declaration").node;return{name:"default",params:r.captures.find(i=>i.name==="func_params").node.text,startLine:o.startPosition.row+1,endLine:o.endPosition.row+1,startIndex:o.startIndex,endIndex:o.endIndex,node:o}})}function Pe(t,e){return k(e,"arrowFunctions.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(c=>c.name==="func_name"),s=n.captures.find(c=>c.name==="func_params"),a=n.captures.find(c=>c.name==="func_declaration");return{name:i.node.text,params:s.node.text,startLine:a.node.startPosition.row+1,endLine:a.node.endPosition.row+1,startIndex:a.node.startIndex,endIndex:a.node.endIndex,node:i.node}})}function Te(t,e){let o=k(e,"classMethods.scm").matches(t.rootNode),n=T(e);return o.map(i=>{let s=i.captures.find(y=>y.name==="class_name"),a=i.captures.find(y=>y.name==="method_name"),c=i.captures.find(y=>y.name==="method_params"),p=i.captures.find(y=>y.name==="method_declaration"),h=p.node;if(n)for(let y=h.previousNamedSibling;y&&y.type==="decorator";y=y.previousNamedSibling)h=y;return{className:s?s.node.text:"default",methodName:a.node.text,params:c.node.text,startLine:h.startPosition.row+1,endLine:p.node.endPosition.row+1,startIndex:h.startIndex,endIndex:p.node.endIndex,node:a.node}})}function Le(t,e){let o=k(e,"imports.scm").matches(t.rootNode),n=T(e);return o.filter(i=>!(n&&Lt(i.captures[0].node))).map(i=>{let s=i.captures[0].node;return{source:s.text,startLine:s.startPosition.row+1}})}function Lt(t){let e=t;for(;e&&e.type!=="import_statement";)e=e.parent;if(!e)return!1;if(e.children.some(i=>i.type==="type"))return!0;let r=e.namedChildren.find(i=>i.type==="import_clause");if(!r)return!1;let o=r.namedChildren;if(o.length!==1||o[0].type!=="named_imports")return!1;let n=o[0].namedChildren.filter(i=>i.type==="import_specifier");return n.length>0&&n.every(i=>i.children.some(s=>s.type==="type"))}function W(t){let e=t;for(;e&&e.type!=="call_expression"&&e.type!=="new_expression";)e=e.parent;if(!e)return null;let r=e.childForFieldName("arguments");if(!r)return null;let o=r.namedChildren?.[0];return!o||o.type==="arrow_function"||o.type==="function_expression"?null:o.text}var It=/^(addEventListener|removeEventListener|on|once|postMessage|onmessage|subscribe)$/i;function R(t){let e=t;for(;e.parent;){let r=e.parent;if(r.type==="arrow_function"||r.type==="function_expression"){let o=r.parent;if(o&&(o.type==="variable_declarator"||o.type==="public_field_definition"))break;if(o&&o.type==="return_statement")return!0;if(o&&o.type==="arguments"&&o.parent?.type==="call_expression"){let n=o.parent.childForFieldName("function"),i=n?.type==="member_expression"?n.childForFieldName("property")?.text:n?.text;if(i&&It.test(i))return!0}}else if(r.type==="function_declaration"||r.type==="method_definition")break;e=r}return!1}function Ie(t,e){return k(e,"callExpressions.scm").matches(t.rootNode).map(n=>{let i=n.captures[0].node;return{calleeName:i.text,startLine:i.startPosition.row+1,startColumn:i.startPosition.column,branchPath:A(i),firstArgText:W(i),deferred:R(i),callKind:L(z(i)).kind}})}function Re(t,e){return T(e)?Rt(t):k(e,"methodCalls.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(a=>a.name==="object_name"),s=n.captures.find(a=>a.name==="method_name");return{objectName:i.node.text,methodName:s.node.text,startLine:s.node.startPosition.row+1,startColumn:s.node.startPosition.column,firstArgText:W(s.node),branchPath:A(s.node),deferred:R(s.node),callKind:L(z(s.node)).kind}})}function Rt(t){let e=[];for(let r of t.rootNode.descendantsOfType("call_expression")){let o=r.childForFieldName("function");if(!o||o.type!=="member_expression")continue;let n=S(o.childForFieldName("object")),i=o.childForFieldName("property");!n||n.type!=="identifier"||!i||i.type!=="property_identifier"||e.push({objectName:n.text,methodName:i.text,startLine:i.startPosition.row+1,startColumn:i.startPosition.column,firstArgText:W(i),branchPath:A(i),deferred:R(i),callKind:L(z(i)).kind})}return e}function At(t){if(!t)return"return";if(t.type==="object"){let e=t.namedChildren.filter(o=>o.type==="pair"),r=e.find(o=>o.childForFieldName("key")?.text?.replace(/^['"]|['"]$/g,"")==="type")??e.find(o=>o.childForFieldName("key")?.text?.replace(/^['"]|['"]$/g,"")==="entity");if(r){let o=r.childForFieldName("key"),n=r.childForFieldName("value");return`return { ${o.text}: ${n.text} }`}}return"return "+t.text.split(/\r?\n/)[0].trim()}function Ae(t,e){return k(e,"returns.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(c=>c.name==="return_statement"),s=n.captures.find(c=>c.name==="return_value"),a=i.node;return{label:At(s?.node),startLine:a.startPosition.row+1,startColumn:a.startPosition.column,branchPath:A(a),deferred:R(a)}})}var Ot=new Set(["function_declaration","arrow_function","function_expression","method_definition"]);function J(t,e){return!(!e||t.row<e.startPosition.row||t.row>e.endPosition.row||t.row===e.startPosition.row&&t.column<e.startPosition.column||t.row===e.endPosition.row&&t.column>e.endPosition.column)}function A(t){let e=[],r=t,o=t.startPosition;for(;r.parent;){let n=r.parent;if(Ot.has(n.type))break;if(n.type==="if_statement"){let i=n.childForFieldName("condition"),s=n.childForFieldName("consequence"),a=n.childForFieldName("alternative");J(o,s)?e.push({type:"if",conditionText:i?i.text:"",conditionLine:(i??n).startPosition.row+1,outcome:"true"}):J(o,a)&&e.push({type:"if",conditionText:i?i.text:"",conditionLine:(i??n).startPosition.row+1,outcome:"false"})}else if(n.type==="switch_case"||n.type==="switch_default"){let i=n.parent,s=i&&i.type==="switch_body"?i.parent:null;if(s){let a=s.childForFieldName("value"),c=n.childForFieldName("value");e.push({type:"switch",conditionText:a?a.text:"",conditionLine:(a??s).startPosition.row+1,outcome:n.type==="switch_default"?"default":c?c.text:"case"})}}else if(n.type==="try_statement"){let i=n.childForFieldName("handler"),s=n.childForFieldName("finalizer"),a="try";J(o,i)?a="catch":J(o,s)&&(a="finally"),e.push({type:"try",conditionText:"try / catch",conditionLine:n.startPosition.row+1,outcome:a})}r=n}return e.reverse()}function Oe(t,e){return k(e,"exports.scm").matches(t.rootNode).map(o=>o.captures[0].node.text)}var Ee=new WeakMap;function $t(t,e){let r=Ee.get(t);return r||(r=k(e,"returns.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(a=>a.name==="return_statement"),s=n.captures.find(a=>a.name==="return_value");return{stmtNode:i.node,valueNode:s?.node}}),Ee.set(t,r)),r}function $e(t,e,r){return $t(t,e).filter(o=>o.stmtNode.startPosition.row+1>=r.startLine&&o.stmtNode.endPosition.row+1<=r.endLine).map(o=>({shape:Mt(o.valueNode),condition:qt(o.stmtNode,r),line:o.stmtNode.startPosition.row+1}))}function Mt(t){if(!t)return"nothing";switch(t.type){case"true":case"false":return"boolean";case"number":return"number";case"string":return"string";case"null":return"null";case"identifier":return`variable (${t.text})`;case"object":return"object";case"array":return"array";case"call_expression":return`result of ${t.text.split("(")[0]}()`;case"binary_expression":return"boolean (comparison)";case"ternary_expression":return"conditional value";default:return"expression"}}function qt(t,e){let r=t.parent;for(;r;){if(r.type==="if_statement"){let o=r.childForFieldName("condition");if(o)return o.text}r=r.parent}return null}function Me(t,e){return M(e)==="typescript"?[]:k(e,"jsxElements.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(s=>s.name==="jsx_tag");return{calleeName:i.node.text,startLine:i.node.startPosition.row+1}}).filter(n=>/^[A-Z]/.test(n.calleeName))}function qe(t,e){return k(e,"fetchApiCalls.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(p=>p.name==="url"),s=n.captures.find(p=>p.name==="http_method"),a=i.node.text,c=/^https?:\/\//.test(a)?"external":"internal";return{url:a,method:s?.node.text??"get",startLine:i.node.startPosition.row+1,scope:c}})}function Be(t,e){return k(e,"socketEmit.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(s=>s.name==="event_name");return{eventName:i.node.text,startLine:i.node.startPosition.row+1}})}function We(t,e){return k(e,"rawSocketSend.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(s=>s.name==="event_name");return{eventName:i.node.text,startLine:i.node.startPosition.row+1}})}function He(t,e){return k(e,"rawSocketTypeCheck.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(s=>s.name==="event_name");return{eventName:i.node.text,startLine:i.node.startPosition.row+1}})}function Ue(t,e){return k(e,"socketOn.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(c=>c.name==="event_name"),s=n.captures.find(c=>c.name==="handler_ref"),a=n.captures.find(c=>c.name==="handler_inline");return{eventName:i.node.text,startLine:i.node.startPosition.row+1,handlerName:s?s.node.text:null,isInlineHandler:!!a,handlerStartLine:a?a.node.startPosition.row+1:null,handlerEndLine:a?a.node.endPosition.row+1:null,handlerStartIndex:a?a.node.startIndex:null,handlerEndIndex:a?a.node.endIndex:null}})}function De(t){let e=t.rootNode.descendantsOfType(["call_expression","new_expression","jsx_opening_element","jsx_self_closing_element",...K()?["decorator"]:[]]),r=[];for(let o of e){let n,i,s,a=null,c=null,p=!1,h=null,y=null,N=null;if(o.type==="new_expression"){if(s=o.childForFieldName("constructor"),!s)continue;n="new",i=s,a=s.type==="identifier"?s.text:null;let u=o.childForFieldName("arguments")?.namedChildren?.[0];h=u&&u.type!=="arrow_function"&&u.type!=="function_expression"?u.text:null}else if(o.type==="decorator"){let d=o.namedChild(0);if(!d||d.type==="call_expression")continue;n="decorator",i=d.type==="member_expression"?d.childForFieldName("property")??d:d,s=d}else if(o.type==="call_expression"){let d=L(o);if(d.oldShape)continue;if(n=d.kind,i=d.nameNode,s=o.childForFieldName("function")??o,n==="chainedCall"){let u=Se(o);c=u.head,p=u.called;let l=o.childForFieldName("function"),m=l?.type==="member_expression"?S(l.childForFieldName("object")):null;m?.type==="member_expression"&&S(m.childForFieldName("object"))?.type==="this"&&m.childForFieldName("property")?.type==="property_identifier"&&l.childForFieldName("property")&&(y={thisProp:m.childForFieldName("property").text,thisMethod:l.childForFieldName("property").text})}h=W(i)}else{let d=be(o);if(!d||d.oldShape)continue;n=d.kind,i=d.nameNode,s=d.nameNode}r.push({callKind:n,calleeName:ve(s),startLine:i.startPosition.row+1,startColumn:i.startPosition.column,branchPath:A(i),firstArgText:h,deferred:R(i),chainHead:c,chainHeadCalled:p,ctorName:a,...y??{}})}return r.sort((o,n)=>o.startLine-n.startLine||o.startColumn-n.startColumn)}var oe=new Set(["get","post","put","patch","delete","head","options"]),ie=new Set(["axios","ky","got","superagent","$http"]),Bt=new Set(["function_declaration","function_expression","function","arrow_function","method_definition","generator_function_declaration","generator_function"]),Wt=new Set(["WebSocket","ReconnectingWebSocket","SockJS","W3CWebSocket"]),se=/^(ws|socket|sock|websocket|webSocket|wsClient|socketRef|wsRef|[A-Za-z]+Socket|[A-Za-z]+Ws|[A-Za-z]+WebSocket)$/,C=t=>{for(;t&&(t.type==="parenthesized_expression"||t.type==="as_expression"||t.type==="non_null_expression"||t.type==="await_expression"||t.type==="satisfies_expression"||t.type==="type_assertion")&&(t=t.namedChildren.find(e=>e.type!=="type_annotation"&&e.type!=="predefined_type"&&!/_type$/.test(e.type))??t.namedChildren[0],!!t););return t},H=t=>t.namedChildren.length?t.namedChildren.filter(e=>e.type==="string_fragment"||e.type==="escape_sequence").map(e=>e.text).join(""):t.text.slice(1,-1);function O(t){for(let e=t.parent;e;e=e.parent)if(Bt.has(e.type))return e;return null}function Ge(t){if(!t)return null;let e=t.childForFieldName("name")?.text??null;if(!e&&t.parent){let n=t.parent;n.type==="variable_declarator"?e=n.childForFieldName("name")?.text??null:n.type==="assignment_expression"?e=n.childForFieldName("left")?.text??null:n.type==="pair"&&(e=n.childForFieldName("key")?.text??null)}let r=[],o=t.childForFieldName("parameters");if(o)o.namedChildren.forEach((n,i)=>{let s=n.type==="identifier"?n:n.type==="assignment_pattern"?n.childForFieldName("left"):n.type==="required_parameter"||n.type==="optional_parameter"?n.childForFieldName("pattern")??n.namedChildren[0]:null;r[i]=s&&s.type==="identifier"?s.text:null});else{let n=t.childForFieldName("parameter");n&&n.type==="identifier"&&(r[0]=n.text)}return{name:e,params:r}}function Ke(t){let e=t.rootNode,r={httpCalls:[],pathCallsites:[],httpInstances:[],wsClients:[],wsServers:[],wsSends:[],wsServerSends:[],listens:[],wsReceivers:[]},o=new Map;for(let d of e.descendantsOfType("variable_declarator")){let u=d.childForFieldName("name"),l=d.childForFieldName("value");if(!u||u.type!=="identifier"||!l)continue;(o.get(u.text)??o.set(u.text,[]).get(u.text)).push({value:l,fn:O(d)})}let n=(d,u)=>{let l=o.get(d);if(!l)return null;for(let f=O(u);f;f=O(f)){let x=l.filter(_=>_.fn&&_.fn.id===f.id);if(x.length===1)return x[0].value}let m=l.filter(f=>!f.fn);return m.length===1?m[0].value:l.length===1?l[0].value:null},i=(d,u)=>{for(let l=O(u);l;l=O(l)){let f=Ge(l).params.indexOf(d);if(f>=0)return f}return-1};function s(d,u=0){if(d=C(d),!d)return[{t:"ref",name:""}];switch(d.type){case"string":return[{t:"lit",v:H(d)}];case"template_string":{let l=[];for(let m of d.namedChildren)m.type==="string_fragment"||m.type==="escape_sequence"?l.push({t:"lit",v:m.text}):m.type==="template_substitution"&&l.push(...s(m.namedChildren[0],u+1));return l.length?l:[{t:"lit",v:""}]}case"number":return[{t:"lit",v:d.text}];case"binary_expression":{let l=d.childForFieldName("operator")?.text;if(l==="||"||l==="??"){let m=s(d.childForFieldName("left"),u+1);if(m.length===1&&m[0].t==="env")return[{...m[0],fallback:s(d.childForFieldName("right"),u+1)}]}return l!=="+"?[{t:"ref",name:d.text.slice(0,60)}]:[...s(d.childForFieldName("left"),u+1),...s(d.childForFieldName("right"),u+1)]}case"shorthand_property_identifier":case"identifier":{let l=d.text,m=i(l,d);if(m>=0)return[{t:"param",name:l,index:m}];if(u<4){let f=n(l,d);if(f&&f.id!==d.id)return s(f,u+1)}return[{t:"ref",name:l}]}case"member_expression":{let l=d.text,m=/^(?:process\.env|import\.meta\.env)\.([A-Za-z0-9_$]+)$/.exec(l)??/^(?:process\.env|import\.meta\.env)\[["']([^"']+)["']\]$/.exec(l);return m?[{t:"env",name:m[1]}]:[{t:"ref",name:l.slice(0,60)}]}case"subscript_expression":{let l=/^(?:process\.env|import\.meta\.env)\[["']([^"']+)["']\]$/.exec(d.text);return l?[{t:"env",name:l[1]}]:[{t:"ref",name:d.text.slice(0,60)}]}case"call_expression":{let l=d.childForFieldName("function")?.text,m=d.childForFieldName("arguments")?.namedChildren[0];if(m&&(l==="encodeURIComponent"||l==="String"||l==="encodeURI"))return s(m,u+1);let f=C(d.childForFieldName("function"));if(f&&f.type==="member_expression"&&/^(replace|replaceAll|trim|trimEnd|trimStart|toString|toLowerCase)$/.test(f.childForFieldName("property")?.text??"")){let x=f.childForFieldName("object");if(x){let _=s(x,u+1);if(_.length&&_.every(F=>F.t!=="ref"))return _}}return[{t:"ref",name:d.text.slice(0,60)}]}default:return[{t:"ref",name:d.text.slice(0,60)}]}}let a=(d,u)=>{if(d=C(d),!(!d||d.type!=="object")){for(let l of d.namedChildren)if(l.type==="pair"){let m=l.childForFieldName("key");if((m?m.type==="string"?H(m):m.text:null)===u)return l.childForFieldName("value")}else if(l.type==="shorthand_property_identifier"&&l.text===u)return l}},c=d=>d.childForFieldName("arguments")?.namedChildren.filter(u=>u.type!=="comment")??[],p=d=>{if(!d)return null;let u=s(d);return u.length===1&&u[0].t==="lit"?u[0].v.toUpperCase():u.length===1&&u[0].t==="param"?{param:u[0].index}:"?"},h=(d,u,l,m)=>({client:d,method:u,parts:l,line:m.startPosition.row+1,fn:Ge(O(m))});for(let d of e.descendantsOfType("variable_declarator")){let u=d.childForFieldName("name"),l=C(d.childForFieldName("value"));if(!u||u.type!=="identifier"||!l||l.type!=="call_expression")continue;let m=C(l.childForFieldName("function"));if(!m||m.type!=="member_expression")continue;let f=m.childForFieldName("object")?.text,x=m.childForFieldName("property")?.text;if(!(ie.has(f)&&(x==="create"||x==="extend")))continue;let _=c(l)[0],F=a(_,"baseURL")??a(_,"baseUrl")??a(_,"prefixUrl")??a(_,"prefix_url");r.httpInstances.push({name:u.text,parts:F?s(F):[],line:d.startPosition.row+1})}let y=new Set(r.httpInstances.map(d=>d.name));for(let d of e.descendantsOfType("call_expression")){let u=C(d.childForFieldName("function"));if(!u)continue;let l=c(d),m=!1;if(u.type==="identifier"){let f=u.text;if(f==="fetch"&&l[0]){let x=C(l[0]);if(x&&x.type==="new_expression")continue;let _=a(l[1],"method");r.httpCalls.push(h("fetch",_?p(_):l[1]&&C(l[1])?.type!=="object"?"?":"GET",s(l[0]),d)),m=!0}else if((ie.has(f)||y.has(f))&&l[0]){let x=C(l[0]);if(x&&x.type==="object"){let _=a(x,"url");if(_){let F=a(x,"baseURL");r.httpCalls.push(h(y.has(f)?"inst:"+f:f,p(a(x,"method"))??"GET",[...F?s(F):[],...s(_)],d)),m=!0}}else r.httpCalls.push(h(y.has(f)?"inst:"+f:f,l[1]&&a(l[1],"method")?p(a(l[1],"method")):"GET",s(l[0]),d)),m=!0}else f==="io"&&l[0]&&(r.wsClients.push({client:"io",parts:s(l[0]),line:d.startPosition.row+1}),m=!0)}else if(u.type==="member_expression"){let x=u.childForFieldName("object")?.text??"",_=u.childForFieldName("property")?.text??"",F=ie.has(x)||y.has(x);if(F&&oe.has(_)&&l[0])r.httpCalls.push(h(y.has(x)?"inst:"+x:x,_.toUpperCase(),s(l[0]),d)),m=!0;else if(!F&&oe.has(_)&&/^[A-Za-z_$][\w$]*$/.test(x)&&l[0]&&(C(l[0])?.type==="string"||C(l[0])?.type==="template_string")){let g=s(l[0]);g[0]?.t==="lit"&&(g[0].v.startsWith("/")||/^https?:\/\//.test(g[0].v))&&(r.httpCalls.push(h("obj:"+x,_.toUpperCase(),g,d)),m=!0)}else if(F&&_==="request"&&l[0]){let g=a(l[0],"url");if(g){let b=a(l[0],"baseURL");r.httpCalls.push(h(y.has(x)?"inst:"+x:x,p(a(l[0],"method"))??"GET",[...b?s(b):[],...s(g)],d)),m=!0}}else if(_==="open"&&l.length>=2&&C(l[0])?.type==="string"&&/^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/i.test(H(C(l[0]))))r.httpCalls.push(h("xhr",H(C(l[0])).toUpperCase(),s(l[1]),d)),m=!0;else if(x==="io"&&_==="connect"&&l[0])r.wsClients.push({client:"io",parts:s(l[0]),line:d.startPosition.row+1}),m=!0;else if(_==="listen"&&l[0]){let g=C(l[0]),b=g&&g.type==="object"?a(g,"port"):g;b&&r.listens.push({parts:s(b),line:d.startPosition.row+1}),m=!0}else if(_==="send"&&/^(client|conn|connection|peer)$/.test(x.replace(/^this./,"").split(".").pop())&&l[0]){let g=C(l[0]),b=!1;if(g&&g.type==="call_expression"&&g.childForFieldName("function")?.text==="JSON.stringify"){let v=c(g)[0];b=!!(v&&(a(v,"type")||a(v,"event"))&&C(a(v,"type")??a(v,"event"))?.type==="string")}b||r.wsServerSends.push({recv:x,line:d.startPosition.row+1}),m=!0}else if(_==="send"&&se.test(x.replace(/^this\./,"").replace(/\.current$/,"").split(".").pop())){let g=l[0]?C(l[0]):null,b=!1;if(g&&g.type==="call_expression"&&g.childForFieldName("function")?.text==="JSON.stringify"){let v=c(g)[0];b=!!(v&&(a(v,"type")||a(v,"event"))&&C(a(v,"type")??a(v,"event"))?.type==="string")}b||r.wsSends.push({recv:x,line:d.startPosition.row+1}),m=!0}}if(!m){let f=null;if(u.type==="identifier"?f=u.text:u.type==="member_expression"&&(f=u.childForFieldName("property")?.text??null),!f||oe.has(f)||l.length===0||l.length>6)continue;let x=[],_=!1;l.slice(0,5).forEach((F,g)=>{let b=C(F);if(!b||b.type!=="string"&&b.type!=="template_string")return;let v=s(b);x.push({i:g,parts:v});let j=v[0];j&&j.t==="lit"&&(j.v.startsWith("/")||/^https?:\/\//.test(j.v))&&j.v.length>1&&(_=!0)}),_&&r.pathCallsites.push({callee:f,args:x,line:d.startPosition.row+1})}}let N=d=>d.replace(/^this./,"").replace(/.current$/,"").split(".").pop();for(let d of e.descendantsOfType("assignment_expression")){let u=d.childForFieldName("left");if(!u||u.type!=="member_expression"||u.childForFieldName("property")?.text!=="onmessage"||!se.test(N(u.childForFieldName("object")?.text??"")))continue;let l=C(d.childForFieldName("right"));r.wsReceivers.push({line:d.startPosition.row+1,handlerName:l&&l.type==="identifier"?l.text:null})}for(let d of e.descendantsOfType("call_expression")){let u=C(d.childForFieldName("function"));if(!u||u.type!=="member_expression"||u.childForFieldName("property")?.text!=="addEventListener")continue;let l=c(d),m=l[0]&&C(l[0]);if(!m||m.type!=="string"||H(m)!=="message"||!se.test(N(u.childForFieldName("object")?.text??"")))continue;let f=l[1]&&C(l[1]);r.wsReceivers.push({line:d.startPosition.row+1,handlerName:f&&f.type==="identifier"?f.text:null})}for(let d of e.descendantsOfType("new_expression")){let u=C(d.childForFieldName("constructor"));if(!u)continue;let l=u.text,m=c(d);if(Wt.has(l)&&m[0])r.wsClients.push({client:l,parts:s(m[0]),line:d.startPosition.row+1});else if(/^(WebSocketServer|WebSocket\.Server|WebSocket\.WebSocketServer|ws\.Server|ws\.WebSocketServer|Server)$/.test(l)){if(l==="Server"&&!/socket\.io|socketio/i.test(t.rootNode.text.slice(0,4e3)+""))continue;let f=a(m.find(x=>C(x)?.type==="object"),"path");r.wsServers.push({path:f?s(f):null,line:d.startPosition.row+1})}}return r}var Ht=new Set(["function_declaration","generator_function_declaration","function_expression","generator_function","arrow_function","method_definition"]),Ut=new Set(["arrow_function","function_expression","generator_function"]);function P(t,e){if(t)switch(t.type){case"identifier":case"shorthand_property_identifier_pattern":e.push(t.text);return;case"assignment_pattern":P(t.childForFieldName("left"),e);return;case"rest_pattern":case"object_pattern":case"array_pattern":case"pair_pattern":case"required_parameter":case"optional_parameter":for(let r of t.namedChildren)P(t.type==="pair_pattern"?t.childForFieldName("value"):r,e);return;default:if(t.type==="pattern"||t.namedChildCount){let r=t.childForFieldName?.("pattern");r&&P(r,e)}}}function Dt(t,e){let r=t;if(r.type==="export_statement"&&(r=r.childForFieldName("declaration")),!r)return null;if(r.type==="function_declaration"||r.type==="generator_function_declaration")return r.childForFieldName("name")?.text===e?{kind:"fn",declLine:r.startPosition.row+1}:null;if(r.type==="lexical_declaration"||r.type==="variable_declaration"){for(let o of r.namedChildren){if(o.type!=="variable_declarator")continue;let n=o.childForFieldName("name");if(n?.type==="identifier"){if(n.text!==e)continue;let s=S(o.childForFieldName("value"));return s&&Ut.has(s.type)?{kind:"fn",declLine:o.startPosition.row+1}:{kind:"other"}}let i=[];if(P(n,i),i.includes(e))return{kind:"other"}}return null}return(r.type==="class_declaration"||r.type==="abstract_class_declaration"||r.type==="enum_declaration")&&r.childForFieldName("name")?.text===e?{kind:"other"}:null}function Gt(t){let e=t.namedChildren.find(n=>n.type==="string")?.text.slice(1,-1)??null,r=t.namedChildren.find(n=>n.type==="import_clause"),o=[];if(!r||e==null)return{src:e,out:o};for(let n of r.namedChildren)if(n.type==="identifier")o.push({kind:"default",local:n.text,imported:null});else if(n.type==="namespace_import"){let i=n.namedChildren.find(s=>s.type==="identifier");i&&o.push({kind:"ns",local:i.text,imported:null})}else if(n.type==="named_imports")for(let i of n.namedChildren){if(i.type!=="import_specifier")continue;let s=i.childForFieldName("name"),a=i.childForFieldName("alias");s&&o.push({kind:"named",local:(a??s).text,imported:s.text})}return{src:e,out:o}}function ze(t){let e=[];for(let r of t.rootNode.namedChildren)if(r.type==="import_statement"){if(r.children.some(s=>s.type==="type"))continue;let o=r.namedChildren.find(s=>s.type==="import_require_clause");if(o){let s=o.namedChildren.find(c=>c.type==="identifier"),a=o.namedChildren.find(c=>c.type==="string");s&&a&&e.push({source:U(a),kind:"cjs",local:s.text,imported:null});continue}let{src:n,out:i}=Gt(r);for(let s of i)e.push({source:n,...s})}else if(r.type==="lexical_declaration"||r.type==="variable_declaration")for(let o of r.namedChildren){if(o.type!=="variable_declarator")continue;let n=S(o.childForFieldName("value")),i=null;n?.type==="member_expression"&&(i=n.childForFieldName("property")?.text??null,n=S(n.childForFieldName("object")));let s=Y(n);if(s==null)continue;let a=o.childForFieldName("name");if(a?.type==="identifier")e.push({source:s,kind:i?"named":"cjs",local:a.text,imported:i});else if(a?.type==="object_pattern"&&!i){for(let c of a.namedChildren)if(c.type==="shorthand_property_identifier_pattern")e.push({source:s,kind:"named",local:c.text,imported:c.text});else if(c.type==="pair_pattern"){let p=c.childForFieldName("key"),h=c.childForFieldName("value");p&&h?.type==="identifier"&&e.push({source:s,kind:"named",local:h.text,imported:p.text})}}}return e}var U=t=>t.namedChildren.filter(e=>e.type==="string_fragment").map(e=>e.text).join("");function Y(t){if(t?.type!=="call_expression")return null;let e=t.childForFieldName("function"),r=t.childForFieldName("arguments");if(e?.type!=="identifier"||e.text!=="require"||!r||r.namedChildCount!==1)return null;let o=r.namedChildren[0];return o.type==="string"?U(o):null}function Je(t){let e=[];return(function r(o){if(o.type==="export_statement"){let n=o.childForFieldName("source");n&&!o.children.some(i=>i.type==="type")&&e.push({source:U(n),startLine:n.startPosition.row+1})}else if(o.type==="import_require_clause"){let n=o.namedChildren.find(i=>i.type==="string");n&&e.push({source:U(n),startLine:n.startPosition.row+1})}else if(o.type==="call_expression"){let n=Y(o);n!=null&&e.push({source:n,startLine:o.startPosition.row+1})}for(let n of o.namedChildren)r(n)})(t.rootNode),e}function Ye(t){let e={default:null,named:{},reexports:[],stars:[],nsReexports:[]};for(let r of t.rootNode.namedChildren){if(r.type!=="export_statement")continue;let o=r.children.some(s=>s.type==="default"),n=r.childForFieldName("declaration"),i=r.childForFieldName("source")!=null;if(o){if(n){let s=n.childForFieldName("name");e.default=s&&(n.type==="function_declaration"||n.type==="generator_function_declaration"||n.type==="class_declaration"||n.type==="abstract_class_declaration")?s.text:"<anon>"}else{let s=S(r.childForFieldName("value"));e.default=s?.type==="identifier"?s.text:"<anon>"}continue}if(n){if(n.type==="function_declaration"||n.type==="generator_function_declaration"||n.type==="class_declaration"||n.type==="abstract_class_declaration"){let s=n.childForFieldName("name")?.text;s&&(e.named[s]=s)}else if(n.type==="lexical_declaration"||n.type==="variable_declaration")for(let s of n.namedChildren){let a=s.type==="variable_declarator"?s.childForFieldName("name"):null;a?.type==="identifier"&&(e.named[a.text]=a.text)}continue}if(i){if(r.children.some(p=>p.type==="type"))continue;let s=U(r.childForFieldName("source")),a=r.namedChildren.find(p=>p.type==="export_clause"),c=r.namedChildren.find(p=>p.type==="namespace_export");if(a)for(let p of a.namedChildren){if(p.type!=="export_specifier")continue;let h=p.childForFieldName("name")?.text,y=p.childForFieldName("alias")?.text;h&&e.reexports.push({exported:y??h,imported:h,source:s})}else if(c){let p=c.namedChildren.find(h=>h.type==="identifier");p&&e.nsReexports.push({exported:p.text,source:s})}else e.stars.push(s);continue}for(let s of r.namedChildren)if(s.type==="export_clause")for(let a of s.namedChildren){if(a.type!=="export_specifier")continue;let c=a.childForFieldName("name")?.text,p=a.childForFieldName("alias")?.text;c&&(p==="default"?e.default=c:e.named[p??c]=c)}}for(let r of t.rootNode.namedChildren){let o=r.type==="expression_statement"?r.namedChildren[0]:null;if(o?.type!=="assignment_expression")continue;let n=o.childForFieldName("left"),i=S(o.childForFieldName("right"));if(n?.type!=="member_expression")continue;let s=n.childForFieldName("object"),a=n.childForFieldName("property")?.text,c=p=>p?.type==="member_expression"&&p.childForFieldName("object")?.text==="module"&&p.childForFieldName("property")?.text==="exports";if(c(n))if(i?.type==="identifier")e.default=i.text;else if(i?.type==="object"){for(let p of i.namedChildren)if(p.type==="shorthand_property_identifier")e.named[p.text]=p.text;else if(p.type==="pair"){let h=p.childForFieldName("key"),y=S(p.childForFieldName("value"));h&&y?.type==="identifier"&&(e.named[h.text.replace(/^['"]|['"]$/g,"")]=y.text)}e.default??="<object>"}else Y(i)!=null?e.stars.push(Y(i)):e.default="<anon>";else a&&(s?.type==="identifier"&&s.text==="exports"||c(s))&&i?.type==="identifier"&&(e.named[a]=i.text)}return e}function Kt(t,e,r){for(let o=t.parent;o;o=o.parent)if(Ht.has(o.type)){let n=[],i=o.childForFieldName("parameters");if(i)for(let a of i.namedChildren)P(a,n);let s=o.childForFieldName("parameter");if(s&&P(s,n),o.type==="function_expression"||o.type==="generator_function"){let a=o.childForFieldName("name");a&&n.push(a.text)}if(n.includes(e))return{kind:"other"}}else if(o.type==="catch_clause"){let n=[];if(P(o.childForFieldName("parameter"),n),n.includes(e))return{kind:"other"}}else if(o.type==="for_in_statement"||o.type==="for_statement"){let n=[],i=o.type==="for_statement"?o.childForFieldName("initializer"):o.childForFieldName("left");if(i)if(i.type==="lexical_declaration"||i.type==="variable_declaration")for(let s of i.namedChildren)P(s.childForFieldName?.("name"),n);else P(i,n);if(n.includes(e))return{kind:"other"}}else if(o.type==="statement_block"||o.type==="program"||o.type==="switch_body"){if(o.type==="program"&&r.has(e))return{kind:"import",imp:r.get(e)};for(let n of o.namedChildren){let i=Dt(n,e);if(i)return i}if(o.type==="program"&&r.has(e))return{kind:"import",imp:r.get(e)}}return null}var zt=t=>(t.childForFieldName("function")?.text??"").replace(/\s+/g," ").slice(0,40);function Ve(t,e){let r=new Map(e.map(i=>[i.local,i])),o=[],n=(i,s,a)=>{let c=S(i);if(!c||c.type!=="identifier"&&c.type!=="shorthand_property_identifier")return;let p=c.text,h=Kt(c,p,r);if(!h||h.kind==="other")return;let y=L(a).nameNode,N={name:p,line:c.startPosition.row+1,column:c.startPosition.column,via:s,callee:zt(a),callLine:y.startPosition.row+1,callColumn:y.startPosition.column,b:h.kind};h.kind==="fn"?N.declLine=h.declLine:(N.src=h.imp.source,N.ik=h.imp.kind,N.imp=h.imp.imported),o.push(N)};return(function i(s){if(s.type==="call_expression"){let a=s.childForFieldName("arguments");for(let c of a?.namedChildren??[]){let p=S(c);if(p)if(p.type==="array")for(let h of p.namedChildren)n(h,"array",s);else if(p.type==="object")for(let h of p.namedChildren)h.type==="pair"?n(h.childForFieldName("value"),"object",s):h.type==="shorthand_property_identifier"&&n(h,"object",s);else n(p,"arg",s)}}for(let a of s.namedChildren)i(a)})(t.rootNode),o}function Ze(t){let e=[],r=n=>{let i=n?.namedChildren.find(s=>s.type!=="comment");return i?.type==="type_identifier"?i.text:null},o=n=>{let i=n.childForFieldName("name")?.text,s=n.childForFieldName("body");if(!(!i||!s)){for(let a of s.namedChildren)if(a.type==="public_field_definition"){let c=a.childForFieldName("name"),p=r(a.childForFieldName("type"));c?.type==="property_identifier"&&p&&e.push({className:i,prop:c.text,type:p})}else if(a.type==="method_definition"&&a.childForFieldName("name")?.text==="constructor")for(let c of a.childForFieldName("parameters")?.namedChildren??[]){if(c.type!=="required_parameter"&&c.type!=="optional_parameter"||!c.namedChildren.some(y=>y.type==="accessibility_modifier")&&!c.children.some(y=>y.type==="readonly"))continue;let p=c.childForFieldName("pattern"),h=r(c.childForFieldName("type"));p?.type==="identifier"&&h&&e.push({className:i,prop:p.text,type:h})}}};for(let n of t.rootNode.namedChildren){let i=n.type==="export_statement"?n.childForFieldName("declaration"):n;i&&(i.type==="class_declaration"||i.type==="abstract_class_declaration")&&o(i)}return e}function Qe(t){let e=[];for(let r of t.rootNode.descendantsOfType(["class_declaration","abstract_class_declaration"])){let o=r.childForFieldName("name")?.text;if(!o)continue;let n=r.namedChildren.find(a=>a.type==="class_heritage"),i=null,s=!1;if(n){let a=n.namedChildren.find(p=>p.type==="extends_clause")??(n.namedChildren[0]?.type==="implements_clause"?null:n),c=a&&(a.type==="extends_clause"?a.childForFieldName("value")??a.namedChildren[0]:a.namedChildren[0]);c&&(c.type==="identifier"?i=c.text:s=!0)}e.push({className:o,superName:i,unknownSuper:s})}return e}var Jt={Get:"get",Post:"post",Put:"put",Patch:"patch",Delete:"delete",All:"all"};function Xe(t){let e=t.namedChildren[0];if(!e)return null;if(e.type==="call_expression"){let r=e.childForFieldName("function"),o=r?.type==="identifier"?r.text:null;return o?{name:o,args:e.childForFieldName("arguments")}:null}return e.type==="identifier"?{name:e.text,args:null}:null}var I=t=>t?t.type==="string"?t.namedChildren.filter(e=>e.type==="string_fragment").map(e=>e.text).join(""):t.type==="template_string"&&!t.namedChildren.some(e=>e.type==="template_substitution")?t.text.slice(1,-1):null:null;function et(t){let e=t?.namedChildren.find(r=>r.type!=="comment");if(!e)return"";if(e.type==="array")return I(e.namedChildren.find(r=>r.type!=="comment"))??"";if(e.type==="object"){for(let r of e.namedChildren){if(r.type!=="pair")continue;let o=r.childForFieldName("key");if(o?.text==="path"||o?.text==="'path'"||o?.text==='"path"'){let n=r.childForFieldName("value");return(n?.type==="array"?I(n.namedChildren[0]):I(n))??""}}return""}return I(e)??""}var V=(...t)=>"/"+t.map(r=>String(r??"").replace(/^\/+|\/+$/g,"")).filter(Boolean).join("/");function tt(t){let e=[],r=(o,n)=>{let i=o.childForFieldName("name")?.text,s=o.childForFieldName("body");if(!i||!s)return;let c=[...n,...o.namedChildren.filter(y=>y.type==="decorator")].map(Xe).filter(Boolean).find(y=>y.name==="Controller");if(!c)return;let p=et(c.args),h=[];for(let y of s.namedChildren)if(y.type!=="comment"){if(y.type==="decorator"){h.push(y);continue}if(y.type==="method_definition"){let N=y.childForFieldName("name")?.text;for(let d of h.map(Xe).filter(Boolean)){let u=Jt[d.name];!u||!N||e.push({method:u,path:V(p,et(d.args)),startLine:y.startPosition.row+1,handlerName:null,isInlineHandler:!1,handlerStartLine:null,handlerEndLine:null,handlerStartIndex:null,handlerEndIndex:null,nest:{className:i,methodName:N}})}}h=[]}};for(let o of t.rootNode.namedChildren)if(o.type==="export_statement"){let n=o.childForFieldName("declaration");n&&(n.type==="class_declaration"||n.type==="abstract_class_declaration")&&r(n,o.namedChildren.filter(i=>i.type==="decorator"))}else(o.type==="class_declaration"||o.type==="abstract_class_declaration")&&r(o,[]);return e}function nt(t){let e=[];return(function r(o){if(o.type==="call_expression"){let n=o.childForFieldName("function");if(n?.type==="member_expression"&&n.childForFieldName("property")?.text==="setGlobalPrefix"){let i=(o.childForFieldName("arguments")?.namedChildren??[]).filter(p=>p.type!=="comment"),s=i[0]?I(i[0]):null,a=[],c=i[1];if(c?.type==="object")for(let p of c.namedChildren){if(p.type!=="pair"||p.childForFieldName("key")?.text.replace(/^['"]|['"]$/g,"")!=="exclude")continue;let h=p.childForFieldName("value");for(let y of h?.type==="array"?h.namedChildren:[])if(y.type==="object"){for(let N of y.namedChildren)if(N.type==="pair"&&N.childForFieldName("key")?.text.replace(/^['"]|['"]$/g,"")==="path"){let d=I(N.childForFieldName("value"));d!=null&&a.push(d)}}else{let N=I(y);N!=null&&a.push(N)}}e.push({prefix:s||null,exclude:a})}}for(let n of o.namedChildren)r(n)})(t.rootNode),e}function Dn(t){let e=[];for(let i of t.values())for(let s of i?.globalPrefix??[])e.push(s);if(!e.length||e.some(i=>!i.prefix||i.prefix!==e[0].prefix))return t;let r=e[0].prefix,o=new Set(e.flatMap(i=>i.exclude).map(i=>V(i))),n=new Map;for(let[i,s]of t){if(!s?.routes?.some(a=>a.nest)){n.set(i,s);continue}n.set(i,{...s,routes:s.routes.map(a=>a.nest&&!o.has(V(a.path))?{...a,path:V(r,a.path)}:a)})}return n}import Yt from"node:crypto";var rt=t=>Yt.createHash("sha256").update(t).digest("hex");function Vt(t,e=null){let r=[],o=n=>{if(n.childCount===0){if(n.type==="comment")return;let i=e?e.slice(n.startIndex,n.endIndex):n.text,s=n.type==="jsx_text"?i.replace(/\s+/g," ").trim():i;if(s==="")return;r.push({s:n.startIndex,e:n.endIndex,t:s});return}for(let i=0;i<n.childCount;i++)o(n.child(i))};return o(t),r}function ot(t,e){let r=[];for(let i of[e.functions,e.arrowFunctions,e.classMethods])for(let s of i??[])s.startIndex!=null&&r.push({s:s.startIndex,e:s.endIndex,item:s,field:"bodyHash"});for(let i of[e.routes,e.listeners,e.socketListeners])for(let s of i??[])s.isInlineHandler&&s.handlerStartIndex!=null&&r.push({s:s.handlerStartIndex,e:s.handlerEndIndex,item:s,field:"handlerBodyHash"});let o=new Map(r.map(i=>[i,[]])),n=[];for(let i of Vt(t.rootNode,t.canopyOriginalSource??null)){let s=null,a=1/0;for(let c of r)i.s>=c.s&&i.e<=c.e&&c.e-c.s<a&&(s=c,a=c.e-c.s);(s?o.get(s):n).push(i.t)}for(let i of r)i.item[i.field]=rt(o.get(i).join(" "));e.moduleBodyHash=rt(n.join(" "))}function it(t,e){return k(e,"matchFastifyRoutes.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(p=>p.name==="http_method"),s=n.captures.find(p=>p.name==="route_path"),a=n.captures.find(p=>p.name==="handler_ref"),c=n.captures.find(p=>p.name==="handler_inline");return{method:i.node.text,path:s.node.text,startLine:i.node.startPosition.row+1,handlerName:a?a.node.text:null,isInlineHandler:!!c,handlerStartLine:c?c.node.startPosition.row+1:null,handlerEndLine:c?c.node.endPosition.row+1:null,handlerStartIndex:c?c.node.startIndex:null,handlerEndIndex:c?c.node.endIndex:null}})}function st(t,e){return k(e,"matchExtensionListeners.scm").matches(t.rootNode).map(n=>{let i=n.captures.find(u=>u.name==="event_type"),s=n.captures.find(u=>u.name==="event_name"),a=n.captures.find(u=>u.name==="method_name"||u.name==="listener_method"),c=n.captures.find(u=>u.name==="handler_ref"),p=n.captures.find(u=>u.name==="target"),h=n.captures.find(u=>u.name==="root_object"),y=n.captures.find(u=>u.name==="sub_object"),N=p?p.node.text:h?h.node.text+(y?"."+y.node.text:""):null,d=n.captures.find(u=>u.name==="handler_inline");return{eventName:i?.node.text??s?.node.text??"unknown",targetName:N,startLine:a.node.startPosition.row+1,handlerName:c?c.node.text:null,isInlineHandler:!!d,handlerStartLine:d?d.node.startPosition.row+1:null,handlerEndLine:d?d.node.endPosition.row+1:null,handlerStartIndex:d?d.node.startIndex:null,handlerEndIndex:d?d.node.endIndex:null}})}import rn from"node:fs";import at from"node:os";import ct from"node:path";import Zt from"node:v8";import{Worker as Qt}from"node:worker_threads";import{fileURLToPath as Xt}from"node:url";var lt=500,dt=8,en=24,tn=150;function pt({files:t,env:e=process.env,cpus:r=at.cpus().length,totalMb:o=Math.round(at.totalmem()/1048576),heapLimitMb:n=Math.round(Zt.getHeapStatistics().heap_size_limit/1048576)}={}){if(e.CANOPY_WORKERS!==void 0&&e.CANOPY_WORKERS!==""){let a=Number(e.CANOPY_WORKERS);return!Number.isInteger(a)||a<0?{workers:0,why:"CANOPY_WORKERS is not a whole number: one file at a time"}:a===0?{workers:0,why:"CANOPY_WORKERS=0: one file at a time"}:{workers:Math.min(a,dt),why:`CANOPY_WORKERS=${a}`}}if(t<tn)return{workers:0,why:"a small project: one file at a time"};let i=Math.min(Math.floor(n*.6/lt),Math.floor(o*.5/lt)),s=Math.min(r-1,dt,i);return s>=2?{workers:s,why:`${s} workers (${r} cpus, ${o} MB)`}:{workers:0,why:`not enough spare cpus or memory for threads (${r} cpus, ${o} MB)`}}function nn(){return globalThis.__canopyBundle?globalThis.__canopyBundle:ct.join(ct.dirname(Xt(globalThis.__canopyUrl("src/stage2_parse/parallelParse.js"))),"..","cli.js")}function ut(t,{workers:e,noCache:r,onBatch:o=()=>{},entry:n=nn()}){return new Promise(i=>{let s=new Array(t.length),a=[...new Set(t.map(q))],c=0,p=0,h=!1,y=null,N=e,d=[],u=[],l=()=>{if(!h){h=!0;for(let x of u)try{x.terminate()}catch{}i({outcomes:s,failed:y})}},m=x=>{if(h||y)return;if(c>=t.length){p>=t.length&&l();return}let _=[];for(;_.length<en&&c<t.length;)_.push({i:c,filePath:t[c]}),c++;x.busy=_,x.postMessage({type:"batch",items:_})},f=x=>{y||(y=x),l()};for(let x=0;x<e;x++){let _;try{_=new Qt(n,{workerData:{canopyParseWorker:!0,grammarKeys:a,noCache:r},resourceLimits:{maxOldGenerationSizeMb:1024,stackSizeMb:8},execArgv:[],env:process.env})}catch(F){f(`could not start a worker thread (${F?.message??F})`);return}_.busy=null,u.push(_),_.on("message",F=>{if(F.type==="ready"){m(_);return}if(F.type==="done"){for(let g of F.results)s[g.i]=g.outcome;p+=F.results.length,_.busy=null,o(p),p>=t.length?l():m(_);return}F.type==="fatal"&&f(F.message)}),_.on("error",F=>f(`a worker stopped (${String(F?.message??F).split(`
7
+ `)[0].slice(0,160)})`)),_.on("exit",F=>{N--,d.push(F),!h&&(_.busy?f(`a worker ended early (exit code ${F})`):N<=0&&f("every worker ended before the files were done (exit codes "+d.join(", ")+")"))})}})}var ht=Number(process.env.CANOPY_PARSE_BUDGET_MS)||3e4,ce=class extends Error{constructor(e){super(`parse time budget exceeded (limit ${ht/1e3} s; stopped after ${(e/1e3).toFixed(1)} s)`),this.name="ParseBudgetError"}},ft=100;function ae(t,e,r){return t.map(o=>({...o,returns:$e(e,r,o)}))}var mt=()=>({functions:[],arrowFunctions:[],classMethods:[],imports:[],calls:[],methodCalls:[],returnEntries:[],jsxElements:[],callSites:[],apiCalls:[],socketEmits:[],socketListeners:[],rawSocketSends:[],rawSocketTypeChecks:[],routes:[],listeners:[]});function on(t,e){let{store:r,readCache:o,parser:n,language:i,grammars:s}=e;{let a=null;try{let c=rn.readFileSync(t),p=q(t),h=r?.keyFor(c,p),y=r&&o?r.get(h):null;if(y)return{kind:"hit",plain:y};let N=performance.now(),d=()=>{let j=performance.now()-N;if(j>ht)throw new ce(j)},u=p==="javascript"?{parser:n,language:i}:s?.get(p);if(!u)throw new Error(`no ${p} grammar loaded`);let l=u.language;ke(p),a=Fe(u.parser,t).tree,d();let m=[],f=(j,yt,xt=[])=>{try{return yt()}catch(Z){if(Z.name==="ParseBudgetError")throw Z;return m.push({extractor:j,message:String(Z.message).slice(0,160)}),xt}},x=f("functionDeclarations",()=>ae(je(a,l),a,l));d();let _=f("arrowFunctions",()=>ae(Pe(a,l),a,l));d();let F=f("classMethods",()=>ae(Te(a,l),a,l));d();let g=f("importBindings",()=>ze(a)),b={functions:x,arrowFunctions:_,classMethods:F,imports:f("imports",()=>[...Le(a,l),...Je(a)]),calls:(d(),f("calls",()=>Ie(a,l))),methodCalls:(d(),f("methodCalls",()=>Re(a,l))),returnEntries:f("returnEntries",()=>Ae(a,l)),jsxElements:f("jsxElements",()=>Me(a,l)),callSites:(d(),f("callSites",()=>De(a))),apiCalls:(d(),f("apiCalls",()=>qe(a,l))),socketEmits:f("socketEmits",()=>Be(a,l)),socketListeners:f("socketListeners",()=>Ue(a,l)),rawSocketSends:f("rawSocketSends",()=>We(a,l)),rawSocketTypeChecks:f("rawSocketTypeChecks",()=>He(a,l)),routes:f("routes",()=>[...it(a,l),...T(l)?tt(a):[]]),listeners:f("listeners",()=>st(a,l)),refArgs:f("refArgs",()=>Ve(a,g)),globalPrefix:f("globalPrefix",()=>nt(a)),importBindings:g,classProps:f("classProps",()=>T(l)?Ze(a):[]),classHeritage:f("classHeritage",()=>Qe(a)),httpClients:f("httpClients",()=>Ke(a),null),exportBindings:f("exportBindings",()=>Ye(a),null),hadErrors:ne(a),exportedNames:f("exports",()=>Oe(a,l)),failed:!1,warnings:m};f("bodyHash",()=>ot(a,b),null);let v=he(b);if(r)try{r.set(h,v)}catch{}return{kind:"parsed",plain:v}}catch(c){return c.name==="ParseBudgetError"?{kind:"skipped",msg:c.message,plain:{...mt(),hadErrors:!1,failed:!1,skipped:c.message}}:{kind:"failed",msg:c.message,plain:{...mt(),hadErrors:!0,failed:!0,errorMessage:c.message}}}finally{if(a){try{a.delete()}catch{}a=null}}}}async function gr(t,e,r,{cache:o=!0,onProgress:n=console.log,grammars:i=null,noCache:s=!1,say:a=null}={}){let c=new Map,p=0,h=0,y=0,N=o?me():null,d=!s,u=[],l={store:N,readCache:d,parser:e,language:r,grammars:i},m=new Array(t.length),f=o?pt({files:t.length}):{workers:0,why:"no cache"};if(f.workers>=2){let x=a??n;x(` parsing on ${f.workers} threads`);let _=await ut(t,{workers:f.workers,noCache:s,onBatch:F=>{(F%ft<24||F===t.length)&&n(` parsing: ${F}/${t.length} files`)}});for(let F=0;F<_.outcomes.length;F++)m[F]=_.outcomes[F];_.failed&&x(` parsing on threads did not work (${_.failed}); the rest is parsed one file at a time`)}for(let x=0;x<t.length;x++)m[x]||(x>0&&x%ft===0&&n(` parsing: ${x}/${t.length} files (${y} from cache)`),m[x]=on(t[x],l));for(let x=0;x<t.length;x++){let _=m[x],F=t[x];_.kind==="hit"?y++:h++,_.kind==="skipped"?(u.push({filePath:F,reason:_.msg}),console.warn(` ! Skipped ${F}: ${_.msg}`)):_.kind==="failed"&&(p++,console.warn(` ! Failed to parse ${F}: ${_.msg}`)),c.set(F,_.plain),m[x]=null}return console.log(`Parsed ${c.size} files (${p} failed)`),console.log(` parse cache: ${h} file(s) parsed, ${y} served from cache`),c.cacheStats={parsed:h,cacheHits:y},c.budgetSkips=u,c}export{pn as a,un as b,me as c,k as d,Dn as e,on as f,gr as g};
@@ -0,0 +1,5 @@
1
+ import{fileURLToPath as __cf,pathToFileURL as __cu}from"node:url";import{dirname as __cd,join as __cj}from"node:path";import{createRequire as __cr}from"node:module";
2
+ const require=__cr(import.meta.url);
3
+ globalThis.__canopyBundle??=__cj(__cd(__cf(import.meta.url)),"canopy.js");
4
+ globalThis.__canopyUrl??=(r)=>__cu(__cj(__cd(__cd(globalThis.__canopyBundle)),r)).href;
5
+ import{a as s}from"./chunk-AGOP4Q7U.js";import"./chunk-SVREO35Y.js";import"./chunk-ZQ6F3H6X.js";async function d(o,r={}){let e=await s({repoRoot:o,searchService:{prepared:()=>null},...r}).status(),t=e.version?` (v${e.version})`:"";return e.state==="ready"&&e.source?`Ask AI: Using Claude Code from ${e.source.label}${t}`:e.state==="needs_approval"?`Ask AI: Found Claude Code from ${e.offer?.label??"an editor"}${t}. Open the Ask AI panel in the viewer and press Allow to use it.`:e.state==="unsupported"?`Ask AI: this Claude Code is too old${t}; update it.`:"Ask AI: Claude Code was not found. Install it (https://docs.claude.com/en/docs/claude-code/setup) or set CANOPY_CLAUDE_BIN to its full path."}export{d as describeClaude};
@@ -0,0 +1,5 @@
1
+ import{fileURLToPath as __cf,pathToFileURL as __cu}from"node:url";import{dirname as __cd,join as __cj}from"node:path";import{createRequire as __cr}from"node:module";
2
+ const require=__cr(import.meta.url);
3
+ globalThis.__canopyBundle??=__cj(__cd(__cf(import.meta.url)),"canopy.js");
4
+ globalThis.__canopyUrl??=(r)=>__cu(__cj(__cd(__cd(globalThis.__canopyBundle)),r)).href;
5
+ import s from"node:fs";import o from"node:path";import{fileURLToPath as d}from"node:url";var r=o.join(o.dirname(d(globalThis.__canopyUrl("src/utils/pkgPath.js"))),"..","..");function f(i){let n=o.join(r,"vendor",i);if(l()&&s.existsSync(n))return n;for(let e=r;;e=o.dirname(e)){let t=o.join(e,"node_modules",i);if(s.existsSync(t))return t;if(o.dirname(e)===e)break}return o.join(r,"node_modules",i)}var l=()=>r.split(o.sep).includes("node_modules");export{r as a,f as b,l as c};
@@ -0,0 +1,5 @@
1
+ import{fileURLToPath as __cf,pathToFileURL as __cu}from"node:url";import{dirname as __cd,join as __cj}from"node:path";import{createRequire as __cr}from"node:module";
2
+ const require=__cr(import.meta.url);
3
+ globalThis.__canopyBundle??=__cj(__cd(__cf(import.meta.url)),"canopy.js");
4
+ globalThis.__canopyUrl??=(r)=>__cu(__cj(__cd(__cd(globalThis.__canopyBundle)),r)).href;
5
+ var g=Object.create;var f=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var m=(b,a)=>()=>{try{return a||b((a={exports:{}}).exports,a),a.exports}catch(c){throw a=0,c}};var l=(b,a,c,e)=>{if(a&&typeof a=="object"||typeof a=="function")for(let d of i(a))!k.call(b,d)&&d!==c&&f(b,d,{get:()=>a[d],enumerable:!(e=h(a,d))||e.enumerable});return b};var n=(b,a,c)=>(c=b!=null?g(j(b)):{},l(a||!b||!b.__esModule?f(c,"default",{value:b,enumerable:!0}):c,b));export{m as a,n as b};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canopy-engine",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "Canopy: a map of what a JavaScript / TypeScript codebase does - entry points, call flows and services, drawn from the code itself.",
6
6
  "keywords": [
@@ -34,6 +34,7 @@
34
34
  "!ui-react/dist/*.map",
35
35
  "README.md",
36
36
  "FEEDBACK.md",
37
+ "PRIVACY.md",
37
38
  "LICENSE"
38
39
  ],
39
40
  "engines": {
@@ -44,7 +45,7 @@
44
45
  },
45
46
  "scripts": {
46
47
  "start": "node src/cli.js",
47
- "test": "node test/regression.js && node test/ids.js && node test/bodyhash.js",
48
+ "test": "node test/regression.js && node test/ids.js && node test/bodyhash.js && node test/this-calls.js && node test/telemetry.js",
48
49
  "build:ui": "node scripts/build-ui.js",
49
50
  "snapshot": "node test/snapshot.js",
50
51
  "audit:completeness": "node tools/auditCompleteness.js",
@@ -59,6 +60,7 @@
59
60
  "test:snapshots": "node test/snapshots.js",
60
61
  "test:readme": "node test/readme.js",
61
62
  "test:services": "node test/services.js",
63
+ "test:this-calls": "node test/this-calls.js",
62
64
  "test:dogfood": "node test/dogfood.js",
63
65
  "prepack": "node scripts/build-ui.js && node scripts/vendor-grammars.js && node scripts/build.js",
64
66
  "test:installed-ui": "node test/installed-ui.js",
@@ -82,7 +84,14 @@
82
84
  "test:diff": "node test/diff-endpoint.js",
83
85
  "test:respawn": "node test/respawn.js",
84
86
  "test:term": "node test/term.js",
85
- "test:state": "node test/state.js"
87
+ "test:state": "node test/state.js",
88
+ "test:ai": "node test/ai.js",
89
+ "test:queues-base": "node test/queue-base-links.js",
90
+ "test:ai-prompt": "node test/ai-prompt.js",
91
+ "test:ai-answer": "node test/ai-answer.js",
92
+ "test:ai-detect": "node test/ai-detect.js",
93
+ "test:telemetry": "node test/telemetry.js",
94
+ "ai:eval-notes": "node scripts/ai-eval-notes.js"
86
95
  },
87
96
  "dependencies": {
88
97
  "web-tree-sitter": "0.26.12"
@@ -1,2 +1,2 @@
1
- @font-face{font-family:Inter;font-style:normal;font-weight:400 700;font-display:swap;src:url("./fonts/Inter-latin-var.woff2") format("woff2")}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:400 700;font-display:swap;src:url("./fonts/JetBrainsMono-latin-var.woff2") format("woff2")}:root{--paper: #f3efe3;--paper-raised: #faf7ee;--paper-hover: #eee7d4;--contour: #d9d0ba;--ink: #2b2a22;--ink-dim: #6b664f;--ink-faint: #a39a7d;--shadow-sm: 0 1px 2px rgba(40, 35, 20, .06), 0 2px 6px rgba(40, 35, 20, .05);--shadow-md: 0 2px 4px rgba(40, 35, 20, .07), 0 8px 20px rgba(40, 35, 20, .09);--shadow-lg: 0 4px 10px rgba(40, 35, 20, .1), 0 18px 38px rgba(40, 35, 20, .14);--accent-client: #4f7a5c;--accent-client-bg: #e4ecdf;--accent-server: #9a5b32;--accent-server-bg: #f1e2cf;--accent-unknown: #6b5a8f;--accent-unknown-bg: #e7e1ef;--accent-cross: #4a6480;--accent-cross-bg: #dfe6ec;--font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono: "JetBrains Mono", "SF Mono", Consolas, monospace}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:var(--font-ui);color:var(--ink);background:radial-gradient(ellipse 100% 70% at 50% -10%,#f8f4e8,#efe8d5 55%,#e4dabf);overflow:hidden}.canopy-app{height:100vh;display:flex;flex-direction:column}.canopy-topbar{display:flex;align-items:center;justify-content:space-between;padding:14px 22px;border-bottom:1px solid var(--contour);background:var(--paper-raised);box-shadow:0 1px #28231408,0 2px 10px #2823140d;z-index:10;position:relative}.canopy-brand{font-family:var(--font-ui);font-weight:700;font-size:16px;letter-spacing:-.01em;color:var(--ink);display:flex;align-items:center;gap:7px}.canopy-brand .leaf{color:var(--accent-client);font-size:15px}.canopy-crumbs{display:flex;align-items:center;gap:5px;font-size:13px}.crumb{padding:4px 9px;border-radius:6px;cursor:pointer;color:var(--ink-dim);font-weight:500;transition:background .15s ease,color .15s ease}.crumb:hover{background:var(--paper-hover);color:var(--ink)}.crumb.current{color:var(--ink);font-weight:700;cursor:default;font-size:14px}.crumb.current:hover{background:none}.crumb-sep{color:var(--ink-faint);font-size:11px}.canopy-hint{font-size:11.5px;color:var(--ink-faint);margin-top:1px;font-weight:500}.picker-entry-btn{font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--accent-cross);background:var(--accent-cross-bg);border:1px solid var(--accent-cross);border-radius:7px;padding:6px 11px;cursor:pointer;transition:background .15s ease}.picker-entry-btn:hover{background:var(--accent-cross);color:var(--paper-raised)}.canopy-stats{display:flex;gap:18px}.stat{text-align:right}.stat-num{font-size:17px;font-weight:700;line-height:1;color:var(--ink);font-variant-numeric:tabular-nums}.stat-label{font-size:9.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);font-weight:600;margin-top:2px}.canopy-canvas{flex:1;position:relative}.react-flow__attribution{display:none}.react-flow__controls{box-shadow:var(--shadow-md);border-radius:10px;overflow:hidden;border:1px solid var(--contour)}.react-flow__controls-button{background:var(--paper-raised);border-bottom:1px solid var(--contour);color:var(--ink-dim);width:30px;height:30px}.react-flow__controls-button:hover{background:var(--paper-hover);color:var(--ink)}.react-flow__controls-button svg{fill:currentColor}.react-flow__minimap{border-radius:10px;box-shadow:var(--shadow-md);border:1px solid var(--contour);overflow:hidden}.group-bg{width:100%;height:100%;border-radius:20px;background:#96855414;border:1px dashed rgba(110,95,55,.18);pointer-events:none}.v-scrollbar-track{position:absolute;top:10px;bottom:30px;right:4px;width:10px;border-radius:6px;background:#b4a88a29;z-index:5;cursor:pointer}.v-scrollbar-thumb{position:absolute;left:0;width:100%;border-radius:6px;background:var(--ink-faint);opacity:.55;cursor:grab;transition:opacity .15s ease,background .15s ease}.v-scrollbar-thumb:hover{opacity:.85;background:var(--ink-dim)}.v-scrollbar-thumb:active{cursor:grabbing;opacity:1}.react-flow__edge-path{stroke-linecap:round}.react-flow__edge.same-service .react-flow__edge-path{stroke:var(--ink-dim);stroke-width:2px}.react-flow__edge.cross-service .react-flow__edge-path{stroke:var(--accent-cross);stroke-width:2.2px;stroke-dasharray:7 5}.react-flow__edge.service-link .react-flow__edge-path{stroke:var(--accent-cross);stroke-width:2.4px;stroke-dasharray:8 6}.card{background:var(--paper-raised);border-radius:12px;box-shadow:var(--shadow-sm);transition:box-shadow .18s ease,transform .18s ease;border:1px solid rgba(40,35,20,.06)}.card:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}.svc-node{width:240px;padding:18px 20px;cursor:pointer}.svc-kind{display:inline-block;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;padding:3px 9px;border-radius:5px}.svc-name{font-size:19px;font-weight:700;margin-top:10px;letter-spacing:-.01em;color:var(--ink)}.svc-meta{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-dim);margin-top:6px}.svc-badges{display:flex;gap:6px;margin-top:11px;flex-wrap:wrap}.svc-badge{font-family:var(--font-mono);font-size:9px;font-weight:600;background:var(--accent-cross-bg);color:var(--accent-cross);border-radius:4px;padding:2px 7px}.wf-node{width:100%;height:100%;padding:14px 18px;cursor:pointer;display:flex;align-items:center;gap:14px}.wf-dot{width:9px;height:9px;border-radius:50%;flex-shrink:0}.wf-body{flex:1;min-width:0}.wf-name{font-size:14.5px;font-weight:650;color:var(--ink);line-height:1.3;overflow-wrap:break-word;word-break:normal}.wf-meta{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-faint);margin-top:4px}.wf-chevron{color:var(--ink-faint);font-size:18px;flex-shrink:0}.minor-panel{position:absolute;left:50%;bottom:22px;transform:translate(-50%);z-index:15;width:460px;max-width:calc(100% - 44px);background:var(--paper-raised);border:1px solid var(--contour);border-radius:12px;box-shadow:var(--shadow-md);overflow:hidden}.minor-toggle{width:100%;border:none;background:none;cursor:pointer;padding:11px 18px;display:flex;align-items:center;justify-content:space-between;font-family:var(--font-mono);font-size:11.5px;font-weight:600;color:var(--ink-dim)}.minor-toggle:hover{color:var(--ink);background:var(--paper-hover)}.minor-caret{color:var(--ink-faint);font-size:10px}.minor-list{max-height:260px;overflow-y:auto;border-top:1px solid var(--contour)}.minor-item{padding:8px 18px;font-size:12px;color:var(--ink-dim);cursor:pointer;border-bottom:1px solid rgba(40,35,20,.05);display:flex;justify-content:space-between;gap:10px}.minor-item:last-child{border-bottom:none}.minor-item:hover{background:var(--paper-hover);color:var(--ink)}.minor-item .minor-file{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:1}.context-rail{position:absolute;top:14px;left:10px;z-index:8;width:190px;max-height:calc(100% - 28px);overflow-y:auto;display:flex;flex-direction:column;gap:12px}.context-group{background:var(--paper-raised);border:1px solid var(--contour);border-radius:10px;padding:9px 10px;box-shadow:var(--shadow-sm)}.context-label{font-family:var(--font-mono);font-size:8.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-faint);margin-bottom:6px}.context-chip{font-size:11px;font-weight:600;color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:6px;padding:5px 8px;margin-top:4px;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:background .15s ease}.context-chip:first-child{margin-top:0}.context-chip:hover{background:var(--paper-hover)}.context-more{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);margin-top:4px}.step-node{width:100%;height:100%;min-height:90px;padding:18px;box-sizing:border-box;overflow:visible;position:relative}.step-node.root{border-color:var(--accent-client);box-shadow:0 0 0 1.5px var(--accent-client),var(--shadow-sm)}.step-node.active-expanded:not(.root){border-color:var(--accent-cross);box-shadow:0 0 0 1.5px var(--accent-cross),var(--shadow-sm)}.step-top{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.step-badge{font-family:var(--font-mono);font-size:9px;font-weight:700;background:var(--ink);color:var(--paper);border-radius:4px;padding:1.5px 6px;flex-shrink:0}.step-cat{font-family:var(--font-mono);font-size:8px;font-weight:700;border-radius:4px;padding:1.5px 6px;flex-shrink:0;text-transform:uppercase;letter-spacing:.03em}.step-name{font-size:13px;font-weight:650;color:var(--ink);line-height:1.3;flex:1;min-width:60px;overflow-wrap:break-word;word-break:normal}.step-file{font-family:var(--font-mono);font-size:8.5px;color:var(--ink-faint);margin-top:5px;overflow-wrap:break-word;word-break:normal}.step-expand{margin-top:8px;font-family:var(--font-mono);font-size:10px;font-weight:600;border:1px solid var(--contour);background:var(--paper);color:var(--ink-dim);border-radius:6px;padding:3px 9px;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}.step-expand:hover{background:var(--paper-hover);color:var(--ink)}.step-expand.open{background:var(--ink);color:var(--paper);border-color:var(--ink)}.react-flow__edge.secondary-link .react-flow__edge-path{stroke:var(--ink-faint);stroke-dasharray:3 4;stroke-width:1.3}.source-icon{border:none;background:var(--paper);color:var(--ink-faint);font-family:var(--font-mono);font-size:9.5px;font-weight:700;border-radius:4px;padding:1px 5px;cursor:pointer;flex-shrink:0;margin-left:auto}.source-icon:hover{background:var(--ink);color:var(--paper)}.detail-source{font-family:var(--font-mono);font-size:11px;line-height:1.55;color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:8px;padding:10px 12px;margin-top:6px;white-space:pre-wrap;word-break:break-word;overflow-wrap:break-word;word-break:normal}.source-unavailable{color:var(--accent-server);font-style:italic}.recursive-badge{margin-top:7px;font-family:var(--font-mono);font-size:9.5px;font-weight:700;color:var(--accent-server);background:var(--accent-server-bg);border-radius:5px;padding:3px 7px;display:inline-block}.runtime-bucket{width:100%;padding:16px;border:1px dashed var(--contour);background:var(--paper);box-sizing:border-box;max-height:270px;overflow-y:auto}.runtime-bucket-header{font-family:var(--font-mono);font-size:8.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);padding:2px 8px 6px}.runtime-bucket-item{display:flex;flex-direction:column;align-items:flex-start;gap:3px;padding:6px 8px;font-size:10.5px;color:var(--ink-dim)}.runtime-bucket-item.inline{padding:5px 0}.runtime-bucket-item-row{display:flex;align-items:center;gap:6px;width:100%}.runtime-bucket-label{flex:1;min-width:60px;overflow-wrap:break-word;word-break:normal;font-family:var(--font-mono)}.runtime-tag{font-family:var(--font-mono);font-size:8px;font-weight:700;border-radius:4px;padding:1.5px 5px;flex-shrink:0;text-transform:uppercase;letter-spacing:.02em}.runtime-tag-unresolvable-call-site{background:var(--accent-unknown-bg);color:var(--accent-unknown)}.runtime-tag-runtime-dispatched{background:var(--accent-cross-bg);color:var(--accent-cross)}.runtime-tag-return-value{background:var(--accent-client-bg);color:var(--accent-client)}.runtime-bucket.nested-bucket{margin:0}.inline-call{width:100%;height:100%;padding:16px;display:flex;flex-direction:column;align-items:flex-start;gap:3px;border:1px solid var(--contour);background:var(--paper);box-sizing:border-box;font-size:10.5px;color:var(--ink-dim)}.branch-diagram{width:100%;padding:4px 10px;border:1.5px solid var(--accent-cross);box-sizing:border-box}.branch-diagram.nested{border-width:1px;box-shadow:none;padding:3px 8px}.branch-condition{display:flex;align-items:center;gap:7px;padding-bottom:2px;border-bottom:1px dashed var(--contour)}.branch-condition-kind{font-family:var(--font-mono);font-size:8px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;background:var(--accent-cross-bg);color:var(--accent-cross);border-radius:4px;padding:2px 6px;flex-shrink:0}.branch-condition-text{font-family:var(--font-mono);font-size:11.5px;font-weight:650;color:var(--ink);overflow-wrap:break-word;word-break:normal;flex:1;min-width:80px}.branch-outcomes{display:flex;flex-direction:row;align-items:flex-start;gap:18px;margin-top:4px;overflow-x:visible}.branch-outcome{display:flex;flex-direction:row;align-items:flex-start;gap:8px;min-width:150px;flex-shrink:0}.branch-arrow-row{display:flex;align-items:center;gap:5px;height:12px}.branch-arrow-svg{height:10px}.branch-arrow-label{font-family:var(--font-mono);font-size:9px;font-weight:700;color:var(--accent-cross);text-transform:uppercase;flex-shrink:0}.branch-chain-lane{display:flex;flex-direction:column;gap:3px;min-width:0}.branch-empty{font-family:var(--font-mono);font-size:10px;color:var(--ink-faint);font-style:italic;padding:4px 0}.step-body-contained{margin-top:14px;padding-top:12px;border-top:1px dashed var(--contour)}.chain-row{position:relative;min-width:0}.branch-step-chip{display:flex;align-items:center;gap:6px;background:var(--paper);border:1px solid var(--contour);border-radius:6px;padding:6px 9px;cursor:pointer;transition:background .15s ease,border-color .15s ease}.branch-step-chip:hover{background:var(--paper-hover)}.branch-step-chip.promoted{border-color:var(--accent-cross);border-style:dashed;background:var(--accent-cross-bg)}.branch-step-name{font-size:11.5px;font-weight:600;color:var(--ink);flex:1;min-width:40px;overflow-wrap:break-word;word-break:normal}.promoted-hint{font-family:var(--font-mono);font-size:8.5px;font-weight:600;color:var(--accent-cross);flex-shrink:0}.empty-state{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;color:var(--ink-faint);font-family:var(--font-mono);font-size:12.5px;max-width:320px;line-height:1.6;pointer-events:none}.empty-state .glyph{font-size:26px;margin-bottom:10px;opacity:.55}.detail-panel{position:absolute;top:0;right:0;height:100%;width:320px;background:var(--paper-raised);border-left:1px solid var(--contour);box-shadow:-8px 0 26px #2823141f;padding:24px 22px;overflow-y:auto;transform:translate(0);animation:panelIn .22s cubic-bezier(.22,.9,.3,1) both;z-index:20}@keyframes panelIn{0%{transform:translate(16px);opacity:0}to{transform:translate(0);opacity:1}}.detail-close{position:absolute;top:16px;right:16px;border:none;background:none;cursor:pointer;font-size:15px;color:var(--ink-faint)}.detail-close:hover{color:var(--ink)}.detail-label{font-size:9.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--ink-faint);font-weight:700;margin-top:18px;margin-bottom:5px}.detail-label:first-of-type{margin-top:8px}.detail-name{font-size:18px;font-weight:700;margin-top:20px;color:var(--ink)}.detail-mono{font-family:var(--font-mono);font-size:11.5px;color:var(--ink-dim);word-break:break-all;line-height:1.5}.detail-note{font-size:12.5px;color:var(--ink-dim);line-height:1.5}@keyframes nodeFadeIn{0%{opacity:0}to{opacity:1}}.react-flow__node{animation:nodeFadeIn .18s ease both}@keyframes cardIn{0%{transform:scale(.92) translateY(6px)}to{transform:scale(1) translateY(0)}}.react-flow__node .card{animation:cardIn .22s cubic-bezier(.22,.9,.3,1) both}.inferred-badge{display:inline-block;margin-left:8px;padding:1px 6px;border-radius:8px;font-size:9px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;background:#ece6d2;color:#7a7355;border:1px dashed #b8ae8a;vertical-align:middle}.runtime-bucket-item.compact{flex-direction:row;align-items:center;gap:8px;height:24px;padding:0 2px;box-sizing:border-box;cursor:pointer}.runtime-bucket-item.compact:hover{background:#00000009}.compact-dot{width:7px;height:7px;padding:0;border-radius:50%;flex:0 0 7px}.compact-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;max-width:420px;font-size:10.5px}.branch-arrow-label{line-height:24px;min-width:42px}.changes-btn{border:1px solid var(--contour);background:var(--paper-raised);color:var(--ink);font-family:var(--font-mono);font-size:11px;font-weight:700;border-radius:8px;padding:6px 12px;cursor:pointer}.changes-btn.active,.changes-btn:hover{background:var(--ink);color:var(--paper)}.changes-btn-count{margin-left:7px;background:#c8892c;color:#fff;border-radius:9px;padding:0 6px;font-size:10px}.changes-panel{position:fixed;top:76px;right:12px;bottom:12px;width:440px;z-index:30;overflow-y:auto;background:var(--paper-raised);border:1px solid var(--contour);border-radius:12px;box-shadow:0 10px 40px #3c321438;padding:14px 16px 18px}.chg-empty{font-size:14px;font-weight:600;color:var(--ink);margin:14px 0 8px}.chg-stepper{display:flex;align-items:center;gap:10px;margin:8px 0 10px}.chg-stepper button{width:30px;height:30px;border-radius:8px;border:1px solid var(--contour);background:var(--paper);font-size:18px;cursor:pointer;color:var(--ink)}.chg-stepper button:disabled{opacity:.3;cursor:default}.chg-ver{flex:1;min-width:0;text-align:center}.chg-ver-title{font-family:var(--font-mono);font-weight:700;font-size:14px;color:var(--ink)}.chg-ver-sha{font-weight:500;color:var(--ink-faint);font-size:11px;margin-left:4px}.chg-dirty{color:#c8892c}.chg-ver-msg{font-size:12px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chg-ver-meta{font-size:10px;color:var(--ink-faint);font-family:var(--font-mono)}.chg-summary{display:flex;flex-direction:column;gap:3px;font-family:var(--font-mono);font-size:11px;color:var(--ink-dim);padding:8px 10px;background:var(--paper);border-radius:8px;margin-bottom:10px}.chg-summary b{color:var(--ink)}.chg-section{margin-top:8px}.chg-section-head{width:100%;display:flex;align-items:center;gap:8px;border:none;background:none;cursor:pointer;font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-dim);padding:4px 0;border-bottom:1px dashed var(--contour)}.chg-section-head .chg-count{background:var(--paper);border-radius:8px;padding:0 6px;color:var(--ink)}.chg-section-head .chg-caret{margin-left:auto}.chg-section-body{padding-top:4px}.chg-wf{padding:6px 8px;border-radius:8px;cursor:pointer;border:1px solid transparent}.chg-wf:hover{background:var(--paper);border-color:var(--contour)}.chg-wf.gone{cursor:default;opacity:.6}.chg-wf-name{font-size:12px;font-weight:650;color:var(--ink);display:flex;align-items:center;gap:6px;flex-wrap:wrap}.chg-wf-why{font-family:var(--font-mono);font-size:10px;color:var(--ink-dim);margin-top:2px;overflow-wrap:anywhere}.chg-svc{font-family:var(--font-mono);font-size:9px;color:var(--ink-faint);margin-left:auto}.chg-tag{font-family:var(--font-mono);font-size:8px;font-weight:700;text-transform:uppercase;border-radius:4px;padding:1px 5px;background:#ece6d2;color:#7a7355}.chg-tag-new{background:#dcefd9;color:#2f6a2a}.chg-tag-removed{background:#f3d9d4;color:#8a3326}.chg-tag-changed{background:#f3e6c4;color:#85601a}.chg-row{font-family:var(--font-mono);font-size:10.5px;color:var(--ink);padding:2px 4px;overflow-wrap:anywhere}.chg-row .chg-sign{display:inline-block;width:14px;font-weight:700}.chg-row.add .chg-sign{color:#2f6a2a}.chg-row.del .chg-sign{color:#8a3326}.chg-row.mod .chg-sign{color:#85601a}.chg-row.mv .chg-sign{color:#4a6480}.chg-file{display:block;margin-left:14px;color:var(--ink-faint);font-size:9.5px}.chg-more,.chg-info{font-size:10px;color:var(--ink-faint);padding:4px}.chg-info{margin-top:12px;border-top:1px dashed var(--contour);padding-top:8px}.change-badge{font-family:var(--font-mono);font-size:8px;font-weight:800;letter-spacing:.04em;border-radius:4px;padding:1px 5px;margin-left:6px;flex-shrink:0}.change-badge-new{background:#2f8a3a;color:#fff}.change-badge-modified{background:#c8892c;color:#fff}.canopy-topbar{flex-wrap:wrap;gap:8px 18px;align-items:flex-start;padding:10px 16px}.topbar-title{flex:1 1 280px;min-width:0}.topbar-controls{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px 10px;margin-left:auto;min-width:0}.tb-group{display:inline-flex;gap:4px}.tb-btn{font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--ink);background:var(--paper-raised);border:1px solid var(--contour);border-radius:7px;padding:6px 10px;min-width:30px;cursor:pointer;white-space:nowrap;transition:background .12s ease}.tb-btn:hover:not(:disabled){background:var(--paper-hover)}.tb-btn.active{background:var(--ink);color:var(--paper)}.tb-btn:disabled{opacity:.4;cursor:default}.changes-btn{white-space:nowrap}.picker-entry-btn{white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}@media(max-width:1100px){.canopy-stats{display:none}}@media(max-width:1000px){.canopy-hint{display:none}}.canopy-body{flex:1;display:flex;min-height:0;position:relative}.canopy-canvas{flex:1;position:relative;min-width:0}.side-panel{background:var(--paper-raised);border-right:1px solid var(--contour);min-height:0}.side-panel.docked{flex:0 0 260px;width:260px;position:relative;display:flex;flex-direction:column;transition:flex-basis .15s ease,width .15s ease}.side-panel.docked.collapsed{flex-basis:30px;width:30px}.side-scroll{overflow-y:auto;flex:1;padding:10px 12px 14px 10px;display:flex;flex-direction:column;gap:10px;min-height:0}.side-title{font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);padding-right:26px;display:flex;justify-content:space-between;align-items:center}.side-handle{position:absolute;top:6px;right:4px;width:22px;height:22px;border-radius:6px;border:1px solid var(--contour);background:var(--paper);color:var(--ink-dim);cursor:pointer;z-index:2;line-height:1;font-size:14px;padding:0}.side-panel.docked.collapsed .side-handle{right:3px}.side-handle:hover{background:var(--paper-hover)}.side-panel.drawer{position:absolute;top:0;bottom:0;left:0;width:270px;max-width:86%;z-index:26;display:flex;flex-direction:column;transform:translate(-104%);visibility:hidden;transition:transform .18s ease,visibility 0s linear .18s;box-shadow:var(--shadow-lg)}.side-panel.drawer.open{transform:none;visibility:visible;transition:transform .18s ease}.side-scrim{position:absolute;inset:0;z-index:25;background:#2823142e}.side-close{border:none;background:none;cursor:pointer;color:var(--ink-dim);font-size:13px;padding:0 2px}.side-groups{display:flex;flex-direction:column;gap:12px}.canopy-edge-path{fill:none;stroke:var(--ink-dim);stroke-width:2px;stroke-linecap:round;stroke-linejoin:round;transition:opacity .12s ease,stroke-width .12s ease}.react-flow__edge.cross-service .canopy-edge-path{stroke:var(--accent-cross);stroke-dasharray:7 5}.canopy-edge-path.hl{stroke:#c8892c;stroke-width:3px;stroke-dasharray:none}.canopy-edge-path.dim{opacity:.14}.v-scrollbar-track.minimap-open{bottom:196px}.h-scrollbar-track{position:absolute;left:10px;right:30px;bottom:4px;height:10px;border-radius:6px;background:#b4a88a29;z-index:5;cursor:pointer}.h-scrollbar-track.minimap-open{right:230px}.h-scrollbar-thumb{position:absolute;top:0;height:100%;border-radius:6px;background:var(--ink-faint);opacity:.55;cursor:grab;transition:opacity .15s ease,background .15s ease}.h-scrollbar-thumb:hover{opacity:.85;background:var(--ink-dim)}.h-scrollbar-thumb:active{cursor:grabbing;opacity:1}.react-flow__minimap{margin:14px 14px 18px}.step-expand.step-toggle{width:22px;height:22px;min-width:22px;padding:0;margin:0;display:inline-flex;align-items:center;justify-content:center;border-radius:6px;font-size:12px;line-height:1;border:1px solid var(--contour);background:var(--paper);color:var(--ink-dim);cursor:pointer;flex:0 0 22px}.step-expand.step-toggle:hover{background:var(--paper-hover);color:var(--ink)}.step-expand.step-toggle.open{background:var(--paper);color:var(--ink)}.chip-more{margin-left:auto;font-family:var(--font-mono);font-size:9px;font-weight:700;color:var(--accent-cross);background:var(--accent-cross-bg);border-radius:8px;padding:0 6px}.seg{display:inline-flex;border:1px solid var(--contour);border-radius:8px;overflow:hidden;width:100%}.seg button{flex:1;border:none;background:var(--paper);color:var(--ink-dim);font-family:var(--font-mono);font-size:10.5px;font-weight:700;padding:6px 8px;cursor:pointer}.seg button+button{border-left:1px solid var(--contour)}.seg button.on{background:var(--ink);color:var(--paper)}.step-node.selected-entry{border-color:var(--accent-cross);box-shadow:0 0 0 2px var(--accent-cross),var(--shadow-md)}.step-node.parent-card{border-style:dashed;background:var(--paper)}.caller-tag{font-family:var(--font-mono);font-size:8px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--accent-cross);background:var(--accent-cross-bg);border-radius:4px;padding:1px 5px}.excerpt-gap{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);padding:2px 4px;height:20px;line-height:16px}.branch-step-chip.call-target{background:var(--accent-cross-bg);border-color:var(--accent-cross);border-style:solid;box-shadow:0 0 0 1.5px var(--accent-cross)}.context-chip-node{display:flex;align-items:center;justify-content:center;width:100%;height:100%;border:1px dashed var(--accent-cross);background:var(--accent-cross-bg);box-shadow:none;padding:0 12px}.context-chip-text{font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--accent-cross)}.trigger-node{display:flex;align-items:center;gap:8px;width:100%;height:100%;padding:0 12px;background:var(--paper-raised);border:1px solid var(--accent-cross)}.trigger-node.inferred{border-style:dashed;color:var(--ink-dim)}.trigger-kind{font-family:var(--font-mono);font-size:8.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;background:var(--accent-cross-bg);color:var(--accent-cross);border-radius:4px;padding:2px 6px;flex-shrink:0}.trigger-label{font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}[data-lod=compact] .step-node .step-file,[data-lod=compact] .step-node .step-cat,[data-lod=compact] .step-node .source-icon,[data-lod=compact] .step-node .change-badge,[data-lod=compact] .step-node .caller-tag,[data-lod=compact] .step-node .chip-more,[data-lod=compact] .step-node .promoted-hint,[data-lod=compact] .step-node .excerpt-gap,[data-lod=compact] .step-node .branch-condition-text,[data-lod=compact] .step-node .branch-condition-kind,[data-lod=compact] .step-node .branch-arrow-label,[data-lod=compact] .step-node .branch-step-name,[data-lod=compact] .step-node .compact-label,[data-lod=compact] .step-node .compact-dot,[data-lod=compact] .step-node .runtime-bucket-header,[data-lod=compact] .step-node .runtime-tag{visibility:hidden}[data-lod=compact] .step-node .branch-step-chip{background:var(--accent-cross-bg);border-color:var(--accent-cross)}[data-lod=compact] .step-node .branch-step-chip.promoted{background:var(--accent-cross)}[data-lod=compact] .step-node .runtime-bucket-item.compact{background:var(--accent-unknown-bg);border-radius:4px}[data-lod=compact] .step-node .runtime-bucket-item.compact:has(.runtime-tag-runtime-dispatched){background:var(--accent-cross-bg)}[data-lod=compact] .step-node .runtime-bucket-item.compact:has(.runtime-tag-return-value){background:var(--accent-client-bg)}[data-lod=compact] .step-node .branch-diagram{background:var(--paper)}[data-lod=compact] .step-node .step-name,[data-lod=boxes] .step-node .step-name{transform:scale(var(--inv-zoom, 1));transform-origin:left center;max-width:calc(100% / var(--inv-zoom, 1));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;font-weight:700;position:relative;z-index:2}[data-lod=boxes] .step-node .step-body-contained,[data-lod=boxes] .step-node .step-file,[data-lod=boxes] .step-node .step-cat,[data-lod=boxes] .step-node .source-icon,[data-lod=boxes] .step-node .change-badge,[data-lod=boxes] .step-node .caller-tag,[data-lod=boxes] .step-node .step-badge,[data-lod=boxes] .step-node .step-toggle{visibility:hidden}[data-lod=boxes] .step-node{background:var(--paper-raised)}.back-btn{margin-right:4px;padding:4px 9px}.side-sub{font-size:11.5px;line-height:1.4;color:var(--ink-dim);margin:-4px 0 2px;padding-right:6px}.react-flow__edge.hop-solid .canopy-edge-path{stroke-dasharray:none}.react-flow__edge.hop-dashed .canopy-edge-path{stroke-dasharray:7 5}.react-flow__edge.hop-solid .canopy-edge-path:not(.hl),.react-flow__edge.hop-dashed .canopy-edge-path:not(.hl){stroke:var(--accent-cross);stroke-width:2.2px}.hop-node{width:100%;height:100%;padding:7px 10px 8px;display:flex;flex-direction:column;gap:2px;cursor:pointer;background:var(--accent-cross-bg);border:1.5px solid var(--accent-cross);overflow:hidden}.hop-node.hop-dashed{border-style:dashed}.hop-top{display:flex;align-items:center;justify-content:space-between;gap:6px}.hop-svc{font-family:var(--font-mono);font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--accent-cross);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.hop-tag{font-family:var(--font-mono);font-size:8px;font-weight:700;text-transform:uppercase;border-radius:4px;padding:1px 5px;background:var(--paper-raised);color:var(--ink-dim);flex-shrink:0}.hop-tag-exact,.hop-tag-resolved{color:var(--accent-client)}.hop-call{font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hop-handler{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-height:12px}.hop-view{align-self:flex-start;margin-top:auto;border:1px solid var(--accent-cross);background:var(--paper-raised);color:var(--accent-cross);border-radius:6px;font-size:10px;font-weight:600;padding:2px 8px;cursor:pointer}.hop-view:hover{background:var(--paper-hover)}.called-from-chip{display:block;max-width:100%;margin-top:4px;border:1px solid var(--accent-cross);background:var(--accent-cross-bg);color:var(--accent-cross);border-radius:999px;font-family:var(--font-mono);font-size:9.5px;font-weight:600;padding:2px 9px;cursor:pointer;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.called-from-chip:hover{background:var(--paper-hover)}.hop-chooser{position:fixed;z-index:200;width:284px;background:var(--paper-raised);border:1px solid var(--contour);border-radius:10px;box-shadow:var(--shadow-lg);padding:6px;display:flex;flex-direction:column;gap:2px}.hop-chooser-label{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-faint);padding:2px 6px 4px}.hop-chooser-item{display:flex;flex-direction:column;align-items:flex-start;text-align:left;border:none;background:none;border-radius:6px;padding:5px 8px;cursor:pointer}.hop-chooser-item:hover{background:var(--paper-hover)}.hop-chooser-title{font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--ink);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hop-chooser-sub{font-size:10px;color:var(--ink-dim)}.hop-chooser-more{font-size:10.5px;color:var(--ink-dim);padding:3px 8px;font-style:italic}[data-lod=compact] .hop-node .hop-handler,[data-lod=compact] .hop-node .hop-view,[data-lod=compact] .hop-node .hop-tag,[data-lod=boxes] .hop-node .hop-handler,[data-lod=boxes] .hop-node .hop-view,[data-lod=boxes] .hop-node .hop-tag,[data-lod=boxes] .hop-node .hop-call,[data-lod=boxes] .called-from-chip{visibility:hidden}.canopy-topbar{flex-wrap:nowrap;align-items:center;gap:10px 14px;padding:8px 16px}.canopy-crumbs{flex:1 1 auto;min-width:0;overflow:hidden;flex-wrap:nowrap;white-space:nowrap}button.crumb{background:none;border:none;font:inherit;font-size:13px;font-weight:500}.crumb{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:0 1 auto}.crumb.current{max-width:320px}.topbar-controls{flex:0 0 auto;flex-wrap:nowrap;gap:6px 8px}.icon-btn{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:32px;height:30px;padding:0 8px;border:1px solid var(--contour);border-radius:7px;background:var(--paper-raised);color:var(--ink);cursor:pointer;transition:background .12s ease}.icon-btn:hover{background:var(--paper-hover)}.icon-btn.active{background:var(--ink);color:var(--paper)}.icon-btn .changes-btn-count{margin-left:0}button:focus-visible,[role=button]:focus-visible,.search-input:focus-visible,.code-box:focus-visible,a:focus-visible{outline:2px solid var(--accent-cross);outline-offset:2px}.search{position:relative;flex:0 1 340px;min-width:150px}.search-input{width:100%;box-sizing:border-box;height:30px;padding:0 10px;border:1px solid var(--contour);border-radius:7px;background:var(--paper);color:var(--ink);font:12px var(--font-ui)}.search-input::placeholder{color:var(--ink-faint)}.search-results{position:absolute;top:34px;left:0;width:min(520px,calc(100vw - 32px));max-height:60vh;overflow-y:auto;z-index:45;background:var(--paper-raised);border:1px solid var(--contour);border-radius:10px;box-shadow:var(--shadow-lg);padding:6px}.search-group-title{font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);padding:6px 8px 3px}.search-item{display:flex;align-items:baseline;gap:10px;padding:5px 8px;border-radius:6px;cursor:pointer}.search-item.active,.search-item:hover{background:var(--paper-hover)}.search-name{font-size:12.5px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto;max-width:55%}.search-meta{font-family:var(--font-mono);font-size:10px;color:var(--ink-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;margin-left:auto}.search-empty{padding:10px;font-size:12px;color:var(--ink-dim)}.modebar{display:flex;align-items:center;gap:12px;padding:6px 16px;background:var(--paper-raised);border-bottom:1px solid var(--contour)}.mode-toggle{width:auto}.mode-toggle button{flex:none;padding:5px 14px}.disclaimer-bar{padding:5px 16px;font-size:12px;line-height:1.4;color:var(--ink-dim);background:var(--accent-cross-bg);border-bottom:1px solid var(--contour)}.disclaimer-bar a{color:var(--accent-cross);font-weight:700}button.context-chip{display:block;width:100%;text-align:left;font-family:inherit}.context-chip.current{background:var(--ink);color:var(--paper);border-color:var(--ink)}.context-chip.current:hover{background:var(--ink)}.code-overlay{flex:0 0 440px;width:440px;min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--paper-raised);border-left:1px solid var(--contour);box-shadow:-8px 0 26px #2823141a;z-index:22}.ov-head{display:flex;align-items:flex-start;gap:8px;padding:12px 14px 8px;border-bottom:1px solid var(--contour)}.ov-title{flex:1;min-width:0}.ov-name{font-size:15px;font-weight:700;color:var(--ink);word-break:break-word}.ov-file{font-family:var(--font-mono);font-size:11px;color:var(--ink-dim);margin-top:2px;word-break:break-all}.ov-close{border:none;background:none;cursor:pointer;font-size:15px;color:var(--ink-faint);padding:4px 6px;border-radius:6px}.ov-close:hover{color:var(--ink);background:var(--paper-hover)}.ov-scroll{flex:1;min-height:0;overflow-y:auto;padding:8px 14px 18px}.ov-section{margin-top:12px}.ov-label{font-size:9.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--ink-faint);font-weight:700;margin-bottom:5px}.ov-count{margin-left:4px;background:var(--paper);border-radius:8px;padding:0 6px;color:var(--ink-dim)}.ov-note{font-size:12px;color:var(--ink-dim);line-height:1.5}.ov-warn{color:#8a3326}.ov-removed-note{display:inline-block;font-weight:700;color:#8a3326;background:#f3d9d4;border-radius:6px;padding:3px 9px;margin-bottom:8px}.ov-meta{font-family:var(--font-mono);font-size:11px;color:var(--ink-dim);word-break:break-all;line-height:1.6}.ov-meta b{color:var(--ink);margin-right:6px}.ov-edge-ends{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:4px}.ov-arrow{color:var(--ink-faint)}.code-box{max-height:46vh;overflow:auto;background:var(--paper);border:1px solid var(--contour);border-radius:8px;font-family:var(--font-mono);font-size:11.5px;line-height:1.55;position:relative}.code-line{display:flex;white-space:pre}.code-no{flex:0 0 46px;text-align:right;padding-right:10px;color:var(--ink-faint);user-select:none;background:var(--paper-raised);position:sticky;left:0}.code-text{padding-right:12px;color:var(--ink)}.code-line.in-range{background:#c8892c29}.code-line.in-range .code-no{color:var(--ink);font-weight:700;background:#c8892c38}.ov-item{display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;font:inherit;font-size:12px;color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:6px;padding:5px 8px;margin-top:4px;cursor:pointer}.ov-item:hover{background:var(--paper-hover)}.ov-item.dashed{border-style:dashed}.ov-i-name{font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ov-i-meta{margin-left:auto;font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:55%}.chg-group-title{font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);margin:12px 0 2px}.chg-row.clickable{cursor:pointer;border-radius:6px;padding:3px 6px}.chg-row.clickable:hover{background:var(--paper)}.chg-row.chg-open-wf{color:var(--accent-cross);font-weight:700}.chg-section-head .chg-tag{margin-left:2px}.has-overlay .changes-panel{right:452px}@media(max-width:1099px){.code-overlay{position:fixed;left:0;right:0;bottom:0;width:auto;flex:none;height:58vh;border-left:none;border-top:1px solid var(--contour);border-radius:12px 12px 0 0;box-shadow:0 -10px 30px #2823142e;z-index:36}.code-box{max-height:30vh}.has-overlay .changes-panel{right:12px}.changes-panel{z-index:40}}@media(max-width:820px){.canopy-topbar{flex-wrap:wrap}.canopy-crumbs{flex:1 1 100%}.search{flex:1 1 160px}.topbar-controls{flex-wrap:wrap}}@media(max-width:480px){.changes-panel{left:8px;right:8px;width:auto}.search-results{width:calc(100vw - 16px)}}.changes-panel{top:calc(var(--chrome-h, 68px) + 8px)}.search-flag{font-family:var(--font-mono);font-size:8.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--ink-dim);border:1px solid var(--contour);border-radius:4px;padding:0 5px;margin-left:8px;white-space:nowrap;flex-shrink:0}:root{--diff-add: #dcefd9;--diff-del: #f3d9d4;--diff-paper: var(--paper);--diff-ink: var(--ink);--diff-gutter: var(--ink-faint);--diff-add-sign: #2f6a2a;--diff-del-sign: #8a3326}.ov-tabs{display:flex;gap:4px;border-bottom:1px solid var(--contour);margin:2px 0 10px}.ov-tab{border:1px solid transparent;border-bottom:none;background:none;color:var(--ink-dim);font-family:var(--font-mono);font-size:11px;font-weight:700;padding:5px 12px;border-radius:8px 8px 0 0;cursor:pointer}.ov-tab.on{background:var(--paper-raised);color:var(--ink);border-color:var(--contour);margin-bottom:-1px}.ov-tab:focus-visible{outline:2px solid var(--accent-cross);outline-offset:1px}.diff-head{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px;font-family:var(--font-mono);font-size:11px}.diff-stat{font-weight:700;color:var(--ink)}.diff-flag{border:1px solid var(--contour);border-radius:4px;padding:0 6px;font-size:10px;font-weight:700;color:var(--ink-dim)}.diff-moved{color:var(--ink-dim);overflow-wrap:anywhere;font-size:10px}.diff-box{max-height:52vh;overflow:auto;background:var(--diff-paper);color:var(--diff-ink);border:1px solid var(--contour);border-radius:8px;font-family:var(--font-mono);font-size:11.5px;line-height:1.5;padding:4px 0}.diff-row{display:flex;align-items:baseline;min-width:max-content;width:100%;white-space:pre}.diff-row.add{background:var(--diff-add)}.diff-row.del{background:var(--diff-del)}.diff-row.hunk{background:transparent;color:var(--diff-gutter);font-size:10.5px;padding:3px 8px;border-top:1px dashed var(--contour);border-bottom:1px dashed var(--contour);margin:4px 0}.diff-row.hunk:first-child{margin-top:0;border-top:none}.diff-no{flex:none;width:38px;text-align:right;padding-right:6px;color:var(--diff-gutter);user-select:none;font-size:10.5px}.diff-sign{flex:none;width:16px;text-align:center;font-weight:800;user-select:none}.diff-row.add .diff-sign{color:var(--diff-add-sign)}.diff-row.del .diff-sign{color:var(--diff-del-sign)}.diff-text{flex:1;padding-right:10px}.diff-row.diff-hit{outline:2px solid var(--accent-cross);outline-offset:-2px}.diff-hit-tag,.code-line .diff-hit-tag{flex:none;align-self:center;margin:0 8px 0 auto;font-family:var(--font-mono);font-size:8.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--accent-cross);background:var(--accent-cross-bg);border-radius:4px;padding:0 5px}.code-line.code-hit{outline:2px solid var(--accent-cross);outline-offset:-2px;display:flex}.chg-stat{margin-left:8px;font-size:9.5px;font-weight:700;color:var(--ink-dim);white-space:nowrap}.chg-stat.pending{min-width:1px}.rt-band{width:100%;height:100%;border:1px solid;border-radius:14px;opacity:.55;pointer-events:none}.rt-band-head{display:flex;flex-direction:column;gap:1px;padding:10px 14px}.rt-band-name{font-family:var(--font-mono);font-size:13px;font-weight:700}.rt-band-kind{font-family:var(--font-mono);font-size:9.5px;font-weight:600;opacity:.85}.rt-node{width:100%;height:100%;background:var(--paper-raised);border:1px solid var(--contour);border-left:4px solid var(--accent-cross);border-radius:8px;box-shadow:var(--shadow-sm);padding:5px 10px;cursor:pointer;overflow:hidden}.rt-node:hover{box-shadow:var(--shadow-md)}.rt-node.start{border-color:var(--ink);border-left-width:4px}.rt-name{font-family:var(--font-mono);font-size:11.5px;font-weight:700;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.rt-file{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}.rt-more{font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--ink-dim);background:var(--paper-raised);border:1px dashed var(--contour);border-radius:8px;padding:6px 10px;white-space:nowrap}.react-flow__edge.rt-edge path.react-flow__edge-path{stroke:#6b664f;stroke-width:1.8;fill:none}.react-flow__edge.rt-cross path.react-flow__edge-path{stroke:#4a6480;stroke-width:2.2}.react-flow__edge.rt-dashed path.react-flow__edge-path{stroke-dasharray:8 6}.react-flow__edge.rt-solid path.react-flow__edge-path{stroke-dasharray:none}.project-name{flex:none;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--ink-faint);padding-right:8px;margin-right:3px;border-right:1px solid var(--contour)}.rt-progress{position:absolute;left:50%;top:10px;transform:translate(-50%);z-index:5;font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--ink-dim);background:var(--paper-raised);border:1px solid var(--contour);border-radius:999px;padding:3px 12px;box-shadow:var(--shadow-sm);pointer-events:none}.rt-loop{float:right;font-family:var(--font-mono);font-size:10px;font-weight:800;color:var(--accent-cross)}.chg-modes{display:flex;margin:4px 0 10px;border:1px solid var(--contour);border-radius:8px;overflow:hidden}.chg-modes button{flex:1;font-size:10.5px;padding:5px 6px}.chg-since{background:var(--paper);border:1px solid var(--contour);border-radius:8px;padding:8px 10px;margin-bottom:10px}.chg-since-head{font-family:var(--font-mono);font-size:11.5px;font-weight:700;color:var(--ink)}.chg-since-note{font-size:11px;color:var(--ink-dim);margin-top:4px}.react-flow__edge.rt-lite path.react-flow__edge-path,.rt-line{stroke:#b9b196;stroke-width:1.4;fill:none}.search-item-rich{flex-direction:column;align-items:stretch;gap:3px}.search-line1,.search-line2{display:flex;align-items:baseline;gap:8px;min-width:0}.search-line2{flex-wrap:wrap}.search-method{font-family:var(--font-mono);font-size:10.5px;color:var(--accent-cross)}.search-chip{font-family:var(--font-mono);font-size:8.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--ink-dim);background:var(--paper-hover);border-radius:4px;padding:0 5px;white-space:nowrap;flex-shrink:0}.search-appears{font-size:10.5px;color:var(--ink-dim)}.search-switch{display:inline-flex;flex-wrap:wrap;gap:4px;align-items:baseline}.search-switch-btn{font:10.5px var(--font-ui);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:5px;padding:1px 6px;cursor:pointer;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.search-switch-btn:hover{background:var(--paper-hover)}.search-code-text{font-family:var(--font-mono);font-size:11px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.start-head,.start-more,.start-item{display:flex;align-items:baseline;gap:8px;width:100%;box-sizing:border-box;text-align:left;background:none;border:0;color:var(--ink);font:inherit;cursor:pointer;border-radius:6px;padding:5px 8px}.start-head:hover,.start-more:hover,.start-item:hover{background:var(--paper-hover)}.start-caret{width:10px;color:var(--ink-faint)}.start-item{padding-left:26px}.start-more{padding-left:26px;font-size:11px;color:var(--ink-dim)}.canopy-topbar:focus-within{z-index:50}.search-ask{display:block;width:100%;box-sizing:border-box;text-align:left;background:none;border:0;border-top:1px solid var(--contour);margin-top:6px;padding:8px 8px 4px;font:12px var(--font-ui);color:var(--accent-cross);cursor:pointer}.search-ask:hover{text-decoration:underline}.ask-ai{position:absolute;top:34px;left:0;width:min(520px,calc(100vw - 32px));z-index:46;box-sizing:border-box;background:var(--paper-raised);border:1px solid var(--contour);border-radius:10px;box-shadow:var(--shadow-lg);padding:10px 12px 12px;display:flex;flex-direction:column;gap:8px}.ask-ai-head{display:flex;align-items:center;justify-content:space-between;font-size:12.5px}.ask-ai-text{width:100%;box-sizing:border-box;resize:vertical;font:12.5px var(--font-ui);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:7px;padding:7px 9px}.ask-ai-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.ask-ai-primary{font:600 12px var(--font-ui);color:#fff;background:var(--accent-cross);border:0;border-radius:7px;padding:7px 14px;cursor:pointer}.ask-ai-secondary{display:inline-block;font:12px var(--font-ui);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:7px;padding:5px 10px;cursor:pointer;text-decoration:none}.ask-ai-secondary:hover{background:var(--paper-hover)}.ask-ai-note,.ask-ai-privacy{font-size:10.5px;color:var(--ink-dim)}.ask-ai-preview summary{font-size:11px;color:var(--ink-dim);cursor:pointer}.ask-ai-preview pre{max-height:180px;overflow:auto;white-space:pre-wrap;font:10.5px var(--font-mono);color:var(--ink-dim);margin:6px 0 0}@media(max-width:480px){.ask-ai{width:calc(100vw - 16px)}}.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #555;--xy-background-pattern-lines-color-default: #333;--xy-background-pattern-cross-color-default: #333;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;touch-action:none}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}
1
+ @font-face{font-family:Inter;font-style:normal;font-weight:400 700;font-display:swap;src:url("./fonts/Inter-latin-var.woff2") format("woff2")}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:400 700;font-display:swap;src:url("./fonts/JetBrainsMono-latin-var.woff2") format("woff2")}:root{--paper: #f3efe3;--paper-raised: #faf7ee;--paper-hover: #eee7d4;--contour: #d9d0ba;--ink: #2b2a22;--ink-dim: #6b664f;--ink-faint: #a39a7d;--shadow-sm: 0 1px 2px rgba(40, 35, 20, .06), 0 2px 6px rgba(40, 35, 20, .05);--shadow-md: 0 2px 4px rgba(40, 35, 20, .07), 0 8px 20px rgba(40, 35, 20, .09);--shadow-lg: 0 4px 10px rgba(40, 35, 20, .1), 0 18px 38px rgba(40, 35, 20, .14);--accent-client: #4f7a5c;--accent-client-bg: #e4ecdf;--accent-server: #9a5b32;--accent-server-bg: #f1e2cf;--accent-unknown: #6b5a8f;--accent-unknown-bg: #e7e1ef;--accent-cross: #4a6480;--accent-cross-bg: #dfe6ec;--font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono: "JetBrains Mono", "SF Mono", Consolas, monospace}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:var(--font-ui);color:var(--ink);background:radial-gradient(ellipse 100% 70% at 50% -10%,#f8f4e8,#efe8d5 55%,#e4dabf);overflow:hidden}.canopy-app{height:100vh;display:flex;flex-direction:column}.canopy-topbar{display:flex;align-items:center;justify-content:space-between;padding:14px 22px;border-bottom:1px solid var(--contour);background:var(--paper-raised);box-shadow:0 1px #28231408,0 2px 10px #2823140d;z-index:10;position:relative}.canopy-brand{font-family:var(--font-ui);font-weight:700;font-size:16px;letter-spacing:-.01em;color:var(--ink);display:flex;align-items:center;gap:7px}.canopy-brand .leaf{color:var(--accent-client);font-size:15px}.canopy-crumbs{display:flex;align-items:center;gap:5px;font-size:13px}.crumb{padding:4px 9px;border-radius:6px;cursor:pointer;color:var(--ink-dim);font-weight:500;transition:background .15s ease,color .15s ease}.crumb:hover{background:var(--paper-hover);color:var(--ink)}.crumb.current{color:var(--ink);font-weight:700;cursor:default;font-size:14px}.crumb.current:hover{background:none}.crumb-sep{color:var(--ink-faint);font-size:11px}.canopy-hint{font-size:11.5px;color:var(--ink-faint);margin-top:1px;font-weight:500}.picker-entry-btn{font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--accent-cross);background:var(--accent-cross-bg);border:1px solid var(--accent-cross);border-radius:7px;padding:6px 11px;cursor:pointer;transition:background .15s ease}.picker-entry-btn:hover{background:var(--accent-cross);color:var(--paper-raised)}.canopy-stats{display:flex;gap:18px}.stat{text-align:right}.stat-num{font-size:17px;font-weight:700;line-height:1;color:var(--ink);font-variant-numeric:tabular-nums}.stat-label{font-size:9.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);font-weight:600;margin-top:2px}.canopy-canvas{flex:1;position:relative}.react-flow__attribution{display:none}.react-flow__controls{box-shadow:var(--shadow-md);border-radius:10px;overflow:hidden;border:1px solid var(--contour)}.react-flow__controls-button{background:var(--paper-raised);border-bottom:1px solid var(--contour);color:var(--ink-dim);width:30px;height:30px}.react-flow__controls-button:hover{background:var(--paper-hover);color:var(--ink)}.react-flow__controls-button svg{fill:currentColor}.react-flow__minimap{border-radius:10px;box-shadow:var(--shadow-md);border:1px solid var(--contour);overflow:hidden}.group-bg{width:100%;height:100%;border-radius:20px;background:#96855414;border:1px dashed rgba(110,95,55,.18);pointer-events:none}.v-scrollbar-track{position:absolute;top:10px;bottom:30px;right:4px;width:10px;border-radius:6px;background:#b4a88a29;z-index:5;cursor:pointer}.v-scrollbar-thumb{position:absolute;left:0;width:100%;border-radius:6px;background:var(--ink-faint);opacity:.55;cursor:grab;transition:opacity .15s ease,background .15s ease}.v-scrollbar-thumb:hover{opacity:.85;background:var(--ink-dim)}.v-scrollbar-thumb:active{cursor:grabbing;opacity:1}.react-flow__edge-path{stroke-linecap:round}.react-flow__edge.same-service .react-flow__edge-path{stroke:var(--ink-dim);stroke-width:2px}.react-flow__edge.cross-service .react-flow__edge-path{stroke:var(--accent-cross);stroke-width:2.2px;stroke-dasharray:7 5}.react-flow__edge.service-link .react-flow__edge-path{stroke:var(--accent-cross);stroke-width:2.4px;stroke-dasharray:8 6}.card{background:var(--paper-raised);border-radius:12px;box-shadow:var(--shadow-sm);transition:box-shadow .18s ease,transform .18s ease;border:1px solid rgba(40,35,20,.06)}.card:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}.svc-node{width:240px;padding:18px 20px;cursor:pointer}.svc-kind{display:inline-block;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;padding:3px 9px;border-radius:5px}.svc-name{font-size:19px;font-weight:700;margin-top:10px;letter-spacing:-.01em;color:var(--ink)}.svc-meta{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-dim);margin-top:6px}.svc-badges{display:flex;gap:6px;margin-top:11px;flex-wrap:wrap}.svc-badge{font-family:var(--font-mono);font-size:9px;font-weight:600;background:var(--accent-cross-bg);color:var(--accent-cross);border-radius:4px;padding:2px 7px}.wf-node{width:100%;height:100%;padding:14px 18px;cursor:pointer;display:flex;align-items:center;gap:14px}.wf-dot{width:9px;height:9px;border-radius:50%;flex-shrink:0}.wf-body{flex:1;min-width:0}.wf-name{font-size:14.5px;font-weight:650;color:var(--ink);line-height:1.3;overflow-wrap:break-word;word-break:normal}.wf-meta{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-faint);margin-top:4px}.wf-chevron{color:var(--ink-faint);font-size:18px;flex-shrink:0}.minor-panel{position:absolute;left:50%;bottom:22px;transform:translate(-50%);z-index:15;width:460px;max-width:calc(100% - 44px);background:var(--paper-raised);border:1px solid var(--contour);border-radius:12px;box-shadow:var(--shadow-md);overflow:hidden}.minor-toggle{width:100%;border:none;background:none;cursor:pointer;padding:11px 18px;display:flex;align-items:center;justify-content:space-between;font-family:var(--font-mono);font-size:11.5px;font-weight:600;color:var(--ink-dim)}.minor-toggle:hover{color:var(--ink);background:var(--paper-hover)}.minor-caret{color:var(--ink-faint);font-size:10px}.minor-list{max-height:260px;overflow-y:auto;border-top:1px solid var(--contour)}.minor-item{padding:8px 18px;font-size:12px;color:var(--ink-dim);cursor:pointer;border-bottom:1px solid rgba(40,35,20,.05);display:flex;justify-content:space-between;gap:10px}.minor-item:last-child{border-bottom:none}.minor-item:hover{background:var(--paper-hover);color:var(--ink)}.minor-item .minor-file{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:1}.context-rail{position:absolute;top:14px;left:10px;z-index:8;width:190px;max-height:calc(100% - 28px);overflow-y:auto;display:flex;flex-direction:column;gap:12px}.context-group{background:var(--paper-raised);border:1px solid var(--contour);border-radius:10px;padding:9px 10px;box-shadow:var(--shadow-sm)}.context-label{font-family:var(--font-mono);font-size:8.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-faint);margin-bottom:6px}.context-chip{font-size:11px;font-weight:600;color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:6px;padding:5px 8px;margin-top:4px;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:background .15s ease}.context-chip:first-child{margin-top:0}.context-chip:hover{background:var(--paper-hover)}.context-more{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);margin-top:4px}.step-node{width:100%;height:100%;min-height:90px;padding:18px;box-sizing:border-box;overflow:visible;position:relative}.step-node.root{border-color:var(--accent-client);box-shadow:0 0 0 1.5px var(--accent-client),var(--shadow-sm)}.step-node.active-expanded:not(.root){border-color:var(--accent-cross);box-shadow:0 0 0 1.5px var(--accent-cross),var(--shadow-sm)}.step-top{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.step-badge{font-family:var(--font-mono);font-size:9px;font-weight:700;background:var(--ink);color:var(--paper);border-radius:4px;padding:1.5px 6px;flex-shrink:0}.step-cat{font-family:var(--font-mono);font-size:8px;font-weight:700;border-radius:4px;padding:1.5px 6px;flex-shrink:0;text-transform:uppercase;letter-spacing:.03em}.step-name{font-size:13px;font-weight:650;color:var(--ink);line-height:1.3;flex:1;min-width:60px;overflow-wrap:break-word;word-break:normal}.step-file{font-family:var(--font-mono);font-size:8.5px;color:var(--ink-faint);margin-top:5px;overflow-wrap:break-word;word-break:normal}.step-expand{margin-top:8px;font-family:var(--font-mono);font-size:10px;font-weight:600;border:1px solid var(--contour);background:var(--paper);color:var(--ink-dim);border-radius:6px;padding:3px 9px;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}.step-expand:hover{background:var(--paper-hover);color:var(--ink)}.step-expand.open{background:var(--ink);color:var(--paper);border-color:var(--ink)}.react-flow__edge.secondary-link .react-flow__edge-path{stroke:var(--ink-faint);stroke-dasharray:3 4;stroke-width:1.3}.source-icon{border:none;background:var(--paper);color:var(--ink-faint);font-family:var(--font-mono);font-size:9.5px;font-weight:700;border-radius:4px;padding:1px 5px;cursor:pointer;flex-shrink:0;margin-left:auto}.source-icon:hover{background:var(--ink);color:var(--paper)}.detail-source{font-family:var(--font-mono);font-size:11px;line-height:1.55;color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:8px;padding:10px 12px;margin-top:6px;white-space:pre-wrap;word-break:break-word;overflow-wrap:break-word;word-break:normal}.source-unavailable{color:var(--accent-server);font-style:italic}.recursive-badge{margin-top:7px;font-family:var(--font-mono);font-size:9.5px;font-weight:700;color:var(--accent-server);background:var(--accent-server-bg);border-radius:5px;padding:3px 7px;display:inline-block}.runtime-bucket{width:100%;padding:16px;border:1px dashed var(--contour);background:var(--paper);box-sizing:border-box;max-height:270px;overflow-y:auto}.runtime-bucket-header{font-family:var(--font-mono);font-size:8.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);padding:2px 8px 6px}.runtime-bucket-item{display:flex;flex-direction:column;align-items:flex-start;gap:3px;padding:6px 8px;font-size:10.5px;color:var(--ink-dim)}.runtime-bucket-item.inline{padding:5px 0}.runtime-bucket-item-row{display:flex;align-items:center;gap:6px;width:100%}.runtime-bucket-label{flex:1;min-width:60px;overflow-wrap:break-word;word-break:normal;font-family:var(--font-mono)}.runtime-tag{font-family:var(--font-mono);font-size:8px;font-weight:700;border-radius:4px;padding:1.5px 5px;flex-shrink:0;text-transform:uppercase;letter-spacing:.02em}.runtime-tag-unresolvable-call-site{background:var(--accent-unknown-bg);color:var(--accent-unknown)}.runtime-tag-runtime-dispatched{background:var(--accent-cross-bg);color:var(--accent-cross)}.runtime-tag-return-value{background:var(--accent-client-bg);color:var(--accent-client)}.runtime-bucket.nested-bucket{margin:0}.inline-call{width:100%;height:100%;padding:16px;display:flex;flex-direction:column;align-items:flex-start;gap:3px;border:1px solid var(--contour);background:var(--paper);box-sizing:border-box;font-size:10.5px;color:var(--ink-dim)}.branch-diagram{width:100%;padding:4px 10px;border:1.5px solid var(--accent-cross);box-sizing:border-box}.branch-diagram.nested{border-width:1px;box-shadow:none;padding:3px 8px}.branch-condition{display:flex;align-items:center;gap:7px;padding-bottom:2px;border-bottom:1px dashed var(--contour)}.branch-condition-kind{font-family:var(--font-mono);font-size:8px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;background:var(--accent-cross-bg);color:var(--accent-cross);border-radius:4px;padding:2px 6px;flex-shrink:0}.branch-condition-text{font-family:var(--font-mono);font-size:11.5px;font-weight:650;color:var(--ink);overflow-wrap:break-word;word-break:normal;flex:1;min-width:80px}.branch-outcomes{display:flex;flex-direction:row;align-items:flex-start;gap:18px;margin-top:4px;overflow-x:visible}.branch-outcome{display:flex;flex-direction:row;align-items:flex-start;gap:8px;min-width:150px;flex-shrink:0}.branch-arrow-row{display:flex;align-items:center;gap:5px;height:12px}.branch-arrow-svg{height:10px}.branch-arrow-label{font-family:var(--font-mono);font-size:9px;font-weight:700;color:var(--accent-cross);text-transform:uppercase;flex-shrink:0}.branch-chain-lane{display:flex;flex-direction:column;gap:3px;min-width:0}.branch-empty{font-family:var(--font-mono);font-size:10px;color:var(--ink-faint);font-style:italic;padding:4px 0}.step-body-contained{margin-top:14px;padding-top:12px;border-top:1px dashed var(--contour)}.chain-row{position:relative;min-width:0}.branch-step-chip{display:flex;align-items:center;gap:6px;background:var(--paper);border:1px solid var(--contour);border-radius:6px;padding:6px 9px;cursor:pointer;transition:background .15s ease,border-color .15s ease}.branch-step-chip:hover{background:var(--paper-hover)}.branch-step-chip.promoted{border-color:var(--accent-cross);border-style:dashed;background:var(--accent-cross-bg)}.branch-step-name{font-size:11.5px;font-weight:600;color:var(--ink);flex:1;min-width:40px;overflow-wrap:break-word;word-break:normal}.promoted-hint{font-family:var(--font-mono);font-size:8.5px;font-weight:600;color:var(--accent-cross);flex-shrink:0}.empty-state{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;color:var(--ink-faint);font-family:var(--font-mono);font-size:12.5px;max-width:320px;line-height:1.6;pointer-events:none}.empty-state .glyph{font-size:26px;margin-bottom:10px;opacity:.55}.detail-panel{position:absolute;top:0;right:0;height:100%;width:320px;background:var(--paper-raised);border-left:1px solid var(--contour);box-shadow:-8px 0 26px #2823141f;padding:24px 22px;overflow-y:auto;transform:translate(0);animation:panelIn .22s cubic-bezier(.22,.9,.3,1) both;z-index:20}@keyframes panelIn{0%{transform:translate(16px);opacity:0}to{transform:translate(0);opacity:1}}.detail-close{position:absolute;top:16px;right:16px;border:none;background:none;cursor:pointer;font-size:15px;color:var(--ink-faint)}.detail-close:hover{color:var(--ink)}.detail-label{font-size:9.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--ink-faint);font-weight:700;margin-top:18px;margin-bottom:5px}.detail-label:first-of-type{margin-top:8px}.detail-name{font-size:18px;font-weight:700;margin-top:20px;color:var(--ink)}.detail-mono{font-family:var(--font-mono);font-size:11.5px;color:var(--ink-dim);word-break:break-all;line-height:1.5}.detail-note{font-size:12.5px;color:var(--ink-dim);line-height:1.5}@keyframes nodeFadeIn{0%{opacity:0}to{opacity:1}}.react-flow__node{animation:nodeFadeIn .18s ease both}@keyframes cardIn{0%{transform:scale(.92) translateY(6px)}to{transform:scale(1) translateY(0)}}.react-flow__node .card{animation:cardIn .22s cubic-bezier(.22,.9,.3,1) both}.inferred-badge{display:inline-block;margin-left:8px;padding:1px 6px;border-radius:8px;font-size:9px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;background:#ece6d2;color:#7a7355;border:1px dashed #b8ae8a;vertical-align:middle}.runtime-bucket-item.compact{flex-direction:row;align-items:center;gap:8px;height:24px;padding:0 2px;box-sizing:border-box;cursor:pointer}.runtime-bucket-item.compact:hover{background:#00000009}.compact-dot{width:7px;height:7px;padding:0;border-radius:50%;flex:0 0 7px}.compact-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;max-width:420px;font-size:10.5px}.branch-arrow-label{line-height:24px;min-width:42px}.changes-btn{border:1px solid var(--contour);background:var(--paper-raised);color:var(--ink);font-family:var(--font-mono);font-size:11px;font-weight:700;border-radius:8px;padding:6px 12px;cursor:pointer}.changes-btn.active,.changes-btn:hover{background:var(--ink);color:var(--paper)}.changes-btn-count{margin-left:7px;background:#c8892c;color:#fff;border-radius:9px;padding:0 6px;font-size:10px}.changes-panel{position:fixed;top:76px;right:12px;bottom:12px;width:440px;z-index:30;overflow-y:auto;background:var(--paper-raised);border:1px solid var(--contour);border-radius:12px;box-shadow:0 10px 40px #3c321438;padding:14px 16px 18px}.chg-empty{font-size:14px;font-weight:600;color:var(--ink);margin:14px 0 8px}.chg-stepper{display:flex;align-items:center;gap:10px;margin:8px 0 10px}.chg-stepper button{width:30px;height:30px;border-radius:8px;border:1px solid var(--contour);background:var(--paper);font-size:18px;cursor:pointer;color:var(--ink)}.chg-stepper button:disabled{opacity:.3;cursor:default}.chg-ver{flex:1;min-width:0;text-align:center}.chg-ver-title{font-family:var(--font-mono);font-weight:700;font-size:14px;color:var(--ink)}.chg-ver-sha{font-weight:500;color:var(--ink-faint);font-size:11px;margin-left:4px}.chg-dirty{color:#c8892c}.chg-ver-msg{font-size:12px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chg-ver-meta{font-size:10px;color:var(--ink-faint);font-family:var(--font-mono)}.chg-summary{display:flex;flex-direction:column;gap:3px;font-family:var(--font-mono);font-size:11px;color:var(--ink-dim);padding:8px 10px;background:var(--paper);border-radius:8px;margin-bottom:10px}.chg-summary b{color:var(--ink)}.chg-section{margin-top:8px}.chg-section-head{width:100%;display:flex;align-items:center;gap:8px;border:none;background:none;cursor:pointer;font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-dim);padding:4px 0;border-bottom:1px dashed var(--contour)}.chg-section-head .chg-count{background:var(--paper);border-radius:8px;padding:0 6px;color:var(--ink)}.chg-section-head .chg-caret{margin-left:auto}.chg-section-body{padding-top:4px}.chg-wf{padding:6px 8px;border-radius:8px;cursor:pointer;border:1px solid transparent}.chg-wf:hover{background:var(--paper);border-color:var(--contour)}.chg-wf.gone{cursor:default;opacity:.6}.chg-wf-name{font-size:12px;font-weight:650;color:var(--ink);display:flex;align-items:center;gap:6px;flex-wrap:wrap}.chg-wf-why{font-family:var(--font-mono);font-size:10px;color:var(--ink-dim);margin-top:2px;overflow-wrap:anywhere}.chg-svc{font-family:var(--font-mono);font-size:9px;color:var(--ink-faint);margin-left:auto}.chg-tag{font-family:var(--font-mono);font-size:8px;font-weight:700;text-transform:uppercase;border-radius:4px;padding:1px 5px;background:#ece6d2;color:#7a7355}.chg-tag-new{background:#dcefd9;color:#2f6a2a}.chg-tag-removed{background:#f3d9d4;color:#8a3326}.chg-tag-changed{background:#f3e6c4;color:#85601a}.chg-row{font-family:var(--font-mono);font-size:10.5px;color:var(--ink);padding:2px 4px;overflow-wrap:anywhere}.chg-row .chg-sign{display:inline-block;width:14px;font-weight:700}.chg-row.add .chg-sign{color:#2f6a2a}.chg-row.del .chg-sign{color:#8a3326}.chg-row.mod .chg-sign{color:#85601a}.chg-row.mv .chg-sign{color:#4a6480}.chg-file{display:block;margin-left:14px;color:var(--ink-faint);font-size:9.5px}.chg-more,.chg-info{font-size:10px;color:var(--ink-faint);padding:4px}.chg-info{margin-top:12px;border-top:1px dashed var(--contour);padding-top:8px}.change-badge{font-family:var(--font-mono);font-size:8px;font-weight:800;letter-spacing:.04em;border-radius:4px;padding:1px 5px;margin-left:6px;flex-shrink:0}.change-badge-new{background:#2f8a3a;color:#fff}.change-badge-modified{background:#c8892c;color:#fff}.canopy-topbar{flex-wrap:wrap;gap:8px 18px;align-items:flex-start;padding:10px 16px}.topbar-title{flex:1 1 280px;min-width:0}.topbar-controls{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px 10px;margin-left:auto;min-width:0}.tb-group{display:inline-flex;gap:4px}.tb-btn{font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--ink);background:var(--paper-raised);border:1px solid var(--contour);border-radius:7px;padding:6px 10px;min-width:30px;cursor:pointer;white-space:nowrap;transition:background .12s ease}.tb-btn:hover:not(:disabled){background:var(--paper-hover)}.tb-btn.active{background:var(--ink);color:var(--paper)}.tb-btn:disabled{opacity:.4;cursor:default}.changes-btn{white-space:nowrap}.picker-entry-btn{white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}@media(max-width:1100px){.canopy-stats{display:none}}@media(max-width:1000px){.canopy-hint{display:none}}.canopy-body{flex:1;display:flex;min-height:0;position:relative}.canopy-canvas{flex:1;position:relative;min-width:0}.side-panel{background:var(--paper-raised);border-right:1px solid var(--contour);min-height:0}.side-panel.docked{flex:0 0 260px;width:260px;position:relative;display:flex;flex-direction:column;transition:flex-basis .15s ease,width .15s ease}.side-panel.docked.collapsed{flex-basis:30px;width:30px}.side-scroll{overflow-y:auto;flex:1;padding:10px 12px 14px 10px;display:flex;flex-direction:column;gap:10px;min-height:0}.side-title{font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);padding-right:26px;display:flex;justify-content:space-between;align-items:center}.side-handle{position:absolute;top:6px;right:4px;width:22px;height:22px;border-radius:6px;border:1px solid var(--contour);background:var(--paper);color:var(--ink-dim);cursor:pointer;z-index:2;line-height:1;font-size:14px;padding:0}.side-panel.docked.collapsed .side-handle{right:3px}.side-handle:hover{background:var(--paper-hover)}.side-panel.drawer{position:absolute;top:0;bottom:0;left:0;width:270px;max-width:86%;z-index:26;display:flex;flex-direction:column;transform:translate(-104%);visibility:hidden;transition:transform .18s ease,visibility 0s linear .18s;box-shadow:var(--shadow-lg)}.side-panel.drawer.open{transform:none;visibility:visible;transition:transform .18s ease}.side-scrim{position:absolute;inset:0;z-index:25;background:#2823142e}.side-close{border:none;background:none;cursor:pointer;color:var(--ink-dim);font-size:13px;padding:0 2px}.side-groups{display:flex;flex-direction:column;gap:12px}.canopy-edge-path{fill:none;stroke:var(--ink-dim);stroke-width:2px;stroke-linecap:round;stroke-linejoin:round;transition:opacity .12s ease,stroke-width .12s ease}.react-flow__edge.cross-service .canopy-edge-path{stroke:var(--accent-cross);stroke-dasharray:7 5}.canopy-edge-path.hl{stroke:#c8892c;stroke-width:3px;stroke-dasharray:none}.canopy-edge-path.dim{opacity:.14}.v-scrollbar-track.minimap-open{bottom:196px}.h-scrollbar-track{position:absolute;left:10px;right:30px;bottom:4px;height:10px;border-radius:6px;background:#b4a88a29;z-index:5;cursor:pointer}.h-scrollbar-track.minimap-open{right:230px}.h-scrollbar-thumb{position:absolute;top:0;height:100%;border-radius:6px;background:var(--ink-faint);opacity:.55;cursor:grab;transition:opacity .15s ease,background .15s ease}.h-scrollbar-thumb:hover{opacity:.85;background:var(--ink-dim)}.h-scrollbar-thumb:active{cursor:grabbing;opacity:1}.react-flow__minimap{margin:14px 14px 18px}.step-expand.step-toggle{width:22px;height:22px;min-width:22px;padding:0;margin:0;display:inline-flex;align-items:center;justify-content:center;border-radius:6px;font-size:12px;line-height:1;border:1px solid var(--contour);background:var(--paper);color:var(--ink-dim);cursor:pointer;flex:0 0 22px}.step-expand.step-toggle:hover{background:var(--paper-hover);color:var(--ink)}.step-expand.step-toggle.open{background:var(--paper);color:var(--ink)}.chip-more{margin-left:auto;font-family:var(--font-mono);font-size:9px;font-weight:700;color:var(--accent-cross);background:var(--accent-cross-bg);border-radius:8px;padding:0 6px}.seg{display:inline-flex;border:1px solid var(--contour);border-radius:8px;overflow:hidden;width:100%}.seg button{flex:1;border:none;background:var(--paper);color:var(--ink-dim);font-family:var(--font-mono);font-size:10.5px;font-weight:700;padding:6px 8px;cursor:pointer}.seg button+button{border-left:1px solid var(--contour)}.seg button.on{background:var(--ink);color:var(--paper)}.step-node.selected-entry{border-color:var(--accent-cross);box-shadow:0 0 0 2px var(--accent-cross),var(--shadow-md)}.step-node.parent-card{border-style:dashed;background:var(--paper)}.caller-tag{font-family:var(--font-mono);font-size:8px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--accent-cross);background:var(--accent-cross-bg);border-radius:4px;padding:1px 5px}.excerpt-gap{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);padding:2px 4px;height:20px;line-height:16px}.branch-step-chip.call-target{background:var(--accent-cross-bg);border-color:var(--accent-cross);border-style:solid;box-shadow:0 0 0 1.5px var(--accent-cross)}.context-chip-node{display:flex;align-items:center;justify-content:center;width:100%;height:100%;border:1px dashed var(--accent-cross);background:var(--accent-cross-bg);box-shadow:none;padding:0 12px}.context-chip-text{font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--accent-cross)}.trigger-node{display:flex;align-items:center;gap:8px;width:100%;height:100%;padding:0 12px;background:var(--paper-raised);border:1px solid var(--accent-cross)}.trigger-node.inferred{border-style:dashed;color:var(--ink-dim)}.trigger-kind{font-family:var(--font-mono);font-size:8.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;background:var(--accent-cross-bg);color:var(--accent-cross);border-radius:4px;padding:2px 6px;flex-shrink:0}.trigger-label{font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}[data-lod=compact] .step-node .step-file,[data-lod=compact] .step-node .step-cat,[data-lod=compact] .step-node .source-icon,[data-lod=compact] .step-node .change-badge,[data-lod=compact] .step-node .caller-tag,[data-lod=compact] .step-node .chip-more,[data-lod=compact] .step-node .promoted-hint,[data-lod=compact] .step-node .excerpt-gap,[data-lod=compact] .step-node .branch-condition-text,[data-lod=compact] .step-node .branch-condition-kind,[data-lod=compact] .step-node .branch-arrow-label,[data-lod=compact] .step-node .branch-step-name,[data-lod=compact] .step-node .compact-label,[data-lod=compact] .step-node .compact-dot,[data-lod=compact] .step-node .runtime-bucket-header,[data-lod=compact] .step-node .runtime-tag{visibility:hidden}[data-lod=compact] .step-node .branch-step-chip{background:var(--accent-cross-bg);border-color:var(--accent-cross)}[data-lod=compact] .step-node .branch-step-chip.promoted{background:var(--accent-cross)}[data-lod=compact] .step-node .runtime-bucket-item.compact{background:var(--accent-unknown-bg);border-radius:4px}[data-lod=compact] .step-node .runtime-bucket-item.compact:has(.runtime-tag-runtime-dispatched){background:var(--accent-cross-bg)}[data-lod=compact] .step-node .runtime-bucket-item.compact:has(.runtime-tag-return-value){background:var(--accent-client-bg)}[data-lod=compact] .step-node .branch-diagram{background:var(--paper)}[data-lod=compact] .step-node .step-name,[data-lod=boxes] .step-node .step-name{transform:scale(var(--inv-zoom, 1));transform-origin:left center;max-width:calc(100% / var(--inv-zoom, 1));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;font-weight:700;position:relative;z-index:2}[data-lod=boxes] .step-node .step-body-contained,[data-lod=boxes] .step-node .step-file,[data-lod=boxes] .step-node .step-cat,[data-lod=boxes] .step-node .source-icon,[data-lod=boxes] .step-node .change-badge,[data-lod=boxes] .step-node .caller-tag,[data-lod=boxes] .step-node .step-badge,[data-lod=boxes] .step-node .step-toggle{visibility:hidden}[data-lod=boxes] .step-node{background:var(--paper-raised)}.back-btn{margin-right:4px;padding:4px 9px}.side-sub{font-size:11.5px;line-height:1.4;color:var(--ink-dim);margin:-4px 0 2px;padding-right:6px}.react-flow__edge.hop-solid .canopy-edge-path{stroke-dasharray:none}.react-flow__edge.hop-dashed .canopy-edge-path{stroke-dasharray:7 5}.react-flow__edge.hop-solid .canopy-edge-path:not(.hl),.react-flow__edge.hop-dashed .canopy-edge-path:not(.hl){stroke:var(--accent-cross);stroke-width:2.2px}.hop-node{width:100%;height:100%;padding:7px 10px 8px;display:flex;flex-direction:column;gap:2px;cursor:pointer;background:var(--accent-cross-bg);border:1.5px solid var(--accent-cross);overflow:hidden}.hop-node.hop-dashed{border-style:dashed}.hop-top{display:flex;align-items:center;justify-content:space-between;gap:6px}.hop-svc{font-family:var(--font-mono);font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--accent-cross);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.hop-tag{font-family:var(--font-mono);font-size:8px;font-weight:700;text-transform:uppercase;border-radius:4px;padding:1px 5px;background:var(--paper-raised);color:var(--ink-dim);flex-shrink:0}.hop-tag-exact,.hop-tag-resolved{color:var(--accent-client)}.hop-call{font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hop-handler{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-height:12px}.hop-view{align-self:flex-start;margin-top:auto;border:1px solid var(--accent-cross);background:var(--paper-raised);color:var(--accent-cross);border-radius:6px;font-size:10px;font-weight:600;padding:2px 8px;cursor:pointer}.hop-view:hover{background:var(--paper-hover)}.called-from-chip{display:block;max-width:100%;margin-top:4px;border:1px solid var(--accent-cross);background:var(--accent-cross-bg);color:var(--accent-cross);border-radius:999px;font-family:var(--font-mono);font-size:9.5px;font-weight:600;padding:2px 9px;cursor:pointer;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.called-from-chip:hover{background:var(--paper-hover)}.hop-chooser{position:fixed;z-index:200;width:284px;background:var(--paper-raised);border:1px solid var(--contour);border-radius:10px;box-shadow:var(--shadow-lg);padding:6px;display:flex;flex-direction:column;gap:2px}.hop-chooser-label{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-faint);padding:2px 6px 4px}.hop-chooser-item{display:flex;flex-direction:column;align-items:flex-start;text-align:left;border:none;background:none;border-radius:6px;padding:5px 8px;cursor:pointer}.hop-chooser-item:hover{background:var(--paper-hover)}.hop-chooser-title{font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--ink);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hop-chooser-sub{font-size:10px;color:var(--ink-dim)}.hop-chooser-more{font-size:10.5px;color:var(--ink-dim);padding:3px 8px;font-style:italic}[data-lod=compact] .hop-node .hop-handler,[data-lod=compact] .hop-node .hop-view,[data-lod=compact] .hop-node .hop-tag,[data-lod=boxes] .hop-node .hop-handler,[data-lod=boxes] .hop-node .hop-view,[data-lod=boxes] .hop-node .hop-tag,[data-lod=boxes] .hop-node .hop-call,[data-lod=boxes] .called-from-chip{visibility:hidden}.canopy-topbar{flex-wrap:nowrap;align-items:center;gap:10px 14px;padding:8px 16px}.canopy-crumbs{flex:1 1 auto;min-width:0;overflow:hidden;flex-wrap:nowrap;white-space:nowrap}button.crumb{background:none;border:none;font:inherit;font-size:13px;font-weight:500}.crumb{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:0 1 auto}.crumb.current{max-width:320px}.topbar-controls{flex:0 0 auto;flex-wrap:nowrap;gap:6px 8px}.icon-btn{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:32px;height:30px;padding:0 8px;border:1px solid var(--contour);border-radius:7px;background:var(--paper-raised);color:var(--ink);cursor:pointer;transition:background .12s ease}.icon-btn:hover{background:var(--paper-hover)}.icon-btn.active{background:var(--ink);color:var(--paper)}.icon-btn .changes-btn-count{margin-left:0}button:focus-visible,[role=button]:focus-visible,.search-input:focus-visible,.code-box:focus-visible,a:focus-visible{outline:2px solid var(--accent-cross);outline-offset:2px}.search{position:relative;flex:0 1 340px;min-width:150px}.search-input{width:100%;box-sizing:border-box;height:30px;padding:0 10px;border:1px solid var(--contour);border-radius:7px;background:var(--paper);color:var(--ink);font:12px var(--font-ui)}.search-input::placeholder{color:var(--ink-faint)}.search-results{position:absolute;top:34px;left:0;width:min(520px,calc(100vw - 32px));max-height:60vh;overflow-y:auto;z-index:45;background:var(--paper-raised);border:1px solid var(--contour);border-radius:10px;box-shadow:var(--shadow-lg);padding:6px}.search-group-title{font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);padding:6px 8px 3px}.search-item{display:flex;align-items:baseline;gap:10px;padding:5px 8px;border-radius:6px;cursor:pointer}.search-item.active,.search-item:hover{background:var(--paper-hover)}.search-name{font-size:12.5px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto;max-width:55%}.search-meta{font-family:var(--font-mono);font-size:10px;color:var(--ink-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;margin-left:auto}.search-empty{padding:10px;font-size:12px;color:var(--ink-dim)}.modebar{display:flex;align-items:center;gap:12px;padding:6px 16px;background:var(--paper-raised);border-bottom:1px solid var(--contour)}.mode-toggle{width:auto}.mode-toggle button{flex:none;padding:5px 14px}.disclaimer-bar{padding:5px 16px;font-size:12px;line-height:1.4;color:var(--ink-dim);background:var(--accent-cross-bg);border-bottom:1px solid var(--contour)}.disclaimer-bar a{color:var(--accent-cross);font-weight:700}button.context-chip{display:block;width:100%;text-align:left;font-family:inherit}.context-chip.current{background:var(--ink);color:var(--paper);border-color:var(--ink)}.context-chip.current:hover{background:var(--ink)}.code-overlay{flex:0 0 440px;width:440px;min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--paper-raised);border-left:1px solid var(--contour);box-shadow:-8px 0 26px #2823141a;z-index:22}.ov-head{display:flex;align-items:flex-start;gap:8px;padding:12px 14px 8px;border-bottom:1px solid var(--contour)}.ov-title{flex:1;min-width:0}.ov-name{font-size:15px;font-weight:700;color:var(--ink);word-break:break-word}.ov-file{font-family:var(--font-mono);font-size:11px;color:var(--ink-dim);margin-top:2px;word-break:break-all}.ov-close{border:none;background:none;cursor:pointer;font-size:15px;color:var(--ink-faint);padding:4px 6px;border-radius:6px}.ov-close:hover{color:var(--ink);background:var(--paper-hover)}.ov-scroll{flex:1;min-height:0;overflow-y:auto;padding:8px 14px 18px}.ov-section{margin-top:12px}.ov-label{font-size:9.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--ink-faint);font-weight:700;margin-bottom:5px}.ov-count{margin-left:4px;background:var(--paper);border-radius:8px;padding:0 6px;color:var(--ink-dim)}.ov-note{font-size:12px;color:var(--ink-dim);line-height:1.5}.ov-warn{color:#8a3326}.ov-removed-note{display:inline-block;font-weight:700;color:#8a3326;background:#f3d9d4;border-radius:6px;padding:3px 9px;margin-bottom:8px}.ov-meta{font-family:var(--font-mono);font-size:11px;color:var(--ink-dim);word-break:break-all;line-height:1.6}.ov-meta b{color:var(--ink);margin-right:6px}.ov-edge-ends{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:4px}.ov-arrow{color:var(--ink-faint)}.code-box{max-height:46vh;overflow:auto;background:var(--paper);border:1px solid var(--contour);border-radius:8px;font-family:var(--font-mono);font-size:11.5px;line-height:1.55;position:relative}.code-line{display:flex;white-space:pre}.code-no{flex:0 0 46px;text-align:right;padding-right:10px;color:var(--ink-faint);user-select:none;background:var(--paper-raised);position:sticky;left:0}.code-text{padding-right:12px;color:var(--ink)}.code-line.in-range{background:#c8892c29}.code-line.in-range .code-no{color:var(--ink);font-weight:700;background:#c8892c38}.ov-item{display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;font:inherit;font-size:12px;color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:6px;padding:5px 8px;margin-top:4px;cursor:pointer}.ov-item:hover{background:var(--paper-hover)}.ov-item.dashed{border-style:dashed}.ov-i-name{font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ov-i-meta{margin-left:auto;font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:55%}.chg-group-title{font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);margin:12px 0 2px}.chg-row.clickable{cursor:pointer;border-radius:6px;padding:3px 6px}.chg-row.clickable:hover{background:var(--paper)}.chg-row.chg-open-wf{color:var(--accent-cross);font-weight:700}.chg-section-head .chg-tag{margin-left:2px}.has-overlay .changes-panel{right:452px}@media(max-width:1099px){.code-overlay{position:fixed;left:0;right:0;bottom:0;width:auto;flex:none;height:58vh;border-left:none;border-top:1px solid var(--contour);border-radius:12px 12px 0 0;box-shadow:0 -10px 30px #2823142e;z-index:36}.code-box{max-height:30vh}.has-overlay .changes-panel{right:12px}.changes-panel{z-index:40}}@media(max-width:820px){.canopy-topbar{flex-wrap:wrap}.canopy-crumbs{flex:1 1 100%}.search{flex:1 1 160px}.topbar-controls{flex-wrap:wrap}}@media(max-width:480px){.changes-panel{left:8px;right:8px;width:auto}.search-results{width:calc(100vw - 16px)}}.changes-panel{top:calc(var(--chrome-h, 68px) + 8px)}.search-flag{font-family:var(--font-mono);font-size:8.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--ink-dim);border:1px solid var(--contour);border-radius:4px;padding:0 5px;margin-left:8px;white-space:nowrap;flex-shrink:0}:root{--diff-add: #dcefd9;--diff-del: #f3d9d4;--diff-paper: var(--paper);--diff-ink: var(--ink);--diff-gutter: var(--ink-faint);--diff-add-sign: #2f6a2a;--diff-del-sign: #8a3326}.ov-tabs{display:flex;gap:4px;border-bottom:1px solid var(--contour);margin:2px 0 10px}.ov-tab{border:1px solid transparent;border-bottom:none;background:none;color:var(--ink-dim);font-family:var(--font-mono);font-size:11px;font-weight:700;padding:5px 12px;border-radius:8px 8px 0 0;cursor:pointer}.ov-tab.on{background:var(--paper-raised);color:var(--ink);border-color:var(--contour);margin-bottom:-1px}.ov-tab:focus-visible{outline:2px solid var(--accent-cross);outline-offset:1px}.diff-head{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px;font-family:var(--font-mono);font-size:11px}.diff-stat{font-weight:700;color:var(--ink)}.diff-flag{border:1px solid var(--contour);border-radius:4px;padding:0 6px;font-size:10px;font-weight:700;color:var(--ink-dim)}.diff-moved{color:var(--ink-dim);overflow-wrap:anywhere;font-size:10px}.diff-box{max-height:52vh;overflow:auto;background:var(--diff-paper);color:var(--diff-ink);border:1px solid var(--contour);border-radius:8px;font-family:var(--font-mono);font-size:11.5px;line-height:1.5;padding:4px 0}.diff-row{display:flex;align-items:baseline;min-width:max-content;width:100%;white-space:pre}.diff-row.add{background:var(--diff-add)}.diff-row.del{background:var(--diff-del)}.diff-row.hunk{background:transparent;color:var(--diff-gutter);font-size:10.5px;padding:3px 8px;border-top:1px dashed var(--contour);border-bottom:1px dashed var(--contour);margin:4px 0}.diff-row.hunk:first-child{margin-top:0;border-top:none}.diff-no{flex:none;width:38px;text-align:right;padding-right:6px;color:var(--diff-gutter);user-select:none;font-size:10.5px}.diff-sign{flex:none;width:16px;text-align:center;font-weight:800;user-select:none}.diff-row.add .diff-sign{color:var(--diff-add-sign)}.diff-row.del .diff-sign{color:var(--diff-del-sign)}.diff-text{flex:1;padding-right:10px}.diff-row.diff-hit{outline:2px solid var(--accent-cross);outline-offset:-2px}.diff-hit-tag,.code-line .diff-hit-tag{flex:none;align-self:center;margin:0 8px 0 auto;font-family:var(--font-mono);font-size:8.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--accent-cross);background:var(--accent-cross-bg);border-radius:4px;padding:0 5px}.code-line.code-hit{outline:2px solid var(--accent-cross);outline-offset:-2px;display:flex}.chg-stat{margin-left:8px;font-size:9.5px;font-weight:700;color:var(--ink-dim);white-space:nowrap}.chg-stat.pending{min-width:1px}.rt-band{width:100%;height:100%;border:1px solid;border-radius:14px;opacity:.55;pointer-events:none}.rt-band-head{display:flex;flex-direction:column;gap:1px;padding:10px 14px}.rt-band-name{font-family:var(--font-mono);font-size:13px;font-weight:700}.rt-band-kind{font-family:var(--font-mono);font-size:9.5px;font-weight:600;opacity:.85}.rt-node{width:100%;height:100%;background:var(--paper-raised);border:1px solid var(--contour);border-left:4px solid var(--accent-cross);border-radius:8px;box-shadow:var(--shadow-sm);padding:5px 10px;cursor:pointer;overflow:hidden}.rt-node:hover{box-shadow:var(--shadow-md)}.rt-node.start{border-color:var(--ink);border-left-width:4px}.rt-name{font-family:var(--font-mono);font-size:11.5px;font-weight:700;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.rt-file{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}.rt-more{font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--ink-dim);background:var(--paper-raised);border:1px dashed var(--contour);border-radius:8px;padding:6px 10px;white-space:nowrap}.react-flow__edge.rt-edge path.react-flow__edge-path{stroke:#6b664f;stroke-width:1.8;fill:none}.react-flow__edge.rt-cross path.react-flow__edge-path{stroke:#4a6480;stroke-width:2.2}.react-flow__edge.rt-dashed path.react-flow__edge-path{stroke-dasharray:8 6}.react-flow__edge.rt-solid path.react-flow__edge-path{stroke-dasharray:none}.project-name{flex:none;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--ink-faint);padding-right:8px;margin-right:3px;border-right:1px solid var(--contour)}.rt-progress{position:absolute;left:50%;top:10px;transform:translate(-50%);z-index:5;font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--ink-dim);background:var(--paper-raised);border:1px solid var(--contour);border-radius:999px;padding:3px 12px;box-shadow:var(--shadow-sm);pointer-events:none}.rt-loop{float:right;font-family:var(--font-mono);font-size:10px;font-weight:800;color:var(--accent-cross)}.chg-modes{display:flex;margin:4px 0 10px;border:1px solid var(--contour);border-radius:8px;overflow:hidden}.chg-modes button{flex:1;font-size:10.5px;padding:5px 6px}.chg-since{background:var(--paper);border:1px solid var(--contour);border-radius:8px;padding:8px 10px;margin-bottom:10px}.chg-since-head{font-family:var(--font-mono);font-size:11.5px;font-weight:700;color:var(--ink)}.chg-since-note{font-size:11px;color:var(--ink-dim);margin-top:4px}.react-flow__edge.rt-lite path.react-flow__edge-path,.rt-line{stroke:#b9b196;stroke-width:1.4;fill:none}.search-item-rich{flex-direction:column;align-items:stretch;gap:3px}.search-line1,.search-line2{display:flex;align-items:baseline;gap:8px;min-width:0}.search-line2{flex-wrap:wrap}.search-method{font-family:var(--font-mono);font-size:10.5px;color:var(--accent-cross)}.search-chip{font-family:var(--font-mono);font-size:8.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--ink-dim);background:var(--paper-hover);border-radius:4px;padding:0 5px;white-space:nowrap;flex-shrink:0}.search-appears{font-size:10.5px;color:var(--ink-dim)}.search-switch{display:inline-flex;flex-wrap:wrap;gap:4px;align-items:baseline}.search-switch-btn{font:10.5px var(--font-ui);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:5px;padding:1px 6px;cursor:pointer;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.search-switch-btn:hover{background:var(--paper-hover)}.search-code-text{font-family:var(--font-mono);font-size:11px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.start-head,.start-more,.start-item{display:flex;align-items:baseline;gap:8px;width:100%;box-sizing:border-box;text-align:left;background:none;border:0;color:var(--ink);font:inherit;cursor:pointer;border-radius:6px;padding:5px 8px}.start-head:hover,.start-more:hover,.start-item:hover{background:var(--paper-hover)}.start-caret{width:10px;color:var(--ink-faint)}.start-item{padding-left:26px}.start-more{padding-left:26px;font-size:11px;color:var(--ink-dim)}.canopy-topbar:focus-within{z-index:50}.search-ask{display:block;width:100%;box-sizing:border-box;text-align:left;background:none;border:0;border-top:1px solid var(--contour);margin-top:6px;padding:8px 8px 4px;font:12px var(--font-ui);color:var(--accent-cross);cursor:pointer}.search-ask:hover{text-decoration:underline}.ask-ai{position:absolute;top:34px;left:0;width:min(520px,calc(100vw - 32px));z-index:46;box-sizing:border-box;background:var(--paper-raised);border:1px solid var(--contour);border-radius:10px;box-shadow:var(--shadow-lg);padding:10px 12px 12px;display:flex;flex-direction:column;gap:8px}.ask-ai-head{display:flex;align-items:center;justify-content:space-between;font-size:12.5px}.ask-ai-text{width:100%;box-sizing:border-box;resize:vertical;font:12.5px var(--font-ui);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:7px;padding:7px 9px}.ask-ai-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.ask-ai-primary{font:600 12px var(--font-ui);color:#fff;background:var(--accent-cross);border:0;border-radius:7px;padding:7px 14px;cursor:pointer}.ask-ai-secondary{display:inline-block;font:12px var(--font-ui);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:7px;padding:5px 10px;cursor:pointer;text-decoration:none}.ask-ai-secondary:hover{background:var(--paper-hover)}.ask-ai-note,.ask-ai-privacy{font-size:10.5px;color:var(--ink-dim)}.ask-ai-preview summary{font-size:11px;color:var(--ink-dim);cursor:pointer}.ask-ai-preview pre{max-height:180px;overflow:auto;white-space:pre-wrap;font:10.5px var(--font-mono);color:var(--ink-dim);margin:6px 0 0}@media(max-width:480px){.ask-ai{width:calc(100vw - 16px)}}.modebar-right{margin-left:auto;display:flex;align-items:center;gap:14px}.fb-link{font:11.5px var(--font-ui);color:var(--ink-dim);text-decoration:none;border-bottom:1px dotted var(--ink-faint)}.fb-link:hover{color:var(--ink);border-bottom-color:var(--ink)}.ai-open-btn{display:inline-flex;align-items:center;gap:6px;font:600 12px var(--font-ui);color:var(--paper-raised);background:var(--accent-cross);border:1px solid var(--accent-cross);border-radius:8px;padding:5px 12px;cursor:pointer}.ai-open-btn:hover{filter:brightness(1.08)}.ai-open-btn.on{background:var(--ink);border-color:var(--ink)}.ai-open-btn:focus-visible,.ai-btn:focus-visible,.ai-primary:focus-visible,.ai-row:focus-visible,.ai-link:focus-visible{outline:2px solid var(--accent-cross);outline-offset:2px}.ai-panel{position:fixed;right:16px;width:min(440px,calc(100vw - 32px));max-height:calc(100vh - 120px);z-index:48;display:flex;flex-direction:column;background:var(--paper-raised);color:var(--ink);border:1px solid var(--contour);border-radius:12px;box-shadow:var(--shadow-lg);font-family:var(--font-ui)}.ai-head{display:flex;align-items:center;gap:10px;padding:10px 12px 8px;font-size:13px;border-bottom:1px solid var(--contour)}.ai-sub{font-size:10.5px;color:var(--ink-dim);font-family:var(--font-mono)}.ai-spacer{flex:1}.ai-link{background:none;border:0;padding:2px 0;font:11.5px var(--font-ui);color:var(--accent-cross);cursor:pointer;text-decoration:underline;text-underline-offset:2px}.ai-link:disabled{color:var(--ink-faint);cursor:default;text-decoration:none}.ai-list{flex:1 1 auto;min-height:120px;overflow:auto;padding:10px 12px;display:flex;flex-direction:column;gap:14px}.ai-empty{font-size:12.5px;color:var(--ink-dim);line-height:1.5;padding:6px 2px}.ai-entry{display:flex;flex-direction:column;gap:8px}.ai-q{align-self:flex-end;max-width:92%;background:var(--paper-hover);border:1px solid var(--contour);border-radius:10px 10px 2px;padding:6px 10px;font-size:12.5px;white-space:pre-wrap;overflow-wrap:anywhere}.ai-status{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--ink-dim)}.ai-dots{width:8px;height:8px;border-radius:50%;background:var(--accent-cross);animation:ai-pulse 1s ease-in-out infinite}@keyframes ai-pulse{0%,to{opacity:.25}50%{opacity:1}}@media(prefers-reduced-motion:reduce){.ai-dots{animation:none}}.ai-summary{font-size:12.5px;color:var(--ink)}.ai-note,.ai-subtle{font-size:11px;color:var(--ink-dim)}.ai-results{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.ai-row{width:100%;text-align:left;display:flex;flex-direction:column;gap:3px;background:var(--paper);border:1px solid var(--contour);border-radius:8px;padding:7px 10px;cursor:pointer;font-family:var(--font-ui);color:var(--ink)}.ai-row:hover{background:var(--paper-hover)}.ai-row.active{border-color:var(--accent-cross);box-shadow:inset 3px 0 0 var(--accent-cross)}.ai-row-top{display:flex;align-items:baseline;gap:8px}.ai-row-label{font-size:12.5px;font-weight:600;flex:1;min-width:0;overflow-wrap:anywhere}.ai-row-reason{font-size:11.5px;color:var(--ink-dim);line-height:1.4}.ai-row-where{font:10px var(--font-mono);color:var(--ink-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-badge{font:600 9.5px var(--font-mono);letter-spacing:.04em;text-transform:uppercase;padding:1px 6px;border-radius:99px;border:1px solid var(--contour);color:var(--ink-dim);background:var(--paper-raised);flex:none}.ai-badge-node{color:var(--accent-client);background:var(--accent-client-bg);border-color:transparent}.ai-badge-workflow{color:var(--accent-server);background:var(--accent-server-bg);border-color:transparent}.ai-badge-search{color:var(--accent-cross);background:var(--accent-cross-bg);border-color:transparent}.ai-entry-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.ai-btn{font:12px var(--font-ui);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:7px;padding:5px 11px;cursor:pointer}.ai-btn:hover:not(:disabled){background:var(--paper-hover)}.ai-btn:disabled{color:var(--ink-faint);cursor:default}.ai-primary{font:600 12px var(--font-ui);color:var(--paper-raised);background:var(--accent-cross);border:0;border-radius:7px;padding:6px 16px;cursor:pointer}.ai-primary:disabled{background:var(--contour);color:var(--ink-faint);cursor:default}.ai-problem{display:flex;flex-direction:column;align-items:flex-start;gap:6px;font-size:12px;line-height:1.45;background:var(--paper);border:1px solid var(--contour);border-left:3px solid var(--accent-server);border-radius:8px;padding:8px 10px;overflow-wrap:anywhere}.ai-problem span{color:var(--ink-dim)}.ai-install{color:var(--accent-cross);font-weight:600}.ai-small{font-size:11px}.ai-compose{border-top:1px solid var(--contour);padding:8px 12px 10px;display:flex;flex-direction:column;gap:8px}.ai-text{width:100%;resize:vertical;min-height:44px;max-height:160px;font:12.5px var(--font-ui);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:8px;padding:7px 9px}.ai-row-actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.ai-follow{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;color:var(--ink-dim);cursor:pointer}.ai-foot{font-size:10.5px;color:var(--ink-dim)}.react-flow__node.ai-hit{outline:3px solid var(--accent-cross);outline-offset:3px;border-radius:10px}@media(max-width:700px){.ai-panel{right:8px;left:8px;width:auto}.modebar{flex-wrap:wrap}}.ai-answer{font-size:12.5px;line-height:1.55;color:var(--ink);overflow-wrap:anywhere}.ai-answer p{margin:0 0 8px}.ai-answer ul,.ai-answer ol{margin:0 0 8px;padding-left:20px}.ai-answer li{margin:2px 0}.ai-code{font:11.5px var(--font-mono);background:var(--paper-hover);border-radius:4px;padding:0 4px}.ai-inline-link{background:none;border:0;padding:0;font:inherit;color:var(--accent-cross);text-decoration:underline;text-underline-offset:2px;cursor:pointer}.ai-inline-link.code{font:11.5px var(--font-mono);background:var(--accent-cross-bg);border-radius:4px;padding:0 4px;text-decoration:none}.ai-inline-link.bold{font-weight:700}.ai-topline{align-self:flex-start;background:none;border:0;padding:0;font:11px var(--font-ui);color:var(--accent-cross);cursor:pointer;text-decoration:underline;text-underline-offset:2px}.ai-places{display:flex;flex-direction:column;gap:8px;border-top:1px dashed var(--contour);padding-top:8px}.ai-places-title{font:700 10.5px var(--font-mono);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-dim)}.ai-allow-row{display:flex;gap:8px}.ai-using{font-size:11px;color:var(--ink-dim)}.ai-change{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.ai-paste{display:inline-flex;gap:6px;flex:1 1 100%}.ai-text-input{flex:1;min-width:0;font:11.5px var(--font-mono);color:var(--ink);background:var(--paper);border:1px solid var(--contour);border-radius:7px;padding:5px 8px}.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #555;--xy-background-pattern-lines-color-default: #333;--xy-background-pattern-cross-color-default: #333;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;touch-action:none}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}
2
2
  /*# sourceMappingURL=bundle.css.map */