diff --git a/README.md b/README.md
index 9d85bce5..dddd0b7b 100644
--- a/README.md
+++ b/README.md
@@ -509,6 +509,17 @@ name for a *controller action*. The two are unrelated and neither affects the ot
"an Action" unqualified means this pattern in ordinary Laravel usage, the interface gives the
unqualified label to `action_class` and calls `action` a **Controller action**.
+### Reachability
+Every other tab is grown forward from one entry point, so a gap in the graph is invisible from inside it — measured on one application, the graph knew 45 of its 211 event classes and 27 of its 113 job classes, and no screen said so.
+The **Reachability** tab is the inverse view: an inventory of every entry point (routes, console commands, scheduled entries, broadcast channels, queued listeners, Filament panels/resources/pages), and the classes under `source_paths` that no entry point's traced call chain arrives at, grouped by kind so "17 jobs nothing dispatches" is answerable at a glance.
+**It is not a dead-code report.** "Nothing reaches this from a traced entry point" is a statement about the tracer, not about whether the code runs: a class resolved out of the container, fronted by a facade, named as a string in config, or built by reflection is alive and still lands on the list. Every reference Brain *did* find — container binding, facade, `config/`, inherited by a reached class, named as a class-string elsewhere — is shown next to the class, so the two cases can be told apart. Service providers and exceptions, which Brain has no call edge for at all, are filed in a section of their own rather than mixed in.
+```php
+// config/laravel-brain.php — on by default
+'reachability' => [
+ 'enabled' => false, // or LARAVEL_BRAIN_REACHABILITY_ENABLED=false
+],
+```
+
## Graph Node Types
| Node | Accent Color | Represents |
@@ -530,6 +541,8 @@ unqualified label to `action_class` and calls `action` a **Controller action**.
| Filament Page Method | Pink `#E879F9` | Method on a Filament page |
| Filament Widget | Cyan `#06B6D4` | Filament widget class |
| Filament Relation Manager | Teal `#0891B2` | Filament relation manager |
+| Entry Point | Cyan `#22D3EE` | A root on the Reachability tab |
+| Not Reached | Grey `#94A3B8` | A class no entry point's chain arrives at. Grey on purpose — it is a question, not a verdict |
> **Note:** Command, Schedule, Channel, and Repository nodes are discovered and added to the graph but use the closest matching accent color from their parent type.
diff --git a/config/laravel-brain.php b/config/laravel-brain.php
index 0e3b625c..01b601cd 100644
--- a/config/laravel-brain.php
+++ b/config/laravel-brain.php
@@ -624,6 +624,44 @@
'src',
],
+ // -------------------------------------------------------------------------
+ // Reachability
+ // -------------------------------------------------------------------------
+ // The "Reachability" tab: an inventory of every entry point the application can be
+ // entered from (routes, console commands, scheduled entries, broadcast channels, queued
+ // listeners, Filament panels/resources/pages), and the classes under `source_paths` that
+ // no entry point's traced call chain arrives at.
+ //
+ // Every other tab is grown forward from one entry point, so a gap in the graph is
+ // invisible from inside it. This is the inverse view — what exists, and what nothing
+ // reaches.
+ //
+ // Read what it reports carefully: "nothing reaches this from a traced entry point" is a
+ // statement about the tracer, not about whether the code runs. A class resolved out of
+ // the container, fronted by a facade, named as a string in config, or built by reflection
+ // is alive and still lands on the list, which is why every reference Brain *did* find is
+ // shown next to the class. It is not a dead-code report and must not be read as one.
+ //
+ // Cost: one extra parse pass over `source_paths` and over `config/`, on top of the pass a
+ // scan already makes. Turn it off if you do not want it.
+ //
+ // Override via the LARAVEL_BRAIN_REACHABILITY_ENABLED env variable.
+ //
+ 'reachability' => [
+ // Off by default, which is the one setting here that is a judgement rather than a
+ // fact. The pass opens every declared class, and what that costs depends entirely on
+ // how much of the codebase the rest of the scan already parsed:
+ //
+ // application A build parses 746 files, inventory 4,416 scan ×3.0
+ // application B 7,546 source files, nearly all already parsed +2% (within noise)
+ //
+ // So the worst case is real and the typical case may be nothing. What does not vary is
+ // the shape of the answer: on a large modular application 79-90% of declared classes
+ // come back unreached, and a list that long is read once and then ignored. Turn it on
+ // when you are hunting for what nothing reaches; leave it off for a scan you run often.
+ 'enabled' => env('LARAVEL_BRAIN_REACHABILITY_ENABLED', false),
+ ],
+
// -------------------------------------------------------------------------
// Watch Paths
// -------------------------------------------------------------------------
diff --git a/docs/how-it-works.md b/docs/how-it-works.md
index b91bc6a4..59aa3627 100644
--- a/docs/how-it-works.md
+++ b/docs/how-it-works.md
@@ -315,6 +315,42 @@ Agents are ordinary application classes, so the scan follows `source_paths` by d
`enabled` is a second, independent switch: it answers "this application uses the SDK and I still do not want it on the graph", which the string prefilter above cannot. Turning it off skips the pass before the directory scan, so nothing is read and nothing is parsed.
+
+## Reachability
+
+Every other tab is grown forward from one entry point, which means a gap in the graph is invisible from inside it. Measured on one application, the graph knew 45 of its 211 event classes and 27 of its 113 job classes, and no screen said so.
+
+The **Reachability** tab is the inverse view. It has three sections:
+
+1. **Entry points**, grouped by kind — routes, console commands, scheduled entries, broadcast channels, queued listeners, Filament panels/resources/pages. Nothing in the application is reachable except through one of these, so their inventory is the denominator for everything below.
+2. **Nothing reaches these from an entry point**, grouped by kind, largest group first — so "17 jobs nothing dispatches" is answerable at a glance.
+3. **Outside what the tracer follows** — service providers and exceptions, kinds Brain has no call edge for at all. The framework boots a provider and an exception is thrown rather than called, so their absence from the graph is the expected outcome and says nothing either way. They are kept apart so they do not bury the section above.
+
+::: warning This is not a dead-code report
+"Nothing reaches this from a traced entry point" is a statement about the tracer, not about whether the code runs. A class resolved out of the container, fronted by a facade, named as a string in config, or built by reflection is alive and will still land on the list.
+
+Every reference Brain *did* find is shown next to the class, so you can tell the two apart:
+
+| Shown as | Means |
+|----------|-------|
+| bound in the container | named as the abstract or the concrete of a `bind()` / `singleton()` in a provider |
+| reached through a facade | is a facade, or the class one resolves to |
+| named in config/ | appears as `Foo::class` or a quoted FQCN under `config/` |
+| inherited by a class that is reached | a class the tracer did reach extends it, implements it, or uses it as a trait |
+| named as a class-string elsewhere | appears as `Foo::class` or a quoted FQCN in another source file |
+
+A class with none of these is the one worth opening first — and still worth opening rather than deleting.
+:::
+
+The tab reads the classes declared under [`source_paths`](#source-paths-and-watch-mode) and costs one extra parse pass over them plus `config/`. Turn it off with:
+
+```php
+// config/laravel-brain.php
+'reachability' => [
+ 'enabled' => false, // or LARAVEL_BRAIN_REACHABILITY_ENABLED=false
+],
+```
+
## Graph Node Types
| Node | Accent Color | Represents |
@@ -335,6 +371,8 @@ Agents are ordinary application classes, so the scan follows `source_paths` by d
| Filament Page Method | Pink `#E879F9` | Method on a Filament page |
| Filament Widget | Cyan `#06B6D4` | Filament widget class |
| Filament Relation Manager | Teal `#0891B2` | Filament relation manager |
+| Entry Point | Cyan `#22D3EE` | A root on the Reachability tab |
+| Not Reached | Grey `#94A3B8` | A class no entry point's chain arrives at. Grey on purpose — it is a question, not a verdict |
::: tip Note
Command, Schedule, Channel, and Repository nodes are discovered and added to the graph but use the closest matching accent color from their parent type.
diff --git a/frontend/src/App.css b/frontend/src/App.css
index b2ff67c0..dc8f319d 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -582,6 +582,16 @@ body::before {
.sidebar-section { padding: 12px 16px; border-top: 1px solid var(--border); }
.sidebar-section h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--dim); margin-bottom: 8px; }
+/* Reachability tab — the caveat that has to travel with every unreached class. */
+.reachability-note { font-size: 12px; line-height: 1.5; color: var(--dim); margin-bottom: 8px; }
+.reachability-note:last-child { margin-bottom: 0; }
+.reachability-references { list-style: none; margin: 0; padding: 0; font-size: 12px; }
+.reachability-references li {
+ padding: 4px 8px; margin-bottom: 4px; border-radius: 4px;
+ border: 1px solid var(--border); background: var(--panel);
+}
+.reachability-references li:last-child { margin-bottom: 0; }
+
.sidebar-structure-list { list-style: none; margin: 0; padding: 0; font-size: 12px; }
.sidebar-structure-item {
display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index a7157f7d..b216a972 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -19,6 +19,10 @@ const ALL_TYPES: GraphNode['type'][] = [
'view', 'mail', 'notification', 'enum', 'interface', 'trait', 'abstract_class', 'service_provider',
'ai_agent', 'ai_tool',
'filament_panel', 'filament_resource', 'filament_page', 'filament_page_method', 'filament_widget', 'filament_relation_manager',
+ // The inventory's own kinds. Membership here is what makes a type filterable at all, and
+ // `edgeVisible` reads it too — a type left out has no visible edges, so folding a group
+ // finds no children to fold.
+ 'entry_point', 'entry_point_group', 'unreached_class', 'unreached_group',
]
/**
diff --git a/frontend/src/components/FilterPanel.tsx b/frontend/src/components/FilterPanel.tsx
index c8c46e2f..213e3827 100644
--- a/frontend/src/components/FilterPanel.tsx
+++ b/frontend/src/components/FilterPanel.tsx
@@ -38,6 +38,10 @@ const TYPE_LABELS: Partial> = {
filament_page_method: 'F. Methods',
filament_widget: 'F. Widgets',
filament_relation_manager: 'F. Relations',
+ entry_point: 'Entry points',
+ entry_point_group: 'Entry groups',
+ unreached_class: 'Not reached',
+ unreached_group: 'Unreached groups',
}
// Stable order matching App.tsx ALL_TYPES
@@ -48,6 +52,7 @@ const ORDER: GraphNode['type'][] = [
'service_provider', 'facade', 'ai_agent', 'ai_tool',
'filament_panel', 'filament_resource', 'filament_page',
'filament_page_method', 'filament_widget', 'filament_relation_manager',
+ 'entry_point', 'entry_point_group', 'unreached_class', 'unreached_group',
]
/**
diff --git a/frontend/src/components/GraphView.tsx b/frontend/src/components/GraphView.tsx
index 73caf08c..2bbbbb36 100644
--- a/frontend/src/components/GraphView.tsx
+++ b/frontend/src/components/GraphView.tsx
@@ -340,6 +340,17 @@ interface Props {
* misread, this is the one function to look at, and logging
* `{deltaX, deltaY, deltaMode, wheelDeltaY}` from the real hardware is what decides it.
*/
+/**
+ * The nodes a tab asks to open folded.
+ *
+ * The flag is set by the splitter, not guessed from a node's size or its child count here: only
+ * the pass that built the tab knows whether its groups are the point of the screen or an
+ * incidental grouping the reader still wants to see through.
+ */
+function defaultCollapsed(ns: LayoutNode[]): Set {
+ return new Set(ns.filter((n) => n.data?.collapsedByDefault === true).map((n) => n.id))
+}
+
function isTrackpadPan(ev: WheelEvent): boolean {
if (ev.ctrlKey) return false
if (ev.deltaX !== 0) return true
@@ -438,14 +449,23 @@ export function GraphView({
const isDraggingRef = useRef(false)
// ── Collapse state ─────────────────────────────────────────────────────────
- const [collapsedNodes, setCollapsedNodes] = useState>(new Set())
+ //
+ // A tab may open with parts of itself already folded. The inventory needs it: it holds one
+ // node per class nothing reaches, which on a real application is a few thousand of them, and
+ // a canvas that draws them all is unreadable at any zoom — not because the layout packs them
+ // badly, but because a list of three thousand names was never a picture. Folded to its
+ // groups it opens as a couple of dozen nodes, each saying how many it holds, and the reader
+ // opens the one they came for.
+ const [collapsedNodes, setCollapsedNodes] = useState>(
+ () => defaultCollapsed(nodes),
+ )
// Reset drag and collapse state when nodes change (during render, not in an effect).
const [prevNodes, setPrevNodes] = useState(nodes)
if (prevNodes !== nodes) {
setPrevNodes(nodes)
setDraggedPositions(new Map())
- setCollapsedNodes(new Set())
+ setCollapsedNodes(defaultCollapsed(nodes))
}
const effectiveNodes = useMemo(() => {
@@ -1100,11 +1120,18 @@ export function GraphView({
const zb = zoomBehaviorRef.current
if (!svg || !container || !zb || !nodes.length) return
+ // Only what is drawn. A folded node keeps its descendants in the layout — they still hold
+ // positions, they are simply not rendered — so measuring every node fits the view to a
+ // picture nobody is looking at. On the inventory that is the difference between framing
+ // twenty-eight groups and framing four thousand classes behind them.
+ const shown = nodes.filter((n) => !hiddenNodeIds.has(n.id))
+ const framed = shown.length ? shown : nodes
+
let minX = Infinity
let minY = Infinity
let maxX = -Infinity
let maxY = -Infinity
- for (const n of nodes) {
+ for (const n of framed) {
minX = Math.min(minX, n.x - n.width / 2)
maxX = Math.max(maxX, n.x + n.width / 2)
minY = Math.min(minY, n.y - n.height / 2)
@@ -1122,7 +1149,7 @@ export function GraphView({
const ty = h / 2 - scale * cy
const tr = zoomIdentity.translate(tx, ty).scale(scale)
select(svg).call(zb.transform, tr)
- }, [nodes])
+ }, [nodes, hiddenNodeIds])
const zoomBy = useCallback((factor: number) => {
const svg = svgRef.current
diff --git a/frontend/src/components/LeftSidebar.tsx b/frontend/src/components/LeftSidebar.tsx
index 0f184bbc..ac113aee 100644
--- a/frontend/src/components/LeftSidebar.tsx
+++ b/frontend/src/components/LeftSidebar.tsx
@@ -325,6 +325,7 @@ const CATEGORY_ICONS: Record = {
'Model ERD': 'box',
'Event Choreography': 'zap',
'AI Agents': 'zap',
+ Reachability: 'search',
Other: 'route',
}
@@ -346,6 +347,7 @@ function categoryBucket(tab: TabEntry): string {
if (tab.category === 'ERD') return 'Model ERD'
if (tab.category === 'Events') return 'Event Choreography'
if (tab.category === 'AI') return 'AI Agents'
+ if (tab.category === 'Reachability') return 'Reachability'
if (tab.category === 'Filament') {
const p = tab.panelId ?? ''
return p ? `Filament · ${p.charAt(0).toUpperCase()}${p.slice(1)} Panel` : 'Filament'
diff --git a/frontend/src/components/Legend.tsx b/frontend/src/components/Legend.tsx
index d7b53dc6..4a184189 100644
--- a/frontend/src/components/Legend.tsx
+++ b/frontend/src/components/Legend.tsx
@@ -25,6 +25,10 @@ const NODE_TYPES = [
{ type: 'filament_page', label: 'Filament Page', color: '#C084FC' },
{ type: 'filament_widget', label: 'Filament Widget', color: '#06B6D4' },
{ type: 'filament_relation_manager', label: 'Relation Manager', color: '#0891B2' },
+ { type: 'entry_point', label: 'Entry point', color: '#22D3EE' },
+ { type: 'unreached_class', label: 'Not reached', color: '#94A3B8' },
+ { type: 'entry_point_group', label: 'Entry group', color: '#0E7490' },
+ { type: 'unreached_group', label: 'Unreached group', color: '#475569' },
]
export function Legend() {
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index 13bbc361..6d017ac2 100644
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -1,5 +1,6 @@
import React, { useMemo, useRef, useCallback, useState } from 'react'
import type { GraphData, GraphNode, GraphEdge, FlowStep, DbQuery, CacheOperation, HttpCall } from '../types/graph'
+import { UNFOLLOWABLE_REFERENCE_LABELS } from '../types/graph'
import { SECURITY_EXPOSURE_COLORS, SECURITY_EXPOSURE_COLORS_LIGHT, SECURITY_RISK_COLORS, SECURITY_ISSUE_META, SECURITY_SEVERITY_LABELS } from '../utils/graphConstants'
import { FlowchartView } from './FlowchartView'
import { FlowchartModal } from './FlowchartModal'
@@ -25,6 +26,10 @@ interface Props {
}
const TYPE_COLORS: Record = {
+ entry_point: '#22D3EE',
+ entry_point_group: '#0E7490',
+ unreached_class: '#94A3B8',
+ unreached_group: '#475569',
route: '#4CAF50',
middleware: '#FF9800',
controller: '#2196F3',
@@ -343,6 +348,8 @@ export function Sidebar({ selectedId, graphData, theme, onClose, onStressChange
key !== 'job' &&
key !== 'deferredDefect' &&
key !== 'deferredDefectMessage' &&
+ key !== 'note' &&
+ key !== 'unfollowableReferences' &&
!(Array.isArray(val) && val.length === 0)
)
@@ -353,6 +360,14 @@ export function Sidebar({ selectedId, graphData, theme, onClose, onStressChange
const listener = node.data?.listener as import('../types/graph').ListenerNodeData | undefined
const job = node.data?.job as import('../types/graph').JobNodeData | undefined
+ // The Reachability tab's caveat, rendered next to the class rather than left in a heading
+ // three levels up the tree. A reader who clicks a class and is told only that nothing
+ // reaches it has been told the wrong thing.
+ const reachabilityNote = typeof node.data?.note === 'string' ? node.data.note : ''
+ const unfollowableReferences = Array.isArray(node.data?.unfollowableReferences)
+ ? (node.data.unfollowableReferences as string[])
+ : []
+
const hasFlow = flowSteps.length > 0 || !!sequenceDiagram
const hasSource = !!filePath
const hasEdges = incomingEdges.length > 0 || outgoingEdges.length > 0
@@ -1023,6 +1038,25 @@ export function Sidebar({ selectedId, graphData, theme, onClose, onStressChange
)}
+ {reachabilityNote !== '' && (
+
+
What this means
+
{reachabilityNote}
+ {unfollowableReferences.length > 0 && (
+ <>
+
+ Brain did find this class referenced, in ways it cannot follow:
+
+
+ {unfollowableReferences.map(ref => (
+
{UNFOLLOWABLE_REFERENCE_LABELS[ref] ?? ref}
+ ))}
+
+ >
+ )}
+
+ )}
+
{erd && (
Model Schema
diff --git a/frontend/src/types/graph.ts b/frontend/src/types/graph.ts
index d322dfca..4bfdeda8 100644
--- a/frontend/src/types/graph.ts
+++ b/frontend/src/types/graph.ts
@@ -75,7 +75,7 @@ export interface GraphNodeMetrics {
export interface GraphNode {
id: string
- type: 'route' | 'middleware' | 'controller' | 'livewire_component' | 'action' | 'service' | 'validation_request' | 'model' | 'event' | 'listener' | 'job' | 'command' | 'channel' | 'schedule' | 'view' | 'mail' | 'notification' | 'enum' | 'interface' | 'trait' | 'abstract_class' | 'service_provider' | 'facade' | 'filament_panel' | 'filament_resource' | 'filament_page' | 'filament_page_method' | 'filament_widget' | 'filament_relation_manager' | 'ai_agent' | 'ai_tool' | 'action_class'
+ type: 'route' | 'middleware' | 'controller' | 'livewire_component' | 'action' | 'service' | 'validation_request' | 'model' | 'event' | 'listener' | 'job' | 'command' | 'channel' | 'schedule' | 'view' | 'mail' | 'notification' | 'enum' | 'interface' | 'trait' | 'abstract_class' | 'service_provider' | 'facade' | 'filament_panel' | 'filament_resource' | 'filament_page' | 'filament_page_method' | 'filament_widget' | 'filament_relation_manager' | 'ai_agent' | 'ai_tool' | 'action_class' | 'entry_point' | 'entry_point_group' | 'unreached_class' | 'unreached_group'
label: string
data: Record
}
@@ -144,6 +144,31 @@ export interface ErdModelData {
morphAliasMissing?: boolean
}
+/**
+ * Shape of the `data` an `unreached_class` node carries on the Reachability tab.
+ *
+ * `unfollowableReferences` is the load-bearing field: it is the difference between "nothing
+ * reaches this from a traced entry point" and "this is dead code", and the second sentence is
+ * not one Brain is in a position to make. Never render the class without it.
+ */
+export interface UnreachedClassData {
+ kind: string
+ fqcn: string
+ file: string
+ unfollowableReferences: string[]
+ tracerBlind: boolean
+ note: string
+}
+
+/** How each unfollowable-reference tag reads to someone who did not write the analyzer. */
+export const UNFOLLOWABLE_REFERENCE_LABELS: Record = {
+ 'container-binding': 'bound in the container',
+ facade: 'reached through a facade',
+ config: 'named in config/',
+ 'inherited-by-reached-class': 'inherited by a class that is reached',
+ 'class-string': 'named as a class-string elsewhere',
+}
+
/** One node or edge in the format produced from `GraphData` (Cytoscape-compatible shape). */
export interface GraphElement {
data: Record & {
diff --git a/frontend/src/utils/graphConstants.ts b/frontend/src/utils/graphConstants.ts
index 5ab38b51..3aa6fec2 100644
--- a/frontend/src/utils/graphConstants.ts
+++ b/frontend/src/utils/graphConstants.ts
@@ -87,6 +87,12 @@ export const ACCENT_COLORS: Record = {
filament_page_method: '#E879F9',
filament_widget: '#06B6D4',
filament_relation_manager: '#0891B2',
+ entry_point: '#22D3EE',
+ entry_point_group: '#0E7490',
+ // Deliberately grey. Everything on the unreached side of the Reachability tab is a
+ // question, not a verdict, and a red node reads as one.
+ unreached_class: '#94A3B8',
+ unreached_group: '#475569',
}
/** Darkened accent colours for text / icons on light card backgrounds */
@@ -123,6 +129,10 @@ export const ACCENT_COLORS_LIGHT: Record = {
filament_page_method: '#a21caf',
filament_widget: '#0369a1',
filament_relation_manager: '#075985',
+ entry_point: '#0E7490',
+ entry_point_group: '#155E75',
+ unreached_class: '#475569',
+ unreached_group: '#334155',
}
/** Dark-mode node background colours (deep tinted darks) */
@@ -159,6 +169,10 @@ export const BG_COLORS: Record = {
filament_page_method: '#240E30',
filament_widget: '#071A1E',
filament_relation_manager: '#06161A',
+ entry_point: '#04171C',
+ entry_point_group: '#03151A',
+ unreached_class: '#111827',
+ unreached_group: '#0B1120',
}
/** Light-mode node background colours (soft tinted pastels) */
@@ -195,6 +209,10 @@ export const BG_COLORS_LIGHT: Record = {
filament_page_method: '#fef0ff',
filament_widget: '#ecfeff',
filament_relation_manager: '#e0f2fe',
+ entry_point: '#ecfeff',
+ entry_point_group: '#cffafe',
+ unreached_class: '#f8fafc',
+ unreached_group: '#f1f5f9',
}
export const HIGHLIGHT_COLOR = '#8B6FE8'
diff --git a/resources/assets/assets/index-CUVOYzgk.js b/resources/assets/assets/index-CUVOYzgk.js
deleted file mode 100644
index 71575f5d..00000000
--- a/resources/assets/assets/index-CUVOYzgk.js
+++ /dev/null
@@ -1,10 +0,0 @@
-const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/vendor-utils-D7YtnGoz.js","assets/rolldown-runtime-BHe-jwch.js"])))=>i.map(i=>d[i]);
-import{r as e}from"./rolldown-runtime-BHe-jwch.js";import{_ as t,a as n,c as r,d as i,f as a,g as o,h as s,i as c,l,m as u,n as d,o as f,p,r as m,s as h,t as g,u as _,v}from"./vendor-react-CNktMmef.js";import{C as y,t as b}from"./vendor-CsjAK7B8.js";import{a as x,c as S,i as C,n as w,o as T,r as E,s as D,t as O}from"./vendor-d3-DThTr3c3.js";import{t as k}from"./vendor-utils-D7YtnGoz.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var A=e(v(),1),j=t();function M(){let[e,t]=(0,A.useState)(null),[n,r]=(0,A.useState)(!0),[i,a]=(0,A.useState)(null);return(0,A.useEffect)(()=>{fetch(`/_laravel-brain/.graph-manifest.json`).then(e=>{if(!e.ok)throw Error(`HTTP ${e.status}`);return e.json()}).then(e=>{t(e),r(!1)}).catch(e=>{a(e.message),r(!1)})},[]),{manifest:e,loading:n,error:i}}function N(e){return[...e.nodes.map(e=>({data:{id:e.id,label:e.label,type:e.type,...e.data,metrics_cc:e.data?.metrics?.cyclomaticComplexity??0}})),...e.edges.map(e=>({data:{id:e.id,source:e.source,target:e.target,label:e.label,type:e.type}}))]}function P(){let[e,t]=(0,A.useState)({data:null,loading:!1,error:null}),n=(0,A.useRef)(new Map),r=(0,A.useRef)(null),i=(0,A.useCallback)(e=>{if(r.current===e)return;r.current=e;let i=n.current.get(e);if(i){t({data:i,loading:!1,error:null});return}t(e=>({...e,loading:!0,error:null})),fetch(`/_laravel-brain/`+e).then(e=>{if(!e.ok)throw Error(`HTTP ${e.status}`);return e.json()}).then(i=>{n.current.set(e,i),r.current===e&&t({data:i,loading:!1,error:null})}).catch(n=>{r.current===e&&t({data:null,loading:!1,error:n.message})})},[]);return{state:e,elements:(0,A.useMemo)(()=>e.data?N(e.data):[],[e.data]),load:i}}function F(e,t=300){let[n,r]=(0,A.useState)(t),[i,a]=(0,A.useState)(e);return e!==i&&(a(e),r(t)),(0,A.useEffect)(()=>{if(n>=e.length)return;let t=window,i=(t.requestIdleCallback?t.requestIdleCallback.bind(t):e=>setTimeout(()=>e({didTimeout:!1,timeRemaining:()=>0}),100))(()=>{r(t=>Math.min(t+200,e.length))});return()=>{t.cancelIdleCallback?t.cancelIdleCallback(i):clearTimeout(i)}},[n,e.length]),(0,A.useMemo)(()=>e.length<=t?e:e.slice(0,n),[e,n,t])}function ee(){let[e,t]=(0,A.useState)(()=>localStorage.getItem(`lb-theme`)??`dark`);return(0,A.useEffect)(()=>{document.documentElement.setAttribute(`data-theme`,e),localStorage.setItem(`lb-theme`,e)},[e]),{theme:e,toggle:()=>t(e=>e===`dark`?`light`:`dark`)}}var te=e(k(),1),I={transaction:`#d99a2b`,rollback:`#c2554a`,chain:`#5f8fa8`,batch:`#8a7fb5`},ne={transaction:`6 5`,rollback:`2 4`,chain:`10 4`,batch:`4 4`},L={route:`#4CAF50`,middleware:`#FF9800`,controller:`#2196F3`,livewire_component:`#FB7185`,action:`#03A9F4`,service:`#9C27B0`,action_class:`#84cc16`,validation_request:`#0d9488`,model:`#F44336`,event:`#FFD600`,listener:`#C9A227`,job:`#607D8B`,command:`#14b8a6`,channel:`#8b5cf6`,schedule:`#f97316`,view:`#ec4899`,mail:`#f472b6`,notification:`#db2777`,enum:`#0ea5e9`,interface:`#38bdf8`,trait:`#a78bfa`,abstract_class:`#94a3b8`,service_provider:`#ca8a04`,facade:`#00BCD4`,ai_agent:`#A3E635`,ai_tool:`#65A30D`,filament_panel:`#7C3AED`,filament_resource:`#A855F7`,filament_page:`#C084FC`,filament_page_method:`#E879F9`,filament_widget:`#06B6D4`,filament_relation_manager:`#0891B2`},re={route:`#2e7d32`,middleware:`#e65100`,controller:`#1565c0`,livewire_component:`#e11d48`,action:`#0277bd`,service:`#6a1b9a`,action_class:`#4d7c0f`,validation_request:`#0f766e`,model:`#c62828`,event:`#b45309`,listener:`#8a6d1f`,job:`#37474f`,command:`#0d7d6e`,channel:`#5b21b6`,schedule:`#c2410c`,view:`#be185d`,mail:`#c026d3`,notification:`#9d174d`,enum:`#0369a1`,interface:`#0c4a6e`,trait:`#5b21b6`,abstract_class:`#64748b`,service_provider:`#a16207`,facade:`#00838f`,ai_agent:`#4d7c0f`,ai_tool:`#3f6212`,filament_panel:`#5b21b6`,filament_resource:`#7e22ce`,filament_page:`#9333ea`,filament_page_method:`#a21caf`,filament_widget:`#0369a1`,filament_relation_manager:`#075985`},R={route:`#0C1A0C`,middleware:`#1C1408`,controller:`#08141C`,livewire_component:`#1C0711`,action:`#07151D`,service:`#150C1C`,action_class:`#131C06`,validation_request:`#042f2e`,model:`#1C0C0C`,event:`#1C1A08`,listener:`#181405`,job:`#0D1113`,command:`#061514`,channel:`#110c1c`,schedule:`#1c1008`,view:`#1c0a14`,mail:`#1c0f18`,notification:`#1c0510`,enum:`#071318`,interface:`#081420`,trait:`#140822`,abstract_class:`#0f172a`,service_provider:`#422006`,facade:`#001F28`,ai_agent:`#131A08`,ai_tool:`#101705`,filament_panel:`#150C2A`,filament_resource:`#1A0C26`,filament_page:`#1E0F2E`,filament_page_method:`#240E30`,filament_widget:`#071A1E`,filament_relation_manager:`#06161A`},z={route:`#f0fdf4`,middleware:`#fff7ed`,controller:`#eff6ff`,livewire_component:`#fff1f2`,action:`#e0f7fa`,service:`#fdf4ff`,action_class:`#f7fee7`,validation_request:`#ccfbf1`,model:`#fff1f2`,event:`#fefce8`,listener:`#fdf6dd`,job:`#f1f5f9`,command:`#f0fdfa`,channel:`#f5f3ff`,schedule:`#fff7ed`,view:`#fdf2f8`,mail:`#fce7f3`,notification:`#fce7f3`,enum:`#f0f9ff`,interface:`#ecfeff`,trait:`#f5f3ff`,abstract_class:`#f1f5f9`,service_provider:`#fef9c3`,facade:`#e0f7fa`,ai_agent:`#f7fee7`,ai_tool:`#ecfccb`,filament_panel:`#f5f3ff`,filament_resource:`#faf5ff`,filament_page:`#fdf4ff`,filament_page_method:`#fef0ff`,filament_widget:`#ecfeff`,filament_relation_manager:`#e0f2fe`},ie=`#8B6FE8`,B={public:{bg:`#1c0808`,border:`#ef4444`,accent:`#f87171`,label:`Public`},guest:{bg:`#1c1408`,border:`#f59e0b`,accent:`#fbbf24`,label:`Guest`},authed:{bg:`#081c10`,border:`#10b981`,accent:`#34d399`,label:`Auth`},admin:{bg:`#110c1c`,border:`#8b5cf6`,accent:`#a78bfa`,label:`Admin`}},ae={public:{bg:`#fff1f2`,border:`#ef4444`,accent:`#dc2626`,label:`Public`},guest:{bg:`#fffbeb`,border:`#f59e0b`,accent:`#d97706`,label:`Guest`},authed:{bg:`#ecfdf5`,border:`#10b981`,accent:`#059669`,label:`Auth`},admin:{bg:`#f5f3ff`,border:`#8b5cf6`,accent:`#7c3aed`,label:`Admin`}},V={none:`#6b7280`,low:`#10b981`,medium:`#f59e0b`,high:`#f97316`,critical:`#ef4444`},oe={none:`No Issues`,low:`Low`,medium:`Medium`,high:`High`,critical:`Critical`},se={MISSING_FK_INDEX:{icon:`🔑`,name:`Unindexed foreign key`},MASS_ASSIGNMENT:{icon:`⚡`,name:`Mass Assignment`},UNVALIDATED_INPUT:{icon:`⚠️`,name:`Unvalidated Input`},MISSING_THROTTLE:{icon:`🔓`,name:`Missing Throttle`},PUBLIC_WRITE:{icon:`🌐`,name:`Public Write`},XSS_DIRECT_OUTPUT:{icon:`💉`,name:`XSS — Direct Output`},XSS_HTML_DECODE:{icon:`🔓`,name:`XSS — HTML Decode`},XSS_BLADE_UNESCAPED:{icon:`📄`,name:`XSS — Blade {!! !!}`},SQL_INJECTION:{icon:`🛢️`,name:`SQL Injection`},OPEN_REDIRECT:{icon:`↪️`,name:`Open Redirect`},SSRF:{icon:`🌐`,name:`SSRF`},DEBUG_CODE:{icon:`🐞`,name:`Debug Code Leak`},ENV_LEAK:{icon:`🔑`,name:`Env Leak`},CSRF_BYPASS:{icon:`🛡️`,name:`CSRF Bypass`},INSECURE_COOKIE:{icon:`🍪`,name:`Insecure Cookie`},UNSAFE_STORAGE_PATH:{icon:`📁`,name:`Unsafe Storage Path`},FILE_UPLOAD_VALIDATION:{icon:`📎`,name:`File Upload Validation`},UNSAFE_AUTH:{icon:`🚪`,name:`Unsafe Auth`},UNSAFE_CRYPT:{icon:`🔐`,name:`Unsafe Crypt`},ARTISAN_CALL:{icon:`⚙️`,name:`Tainted Artisan Call`},PROCESS_SHELL:{icon:`💻`,name:`Shell Injection`},CONFIG_INJECTION:{icon:`🧩`,name:`Config Injection`},TAINTED_VIEW_NAME:{icon:`🖼️`,name:`Tainted View Name`},SESSION_FIXATION:{icon:`🎫`,name:`Session Fixation`},MAIL_TAINTED_HEADER:{icon:`✉️`,name:`Mail Header Injection`}},ce=[{label:`Low`,min:1,max:5,fill:`#0d2e1a`,border:`#4ade80`},{label:`Moderate`,min:6,max:10,fill:`#2e2200`,border:`#facc15`},{label:`High`,min:11,max:15,fill:`#2e1200`,border:`#fb923c`},{label:`Critical`,min:16,max:1/0,fill:`#2e0a0a`,border:`#f87171`}],H=[{label:`Low`,min:1,max:5,fill:`#f0fdf4`,border:`#16a34a`},{label:`Moderate`,min:6,max:10,fill:`#fefce8`,border:`#ca8a04`},{label:`High`,min:11,max:15,fill:`#fff7ed`,border:`#ea580c`},{label:`Critical`,min:16,max:1/0,fill:`#fff1f2`,border:`#dc2626`}],le=[`chain`],ue={transaction:`transaction`,rollback:`rollback`,chain:`chain`,batch:`batch`},de={transaction:`transactions`,rollback:`rollbacks`,chain:`chains`,batch:`batches`},fe=[`transaction`,`rollback`,`chain`,`batch`];function pe(e){let t=e.width/2,n=e.height/2;return[[e.x-t,e.y-n],[e.x+t,e.y-n],[e.x+t,e.y+n],[e.x-t,e.y+n]]}function U(e){if(e.length<3)return e;let t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]),n=(e,t,n)=>(t[0]-e[0])*(n[1]-e[1])-(t[1]-e[1])*(n[0]-e[0]),r=e=>{let t=[];for(let r of e){for(;t.length>=2&&n(t[t.length-2],t[t.length-1],r)<=0;)t.pop();t.push(r)}return t.pop(),t};return[...r(t),...r([...t].reverse())]}function me(e,t){if(e.length===0)return e;let n=e.reduce((e,t)=>e+t[0],0)/e.length,r=e.reduce((e,t)=>e+t[1],0)/e.length;return e.map(([e,i])=>{let a=e-n,o=i-r,s=Math.hypot(a,o)||1;return[e+a/s*t,i+o/s*t]})}function W(e,t,n){let r=!1;for(let i=0,a=e.length-1;in!=l>n&&t<(c-o)*(n-s)/(l-s)+o&&(r=!r)}return r}var G=new Set([`transaction`,`rollback`,`chain`,`batch`]);function he(e,t,n=4){let r=t.x-e.x,i=t.y-e.y;if(r===0&&i===0)return null;let a=e=>{let t=r===0?1/0:(e.width/2+n)/Math.abs(r),a=i===0?1/0:(e.height/2+n)/Math.abs(i);return Math.min(t,a)},o=a(e),s=1-a(t);return o>=s?null:{x1:e.x+r*o,y1:e.y+i*o,x2:e.x+r*s,y2:e.y+i*s}}function K(e){let t=e.data?.regions;if(!Array.isArray(t))return[];let n=[];for(let e of t){let t=e?.id,r=e?.kind;typeof t!=`string`||t===``||typeof r!=`string`||!G.has(r)||n.push({id:t,kind:r,position:typeof e.position==`number`?e.position:null})}return n}function ge(e,t=22){let n=new Map;for(let t of e)for(let e of K(t)){let r=n.get(e.id)??{kind:e.kind,members:[]};r.members.push({node:t,position:e.position}),n.set(e.id,r)}let r=[],i=new Map,a=new Map;for(let e of[...n.keys()].sort()){let t=n.get(e).kind,r=(a.get(t)??0)+1;a.set(t,r),i.set(e,r)}for(let[a,o]of n){let n=le.includes(o.kind),s=(n?[...o.members].sort((e,t)=>(e.position??0)-(t.position??0)):o.members).map(e=>e.node),c=me(U(s.flatMap(pe)),t);if(c.length<3)continue;let l=new Set(s.map(e=>e.id)),u=!e.some(e=>!l.has(e.id)&&pe(e).some(([e,t])=>W(c,e,t)));r.push({id:a,kind:o.kind,index:i.get(a)??1,points:c,members:s,ordered:n,pure:u})}return r}var q=e(y(),1);function _e(e,t){let n=e.indexOf(`@`),r=e.indexOf(`::`);return n===-1?r===-1?{className:e,method:t??``}:{className:e.slice(0,r),method:e.slice(r+2)}:{className:e.slice(0,n),method:t??e.slice(n+1)}}function J(e,t=!1){let{className:n,method:r}=_e(String(e.label??e.id),e.method),i=t||n.length>r.length?n:r,a=Math.max(t?120:185,Math.min(270,i.length*7.6+44)),o=t?40:90;return{id:e.id,x:0,y:0,width:a,height:o,lines:[n,r].filter(Boolean),data:e}}function Y(e){if(!e.length)return;let t=0,n=0;for(let r of e)t+=r.x,n+=r.y;let r=t/e.length,i=n/e.length;for(let t of e)t.x-=r,t.y-=i}function ve(e,t,n){let r=new q.default.graphlib.Graph({compound:!0});r.setGraph({rankdir:n,nodesep:n===`TB`?70:50,ranksep:n===`TB`?100:120,marginx:60,marginy:60}),r.setDefaultEdgeLabel(()=>({}));for(let t of e)r.setNode(t.id,{width:t.width,height:t.height});for(let[t,n]of be(e)){r.setNode(t,{});for(let e of n)r.setParent(e.id,t)}for(let e of t)r.hasNode(e.source)&&r.hasNode(e.target)&&r.setEdge(e.source,e.target);q.default.layout(r);for(let t of e){let e=r.node(t.id);e&&(t.x=e.x,t.y=e.y)}}function ye(e){let t=K(e);return t.length===0?null:(t.find(e=>le.includes(e.kind))??t[0]).id}function be(e){let t=new Map;for(let n of e){let e=ye(n);e!==null&&t.set(e,[...t.get(e)??[],n])}for(let[e,n]of t)n.length<2&&t.delete(e);return new Map([...t].map(([e,t])=>[`cluster::${e}`,t]))}function xe(e,t,n,r=60,i=110){let a=new Set(e.map(e=>e.id)),o=new Map,s=new Map;for(let t of e)o.set(t.id,[]),s.set(t.id,0);for(let e of t)!a.has(e.source)||!a.has(e.target)||(o.get(e.source).push(e.target),s.set(e.target,(s.get(e.target)??0)+1));let c=e.filter(e=>s.get(e.id)===0).map(e=>e.id),l=new Map,u=[...c];for(let e of c)l.set(e,0);let d=0,f=()=>{for(;d0)for(let[,e]of p){let t=new Map,n=0;for(let r of e){let e=m.get(r)??`\u0000${r}`;t.has(e)||t.set(e,n++)}e.sort((e,n)=>t.get(m.get(e)??`\u0000${e}`)-t.get(m.get(n)??`\u0000${n}`))}for(let e of p.values())e.sort();let h=new Map(e.map(e=>[e.id,e])),g=0;for(let e of[...p.keys()].sort((e,t)=>e-t)){let t=p.get(e).map(e=>h.get(e)),a=Se(t.length);if(n===`TB`){let e=Ce(t,a),n=g;for(let t of e){let e=t.reduce((e,t)=>e+t.width,0)+r*(t.length-1),i=Te(t,e=>e.height),a=-e/2;for(let e of t)e.x=a+e.width/2,e.y=n+i/2,a+=e.width+r;n+=i+r}g=n-r+i}else{let e=Ce(t,a),n=g;for(let t of e){let e=t.reduce((e,t)=>e+t.height,0)+r*(t.length-1),i=Te(t,e=>e.width),a=-e/2;for(let e of t)e.x=n+i/2,e.y=a+e.height/2,a+=e.height+r;n+=i+r}g=n-r+i}}}function Se(e,t=12){return e<=t?e:Math.ceil(Math.sqrt(e)*1.4)}function Ce(e,t){if(t>=e.length)return[e];let n=[];for(let r=0;rObject.assign({},e)),r=new Map(n.map(e=>[e.id,e])),i=t.filter(e=>r.has(e.source)&&r.has(e.target)).map(e=>({source:e.source,target:e.target})),a=C(n).force(`link`,x(i).id(e=>e.id).distance(90)).force(`charge`,E().strength(-420)).force(`center`,D(0,0)).force(`collide`,T().radius(e=>Math.hypot(e.width,e.height)/2+14));a.stop();for(let e=0;e<450&&a.alpha()>.02;e++)a.tick();for(let t of e){let e=r.get(t.id);e&&(t.x=e.x??0,t.y=e.y??0)}}function Te(e,t){return e.reduce((e,n)=>Math.max(e,t(n)),-1/0)}function Ee(e,t=40){let n=e.length;if(!n)return;let r=Te(e,e=>Math.max(e.width,e.height))+t,i=Math.max(r,n*r/(2*Math.PI));e.forEach((e,t)=>{let r=t/n*Math.PI*2-Math.PI/2;e.x=i*Math.cos(r),e.y=i*Math.sin(r)})}function De(e,t=60,n=60){if(!e.length)return;let r=Te(e,e=>e.width)+t,i=Te(e,e=>e.height)+n,a=Math.ceil(Math.sqrt(e.length));e.forEach((e,t)=>{e.x=t%a*r,e.y=Math.floor(t/a)*i})}function Oe(e,t,n){return e===`dagre`&&t>n?`breadthfirst`:e===`dagre`?`dagre`:e===`cose-bilkent`?`force`:e===`breadthfirst`?`breadthfirst`:e===`circle`?`circle`:e===`grid`?`grid`:`dagre`}function ke(e,t=!1){let n=[],r=[];for(let i of e){let e=i.data;e.source!=null&&e.target!=null?r.push({id:e.id,source:String(e.source),target:String(e.target),data:e}):n.push(J(e,t))}return{nodes:n,edges:r}}var X=o();function Ae(e){return Math.max(0,Math.min(255,Math.round(e))).toString(16).padStart(2,`0`)}function je(e,t,n){return{x:n.applyX(e),y:n.applyY(t)}}function Me(e,t){if(t.length===0)return{x:0,y:0};if(t.length===1||e<=0)return t[0];if(e>=1)return t[t.length-1];let n=0,r=[];for(let e=0;e=i||e===r.length-1){let r=(i-a)/n,o=t[e],s=t[e+1];return{x:o.x+(s.x-o.x)*r,y:o.y+(s.y-o.y)*r}}a+=n}}return t[t.length-1]}function Ne(e,t){let n=t.x-e.x,r=t.y-e.y,i=Math.abs(n)-(e.width+t.width)/2;return Math.abs(r)-(e.height+t.height)/2>=i?r>=0?{ex:e.x,ey:e.y+e.height/2,tx:t.x,ty:t.y-t.height/2,vertical:!0}:{ex:e.x,ey:e.y-e.height/2,tx:t.x,ty:t.y+t.height/2,vertical:!0}:n>=0?{ex:e.x+e.width/2,ey:e.y,tx:t.x-t.width/2,ty:t.y,vertical:!1}:{ex:e.x-e.width/2,ey:e.y,tx:t.x+t.width/2,ty:t.y,vertical:!1}}function Pe(e,t){let{ex:n,ey:r,tx:i,ty:a,vertical:o}=Ne(e,t);if(o){if(Math.abs(n-i)<3||Math.abs(a-r)<=8)return[{x:n,y:r},{x:i,y:a}];let e=(r+a)/2;return[{x:n,y:r},{x:n,y:e},{x:i,y:e},{x:i,y:a}]}else{if(Math.abs(r-a)<3||Math.abs(i-n)<=8)return[{x:n,y:r},{x:i,y:a}];let e=(n+i)/2;return[{x:n,y:r},{x:e,y:r},{x:e,y:a},{x:i,y:a}]}}var Fe=7;function Ie(...e){return Math.max(0,Math.min(Fe,...e.map(e=>e-1)))}function Le(e,t){let{ex:n,ey:r,tx:i,ty:a,vertical:o}=Ne(e,t);if(o){if(Math.abs(n-i)<3||Math.abs(a-r)<=8)return{d:`M${n},${r} L${i},${a}`,lx:n+6,ly:(r+a)/2,exitX:n,exitY:r,entryX:i,entryY:a};let e=(r+a)/2,t=a>r?1:-1,o=Ie(Math.abs(e-r),Math.abs(a-e),Math.abs(i-n)),s=i>n?o:-o;return{d:o>0?`M${n},${r} V${e-o*t} Q${n},${e} ${n+s},${e} H${i-s} Q${i},${e} ${i},${e+o*t} V${a}`:`M${n},${r} V${e} H${i} V${a}`,lx:(n+i)/2,ly:e-14*t,exitX:n,exitY:r,entryX:i,entryY:a}}else{if(Math.abs(r-a)<3||Math.abs(i-n)<=8)return{d:`M${n},${r} L${i},${a}`,lx:(n+i)/2,ly:r-10,exitX:n,exitY:r,entryX:i,entryY:a};let e=(n+i)/2,t=i>n?1:-1,o=Ie(Math.abs(e-n),Math.abs(i-e),Math.abs(a-r)),s=a>r?o:-o;return{d:o>0?`M${n},${r} H${e-o*t} Q${e},${r} ${e},${r+s} V${a-s} Q${e},${a} ${e+o*t},${a} H${i}`:`M${n},${r} H${e} V${a} H${i}`,lx:e+6*t,ly:(r+a)/2,exitX:n,exitY:r,entryX:i,entryY:a}}}function Re(e,t){let n=String(e.label??``);return n?{text:n,fill:t?`rgba(255,255,255,0.4)`:`rgba(0,0,0,0.5)`,bg:t?`#111218`:`#fff`}:null}function ze(e,t,n,r,i,a){let o=String(e.data.type??``),s=t?L[o]??`#c9d1d9`:re[o]??`#333`,c=t?R[o]??`#0d1117`:z[o]??`#ffffff`,l=Number(e.data.metrics_cc??0)||0;if(n){let n=t?ce:H,r=n.find(e=>l>=e.min&&l<=e.max)??n[0],a=i?`#a855f7`:e.data.hasN1?`#F44336`:r.border;return{bg:r.fill,border:a,borderW:1.5,accent:r.border}}if(a&&o===`route`){let n=e.data.security;if(n){let e=t?B:ae,a=e[n.exposure]??e.public,o=V[n.riskLevel]??V.none,c=r?s:i?`#a855f7`:n.riskLevel===`none`?a.border:o;return{bg:a.bg,border:c,borderW:r||n.riskLevel!==`none`?2:1.5,accent:a.accent}}}let u=t?`rgba(255,255,255,0.1)`:`rgba(0,0,0,0.12)`,d=1;return e.data.hasN1&&(u=`#F44336`,d=2),r&&(u=s,d=2),i&&(u=`#a855f7`,d=2),{bg:c,border:u,borderW:d,accent:s}}function Be(e){if(e.ctrlKey)return!1;if(e.deltaX!==0)return!0;if(e.deltaMode!==0)return!1;let t=e.wheelDeltaY;return typeof t==`number`&&t!==0?Math.abs(t+3*e.deltaY)<=2?!0:!(Math.abs(t)%120==0&&Math.abs(e.deltaY)>=100):!(Number.isInteger(e.deltaY)&&Math.abs(e.deltaY)>=100)}function Ve({elements:e,layout:t,rankDir:n,searchQuery:r,visibleTypes:i,theme:a,onNodeSelect:o,graphRef:s,stressTestNodeId:c,stressRunKey:l,complexityOverlay:u,securityOverlay:d=!1,compact:f=!1,onLayoutChange:p,onRankDirChange:m,onToggleComplexityOverlay:h,onToggleSecurityOverlay:g,onToggleCompact:_}){let v=a===`dark`,y=v?`rgba(255,255,255,0.32)`:`rgba(0,0,0,0.38)`,b=v?`rgba(255,255,255,0.55)`:`rgba(0,0,0,0.55)`,{nodes:x,edges:C}=(0,A.useMemo)(()=>ke(e,f),[e,f]),T=(0,A.useMemo)(()=>x.filter(e=>i.has(String(e.data.type))).length,[x,i]),[E,D]=(0,A.useState)(0),k=(0,A.useRef)(null),j=(0,A.useRef)(!0);(0,A.useEffect)(()=>{if(j.current){j.current=!1;return}return k.current&&window.clearTimeout(k.current),k.current=window.setTimeout(()=>{D(e=>e+1)},200),()=>{k.current&&window.clearTimeout(k.current)}},[i,t,n,f]);let{nodes:M,edges:N}=(0,A.useMemo)(()=>{let e=x.map(e=>({...e,lines:[...e.lines]})),r=C.map(e=>({...e})),i=Oe(t,T,80);return i===`dagre`?ve(e,r,n):i===`breadthfirst`?xe(e,r,n):i===`force`?we(e,r):i===`circle`?Ee(e):De(e),Y(e),{nodes:e,edges:r}},[x,C,t,n,E,T]),P=(0,A.useMemo)(()=>new Map(M.map(e=>[e.id,e])),[M]),[F,ee]=(0,A.useState)(new Map),re=(0,A.useRef)(null),R=(0,A.useRef)(!1),[z,ae]=(0,A.useState)(new Set),[oe,se]=(0,A.useState)(M);oe!==M&&(se(M),ee(new Map),ae(new Set));let H=(0,A.useMemo)(()=>F.size===0?M:M.map(e=>{let t=F.get(e.id);return t?{...e,x:t.x,y:t.y}:e}),[M,F]),le=(0,A.useMemo)(()=>ge(H),[H]),pe=(0,A.useCallback)(e=>i.has(e===`rollback`?`transaction`:e),[i]),U=(0,A.useMemo)(()=>le.filter(e=>pe(e.kind)),[le,pe]),me=(0,A.useMemo)(()=>{let e=new Map,t=[],n=(n,r,i)=>{let a=0;for(;t.some(e=>Math.abs(e.x-r)<140&&Math.abs(e.y-(i-a*11))<10);)a++;t.push({x:r,y:i-a*11}),e.set(n,a)};for(let e of U){if(e.pure){n(e.id,Math.min(...e.points.map(([e])=>e)),Math.min(...e.points.map(([,e])=>e)));continue}for(let t of e.members)n(`${e.id}|${t.id}`,t.x-t.width/2,t.y-t.height/2)}return e},[U]),W=(0,A.useMemo)(()=>new Map(H.map(e=>[e.id,e])),[H]),G=(0,A.useRef)(W);(0,A.useEffect)(()=>{G.current=W},[W]);let K=(0,A.useCallback)(e=>i.has(String(e)),[i]),q=(0,A.useCallback)(e=>K(P.get(e.source)?.data.type)&&K(P.get(e.target)?.data.type),[P,K]),J=(0,A.useMemo)(()=>{let e=new Map;for(let t of M)e.set(t.id,[]);for(let t of N)q(t)&&e.get(t.source)?.push(t.target);let t=new Set;for(let n of z){let r=[n],i=new Set([n]);for(;r.length;){let n=r.shift();for(let a of e.get(n)??[])i.has(a)||(i.add(a),t.add(a),r.push(a))}}return t},[M,N,q,z]),ye=(0,A.useMemo)(()=>{let e=new Map;for(let t of N)q(t)&&(J.has(t.target)||e.set(t.source,(e.get(t.source)??0)+1));return e},[N,q,J]),be=(0,A.useCallback)((e,t)=>{e.stopPropagation(),ae(e=>{let n=new Set(e);return n.has(t)?n.delete(t):n.add(t),n})},[]),Se=(0,A.useMemo)(()=>{let e=new Map;for(let t of z){let n=0,r=new Set,i=[t];for(;i.length;){let e=i.shift();for(let t of N){if(t.source!==e||!q(t))continue;let a=t.target;r.has(a)||(r.add(a),J.has(a)&&(n++,i.push(a)))}}e.set(t,n)}return e},[z,J,N,q]),Ce=(0,A.useMemo)(()=>{if(!r.trim())return null;let e=r.toLowerCase(),t=new Set;for(let n of M)String(n.data.label??n.id).toLowerCase().includes(e)&&t.add(n.id);return t},[M,r]),Te=(0,A.useMemo)(()=>{let e=new Set,t=new Set;if(!c||!P.has(c))return{nodes:e,edges:t};let n=new Set,r=new Set,i=new Set,a=[c];for(;a.length;){let e=a.shift();if(!i.has(e)){i.add(e),n.add(e);for(let t of N){if(t.source!==e||!q(t))continue;r.add(t.id);let n=t.target;i.has(n)||a.push(n)}}}return{nodes:n,edges:r}},[c,l,N,q,P]),[Ne,Fe]=(0,A.useState)(new Set),[Ie,Ve]=(0,A.useState)(null),He=(0,A.useCallback)(e=>{let t=new Set;for(let n of N)(n.source===e||n.target===e)&&t.add(n.id);Fe(t),Ve(e),o(e)},[N,o]),Ue=(0,A.useCallback)(()=>{Fe(new Set),Ve(null),o(null)},[o]),We=(0,A.useCallback)((e,t,n,r)=>{e.stopPropagation(),e.currentTarget.setPointerCapture(e.pointerId),R.current=!1,re.current={nodeId:t,startSX:e.clientX,startSY:e.clientY,origMX:n,origMY:r}},[]),Ge=(0,A.useCallback)((e,t)=>{let n=re.current;if(!n||n.nodeId!==t)return;let r=e.clientX-n.startSX,i=e.clientY-n.startSY;if(!R.current&&Math.abs(r)<4&&Math.abs(i)<4)return;R.current=!0;let a=tt.current.k;ee(e=>{let o=new Map(e);return o.set(t,{x:n.origMX+r/a,y:n.origMY+i/a}),o})},[]),Ke=(0,A.useCallback)((e,t)=>{re.current?.nodeId===t&&(re.current=null)},[]),qe=(0,A.useRef)(null),Je=(0,A.useRef)(null),Ye=(0,A.useRef)(null),Xe=(0,A.useRef)(null),Ze=(0,A.useRef)([]),Qe=(0,A.useRef)([]),$e=(0,A.useRef)(0),et=(0,A.useRef)(new Map),tt=(0,A.useRef)(w),nt=(0,A.useRef)(null),[rt,it]=(0,A.useState)(100),[at,ot]=(0,A.useState)(!0),st=(0,A.useCallback)((e,t,n=0,r=!1)=>{let i=N.find(t=>t.id===e);if(!i||!q(i))return;let a=G.current.get(i.source),o=G.current.get(i.target);if(!a||!o)return;let s=Pe(a,o),c=r&&Math.random()<.65?.15+Math.random()*.55:0,l=c>0?120+Math.random()*700:0,u=r&&Math.random()<.12,d=u?.25+Math.random()*.55:0;setTimeout(()=>{Ze.current.push({id:`${e}-${Date.now()}-${Math.random()}`,waypoints:s,progress:0,speed:9e-4+Math.random()*4e-4,color:t,pulse:0,sparkCooldown:0,tgtNodeId:i.target,chained:r,arrived:!1,stallAt:c,stallRemaining:l,timedOut:u,timeoutAt:d})},n)},[N,q]),ct=(0,A.useCallback)((e,t,n=0)=>{let r=Date.now();if(r-(et.current.get(e)??0)<1800)return;et.current.set(e,r);let i=0;for(let r of N)r.source===e&&q(r)&&(st(r.id,t,n+i*60,!0),i++)},[N,q,st]);(0,A.useEffect)(()=>{if(!c||!P.has(c))return;let e=()=>{let e=0;for(let t of N)t.source===c&&q(t)&&(st(t.id,`#a855f7`,e*80,!0),e++)};e();let t=window.setInterval(e,700);return()=>window.clearInterval(t)},[c,l,N,q,P,st]),(0,A.useEffect)(()=>{let e;function t(n){e=requestAnimationFrame(t);let r=Xe.current;if(!r)return;let i=Math.min(n-$e.current,50);$e.current=n;let a=r.getContext(`2d`);if(!a)return;a.clearRect(0,0,r.width,r.height);let o=tt.current,s=Math.max(.6,o.k);a.globalCompositeOperation=`lighter`;let l=[],u=M.length<=40||c,d=Ze.current.filter(e=>e.progress<1).length,f=Math.max(.12,1-Math.max(0,d-4)*.055);for(let e of Ze.current){if(!u)continue;if(e.timedOut&&e.timeoutAt>0&&e.progress>=e.timeoutAt){let t=e.waypoints.map(e=>je(e.x,e.y,o)),n=Me(e.timeoutAt,t);for(let e=0;e<18;e++){let t=e/18*Math.PI*2+Math.random()*.4,r=.06+Math.random()*.14;Qe.current.push({x:n.x,y:n.y,vx:Math.cos(t)*r,vy:Math.sin(t)*r,life:1,decay:.0014+Math.random()*.001,size:(1.4+Math.random()*2)*s,color:`#ef4444`})}continue}let t=e.stallAt>0&&e.progress>=e.stallAt&&e.stallRemaining>0;t?e.stallRemaining-=i:e.progress<1&&(e.progress=Math.min(1,e.progress+e.speed*f*i));let r=e.waypoints.map(e=>je(e.x,e.y,o)),c=r[r.length-1],d=Me(e.progress,r);if(!isFinite(d.x)||!isFinite(d.y)){l.push(e);continue}let p=e.stallAt>0&&e.stallRemaining>0?Math.min(1,e.stallRemaining/400):0,m=t?p>.5?`#f59e0b`:`#fb923c`:e.color;for(let t=18;t>=1;t--){let n=e.progress-t/18*.09;if(n<0)continue;let i=Me(n,r),o=1-t/18,c=o*o*.55,l=(.8+o*2.6)*s;a.beginPath(),a.arc(i.x,i.y,l,0,Math.PI*2),a.fillStyle=m+Ae(c*255),a.fill()}a.save(),a.shadowBlur=(t?34:24)*s,a.shadowColor=m,a.beginPath(),a.arc(d.x,d.y,5*s,0,Math.PI*2),a.fillStyle=m+`66`,a.fill(),a.restore();let h=a.createRadialGradient(d.x,d.y,0,d.x,d.y,8*s);if(h.addColorStop(0,`#ffffffee`),h.addColorStop(.35,m+`cc`),h.addColorStop(1,m+`00`),a.fillStyle=h,a.beginPath(),a.arc(d.x,d.y,8*s,0,Math.PI*2),a.fill(),t){let e=.5+.5*Math.sin(n*.012);a.beginPath(),a.arc(d.x,d.y,(10+e*8)*s,0,Math.PI*2),a.strokeStyle=`#f59e0b`+Ae(e*160),a.lineWidth=1.5*s,a.stroke()}let g=1+.18*Math.sin(n*.018+e.progress*12);if(a.beginPath(),a.arc(d.x,d.y,2.2*s*g,0,Math.PI*2),a.fillStyle=`#ffffff`,a.fill(),e.progress<1&&(e.sparkCooldown-=i,e.sparkCooldown<=0)){e.sparkCooldown=35+Math.random()*40;let t=Math.random()*Math.PI*2,n=.02+Math.random()*.04;Qe.current.push({x:d.x,y:d.y,vx:Math.cos(t)*n,vy:Math.sin(t)*n,life:1,decay:.0028+Math.random()*.0012,size:(.8+Math.random()*1.4)*s,color:m})}if(e.progress>=1){if(!e.arrived){e.arrived=!0;for(let t=0;t<14;t++){let n=t/14*Math.PI*2+Math.random()*.3,r=.08+Math.random()*.12;Qe.current.push({x:c.x,y:c.y,vx:Math.cos(n)*r,vy:Math.sin(n)*r,life:1,decay:.0018+Math.random()*8e-4,size:(1.2+Math.random()*1.6)*s,color:e.color})}if(e.chained){let t=P.get(e.tgtNodeId),n=t&&L[String(t.data.type)]||e.color;ct(e.tgtNodeId,n,120)}}if(e.pulse=Math.min(1,e.pulse+.025),e.pulse<1){for(let t=0;t<3;t++){let n=e.pulse-t*.18;if(n<=0||n>=1)continue;let r=(3+n*38)*s,i=(1-n)*(1-n)*220;a.beginPath(),a.arc(c.x,c.y,r,0,Math.PI*2),a.strokeStyle=e.color+Ae(i),a.lineWidth=1.5*s,a.stroke()}let t=(1-e.pulse)*(1-e.pulse)*255;a.save(),a.shadowBlur=18*s,a.shadowColor=e.color,a.beginPath(),a.arc(c.x,c.y,4*s,0,Math.PI*2),a.fillStyle=`#ffffff`+Ae(t),a.fill(),a.restore(),l.push(e)}}else l.push(e)}let p=[];for(let e of Qe.current){if(e.x+=e.vx*i,e.y+=e.vy*i,e.vx*=.985,e.vy*=.985,e.life-=e.decay*i,e.life<=0)continue;let t=Math.max(.3,e.size*e.life);a.beginPath(),a.arc(e.x,e.y,t,0,Math.PI*2),a.fillStyle=e.color+Ae(e.life*220),a.fill(),p.push(e)}Qe.current=p,a.globalCompositeOperation=`source-over`,Ze.current=l}return $e.current=performance.now(),e=requestAnimationFrame(t),()=>cancelAnimationFrame(e)},[P,ct,M.length,c]),(0,A.useEffect)(()=>{M.length>40&&!c&&(Ze.current=[],Qe.current=[])},[M.length,c]),(0,A.useEffect)(()=>{let e=qe.current,t=Xe.current;if(!e||!t)return;let n=new ResizeObserver(()=>{t.width=e.clientWidth,t.height=e.clientHeight});return n.observe(e),t.width=e.clientWidth,t.height=e.clientHeight,()=>n.disconnect()},[]),(0,A.useEffect)(()=>{let e=Je.current,t=Ye.current;if(!e||!t)return;let n=O().scaleExtent([.02,5]).filter(e=>!re.current&&(!e.ctrlKey||e.type===`wheel`)&&!(e.type===`wheel`&&Be(e))&&!e.button).on(`zoom`,e=>{tt.current=e.transform,S(t).attr(`transform`,e.transform.toString()),it(Math.round(e.transform.k*100))});S(e).call(n),nt.current=n;let r=t=>{if(!Be(t))return;t.preventDefault();let r=tt.current.k;S(e).call(n.translateBy,-t.deltaX/r,-t.deltaY/r)};return e.addEventListener(`wheel`,r,{passive:!1}),()=>{S(e).on(`.zoom`,null),e.removeEventListener(`wheel`,r)}},[]);let lt=(0,A.useCallback)(()=>{let e=Je.current,t=qe.current,n=nt.current;if(!e||!t||!n||!M.length)return;let r=1/0,i=1/0,a=-1/0,o=-1/0;for(let e of M)r=Math.min(r,e.x-e.width/2),a=Math.max(a,e.x+e.width/2),i=Math.min(i,e.y-e.height/2),o=Math.max(o,e.y+e.height/2);let s=a-r+96,c=o-i+96,l=t.clientWidth,u=t.clientHeight,d=Math.min(l/s,u/c,2)*.92,f=(r+a)/2,p=(i+o)/2,m=l/2-d*f,h=u/2-d*p,g=w.translate(m,h).scale(d);S(e).call(n.transform,g)},[M]),ut=(0,A.useCallback)(e=>{let t=Je.current,n=nt.current;!t||!n||S(t).transition().duration(150).call(n.scaleBy,e)},[]),dt=(0,A.useCallback)(async e=>{let t=qe.current;return t?(await(0,te.default)(t,{scale:e?.scale??2,useCORS:!0,backgroundColor:v?`#0a0c10`:`#f6f7f9`,ignoreElements:e=>e.classList?.contains(`g-rails`)||e.classList?.contains(`g-toolbar`)||e.classList?.contains(`g-breadcrumb`)||e.classList?.contains(`g-zoom`)})).toDataURL(`image/png`):null},[v]);(0,A.useEffect)(()=>(s.current={fit:lt,toPng:dt},()=>{s.current=null}),[s,lt,dt]);let ft=(0,A.useRef)(!1);return(0,A.useEffect)(()=>{ft.current=!1},[e]),(0,A.useEffect)(()=>{if(!M.length||ft.current)return;ft.current=!0;let e=requestAnimationFrame(()=>lt());return()=>cancelAnimationFrame(e)},[M.length,lt,e]),(0,X.jsxs)(`div`,{ref:qe,className:`g-canvas ${at?``:`g-no-edge-labels`}`,style:{position:`relative`,width:`100%`,height:`100%`},children:[(0,X.jsxs)(`svg`,{ref:Je,role:`img`,"aria-label":`Execution graph`,style:{width:`100%`,height:`100%`,display:`block`,cursor:`grab`,touchAction:`none`},children:[(0,X.jsxs)(`defs`,{children:[(0,X.jsx)(`marker`,{id:`arrow-def`,markerWidth:`9`,markerHeight:`9`,refX:`8`,refY:`4.5`,orient:`auto`,markerUnits:`strokeWidth`,children:(0,X.jsx)(`path`,{d:`M0,0 L0,9 L9,4.5 z`,fill:b})}),(0,X.jsx)(`marker`,{id:`arrow-hi`,markerWidth:`9`,markerHeight:`9`,refX:`8`,refY:`4.5`,orient:`auto`,markerUnits:`strokeWidth`,children:(0,X.jsx)(`path`,{d:`M0,0 L0,9 L9,4.5 z`,fill:ie})}),(0,X.jsx)(`marker`,{id:`arrow-st`,markerWidth:`9`,markerHeight:`9`,refX:`8`,refY:`4.5`,orient:`auto`,markerUnits:`strokeWidth`,children:(0,X.jsx)(`path`,{d:`M0,0 L0,9 L9,4.5 z`,fill:`#a855f7`})}),(0,X.jsx)(`marker`,{id:`arrow-region`,markerWidth:`8`,markerHeight:`8`,refX:`7`,refY:`4`,orient:`auto`,markerUnits:`strokeWidth`,children:(0,X.jsx)(`path`,{d:`M0,0.5 L0,7.5 L8,4 z`,fill:`context-stroke`})})]}),(0,X.jsxs)(`g`,{ref:Ye,children:[(0,X.jsx)(`rect`,{x:-1e5,y:-1e5,width:2e5,height:2e5,fill:`transparent`,onClick:Ue,style:{pointerEvents:`all`}}),U.map(e=>{let t=I[e.kind]??`#d99a2b`,n=ne[e.kind]??`6 5`,r=`${ue[e.kind]} ${e.index}`;return(0,X.jsxs)(`g`,{style:{pointerEvents:`none`},children:[e.pure&&(0,X.jsx)(`polygon`,{points:e.points.map(([e,t])=>`${e},${t}`).join(` `),fill:t,fillOpacity:.05,stroke:t,strokeWidth:1.5,strokeDasharray:n,opacity:.55}),!e.pure&&e.members.map(e=>(0,X.jsx)(`rect`,{x:e.x-e.width/2-5,y:e.y-e.height/2-5,width:e.width+10,height:e.height+10,rx:13,fill:`none`,stroke:t,strokeWidth:1.5,strokeDasharray:n,opacity:.85},e.id)),e.ordered&&e.members.slice(1).map((n,r)=>{let i=he(e.members[r],n);return i?(0,X.jsx)(`line`,{x1:i.x1,y1:i.y1,x2:i.x2,y2:i.y2,stroke:t,strokeWidth:1.4,opacity:.75,markerEnd:`url(#arrow-region)`},`${e.id}-${n.id}-step`):null}),e.pure?(0,X.jsx)(`text`,{x:Math.min(...e.points.map(([e])=>e))+10,y:Math.min(...e.points.map(([,e])=>e))-6-(me.get(e.id)??0)*11,fontSize:10,fontFamily:`ui-monospace, monospace`,fill:t,opacity:.9,children:r}):e.members.map((n,i)=>(0,X.jsx)(`text`,{x:n.x-n.width/2-4,y:n.y-n.height/2-10-(me.get(`${e.id}|${n.id}`)??0)*11,fontSize:9,fontFamily:`ui-monospace, monospace`,fill:t,opacity:.85,children:e.ordered?`${r} · ${i+1}`:r},`${n.id}-label`))]},e.id)}),N.map(e=>{if(!q(e)||z.has(e.source)||J.has(e.source)||J.has(e.target))return null;let t=W.get(e.source),n=W.get(e.target);if(!t||!n)return null;let{d:r,lx:i,ly:a}=Le(t,n),o={x:i,y:a},s=Re(e.data,v),c=Ne.has(e.id),l=Te.edges.has(e.id),u=y,d=1.75,f=`url(#arrow-def)`,p=1;return l&&(u=`#a855f7`,d=2,f=`url(#arrow-st)`,p=.7),c&&(u=ie,d=1.5,f=`url(#arrow-hi)`,p=1),Ce&&!(Ce.has(e.source)||Ce.has(e.target))&&(p*=.02),(0,X.jsxs)(`g`,{children:[(0,X.jsx)(`path`,{d:r,fill:`none`,stroke:u,strokeWidth:d,strokeLinecap:`round`,strokeLinejoin:`round`,opacity:p,markerEnd:f,style:{pointerEvents:`auto`}}),s&&p>.05&&(0,X.jsx)(`g`,{className:`g-edge-label`,transform:`translate(${o.x},${o.y})`,children:(0,X.jsx)(`text`,{textAnchor:`middle`,dominantBaseline:`middle`,fill:s.fill,fontSize:9,fontFamily:`ui-monospace, monospace`,style:{pointerEvents:`none`},children:(0,X.jsx)(`tspan`,{dx:0,dy:-8,paintOrder:`stroke fill`,stroke:s.bg,strokeWidth:6,strokeLinejoin:`round`,children:s.text})})})]},e.id)}),H.map(e=>{if(J.has(e.id))return null;let t=K(e.data.type),n=Ce&&!Ce.has(e.id),r=t?n?.07:1:0,i=Te.nodes.has(e.id),a=Ie===e.id,{bg:o,border:s,borderW:c,accent:l}=ze(e,v,u,a,i,d),{className:p,method:m}=_e(String(e.data.label??e.id),e.data.method),h=m&&!m.includes(`(`)?m+`()`:m,g=String(e.data.type??``),_=e.width,y=e.height,b=_/2,x=y/2,S=v?`#e6edf3`:`#0d1117`,C=v?`rgba(255,255,255,0.5)`:`rgba(0,0,0,0.5)`,w=e.data.security,T=!!(e.data.hasN1||e.data.fatMethod||e.data.fatClass||w&&((w.issues?.length??0)>0||w.riskLevel&&w.riskLevel!==`none`)),E=e.data.httpCalls??[],D=Array.from(new Set(E.map(e=>e.host||e.configKey||`external`))).map(e=>e.length>14?e.slice(0,13)+`…`:e),O=p.length>24?p.slice(0,23)+`…`:p,k=h.length>26?h.slice(0,25)+`…`:h;return(0,X.jsxs)(`g`,{className:`g-node`,transform:`translate(${e.x},${e.y})`,opacity:r,style:{pointerEvents:t&&r>.05?`auto`:`none`,cursor:`grab`},onPointerDown:t=>We(t,e.id,e.x,e.y),onPointerMove:t=>Ge(t,e.id),onPointerUp:t=>Ke(t,e.id),onClick:t=>{t.stopPropagation(),R.current||He(e.id)},children:[a&&(0,X.jsx)(`rect`,{x:-b-3,y:-x-3,width:_+6,height:y+6,rx:f?7:13,fill:`none`,stroke:l,strokeWidth:6,opacity:.15}),(0,X.jsx)(`rect`,{x:-b,y:-x,width:_,height:y,rx:f?6:10,fill:o,stroke:s,strokeWidth:c,filter:e.data.hasN1&&!u?`drop-shadow(0 0 8px rgba(244,67,54,0.4))`:void 0}),T&&(0,X.jsxs)(`g`,{style:{pointerEvents:`none`},children:[(0,X.jsx)(`circle`,{cx:b-3,cy:-x+3,r:10,fill:`#ef4444`,opacity:.22}),(0,X.jsx)(`circle`,{cx:b-3,cy:-x+3,r:5,fill:`#ef4444`,stroke:o,strokeWidth:1.5})]}),f?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:-b+10,cy:0,r:3.5,fill:l}),(0,X.jsx)(`text`,{x:-b+20,y:0,fontSize:11,fontWeight:700,fontFamily:`ui-sans-serif, system-ui, -apple-system, sans-serif`,fill:S,dominantBaseline:`middle`,style:{pointerEvents:`none`},children:O}),!!e.data.hasN1&&(0,X.jsx)(`text`,{x:b-6,y:0,fontSize:9,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:`#F44336`,dominantBaseline:`middle`,style:{pointerEvents:`none`},children:`N+1`}),D.length>0&&!e.data.hasN1&&(0,X.jsx)(`text`,{x:b-6,y:0,fontSize:9,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:`#38bdf8`,dominantBaseline:`middle`,style:{pointerEvents:`none`},children:`🌐`}),d&&e.data.security&&(0,X.jsx)(`text`,{x:e.data.hasN1?b-28:b-6,y:0,fontSize:8,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:(B[e.data.security.exposure]??B.public).accent,dominantBaseline:`middle`,style:{pointerEvents:`none`},children:(B[e.data.security.exposure]??B.public).label.toUpperCase()})]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:-b+14,cy:-x+18,r:4,fill:l}),(0,X.jsx)(`text`,{x:-b+24,y:-x+22,fontSize:10,fontFamily:`ui-monospace, monospace`,fill:l,opacity:.9,style:{pointerEvents:`none`},children:g}),!!e.data.hasN1&&(0,X.jsx)(`text`,{x:b-10,y:-x+22,fontSize:10,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:`#F44336`,style:{pointerEvents:`none`},children:`N+1`}),d&&e.data.security&&(()=>{let t=e.data.security,n=B[t.exposure]??B.public,r=V[t.riskLevel]??V.none;return(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`text`,{x:e.data.hasN1?b-42:b-10,y:-x+22,fontSize:9,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:n.accent,style:{pointerEvents:`none`},children:[`🔒 `,n.label.toUpperCase()]}),t.riskLevel!==`none`&&(0,X.jsxs)(`text`,{x:b-10,y:-x+38,fontSize:8,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:r,style:{pointerEvents:`none`},children:[`⚠ `,t.issues.length,` issue`,t.issues.length===1?``:`s`]})]})})(),(0,X.jsx)(`text`,{x:-b+14,y:-x+46,fontSize:13,fontWeight:700,fontFamily:`ui-sans-serif, system-ui, -apple-system, sans-serif`,fill:S,style:{pointerEvents:`none`},children:O}),k&&(0,X.jsxs)(`text`,{x:-b+14,y:-x+64,fontSize:11,fontFamily:`ui-monospace, monospace`,fill:C,style:{pointerEvents:`none`},children:[`↻ `,k]}),D.length>0&&(0,X.jsxs)(`text`,{x:b-10,y:x-10,fontSize:9,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:`#38bdf8`,style:{pointerEvents:`none`},children:[`🌐 `,D[0],D.length>1?` +${D.length-1}`:``]})]}),(z.has(e.id)||(ye.get(e.id)??0)>4)&&(0,X.jsxs)(`g`,{transform:`translate(${b+2}, 0)`,onPointerDown:e=>e.stopPropagation(),onClick:t=>be(t,e.id),style:{cursor:`pointer`,pointerEvents:`all`},children:[(0,X.jsx)(`rect`,{x:0,y:-10,width:64,height:20,rx:10,fill:z.has(e.id)?l:v?`rgba(255,255,255,0.12)`:`rgba(0,0,0,0.10)`,stroke:l,strokeWidth:1}),(0,X.jsx)(`text`,{x:32,y:0,textAnchor:`middle`,dominantBaseline:`middle`,fill:z.has(e.id)?`#fff`:l,fontSize:10,fontWeight:700,fontFamily:`ui-monospace, monospace`,style:{pointerEvents:`none`},children:z.has(e.id)?`▶ ${Se.get(e.id)??ye.get(e.id)} hidden`:`▾ fold`})]})]},e.id)})]})]}),(0,X.jsx)(`canvas`,{ref:Xe,style:{position:`absolute`,top:0,left:0,pointerEvents:`none`,width:`100%`,height:`100%`}}),(u||d)&&(0,X.jsxs)(`div`,{className:`g-legends`,children:[u&&(0,X.jsxs)(`div`,{className:`cc-legend`,children:[(0,X.jsx)(`div`,{className:`cc-legend-title`,children:`Cyclomatic Complexity`}),ce.map(e=>(0,X.jsxs)(`div`,{className:`cc-legend-row`,children:[(0,X.jsx)(`span`,{className:`cc-legend-swatch`,style:{background:e.border}}),(0,X.jsx)(`span`,{className:`cc-legend-label`,style:{color:e.border},children:e.label}),(0,X.jsx)(`span`,{className:`cc-legend-range`,children:e.max===1/0?`≥${e.min}`:`${e.min}–${e.max}`})]},e.label))]}),d&&(0,X.jsxs)(`div`,{className:`cc-legend`,children:[(0,X.jsx)(`div`,{className:`cc-legend-title`,children:`🔒 Security Surface`}),Object.entries(B).map(([e,t])=>(0,X.jsxs)(`div`,{className:`cc-legend-row`,children:[(0,X.jsx)(`span`,{className:`cc-legend-swatch`,style:{background:t.border}}),(0,X.jsx)(`span`,{className:`cc-legend-label`,style:{color:t.accent},children:t.label})]},e)),(0,X.jsx)(`div`,{className:`cc-legend-title`,style:{marginTop:`8px`},children:`Risk Level`}),[{key:`critical`,label:`Critical`,color:V.critical},{key:`high`,label:`High`,color:V.high},{key:`medium`,label:`Medium`,color:V.medium},{key:`none`,label:`Clean`,color:V.none}].map(({key:e,label:t,color:n})=>(0,X.jsxs)(`div`,{className:`cc-legend-row`,children:[(0,X.jsx)(`span`,{className:`cc-legend-swatch`,style:{background:n}}),(0,X.jsx)(`span`,{className:`cc-legend-label`,style:{color:n},children:t})]},e))]})]}),(0,X.jsx)(`div`,{className:`g-rails`,"aria-hidden":!0,children:[{n:1,label:`Route`,c:`var(--nc-route)`},{n:2,label:`Controller`,c:`var(--nc-controller)`},{n:3,label:`Action`,c:`var(--nc-action)`},{n:4,label:`Service · View`,c:`var(--nc-service)`},{n:5,label:`Interface`,c:`var(--nc-interface)`},{n:6,label:`Implementation`,c:`var(--nc-provider)`}].map(e=>(0,X.jsxs)(`div`,{className:`g-rail`,children:[(0,X.jsx)(`span`,{className:`g-rail-pill`,style:{"--rc":e.c},children:e.n}),(0,X.jsx)(`span`,{className:`g-rail-label`,children:e.label})]},e.n))}),(0,X.jsxs)(`div`,{className:`g-toolbar`,children:[(0,X.jsxs)(`select`,{className:`g-tool-select`,value:t,onChange:e=>p(e.target.value),title:`Layout algorithm`,children:[(0,X.jsx)(`option`,{value:`dagre`,children:`Hierarchical`}),(0,X.jsx)(`option`,{value:`breadthfirst`,children:`Breadth-first`}),(0,X.jsx)(`option`,{value:`cose-bilkent`,children:`Force`}),(0,X.jsx)(`option`,{value:`circle`,children:`Circle`}),(0,X.jsx)(`option`,{value:`grid`,children:`Grid`})]}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${n===`TB`?`g-tool--on`:``}`,onClick:()=>m(n===`TB`?`LR`:`TB`),title:`Toggle orientation`,children:n===`TB`?`Top-down`:`Left-right`}),(0,X.jsx)(`span`,{className:`g-tool-sep`}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${at?`g-tool--on`:``}`,onClick:()=>ot(e=>!e),children:`Edge labels`}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${u?`g-tool--on`:``}`,onClick:h,children:`Complexity`}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${d?`g-tool--on`:``}`,onClick:g,children:`Security`}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${f?`g-tool--on`:``}`,onClick:_,children:`Compact`})]}),(0,X.jsxs)(`div`,{className:`g-breadcrumb`,children:[[{label:`Route`,c:`var(--nc-route)`},{label:`Controller`,c:`var(--nc-controller)`},{label:`Action`,c:`var(--nc-action)`},{label:`Service`,c:`var(--nc-service)`},{label:`Interface`,c:`var(--nc-interface)`},{label:`Impl`,c:`var(--nc-provider)`}].map((e,t,n)=>(0,X.jsxs)(`span`,{className:`g-crumb`,children:[(0,X.jsx)(`span`,{className:`g-crumb-dot`,style:{background:e.c}}),e.label,t{let t=U.filter(t=>t.kind===e).length;return t===0?null:(0,X.jsxs)(`span`,{className:`g-crumb g-crumb--aside`,children:[(0,X.jsx)(`span`,{className:`g-crumb-sep`,children:`·`}),(0,X.jsx)(`span`,{className:`g-crumb-dot g-crumb-dot--dashed`,style:{borderColor:I[e]}}),t===1?ue[e]:`${t} ${de[e]}`]},e)})]}),(0,X.jsxs)(`div`,{className:`g-zoom`,children:[(0,X.jsx)(`button`,{type:`button`,className:`g-zoom-btn`,onClick:()=>ut(.8),"aria-label":`Zoom out`,children:`−`}),(0,X.jsxs)(`span`,{className:`g-zoom-pct`,children:[rt,`%`]}),(0,X.jsx)(`button`,{type:`button`,className:`g-zoom-btn`,onClick:()=>ut(1.25),"aria-label":`Zoom in`,children:`+`}),(0,X.jsx)(`button`,{type:`button`,className:`g-zoom-btn g-zoom-fit`,onClick:()=>lt(),"aria-label":`Fit to view`,children:`⊡`})]})]})}var He=`modulepreload`,Ue=function(e){return`/_laravel-brain/`+e},We={},Ge=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=Ue(t,n),t in We)return;We[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:He,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},Ke=`route.middleware.controller.action.action_class.service.validation_request.repository.model.job.event.view.mail.notification.enum.interface.trait.abstract_class.service_provider.ai_agent.ai_tool.filament_panel.filament_resource.filament_page.filament_page_method.filament_widget.filament_relation_manager`.split(`.`);function qe(e,t){let n=[];n.push(`%%{init: {'theme': 'dark', 'themeVariables': {`),n.push(` 'background': '#0a0c10',`),n.push(` 'mainBkg': '#0d1117',`),n.push(` 'lineColor': 'rgba(255,255,255,0.35)',`),n.push(` 'edgeLabelBackground': '#111218',`),n.push(` 'edgeLabelColor': 'rgba(255,255,255,0.5)'`),n.push(`}}}%%`),n.push(`%% Laravel Brain — ${t}`),n.push(`flowchart TD`),n.push(``);let r=new Map,i=new Set,a=e=>{if(r.has(e))return r.get(e);let t=e.replace(/[^a-zA-Z0-9_]/g,`_`).replace(/^_+/,``).replace(/_+$/,``).substring(0,40);t||=`node`;let n=t,a=0;for(;i.has(n);)n=`${t}_${++a}`;return i.add(n),r.set(e,n),n},o=new Map;for(let t of e.nodes)o.has(t.type)||o.set(t.type,[]),o.get(t.type).push(t);let s=[...new Set([...Ke,...o.keys()])].filter(e=>(o.get(e)?.length??0)>0);for(let e of s){let t=o.get(e);n.push(` %% ${e}`);for(let e of t){let t=a(e.id),r=Je(e);n.push(` ${t}["${rt(r)}"]`)}n.push(``)}n.push(` %% Edges`);for(let t of e.edges){let e=a(t.source),r=a(t.target),i=t.label?`|"${rt(t.label)}"| `:``;n.push(` ${e} -->${i}${r}`)}n.push(``),n.push(` %% Styles`);for(let e of s){let t=L[e]??`#c9d1d9`,r=R[e]??`#0d1117`;n.push(` classDef cls_${e} fill:${r},stroke:${t},stroke-width:2px,color:#e6edf3`)}n.push(``);for(let e of s){let t=o.get(e).map(e=>a(e.id)).join(`,`);n.push(` class ${t} cls_${e}`)}return n.join(`
-`)}function Je(e){let t=String(e.label??``),n=e.data?.method,{className:r,method:i}=_e(t,n),a=i&&!i.includes(`(`)?i+`()`:i,o=[`● ${e.type}`,r];return a&&o.push(`↻ ${a}`),o.join(`
-`)}function Ye(e,t){let n=[`%% Method Flow — ${t}`,`flowchart TD`],r=0,i=()=>`s${r++}`,a=i();n.push(` ${a}([" 🚀 ${rt(t)} "])`);let o=(e,t)=>{let r=t;for(let t of e){let e=i();if(t.type===`if`){let[a,s]=[`{`,`}`];if(n.push(` ${e}${a}"${rt(t.label)}"${s}`),n.push(` ${r} --> ${e}`),n.push(` class ${e} cls_if`),t.then&&t.then.length>0){let r=i(),a=t.then[0];n.push(` ${r}${et(a.type)}"${rt(a.label)}"${tt(a.type)}`),n.push(` ${e} -->|"yes"| ${r}`),n.push(` class ${r} cls_${a.type}`),o(t.then.slice(1),r)}if(t.else&&t.else.length>0){let r=i(),a=t.else[0];n.push(` ${r}${et(a.type)}"${rt(a.label)}"${tt(a.type)}`),n.push(` ${e} -->|"no"| ${r}`),n.push(` class ${r} cls_${a.type}`),o(t.else.slice(1),r)}r=e}else if(t.type===`loop`){let i=t.n1?` ⚠️ N+1 `:``,a=t.cache?` [cache ${t.cache.kind}] `:``;n.push(` ${e}[/"${i}${a}${rt(t.label)}"/]`),n.push(` ${r} --> ${e}`),n.push(` class ${e} ${t.n1?`cls_n1`:t.cache?`cls_cache`:`cls_loop`}`),t.body&&t.body.length>0&&o(t.body,e),r=e}else{let[i,a]=[et(t.type),tt(t.type)],o=nt(t.type),s=t.n1?` ⚠️ N+1 `:``,c=t.cache?` [cache ${t.cache.kind}] `:``;n.push(` ${e}${i}"${s}${c}${o}${rt(t.label)}"${a}`),n.push(` ${r} --> ${e}`),n.push(` class ${e} ${t.n1?`cls_n1`:`cls_${t.type}`}`),r=e}}return r};return o(e,a),n.push(``),n.push(` %% STYLES`),n.push(` classDef cls_call fill:#0d47a1,stroke:#2196F3,color:#fff`),n.push(` classDef cls_assign fill:#212121,stroke:#616161,color:#ccc`),n.push(` classDef cls_return fill:#1b5e20,stroke:#4CAF50,color:#fff`),n.push(` classDef cls_throw fill:#b71c1c,stroke:#F44336,color:#fff`),n.push(` classDef cls_if fill:#f9a825,stroke:#fbc02d,color:#000`),n.push(` classDef cls_loop fill:#6a1b9a,stroke:#9c27b0,color:#fff`),n.push(` classDef cls_n1 fill:#b71c1c,stroke:#ff5252,color:#fff`),n.push(` classDef cls_dispatch fill:#bf360c,stroke:#FF5722,color:#fff`),n.push(` classDef cls_event fill:#0e47a1,stroke:#00BCD4,color:#fff`),n.push(` classDef cls_cache fill:#004d40,stroke:#009688,color:#fff`),n.join(`
-`)}function Xe(e,t){Qe(new Blob([e],{type:`text/plain`}),t)}function Ze(e,t){let n=document.createElement(`a`);n.href=e,n.download=t,n.click()}function Qe(e,t){let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,r.click(),setTimeout(()=>URL.revokeObjectURL(n),2e3)}async function $e(t,n=`#0d0f14`){let{default:r}=await Ge(async()=>{let{default:t}=await import(`./vendor-utils-D7YtnGoz.js`).then(t=>e(t.t(),1));return{default:t}},__vite__mapDeps([0,1]));return(await r(t,{backgroundColor:n,scale:2,useCORS:!0,logging:!1})).toDataURL(`image/png`)}function et(e){switch(e){case`return`:return`([`;case`throw`:return`([`;case`dispatch`:return`[[`;case`event`:return`((`;default:return`[`}}function tt(e){switch(e){case`return`:return`])`;case`throw`:return`])`;case`dispatch`:return`]]`;case`event`:return`))`;default:return`]`}}function nt(e){switch(e){case`call`:return`→ `;case`assign`:return`= `;case`return`:return`◀ `;case`throw`:return`⚠ `;case`dispatch`:return`⚡ `;case`event`:return`📡 `;case`cache`:return`⛃ `;default:return``}}function rt(e){return e.replace(/"/g,`'`).replace(/\n/g,`\\n`).replace(/[<>]/g,e=>e===`<`?`<`:`>`)}function it({mermaidCode:e,filename:t,title:n,onClose:r}){let[i,a]=(0,A.useState)(!1),o=(0,A.useRef)(null);return(0,A.useEffect)(()=>{let e=e=>{e.key===`Escape`&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r]),(0,X.jsx)(`div`,{className:`export-overlay`,onClick:e=>{e.target===e.currentTarget&&r()},children:(0,X.jsxs)(`div`,{className:`export-modal`,children:[(0,X.jsxs)(`div`,{className:`export-modal-header`,children:[(0,X.jsxs)(`div`,{className:`export-modal-title`,children:[(0,X.jsx)(`span`,{className:`export-modal-icon`,children:`🗺`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:n}),(0,X.jsx)(`span`,{className:`export-modal-sub`,children:`Mermaid Flowchart`})]})]}),(0,X.jsx)(`button`,{className:`export-modal-close`,onClick:r,title:`Close (Esc)`,children:`×`})]}),(0,X.jsxs)(`div`,{className:`export-modal-actions`,children:[(0,X.jsx)(`button`,{className:`export-btn export-btn--primary`,onClick:async()=>{try{await navigator.clipboard.writeText(e),a(!0),setTimeout(()=>a(!1),2e3)}catch{o.current?.select(),document.execCommand(`copy`),a(!0),setTimeout(()=>a(!1),2e3)}},children:i?`✓ Copied!`:`⎘ Copy Code`}),(0,X.jsx)(`button`,{className:`export-btn export-btn--secondary`,onClick:()=>Xe(e,t),children:`↓ Download .mmd`}),(0,X.jsx)(`button`,{className:`export-btn export-btn--accent`,onClick:()=>{let t=JSON.stringify({code:e,mermaid:`{}`,autoSync:!0}),n=btoa(unescape(encodeURIComponent(t)));window.open(`https://mermaid.live/edit#base64:${n}`,`_blank`)},children:`↗ Open in Mermaid Live`})]}),(0,X.jsxs)(`div`,{className:`export-modal-hint`,children:[`Paste this code at`,` `,(0,X.jsx)(`a`,{href:`https://mermaid.live`,target:`_blank`,rel:`noreferrer`,children:`mermaid.live`}),` `,`to render the diagram, or use any Mermaid-compatible tool.`]}),(0,X.jsxs)(`div`,{className:`export-code-wrapper`,children:[(0,X.jsx)(`div`,{className:`export-code-lang`,children:`mermaid`}),(0,X.jsx)(`textarea`,{ref:o,className:`export-code`,value:e,readOnly:!0,spellCheck:!1,onClick:e=>e.target.select()})]}),(0,X.jsxs)(`div`,{className:`export-modal-stats`,children:[(0,X.jsxs)(`span`,{children:[e.split(`
-`).length,` lines`]}),(0,X.jsxs)(`span`,{children:[(e.length/1024).toFixed(1),` KB`]})]})]})})}function at({steps:e,title:t,isFatMethod:n}){let[r,i]=(0,A.useState)(!1),[a,o]=(0,A.useState)(!1),s=(0,A.useRef)(null);if(!e||e.length===0)return(0,X.jsx)(`div`,{className:`flowchart-empty`,children:(0,X.jsx)(`span`,{children:`No flow data available`})});let c=t??`method`;return(0,X.jsxs)(X.Fragment,{children:[n&&(0,X.jsx)(`div`,{className:`flowchart-fat-banner`,title:`Fat Method: this method exceeds complexity or line-count thresholds`,children:`🧱 Fat Method — consider breaking this into smaller methods`}),(0,X.jsxs)(`div`,{className:`flowchart-export-bar`,children:[(0,X.jsxs)(`button`,{className:`flowchart-export-btn`,onClick:async()=>{if(s.current){o(!0);try{Ze(await $e(s.current),`${c.replace(/[^a-z0-9]/gi,`_`)}_flow.png`)}finally{o(!1)}}},disabled:a,title:`Export as PNG`,children:[a?`⏳`:`🖼`,` PNG`]}),(0,X.jsx)(`button`,{className:`flowchart-export-btn`,onClick:()=>i(!0),title:`Export as Mermaid`,children:`🗺 Mermaid`})]}),(0,X.jsxs)(`div`,{className:`flowchart-root`,ref:s,children:[t&&(0,X.jsx)(`div`,{className:`flowchart-title`,children:t}),(0,X.jsx)(ot,{steps:e})]}),r&&(0,X.jsx)(it,{mermaidCode:Ye(e,c),filename:`${c.replace(/[^a-z0-9]/gi,`_`)}_flow.mmd`,title:c,onClose:()=>i(!1)})]})}function ot({steps:e}){return(0,X.jsx)(`div`,{className:`flowchart-list`,children:e.map((t,n)=>(0,X.jsx)(st,{step:t,isLast:n===e.length-1},n))})}function st({step:e,isLast:t}){return e.type===`if`?(0,X.jsxs)(`div`,{className:`flowchart-branch-wrapper`,children:[(0,X.jsx)(ct,{step:e}),(0,X.jsxs)(`div`,{className:`flowchart-branches`,children:[e.then&&e.then.length>0&&(0,X.jsxs)(`div`,{className:`flowchart-branch flowchart-branch--then`,children:[(0,X.jsx)(`div`,{className:`flowchart-branch-label`,children:`then`}),(0,X.jsx)(ot,{steps:e.then})]}),e.else&&e.else.length>0&&(0,X.jsxs)(`div`,{className:`flowchart-branch flowchart-branch--else`,children:[(0,X.jsx)(`div`,{className:`flowchart-branch-label`,children:`else`}),(0,X.jsx)(ot,{steps:e.else})]})]}),!t&&(0,X.jsx)(ut,{})]}):e.type===`loop`?(0,X.jsxs)(`div`,{className:`flowchart-branch-wrapper`,children:[(0,X.jsx)(ct,{step:e}),e.body&&e.body.length>0&&(0,X.jsx)(`div`,{className:`flowchart-loop-body`,children:(0,X.jsx)(ot,{steps:e.body})}),!t&&(0,X.jsx)(ut,{})]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(ct,{step:e}),!t&&(0,X.jsx)(ut,{})]})}function ct({step:e}){let t=`flowchart-box flowchart-box--${e.type} ${e.n1?`flowchart-box--n1`:``}`,n=dt[e.type]??``;return(0,X.jsxs)(`div`,{className:`${t} flowchart-shape--${e.type===`if`?`diamond`:e.type===`return`||e.type===`throw`?`terminal`:`rect`}`,title:e.label,children:[n&&(0,X.jsx)(`span`,{className:`flowchart-icon`,children:n}),(0,X.jsx)(`span`,{className:`flowchart-label`,children:e.label}),e.cache&&(0,X.jsx)(`span`,{className:`flowchart-cache-badge flowchart-cache-badge--${e.cache.kind}`,title:lt(e),children:e.cache.kind}),e.n1&&(0,X.jsx)(`span`,{className:`flowchart-n1-warn`,title:`N+1 Query Detected: This database operation is inside a loop!`,children:`⚠️ N+1`}),e.http&&e.http.length>0&&(0,X.jsxs)(`span`,{className:`flowchart-http`,title:e.http.map(e=>`${e.method||`REQUEST`} ${e.host||e.configKey&&`config('${e.configKey}')`||`address computed at runtime`}`+(e.timeout===null?` · no timeout`:` · timeout ${e.timeout}s`)).join(`
-`),children:[`🌐 `,e.http.map(e=>e.host).find(Boolean)??`external`]})]})}function lt(e){let t=e.cache;if(!t)return``;let n=t.keyKind===`computed`?`computed key`:t.keyKind===`none`?`whole store`:`"${t.key}"`,r=[t.ttl===null?``:`ttl ${t.ttl}s`,t.store===``?``:`store ${t.store}`,t.tags.length>0?`tags ${t.tags.join(`, `)}`:``].filter(Boolean);return`${t.kind} · ${t.method} ${n}${r.length>0?` · ${r.join(` · `)}`:``}`}function ut(){return(0,X.jsxs)(`div`,{className:`flowchart-arrow`,children:[(0,X.jsx)(`div`,{className:`flowchart-arrow-line`}),(0,X.jsx)(`div`,{className:`flowchart-arrow-head`})]})}var dt={call:`→`,assign:`=`,return:`◀`,throw:`⚠`,if:`◆`,loop:`↻`,dispatch:`⚡`,event:`📡`,cache:`⛃`};function ft({steps:e,title:t,isFatMethod:n,onClose:r}){return(0,A.useEffect)(()=>{let e=e=>{e.key===`Escape`&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r]),(0,X.jsx)(`div`,{className:`modal-overlay`,onClick:e=>{e.target===e.currentTarget&&r()},children:(0,X.jsxs)(`div`,{className:`modal-container modal-container--large`,children:[(0,X.jsxs)(`div`,{className:`modal-header`,children:[(0,X.jsxs)(`div`,{className:`modal-title`,children:[(0,X.jsx)(`span`,{className:`modal-icon`,children:`⛓`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:t}),(0,X.jsx)(`span`,{className:`modal-sub`,children:`Method Flow Visualization`})]})]}),(0,X.jsx)(`button`,{className:`modal-close`,onClick:r,title:`Close (Esc)`,children:`×`})]}),(0,X.jsx)(`div`,{className:`modal-body flowchart-modal-body`,children:(0,X.jsx)(at,{steps:e,isFatMethod:n})})]})})}function pt(e){let[t,n]=(0,A.useState)(null),[r,i]=(0,A.useState)(!1),[a,o]=(0,A.useState)(null),[s,c]=(0,A.useState)(e);return e!==s&&(c(e),e?(i(!0),o(null),n(null)):(n(null),o(null))),(0,A.useEffect)(()=>{e&&fetch(`/_laravel-brain/api/source?path=${encodeURIComponent(e)}`).then(e=>e.json()).then(e=>{if(e.error)throw Error(e.error);n(e.content)}).catch(e=>o(e.message)).finally(()=>i(!1))},[e]),{content:t,loading:r,error:a}}s.registerLanguage(`php`,u);function mt({filePath:e,highlightLine:t,theme:n}){let{content:r,loading:i,error:o}=pt(e),c=(0,A.useRef)(null);(0,A.useEffect)(()=>{c.current&&c.current.scrollIntoView({block:`center`,behavior:`smooth`})},[r]);let l=e.replace(/.*\/(app|src)\//,`$1/`);return i?(0,X.jsxs)(`div`,{className:`source-state`,children:[(0,X.jsx)(`div`,{className:`loading-spinner`,style:{width:20,height:20,borderWidth:2}}),(0,X.jsx)(`span`,{children:`Loading source…`})]}):o?(0,X.jsxs)(`div`,{className:`source-state source-state--error`,children:[`Could not load file`,(0,X.jsx)(`small`,{style:{display:`block`,opacity:.6,marginTop:4},children:o})]}):r?(0,X.jsxs)(`div`,{className:`source-view`,children:[(0,X.jsx)(`div`,{className:`source-path`,title:e,children:l}),(0,X.jsx)(s,{language:`php`,style:n===`dark`?p:a,showLineNumbers:!0,wrapLines:!0,lineNumberStyle:{minWidth:`2.5em`,paddingRight:`1em`,userSelect:`none`,opacity:.4,fontSize:11},lineProps:e=>e===t?{ref:c,style:{display:`block`,backgroundColor:n===`dark`?`rgba(139,111,232,0.2)`:`rgba(139,111,232,0.12)`,borderLeft:`3px solid #8B6FE8`}}:{style:{display:`block`}},customStyle:{margin:0,padding:`12px 0`,background:`transparent`,fontSize:12,lineHeight:`1.6`,fontFamily:`ui-monospace, "Cascadia Code", monospace`},children:r})]}):null}function ht({filePath:e,highlightLine:t,theme:n,onClose:r}){(0,A.useEffect)(()=>{let e=e=>{e.key===`Escape`&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r]);let i=e.split(`/`).pop()||`Source Code`;return(0,X.jsx)(`div`,{className:`modal-overlay`,onClick:e=>{e.target===e.currentTarget&&r()},children:(0,X.jsxs)(`div`,{className:`modal-container modal-container--large`,children:[(0,X.jsxs)(`div`,{className:`modal-header`,children:[(0,X.jsxs)(`div`,{className:`modal-title`,children:[(0,X.jsx)(`span`,{className:`modal-icon`,children:`📄`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:i}),(0,X.jsx)(`span`,{className:`modal-sub`,children:e})]})]}),(0,X.jsx)(`button`,{className:`modal-close`,onClick:r,title:`Close (Esc)`,children:`×`})]}),(0,X.jsx)(`div`,{className:`modal-body source-modal-body`,children:(0,X.jsx)(mt,{filePath:e,highlightLine:t,theme:n})})]})})}function gt(e){let[t,n]=(0,A.useState)(null),[r,i]=(0,A.useState)(!1),[a,o]=(0,A.useState)(null),[s,c]=(0,A.useState)(e);return e!==s&&(c(e),e?(i(!0),o(null),n(null)):(n(null),o(null))),(0,A.useEffect)(()=>{e&&fetch(`/_laravel-brain/api/usages?nodeId=${encodeURIComponent(e)}`).then(e=>e.json()).then(e=>{if(e.error)throw Error(e.error);n(e)}).catch(e=>o(e.message)).finally(()=>i(!1))},[e]),{data:t,loading:r,error:a}}function _t({nodeId:e}){let{data:t,loading:n,error:r}=gt(e);return n?(0,X.jsxs)(`div`,{className:`source-state`,children:[(0,X.jsx)(`div`,{className:`loading-spinner`,style:{width:20,height:20,borderWidth:2}}),(0,X.jsx)(`span`,{children:`Finding usages…`})]}):r?(0,X.jsxs)(`div`,{className:`source-state source-state--error`,children:[`Could not load usages`,(0,X.jsx)(`small`,{style:{display:`block`,opacity:.6,marginTop:4},children:r})]}):t?t.usageCount===0?(0,X.jsx)(`div`,{className:`sidebar-section`,children:(0,X.jsxs)(`div`,{className:`security-clean`,children:[(0,X.jsx)(`span`,{children:`✓`}),` Not used anywhere else in the project.`]})}):(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Used in `,t.fileCount,` file`,t.fileCount===1?``:`s`,` · `,t.usageCount,` reference`,t.usageCount===1?``:`s`]}),t.files.map(e=>(0,X.jsxs)(`div`,{style:{marginBottom:12},children:[(0,X.jsxs)(`span`,{className:`ins-chip ins-chip--neutral`,title:e.file??`Location could not be resolved`,style:{display:`inline-block`,marginBottom:6},children:[e.file?e.file.split(`/`).slice(-2).join(`/`):`Unresolved location`,` · `,e.count]}),e.usages.map(e=>(0,X.jsxs)(`div`,{className:`edge-row`,children:[(0,X.jsx)(`span`,{className:`edge-target`,children:e.label}),(0,X.jsx)(`span`,{className:`edge-label`,children:e.edgeLabel})]},e.nodeId))]},e.file??`#${e.usages[0]?.nodeId??``}`))]}):null}var vt=new Set([`POST`,`PUT`,`PATCH`,`QUERY`]),yt=new Set([`POST`,`PUT`,`PATCH`,`DELETE`,`QUERY`]);function bt(e){let t=parseInt(e,10);return t>=200&&t<300?`#22c55e`:t>=400&&t<500?`#f97316`:t>=500?`#ef4444`:`#6b7280`}function xt(e){let t=Math.floor((Date.now()-e)/1e3);return t<60?`${t}s ago`:t<3600?`${Math.floor(t/60)}m ago`:`${Math.floor(t/3600)}h ago`}var St=new Map;function Z(e){let t=St.get(e);if(t)return t;try{let t=localStorage.getItem(`lb_st_${e}`);if(t){let n=JSON.parse(t);return St.set(e,n),n}}catch{}}function Ct(e,t){let n={...t,savedAt:Date.now()};St.set(e,n);try{localStorage.setItem(`lb_st_${e}`,JSON.stringify(n))}catch{}}function wt(e){let t=new Set,n=[];for(let r of e.matchAll(/\{([^}?]+)(\?)?\}/g))t.has(r[1])||(n.push({name:r[1],optional:!!r[2]}),t.add(r[1]));return n}function Tt(e,t){let n=e;return n=n.replace(/\/\{([^}?]+)\?\}/g,(e,n)=>{let r=t[n]?.trim();return r?`/`+encodeURIComponent(r):``}),n=n.replace(/\{([^}?]+)\}/g,(e,n)=>encodeURIComponent(t[n]?.trim()??``)),n||`/`}function Et(e){try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?null:Object.entries(t).map(([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`).join(`&`)}catch{return null}}function Dt({method:e,uri:t,selectedId:n,onStressChange:r}){let i=`${e}::${t}`,a=wt(t),[o,s]=(0,A.useState)(()=>{let e=window.location.href,t=e.indexOf(`/_laravel-brain`);return t===-1?window.location.origin:e.slice(0,t)}),[c,l]=(0,A.useState)(()=>Z(i)?.count??10),[u,d]=(0,A.useState)(()=>Z(i)?.concurrency??2),[f,p]=(0,A.useState)(()=>Z(i)?.headersRaw??``),[m,h]=(0,A.useState)(()=>Z(i)?.body??(vt.has(e.toUpperCase())?`{}`:``)),[g,_]=(0,A.useState)(()=>Z(i)?.timeout??10),[v,y]=(0,A.useState)(()=>{let e=Z(i);return!!(e?.jobId&&!e?.result)}),[b,x]=(0,A.useState)(()=>Z(i)?.jobId??null),[S,C]=(0,A.useState)(()=>Z(i)?.result??null),[w,T]=(0,A.useState)(()=>Z(i)?.error??null),[E,D]=(0,A.useState)(()=>Z(i)?.routeParams??{}),[O,k]=(0,A.useState)(()=>Z(i)?.includeCsrf??yt.has(e.toUpperCase())),[j,M]=(0,A.useState)(()=>Z(i)?.sendAsFormData??yt.has(e.toUpperCase())),[N,P]=(0,A.useState)(0),F=(0,A.useRef)(null),ee=(0,A.useRef)({result:S,error:w,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:b,routeParams:E,includeCsrf:O,sendAsFormData:j,key:i});async function te(e,t){let n=0;for(;n<180;){if(t.aborted||(await new Promise(e=>setTimeout(e,1e3)),n++,P(n),t.aborted))return;try{let n=await(await fetch(`/_laravel-brain/api/stress-test/${e}`,{signal:t})).json();if(n.status===`done`){let e=n.result;C(e),x(null),Ct(i,{result:e,error:null,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:null,routeParams:E,includeCsrf:O,sendAsFormData:j}),P(0);return}if(n.status===`error`){T(n.error??`Unknown error`),x(null),Ct(i,{result:null,error:n.error??`Unknown error`,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:null,routeParams:E,includeCsrf:O,sendAsFormData:j}),P(0);return}}catch(e){if(e.name===`AbortError`)return}}P(0),x(null),Ct(i,{result:null,error:`Stress test timed out after 3 minutes`,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:null,routeParams:E,includeCsrf:O,sendAsFormData:j}),T(`Stress test timed out after 3 minutes`)}(0,A.useEffect)(()=>{ee.current={result:S,error:w,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:b,routeParams:E,includeCsrf:O,sendAsFormData:j,key:i}}),(0,A.useEffect)(()=>{let e=Z(i);e?.jobId&&!e?.result&&(r(n),F.current=new AbortController,te(e.jobId,F.current.signal).finally(()=>{y(!1),P(0),r(null)}))},[]),(0,A.useEffect)(()=>()=>{F.current?.abort();let e=ee.current;Ct(e.key,{result:e.result,error:e.error,count:e.count,concurrency:e.concurrency,headersRaw:e.headersRaw,body:e.body,timeout:e.timeout,jobId:e.jobId,routeParams:e.routeParams,includeCsrf:e.includeCsrf,sendAsFormData:e.sendAsFormData})},[]);let I=Z(i),ne=I?.savedAt&&I.result?xt(I.savedAt):null;function L(e){let t={};for(let n of e.split(`
-`)){let e=n.indexOf(`:`);if(e>0){let r=n.slice(0,e).trim(),i=n.slice(e+1).trim();r&&(t[r]=i)}}return t}async function re(){let s=a.filter(e=>!e.optional&&!E[e.name]?.trim());if(s.length>0){T(`Required route param${s.length>1?`s`:``} missing: ${s.map(e=>e.name).join(`, `)}`);return}y(!0),C(null),T(null),r(n);let l=Tt(t,E),d=o.replace(/\/$/,``)+`/`+l.replace(/^\//,``);F.current=new AbortController;let p=F.current.signal,h={},_=m||null;if(vt.has(e.toUpperCase())&&j&&m){let e=Et(m);e!==null&&(_=e,h[`Content-Type`]=`application/x-www-form-urlencoded`)}let v={...h,...L(f)};try{let t=await fetch(`/_laravel-brain/api/stress-test`,{method:`POST`,signal:p,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({method:e.toUpperCase(),url:d,count:c,concurrency:u,headers:v,body:_,timeout:g,includeCsrf:yt.has(e.toUpperCase())?O:!1})}),n=await t.json();if(!t.ok){T(n.error??`Request failed (${t.status})`);return}if(n.jobId){x(n.jobId),Ct(i,{result:null,error:null,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:n.jobId,routeParams:E,includeCsrf:O,sendAsFormData:j}),await te(n.jobId,p);return}let r=n;C(r),x(null),Ct(i,{result:r,error:null,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:null,routeParams:E,includeCsrf:O,sendAsFormData:j})}catch(e){e.name!==`AbortError`&&T(e instanceof Error?e.message:`Network error`)}finally{y(!1),P(0),r(null)}}let R=S?[{label:`Min`,value:`${S.timing.min}ms`},{label:`Avg`,value:`${S.timing.avg}ms`},{label:`P50`,value:`${S.timing.p50}ms`},{label:`P95`,value:`${S.timing.p95}ms`},{label:`P99`,value:`${S.timing.p99}ms`},{label:`Max`,value:`${S.timing.max}ms`},{label:`Req/s`,value:String(S.throughput)},{label:`Success`,value:`${S.successRate}%`},{label:`Wall`,value:`${S.wallTimeMs}ms`}]:[];return(0,X.jsxs)(`div`,{className:`st-section sidebar-section`,children:[(0,X.jsx)(`div`,{className:`st-toggle`,children:(0,X.jsx)(`h3`,{children:`Stress Test`})}),(0,X.jsx)(`div`,{className:`st-body`,children:(0,X.jsxs)(`div`,{className:`st-form`,children:[(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Base URL`}),(0,X.jsx)(`input`,{className:`st-input`,type:`text`,placeholder:`http://localhost:8000`,value:o,onChange:e=>s(e.target.value)})]}),(0,X.jsxs)(`div`,{className:`st-docker-hint`,children:[(0,X.jsx)(`strong`,{children:`Docker?`}),` The stress test runs `,(0,X.jsx)(`em`,{children:`inside`}),` the container — `,(0,X.jsx)(`code`,{children:`localhost:8080`}),` is the host-side port and won't be reachable there. Change Base URL to the internal service address, e.g. `,(0,X.jsx)(`code`,{children:`http://nginx`}),` or `,(0,X.jsx)(`code`,{children:`http://localhost:80`}),`.`]}),(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Target`}),(0,X.jsxs)(`span`,{className:`st-uri-preview`,children:[(0,X.jsx)(`span`,{className:`st-method-badge`,children:e.toUpperCase()}),a.length>0?Tt(t,E):t]})]}),a.length>0&&(0,X.jsxs)(`div`,{className:`st-form-col`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Route Params`}),a.map(({name:e,optional:t})=>(0,X.jsxs)(`div`,{className:`st-form-row`,style:{marginTop:4},children:[(0,X.jsxs)(`span`,{className:`st-label`,style:{minWidth:80},children:[e,t?` (opt)`:``]}),(0,X.jsx)(`input`,{className:`st-input`,type:`text`,placeholder:t?`optional`:`required`,value:E[e]??``,onChange:t=>D(n=>({...n,[e]:t.target.value}))})]},e))]}),(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Requests`}),(0,X.jsx)(`input`,{className:`st-input st-input--short`,type:`number`,min:1,max:200,value:c,onChange:e=>l(Math.max(1,Math.min(200,parseInt(e.target.value)||1)))}),(0,X.jsx)(`span`,{className:`st-label`,style:{minWidth:`auto`,marginLeft:8},children:`Concurrency`}),(0,X.jsx)(`input`,{className:`st-input st-input--short`,type:`number`,min:1,max:20,value:u,onChange:e=>d(Math.max(1,Math.min(20,parseInt(e.target.value)||1)))})]}),(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Timeout (s)`}),(0,X.jsx)(`input`,{className:`st-input st-input--short`,type:`number`,min:1,max:30,value:g,onChange:e=>_(Math.max(1,Math.min(30,parseInt(e.target.value)||10)))})]}),yt.has(e.toUpperCase())&&(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`CSRF Token`}),(0,X.jsxs)(`label`,{style:{display:`flex`,alignItems:`center`,gap:6,cursor:`pointer`,fontSize:13},children:[(0,X.jsx)(`input`,{type:`checkbox`,checked:O,onChange:e=>k(e.target.checked)}),`Auto-inject from session`]})]}),vt.has(e.toUpperCase())&&(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Body Format`}),(0,X.jsxs)(`label`,{style:{display:`flex`,alignItems:`center`,gap:6,cursor:`pointer`,fontSize:13},children:[(0,X.jsx)(`input`,{type:`checkbox`,checked:j,onChange:e=>M(e.target.checked)}),`Form data (application/x-www-form-urlencoded)`]})]}),(0,X.jsxs)(`div`,{className:`st-form-col`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Headers`}),(0,X.jsx)(`textarea`,{className:`st-textarea`,rows:3,placeholder:`Authorization: Bearer token
-Accept: application/json`,value:f,onChange:e=>p(e.target.value)})]}),vt.has(e.toUpperCase())&&(0,X.jsxs)(`div`,{className:`st-form-col`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:j?`Body (JSON → form)`:`Body (JSON)`}),(0,X.jsx)(`textarea`,{className:`st-textarea`,rows:4,placeholder:`{}`,value:m,onChange:e=>h(e.target.value)})]}),(0,X.jsx)(`button`,{className:`st-run-btn`,onClick:re,disabled:v,children:v?`⏳ Running… ${N>0?`(${N}s)`:``}`:`▶ Run Stress Test`}),ne&&(0,X.jsxs)(`span`,{className:`st-last-run st-last-run--form`,children:[`Last run: `,ne]}),w&&(0,X.jsx)(`div`,{className:`st-error-box`,children:w}),S&&(0,X.jsxs)(`div`,{className:`st-results`,children:[(0,X.jsx)(`div`,{className:`st-metrics-grid`,children:R.map(e=>(0,X.jsxs)(`div`,{className:`st-metric`,children:[(0,X.jsx)(`div`,{className:`st-metric-value`,children:e.value}),(0,X.jsx)(`div`,{className:`st-metric-label`,children:e.label})]},e.label))}),Object.keys(S.statusDistribution).length>0&&(0,X.jsxs)(`div`,{className:`st-dist`,children:[(0,X.jsx)(`div`,{className:`st-dist-title`,children:`Status Distribution`}),Object.entries(S.statusDistribution).sort(([e],[t])=>parseInt(e)-parseInt(t)).map(([e,t])=>(0,X.jsxs)(`div`,{className:`st-dist-row`,children:[(0,X.jsx)(`span`,{className:`st-dist-label`,children:e===`0`?`err`:e}),(0,X.jsx)(`div`,{className:`st-dist-bar-wrap`,children:(0,X.jsx)(`div`,{className:`st-dist-bar`,style:{width:`${t/S.total*100}%`,background:bt(e)}})}),(0,X.jsx)(`span`,{className:`st-dist-count`,children:t})]},e))]}),S.errors.length>0&&(0,X.jsx)(`div`,{className:`st-error-box`,style:{marginTop:8},children:S.errors.map((e,t)=>(0,X.jsx)(`div`,{children:e},t))})]})]})})]})}var Ot=[`route`,`middleware`,`controller`,`action`,`validation_request`,`action_class`,`service`,`model`,`event`,`job`,`command`,`channel`,`schedule`,`view`,`mail`,`notification`,`enum`,`interface`,`trait`,`abstract_class`,`service_provider`,`ai_agent`,`ai_tool`];function kt(e){return e===`action`?`controller`:e}function At(e){if(!e)return 99;let t=kt(e.type),n=Ot.indexOf(t);return n===-1?99:n}function jt(e){let t=e.split(`\\`),n=t[t.length-1];return n.length<=20?n:n.substring(0,18)+`…`}function Mt(e){let t=new Map;for(let n of e)t.has(n.source)||t.set(n.source,[]),t.get(n.source).push(n);return t}function Nt(e){return e.includes(`-to-job`)||e.includes(`-to-event`)||e===`model-to-event`}function Pt(e,t){let n=new Map(t.nodes.map(e=>[e.id,e])),r=Mt(t.edges),i=new Set,a=[],o=[],s=[e];for(i.add(e);s.length>0;){let e=s.shift();a.push(e);for(let t of r.get(e)??[])o.push(t),i.has(t.target)||(i.add(t.target),s.push(t.target))}let c=[],l=new Map,u=[...a].sort((e,t)=>{let r=At(n.get(e)),i=At(n.get(t));return r===i?e.localeCompare(t):r-i});for(let e of u){let t=n.get(e);if(!t)continue;let r=c.length;l.set(e,r);let i=kt(t.type);c.push({id:t.id,label:jt(t.label),type:i,color:L[t.type]??L[i]??`#888`})}c.unshift({id:`__client__`,label:`Client`,type:`client`,color:`#78909C`});for(let e of[...l.keys()])l.set(e,l.get(e)+1);let d=u.filter(e=>n.get(e)?.type===`model`),f=null;d.length>0&&(f=c.length,c.push({id:`__db__`,label:`Database`,type:`db`,color:`#78909C`}));let p=[],m=l.get(e);m!==void 0&&p.push({fromIndex:0,toIndex:m,label:`request`,isReturn:!1});for(let e of o){let t=l.get(e.source),n=l.get(e.target);if(t===void 0||n===void 0||t===n)continue;let r=Nt(e.type);p.push({fromIndex:t,toIndex:n,label:e.label||``,isAsync:r})}if(f!==null)for(let e of d){let t=l.get(e);t!==void 0&&(p.push({fromIndex:t,toIndex:f,label:`query`,isReturn:!1}),p.push({fromIndex:f,toIndex:t,label:`result`,isReturn:!0}))}m!==void 0&&p.push({fromIndex:m,toIndex:0,label:`response`,isReturn:!0});let h=new Map,g=[];for(let e of p){let t=`${e.fromIndex}|${e.toIndex}|${e.label}|${e.isReturn?`r`:``}|${e.isAsync?`a`:``}`,n=h.get(t);if(n){n.count++;let t=e.label;g[n.idx]={...g[n.idx],label:`${t} ×${n.count}`}}else h.set(t,{idx:g.length,count:1}),g.push(e)}return{actors:c,messages:g}}function Ft(e,t){let n=[`%% Sequence Diagram — ${t}`,`sequenceDiagram`,` autonumber`];for(let t=0;t>`:t.isReturn?`-->>`:`->>`,n.push(` ${e}${a}${r}: ${i}`)}return n.join(`
-`)}var It=110,Q=52,Lt=38,Rt=16;function zt({diagram:e,title:t,theme:n=`dark`}){let[r,i]=(0,A.useState)(!1),[a,o]=(0,A.useState)(!1),s=(0,A.useRef)(null),c=n===`dark`,l=Rt*2+e.actors.length*It,u=Q+e.messages.length*Lt+Lt+Q,d=e=>Rt+e*It+It/2,f=e=>Q+e*Lt+Lt/2,p=c?`#e0e0e0`:`#1a1a1a`,m=c?`#888`:`#999`,h=c?`rgba(255,255,255,0.10)`:`rgba(0,0,0,0.12)`,g=c?`#0d0f14`:`#ffffff`,_=c?`rgba(255,255,255,0.35)`:`rgba(0,0,0,0.30)`,v=c?`seq-arrow-dark`:`seq-arrow-light`,y=c?`seq-arrow-return-dark`:`seq-arrow-return-light`,b=c?`seq-arrow-async-dark`:`seq-arrow-async-light`,x=c?`#a0aec0`:`#555`,S=c?`#b39ddb`:`#7c4dff`;return e.actors.length===0?(0,X.jsx)(`div`,{className:`flowchart-empty`,children:(0,X.jsx)(`span`,{children:`No sequence data available`})}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`div`,{className:`flowchart-export-bar`,children:[(0,X.jsxs)(`button`,{className:`flowchart-export-btn`,onClick:async()=>{if(s.current){o(!0);try{Ze(await $e(s.current,g),`${(t??`sequence`).replace(/[^a-z0-9]/gi,`_`)}_sequence.png`)}finally{o(!1)}}},disabled:a,title:`Export as PNG`,children:[a?`⏳`:`🖼`,` PNG`]}),(0,X.jsx)(`button`,{className:`flowchart-export-btn`,onClick:()=>i(!0),title:`Export as Mermaid`,children:`🧜 Mermaid`})]}),(0,X.jsx)(`div`,{className:`seq-diagram-root`,ref:s,children:(0,X.jsxs)(`svg`,{className:`seq-diagram-svg`,viewBox:`0 0 ${l} ${u}`,width:`100%`,style:{background:g,display:`block`},xmlns:`http://www.w3.org/2000/svg`,children:[(0,X.jsxs)(`defs`,{children:[(0,X.jsx)(`marker`,{id:v,markerWidth:`8`,markerHeight:`6`,refX:`7`,refY:`3`,orient:`auto`,children:(0,X.jsx)(`polygon`,{points:`0 0, 8 3, 0 6`,fill:x})}),(0,X.jsx)(`marker`,{id:y,markerWidth:`8`,markerHeight:`6`,refX:`7`,refY:`3`,orient:`auto`,children:(0,X.jsx)(`polyline`,{points:`0 0, 8 3, 0 6`,fill:`none`,stroke:_,strokeWidth:`1.5`})}),(0,X.jsx)(`marker`,{id:b,markerWidth:`8`,markerHeight:`6`,refX:`7`,refY:`3`,orient:`auto`,children:(0,X.jsx)(`polygon`,{points:`0 0, 8 3, 0 6`,fill:S})})]}),e.actors.map((e,t)=>{let n=d(t),r=It-8,i=n-r/2,a=Math.floor(r/6.5),o=e.label.length>a?e.label.substring(0,a-1)+`…`:e.label;return(0,X.jsxs)(`g`,{children:[(0,X.jsx)(`rect`,{x:i,y:4,width:r,height:Q-10,rx:5,fill:c?`#1a1d24`:`#f5f5f5`,stroke:e.color,strokeWidth:1.5}),(0,X.jsx)(`text`,{x:n,y:Q/2-4,textAnchor:`middle`,dominantBaseline:`middle`,fontSize:10.5,fontFamily:`system-ui, sans-serif`,fill:e.color,fontWeight:`600`,children:o}),(0,X.jsx)(`text`,{x:n,y:Q-12,textAnchor:`middle`,dominantBaseline:`middle`,fontSize:8,fontFamily:`system-ui, sans-serif`,fill:m,children:e.type})]},e.id)}),e.actors.map((e,t)=>(0,X.jsx)(`line`,{x1:d(t),y1:Q,x2:d(t),y2:u-Q,stroke:h,strokeWidth:1,strokeDasharray:`4 4`},`life-${e.id}`)),e.actors.map((e,t)=>{let n=d(t),r=It-8,i=n-r/2,a=u-Q+4,o=Math.floor(r/6.5),s=e.label.length>o?e.label.substring(0,o-1)+`…`:e.label;return(0,X.jsxs)(`g`,{children:[(0,X.jsx)(`rect`,{x:i,y:a,width:r,height:Q-10,rx:5,fill:c?`#1a1d24`:`#f5f5f5`,stroke:e.color,strokeWidth:1.5}),(0,X.jsx)(`text`,{x:n,y:a+Q/2-8,textAnchor:`middle`,dominantBaseline:`middle`,fontSize:10.5,fontFamily:`system-ui, sans-serif`,fill:e.color,fontWeight:`600`,children:s}),(0,X.jsx)(`text`,{x:n,y:a+Q-18,textAnchor:`middle`,dominantBaseline:`middle`,fontSize:8,fontFamily:`system-ui, sans-serif`,fill:m,children:e.type})]},`bottom-${e.id}`)}),e.messages.map((e,t)=>{let n=f(t),r=d(e.fromIndex),i=d(e.toIndex),a=i>r,o=a?r+6:r-6,s=a?i-6:i+6,c=e.isReturn===!0,l=e.isAsync===!0,u=c?_:l?S:x,h=c?`5 3`:l?`6 3`:void 0,g=c?y:l?b:v,C=(r+i)/2,w=Math.abs(i-r)-12,T=Math.max(10,Math.floor(w/6)),E=e.label.length>T?e.label.substring(0,T-1)+`…`:e.label;return(0,X.jsxs)(`g`,{children:[(0,X.jsx)(`line`,{x1:o,y1:n,x2:s,y2:n,stroke:u,strokeWidth:c?1:1.5,strokeDasharray:h,markerEnd:`url(#${g})`}),e.label&&(0,X.jsx)(`text`,{x:C,y:n-6,textAnchor:`middle`,fontSize:9,fontFamily:`system-ui, sans-serif`,fill:c?m:p,opacity:c?.75:1,children:E})]},t)})]})}),r&&(0,X.jsx)(it,{mermaidCode:Ft(e,t??`sequence`),filename:`${(t??`sequence`).replace(/[^a-z0-9]/gi,`_`)}_sequence.mmd`,title:t??`Sequence Diagram`,onClose:()=>i(!1)})]})}function Bt({diagram:e,title:t,theme:n,onClose:r}){return(0,A.useEffect)(()=>{let e=e=>{e.key===`Escape`&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r]),(0,X.jsx)(`div`,{className:`modal-overlay`,onClick:e=>{e.target===e.currentTarget&&r()},children:(0,X.jsxs)(`div`,{className:`modal-container modal-container--large`,children:[(0,X.jsxs)(`div`,{className:`modal-header`,children:[(0,X.jsxs)(`div`,{className:`modal-title`,children:[(0,X.jsx)(`span`,{className:`modal-icon`,children:`⇄`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:t}),(0,X.jsx)(`span`,{className:`modal-sub`,children:`Sequence Diagram`})]})]}),(0,X.jsx)(`button`,{className:`modal-close`,onClick:r,title:`Close (Esc)`,children:`×`})]}),(0,X.jsx)(`div`,{className:`modal-body sequence-modal-body`,children:(0,X.jsx)(zt,{diagram:e,title:t,theme:n,compact:!1})})]})})}function $({content:e,children:t,placement:a=`top`,disabled:o=!1,className:s}){let[u,p]=(0,A.useState)(!1),{refs:v,floatingStyles:y,context:x}=m({open:o?!1:u,onOpenChange:p,placement:a,middleware:[_(8),l(),i({padding:8})],whileElementsMounted:b}),{getReferenceProps:S,getFloatingProps:C}=f([n(x,{move:!1,enabled:!o,delay:{open:280,close:80}}),c(x,{enabled:!o}),d(x),r(x,{role:`tooltip`})]),w=h([v.setReference]);return(0,A.isValidElement)(t)?(0,X.jsxs)(X.Fragment,{children:[(0,A.cloneElement)(t,{ref:w,...S()}),u&&!o&&(0,X.jsx)(g,{children:(0,X.jsx)(`div`,{ref:v.setFloating,style:y,className:[`floating-tooltip`,s].filter(Boolean).join(` `),...C(),children:e})})]}):(0,X.jsx)(X.Fragment,{children:t})}var Vt=360,Ht=640,Ut=380,Wt={route:`#4CAF50`,middleware:`#FF9800`,controller:`#2196F3`,action:`#03A9F4`,action_class:`#84cc16`,service:`#9C27B0`,validation_request:`#0d9488`,model:`#F44336`,event:`#FFD600`,job:`#607D8B`,command:`#14b8a6`,channel:`#8b5cf6`,schedule:`#f97316`,view:`#ec4899`,mail:`#f472b6`,notification:`#db2777`,enum:`#0ea5e9`,interface:`#38bdf8`,trait:`#a78bfa`,abstract_class:`#94a3b8`,service_provider:`#ca8a04`,facade:`#00BCD4`,ai_agent:`#A3E635`,ai_tool:`#65A30D`,filament_panel:`#7C3AED`,filament_resource:`#A855F7`,filament_page:`#C084FC`,filament_page_method:`#E879F9`,filament_widget:`#06B6D4`,filament_relation_manager:`#0891B2`};function Gt(e){if(e===null)return`—`;if(e<1024)return`${e} B`;let t=[`KB`,`MB`,`GB`,`TB`],n=e/1024,r=0;for(;n>=1024&&r{e.preventDefault(),s.current=!0,c.current=e.clientX,l.current=a;let t=e=>{if(!s.current)return;let t=c.current-e.clientX;o(Math.min(Ht,Math.max(Vt,l.current+t)))},n=()=>{s.current=!1,window.removeEventListener(`mousemove`,t),window.removeEventListener(`mouseup`,n),document.body.style.cursor=``,document.body.style.userSelect=``};document.body.style.cursor=`col-resize`,document.body.style.userSelect=`none`,window.addEventListener(`mousemove`,t),window.addEventListener(`mouseup`,n)},[a]),[d,f]=(0,A.useState)(`info`),[p,m]=(0,A.useState)(!1),[h,g]=(0,A.useState)(!1),[_,v]=(0,A.useState)(!1),[y,b]=(0,A.useState)(!1),[x,S]=(0,A.useState)(!1),[C,w]=(0,A.useState)(e);e!==C&&(w(e),f(`info`),m(!1),g(!1),v(!1),b(!1),S(!1));let T=(0,A.useMemo)(()=>{let e=new Map;return t&&t.nodes.forEach(t=>e.set(t.id,t)),e},[t]),E=(0,A.useMemo)(()=>{let e=new Map;return t&&t.edges.forEach(t=>{let n=e.get(t.target)??[];n.push(t),e.set(t.target,n)}),e},[t]),D=(0,A.useMemo)(()=>{let e=new Map;return t&&t.edges.forEach(t=>{let n=e.get(t.source)??[];n.push(t),e.set(t.source,n)}),e},[t]),O=(0,A.useMemo)(()=>!t||!e||t.nodes.find(t=>t.id===e)?.type!==`route`?null:Pt(e,t),[e,t]),k=(0,A.useCallback)(async()=>{if(e){S(!0);try{let t=await fetch(`/_laravel-brain/api/context?nodeId=${encodeURIComponent(e)}&budget=6000`);if(!t.ok)throw Error(`Failed to fetch context`);let n=await t.text();await navigator.clipboard.writeText(n),b(!0),setTimeout(()=>b(!1),2500)}catch{alert(`Could not copy AI context.`)}finally{S(!1)}}},[e]);if(!t)return null;if(!e)return(0,X.jsxs)(`div`,{className:`sidebar-resizable`,style:{width:a},children:[(0,X.jsx)($,{content:`Drag to resize`,children:(0,X.jsx)(`div`,{className:`sidebar-drag-handle`,onMouseDown:u})}),(0,X.jsxs)(`div`,{className:`sidebar`,children:[(0,X.jsxs)(`div`,{className:`sidebar-header`,children:[(0,X.jsx)(`h2`,{children:t.meta.project}),(0,X.jsx)(`span`,{className:`sidebar-subtitle`,children:`Laravel Lifecycle Graph`})]}),(0,X.jsxs)(`div`,{className:`sidebar-stats`,children:[(0,X.jsx)($,{content:`Total symbols in this tab's JSON graph (routes, classes, views, …).`,children:(0,X.jsxs)(`div`,{className:`stat`,children:[(0,X.jsx)(`span`,{className:`stat-value`,children:t.meta.nodeCount}),(0,X.jsx)(`span`,{className:`stat-label`,children:`Nodes`})]})}),(0,X.jsx)($,{content:`Directed links between nodes: calls, type-hints, events, views, Eloquent relations, etc.`,children:(0,X.jsxs)(`div`,{className:`stat`,children:[(0,X.jsx)(`span`,{className:`stat-value`,children:t.meta.edgeCount}),(0,X.jsx)(`span`,{className:`stat-label`,children:`Edges`})]})}),(0,X.jsx)($,{content:`HTTP route entry nodes only (subset of all node types).`,children:(0,X.jsxs)(`div`,{className:`stat`,children:[(0,X.jsx)(`span`,{className:`stat-value`,children:t.nodes.filter(e=>e.type===`route`).length}),(0,X.jsx)(`span`,{className:`stat-label`,children:`Routes`})]})})]}),(0,X.jsx)($,{content:`The inspector shows details for the selected node: metrics, flow, source, and incoming/outgoing edges.`,children:(0,X.jsx)(`p`,{className:`sidebar-hint`,children:`Click any node to inspect it`})})]})]});let j=T.get(e);if(!j)return null;let M=E.get(e)??[],N=D.get(e)??[],P=j.data?.flowSteps??[],F=j.data?.file||null,ee=j.data?.line||void 0,te=Wt[j.type]??`#999`,I=j.data?.metrics,ne=!!j.data?.fatMethod,L=!!j.data?.fatClass,re=!!j.data?.hasN1,R=typeof j.data?.deferredDefect==`string`?j.data.deferredDefect:null,z=typeof j.data?.deferredDefectMessage==`string`?j.data.deferredDefectMessage:``,ie=j.data?.dbQueries??[],ce=j.data?.cacheOps??[],H=j.data?.httpCalls??[],le=j.data?.relationships??[],ue=j.type===`middleware`&&typeof j.data?.params==`string`&&j.data.params?j.data.params.split(`,`).map(e=>e.trim()).filter(Boolean):[],de=j.data?.members??[],fe=j.data?.validationRules??[],pe=Object.entries(j.data??{}).filter(([e,t])=>e!==`flowSteps`&&e!==`metrics`&&e!==`fatMethod`&&e!==`fatClass`&&e!==`hasN1`&&e!==`classMetrics`&&e!==`dbQueries`&&e!==`cacheOps`&&e!==`httpCalls`&&e!==`relationships`&&e!==`params`&&e!==`members`&&e!==`validationRules`&&e!==`security`&&e!==`erd`&&e!==`tableStats`&&e!==`schema`&&e!==`event`&&e!==`listener`&&e!==`job`&&e!==`deferredDefect`&&e!==`deferredDefectMessage`&&!(Array.isArray(t)&&t.length===0)),U=j.data?.erd,me=j.data?.tableStats,W=j.data?.schema,G=j.data?.event,he=j.data?.listener,K=j.data?.job,ge=P.length>0||!!O,q=!!F,_e=M.length>0||N.length>0,J=j.type===`route`,Y=j.data?.security?j.data.security:null,ve=d===`flow`&&!ge||d===`source`&&!q||d===`edges`&&!_e||d===`stress`&&!J||d===`schema`&&!W||d===`risks`&&!J&&!Y?`info`:d,ye=Y?Y.issues.length:0,be=n===`light`?ae:B,xe=[{id:`info`,label:`Info`,title:`Identity, type, smells, and code metrics (lines, cyclomatic complexity, …).`},...J||ye>0?[{id:`risks`,label:`Risks`,count:ye||void 0,alert:ye>0,title:`Findings that need attention: a route’s exposure and rate-limiting, or a table’s missing indexes.`}]:[],...W?[{id:`schema`,label:`Schema`,count:W.columns.length||void 0,title:`Columns, indexes and foreign keys as the database itself reports them.`}]:[],...ge?[{id:`flow`,label:`Flow`,title:`Control-flow steps through this method or request (and sequence diagram for routes).`}]:[],..._e?[{id:`edges`,label:`Edges`,count:M.length+N.length,title:`What calls or references this node (incoming) and what it calls (outgoing).`}]:[],{id:`usages`,label:`Usages`,title:`Where this symbol is referenced across the whole project, grouped by file.`},...q?[{id:`source`,label:`Source`,title:`Syntax-highlighted PHP source around this symbol.`}]:[],...J?[{id:`stress`,label:`Stress`,title:`Send HTTP requests against this route and inspect responses (dev only).`}]:[]];return(0,X.jsxs)(`div`,{className:`sidebar-resizable`,style:{width:a},children:[(0,X.jsx)($,{content:`Drag to resize`,children:(0,X.jsx)(`div`,{className:`sidebar-drag-handle`,onMouseDown:u})}),(0,X.jsxs)(`div`,{className:`sidebar`,children:[(0,X.jsxs)(`div`,{className:`sidebar-header`,children:[(0,X.jsxs)(`div`,{className:`sidebar-header-actions`,children:[(0,X.jsx)($,{content:`Copy AI context to clipboard`,children:(0,X.jsx)(`span`,{className:`tooltip-trigger-wrap`,children:(0,X.jsx)(`button`,{type:`button`,className:`flow-popup-btn sidebar-ai-btn`,onClick:k,disabled:x,children:x?`…`:y?`✓`:`🤖`})})}),(0,X.jsx)($,{content:`Clear selection (close inspector header)`,children:(0,X.jsx)(`button`,{className:`sidebar-close`,type:`button`,onClick:r,children:`×`})})]}),(0,X.jsxs)(`div`,{className:`sidebar-eyebrow`,children:[(0,X.jsx)(`span`,{className:`sidebar-eyebrow-dot`,style:{backgroundColor:te}}),(0,X.jsx)(`span`,{className:`sidebar-eyebrow-type`,children:j.type.replace(/_/g,` `)})]}),(0,X.jsx)(`h2`,{className:`sidebar-node-title`,children:j.label}),(0,X.jsxs)(`div`,{className:`sidebar-chips`,children:[Y&&be[Y.exposure]&&(()=>{let e=be[Y.exposure];return(0,X.jsxs)(`span`,{className:`ins-chip`,style:{"--cc":e.accent},children:[`● `,e.label]})})(),Y&&Y.riskLevel!==`none`&&(0,X.jsxs)(`span`,{className:`ins-chip`,style:{"--cc":V[Y.riskLevel]},children:[`⚠ `,oe[Y.riskLevel],` risk · `,ye]}),H.length>0&&(0,X.jsx)($,{content:`Leaves the application: ${H.map(e=>e.host||e.configKey||`computed address`).join(`, `)}`,children:(0,X.jsxs)(`span`,{className:`ins-chip ins-chip--http`,children:[`🌐 `,H.length,` outgoing`]})}),M.length+N.length>0&&(0,X.jsxs)(`span`,{className:`ins-chip ins-chip--neutral`,children:[`Edges `,M.length+N.length]}),F&&(0,X.jsxs)(`span`,{className:`ins-chip ins-chip--neutral`,title:F,children:[F.split(`/`).slice(-2).join(`/`),ee?` : ${ee}`:``]})]})]}),(ne||L||re||R)&&(0,X.jsxs)(`div`,{className:`sidebar-smells`,children:[re&&(0,X.jsx)($,{content:`N+1 Query: database query inside a loop`,children:(0,X.jsx)(`span`,{className:`smell-badge smell-badge--n1`,children:`⚠️ N+1 Query`})}),R&&(0,X.jsx)($,{content:z,children:(0,X.jsx)(`span`,{className:`smell-badge smell-badge--deferred`,children:R===`never-boots`?`⏳ Never boots`:R===`unbacked-provides`?`⏳ Unbacked provides()`:`⏳ $defer ignored`})}),ne&&(0,X.jsx)($,{content:`Fat Method: more than 30 lines or cyclomatic complexity > 10`,children:(0,X.jsx)(`span`,{className:`smell-badge smell-badge--fat-method`,children:`🧱 Fat Method`})}),L&&(0,X.jsx)($,{content:`Fat Class: more than 10 methods or 300+ total lines`,children:(0,X.jsx)(`span`,{className:`smell-badge smell-badge--fat-class`,children:`🏗️ Fat Class`})})]}),(0,X.jsx)(`div`,{className:`sidebar-tab-bar`,children:xe.map(e=>(0,X.jsx)($,{content:e.title,children:(0,X.jsxs)(`button`,{type:`button`,className:`sidebar-tab${ve===e.id?` sidebar-tab--active`:``}`,onClick:()=>f(e.id),children:[e.label,e.count!==void 0&&(0,X.jsx)(`span`,{className:`sidebar-tab-badge${e.alert?` sidebar-tab-badge--alert`:``}`,children:e.count})]})},e.id))}),(0,X.jsxs)(`div`,{className:`sidebar-tab-content`,children:[ve===`info`&&(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`div`,{className:`ins-actions`,children:[(0,X.jsxs)(`button`,{type:`button`,className:`ins-action-btn`,disabled:!q,onClick:()=>f(`source`),children:[(0,X.jsxs)(`svg`,{className:`ins-action-icon`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`path`,{d:`M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6`}),(0,X.jsx)(`polyline`,{points:`15 3 21 3 21 9`}),(0,X.jsx)(`line`,{x1:`10`,y1:`14`,x2:`21`,y2:`3`})]}),`Open file`]}),(0,X.jsxs)(`button`,{type:`button`,className:`ins-action-btn`,onClick:()=>navigator.clipboard.writeText(String(j.data?.uri??j.label)),children:[(0,X.jsxs)(`svg`,{className:`ins-action-icon`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`rect`,{x:`9`,y:`9`,width:`13`,height:`13`,rx:`2`,ry:`2`}),(0,X.jsx)(`path`,{d:`M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1`})]}),`Copy URI`]})]}),(()=>{let e=I?.cyclomaticComplexity??0,t=N.length,n={none:0,low:25,medium:55,high:80,critical:100}[Y?.riskLevel??`none`]??0;return(0,X.jsx)(`div`,{className:`ins-meters`,children:[{label:`Complexity`,value:e,pct:Math.min(100,e*6),tone:e>15?`var(--danger)`:e>10?`var(--warn)`:`var(--ok)`},{label:`Fan-out`,value:t,pct:Math.min(100,t*10),tone:t>8?`var(--danger)`:t>4?`var(--warn)`:`var(--ok)`},{label:`Risk`,value:ye,pct:n,tone:n>=80?`var(--danger)`:n>=55?`var(--warn)`:`var(--ok)`}].map(e=>(0,X.jsxs)(`div`,{className:`ins-meter`,children:[(0,X.jsx)(`span`,{className:`ins-meter-label`,children:e.label}),(0,X.jsx)(`span`,{className:`ins-meter-track`,children:(0,X.jsx)(`span`,{className:`ins-meter-fill`,style:{width:`${e.pct}%`,background:e.tone}})}),(0,X.jsx)(`span`,{className:`ins-meter-value`,children:e.value})]},e.label))})})(),I&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--metrics`,children:[(0,X.jsx)(`h3`,{children:`Code Metrics`}),(0,X.jsxs)(`div`,{className:`metrics-grid`,children:[(0,X.jsx)($,{content:`Physical lines of code in this method (approximate, from static analysis).`,children:(0,X.jsxs)(`div`,{className:`metric-item`,children:[(0,X.jsx)(`span`,{className:`metric-value`,children:I.lineCount}),(0,X.jsx)(`span`,{className:`metric-label`,children:`Lines`})]})}),(0,X.jsx)($,{content:`Cyclomatic complexity: decision paths (branches, loops, boolean operators). Rough guide: above 10 is harder to test; above 15 is very complex.`,children:(0,X.jsxs)(`div`,{className:`metric-item`,children:[(0,X.jsx)(`span`,{className:`metric-value`,style:{color:I.cyclomaticComplexity>10?`#FF6D00`:`inherit`},children:I.cyclomaticComplexity}),(0,X.jsx)(`span`,{className:`metric-label`,children:`Complexity`})]})}),(0,X.jsx)($,{content:`Executable statements counted in this method body.`,children:(0,X.jsxs)(`div`,{className:`metric-item`,children:[(0,X.jsx)(`span`,{className:`metric-value`,children:I.statementCount}),(0,X.jsx)(`span`,{className:`metric-label`,children:`Statements`})]})}),(0,X.jsx)($,{content:`Parameters on this function or method signature.`,children:(0,X.jsxs)(`div`,{className:`metric-item`,children:[(0,X.jsx)(`span`,{className:`metric-value`,children:I.paramCount}),(0,X.jsx)(`span`,{className:`metric-label`,children:`Params`})]})})]})]}),j.type===`filament_resource`&&!!j.data?.route&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Filament URL`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`route`}),(0,X.jsx)(`span`,{className:`prop-value`,style:{fontFamily:`monospace`,color:`#A855F7`},children:String(j.data.route)})]})]}),j.type===`ai_agent`&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Model & limits`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`model`}),(0,X.jsx)(`span`,{className:`prop-value`,style:{fontFamily:`monospace`,color:Jt},children:Yt(j.data)})]}),Xt.map(({key:e,label:t})=>j.data?.[e]===void 0?null:(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:t}),(0,X.jsx)(`span`,{className:`prop-value`,style:{fontFamily:`monospace`},children:String(j.data[e])})]},e)),Zt.map(({key:e,label:t})=>j.data?.[e]?(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:t}),(0,X.jsx)(`span`,{className:`prop-value`,children:`yes`})]},e):null),Array.isArray(j.data?.methodOverrides)&&j.data.methodOverrides.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`overridable`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data.methodOverrides.join(`, `)})]}),typeof j.data?.shadowedModelAttribute==`string`&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#FF6D00`},children:`dead #[Model]`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[j.data.shadowedModelAttribute,` — a model() method is read instead`]})]}),typeof j.data?.shadowedProviderAttribute==`string`&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#FF6D00`},children:`dead #[Provider]`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[j.data.shadowedProviderAttribute,` — a provider() method is read instead`]})]}),Array.isArray(j.data?.contracts)&&j.data.contracts.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`contracts`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data.contracts.join(`, `)})]}),j.data?.toolsDecidedAtRuntime===!0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`tools()`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`decided at runtime — this agent has tools Brain cannot name from tools()`})]}),Array.isArray(j.data?.injectedTools)&&j.data.injectedTools.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`supplied tools`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[j.data.injectedTools.length,` handed to the constructor where the agent is built`]})]}),Array.isArray(j.data?.unwiredTools)&&j.data.unwiredTools.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#FF6D00`},children:`unwired tools`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[j.data.unwiredTools.map(e=>e.split(`\\`).pop()).join(`, `),` — tools() is never called without the HasTools contract`]})]}),Array.isArray(j.data?.unresolvedTools)&&j.data.unresolvedTools.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`unresolved tools`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data.unresolvedTools.join(`, `)})]})]}),j.type===`ai_tool`&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Tool`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`kind`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data?.toolKind===`mcp`?`MCP server tool`:`laravel/ai tool`})]}),typeof j.data?.description==`string`&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`description`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data.description})]})]}),le.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Relationships`}),le.map((e,t)=>(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#9C27B0`},children:e.type}),(0,X.jsx)(`span`,{className:`prop-value`,children:e.related.split(`\\`).pop()??e.related})]},t))]}),ue.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`ATTRIBUTES`}),ue.map((e,t)=>(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#FF9800`},children:t+1}),(0,X.jsx)(`span`,{className:`prop-value`,children:e})]},t))]}),fe.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--validation-rules`,children:[(0,X.jsx)(`h3`,{children:`Validation rules`}),(0,X.jsx)(`ul`,{className:`sidebar-structure-list`,children:fe.map((e,t)=>(0,X.jsxs)(`li`,{className:`sidebar-structure-item`,children:[(0,X.jsx)(`span`,{className:`structure-kind`,children:`field`}),(0,X.jsx)(`span`,{className:`structure-name`,children:e.field}),(0,X.jsx)(`span`,{className:`structure-value`,children:e.rules})]},t))})]}),ie.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--queries`,children:[(0,X.jsx)(`h3`,{children:`DB Queries`}),(0,X.jsx)(`div`,{className:`query-list`,children:ie.map((e,t)=>{let n=e.table||(e.model?e.model.split(`\\`).pop():`?`);return(0,X.jsxs)(`div`,{className:`query-item`,children:[(0,X.jsx)(`span`,{className:`query-op query-op--${[`insert`,`update`,`delete`,`statement`].includes(e.operation)?`write`:`read`}`,children:e.operation}),(0,X.jsx)(`span`,{className:`query-table`,title:e.model||void 0,children:n}),e.type===`raw`&&(0,X.jsx)(`span`,{className:`query-badge query-badge--raw`,children:`SQL`})]},t)})})]}),ce.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--cache`,children:[(0,X.jsx)(`h3`,{children:`Cache`}),(0,X.jsx)(`div`,{className:`cache-list`,children:ce.map((e,t)=>(0,X.jsxs)(`div`,{className:`cache-item`,children:[(0,X.jsxs)(`div`,{className:`cache-item-head`,children:[(0,X.jsx)($,{content:qt[e.kind]??e.kind,children:(0,X.jsx)(`span`,{className:`cache-kind cache-kind--${e.kind}`,children:e.kind})}),(0,X.jsx)(`span`,{className:`cache-method`,children:e.method}),e.keyKind===`computed`?(0,X.jsx)($,{content:`The key is built at runtime, so it cannot be read from the source.`,children:(0,X.jsx)(`span`,{className:`cache-key cache-key--computed`,children:`computed key`})}):e.keyKind===`none`?(0,X.jsx)(`span`,{className:`cache-key cache-key--computed`,children:`whole store`}):(0,X.jsx)(`span`,{className:`cache-key cache-key--${e.keyKind}`,title:e.key,children:e.key})]}),(e.tags.length>0||e.store!==``||e.ttl!==null)&&(0,X.jsxs)(`div`,{className:`cache-item-meta`,children:[e.ttl!==null&&(0,X.jsxs)(`span`,{className:`cache-meta`,children:[`ttl `,e.ttl,`s`]}),e.store!==``&&(0,X.jsxs)(`span`,{className:`cache-meta`,children:[`store `,e.store]}),e.tags.map((e,t)=>(0,X.jsx)(`span`,{className:`cache-meta cache-meta--tag`,children:e},t))]})]},t))})]}),H.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--http`,children:[(0,X.jsx)(`h3`,{children:`Outgoing HTTP`}),(0,X.jsx)(`div`,{className:`http-list`,children:H.map((e,t)=>{let n=e.configKey?`config('${e.configKey}')${e.url}`:e.url||`address computed at runtime`;return(0,X.jsxs)(`div`,{className:`http-item`,children:[(0,X.jsxs)(`div`,{className:`http-item-head`,children:[(0,X.jsx)(`span`,{className:`http-method http-method--${(e.method||`unknown`).toLowerCase()}`,children:e.method||`REQUEST`}),(0,X.jsx)(`span`,{className:`http-target`,title:n,children:n})]}),(0,X.jsxs)(`div`,{className:`http-item-meta`,children:[(0,X.jsx)(`span`,{className:`http-badge http-badge--client`,children:e.client}),e.urlSource===`constructed`&&(0,X.jsx)($,{content:`The address starts with this literal and continues with something computed at runtime`,children:(0,X.jsx)(`span`,{className:`http-badge`,children:`partly computed`})}),e.async&&(0,X.jsx)(`span`,{className:`http-badge`,children:`async`}),e.timeout===null?(0,X.jsx)($,{content:`No timeout declared: this request waits as long as the third party takes`,children:(0,X.jsx)(`span`,{className:`http-badge http-badge--absent`,children:`no timeout`})}):(0,X.jsxs)(`span`,{className:`http-badge`,children:[`timeout `,e.timeout,`s`]}),e.retryTimes===null?(0,X.jsx)(`span`,{className:`http-badge http-badge--muted`,children:`no retry`}):(0,X.jsxs)(`span`,{className:`http-badge`,children:[`retry `,e.retryTimes,`×`,e.retrySleep===null?``:` / ${e.retrySleep}ms`]})]})]},t)})})]}),de.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Structure`}),(0,X.jsx)(`ul`,{className:`sidebar-structure-list`,children:de.map((e,t)=>(0,X.jsxs)(`li`,{className:`sidebar-structure-item`,children:[(0,X.jsx)(`span`,{className:`structure-kind`,children:String(e.kind??`item`)}),(0,X.jsx)(`span`,{className:`structure-name`,children:String(e.name??``)}),typeof e.declaringClass==`string`&&e.declaringClass!==``&&(0,X.jsx)(`span`,{className:`structure-decl`,title:`Declared on parent class`,children:e.declaringClass}),e.value!==void 0&&e.value!==null&&(0,X.jsx)(`span`,{className:`structure-value`,children:String(e.value)}),e.static===!0&&(0,X.jsx)(`span`,{className:`structure-flag`,children:`static`}),typeof e.visibility==`string`&&(0,X.jsx)(`span`,{className:`structure-vis`,children:e.visibility})]},t))})]}),me&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Table Data`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`rows`}),(0,X.jsx)(`span`,{className:`prop-value`,children:Kt(me.rows,me.rowsEstimated)})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`table`}),(0,X.jsx)(`span`,{className:`prop-value`,children:Gt(me.tableBytes)})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`indexes`}),(0,X.jsx)(`span`,{className:`prop-value`,children:Gt(me.indexBytes)})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`total`}),(0,X.jsx)(`span`,{className:`prop-value`,children:Gt(me.totalBytes)})]})]}),G&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Event`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`listeners`}),(0,X.jsx)(`span`,{className:`prop-value`,children:G.orphan?`none — firing this does nothing`:`${G.listenerCount}`})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`dispatch`}),(0,X.jsx)(`span`,{className:`prop-value`,children:G.deferred?`after commit (ShouldDispatchAfterCommit)`:`immediate`})]}),G.broadcast&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`broadcast`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`yes`})]}),!G.orphan&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`before commit`}),(0,X.jsx)(`span`,{className:`prop-value`,children:G.observableBeforeCommit?`a listener can act before a surrounding transaction commits`:`no listener runs before the commit`})]}),G.properties?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`payload`}),(0,X.jsx)(`span`,{className:`prop-value`,children:G.properties.join(`, `)})]})]}),he&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Listener`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`runs`}),(0,X.jsx)(`span`,{className:`prop-value`,children:he.queued?`on a queue`:`in the dispatching request`})]}),he.queued&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`waits for commit`}),(0,X.jsx)(`span`,{className:`prop-value`,children:he.deferred?`yes (queue after_commit)`:`no`})]})]}),K&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Queue behaviour`}),K.tries!==null&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`attempts`}),(0,X.jsx)(`span`,{className:`prop-value`,children:K.tries})]}),K.timeout!==null&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`timeout`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[K.timeout,`s`]})]}),K.backoff!==null&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`backoff`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[K.backoff,`s`]})]}),K.maxExceptions!==null&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`max exceptions`}),(0,X.jsx)(`span`,{className:`prop-value`,children:K.maxExceptions})]}),K.unique&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`unique`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[K.uniqueUntilProcessing?`until it starts processing`:`while it is queued or running`,K.uniqueFor===null?``:` \u00b7 ${K.uniqueFor}s`]})]}),K.batchable&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`batch`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`runs as part of one`})]}),K.afterCommit&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`dispatch`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`after the transaction commits`})]}),K.encrypted&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`payload`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`encrypted`})]}),K.middleware.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`middleware`}),(0,X.jsx)(`span`,{className:`prop-value`,children:K.middleware.join(`, `)})]}),K.dynamic.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`decided at runtime`}),(0,X.jsx)(`span`,{className:`prop-value`,children:K.dynamic.map(e=>`${e}()`).join(`, `)})]})]}),U&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Model Schema`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`table`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.table||`—`})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`primary key`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[U.primaryKey,` (`,U.keyType,`)`]})]}),U.morphAlias&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`morph alias`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.morphAlias})]}),!U.morphAlias&&U.morphAliasMissing&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`morph alias`}),(0,X.jsx)(`span`,{className:`prop-value prop-value--warn`,children:`none — this app enforces a morph map`})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`timestamps`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.timestamps?`yes`:`no`})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`soft deletes`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.softDeletes?`yes`:`no`})]}),U.fillable?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`fillable`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.fillable.join(`, `)})]}),U.guarded?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`guarded`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.guarded.join(`, `)})]}),Object.keys(U.casts??{}).length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`casts`}),(0,X.jsx)(`span`,{className:`prop-value`,children:Object.entries(U.casts).map(([e,t])=>`${e}: ${t}`).join(`, `)})]}),U.dates?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`dates`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.dates.join(`, `)})]}),U.appends?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`appends`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.appends.join(`, `)})]}),U.accessors?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`accessors`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.accessors.join(`, `)})]}),U.relationships?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`relationships`}),(0,X.jsx)(`span`,{className:`prop-value`,children:U.relationships.map(e=>`${e.type}(${e.related})`).join(`, `)})]})]}),(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Properties`}),pe.map(([e,t])=>(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:e}),(0,X.jsx)(`span`,{className:`prop-value`,children:Array.isArray(t)?t.map(e=>typeof e==`object`&&e?Object.values(e).join(` `):String(e)).join(`, `)||`—`:String(t)||`—`})]},e))]})]}),ve===`flow`&&(0,X.jsxs)(X.Fragment,{children:[P.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--flowchart`,children:[(0,X.jsxs)(`div`,{className:`sidebar-section-header`,children:[(0,X.jsx)(`h3`,{children:`Method Flow`}),(0,X.jsx)(`button`,{className:`flow-popup-btn`,title:`Open in large view`,onClick:()=>m(!0),children:`⤢`})]}),(0,X.jsx)(at,{steps:P,isFatMethod:ne}),p&&(0,X.jsx)(ft,{steps:P,title:j.label,isFatMethod:ne,onClose:()=>m(!1)})]}),O&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--sequence`,children:[(0,X.jsxs)(`div`,{className:`sidebar-section-header`,children:[(0,X.jsx)(`h3`,{children:`Sequence Diagram`}),(0,X.jsx)(`button`,{className:`flow-popup-btn`,title:`Open in large view`,onClick:()=>v(!0),children:`⤢`})]}),(0,X.jsx)(zt,{diagram:O,title:j.label,theme:n}),_&&(0,X.jsx)(Bt,{diagram:O,title:j.label,theme:n,onClose:()=>v(!1)})]})]}),ve===`source`&&F&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--source`,children:[(0,X.jsxs)(`div`,{className:`sidebar-section-header`,children:[(0,X.jsx)(`h3`,{children:`Source Code`}),(0,X.jsx)(`button`,{className:`flow-popup-btn`,title:`Open in large view`,onClick:()=>g(!0),children:`⤢`})]}),(0,X.jsx)(mt,{filePath:F,highlightLine:ee,theme:n}),h&&(0,X.jsx)(ht,{filePath:F,highlightLine:ee,theme:n,onClose:()=>g(!1)})]}),ve===`edges`&&(0,X.jsxs)(X.Fragment,{children:[N.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Outgoing (`,N.length,`)`]}),N.map(e=>{let t=T.get(e.target);return(0,X.jsxs)(`div`,{className:`edge-row`,children:[(0,X.jsx)(`span`,{className:`edge-label`,children:e.label}),(0,X.jsx)(`span`,{className:`edge-target`,children:t?.label??e.target})]},e.id)})]}),M.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Incoming (`,M.length,`)`]}),M.map(e=>(0,X.jsxs)(`div`,{className:`edge-row`,children:[(0,X.jsx)(`span`,{className:`edge-target`,children:T.get(e.source)?.label??e.source}),(0,X.jsx)(`span`,{className:`edge-label`,children:e.label})]},e.id))]})]}),ve===`schema`&&W&&(0,X.jsxs)(`div`,{children:[(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Columns `,(0,X.jsx)(`span`,{className:`section-count`,children:W.columns.length})]}),(0,X.jsx)(`div`,{className:`schema-table`,children:W.columns.map(e=>(0,X.jsxs)(`div`,{className:`schema-row`,children:[(0,X.jsx)(`span`,{className:`schema-name`,children:e.name}),(0,X.jsx)(`span`,{className:`schema-type`,children:e.type}),(0,X.jsxs)(`span`,{className:`schema-flags`,children:[e.autoIncrement&&(0,X.jsx)(`span`,{className:`schema-flag`,children:`auto`}),e.nullable?(0,X.jsx)(`span`,{className:`schema-flag schema-flag--muted`,children:`null`}):(0,X.jsx)(`span`,{className:`schema-flag`,children:`not null`}),e.default!==null&&(0,X.jsxs)(`span`,{className:`schema-flag schema-flag--muted`,children:[`= `,e.default]})]})]},e.name))})]}),(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Indexes `,(0,X.jsx)(`span`,{className:`section-count`,children:W.indexes.length})]}),W.indexes.length===0&&(0,X.jsx)(`div`,{className:`sidebar-empty`,children:`No indexes.`}),(0,X.jsx)(`div`,{className:`schema-table`,children:W.indexes.map(e=>(0,X.jsxs)(`div`,{className:`schema-row`,children:[(0,X.jsx)(`span`,{className:`schema-name`,children:e.columns.join(`, `)}),(0,X.jsx)(`span`,{className:`schema-type`,children:e.name}),(0,X.jsxs)(`span`,{className:`schema-flags`,children:[e.primary&&(0,X.jsx)(`span`,{className:`schema-flag`,children:`primary`}),e.unique&&!e.primary&&(0,X.jsx)(`span`,{className:`schema-flag`,children:`unique`})]})]},e.name))})]}),(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Foreign keys `,(0,X.jsx)(`span`,{className:`section-count`,children:W.foreignKeys.length})]}),W.foreignKeys.length===0&&(0,X.jsx)(`div`,{className:`sidebar-empty`,children:`No foreign keys.`}),(0,X.jsx)(`div`,{className:`schema-table`,children:W.foreignKeys.map(e=>{let t=W.indexes.some(t=>t.columns.slice(0,e.columns.length).join(`\0`)===e.columns.join(`\0`));return(0,X.jsxs)(`div`,{className:`schema-row${t?``:` schema-row--flagged`}`,children:[(0,X.jsx)(`span`,{className:`schema-name`,children:e.columns.join(`, `)}),(0,X.jsxs)(`span`,{className:`schema-type`,children:[`→ `,e.foreignTable,`.`,e.foreignColumns.join(`, `)]}),(0,X.jsxs)(`span`,{className:`schema-flags`,children:[e.onDelete&&e.onDelete!==`no action`&&(0,X.jsxs)(`span`,{className:`schema-flag schema-flag--muted`,children:[`on delete `,e.onDelete]}),!t&&(0,X.jsx)(`span`,{className:`schema-flag schema-flag--warn`,children:`no index`})]})]},e.name)})})]})]}),ve===`usages`&&e&&(0,X.jsx)(_t,{nodeId:e}),ve===`risks`&&Y&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--security`,children:[be[Y.exposure]&&(()=>{let e=be[Y.exposure],t={public:`This route is publicly accessible — no authentication middleware detected.`,guest:`This route is for unauthenticated users and redirects authenticated ones away.`,authed:`This route requires authentication (auth / sanctum / jwt / passport).`,admin:`This route requires elevated permissions (can:, role:, permission:, ability:, gate:).`};return(0,X.jsxs)(`div`,{className:`security-exposure-card`,style:{borderColor:e.border,background:e.bg+`88`},children:[(0,X.jsx)(`div`,{className:`security-exposure-header`,children:(0,X.jsxs)(`span`,{className:`security-exposure-badge`,style:{color:e.accent},children:[`🔒 `,e.label,` Route`]})}),(0,X.jsx)(`p`,{className:`security-exposure-desc`,children:t[Y.exposure]??t.public})]})})(),Y.issues.length===0?(0,X.jsxs)(`div`,{className:`security-clean`,children:[(0,X.jsx)(`span`,{style:{color:V.none},children:`✓`}),` Nothing flagged here.`]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`div`,{className:`security-issues-title`,children:[Y.issues.length,` Issue`,Y.issues.length===1?``:`s`,` Detected`]}),Y.issues.map((e,t)=>{let n=se[e.type]??{icon:`•`,name:e.type},r=V[e.severity]??V.medium;return(0,X.jsxs)(`div`,{className:`security-issue-card`,style:{borderLeftColor:r},children:[(0,X.jsxs)(`div`,{className:`security-issue-header`,children:[(0,X.jsx)(`span`,{className:`security-issue-icon`,children:n.icon}),(0,X.jsx)(`span`,{className:`security-issue-name`,style:{color:r},children:n.name}),(0,X.jsx)(`span`,{className:`security-issue-severity`,style:{color:r},children:e.severity.toUpperCase()})]}),(0,X.jsx)(`p`,{className:`security-issue-message`,children:e.message}),e.file&&(0,X.jsxs)(`div`,{className:`security-issue-location`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`file`}),(0,X.jsxs)(`span`,{className:`prop-val`,title:e.file,children:[`…`,e.file.split(`/`).slice(-2).join(`/`),e.line?`:${e.line}`:``]})]})]},t)})]})]}),ve===`risks`&&J&&!Y&&(0,X.jsx)(`div`,{className:`sidebar-section`,children:(0,X.jsxs)(`p`,{style:{opacity:.6,fontSize:13},children:[`Security data not available. Re-run `,(0,X.jsx)(`code`,{children:`brain:scan`}),` to generate it.`]})}),ve===`stress`&&J&&e&&(0,X.jsx)(Dt,{method:String(j.data?.method??`GET`),uri:String(j.data?.uri??`/`),theme:n,selectedId:e,onStressChange:i},e)]})]})]})}var $t=[{id:`claude`,label:`Claude Code`,path:`CLAUDE.md`,icon:`🟠`,description:`Anthropic Claude Code CLI & IDE`},{id:`cursor`,label:`Cursor`,path:`.cursor/rules/laravel-brain.mdc`,icon:`⬛`,description:`Cursor AI editor (MDC format with frontmatter)`},{id:`windsurf`,label:`Windsurf`,path:`.windsurf/rules/laravel-brain.md`,icon:`🌊`,description:`Windsurf by Codeium`},{id:`copilot`,label:`GitHub Copilot`,path:`.github/copilot-instructions.md`,icon:`🐙`,description:`Applied repo-wide automatically`},{id:`junie`,label:`JetBrains Junie`,path:`.junie/guidelines.md`,icon:`🧠`,description:`JetBrains AI assistant`},{id:`aider`,label:`Aider`,path:`CONVENTIONS.md`,icon:`⌨️`,description:`Load with: aider --read CONVENTIONS.md`},{id:`agents`,label:`AGENTS.md`,path:`AGENTS.md`,icon:`🌐`,description:`Universal open standard — 60+ tools`},{id:`codex`,label:`OpenAI Codex`,path:`CODEX.md`,icon:`🟢`,description:`Load with: codex --context CODEX.md`}];function en({onClose:e}){let[t,n]=(0,A.useState)(new Set($t.map(e=>e.id))),[r,i]=(0,A.useState)({}),[a,o]=(0,A.useState)(!1),[s,c]=(0,A.useState)(null),l=(0,A.useCallback)(e=>{n(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),u=(0,A.useCallback)(()=>n(new Set($t.map(e=>e.id))),[]),d=(0,A.useCallback)(()=>n(new Set),[]),f=(0,A.useCallback)(async e=>{o(!0),c(null);let n={};t.forEach(e=>{n[e]={status:`generating`}}),i(n);try{let n=await fetch(`/_laravel-brain/api/generate-rules`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({targets:[...t],force:e})}),r=await n.json();if(n.status===409&&r.existing){c(r.existing);let e={};t.forEach(t=>{e[t]={status:`idle`}}),i(e);return}if(!n.ok){let e=r.error??`Generation failed`,n={};t.forEach(t=>{n[t]={status:`error`,error:e}}),i(n);return}let a={};for(let e of r.results??[])a[e.target]=e.success?{status:`success`,path:e.path}:{status:`error`,error:e.error??`Unknown error`};i(a)}catch{let e={};t.forEach(t=>{e[t]={status:`error`,error:`Network error`}}),i(e)}finally{o(!1)}},[t]),p=(0,A.useCallback)(()=>f(!1),[f]),m=(0,A.useCallback)(()=>f(!0),[f]),h=(0,A.useCallback)(()=>c(null),[]),g=Object.values(r).filter(e=>e.status===`success`).length,_=Object.values(r).filter(e=>e.status===`error`).length,v=g+_>0;return(0,X.jsx)(`div`,{className:`export-overlay`,onClick:t=>{t.target===t.currentTarget&&e()},children:(0,X.jsxs)(`div`,{className:`export-modal ai-rules-modal`,children:[(0,X.jsxs)(`div`,{className:`export-modal-header`,children:[(0,X.jsxs)(`div`,{className:`export-modal-title`,children:[(0,X.jsx)(`span`,{className:`export-modal-icon`,children:`🤖`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:`Generate AI Rules Files`}),(0,X.jsx)(`div`,{className:`export-modal-sub`,children:`Write context files for AI coding assistants into your project`})]})]}),(0,X.jsx)(`button`,{className:`export-modal-close`,onClick:e,children:`×`})]}),s&&(0,X.jsxs)(`div`,{className:`ai-rules-overwrite-banner`,children:[(0,X.jsx)(`div`,{className:`ai-rules-overwrite-icon`,children:`⚠️`}),(0,X.jsxs)(`div`,{className:`ai-rules-overwrite-body`,children:[(0,X.jsxs)(`strong`,{children:[`The following file`,s.length===1?``:`s`,` already exist`,s.length===1?`s`:``,`:`]}),(0,X.jsx)(`ul`,{className:`ai-rules-overwrite-list`,children:s.map(e=>(0,X.jsx)(`li`,{children:(0,X.jsx)(`code`,{children:e.path})},e.target))}),(0,X.jsxs)(`span`,{children:[`Do you want to overwrite `,s.length===1?`it`:`them`,`?`]})]}),(0,X.jsxs)(`div`,{className:`ai-rules-overwrite-actions`,children:[(0,X.jsx)(`button`,{className:`export-btn export-btn--secondary`,onClick:h,children:`Cancel`}),(0,X.jsx)(`button`,{className:`export-btn export-btn--danger`,onClick:m,children:`Overwrite`})]})]}),(0,X.jsxs)(`div`,{className:`ai-rules-select-bar`,children:[(0,X.jsxs)(`span`,{className:`ai-rules-select-label`,children:[t.size,` of `,$t.length,` selected`]}),(0,X.jsx)(`button`,{className:`ai-rules-select-link`,onClick:u,children:`All`}),(0,X.jsx)(`span`,{className:`ai-rules-select-sep`,children:`·`}),(0,X.jsx)(`button`,{className:`ai-rules-select-link`,onClick:d,children:`None`})]}),(0,X.jsx)(`div`,{className:`ai-rules-grid`,children:$t.map(e=>{let n=t.has(e.id),i=r[e.id];return(0,X.jsxs)(`label`,{className:`ai-rules-card ${n?`ai-rules-card--selected`:``} ${a?`ai-rules-card--disabled`:``}`,children:[(0,X.jsx)(`input`,{type:`checkbox`,className:`ai-rules-checkbox`,checked:n,disabled:a,onChange:()=>l(e.id)}),(0,X.jsx)(`span`,{className:`ai-rules-card-icon`,children:e.icon}),(0,X.jsxs)(`div`,{className:`ai-rules-card-body`,children:[(0,X.jsx)(`span`,{className:`ai-rules-card-label`,children:e.label}),(0,X.jsx)(`code`,{className:`ai-rules-card-path`,children:e.path}),(0,X.jsx)(`span`,{className:`ai-rules-card-desc`,children:e.description})]}),(0,X.jsxs)(`div`,{className:`ai-rules-card-status`,children:[i?.status===`generating`&&(0,X.jsx)(`span`,{className:`ai-rules-status ai-rules-status--spinning`,children:`⏳`}),i?.status===`success`&&(0,X.jsx)(`span`,{className:`ai-rules-status ai-rules-status--ok`,title:i.path,children:`✓`}),i?.status===`error`&&(0,X.jsx)(`span`,{className:`ai-rules-status ai-rules-status--err`,title:i.error,children:`✗`})]})]},e.id)})}),v&&(0,X.jsxs)(`div`,{className:`ai-rules-summary`,children:[g>0&&(0,X.jsxs)(`span`,{className:`ai-rules-summary--ok`,children:[`✓ `,g,` file`,g===1?``:`s`,` written`]}),_>0&&(0,X.jsxs)(`span`,{className:`ai-rules-summary--err`,children:[`✗ `,_,` error`,_===1?``:`s`]})]}),(0,X.jsxs)(`div`,{className:`ai-rules-footer`,children:[(0,X.jsx)(`button`,{className:`export-btn export-btn--secondary`,onClick:e,disabled:a,children:v?`Close`:`Cancel`}),(0,X.jsx)(`button`,{className:`export-btn export-btn--primary ${a?`export-btn--loading`:``}`,onClick:p,disabled:a||t.size===0,children:a?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`span`,{className:`btn-spinner btn-spinner--small`}),` Generating…`]}):`Generate ${t.size>0?t.size:``} File${t.size===1?``:`s`}`})]})]})})}function tn(e){let t=Math.floor(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);if(n<60)return`${n}m`;let r=Math.floor(n/60);return r<24?`${r}h`:`${Math.floor(r/24)}d`}function nn({label:e,active:t,children:n}){let[r,i]=(0,A.useState)(!1),a=(0,A.useRef)(null);return(0,A.useEffect)(()=>{let e=e=>{a.current&&!a.current.contains(e.target)&&i(!1)};return document.addEventListener(`mousedown`,e,!0),()=>document.removeEventListener(`mousedown`,e,!0)},[]),(0,X.jsxs)(`div`,{className:`seg-dropdown`,ref:a,children:[(0,X.jsx)(`button`,{type:`button`,className:`seg-btn ${t||r?`seg-btn--active`:``}`,onClick:()=>i(!r),children:e}),r&&(0,X.jsx)(`div`,{className:`seg-dropdown-menu`,children:n})]})}function rn({nodeCount:e,edgeCount:t,visibleCount:n,activeTabLabel:r,graphData:i,analyzedAt:a,highRiskCount:o,onOpenRisks:s,theme:c,onSearch:l,onToggleTheme:u,graphRef:d}){let[f,p]=(0,A.useState)(``),[m,h]=(0,A.useState)(!1),[g,_]=(0,A.useState)(!1),[v,y]=(0,A.useState)(!1),b=(0,A.useRef)(null),x=(0,A.useRef)(null);(0,A.useEffect)(()=>(b.current&&clearTimeout(b.current),b.current=setTimeout(()=>l(f),250),()=>{b.current&&clearTimeout(b.current)}),[f,l]),(0,A.useEffect)(()=>{let e=e=>{(e.metaKey||e.ctrlKey)&&e.key.toLowerCase()===`k`?(e.preventDefault(),x.current?.focus(),x.current?.select()):e.key===`Escape`&&document.activeElement===x.current&&x.current?.blur()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]);let S=()=>{d.current?.toPng({scale:2}).then(e=>{e&&Ze(e,`${r.replace(/[^a-z0-9]/gi,`_`)}_graph.png`)})},C=()=>{i&&h(!0)},w=async()=>{if(window.confirm(`This will re-scan the entire project. Proceed?`)){y(!0);try{(await fetch(`/_laravel-brain/api/scan`,{method:`POST`})).ok?window.location.reload():alert(`Scan failed.`)}catch{alert(`Scan failed.`)}finally{y(!1)}}},[T,E]=(0,A.useState)(()=>Date.now());(0,A.useEffect)(()=>{let e=setInterval(()=>E(Date.now()),6e4);return()=>clearInterval(e)},[]);let D=(0,A.useMemo)(()=>a?`scanned ${tn(T-new Date(a).getTime())} ago`:null,[a,T]);return(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`div`,{className:`toolbar`,children:[(0,X.jsxs)(`div`,{className:`toolbar-brand`,children:[(0,X.jsx)(`img`,{src:`/_laravel-brain/logo.png`,alt:`Laravel Brain`,className:`toolbar-logo-img`,width:28,height:28,decoding:`async`}),(0,X.jsxs)(`div`,{className:`toolbar-brand-text`,children:[(0,X.jsx)(`span`,{className:`toolbar-brand-name`,children:`Laravel Brain`}),D&&(0,X.jsx)(`span`,{className:`toolbar-brand-sub`,children:D})]})]}),(0,X.jsxs)(`div`,{className:`toolbar-center`,children:[(0,X.jsxs)(`div`,{className:`toolbar-search-wrapper`,children:[(0,X.jsxs)(`svg`,{className:`toolbar-search-icon`,width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`circle`,{cx:`11`,cy:`11`,r:`8`}),(0,X.jsx)(`line`,{x1:`21`,y1:`21`,x2:`16.65`,y2:`16.65`})]}),(0,X.jsx)(`input`,{ref:x,type:`search`,placeholder:`Search routes, nodes, files…`,className:`toolbar-search`,value:f,onChange:e=>p(e.target.value)}),(0,X.jsx)(`kbd`,{className:`toolbar-kbd`,children:`⌘K`})]}),(0,X.jsx)($,{content:`Routes flagged high or critical risk. Click to open the Risks list.`,children:(0,X.jsxs)(`button`,{type:`button`,className:`risk-pill ${o>0?`risk-pill--alert`:``}`,onClick:s,children:[(0,X.jsx)(`span`,{className:`risk-pill-dot`}),`High-risk`,(0,X.jsx)(`span`,{className:`risk-pill-count`,children:o})]})}),e>80&&(0,X.jsx)($,{content:`Large graph: dagre auto-switched to breadthfirst`,children:(0,X.jsx)(`span`,{className:`stat-chip stat-chip--warn`,children:`⚠ large`})}),(0,X.jsx)($,{content:`Nodes / edges in this graph (visible respects type filters).`,children:(0,X.jsxs)(`span`,{className:`stat-chip`,children:[n,`/`,e,` · `,t,`e`]})})]}),(0,X.jsxs)(`div`,{className:`toolbar-right`,children:[(0,X.jsx)($,{content:c===`dark`?`Switch to light mode`:`Switch to dark mode`,children:(0,X.jsx)(`button`,{type:`button`,onClick:u,className:`icon-btn`,children:c===`dark`?`☀`:`☾`})}),(0,X.jsxs)(nn,{label:`↧`,children:[(0,X.jsx)(`button`,{type:`button`,onClick:S,className:`seg-menu-btn`,children:`Download PNG`}),(0,X.jsx)(`button`,{type:`button`,onClick:C,className:`seg-menu-btn`,disabled:!i,children:`Copy Mermaid`}),(0,X.jsx)(`button`,{type:`button`,onClick:()=>_(!0),className:`seg-menu-btn`,children:`Generate AI Rules`})]}),(0,X.jsx)(`button`,{type:`button`,onClick:w,className:`rescan-btn ${v?`rescan-btn--loading`:``}`,disabled:v,"aria-busy":v,children:v?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`span`,{className:`btn-spinner btn-spinner--small`,"aria-hidden":!0}),(0,X.jsx)(`span`,{children:`Scanning…`})]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`path`,{d:`M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`}),(0,X.jsx)(`path`,{d:`M3 3v5h5`}),(0,X.jsx)(`path`,{d:`M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16`}),(0,X.jsx)(`path`,{d:`M16 16h5v5`})]}),(0,X.jsx)(`span`,{children:`Re-scan`})]})})]})]}),g&&(0,X.jsx)(en,{onClose:()=>_(!1)}),m&&i&&(0,X.jsx)(it,{mermaidCode:qe(i,r),filename:`${r.replace(/[^a-z0-9]/gi,`_`)}_graph.mmd`,title:`${r} — Full Lifecycle Graph`,onClose:()=>h(!1)})]})}var an={route:`Routes`,middleware:`Middleware`,controller:`Controllers`,livewire_component:`Livewire`,action:`Controller actions`,action_class:`Actions`,service:`Services`,validation_request:`Validation`,model:`Models`,event:`Events`,listener:`Listeners`,job:`Jobs`,command:`Commands`,channel:`Channels`,schedule:`Schedules`,view:`Views`,mail:`Mail`,notification:`Notifications`,enum:`Enums`,interface:`Interfaces`,trait:`Traits`,abstract_class:`Abstract`,service_provider:`Providers`,facade:`Facades`,ai_agent:`AI Agents`,ai_tool:`AI Tools`,filament_panel:`F. Panels`,filament_resource:`F. Resources`,filament_page:`F. Pages`,filament_page_method:`F. Methods`,filament_widget:`F. Widgets`,filament_relation_manager:`F. Relations`},on=`route.middleware.controller.livewire_component.action.action_class.service.validation_request.model.event.listener.job.command.channel.schedule.view.mail.notification.enum.interface.trait.abstract_class.service_provider.facade.ai_agent.ai_tool.filament_panel.filament_resource.filament_page.filament_page_method.filament_widget.filament_relation_manager`.split(`.`),sn=[{type:`transaction`,label:`Transactions`,description:`the boundary drawn around work that runs in one transaction`},{type:`chain`,label:`Chains`,description:`the boundary and the arrows drawn around jobs that run one after another`},{type:`batch`,label:`Batches`,description:`the boundary drawn around jobs dispatched together, in no particular order`}];function cn({visibleTypes:e,counts:t,onToggle:n,onShowAll:r,onHideAll:i}){let a=on.filter(e=>(t[e]??0)>0),o=new Map(sn.map(e=>[e.type,e]));for(let e of sn)(t[e.type]??0)>0&&a.push(e.type);return(0,X.jsxs)(`div`,{className:`show-graph`,children:[(0,X.jsxs)(`div`,{className:`show-graph-header`,children:[(0,X.jsx)(`span`,{className:`show-graph-title`,children:`Show on graph`}),(0,X.jsxs)(`div`,{className:`show-graph-actions`,children:[(0,X.jsx)(`button`,{type:`button`,onClick:r,className:`show-graph-link`,children:`All`}),(0,X.jsx)(`span`,{className:`show-graph-sep`,children:`/`}),(0,X.jsx)(`button`,{type:`button`,onClick:i,className:`show-graph-link`,children:`None`})]})]}),(0,X.jsx)(`div`,{className:`show-graph-grid`,children:a.map(r=>{let i=t[r]??0,a=e.has(r),s=o.get(r),c=s?I[r]??`#94a3b8`:L[r]??`#94a3b8`,l=s?.label??an[r]??r;return(0,X.jsx)($,{content:s?`${a?`Hide`:`Show`} ${s.description}`:`${a?`Hide`:`Show`} ${l} nodes`,children:(0,X.jsxs)(`button`,{type:`button`,className:`show-graph-item ${a?``:`show-graph-item--off`}`,onClick:()=>n(r),children:[(0,X.jsx)(`span`,{className:`show-graph-dot`,style:{backgroundColor:c}}),(0,X.jsx)(`span`,{className:`show-graph-label`,children:l}),(0,X.jsx)(`span`,{className:`show-graph-count`,children:i})]})},r)})})]})}var ln={none:0,low:1,medium:2,high:3,critical:4},un=280,dn=480,fn=300,pn={GET:`#4ade80`,POST:`#60a5fa`,PUT:`#f59e0b`,PATCH:`#a78bfa`,DELETE:`#f87171`,OPTIONS:`#22d3ee`,QUERY:`#f472b6`},mn=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`OPTIONS`,`QUERY`];function hn(e){let[t,...n]=e.split(` `);return t in pn?{method:t,uri:n.join(` `)}:{method:null,uri:e}}function gn(e){let t=e.schedule,n=t?` ${t.cadence} ${t.timezone} ${t.modifiers.join(` `)}`:``;return`${e.label}${n}`.toLowerCase()}function _n(e){return e.riskLevel??`none`}function vn(e){let t=[];e.securityCount&&t.push(`${e.securityCount} security`),e.n1Count&&t.push(`${e.n1Count} N+1`);let n=(e.fatMethodCount??0)+(e.fatClassCount??0);return n&&t.push(`${n} fat`),t.length?t.join(` · `):`flagged for review`}function yn(e){if(!e)return`new`;let t=Date.now()-new Date(e).getTime(),n=Math.floor(t/6e4);if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function bn({tab:e,isActive:t,isLoading:n,onSelect:r}){let{method:i,uri:a}=hn(e.label),o=i?pn[i]:`var(--faint)`,s=_n(e),c=s===`high`||s===`critical`?`var(--danger)`:e.issueCount?`var(--warn)`:null;return(0,X.jsx)($,{content:`Open lifecycle graph · ${e.nodeCount} nodes · ${e.edgeCount} edges`,children:(0,X.jsxs)(`button`,{className:`route-row ${t?`route-row--active`:``}`,type:`button`,onClick:()=>r(e),children:[(0,X.jsx)(`span`,{className:`route-row-method`,style:{color:o},children:i??`›`}),(0,X.jsx)(`span`,{className:`route-row-uri`,children:a}),c&&(0,X.jsx)(`span`,{className:`route-row-risk`,style:{"--rc":c},children:e.issueCount}),n&&(0,X.jsx)(`span`,{className:`route-row-loading`,children:`…`})]})})}var xn={command:`CMD`,job:`JOB`,call:`FN`},Sn={withoutOverlapping:`no overlap`,onOneServer:`one server`,runInBackground:`background`,evenInMaintenanceMode:`in maintenance`};function Cn(e){let t=e.split(`\\`).pop();return t&&t.length>0?t:e}function wn({tab:e,schedule:t,isActive:n,isLoading:r,onSelect:i}){let a=t.cadence||`no cadence stated`,o=t.modifiers.map(e=>Sn[e]??e);return(0,X.jsx)($,{content:`${t.target} · ${a}${t.timezone?` · ${t.timezone}`:``} · ${e.nodeCount} nodes`,children:(0,X.jsxs)(`button`,{className:`route-row route-row--stacked ${n?`route-row--active`:``}`,type:`button`,onClick:()=>i(e),children:[(0,X.jsx)(`span`,{className:`route-row-method`,style:{color:L[t.type===`job`?`job`:`command`]},children:xn[t.type]??`›`}),(0,X.jsxs)(`span`,{className:`schedule-row-body`,children:[(0,X.jsx)(`span`,{className:`schedule-row-scroll`,children:(0,X.jsx)(`span`,{className:`route-row-uri`,children:Cn(t.target)})}),(0,X.jsx)(`span`,{className:`schedule-cadence ${t.cadence?``:`schedule-cadence--unknown`}`,children:a}),(t.timezone||o.length>0)&&(0,X.jsxs)(`span`,{className:`schedule-row-scroll schedule-row-badges`,children:[t.timezone&&(0,X.jsx)(`span`,{className:`schedule-chip`,children:t.timezone}),o.map(e=>(0,X.jsx)(`span`,{className:`schedule-chip`,children:e},e))]})]}),r&&(0,X.jsx)(`span`,{className:`route-row-loading`,children:`…`})]})})}function Tn({tab:e,isActive:t,isLoading:n,onSelect:r}){return e.schedule?(0,X.jsx)(wn,{tab:e,schedule:e.schedule,isActive:t,isLoading:n,onSelect:r}):(0,X.jsx)(bn,{tab:e,isActive:t,isLoading:n,onSelect:r})}var En={shield:(0,X.jsx)(`path`,{d:`M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z`}),lock:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`3`,y:`11`,width:`18`,height:`11`,rx:`2`}),(0,X.jsx)(`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`})]}),key:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`7.5`,cy:`15.5`,r:`4.5`}),(0,X.jsx)(`path`,{d:`m10.7 12.3 8.3-8.3`}),(0,X.jsx)(`path`,{d:`m17 5 3 3`}),(0,X.jsx)(`path`,{d:`m15 7 3 3`})]}),user:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2`}),(0,X.jsx)(`circle`,{cx:`12`,cy:`7`,r:`4`})]}),users:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2`}),(0,X.jsx)(`circle`,{cx:`9`,cy:`7`,r:`4`}),(0,X.jsx)(`path`,{d:`M22 21v-2a4 4 0 0 0-3-3.87`}),(0,X.jsx)(`path`,{d:`M16 3.13a4 4 0 0 1 0 7.75`})]}),building:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`4`,y:`2`,width:`16`,height:`20`,rx:`2`}),(0,X.jsx)(`path`,{d:`M9 22v-4h6v4`}),(0,X.jsx)(`path`,{d:`M8 6h.01M16 6h.01M8 10h.01M16 10h.01M8 14h.01M16 14h.01`})]}),dashboard:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`3`,y:`3`,width:`7`,height:`9`}),(0,X.jsx)(`rect`,{x:`14`,y:`3`,width:`7`,height:`5`}),(0,X.jsx)(`rect`,{x:`14`,y:`12`,width:`7`,height:`9`}),(0,X.jsx)(`rect`,{x:`3`,y:`16`,width:`7`,height:`5`})]}),settings:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z`}),(0,X.jsx)(`circle`,{cx:`12`,cy:`12`,r:`3`})]}),card:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`2`,y:`5`,width:`20`,height:`14`,rx:`2`}),(0,X.jsx)(`line`,{x1:`2`,y1:`10`,x2:`22`,y2:`10`})]}),cart:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`9`,cy:`21`,r:`1`}),(0,X.jsx)(`circle`,{cx:`20`,cy:`21`,r:`1`}),(0,X.jsx)(`path`,{d:`M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6`})]}),package:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z`}),(0,X.jsx)(`path`,{d:`M3.27 6.96 12 12.01l8.73-5.05`}),(0,X.jsx)(`path`,{d:`M12 22.08V12`})]}),file:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z`}),(0,X.jsx)(`polyline`,{points:`14 2 14 8 20 8`}),(0,X.jsx)(`line`,{x1:`16`,y1:`13`,x2:`8`,y2:`13`}),(0,X.jsx)(`line`,{x1:`16`,y1:`17`,x2:`8`,y2:`17`})]}),message:(0,X.jsx)(`path`,{d:`M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z`}),bell:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9`}),(0,X.jsx)(`path`,{d:`M13.73 21a2 2 0 0 1-3.46 0`})]}),mail:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`2`,y:`4`,width:`20`,height:`16`,rx:`2`}),(0,X.jsx)(`path`,{d:`m22 7-10 5L2 7`})]}),search:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`11`,cy:`11`,r:`8`}),(0,X.jsx)(`line`,{x1:`21`,y1:`21`,x2:`16.65`,y2:`16.65`})]}),folder:(0,X.jsx)(`path`,{d:`M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z`}),download:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`}),(0,X.jsx)(`polyline`,{points:`7 10 12 15 17 10`}),(0,X.jsx)(`line`,{x1:`12`,y1:`15`,x2:`12`,y2:`3`})]}),upload:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`}),(0,X.jsx)(`polyline`,{points:`17 8 12 3 7 8`}),(0,X.jsx)(`line`,{x1:`12`,y1:`3`,x2:`12`,y2:`15`})]}),chart:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`line`,{x1:`12`,y1:`20`,x2:`12`,y2:`10`}),(0,X.jsx)(`line`,{x1:`18`,y1:`20`,x2:`18`,y2:`4`}),(0,X.jsx)(`line`,{x1:`6`,y1:`20`,x2:`6`,y2:`16`})]}),list:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`line`,{x1:`8`,y1:`6`,x2:`21`,y2:`6`}),(0,X.jsx)(`line`,{x1:`8`,y1:`12`,x2:`21`,y2:`12`}),(0,X.jsx)(`line`,{x1:`8`,y1:`18`,x2:`21`,y2:`18`}),(0,X.jsx)(`line`,{x1:`3`,y1:`6`,x2:`3.01`,y2:`6`}),(0,X.jsx)(`line`,{x1:`3`,y1:`12`,x2:`3.01`,y2:`12`}),(0,X.jsx)(`line`,{x1:`3`,y1:`18`,x2:`3.01`,y2:`18`})]}),activity:(0,X.jsx)(`polyline`,{points:`22 12 18 12 15 21 9 3 6 12 2 12`}),link:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71`}),(0,X.jsx)(`path`,{d:`M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71`})]}),zap:(0,X.jsx)(`polygon`,{points:`13 2 3 14 12 14 11 22 21 10 12 10 13 2`}),box:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`4`,y:`4`,width:`16`,height:`16`,rx:`2`}),(0,X.jsx)(`rect`,{x:`9`,y:`9`,width:`6`,height:`6`})]}),calendar:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`3`,y:`4`,width:`18`,height:`18`,rx:`2`}),(0,X.jsx)(`line`,{x1:`16`,y1:`2`,x2:`16`,y2:`6`}),(0,X.jsx)(`line`,{x1:`8`,y1:`2`,x2:`8`,y2:`6`}),(0,X.jsx)(`line`,{x1:`3`,y1:`10`,x2:`21`,y2:`10`})]}),pin:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z`}),(0,X.jsx)(`circle`,{cx:`12`,cy:`10`,r:`3`})]}),book:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z`}),(0,X.jsx)(`path`,{d:`M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z`})]}),info:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`12`,cy:`12`,r:`10`}),(0,X.jsx)(`line`,{x1:`12`,y1:`16`,x2:`12`,y2:`12`}),(0,X.jsx)(`line`,{x1:`12`,y1:`8`,x2:`12.01`,y2:`8`})]}),beaker:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M9 3h6`}),(0,X.jsx)(`path`,{d:`M10 3v6l-5.5 9.5A2 2 0 0 0 6.2 21h11.6a2 2 0 0 0 1.7-3.5L14 9V3`})]}),tag:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M20.59 13.41 13.42 20.58a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z`}),(0,X.jsx)(`line`,{x1:`7`,y1:`7`,x2:`7.01`,y2:`7`})]}),broadcast:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M4 11a9 9 0 0 1 9 9`}),(0,X.jsx)(`path`,{d:`M4 4a16 16 0 0 1 16 16`}),(0,X.jsx)(`circle`,{cx:`5`,cy:`19`,r:`1`})]}),hash:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`line`,{x1:`4`,y1:`9`,x2:`20`,y2:`9`}),(0,X.jsx)(`line`,{x1:`4`,y1:`15`,x2:`20`,y2:`15`}),(0,X.jsx)(`line`,{x1:`10`,y1:`3`,x2:`8`,y2:`21`}),(0,X.jsx)(`line`,{x1:`16`,y1:`3`,x2:`14`,y2:`21`})]}),terminal:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`polyline`,{points:`4 17 10 11 4 5`}),(0,X.jsx)(`line`,{x1:`12`,y1:`19`,x2:`20`,y2:`19`})]}),clock:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`12`,cy:`12`,r:`10`}),(0,X.jsx)(`polyline`,{points:`12 6 12 12 16 14`})]}),route:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`6`,cy:`19`,r:`3`}),(0,X.jsx)(`circle`,{cx:`18`,cy:`5`,r:`3`}),(0,X.jsx)(`path`,{d:`M9 19h6a4 4 0 0 0 4-4V9`})]})};function Dn({name:e}){return(0,X.jsx)(`svg`,{className:`tree-group-icon`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:En[e]})}var On=[[/^(auth|login|register|signin|signup|signout|logout|verify)/i,`lock`],[/^(password|forgot|reset|recover)/i,`key`],[/^(oauth|sso|saml|token|jwt|sanctum|passport)/i,`key`],[/^(admin|backend|manage|mgmt|cp|role|permission|acl|guard|policy|gate|abilit|security|firewall|protect|shield)/i,`shield`],[/^(team|organization|org|company|tenant|workspace)/i,`building`],[/^(user|account|profile|member|people|person)/i,`user`],[/^(group|staff|contributor|follower)/i,`users`],[/^(dashboard|home|overview|index|main|panel)/i,`dashboard`],[/^(setting|config|preference|option|env)/i,`settings`],[/^(billing|payment|invoice|subscription|plan|pricing|wallet|transaction|refund)/i,`card`],[/^(checkout|cart|basket|bag)/i,`cart`],[/^(order|purchase|fulfil|shipping|delivery|product|catalog|catalogue|item|shop|store|inventory|stock)/i,`package`],[/^(blog|post|article|news|content|page|cms)/i,`file`],[/^(message|chat|conversation|inbox|thread|dm|comment|review|rating|feedback|reply)/i,`message`],[/^(notification|notif|alert|push)/i,`bell`],[/^(mail|email|newsletter|campaign)/i,`mail`],[/^(search|explore|discover|find|query|filter)/i,`search`],[/^(upload|file|files|media|image|photo|asset|document|docs?|attachment|storage)/i,`folder`],[/^(download|export|backup|dump)/i,`download`],[/^(import|sync|migrate)/i,`upload`],[/^(report|analytic|stat|statistic|metric|insight|chart|kpi)/i,`chart`],[/^(log|logs|audit|activity|history|track|trace)/i,`list`],[/^(health|status|ping|up|ready|live|heartbeat|probe|monitor)/i,`activity`],[/^(webhook|callback|hook|integration|connect|link)/i,`link`],[/^(cache|redis|optimize)/i,`zap`],[/^(queue|job|jobs|worker|batch|cron)/i,`box`],[/^(calendar|event|booking|appointment|reservation|slot)/i,`calendar`],[/^(map|location|geo|address|place|region|country)/i,`pin`],[/^(project|board|workflow|pipeline)/i,`folder`],[/^(help|support|faq|guide|tutorial|kb|knowledge|wiki)/i,`book`],[/^(contact|enquir|inquir|lead)/i,`user`],[/^(about|info|legal|privacy|terms|policy)/i,`info`],[/^(test|tests|debug|dev|sandbox|playground|demo|example)/i,`beaker`],[/^(tag|tags|category|categories|topic|label)/i,`tag`],[/^(feed|rss|atom|socket|ws|realtime|broadcast|stream)/i,`broadcast`],[/^(api|graphql|ql|rest|rpc)$/i,`hash`],[/^v?\d+(\.\d+)*$/i,`hash`]],kn={"Console Commands":`terminal`,"Broadcast Channels":`broadcast`,Schedules:`clock`,"Model ERD":`box`,"Event Choreography":`zap`,"AI Agents":`zap`,Other:`route`};function An(e,t){if(t)return e.startsWith(`Filament`)?`box`:kn[e]??`route`;for(let[t,n]of On)if(t.test(e))return n;return`route`}function jn(e){if(e.category===`Command`)return`Console Commands`;if(e.category===`Channel`)return`Broadcast Channels`;if(e.category===`Schedule`)return`Schedules`;if(e.category===`ERD`)return`Model ERD`;if(e.category===`Events`)return`Event Choreography`;if(e.category===`AI`)return`AI Agents`;if(e.category===`Filament`){let t=e.panelId??``;return t?`Filament · ${t.charAt(0).toUpperCase()}${t.slice(1)} Panel`:`Filament`}return`Other`}function Mn(e){e.children.sort((e,t)=>e.name.localeCompare(t.name)),e.leaves.sort((e,t)=>e.label.localeCompare(t.label)),e.children.forEach(Mn)}function Nn(e){let t=e.label.split(` `)[0];return t in pn?e.label.slice(t.length).trim().split(`/`).filter(Boolean):null}function Pn(e){let t={name:``,path:``,isCategory:!1,children:[],leaves:[]},n=(e,t,n)=>{let r=e.children.find(e=>e.name===t);return r||(r={name:t,path:e.path?`${e.path}/${t}`:t,isCategory:n,children:[],leaves:[]},e.children.push(r)),r},r=new Set;for(let t of e){let e=Nn(t);if(!e)continue;let n=e.slice(0,-1);for(let e=1;e<=n.length;e++)r.add(n.slice(0,e).join(`/`))}for(let i of e){let e=Nn(i);if(!e){n(t,jn(i),!0).leaves.push(i);continue}let a=e.join(`/`),o=a!==``&&r.has(a)?e:e.slice(0,-1),s=t;for(let e of o)s=n(s,e,!1);s.leaves.push(i)}return Mn(t),t}function Fn(e){return e.leaves.length+e.children.reduce((e,t)=>e+Fn(t),0)}function In({node:e,forceOpen:t,expanded:n,onToggle:r,activeId:i,loadingId:a,onSelect:o,level:s=0}){let c=t||n.has(e.path),l=e.isCategory?e.name:`/${e.name}`;return(0,X.jsxs)(`div`,{className:`tree-group`,children:[(0,X.jsxs)(`button`,{type:`button`,className:`tree-group-header`,onClick:()=>r(e.path),children:[(0,X.jsx)(`span`,{className:`tree-group-chevron`,children:c?`▾`:`▸`}),s===0&&(0,X.jsx)(Dn,{name:An(e.name,e.isCategory)}),(0,X.jsx)(`span`,{className:`tree-group-name`,children:l}),(0,X.jsx)(`span`,{className:`tree-group-count`,children:Fn(e)})]}),c&&(0,X.jsxs)(`div`,{className:`tree-group-body`,children:[e.children.map(e=>(0,X.jsx)(In,{node:e,forceOpen:t,expanded:n,onToggle:r,activeId:i,loadingId:a,onSelect:o,level:s+1},e.path)),e.leaves.map(e=>(0,X.jsx)(Tn,{tab:e,isActive:e.id===i,isLoading:e.id===a,onSelect:o},e.id))]})]})}function Ln({tab:e,isActive:t,onSelect:n,timestamp:r}){let{method:i,uri:a}=hn(e.label),o=_n(e),s=o===`critical`?`critical`:o===`high`?`high`:o===`medium`?`medium`:`low`,c=V[s]??V.medium;return(0,X.jsxs)(`button`,{type:`button`,className:`flag-card ${t?`flag-card--active`:``}`,onClick:()=>n(e),children:[(0,X.jsxs)(`div`,{className:`flag-card-top`,children:[r?(0,X.jsx)(`span`,{className:`flag-card-time`,children:r}):(0,X.jsx)(`span`,{className:`flag-card-sev`,style:{"--sc":c},children:(oe[s]??s).toUpperCase()}),i&&(0,X.jsx)(`span`,{className:`flag-card-method`,style:{color:pn[i]},children:i})]}),(0,X.jsx)(`div`,{className:`flag-card-path`,children:a}),(0,X.jsx)(`div`,{className:`flag-card-desc`,children:vn(e)})]})}function Rn({tabs:e,activeId:t,loadingId:n,onSelect:r,mode:i,onModeChange:a,previousAnalyzedAt:o,visibleTypes:s,counts:c,onToggle:l,onShowAll:u,onHideAll:d}){let[f,p]=(0,A.useState)(fn),[m,h]=(0,A.useState)(``),[g,_]=(0,A.useState)(new Set(mn)),[v,y]=(0,A.useState)(new Set),b=(0,A.useCallback)(e=>{_(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),x=(0,A.useCallback)(e=>y(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n}),[]),S=(0,A.useRef)(!1),C=(0,A.useRef)(0),w=(0,A.useRef)(fn),T=(0,A.useCallback)(e=>{e.preventDefault(),S.current=!0,C.current=e.clientX,w.current=f;let t=e=>{if(!S.current)return;let t=e.clientX-C.current;p(Math.min(dn,Math.max(un,w.current+t)))},n=()=>{S.current=!1,window.removeEventListener(`mousemove`,t),window.removeEventListener(`mouseup`,n),document.body.style.cursor=``,document.body.style.userSelect=``};document.body.style.cursor=`col-resize`,document.body.style.userSelect=`none`,window.addEventListener(`mousemove`,t),window.addEventListener(`mouseup`,n)},[f]),E=m.trim().toLowerCase(),D=(0,A.useMemo)(()=>{let t=mn.every(e=>g.has(e));return e.filter(e=>{if(E&&!gn(e).includes(E))return!1;if(!t){let t=e.label.split(` `)[0];if(t in pn&&!g.has(t))return!1}return!0})},[e,E,g]),O=E.length>0||!mn.every(e=>g.has(e)),k=(0,A.useMemo)(()=>Pn(D),[D]),j=(0,A.useMemo)(()=>D.filter(e=>_n(e)!==`none`).sort((e,t)=>(ln[_n(t)]??0)-(ln[_n(e)]??0)),[D]),M=(0,A.useMemo)(()=>D.filter(e=>e.changeStatus===`new`||e.changeStatus===`changed`),[D]),N=[{id:`routes`,label:`Routes`,count:D.length},{id:`risks`,label:`Risks`,count:j.length},{id:`recent`,label:`Recent`,count:M.length}];return(0,X.jsxs)(`div`,{className:`left-sidebar-resizable`,style:{width:f,"--left-sidebar-width":`${f}px`},children:[(0,X.jsxs)(`div`,{className:`left-sidebar`,children:[(0,X.jsxs)(`div`,{className:`left-search`,children:[(0,X.jsx)(`input`,{className:`left-search-input`,type:`text`,placeholder:`Search routes…`,value:m,onChange:e=>h(e.target.value)}),m&&(0,X.jsx)(`button`,{type:`button`,className:`left-search-clear`,onClick:()=>h(``),children:`×`})]}),(0,X.jsx)(`div`,{className:`left-method-chips`,children:mn.map(e=>(0,X.jsx)(`button`,{type:`button`,className:`method-chip ${g.has(e)?`method-chip--on`:``}`,style:{"--mc":pn[e]},onClick:()=>b(e),children:e},e))}),(0,X.jsx)(`div`,{className:`mode-tabs`,children:N.map(e=>(0,X.jsxs)(`button`,{type:`button`,className:`mode-tab ${i===e.id?`mode-tab--active`:``}`,onClick:()=>a(e.id),children:[e.label,(0,X.jsx)(`span`,{className:`mode-tab-count ${e.id===`risks`&&i===`risks`&&e.count>0?`mode-tab-count--alert`:``}`,children:e.count})]},e.id))}),(0,X.jsxs)(`div`,{className:`left-content`,children:[i===`routes`&&(0,X.jsxs)(`div`,{className:`route-tree`,children:[k.children.length===0&&k.leaves.length===0&&(0,X.jsx)(`div`,{className:`left-empty`,children:`No routes match.`}),k.children.map(e=>(0,X.jsx)(In,{node:e,forceOpen:E.length>0,expanded:v,onToggle:x,activeId:t,loadingId:n,onSelect:r},e.path)),k.leaves.map(e=>(0,X.jsx)(Tn,{tab:e,isActive:e.id===t,isLoading:e.id===n,onSelect:r},e.id))]}),i===`risks`&&(0,X.jsxs)(`div`,{className:`flag-list`,children:[j.length===0&&(0,X.jsx)(`div`,{className:`left-empty`,children:O?`No flagged routes match the filter.`:`No flagged routes. ✓`}),j.map(e=>(0,X.jsx)(Ln,{tab:e,isActive:e.id===t,onSelect:r},e.id))]}),i===`recent`&&(0,X.jsxs)(`div`,{className:`flag-list`,children:[M.length===0&&(0,X.jsx)(`div`,{className:`left-empty`,children:O?`Nothing matching the filter changed since the previous scan.`:`Nothing changed since the previous scan.`}),M.map(e=>(0,X.jsx)(Ln,{tab:e,isActive:e.id===t,onSelect:r,timestamp:`${e.changeStatus===`new`?`new`:`changed`} · ${yn(o)}`},e.id))]})]}),(0,X.jsx)(`div`,{className:`left-footer`,children:(0,X.jsx)(cn,{visibleTypes:s,counts:c,onToggle:l,onShowAll:u,onHideAll:d})})]}),(0,X.jsx)($,{content:`Drag to resize`,children:(0,X.jsx)(`div`,{className:`left-sidebar-drag-handle`,onMouseDown:T})})]})}var zn=[...`route.middleware.controller.livewire_component.action.action_class.service.validation_request.model.event.listener.job.command.channel.schedule.view.mail.notification.enum.interface.trait.abstract_class.service_provider.ai_agent.ai_tool.filament_panel.filament_resource.filament_page.filament_page_method.filament_widget.filament_relation_manager`.split(`.`),`transaction`,`chain`,`batch`];function Bn(){let{theme:e,toggle:t}=ee(),{manifest:n,loading:r,error:i}=M(),{state:a,elements:o,load:s}=P(),c=F(o),[l,u]=(0,A.useState)(null),[d,f]=(0,A.useState)(null),[p,m]=(0,A.useState)(`dagre`),[h,g]=(0,A.useState)(null),[_,v]=(0,A.useState)(`routes`),[y,b]=(0,A.useState)(``),[x,S]=(0,A.useState)(!1),[C,w]=(0,A.useState)(new Set(zn)),[T,E]=(0,A.useState)(`TB`),[D,O]=(0,A.useState)(null),[k,j]=(0,A.useState)(0),N=(0,A.useRef)(null),te=(0,A.useCallback)(e=>{if(l?.id===e.id)return;let t=new URL(window.location.href);t.searchParams.get(`tab`)!==e.id&&(t.searchParams.set(`tab`,e.id),window.history.pushState({tabId:e.id},``,t.toString())),u(e),b(``),S(!0),s(e.file)},[l,s]),[I,ne]=(0,A.useState)(n);if(n!==I&&(ne(n),n&&!l)){let e=new URLSearchParams(window.location.search).get(`tab`),t=n.tabs.find(t=>t.id===e);t&&te(t)}let[L,re]=(0,A.useState)(a.data);if(a.data!==L)if(re(a.data),a.data)if(w(new Set(zn)),x){S(!1);let e=a.data.nodes.find(e=>e.type===`route`);g(e?e.id:null)}else g(null);else g(null);(0,A.useEffect)(()=>{let e=()=>{if(!n)return;let e=new URLSearchParams(window.location.search).get(`tab`),t=n.tabs.find(t=>t.id===e);t&&(u(t),s(t.file))};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[n,s]);let R=(0,A.useCallback)(e=>{g(e)},[]),[z,ie]=(0,A.useState)(a.loading);a.loading!==z&&(ie(a.loading),a.loading||f(null));let B=(0,A.useMemo)(()=>n?.tabs??[],[n]),ae=(0,A.useMemo)(()=>B.filter(e=>e.riskLevel===`high`||e.riskLevel===`critical`).length,[B]),V=(0,A.useMemo)(()=>{if(!a.data)return{};let e=a.data.nodes.reduce((e,t)=>(e[t.type]=(e[t.type]??0)+1,e),{}),t={};for(let e of a.data.nodes)for(let n of K(e)){let e=n.kind===`rollback`?`transaction`:n.kind;t[e]=(t[e]??new Set).add(n.id)}for(let[n,r]of Object.entries(t))e[n]=r.size;return e},[a.data]),oe=(0,A.useMemo)(()=>a.data?a.data.nodes.filter(e=>C.has(e.type)).length:0,[a.data,C]),se=(0,A.useCallback)(e=>{w(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),ce=(0,A.useCallback)(()=>w(new Set(zn)),[]),H=(0,A.useCallback)(()=>w(new Set),[]),[le,ue]=(0,A.useState)(!1),[de,fe]=(0,A.useState)(!1),[pe,U]=(0,A.useState)(`all`),[me,W]=(0,A.useState)(!1),[G,he]=(0,A.useState)(!1);return r?(0,X.jsxs)(`div`,{className:`loading-screen`,children:[(0,X.jsx)(`div`,{className:`loading-spinner`}),(0,X.jsx)(`p`,{children:`Loading project graph...`})]}):i||!n?(0,X.jsx)(`div`,{className:`error-screen welcome-screen`,children:(0,X.jsxs)(`div`,{className:`welcome-card`,children:[(0,X.jsx)(`div`,{className:`welcome-icon`,children:(0,X.jsx)(`img`,{src:`/_laravel-brain/logo.png`,alt:`Laravel Brain`})}),(0,X.jsx)(`h2`,{children:`Welcome to Laravel Brain`}),(0,X.jsx)(`p`,{children:`No project analysis found. To begin exploring your code architecture, please run an initial scan.`}),i&&i!==`HTTP 404`&&(0,X.jsx)(`div`,{className:`error-details`,children:(0,X.jsxs)(`small`,{children:[`Error: `,i]})}),(0,X.jsx)(`button`,{className:`scan-btn ${le?`scan-btn--loading`:``}`,onClick:async()=>{if(window.confirm(`This will scan the entire project. Proceed?`)){ue(!0);try{(await fetch(`/_laravel-brain/api/scan`,{method:`POST`})).ok?window.location.reload():alert(`Scan failed.`)}catch{alert(`Scan failed.`)}finally{ue(!1)}}},disabled:le,children:le?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`div`,{className:`btn-spinner`}),`Analyzing Project...`]}):`🚀 Start Initial Scan`}),(0,X.jsxs)(`div`,{className:`welcome-hint`,children:[`Alternatively, run `,(0,X.jsx)(`code`,{children:`php artisan brain:scan`}),` in your terminal.`]})]})}):(0,X.jsxs)(`div`,{className:`app`,children:[(0,X.jsx)(rn,{nodeCount:a.data?.meta.nodeCount??l?.nodeCount??0,edgeCount:a.data?.meta.edgeCount??l?.edgeCount??0,visibleCount:oe,activeTabLabel:l?.label??`graph`,graphData:a.data??null,analyzedAt:n.analyzedAt,highRiskCount:ae,onOpenRisks:()=>v(`risks`),theme:e,onSearch:b,onToggleTheme:t,graphRef:N}),(0,X.jsxs)(`div`,{className:`main`,children:[(0,X.jsx)(Rn,{tabs:B,activeId:l?.id??null,loadingId:d,onSelect:te,mode:_,onModeChange:v,previousAnalyzedAt:n.previousAnalyzedAt,visibleTypes:C,counts:V,onToggle:se,onShowAll:ce,onHideAll:H,graphData:a.data??null,complexityFilter:pe,onComplexityFilterChange:U,onNodeSelect:R,selectedId:h}),(0,X.jsxs)(`div`,{className:`graph-container`,children:[a.loading&&(0,X.jsxs)(`div`,{className:`graph-loading-overlay`,children:[(0,X.jsx)(`div`,{className:`loading-spinner`}),(0,X.jsxs)(`p`,{children:[`Loading `,l?.label,`…`]})]}),a.error&&(0,X.jsx)(`div`,{className:`graph-loading-overlay`,children:(0,X.jsxs)(`p`,{style:{color:`#F44336`},children:[`Error: `,a.error]})}),!l&&!a.loading&&(0,X.jsx)($,{content:`Pick a route or command in the left sidebar to load its dependency graph.`,children:(0,X.jsxs)(`div`,{className:`graph-placeholder`,children:[(0,X.jsx)(`div`,{className:`placeholder-icon`,children:(0,X.jsx)(`svg`,{width:`64`,height:`64`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,X.jsx)(`polyline`,{points:`22 12 18 12 15 21 9 3 6 12 2 12`})})}),(0,X.jsx)(`h3`,{children:`Select a route to explore`}),(0,X.jsx)(`p`,{children:`Expand the files in the sidebar and choose a route or command to visualize its execution lifecycle and dependencies.`})]})}),!a.loading&&l&&c.length===0&&!a.error&&(0,X.jsx)($,{content:`This endpoint produced no analyzable nodes. It may be a closure, a redirect-only route, or outside the scanner’s rules.`,children:(0,X.jsxs)(`div`,{className:`graph-placeholder`,children:[(0,X.jsx)(`div`,{className:`placeholder-icon`,children:(0,X.jsxs)(`svg`,{width:`64`,height:`64`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`circle`,{cx:`12`,cy:`12`,r:`10`}),(0,X.jsx)(`line`,{x1:`12`,y1:`8`,x2:`12`,y2:`12`}),(0,X.jsx)(`line`,{x1:`12`,y1:`16`,x2:`12.01`,y2:`16`})]})}),(0,X.jsx)(`h3`,{children:`Empty Graph`}),(0,X.jsx)(`p`,{children:`No nodes or edges found for this route.`})]})}),!a.loading&&c.length>0&&(0,X.jsx)(Ve,{elements:c,layout:p,searchQuery:y,rankDir:T,visibleTypes:C,theme:e,onNodeSelect:R,graphRef:N,stressTestNodeId:D,stressRunKey:k,complexityOverlay:de,securityOverlay:me,compact:G,onLayoutChange:m,onRankDirChange:E,onToggleComplexityOverlay:()=>fe(e=>!e),onToggleSecurityOverlay:()=>W(e=>!e),onToggleCompact:()=>he(e=>!e)},l?.id)]}),h&&(0,X.jsx)(Qt,{selectedId:h,graphData:a.data,theme:e,onClose:()=>g(null),onStressChange:e=>{O(e),e!==null&&j(e=>e+1)}})]})]})}(0,j.createRoot)(document.getElementById(`root`)).render((0,X.jsx)(A.StrictMode,{children:(0,X.jsx)(Bn,{})}));
\ No newline at end of file
diff --git a/resources/assets/assets/index-D8B_A_Gw.css b/resources/assets/assets/index-D8B_A_Gw.css
deleted file mode 100644
index 878349f0..00000000
--- a/resources/assets/assets/index-D8B_A_Gw.css
+++ /dev/null
@@ -1 +0,0 @@
-*{box-sizing:border-box;margin:0;padding:0}body{background:#0f1117;margin:0}#root{width:100%;height:100vh}.flowchart-root{padding:12px 0}.flowchart-title{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);margin-bottom:12px;padding:0 16px;font-size:11px;font-weight:600}.flowchart-empty{color:var(--dim);padding:12px 16px;font-size:12px;font-style:italic}.flowchart-list{flex-direction:column;align-items:flex-start;padding:0 16px;display:flex}.flowchart-box{word-break:break-all;box-sizing:border-box;border:1px solid #0000;border-radius:6px;align-items:center;gap:6px;width:100%;max-width:100%;padding:6px 10px;font-family:ui-monospace,Cascadia Code,monospace;font-size:11px;display:flex;position:relative}.flowchart-box--call{color:#90caf9;background:#2196f31f;border-color:#2196f34d}.flowchart-box--assign{background:var(--border);border-color:var(--border);color:var(--dim)}.flowchart-box--return{color:#a5d6a7;background:#4caf501f;border-color:#4caf5059}.flowchart-box--throw{color:#ef9a9a;background:#f443361f;border-color:#f4433659}.flowchart-box--if{color:#ffe082;background:#ffc1071a;border-color:#ffc10759;border-radius:4px}.flowchart-box--loop{color:#ce93d8;background:#9c27b01a;border-color:#9c27b059}.flowchart-box--dispatch{color:#ffab91;background:#ff57221f;border-color:#ff572259}.flowchart-box--event{color:#80deea;background:#00bcd41a;border-color:#00bcd44d}.flowchart-box--cache{color:#80cbc4;background:#0096881f;border-color:#00968859}.flowchart-icon{opacity:.7;flex-shrink:0;font-size:10px}.flowchart-label{white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;flex:1}.flowchart-arrow{flex-direction:column;align-items:flex-start;margin:1px 0;padding-left:16px;display:flex}.flowchart-arrow-line{background:var(--dim);width:1px;height:12px}.flowchart-arrow-head{border-left:4px solid #0000;border-right:4px solid #0000;border-top:5px solid var(--dim);width:0;height:0;margin-left:-3px}.flowchart-branch-wrapper{width:100%}.flowchart-branches{border-left:2px solid #ffc10759;gap:8px;margin-top:4px;margin-left:8px;padding-left:8px;display:flex}.flowchart-branch{flex:1;min-width:0}.flowchart-branch-label{text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px;font-size:9px;font-weight:700}.flowchart-branch--then .flowchart-branch-label{color:#a5d6a7}.flowchart-branch--else .flowchart-branch-label{color:#ef9a9a}.flowchart-loop-body{border-left:2px solid #9c27b073;margin-top:4px;margin-left:8px;padding-left:8px}.flowchart-cache-badge{letter-spacing:.05em;text-transform:uppercase;border-radius:4px;flex-shrink:0;margin-left:auto;padding:1px 6px;font-size:9px;font-weight:700}.flowchart-cache-badge--read{color:#90caf9;background:#2196f333;border:1px solid #2196f366}.flowchart-cache-badge--write{color:#ef9a9a;background:#f4433633;border:1px solid #f4433666}.flowchart-cache-badge--invalidate{color:#ffcc80;background:#ff980033;border:1px solid #ff980066}.flowchart-cache-badge--lock{color:#ce93d8;background:#9c27b033;border:1px solid #9c27b066}.flowchart-cache-badge+.flowchart-n1-warn{margin-left:4px}.flowchart-n1-warn{color:#ff9e80;letter-spacing:.05em;white-space:nowrap;background:#f4433633;border:1px solid #f4433666;border-radius:4px;align-items:center;gap:3px;margin-left:auto;padding:1px 6px;font-size:9px;font-weight:700;animation:2s infinite pulse-red;display:flex}@keyframes pulse-red{0%{box-shadow:0 0 #f4433666}70%{box-shadow:0 0 0 4px #f4433600}to{box-shadow:0 0 #f4433600}}.flowchart-box--n1{box-shadow:inset 0 0 8px #f4433633;color:#ff8a80!important;background:#f4433626!important;border-color:#f44336!important}[data-theme=light] .flowchart-box--call{color:#1565c0;background:#2196f31a;border-color:#2196f366}[data-theme=light] .flowchart-box--assign{color:#555;background:#0000000d;border-color:#00000026}[data-theme=light] .flowchart-box--return{color:#2e7d32;background:#4caf501a;border-color:#4caf5073}[data-theme=light] .flowchart-box--throw{color:#c62828;background:#f443361a;border-color:#f4433673}[data-theme=light] .flowchart-box--if{color:#e65100;background:#ffc1071a;border-color:#ffc10780}[data-theme=light] .flowchart-box--loop{color:#6a1b9a;background:#9c27b014;border-color:#9c27b066}[data-theme=light] .flowchart-box--dispatch{color:#bf360c;background:#ff572214;border-color:#ff572266}[data-theme=light] .flowchart-box--event{color:#006064;background:#00bcd414;border-color:#00bcd466}[data-theme=light] .flowchart-box--cache{color:#00695c;background:#00968814;border-color:#00968866}[data-theme=light] .flowchart-branch--then .flowchart-branch-label{color:#2e7d32}[data-theme=light] .flowchart-branch--else .flowchart-branch-label{color:#c62828}[data-theme=light] .flowchart-box--n1{color:#b71c1c!important}.flowchart-fat-banner{color:#ffab40;letter-spacing:.02em;background:#ff6d001f;border-bottom:1px solid #ff6d0059;align-items:center;gap:6px;padding:7px 14px;font-size:11px;font-weight:600;animation:3s ease-in-out infinite pulse-fat;display:flex}@keyframes pulse-fat{0%,to{background:#ff6d001a}50%{background:#ff6d002e}}[data-theme=light] .flowchart-fat-banner{color:#e65100;background:#ff6d0014;border-bottom-color:#ff6d004d}.seq-diagram-root{padding:6px 0 10px;overflow-x:auto}.seq-diagram-svg{display:block}.sequence-modal-body{padding:0;overflow:auto}.sequence-modal-body .seq-diagram-root{padding:16px}.flowchart-http{color:#7dd3fc;letter-spacing:.04em;white-space:nowrap;text-overflow:ellipsis;background:#38bdf824;border:1px solid #38bdf859;border-radius:4px;align-items:center;gap:3px;max-width:180px;margin-left:auto;padding:1px 6px;font-size:9px;font-weight:700;display:flex;overflow:hidden}*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root,[data-theme=dark]{--bg:#0a0a10;--panel:#0f1018;--panel-2:#161823;--border:#242636;--text:#e8e9f1;--dim:#9092a4;--faint:#5b5d72;--accent:#8b6cf6;--accent-soft:color-mix(in srgb, var(--accent) 14%, transparent);--accent-glow:color-mix(in srgb, var(--accent) 35%, transparent);--frost:color-mix(in srgb, var(--panel) 88%, transparent);--frost-blur:8px;--glass-border:var(--border);--glass-border-strong:color-mix(in srgb, var(--text) 22%, transparent);--input-bg:color-mix(in srgb, var(--text) 5%, transparent);--input-border:var(--border);--glass-blur:blur(8px);--glass-blur-sm:blur(8px);--mono:"JetBrains Mono", ui-monospace, "Cascadia Code", monospace;--ok:#46c98b;--warn:#e9b14b;--danger:#ef5a5a;--nc-route:#4ade80;--nc-controller:#38d3d3;--nc-action:#8b8bf0;--nc-service:#b07cf6;--nc-view:#ef7bb8;--nc-interface:#e9b14b;--nc-provider:#f0944a}[data-theme=light]{--bg:#f4f5f9;--panel:#fff;--panel-2:#f7f8fc;--border:#e4e6ee;--text:#14151c;--dim:#5b5d72;--faint:#9092a4;--accent:#6b46e8;--accent-soft:color-mix(in srgb, var(--accent) 12%, transparent);--accent-glow:color-mix(in srgb, var(--accent) 28%, transparent);--frost:color-mix(in srgb, var(--panel) 88%, transparent);--frost-blur:8px;--glass-border:var(--border);--glass-border-strong:color-mix(in srgb, var(--text) 18%, transparent);--input-bg:color-mix(in srgb, var(--text) 4%, transparent);--input-border:var(--border);--glass-blur:blur(8px);--glass-blur-sm:blur(8px);--ok:#1f9d63;--warn:#b9802a;--danger:#d63b3b;--nc-route:#2e9e54;--nc-controller:#1f8f8f;--nc-action:#5a5ad6;--nc-service:#7e46d8;--nc-view:#c83d8a;--nc-interface:#b9802a;--nc-provider:#c2640f}body{background:var(--bg);color:var(--text);height:100vh;font-family:Inter,system-ui,-apple-system,sans-serif;font-size:13px;overflow:hidden}body:before{content:"";pointer-events:none;z-index:0;background:radial-gradient(ellipse 55% 45% at 28% 22%, var(--accent-soft) 0%, transparent 60%);position:fixed;inset:0}.app{z-index:1;flex-direction:column;height:100vh;display:flex;position:relative}.main{flex:1;display:flex;overflow:hidden}.graph-container{background-color:#0000;background-image:radial-gradient(var(--border) 1px, transparent 1px);background-size:24px 24px;flex:1;position:relative;overflow:hidden}.toolbar{background:var(--frost);height:64px;-webkit-backdrop-filter:var(--glass-blur);border-bottom:1px solid var(--glass-border);box-shadow:0 1px 0 var(--glass-border), 0 4px 24px #00000040;z-index:100;flex-shrink:0;align-items:center;gap:16px;padding:0 24px;display:flex;position:relative}.toolbar-brand{flex-shrink:0;align-items:center;gap:6px;margin-right:4px;display:flex}.toolbar-logo-img{width:auto;height:38px;display:block}.toolbar-stats{flex-shrink:0;align-items:center;gap:6px;display:flex}.stat-chip{border:1px solid var(--glass-border);color:var(--dim);-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0d;border-radius:8px;padding:4px 10px;font-size:11px;font-weight:500;transition:all .2s}.stat-chip--warn{color:#ffa000;background:#ffa0001a;border-color:#ffa0004d}.stat-chip--stale{color:#f44336;cursor:pointer;background:#f443361a;border-color:#f443364d}.stat-chip--stale:hover{background:#f4433633;transform:translateY(-1px)}.toolbar-controls{align-items:center;gap:20px;margin-left:auto;display:flex}.toolbar-group{align-items:center;gap:10px;display:flex;position:relative}.toolbar-group:not(:last-child):after{content:"";background:var(--glass-border);width:1px;height:24px;margin-left:10px}.toolbar-select,.toolbar-search{border:1px solid var(--glass-border);color:var(--text);-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:10px;outline:none;padding:7px 12px;font-family:inherit;font-size:13px;transition:all .2s}.toolbar-select:hover,.toolbar-search:hover{background:#ffffff17;border-color:#8b6fe873}.toolbar-select:focus,.toolbar-search:focus{background:#8b6fe81a;border-color:#8b6fe8;box-shadow:0 0 0 3px #8b6fe82e,0 0 12px #8b6fe81f}.toolbar-search{width:180px}.toolbar-search-wrapper{position:relative}@media (width<=1200px){.toolbar-btn span:last-child{display:none}.toolbar-btn{padding:4px 8px}}@media (width<=1000px){.toolbar-stats{display:none}}@media (width<=800px){.toolbar-search{width:100px}.toolbar-select{max-width:120px}}.toolbar-btn{border:1px solid var(--glass-border);color:var(--text);cursor:pointer;white-space:nowrap;-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:10px;align-items:center;gap:8px;padding:7px 14px;font-family:inherit;font-size:13px;font-weight:500;transition:all .2s cubic-bezier(.16,1,.3,1);display:flex}.toolbar-btn:hover:not(:disabled){color:var(--text);background:#ffffff1a;border-color:#8b6fe88c;transform:translateY(-1px);box-shadow:0 0 0 1px #8b6fe826,0 4px 12px #0003}.toolbar-btn:active:not(:disabled){transform:translateY(0)}.toolbar-btn--rank{color:#a78bfa;background:#8b6fe81a;border-color:#8b6fe833}.toolbar-btn--rank:hover{background:#8b6fe833;border-color:#8b6fe8}.toolbar-btn:disabled{opacity:.5;cursor:not-allowed}.toolbar-btn--loading{opacity:.7;cursor:wait}.animate-spin{animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.action-dropdown{position:relative}.action-dropdown-menu{background:var(--panel-2);border:1px solid var(--glass-border-strong);z-index:1000;border-radius:14px;flex-direction:column;gap:4px;min-width:200px;padding:8px;animation:.2s cubic-bezier(.16,1,.3,1) dropdownIn;display:flex;position:absolute;top:calc(100% + 8px);left:0;box-shadow:0 16px 48px #00000073,0 0 0 1px #ffffff0a,inset 0 1px #ffffff14}@keyframes dropdownIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.floating-tooltip{z-index:20000;max-width:min(320px,100vw - 24px);color:var(--text);background:var(--panel-2);border:1px solid var(--glass-border-strong);pointer-events:none;border-radius:10px;padding:8px 12px;font-family:inherit;font-size:12px;font-weight:500;line-height:1.45;box-shadow:inset 0 1px #ffffff0f,0 12px 40px #00000059,0 0 0 1px #7c3aed24}[data-theme=light] .floating-tooltip{box-shadow:inset 0 1px #fffffff2,0 12px 36px #00000024,0 0 0 1px #7c3aed24}.tooltip-trigger-wrap{vertical-align:middle;display:inline-flex}.tooltip-trigger-wrap--block{width:100%}.dropdown-item{flex-direction:column;gap:4px;padding:8px;display:flex}.dropdown-item label{color:var(--dim);text-transform:uppercase;letter-spacing:.05em;margin-left:4px;font-size:10px;font-weight:700}.dropdown-item .toolbar-btn,.dropdown-item .toolbar-select{width:100%}.dropdown-chevron{opacity:.5;margin-left:4px;font-size:10px}.toolbar-btn--active{color:#fff;background:#8b6fe82e;border-color:#8b6fe8;box-shadow:0 0 0 1px #8b6fe84d,0 0 16px #8b6fe833}.toolbar-btn-beta{text-transform:uppercase;letter-spacing:.04em;color:#f59e0b;opacity:.8;vertical-align:super;font-size:9px;font-weight:700;line-height:1}.w-full{width:100%}.sidebar-resizable{flex-direction:row;flex-shrink:0;display:flex;position:relative}.sidebar-drag-handle{cursor:col-resize;z-index:10;background:0 0;width:5px;transition:background .15s;position:absolute;top:0;bottom:0;left:0}.sidebar-drag-handle:hover,.sidebar-drag-handle:active{background:var(--border)}.sidebar-drag-handle:after{content:"";background:var(--border);border-radius:1px;width:1px;height:32px;transition:background .15s,height .15s;position:absolute;top:50%;left:2px;transform:translateY(-50%)}.sidebar-drag-handle:hover:after,.sidebar-drag-handle:active:after{background:var(--dim);height:48px}.sidebar{min-width:0;-webkit-backdrop-filter:var(--glass-blur);border-left:1px solid var(--glass-border);background:#ffffff0a;flex-direction:column;flex:1;display:flex;overflow-y:auto;box-shadow:-4px 0 32px #00000040,inset 1px 0 #ffffff0f}.sidebar-header{border-bottom:1px solid var(--glass-border);background:#ffffff06;padding:16px;position:relative}.sidebar-header h2{color:var(--text);margin-top:6px;font-size:14px;font-weight:600}.sidebar-subtitle{color:var(--dim);font-size:11px}.sidebar-header-actions{align-items:center;gap:4px;display:flex;position:absolute;top:10px;right:10px}.sidebar-close{color:var(--dim);cursor:pointer;background:0 0;border:none;padding:0 2px;font-size:18px;line-height:1}.sidebar-ai-btn{padding:2px 5px;font-size:13px}.sidebar-expand-btn{background:var(--accent);color:#fff;cursor:pointer;border:none;border-radius:6px;justify-content:center;align-items:center;gap:6px;width:100%;margin-top:12px;padding:8px 12px;font-size:12px;font-weight:600;transition:background .15s,opacity .15s;display:flex}.sidebar-expand-btn:hover:not(:disabled){background:#6d28d9}.sidebar-expand-btn--done{background:var(--border);color:var(--dim);cursor:default}.type-badge{color:#000;text-transform:uppercase;letter-spacing:.06em;border-radius:99px;padding:2px 8px;font-size:10px;font-weight:600;display:inline-block}.sidebar-badges{align-items:center;gap:8px;margin-bottom:8px;display:flex}.visibility-badge{text-transform:uppercase;background:#ffffff0d;border-radius:4px;padding:2px 8px;font-size:10px;font-weight:700}.visibility-badge--public{color:#4ade80;border:1px solid #4ade8033}.visibility-badge--protected{color:#f59e0b;border:1px solid #f59e0b33}.visibility-badge--private{color:#f87171;border:1px solid #f8717133}.sidebar-stats{background:var(--glass-border);border-radius:10px;gap:1px;margin:12px 16px;display:flex;overflow:hidden;box-shadow:0 2px 12px #0003}.stat{background:#ffffff0a;flex-direction:column;flex:1;align-items:center;padding:10px 0;display:flex}.stat-value{color:var(--text);font-size:20px;font-weight:700}.stat-label{color:var(--dim);margin-top:2px;font-size:10px}.sidebar-hint{color:var(--dim);padding:0 16px 16px;font-size:11px}.sidebar-section{border-top:1px solid var(--border);padding:12px 16px}.sidebar-section h3{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);margin-bottom:8px;font-size:11px}.sidebar-structure-list{margin:0;padding:0;font-size:12px;list-style:none}.sidebar-structure-item{border-bottom:1px solid var(--border);flex-wrap:wrap;align-items:baseline;gap:4px 10px;padding:5px 0;display:flex}.sidebar-structure-item:last-child{border-bottom:none}.structure-kind{text-transform:uppercase;color:var(--dim);min-width:56px;font-size:10px}.structure-name{color:var(--text);font-family:ui-monospace,monospace}.structure-value{color:var(--dim);font-size:11px}.structure-flag,.structure-vis,.structure-decl{color:var(--dim);font-size:10px}.structure-decl{margin-left:6px;font-style:italic}.prop-row{gap:8px;margin-bottom:6px;font-size:12px;display:flex}.prop-key{color:var(--dim);flex-shrink:0;min-width:80px}.prop-value{color:var(--text);word-break:break-all}.prop-value--warn{color:var(--warn)}.edge-row{align-items:center;gap:6px;margin-bottom:5px;font-size:11px;display:flex}.edge-label{color:var(--dim);font-style:italic}.edge-target{color:var(--text)}.sidebar-node-title{color:var(--text);white-space:nowrap;text-overflow:ellipsis;max-width:100%;margin-top:6px;font-size:13px;font-weight:600;overflow:hidden}.sidebar-tab-bar{background:var(--panel-2);border:1px solid var(--border);scrollbar-width:none;border-radius:8px;flex-shrink:0;align-items:stretch;gap:2px;margin:10px 12px;padding:2px;display:flex;overflow-x:auto}.sidebar-tab-bar::-webkit-scrollbar{display:none}.sidebar-tab{color:var(--dim);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:6px;flex:1;justify-content:center;align-items:center;gap:6px;padding:6px 8px;font-family:inherit;font-size:12px;font-weight:500;transition:color .15s,background .15s;display:flex}.sidebar-tab:hover{color:var(--text)}.sidebar-tab--active{color:var(--text);background:var(--accent-soft)}.sidebar-tab-beta{text-transform:uppercase;letter-spacing:.04em;color:var(--warn);opacity:.8;vertical-align:super;font-size:9px;font-weight:700;line-height:1}.sidebar-tab-badge{background:var(--panel);color:var(--faint);font-size:10px;font-family:var(--mono);border-radius:99px;padding:1px 6px}.sidebar-tab--active .sidebar-tab-badge{background:var(--accent-soft);color:var(--accent)}.sidebar-tab-content{flex-direction:column;flex:1;display:flex;overflow-y:auto}.sidebar-section-header{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.sidebar-section-header h3{margin-bottom:0}.tab-bar{height:40px;-webkit-backdrop-filter:var(--glass-blur-sm);border-bottom:1px solid var(--glass-border);scrollbar-width:none;background:#ffffff08;flex-shrink:0;align-items:center;gap:16px;padding:0 16px;display:flex;overflow-x:auto}.tab-bar::-webkit-scrollbar{display:none}.tab-group{align-items:center;gap:8px;height:100%;display:flex}.tab-group-header{color:var(--dim);text-transform:uppercase;letter-spacing:.1em;background:var(--border);white-space:nowrap;border-radius:4px;padding:2px 6px;font-size:10px;font-weight:700}.tab-group-content{align-items:stretch;height:100%;display:flex}.tab-item{color:var(--dim);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;flex-shrink:0;align-items:center;gap:6px;padding:0 10px;font-family:inherit;font-size:12px;transition:color .15s,border-color .15s;display:flex}.tab-item:hover{color:var(--text)}.tab-item--active{color:#a78bfa;text-shadow:0 0 12px #a78bfa80;border-bottom-color:#a78bfa}.tab-label{font-weight:500}.tab-badge{color:var(--dim);text-align:center;background:#ffffff12;border-radius:99px;min-width:20px;padding:1px 6px;font-size:10px}.tab-item--active .tab-badge{color:#a78bfa;background:#a78bfa26}.graph-loading-overlay{color:var(--dim);z-index:10;background:0 0;flex-direction:column;justify-content:center;align-items:center;gap:12px;font-size:13px;display:flex;position:absolute;inset:0}.graph-placeholder{text-align:center;z-index:5;background:0 0;flex-direction:column;justify-content:center;align-items:center;gap:20px;padding:40px;display:flex;position:absolute;inset:0}.placeholder-icon{width:120px;height:120px;-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--glass-border-strong);color:var(--accent);box-shadow:0 20px 40px #0000004d, 0 0 40px var(--accent-glow);background:#ffffff0f;border-radius:32px;justify-content:center;align-items:center;margin-bottom:8px;display:flex;position:relative;overflow:hidden}.placeholder-icon:after{content:"";background:radial-gradient(circle at 50% 50%, var(--accent) 0%, transparent 70%);opacity:.08;position:absolute;inset:0}.placeholder-icon svg{filter:drop-shadow(0 0 8px #7c3aed4d);width:48px;height:48px;animation:4s ease-in-out infinite pulse-gentle}.graph-placeholder h3{color:var(--text);letter-spacing:-.02em;margin:0;font-size:24px;font-weight:700}.graph-placeholder p{color:var(--dim);max-width:440px;margin:0;font-size:14px;line-height:1.6}@keyframes pulse-gentle{0%,to{opacity:1;transform:scale(1)}50%{opacity:.8;transform:scale(1.05)}}.left-sidebar-resizable{flex-direction:row;flex-shrink:0;display:flex;position:relative}.left-sidebar-drag-handle{cursor:col-resize;z-index:10;background:0 0;width:5px;transition:background .15s;position:absolute;top:0;bottom:0;right:0}.left-sidebar-drag-handle:hover,.left-sidebar-drag-handle:active{background:var(--border)}.left-sidebar-drag-handle:after{content:"";background:var(--border);border-radius:1px;width:1px;height:32px;transition:background .15s,height .15s;position:absolute;top:50%;right:2px;transform:translateY(-50%)}.left-sidebar-drag-handle:hover:after,.left-sidebar-drag-handle:active:after{background:var(--dim);height:48px}.left-sidebar{min-width:0;-webkit-backdrop-filter:var(--glass-blur);border-right:1px solid var(--glass-border);background:#ffffff0a;flex-direction:column;flex:1;display:flex;overflow:hidden;box-shadow:4px 0 32px #00000040,inset -1px 0 #ffffff0f}.left-sidebar-top{flex-shrink:0;overflow:hidden auto}.left-sidebar-handle{cursor:row-resize;border-top:1px solid var(--border);border-bottom:1px solid var(--border);background:0 0;flex-shrink:0;height:5px;transition:background .15s;position:relative}.left-sidebar-handle:hover,.left-sidebar-handle:active{background:var(--border)}.left-sidebar-handle:after{content:"";background:var(--border);border-radius:1px;width:32px;height:1px;transition:background .15s,width .15s;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.left-sidebar-handle:hover:after,.left-sidebar-handle:active:after{background:var(--dim);width:48px}.left-sidebar-bottom{flex:1;min-height:0;overflow:hidden auto}.left-nav-search{border-bottom:1px solid var(--border);flex-shrink:0;padding:8px 10px 6px;position:relative}.left-nav-search-input{border:1px solid var(--glass-border);width:100%;color:var(--text);-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:8px;outline:none;padding:5px 24px 5px 8px;font-family:inherit;font-size:12px;transition:border-color .15s,box-shadow .15s}.left-nav-search-input::placeholder{color:var(--dim)}.left-nav-search-input:focus{border-color:#8b6fe8;box-shadow:0 0 0 3px #8b6fe82e}.left-nav-search-clear{color:var(--dim);cursor:pointer;background:0 0;border:none;padding:0 2px;font-size:16px;line-height:1;position:absolute;top:50%;right:16px;transform:translateY(-50%)}.left-nav-search-clear:hover{color:var(--text)}.left-nav-method-filters{border-bottom:1px solid var(--border);flex-wrap:wrap;flex-shrink:0;gap:4px;padding:4px 8px 6px;display:flex}.left-nav-method-badge{border:1px solid var(--method-color);color:var(--method-color);cursor:pointer;opacity:1;background:0 0;border-radius:3px;padding:1px 5px;font-family:inherit;font-size:10px;font-weight:700;transition:opacity .15s,background .15s}.left-nav-method-badge--off{opacity:.3}.left-nav-method-badge:hover{background:color-mix(in srgb, var(--method-color) 15%, transparent);opacity:1}.left-nav{padding:8px 0}.left-nav-overview{padding:6px 8px 4px}.left-nav-item--all{border-radius:6px;gap:7px;border-left:none!important;padding:6px 10px!important}.left-nav-all-icon{color:#a78bfa;flex-shrink:0;font-size:13px}.left-nav-file-group{margin-bottom:2px}.left-nav-file-header{width:100%;color:var(--text);cursor:pointer;text-align:left;letter-spacing:.01em;background:0 0;border:none;align-items:center;gap:5px;padding:5px 10px 5px 8px;font-family:inherit;font-size:11px;font-weight:600;display:flex}.left-nav-file-header:hover{background:var(--border)}.left-nav-file-chevron{color:var(--dim);flex-shrink:0;font-size:9px}.left-nav-file-icon{color:#a78bfa;opacity:.8;flex-shrink:0;justify-content:center;align-items:center;display:flex}.left-nav-file-name{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.left-nav-file-count{color:var(--dim);background:#ffffff0f;border-radius:99px;flex-shrink:0;padding:1px 6px;font-size:10px}.left-nav-prefix-group{border-left:1px solid var(--border);margin-left:8px}.left-nav-empty{color:var(--dim);padding:10px;font-family:"ui-monospace",Fira Code,monospace;font-size:11px}.left-nav-prefix-header{width:100%;color:var(--dim);cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:5px;padding:4px 10px;font-family:"ui-monospace",Fira Code,monospace;font-size:11px;display:flex}.left-nav-prefix-header:hover{color:var(--text);background:var(--border)}.left-nav-prefix-header:hover .left-nav-prefix-icon{color:#f59e0b;opacity:1}.left-nav-prefix-chevron{flex-shrink:0;font-size:9px}.left-nav-prefix-icon{color:var(--dim);opacity:.6;flex-shrink:0;justify-content:center;align-items:center;display:flex}.left-nav-prefix-name{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.left-nav-prefix-count{color:var(--dim);background:#ffffff0d;border-radius:99px;flex-shrink:0;padding:1px 5px;font-size:10px}.left-nav-item{width:100%;color:var(--dim);cursor:pointer;text-align:left;background:0 0;border:none;border-left:2px solid #0000;align-items:center;gap:6px;padding:4px 10px 4px 20px;font-family:inherit;font-size:11px;transition:color .12s,border-color .12s,background .12s;display:flex}.left-nav-item:hover{color:var(--text);background:var(--border)}.left-nav-item--active{color:var(--text);background:#a78bfa1a;border-left-color:#a78bfa;box-shadow:inset 2px 0 8px #a78bfa26}.left-nav-method{text-align:right;flex-shrink:0;width:36px;font-family:"ui-monospace",Fira Code,monospace;font-size:9px;font-weight:700}.left-nav-uri{text-overflow:ellipsis;white-space:nowrap;flex:1;font-family:"ui-monospace",Fira Code,monospace;font-size:11px;overflow:hidden}.left-nav-badge{color:var(--dim);background:#ffffff12;border-radius:99px;flex-shrink:0;padding:1px 5px;font-size:10px}.left-nav-issue-badges{flex-shrink:0;align-items:center;gap:3px;display:inline-flex}.left-nav-issue-badge{background:color-mix(in srgb, var(--issue-color) 18%, transparent);color:var(--issue-color);border:1px solid color-mix(in srgb, var(--issue-color) 45%, transparent);border-radius:99px;flex-shrink:0;align-items:center;gap:3px;height:16px;padding:0 5px;font-size:10px;font-weight:700;line-height:1;display:inline-flex}.left-nav-issue-badge svg{flex-shrink:0}.filter-panel{background:#ffffff06;width:100%;padding:12px 0;overflow-y:auto}.filter-header{border-bottom:1px solid var(--border);justify-content:space-between;align-items:center;margin-bottom:8px;padding:0 12px 8px;display:flex}.filter-title{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);font-size:11px;font-weight:600}.filter-actions{align-items:center;gap:4px;display:flex}.filter-link{color:var(--dim);cursor:pointer;background:0 0;border:none;padding:0;font-family:inherit;font-size:11px}.filter-link:hover{color:var(--text)}.filter-sep{color:var(--border);font-size:11px}.filter-item{cursor:pointer;align-items:center;gap:7px;padding:5px 12px;transition:opacity .15s;display:flex}.filter-item:hover{background:var(--border)}.filter-item--dim{opacity:.45}.filter-checkbox{display:none}.filter-dot{border-radius:50%;flex-shrink:0;width:9px;height:9px}.filter-label{color:var(--text);flex:1;font-size:12px}.filter-count{color:var(--dim);background:var(--bg);text-align:center;border-radius:99px;min-width:22px;padding:1px 6px;font-size:11px}.sidebar-section--source{padding-bottom:0}.source-toggle-wrapper{justify-content:space-between;align-items:center;padding:2px 0 8px;display:flex}.source-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;flex:1;align-items:center;gap:8px;display:flex}.source-toggle:hover h3{color:var(--text)}.source-toggle h3{margin:0}.source-toggle-icon{border-right:1.5px solid var(--dim);border-bottom:1.5px solid var(--dim);flex-shrink:0;align-self:center;width:7px;height:7px;margin-top:-3px;transition:transform .2s;transform:rotate(45deg)}.source-toggle-icon--open{margin-top:1px;transform:rotate(-135deg)}.source-view{border:1px solid var(--glass-border);border-radius:8px;margin-top:4px;margin-bottom:12px;overflow:hidden;box-shadow:0 4px 16px #00000040}.source-path{color:var(--dim);border-bottom:1px solid var(--glass-border);white-space:nowrap;text-overflow:ellipsis;background:#0003;padding:5px 10px;font-size:10px;overflow:hidden}.source-code{background:#00000040;max-height:360px;font-family:ui-monospace,Cascadia Code,Fira Code,monospace;font-size:11.5px;line-height:1.55;overflow:auto}.source-line{gap:0;min-width:max-content;display:flex}.source-line--highlight{background:#a78bfa26;outline:1px solid #a78bfa4d}.source-line-num{text-align:right;width:36px;color:var(--dim);border-right:1px solid var(--border);-webkit-user-select:none;user-select:none;background:#ffffff08;flex-shrink:0;padding:0 8px 0 6px;font-size:10.5px}.source-line-text{white-space:pre;color:var(--text);padding:0 12px}.source-state{color:var(--dim);align-items:center;gap:8px;padding:10px 0;font-size:12px;display:flex}.source-state--error{color:#f44336}.welcome-screen{background:0 0;justify-content:center;align-items:center;width:100%;min-height:100vh;padding:16px;display:flex}.welcome-card{-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--glass-border-strong);text-align:center;background:#0c0d1699;border-radius:24px;width:100%;max-width:480px;padding:48px;animation:.6s cubic-bezier(.16,1,.3,1) slideUp;box-shadow:0 40px 80px #0009,0 0 0 1px #ffffff0a,inset 0 1px #ffffff1f}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.welcome-icon{filter:drop-shadow(0 0 20px #7c3aed66);justify-content:center;margin-bottom:24px;display:flex}.welcome-icon img{width:clamp(80px,30vw,140px);height:auto}@media (width<=480px){.welcome-card{border-radius:16px;padding:32px 24px}}.welcome-card h2{background:linear-gradient(135deg,#fff 0%,#a78bfa 100%);-webkit-text-fill-color:transparent;-webkit-background-clip:text;margin-bottom:16px;font-size:28px;font-weight:800}.welcome-card p{color:var(--dim);margin-bottom:32px;font-size:15px;line-height:1.6}.scan-btn{color:#fff;cursor:pointer;background:linear-gradient(135deg,#7c3aed 0%,#6d28d9 100%);border:none;border-radius:12px;justify-content:center;align-items:center;gap:12px;width:100%;padding:16px 32px;font-size:16px;font-weight:700;transition:all .2s cubic-bezier(.16,1,.3,1);display:flex;box-shadow:0 8px 24px #7c3aed4d}.scan-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 12px 32px #7c3aed66}.scan-btn:active:not(:disabled){transform:translateY(0)}.scan-btn:disabled{opacity:.6;cursor:wait}.btn-spinner{border:2px solid #ffffff4d;border-top-color:#fff;border-radius:50%;width:18px;height:18px;animation:.8s linear infinite spin}.btn-spinner--small{border-width:1.5px;width:12px;height:12px}.welcome-hint{color:var(--dim);margin-top:24px;font-size:12px}.welcome-hint code{color:#a78bfa;background:#0000004d;border-radius:4px;padding:2px 6px}.error-details{color:#ef4444;background:#f443361a;border:1px solid #f4433633;border-radius:8px;margin-bottom:24px;padding:12px;font-family:monospace}.loading-screen{width:100%;min-height:100vh;color:var(--dim);background:0 0;flex-direction:column;justify-content:center;align-items:center;gap:20px;padding:16px;font-size:14px;display:flex}.loading-spinner{border:4px solid var(--border);border-top-color:var(--accent);filter:drop-shadow(0 0 10px #7c3aed33);border-radius:50%;width:48px;height:48px;animation:.8s linear infinite spin}.error-screen h2{color:#f44336;font-size:18px}.error-screen p{font-size:13px}.export-overlay{-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:1000;background:#000000b3;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}.export-modal{-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--glass-border-strong);background:#0c0d16b8;border-radius:16px;flex-direction:column;width:100%;max-width:760px;max-height:85vh;display:flex;overflow:hidden;box-shadow:0 32px 80px #0000008c,0 0 0 1px #ffffff0a,inset 0 1px #ffffff1a}.export-modal-header{border-bottom:1px solid var(--glass-border);background:#ffffff06;flex-shrink:0;justify-content:space-between;align-items:center;padding:18px 20px;display:flex}.export-modal-title{align-items:center;gap:12px;display:flex}.export-modal-icon{font-size:20px}.export-modal-title h2{color:var(--text);margin:0;font-size:15px;font-weight:600}.export-modal-sub{color:var(--dim);text-transform:uppercase;letter-spacing:.06em;font-size:11px}.export-modal-close{color:var(--dim);cursor:pointer;background:0 0;border:none;border-radius:4px;padding:4px 8px;font-size:20px;line-height:1;transition:color .15s,background .15s}.export-modal-close:hover{color:var(--text);background:var(--border)}.export-modal-actions{border-bottom:1px solid var(--border);flex-wrap:wrap;flex-shrink:0;gap:8px;padding:14px 20px;display:flex}.export-btn{cursor:pointer;border:1px solid #0000;border-radius:6px;padding:7px 14px;font-size:12px;font-weight:500;transition:opacity .15s,transform .1s}.export-btn:hover{opacity:.85;transform:translateY(-1px)}.export-btn:active{transform:translateY(0)}.export-btn--primary{color:#fff;background:#1565c0;border-color:#2196f3}.export-btn--secondary{background:var(--border);color:var(--text);border-color:var(--border)}.export-btn--danger{color:#fff;background:#c62828;border-color:#ef5350}.ai-rules-overwrite-banner{background:#ff98001a;border:1px solid #ff980066;border-radius:8px;flex-shrink:0;align-items:flex-start;gap:12px;margin:0 20px;padding:14px 16px;display:flex}.ai-rules-overwrite-icon{flex-shrink:0;margin-top:2px;font-size:20px}.ai-rules-overwrite-body{color:var(--text);flex:1;font-size:13px;line-height:1.5}.ai-rules-overwrite-body strong{margin-bottom:6px;display:block}.ai-rules-overwrite-list{margin:0 0 8px;padding-left:18px;list-style:outside}.ai-rules-overwrite-list li{margin-bottom:2px}.ai-rules-overwrite-list code{background:#ffffff12;border-radius:3px;padding:1px 5px;font-size:12px}.ai-rules-overwrite-actions{flex-direction:column;flex-shrink:0;gap:6px;display:flex}.export-btn--accent{color:#fff;background:#6a1b9a;border-color:#9c27b0}.export-modal-hint{color:var(--dim);border-bottom:1px solid var(--border);flex-shrink:0;padding:8px 20px;font-size:11px}.export-modal-hint a{color:#90caf9;text-decoration:none}.export-modal-hint a:hover{text-decoration:underline}.export-code-wrapper{flex-direction:column;flex:1;display:flex;position:relative;overflow:hidden}.export-code-lang{color:var(--dim);text-transform:uppercase;letter-spacing:.1em;pointer-events:none;font-size:10px;position:absolute;top:8px;right:12px}.export-code{background:var(--bg);color:#a8d8a8;resize:none;white-space:pre;cursor:text;border:none;outline:none;flex:1;min-height:200px;padding:16px;font-family:ui-monospace,Cascadia Code,Fira Code,monospace;font-size:11.5px;line-height:1.6;overflow-y:auto}.export-modal-stats{color:var(--dim);border-top:1px solid var(--border);flex-shrink:0;gap:16px;padding:8px 20px;font-size:11px;display:flex}.flowchart-export-bar{border-bottom:1px solid var(--border);gap:6px;padding:6px 16px;display:flex}.flowchart-export-btn{border:1px solid var(--border);background:var(--bg);color:var(--dim);cursor:pointer;border-radius:5px;padding:4px 10px;font-size:11px;transition:color .15s,background .15s}.flowchart-export-btn:hover:not(:disabled){color:var(--text);background:var(--border)}.flowchart-export-btn:disabled{opacity:.4;cursor:default}.ai-rules-modal{max-width:640px}.ai-rules-select-bar{border-bottom:1px solid var(--border);flex-shrink:0;align-items:center;gap:6px;padding:10px 20px;display:flex}.ai-rules-select-label{color:var(--dim);flex:1;font-size:11px}.ai-rules-select-link{color:#90caf9;cursor:pointer;background:0 0;border:none;padding:0;font-size:11px}.ai-rules-select-link:hover{text-decoration:underline}.ai-rules-select-sep{color:var(--dim);font-size:11px}.ai-rules-grid{flex-direction:column;flex:1;gap:4px;padding:12px 16px;display:flex;overflow-y:auto}.ai-rules-card{border:1px solid var(--glass-border);cursor:pointer;-webkit-user-select:none;user-select:none;background:#ffffff08;border-radius:10px;align-items:center;gap:10px;padding:10px 12px;transition:background .15s,border-color .15s,box-shadow .15s;display:flex}.ai-rules-card:hover{border-color:var(--glass-border-strong);background:#ffffff12}.ai-rules-card--selected{background:#2196f312;border-color:#2196f3}.ai-rules-card--disabled{opacity:.6;cursor:default;pointer-events:none}.ai-rules-checkbox{accent-color:#2196f3;cursor:pointer;flex-shrink:0;width:15px;height:15px}.ai-rules-card-icon{text-align:center;flex-shrink:0;width:24px;font-size:18px}.ai-rules-card-body{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.ai-rules-card-label{color:var(--text);font-size:13px;font-weight:600}.ai-rules-card-path{color:#90caf9;white-space:nowrap;text-overflow:ellipsis;font-family:ui-monospace,Cascadia Code,monospace;font-size:10px;overflow:hidden}.ai-rules-card-desc{color:var(--dim);font-size:11px}.ai-rules-card-status{text-align:center;flex-shrink:0;width:20px;font-size:14px}.ai-rules-status{font-size:14px}.ai-rules-status--ok{color:#4caf50}.ai-rules-status--err{color:#f44336;cursor:help}@keyframes ai-rules-spin{to{transform:rotate(360deg)}}.ai-rules-status--spinning{animation:1s linear infinite ai-rules-spin;display:inline-block}.ai-rules-summary{border-top:1px solid var(--border);flex-shrink:0;gap:16px;padding:8px 20px;font-size:12px;display:flex}.ai-rules-summary--ok{color:#4caf50}.ai-rules-summary--err{color:#f44336}.ai-rules-footer{border-top:1px solid var(--border);flex-shrink:0;justify-content:flex-end;gap:8px;padding:14px 20px;display:flex}.export-btn--loading{opacity:.8;cursor:wait;align-items:center;gap:6px;display:flex}.theme-toggle{border:1px solid var(--glass-border);color:var(--dim);cursor:pointer;-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:8px;flex-shrink:0;padding:5px 9px;font-size:15px;line-height:1;transition:color .15s,background .15s,box-shadow .15s}.theme-toggle:hover{color:var(--text);background:#ffffff1a;box-shadow:0 0 12px #ffc86426}.toolbar-btn--scan{isolation:isolate;letter-spacing:.02em;color:#f5f3ff;background:linear-gradient(165deg,#c4b5fd61 0%,#7c3aed47 48%,#4c1d9566 100%);border:1px solid #c4b5fd8c;border-radius:999px;gap:10px;padding:5px 16px 5px 6px;font-weight:600;transition:transform .2s cubic-bezier(.16,1,.3,1),box-shadow .2s,border-color .2s,background .25s,color .2s;position:relative;overflow:hidden;box-shadow:inset 0 1px #ffffff24,0 4px 16px #31176373}.toolbar-scan__glyph{background:#0000003d;border:1px solid #ffffff24;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:32px;height:32px;display:flex}.toolbar-scan__glyph svg{opacity:.96;display:block}.toolbar-btn--scan:hover:not(:disabled) .toolbar-scan__glyph svg{animation:.7s cubic-bezier(.4,0,.2,1) toolbar-scan-nudge}@keyframes toolbar-scan-nudge{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.toolbar-btn--scan:after{content:"";border-radius:inherit;pointer-events:none;background:linear-gradient(105deg,#0000 35%,#ffffff24 50%,#0000 65%);transition:transform .55s;position:absolute;inset:0;transform:translate(-120%)}.toolbar-btn--scan:hover:not(:disabled):after{transform:translate(120%)}.toolbar-btn--scan:hover:not(:disabled){background:linear-gradient(165deg,#ddd6fe7a 0%,#7c3aed66 52%,#3b076473 100%);border-color:#ddd6fee6;transform:translateY(-1px);box-shadow:inset 0 1px #fff3,0 8px 22px #31176380,0 0 0 2px #7c3aed47}.toolbar-btn--scan:active:not(:disabled){transform:translateY(0);box-shadow:inset 0 1px #ffffff1a,0 2px 10px #31176366}.toolbar-btn--scan.toolbar-btn--loading{box-shadow:none;opacity:.92;background:linear-gradient(165deg,#4c1d95a6 0%,#270f4abf 100%);border-color:#a78bfa59;gap:8px;padding:7px 16px}.toolbar-btn--scan.toolbar-btn--loading:after{display:none}.toolbar-btn--scan:disabled:not(.toolbar-btn--loading){background:var(--panel);color:var(--dim);border-color:var(--glass-border);box-shadow:none}[data-theme=light] .toolbar-btn--scan{color:#3b1a6e;background:linear-gradient(165deg,#f5f3fff5 0%,#c4b5fd8c 100%);border-color:#5b21b652;box-shadow:inset 0 1px #fffffff2,0 4px 16px #5b21b624}[data-theme=light] .toolbar-scan__glyph{background:#7c3aed1f;border-color:#5b21b638}[data-theme=light] .toolbar-btn--scan:hover:not(:disabled){border-color:#7c3aed;box-shadow:inset 0 1px #fff,0 8px 22px #5b21b633,0 0 0 2px #7c3aed38}[data-theme=light] .toolbar-btn--scan.toolbar-btn--loading{color:#f5f3ff;background:linear-gradient(165deg,#6d28d9 0%,#5b21b6 100%);border-color:#7c3aed73}[data-theme=light] .toolbar-btn--scan:disabled:not(.toolbar-btn--loading){color:var(--dim);background:var(--panel)}.sidebar-smells{border-top:1px solid var(--border);flex-wrap:wrap;gap:6px;padding:8px 16px;display:flex}.smell-badge{letter-spacing:.03em;cursor:default;border-radius:99px;align-items:center;gap:4px;padding:3px 9px;font-size:11px;font-weight:600;display:inline-flex}.smell-badge--n1{color:#ff8a80;background:#f4433626;border:1px solid #f4433666;animation:2.5s ease-in-out infinite pulse-n1}@keyframes pulse-n1{0%,to{box-shadow:0 0 #f443364d}50%{box-shadow:0 0 0 5px #f4433600}}.smell-badge--fat-method{color:#ffab40;background:#ff6d0026;border:1px solid #ff6d0066}.smell-badge--fat-class{color:#ce93d8;background:#aa00ff1f;border:1px solid #aa00ff59}.smell-badge--deferred{color:#fbc02d;background:#ca8a0426;border:1px solid #ca8a0466}.metrics-grid{grid-template-columns:repeat(4,1fr);gap:6px;display:grid}.metric-item{-webkit-backdrop-filter:var(--glass-blur-sm);border:1px solid var(--glass-border);background:#ffffff0a;border-radius:8px;flex-direction:column;align-items:center;padding:8px 4px;transition:background .2s,border-color .2s;display:flex}.metric-item:hover{border-color:var(--glass-border-strong);background:#ffffff12}.metric-value{color:var(--text);font-size:18px;font-weight:700;line-height:1}.metric-label{color:var(--dim);text-transform:uppercase;letter-spacing:.07em;margin-top:4px;font-size:9px}.stat-chip--stale{color:#ffa000;cursor:pointer;background:#ffa00014;border-color:#ffa00099;font-family:inherit;font-size:11px;animation:2.5s ease-in-out infinite stale-pulse}.stat-chip--stale:hover{background:#ffa0002e;border-color:#ffa000e6}@keyframes stale-pulse{0%,to{opacity:1}50%{opacity:.65}}.stat-chip--age{color:var(--dim);font-size:11px}.sidebar-section--queries h3{align-items:center;gap:6px;display:flex}.sidebar-section--queries h3:before{content:"⛁";font-size:12px}.query-list{flex-direction:column;gap:4px;margin-top:6px;display:flex}.query-item{border:1px solid var(--glass-border);background:#ffffff0a;border-radius:6px;align-items:center;gap:6px;padding:4px 6px;font-size:11px;display:flex}.query-op{letter-spacing:.05em;text-transform:uppercase;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:10px;font-weight:700}.query-op--read{color:#2196f3;background:#2196f326}.query-op--write{color:#f44336;background:#f4433626}.query-table{color:var(--text);text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.query-badge{letter-spacing:.06em;text-transform:uppercase;border-radius:3px;flex-shrink:0;padding:1px 4px;font-size:9px;font-weight:700}.query-badge--raw{color:#9c27b0;background:#9c27b026}.sidebar-section--cache h3{align-items:center;gap:6px;display:flex}.sidebar-section--cache h3:before{content:"⛃";font-size:12px}.cache-list{flex-direction:column;gap:4px;margin-top:6px;display:flex}.cache-item{border:1px solid var(--glass-border);background:#ffffff0a;border-radius:6px;padding:4px 6px;font-size:11px}.cache-item-head{align-items:center;gap:6px;min-width:0;display:flex}.cache-kind{letter-spacing:.05em;text-transform:uppercase;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:10px;font-weight:700}.cache-kind--read{color:#2196f3;background:#2196f326}.cache-kind--write{color:#f44336;background:#f4433626}.cache-kind--invalidate{color:#ff9800;background:#ff980026}.cache-kind--lock{color:#ba68c8;background:#9c27b026}.cache-method{color:var(--dim);font-family:var(--font-mono,monospace);flex-shrink:0}.cache-key{min-width:0;color:var(--text);font-family:var(--font-mono,monospace);text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.cache-key--computed{color:var(--dim);font-style:italic}.cache-key--constructed{color:#ce93d8}.cache-item-meta{flex-wrap:wrap;gap:4px;margin-top:3px;padding-left:2px;display:flex}.cache-meta{letter-spacing:.04em;border:1px solid var(--glass-border);color:var(--dim);background:#ffffff0d;border-radius:3px;padding:1px 4px;font-size:9px}.cache-meta--tag{color:#4db6ac;background:#0096881f;border-color:#0096884d}[data-theme=light] .cache-item,[data-theme=light] .cache-meta{background:#00000008}[data-theme=light] .cache-key--constructed{color:#6a1b9a}[data-theme=light] .cache-meta--tag{color:#00695c}.sidebar-section--http h3{align-items:center;gap:6px;display:flex}.sidebar-section--http h3:before{content:"🌐";font-size:11px}.ins-chip--http{--cc:#38bdf8}.http-list{flex-direction:column;gap:6px;margin-top:6px;display:flex}.http-item{border:1px solid var(--glass-border);background:#ffffff0a;border-radius:6px;flex-direction:column;gap:4px;padding:6px;font-size:11px;display:flex}.http-item-head{align-items:center;gap:6px;min-width:0;display:flex}.http-method{letter-spacing:.05em;color:#38bdf8;background:#38bdf826;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:10px;font-weight:700}.http-method--post,.http-method--put,.http-method--patch,.http-method--delete{color:#f44336;background:#f4433626}.http-method--unknown{color:var(--dim);background:#ffffff14}.http-target{min-width:0;color:var(--text);text-overflow:ellipsis;white-space:nowrap;flex:1;font-family:ui-monospace,monospace;overflow:hidden}.http-item-meta{flex-wrap:wrap;gap:4px;display:flex}.http-badge{letter-spacing:.04em;color:var(--dim);white-space:nowrap;background:#ffffff0f;border-radius:3px;padding:1px 4px;font-size:9px;font-weight:600}.http-badge--client{color:#9c27b0;text-transform:uppercase;background:#9c27b026}.http-badge--absent{color:#ff9800;background:#ff980026}.http-badge--muted{opacity:.6}.modal-overlay{-webkit-backdrop-filter:blur(8px);z-index:2000;background:#0009;justify-content:center;align-items:center;padding:40px;display:flex;position:fixed;inset:0}.modal-container{-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--glass-border-strong);background:#0c0d16b8;border-radius:16px;flex-direction:column;width:100%;max-width:800px;max-height:100%;display:flex;overflow:hidden;box-shadow:0 32px 80px #0000008c,0 0 0 1px #ffffff0a,inset 0 1px #ffffff1a}.modal-container--large{max-width:1100px}.modal-header{border-bottom:1px solid var(--glass-border);background:#ffffff06;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.modal-title{align-items:center;gap:12px;display:flex}.modal-icon{font-size:24px}.modal-title h2{color:var(--text);font-size:16px;font-weight:700}.modal-sub{color:var(--dim);font-size:11px}.modal-close{color:var(--dim);cursor:pointer;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;width:32px;height:32px;font-size:24px;line-height:1;transition:all .15s;display:flex}.modal-close:hover{color:var(--text);background:var(--border)}.modal-body{flex:1;padding:20px;overflow-y:auto}.flowchart-modal-body{background:var(--bg);padding:40px}.flowchart-modal-body .flowchart{max-width:900px;margin:0 auto}.flow-header-wrapper{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.flow-popup-btn{color:var(--dim);cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;padding:4px;font-size:14px;transition:all .15s;display:flex}.flow-popup-btn:hover{color:var(--text);background:var(--border)}.source-modal-body{background:var(--bg);padding:0}.source-modal-body .source-view{border:none;border-radius:0}.source-modal-body .source-view .source-path{display:none}.source-modal-body pre{max-height:calc(90vh - 100px)!important}.st-section{border-top:1px solid var(--border);padding:12px 16px}.st-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;display:flex}.st-toggle:hover h3{color:var(--text)}.st-toggle h3{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);margin:0;font-size:11px;transition:color .15s}.st-toggle-icon{color:var(--dim);font-size:10px}.st-body{margin-top:10px}.st-form{flex-direction:column;gap:7px;display:flex}.st-form-row{align-items:center;gap:6px;display:flex}.st-form-col{flex-direction:column;gap:4px;display:flex}.st-label{color:var(--dim);flex-shrink:0;min-width:76px;font-size:11px}.st-uri-preview{color:var(--text);text-overflow:ellipsis;white-space:nowrap;align-items:center;gap:5px;font-size:12px;display:flex;overflow:hidden}.st-method-badge{background:var(--accent);color:#fff;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:10px;font-weight:700}.st-input{border:1px solid var(--glass-border);color:var(--text);-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:8px;outline:none;flex:1;padding:5px 10px;font-family:inherit;font-size:12px;transition:border-color .2s,box-shadow .2s}.st-input--short{text-align:center;flex:0 0 52px}.st-input:focus{border-color:#8b6fe8;box-shadow:0 0 0 3px #8b6fe826}.st-textarea{border:1px solid var(--glass-border);color:var(--text);resize:vertical;box-sizing:border-box;background:#ffffff0f;border-radius:8px;outline:none;width:100%;padding:6px 10px;font-family:SFMono-Regular,Consolas,monospace;font-size:11px;transition:border-color .2s,box-shadow .2s}.st-textarea:focus{border-color:#8b6fe8;box-shadow:0 0 0 3px #8b6fe826}.st-run-btn{color:#fff;cursor:pointer;background:linear-gradient(135deg,#7c3aed 0%,#a78bfa 100%);border:1px solid #ffffff1a;border-radius:8px;width:100%;margin-top:2px;padding:7px 14px;font-family:inherit;font-size:12px;font-weight:600;transition:all .2s;box-shadow:0 3px 10px #7c3aed4d}.st-run-btn:hover:not(:disabled){background:linear-gradient(135deg,#6d28d9 0%,#8b5cf6 100%);transform:translateY(-1px);box-shadow:0 5px 14px #7c3aed66}.st-run-btn:active:not(:disabled){transform:translateY(1px)}.st-run-btn:disabled{opacity:.5;cursor:not-allowed}.st-results{margin-top:10px}.st-metrics-grid{grid-template-columns:repeat(3,1fr);gap:5px;margin-bottom:10px;display:grid}.st-metric{border:1px solid var(--glass-border);text-align:center;background:#ffffff0a;border-radius:6px;padding:6px 6px 5px}.st-metric-value{color:var(--text);font-size:13px;font-weight:600;line-height:1.2}.st-metric-label{color:var(--dim);text-transform:uppercase;letter-spacing:.06em;margin-top:2px;font-size:9px}.st-dist{margin-bottom:8px}.st-dist-title{text-transform:uppercase;letter-spacing:.07em;color:var(--dim);margin-bottom:6px;font-size:10px}.st-dist-row{align-items:center;gap:6px;margin-bottom:4px;display:flex}.st-dist-label{color:var(--dim);min-width:32px;font-family:SFMono-Regular,Consolas,monospace;font-size:11px}.st-dist-bar-wrap{background:var(--border);border-radius:3px;flex:1;height:7px;overflow:hidden}.st-dist-bar{border-radius:3px;min-width:2px;height:100%;transition:width .4s}.st-dist-count{color:var(--dim);text-align:right;min-width:22px;font-size:11px}.st-docker-hint{color:#fbbf24;background:#fbbf2414;border:1px solid #fbbf2440;border-radius:6px;padding:8px 10px;font-size:11px;line-height:1.6}.st-docker-hint code{background:#fbbf2426;border-radius:3px;padding:1px 4px;font-family:SFMono-Regular,Consolas,monospace;font-size:10.5px}.st-error-box{color:#f87171;word-break:break-word;background:#ef444414;border:1px solid #ef444433;border-radius:6px;padding:8px 10px;font-family:SFMono-Regular,Consolas,monospace;font-size:11px;line-height:1.5}.st-last-run{color:var(--dim);opacity:.7;font-size:10px}.st-last-run--form{text-align:center;margin-top:2px}.st-trace{background:var(--bg-card,#ffffff08);border:1px solid #ffffff12;border-radius:8px;margin-bottom:12px;padding:10px 12px}.st-trace-title{letter-spacing:.06em;text-transform:uppercase;color:var(--dim);margin-bottom:8px;font-size:10px;font-weight:700}.st-trace-list{flex-direction:column;gap:0;display:flex}.st-trace-node{opacity:0;animation:.25s forwards st-trace-in;animation-delay:calc(var(--trace-i,0) * 60ms)}@keyframes st-trace-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.st-trace-node--running .st-trace-row{animation:1.2s ease-in-out infinite st-trace-pulse;animation-delay:calc(var(--trace-i,0) * .12s)}@keyframes st-trace-pulse{0%,to{opacity:1}50%{opacity:.55}}.st-trace-connector{align-items:center;gap:6px;padding:2px 0 2px 6px;display:flex}.st-trace-arrow{color:var(--dim);opacity:.5;font-size:11px;line-height:1}.st-trace-edge-label{color:var(--dim);opacity:.55;white-space:nowrap;text-overflow:ellipsis;max-width:100px;font-size:9px;font-style:italic;overflow:hidden}.st-trace-row{border-radius:5px;align-items:center;gap:7px;padding:3px 4px;display:flex}.st-trace-badge{letter-spacing:.05em;text-transform:uppercase;color:#fff;white-space:nowrap;border-radius:3px;flex-shrink:0;padding:2px 5px;font-size:8px;font-weight:700}.st-trace-label{color:var(--text);white-space:nowrap;text-overflow:ellipsis;min-width:0;font-family:ui-monospace,monospace;font-size:11px;overflow:hidden}.left-sidebar-tabs{border-bottom:1px solid var(--glass-border);background:#0000001f;flex-shrink:0;display:flex}.left-sidebar-tab{color:var(--dim);letter-spacing:.03em;text-transform:uppercase;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;flex:1;padding:8px 4px;font-family:inherit;font-size:11px;font-weight:600;transition:color .15s,border-color .15s}.left-sidebar-tab:hover{color:var(--text)}.left-sidebar-tab--active{color:#a78bfa;text-shadow:0 0 10px #a78bfa73;border-bottom-color:#a78bfa}.complexity-panel{flex-direction:column;height:100%;display:flex;overflow:hidden}.complexity-filters{flex-shrink:0;gap:4px;padding:8px 10px 4px;display:flex}.complexity-filter-btn{border:1px solid var(--border);color:var(--dim);cursor:pointer;background:#ffffff0a;border-radius:4px;padding:3px 8px;font-family:ui-monospace,monospace;font-size:10px;font-weight:600;transition:color .15s,border-color .15s,background .15s}.complexity-filter-btn:hover{color:var(--text);border-color:#a78bfa}.complexity-filter-btn--active{color:#a78bfa;background:#a78bfa1a;border-color:#a78bfa}.complexity-summary{color:var(--dim);flex-shrink:0;padding:2px 10px 6px;font-size:10px}.complexity-empty{color:var(--dim);text-align:center;padding:24px 16px;font-size:12px}.complexity-list{flex:1;padding:0 0 8px;overflow:hidden auto}.complexity-row{cursor:pointer;text-align:left;background:0 0;border:none;border-bottom:1px solid #0000;align-items:center;gap:8px;width:100%;padding:5px 10px;transition:background .1s;display:flex}.complexity-row:hover{background:#ffffff0a}.complexity-row--active{background:#a78bfa14;border-bottom-color:#a78bfa33}.complexity-badge{text-align:center;border:1px solid;border-radius:4px;flex-shrink:0;min-width:28px;padding:1px 4px;font-family:ui-monospace,monospace;font-size:11px;font-weight:700}.complexity-label{min-width:0;color:var(--text);white-space:nowrap;text-overflow:ellipsis;flex:1;font-family:ui-monospace,monospace;font-size:11px;overflow:hidden}.complexity-type{letter-spacing:.04em;text-transform:uppercase;opacity:.85;flex-shrink:0;font-size:9px;font-weight:600}.g-legends{z-index:4;pointer-events:none;flex-direction:column;align-items:flex-end;gap:10px;max-height:calc(100% - 140px);display:flex;position:absolute;top:64px;right:16px;overflow-y:auto}.cc-legend{border:1px solid var(--glass-border-strong);pointer-events:none;-webkit-backdrop-filter:var(--glass-blur);background:#07080f8c;border-radius:12px;min-width:160px;padding:10px 14px;position:static;box-shadow:0 8px 32px #0006,inset 0 1px #ffffff14}.cc-legend-title{letter-spacing:.08em;text-transform:uppercase;color:#fff6;margin-bottom:8px;font-size:9px;font-weight:700}.cc-legend-row{align-items:center;gap:8px;margin-bottom:5px;display:flex}.cc-legend-row:last-child{margin-bottom:0}.cc-legend-swatch{border-radius:2px;flex-shrink:0;width:10px;height:10px}.cc-legend-label{flex:1;font-size:11px;font-weight:600}.cc-legend-range{color:#fff6;font-family:ui-monospace,monospace;font-size:10px}.collapse-toggle-btn{cursor:pointer;z-index:50;pointer-events:all;-webkit-user-select:none;user-select:none;color:#ffffffc7;will-change:transform, left, top;background:#282a36eb;border:1.25px solid #ffffff59;border-radius:50%;justify-content:center;align-items:center;width:14px;height:14px;padding:0;transition:transform .12s ease-out,background .12s,border-color .12s,color .12s,box-shadow .12s;display:flex;position:absolute;transform:translate(-50%,-50%);box-shadow:0 1px 3px #00000073,inset 0 0 0 1px #00000040}.collapse-toggle-btn:hover{color:#fff;background:#7c3aed;border-color:#c4b5fd;transform:translate(-50%,-50%)scale(1.25);box-shadow:0 2px 6px #7c3aed8c,inset 0 0 0 1px #fff3}.collapse-toggle-btn--collapsed{color:#fff;background:#7c3aed;border-color:#c4b5fd;box-shadow:0 1px 4px #7c3aed8c,inset 0 0 0 1px #fff3}.collapse-toggle-btn--collapsed:hover{background:#8b5cf6;border-color:#fff}.collapse-toggle-btn--dimmed{opacity:.18;pointer-events:none}[data-theme=light] .collapse-toggle-btn{color:#000000b3;background:#fffffff5;border-color:#00000040;box-shadow:0 1px 3px #0000002e}[data-theme=light] .collapse-toggle-btn:hover,[data-theme=light] .collapse-toggle-btn--collapsed{color:#fff;background:#7c3aed;border-color:#5b21b6}[data-theme=light] .welcome-screen,[data-theme=light] .loading-screen{background:0 0}[data-theme=light] .welcome-card{-webkit-backdrop-filter:var(--glass-blur);background:#ffffffa6;border-color:#0000001f;box-shadow:0 32px 64px #0000001f,inset 0 1px #fffc}[data-theme=light] .welcome-card h2{background:linear-gradient(135deg,#1e1e2e 0%,#7c3aed 100%);-webkit-text-fill-color:transparent;-webkit-background-clip:text;background-clip:text}[data-theme=light] .welcome-hint code{color:#7c3aed;background:#0000000f}[data-theme=light] .cc-legend{background:#fffffff2;border-color:#0000001a}[data-theme=light] .cc-legend-title,[data-theme=light] .cc-legend-range{color:#0006}[data-theme=light] .left-nav-file-count{background:#00000012}[data-theme=light] .left-nav-prefix-count{background:#0000000f}[data-theme=light] .left-nav-badge,[data-theme=light] .tab-badge{background:#00000012}[data-theme=light] .tab-item--active .tab-badge{background:#7c3aed1f}[data-theme=light] .visibility-badge{background:#0000000a}[data-theme=light] .source-line-num{background:#00000008}[data-theme=light] .complexity-filter-btn,[data-theme=light] .complexity-row:hover{background:#0000000a}[data-theme=light] .st-trace{background:#00000005;border-color:#00000014}[data-theme=light] .smell-badge--n1{color:#c62828;background:#f443361a;border-color:#f4433659}[data-theme=light] .smell-badge--fat-method{color:#bf360c;background:#ff6d001a;border-color:#ff6d0059}[data-theme=light] .smell-badge--fat-class{color:#6a1b9a;background:#aa00ff14;border-color:#aa00ff4d}[data-theme=light] .smell-badge--deferred{color:#8a5a00;background:#ca8a041f;border-color:#ca8a0459}[data-theme=light] .toolbar-btn--active{color:#5b21b6;background:#7c3aed1f;border-color:#8b6fe8}[data-theme=light] .export-modal-hint a,[data-theme=light] .ai-rules-select-link,[data-theme=light] .ai-rules-card-path{color:#1565c0}[data-theme=light] .export-code{color:#2e7d32;background:#f8fffe}[data-theme=light] .st-docker-hint{background:#fbbf241a;border-color:#fbbf2466}[data-theme=light] .modal-container{background:#ffffffb8;border-color:#0000001f;box-shadow:0 20px 40px #00000026,inset 0 1px #ffffffe6}[data-theme=light] .export-modal{background:#ffffffb8;border-color:#0000001f;box-shadow:0 24px 80px #0000002e,inset 0 1px #ffffffe6}[data-theme=light] .action-dropdown-menu{box-shadow:0 12px 32px #00000024}[data-theme=light] .placeholder-icon{background:#ffffff8c;box-shadow:0 20px 40px #00000014,0 0 30px #7c3aed26}[data-theme=light] .sidebar{background:#ffffff8c;box-shadow:-4px 0 24px #00000014,inset 1px 0 #fffc}[data-theme=light] .left-sidebar{background:#ffffff8c;box-shadow:4px 0 24px #00000014,inset -1px 0 #fffc}.collapse-toggle-btn svg{stroke:currentColor;stroke-width:2.5px;stroke-linecap:round;fill:none;pointer-events:none;width:8px;height:8px;display:block}.sidebar-section--security{flex-direction:column;gap:10px;padding:12px 16px;display:flex}.security-exposure-card{border:1.5px solid;border-radius:8px;flex-direction:column;gap:4px;padding:10px 12px;display:flex}.security-exposure-header{align-items:center;gap:8px;display:flex}.security-exposure-badge{letter-spacing:.04em;font-family:ui-monospace,monospace;font-size:12px;font-weight:700}.security-exposure-desc{opacity:.8;margin:0;font-size:12px;line-height:1.5}.security-clean{opacity:.7;align-items:center;gap:6px;padding:10px 0;font-size:13px;display:flex}.security-issues-title{text-transform:uppercase;letter-spacing:.08em;opacity:.6;margin-bottom:2px;font-size:11px;font-weight:700}.security-issue-card{background:#ffffff08;border-left:3px solid;border-radius:0 6px 6px 0;flex-direction:column;gap:4px;padding:8px 10px;display:flex}.security-issue-header{align-items:center;gap:6px;display:flex}.security-issue-icon{font-size:13px}.security-issue-name{flex:1;font-size:12px;font-weight:700}.security-issue-severity{letter-spacing:.06em;opacity:.9;font-family:ui-monospace,monospace;font-size:9px;font-weight:700}.security-issue-message{opacity:.8;margin:0;font-size:12px;line-height:1.5}.security-issue-location{align-items:center;gap:6px;margin-top:2px;font-size:11px;display:flex}[data-theme=light] .security-issue-card{background:#00000005}kbd,.toolbar-kbd,.stat-chip,.route-row-uri,.route-row-method,.flag-card-path,.sidebar-node-title,.ins-chip,.prop-key,.prop-value,.show-graph-count,.g-rail-pill,.g-zoom-pct,.ins-meter-value{font-family:var(--mono)}.toolbar{background:var(--frost);height:52px;-webkit-backdrop-filter:blur(var(--frost-blur));border-bottom:1px solid var(--border);box-shadow:none;gap:14px;padding:0 14px}.toolbar-brand{align-items:center;gap:9px;display:flex}.toolbar-logo-img{width:26px;height:26px}.toolbar-brand-text{flex-direction:column;line-height:1.1;display:flex}.toolbar-brand-name{color:var(--text);font-size:13px;font-weight:600}.toolbar-brand-sub{color:var(--faint);font-size:10px}.seg-group{background:var(--panel-2);border:1px solid var(--border);border-radius:8px;align-items:center;gap:2px;padding:2px;display:flex}.seg-btn{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:5px 11px;font-size:12px}.seg-btn:hover{color:var(--text)}.seg-btn--active{background:var(--accent-soft);color:var(--text)}.seg-dropdown{position:relative}.seg-dropdown-menu{background:var(--panel);border:1px solid var(--border);z-index:200;border-radius:8px;flex-direction:column;gap:4px;min-width:200px;padding:6px;display:flex;position:absolute;top:calc(100% + 6px);left:0;right:auto;box-shadow:0 12px 36px #0006}.seg-menu-row{flex-direction:column;gap:4px;padding:4px 6px;display:flex}.seg-menu-row label{text-transform:uppercase;letter-spacing:.06em;color:var(--faint);font-size:10px}.seg-select,.seg-menu-btn{background:var(--panel-2);border:1px solid var(--border);color:var(--text);font:inherit;cursor:pointer;text-align:left;border-radius:6px;padding:6px 8px;font-size:12px}.seg-menu-btn:hover{border-color:var(--accent)}.seg-menu-btn--on{background:var(--accent-soft);border-color:var(--accent)}.toolbar-center{flex:1;justify-content:center;align-items:center;gap:10px;display:flex}.toolbar-search-wrapper{align-items:center;width:min(520px,42vw);display:flex;position:relative}.toolbar-search-icon{color:var(--faint);position:absolute;left:11px}.toolbar-search{background:var(--panel-2);border:1px solid var(--border);width:100%;color:var(--text);font:inherit;border-radius:8px;padding:7px 44px 7px 32px;font-size:12px}.toolbar-search:focus{border-color:var(--accent);outline:none}.toolbar-kbd{color:var(--faint);background:var(--panel);border:1px solid var(--border);border-radius:4px;padding:1px 5px;font-size:10px;position:absolute;right:8px}.risk-pill{background:var(--panel-2);border:1px solid var(--border);color:var(--dim);font:inherit;cursor:pointer;border-radius:999px;align-items:center;gap:7px;padding:5px 11px;font-size:12px;display:flex}.risk-pill-dot{background:var(--faint);border-radius:50%;width:7px;height:7px}.risk-pill--alert{color:var(--text);border-color:color-mix(in srgb, var(--danger) 50%, transparent)}.risk-pill--alert .risk-pill-dot{background:var(--danger);box-shadow:0 0 8px var(--danger)}.risk-pill-count{font-family:var(--mono);background:var(--panel);border-radius:999px;padding:1px 7px;font-size:11px}.risk-pill--alert .risk-pill-count{background:var(--danger);color:#fff}.toolbar-right{align-items:center;gap:8px;display:flex}.toolbar-right .seg-dropdown-menu{left:auto;right:0}.icon-btn{background:var(--panel-2);border:1px solid var(--border);color:var(--text);cursor:pointer;border-radius:7px;width:30px;height:30px;font-size:14px}.icon-btn:hover{border-color:var(--accent)}.rescan-btn{background:var(--accent);color:#fff;font:inherit;cursor:pointer;border:0;border-radius:7px;align-items:center;gap:7px;padding:7px 13px;font-size:12px;font-weight:600;display:flex}.rescan-btn:hover{filter:brightness(1.1)}.rescan-btn:disabled{opacity:.6;cursor:default}.stat-chip{color:var(--dim);background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:3px 8px;font-size:11px}.stat-chip--warn{color:var(--warn);border-color:color-mix(in srgb, var(--warn) 40%, transparent)}.left-sidebar-resizable{flex-shrink:0;position:relative}.left-sidebar{background:var(--panel);border-right:1px solid var(--border);flex-direction:column;width:100%;height:100%;display:flex}.left-sidebar-drag-handle{cursor:col-resize;z-index:5;width:6px;height:100%;position:absolute;top:0;right:-3px}.left-search{padding:12px 12px 8px;position:relative}.left-search-input{background:var(--panel-2);border:1px solid var(--border);width:100%;color:var(--text);font:inherit;border-radius:7px;padding:7px 26px 7px 10px;font-size:12px}.left-search-input:focus{border-color:var(--accent);outline:none}.left-search-clear{color:var(--faint);cursor:pointer;background:0 0;border:0;font-size:14px;position:absolute;top:50%;right:18px;transform:translateY(-50%)}.left-method-chips{flex-wrap:wrap;gap:5px;padding:0 12px 10px;display:flex}.method-chip{border:1px solid var(--border);color:var(--faint);font-family:var(--mono);cursor:pointer;background:0 0;border-radius:6px;flex:auto;padding:4px 6px;font-size:10px;font-weight:600}.method-chip--on{color:var(--mc);background:color-mix(in srgb, var(--mc) 16%, transparent);border-color:color-mix(in srgb, var(--mc) 55%, transparent)}.mode-tabs{background:var(--panel-2);border:1px solid var(--border);border-radius:8px;gap:2px;margin:0 12px 8px;padding:2px;display:flex}.mode-tab{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:1;justify-content:center;align-items:center;gap:6px;padding:6px 0;font-size:12px;display:flex}.mode-tab--active{background:var(--accent-soft);color:var(--text)}.mode-tab-count{font-family:var(--mono);color:var(--faint);background:var(--panel);border-radius:999px;padding:0 6px;font-size:10px}.mode-tab-count--alert{background:var(--danger);color:#fff}.left-content{flex:1;padding:0 8px;overflow:auto}.route-tree{width:100%;min-width:0}.left-empty{color:var(--faint);text-align:center;padding:18px 12px;font-size:12px}.tree-group-header{width:100%;color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:5px;align-items:center;gap:7px;padding:5px 6px;font-size:12px;display:flex}.tree-group-header:hover{background:var(--panel-2);color:var(--text)}.tree-group-chevron{width:10px;color:var(--faint);font-size:9px}.tree-group-icon{width:14px;height:14px;color:var(--faint);flex-shrink:0}.tree-group-header:hover .tree-group-icon{color:var(--dim)}.tree-group-name{text-align:left;flex:1}.tree-group-count{font-family:var(--mono);color:var(--faint);font-size:10px}.tree-group-body{padding-left:10px}.route-row{width:100%;color:var(--text);font:inherit;cursor:pointer;background:0 0;border:0;border-left:2px solid #0000;border-radius:0 5px 5px 0;align-items:center;gap:9px;padding:6px 8px;display:flex}.route-row:hover{background:var(--panel-2)}.route-row--active{border-left-color:var(--accent);background:var(--accent-soft)}.route-row-method{font-family:var(--mono);min-width:38px;font-size:10px;font-weight:700}.route-row-uri{text-align:left;white-space:nowrap;scrollbar-width:none;flex:1;min-width:0;font-size:12px;overflow:auto hidden}.route-row-uri::-webkit-scrollbar{display:none}.route-row-risk{font-family:var(--mono);color:var(--rc);background:color-mix(in srgb, var(--rc) 16%, transparent);border:1px solid color-mix(in srgb, var(--rc) 45%, transparent);border-radius:999px;padding:0 6px;font-size:10px}.route-row-loading{color:var(--faint)}.route-row--stacked{align-items:flex-start}.route-row--stacked .route-row-method{padding-top:1px}.schedule-row-body{flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}.schedule-row-body .route-row-uri{white-space:nowrap;overflow-wrap:normal}.schedule-row-scroll{max-width:calc(var(--left-sidebar-width,300px) - 76px);scrollbar-width:none;align-items:center;gap:4px;display:flex;overflow:auto hidden}.schedule-row-scroll::-webkit-scrollbar{display:none}.schedule-row-scroll>*{flex:none}.schedule-row-badges{margin-top:2px}.schedule-cadence{font-family:var(--mono);color:var(--accent);text-align:left;font-size:10px}.schedule-cadence--unknown{color:var(--faint);font-style:italic}.schedule-chip{color:var(--dim);background:var(--panel-2);border:1px solid var(--border);border-radius:999px;padding:0 5px;font-size:9px;line-height:1.6}.flag-list{flex-direction:column;gap:7px;padding:6px 4px;display:flex}.flag-card{text-align:left;background:var(--panel-2);border:1px solid var(--border);cursor:pointer;color:var(--text);font:inherit;border-radius:8px;padding:9px 11px}.flag-card:hover{border-color:var(--accent)}.flag-card--active{border-color:var(--accent);background:var(--accent-soft)}.flag-card-top{justify-content:space-between;align-items:center;margin-bottom:5px;display:flex}.flag-card-sev{font-family:var(--mono);color:var(--sc);background:color-mix(in srgb, var(--sc) 16%, transparent);border-radius:4px;padding:1px 6px;font-size:10px;font-weight:700}.flag-card-time{color:var(--faint);font-size:10px}.flag-card-method{font-family:var(--mono);font-size:10px;font-weight:700}.flag-card-path{word-break:break-all;margin-bottom:3px;font-size:12px}.flag-card-desc{color:var(--dim);font-size:11px}.left-footer{border-top:1px solid var(--border);background:var(--panel)}.show-graph{flex-direction:column;max-height:220px;padding:10px 12px;display:flex}.show-graph-header{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.show-graph-title{text-transform:uppercase;letter-spacing:.06em;color:var(--faint);font-size:11px}.show-graph-actions{align-items:center;gap:5px;display:flex}.show-graph-link{color:var(--accent);font:inherit;cursor:pointer;background:0 0;border:0;font-size:11px}.show-graph-sep{color:var(--faint);font-size:11px}.show-graph-grid{grid-template-columns:1fr 1fr;gap:4px;display:grid;overflow-y:auto}.show-graph-item{color:var(--text);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:4px;align-items:center;gap:6px;padding:3px 4px;font-size:11px;display:flex}.show-graph-item:hover{background:var(--panel-2)}.show-graph-item--off{opacity:.4}.show-graph-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px}.show-graph-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:1;overflow:hidden}.show-graph-count{color:var(--faint);font-size:10px}.sidebar-eyebrow{align-items:center;gap:7px;margin-bottom:6px;display:flex}.sidebar-eyebrow-dot{border-radius:50%;width:8px;height:8px;box-shadow:0 0 7px}.sidebar-eyebrow-type{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);font-size:11px}.sidebar-node-title{word-break:break-all;font-size:16px;font-weight:600}.sidebar-chips{flex-wrap:wrap;gap:6px;margin-top:9px;display:flex}.ins-chip{color:var(--cc);background:color-mix(in srgb, var(--cc) 14%, transparent);border:1px solid color-mix(in srgb, var(--cc) 40%, transparent);border-radius:999px;padding:2px 9px;font-size:11px}.ins-chip--neutral{color:var(--dim);background:var(--panel-2);border-color:var(--border)}.ins-actions{gap:6px;padding:14px 16px 0;display:flex}.ins-action-btn{background:var(--panel-2);border:1px solid var(--border);color:var(--text);font:inherit;cursor:pointer;border-radius:8px;flex:1;justify-content:center;align-items:center;gap:7px;padding:9px 0;font-size:12px;font-weight:500;transition:border-color .15s,background .15s,color .15s;display:flex}.ins-action-btn:hover:not(:disabled){border-color:var(--accent);background:var(--accent-soft)}.ins-action-btn:disabled{opacity:.4;cursor:default}.ins-action-icon{width:15px;height:15px;color:var(--dim);flex-shrink:0}.ins-action-btn:hover:not(:disabled) .ins-action-icon{color:var(--accent)}.ins-meters{flex-direction:column;gap:7px;padding:14px 16px;display:flex}.ins-meter{align-items:center;gap:9px;display:flex}.ins-meter-label{color:var(--dim);width:78px;font-size:11px}.ins-meter-track{background:var(--panel-2);border-radius:999px;flex:1;height:4px;overflow:hidden}.ins-meter-fill{border-radius:999px;height:100%;display:block}.ins-meter-value{color:var(--text);text-align:right;min-width:30px;font-size:11px}.sidebar-tab-badge--alert{background:var(--danger);color:#fff}.g-canvas.g-no-edge-labels .g-edge-label{display:none}.g-rails{pointer-events:none;z-index:4;flex-direction:column;gap:26px;display:flex;position:absolute;top:70px;left:14px}.g-rail{align-items:center;gap:8px;display:flex}.g-rail-pill{width:20px;height:20px;font-family:var(--mono);color:var(--rc);background:color-mix(in srgb, var(--rc) 14%, transparent);border:1px solid color-mix(in srgb, var(--rc) 40%, transparent);border-radius:6px;place-items:center;font-size:11px;font-weight:700;display:grid}.g-rail-label{text-transform:uppercase;letter-spacing:.12em;color:var(--faint);font-size:9px}.g-toolbar,.g-breadcrumb,.g-zoom{z-index:5;background:var(--frost);-webkit-backdrop-filter:blur(var(--frost-blur));border:1px solid var(--border);border-radius:9px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute}.g-toolbar{top:14px;left:50%;transform:translate(-50%)}.g-tool{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:5px 10px;font-size:11px}.g-tool:hover{color:var(--text)}.g-tool--on{background:var(--accent-soft);color:var(--text)}.g-tool-select{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:5px 8px;font-size:11px}.g-tool-select:hover{color:var(--text)}.g-tool-select option{background:var(--panel);color:var(--text)}.g-tool-sep{background:var(--border);width:1px;height:16px;margin:0 2px}.g-breadcrumb{gap:8px;padding:7px 11px;bottom:14px;left:14px}.g-crumb{color:var(--dim);align-items:center;gap:6px;font-size:10px;display:flex}.g-crumb-dot{border-radius:50%;width:7px;height:7px}.g-crumb-arrow{color:var(--faint);margin:0 1px}.g-zoom{bottom:14px;right:14px}.g-zoom-btn{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;width:26px;height:26px;font-size:13px}.g-zoom-btn:hover{background:var(--panel-2);color:var(--text)}.g-zoom-pct{font-family:var(--mono);color:var(--dim);text-align:center;min-width:42px;font-size:11px}.g-zoom-fit{font-size:12px}.g-node{transition:filter .15s}.g-node:hover{animation:1.1s ease-in-out infinite g-node-pulse}@keyframes g-node-pulse{0%,to{filter:drop-shadow(0 0 1px var(--accent-soft))}50%{filter:drop-shadow(0 0 7px var(--accent-glow))}}.section-count{color:var(--dim);margin-left:6px;font-weight:400}.schema-table{flex-direction:column;gap:2px;display:flex}.schema-row{border-radius:4px;grid-template-columns:minmax(0,1fr) minmax(0,1fr) 126px;align-items:baseline;gap:10px;padding:4px 6px;font-size:12px;display:grid}.schema-row:nth-child(odd){background:var(--panel-2)}.schema-row--flagged{background:color-mix(in srgb, var(--danger) 12%, transparent);box-shadow:inset 2px 0 0 var(--danger)}.schema-name{font-family:var(--mono);color:var(--text);overflow-wrap:anywhere}.schema-type{font-family:var(--mono);color:var(--dim);overflow-wrap:anywhere}.schema-flags{flex-wrap:wrap;place-content:flex-start flex-end;gap:4px;display:flex}.schema-flag{font-family:var(--mono);background:var(--panel);border:1px solid var(--border);color:var(--dim);white-space:nowrap;text-overflow:ellipsis;border-radius:3px;max-width:100%;padding:0 5px;font-size:10px;line-height:1.6;overflow:hidden}.schema-flag--muted{opacity:.7}.schema-flag--warn{color:var(--danger);border-color:color-mix(in srgb, var(--danger) 45%, transparent);background:color-mix(in srgb, var(--danger) 14%, transparent)}.sidebar-empty{color:var(--dim);padding:4px 6px;font-size:12px}.g-crumb--aside{opacity:.9}.g-crumb-sep{opacity:.45;margin-right:8px}.g-crumb-dot--dashed{border:1.5px dashed;border-color:inherit;background:0 0!important}
diff --git a/resources/assets/assets/index-VkfqTRNL.css b/resources/assets/assets/index-VkfqTRNL.css
new file mode 100644
index 00000000..32fa2a36
--- /dev/null
+++ b/resources/assets/assets/index-VkfqTRNL.css
@@ -0,0 +1 @@
+*{box-sizing:border-box;margin:0;padding:0}body{background:#0f1117;margin:0}#root{width:100%;height:100vh}.flowchart-root{padding:12px 0}.flowchart-title{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);margin-bottom:12px;padding:0 16px;font-size:11px;font-weight:600}.flowchart-empty{color:var(--dim);padding:12px 16px;font-size:12px;font-style:italic}.flowchart-list{flex-direction:column;align-items:flex-start;padding:0 16px;display:flex}.flowchart-box{word-break:break-all;box-sizing:border-box;border:1px solid #0000;border-radius:6px;align-items:center;gap:6px;width:100%;max-width:100%;padding:6px 10px;font-family:ui-monospace,Cascadia Code,monospace;font-size:11px;display:flex;position:relative}.flowchart-box--call{color:#90caf9;background:#2196f31f;border-color:#2196f34d}.flowchart-box--assign{background:var(--border);border-color:var(--border);color:var(--dim)}.flowchart-box--return{color:#a5d6a7;background:#4caf501f;border-color:#4caf5059}.flowchart-box--throw{color:#ef9a9a;background:#f443361f;border-color:#f4433659}.flowchart-box--if{color:#ffe082;background:#ffc1071a;border-color:#ffc10759;border-radius:4px}.flowchart-box--loop{color:#ce93d8;background:#9c27b01a;border-color:#9c27b059}.flowchart-box--dispatch{color:#ffab91;background:#ff57221f;border-color:#ff572259}.flowchart-box--event{color:#80deea;background:#00bcd41a;border-color:#00bcd44d}.flowchart-box--cache{color:#80cbc4;background:#0096881f;border-color:#00968859}.flowchart-icon{opacity:.7;flex-shrink:0;font-size:10px}.flowchart-label{white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;flex:1}.flowchart-arrow{flex-direction:column;align-items:flex-start;margin:1px 0;padding-left:16px;display:flex}.flowchart-arrow-line{background:var(--dim);width:1px;height:12px}.flowchart-arrow-head{border-left:4px solid #0000;border-right:4px solid #0000;border-top:5px solid var(--dim);width:0;height:0;margin-left:-3px}.flowchart-branch-wrapper{width:100%}.flowchart-branches{border-left:2px solid #ffc10759;gap:8px;margin-top:4px;margin-left:8px;padding-left:8px;display:flex}.flowchart-branch{flex:1;min-width:0}.flowchart-branch-label{text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px;font-size:9px;font-weight:700}.flowchart-branch--then .flowchart-branch-label{color:#a5d6a7}.flowchart-branch--else .flowchart-branch-label{color:#ef9a9a}.flowchart-loop-body{border-left:2px solid #9c27b073;margin-top:4px;margin-left:8px;padding-left:8px}.flowchart-cache-badge{letter-spacing:.05em;text-transform:uppercase;border-radius:4px;flex-shrink:0;margin-left:auto;padding:1px 6px;font-size:9px;font-weight:700}.flowchart-cache-badge--read{color:#90caf9;background:#2196f333;border:1px solid #2196f366}.flowchart-cache-badge--write{color:#ef9a9a;background:#f4433633;border:1px solid #f4433666}.flowchart-cache-badge--invalidate{color:#ffcc80;background:#ff980033;border:1px solid #ff980066}.flowchart-cache-badge--lock{color:#ce93d8;background:#9c27b033;border:1px solid #9c27b066}.flowchart-cache-badge+.flowchart-n1-warn{margin-left:4px}.flowchart-n1-warn{color:#ff9e80;letter-spacing:.05em;white-space:nowrap;background:#f4433633;border:1px solid #f4433666;border-radius:4px;align-items:center;gap:3px;margin-left:auto;padding:1px 6px;font-size:9px;font-weight:700;animation:2s infinite pulse-red;display:flex}@keyframes pulse-red{0%{box-shadow:0 0 #f4433666}70%{box-shadow:0 0 0 4px #f4433600}to{box-shadow:0 0 #f4433600}}.flowchart-box--n1{box-shadow:inset 0 0 8px #f4433633;color:#ff8a80!important;background:#f4433626!important;border-color:#f44336!important}[data-theme=light] .flowchart-box--call{color:#1565c0;background:#2196f31a;border-color:#2196f366}[data-theme=light] .flowchart-box--assign{color:#555;background:#0000000d;border-color:#00000026}[data-theme=light] .flowchart-box--return{color:#2e7d32;background:#4caf501a;border-color:#4caf5073}[data-theme=light] .flowchart-box--throw{color:#c62828;background:#f443361a;border-color:#f4433673}[data-theme=light] .flowchart-box--if{color:#e65100;background:#ffc1071a;border-color:#ffc10780}[data-theme=light] .flowchart-box--loop{color:#6a1b9a;background:#9c27b014;border-color:#9c27b066}[data-theme=light] .flowchart-box--dispatch{color:#bf360c;background:#ff572214;border-color:#ff572266}[data-theme=light] .flowchart-box--event{color:#006064;background:#00bcd414;border-color:#00bcd466}[data-theme=light] .flowchart-box--cache{color:#00695c;background:#00968814;border-color:#00968866}[data-theme=light] .flowchart-branch--then .flowchart-branch-label{color:#2e7d32}[data-theme=light] .flowchart-branch--else .flowchart-branch-label{color:#c62828}[data-theme=light] .flowchart-box--n1{color:#b71c1c!important}.flowchart-fat-banner{color:#ffab40;letter-spacing:.02em;background:#ff6d001f;border-bottom:1px solid #ff6d0059;align-items:center;gap:6px;padding:7px 14px;font-size:11px;font-weight:600;animation:3s ease-in-out infinite pulse-fat;display:flex}@keyframes pulse-fat{0%,to{background:#ff6d001a}50%{background:#ff6d002e}}[data-theme=light] .flowchart-fat-banner{color:#e65100;background:#ff6d0014;border-bottom-color:#ff6d004d}.seq-diagram-root{padding:6px 0 10px;overflow-x:auto}.seq-diagram-svg{display:block}.sequence-modal-body{padding:0;overflow:auto}.sequence-modal-body .seq-diagram-root{padding:16px}.flowchart-http{color:#7dd3fc;letter-spacing:.04em;white-space:nowrap;text-overflow:ellipsis;background:#38bdf824;border:1px solid #38bdf859;border-radius:4px;align-items:center;gap:3px;max-width:180px;margin-left:auto;padding:1px 6px;font-size:9px;font-weight:700;display:flex;overflow:hidden}*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root,[data-theme=dark]{--bg:#0a0a10;--panel:#0f1018;--panel-2:#161823;--border:#242636;--text:#e8e9f1;--dim:#9092a4;--faint:#5b5d72;--accent:#8b6cf6;--accent-soft:color-mix(in srgb, var(--accent) 14%, transparent);--accent-glow:color-mix(in srgb, var(--accent) 35%, transparent);--frost:color-mix(in srgb, var(--panel) 88%, transparent);--frost-blur:8px;--glass-border:var(--border);--glass-border-strong:color-mix(in srgb, var(--text) 22%, transparent);--input-bg:color-mix(in srgb, var(--text) 5%, transparent);--input-border:var(--border);--glass-blur:blur(8px);--glass-blur-sm:blur(8px);--mono:"JetBrains Mono", ui-monospace, "Cascadia Code", monospace;--ok:#46c98b;--warn:#e9b14b;--danger:#ef5a5a;--nc-route:#4ade80;--nc-controller:#38d3d3;--nc-action:#8b8bf0;--nc-service:#b07cf6;--nc-view:#ef7bb8;--nc-interface:#e9b14b;--nc-provider:#f0944a}[data-theme=light]{--bg:#f4f5f9;--panel:#fff;--panel-2:#f7f8fc;--border:#e4e6ee;--text:#14151c;--dim:#5b5d72;--faint:#9092a4;--accent:#6b46e8;--accent-soft:color-mix(in srgb, var(--accent) 12%, transparent);--accent-glow:color-mix(in srgb, var(--accent) 28%, transparent);--frost:color-mix(in srgb, var(--panel) 88%, transparent);--frost-blur:8px;--glass-border:var(--border);--glass-border-strong:color-mix(in srgb, var(--text) 18%, transparent);--input-bg:color-mix(in srgb, var(--text) 4%, transparent);--input-border:var(--border);--glass-blur:blur(8px);--glass-blur-sm:blur(8px);--ok:#1f9d63;--warn:#b9802a;--danger:#d63b3b;--nc-route:#2e9e54;--nc-controller:#1f8f8f;--nc-action:#5a5ad6;--nc-service:#7e46d8;--nc-view:#c83d8a;--nc-interface:#b9802a;--nc-provider:#c2640f}body{background:var(--bg);color:var(--text);height:100vh;font-family:Inter,system-ui,-apple-system,sans-serif;font-size:13px;overflow:hidden}body:before{content:"";pointer-events:none;z-index:0;background:radial-gradient(ellipse 55% 45% at 28% 22%, var(--accent-soft) 0%, transparent 60%);position:fixed;inset:0}.app{z-index:1;flex-direction:column;height:100vh;display:flex;position:relative}.main{flex:1;display:flex;overflow:hidden}.graph-container{background-color:#0000;background-image:radial-gradient(var(--border) 1px, transparent 1px);background-size:24px 24px;flex:1;position:relative;overflow:hidden}.toolbar{background:var(--frost);height:64px;-webkit-backdrop-filter:var(--glass-blur);border-bottom:1px solid var(--glass-border);box-shadow:0 1px 0 var(--glass-border), 0 4px 24px #00000040;z-index:100;flex-shrink:0;align-items:center;gap:16px;padding:0 24px;display:flex;position:relative}.toolbar-brand{flex-shrink:0;align-items:center;gap:6px;margin-right:4px;display:flex}.toolbar-logo-img{width:auto;height:38px;display:block}.toolbar-stats{flex-shrink:0;align-items:center;gap:6px;display:flex}.stat-chip{border:1px solid var(--glass-border);color:var(--dim);-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0d;border-radius:8px;padding:4px 10px;font-size:11px;font-weight:500;transition:all .2s}.stat-chip--warn{color:#ffa000;background:#ffa0001a;border-color:#ffa0004d}.stat-chip--stale{color:#f44336;cursor:pointer;background:#f443361a;border-color:#f443364d}.stat-chip--stale:hover{background:#f4433633;transform:translateY(-1px)}.toolbar-controls{align-items:center;gap:20px;margin-left:auto;display:flex}.toolbar-group{align-items:center;gap:10px;display:flex;position:relative}.toolbar-group:not(:last-child):after{content:"";background:var(--glass-border);width:1px;height:24px;margin-left:10px}.toolbar-select,.toolbar-search{border:1px solid var(--glass-border);color:var(--text);-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:10px;outline:none;padding:7px 12px;font-family:inherit;font-size:13px;transition:all .2s}.toolbar-select:hover,.toolbar-search:hover{background:#ffffff17;border-color:#8b6fe873}.toolbar-select:focus,.toolbar-search:focus{background:#8b6fe81a;border-color:#8b6fe8;box-shadow:0 0 0 3px #8b6fe82e,0 0 12px #8b6fe81f}.toolbar-search{width:180px}.toolbar-search-wrapper{position:relative}@media (width<=1200px){.toolbar-btn span:last-child{display:none}.toolbar-btn{padding:4px 8px}}@media (width<=1000px){.toolbar-stats{display:none}}@media (width<=800px){.toolbar-search{width:100px}.toolbar-select{max-width:120px}}.toolbar-btn{border:1px solid var(--glass-border);color:var(--text);cursor:pointer;white-space:nowrap;-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:10px;align-items:center;gap:8px;padding:7px 14px;font-family:inherit;font-size:13px;font-weight:500;transition:all .2s cubic-bezier(.16,1,.3,1);display:flex}.toolbar-btn:hover:not(:disabled){color:var(--text);background:#ffffff1a;border-color:#8b6fe88c;transform:translateY(-1px);box-shadow:0 0 0 1px #8b6fe826,0 4px 12px #0003}.toolbar-btn:active:not(:disabled){transform:translateY(0)}.toolbar-btn--rank{color:#a78bfa;background:#8b6fe81a;border-color:#8b6fe833}.toolbar-btn--rank:hover{background:#8b6fe833;border-color:#8b6fe8}.toolbar-btn:disabled{opacity:.5;cursor:not-allowed}.toolbar-btn--loading{opacity:.7;cursor:wait}.animate-spin{animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.action-dropdown{position:relative}.action-dropdown-menu{background:var(--panel-2);border:1px solid var(--glass-border-strong);z-index:1000;border-radius:14px;flex-direction:column;gap:4px;min-width:200px;padding:8px;animation:.2s cubic-bezier(.16,1,.3,1) dropdownIn;display:flex;position:absolute;top:calc(100% + 8px);left:0;box-shadow:0 16px 48px #00000073,0 0 0 1px #ffffff0a,inset 0 1px #ffffff14}@keyframes dropdownIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.floating-tooltip{z-index:20000;max-width:min(320px,100vw - 24px);color:var(--text);background:var(--panel-2);border:1px solid var(--glass-border-strong);pointer-events:none;border-radius:10px;padding:8px 12px;font-family:inherit;font-size:12px;font-weight:500;line-height:1.45;box-shadow:inset 0 1px #ffffff0f,0 12px 40px #00000059,0 0 0 1px #7c3aed24}[data-theme=light] .floating-tooltip{box-shadow:inset 0 1px #fffffff2,0 12px 36px #00000024,0 0 0 1px #7c3aed24}.tooltip-trigger-wrap{vertical-align:middle;display:inline-flex}.tooltip-trigger-wrap--block{width:100%}.dropdown-item{flex-direction:column;gap:4px;padding:8px;display:flex}.dropdown-item label{color:var(--dim);text-transform:uppercase;letter-spacing:.05em;margin-left:4px;font-size:10px;font-weight:700}.dropdown-item .toolbar-btn,.dropdown-item .toolbar-select{width:100%}.dropdown-chevron{opacity:.5;margin-left:4px;font-size:10px}.toolbar-btn--active{color:#fff;background:#8b6fe82e;border-color:#8b6fe8;box-shadow:0 0 0 1px #8b6fe84d,0 0 16px #8b6fe833}.toolbar-btn-beta{text-transform:uppercase;letter-spacing:.04em;color:#f59e0b;opacity:.8;vertical-align:super;font-size:9px;font-weight:700;line-height:1}.w-full{width:100%}.sidebar-resizable{flex-direction:row;flex-shrink:0;display:flex;position:relative}.sidebar-drag-handle{cursor:col-resize;z-index:10;background:0 0;width:5px;transition:background .15s;position:absolute;top:0;bottom:0;left:0}.sidebar-drag-handle:hover,.sidebar-drag-handle:active{background:var(--border)}.sidebar-drag-handle:after{content:"";background:var(--border);border-radius:1px;width:1px;height:32px;transition:background .15s,height .15s;position:absolute;top:50%;left:2px;transform:translateY(-50%)}.sidebar-drag-handle:hover:after,.sidebar-drag-handle:active:after{background:var(--dim);height:48px}.sidebar{min-width:0;-webkit-backdrop-filter:var(--glass-blur);border-left:1px solid var(--glass-border);background:#ffffff0a;flex-direction:column;flex:1;display:flex;overflow-y:auto;box-shadow:-4px 0 32px #00000040,inset 1px 0 #ffffff0f}.sidebar-header{border-bottom:1px solid var(--glass-border);background:#ffffff06;padding:16px;position:relative}.sidebar-header h2{color:var(--text);margin-top:6px;font-size:14px;font-weight:600}.sidebar-subtitle{color:var(--dim);font-size:11px}.sidebar-header-actions{align-items:center;gap:4px;display:flex;position:absolute;top:10px;right:10px}.sidebar-close{color:var(--dim);cursor:pointer;background:0 0;border:none;padding:0 2px;font-size:18px;line-height:1}.sidebar-ai-btn{padding:2px 5px;font-size:13px}.sidebar-expand-btn{background:var(--accent);color:#fff;cursor:pointer;border:none;border-radius:6px;justify-content:center;align-items:center;gap:6px;width:100%;margin-top:12px;padding:8px 12px;font-size:12px;font-weight:600;transition:background .15s,opacity .15s;display:flex}.sidebar-expand-btn:hover:not(:disabled){background:#6d28d9}.sidebar-expand-btn--done{background:var(--border);color:var(--dim);cursor:default}.type-badge{color:#000;text-transform:uppercase;letter-spacing:.06em;border-radius:99px;padding:2px 8px;font-size:10px;font-weight:600;display:inline-block}.sidebar-badges{align-items:center;gap:8px;margin-bottom:8px;display:flex}.visibility-badge{text-transform:uppercase;background:#ffffff0d;border-radius:4px;padding:2px 8px;font-size:10px;font-weight:700}.visibility-badge--public{color:#4ade80;border:1px solid #4ade8033}.visibility-badge--protected{color:#f59e0b;border:1px solid #f59e0b33}.visibility-badge--private{color:#f87171;border:1px solid #f8717133}.sidebar-stats{background:var(--glass-border);border-radius:10px;gap:1px;margin:12px 16px;display:flex;overflow:hidden;box-shadow:0 2px 12px #0003}.stat{background:#ffffff0a;flex-direction:column;flex:1;align-items:center;padding:10px 0;display:flex}.stat-value{color:var(--text);font-size:20px;font-weight:700}.stat-label{color:var(--dim);margin-top:2px;font-size:10px}.sidebar-hint{color:var(--dim);padding:0 16px 16px;font-size:11px}.sidebar-section{border-top:1px solid var(--border);padding:12px 16px}.sidebar-section h3{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);margin-bottom:8px;font-size:11px}.reachability-note{color:var(--dim);margin-bottom:8px;font-size:12px;line-height:1.5}.reachability-note:last-child{margin-bottom:0}.reachability-references{margin:0;padding:0;font-size:12px;list-style:none}.reachability-references li{border:1px solid var(--border);background:var(--panel);border-radius:4px;margin-bottom:4px;padding:4px 8px}.reachability-references li:last-child{margin-bottom:0}.sidebar-structure-list{margin:0;padding:0;font-size:12px;list-style:none}.sidebar-structure-item{border-bottom:1px solid var(--border);flex-wrap:wrap;align-items:baseline;gap:4px 10px;padding:5px 0;display:flex}.sidebar-structure-item:last-child{border-bottom:none}.structure-kind{text-transform:uppercase;color:var(--dim);min-width:56px;font-size:10px}.structure-name{color:var(--text);font-family:ui-monospace,monospace}.structure-value{color:var(--dim);font-size:11px}.structure-flag,.structure-vis,.structure-decl{color:var(--dim);font-size:10px}.structure-decl{margin-left:6px;font-style:italic}.prop-row{gap:8px;margin-bottom:6px;font-size:12px;display:flex}.prop-key{color:var(--dim);flex-shrink:0;min-width:80px}.prop-value{color:var(--text);word-break:break-all}.prop-value--warn{color:var(--warn)}.edge-row{align-items:center;gap:6px;margin-bottom:5px;font-size:11px;display:flex}.edge-label{color:var(--dim);font-style:italic}.edge-target{color:var(--text)}.sidebar-node-title{color:var(--text);white-space:nowrap;text-overflow:ellipsis;max-width:100%;margin-top:6px;font-size:13px;font-weight:600;overflow:hidden}.sidebar-tab-bar{background:var(--panel-2);border:1px solid var(--border);scrollbar-width:none;border-radius:8px;flex-shrink:0;align-items:stretch;gap:2px;margin:10px 12px;padding:2px;display:flex;overflow-x:auto}.sidebar-tab-bar::-webkit-scrollbar{display:none}.sidebar-tab{color:var(--dim);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:6px;flex:1;justify-content:center;align-items:center;gap:6px;padding:6px 8px;font-family:inherit;font-size:12px;font-weight:500;transition:color .15s,background .15s;display:flex}.sidebar-tab:hover{color:var(--text)}.sidebar-tab--active{color:var(--text);background:var(--accent-soft)}.sidebar-tab-beta{text-transform:uppercase;letter-spacing:.04em;color:var(--warn);opacity:.8;vertical-align:super;font-size:9px;font-weight:700;line-height:1}.sidebar-tab-badge{background:var(--panel);color:var(--faint);font-size:10px;font-family:var(--mono);border-radius:99px;padding:1px 6px}.sidebar-tab--active .sidebar-tab-badge{background:var(--accent-soft);color:var(--accent)}.sidebar-tab-content{flex-direction:column;flex:1;display:flex;overflow-y:auto}.sidebar-section-header{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.sidebar-section-header h3{margin-bottom:0}.tab-bar{height:40px;-webkit-backdrop-filter:var(--glass-blur-sm);border-bottom:1px solid var(--glass-border);scrollbar-width:none;background:#ffffff08;flex-shrink:0;align-items:center;gap:16px;padding:0 16px;display:flex;overflow-x:auto}.tab-bar::-webkit-scrollbar{display:none}.tab-group{align-items:center;gap:8px;height:100%;display:flex}.tab-group-header{color:var(--dim);text-transform:uppercase;letter-spacing:.1em;background:var(--border);white-space:nowrap;border-radius:4px;padding:2px 6px;font-size:10px;font-weight:700}.tab-group-content{align-items:stretch;height:100%;display:flex}.tab-item{color:var(--dim);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;flex-shrink:0;align-items:center;gap:6px;padding:0 10px;font-family:inherit;font-size:12px;transition:color .15s,border-color .15s;display:flex}.tab-item:hover{color:var(--text)}.tab-item--active{color:#a78bfa;text-shadow:0 0 12px #a78bfa80;border-bottom-color:#a78bfa}.tab-label{font-weight:500}.tab-badge{color:var(--dim);text-align:center;background:#ffffff12;border-radius:99px;min-width:20px;padding:1px 6px;font-size:10px}.tab-item--active .tab-badge{color:#a78bfa;background:#a78bfa26}.graph-loading-overlay{color:var(--dim);z-index:10;background:0 0;flex-direction:column;justify-content:center;align-items:center;gap:12px;font-size:13px;display:flex;position:absolute;inset:0}.graph-placeholder{text-align:center;z-index:5;background:0 0;flex-direction:column;justify-content:center;align-items:center;gap:20px;padding:40px;display:flex;position:absolute;inset:0}.placeholder-icon{width:120px;height:120px;-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--glass-border-strong);color:var(--accent);box-shadow:0 20px 40px #0000004d, 0 0 40px var(--accent-glow);background:#ffffff0f;border-radius:32px;justify-content:center;align-items:center;margin-bottom:8px;display:flex;position:relative;overflow:hidden}.placeholder-icon:after{content:"";background:radial-gradient(circle at 50% 50%, var(--accent) 0%, transparent 70%);opacity:.08;position:absolute;inset:0}.placeholder-icon svg{filter:drop-shadow(0 0 8px #7c3aed4d);width:48px;height:48px;animation:4s ease-in-out infinite pulse-gentle}.graph-placeholder h3{color:var(--text);letter-spacing:-.02em;margin:0;font-size:24px;font-weight:700}.graph-placeholder p{color:var(--dim);max-width:440px;margin:0;font-size:14px;line-height:1.6}@keyframes pulse-gentle{0%,to{opacity:1;transform:scale(1)}50%{opacity:.8;transform:scale(1.05)}}.left-sidebar-resizable{flex-direction:row;flex-shrink:0;display:flex;position:relative}.left-sidebar-drag-handle{cursor:col-resize;z-index:10;background:0 0;width:5px;transition:background .15s;position:absolute;top:0;bottom:0;right:0}.left-sidebar-drag-handle:hover,.left-sidebar-drag-handle:active{background:var(--border)}.left-sidebar-drag-handle:after{content:"";background:var(--border);border-radius:1px;width:1px;height:32px;transition:background .15s,height .15s;position:absolute;top:50%;right:2px;transform:translateY(-50%)}.left-sidebar-drag-handle:hover:after,.left-sidebar-drag-handle:active:after{background:var(--dim);height:48px}.left-sidebar{min-width:0;-webkit-backdrop-filter:var(--glass-blur);border-right:1px solid var(--glass-border);background:#ffffff0a;flex-direction:column;flex:1;display:flex;overflow:hidden;box-shadow:4px 0 32px #00000040,inset -1px 0 #ffffff0f}.left-sidebar-top{flex-shrink:0;overflow:hidden auto}.left-sidebar-handle{cursor:row-resize;border-top:1px solid var(--border);border-bottom:1px solid var(--border);background:0 0;flex-shrink:0;height:5px;transition:background .15s;position:relative}.left-sidebar-handle:hover,.left-sidebar-handle:active{background:var(--border)}.left-sidebar-handle:after{content:"";background:var(--border);border-radius:1px;width:32px;height:1px;transition:background .15s,width .15s;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.left-sidebar-handle:hover:after,.left-sidebar-handle:active:after{background:var(--dim);width:48px}.left-sidebar-bottom{flex:1;min-height:0;overflow:hidden auto}.left-nav-search{border-bottom:1px solid var(--border);flex-shrink:0;padding:8px 10px 6px;position:relative}.left-nav-search-input{border:1px solid var(--glass-border);width:100%;color:var(--text);-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:8px;outline:none;padding:5px 24px 5px 8px;font-family:inherit;font-size:12px;transition:border-color .15s,box-shadow .15s}.left-nav-search-input::placeholder{color:var(--dim)}.left-nav-search-input:focus{border-color:#8b6fe8;box-shadow:0 0 0 3px #8b6fe82e}.left-nav-search-clear{color:var(--dim);cursor:pointer;background:0 0;border:none;padding:0 2px;font-size:16px;line-height:1;position:absolute;top:50%;right:16px;transform:translateY(-50%)}.left-nav-search-clear:hover{color:var(--text)}.left-nav-method-filters{border-bottom:1px solid var(--border);flex-wrap:wrap;flex-shrink:0;gap:4px;padding:4px 8px 6px;display:flex}.left-nav-method-badge{border:1px solid var(--method-color);color:var(--method-color);cursor:pointer;opacity:1;background:0 0;border-radius:3px;padding:1px 5px;font-family:inherit;font-size:10px;font-weight:700;transition:opacity .15s,background .15s}.left-nav-method-badge--off{opacity:.3}.left-nav-method-badge:hover{background:color-mix(in srgb, var(--method-color) 15%, transparent);opacity:1}.left-nav{padding:8px 0}.left-nav-overview{padding:6px 8px 4px}.left-nav-item--all{border-radius:6px;gap:7px;border-left:none!important;padding:6px 10px!important}.left-nav-all-icon{color:#a78bfa;flex-shrink:0;font-size:13px}.left-nav-file-group{margin-bottom:2px}.left-nav-file-header{width:100%;color:var(--text);cursor:pointer;text-align:left;letter-spacing:.01em;background:0 0;border:none;align-items:center;gap:5px;padding:5px 10px 5px 8px;font-family:inherit;font-size:11px;font-weight:600;display:flex}.left-nav-file-header:hover{background:var(--border)}.left-nav-file-chevron{color:var(--dim);flex-shrink:0;font-size:9px}.left-nav-file-icon{color:#a78bfa;opacity:.8;flex-shrink:0;justify-content:center;align-items:center;display:flex}.left-nav-file-name{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.left-nav-file-count{color:var(--dim);background:#ffffff0f;border-radius:99px;flex-shrink:0;padding:1px 6px;font-size:10px}.left-nav-prefix-group{border-left:1px solid var(--border);margin-left:8px}.left-nav-empty{color:var(--dim);padding:10px;font-family:"ui-monospace",Fira Code,monospace;font-size:11px}.left-nav-prefix-header{width:100%;color:var(--dim);cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:5px;padding:4px 10px;font-family:"ui-monospace",Fira Code,monospace;font-size:11px;display:flex}.left-nav-prefix-header:hover{color:var(--text);background:var(--border)}.left-nav-prefix-header:hover .left-nav-prefix-icon{color:#f59e0b;opacity:1}.left-nav-prefix-chevron{flex-shrink:0;font-size:9px}.left-nav-prefix-icon{color:var(--dim);opacity:.6;flex-shrink:0;justify-content:center;align-items:center;display:flex}.left-nav-prefix-name{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.left-nav-prefix-count{color:var(--dim);background:#ffffff0d;border-radius:99px;flex-shrink:0;padding:1px 5px;font-size:10px}.left-nav-item{width:100%;color:var(--dim);cursor:pointer;text-align:left;background:0 0;border:none;border-left:2px solid #0000;align-items:center;gap:6px;padding:4px 10px 4px 20px;font-family:inherit;font-size:11px;transition:color .12s,border-color .12s,background .12s;display:flex}.left-nav-item:hover{color:var(--text);background:var(--border)}.left-nav-item--active{color:var(--text);background:#a78bfa1a;border-left-color:#a78bfa;box-shadow:inset 2px 0 8px #a78bfa26}.left-nav-method{text-align:right;flex-shrink:0;width:36px;font-family:"ui-monospace",Fira Code,monospace;font-size:9px;font-weight:700}.left-nav-uri{text-overflow:ellipsis;white-space:nowrap;flex:1;font-family:"ui-monospace",Fira Code,monospace;font-size:11px;overflow:hidden}.left-nav-badge{color:var(--dim);background:#ffffff12;border-radius:99px;flex-shrink:0;padding:1px 5px;font-size:10px}.left-nav-issue-badges{flex-shrink:0;align-items:center;gap:3px;display:inline-flex}.left-nav-issue-badge{background:color-mix(in srgb, var(--issue-color) 18%, transparent);color:var(--issue-color);border:1px solid color-mix(in srgb, var(--issue-color) 45%, transparent);border-radius:99px;flex-shrink:0;align-items:center;gap:3px;height:16px;padding:0 5px;font-size:10px;font-weight:700;line-height:1;display:inline-flex}.left-nav-issue-badge svg{flex-shrink:0}.filter-panel{background:#ffffff06;width:100%;padding:12px 0;overflow-y:auto}.filter-header{border-bottom:1px solid var(--border);justify-content:space-between;align-items:center;margin-bottom:8px;padding:0 12px 8px;display:flex}.filter-title{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);font-size:11px;font-weight:600}.filter-actions{align-items:center;gap:4px;display:flex}.filter-link{color:var(--dim);cursor:pointer;background:0 0;border:none;padding:0;font-family:inherit;font-size:11px}.filter-link:hover{color:var(--text)}.filter-sep{color:var(--border);font-size:11px}.filter-item{cursor:pointer;align-items:center;gap:7px;padding:5px 12px;transition:opacity .15s;display:flex}.filter-item:hover{background:var(--border)}.filter-item--dim{opacity:.45}.filter-checkbox{display:none}.filter-dot{border-radius:50%;flex-shrink:0;width:9px;height:9px}.filter-label{color:var(--text);flex:1;font-size:12px}.filter-count{color:var(--dim);background:var(--bg);text-align:center;border-radius:99px;min-width:22px;padding:1px 6px;font-size:11px}.sidebar-section--source{padding-bottom:0}.source-toggle-wrapper{justify-content:space-between;align-items:center;padding:2px 0 8px;display:flex}.source-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;flex:1;align-items:center;gap:8px;display:flex}.source-toggle:hover h3{color:var(--text)}.source-toggle h3{margin:0}.source-toggle-icon{border-right:1.5px solid var(--dim);border-bottom:1.5px solid var(--dim);flex-shrink:0;align-self:center;width:7px;height:7px;margin-top:-3px;transition:transform .2s;transform:rotate(45deg)}.source-toggle-icon--open{margin-top:1px;transform:rotate(-135deg)}.source-view{border:1px solid var(--glass-border);border-radius:8px;margin-top:4px;margin-bottom:12px;overflow:hidden;box-shadow:0 4px 16px #00000040}.source-path{color:var(--dim);border-bottom:1px solid var(--glass-border);white-space:nowrap;text-overflow:ellipsis;background:#0003;padding:5px 10px;font-size:10px;overflow:hidden}.source-code{background:#00000040;max-height:360px;font-family:ui-monospace,Cascadia Code,Fira Code,monospace;font-size:11.5px;line-height:1.55;overflow:auto}.source-line{gap:0;min-width:max-content;display:flex}.source-line--highlight{background:#a78bfa26;outline:1px solid #a78bfa4d}.source-line-num{text-align:right;width:36px;color:var(--dim);border-right:1px solid var(--border);-webkit-user-select:none;user-select:none;background:#ffffff08;flex-shrink:0;padding:0 8px 0 6px;font-size:10.5px}.source-line-text{white-space:pre;color:var(--text);padding:0 12px}.source-state{color:var(--dim);align-items:center;gap:8px;padding:10px 0;font-size:12px;display:flex}.source-state--error{color:#f44336}.welcome-screen{background:0 0;justify-content:center;align-items:center;width:100%;min-height:100vh;padding:16px;display:flex}.welcome-card{-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--glass-border-strong);text-align:center;background:#0c0d1699;border-radius:24px;width:100%;max-width:480px;padding:48px;animation:.6s cubic-bezier(.16,1,.3,1) slideUp;box-shadow:0 40px 80px #0009,0 0 0 1px #ffffff0a,inset 0 1px #ffffff1f}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.welcome-icon{filter:drop-shadow(0 0 20px #7c3aed66);justify-content:center;margin-bottom:24px;display:flex}.welcome-icon img{width:clamp(80px,30vw,140px);height:auto}@media (width<=480px){.welcome-card{border-radius:16px;padding:32px 24px}}.welcome-card h2{background:linear-gradient(135deg,#fff 0%,#a78bfa 100%);-webkit-text-fill-color:transparent;-webkit-background-clip:text;margin-bottom:16px;font-size:28px;font-weight:800}.welcome-card p{color:var(--dim);margin-bottom:32px;font-size:15px;line-height:1.6}.scan-btn{color:#fff;cursor:pointer;background:linear-gradient(135deg,#7c3aed 0%,#6d28d9 100%);border:none;border-radius:12px;justify-content:center;align-items:center;gap:12px;width:100%;padding:16px 32px;font-size:16px;font-weight:700;transition:all .2s cubic-bezier(.16,1,.3,1);display:flex;box-shadow:0 8px 24px #7c3aed4d}.scan-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 12px 32px #7c3aed66}.scan-btn:active:not(:disabled){transform:translateY(0)}.scan-btn:disabled{opacity:.6;cursor:wait}.btn-spinner{border:2px solid #ffffff4d;border-top-color:#fff;border-radius:50%;width:18px;height:18px;animation:.8s linear infinite spin}.btn-spinner--small{border-width:1.5px;width:12px;height:12px}.welcome-hint{color:var(--dim);margin-top:24px;font-size:12px}.welcome-hint code{color:#a78bfa;background:#0000004d;border-radius:4px;padding:2px 6px}.error-details{color:#ef4444;background:#f443361a;border:1px solid #f4433633;border-radius:8px;margin-bottom:24px;padding:12px;font-family:monospace}.loading-screen{width:100%;min-height:100vh;color:var(--dim);background:0 0;flex-direction:column;justify-content:center;align-items:center;gap:20px;padding:16px;font-size:14px;display:flex}.loading-spinner{border:4px solid var(--border);border-top-color:var(--accent);filter:drop-shadow(0 0 10px #7c3aed33);border-radius:50%;width:48px;height:48px;animation:.8s linear infinite spin}.error-screen h2{color:#f44336;font-size:18px}.error-screen p{font-size:13px}.export-overlay{-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:1000;background:#000000b3;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}.export-modal{-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--glass-border-strong);background:#0c0d16b8;border-radius:16px;flex-direction:column;width:100%;max-width:760px;max-height:85vh;display:flex;overflow:hidden;box-shadow:0 32px 80px #0000008c,0 0 0 1px #ffffff0a,inset 0 1px #ffffff1a}.export-modal-header{border-bottom:1px solid var(--glass-border);background:#ffffff06;flex-shrink:0;justify-content:space-between;align-items:center;padding:18px 20px;display:flex}.export-modal-title{align-items:center;gap:12px;display:flex}.export-modal-icon{font-size:20px}.export-modal-title h2{color:var(--text);margin:0;font-size:15px;font-weight:600}.export-modal-sub{color:var(--dim);text-transform:uppercase;letter-spacing:.06em;font-size:11px}.export-modal-close{color:var(--dim);cursor:pointer;background:0 0;border:none;border-radius:4px;padding:4px 8px;font-size:20px;line-height:1;transition:color .15s,background .15s}.export-modal-close:hover{color:var(--text);background:var(--border)}.export-modal-actions{border-bottom:1px solid var(--border);flex-wrap:wrap;flex-shrink:0;gap:8px;padding:14px 20px;display:flex}.export-btn{cursor:pointer;border:1px solid #0000;border-radius:6px;padding:7px 14px;font-size:12px;font-weight:500;transition:opacity .15s,transform .1s}.export-btn:hover{opacity:.85;transform:translateY(-1px)}.export-btn:active{transform:translateY(0)}.export-btn--primary{color:#fff;background:#1565c0;border-color:#2196f3}.export-btn--secondary{background:var(--border);color:var(--text);border-color:var(--border)}.export-btn--danger{color:#fff;background:#c62828;border-color:#ef5350}.ai-rules-overwrite-banner{background:#ff98001a;border:1px solid #ff980066;border-radius:8px;flex-shrink:0;align-items:flex-start;gap:12px;margin:0 20px;padding:14px 16px;display:flex}.ai-rules-overwrite-icon{flex-shrink:0;margin-top:2px;font-size:20px}.ai-rules-overwrite-body{color:var(--text);flex:1;font-size:13px;line-height:1.5}.ai-rules-overwrite-body strong{margin-bottom:6px;display:block}.ai-rules-overwrite-list{margin:0 0 8px;padding-left:18px;list-style:outside}.ai-rules-overwrite-list li{margin-bottom:2px}.ai-rules-overwrite-list code{background:#ffffff12;border-radius:3px;padding:1px 5px;font-size:12px}.ai-rules-overwrite-actions{flex-direction:column;flex-shrink:0;gap:6px;display:flex}.export-btn--accent{color:#fff;background:#6a1b9a;border-color:#9c27b0}.export-modal-hint{color:var(--dim);border-bottom:1px solid var(--border);flex-shrink:0;padding:8px 20px;font-size:11px}.export-modal-hint a{color:#90caf9;text-decoration:none}.export-modal-hint a:hover{text-decoration:underline}.export-code-wrapper{flex-direction:column;flex:1;display:flex;position:relative;overflow:hidden}.export-code-lang{color:var(--dim);text-transform:uppercase;letter-spacing:.1em;pointer-events:none;font-size:10px;position:absolute;top:8px;right:12px}.export-code{background:var(--bg);color:#a8d8a8;resize:none;white-space:pre;cursor:text;border:none;outline:none;flex:1;min-height:200px;padding:16px;font-family:ui-monospace,Cascadia Code,Fira Code,monospace;font-size:11.5px;line-height:1.6;overflow-y:auto}.export-modal-stats{color:var(--dim);border-top:1px solid var(--border);flex-shrink:0;gap:16px;padding:8px 20px;font-size:11px;display:flex}.flowchart-export-bar{border-bottom:1px solid var(--border);gap:6px;padding:6px 16px;display:flex}.flowchart-export-btn{border:1px solid var(--border);background:var(--bg);color:var(--dim);cursor:pointer;border-radius:5px;padding:4px 10px;font-size:11px;transition:color .15s,background .15s}.flowchart-export-btn:hover:not(:disabled){color:var(--text);background:var(--border)}.flowchart-export-btn:disabled{opacity:.4;cursor:default}.ai-rules-modal{max-width:640px}.ai-rules-select-bar{border-bottom:1px solid var(--border);flex-shrink:0;align-items:center;gap:6px;padding:10px 20px;display:flex}.ai-rules-select-label{color:var(--dim);flex:1;font-size:11px}.ai-rules-select-link{color:#90caf9;cursor:pointer;background:0 0;border:none;padding:0;font-size:11px}.ai-rules-select-link:hover{text-decoration:underline}.ai-rules-select-sep{color:var(--dim);font-size:11px}.ai-rules-grid{flex-direction:column;flex:1;gap:4px;padding:12px 16px;display:flex;overflow-y:auto}.ai-rules-card{border:1px solid var(--glass-border);cursor:pointer;-webkit-user-select:none;user-select:none;background:#ffffff08;border-radius:10px;align-items:center;gap:10px;padding:10px 12px;transition:background .15s,border-color .15s,box-shadow .15s;display:flex}.ai-rules-card:hover{border-color:var(--glass-border-strong);background:#ffffff12}.ai-rules-card--selected{background:#2196f312;border-color:#2196f3}.ai-rules-card--disabled{opacity:.6;cursor:default;pointer-events:none}.ai-rules-checkbox{accent-color:#2196f3;cursor:pointer;flex-shrink:0;width:15px;height:15px}.ai-rules-card-icon{text-align:center;flex-shrink:0;width:24px;font-size:18px}.ai-rules-card-body{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.ai-rules-card-label{color:var(--text);font-size:13px;font-weight:600}.ai-rules-card-path{color:#90caf9;white-space:nowrap;text-overflow:ellipsis;font-family:ui-monospace,Cascadia Code,monospace;font-size:10px;overflow:hidden}.ai-rules-card-desc{color:var(--dim);font-size:11px}.ai-rules-card-status{text-align:center;flex-shrink:0;width:20px;font-size:14px}.ai-rules-status{font-size:14px}.ai-rules-status--ok{color:#4caf50}.ai-rules-status--err{color:#f44336;cursor:help}@keyframes ai-rules-spin{to{transform:rotate(360deg)}}.ai-rules-status--spinning{animation:1s linear infinite ai-rules-spin;display:inline-block}.ai-rules-summary{border-top:1px solid var(--border);flex-shrink:0;gap:16px;padding:8px 20px;font-size:12px;display:flex}.ai-rules-summary--ok{color:#4caf50}.ai-rules-summary--err{color:#f44336}.ai-rules-footer{border-top:1px solid var(--border);flex-shrink:0;justify-content:flex-end;gap:8px;padding:14px 20px;display:flex}.export-btn--loading{opacity:.8;cursor:wait;align-items:center;gap:6px;display:flex}.theme-toggle{border:1px solid var(--glass-border);color:var(--dim);cursor:pointer;-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:8px;flex-shrink:0;padding:5px 9px;font-size:15px;line-height:1;transition:color .15s,background .15s,box-shadow .15s}.theme-toggle:hover{color:var(--text);background:#ffffff1a;box-shadow:0 0 12px #ffc86426}.toolbar-btn--scan{isolation:isolate;letter-spacing:.02em;color:#f5f3ff;background:linear-gradient(165deg,#c4b5fd61 0%,#7c3aed47 48%,#4c1d9566 100%);border:1px solid #c4b5fd8c;border-radius:999px;gap:10px;padding:5px 16px 5px 6px;font-weight:600;transition:transform .2s cubic-bezier(.16,1,.3,1),box-shadow .2s,border-color .2s,background .25s,color .2s;position:relative;overflow:hidden;box-shadow:inset 0 1px #ffffff24,0 4px 16px #31176373}.toolbar-scan__glyph{background:#0000003d;border:1px solid #ffffff24;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:32px;height:32px;display:flex}.toolbar-scan__glyph svg{opacity:.96;display:block}.toolbar-btn--scan:hover:not(:disabled) .toolbar-scan__glyph svg{animation:.7s cubic-bezier(.4,0,.2,1) toolbar-scan-nudge}@keyframes toolbar-scan-nudge{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.toolbar-btn--scan:after{content:"";border-radius:inherit;pointer-events:none;background:linear-gradient(105deg,#0000 35%,#ffffff24 50%,#0000 65%);transition:transform .55s;position:absolute;inset:0;transform:translate(-120%)}.toolbar-btn--scan:hover:not(:disabled):after{transform:translate(120%)}.toolbar-btn--scan:hover:not(:disabled){background:linear-gradient(165deg,#ddd6fe7a 0%,#7c3aed66 52%,#3b076473 100%);border-color:#ddd6fee6;transform:translateY(-1px);box-shadow:inset 0 1px #fff3,0 8px 22px #31176380,0 0 0 2px #7c3aed47}.toolbar-btn--scan:active:not(:disabled){transform:translateY(0);box-shadow:inset 0 1px #ffffff1a,0 2px 10px #31176366}.toolbar-btn--scan.toolbar-btn--loading{box-shadow:none;opacity:.92;background:linear-gradient(165deg,#4c1d95a6 0%,#270f4abf 100%);border-color:#a78bfa59;gap:8px;padding:7px 16px}.toolbar-btn--scan.toolbar-btn--loading:after{display:none}.toolbar-btn--scan:disabled:not(.toolbar-btn--loading){background:var(--panel);color:var(--dim);border-color:var(--glass-border);box-shadow:none}[data-theme=light] .toolbar-btn--scan{color:#3b1a6e;background:linear-gradient(165deg,#f5f3fff5 0%,#c4b5fd8c 100%);border-color:#5b21b652;box-shadow:inset 0 1px #fffffff2,0 4px 16px #5b21b624}[data-theme=light] .toolbar-scan__glyph{background:#7c3aed1f;border-color:#5b21b638}[data-theme=light] .toolbar-btn--scan:hover:not(:disabled){border-color:#7c3aed;box-shadow:inset 0 1px #fff,0 8px 22px #5b21b633,0 0 0 2px #7c3aed38}[data-theme=light] .toolbar-btn--scan.toolbar-btn--loading{color:#f5f3ff;background:linear-gradient(165deg,#6d28d9 0%,#5b21b6 100%);border-color:#7c3aed73}[data-theme=light] .toolbar-btn--scan:disabled:not(.toolbar-btn--loading){color:var(--dim);background:var(--panel)}.sidebar-smells{border-top:1px solid var(--border);flex-wrap:wrap;gap:6px;padding:8px 16px;display:flex}.smell-badge{letter-spacing:.03em;cursor:default;border-radius:99px;align-items:center;gap:4px;padding:3px 9px;font-size:11px;font-weight:600;display:inline-flex}.smell-badge--n1{color:#ff8a80;background:#f4433626;border:1px solid #f4433666;animation:2.5s ease-in-out infinite pulse-n1}@keyframes pulse-n1{0%,to{box-shadow:0 0 #f443364d}50%{box-shadow:0 0 0 5px #f4433600}}.smell-badge--fat-method{color:#ffab40;background:#ff6d0026;border:1px solid #ff6d0066}.smell-badge--fat-class{color:#ce93d8;background:#aa00ff1f;border:1px solid #aa00ff59}.smell-badge--deferred{color:#fbc02d;background:#ca8a0426;border:1px solid #ca8a0466}.metrics-grid{grid-template-columns:repeat(4,1fr);gap:6px;display:grid}.metric-item{-webkit-backdrop-filter:var(--glass-blur-sm);border:1px solid var(--glass-border);background:#ffffff0a;border-radius:8px;flex-direction:column;align-items:center;padding:8px 4px;transition:background .2s,border-color .2s;display:flex}.metric-item:hover{border-color:var(--glass-border-strong);background:#ffffff12}.metric-value{color:var(--text);font-size:18px;font-weight:700;line-height:1}.metric-label{color:var(--dim);text-transform:uppercase;letter-spacing:.07em;margin-top:4px;font-size:9px}.stat-chip--stale{color:#ffa000;cursor:pointer;background:#ffa00014;border-color:#ffa00099;font-family:inherit;font-size:11px;animation:2.5s ease-in-out infinite stale-pulse}.stat-chip--stale:hover{background:#ffa0002e;border-color:#ffa000e6}@keyframes stale-pulse{0%,to{opacity:1}50%{opacity:.65}}.stat-chip--age{color:var(--dim);font-size:11px}.sidebar-section--queries h3{align-items:center;gap:6px;display:flex}.sidebar-section--queries h3:before{content:"⛁";font-size:12px}.query-list{flex-direction:column;gap:4px;margin-top:6px;display:flex}.query-item{border:1px solid var(--glass-border);background:#ffffff0a;border-radius:6px;align-items:center;gap:6px;padding:4px 6px;font-size:11px;display:flex}.query-op{letter-spacing:.05em;text-transform:uppercase;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:10px;font-weight:700}.query-op--read{color:#2196f3;background:#2196f326}.query-op--write{color:#f44336;background:#f4433626}.query-table{color:var(--text);text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.query-badge{letter-spacing:.06em;text-transform:uppercase;border-radius:3px;flex-shrink:0;padding:1px 4px;font-size:9px;font-weight:700}.query-badge--raw{color:#9c27b0;background:#9c27b026}.sidebar-section--cache h3{align-items:center;gap:6px;display:flex}.sidebar-section--cache h3:before{content:"⛃";font-size:12px}.cache-list{flex-direction:column;gap:4px;margin-top:6px;display:flex}.cache-item{border:1px solid var(--glass-border);background:#ffffff0a;border-radius:6px;padding:4px 6px;font-size:11px}.cache-item-head{align-items:center;gap:6px;min-width:0;display:flex}.cache-kind{letter-spacing:.05em;text-transform:uppercase;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:10px;font-weight:700}.cache-kind--read{color:#2196f3;background:#2196f326}.cache-kind--write{color:#f44336;background:#f4433626}.cache-kind--invalidate{color:#ff9800;background:#ff980026}.cache-kind--lock{color:#ba68c8;background:#9c27b026}.cache-method{color:var(--dim);font-family:var(--font-mono,monospace);flex-shrink:0}.cache-key{min-width:0;color:var(--text);font-family:var(--font-mono,monospace);text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.cache-key--computed{color:var(--dim);font-style:italic}.cache-key--constructed{color:#ce93d8}.cache-item-meta{flex-wrap:wrap;gap:4px;margin-top:3px;padding-left:2px;display:flex}.cache-meta{letter-spacing:.04em;border:1px solid var(--glass-border);color:var(--dim);background:#ffffff0d;border-radius:3px;padding:1px 4px;font-size:9px}.cache-meta--tag{color:#4db6ac;background:#0096881f;border-color:#0096884d}[data-theme=light] .cache-item,[data-theme=light] .cache-meta{background:#00000008}[data-theme=light] .cache-key--constructed{color:#6a1b9a}[data-theme=light] .cache-meta--tag{color:#00695c}.sidebar-section--http h3{align-items:center;gap:6px;display:flex}.sidebar-section--http h3:before{content:"🌐";font-size:11px}.ins-chip--http{--cc:#38bdf8}.http-list{flex-direction:column;gap:6px;margin-top:6px;display:flex}.http-item{border:1px solid var(--glass-border);background:#ffffff0a;border-radius:6px;flex-direction:column;gap:4px;padding:6px;font-size:11px;display:flex}.http-item-head{align-items:center;gap:6px;min-width:0;display:flex}.http-method{letter-spacing:.05em;color:#38bdf8;background:#38bdf826;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:10px;font-weight:700}.http-method--post,.http-method--put,.http-method--patch,.http-method--delete{color:#f44336;background:#f4433626}.http-method--unknown{color:var(--dim);background:#ffffff14}.http-target{min-width:0;color:var(--text);text-overflow:ellipsis;white-space:nowrap;flex:1;font-family:ui-monospace,monospace;overflow:hidden}.http-item-meta{flex-wrap:wrap;gap:4px;display:flex}.http-badge{letter-spacing:.04em;color:var(--dim);white-space:nowrap;background:#ffffff0f;border-radius:3px;padding:1px 4px;font-size:9px;font-weight:600}.http-badge--client{color:#9c27b0;text-transform:uppercase;background:#9c27b026}.http-badge--absent{color:#ff9800;background:#ff980026}.http-badge--muted{opacity:.6}.modal-overlay{-webkit-backdrop-filter:blur(8px);z-index:2000;background:#0009;justify-content:center;align-items:center;padding:40px;display:flex;position:fixed;inset:0}.modal-container{-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--glass-border-strong);background:#0c0d16b8;border-radius:16px;flex-direction:column;width:100%;max-width:800px;max-height:100%;display:flex;overflow:hidden;box-shadow:0 32px 80px #0000008c,0 0 0 1px #ffffff0a,inset 0 1px #ffffff1a}.modal-container--large{max-width:1100px}.modal-header{border-bottom:1px solid var(--glass-border);background:#ffffff06;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.modal-title{align-items:center;gap:12px;display:flex}.modal-icon{font-size:24px}.modal-title h2{color:var(--text);font-size:16px;font-weight:700}.modal-sub{color:var(--dim);font-size:11px}.modal-close{color:var(--dim);cursor:pointer;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;width:32px;height:32px;font-size:24px;line-height:1;transition:all .15s;display:flex}.modal-close:hover{color:var(--text);background:var(--border)}.modal-body{flex:1;padding:20px;overflow-y:auto}.flowchart-modal-body{background:var(--bg);padding:40px}.flowchart-modal-body .flowchart{max-width:900px;margin:0 auto}.flow-header-wrapper{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.flow-popup-btn{color:var(--dim);cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;padding:4px;font-size:14px;transition:all .15s;display:flex}.flow-popup-btn:hover{color:var(--text);background:var(--border)}.source-modal-body{background:var(--bg);padding:0}.source-modal-body .source-view{border:none;border-radius:0}.source-modal-body .source-view .source-path{display:none}.source-modal-body pre{max-height:calc(90vh - 100px)!important}.st-section{border-top:1px solid var(--border);padding:12px 16px}.st-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;display:flex}.st-toggle:hover h3{color:var(--text)}.st-toggle h3{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);margin:0;font-size:11px;transition:color .15s}.st-toggle-icon{color:var(--dim);font-size:10px}.st-body{margin-top:10px}.st-form{flex-direction:column;gap:7px;display:flex}.st-form-row{align-items:center;gap:6px;display:flex}.st-form-col{flex-direction:column;gap:4px;display:flex}.st-label{color:var(--dim);flex-shrink:0;min-width:76px;font-size:11px}.st-uri-preview{color:var(--text);text-overflow:ellipsis;white-space:nowrap;align-items:center;gap:5px;font-size:12px;display:flex;overflow:hidden}.st-method-badge{background:var(--accent);color:#fff;border-radius:4px;flex-shrink:0;padding:1px 5px;font-size:10px;font-weight:700}.st-input{border:1px solid var(--glass-border);color:var(--text);-webkit-backdrop-filter:var(--glass-blur-sm);background:#ffffff0f;border-radius:8px;outline:none;flex:1;padding:5px 10px;font-family:inherit;font-size:12px;transition:border-color .2s,box-shadow .2s}.st-input--short{text-align:center;flex:0 0 52px}.st-input:focus{border-color:#8b6fe8;box-shadow:0 0 0 3px #8b6fe826}.st-textarea{border:1px solid var(--glass-border);color:var(--text);resize:vertical;box-sizing:border-box;background:#ffffff0f;border-radius:8px;outline:none;width:100%;padding:6px 10px;font-family:SFMono-Regular,Consolas,monospace;font-size:11px;transition:border-color .2s,box-shadow .2s}.st-textarea:focus{border-color:#8b6fe8;box-shadow:0 0 0 3px #8b6fe826}.st-run-btn{color:#fff;cursor:pointer;background:linear-gradient(135deg,#7c3aed 0%,#a78bfa 100%);border:1px solid #ffffff1a;border-radius:8px;width:100%;margin-top:2px;padding:7px 14px;font-family:inherit;font-size:12px;font-weight:600;transition:all .2s;box-shadow:0 3px 10px #7c3aed4d}.st-run-btn:hover:not(:disabled){background:linear-gradient(135deg,#6d28d9 0%,#8b5cf6 100%);transform:translateY(-1px);box-shadow:0 5px 14px #7c3aed66}.st-run-btn:active:not(:disabled){transform:translateY(1px)}.st-run-btn:disabled{opacity:.5;cursor:not-allowed}.st-results{margin-top:10px}.st-metrics-grid{grid-template-columns:repeat(3,1fr);gap:5px;margin-bottom:10px;display:grid}.st-metric{border:1px solid var(--glass-border);text-align:center;background:#ffffff0a;border-radius:6px;padding:6px 6px 5px}.st-metric-value{color:var(--text);font-size:13px;font-weight:600;line-height:1.2}.st-metric-label{color:var(--dim);text-transform:uppercase;letter-spacing:.06em;margin-top:2px;font-size:9px}.st-dist{margin-bottom:8px}.st-dist-title{text-transform:uppercase;letter-spacing:.07em;color:var(--dim);margin-bottom:6px;font-size:10px}.st-dist-row{align-items:center;gap:6px;margin-bottom:4px;display:flex}.st-dist-label{color:var(--dim);min-width:32px;font-family:SFMono-Regular,Consolas,monospace;font-size:11px}.st-dist-bar-wrap{background:var(--border);border-radius:3px;flex:1;height:7px;overflow:hidden}.st-dist-bar{border-radius:3px;min-width:2px;height:100%;transition:width .4s}.st-dist-count{color:var(--dim);text-align:right;min-width:22px;font-size:11px}.st-docker-hint{color:#fbbf24;background:#fbbf2414;border:1px solid #fbbf2440;border-radius:6px;padding:8px 10px;font-size:11px;line-height:1.6}.st-docker-hint code{background:#fbbf2426;border-radius:3px;padding:1px 4px;font-family:SFMono-Regular,Consolas,monospace;font-size:10.5px}.st-error-box{color:#f87171;word-break:break-word;background:#ef444414;border:1px solid #ef444433;border-radius:6px;padding:8px 10px;font-family:SFMono-Regular,Consolas,monospace;font-size:11px;line-height:1.5}.st-last-run{color:var(--dim);opacity:.7;font-size:10px}.st-last-run--form{text-align:center;margin-top:2px}.st-trace{background:var(--bg-card,#ffffff08);border:1px solid #ffffff12;border-radius:8px;margin-bottom:12px;padding:10px 12px}.st-trace-title{letter-spacing:.06em;text-transform:uppercase;color:var(--dim);margin-bottom:8px;font-size:10px;font-weight:700}.st-trace-list{flex-direction:column;gap:0;display:flex}.st-trace-node{opacity:0;animation:.25s forwards st-trace-in;animation-delay:calc(var(--trace-i,0) * 60ms)}@keyframes st-trace-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.st-trace-node--running .st-trace-row{animation:1.2s ease-in-out infinite st-trace-pulse;animation-delay:calc(var(--trace-i,0) * .12s)}@keyframes st-trace-pulse{0%,to{opacity:1}50%{opacity:.55}}.st-trace-connector{align-items:center;gap:6px;padding:2px 0 2px 6px;display:flex}.st-trace-arrow{color:var(--dim);opacity:.5;font-size:11px;line-height:1}.st-trace-edge-label{color:var(--dim);opacity:.55;white-space:nowrap;text-overflow:ellipsis;max-width:100px;font-size:9px;font-style:italic;overflow:hidden}.st-trace-row{border-radius:5px;align-items:center;gap:7px;padding:3px 4px;display:flex}.st-trace-badge{letter-spacing:.05em;text-transform:uppercase;color:#fff;white-space:nowrap;border-radius:3px;flex-shrink:0;padding:2px 5px;font-size:8px;font-weight:700}.st-trace-label{color:var(--text);white-space:nowrap;text-overflow:ellipsis;min-width:0;font-family:ui-monospace,monospace;font-size:11px;overflow:hidden}.left-sidebar-tabs{border-bottom:1px solid var(--glass-border);background:#0000001f;flex-shrink:0;display:flex}.left-sidebar-tab{color:var(--dim);letter-spacing:.03em;text-transform:uppercase;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;flex:1;padding:8px 4px;font-family:inherit;font-size:11px;font-weight:600;transition:color .15s,border-color .15s}.left-sidebar-tab:hover{color:var(--text)}.left-sidebar-tab--active{color:#a78bfa;text-shadow:0 0 10px #a78bfa73;border-bottom-color:#a78bfa}.complexity-panel{flex-direction:column;height:100%;display:flex;overflow:hidden}.complexity-filters{flex-shrink:0;gap:4px;padding:8px 10px 4px;display:flex}.complexity-filter-btn{border:1px solid var(--border);color:var(--dim);cursor:pointer;background:#ffffff0a;border-radius:4px;padding:3px 8px;font-family:ui-monospace,monospace;font-size:10px;font-weight:600;transition:color .15s,border-color .15s,background .15s}.complexity-filter-btn:hover{color:var(--text);border-color:#a78bfa}.complexity-filter-btn--active{color:#a78bfa;background:#a78bfa1a;border-color:#a78bfa}.complexity-summary{color:var(--dim);flex-shrink:0;padding:2px 10px 6px;font-size:10px}.complexity-empty{color:var(--dim);text-align:center;padding:24px 16px;font-size:12px}.complexity-list{flex:1;padding:0 0 8px;overflow:hidden auto}.complexity-row{cursor:pointer;text-align:left;background:0 0;border:none;border-bottom:1px solid #0000;align-items:center;gap:8px;width:100%;padding:5px 10px;transition:background .1s;display:flex}.complexity-row:hover{background:#ffffff0a}.complexity-row--active{background:#a78bfa14;border-bottom-color:#a78bfa33}.complexity-badge{text-align:center;border:1px solid;border-radius:4px;flex-shrink:0;min-width:28px;padding:1px 4px;font-family:ui-monospace,monospace;font-size:11px;font-weight:700}.complexity-label{min-width:0;color:var(--text);white-space:nowrap;text-overflow:ellipsis;flex:1;font-family:ui-monospace,monospace;font-size:11px;overflow:hidden}.complexity-type{letter-spacing:.04em;text-transform:uppercase;opacity:.85;flex-shrink:0;font-size:9px;font-weight:600}.g-legends{z-index:4;pointer-events:none;flex-direction:column;align-items:flex-end;gap:10px;max-height:calc(100% - 140px);display:flex;position:absolute;top:64px;right:16px;overflow-y:auto}.cc-legend{border:1px solid var(--glass-border-strong);pointer-events:none;-webkit-backdrop-filter:var(--glass-blur);background:#07080f8c;border-radius:12px;min-width:160px;padding:10px 14px;position:static;box-shadow:0 8px 32px #0006,inset 0 1px #ffffff14}.cc-legend-title{letter-spacing:.08em;text-transform:uppercase;color:#fff6;margin-bottom:8px;font-size:9px;font-weight:700}.cc-legend-row{align-items:center;gap:8px;margin-bottom:5px;display:flex}.cc-legend-row:last-child{margin-bottom:0}.cc-legend-swatch{border-radius:2px;flex-shrink:0;width:10px;height:10px}.cc-legend-label{flex:1;font-size:11px;font-weight:600}.cc-legend-range{color:#fff6;font-family:ui-monospace,monospace;font-size:10px}.collapse-toggle-btn{cursor:pointer;z-index:50;pointer-events:all;-webkit-user-select:none;user-select:none;color:#ffffffc7;will-change:transform, left, top;background:#282a36eb;border:1.25px solid #ffffff59;border-radius:50%;justify-content:center;align-items:center;width:14px;height:14px;padding:0;transition:transform .12s ease-out,background .12s,border-color .12s,color .12s,box-shadow .12s;display:flex;position:absolute;transform:translate(-50%,-50%);box-shadow:0 1px 3px #00000073,inset 0 0 0 1px #00000040}.collapse-toggle-btn:hover{color:#fff;background:#7c3aed;border-color:#c4b5fd;transform:translate(-50%,-50%)scale(1.25);box-shadow:0 2px 6px #7c3aed8c,inset 0 0 0 1px #fff3}.collapse-toggle-btn--collapsed{color:#fff;background:#7c3aed;border-color:#c4b5fd;box-shadow:0 1px 4px #7c3aed8c,inset 0 0 0 1px #fff3}.collapse-toggle-btn--collapsed:hover{background:#8b5cf6;border-color:#fff}.collapse-toggle-btn--dimmed{opacity:.18;pointer-events:none}[data-theme=light] .collapse-toggle-btn{color:#000000b3;background:#fffffff5;border-color:#00000040;box-shadow:0 1px 3px #0000002e}[data-theme=light] .collapse-toggle-btn:hover,[data-theme=light] .collapse-toggle-btn--collapsed{color:#fff;background:#7c3aed;border-color:#5b21b6}[data-theme=light] .welcome-screen,[data-theme=light] .loading-screen{background:0 0}[data-theme=light] .welcome-card{-webkit-backdrop-filter:var(--glass-blur);background:#ffffffa6;border-color:#0000001f;box-shadow:0 32px 64px #0000001f,inset 0 1px #fffc}[data-theme=light] .welcome-card h2{background:linear-gradient(135deg,#1e1e2e 0%,#7c3aed 100%);-webkit-text-fill-color:transparent;-webkit-background-clip:text;background-clip:text}[data-theme=light] .welcome-hint code{color:#7c3aed;background:#0000000f}[data-theme=light] .cc-legend{background:#fffffff2;border-color:#0000001a}[data-theme=light] .cc-legend-title,[data-theme=light] .cc-legend-range{color:#0006}[data-theme=light] .left-nav-file-count{background:#00000012}[data-theme=light] .left-nav-prefix-count{background:#0000000f}[data-theme=light] .left-nav-badge,[data-theme=light] .tab-badge{background:#00000012}[data-theme=light] .tab-item--active .tab-badge{background:#7c3aed1f}[data-theme=light] .visibility-badge{background:#0000000a}[data-theme=light] .source-line-num{background:#00000008}[data-theme=light] .complexity-filter-btn,[data-theme=light] .complexity-row:hover{background:#0000000a}[data-theme=light] .st-trace{background:#00000005;border-color:#00000014}[data-theme=light] .smell-badge--n1{color:#c62828;background:#f443361a;border-color:#f4433659}[data-theme=light] .smell-badge--fat-method{color:#bf360c;background:#ff6d001a;border-color:#ff6d0059}[data-theme=light] .smell-badge--fat-class{color:#6a1b9a;background:#aa00ff14;border-color:#aa00ff4d}[data-theme=light] .smell-badge--deferred{color:#8a5a00;background:#ca8a041f;border-color:#ca8a0459}[data-theme=light] .toolbar-btn--active{color:#5b21b6;background:#7c3aed1f;border-color:#8b6fe8}[data-theme=light] .export-modal-hint a,[data-theme=light] .ai-rules-select-link,[data-theme=light] .ai-rules-card-path{color:#1565c0}[data-theme=light] .export-code{color:#2e7d32;background:#f8fffe}[data-theme=light] .st-docker-hint{background:#fbbf241a;border-color:#fbbf2466}[data-theme=light] .modal-container{background:#ffffffb8;border-color:#0000001f;box-shadow:0 20px 40px #00000026,inset 0 1px #ffffffe6}[data-theme=light] .export-modal{background:#ffffffb8;border-color:#0000001f;box-shadow:0 24px 80px #0000002e,inset 0 1px #ffffffe6}[data-theme=light] .action-dropdown-menu{box-shadow:0 12px 32px #00000024}[data-theme=light] .placeholder-icon{background:#ffffff8c;box-shadow:0 20px 40px #00000014,0 0 30px #7c3aed26}[data-theme=light] .sidebar{background:#ffffff8c;box-shadow:-4px 0 24px #00000014,inset 1px 0 #fffc}[data-theme=light] .left-sidebar{background:#ffffff8c;box-shadow:4px 0 24px #00000014,inset -1px 0 #fffc}.collapse-toggle-btn svg{stroke:currentColor;stroke-width:2.5px;stroke-linecap:round;fill:none;pointer-events:none;width:8px;height:8px;display:block}.sidebar-section--security{flex-direction:column;gap:10px;padding:12px 16px;display:flex}.security-exposure-card{border:1.5px solid;border-radius:8px;flex-direction:column;gap:4px;padding:10px 12px;display:flex}.security-exposure-header{align-items:center;gap:8px;display:flex}.security-exposure-badge{letter-spacing:.04em;font-family:ui-monospace,monospace;font-size:12px;font-weight:700}.security-exposure-desc{opacity:.8;margin:0;font-size:12px;line-height:1.5}.security-clean{opacity:.7;align-items:center;gap:6px;padding:10px 0;font-size:13px;display:flex}.security-issues-title{text-transform:uppercase;letter-spacing:.08em;opacity:.6;margin-bottom:2px;font-size:11px;font-weight:700}.security-issue-card{background:#ffffff08;border-left:3px solid;border-radius:0 6px 6px 0;flex-direction:column;gap:4px;padding:8px 10px;display:flex}.security-issue-header{align-items:center;gap:6px;display:flex}.security-issue-icon{font-size:13px}.security-issue-name{flex:1;font-size:12px;font-weight:700}.security-issue-severity{letter-spacing:.06em;opacity:.9;font-family:ui-monospace,monospace;font-size:9px;font-weight:700}.security-issue-message{opacity:.8;margin:0;font-size:12px;line-height:1.5}.security-issue-location{align-items:center;gap:6px;margin-top:2px;font-size:11px;display:flex}[data-theme=light] .security-issue-card{background:#00000005}kbd,.toolbar-kbd,.stat-chip,.route-row-uri,.route-row-method,.flag-card-path,.sidebar-node-title,.ins-chip,.prop-key,.prop-value,.show-graph-count,.g-rail-pill,.g-zoom-pct,.ins-meter-value{font-family:var(--mono)}.toolbar{background:var(--frost);height:52px;-webkit-backdrop-filter:blur(var(--frost-blur));border-bottom:1px solid var(--border);box-shadow:none;gap:14px;padding:0 14px}.toolbar-brand{align-items:center;gap:9px;display:flex}.toolbar-logo-img{width:26px;height:26px}.toolbar-brand-text{flex-direction:column;line-height:1.1;display:flex}.toolbar-brand-name{color:var(--text);font-size:13px;font-weight:600}.toolbar-brand-sub{color:var(--faint);font-size:10px}.seg-group{background:var(--panel-2);border:1px solid var(--border);border-radius:8px;align-items:center;gap:2px;padding:2px;display:flex}.seg-btn{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:5px 11px;font-size:12px}.seg-btn:hover{color:var(--text)}.seg-btn--active{background:var(--accent-soft);color:var(--text)}.seg-dropdown{position:relative}.seg-dropdown-menu{background:var(--panel);border:1px solid var(--border);z-index:200;border-radius:8px;flex-direction:column;gap:4px;min-width:200px;padding:6px;display:flex;position:absolute;top:calc(100% + 6px);left:0;right:auto;box-shadow:0 12px 36px #0006}.seg-menu-row{flex-direction:column;gap:4px;padding:4px 6px;display:flex}.seg-menu-row label{text-transform:uppercase;letter-spacing:.06em;color:var(--faint);font-size:10px}.seg-select,.seg-menu-btn{background:var(--panel-2);border:1px solid var(--border);color:var(--text);font:inherit;cursor:pointer;text-align:left;border-radius:6px;padding:6px 8px;font-size:12px}.seg-menu-btn:hover{border-color:var(--accent)}.seg-menu-btn--on{background:var(--accent-soft);border-color:var(--accent)}.toolbar-center{flex:1;justify-content:center;align-items:center;gap:10px;display:flex}.toolbar-search-wrapper{align-items:center;width:min(520px,42vw);display:flex;position:relative}.toolbar-search-icon{color:var(--faint);position:absolute;left:11px}.toolbar-search{background:var(--panel-2);border:1px solid var(--border);width:100%;color:var(--text);font:inherit;border-radius:8px;padding:7px 44px 7px 32px;font-size:12px}.toolbar-search:focus{border-color:var(--accent);outline:none}.toolbar-kbd{color:var(--faint);background:var(--panel);border:1px solid var(--border);border-radius:4px;padding:1px 5px;font-size:10px;position:absolute;right:8px}.risk-pill{background:var(--panel-2);border:1px solid var(--border);color:var(--dim);font:inherit;cursor:pointer;border-radius:999px;align-items:center;gap:7px;padding:5px 11px;font-size:12px;display:flex}.risk-pill-dot{background:var(--faint);border-radius:50%;width:7px;height:7px}.risk-pill--alert{color:var(--text);border-color:color-mix(in srgb, var(--danger) 50%, transparent)}.risk-pill--alert .risk-pill-dot{background:var(--danger);box-shadow:0 0 8px var(--danger)}.risk-pill-count{font-family:var(--mono);background:var(--panel);border-radius:999px;padding:1px 7px;font-size:11px}.risk-pill--alert .risk-pill-count{background:var(--danger);color:#fff}.toolbar-right{align-items:center;gap:8px;display:flex}.toolbar-right .seg-dropdown-menu{left:auto;right:0}.icon-btn{background:var(--panel-2);border:1px solid var(--border);color:var(--text);cursor:pointer;border-radius:7px;width:30px;height:30px;font-size:14px}.icon-btn:hover{border-color:var(--accent)}.rescan-btn{background:var(--accent);color:#fff;font:inherit;cursor:pointer;border:0;border-radius:7px;align-items:center;gap:7px;padding:7px 13px;font-size:12px;font-weight:600;display:flex}.rescan-btn:hover{filter:brightness(1.1)}.rescan-btn:disabled{opacity:.6;cursor:default}.stat-chip{color:var(--dim);background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:3px 8px;font-size:11px}.stat-chip--warn{color:var(--warn);border-color:color-mix(in srgb, var(--warn) 40%, transparent)}.left-sidebar-resizable{flex-shrink:0;position:relative}.left-sidebar{background:var(--panel);border-right:1px solid var(--border);flex-direction:column;width:100%;height:100%;display:flex}.left-sidebar-drag-handle{cursor:col-resize;z-index:5;width:6px;height:100%;position:absolute;top:0;right:-3px}.left-search{padding:12px 12px 8px;position:relative}.left-search-input{background:var(--panel-2);border:1px solid var(--border);width:100%;color:var(--text);font:inherit;border-radius:7px;padding:7px 26px 7px 10px;font-size:12px}.left-search-input:focus{border-color:var(--accent);outline:none}.left-search-clear{color:var(--faint);cursor:pointer;background:0 0;border:0;font-size:14px;position:absolute;top:50%;right:18px;transform:translateY(-50%)}.left-method-chips{flex-wrap:wrap;gap:5px;padding:0 12px 10px;display:flex}.method-chip{border:1px solid var(--border);color:var(--faint);font-family:var(--mono);cursor:pointer;background:0 0;border-radius:6px;flex:auto;padding:4px 6px;font-size:10px;font-weight:600}.method-chip--on{color:var(--mc);background:color-mix(in srgb, var(--mc) 16%, transparent);border-color:color-mix(in srgb, var(--mc) 55%, transparent)}.mode-tabs{background:var(--panel-2);border:1px solid var(--border);border-radius:8px;gap:2px;margin:0 12px 8px;padding:2px;display:flex}.mode-tab{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:1;justify-content:center;align-items:center;gap:6px;padding:6px 0;font-size:12px;display:flex}.mode-tab--active{background:var(--accent-soft);color:var(--text)}.mode-tab-count{font-family:var(--mono);color:var(--faint);background:var(--panel);border-radius:999px;padding:0 6px;font-size:10px}.mode-tab-count--alert{background:var(--danger);color:#fff}.left-content{flex:1;padding:0 8px;overflow:auto}.route-tree{width:100%;min-width:0}.left-empty{color:var(--faint);text-align:center;padding:18px 12px;font-size:12px}.tree-group-header{width:100%;color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:5px;align-items:center;gap:7px;padding:5px 6px;font-size:12px;display:flex}.tree-group-header:hover{background:var(--panel-2);color:var(--text)}.tree-group-chevron{width:10px;color:var(--faint);font-size:9px}.tree-group-icon{width:14px;height:14px;color:var(--faint);flex-shrink:0}.tree-group-header:hover .tree-group-icon{color:var(--dim)}.tree-group-name{text-align:left;flex:1}.tree-group-count{font-family:var(--mono);color:var(--faint);font-size:10px}.tree-group-body{padding-left:10px}.route-row{width:100%;color:var(--text);font:inherit;cursor:pointer;background:0 0;border:0;border-left:2px solid #0000;border-radius:0 5px 5px 0;align-items:center;gap:9px;padding:6px 8px;display:flex}.route-row:hover{background:var(--panel-2)}.route-row--active{border-left-color:var(--accent);background:var(--accent-soft)}.route-row-method{font-family:var(--mono);min-width:38px;font-size:10px;font-weight:700}.route-row-uri{text-align:left;white-space:nowrap;scrollbar-width:none;flex:1;min-width:0;font-size:12px;overflow:auto hidden}.route-row-uri::-webkit-scrollbar{display:none}.route-row-risk{font-family:var(--mono);color:var(--rc);background:color-mix(in srgb, var(--rc) 16%, transparent);border:1px solid color-mix(in srgb, var(--rc) 45%, transparent);border-radius:999px;padding:0 6px;font-size:10px}.route-row-loading{color:var(--faint)}.route-row--stacked{align-items:flex-start}.route-row--stacked .route-row-method{padding-top:1px}.schedule-row-body{flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}.schedule-row-body .route-row-uri{white-space:nowrap;overflow-wrap:normal}.schedule-row-scroll{max-width:calc(var(--left-sidebar-width,300px) - 76px);scrollbar-width:none;align-items:center;gap:4px;display:flex;overflow:auto hidden}.schedule-row-scroll::-webkit-scrollbar{display:none}.schedule-row-scroll>*{flex:none}.schedule-row-badges{margin-top:2px}.schedule-cadence{font-family:var(--mono);color:var(--accent);text-align:left;font-size:10px}.schedule-cadence--unknown{color:var(--faint);font-style:italic}.schedule-chip{color:var(--dim);background:var(--panel-2);border:1px solid var(--border);border-radius:999px;padding:0 5px;font-size:9px;line-height:1.6}.flag-list{flex-direction:column;gap:7px;padding:6px 4px;display:flex}.flag-card{text-align:left;background:var(--panel-2);border:1px solid var(--border);cursor:pointer;color:var(--text);font:inherit;border-radius:8px;padding:9px 11px}.flag-card:hover{border-color:var(--accent)}.flag-card--active{border-color:var(--accent);background:var(--accent-soft)}.flag-card-top{justify-content:space-between;align-items:center;margin-bottom:5px;display:flex}.flag-card-sev{font-family:var(--mono);color:var(--sc);background:color-mix(in srgb, var(--sc) 16%, transparent);border-radius:4px;padding:1px 6px;font-size:10px;font-weight:700}.flag-card-time{color:var(--faint);font-size:10px}.flag-card-method{font-family:var(--mono);font-size:10px;font-weight:700}.flag-card-path{word-break:break-all;margin-bottom:3px;font-size:12px}.flag-card-desc{color:var(--dim);font-size:11px}.left-footer{border-top:1px solid var(--border);background:var(--panel)}.show-graph{flex-direction:column;max-height:220px;padding:10px 12px;display:flex}.show-graph-header{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.show-graph-title{text-transform:uppercase;letter-spacing:.06em;color:var(--faint);font-size:11px}.show-graph-actions{align-items:center;gap:5px;display:flex}.show-graph-link{color:var(--accent);font:inherit;cursor:pointer;background:0 0;border:0;font-size:11px}.show-graph-sep{color:var(--faint);font-size:11px}.show-graph-grid{grid-template-columns:1fr 1fr;gap:4px;display:grid;overflow-y:auto}.show-graph-item{color:var(--text);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:4px;align-items:center;gap:6px;padding:3px 4px;font-size:11px;display:flex}.show-graph-item:hover{background:var(--panel-2)}.show-graph-item--off{opacity:.4}.show-graph-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px}.show-graph-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:1;overflow:hidden}.show-graph-count{color:var(--faint);font-size:10px}.sidebar-eyebrow{align-items:center;gap:7px;margin-bottom:6px;display:flex}.sidebar-eyebrow-dot{border-radius:50%;width:8px;height:8px;box-shadow:0 0 7px}.sidebar-eyebrow-type{text-transform:uppercase;letter-spacing:.08em;color:var(--dim);font-size:11px}.sidebar-node-title{word-break:break-all;font-size:16px;font-weight:600}.sidebar-chips{flex-wrap:wrap;gap:6px;margin-top:9px;display:flex}.ins-chip{color:var(--cc);background:color-mix(in srgb, var(--cc) 14%, transparent);border:1px solid color-mix(in srgb, var(--cc) 40%, transparent);border-radius:999px;padding:2px 9px;font-size:11px}.ins-chip--neutral{color:var(--dim);background:var(--panel-2);border-color:var(--border)}.ins-actions{gap:6px;padding:14px 16px 0;display:flex}.ins-action-btn{background:var(--panel-2);border:1px solid var(--border);color:var(--text);font:inherit;cursor:pointer;border-radius:8px;flex:1;justify-content:center;align-items:center;gap:7px;padding:9px 0;font-size:12px;font-weight:500;transition:border-color .15s,background .15s,color .15s;display:flex}.ins-action-btn:hover:not(:disabled){border-color:var(--accent);background:var(--accent-soft)}.ins-action-btn:disabled{opacity:.4;cursor:default}.ins-action-icon{width:15px;height:15px;color:var(--dim);flex-shrink:0}.ins-action-btn:hover:not(:disabled) .ins-action-icon{color:var(--accent)}.ins-meters{flex-direction:column;gap:7px;padding:14px 16px;display:flex}.ins-meter{align-items:center;gap:9px;display:flex}.ins-meter-label{color:var(--dim);width:78px;font-size:11px}.ins-meter-track{background:var(--panel-2);border-radius:999px;flex:1;height:4px;overflow:hidden}.ins-meter-fill{border-radius:999px;height:100%;display:block}.ins-meter-value{color:var(--text);text-align:right;min-width:30px;font-size:11px}.sidebar-tab-badge--alert{background:var(--danger);color:#fff}.g-canvas.g-no-edge-labels .g-edge-label{display:none}.g-rails{pointer-events:none;z-index:4;flex-direction:column;gap:26px;display:flex;position:absolute;top:70px;left:14px}.g-rail{align-items:center;gap:8px;display:flex}.g-rail-pill{width:20px;height:20px;font-family:var(--mono);color:var(--rc);background:color-mix(in srgb, var(--rc) 14%, transparent);border:1px solid color-mix(in srgb, var(--rc) 40%, transparent);border-radius:6px;place-items:center;font-size:11px;font-weight:700;display:grid}.g-rail-label{text-transform:uppercase;letter-spacing:.12em;color:var(--faint);font-size:9px}.g-toolbar,.g-breadcrumb,.g-zoom{z-index:5;background:var(--frost);-webkit-backdrop-filter:blur(var(--frost-blur));border:1px solid var(--border);border-radius:9px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute}.g-toolbar{top:14px;left:50%;transform:translate(-50%)}.g-tool{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:5px 10px;font-size:11px}.g-tool:hover{color:var(--text)}.g-tool--on{background:var(--accent-soft);color:var(--text)}.g-tool-select{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:5px 8px;font-size:11px}.g-tool-select:hover{color:var(--text)}.g-tool-select option{background:var(--panel);color:var(--text)}.g-tool-sep{background:var(--border);width:1px;height:16px;margin:0 2px}.g-breadcrumb{gap:8px;padding:7px 11px;bottom:14px;left:14px}.g-crumb{color:var(--dim);align-items:center;gap:6px;font-size:10px;display:flex}.g-crumb-dot{border-radius:50%;width:7px;height:7px}.g-crumb-arrow{color:var(--faint);margin:0 1px}.g-zoom{bottom:14px;right:14px}.g-zoom-btn{color:var(--dim);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;width:26px;height:26px;font-size:13px}.g-zoom-btn:hover{background:var(--panel-2);color:var(--text)}.g-zoom-pct{font-family:var(--mono);color:var(--dim);text-align:center;min-width:42px;font-size:11px}.g-zoom-fit{font-size:12px}.g-node{transition:filter .15s}.g-node:hover{animation:1.1s ease-in-out infinite g-node-pulse}@keyframes g-node-pulse{0%,to{filter:drop-shadow(0 0 1px var(--accent-soft))}50%{filter:drop-shadow(0 0 7px var(--accent-glow))}}.section-count{color:var(--dim);margin-left:6px;font-weight:400}.schema-table{flex-direction:column;gap:2px;display:flex}.schema-row{border-radius:4px;grid-template-columns:minmax(0,1fr) minmax(0,1fr) 126px;align-items:baseline;gap:10px;padding:4px 6px;font-size:12px;display:grid}.schema-row:nth-child(odd){background:var(--panel-2)}.schema-row--flagged{background:color-mix(in srgb, var(--danger) 12%, transparent);box-shadow:inset 2px 0 0 var(--danger)}.schema-name{font-family:var(--mono);color:var(--text);overflow-wrap:anywhere}.schema-type{font-family:var(--mono);color:var(--dim);overflow-wrap:anywhere}.schema-flags{flex-wrap:wrap;place-content:flex-start flex-end;gap:4px;display:flex}.schema-flag{font-family:var(--mono);background:var(--panel);border:1px solid var(--border);color:var(--dim);white-space:nowrap;text-overflow:ellipsis;border-radius:3px;max-width:100%;padding:0 5px;font-size:10px;line-height:1.6;overflow:hidden}.schema-flag--muted{opacity:.7}.schema-flag--warn{color:var(--danger);border-color:color-mix(in srgb, var(--danger) 45%, transparent);background:color-mix(in srgb, var(--danger) 14%, transparent)}.sidebar-empty{color:var(--dim);padding:4px 6px;font-size:12px}.g-crumb--aside{opacity:.9}.g-crumb-sep{opacity:.45;margin-right:8px}.g-crumb-dot--dashed{border:1.5px dashed;border-color:inherit;background:0 0!important}
diff --git a/resources/assets/assets/index-X7dpiz5p.js b/resources/assets/assets/index-X7dpiz5p.js
new file mode 100644
index 00000000..e104cbf2
--- /dev/null
+++ b/resources/assets/assets/index-X7dpiz5p.js
@@ -0,0 +1,10 @@
+const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/vendor-utils-D7YtnGoz.js","assets/rolldown-runtime-BHe-jwch.js"])))=>i.map(i=>d[i]);
+import{r as e}from"./rolldown-runtime-BHe-jwch.js";import{_ as t,a as n,c as r,d as i,f as a,g as o,h as s,i as c,l,m as u,n as d,o as f,p,r as m,s as h,t as g,u as _,v}from"./vendor-react-CNktMmef.js";import{C as y,t as b}from"./vendor-CsjAK7B8.js";import{a as x,c as S,i as C,n as w,o as T,r as E,s as D,t as O}from"./vendor-d3-DThTr3c3.js";import{t as k}from"./vendor-utils-D7YtnGoz.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var A=e(v(),1),j=t();function M(){let[e,t]=(0,A.useState)(null),[n,r]=(0,A.useState)(!0),[i,a]=(0,A.useState)(null);return(0,A.useEffect)(()=>{fetch(`/_laravel-brain/.graph-manifest.json`).then(e=>{if(!e.ok)throw Error(`HTTP ${e.status}`);return e.json()}).then(e=>{t(e),r(!1)}).catch(e=>{a(e.message),r(!1)})},[]),{manifest:e,loading:n,error:i}}function N(e){return[...e.nodes.map(e=>({data:{id:e.id,label:e.label,type:e.type,...e.data,metrics_cc:e.data?.metrics?.cyclomaticComplexity??0}})),...e.edges.map(e=>({data:{id:e.id,source:e.source,target:e.target,label:e.label,type:e.type}}))]}function P(){let[e,t]=(0,A.useState)({data:null,loading:!1,error:null}),n=(0,A.useRef)(new Map),r=(0,A.useRef)(null),i=(0,A.useCallback)(e=>{if(r.current===e)return;r.current=e;let i=n.current.get(e);if(i){t({data:i,loading:!1,error:null});return}t(e=>({...e,loading:!0,error:null})),fetch(`/_laravel-brain/`+e).then(e=>{if(!e.ok)throw Error(`HTTP ${e.status}`);return e.json()}).then(i=>{n.current.set(e,i),r.current===e&&t({data:i,loading:!1,error:null})}).catch(n=>{r.current===e&&t({data:null,loading:!1,error:n.message})})},[]);return{state:e,elements:(0,A.useMemo)(()=>e.data?N(e.data):[],[e.data]),load:i}}function F(e,t=300){let[n,r]=(0,A.useState)(t),[i,a]=(0,A.useState)(e);return e!==i&&(a(e),r(t)),(0,A.useEffect)(()=>{if(n>=e.length)return;let t=window,i=(t.requestIdleCallback?t.requestIdleCallback.bind(t):e=>setTimeout(()=>e({didTimeout:!1,timeRemaining:()=>0}),100))(()=>{r(t=>Math.min(t+200,e.length))});return()=>{t.cancelIdleCallback?t.cancelIdleCallback(i):clearTimeout(i)}},[n,e.length]),(0,A.useMemo)(()=>e.length<=t?e:e.slice(0,n),[e,n,t])}function ee(){let[e,t]=(0,A.useState)(()=>localStorage.getItem(`lb-theme`)??`dark`);return(0,A.useEffect)(()=>{document.documentElement.setAttribute(`data-theme`,e),localStorage.setItem(`lb-theme`,e)},[e]),{theme:e,toggle:()=>t(e=>e===`dark`?`light`:`dark`)}}var te=e(k(),1),I={transaction:`#d99a2b`,rollback:`#c2554a`,chain:`#5f8fa8`,batch:`#8a7fb5`},ne={transaction:`6 5`,rollback:`2 4`,chain:`10 4`,batch:`4 4`},re={route:`#4CAF50`,middleware:`#FF9800`,controller:`#2196F3`,livewire_component:`#FB7185`,action:`#03A9F4`,service:`#9C27B0`,action_class:`#84cc16`,validation_request:`#0d9488`,model:`#F44336`,event:`#FFD600`,listener:`#C9A227`,job:`#607D8B`,command:`#14b8a6`,channel:`#8b5cf6`,schedule:`#f97316`,view:`#ec4899`,mail:`#f472b6`,notification:`#db2777`,enum:`#0ea5e9`,interface:`#38bdf8`,trait:`#a78bfa`,abstract_class:`#94a3b8`,service_provider:`#ca8a04`,facade:`#00BCD4`,ai_agent:`#A3E635`,ai_tool:`#65A30D`,filament_panel:`#7C3AED`,filament_resource:`#A855F7`,filament_page:`#C084FC`,filament_page_method:`#E879F9`,filament_widget:`#06B6D4`,filament_relation_manager:`#0891B2`,entry_point:`#22D3EE`,entry_point_group:`#0E7490`,unreached_class:`#94A3B8`,unreached_group:`#475569`},ie={route:`#2e7d32`,middleware:`#e65100`,controller:`#1565c0`,livewire_component:`#e11d48`,action:`#0277bd`,service:`#6a1b9a`,action_class:`#4d7c0f`,validation_request:`#0f766e`,model:`#c62828`,event:`#b45309`,listener:`#8a6d1f`,job:`#37474f`,command:`#0d7d6e`,channel:`#5b21b6`,schedule:`#c2410c`,view:`#be185d`,mail:`#c026d3`,notification:`#9d174d`,enum:`#0369a1`,interface:`#0c4a6e`,trait:`#5b21b6`,abstract_class:`#64748b`,service_provider:`#a16207`,facade:`#00838f`,ai_agent:`#4d7c0f`,ai_tool:`#3f6212`,filament_panel:`#5b21b6`,filament_resource:`#7e22ce`,filament_page:`#9333ea`,filament_page_method:`#a21caf`,filament_widget:`#0369a1`,filament_relation_manager:`#075985`,entry_point:`#0E7490`,entry_point_group:`#155E75`,unreached_class:`#475569`,unreached_group:`#334155`},L={route:`#0C1A0C`,middleware:`#1C1408`,controller:`#08141C`,livewire_component:`#1C0711`,action:`#07151D`,service:`#150C1C`,action_class:`#131C06`,validation_request:`#042f2e`,model:`#1C0C0C`,event:`#1C1A08`,listener:`#181405`,job:`#0D1113`,command:`#061514`,channel:`#110c1c`,schedule:`#1c1008`,view:`#1c0a14`,mail:`#1c0f18`,notification:`#1c0510`,enum:`#071318`,interface:`#081420`,trait:`#140822`,abstract_class:`#0f172a`,service_provider:`#422006`,facade:`#001F28`,ai_agent:`#131A08`,ai_tool:`#101705`,filament_panel:`#150C2A`,filament_resource:`#1A0C26`,filament_page:`#1E0F2E`,filament_page_method:`#240E30`,filament_widget:`#071A1E`,filament_relation_manager:`#06161A`,entry_point:`#04171C`,entry_point_group:`#03151A`,unreached_class:`#111827`,unreached_group:`#0B1120`},R={route:`#f0fdf4`,middleware:`#fff7ed`,controller:`#eff6ff`,livewire_component:`#fff1f2`,action:`#e0f7fa`,service:`#fdf4ff`,action_class:`#f7fee7`,validation_request:`#ccfbf1`,model:`#fff1f2`,event:`#fefce8`,listener:`#fdf6dd`,job:`#f1f5f9`,command:`#f0fdfa`,channel:`#f5f3ff`,schedule:`#fff7ed`,view:`#fdf2f8`,mail:`#fce7f3`,notification:`#fce7f3`,enum:`#f0f9ff`,interface:`#ecfeff`,trait:`#f5f3ff`,abstract_class:`#f1f5f9`,service_provider:`#fef9c3`,facade:`#e0f7fa`,ai_agent:`#f7fee7`,ai_tool:`#ecfccb`,filament_panel:`#f5f3ff`,filament_resource:`#faf5ff`,filament_page:`#fdf4ff`,filament_page_method:`#fef0ff`,filament_widget:`#ecfeff`,filament_relation_manager:`#e0f2fe`,entry_point:`#ecfeff`,entry_point_group:`#cffafe`,unreached_class:`#f8fafc`,unreached_group:`#f1f5f9`},ae=`#8B6FE8`,z={public:{bg:`#1c0808`,border:`#ef4444`,accent:`#f87171`,label:`Public`},guest:{bg:`#1c1408`,border:`#f59e0b`,accent:`#fbbf24`,label:`Guest`},authed:{bg:`#081c10`,border:`#10b981`,accent:`#34d399`,label:`Auth`},admin:{bg:`#110c1c`,border:`#8b5cf6`,accent:`#a78bfa`,label:`Admin`}},oe={public:{bg:`#fff1f2`,border:`#ef4444`,accent:`#dc2626`,label:`Public`},guest:{bg:`#fffbeb`,border:`#f59e0b`,accent:`#d97706`,label:`Guest`},authed:{bg:`#ecfdf5`,border:`#10b981`,accent:`#059669`,label:`Auth`},admin:{bg:`#f5f3ff`,border:`#8b5cf6`,accent:`#7c3aed`,label:`Admin`}},B={none:`#6b7280`,low:`#10b981`,medium:`#f59e0b`,high:`#f97316`,critical:`#ef4444`},se={none:`No Issues`,low:`Low`,medium:`Medium`,high:`High`,critical:`Critical`},ce={MISSING_FK_INDEX:{icon:`🔑`,name:`Unindexed foreign key`},MASS_ASSIGNMENT:{icon:`⚡`,name:`Mass Assignment`},UNVALIDATED_INPUT:{icon:`⚠️`,name:`Unvalidated Input`},MISSING_THROTTLE:{icon:`🔓`,name:`Missing Throttle`},PUBLIC_WRITE:{icon:`🌐`,name:`Public Write`},XSS_DIRECT_OUTPUT:{icon:`💉`,name:`XSS — Direct Output`},XSS_HTML_DECODE:{icon:`🔓`,name:`XSS — HTML Decode`},XSS_BLADE_UNESCAPED:{icon:`📄`,name:`XSS — Blade {!! !!}`},SQL_INJECTION:{icon:`🛢️`,name:`SQL Injection`},OPEN_REDIRECT:{icon:`↪️`,name:`Open Redirect`},SSRF:{icon:`🌐`,name:`SSRF`},DEBUG_CODE:{icon:`🐞`,name:`Debug Code Leak`},ENV_LEAK:{icon:`🔑`,name:`Env Leak`},CSRF_BYPASS:{icon:`🛡️`,name:`CSRF Bypass`},INSECURE_COOKIE:{icon:`🍪`,name:`Insecure Cookie`},UNSAFE_STORAGE_PATH:{icon:`📁`,name:`Unsafe Storage Path`},FILE_UPLOAD_VALIDATION:{icon:`📎`,name:`File Upload Validation`},UNSAFE_AUTH:{icon:`🚪`,name:`Unsafe Auth`},UNSAFE_CRYPT:{icon:`🔐`,name:`Unsafe Crypt`},ARTISAN_CALL:{icon:`⚙️`,name:`Tainted Artisan Call`},PROCESS_SHELL:{icon:`💻`,name:`Shell Injection`},CONFIG_INJECTION:{icon:`🧩`,name:`Config Injection`},TAINTED_VIEW_NAME:{icon:`🖼️`,name:`Tainted View Name`},SESSION_FIXATION:{icon:`🎫`,name:`Session Fixation`},MAIL_TAINTED_HEADER:{icon:`✉️`,name:`Mail Header Injection`}},le=[{label:`Low`,min:1,max:5,fill:`#0d2e1a`,border:`#4ade80`},{label:`Moderate`,min:6,max:10,fill:`#2e2200`,border:`#facc15`},{label:`High`,min:11,max:15,fill:`#2e1200`,border:`#fb923c`},{label:`Critical`,min:16,max:1/0,fill:`#2e0a0a`,border:`#f87171`}],V=[{label:`Low`,min:1,max:5,fill:`#f0fdf4`,border:`#16a34a`},{label:`Moderate`,min:6,max:10,fill:`#fefce8`,border:`#ca8a04`},{label:`High`,min:11,max:15,fill:`#fff7ed`,border:`#ea580c`},{label:`Critical`,min:16,max:1/0,fill:`#fff1f2`,border:`#dc2626`}],ue=[`chain`],de={transaction:`transaction`,rollback:`rollback`,chain:`chain`,batch:`batch`},fe={transaction:`transactions`,rollback:`rollbacks`,chain:`chains`,batch:`batches`},pe=[`transaction`,`rollback`,`chain`,`batch`];function me(e){let t=e.width/2,n=e.height/2;return[[e.x-t,e.y-n],[e.x+t,e.y-n],[e.x+t,e.y+n],[e.x-t,e.y+n]]}function H(e){if(e.length<3)return e;let t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]),n=(e,t,n)=>(t[0]-e[0])*(n[1]-e[1])-(t[1]-e[1])*(n[0]-e[0]),r=e=>{let t=[];for(let r of e){for(;t.length>=2&&n(t[t.length-2],t[t.length-1],r)<=0;)t.pop();t.push(r)}return t.pop(),t};return[...r(t),...r([...t].reverse())]}function he(e,t){if(e.length===0)return e;let n=e.reduce((e,t)=>e+t[0],0)/e.length,r=e.reduce((e,t)=>e+t[1],0)/e.length;return e.map(([e,i])=>{let a=e-n,o=i-r,s=Math.hypot(a,o)||1;return[e+a/s*t,i+o/s*t]})}function U(e,t,n){let r=!1;for(let i=0,a=e.length-1;in!=l>n&&t<(c-o)*(n-s)/(l-s)+o&&(r=!r)}return r}var W=new Set([`transaction`,`rollback`,`chain`,`batch`]);function ge(e,t,n=4){let r=t.x-e.x,i=t.y-e.y;if(r===0&&i===0)return null;let a=e=>{let t=r===0?1/0:(e.width/2+n)/Math.abs(r),a=i===0?1/0:(e.height/2+n)/Math.abs(i);return Math.min(t,a)},o=a(e),s=1-a(t);return o>=s?null:{x1:e.x+r*o,y1:e.y+i*o,x2:e.x+r*s,y2:e.y+i*s}}function G(e){let t=e.data?.regions;if(!Array.isArray(t))return[];let n=[];for(let e of t){let t=e?.id,r=e?.kind;typeof t!=`string`||t===``||typeof r!=`string`||!W.has(r)||n.push({id:t,kind:r,position:typeof e.position==`number`?e.position:null})}return n}function _e(e,t=22){let n=new Map;for(let t of e)for(let e of G(t)){let r=n.get(e.id)??{kind:e.kind,members:[]};r.members.push({node:t,position:e.position}),n.set(e.id,r)}let r=[],i=new Map,a=new Map;for(let e of[...n.keys()].sort()){let t=n.get(e).kind,r=(a.get(t)??0)+1;a.set(t,r),i.set(e,r)}for(let[a,o]of n){let n=ue.includes(o.kind),s=(n?[...o.members].sort((e,t)=>(e.position??0)-(t.position??0)):o.members).map(e=>e.node),c=he(H(s.flatMap(me)),t);if(c.length<3)continue;let l=new Set(s.map(e=>e.id)),u=!e.some(e=>!l.has(e.id)&&me(e).some(([e,t])=>U(c,e,t)));r.push({id:a,kind:o.kind,index:i.get(a)??1,points:c,members:s,ordered:n,pure:u})}return r}var K=e(y(),1);function ve(e,t){let n=e.indexOf(`@`),r=e.indexOf(`::`);return n===-1?r===-1?{className:e,method:t??``}:{className:e.slice(0,r),method:e.slice(r+2)}:{className:e.slice(0,n),method:t??e.slice(n+1)}}function q(e,t=!1){let{className:n,method:r}=ve(String(e.label??e.id),e.method),i=t||n.length>r.length?n:r,a=Math.max(t?120:185,Math.min(270,i.length*7.6+44)),o=t?40:90;return{id:e.id,x:0,y:0,width:a,height:o,lines:[n,r].filter(Boolean),data:e}}function ye(e){if(!e.length)return;let t=0,n=0;for(let r of e)t+=r.x,n+=r.y;let r=t/e.length,i=n/e.length;for(let t of e)t.x-=r,t.y-=i}function be(e,t,n){let r=new K.default.graphlib.Graph({compound:!0});r.setGraph({rankdir:n,nodesep:n===`TB`?70:50,ranksep:n===`TB`?100:120,marginx:60,marginy:60}),r.setDefaultEdgeLabel(()=>({}));for(let t of e)r.setNode(t.id,{width:t.width,height:t.height});for(let[t,n]of Y(e)){r.setNode(t,{});for(let e of n)r.setParent(e.id,t)}for(let e of t)r.hasNode(e.source)&&r.hasNode(e.target)&&r.setEdge(e.source,e.target);K.default.layout(r);for(let t of e){let e=r.node(t.id);e&&(t.x=e.x,t.y=e.y)}}function J(e){let t=G(e);return t.length===0?null:(t.find(e=>ue.includes(e.kind))??t[0]).id}function Y(e){let t=new Map;for(let n of e){let e=J(n);e!==null&&t.set(e,[...t.get(e)??[],n])}for(let[e,n]of t)n.length<2&&t.delete(e);return new Map([...t].map(([e,t])=>[`cluster::${e}`,t]))}function xe(e,t,n,r=60,i=110){let a=new Set(e.map(e=>e.id)),o=new Map,s=new Map;for(let t of e)o.set(t.id,[]),s.set(t.id,0);for(let e of t)!a.has(e.source)||!a.has(e.target)||(o.get(e.source).push(e.target),s.set(e.target,(s.get(e.target)??0)+1));let c=e.filter(e=>s.get(e.id)===0).map(e=>e.id),l=new Map,u=[...c];for(let e of c)l.set(e,0);let d=0,f=()=>{for(;d0)for(let[,e]of p){let t=new Map,n=0;for(let r of e){let e=m.get(r)??`\u0000${r}`;t.has(e)||t.set(e,n++)}e.sort((e,n)=>t.get(m.get(e)??`\u0000${e}`)-t.get(m.get(n)??`\u0000${n}`))}for(let e of p.values())e.sort();let h=new Map(e.map(e=>[e.id,e])),g=0;for(let e of[...p.keys()].sort((e,t)=>e-t)){let t=p.get(e).map(e=>h.get(e)),a=Se(t.length);if(n===`TB`){let e=Ce(t,a),n=g;for(let t of e){let e=t.reduce((e,t)=>e+t.width,0)+r*(t.length-1),i=Te(t,e=>e.height),a=-e/2;for(let e of t)e.x=a+e.width/2,e.y=n+i/2,a+=e.width+r;n+=i+r}g=n-r+i}else{let e=Ce(t,a),n=g;for(let t of e){let e=t.reduce((e,t)=>e+t.height,0)+r*(t.length-1),i=Te(t,e=>e.width),a=-e/2;for(let e of t)e.x=n+i/2,e.y=a+e.height/2,a+=e.height+r;n+=i+r}g=n-r+i}}}function Se(e,t=12){return e<=t?e:Math.ceil(Math.sqrt(e)*1.4)}function Ce(e,t){if(t>=e.length)return[e];let n=[];for(let r=0;rObject.assign({},e)),r=new Map(n.map(e=>[e.id,e])),i=t.filter(e=>r.has(e.source)&&r.has(e.target)).map(e=>({source:e.source,target:e.target})),a=C(n).force(`link`,x(i).id(e=>e.id).distance(90)).force(`charge`,E().strength(-420)).force(`center`,D(0,0)).force(`collide`,T().radius(e=>Math.hypot(e.width,e.height)/2+14));a.stop();for(let e=0;e<450&&a.alpha()>.02;e++)a.tick();for(let t of e){let e=r.get(t.id);e&&(t.x=e.x??0,t.y=e.y??0)}}function Te(e,t){return e.reduce((e,n)=>Math.max(e,t(n)),-1/0)}function Ee(e,t=40){let n=e.length;if(!n)return;let r=Te(e,e=>Math.max(e.width,e.height))+t,i=Math.max(r,n*r/(2*Math.PI));e.forEach((e,t)=>{let r=t/n*Math.PI*2-Math.PI/2;e.x=i*Math.cos(r),e.y=i*Math.sin(r)})}function De(e,t=60,n=60){if(!e.length)return;let r=Te(e,e=>e.width)+t,i=Te(e,e=>e.height)+n,a=Math.ceil(Math.sqrt(e.length));e.forEach((e,t)=>{e.x=t%a*r,e.y=Math.floor(t/a)*i})}function Oe(e,t,n){return e===`dagre`&&t>n?`breadthfirst`:e===`dagre`?`dagre`:e===`cose-bilkent`?`force`:e===`breadthfirst`?`breadthfirst`:e===`circle`?`circle`:e===`grid`?`grid`:`dagre`}function ke(e,t=!1){let n=[],r=[];for(let i of e){let e=i.data;e.source!=null&&e.target!=null?r.push({id:e.id,source:String(e.source),target:String(e.target),data:e}):n.push(q(e,t))}return{nodes:n,edges:r}}var X=o();function Ae(e){return Math.max(0,Math.min(255,Math.round(e))).toString(16).padStart(2,`0`)}function je(e,t,n){return{x:n.applyX(e),y:n.applyY(t)}}function Me(e,t){if(t.length===0)return{x:0,y:0};if(t.length===1||e<=0)return t[0];if(e>=1)return t[t.length-1];let n=0,r=[];for(let e=0;e=i||e===r.length-1){let r=(i-a)/n,o=t[e],s=t[e+1];return{x:o.x+(s.x-o.x)*r,y:o.y+(s.y-o.y)*r}}a+=n}}return t[t.length-1]}function Ne(e,t){let n=t.x-e.x,r=t.y-e.y,i=Math.abs(n)-(e.width+t.width)/2;return Math.abs(r)-(e.height+t.height)/2>=i?r>=0?{ex:e.x,ey:e.y+e.height/2,tx:t.x,ty:t.y-t.height/2,vertical:!0}:{ex:e.x,ey:e.y-e.height/2,tx:t.x,ty:t.y+t.height/2,vertical:!0}:n>=0?{ex:e.x+e.width/2,ey:e.y,tx:t.x-t.width/2,ty:t.y,vertical:!1}:{ex:e.x-e.width/2,ey:e.y,tx:t.x+t.width/2,ty:t.y,vertical:!1}}function Pe(e,t){let{ex:n,ey:r,tx:i,ty:a,vertical:o}=Ne(e,t);if(o){if(Math.abs(n-i)<3||Math.abs(a-r)<=8)return[{x:n,y:r},{x:i,y:a}];let e=(r+a)/2;return[{x:n,y:r},{x:n,y:e},{x:i,y:e},{x:i,y:a}]}else{if(Math.abs(r-a)<3||Math.abs(i-n)<=8)return[{x:n,y:r},{x:i,y:a}];let e=(n+i)/2;return[{x:n,y:r},{x:e,y:r},{x:e,y:a},{x:i,y:a}]}}var Fe=7;function Ie(...e){return Math.max(0,Math.min(Fe,...e.map(e=>e-1)))}function Le(e,t){let{ex:n,ey:r,tx:i,ty:a,vertical:o}=Ne(e,t);if(o){if(Math.abs(n-i)<3||Math.abs(a-r)<=8)return{d:`M${n},${r} L${i},${a}`,lx:n+6,ly:(r+a)/2,exitX:n,exitY:r,entryX:i,entryY:a};let e=(r+a)/2,t=a>r?1:-1,o=Ie(Math.abs(e-r),Math.abs(a-e),Math.abs(i-n)),s=i>n?o:-o;return{d:o>0?`M${n},${r} V${e-o*t} Q${n},${e} ${n+s},${e} H${i-s} Q${i},${e} ${i},${e+o*t} V${a}`:`M${n},${r} V${e} H${i} V${a}`,lx:(n+i)/2,ly:e-14*t,exitX:n,exitY:r,entryX:i,entryY:a}}else{if(Math.abs(r-a)<3||Math.abs(i-n)<=8)return{d:`M${n},${r} L${i},${a}`,lx:(n+i)/2,ly:r-10,exitX:n,exitY:r,entryX:i,entryY:a};let e=(n+i)/2,t=i>n?1:-1,o=Ie(Math.abs(e-n),Math.abs(i-e),Math.abs(a-r)),s=a>r?o:-o;return{d:o>0?`M${n},${r} H${e-o*t} Q${e},${r} ${e},${r+s} V${a-s} Q${e},${a} ${e+o*t},${a} H${i}`:`M${n},${r} H${e} V${a} H${i}`,lx:e+6*t,ly:(r+a)/2,exitX:n,exitY:r,entryX:i,entryY:a}}}function Re(e,t){let n=String(e.label??``);return n?{text:n,fill:t?`rgba(255,255,255,0.4)`:`rgba(0,0,0,0.5)`,bg:t?`#111218`:`#fff`}:null}function ze(e,t,n,r,i,a){let o=String(e.data.type??``),s=t?re[o]??`#c9d1d9`:ie[o]??`#333`,c=t?L[o]??`#0d1117`:R[o]??`#ffffff`,l=Number(e.data.metrics_cc??0)||0;if(n){let n=t?le:V,r=n.find(e=>l>=e.min&&l<=e.max)??n[0],a=i?`#a855f7`:e.data.hasN1?`#F44336`:r.border;return{bg:r.fill,border:a,borderW:1.5,accent:r.border}}if(a&&o===`route`){let n=e.data.security;if(n){let e=t?z:oe,a=e[n.exposure]??e.public,o=B[n.riskLevel]??B.none,c=r?s:i?`#a855f7`:n.riskLevel===`none`?a.border:o;return{bg:a.bg,border:c,borderW:r||n.riskLevel!==`none`?2:1.5,accent:a.accent}}}let u=t?`rgba(255,255,255,0.1)`:`rgba(0,0,0,0.12)`,d=1;return e.data.hasN1&&(u=`#F44336`,d=2),r&&(u=s,d=2),i&&(u=`#a855f7`,d=2),{bg:c,border:u,borderW:d,accent:s}}function Be(e){return new Set(e.filter(e=>e.data?.collapsedByDefault===!0).map(e=>e.id))}function Ve(e){if(e.ctrlKey)return!1;if(e.deltaX!==0)return!0;if(e.deltaMode!==0)return!1;let t=e.wheelDeltaY;return typeof t==`number`&&t!==0?Math.abs(t+3*e.deltaY)<=2?!0:!(Math.abs(t)%120==0&&Math.abs(e.deltaY)>=100):!(Number.isInteger(e.deltaY)&&Math.abs(e.deltaY)>=100)}function He({elements:e,layout:t,rankDir:n,searchQuery:r,visibleTypes:i,theme:a,onNodeSelect:o,graphRef:s,stressTestNodeId:c,stressRunKey:l,complexityOverlay:u,securityOverlay:d=!1,compact:f=!1,onLayoutChange:p,onRankDirChange:m,onToggleComplexityOverlay:h,onToggleSecurityOverlay:g,onToggleCompact:_}){let v=a===`dark`,y=v?`rgba(255,255,255,0.32)`:`rgba(0,0,0,0.38)`,b=v?`rgba(255,255,255,0.55)`:`rgba(0,0,0,0.55)`,{nodes:x,edges:C}=(0,A.useMemo)(()=>ke(e,f),[e,f]),T=(0,A.useMemo)(()=>x.filter(e=>i.has(String(e.data.type))).length,[x,i]),[E,D]=(0,A.useState)(0),k=(0,A.useRef)(null),j=(0,A.useRef)(!0);(0,A.useEffect)(()=>{if(j.current){j.current=!1;return}return k.current&&window.clearTimeout(k.current),k.current=window.setTimeout(()=>{D(e=>e+1)},200),()=>{k.current&&window.clearTimeout(k.current)}},[i,t,n,f]);let{nodes:M,edges:N}=(0,A.useMemo)(()=>{let e=x.map(e=>({...e,lines:[...e.lines]})),r=C.map(e=>({...e})),i=Oe(t,T,80);return i===`dagre`?be(e,r,n):i===`breadthfirst`?xe(e,r,n):i===`force`?we(e,r):i===`circle`?Ee(e):De(e),ye(e),{nodes:e,edges:r}},[x,C,t,n,E,T]),P=(0,A.useMemo)(()=>new Map(M.map(e=>[e.id,e])),[M]),[F,ee]=(0,A.useState)(new Map),ie=(0,A.useRef)(null),L=(0,A.useRef)(!1),[R,oe]=(0,A.useState)(()=>Be(M)),[se,ce]=(0,A.useState)(M);se!==M&&(ce(M),ee(new Map),oe(Be(M)));let V=(0,A.useMemo)(()=>F.size===0?M:M.map(e=>{let t=F.get(e.id);return t?{...e,x:t.x,y:t.y}:e}),[M,F]),ue=(0,A.useMemo)(()=>_e(V),[V]),me=(0,A.useCallback)(e=>i.has(e===`rollback`?`transaction`:e),[i]),H=(0,A.useMemo)(()=>ue.filter(e=>me(e.kind)),[ue,me]),he=(0,A.useMemo)(()=>{let e=new Map,t=[],n=(n,r,i)=>{let a=0;for(;t.some(e=>Math.abs(e.x-r)<140&&Math.abs(e.y-(i-a*11))<10);)a++;t.push({x:r,y:i-a*11}),e.set(n,a)};for(let e of H){if(e.pure){n(e.id,Math.min(...e.points.map(([e])=>e)),Math.min(...e.points.map(([,e])=>e)));continue}for(let t of e.members)n(`${e.id}|${t.id}`,t.x-t.width/2,t.y-t.height/2)}return e},[H]),U=(0,A.useMemo)(()=>new Map(V.map(e=>[e.id,e])),[V]),W=(0,A.useRef)(U);(0,A.useEffect)(()=>{W.current=U},[U]);let G=(0,A.useCallback)(e=>i.has(String(e)),[i]),K=(0,A.useCallback)(e=>G(P.get(e.source)?.data.type)&&G(P.get(e.target)?.data.type),[P,G]),q=(0,A.useMemo)(()=>{let e=new Map;for(let t of M)e.set(t.id,[]);for(let t of N)K(t)&&e.get(t.source)?.push(t.target);let t=new Set;for(let n of R){let r=[n],i=new Set([n]);for(;r.length;){let n=r.shift();for(let a of e.get(n)??[])i.has(a)||(i.add(a),t.add(a),r.push(a))}}return t},[M,N,K,R]),J=(0,A.useMemo)(()=>{let e=new Map;for(let t of N)K(t)&&(q.has(t.target)||e.set(t.source,(e.get(t.source)??0)+1));return e},[N,K,q]),Y=(0,A.useCallback)((e,t)=>{e.stopPropagation(),oe(e=>{let n=new Set(e);return n.has(t)?n.delete(t):n.add(t),n})},[]),Se=(0,A.useMemo)(()=>{let e=new Map;for(let t of R){let n=0,r=new Set,i=[t];for(;i.length;){let e=i.shift();for(let t of N){if(t.source!==e||!K(t))continue;let a=t.target;r.has(a)||(r.add(a),q.has(a)&&(n++,i.push(a)))}}e.set(t,n)}return e},[R,q,N,K]),Ce=(0,A.useMemo)(()=>{if(!r.trim())return null;let e=r.toLowerCase(),t=new Set;for(let n of M)String(n.data.label??n.id).toLowerCase().includes(e)&&t.add(n.id);return t},[M,r]),Te=(0,A.useMemo)(()=>{let e=new Set,t=new Set;if(!c||!P.has(c))return{nodes:e,edges:t};let n=new Set,r=new Set,i=new Set,a=[c];for(;a.length;){let e=a.shift();if(!i.has(e)){i.add(e),n.add(e);for(let t of N){if(t.source!==e||!K(t))continue;r.add(t.id);let n=t.target;i.has(n)||a.push(n)}}}return{nodes:n,edges:r}},[c,l,N,K,P]),[Ne,Fe]=(0,A.useState)(new Set),[Ie,He]=(0,A.useState)(null),Ue=(0,A.useCallback)(e=>{let t=new Set;for(let n of N)(n.source===e||n.target===e)&&t.add(n.id);Fe(t),He(e),o(e)},[N,o]),We=(0,A.useCallback)(()=>{Fe(new Set),He(null),o(null)},[o]),Ge=(0,A.useCallback)((e,t,n,r)=>{e.stopPropagation(),e.currentTarget.setPointerCapture(e.pointerId),L.current=!1,ie.current={nodeId:t,startSX:e.clientX,startSY:e.clientY,origMX:n,origMY:r}},[]),Ke=(0,A.useCallback)((e,t)=>{let n=ie.current;if(!n||n.nodeId!==t)return;let r=e.clientX-n.startSX,i=e.clientY-n.startSY;if(!L.current&&Math.abs(r)<4&&Math.abs(i)<4)return;L.current=!0;let a=nt.current.k;ee(e=>{let o=new Map(e);return o.set(t,{x:n.origMX+r/a,y:n.origMY+i/a}),o})},[]),qe=(0,A.useCallback)((e,t)=>{ie.current?.nodeId===t&&(ie.current=null)},[]),Je=(0,A.useRef)(null),Ye=(0,A.useRef)(null),Xe=(0,A.useRef)(null),Ze=(0,A.useRef)(null),Qe=(0,A.useRef)([]),$e=(0,A.useRef)([]),et=(0,A.useRef)(0),tt=(0,A.useRef)(new Map),nt=(0,A.useRef)(w),rt=(0,A.useRef)(null),[it,at]=(0,A.useState)(100),[ot,st]=(0,A.useState)(!0),ct=(0,A.useCallback)((e,t,n=0,r=!1)=>{let i=N.find(t=>t.id===e);if(!i||!K(i))return;let a=W.current.get(i.source),o=W.current.get(i.target);if(!a||!o)return;let s=Pe(a,o),c=r&&Math.random()<.65?.15+Math.random()*.55:0,l=c>0?120+Math.random()*700:0,u=r&&Math.random()<.12,d=u?.25+Math.random()*.55:0;setTimeout(()=>{Qe.current.push({id:`${e}-${Date.now()}-${Math.random()}`,waypoints:s,progress:0,speed:9e-4+Math.random()*4e-4,color:t,pulse:0,sparkCooldown:0,tgtNodeId:i.target,chained:r,arrived:!1,stallAt:c,stallRemaining:l,timedOut:u,timeoutAt:d})},n)},[N,K]),lt=(0,A.useCallback)((e,t,n=0)=>{let r=Date.now();if(r-(tt.current.get(e)??0)<1800)return;tt.current.set(e,r);let i=0;for(let r of N)r.source===e&&K(r)&&(ct(r.id,t,n+i*60,!0),i++)},[N,K,ct]);(0,A.useEffect)(()=>{if(!c||!P.has(c))return;let e=()=>{let e=0;for(let t of N)t.source===c&&K(t)&&(ct(t.id,`#a855f7`,e*80,!0),e++)};e();let t=window.setInterval(e,700);return()=>window.clearInterval(t)},[c,l,N,K,P,ct]),(0,A.useEffect)(()=>{let e;function t(n){e=requestAnimationFrame(t);let r=Ze.current;if(!r)return;let i=Math.min(n-et.current,50);et.current=n;let a=r.getContext(`2d`);if(!a)return;a.clearRect(0,0,r.width,r.height);let o=nt.current,s=Math.max(.6,o.k);a.globalCompositeOperation=`lighter`;let l=[],u=M.length<=40||c,d=Qe.current.filter(e=>e.progress<1).length,f=Math.max(.12,1-Math.max(0,d-4)*.055);for(let e of Qe.current){if(!u)continue;if(e.timedOut&&e.timeoutAt>0&&e.progress>=e.timeoutAt){let t=e.waypoints.map(e=>je(e.x,e.y,o)),n=Me(e.timeoutAt,t);for(let e=0;e<18;e++){let t=e/18*Math.PI*2+Math.random()*.4,r=.06+Math.random()*.14;$e.current.push({x:n.x,y:n.y,vx:Math.cos(t)*r,vy:Math.sin(t)*r,life:1,decay:.0014+Math.random()*.001,size:(1.4+Math.random()*2)*s,color:`#ef4444`})}continue}let t=e.stallAt>0&&e.progress>=e.stallAt&&e.stallRemaining>0;t?e.stallRemaining-=i:e.progress<1&&(e.progress=Math.min(1,e.progress+e.speed*f*i));let r=e.waypoints.map(e=>je(e.x,e.y,o)),c=r[r.length-1],d=Me(e.progress,r);if(!isFinite(d.x)||!isFinite(d.y)){l.push(e);continue}let p=e.stallAt>0&&e.stallRemaining>0?Math.min(1,e.stallRemaining/400):0,m=t?p>.5?`#f59e0b`:`#fb923c`:e.color;for(let t=18;t>=1;t--){let n=e.progress-t/18*.09;if(n<0)continue;let i=Me(n,r),o=1-t/18,c=o*o*.55,l=(.8+o*2.6)*s;a.beginPath(),a.arc(i.x,i.y,l,0,Math.PI*2),a.fillStyle=m+Ae(c*255),a.fill()}a.save(),a.shadowBlur=(t?34:24)*s,a.shadowColor=m,a.beginPath(),a.arc(d.x,d.y,5*s,0,Math.PI*2),a.fillStyle=m+`66`,a.fill(),a.restore();let h=a.createRadialGradient(d.x,d.y,0,d.x,d.y,8*s);if(h.addColorStop(0,`#ffffffee`),h.addColorStop(.35,m+`cc`),h.addColorStop(1,m+`00`),a.fillStyle=h,a.beginPath(),a.arc(d.x,d.y,8*s,0,Math.PI*2),a.fill(),t){let e=.5+.5*Math.sin(n*.012);a.beginPath(),a.arc(d.x,d.y,(10+e*8)*s,0,Math.PI*2),a.strokeStyle=`#f59e0b`+Ae(e*160),a.lineWidth=1.5*s,a.stroke()}let g=1+.18*Math.sin(n*.018+e.progress*12);if(a.beginPath(),a.arc(d.x,d.y,2.2*s*g,0,Math.PI*2),a.fillStyle=`#ffffff`,a.fill(),e.progress<1&&(e.sparkCooldown-=i,e.sparkCooldown<=0)){e.sparkCooldown=35+Math.random()*40;let t=Math.random()*Math.PI*2,n=.02+Math.random()*.04;$e.current.push({x:d.x,y:d.y,vx:Math.cos(t)*n,vy:Math.sin(t)*n,life:1,decay:.0028+Math.random()*.0012,size:(.8+Math.random()*1.4)*s,color:m})}if(e.progress>=1){if(!e.arrived){e.arrived=!0;for(let t=0;t<14;t++){let n=t/14*Math.PI*2+Math.random()*.3,r=.08+Math.random()*.12;$e.current.push({x:c.x,y:c.y,vx:Math.cos(n)*r,vy:Math.sin(n)*r,life:1,decay:.0018+Math.random()*8e-4,size:(1.2+Math.random()*1.6)*s,color:e.color})}if(e.chained){let t=P.get(e.tgtNodeId),n=t&&re[String(t.data.type)]||e.color;lt(e.tgtNodeId,n,120)}}if(e.pulse=Math.min(1,e.pulse+.025),e.pulse<1){for(let t=0;t<3;t++){let n=e.pulse-t*.18;if(n<=0||n>=1)continue;let r=(3+n*38)*s,i=(1-n)*(1-n)*220;a.beginPath(),a.arc(c.x,c.y,r,0,Math.PI*2),a.strokeStyle=e.color+Ae(i),a.lineWidth=1.5*s,a.stroke()}let t=(1-e.pulse)*(1-e.pulse)*255;a.save(),a.shadowBlur=18*s,a.shadowColor=e.color,a.beginPath(),a.arc(c.x,c.y,4*s,0,Math.PI*2),a.fillStyle=`#ffffff`+Ae(t),a.fill(),a.restore(),l.push(e)}}else l.push(e)}let p=[];for(let e of $e.current){if(e.x+=e.vx*i,e.y+=e.vy*i,e.vx*=.985,e.vy*=.985,e.life-=e.decay*i,e.life<=0)continue;let t=Math.max(.3,e.size*e.life);a.beginPath(),a.arc(e.x,e.y,t,0,Math.PI*2),a.fillStyle=e.color+Ae(e.life*220),a.fill(),p.push(e)}$e.current=p,a.globalCompositeOperation=`source-over`,Qe.current=l}return et.current=performance.now(),e=requestAnimationFrame(t),()=>cancelAnimationFrame(e)},[P,lt,M.length,c]),(0,A.useEffect)(()=>{M.length>40&&!c&&(Qe.current=[],$e.current=[])},[M.length,c]),(0,A.useEffect)(()=>{let e=Je.current,t=Ze.current;if(!e||!t)return;let n=new ResizeObserver(()=>{t.width=e.clientWidth,t.height=e.clientHeight});return n.observe(e),t.width=e.clientWidth,t.height=e.clientHeight,()=>n.disconnect()},[]),(0,A.useEffect)(()=>{let e=Ye.current,t=Xe.current;if(!e||!t)return;let n=O().scaleExtent([.02,5]).filter(e=>!ie.current&&(!e.ctrlKey||e.type===`wheel`)&&!(e.type===`wheel`&&Ve(e))&&!e.button).on(`zoom`,e=>{nt.current=e.transform,S(t).attr(`transform`,e.transform.toString()),at(Math.round(e.transform.k*100))});S(e).call(n),rt.current=n;let r=t=>{if(!Ve(t))return;t.preventDefault();let r=nt.current.k;S(e).call(n.translateBy,-t.deltaX/r,-t.deltaY/r)};return e.addEventListener(`wheel`,r,{passive:!1}),()=>{S(e).on(`.zoom`,null),e.removeEventListener(`wheel`,r)}},[]);let ut=(0,A.useCallback)(()=>{let e=Ye.current,t=Je.current,n=rt.current;if(!e||!t||!n||!M.length)return;let r=M.filter(e=>!q.has(e.id)),i=r.length?r:M,a=1/0,o=1/0,s=-1/0,c=-1/0;for(let e of i)a=Math.min(a,e.x-e.width/2),s=Math.max(s,e.x+e.width/2),o=Math.min(o,e.y-e.height/2),c=Math.max(c,e.y+e.height/2);let l=s-a+96,u=c-o+96,d=t.clientWidth,f=t.clientHeight,p=Math.min(d/l,f/u,2)*.92,m=(a+s)/2,h=(o+c)/2,g=d/2-p*m,_=f/2-p*h,v=w.translate(g,_).scale(p);S(e).call(n.transform,v)},[M,q]),dt=(0,A.useCallback)(e=>{let t=Ye.current,n=rt.current;!t||!n||S(t).transition().duration(150).call(n.scaleBy,e)},[]),ft=(0,A.useCallback)(async e=>{let t=Je.current;return t?(await(0,te.default)(t,{scale:e?.scale??2,useCORS:!0,backgroundColor:v?`#0a0c10`:`#f6f7f9`,ignoreElements:e=>e.classList?.contains(`g-rails`)||e.classList?.contains(`g-toolbar`)||e.classList?.contains(`g-breadcrumb`)||e.classList?.contains(`g-zoom`)})).toDataURL(`image/png`):null},[v]);(0,A.useEffect)(()=>(s.current={fit:ut,toPng:ft},()=>{s.current=null}),[s,ut,ft]);let pt=(0,A.useRef)(!1);return(0,A.useEffect)(()=>{pt.current=!1},[e]),(0,A.useEffect)(()=>{if(!M.length||pt.current)return;pt.current=!0;let e=requestAnimationFrame(()=>ut());return()=>cancelAnimationFrame(e)},[M.length,ut,e]),(0,X.jsxs)(`div`,{ref:Je,className:`g-canvas ${ot?``:`g-no-edge-labels`}`,style:{position:`relative`,width:`100%`,height:`100%`},children:[(0,X.jsxs)(`svg`,{ref:Ye,role:`img`,"aria-label":`Execution graph`,style:{width:`100%`,height:`100%`,display:`block`,cursor:`grab`,touchAction:`none`},children:[(0,X.jsxs)(`defs`,{children:[(0,X.jsx)(`marker`,{id:`arrow-def`,markerWidth:`9`,markerHeight:`9`,refX:`8`,refY:`4.5`,orient:`auto`,markerUnits:`strokeWidth`,children:(0,X.jsx)(`path`,{d:`M0,0 L0,9 L9,4.5 z`,fill:b})}),(0,X.jsx)(`marker`,{id:`arrow-hi`,markerWidth:`9`,markerHeight:`9`,refX:`8`,refY:`4.5`,orient:`auto`,markerUnits:`strokeWidth`,children:(0,X.jsx)(`path`,{d:`M0,0 L0,9 L9,4.5 z`,fill:ae})}),(0,X.jsx)(`marker`,{id:`arrow-st`,markerWidth:`9`,markerHeight:`9`,refX:`8`,refY:`4.5`,orient:`auto`,markerUnits:`strokeWidth`,children:(0,X.jsx)(`path`,{d:`M0,0 L0,9 L9,4.5 z`,fill:`#a855f7`})}),(0,X.jsx)(`marker`,{id:`arrow-region`,markerWidth:`8`,markerHeight:`8`,refX:`7`,refY:`4`,orient:`auto`,markerUnits:`strokeWidth`,children:(0,X.jsx)(`path`,{d:`M0,0.5 L0,7.5 L8,4 z`,fill:`context-stroke`})})]}),(0,X.jsxs)(`g`,{ref:Xe,children:[(0,X.jsx)(`rect`,{x:-1e5,y:-1e5,width:2e5,height:2e5,fill:`transparent`,onClick:We,style:{pointerEvents:`all`}}),H.map(e=>{let t=I[e.kind]??`#d99a2b`,n=ne[e.kind]??`6 5`,r=`${de[e.kind]} ${e.index}`;return(0,X.jsxs)(`g`,{style:{pointerEvents:`none`},children:[e.pure&&(0,X.jsx)(`polygon`,{points:e.points.map(([e,t])=>`${e},${t}`).join(` `),fill:t,fillOpacity:.05,stroke:t,strokeWidth:1.5,strokeDasharray:n,opacity:.55}),!e.pure&&e.members.map(e=>(0,X.jsx)(`rect`,{x:e.x-e.width/2-5,y:e.y-e.height/2-5,width:e.width+10,height:e.height+10,rx:13,fill:`none`,stroke:t,strokeWidth:1.5,strokeDasharray:n,opacity:.85},e.id)),e.ordered&&e.members.slice(1).map((n,r)=>{let i=ge(e.members[r],n);return i?(0,X.jsx)(`line`,{x1:i.x1,y1:i.y1,x2:i.x2,y2:i.y2,stroke:t,strokeWidth:1.4,opacity:.75,markerEnd:`url(#arrow-region)`},`${e.id}-${n.id}-step`):null}),e.pure?(0,X.jsx)(`text`,{x:Math.min(...e.points.map(([e])=>e))+10,y:Math.min(...e.points.map(([,e])=>e))-6-(he.get(e.id)??0)*11,fontSize:10,fontFamily:`ui-monospace, monospace`,fill:t,opacity:.9,children:r}):e.members.map((n,i)=>(0,X.jsx)(`text`,{x:n.x-n.width/2-4,y:n.y-n.height/2-10-(he.get(`${e.id}|${n.id}`)??0)*11,fontSize:9,fontFamily:`ui-monospace, monospace`,fill:t,opacity:.85,children:e.ordered?`${r} · ${i+1}`:r},`${n.id}-label`))]},e.id)}),N.map(e=>{if(!K(e)||R.has(e.source)||q.has(e.source)||q.has(e.target))return null;let t=U.get(e.source),n=U.get(e.target);if(!t||!n)return null;let{d:r,lx:i,ly:a}=Le(t,n),o={x:i,y:a},s=Re(e.data,v),c=Ne.has(e.id),l=Te.edges.has(e.id),u=y,d=1.75,f=`url(#arrow-def)`,p=1;return l&&(u=`#a855f7`,d=2,f=`url(#arrow-st)`,p=.7),c&&(u=ae,d=1.5,f=`url(#arrow-hi)`,p=1),Ce&&!(Ce.has(e.source)||Ce.has(e.target))&&(p*=.02),(0,X.jsxs)(`g`,{children:[(0,X.jsx)(`path`,{d:r,fill:`none`,stroke:u,strokeWidth:d,strokeLinecap:`round`,strokeLinejoin:`round`,opacity:p,markerEnd:f,style:{pointerEvents:`auto`}}),s&&p>.05&&(0,X.jsx)(`g`,{className:`g-edge-label`,transform:`translate(${o.x},${o.y})`,children:(0,X.jsx)(`text`,{textAnchor:`middle`,dominantBaseline:`middle`,fill:s.fill,fontSize:9,fontFamily:`ui-monospace, monospace`,style:{pointerEvents:`none`},children:(0,X.jsx)(`tspan`,{dx:0,dy:-8,paintOrder:`stroke fill`,stroke:s.bg,strokeWidth:6,strokeLinejoin:`round`,children:s.text})})})]},e.id)}),V.map(e=>{if(q.has(e.id))return null;let t=G(e.data.type),n=Ce&&!Ce.has(e.id),r=t?n?.07:1:0,i=Te.nodes.has(e.id),a=Ie===e.id,{bg:o,border:s,borderW:c,accent:l}=ze(e,v,u,a,i,d),{className:p,method:m}=ve(String(e.data.label??e.id),e.data.method),h=m&&!m.includes(`(`)?m+`()`:m,g=String(e.data.type??``),_=e.width,y=e.height,b=_/2,x=y/2,S=v?`#e6edf3`:`#0d1117`,C=v?`rgba(255,255,255,0.5)`:`rgba(0,0,0,0.5)`,w=e.data.security,T=!!(e.data.hasN1||e.data.fatMethod||e.data.fatClass||w&&((w.issues?.length??0)>0||w.riskLevel&&w.riskLevel!==`none`)),E=e.data.httpCalls??[],D=Array.from(new Set(E.map(e=>e.host||e.configKey||`external`))).map(e=>e.length>14?e.slice(0,13)+`…`:e),O=p.length>24?p.slice(0,23)+`…`:p,k=h.length>26?h.slice(0,25)+`…`:h;return(0,X.jsxs)(`g`,{className:`g-node`,transform:`translate(${e.x},${e.y})`,opacity:r,style:{pointerEvents:t&&r>.05?`auto`:`none`,cursor:`grab`},onPointerDown:t=>Ge(t,e.id,e.x,e.y),onPointerMove:t=>Ke(t,e.id),onPointerUp:t=>qe(t,e.id),onClick:t=>{t.stopPropagation(),L.current||Ue(e.id)},children:[a&&(0,X.jsx)(`rect`,{x:-b-3,y:-x-3,width:_+6,height:y+6,rx:f?7:13,fill:`none`,stroke:l,strokeWidth:6,opacity:.15}),(0,X.jsx)(`rect`,{x:-b,y:-x,width:_,height:y,rx:f?6:10,fill:o,stroke:s,strokeWidth:c,filter:e.data.hasN1&&!u?`drop-shadow(0 0 8px rgba(244,67,54,0.4))`:void 0}),T&&(0,X.jsxs)(`g`,{style:{pointerEvents:`none`},children:[(0,X.jsx)(`circle`,{cx:b-3,cy:-x+3,r:10,fill:`#ef4444`,opacity:.22}),(0,X.jsx)(`circle`,{cx:b-3,cy:-x+3,r:5,fill:`#ef4444`,stroke:o,strokeWidth:1.5})]}),f?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:-b+10,cy:0,r:3.5,fill:l}),(0,X.jsx)(`text`,{x:-b+20,y:0,fontSize:11,fontWeight:700,fontFamily:`ui-sans-serif, system-ui, -apple-system, sans-serif`,fill:S,dominantBaseline:`middle`,style:{pointerEvents:`none`},children:O}),!!e.data.hasN1&&(0,X.jsx)(`text`,{x:b-6,y:0,fontSize:9,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:`#F44336`,dominantBaseline:`middle`,style:{pointerEvents:`none`},children:`N+1`}),D.length>0&&!e.data.hasN1&&(0,X.jsx)(`text`,{x:b-6,y:0,fontSize:9,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:`#38bdf8`,dominantBaseline:`middle`,style:{pointerEvents:`none`},children:`🌐`}),d&&e.data.security&&(0,X.jsx)(`text`,{x:e.data.hasN1?b-28:b-6,y:0,fontSize:8,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:(z[e.data.security.exposure]??z.public).accent,dominantBaseline:`middle`,style:{pointerEvents:`none`},children:(z[e.data.security.exposure]??z.public).label.toUpperCase()})]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:-b+14,cy:-x+18,r:4,fill:l}),(0,X.jsx)(`text`,{x:-b+24,y:-x+22,fontSize:10,fontFamily:`ui-monospace, monospace`,fill:l,opacity:.9,style:{pointerEvents:`none`},children:g}),!!e.data.hasN1&&(0,X.jsx)(`text`,{x:b-10,y:-x+22,fontSize:10,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:`#F44336`,style:{pointerEvents:`none`},children:`N+1`}),d&&e.data.security&&(()=>{let t=e.data.security,n=z[t.exposure]??z.public,r=B[t.riskLevel]??B.none;return(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`text`,{x:e.data.hasN1?b-42:b-10,y:-x+22,fontSize:9,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:n.accent,style:{pointerEvents:`none`},children:[`🔒 `,n.label.toUpperCase()]}),t.riskLevel!==`none`&&(0,X.jsxs)(`text`,{x:b-10,y:-x+38,fontSize:8,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:r,style:{pointerEvents:`none`},children:[`⚠ `,t.issues.length,` issue`,t.issues.length===1?``:`s`]})]})})(),(0,X.jsx)(`text`,{x:-b+14,y:-x+46,fontSize:13,fontWeight:700,fontFamily:`ui-sans-serif, system-ui, -apple-system, sans-serif`,fill:S,style:{pointerEvents:`none`},children:O}),k&&(0,X.jsxs)(`text`,{x:-b+14,y:-x+64,fontSize:11,fontFamily:`ui-monospace, monospace`,fill:C,style:{pointerEvents:`none`},children:[`↻ `,k]}),D.length>0&&(0,X.jsxs)(`text`,{x:b-10,y:x-10,fontSize:9,textAnchor:`end`,fontFamily:`ui-monospace, monospace`,fill:`#38bdf8`,style:{pointerEvents:`none`},children:[`🌐 `,D[0],D.length>1?` +${D.length-1}`:``]})]}),(R.has(e.id)||(J.get(e.id)??0)>4)&&(0,X.jsxs)(`g`,{transform:`translate(${b+2}, 0)`,onPointerDown:e=>e.stopPropagation(),onClick:t=>Y(t,e.id),style:{cursor:`pointer`,pointerEvents:`all`},children:[(0,X.jsx)(`rect`,{x:0,y:-10,width:64,height:20,rx:10,fill:R.has(e.id)?l:v?`rgba(255,255,255,0.12)`:`rgba(0,0,0,0.10)`,stroke:l,strokeWidth:1}),(0,X.jsx)(`text`,{x:32,y:0,textAnchor:`middle`,dominantBaseline:`middle`,fill:R.has(e.id)?`#fff`:l,fontSize:10,fontWeight:700,fontFamily:`ui-monospace, monospace`,style:{pointerEvents:`none`},children:R.has(e.id)?`▶ ${Se.get(e.id)??J.get(e.id)} hidden`:`▾ fold`})]})]},e.id)})]})]}),(0,X.jsx)(`canvas`,{ref:Ze,style:{position:`absolute`,top:0,left:0,pointerEvents:`none`,width:`100%`,height:`100%`}}),(u||d)&&(0,X.jsxs)(`div`,{className:`g-legends`,children:[u&&(0,X.jsxs)(`div`,{className:`cc-legend`,children:[(0,X.jsx)(`div`,{className:`cc-legend-title`,children:`Cyclomatic Complexity`}),le.map(e=>(0,X.jsxs)(`div`,{className:`cc-legend-row`,children:[(0,X.jsx)(`span`,{className:`cc-legend-swatch`,style:{background:e.border}}),(0,X.jsx)(`span`,{className:`cc-legend-label`,style:{color:e.border},children:e.label}),(0,X.jsx)(`span`,{className:`cc-legend-range`,children:e.max===1/0?`≥${e.min}`:`${e.min}–${e.max}`})]},e.label))]}),d&&(0,X.jsxs)(`div`,{className:`cc-legend`,children:[(0,X.jsx)(`div`,{className:`cc-legend-title`,children:`🔒 Security Surface`}),Object.entries(z).map(([e,t])=>(0,X.jsxs)(`div`,{className:`cc-legend-row`,children:[(0,X.jsx)(`span`,{className:`cc-legend-swatch`,style:{background:t.border}}),(0,X.jsx)(`span`,{className:`cc-legend-label`,style:{color:t.accent},children:t.label})]},e)),(0,X.jsx)(`div`,{className:`cc-legend-title`,style:{marginTop:`8px`},children:`Risk Level`}),[{key:`critical`,label:`Critical`,color:B.critical},{key:`high`,label:`High`,color:B.high},{key:`medium`,label:`Medium`,color:B.medium},{key:`none`,label:`Clean`,color:B.none}].map(({key:e,label:t,color:n})=>(0,X.jsxs)(`div`,{className:`cc-legend-row`,children:[(0,X.jsx)(`span`,{className:`cc-legend-swatch`,style:{background:n}}),(0,X.jsx)(`span`,{className:`cc-legend-label`,style:{color:n},children:t})]},e))]})]}),(0,X.jsx)(`div`,{className:`g-rails`,"aria-hidden":!0,children:[{n:1,label:`Route`,c:`var(--nc-route)`},{n:2,label:`Controller`,c:`var(--nc-controller)`},{n:3,label:`Action`,c:`var(--nc-action)`},{n:4,label:`Service · View`,c:`var(--nc-service)`},{n:5,label:`Interface`,c:`var(--nc-interface)`},{n:6,label:`Implementation`,c:`var(--nc-provider)`}].map(e=>(0,X.jsxs)(`div`,{className:`g-rail`,children:[(0,X.jsx)(`span`,{className:`g-rail-pill`,style:{"--rc":e.c},children:e.n}),(0,X.jsx)(`span`,{className:`g-rail-label`,children:e.label})]},e.n))}),(0,X.jsxs)(`div`,{className:`g-toolbar`,children:[(0,X.jsxs)(`select`,{className:`g-tool-select`,value:t,onChange:e=>p(e.target.value),title:`Layout algorithm`,children:[(0,X.jsx)(`option`,{value:`dagre`,children:`Hierarchical`}),(0,X.jsx)(`option`,{value:`breadthfirst`,children:`Breadth-first`}),(0,X.jsx)(`option`,{value:`cose-bilkent`,children:`Force`}),(0,X.jsx)(`option`,{value:`circle`,children:`Circle`}),(0,X.jsx)(`option`,{value:`grid`,children:`Grid`})]}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${n===`TB`?`g-tool--on`:``}`,onClick:()=>m(n===`TB`?`LR`:`TB`),title:`Toggle orientation`,children:n===`TB`?`Top-down`:`Left-right`}),(0,X.jsx)(`span`,{className:`g-tool-sep`}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${ot?`g-tool--on`:``}`,onClick:()=>st(e=>!e),children:`Edge labels`}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${u?`g-tool--on`:``}`,onClick:h,children:`Complexity`}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${d?`g-tool--on`:``}`,onClick:g,children:`Security`}),(0,X.jsx)(`button`,{type:`button`,className:`g-tool ${f?`g-tool--on`:``}`,onClick:_,children:`Compact`})]}),(0,X.jsxs)(`div`,{className:`g-breadcrumb`,children:[[{label:`Route`,c:`var(--nc-route)`},{label:`Controller`,c:`var(--nc-controller)`},{label:`Action`,c:`var(--nc-action)`},{label:`Service`,c:`var(--nc-service)`},{label:`Interface`,c:`var(--nc-interface)`},{label:`Impl`,c:`var(--nc-provider)`}].map((e,t,n)=>(0,X.jsxs)(`span`,{className:`g-crumb`,children:[(0,X.jsx)(`span`,{className:`g-crumb-dot`,style:{background:e.c}}),e.label,t{let t=H.filter(t=>t.kind===e).length;return t===0?null:(0,X.jsxs)(`span`,{className:`g-crumb g-crumb--aside`,children:[(0,X.jsx)(`span`,{className:`g-crumb-sep`,children:`·`}),(0,X.jsx)(`span`,{className:`g-crumb-dot g-crumb-dot--dashed`,style:{borderColor:I[e]}}),t===1?de[e]:`${t} ${fe[e]}`]},e)})]}),(0,X.jsxs)(`div`,{className:`g-zoom`,children:[(0,X.jsx)(`button`,{type:`button`,className:`g-zoom-btn`,onClick:()=>dt(.8),"aria-label":`Zoom out`,children:`−`}),(0,X.jsxs)(`span`,{className:`g-zoom-pct`,children:[it,`%`]}),(0,X.jsx)(`button`,{type:`button`,className:`g-zoom-btn`,onClick:()=>dt(1.25),"aria-label":`Zoom in`,children:`+`}),(0,X.jsx)(`button`,{type:`button`,className:`g-zoom-btn g-zoom-fit`,onClick:()=>ut(),"aria-label":`Fit to view`,children:`⊡`})]})]})}var Ue={"container-binding":`bound in the container`,facade:`reached through a facade`,config:`named in config/`,"inherited-by-reached-class":`inherited by a class that is reached`,"class-string":`named as a class-string elsewhere`},We=`modulepreload`,Ge=function(e){return`/_laravel-brain/`+e},Ke={},qe=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=Ge(t,n),t in Ke)return;Ke[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:We,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},Je=`route.middleware.controller.action.action_class.service.validation_request.repository.model.job.event.view.mail.notification.enum.interface.trait.abstract_class.service_provider.ai_agent.ai_tool.filament_panel.filament_resource.filament_page.filament_page_method.filament_widget.filament_relation_manager`.split(`.`);function Ye(e,t){let n=[];n.push(`%%{init: {'theme': 'dark', 'themeVariables': {`),n.push(` 'background': '#0a0c10',`),n.push(` 'mainBkg': '#0d1117',`),n.push(` 'lineColor': 'rgba(255,255,255,0.35)',`),n.push(` 'edgeLabelBackground': '#111218',`),n.push(` 'edgeLabelColor': 'rgba(255,255,255,0.5)'`),n.push(`}}}%%`),n.push(`%% Laravel Brain — ${t}`),n.push(`flowchart TD`),n.push(``);let r=new Map,i=new Set,a=e=>{if(r.has(e))return r.get(e);let t=e.replace(/[^a-zA-Z0-9_]/g,`_`).replace(/^_+/,``).replace(/_+$/,``).substring(0,40);t||=`node`;let n=t,a=0;for(;i.has(n);)n=`${t}_${++a}`;return i.add(n),r.set(e,n),n},o=new Map;for(let t of e.nodes)o.has(t.type)||o.set(t.type,[]),o.get(t.type).push(t);let s=[...new Set([...Je,...o.keys()])].filter(e=>(o.get(e)?.length??0)>0);for(let e of s){let t=o.get(e);n.push(` %% ${e}`);for(let e of t){let t=a(e.id),r=Xe(e);n.push(` ${t}["${at(r)}"]`)}n.push(``)}n.push(` %% Edges`);for(let t of e.edges){let e=a(t.source),r=a(t.target),i=t.label?`|"${at(t.label)}"| `:``;n.push(` ${e} -->${i}${r}`)}n.push(``),n.push(` %% Styles`);for(let e of s){let t=re[e]??`#c9d1d9`,r=L[e]??`#0d1117`;n.push(` classDef cls_${e} fill:${r},stroke:${t},stroke-width:2px,color:#e6edf3`)}n.push(``);for(let e of s){let t=o.get(e).map(e=>a(e.id)).join(`,`);n.push(` class ${t} cls_${e}`)}return n.join(`
+`)}function Xe(e){let t=String(e.label??``),n=e.data?.method,{className:r,method:i}=ve(t,n),a=i&&!i.includes(`(`)?i+`()`:i,o=[`● ${e.type}`,r];return a&&o.push(`↻ ${a}`),o.join(`
+`)}function Ze(e,t){let n=[`%% Method Flow — ${t}`,`flowchart TD`],r=0,i=()=>`s${r++}`,a=i();n.push(` ${a}([" 🚀 ${at(t)} "])`);let o=(e,t)=>{let r=t;for(let t of e){let e=i();if(t.type===`if`){let[a,s]=[`{`,`}`];if(n.push(` ${e}${a}"${at(t.label)}"${s}`),n.push(` ${r} --> ${e}`),n.push(` class ${e} cls_if`),t.then&&t.then.length>0){let r=i(),a=t.then[0];n.push(` ${r}${nt(a.type)}"${at(a.label)}"${rt(a.type)}`),n.push(` ${e} -->|"yes"| ${r}`),n.push(` class ${r} cls_${a.type}`),o(t.then.slice(1),r)}if(t.else&&t.else.length>0){let r=i(),a=t.else[0];n.push(` ${r}${nt(a.type)}"${at(a.label)}"${rt(a.type)}`),n.push(` ${e} -->|"no"| ${r}`),n.push(` class ${r} cls_${a.type}`),o(t.else.slice(1),r)}r=e}else if(t.type===`loop`){let i=t.n1?` ⚠️ N+1 `:``,a=t.cache?` [cache ${t.cache.kind}] `:``;n.push(` ${e}[/"${i}${a}${at(t.label)}"/]`),n.push(` ${r} --> ${e}`),n.push(` class ${e} ${t.n1?`cls_n1`:t.cache?`cls_cache`:`cls_loop`}`),t.body&&t.body.length>0&&o(t.body,e),r=e}else{let[i,a]=[nt(t.type),rt(t.type)],o=it(t.type),s=t.n1?` ⚠️ N+1 `:``,c=t.cache?` [cache ${t.cache.kind}] `:``;n.push(` ${e}${i}"${s}${c}${o}${at(t.label)}"${a}`),n.push(` ${r} --> ${e}`),n.push(` class ${e} ${t.n1?`cls_n1`:`cls_${t.type}`}`),r=e}}return r};return o(e,a),n.push(``),n.push(` %% STYLES`),n.push(` classDef cls_call fill:#0d47a1,stroke:#2196F3,color:#fff`),n.push(` classDef cls_assign fill:#212121,stroke:#616161,color:#ccc`),n.push(` classDef cls_return fill:#1b5e20,stroke:#4CAF50,color:#fff`),n.push(` classDef cls_throw fill:#b71c1c,stroke:#F44336,color:#fff`),n.push(` classDef cls_if fill:#f9a825,stroke:#fbc02d,color:#000`),n.push(` classDef cls_loop fill:#6a1b9a,stroke:#9c27b0,color:#fff`),n.push(` classDef cls_n1 fill:#b71c1c,stroke:#ff5252,color:#fff`),n.push(` classDef cls_dispatch fill:#bf360c,stroke:#FF5722,color:#fff`),n.push(` classDef cls_event fill:#0e47a1,stroke:#00BCD4,color:#fff`),n.push(` classDef cls_cache fill:#004d40,stroke:#009688,color:#fff`),n.join(`
+`)}function Qe(e,t){et(new Blob([e],{type:`text/plain`}),t)}function $e(e,t){let n=document.createElement(`a`);n.href=e,n.download=t,n.click()}function et(e,t){let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,r.click(),setTimeout(()=>URL.revokeObjectURL(n),2e3)}async function tt(t,n=`#0d0f14`){let{default:r}=await qe(async()=>{let{default:t}=await import(`./vendor-utils-D7YtnGoz.js`).then(t=>e(t.t(),1));return{default:t}},__vite__mapDeps([0,1]));return(await r(t,{backgroundColor:n,scale:2,useCORS:!0,logging:!1})).toDataURL(`image/png`)}function nt(e){switch(e){case`return`:return`([`;case`throw`:return`([`;case`dispatch`:return`[[`;case`event`:return`((`;default:return`[`}}function rt(e){switch(e){case`return`:return`])`;case`throw`:return`])`;case`dispatch`:return`]]`;case`event`:return`))`;default:return`]`}}function it(e){switch(e){case`call`:return`→ `;case`assign`:return`= `;case`return`:return`◀ `;case`throw`:return`⚠ `;case`dispatch`:return`⚡ `;case`event`:return`📡 `;case`cache`:return`⛃ `;default:return``}}function at(e){return e.replace(/"/g,`'`).replace(/\n/g,`\\n`).replace(/[<>]/g,e=>e===`<`?`<`:`>`)}function ot({mermaidCode:e,filename:t,title:n,onClose:r}){let[i,a]=(0,A.useState)(!1),o=(0,A.useRef)(null);return(0,A.useEffect)(()=>{let e=e=>{e.key===`Escape`&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r]),(0,X.jsx)(`div`,{className:`export-overlay`,onClick:e=>{e.target===e.currentTarget&&r()},children:(0,X.jsxs)(`div`,{className:`export-modal`,children:[(0,X.jsxs)(`div`,{className:`export-modal-header`,children:[(0,X.jsxs)(`div`,{className:`export-modal-title`,children:[(0,X.jsx)(`span`,{className:`export-modal-icon`,children:`🗺`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:n}),(0,X.jsx)(`span`,{className:`export-modal-sub`,children:`Mermaid Flowchart`})]})]}),(0,X.jsx)(`button`,{className:`export-modal-close`,onClick:r,title:`Close (Esc)`,children:`×`})]}),(0,X.jsxs)(`div`,{className:`export-modal-actions`,children:[(0,X.jsx)(`button`,{className:`export-btn export-btn--primary`,onClick:async()=>{try{await navigator.clipboard.writeText(e),a(!0),setTimeout(()=>a(!1),2e3)}catch{o.current?.select(),document.execCommand(`copy`),a(!0),setTimeout(()=>a(!1),2e3)}},children:i?`✓ Copied!`:`⎘ Copy Code`}),(0,X.jsx)(`button`,{className:`export-btn export-btn--secondary`,onClick:()=>Qe(e,t),children:`↓ Download .mmd`}),(0,X.jsx)(`button`,{className:`export-btn export-btn--accent`,onClick:()=>{let t=JSON.stringify({code:e,mermaid:`{}`,autoSync:!0}),n=btoa(unescape(encodeURIComponent(t)));window.open(`https://mermaid.live/edit#base64:${n}`,`_blank`)},children:`↗ Open in Mermaid Live`})]}),(0,X.jsxs)(`div`,{className:`export-modal-hint`,children:[`Paste this code at`,` `,(0,X.jsx)(`a`,{href:`https://mermaid.live`,target:`_blank`,rel:`noreferrer`,children:`mermaid.live`}),` `,`to render the diagram, or use any Mermaid-compatible tool.`]}),(0,X.jsxs)(`div`,{className:`export-code-wrapper`,children:[(0,X.jsx)(`div`,{className:`export-code-lang`,children:`mermaid`}),(0,X.jsx)(`textarea`,{ref:o,className:`export-code`,value:e,readOnly:!0,spellCheck:!1,onClick:e=>e.target.select()})]}),(0,X.jsxs)(`div`,{className:`export-modal-stats`,children:[(0,X.jsxs)(`span`,{children:[e.split(`
+`).length,` lines`]}),(0,X.jsxs)(`span`,{children:[(e.length/1024).toFixed(1),` KB`]})]})]})})}function st({steps:e,title:t,isFatMethod:n}){let[r,i]=(0,A.useState)(!1),[a,o]=(0,A.useState)(!1),s=(0,A.useRef)(null);if(!e||e.length===0)return(0,X.jsx)(`div`,{className:`flowchart-empty`,children:(0,X.jsx)(`span`,{children:`No flow data available`})});let c=t??`method`;return(0,X.jsxs)(X.Fragment,{children:[n&&(0,X.jsx)(`div`,{className:`flowchart-fat-banner`,title:`Fat Method: this method exceeds complexity or line-count thresholds`,children:`🧱 Fat Method — consider breaking this into smaller methods`}),(0,X.jsxs)(`div`,{className:`flowchart-export-bar`,children:[(0,X.jsxs)(`button`,{className:`flowchart-export-btn`,onClick:async()=>{if(s.current){o(!0);try{$e(await tt(s.current),`${c.replace(/[^a-z0-9]/gi,`_`)}_flow.png`)}finally{o(!1)}}},disabled:a,title:`Export as PNG`,children:[a?`⏳`:`🖼`,` PNG`]}),(0,X.jsx)(`button`,{className:`flowchart-export-btn`,onClick:()=>i(!0),title:`Export as Mermaid`,children:`🗺 Mermaid`})]}),(0,X.jsxs)(`div`,{className:`flowchart-root`,ref:s,children:[t&&(0,X.jsx)(`div`,{className:`flowchart-title`,children:t}),(0,X.jsx)(ct,{steps:e})]}),r&&(0,X.jsx)(ot,{mermaidCode:Ze(e,c),filename:`${c.replace(/[^a-z0-9]/gi,`_`)}_flow.mmd`,title:c,onClose:()=>i(!1)})]})}function ct({steps:e}){return(0,X.jsx)(`div`,{className:`flowchart-list`,children:e.map((t,n)=>(0,X.jsx)(lt,{step:t,isLast:n===e.length-1},n))})}function lt({step:e,isLast:t}){return e.type===`if`?(0,X.jsxs)(`div`,{className:`flowchart-branch-wrapper`,children:[(0,X.jsx)(ut,{step:e}),(0,X.jsxs)(`div`,{className:`flowchart-branches`,children:[e.then&&e.then.length>0&&(0,X.jsxs)(`div`,{className:`flowchart-branch flowchart-branch--then`,children:[(0,X.jsx)(`div`,{className:`flowchart-branch-label`,children:`then`}),(0,X.jsx)(ct,{steps:e.then})]}),e.else&&e.else.length>0&&(0,X.jsxs)(`div`,{className:`flowchart-branch flowchart-branch--else`,children:[(0,X.jsx)(`div`,{className:`flowchart-branch-label`,children:`else`}),(0,X.jsx)(ct,{steps:e.else})]})]}),!t&&(0,X.jsx)(ft,{})]}):e.type===`loop`?(0,X.jsxs)(`div`,{className:`flowchart-branch-wrapper`,children:[(0,X.jsx)(ut,{step:e}),e.body&&e.body.length>0&&(0,X.jsx)(`div`,{className:`flowchart-loop-body`,children:(0,X.jsx)(ct,{steps:e.body})}),!t&&(0,X.jsx)(ft,{})]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(ut,{step:e}),!t&&(0,X.jsx)(ft,{})]})}function ut({step:e}){let t=`flowchart-box flowchart-box--${e.type} ${e.n1?`flowchart-box--n1`:``}`,n=pt[e.type]??``;return(0,X.jsxs)(`div`,{className:`${t} flowchart-shape--${e.type===`if`?`diamond`:e.type===`return`||e.type===`throw`?`terminal`:`rect`}`,title:e.label,children:[n&&(0,X.jsx)(`span`,{className:`flowchart-icon`,children:n}),(0,X.jsx)(`span`,{className:`flowchart-label`,children:e.label}),e.cache&&(0,X.jsx)(`span`,{className:`flowchart-cache-badge flowchart-cache-badge--${e.cache.kind}`,title:dt(e),children:e.cache.kind}),e.n1&&(0,X.jsx)(`span`,{className:`flowchart-n1-warn`,title:`N+1 Query Detected: This database operation is inside a loop!`,children:`⚠️ N+1`}),e.http&&e.http.length>0&&(0,X.jsxs)(`span`,{className:`flowchart-http`,title:e.http.map(e=>`${e.method||`REQUEST`} ${e.host||e.configKey&&`config('${e.configKey}')`||`address computed at runtime`}`+(e.timeout===null?` · no timeout`:` · timeout ${e.timeout}s`)).join(`
+`),children:[`🌐 `,e.http.map(e=>e.host).find(Boolean)??`external`]})]})}function dt(e){let t=e.cache;if(!t)return``;let n=t.keyKind===`computed`?`computed key`:t.keyKind===`none`?`whole store`:`"${t.key}"`,r=[t.ttl===null?``:`ttl ${t.ttl}s`,t.store===``?``:`store ${t.store}`,t.tags.length>0?`tags ${t.tags.join(`, `)}`:``].filter(Boolean);return`${t.kind} · ${t.method} ${n}${r.length>0?` · ${r.join(` · `)}`:``}`}function ft(){return(0,X.jsxs)(`div`,{className:`flowchart-arrow`,children:[(0,X.jsx)(`div`,{className:`flowchart-arrow-line`}),(0,X.jsx)(`div`,{className:`flowchart-arrow-head`})]})}var pt={call:`→`,assign:`=`,return:`◀`,throw:`⚠`,if:`◆`,loop:`↻`,dispatch:`⚡`,event:`📡`,cache:`⛃`};function mt({steps:e,title:t,isFatMethod:n,onClose:r}){return(0,A.useEffect)(()=>{let e=e=>{e.key===`Escape`&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r]),(0,X.jsx)(`div`,{className:`modal-overlay`,onClick:e=>{e.target===e.currentTarget&&r()},children:(0,X.jsxs)(`div`,{className:`modal-container modal-container--large`,children:[(0,X.jsxs)(`div`,{className:`modal-header`,children:[(0,X.jsxs)(`div`,{className:`modal-title`,children:[(0,X.jsx)(`span`,{className:`modal-icon`,children:`⛓`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:t}),(0,X.jsx)(`span`,{className:`modal-sub`,children:`Method Flow Visualization`})]})]}),(0,X.jsx)(`button`,{className:`modal-close`,onClick:r,title:`Close (Esc)`,children:`×`})]}),(0,X.jsx)(`div`,{className:`modal-body flowchart-modal-body`,children:(0,X.jsx)(st,{steps:e,isFatMethod:n})})]})})}function ht(e){let[t,n]=(0,A.useState)(null),[r,i]=(0,A.useState)(!1),[a,o]=(0,A.useState)(null),[s,c]=(0,A.useState)(e);return e!==s&&(c(e),e?(i(!0),o(null),n(null)):(n(null),o(null))),(0,A.useEffect)(()=>{e&&fetch(`/_laravel-brain/api/source?path=${encodeURIComponent(e)}`).then(e=>e.json()).then(e=>{if(e.error)throw Error(e.error);n(e.content)}).catch(e=>o(e.message)).finally(()=>i(!1))},[e]),{content:t,loading:r,error:a}}s.registerLanguage(`php`,u);function gt({filePath:e,highlightLine:t,theme:n}){let{content:r,loading:i,error:o}=ht(e),c=(0,A.useRef)(null);(0,A.useEffect)(()=>{c.current&&c.current.scrollIntoView({block:`center`,behavior:`smooth`})},[r]);let l=e.replace(/.*\/(app|src)\//,`$1/`);return i?(0,X.jsxs)(`div`,{className:`source-state`,children:[(0,X.jsx)(`div`,{className:`loading-spinner`,style:{width:20,height:20,borderWidth:2}}),(0,X.jsx)(`span`,{children:`Loading source…`})]}):o?(0,X.jsxs)(`div`,{className:`source-state source-state--error`,children:[`Could not load file`,(0,X.jsx)(`small`,{style:{display:`block`,opacity:.6,marginTop:4},children:o})]}):r?(0,X.jsxs)(`div`,{className:`source-view`,children:[(0,X.jsx)(`div`,{className:`source-path`,title:e,children:l}),(0,X.jsx)(s,{language:`php`,style:n===`dark`?p:a,showLineNumbers:!0,wrapLines:!0,lineNumberStyle:{minWidth:`2.5em`,paddingRight:`1em`,userSelect:`none`,opacity:.4,fontSize:11},lineProps:e=>e===t?{ref:c,style:{display:`block`,backgroundColor:n===`dark`?`rgba(139,111,232,0.2)`:`rgba(139,111,232,0.12)`,borderLeft:`3px solid #8B6FE8`}}:{style:{display:`block`}},customStyle:{margin:0,padding:`12px 0`,background:`transparent`,fontSize:12,lineHeight:`1.6`,fontFamily:`ui-monospace, "Cascadia Code", monospace`},children:r})]}):null}function _t({filePath:e,highlightLine:t,theme:n,onClose:r}){(0,A.useEffect)(()=>{let e=e=>{e.key===`Escape`&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r]);let i=e.split(`/`).pop()||`Source Code`;return(0,X.jsx)(`div`,{className:`modal-overlay`,onClick:e=>{e.target===e.currentTarget&&r()},children:(0,X.jsxs)(`div`,{className:`modal-container modal-container--large`,children:[(0,X.jsxs)(`div`,{className:`modal-header`,children:[(0,X.jsxs)(`div`,{className:`modal-title`,children:[(0,X.jsx)(`span`,{className:`modal-icon`,children:`📄`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:i}),(0,X.jsx)(`span`,{className:`modal-sub`,children:e})]})]}),(0,X.jsx)(`button`,{className:`modal-close`,onClick:r,title:`Close (Esc)`,children:`×`})]}),(0,X.jsx)(`div`,{className:`modal-body source-modal-body`,children:(0,X.jsx)(gt,{filePath:e,highlightLine:t,theme:n})})]})})}function vt(e){let[t,n]=(0,A.useState)(null),[r,i]=(0,A.useState)(!1),[a,o]=(0,A.useState)(null),[s,c]=(0,A.useState)(e);return e!==s&&(c(e),e?(i(!0),o(null),n(null)):(n(null),o(null))),(0,A.useEffect)(()=>{e&&fetch(`/_laravel-brain/api/usages?nodeId=${encodeURIComponent(e)}`).then(e=>e.json()).then(e=>{if(e.error)throw Error(e.error);n(e)}).catch(e=>o(e.message)).finally(()=>i(!1))},[e]),{data:t,loading:r,error:a}}function yt({nodeId:e}){let{data:t,loading:n,error:r}=vt(e);return n?(0,X.jsxs)(`div`,{className:`source-state`,children:[(0,X.jsx)(`div`,{className:`loading-spinner`,style:{width:20,height:20,borderWidth:2}}),(0,X.jsx)(`span`,{children:`Finding usages…`})]}):r?(0,X.jsxs)(`div`,{className:`source-state source-state--error`,children:[`Could not load usages`,(0,X.jsx)(`small`,{style:{display:`block`,opacity:.6,marginTop:4},children:r})]}):t?t.usageCount===0?(0,X.jsx)(`div`,{className:`sidebar-section`,children:(0,X.jsxs)(`div`,{className:`security-clean`,children:[(0,X.jsx)(`span`,{children:`✓`}),` Not used anywhere else in the project.`]})}):(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Used in `,t.fileCount,` file`,t.fileCount===1?``:`s`,` · `,t.usageCount,` reference`,t.usageCount===1?``:`s`]}),t.files.map(e=>(0,X.jsxs)(`div`,{style:{marginBottom:12},children:[(0,X.jsxs)(`span`,{className:`ins-chip ins-chip--neutral`,title:e.file??`Location could not be resolved`,style:{display:`inline-block`,marginBottom:6},children:[e.file?e.file.split(`/`).slice(-2).join(`/`):`Unresolved location`,` · `,e.count]}),e.usages.map(e=>(0,X.jsxs)(`div`,{className:`edge-row`,children:[(0,X.jsx)(`span`,{className:`edge-target`,children:e.label}),(0,X.jsx)(`span`,{className:`edge-label`,children:e.edgeLabel})]},e.nodeId))]},e.file??`#${e.usages[0]?.nodeId??``}`))]}):null}var bt=new Set([`POST`,`PUT`,`PATCH`,`QUERY`]),xt=new Set([`POST`,`PUT`,`PATCH`,`DELETE`,`QUERY`]);function St(e){let t=parseInt(e,10);return t>=200&&t<300?`#22c55e`:t>=400&&t<500?`#f97316`:t>=500?`#ef4444`:`#6b7280`}function Ct(e){let t=Math.floor((Date.now()-e)/1e3);return t<60?`${t}s ago`:t<3600?`${Math.floor(t/60)}m ago`:`${Math.floor(t/3600)}h ago`}var wt=new Map;function Z(e){let t=wt.get(e);if(t)return t;try{let t=localStorage.getItem(`lb_st_${e}`);if(t){let n=JSON.parse(t);return wt.set(e,n),n}}catch{}}function Tt(e,t){let n={...t,savedAt:Date.now()};wt.set(e,n);try{localStorage.setItem(`lb_st_${e}`,JSON.stringify(n))}catch{}}function Et(e){let t=new Set,n=[];for(let r of e.matchAll(/\{([^}?]+)(\?)?\}/g))t.has(r[1])||(n.push({name:r[1],optional:!!r[2]}),t.add(r[1]));return n}function Dt(e,t){let n=e;return n=n.replace(/\/\{([^}?]+)\?\}/g,(e,n)=>{let r=t[n]?.trim();return r?`/`+encodeURIComponent(r):``}),n=n.replace(/\{([^}?]+)\}/g,(e,n)=>encodeURIComponent(t[n]?.trim()??``)),n||`/`}function Ot(e){try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?null:Object.entries(t).map(([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`).join(`&`)}catch{return null}}function kt({method:e,uri:t,selectedId:n,onStressChange:r}){let i=`${e}::${t}`,a=Et(t),[o,s]=(0,A.useState)(()=>{let e=window.location.href,t=e.indexOf(`/_laravel-brain`);return t===-1?window.location.origin:e.slice(0,t)}),[c,l]=(0,A.useState)(()=>Z(i)?.count??10),[u,d]=(0,A.useState)(()=>Z(i)?.concurrency??2),[f,p]=(0,A.useState)(()=>Z(i)?.headersRaw??``),[m,h]=(0,A.useState)(()=>Z(i)?.body??(bt.has(e.toUpperCase())?`{}`:``)),[g,_]=(0,A.useState)(()=>Z(i)?.timeout??10),[v,y]=(0,A.useState)(()=>{let e=Z(i);return!!(e?.jobId&&!e?.result)}),[b,x]=(0,A.useState)(()=>Z(i)?.jobId??null),[S,C]=(0,A.useState)(()=>Z(i)?.result??null),[w,T]=(0,A.useState)(()=>Z(i)?.error??null),[E,D]=(0,A.useState)(()=>Z(i)?.routeParams??{}),[O,k]=(0,A.useState)(()=>Z(i)?.includeCsrf??xt.has(e.toUpperCase())),[j,M]=(0,A.useState)(()=>Z(i)?.sendAsFormData??xt.has(e.toUpperCase())),[N,P]=(0,A.useState)(0),F=(0,A.useRef)(null),ee=(0,A.useRef)({result:S,error:w,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:b,routeParams:E,includeCsrf:O,sendAsFormData:j,key:i});async function te(e,t){let n=0;for(;n<180;){if(t.aborted||(await new Promise(e=>setTimeout(e,1e3)),n++,P(n),t.aborted))return;try{let n=await(await fetch(`/_laravel-brain/api/stress-test/${e}`,{signal:t})).json();if(n.status===`done`){let e=n.result;C(e),x(null),Tt(i,{result:e,error:null,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:null,routeParams:E,includeCsrf:O,sendAsFormData:j}),P(0);return}if(n.status===`error`){T(n.error??`Unknown error`),x(null),Tt(i,{result:null,error:n.error??`Unknown error`,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:null,routeParams:E,includeCsrf:O,sendAsFormData:j}),P(0);return}}catch(e){if(e.name===`AbortError`)return}}P(0),x(null),Tt(i,{result:null,error:`Stress test timed out after 3 minutes`,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:null,routeParams:E,includeCsrf:O,sendAsFormData:j}),T(`Stress test timed out after 3 minutes`)}(0,A.useEffect)(()=>{ee.current={result:S,error:w,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:b,routeParams:E,includeCsrf:O,sendAsFormData:j,key:i}}),(0,A.useEffect)(()=>{let e=Z(i);e?.jobId&&!e?.result&&(r(n),F.current=new AbortController,te(e.jobId,F.current.signal).finally(()=>{y(!1),P(0),r(null)}))},[]),(0,A.useEffect)(()=>()=>{F.current?.abort();let e=ee.current;Tt(e.key,{result:e.result,error:e.error,count:e.count,concurrency:e.concurrency,headersRaw:e.headersRaw,body:e.body,timeout:e.timeout,jobId:e.jobId,routeParams:e.routeParams,includeCsrf:e.includeCsrf,sendAsFormData:e.sendAsFormData})},[]);let I=Z(i),ne=I?.savedAt&&I.result?Ct(I.savedAt):null;function re(e){let t={};for(let n of e.split(`
+`)){let e=n.indexOf(`:`);if(e>0){let r=n.slice(0,e).trim(),i=n.slice(e+1).trim();r&&(t[r]=i)}}return t}async function ie(){let s=a.filter(e=>!e.optional&&!E[e.name]?.trim());if(s.length>0){T(`Required route param${s.length>1?`s`:``} missing: ${s.map(e=>e.name).join(`, `)}`);return}y(!0),C(null),T(null),r(n);let l=Dt(t,E),d=o.replace(/\/$/,``)+`/`+l.replace(/^\//,``);F.current=new AbortController;let p=F.current.signal,h={},_=m||null;if(bt.has(e.toUpperCase())&&j&&m){let e=Ot(m);e!==null&&(_=e,h[`Content-Type`]=`application/x-www-form-urlencoded`)}let v={...h,...re(f)};try{let t=await fetch(`/_laravel-brain/api/stress-test`,{method:`POST`,signal:p,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({method:e.toUpperCase(),url:d,count:c,concurrency:u,headers:v,body:_,timeout:g,includeCsrf:xt.has(e.toUpperCase())?O:!1})}),n=await t.json();if(!t.ok){T(n.error??`Request failed (${t.status})`);return}if(n.jobId){x(n.jobId),Tt(i,{result:null,error:null,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:n.jobId,routeParams:E,includeCsrf:O,sendAsFormData:j}),await te(n.jobId,p);return}let r=n;C(r),x(null),Tt(i,{result:r,error:null,count:c,concurrency:u,headersRaw:f,body:m,timeout:g,jobId:null,routeParams:E,includeCsrf:O,sendAsFormData:j})}catch(e){e.name!==`AbortError`&&T(e instanceof Error?e.message:`Network error`)}finally{y(!1),P(0),r(null)}}let L=S?[{label:`Min`,value:`${S.timing.min}ms`},{label:`Avg`,value:`${S.timing.avg}ms`},{label:`P50`,value:`${S.timing.p50}ms`},{label:`P95`,value:`${S.timing.p95}ms`},{label:`P99`,value:`${S.timing.p99}ms`},{label:`Max`,value:`${S.timing.max}ms`},{label:`Req/s`,value:String(S.throughput)},{label:`Success`,value:`${S.successRate}%`},{label:`Wall`,value:`${S.wallTimeMs}ms`}]:[];return(0,X.jsxs)(`div`,{className:`st-section sidebar-section`,children:[(0,X.jsx)(`div`,{className:`st-toggle`,children:(0,X.jsx)(`h3`,{children:`Stress Test`})}),(0,X.jsx)(`div`,{className:`st-body`,children:(0,X.jsxs)(`div`,{className:`st-form`,children:[(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Base URL`}),(0,X.jsx)(`input`,{className:`st-input`,type:`text`,placeholder:`http://localhost:8000`,value:o,onChange:e=>s(e.target.value)})]}),(0,X.jsxs)(`div`,{className:`st-docker-hint`,children:[(0,X.jsx)(`strong`,{children:`Docker?`}),` The stress test runs `,(0,X.jsx)(`em`,{children:`inside`}),` the container — `,(0,X.jsx)(`code`,{children:`localhost:8080`}),` is the host-side port and won't be reachable there. Change Base URL to the internal service address, e.g. `,(0,X.jsx)(`code`,{children:`http://nginx`}),` or `,(0,X.jsx)(`code`,{children:`http://localhost:80`}),`.`]}),(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Target`}),(0,X.jsxs)(`span`,{className:`st-uri-preview`,children:[(0,X.jsx)(`span`,{className:`st-method-badge`,children:e.toUpperCase()}),a.length>0?Dt(t,E):t]})]}),a.length>0&&(0,X.jsxs)(`div`,{className:`st-form-col`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Route Params`}),a.map(({name:e,optional:t})=>(0,X.jsxs)(`div`,{className:`st-form-row`,style:{marginTop:4},children:[(0,X.jsxs)(`span`,{className:`st-label`,style:{minWidth:80},children:[e,t?` (opt)`:``]}),(0,X.jsx)(`input`,{className:`st-input`,type:`text`,placeholder:t?`optional`:`required`,value:E[e]??``,onChange:t=>D(n=>({...n,[e]:t.target.value}))})]},e))]}),(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Requests`}),(0,X.jsx)(`input`,{className:`st-input st-input--short`,type:`number`,min:1,max:200,value:c,onChange:e=>l(Math.max(1,Math.min(200,parseInt(e.target.value)||1)))}),(0,X.jsx)(`span`,{className:`st-label`,style:{minWidth:`auto`,marginLeft:8},children:`Concurrency`}),(0,X.jsx)(`input`,{className:`st-input st-input--short`,type:`number`,min:1,max:20,value:u,onChange:e=>d(Math.max(1,Math.min(20,parseInt(e.target.value)||1)))})]}),(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Timeout (s)`}),(0,X.jsx)(`input`,{className:`st-input st-input--short`,type:`number`,min:1,max:30,value:g,onChange:e=>_(Math.max(1,Math.min(30,parseInt(e.target.value)||10)))})]}),xt.has(e.toUpperCase())&&(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`CSRF Token`}),(0,X.jsxs)(`label`,{style:{display:`flex`,alignItems:`center`,gap:6,cursor:`pointer`,fontSize:13},children:[(0,X.jsx)(`input`,{type:`checkbox`,checked:O,onChange:e=>k(e.target.checked)}),`Auto-inject from session`]})]}),bt.has(e.toUpperCase())&&(0,X.jsxs)(`div`,{className:`st-form-row`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Body Format`}),(0,X.jsxs)(`label`,{style:{display:`flex`,alignItems:`center`,gap:6,cursor:`pointer`,fontSize:13},children:[(0,X.jsx)(`input`,{type:`checkbox`,checked:j,onChange:e=>M(e.target.checked)}),`Form data (application/x-www-form-urlencoded)`]})]}),(0,X.jsxs)(`div`,{className:`st-form-col`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:`Headers`}),(0,X.jsx)(`textarea`,{className:`st-textarea`,rows:3,placeholder:`Authorization: Bearer token
+Accept: application/json`,value:f,onChange:e=>p(e.target.value)})]}),bt.has(e.toUpperCase())&&(0,X.jsxs)(`div`,{className:`st-form-col`,children:[(0,X.jsx)(`span`,{className:`st-label`,children:j?`Body (JSON → form)`:`Body (JSON)`}),(0,X.jsx)(`textarea`,{className:`st-textarea`,rows:4,placeholder:`{}`,value:m,onChange:e=>h(e.target.value)})]}),(0,X.jsx)(`button`,{className:`st-run-btn`,onClick:ie,disabled:v,children:v?`⏳ Running… ${N>0?`(${N}s)`:``}`:`▶ Run Stress Test`}),ne&&(0,X.jsxs)(`span`,{className:`st-last-run st-last-run--form`,children:[`Last run: `,ne]}),w&&(0,X.jsx)(`div`,{className:`st-error-box`,children:w}),S&&(0,X.jsxs)(`div`,{className:`st-results`,children:[(0,X.jsx)(`div`,{className:`st-metrics-grid`,children:L.map(e=>(0,X.jsxs)(`div`,{className:`st-metric`,children:[(0,X.jsx)(`div`,{className:`st-metric-value`,children:e.value}),(0,X.jsx)(`div`,{className:`st-metric-label`,children:e.label})]},e.label))}),Object.keys(S.statusDistribution).length>0&&(0,X.jsxs)(`div`,{className:`st-dist`,children:[(0,X.jsx)(`div`,{className:`st-dist-title`,children:`Status Distribution`}),Object.entries(S.statusDistribution).sort(([e],[t])=>parseInt(e)-parseInt(t)).map(([e,t])=>(0,X.jsxs)(`div`,{className:`st-dist-row`,children:[(0,X.jsx)(`span`,{className:`st-dist-label`,children:e===`0`?`err`:e}),(0,X.jsx)(`div`,{className:`st-dist-bar-wrap`,children:(0,X.jsx)(`div`,{className:`st-dist-bar`,style:{width:`${t/S.total*100}%`,background:St(e)}})}),(0,X.jsx)(`span`,{className:`st-dist-count`,children:t})]},e))]}),S.errors.length>0&&(0,X.jsx)(`div`,{className:`st-error-box`,style:{marginTop:8},children:S.errors.map((e,t)=>(0,X.jsx)(`div`,{children:e},t))})]})]})})]})}var At=[`route`,`middleware`,`controller`,`action`,`validation_request`,`action_class`,`service`,`model`,`event`,`job`,`command`,`channel`,`schedule`,`view`,`mail`,`notification`,`enum`,`interface`,`trait`,`abstract_class`,`service_provider`,`ai_agent`,`ai_tool`];function jt(e){return e===`action`?`controller`:e}function Mt(e){if(!e)return 99;let t=jt(e.type),n=At.indexOf(t);return n===-1?99:n}function Nt(e){let t=e.split(`\\`),n=t[t.length-1];return n.length<=20?n:n.substring(0,18)+`…`}function Pt(e){let t=new Map;for(let n of e)t.has(n.source)||t.set(n.source,[]),t.get(n.source).push(n);return t}function Ft(e){return e.includes(`-to-job`)||e.includes(`-to-event`)||e===`model-to-event`}function It(e,t){let n=new Map(t.nodes.map(e=>[e.id,e])),r=Pt(t.edges),i=new Set,a=[],o=[],s=[e];for(i.add(e);s.length>0;){let e=s.shift();a.push(e);for(let t of r.get(e)??[])o.push(t),i.has(t.target)||(i.add(t.target),s.push(t.target))}let c=[],l=new Map,u=[...a].sort((e,t)=>{let r=Mt(n.get(e)),i=Mt(n.get(t));return r===i?e.localeCompare(t):r-i});for(let e of u){let t=n.get(e);if(!t)continue;let r=c.length;l.set(e,r);let i=jt(t.type);c.push({id:t.id,label:Nt(t.label),type:i,color:re[t.type]??re[i]??`#888`})}c.unshift({id:`__client__`,label:`Client`,type:`client`,color:`#78909C`});for(let e of[...l.keys()])l.set(e,l.get(e)+1);let d=u.filter(e=>n.get(e)?.type===`model`),f=null;d.length>0&&(f=c.length,c.push({id:`__db__`,label:`Database`,type:`db`,color:`#78909C`}));let p=[],m=l.get(e);m!==void 0&&p.push({fromIndex:0,toIndex:m,label:`request`,isReturn:!1});for(let e of o){let t=l.get(e.source),n=l.get(e.target);if(t===void 0||n===void 0||t===n)continue;let r=Ft(e.type);p.push({fromIndex:t,toIndex:n,label:e.label||``,isAsync:r})}if(f!==null)for(let e of d){let t=l.get(e);t!==void 0&&(p.push({fromIndex:t,toIndex:f,label:`query`,isReturn:!1}),p.push({fromIndex:f,toIndex:t,label:`result`,isReturn:!0}))}m!==void 0&&p.push({fromIndex:m,toIndex:0,label:`response`,isReturn:!0});let h=new Map,g=[];for(let e of p){let t=`${e.fromIndex}|${e.toIndex}|${e.label}|${e.isReturn?`r`:``}|${e.isAsync?`a`:``}`,n=h.get(t);if(n){n.count++;let t=e.label;g[n.idx]={...g[n.idx],label:`${t} ×${n.count}`}}else h.set(t,{idx:g.length,count:1}),g.push(e)}return{actors:c,messages:g}}function Lt(e,t){let n=[`%% Sequence Diagram — ${t}`,`sequenceDiagram`,` autonumber`];for(let t=0;t>`:t.isReturn?`-->>`:`->>`,n.push(` ${e}${a}${r}: ${i}`)}return n.join(`
+`)}var Rt=110,Q=52,zt=38,Bt=16;function Vt({diagram:e,title:t,theme:n=`dark`}){let[r,i]=(0,A.useState)(!1),[a,o]=(0,A.useState)(!1),s=(0,A.useRef)(null),c=n===`dark`,l=Bt*2+e.actors.length*Rt,u=Q+e.messages.length*zt+zt+Q,d=e=>Bt+e*Rt+Rt/2,f=e=>Q+e*zt+zt/2,p=c?`#e0e0e0`:`#1a1a1a`,m=c?`#888`:`#999`,h=c?`rgba(255,255,255,0.10)`:`rgba(0,0,0,0.12)`,g=c?`#0d0f14`:`#ffffff`,_=c?`rgba(255,255,255,0.35)`:`rgba(0,0,0,0.30)`,v=c?`seq-arrow-dark`:`seq-arrow-light`,y=c?`seq-arrow-return-dark`:`seq-arrow-return-light`,b=c?`seq-arrow-async-dark`:`seq-arrow-async-light`,x=c?`#a0aec0`:`#555`,S=c?`#b39ddb`:`#7c4dff`;return e.actors.length===0?(0,X.jsx)(`div`,{className:`flowchart-empty`,children:(0,X.jsx)(`span`,{children:`No sequence data available`})}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`div`,{className:`flowchart-export-bar`,children:[(0,X.jsxs)(`button`,{className:`flowchart-export-btn`,onClick:async()=>{if(s.current){o(!0);try{$e(await tt(s.current,g),`${(t??`sequence`).replace(/[^a-z0-9]/gi,`_`)}_sequence.png`)}finally{o(!1)}}},disabled:a,title:`Export as PNG`,children:[a?`⏳`:`🖼`,` PNG`]}),(0,X.jsx)(`button`,{className:`flowchart-export-btn`,onClick:()=>i(!0),title:`Export as Mermaid`,children:`🧜 Mermaid`})]}),(0,X.jsx)(`div`,{className:`seq-diagram-root`,ref:s,children:(0,X.jsxs)(`svg`,{className:`seq-diagram-svg`,viewBox:`0 0 ${l} ${u}`,width:`100%`,style:{background:g,display:`block`},xmlns:`http://www.w3.org/2000/svg`,children:[(0,X.jsxs)(`defs`,{children:[(0,X.jsx)(`marker`,{id:v,markerWidth:`8`,markerHeight:`6`,refX:`7`,refY:`3`,orient:`auto`,children:(0,X.jsx)(`polygon`,{points:`0 0, 8 3, 0 6`,fill:x})}),(0,X.jsx)(`marker`,{id:y,markerWidth:`8`,markerHeight:`6`,refX:`7`,refY:`3`,orient:`auto`,children:(0,X.jsx)(`polyline`,{points:`0 0, 8 3, 0 6`,fill:`none`,stroke:_,strokeWidth:`1.5`})}),(0,X.jsx)(`marker`,{id:b,markerWidth:`8`,markerHeight:`6`,refX:`7`,refY:`3`,orient:`auto`,children:(0,X.jsx)(`polygon`,{points:`0 0, 8 3, 0 6`,fill:S})})]}),e.actors.map((e,t)=>{let n=d(t),r=Rt-8,i=n-r/2,a=Math.floor(r/6.5),o=e.label.length>a?e.label.substring(0,a-1)+`…`:e.label;return(0,X.jsxs)(`g`,{children:[(0,X.jsx)(`rect`,{x:i,y:4,width:r,height:Q-10,rx:5,fill:c?`#1a1d24`:`#f5f5f5`,stroke:e.color,strokeWidth:1.5}),(0,X.jsx)(`text`,{x:n,y:Q/2-4,textAnchor:`middle`,dominantBaseline:`middle`,fontSize:10.5,fontFamily:`system-ui, sans-serif`,fill:e.color,fontWeight:`600`,children:o}),(0,X.jsx)(`text`,{x:n,y:Q-12,textAnchor:`middle`,dominantBaseline:`middle`,fontSize:8,fontFamily:`system-ui, sans-serif`,fill:m,children:e.type})]},e.id)}),e.actors.map((e,t)=>(0,X.jsx)(`line`,{x1:d(t),y1:Q,x2:d(t),y2:u-Q,stroke:h,strokeWidth:1,strokeDasharray:`4 4`},`life-${e.id}`)),e.actors.map((e,t)=>{let n=d(t),r=Rt-8,i=n-r/2,a=u-Q+4,o=Math.floor(r/6.5),s=e.label.length>o?e.label.substring(0,o-1)+`…`:e.label;return(0,X.jsxs)(`g`,{children:[(0,X.jsx)(`rect`,{x:i,y:a,width:r,height:Q-10,rx:5,fill:c?`#1a1d24`:`#f5f5f5`,stroke:e.color,strokeWidth:1.5}),(0,X.jsx)(`text`,{x:n,y:a+Q/2-8,textAnchor:`middle`,dominantBaseline:`middle`,fontSize:10.5,fontFamily:`system-ui, sans-serif`,fill:e.color,fontWeight:`600`,children:s}),(0,X.jsx)(`text`,{x:n,y:a+Q-18,textAnchor:`middle`,dominantBaseline:`middle`,fontSize:8,fontFamily:`system-ui, sans-serif`,fill:m,children:e.type})]},`bottom-${e.id}`)}),e.messages.map((e,t)=>{let n=f(t),r=d(e.fromIndex),i=d(e.toIndex),a=i>r,o=a?r+6:r-6,s=a?i-6:i+6,c=e.isReturn===!0,l=e.isAsync===!0,u=c?_:l?S:x,h=c?`5 3`:l?`6 3`:void 0,g=c?y:l?b:v,C=(r+i)/2,w=Math.abs(i-r)-12,T=Math.max(10,Math.floor(w/6)),E=e.label.length>T?e.label.substring(0,T-1)+`…`:e.label;return(0,X.jsxs)(`g`,{children:[(0,X.jsx)(`line`,{x1:o,y1:n,x2:s,y2:n,stroke:u,strokeWidth:c?1:1.5,strokeDasharray:h,markerEnd:`url(#${g})`}),e.label&&(0,X.jsx)(`text`,{x:C,y:n-6,textAnchor:`middle`,fontSize:9,fontFamily:`system-ui, sans-serif`,fill:c?m:p,opacity:c?.75:1,children:E})]},t)})]})}),r&&(0,X.jsx)(ot,{mermaidCode:Lt(e,t??`sequence`),filename:`${(t??`sequence`).replace(/[^a-z0-9]/gi,`_`)}_sequence.mmd`,title:t??`Sequence Diagram`,onClose:()=>i(!1)})]})}function Ht({diagram:e,title:t,theme:n,onClose:r}){return(0,A.useEffect)(()=>{let e=e=>{e.key===`Escape`&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r]),(0,X.jsx)(`div`,{className:`modal-overlay`,onClick:e=>{e.target===e.currentTarget&&r()},children:(0,X.jsxs)(`div`,{className:`modal-container modal-container--large`,children:[(0,X.jsxs)(`div`,{className:`modal-header`,children:[(0,X.jsxs)(`div`,{className:`modal-title`,children:[(0,X.jsx)(`span`,{className:`modal-icon`,children:`⇄`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:t}),(0,X.jsx)(`span`,{className:`modal-sub`,children:`Sequence Diagram`})]})]}),(0,X.jsx)(`button`,{className:`modal-close`,onClick:r,title:`Close (Esc)`,children:`×`})]}),(0,X.jsx)(`div`,{className:`modal-body sequence-modal-body`,children:(0,X.jsx)(Vt,{diagram:e,title:t,theme:n,compact:!1})})]})})}function $({content:e,children:t,placement:a=`top`,disabled:o=!1,className:s}){let[u,p]=(0,A.useState)(!1),{refs:v,floatingStyles:y,context:x}=m({open:o?!1:u,onOpenChange:p,placement:a,middleware:[_(8),l(),i({padding:8})],whileElementsMounted:b}),{getReferenceProps:S,getFloatingProps:C}=f([n(x,{move:!1,enabled:!o,delay:{open:280,close:80}}),c(x,{enabled:!o}),d(x),r(x,{role:`tooltip`})]),w=h([v.setReference]);return(0,A.isValidElement)(t)?(0,X.jsxs)(X.Fragment,{children:[(0,A.cloneElement)(t,{ref:w,...S()}),u&&!o&&(0,X.jsx)(g,{children:(0,X.jsx)(`div`,{ref:v.setFloating,style:y,className:[`floating-tooltip`,s].filter(Boolean).join(` `),...C(),children:e})})]}):(0,X.jsx)(X.Fragment,{children:t})}var Ut=360,Wt=640,Gt=380,Kt={entry_point:`#22D3EE`,entry_point_group:`#0E7490`,unreached_class:`#94A3B8`,unreached_group:`#475569`,route:`#4CAF50`,middleware:`#FF9800`,controller:`#2196F3`,action:`#03A9F4`,action_class:`#84cc16`,service:`#9C27B0`,validation_request:`#0d9488`,model:`#F44336`,event:`#FFD600`,job:`#607D8B`,command:`#14b8a6`,channel:`#8b5cf6`,schedule:`#f97316`,view:`#ec4899`,mail:`#f472b6`,notification:`#db2777`,enum:`#0ea5e9`,interface:`#38bdf8`,trait:`#a78bfa`,abstract_class:`#94a3b8`,service_provider:`#ca8a04`,facade:`#00BCD4`,ai_agent:`#A3E635`,ai_tool:`#65A30D`,filament_panel:`#7C3AED`,filament_resource:`#A855F7`,filament_page:`#C084FC`,filament_page_method:`#E879F9`,filament_widget:`#06B6D4`,filament_relation_manager:`#0891B2`};function qt(e){if(e===null)return`—`;if(e<1024)return`${e} B`;let t=[`KB`,`MB`,`GB`,`TB`],n=e/1024,r=0;for(;n>=1024&&r{e.preventDefault(),s.current=!0,c.current=e.clientX,l.current=a;let t=e=>{if(!s.current)return;let t=c.current-e.clientX;o(Math.min(Wt,Math.max(Ut,l.current+t)))},n=()=>{s.current=!1,window.removeEventListener(`mousemove`,t),window.removeEventListener(`mouseup`,n),document.body.style.cursor=``,document.body.style.userSelect=``};document.body.style.cursor=`col-resize`,document.body.style.userSelect=`none`,window.addEventListener(`mousemove`,t),window.addEventListener(`mouseup`,n)},[a]),[d,f]=(0,A.useState)(`info`),[p,m]=(0,A.useState)(!1),[h,g]=(0,A.useState)(!1),[_,v]=(0,A.useState)(!1),[y,b]=(0,A.useState)(!1),[x,S]=(0,A.useState)(!1),[C,w]=(0,A.useState)(e);e!==C&&(w(e),f(`info`),m(!1),g(!1),v(!1),b(!1),S(!1));let T=(0,A.useMemo)(()=>{let e=new Map;return t&&t.nodes.forEach(t=>e.set(t.id,t)),e},[t]),E=(0,A.useMemo)(()=>{let e=new Map;return t&&t.edges.forEach(t=>{let n=e.get(t.target)??[];n.push(t),e.set(t.target,n)}),e},[t]),D=(0,A.useMemo)(()=>{let e=new Map;return t&&t.edges.forEach(t=>{let n=e.get(t.source)??[];n.push(t),e.set(t.source,n)}),e},[t]),O=(0,A.useMemo)(()=>!t||!e||t.nodes.find(t=>t.id===e)?.type!==`route`?null:It(e,t),[e,t]),k=(0,A.useCallback)(async()=>{if(e){S(!0);try{let t=await fetch(`/_laravel-brain/api/context?nodeId=${encodeURIComponent(e)}&budget=6000`);if(!t.ok)throw Error(`Failed to fetch context`);let n=await t.text();await navigator.clipboard.writeText(n),b(!0),setTimeout(()=>b(!1),2500)}catch{alert(`Could not copy AI context.`)}finally{S(!1)}}},[e]);if(!t)return null;if(!e)return(0,X.jsxs)(`div`,{className:`sidebar-resizable`,style:{width:a},children:[(0,X.jsx)($,{content:`Drag to resize`,children:(0,X.jsx)(`div`,{className:`sidebar-drag-handle`,onMouseDown:u})}),(0,X.jsxs)(`div`,{className:`sidebar`,children:[(0,X.jsxs)(`div`,{className:`sidebar-header`,children:[(0,X.jsx)(`h2`,{children:t.meta.project}),(0,X.jsx)(`span`,{className:`sidebar-subtitle`,children:`Laravel Lifecycle Graph`})]}),(0,X.jsxs)(`div`,{className:`sidebar-stats`,children:[(0,X.jsx)($,{content:`Total symbols in this tab's JSON graph (routes, classes, views, …).`,children:(0,X.jsxs)(`div`,{className:`stat`,children:[(0,X.jsx)(`span`,{className:`stat-value`,children:t.meta.nodeCount}),(0,X.jsx)(`span`,{className:`stat-label`,children:`Nodes`})]})}),(0,X.jsx)($,{content:`Directed links between nodes: calls, type-hints, events, views, Eloquent relations, etc.`,children:(0,X.jsxs)(`div`,{className:`stat`,children:[(0,X.jsx)(`span`,{className:`stat-value`,children:t.meta.edgeCount}),(0,X.jsx)(`span`,{className:`stat-label`,children:`Edges`})]})}),(0,X.jsx)($,{content:`HTTP route entry nodes only (subset of all node types).`,children:(0,X.jsxs)(`div`,{className:`stat`,children:[(0,X.jsx)(`span`,{className:`stat-value`,children:t.nodes.filter(e=>e.type===`route`).length}),(0,X.jsx)(`span`,{className:`stat-label`,children:`Routes`})]})})]}),(0,X.jsx)($,{content:`The inspector shows details for the selected node: metrics, flow, source, and incoming/outgoing edges.`,children:(0,X.jsx)(`p`,{className:`sidebar-hint`,children:`Click any node to inspect it`})})]})]});let j=T.get(e);if(!j)return null;let M=E.get(e)??[],N=D.get(e)??[],P=j.data?.flowSteps??[],F=j.data?.file||null,ee=j.data?.line||void 0,te=Kt[j.type]??`#999`,I=j.data?.metrics,ne=!!j.data?.fatMethod,re=!!j.data?.fatClass,ie=!!j.data?.hasN1,L=typeof j.data?.deferredDefect==`string`?j.data.deferredDefect:null,R=typeof j.data?.deferredDefectMessage==`string`?j.data.deferredDefectMessage:``,ae=j.data?.dbQueries??[],le=j.data?.cacheOps??[],V=j.data?.httpCalls??[],ue=j.data?.relationships??[],de=j.type===`middleware`&&typeof j.data?.params==`string`&&j.data.params?j.data.params.split(`,`).map(e=>e.trim()).filter(Boolean):[],fe=j.data?.members??[],pe=j.data?.validationRules??[],me=Object.entries(j.data??{}).filter(([e,t])=>e!==`flowSteps`&&e!==`metrics`&&e!==`fatMethod`&&e!==`fatClass`&&e!==`hasN1`&&e!==`classMetrics`&&e!==`dbQueries`&&e!==`cacheOps`&&e!==`httpCalls`&&e!==`relationships`&&e!==`params`&&e!==`members`&&e!==`validationRules`&&e!==`security`&&e!==`erd`&&e!==`tableStats`&&e!==`schema`&&e!==`event`&&e!==`listener`&&e!==`job`&&e!==`deferredDefect`&&e!==`deferredDefectMessage`&&e!==`note`&&e!==`unfollowableReferences`&&!(Array.isArray(t)&&t.length===0)),H=j.data?.erd,he=j.data?.tableStats,U=j.data?.schema,W=j.data?.event,ge=j.data?.listener,G=j.data?.job,_e=typeof j.data?.note==`string`?j.data.note:``,K=Array.isArray(j.data?.unfollowableReferences)?j.data.unfollowableReferences:[],ve=P.length>0||!!O,q=!!F,ye=M.length>0||N.length>0,be=j.type===`route`,J=j.data?.security?j.data.security:null,Y=d===`flow`&&!ve||d===`source`&&!q||d===`edges`&&!ye||d===`stress`&&!be||d===`schema`&&!U||d===`risks`&&!be&&!J?`info`:d,xe=J?J.issues.length:0,Se=n===`light`?oe:z,Ce=[{id:`info`,label:`Info`,title:`Identity, type, smells, and code metrics (lines, cyclomatic complexity, …).`},...be||xe>0?[{id:`risks`,label:`Risks`,count:xe||void 0,alert:xe>0,title:`Findings that need attention: a route’s exposure and rate-limiting, or a table’s missing indexes.`}]:[],...U?[{id:`schema`,label:`Schema`,count:U.columns.length||void 0,title:`Columns, indexes and foreign keys as the database itself reports them.`}]:[],...ve?[{id:`flow`,label:`Flow`,title:`Control-flow steps through this method or request (and sequence diagram for routes).`}]:[],...ye?[{id:`edges`,label:`Edges`,count:M.length+N.length,title:`What calls or references this node (incoming) and what it calls (outgoing).`}]:[],{id:`usages`,label:`Usages`,title:`Where this symbol is referenced across the whole project, grouped by file.`},...q?[{id:`source`,label:`Source`,title:`Syntax-highlighted PHP source around this symbol.`}]:[],...be?[{id:`stress`,label:`Stress`,title:`Send HTTP requests against this route and inspect responses (dev only).`}]:[]];return(0,X.jsxs)(`div`,{className:`sidebar-resizable`,style:{width:a},children:[(0,X.jsx)($,{content:`Drag to resize`,children:(0,X.jsx)(`div`,{className:`sidebar-drag-handle`,onMouseDown:u})}),(0,X.jsxs)(`div`,{className:`sidebar`,children:[(0,X.jsxs)(`div`,{className:`sidebar-header`,children:[(0,X.jsxs)(`div`,{className:`sidebar-header-actions`,children:[(0,X.jsx)($,{content:`Copy AI context to clipboard`,children:(0,X.jsx)(`span`,{className:`tooltip-trigger-wrap`,children:(0,X.jsx)(`button`,{type:`button`,className:`flow-popup-btn sidebar-ai-btn`,onClick:k,disabled:x,children:x?`…`:y?`✓`:`🤖`})})}),(0,X.jsx)($,{content:`Clear selection (close inspector header)`,children:(0,X.jsx)(`button`,{className:`sidebar-close`,type:`button`,onClick:r,children:`×`})})]}),(0,X.jsxs)(`div`,{className:`sidebar-eyebrow`,children:[(0,X.jsx)(`span`,{className:`sidebar-eyebrow-dot`,style:{backgroundColor:te}}),(0,X.jsx)(`span`,{className:`sidebar-eyebrow-type`,children:j.type.replace(/_/g,` `)})]}),(0,X.jsx)(`h2`,{className:`sidebar-node-title`,children:j.label}),(0,X.jsxs)(`div`,{className:`sidebar-chips`,children:[J&&Se[J.exposure]&&(()=>{let e=Se[J.exposure];return(0,X.jsxs)(`span`,{className:`ins-chip`,style:{"--cc":e.accent},children:[`● `,e.label]})})(),J&&J.riskLevel!==`none`&&(0,X.jsxs)(`span`,{className:`ins-chip`,style:{"--cc":B[J.riskLevel]},children:[`⚠ `,se[J.riskLevel],` risk · `,xe]}),V.length>0&&(0,X.jsx)($,{content:`Leaves the application: ${V.map(e=>e.host||e.configKey||`computed address`).join(`, `)}`,children:(0,X.jsxs)(`span`,{className:`ins-chip ins-chip--http`,children:[`🌐 `,V.length,` outgoing`]})}),M.length+N.length>0&&(0,X.jsxs)(`span`,{className:`ins-chip ins-chip--neutral`,children:[`Edges `,M.length+N.length]}),F&&(0,X.jsxs)(`span`,{className:`ins-chip ins-chip--neutral`,title:F,children:[F.split(`/`).slice(-2).join(`/`),ee?` : ${ee}`:``]})]})]}),(ne||re||ie||L)&&(0,X.jsxs)(`div`,{className:`sidebar-smells`,children:[ie&&(0,X.jsx)($,{content:`N+1 Query: database query inside a loop`,children:(0,X.jsx)(`span`,{className:`smell-badge smell-badge--n1`,children:`⚠️ N+1 Query`})}),L&&(0,X.jsx)($,{content:R,children:(0,X.jsx)(`span`,{className:`smell-badge smell-badge--deferred`,children:L===`never-boots`?`⏳ Never boots`:L===`unbacked-provides`?`⏳ Unbacked provides()`:`⏳ $defer ignored`})}),ne&&(0,X.jsx)($,{content:`Fat Method: more than 30 lines or cyclomatic complexity > 10`,children:(0,X.jsx)(`span`,{className:`smell-badge smell-badge--fat-method`,children:`🧱 Fat Method`})}),re&&(0,X.jsx)($,{content:`Fat Class: more than 10 methods or 300+ total lines`,children:(0,X.jsx)(`span`,{className:`smell-badge smell-badge--fat-class`,children:`🏗️ Fat Class`})})]}),(0,X.jsx)(`div`,{className:`sidebar-tab-bar`,children:Ce.map(e=>(0,X.jsx)($,{content:e.title,children:(0,X.jsxs)(`button`,{type:`button`,className:`sidebar-tab${Y===e.id?` sidebar-tab--active`:``}`,onClick:()=>f(e.id),children:[e.label,e.count!==void 0&&(0,X.jsx)(`span`,{className:`sidebar-tab-badge${e.alert?` sidebar-tab-badge--alert`:``}`,children:e.count})]})},e.id))}),(0,X.jsxs)(`div`,{className:`sidebar-tab-content`,children:[Y===`info`&&(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`div`,{className:`ins-actions`,children:[(0,X.jsxs)(`button`,{type:`button`,className:`ins-action-btn`,disabled:!q,onClick:()=>f(`source`),children:[(0,X.jsxs)(`svg`,{className:`ins-action-icon`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`path`,{d:`M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6`}),(0,X.jsx)(`polyline`,{points:`15 3 21 3 21 9`}),(0,X.jsx)(`line`,{x1:`10`,y1:`14`,x2:`21`,y2:`3`})]}),`Open file`]}),(0,X.jsxs)(`button`,{type:`button`,className:`ins-action-btn`,onClick:()=>navigator.clipboard.writeText(String(j.data?.uri??j.label)),children:[(0,X.jsxs)(`svg`,{className:`ins-action-icon`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`rect`,{x:`9`,y:`9`,width:`13`,height:`13`,rx:`2`,ry:`2`}),(0,X.jsx)(`path`,{d:`M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1`})]}),`Copy URI`]})]}),(()=>{let e=I?.cyclomaticComplexity??0,t=N.length,n={none:0,low:25,medium:55,high:80,critical:100}[J?.riskLevel??`none`]??0;return(0,X.jsx)(`div`,{className:`ins-meters`,children:[{label:`Complexity`,value:e,pct:Math.min(100,e*6),tone:e>15?`var(--danger)`:e>10?`var(--warn)`:`var(--ok)`},{label:`Fan-out`,value:t,pct:Math.min(100,t*10),tone:t>8?`var(--danger)`:t>4?`var(--warn)`:`var(--ok)`},{label:`Risk`,value:xe,pct:n,tone:n>=80?`var(--danger)`:n>=55?`var(--warn)`:`var(--ok)`}].map(e=>(0,X.jsxs)(`div`,{className:`ins-meter`,children:[(0,X.jsx)(`span`,{className:`ins-meter-label`,children:e.label}),(0,X.jsx)(`span`,{className:`ins-meter-track`,children:(0,X.jsx)(`span`,{className:`ins-meter-fill`,style:{width:`${e.pct}%`,background:e.tone}})}),(0,X.jsx)(`span`,{className:`ins-meter-value`,children:e.value})]},e.label))})})(),I&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--metrics`,children:[(0,X.jsx)(`h3`,{children:`Code Metrics`}),(0,X.jsxs)(`div`,{className:`metrics-grid`,children:[(0,X.jsx)($,{content:`Physical lines of code in this method (approximate, from static analysis).`,children:(0,X.jsxs)(`div`,{className:`metric-item`,children:[(0,X.jsx)(`span`,{className:`metric-value`,children:I.lineCount}),(0,X.jsx)(`span`,{className:`metric-label`,children:`Lines`})]})}),(0,X.jsx)($,{content:`Cyclomatic complexity: decision paths (branches, loops, boolean operators). Rough guide: above 10 is harder to test; above 15 is very complex.`,children:(0,X.jsxs)(`div`,{className:`metric-item`,children:[(0,X.jsx)(`span`,{className:`metric-value`,style:{color:I.cyclomaticComplexity>10?`#FF6D00`:`inherit`},children:I.cyclomaticComplexity}),(0,X.jsx)(`span`,{className:`metric-label`,children:`Complexity`})]})}),(0,X.jsx)($,{content:`Executable statements counted in this method body.`,children:(0,X.jsxs)(`div`,{className:`metric-item`,children:[(0,X.jsx)(`span`,{className:`metric-value`,children:I.statementCount}),(0,X.jsx)(`span`,{className:`metric-label`,children:`Statements`})]})}),(0,X.jsx)($,{content:`Parameters on this function or method signature.`,children:(0,X.jsxs)(`div`,{className:`metric-item`,children:[(0,X.jsx)(`span`,{className:`metric-value`,children:I.paramCount}),(0,X.jsx)(`span`,{className:`metric-label`,children:`Params`})]})})]})]}),j.type===`filament_resource`&&!!j.data?.route&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Filament URL`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`route`}),(0,X.jsx)(`span`,{className:`prop-value`,style:{fontFamily:`monospace`,color:`#A855F7`},children:String(j.data.route)})]})]}),j.type===`ai_agent`&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Model & limits`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`model`}),(0,X.jsx)(`span`,{className:`prop-value`,style:{fontFamily:`monospace`,color:Xt},children:Zt(j.data)})]}),Qt.map(({key:e,label:t})=>j.data?.[e]===void 0?null:(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:t}),(0,X.jsx)(`span`,{className:`prop-value`,style:{fontFamily:`monospace`},children:String(j.data[e])})]},e)),$t.map(({key:e,label:t})=>j.data?.[e]?(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:t}),(0,X.jsx)(`span`,{className:`prop-value`,children:`yes`})]},e):null),Array.isArray(j.data?.methodOverrides)&&j.data.methodOverrides.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`overridable`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data.methodOverrides.join(`, `)})]}),typeof j.data?.shadowedModelAttribute==`string`&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#FF6D00`},children:`dead #[Model]`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[j.data.shadowedModelAttribute,` — a model() method is read instead`]})]}),typeof j.data?.shadowedProviderAttribute==`string`&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#FF6D00`},children:`dead #[Provider]`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[j.data.shadowedProviderAttribute,` — a provider() method is read instead`]})]}),Array.isArray(j.data?.contracts)&&j.data.contracts.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`contracts`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data.contracts.join(`, `)})]}),j.data?.toolsDecidedAtRuntime===!0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`tools()`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`decided at runtime — this agent has tools Brain cannot name from tools()`})]}),Array.isArray(j.data?.injectedTools)&&j.data.injectedTools.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`supplied tools`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[j.data.injectedTools.length,` handed to the constructor where the agent is built`]})]}),Array.isArray(j.data?.unwiredTools)&&j.data.unwiredTools.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#FF6D00`},children:`unwired tools`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[j.data.unwiredTools.map(e=>e.split(`\\`).pop()).join(`, `),` — tools() is never called without the HasTools contract`]})]}),Array.isArray(j.data?.unresolvedTools)&&j.data.unresolvedTools.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`unresolved tools`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data.unresolvedTools.join(`, `)})]})]}),j.type===`ai_tool`&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Tool`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`kind`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data?.toolKind===`mcp`?`MCP server tool`:`laravel/ai tool`})]}),typeof j.data?.description==`string`&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`description`}),(0,X.jsx)(`span`,{className:`prop-value`,children:j.data.description})]})]}),ue.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Relationships`}),ue.map((e,t)=>(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#9C27B0`},children:e.type}),(0,X.jsx)(`span`,{className:`prop-value`,children:e.related.split(`\\`).pop()??e.related})]},t))]}),de.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`ATTRIBUTES`}),de.map((e,t)=>(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,style:{color:`#FF9800`},children:t+1}),(0,X.jsx)(`span`,{className:`prop-value`,children:e})]},t))]}),pe.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--validation-rules`,children:[(0,X.jsx)(`h3`,{children:`Validation rules`}),(0,X.jsx)(`ul`,{className:`sidebar-structure-list`,children:pe.map((e,t)=>(0,X.jsxs)(`li`,{className:`sidebar-structure-item`,children:[(0,X.jsx)(`span`,{className:`structure-kind`,children:`field`}),(0,X.jsx)(`span`,{className:`structure-name`,children:e.field}),(0,X.jsx)(`span`,{className:`structure-value`,children:e.rules})]},t))})]}),ae.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--queries`,children:[(0,X.jsx)(`h3`,{children:`DB Queries`}),(0,X.jsx)(`div`,{className:`query-list`,children:ae.map((e,t)=>{let n=e.table||(e.model?e.model.split(`\\`).pop():`?`);return(0,X.jsxs)(`div`,{className:`query-item`,children:[(0,X.jsx)(`span`,{className:`query-op query-op--${[`insert`,`update`,`delete`,`statement`].includes(e.operation)?`write`:`read`}`,children:e.operation}),(0,X.jsx)(`span`,{className:`query-table`,title:e.model||void 0,children:n}),e.type===`raw`&&(0,X.jsx)(`span`,{className:`query-badge query-badge--raw`,children:`SQL`})]},t)})})]}),le.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--cache`,children:[(0,X.jsx)(`h3`,{children:`Cache`}),(0,X.jsx)(`div`,{className:`cache-list`,children:le.map((e,t)=>(0,X.jsxs)(`div`,{className:`cache-item`,children:[(0,X.jsxs)(`div`,{className:`cache-item-head`,children:[(0,X.jsx)($,{content:Yt[e.kind]??e.kind,children:(0,X.jsx)(`span`,{className:`cache-kind cache-kind--${e.kind}`,children:e.kind})}),(0,X.jsx)(`span`,{className:`cache-method`,children:e.method}),e.keyKind===`computed`?(0,X.jsx)($,{content:`The key is built at runtime, so it cannot be read from the source.`,children:(0,X.jsx)(`span`,{className:`cache-key cache-key--computed`,children:`computed key`})}):e.keyKind===`none`?(0,X.jsx)(`span`,{className:`cache-key cache-key--computed`,children:`whole store`}):(0,X.jsx)(`span`,{className:`cache-key cache-key--${e.keyKind}`,title:e.key,children:e.key})]}),(e.tags.length>0||e.store!==``||e.ttl!==null)&&(0,X.jsxs)(`div`,{className:`cache-item-meta`,children:[e.ttl!==null&&(0,X.jsxs)(`span`,{className:`cache-meta`,children:[`ttl `,e.ttl,`s`]}),e.store!==``&&(0,X.jsxs)(`span`,{className:`cache-meta`,children:[`store `,e.store]}),e.tags.map((e,t)=>(0,X.jsx)(`span`,{className:`cache-meta cache-meta--tag`,children:e},t))]})]},t))})]}),V.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--http`,children:[(0,X.jsx)(`h3`,{children:`Outgoing HTTP`}),(0,X.jsx)(`div`,{className:`http-list`,children:V.map((e,t)=>{let n=e.configKey?`config('${e.configKey}')${e.url}`:e.url||`address computed at runtime`;return(0,X.jsxs)(`div`,{className:`http-item`,children:[(0,X.jsxs)(`div`,{className:`http-item-head`,children:[(0,X.jsx)(`span`,{className:`http-method http-method--${(e.method||`unknown`).toLowerCase()}`,children:e.method||`REQUEST`}),(0,X.jsx)(`span`,{className:`http-target`,title:n,children:n})]}),(0,X.jsxs)(`div`,{className:`http-item-meta`,children:[(0,X.jsx)(`span`,{className:`http-badge http-badge--client`,children:e.client}),e.urlSource===`constructed`&&(0,X.jsx)($,{content:`The address starts with this literal and continues with something computed at runtime`,children:(0,X.jsx)(`span`,{className:`http-badge`,children:`partly computed`})}),e.async&&(0,X.jsx)(`span`,{className:`http-badge`,children:`async`}),e.timeout===null?(0,X.jsx)($,{content:`No timeout declared: this request waits as long as the third party takes`,children:(0,X.jsx)(`span`,{className:`http-badge http-badge--absent`,children:`no timeout`})}):(0,X.jsxs)(`span`,{className:`http-badge`,children:[`timeout `,e.timeout,`s`]}),e.retryTimes===null?(0,X.jsx)(`span`,{className:`http-badge http-badge--muted`,children:`no retry`}):(0,X.jsxs)(`span`,{className:`http-badge`,children:[`retry `,e.retryTimes,`×`,e.retrySleep===null?``:` / ${e.retrySleep}ms`]})]})]},t)})})]}),fe.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Structure`}),(0,X.jsx)(`ul`,{className:`sidebar-structure-list`,children:fe.map((e,t)=>(0,X.jsxs)(`li`,{className:`sidebar-structure-item`,children:[(0,X.jsx)(`span`,{className:`structure-kind`,children:String(e.kind??`item`)}),(0,X.jsx)(`span`,{className:`structure-name`,children:String(e.name??``)}),typeof e.declaringClass==`string`&&e.declaringClass!==``&&(0,X.jsx)(`span`,{className:`structure-decl`,title:`Declared on parent class`,children:e.declaringClass}),e.value!==void 0&&e.value!==null&&(0,X.jsx)(`span`,{className:`structure-value`,children:String(e.value)}),e.static===!0&&(0,X.jsx)(`span`,{className:`structure-flag`,children:`static`}),typeof e.visibility==`string`&&(0,X.jsx)(`span`,{className:`structure-vis`,children:e.visibility})]},t))})]}),he&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Table Data`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`rows`}),(0,X.jsx)(`span`,{className:`prop-value`,children:Jt(he.rows,he.rowsEstimated)})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`table`}),(0,X.jsx)(`span`,{className:`prop-value`,children:qt(he.tableBytes)})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`indexes`}),(0,X.jsx)(`span`,{className:`prop-value`,children:qt(he.indexBytes)})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`total`}),(0,X.jsx)(`span`,{className:`prop-value`,children:qt(he.totalBytes)})]})]}),W&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Event`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`listeners`}),(0,X.jsx)(`span`,{className:`prop-value`,children:W.orphan?`none — firing this does nothing`:`${W.listenerCount}`})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`dispatch`}),(0,X.jsx)(`span`,{className:`prop-value`,children:W.deferred?`after commit (ShouldDispatchAfterCommit)`:`immediate`})]}),W.broadcast&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`broadcast`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`yes`})]}),!W.orphan&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`before commit`}),(0,X.jsx)(`span`,{className:`prop-value`,children:W.observableBeforeCommit?`a listener can act before a surrounding transaction commits`:`no listener runs before the commit`})]}),W.properties?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`payload`}),(0,X.jsx)(`span`,{className:`prop-value`,children:W.properties.join(`, `)})]})]}),ge&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Listener`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`runs`}),(0,X.jsx)(`span`,{className:`prop-value`,children:ge.queued?`on a queue`:`in the dispatching request`})]}),ge.queued&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`waits for commit`}),(0,X.jsx)(`span`,{className:`prop-value`,children:ge.deferred?`yes (queue after_commit)`:`no`})]})]}),G&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Queue behaviour`}),G.tries!==null&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`attempts`}),(0,X.jsx)(`span`,{className:`prop-value`,children:G.tries})]}),G.timeout!==null&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`timeout`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[G.timeout,`s`]})]}),G.backoff!==null&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`backoff`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[G.backoff,`s`]})]}),G.maxExceptions!==null&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`max exceptions`}),(0,X.jsx)(`span`,{className:`prop-value`,children:G.maxExceptions})]}),G.unique&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`unique`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[G.uniqueUntilProcessing?`until it starts processing`:`while it is queued or running`,G.uniqueFor===null?``:` \u00b7 ${G.uniqueFor}s`]})]}),G.batchable&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`batch`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`runs as part of one`})]}),G.afterCommit&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`dispatch`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`after the transaction commits`})]}),G.encrypted&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`payload`}),(0,X.jsx)(`span`,{className:`prop-value`,children:`encrypted`})]}),G.middleware.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`middleware`}),(0,X.jsx)(`span`,{className:`prop-value`,children:G.middleware.join(`, `)})]}),G.dynamic.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`decided at runtime`}),(0,X.jsx)(`span`,{className:`prop-value`,children:G.dynamic.map(e=>`${e}()`).join(`, `)})]})]}),_e!==``&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`What this means`}),(0,X.jsx)(`p`,{className:`reachability-note`,children:_e}),K.length>0&&(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`p`,{className:`reachability-note`,children:`Brain did find this class referenced, in ways it cannot follow:`}),(0,X.jsx)(`ul`,{className:`reachability-references`,children:K.map(e=>(0,X.jsx)(`li`,{children:Ue[e]??e},e))})]})]}),H&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Model Schema`}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`table`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.table||`—`})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`primary key`}),(0,X.jsxs)(`span`,{className:`prop-value`,children:[H.primaryKey,` (`,H.keyType,`)`]})]}),H.morphAlias&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`morph alias`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.morphAlias})]}),!H.morphAlias&&H.morphAliasMissing&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`morph alias`}),(0,X.jsx)(`span`,{className:`prop-value prop-value--warn`,children:`none — this app enforces a morph map`})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`timestamps`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.timestamps?`yes`:`no`})]}),(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`soft deletes`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.softDeletes?`yes`:`no`})]}),H.fillable?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`fillable`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.fillable.join(`, `)})]}),H.guarded?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`guarded`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.guarded.join(`, `)})]}),Object.keys(H.casts??{}).length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`casts`}),(0,X.jsx)(`span`,{className:`prop-value`,children:Object.entries(H.casts).map(([e,t])=>`${e}: ${t}`).join(`, `)})]}),H.dates?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`dates`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.dates.join(`, `)})]}),H.appends?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`appends`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.appends.join(`, `)})]}),H.accessors?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`accessors`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.accessors.join(`, `)})]}),H.relationships?.length>0&&(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`relationships`}),(0,X.jsx)(`span`,{className:`prop-value`,children:H.relationships.map(e=>`${e.type}(${e.related})`).join(`, `)})]})]}),(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsx)(`h3`,{children:`Properties`}),me.map(([e,t])=>(0,X.jsxs)(`div`,{className:`prop-row`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:e}),(0,X.jsx)(`span`,{className:`prop-value`,children:Array.isArray(t)?t.map(e=>typeof e==`object`&&e?Object.values(e).join(` `):String(e)).join(`, `)||`—`:String(t)||`—`})]},e))]})]}),Y===`flow`&&(0,X.jsxs)(X.Fragment,{children:[P.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--flowchart`,children:[(0,X.jsxs)(`div`,{className:`sidebar-section-header`,children:[(0,X.jsx)(`h3`,{children:`Method Flow`}),(0,X.jsx)(`button`,{className:`flow-popup-btn`,title:`Open in large view`,onClick:()=>m(!0),children:`⤢`})]}),(0,X.jsx)(st,{steps:P,isFatMethod:ne}),p&&(0,X.jsx)(mt,{steps:P,title:j.label,isFatMethod:ne,onClose:()=>m(!1)})]}),O&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--sequence`,children:[(0,X.jsxs)(`div`,{className:`sidebar-section-header`,children:[(0,X.jsx)(`h3`,{children:`Sequence Diagram`}),(0,X.jsx)(`button`,{className:`flow-popup-btn`,title:`Open in large view`,onClick:()=>v(!0),children:`⤢`})]}),(0,X.jsx)(Vt,{diagram:O,title:j.label,theme:n}),_&&(0,X.jsx)(Ht,{diagram:O,title:j.label,theme:n,onClose:()=>v(!1)})]})]}),Y===`source`&&F&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--source`,children:[(0,X.jsxs)(`div`,{className:`sidebar-section-header`,children:[(0,X.jsx)(`h3`,{children:`Source Code`}),(0,X.jsx)(`button`,{className:`flow-popup-btn`,title:`Open in large view`,onClick:()=>g(!0),children:`⤢`})]}),(0,X.jsx)(gt,{filePath:F,highlightLine:ee,theme:n}),h&&(0,X.jsx)(_t,{filePath:F,highlightLine:ee,theme:n,onClose:()=>g(!1)})]}),Y===`edges`&&(0,X.jsxs)(X.Fragment,{children:[N.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Outgoing (`,N.length,`)`]}),N.map(e=>{let t=T.get(e.target);return(0,X.jsxs)(`div`,{className:`edge-row`,children:[(0,X.jsx)(`span`,{className:`edge-label`,children:e.label}),(0,X.jsx)(`span`,{className:`edge-target`,children:t?.label??e.target})]},e.id)})]}),M.length>0&&(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Incoming (`,M.length,`)`]}),M.map(e=>(0,X.jsxs)(`div`,{className:`edge-row`,children:[(0,X.jsx)(`span`,{className:`edge-target`,children:T.get(e.source)?.label??e.source}),(0,X.jsx)(`span`,{className:`edge-label`,children:e.label})]},e.id))]})]}),Y===`schema`&&U&&(0,X.jsxs)(`div`,{children:[(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Columns `,(0,X.jsx)(`span`,{className:`section-count`,children:U.columns.length})]}),(0,X.jsx)(`div`,{className:`schema-table`,children:U.columns.map(e=>(0,X.jsxs)(`div`,{className:`schema-row`,children:[(0,X.jsx)(`span`,{className:`schema-name`,children:e.name}),(0,X.jsx)(`span`,{className:`schema-type`,children:e.type}),(0,X.jsxs)(`span`,{className:`schema-flags`,children:[e.autoIncrement&&(0,X.jsx)(`span`,{className:`schema-flag`,children:`auto`}),e.nullable?(0,X.jsx)(`span`,{className:`schema-flag schema-flag--muted`,children:`null`}):(0,X.jsx)(`span`,{className:`schema-flag`,children:`not null`}),e.default!==null&&(0,X.jsxs)(`span`,{className:`schema-flag schema-flag--muted`,children:[`= `,e.default]})]})]},e.name))})]}),(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Indexes `,(0,X.jsx)(`span`,{className:`section-count`,children:U.indexes.length})]}),U.indexes.length===0&&(0,X.jsx)(`div`,{className:`sidebar-empty`,children:`No indexes.`}),(0,X.jsx)(`div`,{className:`schema-table`,children:U.indexes.map(e=>(0,X.jsxs)(`div`,{className:`schema-row`,children:[(0,X.jsx)(`span`,{className:`schema-name`,children:e.columns.join(`, `)}),(0,X.jsx)(`span`,{className:`schema-type`,children:e.name}),(0,X.jsxs)(`span`,{className:`schema-flags`,children:[e.primary&&(0,X.jsx)(`span`,{className:`schema-flag`,children:`primary`}),e.unique&&!e.primary&&(0,X.jsx)(`span`,{className:`schema-flag`,children:`unique`})]})]},e.name))})]}),(0,X.jsxs)(`div`,{className:`sidebar-section`,children:[(0,X.jsxs)(`h3`,{children:[`Foreign keys `,(0,X.jsx)(`span`,{className:`section-count`,children:U.foreignKeys.length})]}),U.foreignKeys.length===0&&(0,X.jsx)(`div`,{className:`sidebar-empty`,children:`No foreign keys.`}),(0,X.jsx)(`div`,{className:`schema-table`,children:U.foreignKeys.map(e=>{let t=U.indexes.some(t=>t.columns.slice(0,e.columns.length).join(`\0`)===e.columns.join(`\0`));return(0,X.jsxs)(`div`,{className:`schema-row${t?``:` schema-row--flagged`}`,children:[(0,X.jsx)(`span`,{className:`schema-name`,children:e.columns.join(`, `)}),(0,X.jsxs)(`span`,{className:`schema-type`,children:[`→ `,e.foreignTable,`.`,e.foreignColumns.join(`, `)]}),(0,X.jsxs)(`span`,{className:`schema-flags`,children:[e.onDelete&&e.onDelete!==`no action`&&(0,X.jsxs)(`span`,{className:`schema-flag schema-flag--muted`,children:[`on delete `,e.onDelete]}),!t&&(0,X.jsx)(`span`,{className:`schema-flag schema-flag--warn`,children:`no index`})]})]},e.name)})})]})]}),Y===`usages`&&e&&(0,X.jsx)(yt,{nodeId:e}),Y===`risks`&&J&&(0,X.jsxs)(`div`,{className:`sidebar-section sidebar-section--security`,children:[Se[J.exposure]&&(()=>{let e=Se[J.exposure],t={public:`This route is publicly accessible — no authentication middleware detected.`,guest:`This route is for unauthenticated users and redirects authenticated ones away.`,authed:`This route requires authentication (auth / sanctum / jwt / passport).`,admin:`This route requires elevated permissions (can:, role:, permission:, ability:, gate:).`};return(0,X.jsxs)(`div`,{className:`security-exposure-card`,style:{borderColor:e.border,background:e.bg+`88`},children:[(0,X.jsx)(`div`,{className:`security-exposure-header`,children:(0,X.jsxs)(`span`,{className:`security-exposure-badge`,style:{color:e.accent},children:[`🔒 `,e.label,` Route`]})}),(0,X.jsx)(`p`,{className:`security-exposure-desc`,children:t[J.exposure]??t.public})]})})(),J.issues.length===0?(0,X.jsxs)(`div`,{className:`security-clean`,children:[(0,X.jsx)(`span`,{style:{color:B.none},children:`✓`}),` Nothing flagged here.`]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`div`,{className:`security-issues-title`,children:[J.issues.length,` Issue`,J.issues.length===1?``:`s`,` Detected`]}),J.issues.map((e,t)=>{let n=ce[e.type]??{icon:`•`,name:e.type},r=B[e.severity]??B.medium;return(0,X.jsxs)(`div`,{className:`security-issue-card`,style:{borderLeftColor:r},children:[(0,X.jsxs)(`div`,{className:`security-issue-header`,children:[(0,X.jsx)(`span`,{className:`security-issue-icon`,children:n.icon}),(0,X.jsx)(`span`,{className:`security-issue-name`,style:{color:r},children:n.name}),(0,X.jsx)(`span`,{className:`security-issue-severity`,style:{color:r},children:e.severity.toUpperCase()})]}),(0,X.jsx)(`p`,{className:`security-issue-message`,children:e.message}),e.file&&(0,X.jsxs)(`div`,{className:`security-issue-location`,children:[(0,X.jsx)(`span`,{className:`prop-key`,children:`file`}),(0,X.jsxs)(`span`,{className:`prop-val`,title:e.file,children:[`…`,e.file.split(`/`).slice(-2).join(`/`),e.line?`:${e.line}`:``]})]})]},t)})]})]}),Y===`risks`&&be&&!J&&(0,X.jsx)(`div`,{className:`sidebar-section`,children:(0,X.jsxs)(`p`,{style:{opacity:.6,fontSize:13},children:[`Security data not available. Re-run `,(0,X.jsx)(`code`,{children:`brain:scan`}),` to generate it.`]})}),Y===`stress`&&be&&e&&(0,X.jsx)(kt,{method:String(j.data?.method??`GET`),uri:String(j.data?.uri??`/`),theme:n,selectedId:e,onStressChange:i},e)]})]})]})}var tn=[{id:`claude`,label:`Claude Code`,path:`CLAUDE.md`,icon:`🟠`,description:`Anthropic Claude Code CLI & IDE`},{id:`cursor`,label:`Cursor`,path:`.cursor/rules/laravel-brain.mdc`,icon:`⬛`,description:`Cursor AI editor (MDC format with frontmatter)`},{id:`windsurf`,label:`Windsurf`,path:`.windsurf/rules/laravel-brain.md`,icon:`🌊`,description:`Windsurf by Codeium`},{id:`copilot`,label:`GitHub Copilot`,path:`.github/copilot-instructions.md`,icon:`🐙`,description:`Applied repo-wide automatically`},{id:`junie`,label:`JetBrains Junie`,path:`.junie/guidelines.md`,icon:`🧠`,description:`JetBrains AI assistant`},{id:`aider`,label:`Aider`,path:`CONVENTIONS.md`,icon:`⌨️`,description:`Load with: aider --read CONVENTIONS.md`},{id:`agents`,label:`AGENTS.md`,path:`AGENTS.md`,icon:`🌐`,description:`Universal open standard — 60+ tools`},{id:`codex`,label:`OpenAI Codex`,path:`CODEX.md`,icon:`🟢`,description:`Load with: codex --context CODEX.md`}];function nn({onClose:e}){let[t,n]=(0,A.useState)(new Set(tn.map(e=>e.id))),[r,i]=(0,A.useState)({}),[a,o]=(0,A.useState)(!1),[s,c]=(0,A.useState)(null),l=(0,A.useCallback)(e=>{n(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),u=(0,A.useCallback)(()=>n(new Set(tn.map(e=>e.id))),[]),d=(0,A.useCallback)(()=>n(new Set),[]),f=(0,A.useCallback)(async e=>{o(!0),c(null);let n={};t.forEach(e=>{n[e]={status:`generating`}}),i(n);try{let n=await fetch(`/_laravel-brain/api/generate-rules`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({targets:[...t],force:e})}),r=await n.json();if(n.status===409&&r.existing){c(r.existing);let e={};t.forEach(t=>{e[t]={status:`idle`}}),i(e);return}if(!n.ok){let e=r.error??`Generation failed`,n={};t.forEach(t=>{n[t]={status:`error`,error:e}}),i(n);return}let a={};for(let e of r.results??[])a[e.target]=e.success?{status:`success`,path:e.path}:{status:`error`,error:e.error??`Unknown error`};i(a)}catch{let e={};t.forEach(t=>{e[t]={status:`error`,error:`Network error`}}),i(e)}finally{o(!1)}},[t]),p=(0,A.useCallback)(()=>f(!1),[f]),m=(0,A.useCallback)(()=>f(!0),[f]),h=(0,A.useCallback)(()=>c(null),[]),g=Object.values(r).filter(e=>e.status===`success`).length,_=Object.values(r).filter(e=>e.status===`error`).length,v=g+_>0;return(0,X.jsx)(`div`,{className:`export-overlay`,onClick:t=>{t.target===t.currentTarget&&e()},children:(0,X.jsxs)(`div`,{className:`export-modal ai-rules-modal`,children:[(0,X.jsxs)(`div`,{className:`export-modal-header`,children:[(0,X.jsxs)(`div`,{className:`export-modal-title`,children:[(0,X.jsx)(`span`,{className:`export-modal-icon`,children:`🤖`}),(0,X.jsxs)(`div`,{children:[(0,X.jsx)(`h2`,{children:`Generate AI Rules Files`}),(0,X.jsx)(`div`,{className:`export-modal-sub`,children:`Write context files for AI coding assistants into your project`})]})]}),(0,X.jsx)(`button`,{className:`export-modal-close`,onClick:e,children:`×`})]}),s&&(0,X.jsxs)(`div`,{className:`ai-rules-overwrite-banner`,children:[(0,X.jsx)(`div`,{className:`ai-rules-overwrite-icon`,children:`⚠️`}),(0,X.jsxs)(`div`,{className:`ai-rules-overwrite-body`,children:[(0,X.jsxs)(`strong`,{children:[`The following file`,s.length===1?``:`s`,` already exist`,s.length===1?`s`:``,`:`]}),(0,X.jsx)(`ul`,{className:`ai-rules-overwrite-list`,children:s.map(e=>(0,X.jsx)(`li`,{children:(0,X.jsx)(`code`,{children:e.path})},e.target))}),(0,X.jsxs)(`span`,{children:[`Do you want to overwrite `,s.length===1?`it`:`them`,`?`]})]}),(0,X.jsxs)(`div`,{className:`ai-rules-overwrite-actions`,children:[(0,X.jsx)(`button`,{className:`export-btn export-btn--secondary`,onClick:h,children:`Cancel`}),(0,X.jsx)(`button`,{className:`export-btn export-btn--danger`,onClick:m,children:`Overwrite`})]})]}),(0,X.jsxs)(`div`,{className:`ai-rules-select-bar`,children:[(0,X.jsxs)(`span`,{className:`ai-rules-select-label`,children:[t.size,` of `,tn.length,` selected`]}),(0,X.jsx)(`button`,{className:`ai-rules-select-link`,onClick:u,children:`All`}),(0,X.jsx)(`span`,{className:`ai-rules-select-sep`,children:`·`}),(0,X.jsx)(`button`,{className:`ai-rules-select-link`,onClick:d,children:`None`})]}),(0,X.jsx)(`div`,{className:`ai-rules-grid`,children:tn.map(e=>{let n=t.has(e.id),i=r[e.id];return(0,X.jsxs)(`label`,{className:`ai-rules-card ${n?`ai-rules-card--selected`:``} ${a?`ai-rules-card--disabled`:``}`,children:[(0,X.jsx)(`input`,{type:`checkbox`,className:`ai-rules-checkbox`,checked:n,disabled:a,onChange:()=>l(e.id)}),(0,X.jsx)(`span`,{className:`ai-rules-card-icon`,children:e.icon}),(0,X.jsxs)(`div`,{className:`ai-rules-card-body`,children:[(0,X.jsx)(`span`,{className:`ai-rules-card-label`,children:e.label}),(0,X.jsx)(`code`,{className:`ai-rules-card-path`,children:e.path}),(0,X.jsx)(`span`,{className:`ai-rules-card-desc`,children:e.description})]}),(0,X.jsxs)(`div`,{className:`ai-rules-card-status`,children:[i?.status===`generating`&&(0,X.jsx)(`span`,{className:`ai-rules-status ai-rules-status--spinning`,children:`⏳`}),i?.status===`success`&&(0,X.jsx)(`span`,{className:`ai-rules-status ai-rules-status--ok`,title:i.path,children:`✓`}),i?.status===`error`&&(0,X.jsx)(`span`,{className:`ai-rules-status ai-rules-status--err`,title:i.error,children:`✗`})]})]},e.id)})}),v&&(0,X.jsxs)(`div`,{className:`ai-rules-summary`,children:[g>0&&(0,X.jsxs)(`span`,{className:`ai-rules-summary--ok`,children:[`✓ `,g,` file`,g===1?``:`s`,` written`]}),_>0&&(0,X.jsxs)(`span`,{className:`ai-rules-summary--err`,children:[`✗ `,_,` error`,_===1?``:`s`]})]}),(0,X.jsxs)(`div`,{className:`ai-rules-footer`,children:[(0,X.jsx)(`button`,{className:`export-btn export-btn--secondary`,onClick:e,disabled:a,children:v?`Close`:`Cancel`}),(0,X.jsx)(`button`,{className:`export-btn export-btn--primary ${a?`export-btn--loading`:``}`,onClick:p,disabled:a||t.size===0,children:a?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`span`,{className:`btn-spinner btn-spinner--small`}),` Generating…`]}):`Generate ${t.size>0?t.size:``} File${t.size===1?``:`s`}`})]})]})})}function rn(e){let t=Math.floor(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);if(n<60)return`${n}m`;let r=Math.floor(n/60);return r<24?`${r}h`:`${Math.floor(r/24)}d`}function an({label:e,active:t,children:n}){let[r,i]=(0,A.useState)(!1),a=(0,A.useRef)(null);return(0,A.useEffect)(()=>{let e=e=>{a.current&&!a.current.contains(e.target)&&i(!1)};return document.addEventListener(`mousedown`,e,!0),()=>document.removeEventListener(`mousedown`,e,!0)},[]),(0,X.jsxs)(`div`,{className:`seg-dropdown`,ref:a,children:[(0,X.jsx)(`button`,{type:`button`,className:`seg-btn ${t||r?`seg-btn--active`:``}`,onClick:()=>i(!r),children:e}),r&&(0,X.jsx)(`div`,{className:`seg-dropdown-menu`,children:n})]})}function on({nodeCount:e,edgeCount:t,visibleCount:n,activeTabLabel:r,graphData:i,analyzedAt:a,highRiskCount:o,onOpenRisks:s,theme:c,onSearch:l,onToggleTheme:u,graphRef:d}){let[f,p]=(0,A.useState)(``),[m,h]=(0,A.useState)(!1),[g,_]=(0,A.useState)(!1),[v,y]=(0,A.useState)(!1),b=(0,A.useRef)(null),x=(0,A.useRef)(null);(0,A.useEffect)(()=>(b.current&&clearTimeout(b.current),b.current=setTimeout(()=>l(f),250),()=>{b.current&&clearTimeout(b.current)}),[f,l]),(0,A.useEffect)(()=>{let e=e=>{(e.metaKey||e.ctrlKey)&&e.key.toLowerCase()===`k`?(e.preventDefault(),x.current?.focus(),x.current?.select()):e.key===`Escape`&&document.activeElement===x.current&&x.current?.blur()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]);let S=()=>{d.current?.toPng({scale:2}).then(e=>{e&&$e(e,`${r.replace(/[^a-z0-9]/gi,`_`)}_graph.png`)})},C=()=>{i&&h(!0)},w=async()=>{if(window.confirm(`This will re-scan the entire project. Proceed?`)){y(!0);try{(await fetch(`/_laravel-brain/api/scan`,{method:`POST`})).ok?window.location.reload():alert(`Scan failed.`)}catch{alert(`Scan failed.`)}finally{y(!1)}}},[T,E]=(0,A.useState)(()=>Date.now());(0,A.useEffect)(()=>{let e=setInterval(()=>E(Date.now()),6e4);return()=>clearInterval(e)},[]);let D=(0,A.useMemo)(()=>a?`scanned ${rn(T-new Date(a).getTime())} ago`:null,[a,T]);return(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`div`,{className:`toolbar`,children:[(0,X.jsxs)(`div`,{className:`toolbar-brand`,children:[(0,X.jsx)(`img`,{src:`/_laravel-brain/logo.png`,alt:`Laravel Brain`,className:`toolbar-logo-img`,width:28,height:28,decoding:`async`}),(0,X.jsxs)(`div`,{className:`toolbar-brand-text`,children:[(0,X.jsx)(`span`,{className:`toolbar-brand-name`,children:`Laravel Brain`}),D&&(0,X.jsx)(`span`,{className:`toolbar-brand-sub`,children:D})]})]}),(0,X.jsxs)(`div`,{className:`toolbar-center`,children:[(0,X.jsxs)(`div`,{className:`toolbar-search-wrapper`,children:[(0,X.jsxs)(`svg`,{className:`toolbar-search-icon`,width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`circle`,{cx:`11`,cy:`11`,r:`8`}),(0,X.jsx)(`line`,{x1:`21`,y1:`21`,x2:`16.65`,y2:`16.65`})]}),(0,X.jsx)(`input`,{ref:x,type:`search`,placeholder:`Search routes, nodes, files…`,className:`toolbar-search`,value:f,onChange:e=>p(e.target.value)}),(0,X.jsx)(`kbd`,{className:`toolbar-kbd`,children:`⌘K`})]}),(0,X.jsx)($,{content:`Routes flagged high or critical risk. Click to open the Risks list.`,children:(0,X.jsxs)(`button`,{type:`button`,className:`risk-pill ${o>0?`risk-pill--alert`:``}`,onClick:s,children:[(0,X.jsx)(`span`,{className:`risk-pill-dot`}),`High-risk`,(0,X.jsx)(`span`,{className:`risk-pill-count`,children:o})]})}),e>80&&(0,X.jsx)($,{content:`Large graph: dagre auto-switched to breadthfirst`,children:(0,X.jsx)(`span`,{className:`stat-chip stat-chip--warn`,children:`⚠ large`})}),(0,X.jsx)($,{content:`Nodes / edges in this graph (visible respects type filters).`,children:(0,X.jsxs)(`span`,{className:`stat-chip`,children:[n,`/`,e,` · `,t,`e`]})})]}),(0,X.jsxs)(`div`,{className:`toolbar-right`,children:[(0,X.jsx)($,{content:c===`dark`?`Switch to light mode`:`Switch to dark mode`,children:(0,X.jsx)(`button`,{type:`button`,onClick:u,className:`icon-btn`,children:c===`dark`?`☀`:`☾`})}),(0,X.jsxs)(an,{label:`↧`,children:[(0,X.jsx)(`button`,{type:`button`,onClick:S,className:`seg-menu-btn`,children:`Download PNG`}),(0,X.jsx)(`button`,{type:`button`,onClick:C,className:`seg-menu-btn`,disabled:!i,children:`Copy Mermaid`}),(0,X.jsx)(`button`,{type:`button`,onClick:()=>_(!0),className:`seg-menu-btn`,children:`Generate AI Rules`})]}),(0,X.jsx)(`button`,{type:`button`,onClick:w,className:`rescan-btn ${v?`rescan-btn--loading`:``}`,disabled:v,"aria-busy":v,children:v?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`span`,{className:`btn-spinner btn-spinner--small`,"aria-hidden":!0}),(0,X.jsx)(`span`,{children:`Scanning…`})]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`path`,{d:`M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`}),(0,X.jsx)(`path`,{d:`M3 3v5h5`}),(0,X.jsx)(`path`,{d:`M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16`}),(0,X.jsx)(`path`,{d:`M16 16h5v5`})]}),(0,X.jsx)(`span`,{children:`Re-scan`})]})})]})]}),g&&(0,X.jsx)(nn,{onClose:()=>_(!1)}),m&&i&&(0,X.jsx)(ot,{mermaidCode:Ye(i,r),filename:`${r.replace(/[^a-z0-9]/gi,`_`)}_graph.mmd`,title:`${r} — Full Lifecycle Graph`,onClose:()=>h(!1)})]})}var sn={route:`Routes`,middleware:`Middleware`,controller:`Controllers`,livewire_component:`Livewire`,action:`Controller actions`,action_class:`Actions`,service:`Services`,validation_request:`Validation`,model:`Models`,event:`Events`,listener:`Listeners`,job:`Jobs`,command:`Commands`,channel:`Channels`,schedule:`Schedules`,view:`Views`,mail:`Mail`,notification:`Notifications`,enum:`Enums`,interface:`Interfaces`,trait:`Traits`,abstract_class:`Abstract`,service_provider:`Providers`,facade:`Facades`,ai_agent:`AI Agents`,ai_tool:`AI Tools`,filament_panel:`F. Panels`,filament_resource:`F. Resources`,filament_page:`F. Pages`,filament_page_method:`F. Methods`,filament_widget:`F. Widgets`,filament_relation_manager:`F. Relations`,entry_point:`Entry points`,entry_point_group:`Entry groups`,unreached_class:`Not reached`,unreached_group:`Unreached groups`},cn=`route.middleware.controller.livewire_component.action.action_class.service.validation_request.model.event.listener.job.command.channel.schedule.view.mail.notification.enum.interface.trait.abstract_class.service_provider.facade.ai_agent.ai_tool.filament_panel.filament_resource.filament_page.filament_page_method.filament_widget.filament_relation_manager.entry_point.entry_point_group.unreached_class.unreached_group`.split(`.`),ln=[{type:`transaction`,label:`Transactions`,description:`the boundary drawn around work that runs in one transaction`},{type:`chain`,label:`Chains`,description:`the boundary and the arrows drawn around jobs that run one after another`},{type:`batch`,label:`Batches`,description:`the boundary drawn around jobs dispatched together, in no particular order`}];function un({visibleTypes:e,counts:t,onToggle:n,onShowAll:r,onHideAll:i}){let a=cn.filter(e=>(t[e]??0)>0),o=new Map(ln.map(e=>[e.type,e]));for(let e of ln)(t[e.type]??0)>0&&a.push(e.type);return(0,X.jsxs)(`div`,{className:`show-graph`,children:[(0,X.jsxs)(`div`,{className:`show-graph-header`,children:[(0,X.jsx)(`span`,{className:`show-graph-title`,children:`Show on graph`}),(0,X.jsxs)(`div`,{className:`show-graph-actions`,children:[(0,X.jsx)(`button`,{type:`button`,onClick:r,className:`show-graph-link`,children:`All`}),(0,X.jsx)(`span`,{className:`show-graph-sep`,children:`/`}),(0,X.jsx)(`button`,{type:`button`,onClick:i,className:`show-graph-link`,children:`None`})]})]}),(0,X.jsx)(`div`,{className:`show-graph-grid`,children:a.map(r=>{let i=t[r]??0,a=e.has(r),s=o.get(r),c=s?I[r]??`#94a3b8`:re[r]??`#94a3b8`,l=s?.label??sn[r]??r;return(0,X.jsx)($,{content:s?`${a?`Hide`:`Show`} ${s.description}`:`${a?`Hide`:`Show`} ${l} nodes`,children:(0,X.jsxs)(`button`,{type:`button`,className:`show-graph-item ${a?``:`show-graph-item--off`}`,onClick:()=>n(r),children:[(0,X.jsx)(`span`,{className:`show-graph-dot`,style:{backgroundColor:c}}),(0,X.jsx)(`span`,{className:`show-graph-label`,children:l}),(0,X.jsx)(`span`,{className:`show-graph-count`,children:i})]})},r)})})]})}var dn={none:0,low:1,medium:2,high:3,critical:4},fn=280,pn=480,mn=300,hn={GET:`#4ade80`,POST:`#60a5fa`,PUT:`#f59e0b`,PATCH:`#a78bfa`,DELETE:`#f87171`,OPTIONS:`#22d3ee`,QUERY:`#f472b6`},gn=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`OPTIONS`,`QUERY`];function _n(e){let[t,...n]=e.split(` `);return t in hn?{method:t,uri:n.join(` `)}:{method:null,uri:e}}function vn(e){let t=e.schedule,n=t?` ${t.cadence} ${t.timezone} ${t.modifiers.join(` `)}`:``;return`${e.label}${n}`.toLowerCase()}function yn(e){return e.riskLevel??`none`}function bn(e){let t=[];e.securityCount&&t.push(`${e.securityCount} security`),e.n1Count&&t.push(`${e.n1Count} N+1`);let n=(e.fatMethodCount??0)+(e.fatClassCount??0);return n&&t.push(`${n} fat`),t.length?t.join(` · `):`flagged for review`}function xn(e){if(!e)return`new`;let t=Date.now()-new Date(e).getTime(),n=Math.floor(t/6e4);if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function Sn({tab:e,isActive:t,isLoading:n,onSelect:r}){let{method:i,uri:a}=_n(e.label),o=i?hn[i]:`var(--faint)`,s=yn(e),c=s===`high`||s===`critical`?`var(--danger)`:e.issueCount?`var(--warn)`:null;return(0,X.jsx)($,{content:`Open lifecycle graph · ${e.nodeCount} nodes · ${e.edgeCount} edges`,children:(0,X.jsxs)(`button`,{className:`route-row ${t?`route-row--active`:``}`,type:`button`,onClick:()=>r(e),children:[(0,X.jsx)(`span`,{className:`route-row-method`,style:{color:o},children:i??`›`}),(0,X.jsx)(`span`,{className:`route-row-uri`,children:a}),c&&(0,X.jsx)(`span`,{className:`route-row-risk`,style:{"--rc":c},children:e.issueCount}),n&&(0,X.jsx)(`span`,{className:`route-row-loading`,children:`…`})]})})}var Cn={command:`CMD`,job:`JOB`,call:`FN`},wn={withoutOverlapping:`no overlap`,onOneServer:`one server`,runInBackground:`background`,evenInMaintenanceMode:`in maintenance`};function Tn(e){let t=e.split(`\\`).pop();return t&&t.length>0?t:e}function En({tab:e,schedule:t,isActive:n,isLoading:r,onSelect:i}){let a=t.cadence||`no cadence stated`,o=t.modifiers.map(e=>wn[e]??e);return(0,X.jsx)($,{content:`${t.target} · ${a}${t.timezone?` · ${t.timezone}`:``} · ${e.nodeCount} nodes`,children:(0,X.jsxs)(`button`,{className:`route-row route-row--stacked ${n?`route-row--active`:``}`,type:`button`,onClick:()=>i(e),children:[(0,X.jsx)(`span`,{className:`route-row-method`,style:{color:re[t.type===`job`?`job`:`command`]},children:Cn[t.type]??`›`}),(0,X.jsxs)(`span`,{className:`schedule-row-body`,children:[(0,X.jsx)(`span`,{className:`schedule-row-scroll`,children:(0,X.jsx)(`span`,{className:`route-row-uri`,children:Tn(t.target)})}),(0,X.jsx)(`span`,{className:`schedule-cadence ${t.cadence?``:`schedule-cadence--unknown`}`,children:a}),(t.timezone||o.length>0)&&(0,X.jsxs)(`span`,{className:`schedule-row-scroll schedule-row-badges`,children:[t.timezone&&(0,X.jsx)(`span`,{className:`schedule-chip`,children:t.timezone}),o.map(e=>(0,X.jsx)(`span`,{className:`schedule-chip`,children:e},e))]})]}),r&&(0,X.jsx)(`span`,{className:`route-row-loading`,children:`…`})]})})}function Dn({tab:e,isActive:t,isLoading:n,onSelect:r}){return e.schedule?(0,X.jsx)(En,{tab:e,schedule:e.schedule,isActive:t,isLoading:n,onSelect:r}):(0,X.jsx)(Sn,{tab:e,isActive:t,isLoading:n,onSelect:r})}var On={shield:(0,X.jsx)(`path`,{d:`M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z`}),lock:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`3`,y:`11`,width:`18`,height:`11`,rx:`2`}),(0,X.jsx)(`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`})]}),key:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`7.5`,cy:`15.5`,r:`4.5`}),(0,X.jsx)(`path`,{d:`m10.7 12.3 8.3-8.3`}),(0,X.jsx)(`path`,{d:`m17 5 3 3`}),(0,X.jsx)(`path`,{d:`m15 7 3 3`})]}),user:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2`}),(0,X.jsx)(`circle`,{cx:`12`,cy:`7`,r:`4`})]}),users:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2`}),(0,X.jsx)(`circle`,{cx:`9`,cy:`7`,r:`4`}),(0,X.jsx)(`path`,{d:`M22 21v-2a4 4 0 0 0-3-3.87`}),(0,X.jsx)(`path`,{d:`M16 3.13a4 4 0 0 1 0 7.75`})]}),building:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`4`,y:`2`,width:`16`,height:`20`,rx:`2`}),(0,X.jsx)(`path`,{d:`M9 22v-4h6v4`}),(0,X.jsx)(`path`,{d:`M8 6h.01M16 6h.01M8 10h.01M16 10h.01M8 14h.01M16 14h.01`})]}),dashboard:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`3`,y:`3`,width:`7`,height:`9`}),(0,X.jsx)(`rect`,{x:`14`,y:`3`,width:`7`,height:`5`}),(0,X.jsx)(`rect`,{x:`14`,y:`12`,width:`7`,height:`9`}),(0,X.jsx)(`rect`,{x:`3`,y:`16`,width:`7`,height:`5`})]}),settings:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z`}),(0,X.jsx)(`circle`,{cx:`12`,cy:`12`,r:`3`})]}),card:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`2`,y:`5`,width:`20`,height:`14`,rx:`2`}),(0,X.jsx)(`line`,{x1:`2`,y1:`10`,x2:`22`,y2:`10`})]}),cart:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`9`,cy:`21`,r:`1`}),(0,X.jsx)(`circle`,{cx:`20`,cy:`21`,r:`1`}),(0,X.jsx)(`path`,{d:`M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6`})]}),package:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z`}),(0,X.jsx)(`path`,{d:`M3.27 6.96 12 12.01l8.73-5.05`}),(0,X.jsx)(`path`,{d:`M12 22.08V12`})]}),file:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z`}),(0,X.jsx)(`polyline`,{points:`14 2 14 8 20 8`}),(0,X.jsx)(`line`,{x1:`16`,y1:`13`,x2:`8`,y2:`13`}),(0,X.jsx)(`line`,{x1:`16`,y1:`17`,x2:`8`,y2:`17`})]}),message:(0,X.jsx)(`path`,{d:`M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z`}),bell:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9`}),(0,X.jsx)(`path`,{d:`M13.73 21a2 2 0 0 1-3.46 0`})]}),mail:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`2`,y:`4`,width:`20`,height:`16`,rx:`2`}),(0,X.jsx)(`path`,{d:`m22 7-10 5L2 7`})]}),search:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`11`,cy:`11`,r:`8`}),(0,X.jsx)(`line`,{x1:`21`,y1:`21`,x2:`16.65`,y2:`16.65`})]}),folder:(0,X.jsx)(`path`,{d:`M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z`}),download:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`}),(0,X.jsx)(`polyline`,{points:`7 10 12 15 17 10`}),(0,X.jsx)(`line`,{x1:`12`,y1:`15`,x2:`12`,y2:`3`})]}),upload:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`}),(0,X.jsx)(`polyline`,{points:`17 8 12 3 7 8`}),(0,X.jsx)(`line`,{x1:`12`,y1:`3`,x2:`12`,y2:`15`})]}),chart:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`line`,{x1:`12`,y1:`20`,x2:`12`,y2:`10`}),(0,X.jsx)(`line`,{x1:`18`,y1:`20`,x2:`18`,y2:`4`}),(0,X.jsx)(`line`,{x1:`6`,y1:`20`,x2:`6`,y2:`16`})]}),list:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`line`,{x1:`8`,y1:`6`,x2:`21`,y2:`6`}),(0,X.jsx)(`line`,{x1:`8`,y1:`12`,x2:`21`,y2:`12`}),(0,X.jsx)(`line`,{x1:`8`,y1:`18`,x2:`21`,y2:`18`}),(0,X.jsx)(`line`,{x1:`3`,y1:`6`,x2:`3.01`,y2:`6`}),(0,X.jsx)(`line`,{x1:`3`,y1:`12`,x2:`3.01`,y2:`12`}),(0,X.jsx)(`line`,{x1:`3`,y1:`18`,x2:`3.01`,y2:`18`})]}),activity:(0,X.jsx)(`polyline`,{points:`22 12 18 12 15 21 9 3 6 12 2 12`}),link:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71`}),(0,X.jsx)(`path`,{d:`M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71`})]}),zap:(0,X.jsx)(`polygon`,{points:`13 2 3 14 12 14 11 22 21 10 12 10 13 2`}),box:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`4`,y:`4`,width:`16`,height:`16`,rx:`2`}),(0,X.jsx)(`rect`,{x:`9`,y:`9`,width:`6`,height:`6`})]}),calendar:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`rect`,{x:`3`,y:`4`,width:`18`,height:`18`,rx:`2`}),(0,X.jsx)(`line`,{x1:`16`,y1:`2`,x2:`16`,y2:`6`}),(0,X.jsx)(`line`,{x1:`8`,y1:`2`,x2:`8`,y2:`6`}),(0,X.jsx)(`line`,{x1:`3`,y1:`10`,x2:`21`,y2:`10`})]}),pin:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z`}),(0,X.jsx)(`circle`,{cx:`12`,cy:`10`,r:`3`})]}),book:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z`}),(0,X.jsx)(`path`,{d:`M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z`})]}),info:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`12`,cy:`12`,r:`10`}),(0,X.jsx)(`line`,{x1:`12`,y1:`16`,x2:`12`,y2:`12`}),(0,X.jsx)(`line`,{x1:`12`,y1:`8`,x2:`12.01`,y2:`8`})]}),beaker:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M9 3h6`}),(0,X.jsx)(`path`,{d:`M10 3v6l-5.5 9.5A2 2 0 0 0 6.2 21h11.6a2 2 0 0 0 1.7-3.5L14 9V3`})]}),tag:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M20.59 13.41 13.42 20.58a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z`}),(0,X.jsx)(`line`,{x1:`7`,y1:`7`,x2:`7.01`,y2:`7`})]}),broadcast:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`path`,{d:`M4 11a9 9 0 0 1 9 9`}),(0,X.jsx)(`path`,{d:`M4 4a16 16 0 0 1 16 16`}),(0,X.jsx)(`circle`,{cx:`5`,cy:`19`,r:`1`})]}),hash:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`line`,{x1:`4`,y1:`9`,x2:`20`,y2:`9`}),(0,X.jsx)(`line`,{x1:`4`,y1:`15`,x2:`20`,y2:`15`}),(0,X.jsx)(`line`,{x1:`10`,y1:`3`,x2:`8`,y2:`21`}),(0,X.jsx)(`line`,{x1:`16`,y1:`3`,x2:`14`,y2:`21`})]}),terminal:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`polyline`,{points:`4 17 10 11 4 5`}),(0,X.jsx)(`line`,{x1:`12`,y1:`19`,x2:`20`,y2:`19`})]}),clock:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`12`,cy:`12`,r:`10`}),(0,X.jsx)(`polyline`,{points:`12 6 12 12 16 14`})]}),route:(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`circle`,{cx:`6`,cy:`19`,r:`3`}),(0,X.jsx)(`circle`,{cx:`18`,cy:`5`,r:`3`}),(0,X.jsx)(`path`,{d:`M9 19h6a4 4 0 0 0 4-4V9`})]})};function kn({name:e}){return(0,X.jsx)(`svg`,{className:`tree-group-icon`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:On[e]})}var An=[[/^(auth|login|register|signin|signup|signout|logout|verify)/i,`lock`],[/^(password|forgot|reset|recover)/i,`key`],[/^(oauth|sso|saml|token|jwt|sanctum|passport)/i,`key`],[/^(admin|backend|manage|mgmt|cp|role|permission|acl|guard|policy|gate|abilit|security|firewall|protect|shield)/i,`shield`],[/^(team|organization|org|company|tenant|workspace)/i,`building`],[/^(user|account|profile|member|people|person)/i,`user`],[/^(group|staff|contributor|follower)/i,`users`],[/^(dashboard|home|overview|index|main|panel)/i,`dashboard`],[/^(setting|config|preference|option|env)/i,`settings`],[/^(billing|payment|invoice|subscription|plan|pricing|wallet|transaction|refund)/i,`card`],[/^(checkout|cart|basket|bag)/i,`cart`],[/^(order|purchase|fulfil|shipping|delivery|product|catalog|catalogue|item|shop|store|inventory|stock)/i,`package`],[/^(blog|post|article|news|content|page|cms)/i,`file`],[/^(message|chat|conversation|inbox|thread|dm|comment|review|rating|feedback|reply)/i,`message`],[/^(notification|notif|alert|push)/i,`bell`],[/^(mail|email|newsletter|campaign)/i,`mail`],[/^(search|explore|discover|find|query|filter)/i,`search`],[/^(upload|file|files|media|image|photo|asset|document|docs?|attachment|storage)/i,`folder`],[/^(download|export|backup|dump)/i,`download`],[/^(import|sync|migrate)/i,`upload`],[/^(report|analytic|stat|statistic|metric|insight|chart|kpi)/i,`chart`],[/^(log|logs|audit|activity|history|track|trace)/i,`list`],[/^(health|status|ping|up|ready|live|heartbeat|probe|monitor)/i,`activity`],[/^(webhook|callback|hook|integration|connect|link)/i,`link`],[/^(cache|redis|optimize)/i,`zap`],[/^(queue|job|jobs|worker|batch|cron)/i,`box`],[/^(calendar|event|booking|appointment|reservation|slot)/i,`calendar`],[/^(map|location|geo|address|place|region|country)/i,`pin`],[/^(project|board|workflow|pipeline)/i,`folder`],[/^(help|support|faq|guide|tutorial|kb|knowledge|wiki)/i,`book`],[/^(contact|enquir|inquir|lead)/i,`user`],[/^(about|info|legal|privacy|terms|policy)/i,`info`],[/^(test|tests|debug|dev|sandbox|playground|demo|example)/i,`beaker`],[/^(tag|tags|category|categories|topic|label)/i,`tag`],[/^(feed|rss|atom|socket|ws|realtime|broadcast|stream)/i,`broadcast`],[/^(api|graphql|ql|rest|rpc)$/i,`hash`],[/^v?\d+(\.\d+)*$/i,`hash`]],jn={"Console Commands":`terminal`,"Broadcast Channels":`broadcast`,Schedules:`clock`,"Model ERD":`box`,"Event Choreography":`zap`,"AI Agents":`zap`,Reachability:`search`,Other:`route`};function Mn(e,t){if(t)return e.startsWith(`Filament`)?`box`:jn[e]??`route`;for(let[t,n]of An)if(t.test(e))return n;return`route`}function Nn(e){if(e.category===`Command`)return`Console Commands`;if(e.category===`Channel`)return`Broadcast Channels`;if(e.category===`Schedule`)return`Schedules`;if(e.category===`ERD`)return`Model ERD`;if(e.category===`Events`)return`Event Choreography`;if(e.category===`AI`)return`AI Agents`;if(e.category===`Reachability`)return`Reachability`;if(e.category===`Filament`){let t=e.panelId??``;return t?`Filament · ${t.charAt(0).toUpperCase()}${t.slice(1)} Panel`:`Filament`}return`Other`}function Pn(e){e.children.sort((e,t)=>e.name.localeCompare(t.name)),e.leaves.sort((e,t)=>e.label.localeCompare(t.label)),e.children.forEach(Pn)}function Fn(e){let t=e.label.split(` `)[0];return t in hn?e.label.slice(t.length).trim().split(`/`).filter(Boolean):null}function In(e){let t={name:``,path:``,isCategory:!1,children:[],leaves:[]},n=(e,t,n)=>{let r=e.children.find(e=>e.name===t);return r||(r={name:t,path:e.path?`${e.path}/${t}`:t,isCategory:n,children:[],leaves:[]},e.children.push(r)),r},r=new Set;for(let t of e){let e=Fn(t);if(!e)continue;let n=e.slice(0,-1);for(let e=1;e<=n.length;e++)r.add(n.slice(0,e).join(`/`))}for(let i of e){let e=Fn(i);if(!e){n(t,Nn(i),!0).leaves.push(i);continue}let a=e.join(`/`),o=a!==``&&r.has(a)?e:e.slice(0,-1),s=t;for(let e of o)s=n(s,e,!1);s.leaves.push(i)}return Pn(t),t}function Ln(e){return e.leaves.length+e.children.reduce((e,t)=>e+Ln(t),0)}function Rn({node:e,forceOpen:t,expanded:n,onToggle:r,activeId:i,loadingId:a,onSelect:o,level:s=0}){let c=t||n.has(e.path),l=e.isCategory?e.name:`/${e.name}`;return(0,X.jsxs)(`div`,{className:`tree-group`,children:[(0,X.jsxs)(`button`,{type:`button`,className:`tree-group-header`,onClick:()=>r(e.path),children:[(0,X.jsx)(`span`,{className:`tree-group-chevron`,children:c?`▾`:`▸`}),s===0&&(0,X.jsx)(kn,{name:Mn(e.name,e.isCategory)}),(0,X.jsx)(`span`,{className:`tree-group-name`,children:l}),(0,X.jsx)(`span`,{className:`tree-group-count`,children:Ln(e)})]}),c&&(0,X.jsxs)(`div`,{className:`tree-group-body`,children:[e.children.map(e=>(0,X.jsx)(Rn,{node:e,forceOpen:t,expanded:n,onToggle:r,activeId:i,loadingId:a,onSelect:o,level:s+1},e.path)),e.leaves.map(e=>(0,X.jsx)(Dn,{tab:e,isActive:e.id===i,isLoading:e.id===a,onSelect:o},e.id))]})]})}function zn({tab:e,isActive:t,onSelect:n,timestamp:r}){let{method:i,uri:a}=_n(e.label),o=yn(e),s=o===`critical`?`critical`:o===`high`?`high`:o===`medium`?`medium`:`low`,c=B[s]??B.medium;return(0,X.jsxs)(`button`,{type:`button`,className:`flag-card ${t?`flag-card--active`:``}`,onClick:()=>n(e),children:[(0,X.jsxs)(`div`,{className:`flag-card-top`,children:[r?(0,X.jsx)(`span`,{className:`flag-card-time`,children:r}):(0,X.jsx)(`span`,{className:`flag-card-sev`,style:{"--sc":c},children:(se[s]??s).toUpperCase()}),i&&(0,X.jsx)(`span`,{className:`flag-card-method`,style:{color:hn[i]},children:i})]}),(0,X.jsx)(`div`,{className:`flag-card-path`,children:a}),(0,X.jsx)(`div`,{className:`flag-card-desc`,children:bn(e)})]})}function Bn({tabs:e,activeId:t,loadingId:n,onSelect:r,mode:i,onModeChange:a,previousAnalyzedAt:o,visibleTypes:s,counts:c,onToggle:l,onShowAll:u,onHideAll:d}){let[f,p]=(0,A.useState)(mn),[m,h]=(0,A.useState)(``),[g,_]=(0,A.useState)(new Set(gn)),[v,y]=(0,A.useState)(new Set),b=(0,A.useCallback)(e=>{_(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),x=(0,A.useCallback)(e=>y(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n}),[]),S=(0,A.useRef)(!1),C=(0,A.useRef)(0),w=(0,A.useRef)(mn),T=(0,A.useCallback)(e=>{e.preventDefault(),S.current=!0,C.current=e.clientX,w.current=f;let t=e=>{if(!S.current)return;let t=e.clientX-C.current;p(Math.min(pn,Math.max(fn,w.current+t)))},n=()=>{S.current=!1,window.removeEventListener(`mousemove`,t),window.removeEventListener(`mouseup`,n),document.body.style.cursor=``,document.body.style.userSelect=``};document.body.style.cursor=`col-resize`,document.body.style.userSelect=`none`,window.addEventListener(`mousemove`,t),window.addEventListener(`mouseup`,n)},[f]),E=m.trim().toLowerCase(),D=(0,A.useMemo)(()=>{let t=gn.every(e=>g.has(e));return e.filter(e=>{if(E&&!vn(e).includes(E))return!1;if(!t){let t=e.label.split(` `)[0];if(t in hn&&!g.has(t))return!1}return!0})},[e,E,g]),O=E.length>0||!gn.every(e=>g.has(e)),k=(0,A.useMemo)(()=>In(D),[D]),j=(0,A.useMemo)(()=>D.filter(e=>yn(e)!==`none`).sort((e,t)=>(dn[yn(t)]??0)-(dn[yn(e)]??0)),[D]),M=(0,A.useMemo)(()=>D.filter(e=>e.changeStatus===`new`||e.changeStatus===`changed`),[D]),N=[{id:`routes`,label:`Routes`,count:D.length},{id:`risks`,label:`Risks`,count:j.length},{id:`recent`,label:`Recent`,count:M.length}];return(0,X.jsxs)(`div`,{className:`left-sidebar-resizable`,style:{width:f,"--left-sidebar-width":`${f}px`},children:[(0,X.jsxs)(`div`,{className:`left-sidebar`,children:[(0,X.jsxs)(`div`,{className:`left-search`,children:[(0,X.jsx)(`input`,{className:`left-search-input`,type:`text`,placeholder:`Search routes…`,value:m,onChange:e=>h(e.target.value)}),m&&(0,X.jsx)(`button`,{type:`button`,className:`left-search-clear`,onClick:()=>h(``),children:`×`})]}),(0,X.jsx)(`div`,{className:`left-method-chips`,children:gn.map(e=>(0,X.jsx)(`button`,{type:`button`,className:`method-chip ${g.has(e)?`method-chip--on`:``}`,style:{"--mc":hn[e]},onClick:()=>b(e),children:e},e))}),(0,X.jsx)(`div`,{className:`mode-tabs`,children:N.map(e=>(0,X.jsxs)(`button`,{type:`button`,className:`mode-tab ${i===e.id?`mode-tab--active`:``}`,onClick:()=>a(e.id),children:[e.label,(0,X.jsx)(`span`,{className:`mode-tab-count ${e.id===`risks`&&i===`risks`&&e.count>0?`mode-tab-count--alert`:``}`,children:e.count})]},e.id))}),(0,X.jsxs)(`div`,{className:`left-content`,children:[i===`routes`&&(0,X.jsxs)(`div`,{className:`route-tree`,children:[k.children.length===0&&k.leaves.length===0&&(0,X.jsx)(`div`,{className:`left-empty`,children:`No routes match.`}),k.children.map(e=>(0,X.jsx)(Rn,{node:e,forceOpen:E.length>0,expanded:v,onToggle:x,activeId:t,loadingId:n,onSelect:r},e.path)),k.leaves.map(e=>(0,X.jsx)(Dn,{tab:e,isActive:e.id===t,isLoading:e.id===n,onSelect:r},e.id))]}),i===`risks`&&(0,X.jsxs)(`div`,{className:`flag-list`,children:[j.length===0&&(0,X.jsx)(`div`,{className:`left-empty`,children:O?`No flagged routes match the filter.`:`No flagged routes. ✓`}),j.map(e=>(0,X.jsx)(zn,{tab:e,isActive:e.id===t,onSelect:r},e.id))]}),i===`recent`&&(0,X.jsxs)(`div`,{className:`flag-list`,children:[M.length===0&&(0,X.jsx)(`div`,{className:`left-empty`,children:O?`Nothing matching the filter changed since the previous scan.`:`Nothing changed since the previous scan.`}),M.map(e=>(0,X.jsx)(zn,{tab:e,isActive:e.id===t,onSelect:r,timestamp:`${e.changeStatus===`new`?`new`:`changed`} · ${xn(o)}`},e.id))]})]}),(0,X.jsx)(`div`,{className:`left-footer`,children:(0,X.jsx)(un,{visibleTypes:s,counts:c,onToggle:l,onShowAll:u,onHideAll:d})})]}),(0,X.jsx)($,{content:`Drag to resize`,children:(0,X.jsx)(`div`,{className:`left-sidebar-drag-handle`,onMouseDown:T})})]})}var Vn=[...`route.middleware.controller.livewire_component.action.action_class.service.validation_request.model.event.listener.job.command.channel.schedule.view.mail.notification.enum.interface.trait.abstract_class.service_provider.ai_agent.ai_tool.filament_panel.filament_resource.filament_page.filament_page_method.filament_widget.filament_relation_manager.entry_point.entry_point_group.unreached_class.unreached_group`.split(`.`),`transaction`,`chain`,`batch`];function Hn(){let{theme:e,toggle:t}=ee(),{manifest:n,loading:r,error:i}=M(),{state:a,elements:o,load:s}=P(),c=F(o),[l,u]=(0,A.useState)(null),[d,f]=(0,A.useState)(null),[p,m]=(0,A.useState)(`dagre`),[h,g]=(0,A.useState)(null),[_,v]=(0,A.useState)(`routes`),[y,b]=(0,A.useState)(``),[x,S]=(0,A.useState)(!1),[C,w]=(0,A.useState)(new Set(Vn)),[T,E]=(0,A.useState)(`TB`),[D,O]=(0,A.useState)(null),[k,j]=(0,A.useState)(0),N=(0,A.useRef)(null),te=(0,A.useCallback)(e=>{if(l?.id===e.id)return;let t=new URL(window.location.href);t.searchParams.get(`tab`)!==e.id&&(t.searchParams.set(`tab`,e.id),window.history.pushState({tabId:e.id},``,t.toString())),u(e),b(``),S(!0),s(e.file)},[l,s]),[I,ne]=(0,A.useState)(n);if(n!==I&&(ne(n),n&&!l)){let e=new URLSearchParams(window.location.search).get(`tab`),t=n.tabs.find(t=>t.id===e);t&&te(t)}let[re,ie]=(0,A.useState)(a.data);if(a.data!==re)if(ie(a.data),a.data)if(w(new Set(Vn)),x){S(!1);let e=a.data.nodes.find(e=>e.type===`route`);g(e?e.id:null)}else g(null);else g(null);(0,A.useEffect)(()=>{let e=()=>{if(!n)return;let e=new URLSearchParams(window.location.search).get(`tab`),t=n.tabs.find(t=>t.id===e);t&&(u(t),s(t.file))};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[n,s]);let L=(0,A.useCallback)(e=>{g(e)},[]),[R,ae]=(0,A.useState)(a.loading);a.loading!==R&&(ae(a.loading),a.loading||f(null));let z=(0,A.useMemo)(()=>n?.tabs??[],[n]),oe=(0,A.useMemo)(()=>z.filter(e=>e.riskLevel===`high`||e.riskLevel===`critical`).length,[z]),B=(0,A.useMemo)(()=>{if(!a.data)return{};let e=a.data.nodes.reduce((e,t)=>(e[t.type]=(e[t.type]??0)+1,e),{}),t={};for(let e of a.data.nodes)for(let n of G(e)){let e=n.kind===`rollback`?`transaction`:n.kind;t[e]=(t[e]??new Set).add(n.id)}for(let[n,r]of Object.entries(t))e[n]=r.size;return e},[a.data]),se=(0,A.useMemo)(()=>a.data?a.data.nodes.filter(e=>C.has(e.type)).length:0,[a.data,C]),ce=(0,A.useCallback)(e=>{w(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),le=(0,A.useCallback)(()=>w(new Set(Vn)),[]),V=(0,A.useCallback)(()=>w(new Set),[]),[ue,de]=(0,A.useState)(!1),[fe,pe]=(0,A.useState)(!1),[me,H]=(0,A.useState)(`all`),[he,U]=(0,A.useState)(!1),[W,ge]=(0,A.useState)(!1);return r?(0,X.jsxs)(`div`,{className:`loading-screen`,children:[(0,X.jsx)(`div`,{className:`loading-spinner`}),(0,X.jsx)(`p`,{children:`Loading project graph...`})]}):i||!n?(0,X.jsx)(`div`,{className:`error-screen welcome-screen`,children:(0,X.jsxs)(`div`,{className:`welcome-card`,children:[(0,X.jsx)(`div`,{className:`welcome-icon`,children:(0,X.jsx)(`img`,{src:`/_laravel-brain/logo.png`,alt:`Laravel Brain`})}),(0,X.jsx)(`h2`,{children:`Welcome to Laravel Brain`}),(0,X.jsx)(`p`,{children:`No project analysis found. To begin exploring your code architecture, please run an initial scan.`}),i&&i!==`HTTP 404`&&(0,X.jsx)(`div`,{className:`error-details`,children:(0,X.jsxs)(`small`,{children:[`Error: `,i]})}),(0,X.jsx)(`button`,{className:`scan-btn ${ue?`scan-btn--loading`:``}`,onClick:async()=>{if(window.confirm(`This will scan the entire project. Proceed?`)){de(!0);try{(await fetch(`/_laravel-brain/api/scan`,{method:`POST`})).ok?window.location.reload():alert(`Scan failed.`)}catch{alert(`Scan failed.`)}finally{de(!1)}}},disabled:ue,children:ue?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`div`,{className:`btn-spinner`}),`Analyzing Project...`]}):`🚀 Start Initial Scan`}),(0,X.jsxs)(`div`,{className:`welcome-hint`,children:[`Alternatively, run `,(0,X.jsx)(`code`,{children:`php artisan brain:scan`}),` in your terminal.`]})]})}):(0,X.jsxs)(`div`,{className:`app`,children:[(0,X.jsx)(on,{nodeCount:a.data?.meta.nodeCount??l?.nodeCount??0,edgeCount:a.data?.meta.edgeCount??l?.edgeCount??0,visibleCount:se,activeTabLabel:l?.label??`graph`,graphData:a.data??null,analyzedAt:n.analyzedAt,highRiskCount:oe,onOpenRisks:()=>v(`risks`),theme:e,onSearch:b,onToggleTheme:t,graphRef:N}),(0,X.jsxs)(`div`,{className:`main`,children:[(0,X.jsx)(Bn,{tabs:z,activeId:l?.id??null,loadingId:d,onSelect:te,mode:_,onModeChange:v,previousAnalyzedAt:n.previousAnalyzedAt,visibleTypes:C,counts:B,onToggle:ce,onShowAll:le,onHideAll:V,graphData:a.data??null,complexityFilter:me,onComplexityFilterChange:H,onNodeSelect:L,selectedId:h}),(0,X.jsxs)(`div`,{className:`graph-container`,children:[a.loading&&(0,X.jsxs)(`div`,{className:`graph-loading-overlay`,children:[(0,X.jsx)(`div`,{className:`loading-spinner`}),(0,X.jsxs)(`p`,{children:[`Loading `,l?.label,`…`]})]}),a.error&&(0,X.jsx)(`div`,{className:`graph-loading-overlay`,children:(0,X.jsxs)(`p`,{style:{color:`#F44336`},children:[`Error: `,a.error]})}),!l&&!a.loading&&(0,X.jsx)($,{content:`Pick a route or command in the left sidebar to load its dependency graph.`,children:(0,X.jsxs)(`div`,{className:`graph-placeholder`,children:[(0,X.jsx)(`div`,{className:`placeholder-icon`,children:(0,X.jsx)(`svg`,{width:`64`,height:`64`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,X.jsx)(`polyline`,{points:`22 12 18 12 15 21 9 3 6 12 2 12`})})}),(0,X.jsx)(`h3`,{children:`Select a route to explore`}),(0,X.jsx)(`p`,{children:`Expand the files in the sidebar and choose a route or command to visualize its execution lifecycle and dependencies.`})]})}),!a.loading&&l&&c.length===0&&!a.error&&(0,X.jsx)($,{content:`This endpoint produced no analyzable nodes. It may be a closure, a redirect-only route, or outside the scanner’s rules.`,children:(0,X.jsxs)(`div`,{className:`graph-placeholder`,children:[(0,X.jsx)(`div`,{className:`placeholder-icon`,children:(0,X.jsxs)(`svg`,{width:`64`,height:`64`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,X.jsx)(`circle`,{cx:`12`,cy:`12`,r:`10`}),(0,X.jsx)(`line`,{x1:`12`,y1:`8`,x2:`12`,y2:`12`}),(0,X.jsx)(`line`,{x1:`12`,y1:`16`,x2:`12.01`,y2:`16`})]})}),(0,X.jsx)(`h3`,{children:`Empty Graph`}),(0,X.jsx)(`p`,{children:`No nodes or edges found for this route.`})]})}),!a.loading&&c.length>0&&(0,X.jsx)(He,{elements:c,layout:p,searchQuery:y,rankDir:T,visibleTypes:C,theme:e,onNodeSelect:L,graphRef:N,stressTestNodeId:D,stressRunKey:k,complexityOverlay:fe,securityOverlay:he,compact:W,onLayoutChange:m,onRankDirChange:E,onToggleComplexityOverlay:()=>pe(e=>!e),onToggleSecurityOverlay:()=>U(e=>!e),onToggleCompact:()=>ge(e=>!e)},l?.id)]}),h&&(0,X.jsx)(en,{selectedId:h,graphData:a.data,theme:e,onClose:()=>g(null),onStressChange:e=>{O(e),e!==null&&j(e=>e+1)}})]})]})}(0,j.createRoot)(document.getElementById(`root`)).render((0,X.jsx)(A.StrictMode,{children:(0,X.jsx)(Hn,{})}));
\ No newline at end of file
diff --git a/resources/views/index.blade.php b/resources/views/index.blade.php
index af2df0a4..9d1e862b 100644
--- a/resources/views/index.blade.php
+++ b/resources/views/index.blade.php
@@ -8,13 +8,13 @@
-
+
-
+
diff --git a/src/Analysis/ProjectAnalyzer.php b/src/Analysis/ProjectAnalyzer.php
index 5135849a..6b39797e 100644
--- a/src/Analysis/ProjectAnalyzer.php
+++ b/src/Analysis/ProjectAnalyzer.php
@@ -7,6 +7,11 @@
use LaraMint\LaravelBrain\Analysis\Incremental\IncrementalMerge;
use LaraMint\LaravelBrain\Analysis\Incremental\ScopedRebuildNotApplicable;
use LaraMint\LaravelBrain\Analysis\Incremental\ScopeExpansion;
+use LaraMint\LaravelBrain\Analysis\Reachability\ClassInventory;
+use LaraMint\LaravelBrain\Analysis\Reachability\ClassStringIndex;
+use LaraMint\LaravelBrain\Analysis\Reachability\EntryPointInventory;
+use LaraMint\LaravelBrain\Analysis\Reachability\ReachabilityAnalyzer;
+use LaraMint\LaravelBrain\Analysis\Reachability\ReachabilityReport;
use LaraMint\LaravelBrain\Graph\Graph;
use LaraMint\LaravelBrain\Graph\GraphBuilder;
use LaraMint\LaravelBrain\Graph\GraphSplitter;
@@ -44,6 +49,8 @@ public function __construct(
* @var array
*/
public array $nodesOutsideTabs = [],
+ /** Null when laravel-brain.reachability.enabled is off — the pass did not run. */
+ public ?ReachabilityReport $reachability = null,
) {}
}
@@ -102,7 +109,16 @@ class ProjectAnalyzer
private ?int $schemaTimeout = null;
- /** @var string[] class-file search roots, relative to the project root */
+ /**
+ * Class-file search roots, relative to the project root.
+ *
+ * Kept as a property because the reachability pass needs the same directories the rest of
+ * the build resolves classes against — asking a differently-shaped question of a
+ * differently-shaped source tree would report classes as unreached that the graph never
+ * had a chance to reach.
+ *
+ * @var string[]
+ */
private array $sourcePaths = SourceDirectories::DEFAULT_SOURCE_PATHS;
/** Whether this build reports the outgoing HTTP calls each node makes. */
@@ -110,6 +126,9 @@ class ProjectAnalyzer
private bool $serviceProviderAnalysisEnabled = true;
+ /** Whether the reachability pass runs — see laravel-brain.reachability.enabled. */
+ private bool $reachabilityEnabled = true;
+
/** @var callable(string, array): void */
private $onProgress;
@@ -131,6 +150,8 @@ public function __construct()
$sourcePaths = is_array($sourcePaths) && $sourcePaths !== []
? $sourcePaths
: SourceDirectories::DEFAULT_SOURCE_PATHS;
+ $this->sourcePaths = $sourcePaths;
+ $this->reachabilityEnabled = (bool) config('laravel-brain.reachability.enabled', false);
$listenerPaths = config('laravel-brain.listeners.paths', ['app/Listeners']);
$this->listenerPaths = is_array($listenerPaths) ? array_values($listenerPaths) : ['app/Listeners'];
@@ -435,7 +456,9 @@ private function runAnalysis(string $projectRoot, ?callable $onProgress = null):
}
}
- // Link dispatched events to the listeners that handle them.
+ // Link dispatched events to the listeners that handle them. Held separately as well:
+ // a queued listener is an entry point in its own right — a worker runs it with no
+ // caller — and these edges are the only place a build learns which listeners exist.
$listenerEdges = $this->listenerAnalyzer->analyze($projectRoot, $psr4Map);
foreach ($listenerEdges as $edge) {
$callChain[] = $edge;
@@ -784,6 +807,54 @@ private function runAnalysis(string $projectRoot, ?callable $onProgress = null):
$split['manifest'][] = $ai['manifest'];
}
+ // Built after the split, and from the same graph the split reads, so the tab reports
+ // exactly what the rest of the interface is showing rather than a second opinion.
+ $reachability = null;
+ if ($this->reachabilityEnabled) {
+ $this->emit('step:start', ['step' => 'reachability', 'label' => 'Checking reachability', 'message' => ' → Checking reachability...']);
+
+ $classInventory = ClassInventory::scan($projectRoot, $this->sourcePaths);
+ $entryPoints = EntryPointInventory::collect(
+ routes: $routes,
+ commands: $commands,
+ schedules: $schedules,
+ channels: $channels,
+ listenerEdges: $listenerEdges,
+ filamentPanels: $filamentResult['panels'],
+ filamentResources: $filamentResult['resources'],
+ filamentPages: $filamentResult['pages'],
+ classes: $classInventory,
+ );
+
+ $reachability = (new ReachabilityAnalyzer)->analyze(
+ $fullGraph,
+ $entryPoints,
+ $classInventory,
+ $bindingRegistry,
+ $facadeRegistry,
+ // A second traversal of the same files, not a second parse: PhpFileParser
+ // shares its results process-wide, so both passes read one AST per file.
+ ClassStringIndex::scan($projectRoot, $this->sourcePaths),
+ ClassStringIndex::scan($projectRoot, ['config']),
+ );
+
+ $reachabilityTab = $this->graphSplitter->buildReachabilityTab($reachability, $projectName, $analyzedAt);
+ if ($reachabilityTab !== null) {
+ $split['subgraphs'][$reachabilityTab['id']] = $reachabilityTab['graph'];
+ $split['manifest'][] = $reachabilityTab['manifest'];
+ }
+
+ $this->emit('step:done', [
+ 'step' => 'reachability',
+ 'count' => count($reachability->unreached),
+ 'unit' => 'unreached class',
+ 'extra' => count($entryPoints).' entry points',
+ 'message' => ' '.count($entryPoints).' entry point(s), '
+ .count($reachability->unreached).' of '.$reachability->classesDeclared
+ .' class(es) reached by none of them',
+ ]);
+ }
+
$this->emit('step:done', ['step' => 'split', 'count' => count($split['subgraphs']), 'unit' => 'tab', 'message' => ' '.count($split['subgraphs']).' tab(s) generated']);
$manifestJson = $this->graphSplitter->buildManifestJson(
@@ -804,6 +875,7 @@ private function runAnalysis(string $projectRoot, ?callable $onProgress = null):
unresolvedDispatchers: $this->methodTracer->unresolvedDispatchers(),
isolatedNodes: $fullGraph->isolatedNodeCountsByType(),
nodesOutsideTabs: GraphSplitter::nodesOutsideTabs($fullGraph, $split['subgraphs']),
+ reachability: $reachability,
);
$this->emit('analysis:done', [
diff --git a/src/Analysis/Reachability/ClassInventory.php b/src/Analysis/Reachability/ClassInventory.php
new file mode 100644
index 00000000..80eaa02c
--- /dev/null
+++ b/src/Analysis/Reachability/ClassInventory.php
@@ -0,0 +1,269 @@
+
+ */
+ public const TRACER_BLIND_KINDS = ['service_provider', 'exception'];
+
+ /**
+ * @param array $classes FQCN => declaration
+ */
+ private function __construct(private array $classes) {}
+
+ /**
+ * @param array $classes
+ */
+ public static function of(array $classes): self
+ {
+ return new self($classes);
+ }
+
+ /**
+ * @param string[] $sourcePaths directories or glob patterns, relative to the project root
+ */
+ public static function scan(string $projectRoot, array $sourcePaths): self
+ {
+ $directories = SourceDirectories::resolve($projectRoot, $sourcePaths);
+ $parser = new PhpFileParser;
+ $classes = [];
+
+ foreach (SourceDirectories::phpFiles($projectRoot, $directories) as $file) {
+ foreach (self::declarationsIn($parser, $file) as $declared) {
+ // First declaration wins, matching the by-file-name lookup every other
+ // analyzer falls back to: a duplicated FQCN is a broken autoloader, and
+ // guessing differently here would only disagree with the rest of the build.
+ $classes[$declared->fqcn] ??= $declared;
+ }
+ }
+
+ return new self($classes);
+ }
+
+ /**
+ * @return array
+ */
+ public function all(): array
+ {
+ return $this->classes;
+ }
+
+ public function get(string $fqcn): ?DeclaredClass
+ {
+ return $this->classes[$fqcn] ?? null;
+ }
+
+ public static function isTracerBlind(string $kind): bool
+ {
+ return in_array($kind, self::TRACER_BLIND_KINDS, true);
+ }
+
+ /**
+ * The group a class is filed under.
+ *
+ * The first four names come from the declaration itself; the rest are the same
+ * name-shape heuristics {@see GraphBuilder} classifies a
+ * traced hop with, in the same precedence order, so a group in this tab is named after
+ * the node type the class would carry anywhere else in the graph.
+ *
+ * It goes further than GraphBuilder does in one direction only: middleware, providers,
+ * policies, observers, commands and exceptions get their own names. GraphBuilder never
+ * needs them — it reaches those classes through a dedicated edge that already knows what
+ * they are — but a report has to group by something, and folding six recognisable kinds
+ * into one bucket labelled "service (312)" answers nothing.
+ */
+ public static function kindOf(string $fqcn, string $surface): string
+ {
+ if ($surface !== 'class') {
+ return $surface;
+ }
+
+ // Checked before the \Http\ heuristics below, which would otherwise claim
+ // middleware and API resources as controllers.
+ if (str_contains($fqcn, '\\Http\\Resources\\')) {
+ return 'resource';
+ }
+ if (str_contains($fqcn, '\\Middleware\\') || str_ends_with($fqcn, 'Middleware')) {
+ return 'middleware';
+ }
+ if (str_ends_with($fqcn, 'ServiceProvider') || str_contains($fqcn, '\\Providers\\')) {
+ return 'service_provider';
+ }
+ if (str_contains($fqcn, '\\Exceptions\\') || str_ends_with($fqcn, 'Exception')) {
+ return 'exception';
+ }
+ if (str_contains($fqcn, '\\Console\\Commands\\') || str_ends_with($fqcn, 'Command')) {
+ return 'command';
+ }
+ if (str_contains($fqcn, '\\Policies\\') || str_ends_with($fqcn, 'Policy')) {
+ return 'policy';
+ }
+ if (str_contains($fqcn, '\\Observers\\') || str_ends_with($fqcn, 'Observer')) {
+ return 'observer';
+ }
+ if (str_contains($fqcn, 'Controller') || str_contains($fqcn, '\\Http\\') || str_contains($fqcn, '\\Livewire\\')) {
+ return 'controller';
+ }
+ if (str_contains($fqcn, '\\Mail\\') || str_ends_with($fqcn, 'Mail') || str_ends_with($fqcn, 'Mailable')) {
+ return 'mail';
+ }
+ if (str_contains($fqcn, '\\Notifications\\') || str_ends_with($fqcn, 'Notification')) {
+ return 'notification';
+ }
+ if (str_contains($fqcn, '\\Listeners\\')) {
+ return 'listener';
+ }
+ if (str_contains($fqcn, 'Repository') || str_contains($fqcn, '\\Repositories\\')) {
+ return 'repository';
+ }
+ if (str_contains($fqcn, 'Job') || str_contains($fqcn, '\\Jobs\\')) {
+ return 'job';
+ }
+ if (str_contains($fqcn, 'Event') || str_contains($fqcn, '\\Events\\')) {
+ return 'event';
+ }
+ if (str_contains($fqcn, '\\Models\\') || str_contains($fqcn, '\\Model\\')) {
+ return 'model';
+ }
+
+ return 'service';
+ }
+
+ /**
+ * @return list
+ */
+ private static function declarationsIn(PhpFileParser $parser, string $file): array
+ {
+ $parsed = $parser->parse($file);
+ if ($parsed['ast'] === null) {
+ return [];
+ }
+
+ $visitor = new class($file) extends NodeVisitorAbstract
+ {
+ /** @var list */
+ public array $found = [];
+
+ private string $namespace = '';
+
+ public function __construct(private string $file) {}
+
+ public function enterNode(Node $node): ?int
+ {
+ if ($node instanceof Node\Stmt\Namespace_) {
+ $this->namespace = $node->name !== null ? $node->name->toString() : '';
+
+ return null;
+ }
+
+ if (! $node instanceof Node\Stmt\ClassLike || $node->name === null) {
+ // An anonymous class has no name to be reached *by*, so it cannot be
+ // unreachable in the sense this report means.
+ return null;
+ }
+
+ $fqcn = $this->namespace !== ''
+ ? $this->namespace.'\\'.$node->name->toString()
+ : $node->name->toString();
+
+ $surface = match (true) {
+ $node instanceof Node\Stmt\Interface_ => 'interface',
+ $node instanceof Node\Stmt\Trait_ => 'trait',
+ $node instanceof Node\Stmt\Enum_ => 'enum',
+ $node instanceof Node\Stmt\Class_ && $node->isAbstract() => 'abstract_class',
+ default => 'class',
+ };
+
+ $this->found[] = new DeclaredClass(
+ fqcn: $fqcn,
+ file: $this->file,
+ surface: $surface,
+ kind: ClassInventory::kindOf($fqcn, $surface),
+ parent: $node instanceof Node\Stmt\Class_
+ ? (PhpFileParser::resolvedName($node->extends) ?? '')
+ : '',
+ interfaces: self::implementedNames($node),
+ traits: self::usedTraitNames($node),
+ );
+
+ return null;
+ }
+
+ /**
+ * @return list
+ */
+ private static function implementedNames(Node\Stmt\ClassLike $node): array
+ {
+ $names = [];
+ $clause = match (true) {
+ $node instanceof Node\Stmt\Class_ => $node->implements,
+ $node instanceof Node\Stmt\Enum_ => $node->implements,
+ $node instanceof Node\Stmt\Interface_ => $node->extends,
+ default => [],
+ };
+ foreach ($clause as $name) {
+ $resolved = PhpFileParser::resolvedName($name);
+ if ($resolved !== null) {
+ $names[] = $resolved;
+ }
+ }
+
+ return $names;
+ }
+
+ /**
+ * @return list
+ */
+ private static function usedTraitNames(Node\Stmt\ClassLike $node): array
+ {
+ $names = [];
+ foreach ($node->stmts as $stmt) {
+ if (! $stmt instanceof Node\Stmt\TraitUse) {
+ continue;
+ }
+ foreach ($stmt->traits as $name) {
+ $resolved = PhpFileParser::resolvedName($name);
+ if ($resolved !== null) {
+ $names[] = $resolved;
+ }
+ }
+ }
+
+ return $names;
+ }
+ };
+
+ $traverser = new NodeTraverser;
+ $traverser->addVisitor($visitor);
+ $traverser->traverse($parsed['ast']);
+
+ return $visitor->found;
+ }
+}
diff --git a/src/Analysis/Reachability/ClassStringIndex.php b/src/Analysis/Reachability/ClassStringIndex.php
new file mode 100644
index 00000000..8b616f03
--- /dev/null
+++ b/src/Analysis/Reachability/ClassStringIndex.php
@@ -0,0 +1,134 @@
+> $references FQCN => files naming it
+ */
+ private function __construct(private array $references) {}
+
+ public static function empty(): self
+ {
+ return new self([]);
+ }
+
+ /**
+ * @param string[] $patterns directories or glob patterns, relative to the project root
+ */
+ public static function scan(string $projectRoot, array $patterns): self
+ {
+ $directories = SourceDirectories::resolve($projectRoot, $patterns);
+ $parser = new PhpFileParser;
+ $references = [];
+
+ foreach (SourceDirectories::phpFiles($projectRoot, $directories) as $file) {
+ foreach (self::namesIn($parser, $file) as $fqcn) {
+ $references[$fqcn][$file] = true;
+ }
+ }
+
+ return new self(array_map(
+ static fn (array $files): array => array_keys($files),
+ $references,
+ ));
+ }
+
+ /**
+ * Files that name the class, other than the one given as its declaration site.
+ *
+ * @return list
+ */
+ public function referencesTo(string $fqcn, string $exceptFile = ''): array
+ {
+ $files = $this->references[$fqcn] ?? [];
+
+ if ($exceptFile === '') {
+ return $files;
+ }
+
+ return array_values(array_filter($files, static fn (string $f): bool => $f !== $exceptFile));
+ }
+
+ public function hasReferenceTo(string $fqcn, string $exceptFile = ''): bool
+ {
+ return $this->referencesTo($fqcn, $exceptFile) !== [];
+ }
+
+ /**
+ * @return list
+ */
+ private static function namesIn(PhpFileParser $parser, string $file): array
+ {
+ $parsed = $parser->parse($file);
+ if ($parsed['ast'] === null) {
+ return [];
+ }
+
+ $visitor = new class extends NodeVisitorAbstract
+ {
+ /** @var array */
+ public array $found = [];
+
+ public function enterNode(Node $node): ?int
+ {
+ if ($node instanceof Node\Expr\ClassConstFetch
+ && $node->name instanceof Node\Identifier
+ && strtolower($node->name->toString()) === 'class') {
+ $resolved = PhpFileParser::resolvedName($node->class);
+ if ($resolved !== null) {
+ $this->found[$resolved] = true;
+ }
+
+ return null;
+ }
+
+ if ($node instanceof Node\Scalar\String_
+ && preg_match(ClassStringIndex::FQCN_PATTERN, $node->value) === 1) {
+ $this->found[ltrim($node->value, '\\')] = true;
+ }
+
+ return null;
+ }
+ };
+
+ $traverser = new NodeTraverser;
+ $traverser->addVisitor($visitor);
+ $traverser->traverse($parsed['ast']);
+
+ return array_keys($visitor->found);
+ }
+}
diff --git a/src/Analysis/Reachability/DeclaredClass.php b/src/Analysis/Reachability/DeclaredClass.php
new file mode 100644
index 00000000..55311510
--- /dev/null
+++ b/src/Analysis/Reachability/DeclaredClass.php
@@ -0,0 +1,33 @@
+ $interfaces resolved FQCNs from the `implements` clause
+ * @param list $traits resolved FQCNs from `use` inside the body
+ */
+ public function __construct(
+ public string $fqcn,
+ public string $file,
+ public string $surface,
+ public string $kind,
+ public string $parent = '',
+ public array $interfaces = [],
+ public array $traits = [],
+ ) {}
+}
diff --git a/src/Analysis/Reachability/EntryPoint.php b/src/Analysis/Reachability/EntryPoint.php
new file mode 100644
index 00000000..4819a2ed
--- /dev/null
+++ b/src/Analysis/Reachability/EntryPoint.php
@@ -0,0 +1,64 @@
+
+ */
+ public const KIND_ORDER = [
+ self::KIND_ROUTE,
+ self::KIND_COMMAND,
+ self::KIND_SCHEDULE,
+ self::KIND_CHANNEL,
+ self::KIND_QUEUED_LISTENER,
+ self::KIND_FILAMENT,
+ ];
+
+ /**
+ * @param string $fqcn the class that runs, or '' for a closure route / closure command
+ * @param list $nodeIds graph nodes this entry point is known by. Routes,
+ * commands, channels and schedule entries have an id built
+ * from their signature rather than from a class, so the
+ * FQCN alone cannot find them.
+ */
+ public function __construct(
+ public string $kind,
+ public string $label,
+ public string $fqcn = '',
+ public string $file = '',
+ public array $nodeIds = [],
+ public string $detail = '',
+ ) {}
+}
diff --git a/src/Analysis/Reachability/EntryPointInventory.php b/src/Analysis/Reachability/EntryPointInventory.php
new file mode 100644
index 00000000..29dbb435
--- /dev/null
+++ b/src/Analysis/Reachability/EntryPointInventory.php
@@ -0,0 +1,214 @@
+
+ */
+ public static function collect(
+ array $routes = [],
+ array $commands = [],
+ array $schedules = [],
+ array $channels = [],
+ array $listenerEdges = [],
+ array $filamentPanels = [],
+ array $filamentResources = [],
+ array $filamentPages = [],
+ ?ClassInventory $classes = null,
+ ): array {
+ $entryPoints = [];
+
+ foreach ($routes as $route) {
+ $entryPoints[] = new EntryPoint(
+ kind: EntryPoint::KIND_ROUTE,
+ label: trim("{$route->method} {$route->uri}"),
+ fqcn: $route->controller,
+ file: $route->file,
+ nodeIds: ["route::{$route->method}::{$route->uri}"],
+ detail: $route->name,
+ );
+ }
+
+ foreach ($commands as $command) {
+ $entryPoints[] = new EntryPoint(
+ kind: EntryPoint::KIND_COMMAND,
+ label: $command->signature,
+ fqcn: $command->class,
+ file: $command->file,
+ nodeIds: ["command::{$command->signature}"],
+ detail: $command->description,
+ );
+ }
+
+ foreach ($schedules as $schedule) {
+ $entryPoints[] = new EntryPoint(
+ kind: EntryPoint::KIND_SCHEDULE,
+ label: $schedule->frequency !== ''
+ ? "{$schedule->target} ({$schedule->frequency})"
+ : $schedule->target,
+ // A `->job(Foo::class)` entry names a class; a `->command()` one names a
+ // signature, and the command it points at is already its own entry point.
+ fqcn: $schedule->type === 'job' ? $schedule->target : '',
+ file: $schedule->file,
+ nodeIds: ['schedule::'.md5($schedule->type.$schedule->target.$schedule->frequency)],
+ detail: $schedule->type,
+ );
+ }
+
+ foreach ($channels as $channel) {
+ $entryPoints[] = new EntryPoint(
+ kind: EntryPoint::KIND_CHANNEL,
+ label: $channel->name,
+ fqcn: $channel->class,
+ file: $channel->file,
+ nodeIds: ['channel::'.md5($channel->name)],
+ );
+ }
+
+ foreach (self::queuedListeners($listenerEdges, $classes) as $fqcn => $events) {
+ $declared = $classes?->get($fqcn);
+ $entryPoints[] = new EntryPoint(
+ kind: EntryPoint::KIND_QUEUED_LISTENER,
+ label: self::shortName($fqcn),
+ fqcn: $fqcn,
+ file: $declared !== null ? $declared->file : '',
+ detail: 'queued on '.implode(', ', array_map(
+ static fn (string $event): string => self::shortName($event),
+ $events,
+ )),
+ );
+ }
+
+ foreach ($filamentPanels as $panel) {
+ $entryPoints[] = new EntryPoint(
+ kind: EntryPoint::KIND_FILAMENT,
+ label: $panel->id !== '' ? "panel: {$panel->id}" : self::shortName($panel->fqcn),
+ fqcn: $panel->fqcn,
+ file: $panel->file,
+ nodeIds: ["filament_panel::{$panel->fqcn}"],
+ detail: 'panel',
+ );
+ }
+
+ foreach ($filamentResources as $resource) {
+ $entryPoints[] = new EntryPoint(
+ kind: EntryPoint::KIND_FILAMENT,
+ label: self::shortName($resource->fqcn),
+ fqcn: $resource->fqcn,
+ file: $resource->file,
+ nodeIds: ["filament_resource::{$resource->fqcn}"],
+ detail: 'resource',
+ );
+ }
+
+ foreach ($filamentPages as $page) {
+ $entryPoints[] = new EntryPoint(
+ kind: EntryPoint::KIND_FILAMENT,
+ label: $page->route !== '' ? $page->route : self::shortName($page->fqcn),
+ fqcn: $page->fqcn,
+ file: $page->file,
+ nodeIds: ["filament_page::{$page->fqcn}"],
+ detail: 'page',
+ );
+ }
+
+ return $entryPoints;
+ }
+
+ /**
+ * Listener FQCN => the events it handles, for listeners the queue runs.
+ *
+ * A listener Brain cannot find a declaration for is left out rather than guessed at: the
+ * whole distinction rests on `implements ShouldQueue`, and a listener whose file was never
+ * scanned has no such evidence either way. Counting it as a root would let anything it
+ * calls pass as reached on nothing more than a missing file.
+ *
+ * @param CallChainEdge[] $listenerEdges
+ * @return array>
+ */
+ private static function queuedListeners(array $listenerEdges, ?ClassInventory $classes): array
+ {
+ if ($classes === null) {
+ return [];
+ }
+
+ $queued = [];
+ foreach ($listenerEdges as $edge) {
+ if ($edge->type !== 'listener') {
+ continue;
+ }
+ if (! self::implementsShouldQueue($edge->calleeFqcn, $classes)) {
+ continue;
+ }
+ $queued[$edge->calleeFqcn][$edge->callerFqcn] = true;
+ }
+
+ return array_map(
+ static fn (array $events): array => array_keys($events),
+ $queued,
+ );
+ }
+
+ private static function implementsShouldQueue(string $fqcn, ClassInventory $classes): bool
+ {
+ $seen = [];
+
+ while ($fqcn !== '' && ! isset($seen[$fqcn])) {
+ $seen[$fqcn] = true;
+ $declared = $classes->get($fqcn);
+ if ($declared === null) {
+ return false;
+ }
+ if (in_array(self::SHOULD_QUEUE, $declared->interfaces, true)) {
+ return true;
+ }
+ $fqcn = $declared->parent;
+ }
+
+ return false;
+ }
+
+ private static function shortName(string $fqcn): string
+ {
+ $pos = strrpos($fqcn, '\\');
+
+ return $pos === false ? $fqcn : substr($fqcn, $pos + 1);
+ }
+}
diff --git a/src/Analysis/Reachability/ReachabilityAnalyzer.php b/src/Analysis/Reachability/ReachabilityAnalyzer.php
new file mode 100644
index 00000000..03738895
--- /dev/null
+++ b/src/Analysis/Reachability/ReachabilityAnalyzer.php
@@ -0,0 +1,231 @@
+ $entryPoints
+ */
+ public function analyze(
+ Graph $graph,
+ array $entryPoints,
+ ClassInventory $classes,
+ ?ContainerBindingRegistry $bindings = null,
+ ?FacadeRegistry $facades = null,
+ ?ClassStringIndex $sourceReferences = null,
+ ?ClassStringIndex $configReferences = null,
+ ): ReachabilityReport {
+ $sourceReferences ??= ClassStringIndex::empty();
+ $configReferences ??= ClassStringIndex::empty();
+
+ $reached = $this->reachedFqcns($graph, $entryPoints);
+ $declared = $classes->all();
+
+ $boundNames = $this->boundNames($bindings, $facades);
+ $inherited = $this->inheritedByReached($declared, $reached);
+
+ $unreached = [];
+ foreach ($declared as $fqcn => $class) {
+ if (isset($reached[$fqcn])) {
+ continue;
+ }
+
+ $references = [];
+ if (isset($boundNames[$fqcn])) {
+ $references[] = $boundNames[$fqcn];
+ }
+ if ($configReferences->hasReferenceTo($fqcn, $class->file)) {
+ $references[] = UnreachedClass::REFERENCE_CONFIG;
+ }
+ if (isset($inherited[$fqcn])) {
+ $references[] = UnreachedClass::REFERENCE_INHERITED;
+ }
+ if ($sourceReferences->hasReferenceTo($fqcn, $class->file)) {
+ $references[] = UnreachedClass::REFERENCE_CLASS_STRING;
+ }
+
+ $unreached[] = new UnreachedClass(
+ fqcn: $fqcn,
+ file: $class->file,
+ kind: $class->kind,
+ unfollowableReferences: $references,
+ tracerBlind: ClassInventory::isTracerBlind($class->kind),
+ );
+ }
+
+ return new ReachabilityReport(
+ entryPoints: $entryPoints,
+ unreached: $unreached,
+ classesDeclared: count($declared),
+ classesReached: count($declared) - count($unreached),
+ );
+ }
+
+ /**
+ * Every application FQCN a forward walk from the entry points arrives at.
+ *
+ * Seeds come from two places, because neither alone finds every root. An entry point with
+ * a signature — a route, a command, a channel, a schedule entry — is found by the node id
+ * built from that signature, since a closure route has no class to look up. An entry point
+ * that is only a class — a queued listener above all — is found by matching the FQCN
+ * against every node carrying it, since its node id is a slug of the class and the method
+ * that nothing outside GraphBuilder can reconstruct.
+ *
+ * The walk then follows every edge in the graph, including the two {@see
+ * \LaraMint\LaravelBrain\Graph\GraphSplitter} drops when it cuts per-route tabs: those are
+ * dropped there to stop a shared controller fanning a tab out to its sibling actions,
+ * which is a rendering concern and not a second definition of what reaches what.
+ *
+ * An entry point's own class counts as reached without needing a node at all. A
+ * `$schedule->job(RebuildIndex::class)` entry builds a node keyed by a hash of the entry
+ * and carries the class only as a string; if nothing else dispatches that job, no node
+ * anywhere is keyed by it. It runs every hour — a root, not a finding.
+ *
+ * @param list $entryPoints
+ * @return array
+ */
+ private function reachedFqcns(Graph $graph, array $entryPoints): array
+ {
+ $adjacency = [];
+ foreach ($graph->edges() as $edge) {
+ $adjacency[$edge->source][] = $edge->target;
+ }
+
+ $nodesByFqcn = [];
+ foreach ($graph->nodes() as $node) {
+ foreach (['fqcn', 'class'] as $key) {
+ $value = $node->data[$key] ?? null;
+ if (is_string($value) && $value !== '') {
+ $nodesByFqcn[ltrim($value, '\\')][] = $node->id;
+ }
+ }
+ }
+
+ $reached = [];
+ $seeds = [];
+ foreach ($entryPoints as $entryPoint) {
+ foreach ($entryPoint->nodeIds as $nodeId) {
+ $seeds[] = $nodeId;
+ }
+ if ($entryPoint->fqcn === '') {
+ continue;
+ }
+ $fqcn = ltrim($entryPoint->fqcn, '\\');
+ $reached[$fqcn] = true;
+ foreach ($nodesByFqcn[$fqcn] ?? [] as $nodeId) {
+ $seeds[] = $nodeId;
+ }
+ }
+
+ $visited = [];
+ while ($seeds !== []) {
+ $id = array_pop($seeds);
+ if (isset($visited[$id])) {
+ continue;
+ }
+ $visited[$id] = true;
+
+ $node = $graph->getNode($id);
+ if ($node !== null) {
+ foreach (['fqcn', 'class'] as $key) {
+ $value = $node->data[$key] ?? null;
+ if (is_string($value) && $value !== '') {
+ $reached[ltrim($value, '\\')] = true;
+ }
+ }
+ }
+
+ foreach ($adjacency[$id] ?? [] as $neighbour) {
+ if (! isset($visited[$neighbour])) {
+ $seeds[] = $neighbour;
+ }
+ }
+ }
+
+ return $reached;
+ }
+
+ /**
+ * FQCNs the container or a facade names, mapped to the reference kind to report.
+ *
+ * Both sides of a binding are recorded: the abstract because `app(Contract::class)`
+ * resolves through it, and the concrete because that is the class that actually runs and
+ * the one a reader would otherwise be told nothing reaches.
+ *
+ * @return array
+ */
+ private function boundNames(?ContainerBindingRegistry $bindings, ?FacadeRegistry $facades): array
+ {
+ $named = [];
+
+ foreach (($bindings?->all() ?? []) as $record) {
+ $named[ltrim($record->abstractFqcn, '\\')] = UnreachedClass::REFERENCE_CONTAINER_BINDING;
+ if ($record->concreteFqcn !== null && $record->concreteFqcn !== '') {
+ $named[ltrim($record->concreteFqcn, '\\')] = UnreachedClass::REFERENCE_CONTAINER_BINDING;
+ }
+ }
+
+ foreach (($facades?->all() ?? []) as $record) {
+ $named[ltrim($record->facadeFqcn, '\\')] = UnreachedClass::REFERENCE_FACADE;
+ if ($record->concreteFqcn !== null && $record->concreteFqcn !== '') {
+ $named[ltrim($record->concreteFqcn, '\\')] = UnreachedClass::REFERENCE_FACADE;
+ }
+ }
+
+ return $named;
+ }
+
+ /**
+ * Classes a *reached* class extends, implements or uses as a trait.
+ *
+ * A base class carrying the work its subclasses inherit is reached in every sense a
+ * reader cares about, and the tracer records a hop to it only where a call happens to
+ * resolve there. This is the cheap, exact version of that: the inventory already knows
+ * every declaration's parents, interfaces and traits.
+ *
+ * @param array $declared
+ * @param array $reached
+ * @return array
+ */
+ private function inheritedByReached(array $declared, array $reached): array
+ {
+ $inherited = [];
+
+ foreach ($declared as $fqcn => $class) {
+ if (! isset($reached[$fqcn])) {
+ continue;
+ }
+ foreach (array_merge([$class->parent], $class->interfaces, $class->traits) as $ancestor) {
+ if ($ancestor !== '') {
+ $inherited[ltrim($ancestor, '\\')] = true;
+ }
+ }
+ }
+
+ return $inherited;
+ }
+}
diff --git a/src/Analysis/Reachability/ReachabilityReport.php b/src/Analysis/Reachability/ReachabilityReport.php
new file mode 100644
index 00000000..041d63bf
--- /dev/null
+++ b/src/Analysis/Reachability/ReachabilityReport.php
@@ -0,0 +1,75 @@
+ $entryPoints
+ * @param list $unreached
+ * @param int $classesDeclared classes found under the configured source paths
+ * @param int $classesReached of those, the ones a traced chain arrives at
+ */
+ public function __construct(
+ public array $entryPoints,
+ public array $unreached,
+ public int $classesDeclared,
+ public int $classesReached,
+ ) {}
+
+ /**
+ * Entry points grouped by kind, in {@see EntryPoint::KIND_ORDER}, empty kinds omitted.
+ *
+ * @return array>
+ */
+ public function entryPointsByKind(): array
+ {
+ $byKind = [];
+ foreach ($this->entryPoints as $entryPoint) {
+ $byKind[$entryPoint->kind][] = $entryPoint;
+ }
+
+ $ordered = [];
+ foreach (EntryPoint::KIND_ORDER as $kind) {
+ if (isset($byKind[$kind])) {
+ $ordered[$kind] = $byKind[$kind];
+ }
+ }
+ foreach ($byKind as $kind => $entries) {
+ $ordered[$kind] ??= $entries;
+ }
+
+ return $ordered;
+ }
+
+ /**
+ * Unreached classes grouped by kind, largest group first — "17 jobs nothing dispatches"
+ * is the sentence this tab exists to answer, and it should be the first thing on it.
+ *
+ * @param bool $tracerBlind which half to return: the kinds the tracer can reach, or the
+ * kinds it structurally cannot
+ * @return array>
+ */
+ public function unreachedByKind(bool $tracerBlind = false): array
+ {
+ $byKind = [];
+ foreach ($this->unreached as $class) {
+ if ($class->tracerBlind !== $tracerBlind) {
+ continue;
+ }
+ $byKind[$class->kind][] = $class;
+ }
+
+ uasort($byKind, static function (array $a, array $b): int {
+ return count($b) <=> count($a) ?: strcmp($a[0]->kind, $b[0]->kind);
+ });
+
+ return $byKind;
+ }
+}
diff --git a/src/Analysis/Reachability/UnreachedClass.php b/src/Analysis/Reachability/UnreachedClass.php
new file mode 100644
index 00000000..b39f15dc
--- /dev/null
+++ b/src/Analysis/Reachability/UnreachedClass.php
@@ -0,0 +1,51 @@
+ $unfollowableReferences REFERENCE_* constants — every way Brain
+ * found this class named without a call to
+ * follow into it
+ * @param bool $tracerBlind true when the tracer has no edge type for this kind at all,
+ * so its absence from the graph is expected rather than a finding
+ */
+ public function __construct(
+ public string $fqcn,
+ public string $file,
+ public string $kind,
+ public array $unfollowableReferences = [],
+ public bool $tracerBlind = false,
+ ) {}
+}
diff --git a/src/Graph/GraphSplitter.php b/src/Graph/GraphSplitter.php
index 0b782af2..67067a66 100644
--- a/src/Graph/GraphSplitter.php
+++ b/src/Graph/GraphSplitter.php
@@ -12,6 +12,8 @@
use LaraMint\LaravelBrain\Analysis\FilamentPanelDefinition;
use LaraMint\LaravelBrain\Analysis\FilamentResourceDefinition;
use LaraMint\LaravelBrain\Analysis\ModelDefinition;
+use LaraMint\LaravelBrain\Analysis\Reachability\ReachabilityReport;
+use LaraMint\LaravelBrain\Analysis\Reachability\UnreachedClass;
use LaraMint\LaravelBrain\Analysis\RouteDefinition;
use LaraMint\LaravelBrain\Analysis\ScheduleEntry;
use LaraMint\LaravelBrain\Analysis\SchemaIssueBuilder;
@@ -949,4 +951,240 @@ private function sanitizeId(string $group): string
return $id;
}
+
+ /**
+ * Build the standalone "Reachability" tab: the roots the application can be entered
+ * from, and the classes no root's call chain arrives at.
+ *
+ * Every other tab is grown forward from one entry point, which is why a gap in the graph
+ * has never been visible from inside it — measured on one application the graph knew 45
+ * of 211 event classes and 27 of 113 job classes, and no screen said so. This tab is the
+ * inverse view, and independent of routes for the same reason the ERD tab is.
+ *
+ * Three sections, in the order a reader needs them:
+ *
+ * 1. Entry points, by kind. The denominator — nothing is reachable except through one
+ * of these, so their inventory is what makes the other two sections mean anything.
+ * 2. Classes nothing reaches, by kind, largest kind first. Each carries every reference
+ * Brain found and could not follow, because "nothing reaches this from a traced
+ * entry point" and "this is dead code" are different sentences and the second one is
+ * not Brain's to make.
+ * 3. Kinds the tracer has no edge type for at all — service providers, exceptions. Kept
+ * apart rather than mixed in: their absence from the graph is the expected outcome,
+ * and a hundred non-findings on top of the real ones is how a report gets ignored.
+ *
+ * @return array{id: string, graph: Graph, manifest: TabManifestEntry}|null
+ */
+ public function buildReachabilityTab(
+ ReachabilityReport $report,
+ string $projectName,
+ string $analyzedAt,
+ ): ?array {
+ if ($report->entryPoints === [] && $report->unreached === []) {
+ return null;
+ }
+
+ $graph = new Graph;
+ $graph->setMeta(['project' => $projectName, 'analyzedAt' => $analyzedAt]);
+
+ $this->addEntryPointSection($graph, $report);
+ $this->addUnreachedSection(
+ $graph,
+ $report->unreachedByKind(),
+ 'reachability::unreached',
+ 'Nothing reaches these from an entry point',
+ self::UNREACHED_NOTE,
+ );
+ $this->addUnreachedSection(
+ $graph,
+ $report->unreachedByKind(tracerBlind: true),
+ 'reachability::unfollowed',
+ 'Outside what the tracer follows',
+ self::TRACER_BLIND_NOTE,
+ );
+
+ $tabId = 'reachability--inventory';
+
+ return [
+ 'id' => $tabId,
+ 'graph' => $graph,
+ 'manifest' => new TabManifestEntry(
+ id: $tabId,
+ label: 'Reachability',
+ routeCount: count($report->unreached),
+ nodeCount: $graph->nodeCount(),
+ edgeCount: $graph->edgeCount(),
+ file: ".graph-{$tabId}.json",
+ category: 'Reachability',
+ ),
+ ];
+ }
+
+ /**
+ * The sentence this tab must never be read as saying something stronger than.
+ */
+ private const UNREACHED_NOTE = 'No traced call chain from an entry point arrives at these classes. '
+ .'That is a statement about what the tracer can follow, not about whether the code runs: '
+ .'anything resolved out of the container, fronted by a facade, named as a string in config, '
+ .'or built by reflection is invisible to it. Every reference Brain did find is listed on the class.';
+
+ private const TRACER_BLIND_NOTE = 'Brain has no call edge for these kinds at all — the framework '
+ .'boots a service provider and an exception is thrown rather than called — so their absence '
+ .'from the graph is expected and says nothing either way. They are listed for inventory only.';
+
+ private function addEntryPointSection(Graph $graph, ReachabilityReport $report): void
+ {
+ $rootId = 'reachability::entry-points';
+ $graph->addNode(new Node(
+ id: $rootId,
+ type: 'entry_point_group',
+ label: 'Entry points ('.count($report->entryPoints).')',
+ data: [
+ 'section' => 'entry-points',
+ 'count' => count($report->entryPoints),
+ 'classesDeclared' => $report->classesDeclared,
+ 'classesReached' => $report->classesReached,
+ 'note' => 'Every root the application can be entered from. Nothing in the graph is '
+ .'reachable except through one of these.',
+ ],
+ ));
+
+ foreach ($report->entryPointsByKind() as $kind => $entryPoints) {
+ $groupId = $rootId.'::'.$kind;
+ $graph->addNode(new Node(
+ id: $groupId,
+ type: 'entry_point_group',
+ label: $this->groupLabel($kind, count($entryPoints)),
+ // Folded on arrival: the members are the inventory, and a canvas that opens
+ // with every one of them drawn is unreadable at any zoom. The group says how
+ // many it holds; the reader opens the one they came for.
+ data: ['section' => 'entry-points', 'kind' => $kind, 'count' => count($entryPoints), 'collapsedByDefault' => true],
+ ));
+ $this->addGroupEdge($graph, $rootId, $groupId, 'entry-point-group');
+
+ foreach ($entryPoints as $index => $entryPoint) {
+ $nodeId = $groupId.'::'.$index;
+ $graph->addNode(new Node(
+ id: $nodeId,
+ type: 'entry_point',
+ label: $entryPoint->label,
+ data: [
+ 'section' => 'entry-points',
+ 'kind' => $kind,
+ 'fqcn' => $entryPoint->fqcn,
+ 'file' => $entryPoint->file,
+ 'detail' => $entryPoint->detail,
+ ],
+ ));
+ $this->addGroupEdge($graph, $groupId, $nodeId, 'entry-point');
+ }
+ }
+ }
+
+ /**
+ * @param array> $byKind
+ */
+ private function addUnreachedSection(
+ Graph $graph,
+ array $byKind,
+ string $rootId,
+ string $rootLabel,
+ string $note,
+ ): void {
+ if ($byKind === []) {
+ return;
+ }
+
+ $total = 0;
+ foreach ($byKind as $classes) {
+ $total += count($classes);
+ }
+
+ $graph->addNode(new Node(
+ id: $rootId,
+ type: 'unreached_group',
+ label: "{$rootLabel} ({$total})",
+ data: ['section' => 'unreached', 'count' => $total, 'note' => $note],
+ ));
+
+ foreach ($byKind as $kind => $classes) {
+ $groupId = $rootId.'::'.$kind;
+ $graph->addNode(new Node(
+ id: $groupId,
+ type: 'unreached_group',
+ label: $this->groupLabel($kind, count($classes)),
+ data: ['section' => 'unreached', 'kind' => $kind, 'count' => count($classes), 'note' => $note, 'collapsedByDefault' => true],
+ ));
+ $this->addGroupEdge($graph, $rootId, $groupId, 'unreached-group');
+
+ foreach ($classes as $class) {
+ $nodeId = 'unreached::'.strtolower((string) preg_replace('/[^a-zA-Z0-9_]/', '_', $class->fqcn));
+ $graph->addNode(new Node(
+ id: $nodeId,
+ type: 'unreached_class',
+ label: $this->shortName($class->fqcn),
+ data: [
+ 'section' => 'unreached',
+ 'kind' => $kind,
+ 'fqcn' => $class->fqcn,
+ 'file' => $class->file,
+ 'unfollowableReferences' => $class->unfollowableReferences,
+ 'tracerBlind' => $class->tracerBlind,
+ 'note' => $note,
+ ],
+ ));
+ $this->addGroupEdge($graph, $groupId, $nodeId, 'unreached');
+ }
+ }
+ }
+
+ private function addGroupEdge(Graph $graph, string $source, string $target, string $type): void
+ {
+ $graph->addEdge(new Edge(
+ id: 'reach::'.md5($source.'|'.$target),
+ source: $source,
+ target: $target,
+ label: '',
+ type: $type,
+ ));
+ }
+
+ /**
+ * Plural group heading for a kind. Unknown kinds fall through to the kind name with an
+ * "s" — a new node type added elsewhere in Brain then reads slightly awkwardly rather
+ * than vanishing from the tab.
+ */
+ private function groupLabel(string $kind, int $count): string
+ {
+ $label = match ($kind) {
+ 'route' => 'Routes',
+ 'command' => 'Console commands',
+ 'schedule' => 'Scheduled entries',
+ 'channel' => 'Broadcast channels',
+ 'queued_listener' => 'Queued listeners',
+ 'filament' => 'Filament',
+ 'abstract_class' => 'Abstract classes',
+ 'policy' => 'Policies',
+ 'repository' => 'Repositories',
+ 'service_provider' => 'Service providers',
+ 'middleware' => 'Middleware',
+ 'mail' => 'Mailables',
+ 'notification' => 'Notifications',
+ 'resource' => 'API resources',
+ 'controller' => 'Controllers',
+ 'exception' => 'Exceptions',
+ 'interface' => 'Interfaces',
+ 'trait' => 'Traits',
+ 'enum' => 'Enums',
+ 'model' => 'Models',
+ 'listener' => 'Listeners',
+ 'observer' => 'Observers',
+ 'service' => 'Services',
+ 'event' => 'Events',
+ 'job' => 'Jobs',
+ default => ucfirst(str_replace('_', ' ', $kind)).'s',
+ };
+
+ return "{$label} ({$count})";
+ }
}
diff --git a/tests/Unit/ClassInventoryTest.php b/tests/Unit/ClassInventoryTest.php
new file mode 100644
index 00000000..979f64f1
--- /dev/null
+++ b/tests/Unit/ClassInventoryTest.php
@@ -0,0 +1,74 @@
+all();
+
+ expect($all)->toHaveKey('App\Jobs\ArchiveOrders')
+ ->and($all['App\Jobs\ArchiveOrders']->kind)->toBe('job')
+ ->and($all['App\Contracts\Importer']->surface)->toBe('interface')
+ ->and($all['App\Support\BaseWorkflow']->surface)->toBe('abstract_class')
+ ->and($all['App\Providers\AppServiceProvider']->kind)->toBe('service_provider')
+ ->and($all['App\Exceptions\OrderFailed']->kind)->toBe('exception')
+ ->and($all['App\Http\Controllers\OrderController']->kind)->toBe('controller');
+});
+
+it('records what a declaration extends, implements and uses', function () {
+ $inventory = ClassInventory::scan(fixture('reachability-project'), ['app']);
+
+ expect($inventory->get('App\Services\OrderService')->parent)->toBe('App\Support\BaseWorkflow')
+ ->and($inventory->get('App\Listeners\NotifyWarehouse')->interfaces)
+ ->toBe(['Illuminate\Contracts\Queue\ShouldQueue']);
+});
+
+it('names middleware and API resources before the controller heuristic claims them', function () {
+ // Both live under \Http\, which the controller rule matches. Ordered the other way round
+ // every middleware class in an application is filed as a controller.
+ expect(ClassInventory::kindOf('App\Http\Middleware\EnsureTenant', 'class'))->toBe('middleware')
+ ->and(ClassInventory::kindOf('App\Http\Resources\OrderResource', 'class'))->toBe('resource')
+ ->and(ClassInventory::kindOf('App\Http\Controllers\OrderController', 'class'))->toBe('controller');
+});
+
+it('reports the declaration surface rather than a name-based kind for non-classes', function () {
+ expect(ClassInventory::kindOf('App\Jobs\Contract', 'interface'))->toBe('interface')
+ ->and(ClassInventory::kindOf('App\Jobs\Concerns\Retries', 'trait'))->toBe('trait')
+ ->and(ClassInventory::kindOf('App\Jobs\Status', 'enum'))->toBe('enum');
+});
+
+it('knows which kinds the tracer has no edge for', function () {
+ expect(ClassInventory::isTracerBlind('service_provider'))->toBeTrue()
+ ->and(ClassInventory::isTracerBlind('exception'))->toBeTrue()
+ ->and(ClassInventory::isTracerBlind('job'))->toBeFalse();
+});
+
+it('finds a class named as ::class and as a quoted FQCN, but not by its own file', function () {
+ $index = ClassStringIndex::scan(fixture('reachability-project'), ['app', 'config']);
+
+ expect($index->hasReferenceTo('App\Support\ReportRenderer'))->toBeTrue()
+ ->and($index->hasReferenceTo('App\Jobs\RebuildIndex'))->toBeTrue()
+ ->and($index->hasReferenceTo('App\Jobs\ArchiveOrders'))->toBeFalse()
+ // Paths in the index are resolved, the same way the inventory's are, so the two
+ // agree on what "its own file" is.
+ ->and($index->hasReferenceTo(
+ 'App\Support\ReportRenderer',
+ (string) realpath(fixture('reachability-project/app/Providers/AppServiceProvider.php')),
+ ))->toBeFalse();
+});
+
+it('does not read a path or a regex as a class name', function () {
+ // The scan looks at every string literal in the source tree. A looser pattern turns
+ // "vendor/bin/x" into a reference and every unreached class picks up a hint that means
+ // nothing.
+ expect(preg_match(ClassStringIndex::FQCN_PATTERN, 'App\Jobs\RebuildIndex'))->toBe(1)
+ ->and(preg_match(ClassStringIndex::FQCN_PATTERN, 'routes/web.php'))->toBe(0)
+ ->and(preg_match(ClassStringIndex::FQCN_PATTERN, 'App\\'))->toBe(0)
+ ->and(preg_match(ClassStringIndex::FQCN_PATTERN, 'orders:sync'))->toBe(0)
+ // A separator is required. Without one, every 'handle' and 'default' in the tree is a
+ // reference to some root-namespace class, and the hint stops discriminating anything.
+ ->and(preg_match(ClassStringIndex::FQCN_PATTERN, 'handle'))->toBe(0);
+});
diff --git a/tests/Unit/EntryPointInventoryTest.php b/tests/Unit/EntryPointInventoryTest.php
new file mode 100644
index 00000000..43a95f57
--- /dev/null
+++ b/tests/Unit/EntryPointInventoryTest.php
@@ -0,0 +1,121 @@
+ listenerClass('App\Listeners\NotifyWarehouse', ['Illuminate\Contracts\Queue\ShouldQueue']),
+ 'App\Listeners\LogOrder' => listenerClass('App\Listeners\LogOrder', []),
+ ]);
+
+ $entryPoints = EntryPointInventory::collect(listenerEdges: $edges, classes: $classes);
+
+ expect(array_map(fn (EntryPoint $e): string => $e->fqcn, $entryPoints))
+ ->toBe(['App\Listeners\NotifyWarehouse']);
+});
+
+it('finds ShouldQueue on a listener that inherits it from a base class', function () {
+ $edges = [
+ new CallChainEdge('App\Events\OrderPlaced', 'dispatch', 'App\Listeners\NotifyWarehouse', 'handle', 'listener'),
+ ];
+
+ $classes = ClassInventory::of([
+ 'App\Listeners\NotifyWarehouse' => listenerClass('App\Listeners\NotifyWarehouse', []),
+ 'App\Listeners\QueuedListener' => new DeclaredClass(
+ 'App\Listeners\QueuedListener',
+ '/app/Listeners/QueuedListener.php',
+ 'abstract_class',
+ 'abstract_class',
+ '',
+ ['Illuminate\Contracts\Queue\ShouldQueue'],
+ ),
+ ]);
+
+ expect(EntryPointInventory::collect(listenerEdges: $edges, classes: $classes))->toBe([]);
+
+ $classes = ClassInventory::of([
+ 'App\Listeners\NotifyWarehouse' => new DeclaredClass(
+ 'App\Listeners\NotifyWarehouse',
+ '/app/Listeners/NotifyWarehouse.php',
+ 'class',
+ 'listener',
+ 'App\Listeners\QueuedListener',
+ ),
+ 'App\Listeners\QueuedListener' => new DeclaredClass(
+ 'App\Listeners\QueuedListener',
+ '/app/Listeners/QueuedListener.php',
+ 'abstract_class',
+ 'abstract_class',
+ '',
+ ['Illuminate\Contracts\Queue\ShouldQueue'],
+ ),
+ ]);
+
+ expect(EntryPointInventory::collect(listenerEdges: $edges, classes: $classes))->toHaveCount(1);
+});
+
+it('gives routes, commands, channels and schedule entries the node id the graph built them under', function () {
+ // The FQCN alone cannot find these: their node id is built from a signature, and a closure
+ // route or closure command has no class at all. Get an id wrong and the walk silently
+ // starts from nothing, which reads as a healthy application with no reachable code.
+ $entryPoints = EntryPointInventory::collect(
+ routes: [new RouteDefinition('POST', '/orders', '', '', [], '', '/routes/web.php', 1)],
+ commands: [new ConsoleCommandDefinition('orders:sync', '', '', '/routes/console.php', 'route')],
+ schedules: [new ScheduleEntry('command', 'orders:sync', 'daily', '/routes/console.php')],
+ channels: [new ChannelDefinition('orders.{id}', '', '/routes/channels.php')],
+ );
+
+ $ids = [];
+ foreach ($entryPoints as $entryPoint) {
+ $ids[$entryPoint->kind] = $entryPoint->nodeIds;
+ }
+
+ expect($ids)->toBe([
+ EntryPoint::KIND_ROUTE => ['route::POST::/orders'],
+ EntryPoint::KIND_COMMAND => ['command::orders:sync'],
+ EntryPoint::KIND_SCHEDULE => ['schedule::'.md5('command'.'orders:sync'.'daily')],
+ EntryPoint::KIND_CHANNEL => ['channel::'.md5('orders.{id}')],
+ ]);
+});
+
+it('takes a scheduled job class as its own root but leaves a scheduled command to its own entry', function () {
+ $entryPoints = EntryPointInventory::collect(schedules: [
+ new ScheduleEntry('job', 'App\Jobs\RebuildIndex', 'hourly', '/routes/console.php'),
+ new ScheduleEntry('command', 'orders:sync', 'daily', '/routes/console.php'),
+ ]);
+
+ expect($entryPoints[0]->fqcn)->toBe('App\Jobs\RebuildIndex')
+ ->and($entryPoints[1]->fqcn)->toBe('');
+});
diff --git a/tests/Unit/ReachabilityAnalyzerTest.php b/tests/Unit/ReachabilityAnalyzerTest.php
new file mode 100644
index 00000000..e1ca4050
--- /dev/null
+++ b/tests/Unit/ReachabilityAnalyzerTest.php
@@ -0,0 +1,224 @@
+ */
+function unreachedByFqcn(ReachabilityReport $report): array
+{
+ $byFqcn = [];
+ foreach ($report->unreached as $class) {
+ $byFqcn[$class->fqcn] = $class;
+ }
+
+ return $byFqcn;
+}
+
+it('reaches what a chain from an entry point arrives at, and nothing else', function () {
+ $graph = new Graph;
+ $graph->addNode(new Node('route::GET::/orders', 'route', 'GET /orders'));
+ $graph->addNode(new Node('action::App\Http\Controllers\OrderController::index', 'action', 'index', [
+ 'fqcn' => 'App\Http\Controllers\OrderController',
+ ]));
+ $graph->addNode(new Node('app_services_orderservice::place', 'service', 'OrderService@place', [
+ 'fqcn' => 'App\Services\OrderService',
+ ]));
+ $graph->addEdge(new Edge('e0', 'route::GET::/orders', 'action::App\Http\Controllers\OrderController::index', '', 'flow'));
+ $graph->addEdge(new Edge('e1', 'action::App\Http\Controllers\OrderController::index', 'app_services_orderservice::place', '', 'flow'));
+
+ $inventory = ClassInventory::of([
+ 'App\Http\Controllers\OrderController' => declaredClass('App\Http\Controllers\OrderController'),
+ 'App\Services\OrderService' => declaredClass('App\Services\OrderService'),
+ 'App\Jobs\ArchiveOrders' => declaredClass('App\Jobs\ArchiveOrders'),
+ ]);
+
+ $report = (new ReachabilityAnalyzer)->analyze(
+ $graph,
+ [new EntryPoint(
+ kind: EntryPoint::KIND_ROUTE,
+ label: 'GET /orders',
+ fqcn: 'App\Http\Controllers\OrderController',
+ nodeIds: ['route::GET::/orders'],
+ )],
+ $inventory,
+ );
+
+ expect(array_keys(unreachedByFqcn($report)))->toBe(['App\Jobs\ArchiveOrders'])
+ ->and($report->classesDeclared)->toBe(3)
+ ->and($report->classesReached)->toBe(2);
+});
+
+it('counts an entry point class as reached even when no node carries its name', function () {
+ // `$schedule->job(RebuildIndex::class)` builds a schedule node keyed by a hash of the
+ // entry, carrying the target as a plain string and nothing the graph can key by class; if
+ // nothing else dispatches the job there is no node for it anywhere. It is a root — the
+ // scheduler runs it every hour — and reporting it as unreached would be plainly wrong.
+ $graph = new Graph;
+ $graph->addNode(new Node('schedule::'.md5('jobApp\Jobs\RebuildIndexhourly'), 'schedule', 'RebuildIndex (hourly)', [
+ 'type' => 'job',
+ 'target' => 'App\Jobs\RebuildIndex',
+ ]));
+
+ $report = (new ReachabilityAnalyzer)->analyze(
+ $graph,
+ [new EntryPoint(
+ kind: EntryPoint::KIND_SCHEDULE,
+ label: 'RebuildIndex (hourly)',
+ fqcn: 'App\Jobs\RebuildIndex',
+ nodeIds: ['schedule::'.md5('jobApp\Jobs\RebuildIndexhourly')],
+ )],
+ ClassInventory::of([
+ 'App\Jobs\RebuildIndex' => declaredClass('App\Jobs\RebuildIndex'),
+ ]),
+ );
+
+ expect($report->unreached)->toBe([]);
+});
+
+it('names every reference it found and could not follow, so unreached is never read as dead', function () {
+ // The distinction this whole tab rests on: each of these classes is reachable at runtime
+ // through a mechanism that leaves no call for the tracer to follow, and each must arrive
+ // at the reader carrying the reason.
+ $bindings = new ContainerBindingRegistry;
+ $bindings->add(new ContainerBindingRecord(
+ abstractFqcn: 'App\Contracts\Importer',
+ concreteFqcn: 'App\Services\LegacyImporter',
+ providerFqcn: 'App\Providers\AppServiceProvider',
+ kind: 'singleton',
+ ));
+
+ $facades = new FacadeRegistry;
+ $facades->add(new FacadeRecord('App\Facades\Money', 'money', 'App\Support\MoneyManager'));
+
+ $inventory = ClassInventory::of([
+ 'App\Services\LegacyImporter' => declaredClass('App\Services\LegacyImporter'),
+ 'App\Support\MoneyManager' => declaredClass('App\Support\MoneyManager'),
+ 'App\Jobs\RebuildIndex' => declaredClass('App\Jobs\RebuildIndex'),
+ 'App\Support\BaseWorkflow' => declaredClass('App\Support\BaseWorkflow', 'abstract_class'),
+ 'App\Services\OrderService' => declaredClass('App\Services\OrderService', parent: 'App\Support\BaseWorkflow'),
+ 'App\Support\ReportRenderer' => declaredClass('App\Support\ReportRenderer'),
+ 'App\Jobs\ArchiveOrders' => declaredClass('App\Jobs\ArchiveOrders'),
+ ]);
+
+ $graph = new Graph;
+ $graph->addNode(new Node('app_services_orderservice::place', 'service', 'OrderService@place', [
+ 'fqcn' => 'App\Services\OrderService',
+ ]));
+
+ $report = (new ReachabilityAnalyzer)->analyze(
+ $graph,
+ [new EntryPoint(
+ kind: EntryPoint::KIND_ROUTE,
+ label: 'GET /orders',
+ fqcn: 'App\Services\OrderService',
+ )],
+ $inventory,
+ $bindings,
+ $facades,
+ ClassStringIndex::scan(fixture('reachability-project'), ['app']),
+ ClassStringIndex::scan(fixture('reachability-project'), ['config']),
+ );
+
+ $byFqcn = unreachedByFqcn($report);
+
+ expect($byFqcn['App\Services\LegacyImporter']->unfollowableReferences)
+ ->toContain(UnreachedClass::REFERENCE_CONTAINER_BINDING)
+ ->and($byFqcn['App\Support\MoneyManager']->unfollowableReferences)
+ ->toContain(UnreachedClass::REFERENCE_FACADE)
+ ->and($byFqcn['App\Jobs\RebuildIndex']->unfollowableReferences)
+ ->toContain(UnreachedClass::REFERENCE_CONFIG)
+ ->and($byFqcn['App\Support\BaseWorkflow']->unfollowableReferences)
+ ->toContain(UnreachedClass::REFERENCE_INHERITED)
+ ->and($byFqcn['App\Support\ReportRenderer']->unfollowableReferences)
+ ->toContain(UnreachedClass::REFERENCE_CLASS_STRING)
+ // The one class in the set that nothing names at all. If this ever picks up a
+ // reference the hints have stopped discriminating and the report says nothing.
+ ->and($byFqcn['App\Jobs\ArchiveOrders']->unfollowableReferences)->toBe([]);
+});
+
+it('files kinds the tracer has no edge for apart from the ones it does', function () {
+ // A service provider is booted, an exception is thrown; neither is ever called, so their
+ // absence from the graph is the expected outcome. Mixed in with the jobs they would be a
+ // hundred non-findings burying the real ones.
+ $inventory = ClassInventory::of([
+ 'App\Providers\AppServiceProvider' => declaredClass('App\Providers\AppServiceProvider'),
+ 'App\Exceptions\OrderFailed' => declaredClass('App\Exceptions\OrderFailed'),
+ 'App\Jobs\ArchiveOrders' => declaredClass('App\Jobs\ArchiveOrders'),
+ ]);
+
+ $report = (new ReachabilityAnalyzer)->analyze(new Graph, [], $inventory);
+
+ expect(array_keys($report->unreachedByKind()))->toBe(['job'])
+ ->and(array_keys($report->unreachedByKind(tracerBlind: true)))->toBe(['exception', 'service_provider']);
+});
+
+it('groups the unreached largest kind first', function () {
+ // "17 jobs nothing dispatches" is the sentence the tab exists to answer, so the biggest
+ // group has to be the first one a reader meets.
+ $classes = [
+ 'App\Support\Lonely' => declaredClass('App\Support\Lonely'),
+ 'App\Jobs\A' => declaredClass('App\Jobs\A'),
+ 'App\Jobs\B' => declaredClass('App\Jobs\B'),
+ ];
+
+ $report = (new ReachabilityAnalyzer)->analyze(new Graph, [], ClassInventory::of($classes));
+
+ expect(array_keys($report->unreachedByKind()))->toBe(['job', 'service']);
+});
+
+it('starts a walk from an entry point that has no node id, only a class', function () {
+ // A queued listener is not a node the graph builds by name: its node id is a slug of the
+ // FQCN and the method, which nothing outside GraphBuilder can construct. Finding it by
+ // class is the only way, and without it everything a queued listener calls — for many
+ // applications the whole asynchronous half — reports as reached by nothing.
+ $graph = new Graph;
+ $graph->addNode(new Node('app_listeners_notifywarehouse::handle', 'listener', 'NotifyWarehouse@handle', [
+ 'fqcn' => 'App\Listeners\NotifyWarehouse',
+ ]));
+ $graph->addNode(new Node('app_services_warehouseclient::notify', 'service', 'WarehouseClient@notify', [
+ 'fqcn' => 'App\Services\WarehouseClient',
+ ]));
+ $graph->addEdge(new Edge('e0', 'app_listeners_notifywarehouse::handle', 'app_services_warehouseclient::notify', '', 'flow'));
+
+ $report = (new ReachabilityAnalyzer)->analyze(
+ $graph,
+ [new EntryPoint(
+ kind: EntryPoint::KIND_QUEUED_LISTENER,
+ label: 'NotifyWarehouse',
+ fqcn: 'App\Listeners\NotifyWarehouse',
+ )],
+ ClassInventory::of([
+ 'App\Listeners\NotifyWarehouse' => declaredClass('App\Listeners\NotifyWarehouse'),
+ 'App\Services\WarehouseClient' => declaredClass('App\Services\WarehouseClient'),
+ ]),
+ );
+
+ expect($report->unreached)->toBe([]);
+});
diff --git a/tests/Unit/ReachabilityTabTest.php b/tests/Unit/ReachabilityTabTest.php
new file mode 100644
index 00000000..69b4f9bb
--- /dev/null
+++ b/tests/Unit/ReachabilityTabTest.php
@@ -0,0 +1,194 @@
+buildReachabilityTab($report, 'proj', '2026-01-01T00:00:00Z');
+
+ $byId = [];
+ foreach ($tab['graph']->nodes() as $node) {
+ $byId[$node->id] = $node;
+ }
+
+ return $byId;
+}
+
+function bootBrainConfig(array $overrides = []): void
+{
+ $container = new Container;
+ Container::setInstance($container);
+ $container->instance('config', new Repository([
+ 'app' => ['name' => 'Reachability'],
+ 'laravel-brain' => array_replace_recursive(
+ require __DIR__.'/../../config/laravel-brain.php',
+ $overrides,
+ ),
+ ]));
+}
+
+afterEach(function () {
+ Container::setInstance(null);
+});
+
+it('lays the tab out as entry points, then what nothing reaches, then what it cannot follow', function () {
+ $report = new ReachabilityReport(
+ entryPoints: [new EntryPoint(EntryPoint::KIND_ROUTE, 'GET /orders', 'App\Http\Controllers\OrderController')],
+ unreached: [
+ new UnreachedClass('App\Jobs\ArchiveOrders', '/app/Jobs/ArchiveOrders.php', 'job'),
+ new UnreachedClass('App\Providers\AppServiceProvider', '/app/Providers/AppServiceProvider.php', 'service_provider', [], true),
+ ],
+ classesDeclared: 3,
+ classesReached: 1,
+ );
+
+ $nodes = reachabilityTabNodes($report);
+
+ expect($nodes['reachability::entry-points']->label)->toBe('Entry points (1)')
+ ->and($nodes['reachability::entry-points::route']->label)->toBe('Routes (1)')
+ ->and($nodes['reachability::unreached']->label)->toBe('Nothing reaches these from an entry point (1)')
+ ->and($nodes['reachability::unreached::job']->label)->toBe('Jobs (1)')
+ ->and($nodes['reachability::unfollowed']->label)->toBe('Outside what the tracer follows (1)')
+ ->and($nodes['reachability::unfollowed::service_provider']->label)->toBe('Service providers (1)');
+});
+
+it('carries the caveat onto every unreached node rather than leaving it in a heading', function () {
+ // A reader clicks a class and gets a panel; if the qualification only lives on a group
+ // heading three levels up, the panel says "nothing reaches this" and nothing else, which
+ // is the reading that gets code deleted.
+ $report = new ReachabilityReport(
+ entryPoints: [],
+ unreached: [new UnreachedClass(
+ 'App\Services\LegacyImporter',
+ '/app/Services/LegacyImporter.php',
+ 'service',
+ [UnreachedClass::REFERENCE_CONTAINER_BINDING],
+ )],
+ classesDeclared: 1,
+ classesReached: 0,
+ );
+
+ $node = reachabilityTabNodes($report)['unreached::app_services_legacyimporter'];
+
+ expect($node->data['unfollowableReferences'])->toBe([UnreachedClass::REFERENCE_CONTAINER_BINDING])
+ ->and($node->data['note'])->toContain('not about whether the code runs')
+ ->and($node->data['note'])->not->toContain('dead');
+});
+
+it('omits a section that has nothing in it', function () {
+ $report = new ReachabilityReport(
+ entryPoints: [new EntryPoint(EntryPoint::KIND_ROUTE, 'GET /orders')],
+ unreached: [],
+ classesDeclared: 1,
+ classesReached: 1,
+ );
+
+ expect(reachabilityTabNodes($report))->not->toHaveKey('reachability::unreached');
+});
+
+it('builds no tab at all for a project with neither entry points nor classes', function () {
+ $report = new ReachabilityReport([], [], 0, 0);
+
+ expect((new GraphSplitter)->buildReachabilityTab($report, 'proj', '2026-01-01T00:00:00Z'))->toBeNull();
+});
+
+it('surfaces the tab under its own sidebar category', function () {
+ $report = new ReachabilityReport(
+ entryPoints: [new EntryPoint(EntryPoint::KIND_ROUTE, 'GET /orders')],
+ unreached: [new UnreachedClass('App\Jobs\ArchiveOrders', '/app/Jobs/ArchiveOrders.php', 'job')],
+ classesDeclared: 2,
+ classesReached: 1,
+ );
+
+ $tab = (new GraphSplitter)->buildReachabilityTab($report, 'proj', '2026-01-01T00:00:00Z');
+
+ expect($tab['id'])->toBe('reachability--inventory')
+ ->and($tab['manifest']->category)->toBe('Reachability')
+ ->and($tab['manifest']->label)->toBe('Reachability')
+ ->and($tab['manifest']->routeCount)->toBe(1);
+});
+
+it('reports a real project end to end', function () {
+ // Stated rather than assumed: the pass now ships off, so a test about what it reports has
+ // to turn it on.
+ bootBrainConfig(['reachability' => ['enabled' => true]]);
+
+ $result = (new ProjectAnalyzer)->analyze(fixture('reachability-project'), function () {});
+ $report = $result->reachability;
+
+ $entryPointLabels = [];
+ foreach ($report->entryPoints as $entryPoint) {
+ $entryPointLabels[$entryPoint->kind][] = $entryPoint->label;
+ }
+
+ $unreached = [];
+ foreach ($report->unreached as $class) {
+ $unreached[$class->fqcn] = $class->unfollowableReferences;
+ }
+
+ expect($entryPointLabels)->toBe([
+ EntryPoint::KIND_ROUTE => ['POST /orders'],
+ EntryPoint::KIND_COMMAND => ['orders:sync'],
+ EntryPoint::KIND_QUEUED_LISTENER => ['NotifyWarehouse'],
+ ])
+ // Dispatched from a traced service; the graph already knew about it.
+ ->and($unreached)->not->toHaveKey('App\Jobs\SendReceipt')
+ // Handled by an event the graph reaches, so not a root and not a finding.
+ ->and($unreached)->not->toHaveKey('App\Listeners\LogOrder')
+ ->and($unreached['App\Jobs\ArchiveOrders'])->toBe([])
+ ->and($unreached['App\Jobs\RebuildIndex'])->toBe([UnreachedClass::REFERENCE_CONFIG])
+ ->and($unreached['App\Services\LegacyImporter'])->toContain(UnreachedClass::REFERENCE_CONTAINER_BINDING)
+ ->and($unreached['App\Support\BaseWorkflow'])->toBe([UnreachedClass::REFERENCE_INHERITED])
+ ->and($unreached['App\Support\ReportRenderer'])->toBe([UnreachedClass::REFERENCE_CLASS_STRING])
+ ->and($result->subgraphs)->toHaveKey('reachability--inventory');
+});
+
+it('builds no reachability tab when the feature is switched off', function () {
+ bootBrainConfig(['reachability' => ['enabled' => false]]);
+
+ $result = (new ProjectAnalyzer)->analyze(fixture('reachability-project'), function () {});
+
+ expect($result->reachability)->toBeNull()
+ ->and($result->subgraphs)->not->toHaveKey('reachability--inventory');
+});
+
+it('asks the viewer to open the inventory folded to its groups', function () {
+ // One node per class nothing reaches: a few thousand of them on a real application, which
+ // no zoom makes readable. The groups say how many they hold and the reader opens the one
+ // they came for. Measured before and after on a 60-module application: 3982 nodes drawn at
+ // 0% zoom, against 28 at 19%.
+ bootBrainConfig(['reachability' => ['enabled' => true]]);
+
+ $result = (new ProjectAnalyzer)->analyze(fixture('reachability-project'), function () {});
+ $tab = $result->subgraphs['reachability--inventory'];
+
+ $folded = [];
+ $members = [];
+
+ foreach ($tab->nodes() as $node) {
+ if (($node->data['collapsedByDefault'] ?? false) === true) {
+ $folded[] = $node->type;
+
+ continue;
+ }
+
+ if (in_array($node->type, ['entry_point', 'unreached_class'], true)) {
+ $members[] = $node->type;
+ }
+ }
+
+ // Both kinds of group ask to be folded...
+ expect(array_unique($folded))
+ ->toEqualCanonicalizing(['entry_point_group', 'unreached_group'])
+ // ...and nothing else does: folding a member would hide the thing it names, and folding
+ // a root would close the whole screen.
+ ->and($members)->not->toBeEmpty();
+});
diff --git a/tests/fixtures/reachability-project/app/Console/Commands/SyncCommand.php b/tests/fixtures/reachability-project/app/Console/Commands/SyncCommand.php
new file mode 100644
index 00000000..e9d417ec
--- /dev/null
+++ b/tests/fixtures/reachability-project/app/Console/Commands/SyncCommand.php
@@ -0,0 +1,17 @@
+place();
+ }
+}
diff --git a/tests/fixtures/reachability-project/app/Jobs/ArchiveOrders.php b/tests/fixtures/reachability-project/app/Jobs/ArchiveOrders.php
new file mode 100644
index 00000000..f9cdd5aa
--- /dev/null
+++ b/tests/fixtures/reachability-project/app/Jobs/ArchiveOrders.php
@@ -0,0 +1,15 @@
+ registered by name, never called from here */
+ protected $renderers = [
+ ReportRenderer::class,
+ ];
+
+ public function register(): void
+ {
+ $this->app->singleton(Importer::class, LegacyImporter::class);
+ }
+}
diff --git a/tests/fixtures/reachability-project/app/Services/LegacyImporter.php b/tests/fixtures/reachability-project/app/Services/LegacyImporter.php
new file mode 100644
index 00000000..9dccdd08
--- /dev/null
+++ b/tests/fixtures/reachability-project/app/Services/LegacyImporter.php
@@ -0,0 +1,17 @@
+ RebuildIndex::class,
+];
diff --git a/tests/fixtures/reachability-project/routes/web.php b/tests/fixtures/reachability-project/routes/web.php
new file mode 100644
index 00000000..099b6734
--- /dev/null
+++ b/tests/fixtures/reachability-project/routes/web.php
@@ -0,0 +1,6 @@
+