feat: add comprehensive analysis report for Hook templates compliance with official standards

- Introduced a detailed report outlining compliance issues and recommendations for the `ccw/frontend` implementation of Hook templates.
- Identified critical issues regarding command structure and input reading methods.
- Highlighted errors related to cross-platform compatibility of Bash scripts on Windows.
- Documented warnings regarding matcher formats and exit code usage.
- Provided a summary of supported trigger types and outlined missing triggers.
- Included a section on completed fixes and references to affected files for easier tracking.
This commit is contained in:
catlog22
2026-03-01 15:12:44 +08:00
parent 8799a9c2fd
commit 9ad755e225
6 changed files with 819 additions and 59 deletions

View File

@@ -63,6 +63,8 @@ export interface HookQuickTemplatesProps {
}
// ========== Hook Templates ==========
// NOTE: Hook input is received via stdin (not environment variable)
// Use: const fs=require('fs');const p=JSON.parse(fs.readFileSync(0,'utf8')||'{}');
/**
* Predefined hook templates for quick installation
@@ -90,7 +92,7 @@ export const HOOK_TEMPLATES: readonly HookTemplate[] = [
command: 'node',
args: [
'-e',
'const p=JSON.parse(process.env.HOOK_INPUT||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(/workflow-session\\.json$|session-metadata\\.json$/.test(file)){const fs=require("fs");try{const content=fs.readFileSync(file,"utf8");const data=JSON.parse(content);const cp=require("child_process");const payload=JSON.stringify({type:"SESSION_STATE_CHANGED",file:file,sessionId:data.session_id||"",status:data.status||"unknown",project:process.env.CLAUDE_PROJECT_DIR||process.cwd(),timestamp:Date.now()});cp.spawnSync("curl",["-s","-X","POST","-H","Content-Type: application/json","-d",payload,"http://localhost:3456/api/hook"],{stdio:"inherit",shell:true})}catch(e){}}'
'const fs=require("fs");const p=JSON.parse(fs.readFileSync(0,"utf8")||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(/workflow-session\\.json$|session-metadata\\.json$/.test(file)){try{const content=fs.readFileSync(file,"utf8");const data=JSON.parse(content);const cp=require("child_process");const payload=JSON.stringify({type:"SESSION_STATE_CHANGED",file:file,sessionId:data.session_id||"",status:data.status||"unknown",project:process.env.CLAUDE_PROJECT_DIR||process.cwd(),timestamp:Date.now()});cp.spawnSync("curl",["-s","-X","POST","-H","Content-Type: application/json","-d",payload,"http://localhost:3456/api/hook"],{stdio:"inherit",shell:true})}catch(e){}}'
]
},
// --- Notification ---
@@ -117,7 +119,7 @@ export const HOOK_TEMPLATES: readonly HookTemplate[] = [
command: 'node',
args: [
'-e',
'const p=JSON.parse(process.env.HOOK_INPUT||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(file){const cp=require("child_process");cp.spawnSync("npx",["prettier","--write",file],{stdio:"inherit",shell:true})}'
'const fs=require("fs");const p=JSON.parse(fs.readFileSync(0,"utf8")||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(file){const cp=require("child_process");cp.spawnSync("npx",["prettier","--write",file],{stdio:"inherit",shell:true})}'
]
},
{
@@ -130,7 +132,7 @@ export const HOOK_TEMPLATES: readonly HookTemplate[] = [
command: 'node',
args: [
'-e',
'const p=JSON.parse(process.env.HOOK_INPUT||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(file){const cp=require("child_process");cp.spawnSync("npx",["eslint","--fix",file],{stdio:"inherit",shell:true})}'
'const fs=require("fs");const p=JSON.parse(fs.readFileSync(0,"utf8")||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(file){const cp=require("child_process");cp.spawnSync("npx",["eslint","--fix",file],{stdio:"inherit",shell:true})}'
]
},
{
@@ -143,7 +145,7 @@ export const HOOK_TEMPLATES: readonly HookTemplate[] = [
command: 'node',
args: [
'-e',
'const p=JSON.parse(process.env.HOOK_INPUT||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(/\\.env|secret|credential|\\.key$/.test(file)){process.stderr.write("Blocked: modifying sensitive file "+file);process.exit(2)}'
'const fs=require("fs");const p=JSON.parse(fs.readFileSync(0,"utf8")||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(/\\.env|secret|credential|\\.key$/.test(file)){process.stderr.write("Blocked: modifying sensitive file "+file);process.exit(2)}'
]
},
{
@@ -169,7 +171,7 @@ export const HOOK_TEMPLATES: readonly HookTemplate[] = [
command: 'node',
args: [
'-e',
'const p=JSON.parse(process.env.HOOK_INPUT||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(file){const cp=require("child_process");const payload=JSON.stringify({type:"FILE_MODIFIED",file:file,project:process.env.CLAUDE_PROJECT_DIR||process.cwd(),timestamp:Date.now()});cp.spawnSync("curl",["-s","-X","POST","-H","Content-Type: application/json","-d",payload,"http://localhost:3456/api/hook"],{stdio:"inherit",shell:true})}'
'const fs=require("fs");const p=JSON.parse(fs.readFileSync(0,"utf8")||"{}");const file=(p.tool_input&&p.tool_input.file_path)||"";if(file){const cp=require("child_process");const payload=JSON.stringify({type:"FILE_MODIFIED",file:file,project:process.env.CLAUDE_PROJECT_DIR||process.cwd(),timestamp:Date.now()});cp.spawnSync("curl",["-s","-X","POST","-H","Content-Type: application/json","-d",payload,"http://localhost:3456/api/hook"],{stdio:"inherit",shell:true})}'
]
},
{
@@ -181,7 +183,7 @@ export const HOOK_TEMPLATES: readonly HookTemplate[] = [
command: 'node',
args: [
'-e',
'const p=JSON.parse(process.env.HOOK_INPUT||"{}");const cp=require("child_process");const payload=JSON.stringify({type:"SESSION_SUMMARY",transcript:p.transcript_path||"",project:process.env.CLAUDE_PROJECT_DIR||process.cwd(),timestamp:Date.now()});cp.spawnSync("curl",["-s","-X","POST","-H","Content-Type: application/json","-d",payload,"http://localhost:3456/api/hook"],{stdio:"inherit",shell:true})'
'const fs=require("fs");const p=JSON.parse(fs.readFileSync(0,"utf8")||"{}");const cp=require("child_process");const payload=JSON.stringify({type:"SESSION_SUMMARY",transcript:p.transcript_path||"",project:process.env.CLAUDE_PROJECT_DIR||process.cwd(),timestamp:Date.now()});cp.spawnSync("curl",["-s","-X","POST","-H","Content-Type: application/json","-d",payload,"http://localhost:3456/api/hook"],{stdio:"inherit",shell:true})'
]
},
{
@@ -221,7 +223,7 @@ export const HOOK_TEMPLATES: readonly HookTemplate[] = [
description: 'Sync memory V2 status to dashboard on changes',
category: 'notification',
trigger: 'PostToolUse',
matcher: 'core_memory',
matcher: 'mcp__ccw-tools__core_memory',
command: 'node',
args: [
'-e',

View File

@@ -84,6 +84,9 @@ interface HookTemplate {
timeout?: number;
}
// NOTE: Hook input is received via stdin (not environment variable)
// Node.js: const fs=require('fs');const p=JSON.parse(fs.readFileSync(0,'utf8')||'{}');
// Bash: INPUT=$(cat)
const HOOK_TEMPLATES: Record<string, HookTemplate> = {
'memory-update-queue': {
event: 'Stop',
@@ -95,13 +98,13 @@ const HOOK_TEMPLATES: Record<string, HookTemplate> = {
event: 'UserPromptSubmit',
matcher: '',
command: 'node',
args: ['-e', "const p=JSON.parse(process.env.HOOK_INPUT||'{}');require('child_process').spawnSync('ccw',['tool','exec','skill_context_loader',JSON.stringify({prompt:p.user_prompt||''})],{stdio:'inherit'})"],
args: ['-e', "const fs=require('fs');const p=JSON.parse(fs.readFileSync(0,'utf8')||'{}');require('child_process').spawnSync('ccw',['tool','exec','skill_context_loader',JSON.stringify({prompt:p.prompt||''})],{stdio:'inherit'})"],
},
'skill-context-auto': {
event: 'UserPromptSubmit',
matcher: '',
command: 'node',
args: ['-e', "const p=JSON.parse(process.env.HOOK_INPUT||'{}');require('child_process').spawnSync('ccw',['tool','exec','skill_context_loader',JSON.stringify({mode:'auto',prompt:p.user_prompt||''})],{stdio:'inherit'})"],
args: ['-e', "const fs=require('fs');const p=JSON.parse(fs.readFileSync(0,'utf8')||'{}');require('child_process').spawnSync('ccw',['tool','exec','skill_context_loader',JSON.stringify({mode:'auto',prompt:p.prompt||''})],{stdio:'inherit'})"],
},
'danger-bash-confirm': {
event: 'PreToolUse',
@@ -114,7 +117,7 @@ const HOOK_TEMPLATES: Record<string, HookTemplate> = {
event: 'PreToolUse',
matcher: 'Write|Edit',
command: 'bash',
args: ['-c', 'INPUT=$(cat); FILE=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); PROTECTED=".env|.git/|package-lock.json|yarn.lock|.credentials|secrets|id_rsa|.pem$|.key$"; if echo "$FILE" | grep -qiE "$PROTECTED"; then echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"PreToolUse\\",\\"permissionDecision\\":\\"deny\\",\\"permissionDecisionReason\\":\\"Protected file cannot be modified: $FILE\\"}}" && exit 0; fi; exit 0'],
args: ['-c', 'INPUT=$(cat); FILE=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); PROTECTED=".env|.git/|package-lock.json|yarn.lock|.credentials|secrets|id_rsa|.pem$|.key$"; if echo "$FILE" | grep -qiE "$PROTECTED"; then echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"PreToolUse\\",\\"permissionDecision\\":\\"deny\\",\\"permissionDecisionReason\\":\\"Protected file cannot be modified: $FILE\\"}}" >&2 && exit 2; fi; exit 0'],
timeout: 5000,
},
'danger-git-destructive': {
@@ -135,7 +138,7 @@ const HOOK_TEMPLATES: Record<string, HookTemplate> = {
event: 'PreToolUse',
matcher: 'Write|Edit|Bash',
command: 'bash',
args: ['-c', 'INPUT=$(cat); TOOL=$(echo "$INPUT" | jq -r ".tool_name // empty"); if [ "$TOOL" = "Bash" ]; then CMD=$(echo "$INPUT" | jq -r ".tool_input.command // empty"); SYS_PATHS="/etc/|/usr/|/bin/|/sbin/|/boot/|/sys/|/proc/|C:\\\\Windows|C:\\\\Program Files"; if echo "$CMD" | grep -qiE "$SYS_PATHS"; then echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"PreToolUse\\",\\"permissionDecision\\":\\"ask\\",\\"permissionDecisionReason\\":\\"System path operation requires confirmation\\"}}" && exit 0; fi; else FILE=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); SYS_PATHS="/etc/|/usr/|/bin/|/sbin/|C:\\\\Windows|C:\\\\Program Files"; if echo "$FILE" | grep -qiE "$SYS_PATHS"; then echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"PreToolUse\\",\\"permissionDecision\\":\\"deny\\",\\"permissionDecisionReason\\":\\"Cannot modify system file: $FILE\\"}}" && exit 0; fi; fi; exit 0'],
args: ['-c', 'INPUT=$(cat); TOOL=$(echo "$INPUT" | jq -r ".tool_name // empty"); if [ "$TOOL" = "Bash" ]; then CMD=$(echo "$INPUT" | jq -r ".tool_input.command // empty"); SYS_PATHS="/etc/|/usr/|/bin/|/sbin/|/boot/|/sys/|/proc/|C:\\\\Windows|C:\\\\Program Files"; if echo "$CMD" | grep -qiE "$SYS_PATHS"; then echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"PreToolUse\\",\\"permissionDecision\\":\\"ask\\",\\"permissionDecisionReason\\":\\"System path operation requires confirmation\\"}}" && exit 0; fi; else FILE=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); SYS_PATHS="/etc/|/usr/|/bin/|/sbin/|C:\\\\Windows|C:\\\\Program Files"; if echo "$FILE" | grep -qiE "$SYS_PATHS"; then echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"PreToolUse\\",\\"permissionDecision\\":\\"deny\\",\\"permissionDecisionReason\\":\\"Cannot modify system file: $FILE\\"}}" >&2 && exit 2; fi; fi; exit 0'],
timeout: 5000,
},
'danger-permission-change': {

View File

@@ -94,6 +94,75 @@ function getHooksConfig(projectPath: string): { global: { path: string; hooks: u
};
}
/**
* Normalize hook data to Claude Code's official nested format
* Official format: { matcher?: string, hooks: [{ type: 'command', command: string, timeout?: number }] }
*
* IMPORTANT: All timeout values from frontend are in MILLISECONDS and must be converted to SECONDS.
* Official Claude Code spec requires timeout in seconds.
*
* @param {Object} hookData - Hook configuration (may be flat or nested format)
* @returns {Object} Normalized hook data in official format
*/
function normalizeHookFormat(hookData: Record<string, unknown>): Record<string, unknown> {
/**
* Convert timeout from milliseconds to seconds
* Frontend always sends milliseconds, Claude Code expects seconds
*/
const convertTimeout = (timeout: number): number => {
// Always convert from milliseconds to seconds
// This is safe because:
// - Frontend (HookWizard) uses milliseconds (e.g., 5000ms)
// - Claude Code official spec requires seconds
// - Minimum valid timeout is 1 second, so any value < 1000ms becomes 1s
return Math.max(1, Math.ceil(timeout / 1000));
};
// If already in nested format with hooks array, validate and convert
if (hookData.hooks && Array.isArray(hookData.hooks)) {
// Ensure each hook in the array has required fields
const normalizedHooks = (hookData.hooks as Array<Record<string, unknown>>).map(h => {
const normalized: Record<string, unknown> = {
type: h.type || 'command',
command: h.command || '',
};
// Convert timeout from milliseconds to seconds
if (typeof h.timeout === 'number') {
normalized.timeout = convertTimeout(h.timeout);
}
return normalized;
});
return {
...(hookData.matcher !== undefined ? { matcher: hookData.matcher } : { matcher: '' }),
hooks: normalizedHooks,
};
}
// Convert flat format to nested format
// Old format: { command: '...', timeout: 5000, name: '...', failMode: '...' }
// New format: { matcher: '', hooks: [{ type: 'command', command: '...', timeout: 5 }] }
if (hookData.command && typeof hookData.command === 'string') {
const nestedHook: Record<string, unknown> = {
type: 'command',
command: hookData.command,
};
// Convert timeout from milliseconds to seconds
if (typeof hookData.timeout === 'number') {
nestedHook.timeout = convertTimeout(hookData.timeout);
}
return {
matcher: typeof hookData.matcher === 'string' ? hookData.matcher : '',
hooks: [nestedHook],
};
}
// Return as-is if we can't normalize (let Claude Code validate)
return hookData;
}
/**
* Save a hook to settings file
* @param {string} projectPath
@@ -125,17 +194,19 @@ function saveHookToSettings(
settings.hooks[event] = [settings.hooks[event]];
}
// Normalize hook data to official format
const normalizedData = normalizeHookFormat(hookData);
// Check if we're replacing an existing hook
if (typeof hookData.replaceIndex === 'number') {
const index = hookData.replaceIndex;
delete hookData.replaceIndex;
const hooksForEvent = settings.hooks[event] as unknown[];
if (index >= 0 && index < hooksForEvent.length) {
hooksForEvent[index] = hookData;
hooksForEvent[index] = normalizedData;
}
} else {
// Add new hook
(settings.hooks[event] as unknown[]).push(hookData);
(settings.hooks[event] as unknown[]).push(normalizedData);
}
// Ensure directory exists and write file

View File

@@ -202,22 +202,27 @@ function installRecommendedHook(
settings.hooks[event] = [];
}
// Check if hook already exists (by command)
// Check if hook already exists (by command in nested hooks array)
const existingHooks = (settings.hooks[event] || []) as Array<Record<string, unknown>>;
const existingIndex = existingHooks.findIndex(
(h) => (h as Record<string, unknown>).command === hook.command
);
const existingIndex = existingHooks.findIndex((entry) => {
const hooks = (entry as Record<string, unknown>).hooks as Array<Record<string, unknown>> | undefined;
if (!hooks || !Array.isArray(hooks)) return false;
return hooks.some((h) => (h as Record<string, unknown>).command === hook.command);
});
if (existingIndex >= 0) {
return { success: true, installed: { id: hookId, event, status: 'already-exists' } };
}
// Add new hook
// Add new hook in Claude Code's official nested format
// Format: { matcher: '', hooks: [{ type: 'command', command: '...', timeout: 5 }] }
settings.hooks[event].push({
name: hook.name,
command: hook.command,
timeout: 5000,
failMode: 'silent'
matcher: '',
hooks: [{
type: 'command',
command: hook.command,
timeout: 5 // seconds, not milliseconds
}]
});
// Ensure directory exists

View File

@@ -2371,7 +2371,7 @@
<div class="preview-row">
<div class="preview-box">
<div class="size-128">
<svg viewBox="0 0 24 24" width="96" height="96" fill="none" stroke-linecap="round" stroke-linejoin="round">
<svg viewBox="-1 -1 26 26" width="96" height="96" fill="none" stroke-linecap="round" stroke-linejoin="round">
<defs>
<filter id="d1v2p_blur" x="-80%" y="-80%" width="260%" height="260%">
<feGaussianBlur in="SourceGraphic" stdDeviation="0.6"/>
@@ -2400,9 +2400,9 @@
</radialGradient>
</defs>
<!-- === LAYER 1: Back-facing orbit halves (behind core) === -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke="#D97757" stroke-width="0.9" opacity="0.22"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke="#10A37F" stroke-width="0.9" opacity="0.22"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke="#4285F4" stroke-width="0.9" opacity="0.22"/>
<path d="M4 12 A8 3 0 0 1 20 12" stroke="#D97757" stroke-width="1.4" opacity="0.25"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke="#10A37F" stroke-width="1.4" opacity="0.25"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke="#4285F4" stroke-width="1.4" opacity="0.25"/>
<!-- === LAYER 2: Core - blurred layers === -->
<circle cx="12" cy="12" r="4" fill="#3B82F6" opacity="0.12" filter="url(#d1v2p_blur_outer)"/>
@@ -2412,18 +2412,18 @@
<!-- === LAYER 3: Front-facing orbit halves (above core) - solid & vibrant === -->
<!-- Orbit 1 - Claude orange -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke="#D97757" stroke-width="1.6" opacity="0.92"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke="#D97757" stroke-width="1.6" opacity="0.92"/>
<!-- Orbit 2 - OpenAI green -->
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke="#10A37F" stroke-width="1.6" opacity="0.92"/>
<!-- Orbit 3 - Gemini blue -->
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke="#4285F4" stroke-width="1.6" opacity="0.92"/>
<!-- === LAYER 4: AI provider icons on 3D spheres === -->
<!-- Claude sphere at orbit position (17, 10.5) -->
<!-- Claude sphere at orbit position (17, 9.7) -->
<g>
<ellipse cx="17.1" cy="12.5" rx="1.5" ry="0.4" fill="#000" opacity="0.08"/>
<circle cx="17" cy="10.5" r="2.2" fill="url(#d1v2p_sphere_claude)"/>
<svg x="15.2" y="8.7" width="3.6" height="3.6" viewBox="0 0 16 16" fill="white" opacity="0.92">
<ellipse cx="17.1" cy="11.7" rx="1.5" ry="0.4" fill="#000" opacity="0.08"/>
<circle cx="17" cy="9.7" r="2.2" fill="url(#d1v2p_sphere_claude)"/>
<svg x="15.2" y="7.9" width="3.6" height="3.6" viewBox="0 0 16 16" fill="white" opacity="0.92">
<path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/>
</svg>
</g>
@@ -2449,7 +2449,7 @@
</div>
<div class="preview-box">
<div class="size-48">
<svg viewBox="0 0 24 24" width="36" height="36" fill="none" stroke-linecap="round" stroke-linejoin="round">
<svg viewBox="-1 -1 26 26" width="36" height="36" fill="none" stroke-linecap="round" stroke-linejoin="round">
<defs>
<filter id="d1v2p_blur3" x="-80%" y="-80%" width="260%" height="260%">
<feGaussianBlur in="SourceGraphic" stdDeviation="0.5"/>
@@ -2465,21 +2465,21 @@
</radialGradient>
</defs>
<!-- Back halves -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke="#D97757" stroke-width="1" opacity="0.2"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke="#10A37F" stroke-width="1" opacity="0.2"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke="#4285F4" stroke-width="1" opacity="0.2"/>
<path d="M4 12 A8 3 0 0 1 20 12" stroke="#D97757" stroke-width="1.4" opacity="0.25"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke="#10A37F" stroke-width="1.4" opacity="0.25"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke="#4285F4" stroke-width="1.4" opacity="0.25"/>
<!-- Core - blurred -->
<circle cx="12" cy="12" r="3.2" fill="#3B82F6" opacity="0.2" filter="url(#d1v2p_blur3)"/>
<circle cx="12" cy="12" r="2" fill="#93C5FD" opacity="0.4" filter="url(#d1v2p_blur3)"/>
<circle cx="12" cy="12" r="1.2" fill="white" opacity="0.65" filter="url(#d1v2p_blur3)"/>
<!-- Front halves - solid -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke="#D97757" stroke-width="1.6" opacity="0.92"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke="#D97757" stroke-width="1.6" opacity="0.92"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke="#10A37F" stroke-width="1.6" opacity="0.92"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke="#4285F4" stroke-width="1.6" opacity="0.92"/>
<!-- Claude sphere + icon -->
<ellipse cx="17.1" cy="12.5" rx="1.3" ry="0.35" fill="#000" opacity="0.06"/>
<circle cx="17" cy="10.5" r="2.1" fill="url(#d1v2p3_sphere_claude)"/>
<svg x="15.2" y="8.7" width="3.6" height="3.6" viewBox="0 0 16 16" fill="white" opacity="0.9">
<ellipse cx="17.1" cy="11.7" rx="1.3" ry="0.35" fill="#000" opacity="0.06"/>
<circle cx="17" cy="9.7" r="2.1" fill="url(#d1v2p3_sphere_claude)"/>
<svg x="15.2" y="7.9" width="3.6" height="3.6" viewBox="0 0 16 16" fill="white" opacity="0.9">
<path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/>
</svg>
<!-- OpenAI sphere + icon -->
@@ -2500,7 +2500,7 @@
</div>
<div class="preview-box">
<div class="size-24">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke-linecap="round">
<svg viewBox="-1 -1 26 26" width="20" height="20" fill="none" stroke-linecap="round">
<defs>
<filter id="d1v2p_blur4" x="-80%" y="-80%" width="260%" height="260%">
<feGaussianBlur in="SourceGraphic" stdDeviation="0.4"/>
@@ -2516,18 +2516,18 @@
</radialGradient>
</defs>
<!-- Back halves -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke="#D97757" stroke-width="1" opacity="0.2"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke="#10A37F" stroke-width="1" opacity="0.2"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke="#4285F4" stroke-width="1" opacity="0.2"/>
<path d="M4 12 A8 3 0 0 1 20 12" stroke="#D97757" stroke-width="1.4" opacity="0.25"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke="#10A37F" stroke-width="1.4" opacity="0.25"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke="#4285F4" stroke-width="1.4" opacity="0.25"/>
<!-- Core -->
<circle cx="12" cy="12" r="2.8" fill="#3B82F6" opacity="0.2" filter="url(#d1v2p_blur4)"/>
<circle cx="12" cy="12" r="1.6" fill="white" opacity="0.55" filter="url(#d1v2p_blur4)"/>
<!-- Front halves - solid -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke="#D97757" stroke-width="1.8" opacity="0.9"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke="#D97757" stroke-width="1.8" opacity="0.9"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke="#10A37F" stroke-width="1.8" opacity="0.9"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke="#4285F4" stroke-width="1.8" opacity="0.9"/>
<!-- 3D sphere dots at small size -->
<circle cx="17" cy="10.5" r="1.5" fill="url(#d1v2p4_sc)"/>
<circle cx="17" cy="9.7" r="1.5" fill="url(#d1v2p4_sc)"/>
<circle cx="8" cy="16" r="1.5" fill="url(#d1v2p4_so)"/>
<circle cx="14" cy="5.5" r="1.5" fill="url(#d1v2p4_sg)"/>
</svg>
@@ -2536,7 +2536,7 @@
</div>
<div class="preview-box">
<div class="size-16">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke-linecap="round">
<svg viewBox="-1 -1 26 26" width="14" height="14" fill="none" stroke-linecap="round">
<defs>
<radialGradient id="d1v2p5_sc" cx="38%" cy="30%" r="55%">
<stop offset="0%" stop-color="#F5D5C5"/><stop offset="55%" stop-color="#D97757"/><stop offset="100%" stop-color="#9B4F30"/>
@@ -2549,18 +2549,18 @@
</radialGradient>
</defs>
<!-- Back halves -->
<path d="M19 12 A7 2.5 0 0 1 5 12" stroke="#D97757" stroke-width="1.2" opacity="0.18"/>
<path d="M16.5 19 A7 2.5 30 0 1 7.5 5" stroke="#10A37F" stroke-width="1.2" opacity="0.18"/>
<path d="M7.5 19 A7 2.5 -30 0 1 16.5 5" stroke="#4285F4" stroke-width="1.2" opacity="0.18"/>
<path d="M5 12 A7 2.5 0 0 1 19 12" stroke="#D97757" stroke-width="1.6" opacity="0.25"/>
<path d="M16.5 19 A7 2.5 30 0 1 7.5 5" stroke="#10A37F" stroke-width="1.6" opacity="0.25"/>
<path d="M7.5 19 A7 2.5 -30 0 1 16.5 5" stroke="#4285F4" stroke-width="1.6" opacity="0.25"/>
<!-- Core -->
<circle cx="12" cy="12" r="3" fill="#3B82F6" opacity="0.25"/>
<circle cx="12" cy="12" r="1.6" fill="white" opacity="0.45"/>
<!-- Front halves - solid -->
<path d="M5 12 A7 2.5 0 0 1 19 12" stroke="#D97757" stroke-width="2.2" opacity="0.9"/>
<path d="M19 12 A7 2.5 0 0 1 5 12" stroke="#D97757" stroke-width="2.2" opacity="0.9"/>
<path d="M7.5 5 A7 2.5 30 0 1 16.5 19" stroke="#10A37F" stroke-width="2.2" opacity="0.9"/>
<path d="M16.5 5 A7 2.5 -30 0 1 7.5 19" stroke="#4285F4" stroke-width="2.2" opacity="0.9"/>
<!-- 3D sphere dots -->
<circle cx="17" cy="10" r="1.8" fill="url(#d1v2p5_sc)"/>
<circle cx="17" cy="9.5" r="1.8" fill="url(#d1v2p5_sc)"/>
<circle cx="8" cy="16" r="1.8" fill="url(#d1v2p5_so)"/>
<circle cx="14" cy="5" r="1.8" fill="url(#d1v2p5_sg)"/>
</svg>
@@ -2570,6 +2570,406 @@
</div>
</div>
<!-- D1-V2++ Minimal: Theme-adaptive line art 简约线条版 -->
<div class="card" style="grid-column: 1 / -1; border-color: currentColor; border-opacity: 0.2;">
<h2>D1-V2++ Minimal: Theme Line Art <span class="badge badge-rec">Adaptive</span></h2>
<div class="zh">简约线条版 — 纯 currentColor 跟随主题色 · 无渐变无滤镜 · 极简轨道+点</div>
<div class="desc">
Simplified from D1-V2+: pure <code>currentColor</code> strokes and fills, no gradients, no filters.<br>
Automatically adapts to light/dark theme. Front/back orbit layering preserved via opacity contrast.
</div>
<div class="preview-row">
<div class="preview-box">
<div class="size-128">
<svg viewBox="-1 -1 26 26" width="96" height="96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<!-- Back orbit halves -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.8" opacity="0.15"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.8" opacity="0.15"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.8" opacity="0.15"/>
<!-- Core -->
<circle cx="12" cy="12" r="2.5" fill="currentColor" opacity="0.08"/>
<circle cx="12" cy="12" r="1.5" fill="currentColor" opacity="0.15"/>
<circle cx="12" cy="12" r="0.8" fill="currentColor" opacity="0.3"/>
<!-- Front orbit halves -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.2" opacity="0.7"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.2" opacity="0.7"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.2" opacity="0.7"/>
<!-- Agent dots with AI icon outlines -->
<g>
<circle cx="17" cy="9.7" r="2.2" fill="currentColor" opacity="0.12"/>
<circle cx="17" cy="9.7" r="1.6" fill="currentColor" opacity="0.5"/>
<svg x="15.2" y="7.9" width="3.6" height="3.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95">
<path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/>
</svg>
</g>
<g>
<circle cx="8" cy="16" r="2.2" fill="currentColor" opacity="0.12"/>
<circle cx="8" cy="16" r="1.6" fill="currentColor" opacity="0.5"/>
<svg x="6.2" y="14.2" width="3.6" height="3.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95">
<path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/>
</svg>
</g>
<g>
<circle cx="14" cy="5.5" r="2.2" fill="currentColor" opacity="0.12"/>
<circle cx="14" cy="5.5" r="1.6" fill="currentColor" opacity="0.5"/>
<svg x="12.5" y="4" width="3" height="3" viewBox="0 0 24 24" fill="none">
<path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.3" opacity="0.95"/>
</svg>
</g>
</svg>
</div>
<span>128px</span>
</div>
<div class="preview-box">
<div class="size-48">
<svg viewBox="-1 -1 26 26" width="36" height="36" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<!-- Back halves -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<!-- Core -->
<circle cx="12" cy="12" r="2.2" fill="currentColor" opacity="0.1"/>
<circle cx="12" cy="12" r="1.2" fill="currentColor" opacity="0.2"/>
<!-- Front halves -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.7"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.7"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.7"/>
<!-- Agent icons -->
<g>
<circle cx="17" cy="9.7" r="2" fill="currentColor" opacity="0.12"/>
<circle cx="17" cy="9.7" r="1.4" fill="currentColor" opacity="0.5"/>
<svg x="15.5" y="8.2" width="3" height="3" viewBox="0 0 16 16" fill="currentColor" opacity="0.95">
<path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/>
</svg>
</g>
<g>
<circle cx="8" cy="16" r="2" fill="currentColor" opacity="0.12"/>
<circle cx="8" cy="16" r="1.4" fill="currentColor" opacity="0.5"/>
<svg x="6.5" y="14.5" width="3" height="3" viewBox="0 0 16 16" fill="currentColor" opacity="0.95">
<path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/>
</svg>
</g>
<g>
<circle cx="14" cy="5.5" r="2" fill="currentColor" opacity="0.12"/>
<circle cx="14" cy="5.5" r="1.4" fill="currentColor" opacity="0.5"/>
<svg x="12.7" y="4.2" width="2.6" height="2.6" viewBox="0 0 24 24" fill="none">
<path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.3" opacity="0.95"/>
</svg>
</g>
</svg>
</div>
<span>48px</span>
</div>
<div class="preview-box">
<div class="size-24">
<svg viewBox="-1 -1 26 26" width="20" height="20" fill="none" stroke="currentColor" stroke-linecap="round">
<!-- Back halves -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="1" opacity="0.12"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="1" opacity="0.12"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="1" opacity="0.12"/>
<!-- Core -->
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.15"/>
<!-- Front halves -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.5" opacity="0.7"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.5" opacity="0.7"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.5" opacity="0.7"/>
<!-- Agent dots -->
<circle cx="17" cy="9.7" r="1.5" fill="currentColor" opacity="0.85"/>
<circle cx="8" cy="16" r="1.5" fill="currentColor" opacity="0.85"/>
<circle cx="14" cy="5.5" r="1.5" fill="currentColor" opacity="0.85"/>
</svg>
</div>
<span>24px</span>
</div>
<div class="preview-box">
<div class="size-16">
<svg viewBox="-1 -1 26 26" width="14" height="14" fill="none" stroke="currentColor" stroke-linecap="round">
<!-- Back halves -->
<path d="M5 12 A7 2.5 0 0 1 19 12" stroke-width="1.2" opacity="0.1"/>
<path d="M16.5 19 A7 2.5 30 0 1 7.5 5" stroke-width="1.2" opacity="0.1"/>
<path d="M7.5 19 A7 2.5 -30 0 1 16.5 5" stroke-width="1.2" opacity="0.1"/>
<!-- Core -->
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.15"/>
<!-- Front halves -->
<path d="M19 12 A7 2.5 0 0 1 5 12" stroke-width="2" opacity="0.7"/>
<path d="M7.5 5 A7 2.5 30 0 1 16.5 19" stroke-width="2" opacity="0.7"/>
<path d="M16.5 5 A7 2.5 -30 0 1 7.5 19" stroke-width="2" opacity="0.7"/>
<!-- Agent dots -->
<circle cx="17" cy="9.5" r="1.8" fill="currentColor" opacity="0.85"/>
<circle cx="8" cy="16" r="1.8" fill="currentColor" opacity="0.85"/>
<circle cx="14" cy="5" r="1.8" fill="currentColor" opacity="0.85"/>
</svg>
</div>
<span>16px</span>
</div>
</div>
<!-- Theme color demos -->
<div style="margin-top: 20px; display: flex; gap: 24px; flex-wrap: wrap; align-items: center;">
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#3B82F6;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<g><circle cx="17" cy="9.7" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="17" cy="9.7" r="1.3" fill="currentColor" opacity="0.5"/><svg x="15.7" y="8.4" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/></svg></g><g><circle cx="8" cy="16" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="8" cy="16" r="1.3" fill="currentColor" opacity="0.5"/><svg x="6.7" y="14.7" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/></svg></g><g><circle cx="14" cy="5.5" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="14" cy="5.5" r="1.3" fill="currentColor" opacity="0.5"/><svg x="12.85" y="4.35" width="2.3" height="2.3" viewBox="0 0 24 24" fill="none"><path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.3" opacity="0.95"/></svg></g>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">Brand Blue</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#D97757;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<g><circle cx="17" cy="9.7" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="17" cy="9.7" r="1.3" fill="currentColor" opacity="0.5"/><svg x="15.7" y="8.4" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/></svg></g><g><circle cx="8" cy="16" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="8" cy="16" r="1.3" fill="currentColor" opacity="0.5"/><svg x="6.7" y="14.7" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/></svg></g><g><circle cx="14" cy="5.5" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="14" cy="5.5" r="1.3" fill="currentColor" opacity="0.5"/><svg x="12.85" y="4.35" width="2.3" height="2.3" viewBox="0 0 24 24" fill="none"><path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.3" opacity="0.95"/></svg></g>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">Claude</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#10A37F;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<g><circle cx="17" cy="9.7" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="17" cy="9.7" r="1.3" fill="currentColor" opacity="0.5"/><svg x="15.7" y="8.4" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/></svg></g><g><circle cx="8" cy="16" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="8" cy="16" r="1.3" fill="currentColor" opacity="0.5"/><svg x="6.7" y="14.7" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/></svg></g><g><circle cx="14" cy="5.5" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="14" cy="5.5" r="1.3" fill="currentColor" opacity="0.5"/><svg x="12.85" y="4.35" width="2.3" height="2.3" viewBox="0 0 24 24" fill="none"><path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.3" opacity="0.95"/></svg></g>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">OpenAI</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#8B5CF6;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<g><circle cx="17" cy="9.7" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="17" cy="9.7" r="1.3" fill="currentColor" opacity="0.5"/><svg x="15.7" y="8.4" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/></svg></g><g><circle cx="8" cy="16" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="8" cy="16" r="1.3" fill="currentColor" opacity="0.5"/><svg x="6.7" y="14.7" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/></svg></g><g><circle cx="14" cy="5.5" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="14" cy="5.5" r="1.3" fill="currentColor" opacity="0.5"/><svg x="12.85" y="4.35" width="2.3" height="2.3" viewBox="0 0 24 24" fill="none"><path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.3" opacity="0.95"/></svg></g>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">Purple</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#ffffff;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<g><circle cx="17" cy="9.7" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="17" cy="9.7" r="1.3" fill="currentColor" opacity="0.5"/><svg x="15.7" y="8.4" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/></svg></g><g><circle cx="8" cy="16" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="8" cy="16" r="1.3" fill="currentColor" opacity="0.5"/><svg x="6.7" y="14.7" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/></svg></g><g><circle cx="14" cy="5.5" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="14" cy="5.5" r="1.3" fill="currentColor" opacity="0.5"/><svg x="12.85" y="4.35" width="2.3" height="2.3" viewBox="0 0 24 24" fill="none"><path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.3" opacity="0.95"/></svg></g>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">White</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;background:#f5f5f5;padding:8px 12px;border-radius:8px;">
<div style="color:#1a1a1a;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<g><circle cx="17" cy="9.7" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="17" cy="9.7" r="1.3" fill="currentColor" opacity="0.5"/><svg x="15.7" y="8.4" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/></svg></g><g><circle cx="8" cy="16" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="8" cy="16" r="1.3" fill="currentColor" opacity="0.5"/><svg x="6.7" y="14.7" width="2.6" height="2.6" viewBox="0 0 16 16" fill="currentColor" opacity="0.95"><path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/></svg></g><g><circle cx="14" cy="5.5" r="1.8" fill="currentColor" opacity="0.12"/><circle cx="14" cy="5.5" r="1.3" fill="currentColor" opacity="0.5"/><svg x="12.85" y="4.35" width="2.3" height="2.3" viewBox="0 0 24 24" fill="none"><path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.3" opacity="0.95"/></svg></g>
</svg>
</div>
<span style="font-size:11px;color:#999;">Dark on Light</span>
</div>
</div>
</div>
<!-- D1-V2+++ Hybrid: Theme line art + branded AI icons -->
<div class="card" style="grid-column: 1 / -1; border-color: currentColor; border-opacity: 0.2;">
<h2>D1-V2+++: Branded Agents <span class="badge badge-rec">Hybrid</span></h2>
<div class="zh">品牌代理 — currentColor 轨道 + AI 图标保留品牌色 · 半自适应主题</div>
<div class="desc">
Based on D1-V2++ Minimal: orbits and core use <code>currentColor</code> for theme adaptation,<br>
but AI agent icons retain their brand colors (Claude <span style="color:#D97757;">#D97757</span>, OpenAI <span style="color:#10A37F;">#10A37F</span>, Gemini <span style="color:#4285F4;">#4285F4</span>).
</div>
<div class="preview-row">
<div class="preview-box">
<div class="size-128">
<svg viewBox="-1 -1 26 26" width="96" height="96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<!-- Back orbit halves -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.8" opacity="0.15"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.8" opacity="0.15"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.8" opacity="0.15"/>
<!-- Core -->
<circle cx="12" cy="12" r="2.5" fill="currentColor" opacity="0.08"/>
<circle cx="12" cy="12" r="1.5" fill="currentColor" opacity="0.15"/>
<circle cx="12" cy="12" r="0.8" fill="currentColor" opacity="0.3"/>
<!-- Front orbit halves -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.2" opacity="0.7"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.2" opacity="0.7"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.2" opacity="0.7"/>
<!-- Agent icons with brand colors -->
<g>
<circle cx="17" cy="9.7" r="2.2" fill="#D97757" opacity="0.15"/>
<circle cx="17" cy="9.7" r="1.6" fill="#D97757" opacity="0.45"/>
<svg x="15.2" y="7.9" width="3.6" height="3.6" viewBox="0 0 16 16" fill="#D97757" opacity="0.95">
<path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/>
</svg>
</g>
<g>
<circle cx="8" cy="16" r="2.2" fill="#10A37F" opacity="0.15"/>
<circle cx="8" cy="16" r="1.6" fill="#10A37F" opacity="0.45"/>
<svg x="6.2" y="14.2" width="3.6" height="3.6" viewBox="0 0 16 16" fill="#10A37F" opacity="0.95">
<path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/>
</svg>
</g>
<g>
<circle cx="14" cy="5.5" r="2.2" fill="#4285F4" opacity="0.15"/>
<circle cx="14" cy="5.5" r="1.6" fill="#4285F4" opacity="0.45"/>
<svg x="12.5" y="4" width="3" height="3" viewBox="0 0 24 24" fill="none">
<path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="#4285F4" stroke-width="2" fill="#4285F4" fill-opacity="0.3" opacity="0.95"/>
</svg>
</g>
</svg>
</div>
<span>128px</span>
</div>
<div class="preview-box">
<div class="size-48">
<svg viewBox="-1 -1 26 26" width="36" height="36" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<!-- Back halves -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<!-- Core -->
<circle cx="12" cy="12" r="2.2" fill="currentColor" opacity="0.1"/>
<circle cx="12" cy="12" r="1.2" fill="currentColor" opacity="0.2"/>
<!-- Front halves -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.7"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.7"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.7"/>
<!-- Agent icons with brand colors -->
<g>
<circle cx="17" cy="9.7" r="2" fill="#D97757" opacity="0.15"/>
<circle cx="17" cy="9.7" r="1.4" fill="#D97757" opacity="0.45"/>
<svg x="15.5" y="8.2" width="3" height="3" viewBox="0 0 16 16" fill="#D97757" opacity="0.95">
<path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/>
</svg>
</g>
<g>
<circle cx="8" cy="16" r="2" fill="#10A37F" opacity="0.15"/>
<circle cx="8" cy="16" r="1.4" fill="#10A37F" opacity="0.45"/>
<svg x="6.5" y="14.5" width="3" height="3" viewBox="0 0 16 16" fill="#10A37F" opacity="0.95">
<path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/>
</svg>
</g>
<g>
<circle cx="14" cy="5.5" r="2" fill="#4285F4" opacity="0.15"/>
<circle cx="14" cy="5.5" r="1.4" fill="#4285F4" opacity="0.45"/>
<svg x="12.7" y="4.2" width="2.6" height="2.6" viewBox="0 0 24 24" fill="none">
<path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="#4285F4" stroke-width="2" fill="#4285F4" fill-opacity="0.3" opacity="0.95"/>
</svg>
</g>
</svg>
</div>
<span>48px</span>
</div>
<div class="preview-box">
<div class="size-24">
<svg viewBox="-1 -1 26 26" width="20" height="20" fill="none" stroke="currentColor" stroke-linecap="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="1" opacity="0.12"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="1" opacity="0.12"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="1" opacity="0.12"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.15"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.5" opacity="0.7"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.5" opacity="0.7"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.5" opacity="0.7"/>
<circle cx="17" cy="9.7" r="1.5" fill="#D97757" opacity="0.85"/>
<circle cx="8" cy="16" r="1.5" fill="#10A37F" opacity="0.85"/>
<circle cx="14" cy="5.5" r="1.5" fill="#4285F4" opacity="0.85"/>
</svg>
</div>
<span>24px</span>
</div>
<div class="preview-box">
<div class="size-16">
<svg viewBox="-1 -1 26 26" width="14" height="14" fill="none" stroke="currentColor" stroke-linecap="round">
<path d="M5 12 A7 2.5 0 0 1 19 12" stroke-width="1.2" opacity="0.1"/>
<path d="M16.5 19 A7 2.5 30 0 1 7.5 5" stroke-width="1.2" opacity="0.1"/>
<path d="M7.5 19 A7 2.5 -30 0 1 16.5 5" stroke-width="1.2" opacity="0.1"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.15"/>
<path d="M19 12 A7 2.5 0 0 1 5 12" stroke-width="2" opacity="0.7"/>
<path d="M7.5 5 A7 2.5 30 0 1 16.5 19" stroke-width="2" opacity="0.7"/>
<path d="M16.5 5 A7 2.5 -30 0 1 7.5 19" stroke-width="2" opacity="0.7"/>
<circle cx="17" cy="9.5" r="1.8" fill="#D97757" opacity="0.85"/>
<circle cx="8" cy="16" r="1.8" fill="#10A37F" opacity="0.85"/>
<circle cx="14" cy="5" r="1.8" fill="#4285F4" opacity="0.85"/>
</svg>
</div>
<span>16px</span>
</div>
</div>
<!-- Theme color demos -->
<div style="margin-top: 20px; display: flex; gap: 24px; flex-wrap: wrap; align-items: center;">
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#3B82F6;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<circle cx="17" cy="9.7" r="1.5" fill="#D97757" opacity="0.9"/><circle cx="8" cy="16" r="1.5" fill="#10A37F" opacity="0.9"/><circle cx="14" cy="5.5" r="1.5" fill="#4285F4" opacity="0.9"/>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">Brand Blue</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#D97757;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<circle cx="17" cy="9.7" r="1.5" fill="#D97757" opacity="0.9"/><circle cx="8" cy="16" r="1.5" fill="#10A37F" opacity="0.9"/><circle cx="14" cy="5.5" r="1.5" fill="#4285F4" opacity="0.9"/>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">Claude</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#10A37F;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<circle cx="17" cy="9.7" r="1.5" fill="#D97757" opacity="0.9"/><circle cx="8" cy="16" r="1.5" fill="#10A37F" opacity="0.9"/><circle cx="14" cy="5.5" r="1.5" fill="#4285F4" opacity="0.9"/>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">OpenAI</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#8B5CF6;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<circle cx="17" cy="9.7" r="1.5" fill="#D97757" opacity="0.9"/><circle cx="8" cy="16" r="1.5" fill="#10A37F" opacity="0.9"/><circle cx="14" cy="5.5" r="1.5" fill="#4285F4" opacity="0.9"/>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">Purple</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;">
<div style="color:#ffffff;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<circle cx="17" cy="9.7" r="1.5" fill="#D97757" opacity="0.9"/><circle cx="8" cy="16" r="1.5" fill="#10A37F" opacity="0.9"/><circle cx="14" cy="5.5" r="1.5" fill="#4285F4" opacity="0.9"/>
</svg>
</div>
<span style="font-size:11px;opacity:0.5;">White</span>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;background:#f5f5f5;padding:8px 12px;border-radius:8px;">
<div style="color:#1a1a1a;">
<svg viewBox="-1 -1 26 26" width="48" height="48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 12 A8 3 0 0 1 20 12" stroke-width="0.9" opacity="0.15"/><path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke-width="0.9" opacity="0.15"/><path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke-width="0.9" opacity="0.15"/>
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.12"/><circle cx="12" cy="12" r="1" fill="currentColor" opacity="0.25"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke-width="1.3" opacity="0.75"/><path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke-width="1.3" opacity="0.75"/><path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke-width="1.3" opacity="0.75"/>
<circle cx="17" cy="9.7" r="1.5" fill="#D97757" opacity="0.9"/><circle cx="8" cy="16" r="1.5" fill="#10A37F" opacity="0.9"/><circle cx="14" cy="5.5" r="1.5" fill="#4285F4" opacity="0.9"/>
</svg>
</div>
<span style="font-size:11px;color:#999;">Dark on Light</span>
</div>
</div>
</div>
<!-- D1-V3: Sharp Circuit 锐利电路 -->
<div class="card">
<h2>D1-V3: Sharp Circuit</h2>
@@ -2814,7 +3214,7 @@
</div>
<div class="compare-item">
<div class="compare-box" style="border-color: #3B82F640;">
<svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke-linecap="round">
<svg viewBox="-1 -1 26 26" width="40" height="40" fill="none" stroke-linecap="round">
<defs>
<filter id="d1v2pc_blur" x="-80%" y="-80%" width="260%" height="260%">
<feGaussianBlur in="SourceGraphic" stdDeviation="0.5"/>
@@ -2830,19 +3230,19 @@
</radialGradient>
</defs>
<!-- Back halves -->
<path d="M20 12 A8 3 0 0 1 4 12" stroke="#D97757" stroke-width="0.8" opacity="0.2"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke="#10A37F" stroke-width="0.8" opacity="0.2"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke="#4285F4" stroke-width="0.8" opacity="0.2"/>
<path d="M4 12 A8 3 0 0 1 20 12" stroke="#D97757" stroke-width="1.2" opacity="0.25"/>
<path d="M16.9 19.5 A8 3 30 0 1 7.1 4.5" stroke="#10A37F" stroke-width="1.2" opacity="0.25"/>
<path d="M7.1 19.5 A8 3 -30 0 1 16.9 4.5" stroke="#4285F4" stroke-width="1.2" opacity="0.25"/>
<!-- Core -->
<circle cx="12" cy="12" r="3" fill="#3B82F6" opacity="0.2" filter="url(#d1v2pc_blur)"/>
<circle cx="12" cy="12" r="1.8" fill="#93C5FD" opacity="0.35" filter="url(#d1v2pc_blur)"/>
<circle cx="12" cy="12" r="1" fill="white" opacity="0.55" filter="url(#d1v2pc_blur)"/>
<!-- Front halves - solid -->
<path d="M4 12 A8 3 0 0 1 20 12" stroke="#D97757" stroke-width="1.5" opacity="0.92"/>
<path d="M20 12 A8 3 0 0 1 4 12" stroke="#D97757" stroke-width="1.5" opacity="0.92"/>
<path d="M7.1 4.5 A8 3 30 0 1 16.9 19.5" stroke="#10A37F" stroke-width="1.5" opacity="0.92"/>
<path d="M16.9 4.5 A8 3 -30 0 1 7.1 19.5" stroke="#4285F4" stroke-width="1.5" opacity="0.92"/>
<circle cx="17" cy="10.5" r="2" fill="url(#d1v2pc_sc)"/>
<svg x="15.2" y="8.7" width="3.6" height="3.6" viewBox="0 0 16 16" fill="white" opacity="0.9">
<circle cx="17" cy="9.7" r="2" fill="url(#d1v2pc_sc)"/>
<svg x="15.2" y="7.9" width="3.6" height="3.6" viewBox="0 0 16 16" fill="white" opacity="0.9">
<path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/>
</svg>
<circle cx="8" cy="16" r="2" fill="url(#d1v2pc_so)"/>

View File

@@ -0,0 +1,279 @@
# Hook 模板分析报告
> 基于 Claude Code 官方 Hook 规范对 `ccw/frontend` 前端实现的检查
---
## 概要
| 检查项 | 状态 | 严重级别 |
|--------|------|----------|
| 触发器类型支持 | 12/18 支持 | ⚠️ 缺失 6 种 |
| 命令结构格式 | 不合规 | 🔴 CRITICAL |
| 输入读取方式 | 不合规 | 🔴 CRITICAL |
| Bash 脚本跨平台 | 不兼容 | 🟠 ERROR |
| JSON 决策输出 | 合规 | ✅ 正确 |
| Matcher 格式 | 部分问题 | ⚠️ WARNING |
---
## 1. CRITICAL 问题
### 1.1 命令结构:`command` + `args` 数组格式
**官方规范**:使用单一 `command` 字符串
```json
{
"type": "command",
"command": "bash .claude/hooks/validate.sh",
"timeout": 30
}
```
**当前实现**:使用 `command` + `args` 数组
```typescript
command: 'node',
args: ['-e', 'const cp=require("child_process");...']
```
**影响文件**
- `HookQuickTemplates.tsx` 第 77-229 行(所有 16 个模板)
- `HookWizard.tsx` 第 87-148 行HOOK_TEMPLATES 对象)
**修复方案**
```typescript
// 错误格式
command: 'node',
args: ['-e', 'script...']
// 正确格式
command: "node -e 'script...'"
```
---
### 1.2 输入读取:`process.env.HOOK_INPUT` vs stdin
**官方规范**Hook 输入通过 **stdin** 传入 JSON
```
输入JSON 通过 stdin 传入
```
**当前实现**:使用环境变量
```javascript
const p=JSON.parse(process.env.HOOK_INPUT||"{}");
```
**影响位置**
- `HookQuickTemplates.tsx`: 第 93, 120, 133, 146, 172, 184, 228 行
**修复方案**
```javascript
// Node.js 内联脚本
const fs=require('fs');const p=JSON.parse(fs.readFileSync(0,'utf8')||"{}");
// Bash 脚本
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command')
```
---
## 2. ERROR 问题
### 2.1 Bash 脚本在 Windows 上失败
**问题**`HookWizard.tsx` 中所有 `danger-*` 模板使用 `bash -c`
```typescript
command: 'bash',
args: ['-c', 'INPUT=$(cat); CMD=$(echo "$INPUT" | jq -r ...']
```
**失败原因**
1. Windows 默认没有 `bash`(需要 WSL 或 Git Bash
2. 使用 Unix 命令:`cat`, `jq`, `grep -qiE`
3. 使用 Unix shell 语法:`$(...)`, `if; then; fi`
**影响模板**
- `danger-bash-confirm` (第 106-112 行)
- `danger-file-protection` (第 113-119 行)
- `danger-git-destructive` (第 120-126 行)
- `danger-network-confirm` (第 127-133 行)
- `danger-system-paths` (第 134-140 行)
- `danger-permission-change` (第 141-147 行)
**修复方案**
1. 使用 `node -e` 替代 `bash -c`(跨平台)
2. 或提供 PowerShell 版本的检测脚本
3. 或在运行时检测平台并选择对应脚本
---
### 2.2 平台检测使用浏览器 UA
**问题**`convertToClaudeCodeFormat` 函数(第 185 行)
```javascript
const isWindows = typeof navigator !== 'undefined' && navigator.userAgent.includes('Win');
```
**错误场景**:用户在 Mac 浏览器中配置,但 Hook 在远程 Windows 机器执行
**修复方案**:从后端 API 获取实际执行平台信息
---
## 3. WARNING 问题
### 3.1 无效 MCP Matcher 格式
**位置**`HookQuickTemplates.tsx` 第 224 行
```typescript
matcher: 'core_memory'
```
**官方规范**MCP 工具命名格式 `mcp__<server>__<tool>`
```typescript
// 正确格式
matcher: 'mcp__ccw-tools__core_memory'
```
---
### 3.2 空 Matcher 滥用
**位置**`HookWizard.tsx` 第 90, 96, 102 行
空 matcher `''` 是有效的,但意味着 Hook 会对该事件类型的所有工具触发。对于 `Stop` 事件没有问题,但需要确认是否为预期行为。
---
### 3.3 引号转义脆弱
**位置**`HookWizard.tsx` 第 173, 187-191 行
当前转义逻辑:
```javascript
// bash 脚本
const escapedScript = script.replace(/'/g, "'\\''");
// Windows node 脚本
const escapedScript = script.replace(/"/g, '\\"');
```
**问题**:对于包含反引号、`$()`、嵌套引号的复杂脚本可能失败
---
### 3.4 Exit Code 2 未使用
**官方规范**exit code 2 用于阻止操作并显示反馈
**当前状态**:仅 `block-sensitive-files` 模板使用 `process.exit(2)`,其他 `danger-*` 模板通过 JSON 输出 `permissionDecision` 但未配合 exit code
**修复**:在输出 deny 决策后应使用 `exit 2`
```bash
echo '{"hookSpecificOutput":{...}}' && exit 0 # 当前
echo '{"hookSpecificOutput":{...}}' && exit 2 # 应改为 exit 2 以阻止
```
---
## 4. 触发器类型支持情况
### 4.1 完整支持表
| 触发器 | 代码支持 | UI 过滤器 | 状态 |
|--------|----------|-----------|------|
| SessionStart | ✅ | ✅ | 完整 |
| SessionEnd | ✅ | ❌ | 代码有UI 无 |
| UserPromptSubmit | ✅ | ✅ | 完整 |
| PreToolUse | ✅ | ✅ | 完整 |
| PostToolUse | ✅ | ✅ | 完整 |
| PostToolUseFailure | ✅ | ❌ | 代码有UI 无 |
| PermissionRequest | ✅ | ❌ | 代码有UI 无 |
| Notification | ✅ | ❌ | 代码有UI 无 |
| Stop | ✅ | ✅ | 完整 |
| SubagentStart | ✅ | ❌ | 代码有UI 无 |
| SubagentStop | ✅ | ❌ | 代码有UI 无 |
| PreCompact | ✅ | ❌ | 代码有UI 无 |
| **TeammateIdle** | ❌ | ❌ | **缺失** |
| **TaskCompleted** | ❌ | ❌ | **缺失** |
| **ConfigChange** | ❌ | ❌ | **缺失** |
| **WorktreeCreate** | ❌ | ❌ | **缺失** |
| **WorktreeRemove** | ❌ | ❌ | **缺失** |
### 4.2 需要添加的触发器
**缺失的 6 种触发器**
1. `TeammateIdle` - 团队成员空闲时触发
2. `TaskCompleted` - 任务标记完成时触发
3. `ConfigChange` - 配置文件外部修改时触发
4. `WorktreeCreate` - 工作树创建时触发
5. `WorktreeRemove` - 工作树移除时触发
---
## 5. 正确实现的部分
| 项目 | 状态 | 说明 |
|------|------|------|
| 触发器类型名称 | ✅ | 使用的触发器名称符合官方规范 |
| Matcher 正则语法 | ✅ | `Write\|Edit``Bash` 等格式正确 |
| Timeout 单位转换 | ✅ | 毫秒→秒转换正确 |
| JSON 决策输出格式 | ✅ | `hookSpecificOutput` 结构符合规范 |
| Bash stdin 读取 | ✅ | `INPUT=$(cat)` 方式正确 |
---
## 6. 修复优先级
### P0 - 必须立即修复 ✅ 已修复
1. **命令格式**:将 `command` + `args` 合并为单一字符串
2. **输入读取**:将 `process.env.HOOK_INPUT` 改为 stdin 读取
### P1 - 尽快修复 ✅ 已修复
3. **Windows 兼容**:将 `bash -c` 脚本改为 `node -e` 或提供 PowerShell 版本
4. **Exit code 2**:在 deny 场景使用正确的 exit code
### P2 - 后续优化
5. **缺失触发器**:添加 6 种缺失的触发器类型支持
6. **UI 过滤器**:将所有支持的触发器添加到过滤器
7. **MCP Matcher**:修正 `core_memory``mcp__ccw-tools__core_memory` ✅ 已修复
8. **平台检测**:从后端获取实际执行平台
---
## 7. 已完成的修复
### 7.1 后端修复
**文件**: `ccw/src/core/routes/system-routes.ts`
- `installRecommendedHook` 函数 (第 216-231 行)
- 修复:使用官方嵌套格式 `{ matcher: '', hooks: [{ type: 'command', command: '...', timeout: 5 }] }`
- 修复timeout 从毫秒改为秒
**文件**: `ccw/src/core/routes/hooks-routes.ts`
- 新增 `normalizeHookFormat` 函数
- 自动将旧格式转换为新格式
- 自动将 timeout 从毫秒转换为秒
### 7.2 前端修复
**文件**: `ccw/frontend/src/components/hook/HookQuickTemplates.tsx`
- 所有模板从 `process.env.HOOK_INPUT` 改为 `fs.readFileSync(0, 'utf8')`
- 修复 `memory-sync-dashboard` 的 matcher: `core_memory``mcp__ccw-tools__core_memory`
**文件**: `ccw/frontend/src/components/hook/HookWizard.tsx`
- `skill-context-keyword``skill-context-auto` 模板修复 stdin 读取
- `danger-file-protection``danger-system-paths` 的 deny 决策使用 exit 2
---
## 7. 文件位置参考
| 文件 | 主要问题 |
|------|----------|
| `src/components/hook/HookQuickTemplates.tsx` | 命令格式、输入读取、MCP matcher |
| `src/components/hook/HookWizard.tsx` | Bash 跨平台、命令格式、平台检测 |
| `src/pages/HookManagerPage.tsx` | 缺失触发器类型、UI 过滤器不完整 |
| `src/components/hook/HookCard.tsx` | HookTriggerType 类型定义不完整 |