markup for a named icon. * * The inner paths/rects/circles are copied exactly from the mockup HTML so the * glyphs match 1:1. Stroke width follows the mockup for that glyph (most are * 1.7/1.8; the step check is 2.4, the arrow is 2). An unknown name returns an * empty string so a typo degrades to no icon rather than a fatal. * * @param string $name Icon key (see the switch below for the full set). * @return string Inline SVG markup, or '' for an unknown name. */ function aafm_icon( string $name ): string { $icons = array( // Tab icons (dashboard-a.html lines 44-49). 'dashboard' => array( 'svg' => '' ), 'connection' => array( 'svg' => '' ), 'abilities' => array( 'svg' => '' ), 'playbooks' => array( 'svg' => '' ), 'integrations' => array( 'svg' => '' ), // Deliberately NOT the plug: 'integrations' owns that glyph, and this tab used to carry a // byte-identical copy of it, so the two nav items were indistinguishable. An arrow landing // in a tray reads as abilities arriving from somewhere else, which is what this tab is. 'bridge' => array( 'svg' => '' ), 'settings' => array( 'svg' => '' ), 'activity' => array( 'svg' => '' ), 'help' => array( 'svg' => '' ), // Right-aligned nav links (review + support). 'star' => array( 'svg' => '' ), 'lifebuoy' => array( 'svg' => '' ), 'play' => array( 'svg' => '' ), // Dashboard card / inline icons. 'endpoint' => array( 'svg' => '' ), 'clock' => array( 'svg' => '' ), 'copy' => array( 'svg' => '' ), // Stat icons (dashboard-a.html lines 107-134). 'bolt' => array( 'svg' => '' ), 'recent' => array( 'svg' => '' ), 'audit' => array( 'svg' => '' ), // Registered apps: a grid of equal tiles, so it never reads as the settings shield. 'groups' => array( 'svg' => '' ), // Card-head icons, one per card so no two cards on a tab share a glyph. 'pause' => array( 'svg' => '' ), 'bell' => array( 'svg' => '' ), 'key' => array( 'svg' => '' ), 'terminal' => array( 'svg' => '' ), 'file' => array( 'svg' => '' ), 'tag' => array( 'svg' => '' ), // Client icons (connection.html lines 91-98). 'client-chatgpt' => array( 'svg' => '' ), 'client-claude' => array( 'svg' => '' ), 'client-claude-code' => array( 'svg' => '' ), 'client-cursor' => array( 'svg' => '' ), 'client-vscode' => array( 'svg' => '' ), 'client-windsurf' => array( 'svg' => '' ), 'client-gemini-cli' => array( 'svg' => '' ), 'client-manus' => array( 'svg' => '' ), 'client-generic' => array( 'svg' => '' ), // Settings card-head / notice icons (settings.html lines 42-66). 'shield' => array( 'svg' => '' ), // Per-glyph stroke width: most icons use the 1.7 default; info/warning/success/error need // a slightly heavier 1.8 stroke to balance their smaller glyphs against the rest of the set. 'warning' => array( 'svg' => '', 'width' => '1.8', ), 'info' => array( 'svg' => '', 'width' => '1.8', ), 'success' => array( 'svg' => '', 'width' => '1.8', ), 'error' => array( 'svg' => '', 'width' => '1.8', ), // Locked high-risk ability row (Abilities tab), drawn in the shackle-closed // style the settings shield already uses for its stroke weight. 'lock' => array( 'svg' => '' ), // The step-done check (dashboard-a.html line 61) and the navigation arrow (line 85) are // stroke-weight variants that need a heavier stroke than the 1.7 default. 'check' => array( 'svg' => '', 'width' => '2.4', ), 'arrow-right' => array( 'svg' => '', 'width' => '2', ), ); $icon = $icons[ $name ] ?? null; if ( null === $icon ) { return ''; } return sprintf( '', $icon['width'] ?? '1.7', $icon['svg'] ); }