{
  "generatedAt": "2026-09-15T15:06:01.177Z",
  "count": 77,
  "components": [
    {
      "name": "Accordion",
      "level": "organism",
      "category": "surface",
      "classification": "themed-mui",
      "description": "Expandable sections for progressive disclosure. Themed MUI Accordion.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Accordion",
      "muiDocs": "https://mui.com/material-ui/react-accordion/",
      "a2uiEquivalent": "Accordion",
      "importPath": "@/organisms/Accordion",
      "optionalProps": [
        "expanded",
        "defaultExpanded",
        "disabled",
        "onChange"
      ],
      "supportedStates": [
        "collapsed",
        "expanded",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "radius",
        "typography"
      ],
      "accessibility": [
        "Summary is a button controlling the region; keyboard operable."
      ],
      "usageExamples": [
        "<Accordion><AccordionSummary><Typography variant=\"subtitle1\">Evidence</Typography></AccordionSummary><AccordionDetails><Typography variant=\"body1\">...</Typography></AccordionDetails></Accordion>"
      ],
      "rules": [
        "Summary text is `Typography variant=\"subtitle1\"`; detail copy is `variant=\"body1\"`."
      ],
      "antiPatterns": [
        "Do not hide critical content behind accordions by default."
      ],
      "mountsSurface": "inline",
      "llmSafe": true,
      "whenToUse": [
        "Long detail pages, grouped settings"
      ],
      "whenNotToUse": [
        "Primary navigation"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Alert",
      "level": "molecule",
      "category": "feedback",
      "classification": "themed-mui",
      "description": "Inline contextual feedback message with severity. Themed MUI Alert.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Alert",
      "muiDocs": "https://mui.com/material-ui/react-alert/",
      "a2uiEquivalent": "Alert",
      "importPath": "@/molecules/Alert",
      "optionalProps": [
        "severity",
        "variant",
        "onClose",
        "icon",
        "action"
      ],
      "supportedStates": [
        "success",
        "info",
        "warning",
        "error",
        "dismissible"
      ],
      "designTokens": [
        "colors",
        "radius",
        "typography"
      ],
      "accessibility": [
        "Alerts use role=\"alert\"; place near the related content."
      ],
      "usageExamples": [
        "<Alert severity=\"error\"><AlertTitle>Could not load findings</AlertTitle>The scanner is unreachable.</Alert>"
      ],
      "rules": [
        "Use `AlertTitle` for the heading (themed as subtitle2). The message body uses the Alert body1 slot — do not wrap it in another heading variant."
      ],
      "antiPatterns": [
        "Do not use Alert for transient notifications (use Snackbar)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Persistent inline status/validation messages"
      ],
      "whenNotToUse": [
        "Auto-dismissing toasts (use Snackbar)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "AlertsExample",
      "level": "page",
      "category": "example",
      "classification": "custom",
      "description": "Reference alerts feed page using the Alert molecule.",
      "baseLibrary": "custom",
      "importPath": "@/pages/AlertsExample",
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "colors"
      ],
      "usageExamples": [
        "<AlertsExample />"
      ],
      "llmSafe": false,
      "whenToUse": [
        "Reference for alert/notification feeds"
      ],
      "whenNotToUse": [
        "Production without data wiring"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "AnalystMarkdown",
      "level": "organism",
      "category": "data-display",
      "classification": "custom",
      "description": "Renders an AI-written investigation summary, turning resolved entity values inside the prose into interactive chips an analyst can copy and pivot on. Ships from the @aistrike-dev/ui/markdown subpath so the markdown renderer stays out of the root bundle.",
      "baseLibrary": "react-markdown",
      "importPath": "@aistrike-dev/ui/markdown",
      "requiredProps": [
        "source"
      ],
      "optionalProps": [
        "entities",
        "aiGenerated",
        "maxMarkedPerBlock",
        "entityActions",
        "onEntityAction"
      ],
      "supportedStates": [
        "default",
        "ai-generated",
        "entity-menu-open",
        "no-entities"
      ],
      "designTokens": [
        "typography",
        "spacing",
        "colors"
      ],
      "accessibility": [
        "Entity chips join the tab order in reading order, and each opens its menu with Enter or Space.",
        "Every entity has an accessible name carrying its kind, its full canonical value and its verdict, since the visible text may be truncated and the tint is not perceivable to everyone.",
        "A long summary is a lot of tab stops. Keep the marked-span cap low and treat a chip-dense paragraph as a content problem upstream rather than a navigation problem to work around."
      ],
      "usageExamples": [
        "import { AnalystMarkdown } from '@aistrike-dev/ui/markdown';",
        "<AnalystMarkdown source={summary.markdown} entities={summary.entities} aiGenerated />",
        "<AnalystMarkdown source={md} entities={spans} entityActions={[{ id: \"pivot\", label: \"Pivot to search\" }]} onEntityAction={run} />"
      ],
      "rules": [
        "Import from `@aistrike-dev/ui/markdown`, never from the root entry; the root stays free of the markdown renderer.",
        "Supply `entities` as offsets into the exact `source` string you pass, produced by the model or the enrichment service.",
        "Mark only what an analyst can act on: network and file indicators, principals, and catalogue references. Not ordinary nouns, product names, times or counts.",
        "Pass the canonical full value as `value` and any shortened form as `display`, so a truncated hash still copies in full.",
        "Set `aiGenerated` for model-written content. One marker covers the block.",
        "Keep `maxMarkedPerBlock` at or below the default of 8. A summary genuinely dense with indicators wants a table underneath it, not more chips inside it.",
        "Put pivot and lookup actions in `entityActions`; keep response actions on the entity page."
      ],
      "antiPatterns": [
        "Do not derive entity spans by pattern-matching the prose. A pattern for IP addresses also matches a version number, and a hash pattern matches any long hex string; every false positive attaches a menu to something with no entity behind it.",
        "Do not attach a verdict to a value that was not resolved; that is a fabricated claim.",
        "Do not compute offsets against rendered text. Emphasis markers, links and escapes shift the indices and silently attach entities to the wrong words.",
        "Do not add rehype-raw or any raw-HTML plugin. The input is model output, and parsing HTML in it would make this component an XSS sink.",
        "Do not put an AI marker on every chip; mark the generated block once.",
        "Do not use this for prose the product wrote itself; plain Typography is lighter and needs no renderer."
      ],
      "llmSafe": true,
      "whenToUse": [
        "AI-generated investigation or alert summaries where analysts need to act on the values",
        "Any model output whose entities have already been resolved to structured spans"
      ],
      "whenNotToUse": [
        "Static product copy (use Typography)",
        "Untrusted markdown with no entity resolution (there is nothing to gain over Typography)",
        "Code display (use CodeEditor from @aistrike-dev/ui/monaco)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "AppBar",
      "level": "organism",
      "category": "navigation",
      "classification": "mui-overrides",
      "description": "Top application bar for branding, search and global actions. Themed MUI AppBar.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "AppBar",
      "muiDocs": "https://mui.com/material-ui/react-app-bar/",
      "a2uiEquivalent": "Header",
      "importPath": "@/organisms/AppBar",
      "optionalProps": [
        "position",
        "color",
        "elevation"
      ],
      "supportedStates": [
        "static",
        "sticky",
        "fixed"
      ],
      "designTokens": [
        "colors",
        "shadows",
        "typography"
      ],
      "accessibility": [
        "Use a single banner landmark; provide aria-labels for icon buttons."
      ],
      "usageExamples": [
        "<AppBar position=\"static\"><Toolbar><Typography variant=\"h6\">Octopus</Typography></Toolbar></AppBar>"
      ],
      "rules": [
        "The product name in the bar is `Typography variant=\"h6\"` (or subtitle1 on a compact bar)."
      ],
      "antiPatterns": [
        "Do not stuff primary navigation into the AppBar (use LeftNavigation)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Global top bar",
        "Branding + search + user menu"
      ],
      "whenNotToUse": [
        "Section headers (use Typography/Toolbar)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "AttackPathLegend",
      "level": "organism",
      "category": "data-display",
      "classification": "custom",
      "description": "Legend mapping attack-path node types to their stroke/fill colors, from design tokens.",
      "baseLibrary": "custom",
      "a2uiEquivalent": "Legend",
      "importPath": "@/organisms/AttackPathLegend",
      "optionalProps": [
        "types",
        "orientation"
      ],
      "supportedStates": [
        "horizontal",
        "vertical"
      ],
      "designTokens": [
        "attackPath",
        "typography"
      ],
      "accessibility": [
        "Rendered as a labelled list; swatches are aria-hidden with text labels."
      ],
      "usageExamples": [
        "<AttackPathLegend />",
        "<AttackPathLegend types={[\"host\",\"role\"]} />"
      ],
      "antiPatterns": [
        "Do not hardcode attack-path colors; always source from tokens."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Alongside attack-path graphs/diagrams",
        "Investigation layouts"
      ],
      "whenNotToUse": [
        "Generic status legends (use Chips)"
      ],
      "figma": "https://www.figma.com/design/LJdrBxmaROpXg0hLW3GKe6/AiStrike-Complete-Design-System?node-id=23-476",
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Autocomplete",
      "level": "molecule",
      "category": "input",
      "classification": "themed-mui",
      "description": "Searchable combobox for selecting from many options. Themed MUI Autocomplete.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Autocomplete",
      "muiDocs": "https://mui.com/material-ui/react-autocomplete/",
      "a2uiEquivalent": "Combobox",
      "importPath": "@/molecules/Autocomplete",
      "requiredProps": [
        "options",
        "renderInput"
      ],
      "optionalProps": [
        "multiple",
        "value",
        "onChange",
        "freeSolo",
        "groupBy",
        "loading",
        "disabled"
      ],
      "supportedStates": [
        "default",
        "open",
        "loading",
        "multiple",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Renders an ARIA combobox; ensure renderInput uses a labeled TextField."
      ],
      "usageExamples": [
        "<Autocomplete options={opts} renderInput={(p) => <TextField {...p} label=\"Region\" />} />"
      ],
      "antiPatterns": [
        "Do not use for very small static lists (use Select/Radio)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Searchable selection, tagging, async options"
      ],
      "whenNotToUse": [
        "Few fixed options"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Avatar",
      "level": "atom",
      "category": "data-display",
      "classification": "themed-mui",
      "description": "Represents a user or entity via image, initials or icon. Themed MUI Avatar.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Avatar",
      "muiDocs": "https://mui.com/material-ui/react-avatar/",
      "a2uiEquivalent": "Avatar",
      "importPath": "@/atoms/Avatar",
      "optionalProps": [
        "src",
        "alt",
        "variant",
        "children",
        "sx"
      ],
      "supportedStates": [
        "image",
        "initials",
        "icon",
        "fallback"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Always set alt for image avatars; initials should reflect the entity name."
      ],
      "usageExamples": [
        "<Avatar src=\"/u.png\" alt=\"Jane\" />",
        "<Avatar>JS</Avatar>"
      ],
      "rules": [
        "An avatar is a neutral control fill two rungs above its host, carrying primary text. It reads its own colour from the surface it is on, so there is nothing to pass in.",
        "Two rungs, not one, because one rung up is what a nested card takes - an avatar there would read as a small panel rather than as an identity mark.",
        "If you override bgcolor with a saturated colour, set color as well. The default ink is light, and light ink on brand teal is 1.89:1. Pair the fill with its own contrastText."
      ],
      "antiPatterns": [
        "Do not omit alt on image avatars.",
        "Do not set bgcolor from the brand to make an avatar stand out. Brand marks the primary action and the current selection; an identity mark is neither."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Showing user/entity identity"
      ],
      "whenNotToUse": [
        "Decorative icons (use Icon)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Badge",
      "level": "atom",
      "category": "data-display",
      "classification": "themed-mui",
      "description": "Small count or status indicator anchored to another element. Themed MUI Badge.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Badge",
      "muiDocs": "https://mui.com/material-ui/react-badge/",
      "a2uiEquivalent": "Badge",
      "importPath": "@/atoms/Badge",
      "optionalProps": [
        "badgeContent",
        "color",
        "variant",
        "max",
        "showZero",
        "overlap"
      ],
      "supportedStates": [
        "count",
        "dot",
        "zero",
        "max"
      ],
      "designTokens": [
        "colors"
      ],
      "accessibility": [
        "Ensure the count is conveyed to screen readers via the wrapped control label."
      ],
      "usageExamples": [
        "<Badge badgeContent={4} color=\"error\"><NotificationsIcon /></Badge>"
      ],
      "antiPatterns": [
        "Do not place long text in a Badge (use Chip)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Unread counts, notification dots, status"
      ],
      "whenNotToUse": [
        "Standalone labels (use Chip)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Breadcrumbs",
      "level": "molecule",
      "category": "navigation",
      "classification": "themed-mui",
      "description": "Shows the user location within a hierarchy. Themed MUI Breadcrumbs.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Breadcrumbs",
      "muiDocs": "https://mui.com/material-ui/react-breadcrumbs/",
      "a2uiEquivalent": "Breadcrumbs",
      "importPath": "@/molecules/Breadcrumbs",
      "optionalProps": [
        "separator",
        "maxItems"
      ],
      "supportedStates": [
        "default",
        "collapsed"
      ],
      "designTokens": [
        "colors",
        "typography"
      ],
      "accessibility": [
        "Set aria-label=\"breadcrumb\"; mark the current page as Typography (not a link) so it is not announced as a destination."
      ],
      "usageExamples": [
        "<Breadcrumbs aria-label=\"breadcrumb\"><Link href=\"/assets\">Assets</Link><Typography variant=\"body1\">Finding 4821</Typography></Breadcrumbs>"
      ],
      "rules": [
        "The current page is `Typography variant=\"body1\"`, never a `Link`."
      ],
      "antiPatterns": [
        "Do not use for primary navigation."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Deep hierarchical pages (detail views)"
      ],
      "whenNotToUse": [
        "Flat apps with shallow navigation"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Button",
      "level": "atom",
      "category": "input",
      "classification": "mui-overrides",
      "description": "Primary interactive control for actions. Themed MUI Button with an emphasis ladder of variants: `primary` (one per view), `secondary` (default), `ghost`, and `destructive`.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Button",
      "muiDocs": "https://mui.com/material-ui/react-button/",
      "a2uiEquivalent": "Button",
      "importPath": "@/atoms/Button",
      "requiredProps": [],
      "optionalProps": [
        "variant",
        "size",
        "startIcon",
        "endIcon",
        "disabled",
        "onClick"
      ],
      "props": [
        {
          "name": "variant",
          "type": "'primary' | 'secondary' | 'ghost' | 'destructive'",
          "default": "secondary"
        },
        {
          "name": "size",
          "type": "'small' | 'medium' | 'large'",
          "default": "medium"
        },
        {
          "name": "disabled",
          "type": "boolean",
          "default": "false"
        },
        {
          "name": "onClick",
          "type": "(e: MouseEvent) => void"
        }
      ],
      "customVariants": [
        "primary",
        "secondary",
        "ghost",
        "destructive"
      ],
      "supportedStates": [
        "default",
        "hover",
        "focus",
        "active",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "radius",
        "typography",
        "controlHeights"
      ],
      "accessibility": [
        "Renders a native <button>; keyboard operable (Enter/Space) by default.",
        "Provide descriptive text or aria-label for icon-only usage (prefer IconButton).",
        "Focus ring is visible via Mui-focusVisible."
      ],
      "usageExamples": [
        "<Button variant=\"primary\">Save changes</Button>",
        "<Button variant=\"destructive\">Delete account</Button>"
      ],
      "rules": [
        "Every action control is the design-system Button; never a raw <button>, a directly-imported MUI Button, or a custom-styled clickable element.",
        "Pick the variant from the emphasis ladder: `primary` for the single most important action in a view, `secondary` (the default) for ordinary actions, `ghost` for tertiary ones such as Cancel, `destructive` for irreversible ones.",
        "When several buttons sit inline, make them all `secondary` and promote at most one to `primary`. If you cannot confidently judge which should be primary, ask.",
        "Write labels that name the action (\"Save changes\", \"Send invite\"), not \"OK\" or \"Click here\".",
        "Show a loading state for actions that take time, so the user knows it is working.",
        "For a dedicated icon-only control use IconButton with an accessible label and a tooltip, not Button.",
        "Pick `size` from context: `small` in dense toolbars and tables, `medium` in standard forms, `large` for prominent CTAs. If you are not sure, ask."
      ],
      "antiPatterns": [
        "Do not place more than one primary button in the same view; it dilutes the visual hierarchy.",
        "Do not use the destructive variant without a confirmation step for irreversible actions.",
        "Do not use Button for navigation between pages; use a link.",
        "Do not put long sentences inside a Button."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Triggering an action",
        "Submitting a form",
        "Confirming a dialog"
      ],
      "whenNotToUse": [
        "Navigating to another page (use a link)",
        "Toggling a setting (use Switch/ToggleButton)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "ButtonGroup",
      "level": "molecule",
      "category": "input",
      "classification": "themed-mui",
      "description": "Groups related buttons into a single visual unit. Themed MUI ButtonGroup.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "ButtonGroup",
      "muiDocs": "https://mui.com/material-ui/react-button-group/",
      "a2uiEquivalent": "ButtonGroup",
      "importPath": "@/molecules/ButtonGroup",
      "optionalProps": [
        "variant",
        "orientation",
        "size",
        "disabled"
      ],
      "supportedStates": [
        "default",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Each button remains independently focusable."
      ],
      "usageExamples": [
        "<ButtonGroup><Button>Day</Button><Button>Week</Button></ButtonGroup>"
      ],
      "antiPatterns": [
        "Do not use for mutually-exclusive selection (use ToggleButtonGroup)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Related action clusters"
      ],
      "whenNotToUse": [
        "Single selection toggles"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Card",
      "level": "organism",
      "category": "surface",
      "classification": "mui-overrides",
      "description": "Bordered surface grouping related content. Default is themed MUI Card; variant=\"info\" is a static summary card (32px vertical / 24px horizontal padding) and variant=\"data\" is a collapsible card for text, tables or lists. Use CardDataTag for inline entity pills.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Card",
      "muiDocs": "https://mui.com/material-ui/react-card/",
      "a2uiEquivalent": "Card",
      "importPath": "@/organisms/Card",
      "optionalProps": [
        "elevation",
        "variant",
        "title",
        "emptyText",
        "defaultExpanded",
        "expanded",
        "onChange"
      ],
      "customVariants": [
        "info",
        "data"
      ],
      "supportedStates": [
        "default",
        "hover",
        "selected",
        "expanded",
        "collapsed",
        "empty"
      ],
      "designTokens": [
        "colors",
        "radius",
        "shadows",
        "spacing",
        "typography"
      ],
      "accessibility": [
        "Use semantic headings inside CardHeader; make whole-card click targets accessible.",
        "The data variant header is a button with aria-expanded."
      ],
      "usageExamples": [
        "<Card><CardHeader title=\"prod-db-01\" subheader=\"EC2 instance\" /><CardContent><Typography variant=\"body1\">...</Typography></CardContent></Card>",
        "<Card variant=\"info\" title=\"Overview\"><Typography variant=\"body1\">...<CardDataTag>10.0.0.42</CardDataTag></Typography></Card>",
        "<Card variant=\"data\" title=\"Historical Cases\"><DataTable columns={columns} data={rows} bordered={false} surface=\"transparent\" /></Card>"
      ],
      "rules": [
        "CardHeader title is subtitle1 and the subheader is body1 (theme defaults). Body copy is `Typography variant=\"body1\"`.",
        "Info/data titles are subtitle1; the body slot applies body1 via `sx.typography` so tables are not wrapped in a `<p>`.",
        "A card is separated from its host by the tonal step of the surface ladder, not by a border. It takes the next rung up from whatever contains it, so it does not need to be told which background to use.",
        "A card opens a surface scope at that next rung, so nested tables inherit widget header fills and row separators rather than the page's. Put a table inside a data card with `bordered={false} surface=\"transparent\"`."
      ],
      "antiPatterns": [
        "Do not put a card inside a card inside a card. Three levels of containment is the ceiling; a fourth paints identically to the third and logs an error, so the nesting buys nothing. If you need a fourth, flatten the structure or move the deepest content to its own route.",
        "Do not add a border to a card. The tonal step already separates it, and a border on top of it reads as a second, competing edge.",
        "Do not use variant=\"info\" for collapsible sections (use variant=\"data\")."
      ],
      "mountsSurface": "inline",
      "llmSafe": true,
      "whenToUse": [
        "Summaries, entity tiles, dashboards",
        "Investigation overviews (info)",
        "Optional detail sections (data)"
      ],
      "whenNotToUse": [
        "Full-page layout (use templates)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Chart",
      "level": "organism",
      "category": "data-display",
      "classification": "third-party-wrapper",
      "description": "Config-driven chart. Accepts a serialisable ChartConfig so user-built dashboards can persist and replay a chart definition. Typed per-type components (BarChart, LineChart, PieChart, ScatterChart, HeatmapChart, SankeyChart) are thin adapters over this.",
      "baseLibrary": "echarts",
      "importPath": "@/charts",
      "packageImport": "@aistrike-dev/ui/charts",
      "requiredProps": [
        "config",
        "data"
      ],
      "optionalProps": [
        "mode",
        "media",
        "height",
        "state",
        "onPointClick",
        "transformOption",
        "label",
        "decals"
      ],
      "customVariants": [
        "screen",
        "export"
      ],
      "supportedStates": [
        "default",
        "loading",
        "empty",
        "error"
      ],
      "designTokens": [
        "chart",
        "severity"
      ],
      "accessibility": [
        "ECharts aria description is enabled, giving screen readers a text equivalent of the plot.",
        "Decal textures are available via `decals` for a secondary encoding beyond hue, but are off by default — hue alone carries series identity unless enabled.",
        "A legend appears automatically for two or more series; a single series is named by the title instead."
      ],
      "usageExamples": [
        "<BarChart xKey=\"day\" series={[{ key: \"alerts\", name: \"Alerts\" }]} data={rows} />",
        "<Chart config={savedConfig} data={rows} media=\"export\" mode=\"light\" />"
      ],
      "antiPatterns": [
        "Do not use severity colours as a series palette — severity encodes status, not identity.",
        "Do not exceed three series in scatter or bubble forms; fold the tail into \"Other\" or facet.",
        "Never render two measures of different scale on two y-axes; use two charts or index to a common base."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Any multi-point data visualisation, on screen or in an exported report"
      ],
      "whenNotToUse": [
        "A single headline number (use StatTile)",
        "A row-level trend (use Sparkline)",
        "A tabular result set (use the Table organism)"
      ]
    },
    {
      "name": "Checkbox",
      "level": "atom",
      "category": "input",
      "classification": "themed-mui",
      "description": "Boolean / multi-select control. Themed MUI Checkbox.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Checkbox",
      "muiDocs": "https://mui.com/material-ui/react-checkbox/",
      "a2uiEquivalent": "Checkbox",
      "importPath": "@/atoms/Checkbox",
      "optionalProps": [
        "checked",
        "defaultChecked",
        "indeterminate",
        "disabled",
        "size",
        "onChange"
      ],
      "supportedStates": [
        "default",
        "checked",
        "indeterminate",
        "disabled",
        "focus"
      ],
      "designTokens": [
        "colors"
      ],
      "accessibility": [
        "Wrap with FormControlLabel to provide an accessible label.",
        "Keyboard: Space toggles. Group related checkboxes in a FormGroup."
      ],
      "usageExamples": [
        "<FormControlLabel control={<Checkbox />} label=\"Enable\" />"
      ],
      "antiPatterns": [
        "Do not use a single Checkbox where a Switch (on/off setting) is more appropriate."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Selecting multiple options",
        "Opt-in / consent toggles in forms"
      ],
      "whenNotToUse": [
        "Mutually exclusive choices (use Radio)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Chip",
      "level": "atom",
      "category": "data-display",
      "classification": "mui-overrides",
      "description": "The single compact-label primitive: severity, status, category, metric, lifecycle, filter, select, entity, overflow and picker. Pick a `purpose` from meaning and the appearance, rendered semantics and keyboard contract are derived from it. The older variant/color API still works but is deprecated.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Chip",
      "muiDocs": "https://mui.com/material-ui/react-chip/",
      "a2uiEquivalent": "Badge",
      "importPath": "@/atoms/Chip",
      "requiredProps": [
        "purpose"
      ],
      "optionalProps": [
        "size",
        "icon",
        "disabled"
      ],
      "customVariants": [
        "subtle",
        "dot"
      ],
      "supportedStates": [
        "static",
        "clickable",
        "removable",
        "selected",
        "expanded",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "severity",
        "status",
        "radius",
        "typography",
        "sizes"
      ],
      "accessibility": [
        "Static purposes (severity, status, category, metric, lifecycle) render no tab stop and no click handler.",
        "Interactive purposes carry the right ARIA state automatically: select sets aria-pressed (including inside an exclusive group — exclusivity is parent state, not a radio), entity sets aria-haspopup, overflow sets aria-expanded, picker sets aria-haspopup=\"dialog\" plus aria-expanded.",
        "Filter chips are removed by pointer on the delete icon or by Backspace/Delete on the focused chip, which is MUI behaviour; the chip is named as a filter so the target is unambiguous. A picker chip removes the same way — its X removes the whole filter rather than emptying it.",
        "The delete X keeps a pointer target larger than the glyph it draws — 24px against the 16px medium glyph, meeting WCAG 2.5.8, and the same 24px at `large` and `extraLarge`. At `size=\"small\"` it is ~21px, which is as much as a 22px pill can hold. The glyph is not separately focusable, so clicking it is the only pointer route to removing a chip.",
        "Entity chips put the full canonical value and the verdict in the accessible name, because the visible text may be truncated and the tint is not perceivable to everyone.",
        "Picker chips spell the whole selection out in the accessible name, because the visible text shows only the first value and counts the rest. Past five it counts the remainder rather than reciting them.",
        "Never encode meaning through colour alone — every purpose renders a visible text label."
      ],
      "usageExamples": [
        "<Chip purpose=\"severity\" level=\"critical\" />",
        "<Chip purpose=\"status\" tone=\"warning\" label=\"Investigating\" />",
        "<Chip purpose=\"category\" label=\"Credential Access\" size=\"small\" />",
        "<Chip purpose=\"metric\" label=\"12 related\" size=\"small\" />",
        "<Chip purpose=\"lifecycle\" stage=\"beta\" size=\"small\" />",
        "<Chip purpose=\"filter\" field=\"Severity\" label=\"Critical\" onRemove={clear} />",
        "<Chip purpose=\"select\" label=\"EDR\" selected={on} onToggle={setOn} />",
        "<Chip purpose=\"select\" label=\"EDR\" selected={source === \"EDR\"} onToggle={(on) => on && setSource(\"EDR\")} />",
        "<Chip purpose=\"entity\" kind=\"ip\" value=\"203.0.113.47\" verdict=\"malicious\" onOpen={openMenu} />",
        "<Chip purpose=\"overflow\" count={3} onDisclose={showAll} />",
        "<Chip purpose=\"picker\" field=\"Severity\" values={[\"Critical\", \"High\"]} expanded={open} onOpen={openPanel} onRemove={dropField} />"
      ],
      "rules": [
        "Every tag, filter, status and risk pill is the design-system Chip. Pick `purpose` from what the value means, never from how it should look.",
        "`purpose=\"severity\"` for risk. Pass `level` from critical|high|medium|low|info|untriaged; the canonical word is derived, so do not also pass a label.",
        "`purpose=\"status\"` for workflow or health state. Pass a semantic `tone` plus the product word as `label`, e.g. tone=\"warning\" label=\"Investigating\".",
        "`purpose=\"category\"` for classification metadata: MITRE tactics, data sources, environments, asset classes. Always neutral, because colour is reserved for severity.",
        "`purpose=\"metric\"` for a measured value such as `PDF 262 KB` or `12 related`. Use this rather than Badge when nothing anchors the count.",
        "`purpose=\"lifecycle\"` for how mature a feature is: pass `stage` from new|beta|alpha|preview|deprecated and the word is derived. This is the one chip that carries the brand accent, on the outline rather than a fill, so a release marker is never mistaken for a data pill.",
        "A lifecycle chip marks the product surface — a nav item, a page title, a settings row — so put one on the name of the thing that is new, and only one. If a user could filter a list by the word, it is data about a record and belongs in `purpose=\"category\"`: `Built-in` on a workflow row is a property of that workflow, not a release stage.",
        "`purpose=\"filter\"` for an applied criterion. `onRemove` is required, and `field` keeps the criterion readable out of context.",
        "`purpose=\"select\"` for a toggleable option. `selected` and `onToggle` are both required so the state is always controlled. Each chip toggles itself — clicking it does not change its neighbours. For a mutually exclusive group, keep one selected value in the parent and pass `selected={value === option}` / `onToggle={(on) => on && setValue(option)}`; the chip does not own exclusivity.",
        "`purpose=\"entity\"` for an entity value in prose or a dense cell. It renders monospace at the surrounding text size and opens a detail menu via `onOpen`.",
        "`purpose=\"overflow\"` is the `+N more` disclosure for a capped group — prefer ChipGroup, which manages it for you.",
        "`purpose=\"picker\"` is the trigger for a multi-select filter panel — prefer FilterMenu, which owns the chip, the panel and the open state. Reach for the bare chip only when you are supplying your own surface.",
        "A picker chip is the one chip that both shows a value and changes it, and the exception is narrow: it changes which values are filtered on, never the record underneath. Pass `values` as the labels a user reads, not raw option values.",
        "Every neutral pill — `category`, `metric`, `filter`, `picker` and `overflow` — shares the chrome of an outlined TextField: a quiet fill inside a quiet border, from the same `inputColors` tokens as the inputs and the secondary Button. A filter bar is a row of form controls, so its pills should sit at the same visual level as the controls beside them rather than compete with the severity chips in the table below.",
        "Interactivity is carried by the affordance, not the outline: an X on `filter`, a chevron on `picker`, `+N more` on `overflow`, plus the focus ring and hover response. Do not read border weight as \"clickable\" — most neutral pills draw one either way.",
        "`filter` and `picker` render the field name in muted ink and the value promoted, so two chips in a bar differ where they actually differ. Pass `field` on a filter chip to get it; without one, the whole label is treated as the value.",
        "Use `size=\"small\"` (22px) in tables and dense cells. Default `medium` (28px) matches Button `small`; `large` (36px) matches Button `medium`; `extraLarge` (44px) matches Button `large`. Same size name on Chip vs Button does not mean the same height. `purpose=\"entity\"` does not accept `size` — change the surrounding Typography instead.",
        "Badge is only a count or dot anchored to another element; a standalone pill is always a Chip.",
        "Inside a Table, workflow state is TableStatusCell rather than purpose=\"status\" — a row already carries enough pill shapes."
      ],
      "antiPatterns": [
        "Do not pass `variant` or `color`; they are the deprecated API and warn in development. Use `purpose`.",
        "Do not pass Chip `size=\"small\"` next to Button `size=\"small\"` expecting them to line up — Chip `small` is the 22px table rung; use Chip `medium` beside Button `small`.",
        "Do not import Chip from @mui/material; import { Chip } from '@aistrike-dev/ui'.",
        "Do not hand-roll pill or tag elements from span/div/Box; use Chip.",
        "Do not hardcode severity hex values; pass `level`.",
        "Do not set `fontSize` on an icon passed to `icon`, and do not pre-size the SVG. The theme scales every glyph in a chip — leading icon, delete X, picker chevron — to a fixed ratio of that chip's label, so it stays right at every size and inside an entity chip that sizes itself to the surrounding prose. A fixed px value is what made these render at 24px against a 12px label.",
        "Do not use the severity scale for verdict, confidence, health or chart series.",
        "Do not colour-code categories; colour is committed to severity.",
        "Do not use a Chip as an action trigger; use Button. A chip carries a value — `Isolate host` is a verb and belongs on a Button.",
        "Do not put an interactive purpose inside a MenuItem, a table row link or any other click target.",
        "Do not wire select chips so that clicking one inverts a shared boolean across the group. That only works for two options and breaks as soon as a third appears. Exclusive grouping is one parent value, each chip reflecting whether it is that value.",
        "Do not give one chip two functions, with the single exception of `purpose=\"picker\"`, where the body opens the panel and the X removes the filter. Both operate on the same filter, and the pattern is worth the second target only there — anywhere else, a body that edits beside a corner that removes is two controls wearing one pill.",
        "Do not make the X on a picker chip clear the selection instead of removing the filter. It is the same glyph in the same corner as on `purpose=\"filter\"`, so it has to mean the same thing; emptying the selection while keeping the field is the panel's `Reset`.",
        "Do not pass `onRemove` unless the user can add the filter back, through an `Add filter` menu or similar. A removable chip with no way to return it is a dead end — for a bar of fixed fields, omit it and let `Reset` empty the selection.",
        "Do not use a picker chip to change a record. Picking which severities a table shows is a filter; setting an alert to `Contained` is a Menu behind a Button.",
        "Do not leave a lifecycle chip on a feature after it ships; a permanent `New` marker teaches users to ignore the treatment. Removing it is part of the launch, not a follow-up.",
        "Do not invent stage words by passing `label` for something outside the vocabulary — \"Coming soon\" is a roadmap claim, not a state the UI is in.",
        "Do not put a lifecycle chip on a record, a row of data or a chart series; it describes the product, not the content."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Severity and risk labels",
        "Workflow and health status",
        "Category and classification metadata",
        "Applied filters and toggleable options",
        "A filter bar where each field is a chip that opens its own value panel (via FilterMenu)",
        "Entity values inside prose",
        "Release stage markers on features (New, Beta, Deprecated)"
      ],
      "whenNotToUse": [
        "Primary actions (use Button)",
        "Changing a status in place (use a Menu-backed trigger, not a Chip)",
        "Notification counts anchored to an icon (use Badge)",
        "Long technical identifiers outside the entity purpose (use monospace Typography)",
        "Several filter fields committed together (use FilterPopover)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "ChipGroup",
      "level": "molecule",
      "category": "data-display",
      "classification": "custom",
      "description": "Layout and overflow for a collection of Chips. Wraps on the 8px grid, caps the visible count and discloses the remainder through a single `+N more` control.",
      "baseLibrary": "react",
      "importPath": "@/molecules/ChipGroup",
      "requiredProps": [
        "children"
      ],
      "optionalProps": [
        "max",
        "spacing",
        "noWrap",
        "onOverflow",
        "label",
        "size"
      ],
      "supportedStates": [
        "default",
        "capped",
        "expanded"
      ],
      "designTokens": [
        "spacing"
      ],
      "accessibility": [
        "Pass `label` to expose the group as a named `group` landmark; without it the group is presentational.",
        "The overflow control is a button carrying aria-expanded, so keyboard users can reveal the remainder."
      ],
      "usageExamples": [
        "<ChipGroup label=\"Techniques\" max={3}>{items.map((t) => <Chip key={t} purpose=\"category\" label={t} size=\"small\" />)}</ChipGroup>",
        "<ChipGroup max={2} onOverflow={openDetailPanel}>{chips}</ChipGroup>"
      ],
      "rules": [
        "Use ChipGroup whenever more than two chips sit together; it owns the spacing so children never set their own margins.",
        "Set `max` in tables and dense cells so a long list cannot change row height; leave it unset in detail views where wrapping is fine.",
        "Keep groups to six chips on one line, and wrapping to at most five lines. Past that use a multi-select or a table instead.",
        "Pass `onOverflow` when the remainder belongs in a popover or detail view; the default expands in place.",
        "Give the group a `label` when the chips share a dimension, so assistive technology can announce what the collection is.",
        "Pass `size` so the overflow `+N more` chip matches the group. Children keep their own `size` — set the same value on both.",
        "A ChipGroup of `purpose=\"select\"` chips is layout, not a selection model. Each child stays independently controlled; exclusive grouping lives in the parent."
      ],
      "antiPatterns": [
        "Do not set margins on the chips themselves; the group owns spacing via `gap`.",
        "Do not mix chip purposes in one group — severity beside category reads as one dimension.",
        "Do not render your own `+N more` element; use `max` so the count and the aria-expanded state stay correct."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Collections of tags, techniques, data sources, applied filters, or toggleable options"
      ],
      "whenNotToUse": [
        "A single chip (render it directly)",
        "Selecting from many options (use Autocomplete or FilterPanel)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "CodeEditor",
      "level": "organism",
      "category": "input",
      "classification": "third-party-wrapper",
      "description": "Monaco-backed code editor. Loads only the languages and features the caller names. Published at the separate `@aistrike-dev/ui/monaco` entry.",
      "baseLibrary": "monaco-editor",
      "importPath": "@/monaco",
      "packageImport": "@aistrike-dev/ui/monaco",
      "requiredProps": [
        "value",
        "ariaLabel"
      ],
      "optionalProps": [
        "onChange",
        "language",
        "languages",
        "features",
        "readOnly",
        "height",
        "showTabFocusHint",
        "options",
        "onMount",
        "loadingFallback",
        "onLoadError"
      ],
      "supportedStates": [
        "loading",
        "ready",
        "readOnly",
        "error"
      ],
      "designTokens": [
        "colors",
        "typography"
      ],
      "accessibility": [
        "`ariaLabel` is required — Monaco exposes a bare textarea to assistive technology.",
        "Tab inserts a tab character. Ctrl+M (Ctrl+Shift+M on macOS) toggles tab-focus mode so Tab releases focus instead (WCAG 2.1.2). It works out of the box; `showTabFocusHint` opts into advertising it on screen, and is off by default."
      ],
      "usageExamples": [
        "<CodeEditor value={sql} onChange={setSql} language=\"sql\" ariaLabel=\"SQL query\" />",
        "<CodeEditor value={q} language=\"fsl\" languages={[{ id: \"fsl\", load: () => import(\"@aistrike/fsl-monaco\") }]} ariaLabel=\"FSL query\" />"
      ],
      "antiPatterns": [
        "Do not import from `@aistrike-dev/ui` — the editor lives at `@aistrike-dev/ui/monaco`.",
        "Do not import `monaco-editor` bare; that loads every bundled language.",
        "Do not call `monaco.editor.defineTheme` — the editor uses Monaco's built-in `vs`/`vs-dark`, and the theme service is page-global, so redefining it restyles every editor on the page.",
        "Do not re-enable Monaco's EditContext mode via `options={{ editContext: true }}` — the editor keeps its `<textarea>` typing surface on purpose. Host hotkeys (React Flow's space-to-pan, command palettes, hotkey libraries) decide whether to swallow a key by testing for INPUT/SELECT/TEXTAREA/contenteditable, and EditContext's `<div>` fails that test, so they cancel keys typed into the editor."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Editing queries, code, or configuration where syntax highlighting helps"
      ],
      "whenNotToUse": [
        "Single-line input (use TextField)",
        "Rendering short static snippets"
      ]
    },
    {
      "name": "DashboardExample",
      "level": "page",
      "category": "example",
      "classification": "custom",
      "description": "Reference security dashboard page composed entirely from design-system components.",
      "baseLibrary": "custom",
      "importPath": "@/pages/DashboardExample",
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "colors",
        "spacing",
        "severity"
      ],
      "accessibility": [
        "Demonstrates landmark structure via DashboardLayout."
      ],
      "usageExamples": [
        "<DashboardExample />"
      ],
      "antiPatterns": [
        "This is an example; copy patterns rather than importing into production as-is."
      ],
      "llmSafe": false,
      "whenToUse": [
        "Reference for building dashboards"
      ],
      "whenNotToUse": [
        "Directly in production without adapting data"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "DashboardLayout",
      "level": "template",
      "category": "layout",
      "classification": "custom",
      "description": "Application shell with AppBar, collapsible LeftNavigation and a scrollable content region.",
      "baseLibrary": "custom",
      "a2uiEquivalent": "AppShell",
      "importPath": "@/templates/DashboardLayout",
      "requiredProps": [
        "children",
        "navItems"
      ],
      "optionalProps": [
        "selectedNavId",
        "onNavSelect",
        "brand",
        "toolbar",
        "collapsed",
        "onToggleNav",
        "navLinkComponent"
      ],
      "supportedStates": [
        "default",
        "collapsed-nav"
      ],
      "designTokens": [
        "colors",
        "spacing"
      ],
      "accessibility": [
        "Provides banner, navigation and main landmarks."
      ],
      "usageExamples": [
        "<DashboardLayout navItems={items} selectedNavId={id} onNavSelect={set}>...</DashboardLayout>"
      ],
      "rules": [
        "Give navItems an href and pass navLinkComponent (next/link, react-router Link) when the nav moves between routes, so the rail renders links rather than buttons."
      ],
      "antiPatterns": [
        "Do not nest DashboardLayouts."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Top-level app pages requiring the standard shell"
      ],
      "whenNotToUse": [
        "Auth/marketing pages without navigation"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "DatePicker",
      "level": "molecule",
      "category": "input",
      "classification": "themed-mui",
      "description": "Calendar-based date selection. Themed MUI X DatePicker (dayjs).",
      "baseLibrary": "@mui/x-date-pickers",
      "muiEquivalent": "DatePicker",
      "muiDocs": "https://mui.com/x/react-date-pickers/date-picker/",
      "a2uiEquivalent": "DatePicker",
      "importPath": "@/molecules/DatePicker",
      "optionalProps": [
        "value",
        "defaultValue",
        "minDate",
        "maxDate",
        "disabled",
        "onChange"
      ],
      "supportedStates": [
        "default",
        "open",
        "disabled",
        "error"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Keyboard operable; ensure a visible label is set."
      ],
      "usageExamples": [
        "<DatePicker label=\"Detected on\" value={d} onChange={set} />"
      ],
      "antiPatterns": [
        "Do not use for free-text date entry without validation."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Selecting a single calendar date"
      ],
      "whenNotToUse": [
        "Date ranges (use a range picker)",
        "Times only (use TimePicker)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "DateRangePicker",
      "level": "molecule",
      "category": "input",
      "classification": "custom",
      "description": "Start/end date range picker wrapping rsuite DateRangePicker (same control as the app), with canonical presets, a time panel + Now/AM-PM, dayjs-friendly presets and valueType conversion. Presets sit in a left rail; the footer shows an Apply button (left) with the selected range (right). Times default to 12:00:00 AM–11:59:59 PM, so selecting the same day twice covers the full day automatically.",
      "baseLibrary": "custom",
      "muiEquivalent": "rsuite DateRangePicker (wrapped)",
      "muiDocs": "https://rsuitejs.com/components/date-range-picker/",
      "a2uiEquivalent": "DateRangePicker",
      "importPath": "@/molecules/DateRangePicker",
      "optionalProps": [
        "value",
        "defaultValue",
        "onChange",
        "presets",
        "defaultPreset",
        "allowAllTime",
        "futureMode",
        "minDate",
        "maxDate",
        "valueType",
        "format",
        "showTime",
        "showMeridian",
        "variant",
        "disabled",
        "label",
        "placement"
      ],
      "props": [
        {
          "name": "value",
          "type": "{ start, end }",
          "description": "Controlled range in the chosen valueType"
        },
        {
          "name": "valueType",
          "type": "'dayjs' | 'date' | 'string'",
          "default": "'dayjs'"
        },
        {
          "name": "format",
          "type": "string (date-fns tokens)",
          "default": "'MMM d, yyyy'"
        },
        {
          "name": "showTime",
          "type": "boolean",
          "default": "true"
        },
        {
          "name": "showMeridian",
          "type": "boolean",
          "default": "true"
        },
        {
          "name": "variant",
          "type": "'standalone' | 'embedded'",
          "default": "'standalone'"
        }
      ],
      "customVariants": [
        "standalone",
        "embedded"
      ],
      "supportedStates": [
        "default",
        "open",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "radius",
        "spacing"
      ],
      "accessibility": [
        "Trigger exposes an aria-label; calendars are keyboard operable.",
        "Provide a `label` when there is no adjacent visible label."
      ],
      "usageExamples": [
        "<DateRangePicker value={range} onChange={setRange} allowAllTime />",
        "<DateRangePicker variant=\"embedded\" valueType=\"string\" format=\"YYYY-MM-DD\" />"
      ],
      "antiPatterns": [
        "Do not use for a single date (use DatePicker).",
        "Do not mix value types across reads/writes; set one `valueType`."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Filtering by a time window",
        "Report/date-range editors"
      ],
      "whenNotToUse": [
        "A single calendar date (use DatePicker)",
        "Times only (use TimePicker)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "DetailsPageLayout",
      "level": "template",
      "category": "layout",
      "classification": "custom",
      "description": "Entity detail page with header (breadcrumbs/title/actions), content column and side panel.",
      "baseLibrary": "custom",
      "a2uiEquivalent": "DetailPage",
      "importPath": "@/templates/DetailsPageLayout",
      "requiredProps": [
        "title",
        "children"
      ],
      "optionalProps": [
        "breadcrumbs",
        "actions",
        "aside"
      ],
      "supportedStates": [
        "default",
        "with-aside"
      ],
      "designTokens": [
        "spacing",
        "typography"
      ],
      "accessibility": [
        "Breadcrumbs convey location; title is the page heading."
      ],
      "usageExamples": [
        "<DetailsPageLayout title=\"prod-db-01\" aside={<Card/>}>...</DetailsPageLayout>"
      ],
      "antiPatterns": [
        "Do not overload the aside with primary content."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Asset/finding/entity detail screens"
      ],
      "whenNotToUse": [
        "Lists (use TablePageLayout)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Dialog",
      "level": "organism",
      "category": "feedback",
      "classification": "mui-overrides",
      "description": "Modal surface for focused tasks and confirmations. Themed MUI Dialog; the action row is padded and right-aligned by the theme, with `align` / `dense` props on DialogActions for adjustments.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Dialog",
      "muiDocs": "https://mui.com/material-ui/react-dialog/",
      "a2uiEquivalent": "Dialog",
      "importPath": "@/organisms/Dialog",
      "requiredProps": [
        "open"
      ],
      "optionalProps": [
        "onClose",
        "maxWidth",
        "fullWidth",
        "fullScreen",
        "align (DialogActions)",
        "dense (DialogActions)"
      ],
      "supportedStates": [
        "open",
        "closed"
      ],
      "designTokens": [
        "colors",
        "radius",
        "shadows",
        "spacing",
        "typography"
      ],
      "accessibility": [
        "Traps focus; Esc/backdrop close; labelled by DialogTitle."
      ],
      "usageExamples": [
        "<Dialog open={open} onClose={close}><DialogTitle>Isolate host?</DialogTitle><DialogContent><DialogContentText>This cuts network access until you restore it.</DialogContentText></DialogContent></Dialog>",
        "<DialogActions align=\"stretch\" dense><Button>Cancel</Button><Button>OK</Button></DialogActions>"
      ],
      "rules": [
        "Body copy goes in `DialogContentText` (themed as `body1`). Do not drop a raw string into `DialogContent`."
      ],
      "antiPatterns": [
        "Do not stack multiple dialogs.",
        "Do not use for non-blocking info (use Snackbar).",
        "Do not pad DialogActions with sx — the theme owns it; use `align`/`dense`."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "Confirmations, short focused forms"
      ],
      "whenNotToUse": [
        "Large multi-step flows (use a page/Drawer)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Divider",
      "level": "atom",
      "category": "layout",
      "classification": "themed-mui",
      "description": "Thin rule to separate content or groups. Themed MUI Divider.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Divider",
      "muiDocs": "https://mui.com/material-ui/react-divider/",
      "a2uiEquivalent": "Separator",
      "importPath": "@/atoms/Divider",
      "optionalProps": [
        "orientation",
        "flexItem",
        "textAlign",
        "variant",
        "children"
      ],
      "supportedStates": [
        "horizontal",
        "vertical",
        "with-text"
      ],
      "designTokens": [
        "colors"
      ],
      "accessibility": [
        "Decorative dividers are role=\"separator\"; avoid relying on them to convey meaning."
      ],
      "usageExamples": [
        "<Divider />",
        "<Divider orientation=\"vertical\" flexItem />"
      ],
      "antiPatterns": [
        "Do not overuse dividers; prefer spacing for grouping."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Separating list items or sections"
      ],
      "whenNotToUse": [
        "Creating layout columns (use Grid/Stack)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Drawer",
      "level": "organism",
      "category": "navigation",
      "classification": "mui-overrides",
      "description": "Side panel sliding in from an edge for navigation, details or filters. Themed MUI Drawer.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Drawer",
      "muiDocs": "https://mui.com/material-ui/react-drawer/",
      "a2uiEquivalent": "Drawer",
      "importPath": "@/organisms/Drawer",
      "requiredProps": [
        "open"
      ],
      "optionalProps": [
        "anchor",
        "variant",
        "onClose"
      ],
      "supportedStates": [
        "temporary",
        "persistent",
        "permanent"
      ],
      "designTokens": [
        "colors",
        "shadows",
        "typography"
      ],
      "accessibility": [
        "Temporary drawers trap focus; Esc closes; provide a heading."
      ],
      "usageExamples": [
        "<Drawer anchor=\"right\" open={open} onClose={close}><Typography variant=\"h6\">prod-db-01</Typography><Typography variant=\"body1\">Finding detail</Typography></Drawer>"
      ],
      "rules": [
        "Give every drawer a `Typography variant=\"h6\"` heading and `body1` supporting copy. It is a landmark, not a floating box."
      ],
      "antiPatterns": [
        "Do not use a temporary drawer for permanent primary navigation."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "Detail panels, filter panels, mobile navigation"
      ],
      "whenNotToUse": [
        "Short confirmations (use Dialog)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "EmptyState",
      "level": "organism",
      "category": "feedback",
      "classification": "custom",
      "description": "Zero-data / no-results placeholder with illustration, copy and optional action.",
      "baseLibrary": "custom",
      "muiEquivalent": "Box + Typography (composed)",
      "a2uiEquivalent": "EmptyState",
      "importPath": "@/organisms/EmptyState",
      "requiredProps": [
        "title"
      ],
      "optionalProps": [
        "description",
        "icon",
        "action",
        "dense"
      ],
      "supportedStates": [
        "default",
        "dense",
        "with-action"
      ],
      "designTokens": [
        "colors",
        "typography",
        "spacing"
      ],
      "accessibility": [
        "Uses role=\"status\" so the empty message is announced."
      ],
      "usageExamples": [
        "<EmptyState title=\"No findings match these filters\" description=\"Try widening the severity range.\" action={<Button>Clear filters</Button>} />"
      ],
      "rules": [
        "Pass copy through `title` (h6, or subtitle1 when dense) and `description` (body1). Do not nest a second Typography for the same text."
      ],
      "antiPatterns": [
        "Do not use EmptyState to mask loading (use Skeleton)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Empty tables/lists, no search results, first-run"
      ],
      "whenNotToUse": [
        "Loading states",
        "Errors needing remediation (use Alert)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "EntityMenu",
      "level": "molecule",
      "category": "navigation",
      "classification": "custom",
      "description": "The detail and pivot menu opened by an entity Chip. Three fixed regions -- identity, attributes, actions -- with built-in copy and defanged-copy actions that always yield the canonical value.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Menu (composed)",
      "muiDocs": "https://mui.com/material-ui/react-menu/",
      "importPath": "@/molecules/EntityMenu",
      "requiredProps": [
        "entity",
        "anchorEl",
        "open",
        "onClose"
      ],
      "optionalProps": [
        "actions",
        "onAction",
        "disableCopyFeedback"
      ],
      "supportedStates": [
        "closed",
        "open",
        "action-unavailable",
        "copied",
        "copy-failed"
      ],
      "designTokens": [
        "typography",
        "spacing"
      ],
      "accessibility": [
        "The menu list is named with the kind and value, so a user who opened one of several chips in a paragraph knows which.",
        "Escape closes the menu and returns focus to the chip that opened it, which MUI Menu handles.",
        "Unavailable actions stay focusable and state their reason as text, so keyboard and screen-reader users can find out why rather than retrying."
      ],
      "usageExamples": [
        "<EntityMenu entity={{ kind: \"ip\", value: \"203.0.113.47\", verdict: \"malicious\" }} anchorEl={anchor} open={Boolean(anchor)} onClose={close} />",
        "<EntityMenu entity={entity} anchorEl={anchor} open={open} onClose={close} actions={[{ id: \"pivot\", label: \"Pivot to search\" }]} onAction={run} />"
      ],
      "rules": [
        "Open it from a Chip with purpose=\"entity\"; pass the click event's currentTarget as anchorEl.",
        "Pass the canonical full value as `entity.value` and the shortened form as `entity.display`. Copy actions read `value`, so a truncated hash still copies in full.",
        "Copy value and Copy defanged are built in. Defanged copy appears only for network kinds (ip, domain, url, email), where it is meaningful.",
        "Keep `actions` to about three: pivot to search, add to current filter, open entity page.",
        "Put a reason on any action the user cannot run; never pass a bare disabled action.",
        "Give attributes short labels and let the menu lay them out; two to five is the useful range."
      ],
      "antiPatterns": [
        "Do not put destructive or privileged response actions here (isolate host, disable account). This menu opens from a word in a paragraph and is far too easy to trigger by accident; those belong on the entity page behind confirmation and permission checks.",
        "Do not pass a truncated display string as `entity.value`; the copy actions would then yield the wrong thing.",
        "Do not attach a verdict to a value that was never resolved. Omit it, which reads as \"we did not look this up\" rather than as \"unknown\".",
        "Do not reimplement the copy actions at the call site; they exist here so the canonical-value and defanging rules hold everywhere."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "An analyst needs to copy, look up, or pivot on an entity value shown inline",
        "Showing verdict and enrichment attributes for an indicator without leaving the page"
      ],
      "whenNotToUse": [
        "Response actions that change state (use the entity page)",
        "A generic action menu on a row or toolbar (use Menu)",
        "Non-interactive entity values in card copy (use CardDataTag)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "FAB",
      "level": "atom",
      "category": "input",
      "classification": "themed-mui",
      "description": "Floating action button for the primary, most-common screen action. Themed MUI Fab.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Fab",
      "muiDocs": "https://mui.com/material-ui/react-floating-action-button/",
      "a2uiEquivalent": "FloatingActionButton",
      "importPath": "@/atoms/FAB",
      "optionalProps": [
        "color",
        "size",
        "variant",
        "disabled",
        "onClick"
      ],
      "supportedStates": [
        "default",
        "hover",
        "extended",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "shadows"
      ],
      "accessibility": [
        "Always provide aria-label for icon-only FABs."
      ],
      "usageExamples": [
        "<Fab color=\"primary\" aria-label=\"add\"><AddIcon /></Fab>"
      ],
      "antiPatterns": [
        "Do not use more than one FAB per screen."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Single primary action on mobile/dense screens"
      ],
      "whenNotToUse": [
        "Multiple equal actions (use Button)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "FilterMenu",
      "level": "molecule",
      "category": "inputs",
      "classification": "custom",
      "description": "One filter field behind a chip. The chip names the field and its current selection; the panel is a checkbox list with optional search, select-all and grouping. Selections apply live by default, or behind an Apply button.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Popover + Checkbox (composed)",
      "muiDocs": "https://mui.com/material-ui/react-popover/",
      "importPath": "@/molecules/FilterMenu",
      "requiredProps": [
        "field",
        "options",
        "onChange"
      ],
      "optionalProps": [
        "value",
        "defaultValue",
        "commit",
        "searchable",
        "showSelectAll",
        "summary",
        "onRemove",
        "onOpen",
        "onClose",
        "loading",
        "icon",
        "emptyText",
        "size"
      ],
      "supportedStates": [
        "closed",
        "open",
        "empty",
        "one selected",
        "many selected",
        "no matches",
        "loading"
      ],
      "designTokens": [
        "colors",
        "spacing",
        "typography"
      ],
      "accessibility": [
        "The chip exposes aria-haspopup=\"dialog\" and aria-expanded, and its accessible name spells out the whole selection, because the visible text elides the rest behind the +N counter.",
        "The panel is a named dialog, so a user who opened one of several chips in a filter bar knows which one they are in.",
        "Options are real checkboxes rather than menu rows, so selected state is announced natively and the panel can legally hold the search field and the Apply button.",
        "Escape closes the panel and returns focus to the chip; with `searchable`, focus lands in the filter field on open."
      ],
      "usageExamples": [
        "<FilterMenu field=\"Severity\" options={severityOptions} value={severities} onChange={setSeverities} onRemove={() => dropFilter(\"severity\")} />",
        "<FilterMenu field=\"Datasources\" options={sources} value={picked} onChange={setPicked} summary=\"count\" searchable showSelectAll />",
        "<FilterMenu field=\"Severity\" options={severityOptions} value={severities} onChange={setSeverities} commit=\"apply\" />",
        "<FilterMenu field=\"Severity\" options={severityOptions} value={severities} onChange={setSeverities} onClose={(applied) => refetch(applied)} />"
      ],
      "rules": [
        "Reach for this before a bare Chip with purpose=\"picker\" — it owns the chip, the panel and the open state, and a picker chip on its own opens nothing.",
        "Keep `commit=\"immediate\"` (the default) unless each change costs a query the user should not pay for mid-thought, or a partial selection would show a misleading result. Then use `commit=\"apply\"`.",
        "Use `summary=\"count\"` when option labels are too long to sit in a chip, such as datastore index names; the default names the first selection and counts the rest.",
        "Pass `searchable` past about ten options, and `showSelectAll` when picking most of them is a normal thing to do.",
        "Group options with `option.group` when the same name can come from more than one source, so the origin of a value is visible.",
        "When those sources can be enabled independently, pair the menu with a `Chip purpose=\"select\"` per source and pass the filtered `options`. Each chip is a standalone on/off; turning one off removes that source's values from the panel.",
        "Pass `onRemove` to take the whole field out of the bar, which is what the X on the chip means — the same thing it means on `Chip purpose=\"filter\"`. Pair it with an `Add filter` menu, and omit it for a bar of fixed fields, where the panel `Reset` is how a user empties the selection.",
        "Use `onClose` to run one query per visit instead of one per checkbox: keep `commit=\"immediate\"` so the panel and chip stay live, track the selection from `onChange`, and refetch on close. Prefer this over `commit=\"apply\"` when the only problem is query cost rather than a misleading partial result.",
        "Order `options` the way you want them read: the chip names the first selected option, and selections are held in option order rather than click order.",
        "Pass `size` to size the picker trigger only. Panel search, checkboxes and Apply/Reset stay `small`."
      ],
      "antiPatterns": [
        "Do not use this to change a record — it picks which values are filtered on, not the state of the thing underneath. Changing an alert status is a Menu behind a Button.",
        "Do not put a second FilterMenu inside the panel; nested popovers cannot be dismissed predictably.",
        "Do not pass raw option values as labels. The chip shows what you give it, so `sev_1` reaches the user.",
        "Do not use it for a single yes/no option; that is one Chip with purpose=\"select\".",
        "Do not use it for many fields at once — that is FilterPopover, which commits them together."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "A filter bar above a table or list, where each field is one chip",
        "Picking several values from a closed set, such as severities or data sources",
        "Anywhere the applied filter and the control that changes it should be the same element"
      ],
      "whenNotToUse": [
        "Several filter fields committed together (use FilterPopover)",
        "An always-visible filter sidebar (use FilterPanel)",
        "A free-text or open-ended value (use Autocomplete)",
        "Displaying an applied criterion the user can only remove (use Chip purpose=\"filter\")"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "FilterPanel",
      "level": "organism",
      "category": "input",
      "classification": "custom",
      "description": "Container for filter controls with header and apply/clear actions.",
      "baseLibrary": "custom",
      "muiEquivalent": "Box (composed)",
      "a2uiEquivalent": "FilterPanel",
      "importPath": "@/organisms/FilterPanel",
      "requiredProps": [
        "children"
      ],
      "optionalProps": [
        "title",
        "onApply",
        "onClear",
        "activeCount"
      ],
      "supportedStates": [
        "default",
        "has-active-filters"
      ],
      "designTokens": [
        "colors",
        "radius",
        "spacing",
        "typography"
      ],
      "accessibility": [
        "Group related controls with FormField labels."
      ],
      "usageExamples": [
        "<FilterPanel title=\"Filters\" onApply={apply} onClear={clear}><Search/></FilterPanel>"
      ],
      "rules": [
        "The header title is subtitle2 and the active-count is caption. Pass `title` — do not rebuild the header from Typography."
      ],
      "antiPatterns": [
        "Do not place unrelated actions in the footer."
      ],
      "mountsSurface": "inline",
      "llmSafe": true,
      "whenToUse": [
        "Table/list filtering sidebars and popovers"
      ],
      "whenNotToUse": [
        "Single search field (use Search)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "FilterPopover",
      "level": "organism",
      "category": "input",
      "classification": "custom",
      "description": "Popover-triggered filter editor built from a filters config. Supports per-filter search, select-all, single/multi selection, apply/reset, loading, async hooks and localStorage persistence.",
      "baseLibrary": "custom",
      "muiEquivalent": "Popover (composed)",
      "a2uiEquivalent": "FilterPopover",
      "importPath": "@/organisms/FilterPopover",
      "requiredProps": [
        "filters"
      ],
      "optionalProps": [
        "value",
        "defaultValue",
        "onApply",
        "onReset",
        "loading",
        "searchable",
        "showSelectAll",
        "persistKey",
        "triggerLabel",
        "async"
      ],
      "props": [
        {
          "name": "filters",
          "type": "FilterDef[]",
          "required": true,
          "description": "Filter definitions with options"
        },
        {
          "name": "value",
          "type": "Record<string, { operator, values }>",
          "description": "Controlled selection"
        },
        {
          "name": "persistKey",
          "type": "string",
          "description": "localStorage key to persist the applied selection"
        }
      ],
      "supportedStates": [
        "default",
        "open",
        "loading",
        "has-active-filters"
      ],
      "designTokens": [
        "colors",
        "radius",
        "spacing"
      ],
      "accessibility": [
        "Trigger is a real button; options are labelled checkboxes.",
        "Active filter count is surfaced via a Badge on the trigger."
      ],
      "usageExamples": [
        "<FilterPopover filters={defs} onApply={setFilters} searchable showSelectAll />",
        "<FilterPopover filters={defs} value={value} onApply={apply} persistKey=\"alerts\" />"
      ],
      "antiPatterns": [
        "Do not use for an always-visible sidebar (use FilterPanel).",
        "Do not put unrelated actions in the footer."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "Table/list filter triggers",
        "Compact filter menus"
      ],
      "whenNotToUse": [
        "Persistent filter sidebars (use FilterPanel)",
        "A single search field (use Search)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "FormExample",
      "level": "page",
      "category": "example",
      "classification": "custom",
      "description": "Reference create-entity form page (FormPageLayout + FormField).",
      "baseLibrary": "custom",
      "importPath": "@/pages/FormExample",
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "spacing",
        "typography"
      ],
      "usageExamples": [
        "<FormExample />"
      ],
      "llmSafe": false,
      "whenToUse": [
        "Reference for create/edit form pages"
      ],
      "whenNotToUse": [
        "Production without validation/data wiring"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "FormField",
      "level": "molecule",
      "category": "input",
      "classification": "mui-wrapper",
      "description": "Consistent label + control + helper/error wrapper for any form input.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "FormControl (composed)",
      "muiDocs": "https://mui.com/material-ui/api/form-control/",
      "a2uiEquivalent": "FormField",
      "importPath": "@/molecules/FormField",
      "requiredProps": [
        "label",
        "children"
      ],
      "optionalProps": [
        "helperText",
        "error",
        "required",
        "htmlFor",
        "fullWidth"
      ],
      "supportedStates": [
        "default",
        "required",
        "error"
      ],
      "designTokens": [
        "typography",
        "spacing"
      ],
      "accessibility": [
        "Use htmlFor + control id to associate the label with the input."
      ],
      "usageExamples": [
        "<FormField label=\"Email\" htmlFor=\"email\" helperText=\"Shown throughout the console.\"><TextField id=\"email\" /></FormField>"
      ],
      "rules": [
        "The label is the caption variant and helper text is caption. Pass them as props — do not wrap the label in Typography."
      ],
      "antiPatterns": [
        "Do not nest multiple unrelated controls in one FormField."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Any labeled form input",
        "Composite filter/form sections"
      ],
      "whenNotToUse": [
        "Standalone controls without labels"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "FormPageLayout",
      "level": "template",
      "category": "layout",
      "classification": "custom",
      "description": "Centered single-column form page with header and footer action bar.",
      "baseLibrary": "custom",
      "a2uiEquivalent": "FormPage",
      "importPath": "@/templates/FormPageLayout",
      "requiredProps": [
        "title",
        "children"
      ],
      "optionalProps": [
        "description",
        "actions",
        "maxWidth"
      ],
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "spacing",
        "typography"
      ],
      "accessibility": [
        "Title is the page heading; group fields with FormSection/FormField."
      ],
      "usageExamples": [
        "<FormPageLayout title=\"Connect\" actions={<Button>Save</Button>}>...</FormPageLayout>"
      ],
      "antiPatterns": [
        "Do not use for multi-step wizards (add a Stepper)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Create/edit single-entity forms"
      ],
      "whenNotToUse": [
        "Tables/dashboards"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "FormSection",
      "level": "organism",
      "category": "input",
      "classification": "custom",
      "description": "Titled, described grouping of related form fields with consistent spacing.",
      "baseLibrary": "custom",
      "muiEquivalent": "Box + Typography (composed)",
      "a2uiEquivalent": "FormSection / Fieldset",
      "importPath": "@/organisms/FormSection",
      "requiredProps": [
        "title",
        "children"
      ],
      "optionalProps": [
        "description",
        "divider"
      ],
      "supportedStates": [
        "default",
        "with-divider"
      ],
      "designTokens": [
        "typography",
        "spacing"
      ],
      "accessibility": [
        "Renders a section element; title gives the group context."
      ],
      "usageExamples": [
        "<FormSection title=\"Notifications\" description=\"How we reach you.\"><FormField label=\"Email\" htmlFor=\"email\"><TextField id=\"email\" /></FormField></FormSection>"
      ],
      "rules": [
        "The section title is subtitle1 and the description is body1. Pass them as props — do not rebuild the heading from Typography."
      ],
      "antiPatterns": [
        "Do not nest FormSections more than one level."
      ],
      "mountsSurface": "inline",
      "llmSafe": true,
      "whenToUse": [
        "Composite forms (settings, create/edit flows)"
      ],
      "whenNotToUse": [
        "Single isolated field"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Icon",
      "level": "atom",
      "category": "data-display",
      "classification": "mui-wrapper",
      "description": "Wrapper over MUI SvgIcon that applies semantic icon-color tokens via `tone`.",
      "baseLibrary": "@mui/icons-material",
      "muiEquivalent": "SvgIcon",
      "muiDocs": "https://mui.com/material-ui/icons/",
      "a2uiEquivalent": "Icon",
      "importPath": "@/atoms/Icon",
      "requiredProps": [
        "icon"
      ],
      "optionalProps": [
        "tone",
        "fontSize",
        "sx"
      ],
      "props": [
        {
          "name": "icon",
          "type": "ComponentType<SvgIconProps>",
          "required": true,
          "description": "Icon component, e.g. from @mui/icons-material"
        },
        {
          "name": "tone",
          "type": "IconTone",
          "description": "Semantic color token"
        },
        {
          "name": "fontSize",
          "type": "'small' | 'medium' | 'large' | 'inherit'"
        }
      ],
      "supportedStates": [
        "default",
        "toned"
      ],
      "designTokens": [
        "icon-colors"
      ],
      "accessibility": [
        "Icons are decorative by default; provide titleAccess for meaningful icons.",
        "Do not convey state via color alone."
      ],
      "usageExamples": [
        "<Icon icon={WarningIcon} tone=\"warning\" />"
      ],
      "antiPatterns": [
        "Do not hardcode hex colors; use the tone prop."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Status indicators, decorative glyphs, button icons"
      ],
      "whenNotToUse": [
        "User identity (use Avatar)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "IconButton",
      "level": "atom",
      "category": "input",
      "classification": "mui-wrapper",
      "description": "Icon-only action with an enforced accessible label, optional tooltip, semantic variants and a loading state.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "IconButton",
      "muiDocs": "https://mui.com/material-ui/react-button/#icon-button",
      "a2uiEquivalent": "IconButton",
      "importPath": "@/atoms/IconButton",
      "requiredProps": [
        "icon",
        "label"
      ],
      "optionalProps": [
        "variant",
        "tooltip",
        "tooltipPlacement",
        "loading",
        "size",
        "disabled",
        "onClick"
      ],
      "props": [
        {
          "name": "icon",
          "type": "ReactNode",
          "required": true,
          "description": "Icon element to render"
        },
        {
          "name": "label",
          "type": "string",
          "required": true,
          "description": "Accessible name (aria-label)"
        },
        {
          "name": "variant",
          "type": "'default' | 'primary' | 'danger'",
          "default": "'default'"
        },
        {
          "name": "loading",
          "type": "boolean",
          "default": "false"
        }
      ],
      "customVariants": [
        "default",
        "primary",
        "danger"
      ],
      "supportedStates": [
        "default",
        "hover",
        "disabled",
        "loading"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "A `label` is required and rendered as `aria-label`.",
        "Loading sets `aria-busy` and disables interaction.",
        "Disabled buttons remain tooltip-hoverable via a span wrapper."
      ],
      "usageExamples": [
        "<IconButton icon={<DeleteIcon />} label=\"Delete\" variant=\"danger\" onClick={remove} />",
        "<IconButton icon={<RefreshIcon />} label=\"Refresh\" tooltip=\"Refresh data\" loading={busy} />"
      ],
      "antiPatterns": [
        "Do not omit `label` - the button would be unreadable to screen readers.",
        "Do not use for text actions (use Button)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Toolbar actions",
        "Row actions",
        "Compact icon-only controls"
      ],
      "whenNotToUse": [
        "Primary labelled actions (use Button)",
        "Floating primary action (use FAB)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "ImageList",
      "level": "organism",
      "category": "data-display",
      "classification": "themed-mui",
      "description": "Responsive grid of images/thumbnails. Themed MUI ImageList.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "ImageList",
      "muiDocs": "https://mui.com/material-ui/react-image-list/",
      "a2uiEquivalent": "Gallery",
      "importPath": "@/organisms/ImageList",
      "optionalProps": [
        "cols",
        "gap",
        "rowHeight",
        "variant"
      ],
      "supportedStates": [
        "standard",
        "quilted",
        "masonry",
        "woven"
      ],
      "designTokens": [
        "radius",
        "spacing"
      ],
      "accessibility": [
        "Provide alt text for each image."
      ],
      "usageExamples": [
        "<ImageList cols={3}><ImageListItem>...</ImageListItem></ImageList>"
      ],
      "antiPatterns": [
        "Do not use for tabular data (use Table)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Screenshot/evidence galleries, thumbnails"
      ],
      "whenNotToUse": [
        "Text-heavy lists"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "InventoryExample",
      "level": "page",
      "category": "example",
      "classification": "custom",
      "description": "Reference asset inventory page (TablePageLayout + DataTable).",
      "baseLibrary": "custom",
      "importPath": "@/pages/InventoryExample",
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "colors",
        "severity"
      ],
      "usageExamples": [
        "<InventoryExample />"
      ],
      "llmSafe": false,
      "whenToUse": [
        "Reference for inventory screens"
      ],
      "whenNotToUse": [
        "Production without data wiring"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "InvestigationLayout",
      "level": "template",
      "category": "layout",
      "classification": "custom",
      "description": "Split investigation view with a canvas (graph/timeline) and a docked details + legend panel.",
      "baseLibrary": "custom",
      "a2uiEquivalent": "SplitView",
      "importPath": "@/templates/InvestigationLayout",
      "requiredProps": [
        "canvas"
      ],
      "optionalProps": [
        "title",
        "panel",
        "showLegend"
      ],
      "supportedStates": [
        "default",
        "with-panel"
      ],
      "designTokens": [
        "colors",
        "spacing",
        "attackPath"
      ],
      "accessibility": [
        "Provide accessible descriptions for graph content in the panel."
      ],
      "usageExamples": [
        "<InvestigationLayout canvas={<Graph/>} panel={<Details/>} />"
      ],
      "antiPatterns": [
        "Do not hide the legend when showing attack-path colors."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Attack-path / graph investigation screens"
      ],
      "whenNotToUse": [
        "Simple detail pages (use DetailsPageLayout)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "LeftNavigation",
      "level": "organism",
      "category": "navigation",
      "classification": "custom",
      "description": "Primary vertical app navigation with selected state and collapsed icon-only mode.",
      "baseLibrary": "custom",
      "muiEquivalent": "List + ListItemButton (composed)",
      "muiDocs": "https://mui.com/material-ui/react-list/",
      "a2uiEquivalent": "Sidebar / NavRail",
      "importPath": "@/organisms/LeftNavigation",
      "requiredProps": [
        "items"
      ],
      "optionalProps": [
        "selectedId",
        "onSelect",
        "collapsed",
        "header",
        "width",
        "collapsedWidth",
        "linkComponent"
      ],
      "supportedStates": [
        "default",
        "selected",
        "collapsed",
        "hover"
      ],
      "designTokens": [
        "colors",
        "radius",
        "spacing"
      ],
      "accessibility": [
        "Renders a nav landmark with aria-label=\"Primary\".",
        "Collapsed items expose labels via tooltips.",
        "Items with an href render as links and carry aria-current=\"page\" when selected."
      ],
      "usageExamples": [
        "<LeftNavigation items={items} selectedId={id} onSelect={setId} />",
        "<LeftNavigation linkComponent={Link} selectedId={pathname} items={nav.map(({ to, label, icon }) => ({ id: to, label, icon, href: to }))} />"
      ],
      "rules": [
        "Give each item an href when it maps to a route, so the rail renders links and keeps new-tab clicks, the link context menu and route prefetch.",
        "Pass linkComponent for a router-aware app (next/link, react-router Link) to keep navigation client-side; it defaults to a plain anchor.",
        "With href set, treat onSelect as a notification (analytics, closing a mobile drawer) - the link performs the navigation, and onSelect does not fire on modified clicks.",
        "Use per-item target and rel only for destinations outside the app."
      ],
      "antiPatterns": [
        "Do not duplicate global actions already in the AppBar.",
        "Do not navigate from onSelect when the item has an href; that double-navigates a cmd-clicked item.",
        "Do not omit href and route through onSelect alone for real destinations; middle-click and cmd-click cannot work on a button."
      ],
      "llmSafe": true,
      "whenToUse": [
        "App shell primary navigation"
      ],
      "whenNotToUse": [
        "In-page view switching (use Tabs)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Menu",
      "level": "molecule",
      "category": "navigation",
      "classification": "themed-mui",
      "description": "Popover list of actions anchored to a trigger. Themed MUI Menu.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Menu",
      "muiDocs": "https://mui.com/material-ui/react-menu/",
      "a2uiEquivalent": "Menu",
      "importPath": "@/molecules/Menu",
      "requiredProps": [
        "open",
        "anchorEl",
        "onClose"
      ],
      "optionalProps": [
        "anchorOrigin",
        "transformOrigin"
      ],
      "supportedStates": [
        "open",
        "closed",
        "item-selected",
        "disabled-item"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Keyboard: arrows navigate, Esc closes; focus returns to trigger."
      ],
      "usageExamples": [
        "<Menu anchorEl={el} open={open} onClose={close}><MenuItem>...</MenuItem></Menu>"
      ],
      "antiPatterns": [
        "Do not use Menu for primary navigation."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "Row/overflow action lists",
        "Context menus"
      ],
      "whenNotToUse": [
        "Selecting form values (use Select)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Meter",
      "level": "molecule",
      "category": "data-display",
      "classification": "custom",
      "description": "Radial progress ring for a single bounded score or grade. Replaces three divergent gauge implementations.",
      "baseLibrary": "custom",
      "importPath": "@/molecules/Meter",
      "requiredProps": [
        "value",
        "label"
      ],
      "optionalProps": [
        "displayValue",
        "tone",
        "size",
        "strokeWidth"
      ],
      "supportedStates": [
        "default",
        "empty",
        "full"
      ],
      "designTokens": [
        "chart",
        "severity"
      ],
      "accessibility": [
        "Exposes role=\"meter\" with aria-valuenow/min/max and a required aria-label.",
        "The numeric value is always rendered as text inside the ring, never conveyed by arc length alone."
      ],
      "usageExamples": [
        "<Meter value={72} label=\"Detection coverage\" />",
        "<Meter value={31} tone=\"critical\" label=\"Risk score\" displayValue=\"31%\" />"
      ],
      "antiPatterns": [
        "Do not use for unbounded quantities — a meter implies a 0-100 range."
      ],
      "llmSafe": true,
      "whenToUse": [
        "A single bounded score, grade or percentage"
      ],
      "whenNotToUse": [
        "Multiple values or a trend over time"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Modal",
      "level": "organism",
      "category": "feedback",
      "classification": "themed-mui",
      "description": "Low-level overlay primitive for fully custom modal content. Themed MUI Modal.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Modal",
      "muiDocs": "https://mui.com/material-ui/react-modal/",
      "a2uiEquivalent": "Modal",
      "importPath": "@/organisms/Modal",
      "requiredProps": [
        "open",
        "children"
      ],
      "optionalProps": [
        "onClose",
        "keepMounted"
      ],
      "supportedStates": [
        "open",
        "closed"
      ],
      "designTokens": [
        "colors",
        "radius",
        "shadows"
      ],
      "accessibility": [
        "Provide aria-labelledby/aria-describedby on the content."
      ],
      "usageExamples": [
        "<Modal open={open} onClose={close}><Box>...</Box></Modal>"
      ],
      "antiPatterns": [
        "Prefer Dialog for standard confirmations/forms."
      ],
      "mountsSurface": "portal",
      "llmSafe": false,
      "whenToUse": [
        "Custom overlays not covered by Dialog"
      ],
      "whenNotToUse": [
        "Standard confirmations (use Dialog)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Pagination",
      "level": "molecule",
      "category": "navigation",
      "classification": "themed-mui",
      "description": "Page navigation for long lists and tables. Themed MUI Pagination.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Pagination",
      "muiDocs": "https://mui.com/material-ui/react-pagination/",
      "a2uiEquivalent": "Pagination",
      "importPath": "@/molecules/Pagination",
      "requiredProps": [
        "count"
      ],
      "optionalProps": [
        "page",
        "onChange",
        "variant",
        "shape",
        "color",
        "siblingCount"
      ],
      "supportedStates": [
        "default",
        "selected",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Renders nav with aria-labels per page; keyboard operable."
      ],
      "usageExamples": [
        "<Pagination count={10} page={p} onChange={set} />"
      ],
      "antiPatterns": [
        "Do not use for infinite scroll patterns."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Paged tables/lists with known total pages"
      ],
      "whenNotToUse": [
        "Continuous feeds"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Popover",
      "level": "molecule",
      "category": "feedback",
      "classification": "themed-mui",
      "description": "Contextual overlay anchored to an element for rich content. Themed MUI Popover.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Popover",
      "muiDocs": "https://mui.com/material-ui/react-popover/",
      "a2uiEquivalent": "Popover",
      "importPath": "@/molecules/Popover",
      "requiredProps": [
        "open",
        "anchorEl"
      ],
      "optionalProps": [
        "onClose",
        "anchorOrigin",
        "transformOrigin"
      ],
      "supportedStates": [
        "open",
        "closed"
      ],
      "designTokens": [
        "colors",
        "radius",
        "shadows",
        "typography"
      ],
      "accessibility": [
        "Manages focus trapping; Esc closes; returns focus to anchor."
      ],
      "usageExamples": [
        "<Popover open anchorEl={el} onClose={close}><Typography variant=\"subtitle2\">prod-db-01</Typography><Typography variant=\"body1\">Keep this short.</Typography></Popover>"
      ],
      "rules": [
        "Heading is `subtitle2`; supporting copy is `body1`. Do not drop unstyled text into the popover."
      ],
      "antiPatterns": [
        "Do not put long forms in a Popover (use Dialog/Drawer)."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "Rich hover/click detail cards",
        "Color pickers, mini-forms"
      ],
      "whenNotToUse": [
        "Simple text hints (use Tooltip)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Progress",
      "level": "molecule",
      "category": "feedback",
      "classification": "themed-mui",
      "description": "Linear and circular progress indicators. Themed MUI Progress.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "LinearProgress / CircularProgress",
      "muiDocs": "https://mui.com/material-ui/react-progress/",
      "a2uiEquivalent": "ProgressBar / Spinner",
      "importPath": "@/molecules/Progress",
      "optionalProps": [
        "variant",
        "value",
        "color",
        "size"
      ],
      "supportedStates": [
        "indeterminate",
        "determinate"
      ],
      "designTokens": [
        "colors"
      ],
      "accessibility": [
        "Determinate progress exposes aria-valuenow; provide a label for context."
      ],
      "usageExamples": [
        "<LinearProgress variant=\"determinate\" value={64} />"
      ],
      "antiPatterns": [
        "Do not use indeterminate progress when percentage is known."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Loading, uploads, scans in progress"
      ],
      "whenNotToUse": [
        "Skeleton content (use Skeleton)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Radio",
      "level": "atom",
      "category": "input",
      "classification": "themed-mui",
      "description": "Single-select control for mutually-exclusive options. Themed MUI Radio + RadioGroup.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Radio",
      "muiDocs": "https://mui.com/material-ui/react-radio-button/",
      "a2uiEquivalent": "RadioGroup",
      "importPath": "@/atoms/Radio",
      "optionalProps": [
        "checked",
        "value",
        "disabled",
        "size",
        "onChange"
      ],
      "supportedStates": [
        "default",
        "selected",
        "disabled",
        "focus"
      ],
      "designTokens": [
        "colors"
      ],
      "accessibility": [
        "Always group Radios in a RadioGroup with an associated FormLabel.",
        "Keyboard: arrow keys move selection within the group."
      ],
      "usageExamples": [
        "<RadioGroup><FormControlLabel value=\"a\" control={<Radio />} label=\"A\" /></RadioGroup>"
      ],
      "antiPatterns": [
        "Do not use a single Radio in isolation.",
        "Do not use Radio for on/off (use Switch)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Choosing one option from a small set (2-6)"
      ],
      "whenNotToUse": [
        "Many options (use Select)",
        "Multiple selections (use Checkbox)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Rating",
      "level": "atom",
      "category": "input",
      "classification": "themed-mui",
      "description": "Star-based rating input/display. Themed MUI Rating.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Rating",
      "muiDocs": "https://mui.com/material-ui/react-rating/",
      "a2uiEquivalent": "Rating",
      "importPath": "@/atoms/Rating",
      "optionalProps": [
        "value",
        "defaultValue",
        "precision",
        "readOnly",
        "disabled",
        "max"
      ],
      "supportedStates": [
        "default",
        "hover",
        "readOnly",
        "disabled",
        "half"
      ],
      "designTokens": [
        "colors"
      ],
      "accessibility": [
        "Rating is a radio group; provide a name and label for forms."
      ],
      "usageExamples": [
        "<Rating defaultValue={4} precision={0.5} />"
      ],
      "antiPatterns": [
        "Do not use Rating for non-rating numeric input."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Collecting/displaying subjective scores"
      ],
      "whenNotToUse": [
        "Severity (use severity Chip)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Search",
      "level": "molecule",
      "category": "input",
      "classification": "custom",
      "description": "Search field with leading icon, clear button and debounced change events.",
      "baseLibrary": "custom",
      "muiEquivalent": "TextField (composed)",
      "muiDocs": "https://mui.com/material-ui/react-text-field/",
      "a2uiEquivalent": "SearchBox",
      "importPath": "@/molecules/Search",
      "optionalProps": [
        "value",
        "defaultValue",
        "onSearch",
        "onChange",
        "debounceMs",
        "placeholder"
      ],
      "props": [
        {
          "name": "onSearch",
          "type": "(value: string) => void",
          "description": "Debounced search callback"
        },
        {
          "name": "debounceMs",
          "type": "number",
          "default": "300"
        }
      ],
      "customVariants": [],
      "supportedStates": [
        "empty",
        "typing",
        "has-value",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Clear button has an aria-label.",
        "Provide an associated label or aria-label for the field when no visible label."
      ],
      "usageExamples": [
        "<Search onSearch={(q) => fetch(q)} placeholder=\"Search assets...\" />"
      ],
      "rules": [
        "Every search or filter input is the design-system Search - in new UI, and in any UI you revamp or refactor. Never a TextField with a magnifier InputAdornment, an OutlinedInput, or a raw <input>.",
        "When refactoring, replace existing hand-rolled search inputs with Search rather than leaving them in place.",
        "Use `onSearch` for anything expensive (network requests, large filters); it is debounced via `debounceMs` (default 300). Use `onChange` only when you need every keystroke.",
        "Give it a placeholder that names what is being searched (\"Search assets...\", \"Search findings...\"), and an aria-label when there is no visible label."
      ],
      "antiPatterns": [
        "Do not trigger expensive queries on every keystroke; rely on onSearch debounce."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Filtering lists/tables",
        "Global search bars"
      ],
      "whenNotToUse": [
        "Selecting from a fixed known set (use Select/Autocomplete)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Select",
      "level": "atom",
      "category": "input",
      "classification": "themed-mui",
      "description": "Dropdown for choosing one (or many) option(s) from a list. Themed MUI Select.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Select",
      "muiDocs": "https://mui.com/material-ui/react-select/",
      "a2uiEquivalent": "Dropdown",
      "importPath": "@/atoms/Select",
      "optionalProps": [
        "value",
        "multiple",
        "label",
        "disabled",
        "onChange",
        "size"
      ],
      "props": [
        {
          "name": "size",
          "type": "'small' | 'medium'",
          "default": "medium",
          "description": "Outer height from `controlHeights` (small=28, medium=36), matching Button and TextField of the same size."
        }
      ],
      "supportedStates": [
        "default",
        "open",
        "selected",
        "disabled",
        "error"
      ],
      "designTokens": [
        "colors",
        "radius",
        "controlHeights"
      ],
      "accessibility": [
        "Pair with InputLabel via labelId for an accessible name.",
        "Keyboard: Enter/Space opens; arrows navigate; Esc closes."
      ],
      "usageExamples": [
        "<Select label=\"Region\"><MenuItem value=\"a\">A</MenuItem></Select>"
      ],
      "rules": [
        "Pick `size` from context the same way as Button and TextField: default `medium` (36px) beside medium buttons; `small` (28px) in dense toolbars and tables."
      ],
      "antiPatterns": [
        "Do not use Select for fewer than 3 options (use Radio).",
        "Avoid for searchable lists (use Autocomplete)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Choosing from a medium/large set of known options"
      ],
      "whenNotToUse": [
        "Free-text entry",
        "Very small option sets"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "SettingsExample",
      "level": "page",
      "category": "example",
      "classification": "custom",
      "description": "Reference settings page (SettingsLayout + FormSection + FormField).",
      "baseLibrary": "custom",
      "importPath": "@/pages/SettingsExample",
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "spacing",
        "typography"
      ],
      "usageExamples": [
        "<SettingsExample />"
      ],
      "llmSafe": false,
      "whenToUse": [
        "Reference for settings screens"
      ],
      "whenNotToUse": [
        "Production without data wiring"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "SettingsLayout",
      "level": "template",
      "category": "layout",
      "classification": "custom",
      "description": "Two-column settings page with section navigation and content area.",
      "baseLibrary": "custom",
      "a2uiEquivalent": "SettingsPage",
      "importPath": "@/templates/SettingsLayout",
      "requiredProps": [
        "sections",
        "children"
      ],
      "optionalProps": [
        "title",
        "activeId",
        "onSelect"
      ],
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "spacing",
        "radius"
      ],
      "accessibility": [
        "Section list items reflect selection; content updates accordingly."
      ],
      "usageExamples": [
        "<SettingsLayout sections={s} activeId={id} onSelect={set}>...</SettingsLayout>"
      ],
      "antiPatterns": [
        "Do not use for non-settings content."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Settings / preferences screens"
      ],
      "whenNotToUse": [
        "Tables (use TablePageLayout)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Skeleton",
      "level": "atom",
      "category": "feedback",
      "classification": "themed-mui",
      "description": "Loading placeholder that mimics content shape. Themed MUI Skeleton.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Skeleton",
      "muiDocs": "https://mui.com/material-ui/react-skeleton/",
      "a2uiEquivalent": "Skeleton",
      "importPath": "@/atoms/Skeleton",
      "optionalProps": [
        "variant",
        "width",
        "height",
        "animation"
      ],
      "supportedStates": [
        "pulse",
        "wave",
        "loading"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Mark loading regions with aria-busy; announce when content loads."
      ],
      "usageExamples": [
        "<Skeleton variant=\"rectangular\" height={120} />"
      ],
      "antiPatterns": [
        "Do not leave skeletons visible after data has loaded."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Initial data loading states"
      ],
      "whenNotToUse": [
        "Empty states (use EmptyState)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Slider",
      "level": "atom",
      "category": "input",
      "classification": "themed-mui",
      "description": "Selects a numeric value or range along a track. Themed MUI Slider.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Slider",
      "muiDocs": "https://mui.com/material-ui/react-slider/",
      "a2uiEquivalent": "Slider",
      "importPath": "@/atoms/Slider",
      "optionalProps": [
        "value",
        "defaultValue",
        "min",
        "max",
        "step",
        "marks",
        "disabled",
        "valueLabelDisplay"
      ],
      "supportedStates": [
        "default",
        "focus",
        "dragging",
        "disabled",
        "range"
      ],
      "designTokens": [
        "colors"
      ],
      "accessibility": [
        "Provide aria-label or aria-labelledby; keyboard arrows adjust value."
      ],
      "usageExamples": [
        "<Slider defaultValue={50} aria-label=\"Threshold\" />"
      ],
      "antiPatterns": [
        "Do not use sliders for precise numeric entry (use TextField)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Approximate value selection, ranges, thresholds"
      ],
      "whenNotToUse": [
        "Exact values requiring precision"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Snackbar",
      "level": "molecule",
      "category": "feedback",
      "classification": "themed-mui",
      "description": "Transient, auto-dismissing notification (toast). Themed MUI Snackbar.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Snackbar",
      "muiDocs": "https://mui.com/material-ui/react-snackbar/",
      "a2uiEquivalent": "Toast",
      "importPath": "@/molecules/Snackbar",
      "requiredProps": [
        "open"
      ],
      "optionalProps": [
        "autoHideDuration",
        "onClose",
        "anchorOrigin",
        "message",
        "action"
      ],
      "supportedStates": [
        "open",
        "closing",
        "with-action"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Use role=\"alert\" content (e.g. Alert child) for screen reader announcement."
      ],
      "usageExamples": [
        "<Snackbar open autoHideDuration={3000} onClose={close}><Alert>...</Alert></Snackbar>"
      ],
      "antiPatterns": [
        "Do not use for persistent or critical messages (use Alert/Dialog)."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "Brief confirmations, undo prompts"
      ],
      "whenNotToUse": [
        "Errors that need acknowledgement"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Sparkline",
      "level": "atom",
      "category": "data-display",
      "classification": "custom",
      "description": "Dependency-free trend line for dense contexts such as table cells and stat tiles. Consolidates six divergent hand-rolled implementations; normalisation and decoration are explicit props.",
      "baseLibrary": "custom",
      "importPath": "@/atoms/Sparkline",
      "requiredProps": [
        "data",
        "label"
      ],
      "optionalProps": [
        "variant",
        "normalize",
        "tone",
        "showEndPoint",
        "width",
        "height",
        "strokeWidth"
      ],
      "customVariants": [
        "line",
        "area"
      ],
      "supportedStates": [
        "default",
        "flat",
        "empty"
      ],
      "designTokens": [
        "chart",
        "severity"
      ],
      "accessibility": [
        "Renders role=\"img\" with a required aria-label; a sparkline carries information and must not be hidden from assistive technology.",
        "Never the sole carrier of a value — pair with the numeric value in the surrounding cell or tile."
      ],
      "usageExamples": [
        "<Sparkline data={[3, 5, 4, 9]} label=\"Alert volume, last 4 weeks\" />",
        "<Sparkline data={counts} variant=\"area\" tone=\"high\" showEndPoint label=\"High severity trend\" />"
      ],
      "antiPatterns": [
        "Do not use minMax normalisation when absolute magnitude matters — it exaggerates small movements.",
        "Do not use for more than one series; use LineChart from @aistrike-dev/ui/charts."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Compact trend inside a table row, stat tile or list item"
      ],
      "whenNotToUse": [
        "Anything needing axes, a legend or multiple series"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "SpeedDial",
      "level": "organism",
      "category": "input",
      "classification": "themed-mui",
      "description": "Floating button revealing a cluster of related actions. Themed MUI SpeedDial.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "SpeedDial",
      "muiDocs": "https://mui.com/material-ui/react-speed-dial/",
      "a2uiEquivalent": "SpeedDial",
      "importPath": "@/organisms/SpeedDial",
      "requiredProps": [
        "ariaLabel"
      ],
      "optionalProps": [
        "direction",
        "icon",
        "open",
        "onOpen",
        "onClose"
      ],
      "supportedStates": [
        "closed",
        "open"
      ],
      "designTokens": [
        "colors",
        "shadows"
      ],
      "accessibility": [
        "ariaLabel required; each action exposes a tooltip title."
      ],
      "usageExamples": [
        "<SpeedDial ariaLabel=\"actions\" icon={<SpeedDialIcon />}>...</SpeedDial>"
      ],
      "antiPatterns": [
        "Do not use for primary navigation."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "Compact action clusters on dense screens"
      ],
      "whenNotToUse": [
        "Single action (use FAB)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "SplitBar",
      "level": "molecule",
      "category": "data-display",
      "classification": "custom",
      "description": "Proportional bar split into labelled segments — the compact severity or priority breakdown used in tables and cards.",
      "baseLibrary": "custom",
      "importPath": "@/molecules/SplitBar",
      "requiredProps": [
        "segments",
        "label"
      ],
      "optionalProps": [
        "scale",
        "height",
        "hideValues"
      ],
      "supportedStates": [
        "default",
        "empty"
      ],
      "designTokens": [
        "severity",
        "chart"
      ],
      "accessibility": [
        "Renders role=\"img\" with an aria-label enumerating every segment and its count.",
        "Segment counts are rendered as visible text; severity medium and low fall in the contrast relief band on light surfaces and must not rely on colour.",
        "A 2px surface gap separates adjacent fills, providing the secondary encoding colour-vision separation requires."
      ],
      "usageExamples": [
        "<SplitBar label=\"Findings by severity\" segments={[{ key: \"critical\", value: 3 }, { key: \"high\", value: 12 }]} />"
      ],
      "antiPatterns": [
        "Do not set hideValues unless the same counts appear immediately adjacent.",
        "Do not use for more than about six segments — use a stacked BarChart."
      ],
      "llmSafe": true,
      "whenToUse": [
        "A one-line part-to-whole breakdown inside a row or card"
      ],
      "whenNotToUse": [
        "Trends over time, or many categories"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "StatTile",
      "level": "molecule",
      "category": "data-display",
      "classification": "custom",
      "description": "Headline figure with optional delta and trend. Renders the `kpi` chart type without instantiating a chart.",
      "baseLibrary": "custom",
      "importPath": "@/molecules/StatTile",
      "requiredProps": [
        "value",
        "label"
      ],
      "optionalProps": [
        "caption",
        "delta",
        "deltaPolarity",
        "trend"
      ],
      "supportedStates": [
        "default",
        "with-delta",
        "with-trend"
      ],
      "designTokens": [
        "chart",
        "severity",
        "typography"
      ],
      "accessibility": [
        "Delta direction is carried by an arrow glyph and sign, not by colour alone.",
        "The embedded Sparkline receives a derived aria-label naming the metric."
      ],
      "usageExamples": [
        "<StatTile value=\"1,284\" label=\"Open alerts\" delta={-12.4} deltaPolarity=\"lowerIsBetter\" />",
        "<StatTile value=\"98.2%\" label=\"Detection coverage\" trend={[91, 93, 96, 98]} />"
      ],
      "rules": [
        "The label is body1, the figure is h3, the delta is body2, the caption is caption. Pass them as props — do not wrap the value in Typography."
      ],
      "antiPatterns": [
        "Do not use for more than one measure — use a chart."
      ],
      "mountsSurface": "inline",
      "llmSafe": true,
      "whenToUse": [
        "A single headline number on a dashboard or report"
      ],
      "whenNotToUse": [
        "Comparisons across categories or time series detail"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Stepper",
      "level": "organism",
      "category": "navigation",
      "classification": "themed-mui",
      "description": "Shows progress through a sequential multi-step process. Themed MUI Stepper.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Stepper",
      "muiDocs": "https://mui.com/material-ui/react-stepper/",
      "a2uiEquivalent": "Stepper / Wizard",
      "importPath": "@/organisms/Stepper",
      "requiredProps": [
        "activeStep"
      ],
      "optionalProps": [
        "orientation",
        "alternativeLabel",
        "nonLinear"
      ],
      "supportedStates": [
        "active",
        "completed",
        "disabled",
        "error"
      ],
      "designTokens": [
        "colors",
        "typography"
      ],
      "accessibility": [
        "Convey current step; ensure step labels are descriptive."
      ],
      "usageExamples": [
        "<Stepper activeStep={1}><Step><StepLabel>...</StepLabel></Step></Stepper>"
      ],
      "antiPatterns": [
        "Do not use for peer views (use Tabs)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Wizards, onboarding, multi-step forms"
      ],
      "whenNotToUse": [
        "Non-sequential navigation"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Surface",
      "level": "atom",
      "category": "layout",
      "classification": "custom",
      "description": "A background that resolves its own colour from how deeply it is nested, and publishes --ds-* custom properties so descendants stay consistent with it.",
      "baseLibrary": "custom",
      "a2uiEquivalent": "Container",
      "importPath": "@/atoms/Surface",
      "mountsSurface": "inline",
      "optionalProps": [
        "rung",
        "component",
        "sx",
        "children"
      ],
      "supportedStates": [
        "page",
        "primary",
        "widget",
        "surface",
        "raised",
        "overlay"
      ],
      "designTokens": [
        "colors",
        "surfaceScopes"
      ],
      "accessibility": [
        "Purely presentational: renders a div with no role, so it does not affect the accessibility tree.",
        "Quiet text switches from `text.subtle` to `text.tertiary` above `widget`, which is what keeps de-emphasised text at 4.5:1 on higher rungs."
      ],
      "usageExamples": [
        "<Surface>{/* app shell — resolves to page */}</Surface>",
        "<Surface><Surface>{/* a card on the page body — resolves to widget */}</Surface></Surface>",
        "<Surface rung=\"raised\">{/* portalled tooltip, cannot inherit */}</Surface>"
      ],
      "rules": [
        "Let the rung derive from nesting. Wrap content and the correct colour follows.",
        "Set `rung` explicitly only for portalled content — tooltips, menus, dialogs — which is reparented to document.body and cannot inherit CSS variables. Use `usePortalRung()` to compute the value.",
        "Read `var(--ds-field)` for an input background so it recesses one rung below its host, and `var(--ds-text-quiet)` for de-emphasised text so it stays legible as the ladder lightens.",
        "Three levels of nesting is the maximum. If you need a fourth, one of the outer three should be a plain container instead."
      ],
      "antiPatterns": [
        "Do not hardcode a background hex on a container; that is the drift this component exists to remove.",
        "Do not nest more than three deep. A fourth level is painted at its parent rung and logs an error in development, so the nesting has no visual effect.",
        "Do not use `rung` to skip up the ladder for emphasis. Rungs encode containment, not importance."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Any container that needs a background: a card, a panel, a dialog body, a side drawer",
        "Wrapping portalled overlay content so it declares a rung the DOM cannot give it"
      ],
      "whenNotToUse": [
        "A container that needs no background of its own — use Box or Stack, and let the surface show through",
        "Conveying emphasis or severity, which is what Chip, Alert and the severity tokens are for"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Switch",
      "level": "atom",
      "category": "input",
      "classification": "themed-mui",
      "description": "Instant on/off setting control. Themed MUI Switch.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Switch",
      "muiDocs": "https://mui.com/material-ui/react-switch/",
      "a2uiEquivalent": "Switch",
      "importPath": "@/atoms/Switch",
      "optionalProps": [
        "checked",
        "defaultChecked",
        "disabled",
        "size",
        "onChange"
      ],
      "supportedStates": [
        "on",
        "off",
        "disabled",
        "focus"
      ],
      "designTokens": [
        "colors"
      ],
      "accessibility": [
        "Wrap with FormControlLabel for an accessible label.",
        "Represents an immediate state change (no Save needed)."
      ],
      "usageExamples": [
        "<FormControlLabel control={<Switch />} label=\"Notifications\" />"
      ],
      "antiPatterns": [
        "Do not use a Switch when a form submit is required to apply the change (use Checkbox)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Toggling a setting that applies immediately"
      ],
      "whenNotToUse": [
        "Form fields requiring submit",
        "Selecting from options"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Table",
      "level": "organism",
      "category": "data-display",
      "classification": "mui-wrapper",
      "description": "The standard list view. With only `columns` and `data` it renders a bordered card with a recessed uppercase header, comfortable rows and hover feedback. Toolbar, pagination, sorting and column menus are off unless asked for.",
      "baseLibrary": "material-react-table",
      "muiEquivalent": "Table (enhanced)",
      "muiDocs": "https://www.material-react-table.com/",
      "a2uiEquivalent": "DataTable",
      "importPath": "@/organisms/Table",
      "requiredProps": [
        "columns",
        "data"
      ],
      "optionalProps": [
        "onRowClick",
        "selectedRowId",
        "isRowDimmed",
        "loading",
        "empty",
        "sticky",
        "maxHeight",
        "density",
        "toolbar",
        "pagination",
        "sortable",
        "columnActions",
        "surface",
        "bordered"
      ],
      "supportedStates": [
        "default",
        "hover",
        "selected",
        "dimmed",
        "loading",
        "empty",
        "sorted",
        "paginated",
        "sticky"
      ],
      "designTokens": [
        "colors",
        "radius",
        "spacing",
        "typography"
      ],
      "accessibility": [
        "Renders a semantic table; write column headers that describe the data, not the widget.",
        "`onRowClick` wires Enter/Space, tabIndex and aria-selected - do not hand-roll row interaction.",
        "When a row is the only way to reach a record, the row must be clickable, not just an icon inside it."
      ],
      "usageExamples": [
        "<DataTable columns={columns} data={rows} />",
        "<DataTable columns={columns} data={rows} onRowClick={(row) => open(row.id)} selectedRowId={selectedId} />",
        "<DataTable columns={columns} data={rows} loading={isFetching} empty={<EmptyState title=\"No detection rules yet\" description=\"Import a pack or write your first rule.\" />} />",
        "<DataTable columns={columns} data={rows} toolbar pagination sortable sticky maxHeight={420} />",
        "<Card variant=\"data\" title=\"Historical Cases\"><DataTable columns={columns} data={rows} bordered={false} surface=\"transparent\" /></Card>"
      ],
      "rules": [
        "On a surface (page body, panel, dialog) keep the default bordered table so the outline is the structural edge. Header fill (~1.08:1, no underline unless `sticky`) and row rules (`--ds-separator`, ~1.12:1) track the host.",
        "Inside a Card pass `bordered={false} surface=\"transparent\"`. The card opens the next rung and the table spans its width so the header fill is not inset; 24px cell padding keeps columns aligned with the title."
      ],
      "antiPatterns": [
        "Do not restyle through `muiTablePaperProps` / `muiTableHeadCellProps` descendant selectors - the props above cover it, and column-level cell props silently clobber table-level ones.",
        "Do not wrap the table in a Box to get a border or radius - it is bordered by default, and `bordered={false}` turns it off.",
        "Do not leave the table outlined inside a Card - that double-codes the edge the card already provides.",
        "Do not hardcode hex for surfaces or rules - the table inherits its host rung (`--ds-bg`); header and hover are derived from it; outline uses `--ds-border`, row rules use `--ds-separator`.",
        "Do not pass `enableTopToolbar={false} enablePagination={false} enableSorting={false} enableColumnActions={false}` - those are already the defaults.",
        "Do not build a list view out of a CSS grid of Boxes because the table \"felt heavy\" - it is a table, and grids lose semantics, sorting and keyboard support.",
        "Do not use for a handful of static key/value pairs (use a definition list or Card)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Any list of records the user scans, filters or opens"
      ],
      "whenNotToUse": [
        "A handful of static rows with no interaction"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "TableCells",
      "level": "molecule",
      "category": "data-display",
      "classification": "custom",
      "description": "Row anatomy for DataTable: title, status, metric, meta, chips and actions cells. Use these inside a column `Cell` renderer instead of building cells from Box + Typography.",
      "baseLibrary": "custom",
      "importPath": "@/organisms/Table",
      "requiredProps": [],
      "optionalProps": [
        "title",
        "description",
        "icon",
        "meta",
        "badge",
        "label",
        "tone",
        "value",
        "trend",
        "items",
        "max"
      ],
      "supportedStates": [
        "default",
        "truncated",
        "overflowing",
        "muted",
        "hover-revealed"
      ],
      "designTokens": [
        "colors",
        "radius",
        "spacing",
        "typography"
      ],
      "accessibility": [
        "TableStatusCell conveys state by colour AND text - never drop the label.",
        "TableMetricCell requires `trendLabel` whenever `trend` is supplied.",
        "TableActionsCell stays keyboard-reachable when visually hidden until hover."
      ],
      "usageExamples": [
        "{ accessorKey: 'name', header: 'Workflow', size: 280, Cell: ({ row }) => <TableTitleCell icon={<BoltIcon />} title={row.original.name} meta={row.original.version} badge={row.original.builtIn ? 'Built-in' : undefined} description={row.original.summary} /> }",
        "{ accessorKey: 'status', header: 'Status', size: 120, Cell: ({ row }) => <TableStatusCell tone={row.original.enabled ? 'active' : 'neutral'} label={row.original.enabled ? 'Active' : 'Disabled'} /> }",
        "{ accessorKey: 'volume', header: 'Volume 24h', size: 150, Cell: ({ row }) => <TableMetricCell trend={row.original.volume24h} trendLabel=\"Executions, last 24 hours\" value={formatCompact(total)} /> }"
      ],
      "rules": [
        "Each cell applies its own variant: title subtitle2, description body1, meta/badge uses Chip variant=\"subtle\" color=\"primary\", status body1, metric subtitle2, chips use Chip variant=\"subtle\" color=\"neutral\".",
        "TableChipsCell and the TableTitleCell badge both use the design-system Chip atom internally — never hand-roll pill elements in a table cell."
      ],
      "antiPatterns": [
        "Do not rebuild the two-line title cell from Box + Typography - use TableTitleCell.",
        "Do not use a filled Chip for lifecycle state; state is a dot + label (TableStatusCell), chips are for facets.",
        "Do not hardcode hex for row text - `textColors.subtle` and `textColors.muted` exist for the secondary lines.",
        "Do not build tag chips from Typography + inline sx — use the Chip atom with the appropriate variant and color."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Any column in a DataTable that shows more than a bare string"
      ],
      "whenNotToUse": [
        "Outside a table - these are tuned to row rhythm, not general layout"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "TableExample",
      "level": "page",
      "category": "example",
      "classification": "custom",
      "description": "Reference inventory list page with search-driven filtering and an empty state.",
      "baseLibrary": "custom",
      "importPath": "@/pages/TableExample",
      "supportedStates": [
        "default",
        "empty"
      ],
      "designTokens": [
        "colors",
        "spacing",
        "severity"
      ],
      "accessibility": [
        "Demonstrates TablePageLayout heading + table semantics."
      ],
      "usageExamples": [
        "<TableExample />"
      ],
      "antiPatterns": [
        "Example only; adapt data wiring for production."
      ],
      "llmSafe": false,
      "whenToUse": [
        "Reference for building list/table pages"
      ],
      "whenNotToUse": [
        "Directly in production without adapting data"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "TablePageLayout",
      "level": "template",
      "category": "layout",
      "classification": "custom",
      "description": "List/table page with header (title, actions), toolbar (search/filters) and table content.",
      "baseLibrary": "custom",
      "a2uiEquivalent": "ListPage",
      "importPath": "@/templates/TablePageLayout",
      "requiredProps": [
        "title",
        "children"
      ],
      "optionalProps": [
        "description",
        "actions",
        "toolbar"
      ],
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "typography",
        "spacing"
      ],
      "accessibility": [
        "Title is the page heading; place the table in the content region."
      ],
      "usageExamples": [
        "<TablePageLayout title=\"Inventory\" toolbar={<Search/>}><DataTable .../></TablePageLayout>"
      ],
      "antiPatterns": [
        "Do not embed unrelated dashboards here (use DashboardLayout content)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Any list/table screen"
      ],
      "whenNotToUse": [
        "Dashboards (use DashboardLayout / DashboardExample)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Tabs",
      "level": "molecule",
      "category": "navigation",
      "classification": "themed-mui",
      "description": "Switch between related views within the same context. Themed MUI Tabs/Tab.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Tabs",
      "muiDocs": "https://mui.com/material-ui/react-tabs/",
      "a2uiEquivalent": "Tabs",
      "importPath": "@/molecules/Tabs",
      "requiredProps": [
        "value",
        "onChange"
      ],
      "optionalProps": [
        "variant",
        "orientation",
        "scrollButtons"
      ],
      "supportedStates": [
        "default",
        "selected",
        "disabled",
        "scrollable"
      ],
      "designTokens": [
        "colors",
        "typography"
      ],
      "accessibility": [
        "Provide aria-label on Tabs; use tabpanel roles for content if needed.",
        "Keyboard: arrows move focus, Enter/Space selects."
      ],
      "usageExamples": [
        "<Tabs value={tab} onChange={(_, next) => setTab(next)} aria-label=\"asset detail tabs\"><Tab label=\"Overview\" /></Tabs>"
      ],
      "rules": [
        "The strip ships its own type. Panel content you render underneath is `Typography variant=\"body1\"` (or a layout that already applies a variant)."
      ],
      "antiPatterns": [
        "Do not use tabs for sequential steps (use Stepper)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Peer views within one screen"
      ],
      "whenNotToUse": [
        "Primary navigation (use LeftNavigation)",
        "Wizards (use Stepper)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "TextField",
      "level": "atom",
      "category": "input",
      "classification": "mui-overrides",
      "description": "Single/multi-line text input with label, helper text and error states.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "TextField",
      "muiDocs": "https://mui.com/material-ui/react-text-field/",
      "a2uiEquivalent": "TextInput",
      "importPath": "@/atoms/TextField",
      "optionalProps": [
        "label",
        "placeholder",
        "helperText",
        "error",
        "required",
        "disabled",
        "multiline",
        "value",
        "onChange",
        "size"
      ],
      "props": [
        {
          "name": "size",
          "type": "'small' | 'medium'",
          "default": "medium",
          "description": "Outer height from `controlHeights`: small=28 (Button small), medium=36 (Button medium). Same `size` name as a sibling Button so an inline row matches."
        }
      ],
      "supportedStates": [
        "default",
        "focus",
        "filled",
        "error",
        "disabled",
        "required"
      ],
      "designTokens": [
        "colors",
        "radius",
        "typography",
        "controlHeights"
      ],
      "accessibility": [
        "label is associated automatically; avoid placeholder-only labeling.",
        "Use helperText + error to convey validation; consider aria-describedby."
      ],
      "usageExamples": [
        "<TextField label=\"Email\" helperText=\"Required\" />"
      ],
      "rules": [
        "The fill recesses one rung below whatever surface hosts the field, which is what makes it read as inset rather than as a box laid on top. It resolves from the surface scope, so the same field is correct in a page form, a card and a dialog without being told where it is.",
        "On `page` there is no rung below to recess into, so the field is transparent and a heavier outline carries the affordance instead. That is the intended appearance on that one rung, not a missing background.",
        "Pick `size` from context the same way as Button: default `medium` (36px) in forms and toolbars beside medium buttons; `small` (28px) only in dense toolbars and tables beside small buttons. There is no `large` — MUI TextField does not expose one."
      ],
      "antiPatterns": [
        "Do not rely on placeholder as the only label.",
        "Do not set a background on a field. It defeats the recession and pins the field to one context, so it will be wrong the first time it is used on another surface."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Collecting free-form text",
        "Numbers, emails, search terms"
      ],
      "whenNotToUse": [
        "Choosing from known options (use Select/Autocomplete)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "ThreatsExample",
      "level": "page",
      "category": "example",
      "classification": "custom",
      "description": "Reference threats page listing active attack paths by severity.",
      "baseLibrary": "custom",
      "importPath": "@/pages/ThreatsExample",
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "colors",
        "severity",
        "attackPath"
      ],
      "usageExamples": [
        "<ThreatsExample />"
      ],
      "llmSafe": false,
      "whenToUse": [
        "Reference for threat/attack-path lists"
      ],
      "whenNotToUse": [
        "Production without data wiring"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "ThreatsListExample",
      "level": "page",
      "category": "example",
      "classification": "custom",
      "description": "Reference threats list page: TablePageLayout + search/filter toolbar + DataTable with severity/status chips, row-action Menu, and an empty state.",
      "baseLibrary": "custom",
      "importPath": "@/pages/ThreatsListExample",
      "supportedStates": [
        "default",
        "filtered",
        "empty"
      ],
      "designTokens": [
        "colors",
        "spacing",
        "severity",
        "attackPath"
      ],
      "accessibility": [
        "TablePageLayout heading + table semantics.",
        "Row actions use IconButton with an accessible label."
      ],
      "usageExamples": [
        "<ThreatsListExample />"
      ],
      "antiPatterns": [
        "Example only; adapt data wiring for production."
      ],
      "llmSafe": false,
      "whenToUse": [
        "Reference for threat/attack-path list screens with filtering"
      ],
      "whenNotToUse": [
        "Production without data wiring"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "TimePicker",
      "level": "molecule",
      "category": "input",
      "classification": "themed-mui",
      "description": "Clock-based time selection. Themed MUI X TimePicker (dayjs).",
      "baseLibrary": "@mui/x-date-pickers",
      "muiEquivalent": "TimePicker",
      "muiDocs": "https://mui.com/x/react-date-pickers/time-picker/",
      "a2uiEquivalent": "TimePicker",
      "importPath": "@/molecules/TimePicker",
      "optionalProps": [
        "value",
        "defaultValue",
        "disabled",
        "ampm",
        "onChange"
      ],
      "supportedStates": [
        "default",
        "open",
        "disabled"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Provide a visible label; keyboard operable."
      ],
      "usageExamples": [
        "<TimePicker label=\"Start\" value={t} onChange={set} />"
      ],
      "antiPatterns": [
        "Do not use for durations (use a duration input)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Selecting a time of day"
      ],
      "whenNotToUse": [
        "Dates (use DatePicker)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "ToggleButton",
      "level": "atom",
      "category": "input",
      "classification": "themed-mui",
      "description": "Selectable button for segmented / exclusive controls. Themed MUI ToggleButton(Group).",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "ToggleButton",
      "muiDocs": "https://mui.com/material-ui/react-toggle-button/",
      "a2uiEquivalent": "SegmentedControl",
      "importPath": "@/atoms/ToggleButton",
      "optionalProps": [
        "value",
        "selected",
        "disabled",
        "size",
        "exclusive (group)"
      ],
      "supportedStates": [
        "default",
        "selected",
        "disabled",
        "focus"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Group with ToggleButtonGroup and provide aria-label; icon-only buttons need aria-label."
      ],
      "usageExamples": [
        "<ToggleButtonGroup exclusive><ToggleButton value=\"a\">A</ToggleButton></ToggleButtonGroup>"
      ],
      "antiPatterns": [
        "Do not use a single ToggleButton where a Switch is clearer."
      ],
      "llmSafe": true,
      "whenToUse": [
        "View switches, formatting toolbars, exclusive options"
      ],
      "whenNotToUse": [
        "Submitting actions (use Button)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Tooltip",
      "level": "atom",
      "category": "feedback",
      "classification": "mui-overrides",
      "description": "Contextual hint on hover/focus. Themed MUI Tooltip with arrow by default.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Tooltip",
      "muiDocs": "https://mui.com/material-ui/react-tooltip/",
      "a2uiEquivalent": "Tooltip",
      "importPath": "@/atoms/Tooltip",
      "requiredProps": [
        "title",
        "children"
      ],
      "optionalProps": [
        "placement",
        "arrow",
        "enterDelay"
      ],
      "supportedStates": [
        "hidden",
        "visible"
      ],
      "designTokens": [
        "colors",
        "radius"
      ],
      "accessibility": [
        "Child must be focusable so keyboard users can trigger the tooltip.",
        "Do not put essential information only in a tooltip."
      ],
      "usageExamples": [
        "<Tooltip title=\"Refresh\"><IconButton>...</IconButton></Tooltip>"
      ],
      "antiPatterns": [
        "Do not wrap disabled elements directly (wrap in a span)."
      ],
      "mountsSurface": "portal",
      "llmSafe": true,
      "whenToUse": [
        "Explaining icon buttons or truncated text"
      ],
      "whenNotToUse": [
        "Critical content that must always be visible"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "TransferList",
      "level": "organism",
      "category": "input",
      "classification": "custom",
      "description": "Two-list picker for moving items between available and selected sets.",
      "baseLibrary": "custom",
      "muiEquivalent": "List + Checkbox (composed)",
      "muiDocs": "https://mui.com/material-ui/react-transfer-list/",
      "a2uiEquivalent": "DualListBox",
      "importPath": "@/organisms/TransferList",
      "requiredProps": [
        "items"
      ],
      "optionalProps": [
        "defaultSelected",
        "onChange",
        "leftTitle",
        "rightTitle"
      ],
      "supportedStates": [
        "default",
        "checked",
        "empty"
      ],
      "designTokens": [
        "colors",
        "spacing"
      ],
      "accessibility": [
        "Items are toggle buttons with checkboxes; move buttons disable when nothing is checked."
      ],
      "usageExamples": [
        "<TransferList items={items} defaultSelected={[\"IAM\"]} onChange={set} />"
      ],
      "antiPatterns": [
        "Do not use for very large lists (use Autocomplete multiple)."
      ],
      "llmSafe": true,
      "whenToUse": [
        "Assigning items to groups/roles/scopes"
      ],
      "whenNotToUse": [
        "Single or few selections (use Checkbox/Autocomplete)"
      ],
      "packageImport": "@aistrike-dev/ui"
    },
    {
      "name": "Typography",
      "level": "atom",
      "category": "data-display",
      "classification": "themed-mui",
      "description": "Text primitive with the design system type scale and system-native font stack. Themed MUI Typography with no external font loading required.",
      "baseLibrary": "@mui/material",
      "muiEquivalent": "Typography",
      "muiDocs": "https://mui.com/material-ui/react-typography/",
      "a2uiEquivalent": "Text",
      "importPath": "@/atoms/Typography",
      "optionalProps": [
        "variant",
        "color",
        "align",
        "noWrap",
        "gutterBottom",
        "component"
      ],
      "supportedStates": [
        "default"
      ],
      "designTokens": [
        "typography",
        "colors"
      ],
      "accessibility": [
        "Use semantic variants and the `component` prop to keep heading order correct.",
        "Avoid skipping heading levels for styling reasons."
      ],
      "usageExamples": [
        "<Typography variant=\"h4\">Security overview</Typography>",
        "<Typography variant=\"body1\" color=\"text.secondary\">Supporting copy</Typography>",
        "<Typography variant=\"caption\">Last seen 10h ago</Typography>"
      ],
      "rules": [
        "Always set `variant` explicitly — do not rely on the body1 default.",
        "Pick the variant for the role: h4 page title, h6 drawer/empty-state heading, subtitle1 card or accordion title, subtitle2 compact heading, body1 for default reading text (14px), body2 only for dense annotations (12px), caption metadata, overline labels.",
        "A container that may hold non-text children applies the variant via `sx={{ typography: \"body1\" }}` on a Box, so it does not wrap a table in a `<p>`."
      ],
      "antiPatterns": [
        "Do not use h1-h6 purely for size; pick the right semantic level.",
        "Do not style a Box or span with a font-size literal when a Typography variant exists.",
        "Do not omit `variant` and then override fontSize in sx."
      ],
      "llmSafe": true,
      "whenToUse": [
        "All text content that is not already owned by another component slot"
      ],
      "whenNotToUse": [
        "Interactive labels handled by their own components (Button, Chip, FormField, EmptyState title/description, DialogTitle, AlertTitle)"
      ],
      "packageImport": "@aistrike-dev/ui"
    }
  ]
}
