executable-stories-formatters 0.7.5 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/validation/schema-validator.ts","../schemas/raw-run.schema.json","../src/converters/synthesize.ts","../src/converters/acl/status.ts","../src/converters/acl/ids.ts","../src/converters/acl/steps.ts","../src/converters/acl/attachments.ts","../src/converters/acl/index.ts","../src/converters/acl/validate.ts","../src/index.ts","../src/converters/acl/lines.ts","../src/formatters/cucumber-json.ts","../src/formatters/html/template.ts","../src/formatters/html/styles.ts","../src/formatters/html/themes/default.ts","../src/formatters/html/themes/corporate.ts","../src/formatters/html/themes/terminal.ts","../src/formatters/html/themes/minimal.ts","../src/formatters/html/themes/dashboard.ts","../src/formatters/html/themes/playful.ts","../src/formatters/html/themes/index.ts","../src/formatters/html/renderers/status.ts","../src/formatters/html/renderers/meta.ts","../src/formatters/html/renderers/summary.ts","../src/formatters/html/renderers/tag-bar.ts","../src/formatters/html/renderers/error-box.ts","../src/formatters/html/renderers/attachments.ts","../src/formatters/html/renderers/doc-entries.ts","../src/formatters/html/renderers/steps.ts","../src/formatters/html/renderers/step-params.ts","../src/history/sample-policy.ts","../src/formatters/html/renderers/scenario.ts","../src/formatters/html/renderers/trace-view.ts","../src/formatters/html/renderers/feature.ts","../src/formatters/html/renderers/body.ts","../src/formatters/html/renderers/failure-summary.ts","../src/formatters/html/renderers/toc.ts","../src/formatters/html/renderers/index.ts","../src/formatters/html/index.ts","../src/formatters/junit-xml.ts","../src/formatters/markdown.ts","../src/formatters/cucumber-messages/synthesize-feature.ts","../src/utils/cucumber-messages.ts","../src/formatters/cucumber-messages/build-gherkin-document.ts","../src/formatters/cucumber-messages/build-pickles.ts","../src/formatters/cucumber-messages/build-execution.ts","../src/formatters/cucumber-messages/index.ts","../src/formatters/cucumber-html.ts","../src/types/compare.ts","../src/compare/pr-summary.ts","../src/compare/auto-baseline.ts","../src/compare/index.ts","../src/formatters/run-diff-html.ts","../src/formatters/run-diff-markdown.ts","../src/select-test-cases.ts","../src/bundler/bundle-assets.ts","../src/bundler/scan-html-assets.ts","../src/bundler/copy-asset.ts","../src/converters/ndjson-parser.ts","../src/notifiers/ansi-strip.ts","../src/notifiers/slack.ts","../src/notifiers/teams.ts","../src/notifiers/hmac.ts","../src/notifiers/webhook.ts","../src/notifiers/index.ts","../src/types/ci.ts","../src/history/history-store.ts","../src/list-scenarios.ts"],"sourcesContent":["/**\n * executable-stories CLI\n *\n * Reads raw test results as JSON and generates reports.\n *\n * Usage:\n * executable-stories format run.json --format html,markdown\n * executable-stories format --stdin --format html\n * executable-stories validate run.json\n */\n\nimport { parseArgs } from \"node:util\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { validateRawRun } from \"./validation/schema-validator\";\nimport { synthesizeStories } from \"./converters/synthesize\";\nimport { canonicalizeRun } from \"./converters/acl\";\nimport { assertValidRun } from \"./converters/acl/validate\";\n// eslint-disable-next-line no-restricted-imports -- ReportGenerator and compare helpers currently live in the package entrypoint.\nimport {\n ReportGenerator,\n createPrCommentSummary,\n generateRunComparison,\n} from \"./index.js\";\nimport { parseNdjson } from \"./converters/ndjson-parser\";\nimport type { OutputFormat } from \"./types/options\";\nimport { sendNotifications } from \"./notifiers\";\nimport { toCIInfo } from \"./types/ci\";\nimport type { NotifyCondition, GenericWebhookNotifierOptions, WebhookSignerHmac } from \"./notifiers/types\";\nimport { loadHistory, saveHistory, updateHistory } from \"./history\";\nimport { pickAutoBaseline } from \"./compare/auto-baseline\";\nimport { listScenarios } from \"./list-scenarios\";\nimport { selectTestCases } from \"./select-test-cases\";\nimport type { RawRun } from \"./types/raw\";\nimport type { TestRunResult } from \"./types/test-result\";\n\n// ============================================================================\n// Exit Codes\n// ============================================================================\n\nconst EXIT_SUCCESS = 0;\nconst EXIT_SCHEMA_VALIDATION = 1;\nconst EXIT_CANONICAL_VALIDATION = 2;\nconst EXIT_GENERATION = 3;\nconst EXIT_USAGE = 4;\n\n// ============================================================================\n// CLI Argument Parsing\n// ============================================================================\n\nconst HELP_TEXT = `\nexecutable-stories — Generate reports from test results JSON.\n\nUSAGE\n executable-stories format <file> [options]\n executable-stories format --stdin [options]\n executable-stories compare <baseline-file> <current-file> [options]\n executable-stories list <file> [options]\n executable-stories validate <file>\n executable-stories validate --stdin\n\nSUBCOMMANDS\n format Read raw test results and generate reports\n compare Compare two runs and generate a diff report\n list List scenarios from a test run (text table or JSON)\n validate Validate a JSON file against the schema (no output generated)\n\nOPTIONS\n --format <formats> Comma-separated formats (default: html)\n html Custom HTML report (accessible, dark mode, mermaid)\n cucumber-html Official Cucumber HTML report\n markdown Markdown documentation\n junit JUnit XML\n cucumber-json Cucumber JSON\n cucumber-messages Raw NDJSON (Cucumber Messages)\n --input-type <type> Input type: raw, canonical, or ndjson (default: raw)\n --output-dir <dir> Output directory (default: reports)\n --output-name <name> Base filename (default: test-results)\n --output-name-timestamp Append run timestamp (UTC seconds) to output filename for before/after diffs\n --sort-test-cases <mode> Sort scenarios deterministically: id, source, none (default: none)\n --include <globs> Comma-separated globs to include test cases by sourceFile (e.g. \"**/*.Story*.cs\")\n --exclude <globs> Comma-separated globs to exclude test cases by sourceFile (e.g. \"**/obj/**\")\n --include-tags <tags> Comma-separated tags to include test cases (any match)\n --exclude-tags <tags> Comma-separated tags to exclude test cases (any match)\n --synthesize-stories Synthesize story metadata for plain test results (default)\n --no-synthesize-stories Disable story synthesis (strict mode)\n --html-title <title> HTML report title (default: Test Results)\n --html-theme <name> HTML theme (default, corporate, terminal, minimal, dashboard, playful)\n --html-no-syntax-highlighting Disable syntax highlighting in HTML (enabled by default)\n --html-no-mermaid Disable mermaid diagrams in HTML (enabled by default)\n --html-no-markdown Disable markdown parsing in HTML (enabled by default)\n --html-permalink-base-url <url> Base URL for source permalinks in HTML (e.g. \"https://github.com/org/repo/blob/main\")\n --html-no-toc Disable table of contents sidebar in HTML (enabled by default)\n --html-theme-picker Include theme picker in HTML report (embeds all CSS-only themes)\n --html-ticket-url-template <url> URL template for ticket links in HTML (use {ticket} as placeholder)\n --asset-mode <mode> Asset bundling: \"none\" (default) or \"copy\"\n --allow-missing-assets Warn on missing assets instead of failing\n --stdin Read JSON from stdin instead of file\n --json-summary Print machine-parsable JSON summary\n --baseline <path|auto> Compare baseline file, or auto-pick a prior run for compare\n --baseline-dir <dir> Directory to scan when --baseline auto is used\n --pr-summary Print a PR-friendly markdown summary after compare\n --pr-summary-file <path> Write the PR-friendly markdown summary to a file\n --emit-canonical <path> Write canonical JSON to given path\n --help Show this help message\n\nLIST\n list prints one scenario per line (text by default, JSON with --json-summary)\n list supports --include-tags, --exclude-tags for filtering\n list supports --input-type and --stdin\n\nCOMPARE\n compare supports --format html,markdown\n compare uses the same --input-type for both baseline and current files\n\nNOTIFICATIONS\n --slack-webhook <url> Slack incoming webhook URL (fallback: SLACK_WEBHOOK_URL env var)\n --teams-webhook <url> Teams incoming webhook URL (fallback: TEAMS_WEBHOOK_URL env var)\n --notify <condition> When to send: always, on-failure, never (default: on-failure)\n --report-url <url> URL to link in notification messages\n --max-failed-tests <n> Max failed tests to show in notifications (default: 5)\n\nGENERIC WEBHOOK\n --webhook-url <url> Generic webhook URL (repeatable for multiple endpoints)\n --webhook-header <Key: Value> Custom request header (repeatable)\n --webhook-method <POST|PUT> HTTP method (default: POST)\n --webhook-hmac-secret <s> HMAC-SHA256 signing secret\n --webhook-hmac-header <name> Signature header name (default: X-Signature)\n --webhook-hmac-timestamp Include timestamp in HMAC signing\n Note: all --webhook-url entries share the same method/headers/signing options.\n\nHISTORY\n --history-file <path> Path to JSON history file (enables tracking)\n --max-history-runs <n> Max runs to keep in history per test (default: 10)\n\nEXIT CODES\n 0 Success\n 1 Schema validation failure\n 2 Canonical validation failure\n 3 Formatter/generation failure\n 4 Bad arguments / usage error\n`.trim();\n\ninterface CliArgs {\n subcommand: \"format\" | \"compare\" | \"list\" | \"validate\";\n inputFile?: string;\n baselineFile?: string;\n currentFile?: string;\n baselineMode: \"explicit\" | \"auto\";\n baselineDir?: string;\n stdin: boolean;\n formats: OutputFormat[];\n inputType: \"raw\" | \"canonical\" | \"ndjson\";\n outputDir: string;\n outputName: string;\n outputNameTimestamp: boolean;\n sortTestCases: \"id\" | \"source\" | \"none\";\n include: string[];\n exclude: string[];\n includeTags: string[];\n excludeTags: string[];\n synthesizeStories: boolean;\n htmlTitle: string;\n htmlTheme: string;\n htmlNoSyntaxHighlighting: boolean;\n htmlNoMermaid: boolean;\n htmlNoMarkdown: boolean;\n htmlPermalinkBaseUrl?: string;\n htmlTicketUrlTemplate?: string;\n htmlNoToc: boolean;\n htmlThemePicker: boolean;\n jsonSummary: boolean;\n emitCanonical?: string;\n slackWebhook?: string;\n teamsWebhook?: string;\n notify: NotifyCondition;\n reportUrl?: string;\n maxFailedTests: number;\n historyFile?: string;\n maxHistoryRuns: number;\n webhookUrls: string[];\n webhookHeaders: Record<string, string>;\n webhookMethod: \"POST\" | \"PUT\";\n webhookHmacSecret?: string;\n webhookHmacHeader: string;\n webhookHmacTimestamp: boolean;\n assetMode: \"none\" | \"copy\";\n allowMissingAssets: boolean;\n prSummary: boolean;\n prSummaryFile?: string;\n}\n\nfunction parseCliArgs(argv: string[]): CliArgs {\n // Strip node + script path\n const args = argv.slice(2);\n\n if (args.length === 0 || args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(HELP_TEXT);\n process.exit(EXIT_SUCCESS);\n }\n\n const subcommand = args[0];\n if (subcommand !== \"format\" && subcommand !== \"compare\" && subcommand !== \"list\" && subcommand !== \"validate\") {\n console.error(`Unknown subcommand: \"${subcommand}\". Use \"format\", \"compare\", \"list\", or \"validate\".`);\n process.exit(EXIT_USAGE);\n }\n\n // Parse remaining args with node:util parseArgs\n const { values, positionals } = parseArgs({\n args: args.slice(1),\n options: {\n format: { type: \"string\", default: \"html\" },\n baseline: { type: \"string\" },\n \"baseline-dir\": { type: \"string\" },\n \"input-type\": { type: \"string\", default: \"raw\" },\n \"output-dir\": { type: \"string\", default: \"reports\" },\n \"output-name\": { type: \"string\", default: \"test-results\" },\n \"output-name-timestamp\": { type: \"boolean\", default: false },\n \"sort-test-cases\": { type: \"string\", default: \"none\" },\n include: { type: \"string\" },\n exclude: { type: \"string\" },\n \"include-tags\": { type: \"string\" },\n \"exclude-tags\": { type: \"string\" },\n \"synthesize-stories\": { type: \"boolean\", default: true },\n \"no-synthesize-stories\": { type: \"boolean\", default: false },\n \"html-title\": { type: \"string\", default: \"Test Results\" },\n \"html-theme\": { type: \"string\", default: \"default\" },\n \"html-no-syntax-highlighting\": { type: \"boolean\", default: false },\n \"html-no-mermaid\": { type: \"boolean\", default: false },\n \"html-no-markdown\": { type: \"boolean\", default: false },\n \"html-permalink-base-url\": { type: \"string\" },\n \"html-ticket-url-template\": { type: \"string\" },\n \"html-no-toc\": { type: \"boolean\", default: false },\n \"html-theme-picker\": { type: \"boolean\", default: false },\n stdin: { type: \"boolean\", default: false },\n \"json-summary\": { type: \"boolean\", default: false },\n \"emit-canonical\": { type: \"string\" },\n \"slack-webhook\": { type: \"string\" },\n \"teams-webhook\": { type: \"string\" },\n notify: { type: \"string\", default: \"on-failure\" },\n \"report-url\": { type: \"string\" },\n \"max-failed-tests\": { type: \"string\" },\n \"history-file\": { type: \"string\" },\n \"max-history-runs\": { type: \"string\" },\n \"webhook-url\": { type: \"string\", multiple: true },\n \"webhook-header\": { type: \"string\", multiple: true },\n \"webhook-method\": { type: \"string\" },\n \"webhook-hmac-secret\": { type: \"string\" },\n \"webhook-hmac-header\": { type: \"string\" },\n \"webhook-hmac-timestamp\": { type: \"boolean\", default: false },\n \"asset-mode\": { type: \"string\", default: \"none\" },\n \"allow-missing-assets\": { type: \"boolean\", default: false },\n \"pr-summary\": { type: \"boolean\", default: false },\n \"pr-summary-file\": { type: \"string\" },\n help: { type: \"boolean\", default: false },\n },\n allowPositionals: true,\n strict: true,\n });\n\n if (values.help) {\n console.log(HELP_TEXT);\n process.exit(EXIT_SUCCESS);\n }\n\n const useStdin = values.stdin as boolean;\n const baselineValue = values.baseline as string | undefined;\n const baselineMode = baselineValue === \"auto\" ? \"auto\" : \"explicit\";\n const inputFile = subcommand === \"compare\" ? undefined : positionals[0];\n const baselineFile =\n subcommand === \"compare\"\n ? baselineMode === \"auto\"\n ? baselineValue && baselineValue !== \"auto\"\n ? baselineValue\n : positionals.length > 1\n ? positionals[0]\n : undefined\n : baselineValue && baselineValue !== \"auto\"\n ? baselineValue\n : positionals.length > 1\n ? positionals[0]\n : undefined\n : undefined;\n const currentFile =\n subcommand === \"compare\"\n ? positionals.length > 1\n ? positionals[1]\n : positionals[0]\n : undefined;\n\n if (subcommand === \"compare\") {\n if (useStdin) {\n console.error(\"Error: compare does not support --stdin. Pass baseline and current files.\");\n process.exit(EXIT_USAGE);\n }\n if (!currentFile) {\n console.error(\"Error: compare requires <current-file>, and either <baseline-file> or --baseline auto.\");\n process.exit(EXIT_USAGE);\n }\n if (baselineMode === \"explicit\" && !baselineFile) {\n console.error(\"Error: compare requires <baseline-file> and <current-file>, or use --baseline auto.\");\n process.exit(EXIT_USAGE);\n }\n } else if (!useStdin && !inputFile) {\n console.error(\"Error: No input file specified. Use a positional argument or --stdin.\");\n process.exit(EXIT_USAGE);\n }\n\n const inputType = values[\"input-type\"] as string;\n if (inputType !== \"raw\" && inputType !== \"canonical\" && inputType !== \"ndjson\") {\n console.error(`Error: --input-type must be \"raw\", \"canonical\", or \"ndjson\", got \"${inputType}\".`);\n process.exit(EXIT_USAGE);\n }\n\n // Parse comma-separated formats\n const validFormats = new Set([\"html\", \"markdown\", \"junit\", \"cucumber-json\", \"cucumber-messages\", \"cucumber-html\"]);\n const formatStr = values.format as string;\n const formats = formatStr.split(\",\").map((f) => f.trim()) as OutputFormat[];\n for (const f of formats) {\n if (!validFormats.has(f)) {\n console.error(`Error: Unknown format \"${f}\". Valid: html, markdown, junit, cucumber-json, cucumber-messages, cucumber-html.`);\n process.exit(EXIT_USAGE);\n }\n }\n\n // Validate --html-theme\n const htmlTheme = values[\"html-theme\"] as string;\n const validThemes = new Set([\"default\", \"corporate\", \"terminal\", \"minimal\", \"dashboard\", \"playful\"]);\n if (!validThemes.has(htmlTheme)) {\n console.error(`Error: Unknown theme \"${htmlTheme}\". Valid: ${[...validThemes].join(\", \")}.`);\n process.exit(EXIT_USAGE);\n }\n\n const noSynthesize = values[\"no-synthesize-stories\"] as boolean;\n\n const parseGlobs = (v: string | undefined): string[] =>\n v ? v.split(\",\").map((s) => s.trim()).filter(Boolean) : [];\n\n // Validate --notify\n const notifyValue = values.notify as string;\n const validNotifyConditions = new Set([\"always\", \"on-failure\", \"never\"]);\n if (!validNotifyConditions.has(notifyValue)) {\n console.error(`Error: --notify must be \"always\", \"on-failure\", or \"never\", got \"${notifyValue}\".`);\n process.exit(EXIT_USAGE);\n }\n\n // Parse --max-failed-tests\n const maxFailedTestsStr = values[\"max-failed-tests\"] as string | undefined;\n const maxFailedTests = maxFailedTestsStr ? parseInt(maxFailedTestsStr, 10) : 5;\n if (maxFailedTestsStr && (isNaN(maxFailedTests) || maxFailedTests < 0)) {\n console.error(`Error: --max-failed-tests must be a non-negative integer, got \"${maxFailedTestsStr}\".`);\n process.exit(EXIT_USAGE);\n }\n\n // Slack/Teams webhook: CLI flag (env fallback is handled inside sendNotifications)\n const slackWebhook = values[\"slack-webhook\"] as string | undefined;\n const teamsWebhook = values[\"teams-webhook\"] as string | undefined;\n\n // Parse --webhook-url (repeatable)\n const webhookUrls = (values[\"webhook-url\"] as string[] | undefined) ?? [];\n\n // Parse --webhook-header (repeatable) \"Key: Value\"\n const webhookHeaders: Record<string, string> = {};\n const rawHeaders = (values[\"webhook-header\"] as string[] | undefined) ?? [];\n for (const h of rawHeaders) {\n const colonIdx = h.indexOf(\":\");\n if (colonIdx <= 0) {\n console.error(`Warning: ignoring invalid --webhook-header \"${h}\" (expected \"Key: Value\")`);\n continue;\n }\n const key = h.slice(0, colonIdx).trim();\n const value = h.slice(colonIdx + 1).trim();\n if (!key) {\n console.error(`Warning: ignoring --webhook-header with empty key`);\n continue;\n }\n webhookHeaders[key] = value;\n }\n\n // Parse --webhook-method\n const webhookMethodRaw = values[\"webhook-method\"] as string | undefined;\n let webhookMethod: \"POST\" | \"PUT\" = \"POST\";\n if (webhookMethodRaw) {\n const upper = webhookMethodRaw.toUpperCase();\n if (upper !== \"POST\" && upper !== \"PUT\") {\n console.error(`Error: --webhook-method must be \"POST\" or \"PUT\", got \"${webhookMethodRaw}\".`);\n process.exit(EXIT_USAGE);\n }\n webhookMethod = upper as \"POST\" | \"PUT\";\n }\n\n // Parse --max-history-runs\n const maxHistoryRunsStr = values[\"max-history-runs\"] as string | undefined;\n const maxHistoryRuns = maxHistoryRunsStr ? parseInt(maxHistoryRunsStr, 10) : 10;\n if (maxHistoryRunsStr && (isNaN(maxHistoryRuns) || maxHistoryRuns < 1)) {\n console.error(`Error: --max-history-runs must be a positive integer, got \"${maxHistoryRunsStr}\".`);\n process.exit(EXIT_USAGE);\n }\n\n const sortTestCasesRaw = values[\"sort-test-cases\"] as string;\n const validSortModes = new Set([\"id\", \"source\", \"none\"]);\n if (!validSortModes.has(sortTestCasesRaw)) {\n console.error(`Error: --sort-test-cases must be id, source, or none, got \"${sortTestCasesRaw}\".`);\n process.exit(EXIT_USAGE);\n }\n\n const assetModeRaw = values[\"asset-mode\"] as string;\n const validAssetModes = new Set([\"none\", \"copy\"]);\n if (!validAssetModes.has(assetModeRaw)) {\n console.error(`Error: --asset-mode must be \"none\" or \"copy\", got \"${assetModeRaw}\".`);\n process.exit(EXIT_USAGE);\n }\n\n return {\n subcommand: subcommand as \"format\" | \"compare\" | \"list\" | \"validate\",\n inputFile,\n baselineFile,\n currentFile,\n baselineMode,\n baselineDir: values[\"baseline-dir\"] as string | undefined,\n stdin: useStdin,\n formats,\n inputType: inputType as \"raw\" | \"canonical\" | \"ndjson\",\n outputDir: values[\"output-dir\"] as string,\n outputName: values[\"output-name\"] as string,\n outputNameTimestamp: values[\"output-name-timestamp\"] as boolean,\n sortTestCases: sortTestCasesRaw as \"id\" | \"source\" | \"none\",\n include: parseGlobs(values.include as string | undefined),\n exclude: parseGlobs(values.exclude as string | undefined),\n includeTags: parseGlobs(values[\"include-tags\"] as string | undefined),\n excludeTags: parseGlobs(values[\"exclude-tags\"] as string | undefined),\n synthesizeStories: !noSynthesize,\n htmlTitle: values[\"html-title\"] as string,\n htmlTheme: values[\"html-theme\"] as string,\n htmlNoSyntaxHighlighting: values[\"html-no-syntax-highlighting\"] as boolean,\n htmlNoMermaid: values[\"html-no-mermaid\"] as boolean,\n htmlNoMarkdown: values[\"html-no-markdown\"] as boolean,\n htmlPermalinkBaseUrl: values[\"html-permalink-base-url\"] as string | undefined,\n htmlTicketUrlTemplate: values[\"html-ticket-url-template\"] as string | undefined,\n htmlNoToc: values[\"html-no-toc\"] as boolean,\n htmlThemePicker: values[\"html-theme-picker\"] as boolean,\n jsonSummary: values[\"json-summary\"] as boolean,\n emitCanonical: values[\"emit-canonical\"] as string | undefined,\n slackWebhook,\n teamsWebhook,\n notify: notifyValue as NotifyCondition,\n reportUrl: values[\"report-url\"] as string | undefined,\n maxFailedTests,\n historyFile: values[\"history-file\"] as string | undefined,\n maxHistoryRuns,\n webhookUrls,\n webhookHeaders,\n webhookMethod,\n webhookHmacSecret: values[\"webhook-hmac-secret\"] as string | undefined,\n webhookHmacHeader: (values[\"webhook-hmac-header\"] as string | undefined) ?? \"X-Signature\",\n webhookHmacTimestamp: values[\"webhook-hmac-timestamp\"] as boolean,\n assetMode: assetModeRaw as \"none\" | \"copy\",\n allowMissingAssets: values[\"allow-missing-assets\"] as boolean,\n prSummary: values[\"pr-summary\"] as boolean,\n prSummaryFile: values[\"pr-summary-file\"] as string | undefined,\n };\n}\n\n// ============================================================================\n// Input Reading\n// ============================================================================\n\nasync function readInput(args: CliArgs): Promise<string> {\n if (args.stdin) {\n return readStdin();\n }\n const filePath = path.resolve(args.inputFile!);\n if (!fs.existsSync(filePath)) {\n console.error(`Error: File not found: ${filePath}`);\n process.exit(EXIT_USAGE);\n }\n return fs.readFileSync(filePath, \"utf8\");\n}\n\nfunction readFileInput(filePath: string): string {\n const resolved = path.resolve(filePath);\n if (!fs.existsSync(resolved)) {\n console.error(`Error: File not found: ${resolved}`);\n process.exit(EXIT_USAGE);\n }\n return fs.readFileSync(resolved, \"utf8\");\n}\n\nfunction readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: string[] = [];\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => chunks.push(chunk as string));\n process.stdin.on(\"end\", () => resolve(chunks.join(\"\")));\n process.stdin.on(\"error\", reject);\n });\n}\n\n// ============================================================================\n// Validation Pipeline\n// ============================================================================\n\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: Invalid JSON — ${msg}`);\n process.exit(EXIT_USAGE);\n }\n}\n\nfunction tryParseJson(text: string): unknown | undefined {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeRunFromJsonData(data: unknown, args: CliArgs): {\n run: TestRunResult;\n droppedMissingStory: number;\n} {\n if (args.inputType === \"canonical\") {\n try {\n assertValidRun(data as TestRunResult);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Canonical validation failed:\\n${msg}`);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n\n return { run: data as TestRunResult, droppedMissingStory: 0 };\n }\n\n const obj = data as Record<string, unknown>;\n if (obj.schemaVersion !== 1) {\n console.error(`Unsupported schemaVersion ${obj.schemaVersion}. Supported: 1.`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n const schemaResult = validateRawRun(data);\n if (!schemaResult.valid) {\n console.error(\"Schema validation failed:\");\n for (const err of schemaResult.errors) {\n console.error(` ${err}`);\n }\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n let raw = data as RawRun;\n let droppedMissingStory = 0;\n\n if (args.synthesizeStories) {\n raw = synthesizeStories(raw);\n } else {\n const before = raw.testCases.length;\n const withStory = raw.testCases.filter((tc) => tc.story != null).length;\n droppedMissingStory = before - withStory;\n }\n\n const canonical = canonicalizeRun(raw);\n try {\n assertValidRun(canonical);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Canonical validation failed:\\n${msg}`);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n\n return { run: canonical, droppedMissingStory };\n}\n\nfunction normalizeRunFromText(text: string, args: CliArgs): {\n run: TestRunResult;\n droppedMissingStory: number;\n} {\n if (args.inputType === \"ndjson\") {\n try {\n return { run: parseNdjson(text), droppedMissingStory: 0 };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`NDJSON parse failed: ${msg}`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n }\n\n return normalizeRunFromJsonData(parseJson(text), args);\n}\n\nfunction applySelection(run: TestRunResult, args: CliArgs): TestRunResult {\n const testCases = selectTestCases(\n {\n testCases: run.testCases,\n include: args.include,\n exclude: args.exclude,\n includeTags: args.includeTags,\n excludeTags: args.excludeTags,\n sortTestCases: args.sortTestCases,\n },\n { logger: console }\n );\n\n return { ...run, testCases };\n}\n\nfunction tryNormalizeRunFromText(\n text: string,\n args: CliArgs\n): TestRunResult | undefined {\n if (args.inputType === \"ndjson\") {\n try {\n return parseNdjson(text);\n } catch {\n return undefined;\n }\n }\n\n const data = tryParseJson(text);\n if (data === undefined) return undefined;\n\n if (args.inputType === \"canonical\") {\n try {\n assertValidRun(data as TestRunResult);\n return data as TestRunResult;\n } catch {\n return undefined;\n }\n }\n\n const obj = data as Record<string, unknown>;\n if (obj.schemaVersion !== 1) return undefined;\n\n const schemaResult = validateRawRun(data);\n if (!schemaResult.valid) return undefined;\n\n let raw = data as RawRun;\n if (args.synthesizeStories) {\n raw = synthesizeStories(raw);\n }\n\n const canonical = canonicalizeRun(raw);\n try {\n assertValidRun(canonical);\n return canonical;\n } catch {\n return undefined;\n }\n}\n\nfunction listBaselineCandidates(currentFile: string, args: CliArgs): string[] {\n const baselineDir = path.resolve(args.baselineDir ?? path.dirname(currentFile));\n const currentResolved = path.resolve(currentFile);\n\n if (!fs.existsSync(baselineDir)) {\n console.error(`Error: baseline directory not found: ${baselineDir}`);\n process.exit(EXIT_USAGE);\n }\n\n const entries = fs.readdirSync(baselineDir, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isFile())\n .map((entry) => path.join(baselineDir, entry.name))\n .filter((candidate) => path.resolve(candidate) !== currentResolved)\n .filter((candidate) =>\n args.inputType === \"ndjson\" ? candidate.endsWith(\".ndjson\") : candidate.endsWith(\".json\")\n );\n}\n\nfunction resolveBaselineAuto(\n currentFile: string,\n currentRun: TestRunResult,\n args: CliArgs\n): string {\n const candidates = listBaselineCandidates(currentFile, args);\n const comparable: Array<{ file: string; run: TestRunResult }> = [];\n\n for (const candidate of candidates) {\n const run = tryNormalizeRunFromText(fs.readFileSync(candidate, \"utf8\"), args);\n if (run) {\n comparable.push({ file: candidate, run });\n }\n }\n\n if (comparable.length === 0) {\n console.error(\n `Error: no compatible baseline files found in ${path.resolve(args.baselineDir ?? path.dirname(currentFile))}.`\n );\n process.exit(EXIT_USAGE);\n }\n\n const picked = pickAutoBaseline(currentRun, comparable);\n if (!picked) {\n console.error(\"Error: unable to choose an automatic baseline.\");\n process.exit(EXIT_USAGE);\n }\n return picked.file;\n}\n\n// ============================================================================\n// Main\n// ============================================================================\n\nasync function main() {\n const args = parseCliArgs(process.argv);\n const startMs = Date.now();\n\n if (args.subcommand === \"compare\") {\n const currentText = readFileInput(args.currentFile!);\n const current = applySelection(normalizeRunFromText(currentText, args).run, args);\n const baselineFile =\n args.baselineMode === \"auto\"\n ? resolveBaselineAuto(args.currentFile!, current, args)\n : args.baselineFile!;\n const baselineText = readFileInput(baselineFile);\n const baseline = applySelection(normalizeRunFromText(baselineText, args).run, args);\n\n try {\n const result = await generateCompareReports(baseline, current, baselineFile, args);\n printCompareResult(result, args, startMs);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Comparison failed: ${msg}`);\n process.exit(EXIT_GENERATION);\n }\n }\n\n if (args.subcommand === \"list\") {\n const text = await readInput(args);\n const run = applySelection(normalizeRunFromText(text, args).run, args);\n\n // Use --json-summary to get JSON output for list command\n const outputFormat: \"text\" | \"json\" = args.jsonSummary ? \"json\" : \"text\";\n const output = listScenarios({ testCases: run.testCases, format: outputFormat }, {});\n console.log(output);\n process.exit(EXIT_SUCCESS);\n }\n\n // Read input\n const text = await readInput(args);\n\n // === NDJSON input pipeline ===\n if (args.inputType === \"ndjson\") {\n if (args.subcommand === \"validate\") {\n // Validate each line is valid JSON with exactly one envelope field\n const lines = text.trim().split(\"\\n\").filter(Boolean);\n const validKeys = new Set([\n \"meta\", \"source\", \"gherkinDocument\", \"pickle\",\n \"testRunStarted\", \"testCase\", \"testCaseStarted\",\n \"testStepStarted\", \"testStepFinished\", \"testCaseFinished\",\n \"testRunFinished\", \"attachment\",\n ]);\n for (let i = 0; i < lines.length; i++) {\n try {\n const obj = JSON.parse(lines[i]);\n const keys = Object.keys(obj);\n if (keys.length !== 1 || !validKeys.has(keys[0])) {\n console.error(`Line ${i + 1}: invalid envelope (keys: ${keys.join(\", \")})`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n } catch {\n console.error(`Line ${i + 1}: invalid JSON`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n }\n console.log(`Valid NDJSON (${lines.length} envelopes).`);\n process.exit(EXIT_SUCCESS);\n }\n\n // Parse NDJSON → TestRunResult\n let run;\n try {\n run = parseNdjson(text);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`NDJSON parse failed: ${msg}`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n // Emit canonical if requested\n if (args.emitCanonical) {\n const outPath = path.resolve(args.emitCanonical);\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\n fs.writeFileSync(outPath, JSON.stringify(run, null, 2), \"utf8\");\n }\n\n try {\n const result = await generateReports(run, args);\n await dispatchNotifications(run, args);\n runHistoryPipeline(run, args);\n printResult(result, args, startMs);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Generation failed: ${msg}`);\n process.exit(EXIT_GENERATION);\n }\n }\n\n const data = parseJson(text);\n\n if (args.subcommand === \"validate\") {\n // Validate-only mode\n if (args.inputType === \"canonical\") {\n try {\n assertValidRun(data as TestRunResult);\n console.log(\"Valid canonical TestRunResult.\");\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(msg);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n }\n\n // Check schemaVersion\n const obj = data as Record<string, unknown>;\n if (obj.schemaVersion !== 1) {\n console.error(\n `Unsupported schemaVersion ${obj.schemaVersion}. Supported: 1.`\n );\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n const result = validateRawRun(data);\n if (!result.valid) {\n console.error(\"Schema validation failed:\");\n for (const err of result.errors) {\n console.error(` ${err}`);\n }\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n console.log(\"Valid RawRun (schemaVersion 1).\");\n process.exit(EXIT_SUCCESS);\n }\n\n // === format subcommand ===\n\n if (args.inputType === \"canonical\") {\n // Skip schema validation, go straight to canonical validation\n try {\n assertValidRun(data as TestRunResult);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Canonical validation failed:\\n${msg}`);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n\n const run = data as TestRunResult;\n\n // Emit canonical if requested\n if (args.emitCanonical) {\n const outPath = path.resolve(args.emitCanonical);\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\n fs.writeFileSync(outPath, JSON.stringify(run, null, 2), \"utf8\");\n }\n\n try {\n const result = await generateReports(run, args);\n await dispatchNotifications(run, args);\n runHistoryPipeline(run, args);\n printResult(result, args, startMs);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Generation failed: ${msg}`);\n process.exit(EXIT_GENERATION);\n }\n }\n\n // Raw input pipeline\n // 1. Check schemaVersion\n const obj = data as Record<string, unknown>;\n if (obj.schemaVersion !== 1) {\n console.error(\n `Unsupported schemaVersion ${obj.schemaVersion}. Supported: 1.`\n );\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n // 2. Ajv schema validation\n const schemaResult = validateRawRun(data);\n if (!schemaResult.valid) {\n console.error(\"Schema validation failed:\");\n for (const err of schemaResult.errors) {\n console.error(` ${err}`);\n }\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n // 3. Synthesize stories (optional)\n let raw = data as RawRun;\n let droppedMissingStory = 0;\n\n if (args.synthesizeStories) {\n raw = synthesizeStories(raw);\n } else {\n // Count and warn about dropped test cases\n const before = raw.testCases.length;\n const withStory = raw.testCases.filter(\n (tc) => tc.story != null\n ).length;\n droppedMissingStory = before - withStory;\n if (droppedMissingStory > 0) {\n console.error(\n `Dropped ${droppedMissingStory} test cases missing story (use --synthesize-stories to include)`\n );\n }\n }\n\n // 4. Canonicalize\n const canonical = canonicalizeRun(raw);\n\n // 5. Assert canonical validity\n try {\n assertValidRun(canonical);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Canonical validation failed:\\n${msg}`);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n\n // Emit canonical if requested\n if (args.emitCanonical) {\n const outPath = path.resolve(args.emitCanonical);\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\n fs.writeFileSync(outPath, JSON.stringify(canonical, null, 2), \"utf8\");\n }\n\n // 6. Generate reports\n try {\n const result = await generateReports(canonical, args, droppedMissingStory);\n await dispatchNotifications(canonical, args);\n runHistoryPipeline(canonical, args);\n printResult(result, args, startMs, droppedMissingStory);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Generation failed: ${msg}`);\n process.exit(EXIT_GENERATION);\n }\n}\n\n// ============================================================================\n// Notifications\n// ============================================================================\n\nasync function dispatchNotifications(run: TestRunResult, args: CliArgs): Promise<void> {\n // Build generic webhook configs from CLI flags\n const webhooks: GenericWebhookNotifierOptions[] = args.webhookUrls.map((url) => {\n const opts: GenericWebhookNotifierOptions = { url };\n if (Object.keys(args.webhookHeaders).length > 0) {\n opts.headers = { ...args.webhookHeaders };\n }\n if (args.webhookMethod !== \"POST\") {\n opts.method = args.webhookMethod;\n }\n if (args.webhookHmacSecret) {\n const signer: WebhookSignerHmac = {\n type: \"hmac-sha256\",\n secret: args.webhookHmacSecret,\n header: args.webhookHmacHeader,\n };\n if (args.webhookHmacTimestamp) {\n signer.includeTimestamp = true;\n }\n opts.signer = signer;\n }\n return opts;\n });\n\n await sendNotifications(\n {\n run,\n notification: {\n slackWebhookUrl: args.slackWebhook,\n teamsWebhookUrl: args.teamsWebhook,\n condition: args.notify,\n reportUrl: args.reportUrl,\n maxFailedTests: args.maxFailedTests,\n webhooks: webhooks.length > 0 ? webhooks : undefined,\n },\n },\n {\n fetch: globalThis.fetch,\n logger: console,\n toCIInfo,\n },\n );\n}\n\n// ============================================================================\n// History Pipeline\n// ============================================================================\n\nfunction runHistoryPipeline(run: TestRunResult, args: CliArgs): void {\n if (!args.historyFile) return;\n\n const historyPath = path.resolve(args.historyFile);\n\n // Load existing history\n const store = loadHistory(\n { filePath: historyPath },\n {\n readFile: (p: string) => {\n try {\n return fs.readFileSync(p, \"utf8\");\n } catch {\n return undefined;\n }\n },\n logger: console,\n },\n );\n\n // Update history with current run\n const updated = updateHistory({\n store,\n run,\n maxRuns: args.maxHistoryRuns,\n });\n\n // Save updated history\n const dir = path.dirname(historyPath);\n fs.mkdirSync(dir, { recursive: true });\n saveHistory(\n { filePath: historyPath, store: updated },\n { writeFile: (p: string, content: string) => fs.writeFileSync(p, content, \"utf8\") },\n );\n\n // Compute metrics (log summary for CLI users)\n let metricsCount = 0;\n for (const testId of Object.keys(updated.tests)) {\n const history = updated.tests[testId];\n if (history.entries.length >= 3) {\n metricsCount++;\n }\n }\n if (metricsCount > 0) {\n console.error(`History updated: ${historyPath} (${Object.keys(updated.tests).length} tests tracked)`);\n }\n}\n\n// ============================================================================\n// Report Generation\n// ============================================================================\n\ninterface CliResult {\n files: string[];\n counts: { passed: number; failed: number; skipped: number; pending: number };\n}\n\ninterface CompareCliResult {\n files: string[];\n baselineFile: string;\n summary: {\n added: number;\n removed: number;\n changed: number;\n regressed: number;\n fixed: number;\n unchanged: number;\n };\n prSummary?: string;\n}\n\nasync function generateReports(\n run: TestRunResult,\n args: CliArgs,\n _droppedMissingStory = 0\n): Promise<CliResult> {\n const generator = new ReportGenerator({\n include: args.include,\n exclude: args.exclude,\n includeTags: args.includeTags,\n excludeTags: args.excludeTags,\n formats: args.formats,\n outputDir: args.outputDir,\n outputName: args.outputName,\n outputNameTimestamp: args.outputNameTimestamp,\n sortTestCases: args.sortTestCases,\n html: {\n title: args.htmlTitle,\n theme: args.htmlTheme,\n syntaxHighlighting: !args.htmlNoSyntaxHighlighting,\n mermaidEnabled: !args.htmlNoMermaid,\n markdownEnabled: !args.htmlNoMarkdown,\n permalinkBaseUrl: args.htmlPermalinkBaseUrl,\n ticketUrlTemplate: args.htmlTicketUrlTemplate,\n tocEnabled: !args.htmlNoToc,\n themePickerEnabled: args.htmlThemePicker,\n },\n assetMode: args.assetMode,\n allowMissingAssets: args.allowMissingAssets,\n });\n\n const resultMap = await generator.generate(run);\n\n // Collect all generated file paths\n const files: string[] = [];\n for (const paths of resultMap.values()) {\n files.push(...paths);\n }\n\n // Count statuses\n const counts = { passed: 0, failed: 0, skipped: 0, pending: 0 };\n for (const tc of run.testCases) {\n const status = tc.status as keyof typeof counts;\n if (status in counts) {\n counts[status]++;\n }\n }\n\n return { files, counts };\n}\n\nasync function generateCompareReports(\n baseline: TestRunResult,\n current: TestRunResult,\n baselineFile: string,\n args: CliArgs\n): Promise<CompareCliResult> {\n const unsupportedCompareFormats = args.formats.filter(\n (format) => format !== \"html\" && format !== \"markdown\"\n );\n if (unsupportedCompareFormats.length > 0) {\n throw new Error(\n `compare supports only \"html\" and \"markdown\" formats (unsupported: ${unsupportedCompareFormats.join(\", \")})`\n );\n }\n const compareFormats = args.formats as (\"html\" | \"markdown\")[];\n\n const result = await generateRunComparison({\n baseline,\n current,\n formats: compareFormats,\n outputDir: args.outputDir,\n outputName: args.outputName,\n title: args.htmlTitle,\n });\n\n return {\n files: result.files,\n baselineFile,\n summary: result.diff.summary,\n prSummary: args.prSummary || args.prSummaryFile ? createPrCommentSummary(result.diff) : undefined,\n };\n}\n\nfunction printResult(\n result: CliResult,\n args: CliArgs,\n startMs: number,\n droppedMissingStory = 0\n) {\n const durationMs = Date.now() - startMs;\n\n if (args.jsonSummary) {\n const summary: Record<string, unknown> = {\n files: result.files,\n counts: result.counts,\n durationMs,\n };\n if (droppedMissingStory > 0) {\n summary.droppedMissingStory = droppedMissingStory;\n }\n console.log(JSON.stringify(summary, null, 2));\n } else {\n for (const f of result.files) {\n console.log(f);\n }\n }\n}\n\nfunction printCompareResult(\n result: CompareCliResult,\n args: CliArgs,\n startMs: number\n) {\n const durationMs = Date.now() - startMs;\n\n if (result.prSummary && args.prSummaryFile) {\n const outputPath = path.resolve(args.prSummaryFile);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, result.prSummary, \"utf8\");\n }\n\n if (args.jsonSummary) {\n console.log(\n JSON.stringify(\n {\n files: result.files,\n baselineFile: result.baselineFile,\n diff: result.summary,\n prSummary: result.prSummary,\n durationMs,\n },\n null,\n 2\n )\n );\n return;\n }\n\n for (const f of result.files) {\n console.log(f);\n }\n console.log(`baseline: ${result.baselineFile}`);\n if (result.prSummary && args.prSummary) {\n console.log(\"\");\n console.log(result.prSummary);\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(EXIT_USAGE);\n});\n","/**\n * JSON Schema validation for RawRun using Ajv.\n *\n * Validates raw JSON input against the raw-run.schema.json schema\n * before it enters the ACL pipeline.\n */\n\nimport Ajv from \"ajv/dist/2020.js\";\nimport schema from \"../../schemas/raw-run.schema.json\" with { type: \"json\" };\n\n/** Validation result with JSON-path-based error messages */\nexport interface SchemaValidationResult {\n valid: boolean;\n errors: string[];\n}\n\n// Compile once, reuse across calls\nconst ajv = new Ajv({ allErrors: true });\nconst validate = ajv.compile(schema);\n\n/**\n * Validate raw JSON data against the RawRun schema.\n *\n * Returns JSON-path-based error messages for easy debugging\n * from any language.\n */\nexport function validateRawRun(data: unknown): SchemaValidationResult {\n const valid = validate(data);\n\n if (valid) {\n return { valid: true, errors: [] };\n }\n\n const errors = (validate.errors ?? []).map((err: { instancePath?: string; message?: string; keyword?: string; params?: Record<string, unknown> }) => {\n const path = err.instancePath || \"/\";\n const message = err.message ?? \"unknown error\";\n\n if (err.keyword === \"additionalProperties\") {\n const extra = (err.params as { additionalProperty?: string })\n .additionalProperty;\n return `${path}: ${message} — '${extra}'`;\n }\n\n if (err.keyword === \"enum\") {\n const allowed = (err.params as { allowedValues?: unknown[] })\n .allowedValues;\n return `${path}: ${message} — allowed: ${JSON.stringify(allowed)}`;\n }\n\n return `${path}: ${message}`;\n });\n\n return { valid: false, errors };\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://executable-stories.dev/schemas/raw-run.schema.json\",\n \"title\": \"RawRun\",\n \"description\": \"Raw test run data from any test framework. Feed this to the executable-stories CLI to generate HTML, Markdown, JUnit XML, and Cucumber JSON reports.\",\n \"type\": \"object\",\n \"$ref\": \"#/$defs/RawRun\",\n \"$defs\": {\n \"RawRun\": {\n \"type\": \"object\",\n \"description\": \"Top-level raw run containing all test cases and run metadata.\",\n \"properties\": {\n \"schemaVersion\": {\n \"type\": \"integer\",\n \"const\": 1,\n \"description\": \"Schema version for forward compatibility. Must be 1.\"\n },\n \"testCases\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/RawTestCase\" },\n \"description\": \"All test cases from the run.\"\n },\n \"startedAtMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Run start time as Unix epoch milliseconds.\"\n },\n \"finishedAtMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Run finish time as Unix epoch milliseconds.\"\n },\n \"projectRoot\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"Absolute path to the project root directory. Used for resolving relative attachment paths.\"\n },\n \"packageVersion\": {\n \"type\": \"string\",\n \"description\": \"Version of the package under test.\"\n },\n \"gitSha\": {\n \"type\": \"string\",\n \"description\": \"Git commit SHA at the time of the run.\"\n },\n \"ci\": {\n \"$ref\": \"#/$defs/RawCIInfo\"\n },\n \"meta\": {\n \"type\": \"object\",\n \"description\": \"Arbitrary runner-specific metadata. Escape hatch for data not covered by the schema.\"\n }\n },\n \"required\": [\"schemaVersion\", \"testCases\", \"projectRoot\"],\n \"additionalProperties\": false\n },\n \"RawTestCase\": {\n \"type\": \"object\",\n \"description\": \"A single test case result. Only 'status' is required — everything else is optional to support plain unit tests without BDD metadata.\",\n \"properties\": {\n \"externalId\": {\n \"type\": \"string\",\n \"description\": \"Framework's native test ID (e.g., Jest test path, Playwright test ID).\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Human-readable test title/name.\"\n },\n \"titlePath\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Full title path from describe/suite blocks down to the test name.\"\n },\n \"story\": {\n \"$ref\": \"#/$defs/StoryMeta\"\n },\n \"sourceFile\": {\n \"type\": \"string\",\n \"description\": \"Path to the source file containing this test (relative to projectRoot or absolute).\"\n },\n \"sourceLine\": {\n \"type\": \"integer\",\n \"minimum\": 1,\n \"description\": \"Line number in the source file (1-based).\"\n },\n \"status\": {\n \"$ref\": \"#/$defs/RawStatus\"\n },\n \"durationMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Test duration in milliseconds.\"\n },\n \"error\": {\n \"type\": \"object\",\n \"description\": \"Error information if the test failed.\",\n \"properties\": {\n \"message\": {\n \"type\": \"string\",\n \"description\": \"Error message.\"\n },\n \"stack\": {\n \"type\": \"string\",\n \"description\": \"Error stack trace.\"\n }\n },\n \"additionalProperties\": false\n },\n \"stepEvents\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/RawStepEvent\" },\n \"description\": \"Step-level execution events from the framework (if available).\"\n },\n \"attachments\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/RawAttachment\" },\n \"description\": \"Attachments such as screenshots, logs, or other artifacts.\"\n },\n \"meta\": {\n \"type\": \"object\",\n \"description\": \"Arbitrary framework-specific metadata. Escape hatch for data not covered by the schema.\"\n },\n \"retry\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"description\": \"Retry attempt number (0-based). 0 means first attempt.\"\n },\n \"retries\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"description\": \"Total number of retries configured for this test.\"\n },\n \"projectName\": {\n \"type\": \"string\",\n \"description\": \"Project name (e.g., Playwright project like 'chromium', 'firefox').\"\n }\n },\n \"required\": [\"status\"],\n \"additionalProperties\": false\n },\n \"RawStatus\": {\n \"type\": \"string\",\n \"enum\": [\"pass\", \"fail\", \"skip\", \"todo\", \"pending\", \"timeout\", \"interrupted\", \"unknown\"],\n \"description\": \"Permissive test status from any framework. Mapped to canonical status (passed/failed/skipped/pending) during canonicalization.\"\n },\n \"StoryMeta\": {\n \"type\": \"object\",\n \"description\": \"BDD story metadata for a test case. Contains the scenario title, steps, tags, and documentation.\",\n \"properties\": {\n \"scenario\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"The scenario title (typically the test name).\"\n },\n \"steps\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/StoryStep\" },\n \"description\": \"BDD steps in this scenario. Can be empty or omitted for progressive enrichment.\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Tags for filtering and categorization (e.g., ['smoke', 'auth']).\"\n },\n \"tickets\": {\n \"type\": \"array\",\n \"items\": {\n \"oneOf\": [\n { \"type\": \"string\" },\n {\n \"type\": \"object\",\n \"properties\": {\n \"id\": { \"type\": \"string\" },\n \"url\": { \"type\": \"string\" }\n },\n \"required\": [\"id\"],\n \"additionalProperties\": false\n }\n ]\n },\n \"description\": \"Ticket/issue references. Each item is either a string ID or an object with id and optional url.\"\n },\n \"meta\": {\n \"type\": \"object\",\n \"description\": \"User-defined metadata for this story.\"\n },\n \"suitePath\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Parent describe/suite names for hierarchical grouping.\"\n },\n \"docs\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/DocEntry\" },\n \"description\": \"Story-level documentation entries (before any steps).\"\n },\n \"sourceOrder\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"description\": \"Order in which the story was defined in source (for stable sorting).\"\n }\n },\n \"required\": [\"scenario\"],\n \"additionalProperties\": false\n },\n \"StoryStep\": {\n \"type\": \"object\",\n \"description\": \"A single BDD step in a scenario.\",\n \"properties\": {\n \"keyword\": {\n \"$ref\": \"#/$defs/StepKeyword\"\n },\n \"text\": {\n \"type\": \"string\",\n \"description\": \"The step description text.\"\n },\n \"mode\": {\n \"$ref\": \"#/$defs/StepMode\"\n },\n \"docs\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/DocEntry\" },\n \"description\": \"Rich documentation entries attached to this step.\"\n },\n \"id\": {\n \"type\": \"string\",\n \"description\": \"Unique step identifier within the scenario (e.g., 'step-0').\"\n },\n \"wrapped\": {\n \"type\": \"boolean\",\n \"description\": \"Whether this step wraps a function call (Fn/Expect pattern).\"\n },\n \"durationMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Step-level duration in milliseconds (from startTimer/endTimer).\"\n }\n },\n \"required\": [\"keyword\", \"text\"],\n \"additionalProperties\": false\n },\n \"StepKeyword\": {\n \"type\": \"string\",\n \"description\": \"BDD step keyword. Case-insensitive — 'given' and 'Given' are both accepted. Normalized to title case during processing.\",\n \"pattern\": \"^[Gg]iven$|^[Ww]hen$|^[Tt]hen$|^[Aa]nd$|^[Bb]ut$\"\n },\n \"StepMode\": {\n \"type\": \"string\",\n \"enum\": [\"normal\", \"skip\", \"only\", \"todo\", \"fails\", \"concurrent\"],\n \"description\": \"Step execution mode for documentation rendering.\"\n },\n \"DocPhase\": {\n \"type\": \"string\",\n \"enum\": [\"static\", \"runtime\"],\n \"description\": \"When the doc entry was added — 'static' at definition time, 'runtime' during execution.\"\n },\n \"DocEntry\": {\n \"description\": \"A documentation entry attached to a story or step. Discriminated union on the 'kind' field.\",\n \"oneOf\": [\n {\n \"type\": \"object\",\n \"description\": \"Free-text note.\",\n \"properties\": {\n \"kind\": { \"const\": \"note\" },\n \"text\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"text\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Tag(s) for categorization.\",\n \"properties\": {\n \"kind\": { \"const\": \"tag\" },\n \"names\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"names\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Key-value pair.\",\n \"properties\": {\n \"kind\": { \"const\": \"kv\" },\n \"label\": { \"type\": \"string\" },\n \"value\": {},\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"label\", \"value\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Code block with optional language.\",\n \"properties\": {\n \"kind\": { \"const\": \"code\" },\n \"label\": { \"type\": \"string\" },\n \"content\": { \"type\": \"string\" },\n \"lang\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"label\", \"content\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Markdown table.\",\n \"properties\": {\n \"kind\": { \"const\": \"table\" },\n \"label\": { \"type\": \"string\" },\n \"columns\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n }\n },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"label\", \"columns\", \"rows\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Hyperlink.\",\n \"properties\": {\n \"kind\": { \"const\": \"link\" },\n \"label\": { \"type\": \"string\" },\n \"url\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"label\", \"url\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Titled section with markdown content.\",\n \"properties\": {\n \"kind\": { \"const\": \"section\" },\n \"title\": { \"type\": \"string\" },\n \"markdown\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"title\", \"markdown\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Mermaid diagram.\",\n \"properties\": {\n \"kind\": { \"const\": \"mermaid\" },\n \"code\": { \"type\": \"string\" },\n \"title\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"code\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Screenshot reference.\",\n \"properties\": {\n \"kind\": { \"const\": \"screenshot\" },\n \"path\": { \"type\": \"string\" },\n \"alt\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"path\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Custom documentation entry with arbitrary data.\",\n \"properties\": {\n \"kind\": { \"const\": \"custom\" },\n \"type\": { \"type\": \"string\" },\n \"data\": {},\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"type\", \"data\", \"phase\"],\n \"additionalProperties\": false\n }\n ]\n },\n \"RawAttachment\": {\n \"type\": \"object\",\n \"description\": \"A test attachment (screenshot, log, artifact). In the MVP schema, only path-based attachments are supported (no inline base64).\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Human-readable name for the attachment.\"\n },\n \"mediaType\": {\n \"type\": \"string\",\n \"description\": \"MIME type (e.g., 'image/png', 'text/plain', 'application/json').\"\n },\n \"path\": {\n \"type\": \"string\",\n \"description\": \"File path (relative to projectRoot or absolute).\"\n }\n },\n \"required\": [\"name\", \"mediaType\", \"path\"],\n \"additionalProperties\": false\n },\n \"RawStepEvent\": {\n \"type\": \"object\",\n \"description\": \"Step-level execution event from the framework. Optional — most frameworks don't provide step-level data.\",\n \"properties\": {\n \"index\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"description\": \"Step index (0-based).\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Step title/description.\"\n },\n \"status\": {\n \"$ref\": \"#/$defs/RawStatus\"\n },\n \"durationMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Step duration in milliseconds.\"\n },\n \"errorMessage\": {\n \"type\": \"string\",\n \"description\": \"Error message if the step failed.\"\n }\n },\n \"additionalProperties\": false\n },\n \"RawCIInfo\": {\n \"type\": \"object\",\n \"description\": \"CI environment information.\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"CI provider name (e.g., 'GitHub Actions', 'Jenkins', 'CircleCI').\"\n },\n \"url\": {\n \"type\": \"string\",\n \"description\": \"URL to the CI build/run.\"\n },\n \"buildNumber\": {\n \"type\": \"string\",\n \"description\": \"CI build number or run ID.\"\n }\n },\n \"required\": [\"name\"],\n \"additionalProperties\": false\n }\n }\n}\n","/**\n * Story synthesis for plain test results.\n *\n * Fills in missing story metadata so that test cases without BDD steps\n * are not discarded by the ACL's `.filter((tc) => tc.story != null)`.\n */\n\nimport type { StepKeyword, StoryStep } from \"../types/story\";\nimport type { RawRun, RawTestCase } from \"../types/raw\";\n\n/** Mapping from lowercase keyword to canonical title case */\nconst KEYWORD_MAP: Record<string, StepKeyword> = {\n given: \"Given\",\n when: \"When\",\n then: \"Then\",\n and: \"And\",\n but: \"But\",\n};\n\n/**\n * Normalize a step keyword to title case.\n * \"given\" → \"Given\", \"THEN\" is not a valid keyword and will pass through.\n */\nfunction normalizeKeyword(keyword: string): StepKeyword {\n return KEYWORD_MAP[keyword.toLowerCase()] ?? (keyword as StepKeyword);\n}\n\n/**\n * Normalize all step keywords in a StoryStep array to title case.\n */\nfunction normalizeStepKeywords(steps: StoryStep[]): StoryStep[] {\n return steps.map((step) => ({\n ...step,\n keyword: normalizeKeyword(step.keyword),\n }));\n}\n\n/**\n * Derive a scenario name from a raw test case.\n */\nfunction deriveScenarioName(tc: RawTestCase): string {\n if (tc.title) return tc.title;\n if (tc.titlePath && tc.titlePath.length > 0) {\n return tc.titlePath[tc.titlePath.length - 1];\n }\n return \"Untitled\";\n}\n\n/**\n * Synthesize story metadata for test cases that are missing it.\n *\n * Rules:\n * - If `story` is missing entirely:\n * - `scenario` = title (or last element of titlePath, or \"Untitled\")\n * - `steps` = `[{ keyword: \"Then\", text: scenario }]`\n * - If `story` is present but `steps` is empty/missing:\n * - `steps` = `[{ keyword: \"Then\", text: story.scenario }]`\n * - Normalize keyword casing in all steps: \"given\" → \"Given\", etc.\n *\n * This is a pure function that returns a new RawRun.\n */\nexport function synthesizeStories(raw: RawRun): RawRun {\n return {\n ...raw,\n testCases: raw.testCases.map(synthesizeTestCase),\n };\n}\n\nfunction synthesizeTestCase(tc: RawTestCase): RawTestCase {\n if (tc.story == null) {\n // No story at all — synthesize from test title\n const scenario = deriveScenarioName(tc);\n return {\n ...tc,\n story: {\n scenario,\n steps: [{ keyword: \"Then\", text: scenario }],\n },\n };\n }\n\n // Story exists — check if steps are missing/empty\n const steps = tc.story.steps;\n if (!steps || steps.length === 0) {\n return {\n ...tc,\n story: {\n ...tc.story,\n steps: [{ keyword: \"Then\", text: tc.story.scenario }],\n },\n };\n }\n\n // Steps exist — just normalize keyword casing\n return {\n ...tc,\n story: {\n ...tc.story,\n steps: normalizeStepKeywords(steps),\n },\n };\n}\n","/**\n * Status mapping from raw framework statuses to canonical TestStatus.\n */\n\nimport type { RawStatus } from \"../../types/raw\";\nimport type { TestStatus } from \"../../types/test-result\";\n\n/** Map raw status to canonical TestStatus */\nconst STATUS_MAP: Record<RawStatus, TestStatus> = {\n pass: \"passed\",\n fail: \"failed\",\n skip: \"skipped\",\n pending: \"pending\",\n todo: \"pending\", // Map todo → pending\n timeout: \"failed\", // Map timeout → failed\n interrupted: \"failed\", // Map interrupted → failed\n unknown: \"skipped\", // Safest default\n};\n\n/**\n * Convert a raw status to canonical TestStatus.\n *\n * @param raw - The raw status from a framework\n * @returns The canonical TestStatus\n */\nexport function normalizeStatus(raw: RawStatus): TestStatus {\n return STATUS_MAP[raw] ?? \"skipped\";\n}\n\n/**\n * Convert a string to RawStatus, with fallback to \"unknown\".\n *\n * @param status - Any status string\n * @returns A valid RawStatus\n */\nexport function parseRawStatus(status: string | undefined): RawStatus {\n if (!status) return \"unknown\";\n const lower = status.toLowerCase();\n\n // Direct matches\n if (lower === \"pass\" || lower === \"passed\") return \"pass\";\n if (lower === \"fail\" || lower === \"failed\") return \"fail\";\n if (lower === \"skip\" || lower === \"skipped\") return \"skip\";\n if (lower === \"pending\") return \"pending\";\n if (lower === \"todo\") return \"todo\";\n if (lower === \"timeout\" || lower === \"timedout\" || lower === \"timed_out\") return \"timeout\";\n if (lower === \"interrupted\") return \"interrupted\";\n\n return \"unknown\";\n}\n","/**\n * ID generation and slug helpers for deterministic, Cucumber-compatible IDs.\n */\n\nimport { createHash } from \"node:crypto\";\n\n/**\n * Generate a deterministic test case ID from source file and scenario name.\n *\n * @param sourceFile - The source file path\n * @param scenario - The scenario name\n * @returns A 12-character hex ID\n */\nexport function generateTestCaseId(sourceFile: string, scenario: string): string {\n const input = `${sourceFile}::${scenario}`;\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 12);\n}\n\n/**\n * Generate a deterministic run ID from timestamp and project root.\n *\n * @param startedAtMs - Run start timestamp\n * @param projectRoot - Project root directory\n * @returns A 16-character hex ID\n */\nexport function generateRunId(startedAtMs: number, projectRoot: string): string {\n const input = `${startedAtMs}::${projectRoot}`;\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 16);\n}\n\n/**\n * Slugify a string for Cucumber JSON IDs.\n *\n * Converts to lowercase, replaces path separators/spaces with hyphens,\n * removes other special chars, and trims leading/trailing hyphens.\n *\n * @param text - The text to slugify\n * @returns A URL-safe slug\n */\nexport function slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[/\\\\]+/g, \"-\") // Convert path separators to hyphens\n .replace(/[^\\w\\s-]/g, \"\") // Remove other special characters\n .replace(/[\\s_]+/g, \"-\") // Replace spaces and underscores with hyphens\n .replace(/-+/g, \"-\") // Remove consecutive hyphens\n .replace(/^-+|-+$/g, \"\"); // Trim leading/trailing hyphens\n}\n\n/**\n * Generate a Cucumber-compatible feature ID from file path.\n *\n * Uses the full path (without extension) to ensure uniqueness for files\n * with the same basename in different directories.\n *\n * @param uri - The feature file URI/path\n * @returns A slugified feature ID\n */\nexport function generateFeatureId(uri: string): string {\n // Use full path without extension for uniqueness\n const pathWithoutExt = uri.replace(/\\.[^.]+$/, \"\");\n return slugify(pathWithoutExt);\n}\n\n/**\n * Generate a Cucumber-compatible scenario ID.\n *\n * Format: feature-id;scenario-name\n *\n * @param featureId - The feature ID\n * @param scenarioName - The scenario name\n * @returns A Cucumber-compatible scenario ID\n */\nexport function generateScenarioId(featureId: string, scenarioName: string): string {\n return `${featureId};${slugify(scenarioName)}`;\n}\n","/**\n * Step fallback rules for deriving step results from scenario status.\n *\n * When frameworks don't provide step-level results, we derive them\n * from the overall scenario status using these rules.\n */\n\nimport type { StoryStep } from \"../../types/story\";\nimport type { TestStatus, StepResult } from \"../../types/test-result\";\n\n/**\n * Derive step results from story steps and scenario status.\n *\n * Rules:\n * - Passed: All steps are passed\n * - Skipped/Pending: All steps are skipped/pending\n * - Failed: Steps up to failure are passed, failing step is failed, rest are skipped\n * (Heuristic: last step is the failure, or use error info if available)\n *\n * @param steps - Story steps with keywords and text\n * @param scenarioStatus - Overall scenario status\n * @param error - Optional error information to help identify failing step\n * @returns Array of step results\n */\nexport function deriveStepResults(\n steps: StoryStep[],\n scenarioStatus: TestStatus,\n error?: { message?: string; stack?: string }\n): StepResult[] {\n if (steps.length === 0) {\n return [];\n }\n\n // Passed: all steps passed\n if (scenarioStatus === \"passed\") {\n return steps.map((_, index) => ({\n index,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n }\n\n // Skipped or Pending: all steps have same status\n if (scenarioStatus === \"skipped\" || scenarioStatus === \"pending\") {\n return steps.map((_, index) => ({\n index,\n status: scenarioStatus,\n durationMs: 0,\n }));\n }\n\n // Failed: identify failing step and mark accordingly\n const failingIndex = findFailingStepIndex(steps, error);\n\n return steps.map((_, index) => {\n if (index < failingIndex) {\n // Steps before failure are passed\n return { index, status: \"passed\" as TestStatus, durationMs: 0 };\n } else if (index === failingIndex) {\n // Failing step\n return {\n index,\n status: \"failed\" as TestStatus,\n durationMs: 0,\n errorMessage: error?.message,\n };\n } else {\n // Steps after failure are skipped\n return { index, status: \"skipped\" as TestStatus, durationMs: 0 };\n }\n });\n}\n\n/**\n * Attempt to identify which step failed based on error information.\n *\n * Strategies:\n * 1. Look for step text in error message/stack\n * 2. Default to last step if no match found\n *\n * @param steps - Story steps\n * @param error - Error information\n * @returns Index of the failing step (0-based)\n */\nfunction findFailingStepIndex(\n steps: StoryStep[],\n error?: { message?: string; stack?: string }\n): number {\n if (!error || steps.length === 0) {\n // Default: last step failed\n return steps.length - 1;\n }\n\n const errorText = `${error.message ?? \"\"} ${error.stack ?? \"\"}`.toLowerCase();\n\n // Try to find a step mentioned in the error\n for (let i = 0; i < steps.length; i++) {\n const stepText = steps[i].text.toLowerCase();\n if (errorText.includes(stepText)) {\n return i;\n }\n }\n\n // Default: last step failed\n return steps.length - 1;\n}\n\n/**\n * Merge raw step events with derived step results.\n *\n * When we have partial step data from the framework, merge it with\n * the derived results, preferring actual data over derived.\n *\n * @param derived - Derived step results from fallback rules\n * @param events - Raw step events from framework (if any)\n * @returns Merged step results\n */\nexport function mergeStepResults(\n derived: StepResult[],\n events?: Array<{\n index?: number;\n status?: string;\n durationMs?: number;\n errorMessage?: string;\n }>\n): StepResult[] {\n if (!events || events.length === 0) {\n return derived;\n }\n\n // Create a map of actual results by index\n const actualByIndex = new Map<number, (typeof events)[0]>();\n for (const event of events) {\n if (event.index !== undefined) {\n actualByIndex.set(event.index, event);\n }\n }\n\n return derived.map((step) => {\n const actual = actualByIndex.get(step.index);\n if (!actual) {\n return step;\n }\n\n return {\n index: step.index,\n status: normalizeStepStatus(actual.status) ?? step.status,\n durationMs: actual.durationMs ?? step.durationMs,\n errorMessage: actual.errorMessage ?? step.errorMessage,\n };\n });\n}\n\n/**\n * Normalize step status string to TestStatus.\n */\nfunction normalizeStepStatus(status?: string): TestStatus | undefined {\n if (!status) return undefined;\n\n const lower = status.toLowerCase();\n if (lower === \"pass\" || lower === \"passed\") return \"passed\";\n if (lower === \"fail\" || lower === \"failed\") return \"failed\";\n if (lower === \"skip\" || lower === \"skipped\") return \"skipped\";\n if (lower === \"pending\") return \"pending\";\n\n return undefined;\n}\n","/**\n * Attachment resolution: embed vs link decision.\n *\n * Attachments can either be embedded inline (base64) or linked to\n * external files based on size thresholds.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { RawAttachment } from \"../../types/raw\";\nimport type { Attachment } from \"../../types/test-result\";\n\n/** Default max embed size: 512KB */\nconst DEFAULT_MAX_EMBED_BYTES = 512 * 1024;\n\n/** Options for attachment resolution */\nexport interface AttachmentOptions {\n /** Max bytes before attachment becomes external link. Default: 512KB */\n maxEmbedBytes?: number;\n /** Directory for external attachments */\n externalDir?: string;\n /** Project root for relative paths */\n projectRoot?: string;\n}\n\n/**\n * Resolve a raw attachment to a canonical attachment.\n *\n * Decision logic:\n * 1. If body is already provided, use it (check size for encoding decision)\n * 2. If path is provided, read file and decide embed vs link\n * 3. For large files, return a URL reference instead of embedding\n *\n * @param raw - Raw attachment from framework\n * @param options - Resolution options\n * @returns Resolved canonical attachment\n */\nexport function resolveAttachment(\n raw: RawAttachment,\n options: AttachmentOptions = {}\n): Attachment {\n const maxBytes = options.maxEmbedBytes ?? DEFAULT_MAX_EMBED_BYTES;\n\n // If we already have a body, use it\n if (raw.body) {\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: raw.body,\n contentEncoding: raw.encoding ?? \"BASE64\",\n };\n }\n\n // If we have a path, read and potentially embed\n if (raw.path) {\n return resolveFromPath(raw, maxBytes, options);\n }\n\n // No body or path - return empty attachment\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: \"\",\n contentEncoding: \"IDENTITY\",\n };\n}\n\n/**\n * Resolve attachment from file path.\n */\nfunction resolveFromPath(\n raw: RawAttachment,\n maxBytes: number,\n options: AttachmentOptions\n): Attachment {\n const filePath = raw.path!;\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(options.projectRoot ?? process.cwd(), filePath);\n\n // Check if file exists\n if (!fs.existsSync(absolutePath)) {\n // File doesn't exist - return path as URL reference\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: filePath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Get file size\n const stats = fs.statSync(absolutePath);\n const byteLength = raw.byteLength ?? stats.size;\n\n // If too large, return as URL reference\n if (byteLength > maxBytes) {\n // Copy to external dir if specified\n if (options.externalDir) {\n const destPath = copyToExternalDir(absolutePath, options.externalDir);\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: destPath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Return relative path as URL\n const relativePath = options.projectRoot\n ? path.relative(options.projectRoot, absolutePath)\n : filePath;\n\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: relativePath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Embed as base64\n const content = fs.readFileSync(absolutePath);\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: content.toString(\"base64\"),\n contentEncoding: \"BASE64\",\n };\n}\n\n/**\n * Copy file to external directory and return the destination path.\n */\nfunction copyToExternalDir(sourcePath: string, externalDir: string): string {\n // Ensure external dir exists\n if (!fs.existsSync(externalDir)) {\n fs.mkdirSync(externalDir, { recursive: true });\n }\n\n const filename = path.basename(sourcePath);\n const destPath = path.join(externalDir, filename);\n\n // Handle filename conflicts\n let finalPath = destPath;\n let counter = 1;\n while (fs.existsSync(finalPath)) {\n const ext = path.extname(filename);\n const base = path.basename(filename, ext);\n finalPath = path.join(externalDir, `${base}-${counter}${ext}`);\n counter++;\n }\n\n fs.copyFileSync(sourcePath, finalPath);\n return finalPath;\n}\n\n/**\n * Resolve multiple attachments.\n *\n * @param attachments - Raw attachments array\n * @param options - Resolution options\n * @returns Resolved canonical attachments\n */\nexport function resolveAttachments(\n attachments: RawAttachment[] | undefined,\n options: AttachmentOptions = {}\n): Attachment[] {\n if (!attachments || attachments.length === 0) {\n return [];\n }\n\n return attachments.map((raw) => resolveAttachment(raw, options));\n}\n","/**\n * Anti-Corruption Layer (ACL) - Layer 2.\n *\n * Transforms permissive RawRun data from framework adapters into\n * strict canonical TestRunResult for formatters.\n */\n\nimport type { StoryMeta, NormalizedTicket } from \"../../types/story\";\nimport type { RawRun, RawTestCase } from \"../../types/raw\";\nimport type {\n TestRunResult,\n TestCaseResult,\n TestStatus,\n} from \"../../types/test-result\";\nimport type { CanonicalizeOptions } from \"../../types/options\";\nimport { normalizeStatus } from \"./status\";\nimport { generateTestCaseId, generateRunId } from \"./ids\";\nimport { deriveStepResults, mergeStepResults } from \"./steps\";\nimport { resolveAttachments } from \"./attachments\";\n\n/**\n * Canonicalize a raw run into a strict TestRunResult.\n *\n * This is the main entry point for the ACL. It:\n * - Enforces required fields with defaults\n * - Normalizes statuses to TestStatus enum\n * - Applies step fallback rules\n * - Resolves attachments (embed vs link)\n * - Generates deterministic IDs\n *\n * @param raw - Raw run data from a framework adapter\n * @param options - Canonicalization options\n * @returns Strict canonical TestRunResult\n */\nexport function canonicalizeRun(\n raw: RawRun,\n options: CanonicalizeOptions = {}\n): TestRunResult {\n const now = Date.now();\n const startedAtMs = raw.startedAtMs ?? options.defaults?.startedAtMs ?? now;\n const finishedAtMs = raw.finishedAtMs ?? options.defaults?.finishedAtMs ?? now;\n\n const runId = generateRunId(startedAtMs, raw.projectRoot);\n\n const testCases = raw.testCases\n .filter((tc) => tc.story != null)\n .map((tc) => canonicalizeTestCase(tc, options, raw.projectRoot));\n\n return {\n testCases,\n startedAtMs,\n finishedAtMs,\n durationMs: finishedAtMs - startedAtMs,\n projectRoot: raw.projectRoot,\n runId,\n packageVersion: raw.packageVersion,\n gitSha: raw.gitSha,\n ci: raw.ci,\n };\n}\n\n/**\n * Canonicalize a single test case.\n */\nfunction canonicalizeTestCase(\n raw: RawTestCase,\n options: CanonicalizeOptions,\n projectRoot: string\n): TestCaseResult {\n const story = raw.story!;\n const sourceFile = raw.sourceFile ?? \"unknown\";\n const scenario = story.scenario ?? raw.title ?? \"Unknown Scenario\";\n\n // Generate deterministic ID\n const id = generateTestCaseId(sourceFile, scenario);\n\n // Normalize status\n const status = normalizeStatus(raw.status);\n\n // Derive step results\n const derivedSteps = deriveStepResults(story.steps ?? [], status, raw.error);\n const stepResults = mergeStepResults(\n derivedSteps,\n raw.stepEvents?.map((e) => ({\n index: e.index,\n status: e.status,\n durationMs: e.durationMs,\n errorMessage: e.errorMessage,\n }))\n );\n\n // Resolve attachments\n const attachments = resolveAttachments(raw.attachments, {\n maxEmbedBytes: options.attachments?.maxEmbedBytes,\n externalDir: options.attachments?.externalDir,\n projectRoot,\n });\n\n // Normalize tags\n const tags = normalizeTags(story);\n\n // Normalize tickets (raw JSON may have plain strings)\n if (story.tickets) {\n story.tickets = normalizeTickets(story.tickets as unknown as (string | NormalizedTicket)[]);\n }\n\n // Build title path\n const titlePath = buildTitlePath(raw, story);\n\n return {\n id,\n story,\n sourceFile,\n sourceLine: raw.sourceLine ?? 1,\n status,\n durationMs: raw.durationMs ?? 0,\n errorMessage: raw.error?.message,\n errorStack: raw.error?.stack,\n attachments,\n stepResults,\n titlePath,\n projectName: raw.projectName,\n retry: raw.retry ?? 0,\n retries: raw.retries ?? 0,\n tags,\n };\n}\n\n/**\n * Normalize tags from story metadata.\n *\n * - Ensures array format\n * - Deduplicates\n * - Sorts alphabetically\n */\nfunction normalizeTags(story: StoryMeta): string[] {\n const tags = story.tags ?? [];\n return [...new Set(tags)].sort();\n}\n\n/**\n * Normalize raw tickets to NormalizedTicket objects.\n *\n * Raw JSON may contain plain strings (from language packages or older adapters)\n * or objects with {id, url}. This ensures a uniform shape for formatters.\n */\nfunction normalizeTickets(raw: (string | NormalizedTicket)[]): NormalizedTicket[] {\n return raw.map((t) => (typeof t === \"string\" ? { id: t } : t));\n}\n\n/**\n * Build title path from raw test case and story metadata.\n *\n * Prefers story.suitePath if available, falls back to raw.titlePath.\n */\nfunction buildTitlePath(raw: RawTestCase, story: StoryMeta): string[] {\n if (story.suitePath && story.suitePath.length > 0) {\n return story.suitePath;\n }\n\n if (raw.titlePath && raw.titlePath.length > 0) {\n // Exclude the last element (test name) if titlePath includes it\n const withoutTestName = raw.titlePath.slice(0, -1);\n return withoutTestName.length > 0 ? withoutTestName : [];\n }\n\n return [];\n}\n\n// Re-export helpers for advanced usage\nexport { normalizeStatus } from \"./status\";\nexport { generateTestCaseId, generateRunId, slugify } from \"./ids\";\nexport { deriveStepResults, mergeStepResults } from \"./steps\";\nexport { resolveAttachment, resolveAttachments } from \"./attachments\";\n","/**\n * Validation helpers for canonical TestRunResult.\n *\n * Used in tests to verify ACL output meets all invariants.\n */\n\nimport type { TestRunResult, TestCaseResult } from \"../../types/test-result\";\n\n/** Validation result */\nexport interface ValidationResult {\n /** Whether the run is valid */\n valid: boolean;\n /** List of validation errors */\n errors: string[];\n}\n\n/**\n * Validate a canonical TestRunResult.\n *\n * Checks:\n * - All required fields are present\n * - stepResults length matches story.steps length\n * - stepResults indexes are valid and unique\n * - Durations are non-negative\n * - Timestamps are valid\n *\n * @param run - The TestRunResult to validate\n * @returns Validation result with errors if any\n */\nexport function validateCanonicalRun(run: TestRunResult): ValidationResult {\n const errors: string[] = [];\n\n // Validate run-level fields\n if (!run.runId) {\n errors.push(\"Run missing runId\");\n }\n\n if (!run.projectRoot) {\n errors.push(\"Run missing projectRoot\");\n }\n\n if (typeof run.startedAtMs !== \"number\" || run.startedAtMs < 0) {\n errors.push(`Invalid startedAtMs: ${run.startedAtMs}`);\n }\n\n if (typeof run.finishedAtMs !== \"number\" || run.finishedAtMs < 0) {\n errors.push(`Invalid finishedAtMs: ${run.finishedAtMs}`);\n }\n\n if (run.finishedAtMs < run.startedAtMs) {\n errors.push(`finishedAtMs (${run.finishedAtMs}) < startedAtMs (${run.startedAtMs})`);\n }\n\n if (typeof run.durationMs !== \"number\" || run.durationMs < 0) {\n errors.push(`Invalid durationMs: ${run.durationMs}`);\n }\n\n if (!Array.isArray(run.testCases)) {\n errors.push(\"testCases is not an array\");\n } else {\n // Validate each test case\n for (let i = 0; i < run.testCases.length; i++) {\n const tcErrors = validateTestCase(run.testCases[i], i);\n errors.push(...tcErrors);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Validate a single test case.\n */\nfunction validateTestCase(tc: TestCaseResult, index: number): string[] {\n const errors: string[] = [];\n const prefix = `TestCase[${index}]`;\n\n // Required fields\n if (!tc.id) {\n errors.push(`${prefix}: missing id`);\n }\n\n if (!tc.story) {\n errors.push(`${prefix}: missing story`);\n return errors; // Can't validate further without story\n }\n\n if (!tc.sourceFile) {\n errors.push(`${prefix}: missing sourceFile`);\n }\n\n if (typeof tc.sourceLine !== \"number\" || tc.sourceLine < 1) {\n errors.push(`${prefix}: invalid sourceLine ${tc.sourceLine}`);\n }\n\n // Status must be valid enum value\n const validStatuses = [\"passed\", \"failed\", \"skipped\", \"pending\"];\n if (!validStatuses.includes(tc.status)) {\n errors.push(`${prefix}: invalid status \"${tc.status}\"`);\n }\n\n // Duration must be non-negative\n if (typeof tc.durationMs !== \"number\" || tc.durationMs < 0) {\n errors.push(`${prefix}: invalid durationMs ${tc.durationMs}`);\n }\n\n // Retry fields\n if (typeof tc.retry !== \"number\" || tc.retry < 0) {\n errors.push(`${prefix}: invalid retry ${tc.retry}`);\n }\n\n if (typeof tc.retries !== \"number\" || tc.retries < 0) {\n errors.push(`${prefix}: invalid retries ${tc.retries}`);\n }\n\n // Arrays must be arrays\n if (!Array.isArray(tc.attachments)) {\n errors.push(`${prefix}: attachments is not an array`);\n }\n\n if (!Array.isArray(tc.titlePath)) {\n errors.push(`${prefix}: titlePath is not an array`);\n }\n\n if (!Array.isArray(tc.tags)) {\n errors.push(`${prefix}: tags is not an array`);\n }\n\n // Validate stepResults\n if (!Array.isArray(tc.stepResults)) {\n errors.push(`${prefix}: stepResults is not an array`);\n } else {\n const stepsCount = tc.story.steps?.length ?? 0;\n\n // stepResults length should match story.steps length\n if (tc.stepResults.length !== stepsCount) {\n errors.push(\n `${prefix}: stepResults.length (${tc.stepResults.length}) !== story.steps.length (${stepsCount})`\n );\n }\n\n // Validate each step result\n const seenIndexes = new Set<number>();\n for (const sr of tc.stepResults) {\n // Index must be valid\n if (typeof sr.index !== \"number\" || sr.index < 0 || sr.index >= stepsCount) {\n errors.push(`${prefix}: invalid stepResult index ${sr.index}`);\n }\n\n // Index must be unique\n if (seenIndexes.has(sr.index)) {\n errors.push(`${prefix}: duplicate stepResult index ${sr.index}`);\n }\n seenIndexes.add(sr.index);\n\n // Status must be valid\n if (!validStatuses.includes(sr.status)) {\n errors.push(`${prefix}: invalid stepResult status \"${sr.status}\" at index ${sr.index}`);\n }\n\n // Duration must be non-negative\n if (typeof sr.durationMs !== \"number\" || sr.durationMs < 0) {\n errors.push(`${prefix}: invalid stepResult durationMs ${sr.durationMs} at index ${sr.index}`);\n }\n }\n }\n\n return errors;\n}\n\n/**\n * Assert that a run is valid, throwing if not.\n *\n * Useful in tests.\n *\n * @param run - The TestRunResult to validate\n * @throws Error if validation fails\n */\nexport function assertValidRun(run: TestRunResult): void {\n const result = validateCanonicalRun(run);\n if (!result.valid) {\n throw new Error(`Invalid TestRunResult:\\n${result.errors.join(\"\\n\")}`);\n }\n}\n","/**\n * @executable-stories/formatters\n *\n * Cucumber-compatible report formats (JSON, HTML, JUnit, Markdown)\n * for Jest, Vitest, and Playwright test results.\n *\n * Architecture:\n * - Layer 1: Framework Adapters (adaptJestRun, adaptVitestRun, adaptPlaywrightRun)\n * - Layer 2: Anti-Corruption Layer (canonicalizeRun)\n * - Layer 3: Formatters (CucumberJsonFormatter, HtmlFormatter, JUnitFormatter, MarkdownFormatter)\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport * as fsPromises from \"node:fs/promises\";\nimport type { TestRunResult, TestCaseResult } from \"./types/test-result\";\nimport type {\n FormatterOptions,\n ResolvedFormatterOptions,\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n Logger,\n WriteFile,\n CanonicalizeOptions,\n SortTestCasesMode,\n} from \"./types/options\";\nimport type { RawRun } from \"./types/raw\";\nimport type { RunDiffResult } from \"./types/compare\";\n\nimport { canonicalizeRun } from \"./converters/acl/index\";\nimport { CucumberJsonFormatter } from \"./formatters/cucumber-json\";\nimport { HtmlFormatter } from \"./formatters/html/index\";\nimport { JUnitFormatter } from \"./formatters/junit-xml\";\nimport { MarkdownFormatter } from \"./formatters/markdown\";\nimport { CucumberMessagesFormatter } from \"./formatters/cucumber-messages/index\";\nimport { CucumberHtmlFormatter } from \"./formatters/cucumber-html\";\nimport { diffRuns } from \"./compare/index\";\nimport { RunDiffHtmlFormatter } from \"./formatters/run-diff-html\";\nimport { RunDiffMarkdownFormatter } from \"./formatters/run-diff-markdown\";\nimport { matchesPattern, selectTestCases } from \"./select-test-cases\";\nimport { bundleAssets } from \"./bundler/bundle-assets\";\n\n// Import adapters for convenience functions\nimport { adaptJestRun } from \"./converters/adapters/jest\";\nimport { adaptVitestRun } from \"./converters/adapters/vitest\";\nimport { adaptPlaywrightRun } from \"./converters/adapters/playwright\";\n\n// ============================================================================\n// Type Exports\n// ============================================================================\n\n// Story types (shared vocabulary for all adapters)\nexport type {\n StepKeyword,\n StepMode,\n DocPhase,\n DocEntry,\n StoryStep,\n StoryMeta,\n NormalizedTicket,\n} from \"./types/story\";\nexport { STORY_META_KEY } from \"./types/story\";\n\n// OTel span types (trace waterfall rendering)\nexport type { OtelSpan, OtelAttributeValue } from \"./types/otel\";\n\n// Canonical types (Layer 2 output - what formatters accept)\nexport type {\n TestStatus,\n StepResult,\n Attachment,\n TestCaseResult,\n TestCaseAttempt,\n CIInfo,\n CoverageSummary,\n TestRunResult,\n} from \"./types/test-result\";\n\n// Raw types (Layer 1 - for adapter authors)\nexport type {\n RawStatus,\n RawAttachment,\n RawStepEvent,\n RawTestCase,\n RawCIInfo,\n RawRun,\n} from \"./types/raw\";\n\n// Cucumber JSON types (Layer 3 output)\nexport type {\n IJsonTag,\n IJsonDocString,\n IJsonTableRow,\n IJsonDataTable,\n IJsonStepArgument,\n IJsonEmbedding,\n IJsonStepResult,\n IJsonStep,\n IJsonScenario,\n IJsonFeature,\n} from \"./types/cucumber-json\";\n\n// Options types\nexport type {\n CanonicalizeOptions,\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n OutputConfig,\n Logger,\n WriteFile,\n MarkdownFormatterOptions,\n MarkdownRenderers,\n FormatterOptions,\n ResolvedFormatterOptions,\n SortTestCasesMode,\n} from \"./types/options\";\nexport type {\n ScenarioChangeKind,\n ScenarioChangeFlags,\n ScenarioSnapshot,\n ScenarioDiff,\n RunDiffSummary,\n RunDiffResult,\n CompareFormat,\n CompareFormatterOptions,\n} from \"./types/compare\";\n\n// Theme types\nexport type { HtmlTheme, HtmlThemeName } from \"./formatters/html/themes/index\";\nexport { resolveTheme, getAvailableThemes, getCssOnlyThemes } from \"./formatters/html/themes/index\";\n\n// ============================================================================\n// ACL Exports\n// ============================================================================\n\nexport { canonicalizeRun } from \"./converters/acl/index\";\n\n/** @internal */\nexport { normalizeStatus } from \"./converters/acl/index\";\n/** @internal */\nexport { generateTestCaseId } from \"./converters/acl/index\";\n/** @internal */\nexport { generateRunId } from \"./converters/acl/index\";\n/** @internal */\nexport { slugify } from \"./converters/acl/index\";\n/** @internal */\nexport { deriveStepResults } from \"./converters/acl/index\";\n/** @internal */\nexport { mergeStepResults } from \"./converters/acl/index\";\n/** @internal */\nexport { resolveAttachment } from \"./converters/acl/index\";\n/** @internal */\nexport { resolveAttachments } from \"./converters/acl/index\";\n\nexport {\n validateCanonicalRun,\n assertValidRun,\n type ValidationResult,\n} from \"./converters/acl/validate\";\n\n// ============================================================================\n// Formatter Exports\n// ============================================================================\n\nexport {\n CucumberJsonFormatter,\n type CucumberJsonOptions,\n} from \"./formatters/cucumber-json\";\n\nexport {\n HtmlFormatter,\n type HtmlOptions,\n} from \"./formatters/html/index\";\n\nexport {\n JUnitFormatter,\n type JUnitOptions,\n} from \"./formatters/junit-xml\";\n\nexport {\n MarkdownFormatter,\n type MarkdownOptions,\n} from \"./formatters/markdown\";\n\nexport {\n CucumberMessagesFormatter,\n type CucumberMessagesOptions,\n} from \"./formatters/cucumber-messages/index\";\n\nexport {\n CucumberHtmlFormatter,\n type CucumberHtmlOptions,\n} from \"./formatters/cucumber-html\";\n\nexport {\n RunDiffHtmlFormatter,\n type RunDiffHtmlOptions,\n} from \"./formatters/run-diff-html\";\n\nexport {\n RunDiffMarkdownFormatter,\n type RunDiffMarkdownOptions,\n} from \"./formatters/run-diff-markdown\";\n\n// ============================================================================\n// NDJSON Parser (compat path: NDJSON → TestRunResult)\n// ============================================================================\n\nexport { parseNdjson, parseEnvelopes } from \"./converters/ndjson-parser\";\n\n// ============================================================================\n// Utility Exports\n// ============================================================================\n\n/** @internal */\nexport { readGitSha } from \"./utils/git-info\";\n/** @internal */\nexport { findGitDir } from \"./utils/git-info\";\n/** @internal */\nexport { readBranchName } from \"./utils/git-info\";\n/** @internal */\nexport { formatDuration } from \"./utils/duration\";\n/** @internal */\nexport { msToNanoseconds } from \"./utils/duration\";\n/** @internal */\nexport { nanosecondsToMs } from \"./utils/duration\";\n/** @internal */\nexport { readPackageVersion } from \"./utils/metadata\";\n/** @internal */\nexport { clearVersionCache } from \"./utils/metadata\";\nexport { detectCI } from \"./utils/ci-detect\";\nexport {\n tryGetActiveOtelContext,\n resolveTraceUrl,\n type OtelTraceContext,\n} from \"./utils/otel-detect\";\n\n// ============================================================================\n// Notifier Exports\n// ============================================================================\n\nexport { sendNotifications } from \"./notifiers/index\";\nexport { sendSlackNotification } from \"./notifiers/slack\";\nexport { sendTeamsNotification } from \"./notifiers/teams\";\nexport { sendWebhookNotification } from \"./notifiers/webhook\";\nexport { signBody } from \"./notifiers/hmac\";\nexport { stripAnsi } from \"./notifiers/ansi-strip\";\nexport type { NotificationSummary, NotifyCondition, GenericWebhookNotifierOptions, WebhookSignerHmac, WebhookPayload } from \"./notifiers/types\";\n\n// ============================================================================\n// CI Type Exports\n// ============================================================================\n\nexport type { CIProvider, CIInfo as TypedCIInfo } from \"./types/ci\";\nexport { toCIInfo, toRawCIInfo } from \"./types/ci\";\n\n// ============================================================================\n// History Exports\n// ============================================================================\n\nexport {\n loadHistory,\n saveHistory,\n updateHistory,\n calculateFlakiness,\n detectPerformanceTrend,\n calculateStability,\n computeTestMetrics,\n MIN_PERF_SAMPLES,\n MIN_METRIC_SAMPLES,\n MIN_FLAKINESS_SAMPLES,\n hasSufficientHistory,\n} from \"./history/index\";\n\nexport type {\n HistoryEntry,\n TestHistory,\n HistoryStore,\n StabilityGrade,\n FlakinessLevel,\n PerformanceTrend,\n TestMetrics,\n} from \"./history/index\";\n\n// ============================================================================\n// List Scenarios\n// ============================================================================\n\nexport { listScenarios } from \"./list-scenarios\";\nexport type { ListScenariosArgs, ListScenariosDeps } from \"./list-scenarios\";\n\n// ============================================================================\n// ReportGenerator Types (fn(args, deps) pattern)\n// ============================================================================\n\n/** Arguments for generate function */\nexport interface GenerateArgs {\n /** Canonical test run result */\n run: TestRunResult;\n /** Optional options override */\n options?: FormatterOptions;\n}\n\n/** Dependencies for generate function (injectable for testing) */\nexport interface GenerateDeps {\n /** Logger for warnings */\n logger: Logger;\n /** File writer function */\n writeFile: WriteFile;\n}\n\n/** Result of generate function: Map of format to array of file paths */\nexport type GenerateResult = Map<OutputFormat, string[]>;\n\nexport interface GenerateCompareResult {\n files: string[];\n diff: RunDiffResult;\n}\n\n/** Extension map for output formats */\nconst FORMAT_EXTENSIONS: Record<OutputFormat, string> = {\n markdown: \".md\",\n html: \".html\",\n \"cucumber-html\": \".cucumber.html\",\n junit: \".junit.xml\",\n \"cucumber-json\": \".cucumber.json\",\n \"cucumber-messages\": \".ndjson\",\n};\n\n/** Known test file extensions to strip for colocated naming */\nconst TEST_EXTENSIONS = [\n \".test.ts\", \".test.tsx\", \".spec.ts\", \".spec.tsx\",\n \".test.js\", \".spec.js\", \".story.test.ts\", \".story.spec.ts\",\n];\n\n// ============================================================================\n// Pure Functions for Output Routing\n// ============================================================================\n\n/**\n * Find the first matching rule for a source file.\n */\nfunction findMatchingRule(\n sourceFile: string,\n rules: OutputRule[]\n): OutputRule | undefined {\n for (const rule of rules) {\n if (matchesPattern(rule.match, sourceFile)) {\n return rule;\n }\n }\n return undefined;\n}\n\n/**\n * Normalize path to posix format (forward slashes).\n */\nfunction toPosix(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Compute output path for a test case based on mode and settings.\n */\nfunction computeOutputPath(\n sourceFile: string,\n format: OutputFormat,\n mode: OutputMode,\n colocatedStyle: ColocatedStyle,\n baseOutputDir: string,\n outputName: string,\n outputNameSuffix?: string\n): string {\n const ext = FORMAT_EXTENSIONS[format];\n const effectiveName = outputName + (outputNameSuffix ?? \"\");\n\n if (mode === \"aggregated\") {\n // Aggregated: single file in outputDir\n return toPosix(path.join(baseOutputDir, `${effectiveName}${ext}`));\n }\n\n // Colocated mode - normalize source file to posix first\n const normalizedSource = toPosix(sourceFile);\n const dirOfSource = path.posix.dirname(normalizedSource);\n let baseName = path.posix.basename(normalizedSource);\n\n // Strip test extension\n for (const testExt of TEST_EXTENSIONS) {\n if (baseName.endsWith(testExt)) {\n baseName = baseName.slice(0, -testExt.length);\n break;\n }\n }\n\n const fileName = `${baseName}.${effectiveName}${ext}`;\n\n if (colocatedStyle === \"adjacent\") {\n // Adjacent: write next to source file (ignores outputDir)\n return toPosix(path.posix.join(dirOfSource, fileName));\n }\n\n // Mirrored: preserve directory structure under outputDir\n return toPosix(path.posix.join(baseOutputDir, dirOfSource, fileName));\n}\n\n/**\n * Group test cases by their computed output path.\n */\nfunction groupTestCasesByOutput(\n testCases: TestCaseResult[],\n format: OutputFormat,\n options: ResolvedFormatterOptions,\n logger: Logger,\n outputNameSuffix?: string\n): Map<string, TestCaseResult[]> {\n const groups = new Map<string, TestCaseResult[]>();\n const rules = options.output.rules;\n const defaultMode = options.output.mode;\n const defaultColocatedStyle = options.output.colocatedStyle;\n const defaultFormats = options.formats;\n const defaultOutputDir = options.outputDir;\n const defaultOutputName = options.outputName;\n\n for (const tc of testCases) {\n const sourceFile = tc.sourceFile;\n\n // Check if colocated mode but missing sourceFile\n if (defaultMode === \"colocated\" && sourceFile === \"unknown\") {\n logger.warn(\n `Test case \"${tc.story.scenario}\" missing sourceFile, falling back to aggregated`\n );\n }\n\n // Find matching rule\n const rule = findMatchingRule(sourceFile, rules);\n\n // Determine effective settings (first match wins, fall back to defaults)\n const mode = rule?.mode ?? defaultMode;\n const colocatedStyle = rule?.colocatedStyle ?? defaultColocatedStyle;\n const formats = rule?.formats ?? defaultFormats;\n const outputDir = rule?.outputDir ?? defaultOutputDir;\n const outputName = rule?.outputName ?? options.output.outputName ?? defaultOutputName;\n\n // Warn if rule sets both adjacent style and outputDir\n if (\n rule &&\n rule.colocatedStyle === \"adjacent\" &&\n rule.outputDir !== undefined\n ) {\n logger.warn(\n `Rule for \"${rule.match}\" sets both colocatedStyle: \"adjacent\" and outputDir. outputDir will be ignored for adjacent mode.`\n );\n }\n\n // Skip if format not in effective formats\n if (!formats.includes(format)) {\n continue;\n }\n\n // Handle missing sourceFile in colocated mode\n const effectiveMode =\n mode === \"colocated\" && sourceFile === \"unknown\" ? \"aggregated\" : mode;\n\n const outputPath = computeOutputPath(\n sourceFile,\n format,\n effectiveMode,\n colocatedStyle,\n outputDir,\n outputName,\n outputNameSuffix\n );\n\n const existing = groups.get(outputPath);\n if (existing) {\n existing.push(tc);\n } else {\n groups.set(outputPath, [tc]);\n }\n }\n\n return groups;\n}\n\n// ============================================================================\n// ReportGenerator\n// ============================================================================\n\n/**\n * High-level report generator that combines multiple formatters.\n *\n * Accepts ONLY canonical TestRunResult - use adapters + canonicalizeRun first.\n *\n * Supports output routing:\n * - Aggregated: All test cases in a single file\n * - Colocated mirrored: Files mirrored under outputDir preserving directory structure\n * - Colocated adjacent: Files written next to source files\n * - Rule-based: Different routing based on source file patterns\n */\nexport class ReportGenerator {\n private options: ResolvedFormatterOptions;\n private deps: GenerateDeps;\n\n constructor(options: FormatterOptions = {}, deps?: Partial<GenerateDeps>) {\n this.options = this.resolveOptions(options);\n this.deps = {\n logger: deps?.logger ?? console,\n writeFile: deps?.writeFile ?? ((p, c) => fsPromises.writeFile(p, c, \"utf8\")),\n };\n }\n\n /**\n * Resolve options with defaults.\n */\n private resolveOptions(options: FormatterOptions): ResolvedFormatterOptions {\n return {\n include: options.include ?? [],\n exclude: options.exclude ?? [],\n includeTags: options.includeTags ?? [],\n excludeTags: options.excludeTags ?? [],\n formats: options.formats ?? [\"cucumber-json\"],\n outputDir: options.outputDir ?? \"reports\",\n outputName: options.outputName ?? \"test-results\",\n outputNameTimestamp: options.outputNameTimestamp ?? false,\n sortTestCases: options.sortTestCases ?? \"none\",\n output: {\n mode: options.output?.mode ?? \"aggregated\",\n colocatedStyle: options.output?.colocatedStyle ?? \"mirrored\",\n rules: options.output?.rules ?? [],\n outputName: options.output?.outputName,\n },\n cucumberJson: {\n pretty: options.cucumberJson?.pretty ?? false,\n },\n cucumberMessages: {\n uriStrategy: options.cucumberMessages?.uriStrategy ?? \"sourceFile\",\n includeSynthetics: options.cucumberMessages?.includeSynthetics ?? true,\n idSalt: options.cucumberMessages?.idSalt ?? \"\",\n meta: options.cucumberMessages?.meta,\n },\n html: {\n title: options.html?.title ?? \"Test Results\",\n darkMode: options.html?.darkMode ?? true,\n searchable: options.html?.searchable ?? true,\n startCollapsed: options.html?.startCollapsed ?? false,\n embedScreenshots: options.html?.embedScreenshots ?? true,\n syntaxHighlighting: options.html?.syntaxHighlighting ?? true,\n mermaidEnabled: options.html?.mermaidEnabled ?? true,\n markdownEnabled: options.html?.markdownEnabled ?? true,\n permalinkBaseUrl: options.html?.permalinkBaseUrl,\n ticketUrlTemplate: options.html?.ticketUrlTemplate,\n theme: options.html?.theme ?? \"default\",\n tocEnabled: options.html?.tocEnabled ?? true,\n themePickerEnabled: options.html?.themePickerEnabled ?? false,\n },\n junit: {\n suiteName: options.junit?.suiteName ?? \"Test Suite\",\n includeOutput: options.junit?.includeOutput ?? true,\n },\n markdown: {\n title: options.markdown?.title ?? \"User Stories\",\n includeStatusIcons: options.markdown?.includeStatusIcons ?? true,\n includeMetadata: options.markdown?.includeMetadata ?? true,\n includeErrors: options.markdown?.includeErrors ?? true,\n scenarioHeadingLevel: options.markdown?.scenarioHeadingLevel ?? 3,\n stepStyle: options.markdown?.stepStyle ?? \"bullets\",\n groupBy: options.markdown?.groupBy ?? \"file\",\n sortScenarios: options.markdown?.sortScenarios ?? \"source\",\n suiteSeparator: options.markdown?.suiteSeparator ?? \" - \",\n includeFrontMatter: options.markdown?.includeFrontMatter ?? false,\n includeSummaryTable: options.markdown?.includeSummaryTable ?? false,\n permalinkBaseUrl: options.markdown?.permalinkBaseUrl,\n ticketUrlTemplate: options.markdown?.ticketUrlTemplate,\n traceUrlTemplate: options.markdown?.traceUrlTemplate,\n includeSourceLinks: options.markdown?.includeSourceLinks ?? true,\n customRenderers: options.markdown?.customRenderers,\n },\n assetMode: options.assetMode ?? \"none\",\n allowMissingAssets: options.allowMissingAssets ?? false,\n };\n }\n\n /**\n * Generate reports for a test run.\n *\n * @param run - Canonical TestRunResult (use canonicalizeRun to create from RawRun)\n * @returns Map of output format to generated file paths\n */\n async generate(run: TestRunResult): Promise<GenerateResult> {\n const testCases = selectTestCases(\n {\n testCases: run.testCases,\n include: this.options.include,\n exclude: this.options.exclude,\n includeTags: this.options.includeTags,\n excludeTags: this.options.excludeTags,\n sortTestCases: this.options.sortTestCases,\n },\n { logger: this.deps.logger }\n );\n\n const filteredRun: TestRunResult = { ...run, testCases };\n\n const results: GenerateResult = new Map();\n\n for (const format of this.options.formats) {\n const paths = await this.generateFormat(filteredRun, format);\n results.set(format, paths);\n }\n\n if (this.options.assetMode === \"copy\") {\n const htmlPaths = results.get(\"html\");\n if (htmlPaths) {\n for (const htmlPath of htmlPaths) {\n bundleAssets(htmlPath, {\n allowMissing: this.options.allowMissingAssets,\n });\n }\n }\n }\n\n return results;\n }\n\n /**\n * Generate reports for a single format.\n */\n private async generateFormat(\n run: TestRunResult,\n format: OutputFormat\n ): Promise<string[]> {\n const outputNameSuffix = this.options.outputNameTimestamp\n ? `-${Math.floor(run.startedAtMs / 1000)}`\n : undefined;\n\n // Group test cases by output path\n const groups = groupTestCasesByOutput(\n run.testCases,\n format,\n this.options,\n this.deps.logger,\n outputNameSuffix\n );\n\n // Handle empty runs in aggregated mode - write a single empty file\n if (groups.size === 0 && this.options.output.mode === \"aggregated\") {\n const ext = FORMAT_EXTENSIONS[format];\n const effectiveName = this.options.outputName + (outputNameSuffix ?? \"\");\n const outputPath = toPosix(path.join(this.options.outputDir, `${effectiveName}${ext}`));\n const content = await this.formatContent(run, format);\n const dir = path.dirname(outputPath);\n await fsPromises.mkdir(dir, { recursive: true });\n await this.deps.writeFile(outputPath, content);\n return [outputPath];\n }\n\n const writtenPaths: string[] = [];\n\n for (const [outputPath, testCases] of groups) {\n // Create a run with just these test cases\n const groupRun: TestRunResult = {\n ...run,\n testCases,\n };\n\n // Format content\n const content = await this.formatContent(groupRun, format);\n\n // Ensure directory exists\n const dir = path.dirname(outputPath);\n await fsPromises.mkdir(dir, { recursive: true });\n\n // Write file\n await this.deps.writeFile(outputPath, content);\n writtenPaths.push(outputPath);\n }\n\n return writtenPaths;\n }\n\n /**\n * Format content for a specific format.\n */\n private formatContent(run: TestRunResult, format: OutputFormat): string | Promise<string> {\n switch (format) {\n case \"cucumber-json\": {\n const formatter = new CucumberJsonFormatter({\n pretty: this.options.cucumberJson.pretty,\n });\n return formatter.formatToString(run);\n }\n\n case \"html\": {\n const formatter = new HtmlFormatter({\n title: this.options.html.title,\n theme: this.options.html.theme,\n darkMode: this.options.html.darkMode,\n searchable: this.options.html.searchable,\n startCollapsed: this.options.html.startCollapsed,\n embedScreenshots: this.options.html.embedScreenshots,\n syntaxHighlighting: this.options.html.syntaxHighlighting,\n mermaidEnabled: this.options.html.mermaidEnabled,\n markdownEnabled: this.options.html.markdownEnabled,\n permalinkBaseUrl: this.options.html.permalinkBaseUrl,\n ticketUrlTemplate: this.options.html.ticketUrlTemplate,\n tocEnabled: this.options.html.tocEnabled,\n themePickerEnabled: this.options.html.themePickerEnabled,\n });\n return formatter.format(run);\n }\n\n case \"cucumber-html\": {\n const formatter = new CucumberHtmlFormatter({\n messages: {\n uriStrategy: this.options.cucumberMessages.uriStrategy,\n includeSynthetics: this.options.cucumberMessages.includeSynthetics,\n idSalt: this.options.cucumberMessages.idSalt,\n meta: this.options.cucumberMessages.meta,\n },\n });\n return formatter.formatToString(run);\n }\n\n case \"junit\": {\n const formatter = new JUnitFormatter({\n suiteName: this.options.junit.suiteName,\n includeOutput: this.options.junit.includeOutput,\n });\n return formatter.format(run);\n }\n\n case \"cucumber-messages\": {\n const formatter = new CucumberMessagesFormatter({\n uriStrategy: this.options.cucumberMessages.uriStrategy,\n includeSynthetics: this.options.cucumberMessages.includeSynthetics,\n idSalt: this.options.cucumberMessages.idSalt,\n meta: this.options.cucumberMessages.meta,\n });\n return formatter.formatToString(run);\n }\n\n case \"markdown\": {\n const formatter = new MarkdownFormatter({\n title: this.options.markdown.title,\n includeStatusIcons: this.options.markdown.includeStatusIcons,\n includeMetadata: this.options.markdown.includeMetadata,\n includeErrors: this.options.markdown.includeErrors,\n scenarioHeadingLevel: this.options.markdown.scenarioHeadingLevel,\n stepStyle: this.options.markdown.stepStyle,\n groupBy: this.options.markdown.groupBy,\n sortScenarios: this.options.markdown.sortScenarios,\n suiteSeparator: this.options.markdown.suiteSeparator,\n includeFrontMatter: this.options.markdown.includeFrontMatter,\n includeSummaryTable: this.options.markdown.includeSummaryTable,\n permalinkBaseUrl: this.options.markdown.permalinkBaseUrl,\n ticketUrlTemplate: this.options.markdown.ticketUrlTemplate,\n traceUrlTemplate: this.options.markdown.traceUrlTemplate,\n includeSourceLinks: this.options.markdown.includeSourceLinks,\n customRenderers: this.options.markdown.customRenderers,\n });\n return formatter.format(run);\n }\n\n default:\n throw new Error(`Unknown format: ${format}`);\n }\n }\n}\n\n/**\n * Factory function to create a ReportGenerator with dependency injection.\n *\n * Useful for testing and custom configurations.\n */\nexport function createReportGenerator(\n options?: FormatterOptions,\n deps?: Partial<GenerateDeps>\n): ReportGenerator {\n return new ReportGenerator(options, deps);\n}\n\nexport async function generateRunComparison(args: {\n baseline: TestRunResult;\n current: TestRunResult;\n formats: Array<\"html\" | \"markdown\">;\n outputDir?: string;\n outputName?: string;\n title?: string;\n}): Promise<GenerateCompareResult> {\n const outputDir = args.outputDir ?? \"reports\";\n const outputName = args.outputName ?? \"test-results-diff\";\n const diff = diffRuns(args.baseline, args.current);\n const files: string[] = [];\n\n await fsPromises.mkdir(outputDir, { recursive: true });\n\n for (const format of args.formats) {\n const ext = format === \"html\" ? \".html\" : \".md\";\n const outputPath = toPosix(path.join(outputDir, `${outputName}${ext}`));\n const content =\n format === \"html\"\n ? new RunDiffHtmlFormatter({ title: args.title }).format(diff)\n : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);\n await fsPromises.writeFile(outputPath, content, \"utf8\");\n files.push(outputPath);\n }\n\n return { files, diff };\n}\n\nexport { diffRuns } from \"./compare/index\";\nexport { createPrCommentSummary } from \"./compare/index\";\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n// Re-export adapters\nexport { adaptJestRun, adaptVitestRun, adaptPlaywrightRun };\n\n// ============================================================================\n// Bundler Exports\n// ============================================================================\n\nexport { bundleAssets } from \"./bundler/bundle-assets\";\nexport type { BundleOptions, BundleResult } from \"./bundler/bundle-assets\";\n\n// Re-export adapter types\nexport type {\n JestTestResult,\n JestFileResult,\n JestAggregatedResult,\n StoryFileReport,\n JestAdapterOptions,\n VitestState,\n VitestSerializedError,\n VitestTestResult,\n VitestTestCase,\n VitestTestModule,\n VitestAdapterOptions,\n PlaywrightStatus,\n PlaywrightError,\n PlaywrightAttachment,\n PlaywrightTestResult,\n PlaywrightAnnotation,\n PlaywrightLocation,\n PlaywrightTestCase,\n PlaywrightAdapterOptions,\n} from \"./converters/adapters/index\";\n\n/**\n * Normalize Jest results to canonical TestRunResult.\n *\n * Combines adaptJestRun + canonicalizeRun.\n */\nexport function normalizeJestResults(\n jestResults: Parameters<typeof adaptJestRun>[0],\n storyReports: Parameters<typeof adaptJestRun>[1],\n adapterOptions?: Parameters<typeof adaptJestRun>[2],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptJestRun(jestResults, storyReports, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n\n/**\n * Normalize Vitest results to canonical TestRunResult.\n *\n * Combines adaptVitestRun + canonicalizeRun.\n */\nexport function normalizeVitestResults(\n testModules: Parameters<typeof adaptVitestRun>[0],\n adapterOptions?: Parameters<typeof adaptVitestRun>[1],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptVitestRun(testModules, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n\n/**\n * Normalize Playwright results to canonical TestRunResult.\n *\n * Combines adaptPlaywrightRun + canonicalizeRun.\n */\nexport function normalizePlaywrightResults(\n testResults: Parameters<typeof adaptPlaywrightRun>[0],\n adapterOptions?: Parameters<typeof adaptPlaywrightRun>[1],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptPlaywrightRun(testResults, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n","/**\n * Deterministic line number generation for Cucumber JSON compatibility.\n *\n * When actual line numbers aren't available, we generate deterministic\n * line numbers based on position in the file.\n */\n\n/**\n * Generate deterministic line numbers for scenarios in a file.\n *\n * Starts at line 2 (after feature declaration) and increments by\n * steps count + buffer for each scenario.\n *\n * @param scenarioIndex - Index of the scenario in the file (0-based)\n * @param stepsCount - Number of steps in the scenario\n * @param prevEndLine - End line of previous scenario (default 1)\n * @returns Deterministic line number\n */\nexport function generateScenarioLine(\n scenarioIndex: number,\n stepsCount: number,\n prevEndLine: number = 1\n): number {\n // Each scenario needs:\n // - 1 line for scenario declaration\n // - n lines for steps\n // - 1 blank line after\n const linesPerScenario = 1 + stepsCount + 1;\n\n if (scenarioIndex === 0) {\n // First scenario starts at line 3 (Feature on 1, blank on 2)\n return 3;\n }\n\n return prevEndLine + linesPerScenario;\n}\n\n/**\n * Generate step line numbers for a scenario.\n *\n * @param scenarioLine - The scenario's line number\n * @param stepIndex - Index of the step (0-based)\n * @returns Line number for the step\n */\nexport function generateStepLine(scenarioLine: number, stepIndex: number): number {\n // Steps start on the line after the scenario declaration\n return scenarioLine + 1 + stepIndex;\n}\n\n/**\n * Context for tracking line numbers across multiple scenarios.\n */\nexport interface LineContext {\n /** Current line number */\n currentLine: number;\n}\n\n/**\n * Create a new line context starting at line 1.\n */\nexport function createLineContext(): LineContext {\n return { currentLine: 1 };\n}\n\n/**\n * Advance line context past a feature declaration.\n *\n * @param ctx - Line context\n * @returns Updated line context\n */\nexport function advancePastFeature(ctx: LineContext): LineContext {\n // Feature keyword on line 1, blank line after\n return { currentLine: 3 };\n}\n\n/**\n * Get the current scenario line and advance past it.\n *\n * @param ctx - Line context\n * @param stepsCount - Number of steps in the scenario\n * @returns Tuple of [scenarioLine, updatedContext]\n */\nexport function advancePastScenario(\n ctx: LineContext,\n stepsCount: number\n): [number, LineContext] {\n const scenarioLine = ctx.currentLine;\n // Advance past: scenario keyword + steps + blank line\n const newLine = scenarioLine + 1 + stepsCount + 1;\n return [scenarioLine, { currentLine: newLine }];\n}\n","/**\n * Cucumber JSON Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into Cucumber JSON format\n * compatible with cucumber-js v11.x output.\n */\n\nimport type { StoryStep, DocEntry } from \"../types/story\";\nimport type {\n TestRunResult,\n TestCaseResult,\n StepResult,\n Attachment,\n} from \"../types/test-result\";\nimport type {\n IJsonFeature,\n IJsonScenario,\n IJsonStep,\n IJsonTag,\n IJsonEmbedding,\n IJsonStepResult,\n} from \"../types/cucumber-json\";\nimport { slugify, generateFeatureId, generateScenarioId } from \"../converters/acl/ids\";\nimport { createLineContext, advancePastFeature, advancePastScenario, generateStepLine } from \"../converters/acl/lines\";\n\n/** Options for Cucumber JSON formatting */\nexport interface CucumberJsonOptions {\n /** Pretty-print JSON output. Default: false */\n pretty?: boolean;\n /** Include trailing space in keywords. Default: true */\n keywordSpacing?: boolean;\n}\n\n/**\n * Cucumber JSON Formatter.\n *\n * Transforms TestRunResult into an array of IJsonFeature objects\n * that match the Cucumber JSON format specification.\n */\nexport class CucumberJsonFormatter {\n private options: Required<CucumberJsonOptions>;\n\n constructor(options: CucumberJsonOptions = {}) {\n this.options = {\n pretty: options.pretty ?? false,\n keywordSpacing: options.keywordSpacing ?? true,\n };\n }\n\n /**\n * Format a test run into Cucumber JSON features.\n *\n * Groups test cases by source file (one feature per file).\n *\n * @param run - Canonical test run result\n * @returns Array of Cucumber JSON features\n */\n format(run: TestRunResult): IJsonFeature[] {\n // Group test cases by source file\n const byFile = new Map<string, TestCaseResult[]>();\n for (const tc of run.testCases) {\n const file = tc.sourceFile;\n const existing = byFile.get(file);\n if (existing) {\n existing.push(tc);\n } else {\n byFile.set(file, [tc]);\n }\n }\n\n // Build features\n const features: IJsonFeature[] = [];\n for (const [uri, testCases] of byFile) {\n features.push(this.buildFeature(uri, testCases));\n }\n\n return features;\n }\n\n /**\n * Format and serialize to JSON string.\n *\n * @param run - Canonical test run result\n * @returns JSON string\n */\n formatToString(run: TestRunResult): string {\n const features = this.format(run);\n return this.options.pretty\n ? JSON.stringify(features, null, 2)\n : JSON.stringify(features);\n }\n\n /**\n * Build a single feature from test cases in the same file.\n */\n private buildFeature(uri: string, testCases: TestCaseResult[]): IJsonFeature {\n const featureName = this.extractFeatureName(uri, testCases);\n const featureId = generateFeatureId(uri);\n\n // Extract feature-level tags (union of all scenario tags)\n const featureTags = this.extractFeatureTags(testCases);\n\n // Build scenarios with deterministic line numbers\n let lineCtx = createLineContext();\n lineCtx = advancePastFeature(lineCtx);\n\n const elements: IJsonScenario[] = [];\n for (const tc of testCases) {\n const [scenarioLine, nextCtx] = advancePastScenario(lineCtx, tc.story.steps.length);\n elements.push(this.buildScenario(tc, featureId, scenarioLine));\n lineCtx = nextCtx;\n }\n\n return {\n description: \"\",\n elements,\n id: featureId,\n keyword: \"Feature\",\n line: 1,\n name: featureName,\n tags: featureTags,\n uri,\n };\n }\n\n /**\n * Extract feature name from URI or test cases.\n *\n * Uses the top-level suite path if available, otherwise file name.\n */\n private extractFeatureName(uri: string, testCases: TestCaseResult[]): string {\n // Try to get common suite path prefix\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n\n if (suitePaths.length > 0) {\n // Use the first element of the first suite path as feature name\n const firstPath = suitePaths[0];\n if (firstPath.length > 0) {\n return firstPath[0];\n }\n }\n\n // Fall back to filename without extension\n const filename = uri.split(\"/\").pop() ?? uri;\n return filename.replace(/\\.[^.]+$/, \"\").replace(/[._-]/g, \" \");\n }\n\n /**\n * Extract feature-level tags from all test cases.\n */\n private extractFeatureTags(testCases: TestCaseResult[]): IJsonTag[] {\n // Collect all unique tags\n const allTags = new Set<string>();\n for (const tc of testCases) {\n for (const tag of tc.tags) {\n allTags.add(tag);\n }\n }\n\n // Convert to IJsonTag format (with @ prefix if not present)\n return [...allTags].sort().map((tag) => ({\n name: tag.startsWith(\"@\") ? tag : `@${tag}`,\n line: 1,\n }));\n }\n\n /**\n * Build a scenario from a test case.\n */\n private buildScenario(\n tc: TestCaseResult,\n featureId: string,\n scenarioLine: number\n ): IJsonScenario {\n const scenarioName = tc.story.scenario;\n const scenarioId = generateScenarioId(featureId, scenarioName);\n\n // Build steps\n const steps = this.buildSteps(tc, scenarioLine);\n\n // Build scenario tags\n const tags: IJsonTag[] = tc.tags.map((tag) => ({\n name: tag.startsWith(\"@\") ? tag : `@${tag}`,\n line: scenarioLine,\n }));\n\n return {\n description: \"\",\n id: scenarioId,\n keyword: \"Scenario\",\n line: scenarioLine,\n name: scenarioName,\n steps,\n tags,\n type: \"scenario\",\n };\n }\n\n /**\n * Build steps from story steps and step results.\n */\n private buildSteps(tc: TestCaseResult, scenarioLine: number): IJsonStep[] {\n const storySteps = tc.story.steps;\n const stepResults = tc.stepResults;\n\n // Create a map of step results by index\n const resultsByIndex = new Map<number, StepResult>();\n for (const sr of stepResults) {\n resultsByIndex.set(sr.index, sr);\n }\n\n // Check if any step failed (used to decide if last step gets attachments)\n const hasFailedStep = stepResults.some(sr => sr.status === \"failed\");\n\n const totalSteps = storySteps.length;\n return storySteps.map((step, index) => {\n const stepResult = resultsByIndex.get(index);\n const stepLine = generateStepLine(scenarioLine, index);\n const isLastStep = index === totalSteps - 1;\n return this.buildStep(step, stepResult, stepLine, index, tc.attachments, isLastStep, hasFailedStep);\n });\n }\n\n /**\n * Build a single step.\n */\n private buildStep(\n step: StoryStep,\n result: StepResult | undefined,\n line: number,\n index: number,\n attachments: Attachment[],\n isLastStep: boolean,\n hasFailedStep: boolean\n ): IJsonStep {\n // Keyword with optional trailing space\n const keyword = this.options.keywordSpacing\n ? `${step.keyword} `\n : step.keyword;\n\n // Build result\n const stepResult = this.buildStepResult(result);\n\n // Build embeddings for attachments (attach to failed step, or last step if no failure)\n const embeddings = this.buildEmbeddings(attachments, result, isLastStep, hasFailedStep);\n\n // Add screenshot docs as embeddings (always include with their step)\n const screenshotEmbeddings = this.buildScreenshotEmbeddings(step);\n embeddings.push(...screenshotEmbeddings);\n\n const jsonStep: IJsonStep = {\n keyword,\n line,\n name: step.text,\n result: stepResult,\n };\n\n // Only include optional fields if they have values\n if (embeddings.length > 0) {\n jsonStep.embeddings = embeddings;\n }\n\n // Add step arguments (doc strings from docs, data tables)\n const args = this.buildStepArguments(step);\n if (args.length > 0) {\n jsonStep.arguments = args;\n }\n\n return jsonStep;\n }\n\n /**\n * Build embeddings from screenshot doc entries.\n */\n private buildScreenshotEmbeddings(step: StoryStep): IJsonEmbedding[] {\n if (!step.docs) {\n return [];\n }\n\n const embeddings: IJsonEmbedding[] = [];\n\n for (const doc of step.docs) {\n if (doc.kind !== \"screenshot\" || !doc.path.startsWith(\"data:\")) {\n continue;\n }\n\n // Parse data URI: data:image/png;base64,ABC123...\n const match = doc.path.match(/^data:([^;]+);base64,(.+)$/);\n if (match) {\n embeddings.push({\n data: match[2],\n mime_type: match[1],\n name: doc.alt,\n });\n }\n }\n\n return embeddings;\n }\n\n /**\n * Build step result.\n */\n private buildStepResult(result: StepResult | undefined): IJsonStepResult {\n if (!result) {\n return {\n status: \"undefined\",\n duration: 0,\n };\n }\n\n // Map canonical status to Cucumber status\n const statusMap: Record<string, IJsonStepResult[\"status\"]> = {\n passed: \"passed\",\n failed: \"failed\",\n skipped: \"skipped\",\n pending: \"pending\",\n };\n\n const stepResult: IJsonStepResult = {\n status: statusMap[result.status] ?? \"undefined\",\n // Duration in nanoseconds (Cucumber uses nanoseconds)\n duration: result.durationMs * 1_000_000,\n };\n\n if (result.errorMessage) {\n stepResult.error_message = result.errorMessage;\n }\n\n return stepResult;\n }\n\n /**\n * Build embeddings (attachments) for a step.\n *\n * Cucumber convention: attach to the failing step, or last step if no failure.\n */\n private buildEmbeddings(\n attachments: Attachment[],\n result: StepResult | undefined,\n isLastStep: boolean,\n hasFailedStep: boolean\n ): IJsonEmbedding[] {\n const isFailed = result?.status === \"failed\";\n\n // If this step failed, attach here\n if (isFailed) {\n // Continue to attach\n } else if (isLastStep && !hasFailedStep) {\n // No failed step in scenario, attach to last step\n } else {\n // Not a failed step, and either not last or there's a failed step elsewhere\n return [];\n }\n\n // Only include BASE64 attachments - IDENTITY attachments are URLs/paths, not embeddable data\n const base64Attachments = attachments.filter(\n (att) => att.contentEncoding === \"BASE64\"\n );\n\n if (base64Attachments.length === 0) {\n return [];\n }\n\n return base64Attachments.map((att) => ({\n data: att.body,\n mime_type: att.mediaType,\n name: att.name,\n }));\n }\n\n /**\n * Build step arguments from step docs.\n *\n * Converts doc entries to Cucumber step arguments (doc strings, data tables).\n */\n private buildStepArguments(step: StoryStep): Array<{ doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> }> {\n if (!step.docs || step.docs.length === 0) {\n return [];\n }\n\n const args: Array<{ doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> }> = [];\n\n for (const doc of step.docs) {\n const arg = this.docEntryToArgument(doc);\n if (arg) {\n args.push(arg);\n }\n }\n\n return args;\n }\n\n /**\n * Convert a doc entry to a Cucumber step argument.\n */\n private docEntryToArgument(doc: DocEntry): { doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> } | null {\n switch (doc.kind) {\n case \"code\":\n return {\n doc_string: {\n content: doc.content,\n content_type: doc.lang,\n line: 0,\n },\n };\n\n case \"table\":\n return {\n rows: [\n { cells: doc.columns },\n ...doc.rows.map((row) => ({ cells: row })),\n ],\n };\n\n case \"note\":\n return {\n doc_string: {\n content: doc.text,\n content_type: \"text/plain\",\n line: 0,\n },\n };\n\n case \"mermaid\":\n return {\n doc_string: {\n content: doc.code,\n content_type: \"text/x-mermaid\",\n line: 0,\n },\n };\n\n case \"section\":\n return {\n doc_string: {\n content: `# ${doc.title}\\n\\n${doc.markdown}`,\n content_type: \"text/markdown\",\n line: 0,\n },\n };\n\n case \"link\":\n return {\n doc_string: {\n content: `[${doc.label}](${doc.url})`,\n content_type: \"text/markdown\",\n line: 0,\n },\n };\n\n case \"kv\": {\n const value = typeof doc.value === \"string\"\n ? doc.value\n : JSON.stringify(doc.value, null, 2);\n return {\n doc_string: {\n content: `${doc.label}: ${value}`,\n content_type: \"text/plain\",\n line: 0,\n },\n };\n }\n\n case \"tag\":\n return {\n doc_string: {\n content: doc.names.map((n) => `@${n}`).join(\" \"),\n content_type: \"text/plain\",\n line: 0,\n },\n };\n\n case \"custom\":\n return {\n doc_string: {\n content: JSON.stringify(doc.data, null, 2),\n content_type: \"application/json\",\n line: 0,\n },\n };\n\n case \"screenshot\":\n // Screenshots are handled as embeddings, not arguments\n return null;\n\n default:\n return null;\n }\n }\n}\n","/**\n * HTML Report Template.\n *\n * Generates the JavaScript for interactivity (theme toggle, search, collapse).\n */\n\n/** Theme-related JavaScript (only included when darkMode is enabled) */\nconst JS_THEME = `\n// Theme management\nfunction getSystemTheme() {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nfunction getEffectiveTheme() {\n const saved = localStorage.getItem('theme');\n if (saved === 'dark' || saved === 'light') return saved;\n return getSystemTheme();\n}\n\nfunction toggleTheme() {\n const current = getEffectiveTheme();\n const next = current === 'dark' ? 'light' : 'dark';\n localStorage.setItem('theme', next);\n applyTheme(next);\n}\n\nfunction applyTheme(theme) {\n document.documentElement.setAttribute('data-theme', theme);\n updateThemeIcon(theme);\n}\n\nfunction updateThemeIcon(theme) {\n const btn = document.querySelector('.theme-toggle');\n if (btn) {\n btn.textContent = theme === 'dark' ? '\\\\u2600\\\\ufe0f' : '\\\\ud83c\\\\udf19';\n btn.setAttribute('aria-label', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');\n }\n}\n\nfunction initTheme() {\n const theme = getEffectiveTheme();\n applyTheme(theme);\n\n // Listen for system theme changes\n window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {\n if (!localStorage.getItem('theme')) {\n applyTheme(e.matches ? 'dark' : 'light');\n }\n });\n}\n`;\n\n/** Core JavaScript (always included) */\nconst JS_CORE = `\n// Filter state\nvar activeTags = new Set();\nvar activeStatus = null;\nvar activeDetailLevel = 'full';\n\n// Search functionality\nfunction initSearch() {\n var input = document.querySelector('.search-input');\n if (!input) return;\n\n var debounceTimer;\n input.addEventListener('input', function() {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(function() {\n applyAllFilters();\n }, 150);\n });\n\n // Clear search on Escape\n input.addEventListener('keydown', function(e) {\n if (e.key === 'Escape') {\n e.target.value = '';\n applyAllFilters();\n }\n });\n}\n\n// Tag filter\nfunction initTagFilter() {\n var toggleBtn = document.querySelector('.tag-bar-toggle');\n var tagBar = document.querySelector('.tag-bar');\n if (toggleBtn && tagBar) {\n toggleBtn.addEventListener('click', function() {\n var isCollapsed = tagBar.classList.toggle('tag-bar-collapsed');\n toggleBtn.setAttribute('aria-expanded', String(!isCollapsed));\n });\n }\n\n document.querySelectorAll('.tag-pill').forEach(function(pill) {\n pill.addEventListener('click', function() {\n var tag = pill.dataset.tag;\n if (activeTags.has(tag)) {\n activeTags.delete(tag);\n pill.classList.remove('active');\n pill.setAttribute('aria-pressed', 'false');\n } else {\n activeTags.add(tag);\n pill.classList.add('active');\n pill.setAttribute('aria-pressed', 'true');\n }\n updateTagBarState();\n applyAllFilters();\n });\n });\n\n var clearBtn = document.querySelector('.tag-bar-clear');\n if (clearBtn) {\n clearBtn.addEventListener('click', function(e) {\n e.stopPropagation();\n activeTags.clear();\n document.querySelectorAll('.tag-pill.active').forEach(function(p) {\n p.classList.remove('active');\n p.setAttribute('aria-pressed', 'false');\n });\n updateTagBarState();\n applyAllFilters();\n });\n }\n}\n\nfunction updateTagBarState() {\n var clearBtn = document.querySelector('.tag-bar-clear');\n var countBadge = document.querySelector('.tag-bar-count');\n if (clearBtn) {\n clearBtn.style.display = activeTags.size > 0 ? '' : 'none';\n }\n if (countBadge) {\n countBadge.textContent = activeTags.size > 0 ? activeTags.size + ' selected' : '';\n }\n}\n\n// Status filter (clickable summary cards)\nfunction initStatusFilter() {\n document.querySelectorAll('.summary-card').forEach(function(card) {\n card.style.cursor = 'pointer';\n if (!card.classList.contains('passed') && !card.classList.contains('failed') && !card.classList.contains('skipped')) {\n card.addEventListener('click', function() {\n activeStatus = null;\n document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });\n applyAllFilters();\n });\n return;\n }\n card.addEventListener('click', function() {\n var status = card.classList.contains('passed') ? 'passed' :\n card.classList.contains('failed') ? 'failed' : 'skipped';\n if (activeStatus === status) {\n activeStatus = null;\n card.classList.remove('status-active');\n } else {\n activeStatus = status;\n document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });\n card.classList.add('status-active');\n }\n applyAllFilters();\n });\n });\n}\n\n// Unified filter: composes search + tags + status\nfunction applyAllFilters() {\n var searchInput = document.querySelector('.search-input');\n var searchQuery = searchInput ? searchInput.value.toLowerCase().trim() : '';\n var features = document.querySelectorAll('.feature');\n var visibleCount = 0;\n var totalCount = 0;\n\n features.forEach(function(feature) {\n var scenarios = feature.querySelectorAll('.scenario');\n var featureVisible = 0;\n\n scenarios.forEach(function(scenario) {\n totalCount++;\n var title = (scenario.querySelector('.scenario-title') || {}).textContent || '';\n title = title.toLowerCase();\n var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.toLowerCase(); });\n var steps = Array.from(scenario.querySelectorAll('.step-text')).map(function(s) { return s.textContent.toLowerCase(); });\n var statusEl = scenario.querySelector('.status-icon');\n var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :\n statusEl && statusEl.classList.contains('status-failed') ? 'failed' :\n statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';\n\n var matchesSearch = !searchQuery ||\n title.includes(searchQuery) ||\n tags.some(function(t) { return t.includes(searchQuery); }) ||\n steps.some(function(s) { return s.includes(searchQuery); });\n\n var matchesTags = activeTags.size === 0 ||\n tags.some(function(t) { return activeTags.has(t); });\n\n var matchesStatus = !activeStatus ||\n status === activeStatus ||\n (activeStatus === 'skipped' && status === 'pending');\n\n var visible = matchesSearch && matchesTags && matchesStatus;\n scenario.style.display = visible ? '' : 'none';\n if (visible) { visibleCount++; featureVisible++; }\n });\n\n feature.style.display = featureVisible > 0 ? '' : 'none';\n });\n\n updateFilterResults(visibleCount, totalCount);\n syncTocVisibility();\n writeUrlState();\n}\n\nfunction updateFilterResults(visible, total) {\n var el = document.querySelector('.filter-results');\n if (!el) return;\n var searchInput = document.querySelector('.search-input');\n var isFiltering = activeTags.size > 0 || activeStatus ||\n (searchInput && searchInput.value.trim().length > 0);\n el.style.display = isFiltering ? '' : 'none';\n var vc = el.querySelector('.visible-count');\n var tc = el.querySelector('.total-count');\n if (vc) vc.textContent = visible;\n if (tc) tc.textContent = total;\n}\n\n// Keyboard navigation\nvar focusedScenarioIndex = -1;\n\nfunction getVisibleScenarios() {\n return Array.from(document.querySelectorAll('.scenario')).filter(function(s) {\n return s.style.display !== 'none' && s.closest('.feature').style.display !== 'none';\n });\n}\n\nfunction focusScenario(index) {\n var scenarios = getVisibleScenarios();\n if (scenarios.length === 0) return;\n\n // Remove previous focus\n var prev = document.querySelector('.scenario-focused');\n if (prev) prev.classList.remove('scenario-focused');\n\n // Wrap around\n if (index < 0) index = scenarios.length - 1;\n if (index >= scenarios.length) index = 0;\n focusedScenarioIndex = index;\n\n var scenario = scenarios[index];\n scenario.classList.add('scenario-focused');\n scenario.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n}\n\nfunction showShortcutsOverlay() {\n if (document.querySelector('.shortcuts-overlay')) return;\n var overlay = document.createElement('div');\n overlay.className = 'shortcuts-overlay';\n overlay.innerHTML = '<div class=\"shortcuts-modal\">' +\n '<div class=\"shortcuts-title\">Keyboard Shortcuts</div>' +\n '<div class=\"shortcuts-grid\">' +\n '<kbd>j</kbd><span>Next scenario</span>' +\n '<kbd>k</kbd><span>Previous scenario</span>' +\n '<kbd>Enter</kbd><span>Expand/collapse scenario</span>' +\n '<kbd>Escape</kbd><span>Collapse scenario / close</span>' +\n '<kbd>/</kbd><span>Focus search</span>' +\n '<kbd>?</kbd><span>Toggle this help</span>' +\n '<kbd>e</kbd><span>Expand all</span>' +\n '<kbd>c</kbd><span>Collapse all</span>' +\n '<kbd>t</kbd><span>Toggle table of contents</span>' +\n '</div></div>';\n overlay.addEventListener('click', function(ev) {\n if (ev.target === overlay) hideShortcutsOverlay();\n });\n document.body.appendChild(overlay);\n}\n\nfunction hideShortcutsOverlay() {\n var overlay = document.querySelector('.shortcuts-overlay');\n if (overlay) overlay.remove();\n}\n\nfunction initKeyboardShortcuts() {\n document.addEventListener('keydown', function(e) {\n var tag = e.target.tagName;\n if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') {\n if (e.key === 'Escape') {\n e.target.blur();\n if (e.target.classList.contains('search-input')) {\n e.target.value = '';\n applyAllFilters();\n }\n }\n return;\n }\n\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n\n switch (e.key) {\n case 'j':\n e.preventDefault();\n focusScenario(focusedScenarioIndex + 1);\n break;\n case 'k':\n e.preventDefault();\n focusScenario(focusedScenarioIndex - 1);\n break;\n case 'Enter':\n e.preventDefault();\n var scenarios = getVisibleScenarios();\n if (focusedScenarioIndex >= 0 && focusedScenarioIndex < scenarios.length) {\n var s = scenarios[focusedScenarioIndex];\n var h = s.querySelector('.scenario-header');\n if (h) toggleCollapse(h, s);\n }\n break;\n case 'Escape':\n if (document.querySelector('.shortcuts-overlay')) {\n hideShortcutsOverlay();\n } else {\n var scenarios2 = getVisibleScenarios();\n if (focusedScenarioIndex >= 0 && focusedScenarioIndex < scenarios2.length) {\n var sc = scenarios2[focusedScenarioIndex];\n if (!sc.classList.contains('collapsed')) {\n sc.classList.add('collapsed');\n var sh = sc.querySelector('.scenario-header');\n if (sh) sh.setAttribute('aria-expanded', 'false');\n }\n }\n }\n break;\n case '/':\n e.preventDefault();\n var input = document.querySelector('.search-input');\n if (input) input.focus();\n break;\n case '?':\n e.preventDefault();\n if (document.querySelector('.shortcuts-overlay')) {\n hideShortcutsOverlay();\n } else {\n showShortcutsOverlay();\n }\n break;\n case 'e':\n e.preventDefault();\n expandAll();\n break;\n case 'c':\n e.preventDefault();\n collapseAll();\n break;\n case 't':\n e.preventDefault();\n if (typeof toggleToc === 'function') toggleToc();\n break;\n }\n });\n}\n\n// Collapse/expand functionality\nfunction toggleCollapse(header, container) {\n container?.classList.toggle('collapsed');\n const isCollapsed = container?.classList.contains('collapsed');\n header.setAttribute('aria-expanded', !isCollapsed);\n}\n\nfunction initCollapse() {\n document.querySelectorAll('.feature-header').forEach(header => {\n header.addEventListener('click', () => {\n toggleCollapse(header, header.closest('.feature'));\n });\n header.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse(header, header.closest('.feature'));\n }\n });\n });\n\n document.querySelectorAll('.scenario-header').forEach(header => {\n header.addEventListener('click', () => {\n toggleCollapse(header, header.closest('.scenario'));\n });\n header.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse(header, header.closest('.scenario'));\n }\n });\n });\n\n document.querySelectorAll('.trace-view-header').forEach(header => {\n header.addEventListener('click', () => {\n toggleCollapse(header, header.closest('.trace-view'));\n });\n header.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse(header, header.closest('.trace-view'));\n }\n });\n });\n}\n\nfunction expandAll() {\n document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {\n el.classList.remove('collapsed');\n const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');\n header?.setAttribute('aria-expanded', 'true');\n });\n}\n\nfunction collapseAll() {\n document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {\n el.classList.add('collapsed');\n const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');\n header?.setAttribute('aria-expanded', 'false');\n });\n}\n\n// Detail level toggle\nfunction toggleDetailLevel() {\n activeDetailLevel = activeDetailLevel === 'full' ? 'minimal' : 'full';\n document.documentElement.setAttribute('data-detail-level', activeDetailLevel);\n updateDetailToggle();\n writeUrlState();\n}\n\nfunction updateDetailToggle() {\n var btn = document.querySelector('.detail-toggle');\n if (btn) {\n btn.textContent = activeDetailLevel === 'full' ? '\\\\ud83d\\\\udccb' : '\\\\ud83d\\\\udcc4';\n btn.setAttribute('aria-label', activeDetailLevel === 'full' ? 'Hide documentation (minimal)' : 'Show documentation (full)');\n btn.title = activeDetailLevel === 'full' ? 'Showing full detail' : 'Showing minimal detail';\n }\n}\n\nfunction initDetailLevel() {\n updateDetailToggle();\n}\n\n// URL state sync for shareable URLs\nfunction readUrlState() {\n var params = new URLSearchParams(window.location.search);\n\n var search = params.get('search');\n if (search) {\n var input = document.querySelector('.search-input');\n if (input) input.value = search;\n }\n\n var tags = params.get('tags');\n if (tags) {\n tags.split(',').forEach(function(tag) {\n var pill = document.querySelector('.tag-pill[data-tag=\"' + tag + '\"]');\n if (pill) {\n activeTags.add(tag);\n pill.classList.add('active');\n pill.setAttribute('aria-pressed', 'true');\n }\n });\n updateTagBarState();\n }\n\n var status = params.get('status');\n if (status && ['passed', 'failed', 'skipped'].indexOf(status) !== -1) {\n activeStatus = status;\n var card = document.querySelector('.summary-card.' + status);\n if (card) card.classList.add('status-active');\n }\n\n var detail = params.get('detail');\n if (detail === 'minimal' || detail === 'full') {\n activeDetailLevel = detail;\n document.documentElement.setAttribute('data-detail-level', detail);\n updateDetailToggle();\n }\n}\n\nfunction writeUrlState() {\n var params = new URLSearchParams();\n var input = document.querySelector('.search-input');\n var search = input ? input.value.trim() : '';\n if (search) params.set('search', search);\n if (activeTags.size > 0) params.set('tags', Array.from(activeTags).sort().join(','));\n if (activeStatus) params.set('status', activeStatus);\n if (activeDetailLevel !== 'full') params.set('detail', activeDetailLevel);\n\n var qs = params.toString();\n var url = window.location.pathname + (qs ? '?' + qs : '');\n history.replaceState(null, '', url);\n}\n\n// Permalink copy\nfunction copyPermalink(anchorId) {\n var url = location.origin + location.pathname + location.search + '#' + anchorId;\n navigator.clipboard.writeText(url).then(function() {\n var el = document.getElementById(anchorId);\n if (el) showCopyToast(el);\n });\n}\n\nfunction showCopyToast(el) {\n var existing = el.querySelector('.copy-toast');\n if (existing) existing.remove();\n var toast = document.createElement('span');\n toast.className = 'copy-toast';\n toast.textContent = 'Copied!';\n var header = el.querySelector('.feature-header, .scenario-header');\n if (header) {\n header.style.position = 'relative';\n header.appendChild(toast);\n }\n setTimeout(function() { toast.remove(); }, 1500);\n}\n\n// Copy scenario as markdown\nfunction copyScenarioAsMarkdown(scenarioId) {\n var scenario = document.getElementById(scenarioId);\n if (!scenario) return;\n\n var title = (scenario.querySelector('.scenario-name') || {}).textContent || '';\n var steps = scenario.querySelectorAll('.step, .step.continuation');\n var lines = ['### Scenario: ' + title.trim(), ''];\n\n steps.forEach(function(step) {\n var keyword = step.getAttribute('data-keyword') || '';\n var text = step.getAttribute('data-text') || '';\n lines.push('- **' + keyword + '** ' + text);\n });\n\n var errorBox = scenario.querySelector('.error-message');\n if (errorBox) {\n var errorText = errorBox.textContent || '';\n lines.push('');\n lines.push('> **Error:** ' + errorText.trim());\n }\n\n var md = lines.join('\\\\n');\n navigator.clipboard.writeText(md).then(function() {\n showCopyToast(scenario);\n });\n}\n\n// Hash scroll on load\nfunction initHashScroll() {\n if (!location.hash) return;\n var target = document.querySelector(location.hash);\n if (!target) return;\n var feature = target.closest('.feature');\n if (feature && feature.classList.contains('collapsed')) {\n feature.classList.remove('collapsed');\n var fh = feature.querySelector('.feature-header');\n if (fh) fh.setAttribute('aria-expanded', 'true');\n }\n if (target.classList.contains('collapsed')) {\n target.classList.remove('collapsed');\n var sh = target.querySelector('.scenario-header');\n if (sh) sh.setAttribute('aria-expanded', 'true');\n }\n setTimeout(function() {\n target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n target.classList.add('hash-highlight');\n }, 100);\n}\n\n// Table of contents\nfunction toggleToc() {\n var sidebar = document.querySelector('.toc-sidebar');\n var wrapper = document.querySelector('.report-layout');\n if (!sidebar || !wrapper) return;\n var isMobile = window.matchMedia('(max-width: 767px)').matches;\n if (isMobile) {\n sidebar.classList.toggle('toc-mobile-open');\n } else {\n wrapper.classList.toggle('toc-hidden');\n var hidden = wrapper.classList.contains('toc-hidden');\n localStorage.setItem('toc-visible', String(!hidden));\n }\n}\n\nfunction initToc() {\n var sidebar = document.querySelector('.toc-sidebar');\n if (!sidebar) return;\n\n var saved = localStorage.getItem('toc-visible');\n var wrapper = document.querySelector('.report-layout');\n if (saved === 'false' && wrapper) {\n wrapper.classList.add('toc-hidden');\n }\n\n // Active tracking via IntersectionObserver\n var observer = new IntersectionObserver(function(entries) {\n entries.forEach(function(entry) {\n if (entry.isIntersecting) {\n var id = entry.target.id;\n if (!id) return;\n document.querySelectorAll('.toc-scenario, .toc-feature-toggle').forEach(function(el) {\n el.classList.remove('toc-active');\n });\n var tocLink = sidebar.querySelector('a[href=\"#' + id + '\"]');\n if (tocLink) tocLink.classList.add('toc-active');\n }\n });\n }, { rootMargin: '-10% 0px -80% 0px' });\n\n document.querySelectorAll('.feature, .scenario').forEach(function(el) {\n if (el.id) observer.observe(el);\n });\n\n // Click navigation: expand collapsed parents\n sidebar.querySelectorAll('.toc-scenario').forEach(function(link) {\n link.addEventListener('click', function(e) {\n var hash = link.getAttribute('href');\n if (!hash) return;\n var target = document.querySelector(hash);\n if (!target) return;\n var feature = target.closest('.feature');\n if (feature && feature.classList.contains('collapsed')) {\n feature.classList.remove('collapsed');\n var fh = feature.querySelector('.feature-header');\n if (fh) fh.setAttribute('aria-expanded', 'true');\n }\n if (target.classList.contains('collapsed')) {\n target.classList.remove('collapsed');\n var sh = target.querySelector('.scenario-header');\n if (sh) sh.setAttribute('aria-expanded', 'true');\n }\n });\n });\n}\n\n// Theme picker\nfunction initThemePicker() {\n var picker = document.querySelector('.theme-picker');\n if (!picker) return;\n\n var saved = localStorage.getItem('report-theme');\n if (saved) {\n picker.value = saved;\n switchReportTheme(saved);\n }\n\n picker.addEventListener('change', function(e) {\n switchReportTheme(e.target.value);\n localStorage.setItem('report-theme', e.target.value);\n });\n}\n\nfunction switchReportTheme(name) {\n document.querySelectorAll('style[data-theme-name]').forEach(function(s) {\n s.disabled = s.dataset.themeName !== name;\n });\n}\n\n// Sync TOC visibility with filters\nfunction syncTocVisibility() {\n var sidebar = document.querySelector('.toc-sidebar');\n if (!sidebar) return;\n\n sidebar.querySelectorAll('.toc-scenario').forEach(function(link) {\n var href = link.getAttribute('href');\n if (!href) return;\n var target = document.querySelector(href);\n link.style.display = (target && target.style.display !== 'none') ? '' : 'none';\n });\n\n sidebar.querySelectorAll('.toc-feature').forEach(function(feature) {\n var visibleScenarios = feature.querySelectorAll('.toc-scenario');\n var anyVisible = Array.from(visibleScenarios).some(function(s) {\n return s.style.display !== 'none';\n });\n feature.style.display = anyVisible ? '' : 'none';\n });\n}\n`;\n\n/** Options for HTML template generation */\nexport interface HtmlTemplateOptions {\n includeSearch?: boolean;\n includeDarkMode?: boolean;\n syntaxHighlighting?: boolean;\n mermaidEnabled?: boolean;\n markdownEnabled?: boolean;\n /** Additional inline JS injected after core JS (used by themes). */\n additionalJs?: string;\n /** Additional ESM import statements for CDN libraries (used by themes). */\n additionalImports?: string[];\n /** Pre-rendered TOC sidebar HTML. Placed as sibling of .container inside .report-layout. */\n tocHtml?: string;\n /** Pre-rendered theme picker HTML (select element). */\n themePickerHtml?: string;\n /** Additional theme CSS blocks to embed (for theme picker). */\n additionalThemeCss?: Array<{ name: string; label: string; css: string }>;\n /** Name of the currently active theme (for data-theme-name attribute). */\n activeThemeName?: string;\n}\n\n/** JavaScript for markdown parsing (used as a function body string in the ESM module) */\nconst JS_MARKDOWN_FN = `\nfunction parseMarkdownSections(marked) {\n // Configure marked for safe output\n marked.setOptions({\n breaks: true,\n gfm: true\n });\n\n document.querySelectorAll('.doc-section-content[data-markdown]').forEach(el => {\n const encoded = el.getAttribute('data-markdown');\n if (!encoded) return;\n\n try {\n const markdown = decodeURIComponent(atob(encoded));\n // Use marked.parse and sanitize by escaping script tags\n let html = marked.parse(markdown);\n // Basic XSS prevention - remove script tags\n html = html.replace(/<script\\\\b[^<]*(?:(?!<\\\\/script>)<[^<]*)*<\\\\/script>/gi, '');\n el.innerHTML = html;\n el.removeAttribute('data-markdown');\n } catch (e) {\n console.warn('Failed to parse markdown:', e);\n }\n });\n}\n`;\n\n/** Generate the inline JavaScript for the report (non-CDN parts) */\nfunction generateScript(options: HtmlTemplateOptions): string {\n const initCalls: string[] = [];\n\n if (options.includeDarkMode) {\n initCalls.push('initTheme();');\n }\n initCalls.push('readUrlState();');\n initCalls.push('initSearch();');\n initCalls.push('initTagFilter();');\n initCalls.push('initStatusFilter();');\n initCalls.push('initKeyboardShortcuts();');\n initCalls.push('initCollapse();');\n initCalls.push('initDetailLevel();');\n initCalls.push('applyAllFilters();');\n initCalls.push('initHashScroll();');\n initCalls.push('initToc();');\n initCalls.push('initThemePicker();');\n\n const initScript = `\n// Initialize on load\ndocument.addEventListener('DOMContentLoaded', () => {\n ${initCalls.join('\\n ')}\n});\n`;\n\n let script = options.includeDarkMode ? JS_THEME : '';\n script += JS_CORE;\n if (options.additionalJs) {\n script += options.additionalJs;\n }\n script += initScript;\n\n return script;\n}\n\n/** Generate ESM module script that imports CDN libraries and initializes them */\nfunction generateEsmScript(options: HtmlTemplateOptions): string {\n const imports: string[] = [];\n const initCalls: string[] = [];\n\n if (options.syntaxHighlighting) {\n imports.push('import hljs from \"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js\";');\n initCalls.push('hljs.highlightAll();');\n }\n\n if (options.mermaidEnabled) {\n imports.push('import mermaid from \"https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs\";');\n initCalls.push(\"mermaid.initialize({ startOnLoad: false, theme: 'neutral' });\");\n initCalls.push('await mermaid.run({ querySelector: \".mermaid\" });');\n }\n\n if (options.markdownEnabled) {\n imports.push('import { marked } from \"https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js\";');\n initCalls.push('parseMarkdownSections(marked);');\n }\n\n if (options.additionalImports) {\n imports.push(...options.additionalImports);\n }\n\n if (imports.length === 0) return '';\n\n let script = imports.join('\\n ');\n if (options.markdownEnabled) {\n script += '\\n' + JS_MARKDOWN_FN;\n }\n script += '\\n ' + initCalls.join('\\n ');\n\n return `\\n <script type=\"module\">\\n ${script}\\n </script>`;\n}\n\n/**\n * Generate the HTML template for the report.\n */\nexport function generateHtmlTemplate(\n title: string,\n styles: string,\n body: string,\n options: HtmlTemplateOptions = {}\n): string {\n const {\n includeSearch = true,\n includeDarkMode = true,\n syntaxHighlighting = true,\n mermaidEnabled = true,\n markdownEnabled = true,\n } = options;\n\n const script = generateScript(options);\n\n // Set initial theme to light; initTheme() will update based on system/localStorage\n const themeAttr = includeDarkMode ? ' data-theme=\"light\"' : '';\n\n // CDN stylesheet resources for optional features\n const cdnStyles: string[] = [];\n\n if (syntaxHighlighting) {\n cdnStyles.push('<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css\">');\n cdnStyles.push('<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css\" media=\"(prefers-color-scheme: dark)\">');\n }\n\n const cdnStylesHtml = cdnStyles.length > 0 ? '\\n ' + cdnStyles.join('\\n ') : '';\n const esmScriptHtml = generateEsmScript(options);\n\n const additionalThemeStyles = (options.additionalThemeCss ?? [])\n .map(t => `<style data-theme-name=\"${escapeHtml(t.name)}\" disabled>${t.css}</style>`)\n .join('\\n ');\n\n return `<!DOCTYPE html>\n<html lang=\"en\"${themeAttr} data-detail-level=\"full\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"color-scheme\" content=\"light dark\">\n <title>${escapeHtml(title)}</title>${cdnStylesHtml}\n <style${options.additionalThemeCss ? ` data-theme-name=\"${escapeHtml(options.activeThemeName ?? 'default')}\"` : ''}>${styles}</style>\n ${additionalThemeStyles}\n</head>\n<body>\n <div class=\"report-layout\">\n ${options.tocHtml ?? ''}\n <div class=\"main-content\">\n <div class=\"container\">\n <header class=\"header\">\n <h1>${escapeHtml(title)}</h1>\n <div class=\"header-actions\">\n <button type=\"button\" class=\"toc-toggle\" onclick=\"toggleToc()\" aria-label=\"Toggle table of contents\" title=\"Toggle contents\">&#x2630;</button>\n ${includeSearch ? '<input type=\"text\" class=\"search-input\" placeholder=\"Search scenarios...\" aria-label=\"Search scenarios\">' : ''}\n <button type=\"button\" class=\"detail-toggle\" onclick=\"toggleDetailLevel()\" aria-label=\"Toggle detail level\" title=\"Toggle documentation detail\"></button>\n ${options.themePickerHtml ?? ''}\n ${includeDarkMode ? '<button type=\"button\" class=\"theme-toggle\" onclick=\"toggleTheme()\" aria-label=\"Toggle theme\"></button>' : ''}\n </div>\n </header>\n ${body}\n </div>\n </div>\n </div>\n <script>${script}</script>${esmScriptHtml}\n</body>\n</html>`;\n}\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n","/**\n * HTML Report Styles.\n *\n * Modern, clean CSS inspired by shadcn/ui base theme with Cucumber branding.\n * Supports dark/light mode via CSS custom properties and prefers-color-scheme.\n */\n\nexport const CSS_STYLES = `\n/* ============================================================================\n Google Fonts Import - IBM Plex for refined typography\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Default)\n Cucumber-branded shadcn/ui base theme\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"IBM Plex Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"IBM Plex Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors (shadcn base with cucumber accent) */\n --background: hsl(0 0% 100%);\n --foreground: hsl(0 0% 9%);\n --card: hsl(0 0% 100%);\n --card-foreground: hsl(0 0% 9%);\n --popover: hsl(0 0% 100%);\n --popover-foreground: hsl(0 0% 9%);\n\n /* Cucumber green as primary */\n --primary: hsl(145 63% 42%);\n --primary-foreground: hsl(0 0% 100%);\n\n --secondary: hsl(0 0% 96.5%);\n --secondary-foreground: hsl(0 0% 9%);\n --muted: hsl(0 0% 96.5%);\n --muted-foreground: hsl(0 0% 45%);\n --accent: hsl(0 0% 96.5%);\n --accent-foreground: hsl(0 0% 9%);\n --destructive: hsl(0 84% 60%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 90%);\n --input: hsl(0 0% 90%);\n --ring: hsl(145 63% 42%);\n --radius: 0.5rem;\n\n /* Shadows - refined for depth */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.03);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.06);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.07), 0 2px 4px -2px rgb(0 0 0 / 0.05);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.05);\n\n /* Status colors - cucumber-harmonized */\n --success: hsl(145 63% 42%);\n --success-light: hsl(145 55% 96%);\n --success-border: hsl(145 55% 88%);\n --error: hsl(0 72% 51%);\n --error-light: hsl(0 86% 97%);\n --error-border: hsl(0 72% 92%);\n --warning: hsl(38 92% 50%);\n --warning-light: hsl(48 100% 96%);\n --warning-border: hsl(48 96% 88%);\n --pending: hsl(262 60% 55%);\n --pending-light: hsl(262 55% 97%);\n --pending-border: hsl(262 55% 90%);\n\n /* Cucumber-specific */\n --keyword-color: hsl(145 63% 32%);\n --tag-bg: hsl(145 55% 95%);\n --tag-color: hsl(145 63% 30%);\n --tag-border: hsl(145 55% 85%);\n --step-param-color: hsl(220 70% 50%);\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: hsl(0 0% 98%);\n --accordion-content-bg: hsl(0 0% 98.5%);\n}\n\n/* ============================================================================\n Dark Mode - Cucumber branded\n ============================================================================ */\n[data-theme=\"dark\"] {\n --background: hsl(0 0% 6%);\n --foreground: hsl(0 0% 95%);\n --card: hsl(0 0% 9%);\n --card-foreground: hsl(0 0% 95%);\n --popover: hsl(0 0% 9%);\n --popover-foreground: hsl(0 0% 95%);\n\n /* Cucumber green stays vibrant in dark mode */\n --primary: hsl(145 63% 50%);\n --primary-foreground: hsl(0 0% 6%);\n\n --secondary: hsl(0 0% 13%);\n --secondary-foreground: hsl(0 0% 95%);\n --muted: hsl(0 0% 13%);\n --muted-foreground: hsl(0 0% 55%);\n --accent: hsl(0 0% 13%);\n --accent-foreground: hsl(0 0% 95%);\n --destructive: hsl(0 72% 55%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 16%);\n --input: hsl(0 0% 16%);\n --ring: hsl(145 63% 50%);\n\n /* Shadows (subtle for dark mode) */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.35);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.35);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.45), 0 4px 6px -4px rgb(0 0 0 / 0.35);\n\n /* Status colors (dark mode) */\n --success: hsl(145 63% 55%);\n --success-light: hsl(145 35% 14%);\n --success-border: hsl(145 35% 22%);\n --error: hsl(0 72% 60%);\n --error-light: hsl(0 35% 14%);\n --error-border: hsl(0 35% 22%);\n --warning: hsl(38 92% 55%);\n --warning-light: hsl(38 35% 14%);\n --warning-border: hsl(38 35% 22%);\n --pending: hsl(262 60% 65%);\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n\n /* Cucumber-specific (dark) */\n --keyword-color: hsl(145 63% 60%);\n --tag-bg: hsl(145 35% 14%);\n --tag-color: hsl(145 63% 60%);\n --tag-border: hsl(145 35% 22%);\n --step-param-color: hsl(220 70% 70%);\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: hsl(0 0% 11%);\n --accordion-content-bg: hsl(0 0% 7%);\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: hsl(0 0% 6%);\n --foreground: hsl(0 0% 95%);\n --card: hsl(0 0% 9%);\n --card-foreground: hsl(0 0% 95%);\n --popover: hsl(0 0% 9%);\n --popover-foreground: hsl(0 0% 95%);\n --primary: hsl(145 63% 50%);\n --primary-foreground: hsl(0 0% 6%);\n --secondary: hsl(0 0% 13%);\n --secondary-foreground: hsl(0 0% 95%);\n --muted: hsl(0 0% 13%);\n --muted-foreground: hsl(0 0% 55%);\n --accent: hsl(0 0% 13%);\n --accent-foreground: hsl(0 0% 95%);\n --destructive: hsl(0 72% 55%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 16%);\n --input: hsl(0 0% 16%);\n --ring: hsl(145 63% 50%);\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.35);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.35);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.45), 0 4px 6px -4px rgb(0 0 0 / 0.35);\n --success: hsl(145 63% 55%);\n --success-light: hsl(145 35% 14%);\n --success-border: hsl(145 35% 22%);\n --error: hsl(0 72% 60%);\n --error-light: hsl(0 35% 14%);\n --error-border: hsl(0 35% 22%);\n --warning: hsl(38 92% 55%);\n --warning-light: hsl(38 35% 14%);\n --warning-border: hsl(38 35% 22%);\n --pending: hsl(262 60% 65%);\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n --keyword-color: hsl(145 63% 60%);\n --tag-bg: hsl(145 35% 14%);\n --tag-color: hsl(145 63% 60%);\n --tag-border: hsl(145 35% 22%);\n --step-param-color: hsl(220 70% 70%);\n --accordion-header-hover: hsl(0 0% 11%);\n --accordion-content-bg: hsl(0 0% 7%);\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 14px;\n line-height: 1.6;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout\n ============================================================================ */\n.container {\n max-width: 1100px;\n margin: 0 auto;\n padding: 1.25rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 2rem 2.5rem;\n }\n}\n\n/* ============================================================================\n Header - shadcn style\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1.25rem;\n margin-bottom: 1.5rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-size: 1.375rem;\n font-weight: 600;\n letter-spacing: -0.02em;\n color: var(--foreground);\n}\n\n.header-actions {\n display: flex;\n gap: 0.625rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle - shadcn button style\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Search Input - shadcn input style\n ============================================================================ */\n.search-input {\n height: 2.25rem;\n padding: 0 0.875rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.875rem;\n width: 220px;\n transition: all 0.15s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 3px hsl(145 63% 42% / 0.1);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 260px;\n }\n}\n\n/* ============================================================================\n Meta Info - clean card style\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 1.75rem;\n margin-bottom: 1.25rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 500;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Summary Cards - tinted card style\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 1rem 1.25rem;\n transition: all 0.15s ease;\n}\n\n.summary-card:hover {\n box-shadow: var(--shadow-sm);\n}\n\n.summary-card .label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.375rem;\n}\n\n.summary-card .value {\n font-size: 2rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n font-family: var(--font-sans);\n}\n\n/* Passed - green tint */\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed - red tint */\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped - amber tint */\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending - purple tint */\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 1rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.6875rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--destructive, #dc2626);\n background: var(--destructive-light, #fef2f2);\n border: 1px solid var(--destructive-border, #fecaca);\n cursor: pointer;\n padding: 0.25rem 0.75rem;\n border-radius: var(--radius);\n transition: all 0.15s ease;\n}\n\n.tag-bar-clear:hover {\n background: var(--destructive-border, #fecaca);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.5rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.75rem;\n font-weight: 500;\n padding: 0.25rem 0.625rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.tag-pill:hover {\n background: var(--success-border);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n margin-bottom: 1rem;\n font-weight: 500;\n}\n\n/* ============================================================================\n Feature Sections - shadcn accordion style\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.625rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 0.875rem 1rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 600;\n font-size: 0.9375rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 0.8125rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.625rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios - nested accordion style\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n margin-bottom: 0.5rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.75rem 1rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 500;\n font-size: 0.875rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.375rem;\n}\n\n.tag {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.scenario-duration {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.75rem 1rem 1rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons - refined\n ============================================================================ */\n.status-icon {\n font-size: 0.875rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps - story-like flow\n ============================================================================ */\n.steps {\n margin-top: 0.25rem;\n padding: 0.25rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.5rem;\n padding: 0.375rem 0;\n font-size: 0.8125rem;\n align-items: baseline;\n line-height: 1.5;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1rem;\n text-align: center;\n font-size: 0.75rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 52px;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* Indent continuation keywords (And, But, *) to show they belong to previous step */\n.step.continuation {\n padding-left: 1.25rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 500;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.7;\n}\n\n/* ============================================================================\n Error Display - alert style\n ============================================================================ */\n.error-box {\n margin-top: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments - badge style\n ============================================================================ */\n.attachments {\n margin-top: 0.75rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n font-size: 0.75rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.15s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon - smooth rotation\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars - subtle styling\n ============================================================================ */\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States - cucumber ring color\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection - cucumber tinted\n ============================================================================ */\n::selection {\n background: hsl(145 63% 42% / 0.15);\n color: inherit;\n}\n\n/* ============================================================================\n Animations - smooth reveals\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.2s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.02s; }\n.feature:nth-child(3) { animation-delay: 0.04s; }\n.feature:nth-child(4) { animation-delay: 0.06s; }\n.feature:nth-child(5) { animation-delay: 0.08s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #e5e5e5;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body {\n font-size: 12px;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.75rem;\n padding: 0.75rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.5rem;\n margin-top: 0.25rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0.5rem;\n background: var(--muted);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n font-size: 0.8125rem;\n line-height: 1.5;\n color: var(--foreground);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 0.5rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 0.375rem;\n font-size: 0.8125rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.125rem 0.375rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.5rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.75rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.5rem 0.75rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.375rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.8125rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.15s ease;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"→\";\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.5rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-size: 0.8125rem;\n line-height: 1.6;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n margin-top: 1em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.25rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.8125rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.125em 0.375em;\n background: var(--muted);\n border-radius: 3px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.75em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 2px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.5em 1em;\n border-left: 3px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.8125rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 2px);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid (displayed as code)\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"◇ \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.5rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.375rem;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.375rem 0.75rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Children\n ============================================================================ */\n.doc-children {\n margin-left: 1rem;\n padding-left: 1rem;\n border-left: 2px solid var(--border);\n margin-top: 0.25rem;\n}\n\n/* ============================================================================\n Trace View - OTel span waterfall\n ============================================================================ */\n.trace-view {\n margin-top: 0.75rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-size: 0.8125rem;\n font-weight: 500;\n color: var(--foreground);\n transition: background-color 0.15s ease;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 1px solid var(--border);\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.375rem;\n margin-bottom: 0.375rem;\n border-bottom: 1px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.1875rem 0;\n font-size: 0.75rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1.25rem;\n background: var(--muted);\n border-radius: 2px;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 2px;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.375rem;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: white;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75em; font-weight: 600; margin-left: 4px; vertical-align: middle; }\n.badge-grade { color: #fff; }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #2196F3; }\n.badge-grade-C { background: #FF9800; }\n.badge-grade-D { background: #f44336; }\n.badge-grade-F { background: #9E0000; }\n.badge-flaky { background: #FF9800; color: #fff; }\n.badge-perf { font-size: 0.7em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* Failure summary */\n.failure-summary {\n margin: 1rem 0;\n padding: 0.75rem 1rem;\n border: 1px solid var(--error);\n border-radius: 0.5rem;\n background: color-mix(in srgb, var(--error) 8%, transparent);\n}\n.failure-summary-header {\n font-weight: 600;\n font-size: 0.875rem;\n color: var(--error);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n.failure-summary li a {\n font-size: 0.8125rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* Source permalink */\n.source-link {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Detail Level Toggle\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.detail-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n\n/* ============================================================================\n Permalink Anchors\n ============================================================================ */\n.permalink-anchor {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n border: none;\n background: none;\n color: var(--muted-foreground);\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.15s ease;\n font-size: 0.875rem;\n font-weight: 600;\n padding: 0;\n flex-shrink: 0;\n}\n\n.feature-header:hover .permalink-anchor,\n.scenario-header:hover .permalink-anchor,\n.permalink-anchor:focus-visible {\n opacity: 1;\n}\n\n.permalink-anchor:hover {\n color: var(--primary);\n}\n\n.copy-toast {\n position: absolute;\n right: 0.5rem;\n top: 50%;\n transform: translateY(-50%);\n background: var(--foreground);\n color: var(--background);\n padding: 0.25rem 0.5rem;\n border-radius: var(--radius);\n font-size: 0.75rem;\n font-weight: 500;\n pointer-events: none;\n animation: fadeOut 1.5s ease forwards;\n z-index: 10;\n}\n\n@keyframes fadeOut {\n 0%, 70% { opacity: 1; }\n 100% { opacity: 0; }\n}\n\n.hash-highlight {\n animation: hashPulse 2s ease;\n}\n\n@keyframes hashPulse {\n 0%, 100% { background: transparent; }\n 20% { background: color-mix(in srgb, var(--primary) 12%, transparent); }\n}\n\n.scenario-actions {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n flex-shrink: 0;\n}\n\n.copy-scenario-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n border: none;\n background: none;\n color: var(--muted-foreground);\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.15s ease;\n font-size: 0.875rem;\n padding: 0;\n flex-shrink: 0;\n}\n\n.scenario-header:hover .copy-scenario-btn,\n.copy-scenario-btn:focus-visible {\n opacity: 1;\n}\n\n.copy-scenario-btn:hover {\n color: var(--primary);\n}\n\n/* ============================================================================\n Keyboard Navigation\n ============================================================================ */\n.scenario-focused {\n border-left: 2px solid var(--primary);\n}\n\n.shortcuts-overlay {\n position: fixed;\n inset: 0;\n background: rgb(0 0 0 / 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n}\n\n.shortcuts-modal {\n background: var(--card);\n color: var(--card-foreground);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) * 2);\n padding: 1.5rem 2rem;\n max-width: 400px;\n width: 90vw;\n box-shadow: var(--shadow-md, 0 4px 12px rgb(0 0 0 / 0.15));\n}\n\n.shortcuts-title {\n font-weight: 600;\n font-size: 1.125rem;\n margin-bottom: 1rem;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid var(--border);\n}\n\n.shortcuts-grid {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 0.5rem 1rem;\n align-items: center;\n}\n\n.shortcuts-grid kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.75rem;\n padding: 0.125rem 0.375rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) * 0.5);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.shortcuts-grid span {\n font-size: 0.875rem;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Table of Contents Sidebar\n ============================================================================ */\n.report-layout {\n display: flex;\n min-height: 100vh;\n}\n\n.report-layout.toc-hidden .toc-sidebar {\n display: none;\n}\n\n.main-content {\n flex: 1;\n min-width: 0;\n}\n\n.toc-sidebar {\n width: 260px;\n flex-shrink: 0;\n position: sticky;\n top: 0;\n height: 100vh;\n overflow-y: auto;\n border-right: 1px solid var(--border);\n background: var(--card);\n padding: 1rem 0;\n font-size: 0.8125rem;\n}\n\n.toc-header {\n padding: 0 1rem 0.75rem;\n border-bottom: 1px solid var(--border);\n margin-bottom: 0.5rem;\n}\n\n.toc-title {\n font-weight: 600;\n font-size: 0.875rem;\n color: var(--foreground);\n}\n\n.toc-feature {\n margin-bottom: 0.25rem;\n}\n\n.toc-feature-toggle {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 0.375rem 1rem;\n border: none;\n background: none;\n text-align: left;\n cursor: pointer;\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--foreground);\n font-family: var(--font-sans);\n}\n\n.toc-feature-toggle:hover {\n background: var(--accent);\n}\n\n.toc-feature-toggle[aria-expanded=\"false\"] + .toc-scenarios {\n display: none;\n}\n\n.toc-scenarios {\n display: flex;\n flex-direction: column;\n}\n\n.toc-scenario {\n display: flex;\n align-items: baseline;\n gap: 0.375rem;\n padding: 0.25rem 1rem 0.25rem 1.5rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-size: 0.8125rem;\n line-height: 1.4;\n border-left: 2px solid transparent;\n transition: all 0.1s ease;\n}\n\n.toc-scenario:hover {\n color: var(--foreground);\n background: var(--accent);\n}\n\n.toc-scenario.toc-active {\n color: var(--foreground);\n border-left-color: var(--primary);\n font-weight: 500;\n}\n\n.toc-scenario.toc-failed {\n border-left-color: var(--error, var(--destructive));\n}\n\n.toc-status {\n flex-shrink: 0;\n font-size: 0.75rem;\n}\n\n.toc-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.toc-toggle:hover {\n background: var(--accent);\n}\n\n/* Mobile: overlay sidebar */\n@media (max-width: 767px) {\n .toc-sidebar {\n position: fixed;\n left: 0;\n top: 0;\n z-index: 50;\n box-shadow: var(--shadow-sm, 0 1px 3px rgb(0 0 0 / 0.1));\n transform: translateX(-100%);\n transition: transform 0.2s ease;\n }\n\n .toc-sidebar.toc-mobile-open {\n transform: translateX(0);\n }\n}\n\n/* ============================================================================\n Theme Picker\n ============================================================================ */\n.theme-picker {\n height: 2.25rem;\n padding: 0 0.5rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-size: 0.8125rem;\n font-family: var(--font-sans);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.theme-picker:hover {\n background: var(--accent);\n}\n\n.theme-picker:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n`;\n","/**\n * Default theme — wraps the existing CSS_STYLES.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\nimport { CSS_STYLES } from \"../styles.js\";\n\nexport const defaultTheme: HtmlTheme = {\n name: \"default\",\n label: \"Default\",\n css: CSS_STYLES,\n};\n","/**\n * Corporate theme — editorial/magazine feel with serif typography and navy palette.\n * Two-pane layout: fixed sidebar with TOC navigation + main content area.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\nimport type { BuildBodyArgs, BuildBodyDeps } from \"../renderers/body.js\";\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nfunction corporateBuildBody(args: BuildBodyArgs, deps: BuildBodyDeps): string {\n const { run } = args;\n\n // --- Build sidebar content ---\n const total = run.testCases.length;\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n\n const tocItems: string[] = [];\n let featureIndex = 0;\n for (const [file, testCases] of byFile) {\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const fPassed = testCases.filter((tc) => tc.status === \"passed\").length;\n const fFailed = testCases.filter((tc) => tc.status === \"failed\").length;\n const statusDot = fFailed > 0 ? \"dot-failed\" : \"dot-passed\";\n\n tocItems.push(\n `<a class=\"toc-item\" href=\"#corporate-feature-${featureIndex}\" data-feature-index=\"${featureIndex}\">` +\n `<span class=\"toc-dot ${statusDot}\"></span>` +\n `<span class=\"toc-label\">${escapeForAttr(featureName)}</span>` +\n `<span class=\"toc-count\">${fPassed}/${testCases.length}</span>` +\n `</a>`,\n );\n featureIndex++;\n }\n\n const passRate = total > 0 ? Math.round((passed / total) * 100) : 0;\n\n const sidebar = `\n<nav class=\"toc\">\n <div class=\"toc-header\">\n <div class=\"toc-title\">Test Report</div>\n <div class=\"toc-stats\">\n <div class=\"toc-stat-row\">\n <span class=\"toc-stat-label\">Total</span>\n <span class=\"toc-stat-value\">${total}</span>\n </div>\n <div class=\"toc-stat-row\">\n <span class=\"toc-stat-label\">Passed</span>\n <span class=\"toc-stat-value toc-stat-passed\">${passed}</span>\n </div>\n <div class=\"toc-stat-row\">\n <span class=\"toc-stat-label\">Failed</span>\n <span class=\"toc-stat-value toc-stat-failed\">${failed}</span>\n </div>\n <div class=\"toc-stat-row\">\n <span class=\"toc-stat-label\">Skipped</span>\n <span class=\"toc-stat-value toc-stat-skipped\">${skipped}</span>\n </div>\n <div class=\"toc-progress\">\n <div class=\"toc-progress-bar\" style=\"width: ${passRate}%\"></div>\n </div>\n <div class=\"toc-pass-rate\">${passRate}% pass rate</div>\n </div>\n </div>\n <div class=\"toc-nav\">\n <div class=\"toc-nav-label\">Features</div>\n ${tocItems.join(\"\\n \")}\n </div>\n</nav>`;\n\n // --- Build main content ---\n const mainParts: string[] = [];\n\n mainParts.push(\n deps.renderMetaInfo(\n {\n startedAtMs: run.startedAtMs,\n durationMs: run.durationMs,\n packageVersion: run.packageVersion,\n gitSha: run.gitSha,\n ciName: run.ci?.name,\n ciBranch: run.ci?.branch,\n ciUrl: run.ci?.url,\n ciCommitSha: run.ci?.commitSha,\n ciBuildNumber: run.ci?.buildNumber,\n },\n deps.metaDeps,\n ),\n );\n\n mainParts.push(\n deps.renderSummary(\n { total, passed, failed, skipped },\n deps.summaryDeps,\n ),\n );\n\n const allTags = [\n ...new Set(run.testCases.flatMap((tc) => tc.tags)),\n ].sort();\n mainParts.push(\n deps.renderTagBar(\n { tags: allTags, totalScenarios: total },\n deps.tagBarDeps,\n ),\n );\n\n const failedCases = run.testCases.filter((tc) => tc.status === \"failed\");\n if (failedCases.length > 0) {\n mainParts.push(\n deps.renderFailureSummary(\n { failedCases },\n deps.failureSummaryDeps,\n ),\n );\n }\n\n featureIndex = 0;\n for (const [file, testCases] of byFile) {\n const featureHtml = deps.renderFeature(\n { file, testCases, metricsMap: args.metricsMap },\n deps.featureDeps,\n );\n // Wrap each feature with an ID anchor for sidebar navigation\n mainParts.push(\n `<div id=\"corporate-feature-${featureIndex}\">${featureHtml}</div>`,\n );\n featureIndex++;\n }\n\n return `<div class=\"corporate-layout\">${sidebar}<main class=\"corporate-main\">${mainParts.join(\"\\n\")}</main></div>`;\n}\n\n/** Minimal HTML-safe escaping for attribute values in sidebar */\nfunction escapeForAttr(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\nconst CORPORATE_CSS = `\n/* ============================================================================\n Google Fonts Import — Playfair Display, Source Serif 4, DM Sans\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Playfair+Display:wght@400;500;600;700&family=Source+Serif+4:wght@400;500;600&display=swap');\n\n/* ============================================================================\n CSS Custom Properties — Light Mode (Default)\n Navy palette with editorial serif typography\n ============================================================================ */\n:root {\n /* Typography */\n --font-heading: \"Playfair Display\", Georgia, \"Times New Roman\", serif;\n --font-body: \"Source Serif 4\", Georgia, \"Times New Roman\", serif;\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"DM Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors — warm ivory background, navy accents */\n --background: #faf9f7;\n --foreground: #1a202c;\n --card: #ffffff;\n --card-foreground: #1a202c;\n --popover: #ffffff;\n --popover-foreground: #1a202c;\n\n /* Navy as primary */\n --primary: #1a365d;\n --primary-foreground: #ffffff;\n\n --secondary: #f0ede8;\n --secondary-foreground: #1a202c;\n --muted: #f0ede8;\n --muted-foreground: #64748b;\n --accent: #eee9e0;\n --accent-foreground: #1a202c;\n --destructive: #b91c1c;\n --destructive-foreground: #ffffff;\n --border: #d6d0c4;\n --input: #d6d0c4;\n --ring: #1a365d;\n --radius: 0.375rem;\n\n /* Shadows */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.04);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.04);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.06), 0 2px 4px -2px rgb(0 0 0 / 0.04);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.07), 0 4px 6px -4px rgb(0 0 0 / 0.04);\n\n /* Status colors */\n --success: #166534;\n --success-light: #f0fdf4;\n --success-border: #bbf7d0;\n --error: #b91c1c;\n --error-light: #fef2f2;\n --error-border: #fecaca;\n --warning: #a16207;\n --warning-light: #fefce8;\n --warning-border: #fef08a;\n --pending: #6d28d9;\n --pending-light: #f5f3ff;\n --pending-border: #ddd6fe;\n\n /* Theme-specific */\n --keyword-color: #1a365d;\n --tag-bg: #eff6ff;\n --tag-color: #1e40af;\n --tag-border: #bfdbfe;\n --step-param-color: #7c3aed;\n\n /* Accordion/Collapsible */\n --accordion-header-hover: #f5f2ed;\n --accordion-content-bg: #faf8f5;\n}\n\n/* ============================================================================\n Dark Mode — Navy palette\n ============================================================================ */\n[data-theme=\"dark\"] {\n --font-heading: \"Playfair Display\", Georgia, \"Times New Roman\", serif;\n --font-body: \"Source Serif 4\", Georgia, \"Times New Roman\", serif;\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"DM Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #111827;\n --foreground: #f1f5f9;\n --card: #1e293b;\n --card-foreground: #f1f5f9;\n --popover: #1e293b;\n --popover-foreground: #f1f5f9;\n\n --primary: #93c5fd;\n --primary-foreground: #0f172a;\n\n --secondary: #1e293b;\n --secondary-foreground: #f1f5f9;\n --muted: #1e293b;\n --muted-foreground: #94a3b8;\n --accent: #1e293b;\n --accent-foreground: #f1f5f9;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #334155;\n --input: #334155;\n --ring: #93c5fd;\n\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);\n\n --success: #4ade80;\n --success-light: hsl(145 30% 12%);\n --success-border: hsl(145 30% 22%);\n --error: #f87171;\n --error-light: hsl(0 30% 12%);\n --error-border: hsl(0 30% 22%);\n --warning: #fbbf24;\n --warning-light: hsl(38 30% 12%);\n --warning-border: hsl(38 30% 22%);\n --pending: #a78bfa;\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n\n --keyword-color: #93c5fd;\n --tag-bg: hsl(220 40% 15%);\n --tag-color: #93c5fd;\n --tag-border: hsl(220 30% 25%);\n --step-param-color: #c4b5fd;\n\n --accordion-header-hover: #253347;\n --accordion-content-bg: #162033;\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: #111827;\n --foreground: #f1f5f9;\n --card: #1e293b;\n --card-foreground: #f1f5f9;\n --popover: #1e293b;\n --popover-foreground: #f1f5f9;\n --primary: #93c5fd;\n --primary-foreground: #0f172a;\n --secondary: #1e293b;\n --secondary-foreground: #f1f5f9;\n --muted: #1e293b;\n --muted-foreground: #94a3b8;\n --accent: #1e293b;\n --accent-foreground: #f1f5f9;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #334155;\n --input: #334155;\n --ring: #93c5fd;\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);\n --success: #4ade80;\n --success-light: hsl(145 30% 12%);\n --success-border: hsl(145 30% 22%);\n --error: #f87171;\n --error-light: hsl(0 30% 12%);\n --error-border: hsl(0 30% 22%);\n --warning: #fbbf24;\n --warning-light: hsl(38 30% 12%);\n --warning-border: hsl(38 30% 22%);\n --pending: #a78bfa;\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n --keyword-color: #93c5fd;\n --tag-bg: hsl(220 40% 15%);\n --tag-color: #93c5fd;\n --tag-border: hsl(220 30% 25%);\n --step-param-color: #c4b5fd;\n --accordion-header-hover: #253347;\n --accordion-content-bg: #162033;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-body);\n font-size: 14px;\n line-height: 1.7;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Corporate Two-Pane Layout\n ============================================================================ */\n.corporate-layout {\n display: flex;\n min-height: 100vh;\n}\n\n/* ============================================================================\n Sidebar / Table of Contents\n ============================================================================ */\n.toc {\n position: fixed;\n top: 0;\n left: 0;\n width: 260px;\n height: 100vh;\n overflow-y: auto;\n background: var(--card);\n border-right: 1px solid var(--border);\n display: flex;\n flex-direction: column;\n z-index: 20;\n}\n\n.toc-header {\n padding: 1.5rem 1.25rem 1rem;\n border-bottom: 1px solid var(--border);\n}\n\n.toc-title {\n font-family: var(--font-heading);\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--primary);\n letter-spacing: -0.01em;\n margin-bottom: 1rem;\n}\n\n.toc-stats {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n\n.toc-stat-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-family: var(--font-sans);\n font-size: 0.75rem;\n}\n\n.toc-stat-label {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.toc-stat-value {\n font-weight: 600;\n color: var(--foreground);\n font-family: var(--font-mono);\n}\n\n.toc-stat-passed { color: var(--success); }\n.toc-stat-failed { color: var(--error); }\n.toc-stat-skipped { color: var(--warning); }\n\n.toc-progress {\n height: 4px;\n background: var(--muted);\n border-radius: 2px;\n margin-top: 0.5rem;\n overflow: hidden;\n}\n\n.toc-progress-bar {\n height: 100%;\n background: var(--success);\n border-radius: 2px;\n transition: width 0.3s ease;\n}\n\n.toc-pass-rate {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-align: right;\n margin-top: 0.25rem;\n}\n\n.toc-nav {\n flex: 1;\n overflow-y: auto;\n padding: 0.75rem 0;\n}\n\n.toc-nav-label {\n font-family: var(--font-sans);\n font-size: 0.625rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n padding: 0.5rem 1.25rem 0.375rem;\n}\n\n.toc-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 1.25rem;\n text-decoration: none;\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n font-weight: 400;\n transition: all 0.15s ease;\n border-left: 2px solid transparent;\n}\n\n.toc-item:hover {\n background: var(--accent);\n color: var(--primary);\n}\n\n.toc-item.active {\n background: var(--accent);\n border-left-color: var(--primary);\n color: var(--primary);\n font-weight: 500;\n}\n\n.toc-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.toc-dot.dot-passed {\n background: var(--success);\n}\n\n.toc-dot.dot-failed {\n background: var(--error);\n}\n\n.toc-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.toc-count {\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n flex-shrink: 0;\n}\n\n/* ============================================================================\n Main Content Area\n ============================================================================ */\n.corporate-main {\n margin-left: 260px;\n flex: 1;\n min-width: 0;\n}\n\n.container {\n max-width: 960px;\n margin: 0 auto;\n padding: 1.5rem 2rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 2rem 2.5rem;\n }\n}\n\n/* ============================================================================\n Responsive — collapse sidebar on small screens\n ============================================================================ */\n@media (max-width: 860px) {\n .toc {\n display: none;\n }\n\n .corporate-main {\n margin-left: 0;\n }\n}\n\n/* ============================================================================\n Header — editorial style\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1.25rem;\n margin-bottom: 1.5rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-family: var(--font-heading);\n font-size: 1.5rem;\n font-weight: 600;\n letter-spacing: -0.02em;\n color: var(--primary);\n}\n\n.header-actions {\n display: flex;\n gap: 0.625rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Search Input\n ============================================================================ */\n.search-input {\n height: 2.25rem;\n padding: 0 0.875rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.875rem;\n width: 220px;\n transition: all 0.15s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 3px hsl(220 60% 50% / 0.1);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 260px;\n }\n}\n\n/* ============================================================================\n Meta Info\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 1.75rem;\n margin-bottom: 1.25rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.8125rem;\n font-family: var(--font-body);\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 500;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Summary Cards\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 1rem 1.25rem;\n transition: all 0.15s ease;\n}\n\n.summary-card:hover {\n box-shadow: var(--shadow-sm);\n}\n\n.summary-card .label {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.375rem;\n}\n\n.summary-card .value {\n font-family: var(--font-heading);\n font-size: 2rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n}\n\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n.summary-card.status-active {\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 1rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.6875rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--destructive, #dc2626);\n background: var(--destructive-light, #fef2f2);\n border: 1px solid var(--destructive-border, #fecaca);\n cursor: pointer;\n padding: 0.25rem 0.75rem;\n border-radius: var(--radius);\n transition: all 0.15s ease;\n}\n\n.tag-bar-clear:hover {\n background: var(--destructive-border, #fecaca);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.5rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n padding: 0.25rem 0.625rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.tag-pill:hover {\n background: var(--success-border);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n margin-bottom: 1rem;\n font-weight: 500;\n}\n\n/* ============================================================================\n Feature Sections — editorial card style\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.75rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 1rem 1.25rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-family: var(--font-heading);\n font-weight: 600;\n font-size: 1.0625rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 0.8125rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.75rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n margin-bottom: 0.5rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.75rem 1rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 500;\n font-size: 0.875rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n font-family: var(--font-body);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.375rem;\n}\n\n.tag {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n}\n\n.scenario-duration {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.75rem 1rem 1rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons — colored dots instead of emoji\n ============================================================================ */\n.status-icon {\n font-size: 0.875rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps\n ============================================================================ */\n.steps {\n margin-top: 0.25rem;\n padding: 0.25rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.5rem;\n padding: 0.375rem 0;\n font-size: 0.8125rem;\n align-items: baseline;\n line-height: 1.6;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1rem;\n text-align: center;\n font-size: 0.75rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 52px;\n font-family: var(--font-sans);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.02em;\n}\n\n.step.continuation {\n padding-left: 1.25rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n font-family: var(--font-body);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 500;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.7;\n}\n\n/* ============================================================================\n Error Display\n ============================================================================ */\n.error-box {\n margin-top: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments\n ============================================================================ */\n.attachments {\n margin-top: 0.75rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n font-size: 0.75rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.15s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars\n ============================================================================ */\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection\n ============================================================================ */\n::selection {\n background: hsl(220 60% 50% / 0.15);\n color: inherit;\n}\n\n/* ============================================================================\n Animations\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.2s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.02s; }\n.feature:nth-child(3) { animation-delay: 0.04s; }\n.feature:nth-child(4) { animation-delay: 0.06s; }\n.feature:nth-child(5) { animation-delay: 0.08s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #e5e5e5;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body {\n font-size: 12px;\n }\n\n .corporate-layout {\n display: block;\n }\n\n .toc {\n display: none;\n }\n\n .corporate-main {\n margin-left: 0;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries — Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.75rem;\n padding: 0.75rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.5rem;\n margin-top: 0.25rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n/* ============================================================================\n Documentation Entries — Note\n ============================================================================ */\n.doc-note {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0.5rem;\n background: var(--muted);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n font-size: 0.8125rem;\n line-height: 1.6;\n color: var(--foreground);\n font-family: var(--font-body);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries — Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 0.5rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n}\n\n/* ============================================================================\n Documentation Entries — Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 0.375rem;\n font-size: 0.8125rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-sans);\n font-size: 0.75rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries — Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-family: var(--font-sans);\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.125rem 0.375rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries — Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.5rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.75rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.5rem 0.75rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--foreground);\n font-family: var(--font-sans);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries — Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.375rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.8125rem;\n color: var(--primary);\n text-decoration: none;\n font-family: var(--font-body);\n transition: color 0.15s ease;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"\\\\2192\";\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Documentation Entries — Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.5rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-family: var(--font-heading);\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-size: 0.8125rem;\n line-height: 1.7;\n white-space: pre-wrap;\n color: var(--foreground);\n font-family: var(--font-body);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n font-family: var(--font-heading);\n margin-top: 1em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.25rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.8125rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.125em 0.375em;\n background: var(--muted);\n border-radius: 3px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.75em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 2px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.5em 1em;\n border-left: 3px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.8125rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 2px);\n}\n\n/* ============================================================================\n Documentation Entries — Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"\\\\25C7 \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries — Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.5rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.375rem;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-style: italic;\n font-family: var(--font-body);\n}\n\n/* ============================================================================\n Documentation Entries — Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.375rem 0.75rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View\n ============================================================================ */\n.trace-view {\n margin-top: 0.75rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n font-weight: 500;\n color: var(--foreground);\n transition: background-color 0.15s ease;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 1px solid var(--border);\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.375rem;\n margin-bottom: 0.375rem;\n border-bottom: 1px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.1875rem 0;\n font-size: 0.75rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1.25rem;\n background: var(--muted);\n border-radius: 2px;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 2px;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.375rem;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: white;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75em; font-weight: 600; margin-left: 4px; vertical-align: middle; font-family: var(--font-sans); }\n.badge-grade { color: #fff; }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #2196F3; }\n.badge-grade-C { background: #FF9800; }\n.badge-grade-D { background: #f44336; }\n.badge-grade-F { background: #9E0000; }\n.badge-flaky { background: #FF9800; color: #fff; }\n.badge-perf { font-size: 0.7em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* ============================================================================\n Failure summary\n ============================================================================ */\n.failure-summary {\n margin: 1rem 0;\n padding: 0.75rem 1rem;\n border: 1px solid var(--error);\n border-radius: var(--radius);\n background: color-mix(in srgb, var(--error) 8%, transparent);\n}\n.failure-summary-header {\n font-family: var(--font-heading);\n font-weight: 600;\n font-size: 0.875rem;\n color: var(--error);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n.failure-summary li a {\n font-family: var(--font-body);\n font-size: 0.8125rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* ============================================================================\n Source permalink\n ============================================================================ */\n.source-link {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Detail Level Toggle\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.detail-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n`;\n\nconst CORPORATE_JS = `\n// Sidebar TOC navigation — highlight active section on scroll, click to smooth-scroll\n(function() {\n var tocItems = document.querySelectorAll('.toc-item');\n if (!tocItems.length) return;\n\n var featureAnchors = [];\n tocItems.forEach(function(item) {\n var href = item.getAttribute('href');\n if (href) {\n var el = document.querySelector(href);\n if (el) featureAnchors.push({ el: el, tocItem: item });\n }\n });\n\n // Click handler — smooth scroll\n tocItems.forEach(function(item) {\n item.addEventListener('click', function(e) {\n e.preventDefault();\n var href = item.getAttribute('href');\n if (!href) return;\n var target = document.querySelector(href);\n if (target) {\n target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }\n });\n });\n\n // Scroll handler — highlight active section\n var ticking = false;\n function onScroll() {\n if (ticking) return;\n ticking = true;\n requestAnimationFrame(function() {\n var scrollY = window.scrollY || document.documentElement.scrollTop;\n var viewportH = window.innerHeight;\n var activeIdx = -1;\n\n for (var i = featureAnchors.length - 1; i >= 0; i--) {\n var rect = featureAnchors[i].el.getBoundingClientRect();\n if (rect.top <= viewportH * 0.3) {\n activeIdx = i;\n break;\n }\n }\n\n tocItems.forEach(function(item) { item.classList.remove('active'); });\n if (activeIdx >= 0) {\n featureAnchors[activeIdx].tocItem.classList.add('active');\n }\n\n ticking = false;\n });\n }\n\n window.addEventListener('scroll', onScroll, { passive: true });\n onScroll();\n})();\n`;\n\nexport const corporateTheme: HtmlTheme = {\n name: \"corporate\",\n label: \"Corporate\",\n css: CORPORATE_CSS,\n buildBody: corporateBuildBody,\n additionalJs: CORPORATE_JS,\n};\n","/**\n * Terminal theme — green-on-dark hacker aesthetic.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\n\nexport const terminalTheme: HtmlTheme = {\n name: \"terminal\",\n label: \"Terminal\",\n css: `\n/* ============================================================================\n Google Fonts Import - JetBrains Mono for terminal typography\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Always dark-feeling)\n Terminal theme: green-on-dark, high density, no rounding\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Light terminal — paper-white with green accents */\n --background: #f5f5f0;\n --foreground: #1a1a1a;\n --card: #eaeae5;\n --card-foreground: #1a1a1a;\n --popover: #eaeae5;\n --popover-foreground: #1a1a1a;\n\n /* Green primary, darker for light bg */\n --primary: #008a45;\n --primary-foreground: #f5f5f0;\n\n --secondary: #e0e0d8;\n --secondary-foreground: #1a1a1a;\n --muted: #e8e8e2;\n --muted-foreground: #666660;\n --accent: #e0e0d8;\n --accent-foreground: #1a1a1a;\n --destructive: #cc2222;\n --destructive-foreground: #f5f5f0;\n --border: #c8c8c0;\n --input: #c8c8c0;\n --ring: #008a45;\n --radius: 0;\n\n /* No shadows — flat terminal look */\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n\n /* Status colors — legible on light bg */\n --success: #008a45;\n --success-light: #008a4512;\n --success-border: #008a4533;\n --error: #cc2222;\n --error-light: #cc222212;\n --error-border: #cc222233;\n --warning: #b87800;\n --warning-light: #b8780012;\n --warning-border: #b8780033;\n --pending: #0088a0;\n --pending-light: #0088a012;\n --pending-border: #0088a033;\n\n /* Terminal-specific */\n --keyword-color: #008a45;\n --tag-bg: #008a4515;\n --tag-color: #008a45;\n --tag-border: #008a4533;\n --step-param-color: #0088a0;\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: #e0e0d8;\n --accordion-content-bg: #ebebeb;\n}\n\n/* ============================================================================\n Dark Mode — classic green-on-black terminal\n ============================================================================ */\n[data-theme=\"dark\"] {\n --font-sans: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #0a0a0a;\n --foreground: #d4d4d4;\n --card: #111111;\n --card-foreground: #d4d4d4;\n --popover: #111111;\n --popover-foreground: #d4d4d4;\n\n --primary: #00d26a;\n --primary-foreground: #0a0a0a;\n\n --secondary: #1a1a1a;\n --secondary-foreground: #d4d4d4;\n --muted: #1a1a1a;\n --muted-foreground: #6b6b6b;\n --accent: #1a1a1a;\n --accent-foreground: #d4d4d4;\n --destructive: #ff4444;\n --destructive-foreground: #0a0a0a;\n --border: #2a2a2a;\n --input: #2a2a2a;\n --ring: #00d26a;\n\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n\n --success: #00d26a;\n --success-light: #00d26a12;\n --success-border: #00d26a33;\n --error: #ff4444;\n --error-light: #ff444412;\n --error-border: #ff444433;\n --warning: #ffaa00;\n --warning-light: #ffaa0012;\n --warning-border: #ffaa0033;\n --pending: #00bcd4;\n --pending-light: #00bcd412;\n --pending-border: #00bcd433;\n\n --keyword-color: #00d26a;\n --tag-bg: #00d26a15;\n --tag-color: #00d26a;\n --tag-border: #00d26a33;\n --step-param-color: #00bcd4;\n\n --accordion-header-hover: #1a1a1a;\n --accordion-content-bg: #0e0e0e;\n}\n\n/* Auto dark mode — same values (always dark) */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --font-sans: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #0a0a0a;\n --foreground: #d4d4d4;\n --card: #111111;\n --card-foreground: #d4d4d4;\n --popover: #111111;\n --popover-foreground: #d4d4d4;\n --primary: #00d26a;\n --primary-foreground: #0a0a0a;\n --secondary: #1a1a1a;\n --secondary-foreground: #d4d4d4;\n --muted: #1a1a1a;\n --muted-foreground: #6b6b6b;\n --accent: #1a1a1a;\n --accent-foreground: #d4d4d4;\n --destructive: #ff4444;\n --destructive-foreground: #0a0a0a;\n --border: #2a2a2a;\n --input: #2a2a2a;\n --ring: #00d26a;\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n --success: #00d26a;\n --success-light: #00d26a12;\n --success-border: #00d26a33;\n --error: #ff4444;\n --error-light: #ff444412;\n --error-border: #ff444433;\n --warning: #ffaa00;\n --warning-light: #ffaa0012;\n --warning-border: #ffaa0033;\n --pending: #00bcd4;\n --pending-light: #00bcd412;\n --pending-border: #00bcd433;\n --keyword-color: #00d26a;\n --tag-bg: #00d26a15;\n --tag-color: #00d26a;\n --tag-border: #00d26a33;\n --step-param-color: #00bcd4;\n --accordion-header-hover: #1a1a1a;\n --accordion-content-bg: #0e0e0e;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 13px;\n line-height: 1.5;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout — compact, high density\n ============================================================================ */\n.container {\n max-width: 1200px;\n margin: 0 auto;\n padding: 0.75rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 1rem 1.5rem;\n }\n}\n\n/* ============================================================================\n Header\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 0.75rem;\n margin-bottom: 0.75rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-size: 1.125rem;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: var(--primary);\n}\n\n.header h1::before {\n content: \"> \";\n color: var(--muted-foreground);\n}\n\n.header-actions {\n display: flex;\n gap: 0.5rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--primary);\n font-size: 0.875rem;\n transition: all 0.1s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--primary);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n/* ============================================================================\n Search Input\n ============================================================================ */\n.search-input {\n height: 2rem;\n padding: 0 0.75rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n width: 220px;\n transition: all 0.1s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--primary);\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 280px;\n }\n}\n\n/* ============================================================================\n Meta Info\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.125rem 1.5rem;\n margin-bottom: 0.75rem;\n padding: 0.5rem 0.75rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.75rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 600;\n color: var(--primary);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n/* ============================================================================\n Summary Cards\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0.5rem;\n margin-bottom: 0.75rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.625rem 0.75rem;\n transition: border-color 0.1s ease;\n}\n\n.summary-card:hover {\n border-color: var(--muted-foreground);\n}\n\n.summary-card .label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.25rem;\n}\n\n.summary-card .value {\n font-size: 1.75rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n font-family: var(--font-sans);\n}\n\n/* Passed — green */\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed — red */\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped — amber */\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending — cyan */\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 1px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.625rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.15s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--destructive);\n background: var(--error-light);\n border: 1px solid var(--error-border);\n cursor: pointer;\n padding: 0.125rem 0.625rem;\n border-radius: var(--radius);\n transition: all 0.1s ease;\n}\n\n.tag-bar-clear:hover {\n background: var(--error-border);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 1px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.375rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.1s ease;\n}\n\n.tag-pill:hover {\n background: var(--success-border);\n}\n\n.tag-pill:focus-visible {\n outline: 1px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n font-weight: 500;\n}\n\n/* ============================================================================\n Feature Sections\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.375rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 0.5rem 0.75rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.1s ease;\n gap: 0.75rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 600;\n font-size: 0.8125rem;\n color: var(--primary);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.0625rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.75rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.1875rem;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.375rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.25rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.375rem 0.75rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.1s ease;\n gap: 0.75rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-weight: 500;\n font-size: 0.8125rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-top: 0.25rem;\n}\n\n.tag {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n}\n\n.scenario-duration {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.5rem 0.75rem 0.625rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons\n ============================================================================ */\n.status-icon {\n font-size: 0.8125rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps — compact terminal flow\n ============================================================================ */\n.steps {\n margin-top: 0.125rem;\n padding: 0.125rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.375rem;\n padding: 0.1875rem 0;\n font-size: 0.75rem;\n align-items: baseline;\n line-height: 1.4;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 0.875rem;\n text-align: center;\n font-size: 0.6875rem;\n}\n\n.step-keyword {\n font-weight: 700;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 48px;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n/* Indent continuation keywords (And, But, *) */\n.step.continuation {\n padding-left: 1rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: normal;\n font-weight: 600;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.625rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.6;\n}\n\n/* ============================================================================\n Error Display\n ============================================================================ */\n.error-box {\n margin-top: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--error-light);\n border-radius: var(--radius);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments\n ============================================================================ */\n.attachments {\n margin-top: 0.5rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.1s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--primary);\n border-color: var(--primary);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.375rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.375rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.15s ease;\n font-size: 0.6875rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars — thin green track\n ============================================================================ */\n::-webkit-scrollbar {\n width: 4px;\n height: 4px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 0;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--primary);\n}\n\n/* ============================================================================\n Focus States\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n/* ============================================================================\n Selection — green tinted\n ============================================================================ */\n::selection {\n background: #00d26a33;\n color: inherit;\n}\n\n/* ============================================================================\n Animations — minimal, fast\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.feature {\n animation: fadeIn 0.1s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.01s; }\n.feature:nth-child(3) { animation-delay: 0.02s; }\n.feature:nth-child(4) { animation-delay: 0.03s; }\n.feature:nth-child(5) { animation-delay: 0.04s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #ccc;\n --muted: #f0f0f0;\n --muted-foreground: #555;\n --primary: #006633;\n --keyword-color: #006633;\n }\n\n body {\n font-size: 11px;\n color: black;\n background: white;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.5rem;\n padding: 0.5rem;\n background: var(--accordion-content-bg);\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.25rem;\n margin-top: 0.125rem;\n margin-bottom: 0.375rem;\n padding: 0.375rem 0.625rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: var(--radius);\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.375rem 0.625rem;\n margin-bottom: 0.375rem;\n background: var(--muted);\n border-left: 2px solid var(--primary);\n border-radius: var(--radius);\n font-size: 0.75rem;\n line-height: 1.4;\n color: var(--foreground);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-bottom: 0.375rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.375rem;\n margin-bottom: 0.25rem;\n font-size: 0.75rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--primary);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.doc-kv-label::after {\n content: \":\";\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.5625rem;\n font-weight: 600;\n padding: 0.0625rem 0.3125rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: var(--radius);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.5rem 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.375rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.25rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.3125rem 0.625rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--primary);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.25rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-size: 0.75rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.1s ease;\n}\n\n.doc-link a:hover {\n color: var(--foreground);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \">\";\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.375rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.5rem 0.625rem;\n background: var(--card);\n font-size: 0.75rem;\n line-height: 1.5;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n margin-top: 0.75em;\n margin-bottom: 0.375em;\n font-weight: 700;\n line-height: 1.3;\n color: var(--primary);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.8125rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.75rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.375em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.375em 0;\n padding-left: 1.25em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.125em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.0625em 0.25em;\n background: var(--muted);\n border-radius: var(--radius);\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.5em 0;\n padding: 0.5em;\n background: var(--muted);\n border-radius: var(--radius);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.5em 0;\n padding: 0.375em 0.75em;\n border-left: 2px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 0.75em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.5em 0;\n border-collapse: collapse;\n font-size: 0.75rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.375em 0.625em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: var(--radius);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"> \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.5rem 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.375rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.25rem;\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-style: normal;\n}\n\n.doc-screenshot-caption::before {\n content: \"# \";\n color: var(--primary);\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.25rem 0.625rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-size: 0.625rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.5rem 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View - OTel span waterfall\n ============================================================================ */\n.trace-view {\n margin-top: 0.5rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.625rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--foreground);\n transition: background-color 0.1s ease;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 1px solid var(--border);\n padding: 0.375rem 0.625rem;\n background: var(--accordion-content-bg);\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.5625rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.25rem;\n margin-bottom: 0.25rem;\n border-bottom: 1px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.125rem 0;\n font-size: 0.6875rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 0;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1rem;\n background: var(--muted);\n border-radius: 0;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 0;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.25rem;\n font-size: 0.5625rem;\n font-family: var(--font-mono);\n color: var(--primary-foreground);\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 1px 5px; border-radius: 0; font-size: 0.6875em; font-weight: 600; margin-left: 4px; vertical-align: middle; }\n.badge-grade { color: var(--primary-foreground); }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #2196F3; }\n.badge-grade-C { background: #FF9800; }\n.badge-grade-D { background: #f44336; }\n.badge-grade-F { background: #9E0000; }\n.badge-flaky { background: #FF9800; color: var(--primary-foreground); }\n.badge-perf { font-size: 0.65em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* Failure summary */\n.failure-summary {\n margin: 0.5rem 0;\n padding: 0.5rem 0.75rem;\n border: 1px solid var(--error);\n border-radius: var(--radius);\n background: var(--error-light);\n}\n.failure-summary-header {\n font-weight: 700;\n font-size: 0.8125rem;\n color: var(--error);\n margin-bottom: 0.375rem;\n}\n.failure-summary-note {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n}\n.failure-summary li a {\n font-size: 0.75rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* Source permalink */\n.source-link {\n font-size: 0.625rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--primary);\n}\n\n/* ============================================================================\n Detail Level Toggle\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 0.875rem;\n transition: all 0.1s ease;\n}\n\n.detail-toggle:hover {\n background: var(--accent);\n border-color: var(--primary);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n`,\n};\n","/**\n * Minimal theme — zen-like typography-first aesthetic.\n *\n * Noto Serif Display headings, DM Sans body, warm neutrals, teal accent.\n * No cards, no shadows — just typography and space.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\n\nexport const minimalTheme: HtmlTheme = {\n name: \"minimal\",\n label: \"Minimal\",\n css: `\n/* ============================================================================\n Google Fonts Import - Noto Serif Display + DM Sans\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+Display:wght@400;500;600;700&family=DM+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Default)\n Warm neutral palette with teal accent\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"DM Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors — warm neutrals */\n --background: #fdfcfa;\n --foreground: #2d2d2d;\n --card: #fdfcfa;\n --card-foreground: #2d2d2d;\n --popover: #fdfcfa;\n --popover-foreground: #2d2d2d;\n\n /* Teal primary */\n --primary: #2a9d8f;\n --primary-foreground: #ffffff;\n\n --secondary: #f5f3ef;\n --secondary-foreground: #2d2d2d;\n --muted: #f5f3ef;\n --muted-foreground: #8a8680;\n --accent: #f0ece6;\n --accent-foreground: #2d2d2d;\n --destructive: #c1554d;\n --destructive-foreground: #ffffff;\n --border: #e8e4de;\n --input: #e8e4de;\n --ring: #2a9d8f;\n --radius: 0.25rem;\n\n /* Shadows — nearly invisible for minimal aesthetic */\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n\n /* Status colors — muted, warm tones */\n --success: #2a9d8f;\n --success-light: #f0faf8;\n --success-border: #c4e8e3;\n --error: #c1554d;\n --error-light: #fdf5f4;\n --error-border: #e8c5c2;\n --warning: #c68a19;\n --warning-light: #fdf8ed;\n --warning-border: #e8d9b0;\n --pending: #7c6daa;\n --pending-light: #f7f5fb;\n --pending-border: #d5cfea;\n\n /* Theme-specific */\n --keyword-color: #1f7a6e;\n --tag-bg: #f0faf8;\n --tag-color: #1f7a6e;\n --tag-border: #c4e8e3;\n --step-param-color: #4a7fb5;\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: #f5f3ef;\n --accordion-content-bg: #fdfcfa;\n}\n\n/* ============================================================================\n Dark Mode\n ============================================================================ */\n[data-theme=\"dark\"] {\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"DM Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #1a1a18;\n --foreground: #e0ddd8;\n --card: #1a1a18;\n --card-foreground: #e0ddd8;\n --popover: #1a1a18;\n --popover-foreground: #e0ddd8;\n\n --primary: #3dbcad;\n --primary-foreground: #1a1a18;\n\n --secondary: #262622;\n --secondary-foreground: #e0ddd8;\n --muted: #262622;\n --muted-foreground: #8a8680;\n --accent: #2e2e2a;\n --accent-foreground: #e0ddd8;\n --destructive: #d4706a;\n --destructive-foreground: #ffffff;\n --border: #3a3836;\n --input: #3a3836;\n --ring: #3dbcad;\n --radius: 0.25rem;\n\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n\n --success: #3dbcad;\n --success-light: #1c2b28;\n --success-border: #2a4a44;\n --error: #d4706a;\n --error-light: #2b1c1c;\n --error-border: #4a2a28;\n --warning: #d4a033;\n --warning-light: #2b2618;\n --warning-border: #4a3e22;\n --pending: #9688c0;\n --pending-light: #221e2e;\n --pending-border: #3a3450;\n\n --keyword-color: #4ed4c4;\n --tag-bg: #1c2b28;\n --tag-color: #4ed4c4;\n --tag-border: #2a4a44;\n --step-param-color: #7aade0;\n\n --accordion-header-hover: #262622;\n --accordion-content-bg: #1e1e1c;\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: #1a1a18;\n --foreground: #e0ddd8;\n --card: #1a1a18;\n --card-foreground: #e0ddd8;\n --popover: #1a1a18;\n --popover-foreground: #e0ddd8;\n --primary: #3dbcad;\n --primary-foreground: #1a1a18;\n --secondary: #262622;\n --secondary-foreground: #e0ddd8;\n --muted: #262622;\n --muted-foreground: #8a8680;\n --accent: #2e2e2a;\n --accent-foreground: #e0ddd8;\n --destructive: #d4706a;\n --destructive-foreground: #ffffff;\n --border: #3a3836;\n --input: #3a3836;\n --ring: #3dbcad;\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n --success: #3dbcad;\n --success-light: #1c2b28;\n --success-border: #2a4a44;\n --error: #d4706a;\n --error-light: #2b1c1c;\n --error-border: #4a2a28;\n --warning: #d4a033;\n --warning-light: #2b2618;\n --warning-border: #4a3e22;\n --pending: #9688c0;\n --pending-light: #221e2e;\n --pending-border: #3a3450;\n --keyword-color: #4ed4c4;\n --tag-bg: #1c2b28;\n --tag-color: #4ed4c4;\n --tag-border: #2a4a44;\n --step-param-color: #7aade0;\n --accordion-header-hover: #262622;\n --accordion-content-bg: #1e1e1c;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 15px;\n line-height: 1.8;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout — single-column centered, generous whitespace\n ============================================================================ */\n.container {\n max-width: 680px;\n margin: 0 auto;\n padding: 2rem 1.5rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 3rem 2rem;\n }\n}\n\n/* ============================================================================\n Header — serif heading, minimal rule\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 1.5rem;\n padding-bottom: 1.5rem;\n margin-bottom: 2rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-family: \"Noto Serif Display\", Georgia, \"Times New Roman\", serif;\n font-size: 1.75rem;\n font-weight: 500;\n letter-spacing: -0.02em;\n color: var(--foreground);\n white-space: nowrap;\n}\n\n.header-actions {\n display: flex;\n gap: 0.625rem;\n align-items: center;\n flex-shrink: 0;\n}\n\n/* ============================================================================\n Theme Toggle\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: transparent;\n cursor: pointer;\n color: var(--muted-foreground);\n font-size: 0.875rem;\n transition: color 0.2s ease;\n}\n\n.theme-toggle:hover {\n color: var(--foreground);\n background: transparent;\n border-color: var(--foreground);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Search Input\n ============================================================================ */\n.search-input {\n height: 2rem;\n padding: 0 0.75rem;\n border: none;\n border-bottom: 1px solid var(--border);\n border-radius: 0;\n background: transparent;\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.875rem;\n width: 200px;\n transition: border-color 0.2s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--primary);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 240px;\n }\n}\n\n/* ============================================================================\n Meta Info — understated inline text\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 1.5rem;\n margin-bottom: 2rem;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 500;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Summary Cards — flat, typographic counters\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0;\n margin-bottom: 2.5rem;\n border-top: 1px solid var(--border);\n border-bottom: 1px solid var(--border);\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: transparent;\n border: none;\n border-radius: 0;\n padding: 1rem 1rem;\n border-right: 1px solid var(--border);\n transition: none;\n}\n\n.summary-card:last-child {\n border-right: none;\n}\n\n.summary-card:hover {\n box-shadow: none;\n}\n\n.summary-card .label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.25rem;\n}\n\n.summary-card .value {\n font-family: \"Noto Serif Display\", Georgia, serif;\n font-size: 2rem;\n font-weight: 400;\n letter-spacing: -0.02em;\n line-height: 1.1;\n}\n\n/* Passed — teal text only */\n.summary-card.passed {\n background: transparent;\n border-color: var(--border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed — red text only */\n.summary-card.failed {\n background: transparent;\n border-color: var(--border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped — amber text only */\n.summary-card.skipped {\n background: transparent;\n border-color: var(--border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending — purple text only */\n.summary-card.pending {\n background: transparent;\n border-color: var(--border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 1.5rem;\n padding: 0.75rem 0;\n background: transparent;\n border: none;\n border-bottom: 1px solid var(--border);\n border-radius: 0;\n position: sticky;\n top: 0;\n z-index: 10;\n backdrop-filter: blur(8px);\n background: color-mix(in srgb, var(--background) 90%, transparent);\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.6875rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--destructive);\n background: transparent;\n border: none;\n border-bottom: 1px solid var(--destructive);\n cursor: pointer;\n padding: 0.125rem 0;\n border-radius: 0;\n transition: opacity 0.15s ease;\n}\n\n.tag-bar-clear:hover {\n opacity: 0.7;\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.5rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.75rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: transparent;\n color: var(--muted-foreground);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.tag-pill:hover {\n color: var(--primary);\n border-color: var(--primary);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: none;\n border-bottom: 2px solid var(--ring);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n margin-bottom: 1.5rem;\n font-weight: 400;\n font-style: italic;\n}\n\n/* ============================================================================\n Feature Sections — no card, separated by large rules\n ============================================================================ */\n.feature {\n background: transparent;\n border: none;\n border-radius: 0;\n margin-bottom: 0;\n padding-bottom: 2rem;\n border-bottom: 2px solid var(--border);\n overflow: visible;\n}\n\n.feature:last-child {\n border-bottom: none;\n padding-bottom: 0;\n}\n\n.feature + .feature {\n padding-top: 2rem;\n}\n\n.feature-header {\n padding: 0.75rem 0;\n background: transparent;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: none;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: transparent;\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-family: \"Noto Serif Display\", Georgia, \"Times New Roman\", serif;\n font-weight: 500;\n font-size: 1.25rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 0.8125rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.5rem 0 0 0;\n border-top: none;\n background: transparent;\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios — left-border accent, no card\n ============================================================================ */\n.scenario {\n background: transparent;\n border: none;\n border-left: 3px solid var(--border);\n border-radius: 0;\n margin-bottom: 1.25rem;\n padding-left: 1rem;\n overflow: visible;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.5rem 0;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: none;\n gap: 1rem;\n}\n\n.scenario-header:hover {\n background: transparent;\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 500;\n font-size: 0.9375rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.375rem;\n}\n\n.tag {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: transparent;\n color: var(--muted-foreground);\n border: none;\n border-radius: 0;\n font-family: var(--font-mono);\n}\n\n.tag::before {\n content: \"#\";\n opacity: 0.5;\n}\n\n.scenario-duration {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.25rem 0 0.5rem;\n border-top: none;\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* Status-based left border for scenarios */\n.scenario:has(.status-passed) {\n border-left-color: var(--success);\n}\n\n.scenario:has(.status-failed) {\n border-left-color: var(--error);\n}\n\n.scenario:has(.status-skipped) {\n border-left-color: var(--warning);\n}\n\n.scenario:has(.status-pending) {\n border-left-color: var(--pending);\n}\n\n/* ============================================================================\n Status Icons\n ============================================================================ */\n.status-icon {\n font-size: 0.875rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps — generous line-height, quiet styling\n ============================================================================ */\n.steps {\n margin-top: 0.25rem;\n padding: 0.25rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.5rem;\n padding: 0.25rem 0;\n font-size: 0.875rem;\n align-items: baseline;\n line-height: 1.7;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1rem;\n text-align: center;\n font-size: 0.75rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 52px;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n/* Indent continuation keywords (And, But, *) */\n.step.continuation {\n padding-left: 1.25rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 500;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.5;\n}\n\n/* ============================================================================\n Error Display — left-border accent, minimal\n ============================================================================ */\n.error-box {\n margin-top: 0.75rem;\n padding: 0.75rem 1rem;\n background: transparent;\n border-radius: 0;\n border: none;\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments\n ============================================================================ */\n.attachments {\n margin-top: 0.75rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.25rem 0;\n background: transparent;\n border: none;\n border-bottom: 1px solid var(--border);\n border-radius: 0;\n font-size: 0.75rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: color 0.15s ease;\n}\n\n.attachment:hover {\n background: transparent;\n color: var(--primary);\n border-color: var(--primary);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars — hidden track\n ============================================================================ */\n::-webkit-scrollbar {\n width: 4px;\n height: 4px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 2px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection\n ============================================================================ */\n::selection {\n background: color-mix(in srgb, var(--primary) 15%, transparent);\n color: inherit;\n}\n\n/* ============================================================================\n Animations — subtle fade only\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.feature {\n animation: fadeIn 0.3s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.03s; }\n.feature:nth-child(3) { animation-delay: 0.06s; }\n.feature:nth-child(4) { animation-delay: 0.09s; }\n.feature:nth-child(5) { animation-delay: 0.12s; }\n\n/* ============================================================================\n Print Styles — optimized by default\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #d4d4d4;\n --muted: #f5f5f5;\n --muted-foreground: #555;\n }\n\n body {\n font-size: 11pt;\n line-height: 1.6;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .header h1 {\n font-size: 18pt;\n }\n\n .feature {\n page-break-inside: avoid;\n animation: none;\n border-bottom: 1pt solid #d4d4d4;\n }\n\n .scenario {\n page-break-inside: avoid;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n\n .summary-card .value {\n font-size: 16pt;\n }\n\n .step {\n font-size: 10pt;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 1rem;\n padding: 0.75rem 0;\n background: transparent;\n border-radius: 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.5rem;\n margin-top: 0.25rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0 0.5rem 0.75rem;\n background: transparent;\n border-left: 2px solid var(--primary);\n border-radius: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.5rem 0 0.5rem 0.75rem;\n margin-bottom: 0.5rem;\n background: transparent;\n border-left: 2px solid var(--muted-foreground);\n border-radius: 0;\n font-size: 0.875rem;\n line-height: 1.7;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 0.5rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: transparent;\n color: var(--tag-color);\n border: none;\n border-radius: 0;\n font-family: var(--font-mono);\n}\n\n.doc-tag-item::before {\n content: \"#\";\n opacity: 0.5;\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 0.375rem;\n font-size: 0.875rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.75rem;\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.375rem 0;\n background: transparent;\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: transparent;\n color: var(--muted-foreground);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.75rem 0;\n background: transparent;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.75rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.8125rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.5rem 0.75rem;\n text-align: left;\n border-bottom: 1px solid var(--border);\n}\n\n.doc-table th {\n background: transparent;\n font-weight: 600;\n color: var(--foreground);\n border-bottom: 2px solid var(--border);\n}\n\n.doc-table td {\n background: transparent;\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accent);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.375rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.875rem;\n color: var(--primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.15s ease;\n}\n\n.doc-link a:hover {\n border-bottom-color: var(--primary);\n text-decoration: none;\n}\n\n.doc-link a::before {\n content: \"\\\\2192\";\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.75rem;\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.375rem 0;\n background: transparent;\n border-bottom: 1px solid var(--border);\n font-family: \"Noto Serif Display\", Georgia, serif;\n font-size: 0.9375rem;\n font-weight: 500;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.75rem 0;\n background: transparent;\n font-size: 0.875rem;\n line-height: 1.7;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n font-family: \"Noto Serif Display\", Georgia, serif;\n margin-top: 1.25em;\n margin-bottom: 0.5em;\n font-weight: 500;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.25rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.8125rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n border-bottom-color: var(--primary);\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.125em 0.375em;\n background: var(--muted);\n border-radius: 2px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.75em;\n background: var(--muted);\n border-radius: var(--radius);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.5em 1em;\n border-left: 2px solid var(--primary);\n background: transparent;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1.5em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.875rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border-bottom: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: transparent;\n font-weight: 600;\n border-bottom: 2px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: var(--radius);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.75rem;\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.375rem 0;\n background: transparent;\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"\\\\25C7 \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.75rem 0;\n background: transparent;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.75rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.375rem;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.75rem;\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.375rem 0;\n background: transparent;\n border-bottom: 1px solid var(--border);\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.75rem 0;\n background: transparent;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View\n ============================================================================ */\n.trace-view {\n margin-top: 0.75rem;\n border: none;\n border-top: 1px solid var(--border);\n border-radius: 0;\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0;\n background: transparent;\n cursor: pointer;\n user-select: none;\n font-size: 0.8125rem;\n font-weight: 500;\n color: var(--foreground);\n transition: none;\n}\n\n.trace-view-header:hover {\n background: transparent;\n}\n\n.trace-view-count {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: transparent;\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 1px solid var(--border);\n padding: 0.5rem 0;\n background: transparent;\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.375rem;\n margin-bottom: 0.375rem;\n border-bottom: 1px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.1875rem 0;\n font-size: 0.75rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1.25rem;\n background: var(--muted);\n border-radius: 2px;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 2px;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.375rem;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: white;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 2px 6px; border-radius: 2px; font-size: 0.75em; font-weight: 600; margin-left: 4px; vertical-align: middle; }\n.badge-grade { color: #fff; }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #4a7fb5; }\n.badge-grade-C { background: #c68a19; }\n.badge-grade-D { background: #c1554d; }\n.badge-grade-F { background: #8a2020; }\n.badge-flaky { background: #c68a19; color: #fff; }\n.badge-perf { font-size: 0.7em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* Failure summary */\n.failure-summary {\n margin: 1.5rem 0;\n padding: 0.75rem 0 0.75rem 1rem;\n border: none;\n border-left: 3px solid var(--error);\n border-radius: 0;\n background: transparent;\n}\n.failure-summary-header {\n font-weight: 600;\n font-size: 0.875rem;\n color: var(--error);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n.failure-summary li a {\n font-size: 0.8125rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* Source permalink */\n.source-link {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Detail Level Toggle\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: transparent;\n cursor: pointer;\n color: var(--muted-foreground);\n font-size: 0.875rem;\n transition: color 0.15s ease;\n}\n\n.detail-toggle:hover {\n color: var(--foreground);\n background: transparent;\n border-color: var(--foreground);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n`,\n};\n","/**\n * Dashboard theme — Grafana/Datadog-inspired, data-dense, two-column layout.\n * Dark by default with DM Sans for UI and JetBrains Mono for metrics/timing.\n * Structural theme: overrides both CSS and HTML body via buildBody.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\nimport type { BuildBodyArgs, BuildBodyDeps } from \"../renderers/body.js\";\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nfunction dashboardBuildBody(args: BuildBodyArgs, deps: BuildBodyDeps): string {\n const { run } = args;\n\n const total = run.testCases.length;\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n const passRate = total > 0 ? Math.round((passed / total) * 100) : 0;\n\n const allTags = [...new Set(run.testCases.flatMap((tc) => tc.tags))].sort();\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n\n // Build sidebar feature tree items\n const treeItems: string[] = [];\n let featureIndex = 0;\n for (const [file, testCases] of byFile) {\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const fPassed = testCases.filter((tc) => tc.status === \"passed\").length;\n const fFailed = testCases.filter((tc) => tc.status === \"failed\").length;\n const statusClass =\n fFailed > 0 ? \"error\" : fPassed === testCases.length ? \"success\" : \"warning\";\n\n treeItems.push(\n `<button type=\"button\" class=\"db-tree-item\" data-feature-index=\"${featureIndex}\" title=\"${file}\">\n <span class=\"db-tree-dot db-tree-dot--${statusClass}\"></span>\n <span class=\"db-tree-name\">${featureName}</span>\n <span class=\"db-tree-count\">${testCases.length}</span>\n </button>`,\n );\n featureIndex++;\n }\n\n // Build tag chips\n const tagChips = allTags\n .map(\n (tag) =>\n `<span class=\"db-tag-chip\">${tag}</span>`,\n )\n .join(\"\\n \");\n\n // Build run info\n const startDate = run.startedAtMs\n ? new Date(run.startedAtMs).toLocaleString()\n : \"N/A\";\n const durationSec =\n run.durationMs != null ? (run.durationMs / 1000).toFixed(1) : \"N/A\";\n\n // Build main content using deps (preserves all default classes)\n const mainParts: string[] = [];\n\n // Tag bar\n mainParts.push(\n deps.renderTagBar(\n { tags: allTags, totalScenarios: total },\n deps.tagBarDeps,\n ),\n );\n\n // Failure summary\n const failedCases = run.testCases.filter((tc) => tc.status === \"failed\");\n if (failedCases.length > 0) {\n mainParts.push(\n deps.renderFailureSummary({ failedCases }, deps.failureSummaryDeps),\n );\n }\n\n // Features\n for (const [file, testCases] of byFile) {\n mainParts.push(\n deps.renderFeature(\n { file, testCases, metricsMap: args.metricsMap },\n deps.featureDeps,\n ),\n );\n }\n\n const sidebar = `\n<aside class=\"db-sidebar\">\n <div class=\"db-sidebar-header\">\n <div class=\"db-logo\">Test Report</div>\n <div class=\"db-run-info\">\n <span class=\"db-run-date\">${startDate}</span>\n <span class=\"db-run-duration\">${durationSec}s</span>\n </div>\n </div>\n\n <div class=\"db-metrics\">\n <div class=\"db-metric db-metric--success\">\n <div class=\"db-metric-value\">${passed}</div>\n <div class=\"db-metric-label\">Passed</div>\n </div>\n <div class=\"db-metric db-metric--error\">\n <div class=\"db-metric-value\">${failed}</div>\n <div class=\"db-metric-label\">Failed</div>\n </div>\n <div class=\"db-metric db-metric--warning\">\n <div class=\"db-metric-value\">${skipped}</div>\n <div class=\"db-metric-label\">Skipped</div>\n </div>\n <div class=\"db-metric db-metric--info\">\n <div class=\"db-metric-value\">${passRate}%</div>\n <div class=\"db-metric-label\">Pass Rate</div>\n </div>\n </div>\n\n <div class=\"db-section\">\n <div class=\"db-section-title\">Features</div>\n <div class=\"db-tree\">\n ${treeItems.join(\"\\n \")}\n </div>\n </div>\n\n ${\n allTags.length > 0\n ? `<div class=\"db-section\">\n <div class=\"db-section-title\">Tags</div>\n <div class=\"db-tag-chips\">\n ${tagChips}\n </div>\n </div>`\n : \"\"\n }\n</aside>`;\n\n const main = `\n<div class=\"db-main\">\n ${mainParts.join(\"\\n \")}\n</div>`;\n\n return `<div class=\"dashboard-layout\">${sidebar}${main}</div>`;\n}\n\nconst DASHBOARD_CSS = `\n/* ============================================================================\n Google Fonts Import - DM Sans + JetBrains Mono\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Dark Mode (Default for Dashboard)\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors - dark by default */\n --background: #111318;\n --foreground: #e1e4ea;\n --card: #1a1d24;\n --card-foreground: #e1e4ea;\n --popover: #1a1d24;\n --popover-foreground: #e1e4ea;\n\n /* Primary - vibrant blue */\n --primary: #3b82f6;\n --primary-foreground: #ffffff;\n\n --secondary: #1e2028;\n --secondary-foreground: #c8ccd4;\n --muted: #1e2028;\n --muted-foreground: #6b7280;\n --accent: #252830;\n --accent-foreground: #e1e4ea;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #2a2d35;\n --input: #2a2d35;\n --ring: #3b82f6;\n --radius: 0.375rem;\n\n /* Shadows */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.4);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.45);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.4);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);\n\n /* Status colors - vibrant */\n --success: #10b981;\n --success-light: rgba(16, 185, 129, 0.1);\n --success-border: rgba(16, 185, 129, 0.25);\n --error: #ef4444;\n --error-light: rgba(239, 68, 68, 0.1);\n --error-border: rgba(239, 68, 68, 0.25);\n --warning: #f59e0b;\n --warning-light: rgba(245, 158, 11, 0.1);\n --warning-border: rgba(245, 158, 11, 0.25);\n --pending: #8b5cf6;\n --pending-light: rgba(139, 92, 246, 0.1);\n --pending-border: rgba(139, 92, 246, 0.25);\n\n /* Dashboard-specific */\n --keyword-color: #10b981;\n --tag-bg: rgba(59, 130, 246, 0.1);\n --tag-color: #60a5fa;\n --tag-border: rgba(59, 130, 246, 0.25);\n --step-param-color: #60a5fa;\n\n /* Accordion/Collapsible */\n --accordion-header-hover: #1e2028;\n --accordion-content-bg: #15171e;\n}\n\n/* Dark mode explicit */\n[data-theme=\"dark\"] {\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #111318;\n --foreground: #e1e4ea;\n --card: #1a1d24;\n --card-foreground: #e1e4ea;\n --popover: #1a1d24;\n --popover-foreground: #e1e4ea;\n\n --primary: #3b82f6;\n --primary-foreground: #ffffff;\n\n --secondary: #1e2028;\n --secondary-foreground: #c8ccd4;\n --muted: #1e2028;\n --muted-foreground: #6b7280;\n --accent: #252830;\n --accent-foreground: #e1e4ea;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #2a2d35;\n --input: #2a2d35;\n --ring: #3b82f6;\n --radius: 0.375rem;\n\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.4);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.45);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.4);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);\n\n --success: #10b981;\n --success-light: rgba(16, 185, 129, 0.1);\n --success-border: rgba(16, 185, 129, 0.25);\n --error: #ef4444;\n --error-light: rgba(239, 68, 68, 0.1);\n --error-border: rgba(239, 68, 68, 0.25);\n --warning: #f59e0b;\n --warning-light: rgba(245, 158, 11, 0.1);\n --warning-border: rgba(245, 158, 11, 0.25);\n --pending: #8b5cf6;\n --pending-light: rgba(139, 92, 246, 0.1);\n --pending-border: rgba(139, 92, 246, 0.25);\n\n --keyword-color: #10b981;\n --tag-bg: rgba(59, 130, 246, 0.1);\n --tag-color: #60a5fa;\n --tag-border: rgba(59, 130, 246, 0.25);\n --step-param-color: #60a5fa;\n\n --accordion-header-hover: #1e2028;\n --accordion-content-bg: #15171e;\n}\n\n/* Light mode */\n[data-theme=\"light\"] {\n --background: #f8f9fb;\n --foreground: #1a1d24;\n --card: #ffffff;\n --card-foreground: #1a1d24;\n --popover: #ffffff;\n --popover-foreground: #1a1d24;\n\n --primary: #2563eb;\n --primary-foreground: #ffffff;\n\n --secondary: #f1f3f5;\n --secondary-foreground: #374151;\n --muted: #f1f3f5;\n --muted-foreground: #6b7280;\n --accent: #e8ebef;\n --accent-foreground: #1a1d24;\n --destructive: #dc2626;\n --destructive-foreground: #ffffff;\n --border: #e2e5ea;\n --input: #e2e5ea;\n --ring: #2563eb;\n\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.04);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.06);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.07), 0 2px 4px -2px rgb(0 0 0 / 0.05);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.05);\n\n --success: #059669;\n --success-light: rgba(5, 150, 105, 0.08);\n --success-border: rgba(5, 150, 105, 0.2);\n --error: #dc2626;\n --error-light: rgba(220, 38, 38, 0.08);\n --error-border: rgba(220, 38, 38, 0.2);\n --warning: #d97706;\n --warning-light: rgba(217, 119, 6, 0.08);\n --warning-border: rgba(217, 119, 6, 0.2);\n --pending: #7c3aed;\n --pending-light: rgba(124, 58, 237, 0.08);\n --pending-border: rgba(124, 58, 237, 0.2);\n\n --keyword-color: #059669;\n --tag-bg: rgba(37, 99, 235, 0.08);\n --tag-color: #2563eb;\n --tag-border: rgba(37, 99, 235, 0.2);\n --step-param-color: #2563eb;\n\n --accordion-header-hover: #f1f3f5;\n --accordion-content-bg: #fafbfc;\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: #111318;\n --foreground: #e1e4ea;\n --card: #1a1d24;\n --card-foreground: #e1e4ea;\n --popover: #1a1d24;\n --popover-foreground: #e1e4ea;\n --primary: #3b82f6;\n --primary-foreground: #ffffff;\n --secondary: #1e2028;\n --secondary-foreground: #c8ccd4;\n --muted: #1e2028;\n --muted-foreground: #6b7280;\n --accent: #252830;\n --accent-foreground: #e1e4ea;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #2a2d35;\n --input: #2a2d35;\n --ring: #3b82f6;\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.4);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.45);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.4);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);\n --success: #10b981;\n --success-light: rgba(16, 185, 129, 0.1);\n --success-border: rgba(16, 185, 129, 0.25);\n --error: #ef4444;\n --error-light: rgba(239, 68, 68, 0.1);\n --error-border: rgba(239, 68, 68, 0.25);\n --warning: #f59e0b;\n --warning-light: rgba(245, 158, 11, 0.1);\n --warning-border: rgba(245, 158, 11, 0.25);\n --pending: #8b5cf6;\n --pending-light: rgba(139, 92, 246, 0.1);\n --pending-border: rgba(139, 92, 246, 0.25);\n --keyword-color: #10b981;\n --tag-bg: rgba(59, 130, 246, 0.1);\n --tag-color: #60a5fa;\n --tag-border: rgba(59, 130, 246, 0.25);\n --step-param-color: #60a5fa;\n --accordion-header-hover: #1e2028;\n --accordion-content-bg: #15171e;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 13px;\n line-height: 1.5;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout - Container wraps the dashboard\n ============================================================================ */\n.container {\n max-width: 100%;\n margin: 0;\n padding: 0;\n}\n\n/* ============================================================================\n Dashboard Two-Column Layout\n ============================================================================ */\n.dashboard-layout {\n display: grid;\n grid-template-columns: 280px 1fr;\n min-height: 100vh;\n}\n\n@media (max-width: 900px) {\n .dashboard-layout {\n grid-template-columns: 1fr;\n }\n}\n\n/* ============================================================================\n Sidebar\n ============================================================================ */\n.db-sidebar {\n background: var(--card);\n border-right: 1px solid var(--border);\n padding: 0;\n overflow-y: auto;\n position: sticky;\n top: 0;\n height: 100vh;\n display: flex;\n flex-direction: column;\n}\n\n@media (max-width: 900px) {\n .db-sidebar {\n position: relative;\n height: auto;\n border-right: none;\n border-bottom: 1px solid var(--border);\n }\n}\n\n.db-sidebar-header {\n padding: 1.25rem 1rem 1rem;\n border-bottom: 1px solid var(--border);\n}\n\n.db-logo {\n font-size: 0.9375rem;\n font-weight: 700;\n color: var(--foreground);\n letter-spacing: -0.02em;\n}\n\n.db-run-info {\n display: flex;\n gap: 0.75rem;\n margin-top: 0.375rem;\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n}\n\n.db-run-duration {\n color: var(--primary);\n font-weight: 500;\n}\n\n/* ============================================================================\n Metrics Grid\n ============================================================================ */\n.db-metrics {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--border);\n}\n\n.db-metric {\n padding: 0.625rem 0.75rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n background: var(--background);\n}\n\n.db-metric-value {\n font-size: 1.375rem;\n font-weight: 700;\n font-family: var(--font-mono);\n line-height: 1.1;\n letter-spacing: -0.03em;\n}\n\n.db-metric-label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-top: 0.125rem;\n}\n\n.db-metric--success .db-metric-value { color: var(--success); }\n.db-metric--success { border-color: var(--success-border); background: var(--success-light); }\n.db-metric--error .db-metric-value { color: var(--error); }\n.db-metric--error { border-color: var(--error-border); background: var(--error-light); }\n.db-metric--warning .db-metric-value { color: var(--warning); }\n.db-metric--warning { border-color: var(--warning-border); background: var(--warning-light); }\n.db-metric--info .db-metric-value { color: var(--primary); }\n.db-metric--info { border-color: var(--tag-border); background: var(--tag-bg); }\n\n/* ============================================================================\n Sidebar Sections\n ============================================================================ */\n.db-section {\n padding: 0.75rem 0;\n border-bottom: 1px solid var(--border);\n flex: 1;\n overflow-y: auto;\n}\n\n.db-section:last-child {\n border-bottom: none;\n flex: none;\n}\n\n.db-section-title {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 600;\n padding: 0 1rem 0.5rem;\n}\n\n/* ============================================================================\n Feature Tree\n ============================================================================ */\n.db-tree {\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.db-tree-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4375rem 1rem;\n background: none;\n border: none;\n width: 100%;\n text-align: left;\n cursor: pointer;\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n transition: background-color 0.1s ease;\n}\n\n.db-tree-item:hover {\n background: var(--accent);\n}\n\n.db-tree-item.active {\n background: var(--accent);\n border-left: 2px solid var(--primary);\n padding-left: calc(1rem - 2px);\n}\n\n.db-tree-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.db-tree-dot--success { background: var(--success); }\n.db-tree-dot--error { background: var(--error); }\n.db-tree-dot--warning { background: var(--warning); }\n\n.db-tree-name {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.db-tree-count {\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n flex-shrink: 0;\n}\n\n/* ============================================================================\n Tag Chips (sidebar)\n ============================================================================ */\n.db-tag-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n padding: 0 1rem;\n}\n\n.db-tag-chip {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Main Content Area\n ============================================================================ */\n.db-main {\n padding: 1.25rem 1.5rem;\n overflow-y: auto;\n max-width: 1000px;\n}\n\n@media (max-width: 900px) {\n .db-main {\n padding: 1rem;\n }\n}\n\n/* ============================================================================\n Header - hidden in dashboard (info is in sidebar)\n ============================================================================ */\n.header {\n display: none;\n}\n\n/* ============================================================================\n Meta Info - hidden in dashboard (info is in sidebar)\n ============================================================================ */\n.meta-info {\n display: none;\n}\n\n/* ============================================================================\n Summary Cards - hidden in dashboard (metrics in sidebar)\n ============================================================================ */\n.summary {\n display: none;\n}\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 0.75rem;\n padding: 0.625rem 0.875rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.625rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--error);\n background: var(--error-light);\n border: 1px solid var(--error-border);\n cursor: pointer;\n padding: 0.1875rem 0.625rem;\n border-radius: var(--radius);\n transition: all 0.15s ease;\n}\n\n.tag-bar-clear:hover {\n background: var(--error-border);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.375rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.1875rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.tag-pill:hover {\n background: var(--accent);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.75rem;\n font-weight: 500;\n}\n\n/* ============================================================================\n Failure Summary\n ============================================================================ */\n.failure-summary {\n margin-bottom: 0.75rem;\n padding: 0.75rem 0.875rem;\n background: var(--error-light);\n border: 1px solid var(--error-border);\n border-radius: var(--radius);\n}\n\n.failure-summary-title {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--error);\n margin-bottom: 0.375rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.failure-summary-list {\n list-style: none;\n padding: 0;\n}\n\n.failure-summary-list li {\n padding: 0.25rem 0;\n}\n\n.failure-summary-list a {\n font-size: 0.8125rem;\n color: var(--error);\n text-decoration: none;\n transition: opacity 0.15s ease;\n}\n\n.failure-summary-list a:hover {\n opacity: 0.8;\n text-decoration: underline;\n}\n\n/* ============================================================================\n Feature Sections - compact card style\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.5rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 0.625rem 0.875rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.1s ease;\n gap: 0.75rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 600;\n font-size: 0.8125rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.0625rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.75rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.1875rem;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.5rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios - compact nested style\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n margin-bottom: 0.375rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.5rem 0.75rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.1s ease;\n gap: 0.75rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-weight: 500;\n font-size: 0.8125rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-top: 0.25rem;\n}\n\n.tag {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.scenario-right {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n\n.scenario-duration {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.5rem 0.75rem 0.75rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons\n ============================================================================ */\n.status-icon {\n font-size: 0.8125rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps\n ============================================================================ */\n.steps {\n margin-top: 0.125rem;\n padding: 0.125rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.375rem;\n padding: 0.25rem 0;\n font-size: 0.75rem;\n align-items: baseline;\n line-height: 1.5;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 0.875rem;\n text-align: center;\n font-size: 0.6875rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 48px;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.step.continuation {\n padding-left: 1rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 500;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.625rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.6;\n}\n\n/* ============================================================================\n Error Display\n ============================================================================ */\n.error-box {\n margin-top: 0.5rem;\n padding: 0.625rem 0.75rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 1px);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments\n ============================================================================ */\n.attachments {\n margin-top: 0.5rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.1s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.375rem;\n border-radius: calc(var(--radius) - 1px);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.375rem;\n border-radius: calc(var(--radius) - 1px);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.6875rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars - thin dark\n ============================================================================ */\n::-webkit-scrollbar {\n width: 5px;\n height: 5px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection\n ============================================================================ */\n::selection {\n background: rgba(59, 130, 246, 0.2);\n color: inherit;\n}\n\n/* ============================================================================\n Animations - subtle\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-2px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.15s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.02s; }\n.feature:nth-child(3) { animation-delay: 0.04s; }\n.feature:nth-child(4) { animation-delay: 0.06s; }\n.feature:nth-child(5) { animation-delay: 0.08s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #e5e5e5;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body { font-size: 11px; }\n .container { max-width: 100%; padding: 0; }\n\n .dashboard-layout { grid-template-columns: 1fr; }\n .db-sidebar { display: none; }\n\n .header-actions,\n .tag-bar,\n .filter-results { display: none !important; }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content { display: block; }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.5rem;\n padding: 0.5rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 1px);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.25rem;\n margin-top: 0.125rem;\n margin-bottom: 0.375rem;\n padding: 0.375rem 0.625rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.375rem 0.625rem;\n margin-bottom: 0.375rem;\n background: var(--muted);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0;\n font-size: 0.75rem;\n line-height: 1.5;\n color: var(--foreground);\n}\n\n.doc-note:last-child { margin-bottom: 0; }\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-bottom: 0.375rem;\n}\n\n.doc-tag:last-child { margin-bottom: 0; }\n\n.doc-tag-item {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.375rem;\n margin-bottom: 0.25rem;\n font-size: 0.75rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child { margin-bottom: 0; }\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.doc-code:last-child { margin-bottom: 0; }\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.5625rem;\n font-weight: 500;\n padding: 0.0625rem 0.3125rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.375rem;\n}\n\n.doc-table:last-child { margin-bottom: 0; }\n\n.doc-table-label {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.25rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.375rem 0.625rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.25rem;\n}\n\n.doc-link:last-child { margin-bottom: 0; }\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-size: 0.75rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.1s ease;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"\\\\2192\";\n font-size: 0.6875rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.doc-section:last-child { margin-bottom: 0; }\n\n.doc-section-title {\n padding: 0.375rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.625rem;\n background: var(--card);\n font-size: 0.75rem;\n line-height: 1.5;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content { white-space: normal; }\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n margin-top: 0.75em;\n margin-bottom: 0.375em;\n font-weight: 600;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child { margin-top: 0; }\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.8125rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.75rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p { margin: 0.375em 0; }\n.doc-section-parsed .doc-section-content p:first-child { margin-top: 0; }\n.doc-section-parsed .doc-section-content p:last-child { margin-bottom: 0; }\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol { margin: 0.375em 0; padding-left: 1.25em; }\n.doc-section-parsed .doc-section-content li { margin: 0.125em 0; }\n\n.doc-section-parsed .doc-section-content a { color: var(--primary); text-decoration: none; }\n.doc-section-parsed .doc-section-content a:hover { text-decoration: underline; }\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.0625em 0.25em;\n background: var(--muted);\n border-radius: 3px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.5em 0;\n padding: 0.625em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 1px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code { padding: 0; background: none; }\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.5em 0;\n padding: 0.375em 0.75em;\n border-left: 3px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n}\n\n.doc-section-parsed .doc-section-content blockquote p { margin: 0; }\n\n.doc-section-parsed .doc-section-content hr {\n margin: 0.75em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.5em 0;\n border-collapse: collapse;\n font-size: 0.75rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.375em 0.625em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 1px);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child { margin-bottom: 0; }\n\n.doc-mermaid-title {\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"\\\\25C7 \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot { margin-bottom: 0.375rem; }\n.doc-screenshot:last-child { margin-bottom: 0; }\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.25rem;\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.doc-custom:last-child { margin-bottom: 0; }\n\n.doc-custom-type {\n padding: 0.25rem 0.625rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-size: 0.625rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View\n ============================================================================ */\n.trace-view {\n margin-top: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.625rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--foreground);\n transition: background-color 0.1s ease;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: 9999px;\n}\n\n/* ============================================================================\n Search Input\n ============================================================================ */\n.search-input {\n height: 2rem;\n padding: 0 0.75rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n width: 200px;\n transition: all 0.1s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n/* ============================================================================\n Theme Toggle\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 0.875rem;\n transition: all 0.1s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Summary Card (kept for JS filter compatibility)\n ============================================================================ */\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.75rem 1rem;\n transition: all 0.1s ease;\n}\n\n.summary-card .label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.25rem;\n}\n\n.summary-card .value {\n font-size: 1.5rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n font-family: var(--font-mono);\n}\n\n.summary-card.passed { background: var(--success-light); border-color: var(--success-border); }\n.summary-card.passed .value { color: var(--success); }\n.summary-card.failed { background: var(--error-light); border-color: var(--error-border); }\n.summary-card.failed .value { color: var(--error); }\n.summary-card.skipped { background: var(--warning-light); border-color: var(--warning-border); }\n.summary-card.skipped .value { color: var(--warning); }\n.summary-card.pending { background: var(--pending-light); border-color: var(--pending-border); }\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Retry info\n ============================================================================ */\n.retry-info {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--warning-light);\n color: var(--warning);\n border: 1px solid var(--warning-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Metrics badge (sparkline-like trend indicator)\n ============================================================================ */\n.metrics-badge {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.metrics-badge.improving {\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n}\n\n.metrics-badge.degrading {\n background: var(--error-light);\n color: var(--error);\n border: 1px solid var(--error-border);\n}\n\n.metrics-badge.stable {\n background: var(--muted);\n color: var(--muted-foreground);\n border: 1px solid var(--border);\n}\n`;\n\nconst DASHBOARD_JS = `\n(function() {\n // Feature tree navigation: click to scroll\n var treeItems = document.querySelectorAll('.db-tree-item');\n var features = document.querySelectorAll('.db-main .feature');\n\n treeItems.forEach(function(item) {\n item.addEventListener('click', function() {\n var index = parseInt(item.getAttribute('data-feature-index'), 10);\n var target = features[index];\n if (target) {\n target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n // Expand if collapsed\n if (target.classList.contains('collapsed')) {\n target.classList.remove('collapsed');\n var header = target.querySelector('.feature-header');\n if (header) header.setAttribute('aria-expanded', 'true');\n }\n }\n // Mark active\n treeItems.forEach(function(t) { t.classList.remove('active'); });\n item.classList.add('active');\n });\n });\n\n // Highlight active feature in sidebar on scroll\n var mainEl = document.querySelector('.db-main');\n if (mainEl && features.length > 0) {\n var onScroll = function() {\n var scrollTop = mainEl.scrollTop || window.scrollY;\n var activeIndex = 0;\n features.forEach(function(f, i) {\n var rect = f.getBoundingClientRect();\n if (rect.top <= 120) {\n activeIndex = i;\n }\n });\n treeItems.forEach(function(t, i) {\n if (i === activeIndex) {\n t.classList.add('active');\n } else {\n t.classList.remove('active');\n }\n });\n };\n\n // Listen on both the main element and window for scroll\n mainEl.addEventListener('scroll', onScroll, { passive: true });\n window.addEventListener('scroll', onScroll, { passive: true });\n }\n})();\n`;\n\nexport const dashboardTheme: HtmlTheme = {\n name: \"dashboard\",\n label: \"Dashboard\",\n css: DASHBOARD_CSS,\n buildBody: dashboardBuildBody,\n additionalJs: DASHBOARD_JS,\n};\n","/**\n * Playful theme — warm pastels, rounded corners, cheerful aesthetic.\n * Nunito font, WCAG AAA contrast, color-blind safe palette.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\n\nexport const playfulTheme: HtmlTheme = {\n name: \"playful\",\n label: \"Playful\",\n css: `\n/* ============================================================================\n Google Fonts Import - Nunito for playful headings\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700;800&family=Source+Sans+3:wght@400;500;600&family=Source+Code+Pro:wght@400;500&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Default)\n Warm pastel palette with coral accent\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"Source Sans 3\", \"Nunito\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"Source Code Pro\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors — warm cream background, brown foreground */\n --background: #fef7f0;\n --foreground: #3d3229;\n --card: #ffffff;\n --card-foreground: #3d3229;\n --popover: #ffffff;\n --popover-foreground: #3d3229;\n\n /* Coral/salmon as primary */\n --primary: #e07a5f;\n --primary-foreground: #ffffff;\n\n --secondary: #fdf0e6;\n --secondary-foreground: #3d3229;\n --muted: #f5ebe0;\n --muted-foreground: #6b5b4e;\n --accent: #fdf0e6;\n --accent-foreground: #3d3229;\n --destructive: #c44536;\n --destructive-foreground: #ffffff;\n --border: #e6d5c3;\n --input: #e6d5c3;\n --ring: #e07a5f;\n --radius: 1rem;\n\n /* Shadows — soft and warm */\n --shadow-xs: 0 1px 2px 0 rgb(61 50 41 / 0.04);\n --shadow-sm: 0 1px 4px 0 rgb(61 50 41 / 0.06), 0 1px 2px -1px rgb(61 50 41 / 0.04);\n --shadow: 0 4px 8px -2px rgb(61 50 41 / 0.08), 0 2px 4px -2px rgb(61 50 41 / 0.04);\n --shadow-md: 0 10px 20px -4px rgb(61 50 41 / 0.1), 0 4px 8px -4px rgb(61 50 41 / 0.05);\n\n /* Status colors — color-blind safe, cheerful */\n --success: #2d8659;\n --success-light: #eef8f0;\n --success-border: #b8e0c8;\n --error: #c44536;\n --error-light: #fdf0ee;\n --error-border: #f0c4be;\n --warning: #c77d18;\n --warning-light: #fdf5e6;\n --warning-border: #f0d8a8;\n --pending: #7c5cbf;\n --pending-light: #f4f0fa;\n --pending-border: #d4c8ec;\n\n /* Playful-specific */\n --keyword-color: #b05740;\n --tag-bg: #fdf0e6;\n --tag-color: #b05740;\n --tag-border: #f0cdb8;\n --step-param-color: #5b7fc7;\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: #fdf5ed;\n --accordion-content-bg: #fdf8f3;\n}\n\n/* ============================================================================\n Dark Mode — warm dark palette\n ============================================================================ */\n[data-theme=\"dark\"] {\n --font-sans: \"Source Sans 3\", \"Nunito\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"Source Code Pro\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #1e1814;\n --foreground: #f0e6dc;\n --card: #2a2118;\n --card-foreground: #f0e6dc;\n --popover: #2a2118;\n --popover-foreground: #f0e6dc;\n\n --primary: #e8957d;\n --primary-foreground: #1e1814;\n\n --secondary: #332920;\n --secondary-foreground: #f0e6dc;\n --muted: #332920;\n --muted-foreground: #a89585;\n --accent: #332920;\n --accent-foreground: #f0e6dc;\n --destructive: #e05a4c;\n --destructive-foreground: #ffffff;\n --border: #4a3d32;\n --input: #4a3d32;\n --ring: #e8957d;\n --radius: 1rem;\n\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.25);\n --shadow-sm: 0 1px 4px 0 rgb(0 0 0 / 0.35), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n --shadow: 0 4px 8px -2px rgb(0 0 0 / 0.35), 0 2px 4px -2px rgb(0 0 0 / 0.3);\n --shadow-md: 0 10px 20px -4px rgb(0 0 0 / 0.4), 0 4px 8px -4px rgb(0 0 0 / 0.3);\n\n --success: #5ab87e;\n --success-light: #1e2e22;\n --success-border: #2d5e3e;\n --error: #e05a4c;\n --error-light: #2e1e1c;\n --error-border: #6b302a;\n --warning: #e0a63e;\n --warning-light: #2e2518;\n --warning-border: #6b5120;\n --pending: #a68be0;\n --pending-light: #241e30;\n --pending-border: #4a3870;\n\n --keyword-color: #e8957d;\n --tag-bg: #332920;\n --tag-color: #e8957d;\n --tag-border: #5a4535;\n --step-param-color: #8aade0;\n\n --accordion-header-hover: #332920;\n --accordion-content-bg: #241e18;\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: #1e1814;\n --foreground: #f0e6dc;\n --card: #2a2118;\n --card-foreground: #f0e6dc;\n --popover: #2a2118;\n --popover-foreground: #f0e6dc;\n --primary: #e8957d;\n --primary-foreground: #1e1814;\n --secondary: #332920;\n --secondary-foreground: #f0e6dc;\n --muted: #332920;\n --muted-foreground: #a89585;\n --accent: #332920;\n --accent-foreground: #f0e6dc;\n --destructive: #e05a4c;\n --destructive-foreground: #ffffff;\n --border: #4a3d32;\n --input: #4a3d32;\n --ring: #e8957d;\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.25);\n --shadow-sm: 0 1px 4px 0 rgb(0 0 0 / 0.35), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n --shadow: 0 4px 8px -2px rgb(0 0 0 / 0.35), 0 2px 4px -2px rgb(0 0 0 / 0.3);\n --shadow-md: 0 10px 20px -4px rgb(0 0 0 / 0.4), 0 4px 8px -4px rgb(0 0 0 / 0.3);\n --success: #5ab87e;\n --success-light: #1e2e22;\n --success-border: #2d5e3e;\n --error: #e05a4c;\n --error-light: #2e1e1c;\n --error-border: #6b302a;\n --warning: #e0a63e;\n --warning-light: #2e2518;\n --warning-border: #6b5120;\n --pending: #a68be0;\n --pending-light: #241e30;\n --pending-border: #4a3870;\n --keyword-color: #e8957d;\n --tag-bg: #332920;\n --tag-color: #e8957d;\n --tag-border: #5a4535;\n --step-param-color: #8aade0;\n --accordion-header-hover: #332920;\n --accordion-content-bg: #241e18;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 15px;\n line-height: 1.7;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nh1, h2, h3, h4, h5, h6,\n.feature-title,\n.header h1 {\n font-family: \"Nunito\", var(--font-sans);\n font-weight: 700;\n}\n\n/* ============================================================================\n Layout\n ============================================================================ */\n.container {\n max-width: 1100px;\n margin: 0 auto;\n padding: 1.5rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 2.5rem 3rem;\n }\n}\n\n/* ============================================================================\n Header — playful style\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1.5rem;\n margin-bottom: 2rem;\n border-bottom: 2px solid var(--border);\n}\n\n.header h1 {\n font-size: 1.75rem;\n font-weight: 800;\n letter-spacing: -0.02em;\n color: var(--foreground);\n}\n\n.header-actions {\n display: flex;\n gap: 0.75rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle — large touch target\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.75rem;\n height: 2.75rem;\n border: 2px solid var(--border);\n border-radius: var(--radius);\n background: var(--card);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1.125rem;\n transition: all 0.2s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--primary);\n transform: scale(1.05);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring);\n}\n\n/* ============================================================================\n Search Input — rounded, large touch target\n ============================================================================ */\n.search-input {\n height: 2.75rem;\n padding: 0 1rem;\n border: 2px solid var(--border);\n border-radius: var(--radius);\n background: var(--card);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.9375rem;\n width: 220px;\n transition: all 0.2s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 4px rgb(224 122 95 / 0.15);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 280px;\n }\n}\n\n/* ============================================================================\n Meta Info — warm card\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem 2rem;\n margin-bottom: 1.5rem;\n padding: 1rem 1.25rem;\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.875rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 600;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n/* ============================================================================\n Summary Cards — pastel tinted with hover animation\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n margin-bottom: 2rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: var(--radius);\n padding: 1.25rem 1.5rem;\n transition: all 0.25s ease;\n cursor: default;\n}\n\n.summary-card:hover {\n box-shadow: var(--shadow-md);\n transform: translateY(-3px);\n}\n\n.summary-card .label {\n font-family: \"Nunito\", var(--font-sans);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--muted-foreground);\n font-weight: 700;\n margin-bottom: 0.5rem;\n}\n\n.summary-card .value {\n font-family: \"Nunito\", var(--font-sans);\n font-size: 2.25rem;\n font-weight: 800;\n letter-spacing: -0.03em;\n line-height: 1.1;\n}\n\n/* Passed — green tint */\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed — red tint */\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped — amber tint */\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending — purple tint */\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 1.25rem;\n padding: 1rem 1.25rem;\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-family: \"Nunito\", var(--font-sans);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--muted-foreground);\n font-weight: 700;\n}\n\n.tag-bar-count {\n font-size: 0.75rem;\n font-weight: 700;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--destructive);\n background: var(--error-light);\n border: 2px solid var(--error-border);\n cursor: pointer;\n padding: 0.375rem 0.875rem;\n border-radius: var(--radius);\n transition: all 0.2s ease;\n min-height: 2.25rem;\n}\n\n.tag-bar-clear:hover {\n background: var(--error-border);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.625rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.8125rem;\n font-weight: 600;\n padding: 0.375rem 0.75rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 2px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.2s ease;\n min-height: 2rem;\n}\n\n.tag-pill:hover {\n background: var(--success-border);\n transform: scale(1.05);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.875rem;\n color: var(--muted-foreground);\n margin-bottom: 1.25rem;\n font-weight: 600;\n}\n\n/* ============================================================================\n Feature Sections — rounded accordion\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.875rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 1rem 1.25rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.2s ease;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 700;\n font-size: 1.0625rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.1875rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 0.875rem;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.75rem;\n border-top: 2px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios — pastel nested cards\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n margin-bottom: 0.625rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.875rem 1.25rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.2s ease;\n gap: 1rem;\n min-height: 2.75rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n font-size: 0.9375rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.5rem;\n}\n\n.tag {\n font-size: 0.75rem;\n font-weight: 600;\n padding: 0.1875rem 0.625rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 2px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.scenario-duration {\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.875rem 1.25rem 1.25rem;\n border-top: 2px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons — cheerful\n ============================================================================ */\n.status-icon {\n font-size: 1rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps — individual pastel cards\n ============================================================================ */\n.steps {\n margin-top: 0.375rem;\n padding: 0.25rem 0;\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n}\n\n.step {\n display: flex;\n gap: 0.625rem;\n padding: 0.625rem 0.875rem;\n font-size: 0.875rem;\n align-items: baseline;\n line-height: 1.6;\n background: var(--accordion-content-bg);\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 4px);\n transition: border-color 0.2s ease;\n}\n\n.step:hover {\n border-color: var(--primary);\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1.125rem;\n text-align: center;\n font-size: 0.8125rem;\n}\n\n.step-keyword {\n font-weight: 700;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 56px;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n/* Indent continuation keywords (And, But, *) to show they belong to previous step */\n.step.continuation {\n padding-left: 2rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 600;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 600;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.75rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.7;\n}\n\n/* ============================================================================\n Error Display — alert style\n ============================================================================ */\n.error-box {\n margin-top: 0.875rem;\n padding: 1rem 1.25rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 2px);\n border: 2px solid var(--error-border);\n border-left: 4px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments — badge style\n ============================================================================ */\n.attachments {\n margin-top: 0.875rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.625rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.5rem 0.875rem;\n background: var(--muted);\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n font-size: 0.8125rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.2s ease;\n min-height: 2.25rem;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.625rem;\n border-radius: calc(var(--radius) - 2px);\n border: 2px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.625rem;\n border-radius: calc(var(--radius) - 2px);\n border: 2px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon — smooth rotation\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.8125rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars — rounded, warm\n ============================================================================ */\n::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 4px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States — coral ring\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring);\n}\n\n/* ============================================================================\n Selection — warm coral tint\n ============================================================================ */\n::selection {\n background: rgb(224 122 95 / 0.2);\n color: inherit;\n}\n\n/* ============================================================================\n Animations — playful reveals\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.3s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.03s; }\n.feature:nth-child(3) { animation-delay: 0.06s; }\n.feature:nth-child(4) { animation-delay: 0.09s; }\n.feature:nth-child(5) { animation-delay: 0.12s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: #1a1a1a;\n --card: white;\n --border: #ddd;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body {\n font-size: 12px;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .step {\n background: none;\n border: 1px solid #ddd;\n }\n\n .summary-card:hover {\n transform: none;\n box-shadow: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.875rem;\n padding: 0.875rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 2px);\n border: 2px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.75rem;\n margin-top: 0.375rem;\n margin-bottom: 0.625rem;\n padding: 0.625rem 0.875rem;\n background: var(--accordion-content-bg);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.625rem 0.875rem;\n margin-bottom: 0.625rem;\n background: var(--muted);\n border-left: 4px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n font-size: 0.875rem;\n line-height: 1.6;\n color: var(--foreground);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n margin-bottom: 0.625rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.75rem;\n font-weight: 600;\n padding: 0.1875rem 0.625rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 2px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.625rem;\n margin-bottom: 0.5rem;\n font-size: 0.875rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 700;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.625rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.5rem 0.875rem;\n background: var(--muted);\n border-bottom: 2px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.6875rem;\n font-weight: 700;\n padding: 0.1875rem 0.5rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.875rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.625rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n font-size: 0.8125rem;\n font-family: var(--font-mono);\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.625rem 0.875rem;\n text-align: left;\n border-bottom: 2px solid var(--border);\n border-right: 2px solid var(--border);\n}\n\n.doc-table th:last-child,\n.doc-table td:last-child {\n border-right: none;\n}\n\n.doc-table tr:last-child td {\n border-bottom: none;\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 700;\n color: var(--foreground);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.5rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.875rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.2s ease;\n font-weight: 600;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"\\\\2192\";\n font-size: 0.8125rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.625rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.625rem 0.875rem;\n background: var(--muted);\n border-bottom: 2px solid var(--border);\n font-family: \"Nunito\", var(--font-sans);\n font-size: 0.875rem;\n font-weight: 700;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.875rem;\n background: var(--card);\n font-size: 0.875rem;\n line-height: 1.7;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n font-family: \"Nunito\", var(--font-sans);\n margin-top: 1em;\n margin-bottom: 0.5em;\n font-weight: 700;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.375rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.1875rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1.0625rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.875rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.15em 0.4em;\n background: var(--muted);\n border-radius: 6px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.875em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 2px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.625em 1.125em;\n border-left: 4px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1em 0;\n border: none;\n border-top: 2px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.875rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border: 2px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 700;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 2px);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.625rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.5rem 0.875rem;\n background: var(--muted);\n border-bottom: 2px solid var(--border);\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"\\\\25C7 \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.875rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.625rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.5rem;\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.625rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.5rem 0.875rem;\n background: var(--warning-light);\n border-bottom: 2px solid var(--warning-border);\n font-size: 0.75rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.875rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View - OTel span waterfall\n ============================================================================ */\n.trace-view {\n margin-top: 0.875rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.625rem 0.875rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--foreground);\n transition: background-color 0.2s ease;\n min-height: 2.75rem;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.75rem;\n font-weight: 700;\n padding: 0.1875rem 0.625rem;\n background: var(--success-light);\n color: var(--success);\n border: 2px solid var(--success-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 2px solid var(--border);\n padding: 0.625rem 0.875rem;\n background: var(--accordion-content-bg);\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.5rem;\n margin-bottom: 0.5rem;\n border-bottom: 2px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n padding: 0.25rem 0;\n font-size: 0.8125rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1.5rem;\n background: var(--muted);\n border-radius: 6px;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 6px;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.5rem;\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n color: white;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 3px 8px; border-radius: 8px; font-size: 0.75em; font-weight: 700; margin-left: 4px; vertical-align: middle; }\n.badge-grade { color: #fff; }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #2196F3; }\n.badge-grade-C { background: #FF9800; }\n.badge-grade-D { background: #f44336; }\n.badge-grade-F { background: #9E0000; }\n.badge-flaky { background: #FF9800; color: #fff; }\n.badge-perf { font-size: 0.7em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* Failure summary */\n.failure-summary {\n margin: 1.25rem 0;\n padding: 1rem 1.25rem;\n border: 2px solid var(--error);\n border-radius: var(--radius);\n background: color-mix(in srgb, var(--error) 8%, transparent);\n}\n.failure-summary-header {\n font-family: \"Nunito\", var(--font-sans);\n font-weight: 700;\n font-size: 0.9375rem;\n color: var(--error);\n margin-bottom: 0.625rem;\n}\n.failure-summary-note {\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n margin-bottom: 0.625rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n}\n.failure-summary li a {\n font-size: 0.875rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* Source permalink */\n.source-link {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Detail Level Toggle — large touch target\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.75rem;\n height: 2.75rem;\n border: 2px solid var(--border);\n border-radius: var(--radius);\n background: var(--card);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1.125rem;\n transition: all 0.2s ease;\n}\n\n.detail-toggle:hover {\n background: var(--accent);\n border-color: var(--primary);\n transform: scale(1.05);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n`,\n};\n","/**\n * Theme registry — resolves theme names to theme objects.\n */\n\nimport type { HtmlTheme, HtmlThemeName } from \"./types.js\";\nimport { defaultTheme } from \"./default.js\";\nimport { corporateTheme } from \"./corporate.js\";\nimport { terminalTheme } from \"./terminal.js\";\nimport { minimalTheme } from \"./minimal.js\";\nimport { dashboardTheme } from \"./dashboard.js\";\nimport { playfulTheme } from \"./playful.js\";\n\nconst THEME_REGISTRY = new Map<string, HtmlTheme>([\n [\"default\", defaultTheme],\n [\"corporate\", corporateTheme],\n [\"terminal\", terminalTheme],\n [\"minimal\", minimalTheme],\n [\"dashboard\", dashboardTheme],\n [\"playful\", playfulTheme],\n]);\n\n/** Resolve a theme by name or pass through a custom theme object. */\nexport function resolveTheme(nameOrTheme: string | HtmlTheme): HtmlTheme {\n if (typeof nameOrTheme === \"object\") return nameOrTheme;\n const theme = THEME_REGISTRY.get(nameOrTheme);\n if (!theme) {\n throw new Error(\n `Unknown theme: \"${nameOrTheme}\". Available: ${[...THEME_REGISTRY.keys()].join(\", \")}`,\n );\n }\n return theme;\n}\n\n/** List available built-in theme names. */\nexport function getAvailableThemes(): string[] {\n return [...THEME_REGISTRY.keys()];\n}\n\n/** Get all themes that only use CSS (no custom body/template overrides). */\nexport function getCssOnlyThemes(): HtmlTheme[] {\n return [...THEME_REGISTRY.values()].filter(\n (theme) => !theme.buildBody && !theme.generateTemplate,\n );\n}\n\nexport type { HtmlTheme, HtmlThemeName } from \"./types.js\";\n","/**\n * Pure helper: map test status to display icon.\n * Used by scenario and steps renderers; inject via deps for testability.\n */\n\nimport type { TestStatus } from \"../../../types/test-result\";\n\nexport type GetStatusIcon = (status: TestStatus) => string;\n\nexport function getStatusIcon(status: TestStatus): string {\n switch (status) {\n case \"passed\":\n return \"✓\";\n case \"failed\":\n return \"✗\";\n case \"skipped\":\n return \"○\";\n case \"pending\":\n return \"◔\";\n default:\n return \"?\";\n }\n}\n","/**\n * Render meta info section (fn(args, deps)).\n */\n\nexport interface RenderMetaInfoArgs {\n startedAtMs: number;\n durationMs: number;\n packageVersion?: string;\n gitSha?: string;\n ciName?: string;\n ciBranch?: string;\n ciUrl?: string;\n ciCommitSha?: string;\n ciBuildNumber?: string;\n}\n\nexport interface RenderMetaInfoDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderMetaInfo(\n args: RenderMetaInfoArgs,\n deps: RenderMetaInfoDeps,\n): string {\n const items: string[] = [];\n\n const startDate = new Date(args.startedAtMs);\n items.push(`<dt>Started:</dt><dd>${startDate.toISOString()}</dd>`);\n\n const duration = (args.durationMs / 1000).toFixed(2);\n items.push(`<dt>Duration:</dt><dd>${duration}s</dd>`);\n\n if (args.packageVersion) {\n items.push(`<dt>Version:</dt><dd>${deps.escapeHtml(args.packageVersion)}</dd>`);\n }\n\n if (args.gitSha) {\n const shortSha =\n args.gitSha.length > 7 ? args.gitSha.slice(0, 7) : args.gitSha;\n items.push(`<dt>Git:</dt><dd>${deps.escapeHtml(shortSha)}</dd>`);\n }\n\n if (args.ciName) {\n // When URL and build number are present, render build number as a link\n if (args.ciUrl && args.ciBuildNumber) {\n items.push(\n `<dt>CI:</dt><dd>${deps.escapeHtml(args.ciName)} <a href=\"${deps.escapeHtml(args.ciUrl)}\">#${deps.escapeHtml(args.ciBuildNumber)}</a></dd>`,\n );\n } else {\n items.push(`<dt>CI:</dt><dd>${deps.escapeHtml(args.ciName)}</dd>`);\n }\n }\n\n if (args.ciBranch) {\n items.push(`<dt>Branch:</dt><dd>${deps.escapeHtml(args.ciBranch)}</dd>`);\n }\n\n if (args.ciCommitSha) {\n const shortSha =\n args.ciCommitSha.length > 7\n ? args.ciCommitSha.slice(0, 7)\n : args.ciCommitSha;\n items.push(\n `<dt>Commit:</dt><dd title=\"${deps.escapeHtml(args.ciCommitSha)}\">${deps.escapeHtml(shortSha)}</dd>`,\n );\n }\n\n return `<dl class=\"meta-info\">${items.join(\"\")}</dl>`;\n}\n","/**\n * Render summary cards section (fn(args, deps)).\n * No deps: pure counts to HTML.\n */\n\nexport interface RenderSummaryArgs {\n total: number;\n passed: number;\n failed: number;\n skipped: number;\n}\n\nexport interface RenderSummaryDeps {\n // No dependencies; structure only\n}\n\nexport function renderSummary(\n args: RenderSummaryArgs,\n _deps: RenderSummaryDeps,\n): string {\n const { total, passed, failed, skipped } = args;\n return `\n<div class=\"summary\">\n <div class=\"summary-card\">\n <div class=\"label\">Total</div>\n <div class=\"value\">${total}</div>\n </div>\n <div class=\"summary-card passed\">\n <div class=\"label\">Passed</div>\n <div class=\"value\">${passed}</div>\n </div>\n <div class=\"summary-card failed\">\n <div class=\"label\">Failed</div>\n <div class=\"value\">${failed}</div>\n </div>\n <div class=\"summary-card skipped\">\n <div class=\"label\">Skipped</div>\n <div class=\"value\">${skipped}</div>\n </div>\n</div>`;\n}\n","/**\n * Render tag filter bar (fn(args, deps)).\n * Displays a collapsible tag bar with clickable tag pills for filtering scenarios,\n * ARIA attributes for accessibility, and a results counter.\n */\n\nexport interface RenderTagBarArgs {\n tags: string[];\n totalScenarios: number;\n}\n\nexport interface RenderTagBarDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderTagBar(\n args: RenderTagBarArgs,\n deps: RenderTagBarDeps,\n): string {\n const { tags, totalScenarios } = args;\n\n if (tags.length === 0) return \"\";\n\n const pills = tags\n .map(\n (tag) =>\n `<button type=\"button\" class=\"tag-pill\" data-tag=\"${deps.escapeHtml(tag)}\" aria-pressed=\"false\">${deps.escapeHtml(tag)}</button>`,\n )\n .join(\"\\n \");\n\n return `\n<div class=\"tag-bar tag-bar-collapsed\">\n <div class=\"tag-bar-header\">\n <button type=\"button\" class=\"tag-bar-toggle\" aria-expanded=\"false\" aria-controls=\"tag-pills-region\">\n <span class=\"tag-bar-label\">Filter by tag</span>\n <span class=\"tag-bar-count\" aria-live=\"polite\"></span>\n <svg class=\"tag-bar-chevron\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M4 6l4 4 4-4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"tag-bar-clear\" aria-label=\"Clear all tag filters\" style=\"display:none\">Clear all</button>\n </div>\n <div id=\"tag-pills-region\" class=\"tag-bar-pills\" role=\"group\" aria-label=\"Tag filters\">\n ${pills}\n </div>\n</div>\n<div class=\"filter-results\" style=\"display:none\" aria-live=\"polite\">\n Showing <span class=\"visible-count\">0</span> of <span class=\"total-count\">${totalScenarios}</span> scenarios\n</div>`;\n}\n","/**\n * Render error box (fn(args, deps)).\n */\n\nexport interface RenderErrorBoxArgs {\n message: string;\n stack?: string;\n}\n\nexport interface RenderErrorBoxDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderErrorBox(\n args: RenderErrorBoxArgs,\n deps: RenderErrorBoxDeps,\n): string {\n const body =\n args.stack != null\n ? `${deps.escapeHtml(args.message)}\\n\\n${deps.escapeHtml(args.stack)}`\n : deps.escapeHtml(args.message);\n return `<div class=\"error-box\">${body}</div>`;\n}\n","/**\n * Render attachments section (fn(args, deps)).\n */\n\nimport type { Attachment } from \"../../../types/test-result\";\n\nexport interface RenderAttachmentsArgs {\n attachments: Attachment[];\n}\n\nexport interface RenderAttachmentsDeps {\n escapeHtml: (str: string) => string;\n embedScreenshots: boolean;\n}\n\nexport function renderAttachments(\n args: RenderAttachmentsArgs,\n deps: RenderAttachmentsDeps,\n): string {\n if (args.attachments.length === 0) {\n return \"\";\n }\n\n const items = args.attachments.map((att) => {\n const isImage = att.mediaType.startsWith(\"image/\");\n const isVideo = att.mediaType.startsWith(\"video/\");\n const isBase64 = att.contentEncoding === \"BASE64\";\n\n if (isImage && deps.embedScreenshots && isBase64) {\n return `\n<div class=\"attachment\">\n ${deps.escapeHtml(att.name)}\n <img class=\"attachment-image\" src=\"data:${att.mediaType};base64,${att.body}\" alt=\"${deps.escapeHtml(att.name)}\" />\n</div>`;\n }\n\n if (isVideo && deps.embedScreenshots) {\n const src = isBase64\n ? `data:${att.mediaType};base64,${att.body}`\n : att.body;\n return `\n<div class=\"attachment\">\n ${deps.escapeHtml(att.name)}\n <video class=\"attachment-video\" controls src=\"${deps.escapeHtml(src)}\"></video>\n</div>`;\n }\n\n const href = isBase64\n ? `data:${att.mediaType};base64,${att.body}`\n : att.body;\n\n return `<a class=\"attachment\" href=\"${deps.escapeHtml(href)}\">${deps.escapeHtml(att.name)}</a>`;\n });\n\n return `<div class=\"attachments\">${items.join(\"\")}</div>`;\n}\n","/**\n * Render doc entries (fn(args, deps)).\n * One function per doc kind + dispatcher renderDocEntry.\n */\n\nimport type { DocEntry } from \"../../../types/story\";\n\nexport interface DocEntryDeps {\n escapeHtml: (str: string) => string;\n syntaxHighlighting: boolean;\n markdownEnabled: boolean;\n mermaidEnabled: boolean;\n}\n\nexport function renderDocNote(\n entry: Extract<DocEntry, { kind: \"note\" }>,\n deps: DocEntryDeps,\n): string {\n return `<div class=\"doc-note\">${deps.escapeHtml(entry.text)}</div>`;\n}\n\nexport function renderDocTag(\n entry: Extract<DocEntry, { kind: \"tag\" }>,\n deps: DocEntryDeps,\n): string {\n const tags = entry.names\n .map((t) => `<span class=\"doc-tag-item\">${deps.escapeHtml(t)}</span>`)\n .join(\"\");\n return `<div class=\"doc-tag\">${tags}</div>`;\n}\n\nexport function renderDocKv(\n entry: Extract<DocEntry, { kind: \"kv\" }>,\n deps: DocEntryDeps,\n): string {\n const valueStr =\n typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value, null, 2);\n return `<div class=\"doc-kv\">\n <span class=\"doc-kv-label\">${deps.escapeHtml(entry.label)}:</span>\n <span class=\"doc-kv-value\">${deps.escapeHtml(valueStr)}</span>\n</div>`;\n}\n\nexport function renderDocCode(\n entry: Extract<DocEntry, { kind: \"code\" }>,\n deps: DocEntryDeps,\n): string {\n const langBadge = entry.lang\n ? `<span class=\"doc-code-lang\">${deps.escapeHtml(entry.lang)}</span>`\n : \"\";\n const langClass =\n deps.syntaxHighlighting && entry.lang\n ? ` class=\"language-${deps.escapeHtml(entry.lang)}\"`\n : \"\";\n return `<div class=\"doc-code\">\n <div class=\"doc-code-header\">\n <span class=\"doc-code-label\">${deps.escapeHtml(entry.label)}</span>\n ${langBadge}\n </div>\n <pre class=\"doc-code-content\"><code${langClass}>${deps.escapeHtml(entry.content)}</code></pre>\n</div>`;\n}\n\nexport function renderDocTable(\n entry: Extract<DocEntry, { kind: \"table\" }>,\n deps: DocEntryDeps,\n): string {\n const headers = entry.columns\n .map((c) => `<th>${deps.escapeHtml(c)}</th>`)\n .join(\"\");\n const rows = entry.rows\n .map((r) =>\n `<tr>${r.map((c) => `<td>${deps.escapeHtml(c)}</td>`).join(\"\")}</tr>`,\n )\n .join(\"\");\n return `<div class=\"doc-table\">\n <div class=\"doc-table-label\">${deps.escapeHtml(entry.label)}</div>\n <table>\n <thead><tr>${headers}</tr></thead>\n <tbody>${rows}</tbody>\n </table>\n</div>`;\n}\n\nexport function renderDocLink(\n entry: Extract<DocEntry, { kind: \"link\" }>,\n deps: DocEntryDeps,\n): string {\n return `<div class=\"doc-link\">\n <a href=\"${deps.escapeHtml(entry.url)}\" target=\"_blank\" rel=\"noopener noreferrer\">${deps.escapeHtml(entry.label)}</a>\n</div>`;\n}\n\nexport function renderDocSection(\n entry: Extract<DocEntry, { kind: \"section\" }>,\n deps: DocEntryDeps,\n): string {\n if (deps.markdownEnabled) {\n const encodedMarkdown = btoa(encodeURIComponent(entry.markdown));\n return `<div class=\"doc-section doc-section-parsed\">\n <div class=\"doc-section-title\">${deps.escapeHtml(entry.title)}</div>\n <div class=\"doc-section-content\" data-markdown=\"${encodedMarkdown}\"></div>\n</div>`;\n }\n return `<div class=\"doc-section\">\n <div class=\"doc-section-title\">${deps.escapeHtml(entry.title)}</div>\n <pre class=\"doc-section-content\">${deps.escapeHtml(entry.markdown)}</pre>\n</div>`;\n}\n\nexport function renderDocMermaid(\n entry: Extract<DocEntry, { kind: \"mermaid\" }>,\n deps: DocEntryDeps,\n): string {\n const title = entry.title\n ? `<div class=\"doc-mermaid-title\">${deps.escapeHtml(entry.title)}</div>`\n : \"\";\n\n if (deps.mermaidEnabled) {\n return `<div class=\"doc-mermaid doc-mermaid-live\">\n ${title}\n <pre class=\"mermaid\">${deps.escapeHtml(entry.code)}</pre>\n</div>`;\n }\n return `<div class=\"doc-mermaid\">\n ${title}\n <pre class=\"doc-mermaid-code\"><code>${deps.escapeHtml(entry.code)}</code></pre>\n</div>`;\n}\n\nexport function renderDocScreenshot(\n entry: Extract<DocEntry, { kind: \"screenshot\" }>,\n deps: DocEntryDeps,\n): string {\n const alt = entry.alt ?? \"Screenshot\";\n const src = entry.path;\n return `<div class=\"doc-screenshot\">\n <img src=\"${deps.escapeHtml(src)}\" alt=\"${deps.escapeHtml(alt)}\" class=\"doc-screenshot-img\" />\n ${entry.alt ? `<div class=\"doc-screenshot-caption\">${deps.escapeHtml(entry.alt)}</div>` : \"\"}\n</div>`;\n}\n\nexport function renderDocCustom(\n entry: Extract<DocEntry, { kind: \"custom\" }>,\n deps: DocEntryDeps,\n): string {\n const dataStr = JSON.stringify(entry.data, null, 2);\n return `<div class=\"doc-custom\">\n <div class=\"doc-custom-type\">${deps.escapeHtml(entry.type)}</div>\n <pre class=\"doc-custom-data\"><code>${deps.escapeHtml(dataStr)}</code></pre>\n</div>`;\n}\n\nexport function renderDocEntry(entry: DocEntry, deps: DocEntryDeps): string {\n let html: string;\n switch (entry.kind) {\n case \"note\":\n html = renderDocNote(entry, deps);\n break;\n case \"tag\":\n html = renderDocTag(entry, deps);\n break;\n case \"kv\":\n html = renderDocKv(entry, deps);\n break;\n case \"code\":\n html = renderDocCode(entry, deps);\n break;\n case \"table\":\n html = renderDocTable(entry, deps);\n break;\n case \"link\":\n html = renderDocLink(entry, deps);\n break;\n case \"section\":\n html = renderDocSection(entry, deps);\n break;\n case \"mermaid\":\n html = renderDocMermaid(entry, deps);\n break;\n case \"screenshot\":\n html = renderDocScreenshot(entry, deps);\n break;\n case \"custom\":\n html = renderDocCustom(entry, deps);\n break;\n default:\n html = \"\";\n }\n\n if (entry.children && entry.children.length > 0) {\n const childrenHtml = entry.children\n .map((child) => renderDocEntry(child, deps))\n .join(\"\");\n html += `<div class=\"doc-children\">${childrenHtml}</div>`;\n }\n\n return html;\n}\n","/**\n * Render steps list (fn(args, deps)).\n */\n\nimport type { DocEntry, StoryStep } from \"../../../types/story\";\nimport type { StepResult } from \"../../../types/test-result\";\n\nconst CONTINUATION_KEYWORDS = [\"And\", \"But\", \"*\"];\n\nexport interface RenderStepsArgs {\n steps: StoryStep[];\n stepResults: StepResult[];\n}\n\nexport interface RenderStepsDeps {\n escapeHtml: (str: string) => string;\n getStatusIcon: (status: import(\"../../../types/test-result.js\").TestStatus) => string;\n renderDocs: (docs: DocEntry[] | undefined, containerClass: string) => string;\n highlightStepParams?: (text: string) => string;\n}\n\nexport function renderStep(\n step: StoryStep,\n stepResult: StepResult | undefined,\n index: number,\n deps: RenderStepsDeps,\n): string {\n const statusIcon = stepResult ? deps.getStatusIcon(stepResult.status) : \"○\";\n const statusClass = stepResult ? `status-${stepResult.status}` : \"\";\n const duration =\n stepResult && stepResult.durationMs > 0\n ? `${stepResult.durationMs}ms`\n : \"\";\n\n const keywordTrimmed = step.keyword.trim();\n const isContinuation = CONTINUATION_KEYWORDS.includes(keywordTrimmed);\n const stepClass = isContinuation ? \"step continuation\" : \"step\";\n\n const stepDocs = deps.renderDocs(step.docs, \"step-docs\");\n\n const textHtml = deps.highlightStepParams\n ? deps.highlightStepParams(step.text)\n : deps.escapeHtml(step.text);\n\n return `<div class=\"${stepClass}\" data-keyword=\"${deps.escapeHtml(keywordTrimmed)}\" data-text=\"${deps.escapeHtml(step.text)}\">\n <span class=\"step-status ${statusClass}\">${statusIcon}</span>\n <span class=\"step-keyword\">${deps.escapeHtml(step.keyword)}</span>\n <span class=\"step-text\">${textHtml}</span>\n <span class=\"step-duration\">${duration}</span>\n</div>${stepDocs}`;\n}\n\nexport function renderSteps(\n args: RenderStepsArgs,\n deps: RenderStepsDeps,\n): string {\n const stepsHtml = args.steps\n .map((step, index) => {\n const stepResult = args.stepResults.find((sr) => sr.index === index);\n return renderStep(step, stepResult, index, deps);\n })\n .join(\"\");\n return `<div class=\"steps\">${stepsHtml}</div>`;\n}\n","/**\n * Highlight step parameters (quoted strings, standalone numbers) in step text.\n * Pure function following fn(args, deps) pattern.\n */\n\nexport interface HighlightStepParamsDeps {\n escapeHtml: (str: string) => string;\n}\n\n/**\n * Regex matches:\n * - `\"[^\"]*\"` — double-quoted strings (matched first, so numbers inside quotes are part of the string)\n * - `(?<![\\w.])\\d+(?:\\.\\d+)?(?![\\w.])` — standalone numbers with dot-aware boundaries\n */\nconst STEP_PARAM_PATTERN = /\"[^\"]*\"|(?<![\\w.\\-])\\d+(?:\\.\\d+)?(?![\\w.\\-])/g;\n\nexport function highlightStepParams(\n text: string,\n deps: HighlightStepParamsDeps,\n): string {\n const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));\n\n if (matches.length === 0) {\n return deps.escapeHtml(text);\n }\n\n let result = \"\";\n let lastIndex = 0;\n\n for (const match of matches) {\n const matchStart = match.index;\n const matchEnd = matchStart + match[0].length;\n\n // Append escaped plain text before this match\n if (matchStart > lastIndex) {\n result += deps.escapeHtml(text.slice(lastIndex, matchStart));\n }\n\n // Wrap the matched param in a span (also escape its content)\n result += `<span class=\"step-param\">${deps.escapeHtml(match[0])}</span>`;\n\n lastIndex = matchEnd;\n }\n\n // Append any remaining plain text after the last match\n if (lastIndex < text.length) {\n result += deps.escapeHtml(text.slice(lastIndex));\n }\n\n return result;\n}\n","/**\n * Centralized sample-size policy for history metrics.\n *\n * Easy to tune without hunting across files.\n */\n\n/** Minimum duration-bearing entries for performance trend analysis. */\nexport const MIN_PERF_SAMPLES = 6;\n\n/** Minimum entries before showing badges/metrics in reports. */\nexport const MIN_METRIC_SAMPLES = 5;\n\n/** Minimum entries for flakiness calculation (below this → \"stable\"). */\nexport const MIN_FLAKINESS_SAMPLES = 3;\n\n/** Check whether an array meets the minimum sample threshold. */\nexport function hasSufficientHistory(\n entries: unknown[],\n min: number,\n): boolean {\n return entries.length >= min;\n}\n","/**\n * Render a scenario element (fn(args, deps)).\n */\n\nimport type { DocEntry, NormalizedTicket } from \"../../../types/story\";\nimport type { TestCaseResult } from \"../../../types/test-result\";\nimport type { TestMetrics } from \"../../../history/types\";\nimport { MIN_METRIC_SAMPLES } from \"../../../history/sample-policy\";\n\nexport interface RenderScenarioArgs {\n tc: TestCaseResult;\n metrics?: TestMetrics;\n}\n\nexport interface RenderScenarioDeps {\n escapeHtml: (str: string) => string;\n getStatusIcon: (status: import(\"../../../types/test-result.js\").TestStatus) => string;\n startCollapsed: boolean;\n renderSteps: (\n args: import(\"./steps.js\").RenderStepsArgs,\n deps: import(\"./steps.js\").RenderStepsDeps,\n ) => string;\n renderDocs: (docs: DocEntry[] | undefined, containerClass: string) => string;\n renderErrorBox: (\n args: import(\"./error-box.js\").RenderErrorBoxArgs,\n deps: import(\"./error-box.js\").RenderErrorBoxDeps,\n ) => string;\n renderAttachments: (\n args: import(\"./attachments.js\").RenderAttachmentsArgs,\n deps: import(\"./attachments.js\").RenderAttachmentsDeps,\n ) => string;\n renderTraceView: (\n args: import(\"./trace-view.js\").RenderTraceViewArgs,\n deps: import(\"./trace-view.js\").RenderTraceViewDeps,\n ) => string;\n embedScreenshots: boolean;\n permalinkBaseUrl?: string;\n ticketUrlTemplate?: string;\n}\n\nfunction renderTicket(\n ticket: NormalizedTicket,\n template: string | undefined,\n escapeHtml: (s: string) => string,\n): string {\n const url = ticket.url ?? (template ? template.replace(\"{ticket}\", ticket.id) : undefined);\n if (url) {\n return `<a class=\"tag ticket-tag\" href=\"${escapeHtml(url)}\" target=\"_blank\" rel=\"noopener noreferrer\">${escapeHtml(ticket.id)}</a>`;\n }\n return `<span class=\"tag ticket-tag\">${escapeHtml(ticket.id)}</span>`;\n}\n\nexport function renderScenario(\n args: RenderScenarioArgs,\n deps: RenderScenarioDeps,\n): string {\n const { tc } = args;\n const statusIcon = deps.getStatusIcon(tc.status);\n const statusClass = `status-${tc.status}`;\n const duration =\n tc.durationMs > 0 ? `${(tc.durationMs / 1000).toFixed(2)}s` : \"\";\n\n const tags = tc.tags\n .map((t) => `<span class=\"tag\">${deps.escapeHtml(t)}</span>`)\n .join(\"\");\n\n const tickets = (tc.story.tickets ?? [])\n .map((t) => renderTicket(t, deps.ticketUrlTemplate, deps.escapeHtml))\n .join(\"\");\n\n // Trace badge from OTel bridge\n const otelMeta = (tc.story.meta as Record<string, unknown> | undefined)\n ?.otel as { traceId?: string } | undefined;\n let traceBadge = \"\";\n if (otelMeta?.traceId) {\n const shortId = otelMeta.traceId.slice(0, 16);\n // Look for a \"View Trace\" link in story-level docs for the URL\n const traceLink = tc.story.docs?.find(\n (d): d is Extract<typeof d, { kind: \"link\" }> =>\n d.kind === \"link\" && d.label === \"View Trace\",\n );\n if (traceLink) {\n traceBadge = `<a class=\"tag trace-tag\" href=\"${deps.escapeHtml(traceLink.url)}\" title=\"${deps.escapeHtml(otelMeta.traceId)}\" target=\"_blank\" rel=\"noopener\">${deps.escapeHtml(shortId)}…</a>`;\n } else {\n traceBadge = `<span class=\"tag trace-tag\" title=\"${deps.escapeHtml(otelMeta.traceId)}\">${deps.escapeHtml(shortId)}…</span>`;\n }\n }\n\n // History metric badges\n let metricBadges = \"\";\n const { metrics } = args;\n if (metrics && metrics.sampleSize >= MIN_METRIC_SAMPLES) {\n const grade = metrics.stabilityGrade;\n metricBadges += `<span class=\"badge badge-grade badge-grade-${grade}\" title=\"Pass rate: ${(metrics.passRate * 100).toFixed(0)}% (${metrics.sampleSize} runs)\">${grade}</span>`;\n\n if (metrics.flakinessLevel !== \"stable\") {\n metricBadges += `<span class=\"badge badge-flaky\">${metrics.flakinessLevel}</span>`;\n }\n\n if (metrics.performanceTrend !== \"stable\") {\n const arrow = metrics.performanceTrend === \"improving\" ? \"\\u2191\" : \"\\u2193\";\n metricBadges += `<span class=\"badge badge-perf badge-perf-${metrics.performanceTrend}\">${arrow} ${metrics.performanceTrend}</span>`;\n }\n }\n\n const storyDocs = deps.renderDocs(tc.story.docs, \"story-docs\");\n const steps = deps.renderSteps(\n { steps: tc.story.steps, stepResults: tc.stepResults },\n {\n escapeHtml: deps.escapeHtml,\n getStatusIcon: deps.getStatusIcon,\n renderDocs: deps.renderDocs,\n },\n );\n const error =\n tc.status === \"failed\" && tc.errorMessage\n ? deps.renderErrorBox(\n { message: tc.errorMessage, stack: tc.errorStack },\n { escapeHtml: deps.escapeHtml },\n )\n : \"\";\n const attachments = deps.renderAttachments(\n { attachments: tc.attachments },\n {\n escapeHtml: deps.escapeHtml,\n embedScreenshots: deps.embedScreenshots,\n },\n );\n\n const traceView = deps.renderTraceView(\n { spans: tc.story.otelSpans },\n { escapeHtml: deps.escapeHtml },\n );\n\n // Source permalink\n let sourceLink = \"\";\n if (deps.permalinkBaseUrl && tc.sourceFile && tc.sourceFile !== \"unknown\") {\n const fragment = tc.sourceLine > 0 ? `#L${tc.sourceLine}` : \"\";\n const href = `${deps.permalinkBaseUrl}/${tc.sourceFile}${fragment}`;\n const label = `${tc.sourceFile}${tc.sourceLine > 0 ? `:${tc.sourceLine}` : \"\"}`;\n sourceLink = `<a class=\"source-link\" href=\"${deps.escapeHtml(href)}\" target=\"_blank\" rel=\"noopener\">${deps.escapeHtml(label)}</a>`;\n }\n\n const collapsedClass = deps.startCollapsed ? \" collapsed\" : \"\";\n const ariaExpanded = !deps.startCollapsed;\n\n return `\n<div class=\"scenario${collapsedClass}\" id=\"scenario-${tc.id}\">\n <div class=\"scenario-header\" role=\"button\" tabindex=\"0\" aria-expanded=\"${ariaExpanded}\">\n <div class=\"scenario-info\">\n <div class=\"scenario-title\">\n <span class=\"status-icon ${statusClass}\">${statusIcon}</span>\n <span class=\"scenario-name\">${deps.escapeHtml(tc.story.scenario)}</span>\n </div>\n <div class=\"scenario-meta\">${tags}${tickets}${sourceLink}${traceBadge}${metricBadges}</div>\n </div>\n <div class=\"scenario-actions\">\n <button class=\"copy-scenario-btn\" onclick=\"copyScenarioAsMarkdown('scenario-${tc.id}')\" aria-label=\"Copy scenario as markdown\" title=\"Copy as Markdown\">&#x2398;</button>\n <button class=\"permalink-anchor\" onclick=\"copyPermalink('scenario-${tc.id}')\" aria-label=\"Copy link to scenario\" title=\"Copy link\">#</button>\n <span class=\"scenario-duration\">${duration}</span>\n </div>\n </div>\n <div class=\"scenario-content\">\n ${storyDocs}\n ${steps}\n ${error}\n ${attachments}\n ${traceView}\n </div>\n</div>`;\n}\n","/**\n * Render an OTel trace waterfall (fn(args, deps)).\n */\n\nimport type { OtelSpan } from \"../../../types/otel\";\n\nexport interface RenderTraceViewArgs {\n spans: OtelSpan[] | undefined;\n}\n\nexport interface RenderTraceViewDeps {\n escapeHtml: (str: string) => string;\n}\n\ninterface NormalizedSpan {\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n durationMs: number;\n status: \"ok\" | \"error\" | \"unset\";\n statusMessage?: string;\n attributes?: Record<string, unknown>;\n}\n\ninterface TreeNode {\n span: NormalizedSpan;\n children: TreeNode[];\n depth: number;\n}\n\nconst VALID_STATUSES = new Set<string>([\"ok\", \"error\", \"unset\"]);\nconst TOOLTIP_MAX_LENGTH = 4096;\n\nfunction safeStatus(status: string): \"ok\" | \"error\" | \"unset\" {\n return VALID_STATUSES.has(status) ? (status as \"ok\" | \"error\" | \"unset\") : \"unset\";\n}\n\nfunction formatDuration(ms: number): string {\n if (ms >= 1000) return `${(ms / 1000).toFixed(2)}s`;\n return `${ms.toFixed(1)}ms`;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction normalizeSpans(spans: OtelSpan[]): NormalizedSpan[] {\n const result: NormalizedSpan[] = [];\n for (const span of spans) {\n if (!span || typeof span !== \"object\") continue;\n if (typeof span.spanId !== \"string\" || typeof span.name !== \"string\") continue;\n\n let startTimeMs: number;\n let durationMs: number;\n\n if (span.startTimeMs != null && span.durationMs != null) {\n startTimeMs = span.startTimeMs;\n durationMs = span.durationMs;\n } else if (span.startTimeUnixNano != null && span.endTimeUnixNano != null) {\n startTimeMs = span.startTimeUnixNano / 1e6;\n durationMs = (span.endTimeUnixNano - span.startTimeUnixNano) / 1e6;\n } else {\n continue;\n }\n\n durationMs = Math.max(0, durationMs);\n if (!isFinite(startTimeMs) || !isFinite(durationMs)) continue;\n\n result.push({\n spanId: span.spanId,\n parentSpanId: span.parentSpanId,\n name: span.name,\n startTimeMs,\n durationMs,\n status: safeStatus(span.status),\n statusMessage: span.statusMessage,\n attributes: span.attributes,\n });\n }\n return result;\n}\n\nfunction buildTree(spans: NormalizedSpan[]): TreeNode[] {\n const byId = new Map<string, TreeNode>();\n for (const span of spans) {\n let key = span.spanId;\n if (byId.has(key)) {\n let suffix = 2;\n while (byId.has(`${span.spanId}__dup${suffix}`)) suffix++;\n key = `${span.spanId}__dup${suffix}`;\n }\n byId.set(key, { span: { ...span, spanId: key }, children: [], depth: 0 });\n }\n\n const roots: TreeNode[] = [];\n for (const node of byId.values()) {\n const parentId = node.span.parentSpanId;\n const parent = parentId ? byId.get(parentId) : undefined;\n if (parent && parent !== node) {\n parent.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n // Sort children by startTimeMs within each parent\n for (const node of byId.values()) {\n node.children.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);\n }\n roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);\n\n // Assign depths via DFS with cycle guard\n const visited = new Set<string>();\n function assignDepth(node: TreeNode, depth: number): void {\n if (visited.has(node.span.spanId)) return;\n visited.add(node.span.spanId);\n node.depth = depth;\n for (const child of node.children) {\n assignDepth(child, depth + 1);\n }\n }\n for (const root of roots) {\n assignDepth(root, 0);\n }\n\n // Promote any unvisited nodes to roots (handles cycles like A→B, B→A)\n for (const node of byId.values()) {\n if (!visited.has(node.span.spanId)) {\n node.children = [];\n roots.push(node);\n assignDepth(node, 0);\n }\n }\n roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);\n\n return roots;\n}\n\nfunction flattenTree(roots: TreeNode[]): TreeNode[] {\n const result: TreeNode[] = [];\n function walk(node: TreeNode): void {\n result.push(node);\n for (const child of node.children) {\n walk(child);\n }\n }\n for (const root of roots) {\n walk(root);\n }\n return result;\n}\n\nfunction buildTooltip(\n span: NormalizedSpan,\n escapeHtml: (s: string) => string,\n): string {\n const parts: string[] = [];\n parts.push(`${span.name} (${formatDuration(span.durationMs)})`);\n\n if (span.statusMessage) {\n parts.push(`Status: ${span.statusMessage}`);\n }\n\n if (span.attributes) {\n const keys = Object.keys(span.attributes).sort();\n for (const key of keys) {\n const val = span.attributes[key];\n const formatted = Array.isArray(val)\n ? `[${val.map((v) => String(v)).join(\", \")}]`\n : String(val);\n parts.push(`${key}=${formatted}`);\n }\n }\n\n let text = parts.join(\"\\n\");\n if (text.length > TOOLTIP_MAX_LENGTH) {\n text = text.slice(0, TOOLTIP_MAX_LENGTH - 3) + \"...\";\n }\n\n return escapeHtml(text);\n}\n\nexport function renderTraceView(\n args: RenderTraceViewArgs,\n deps: RenderTraceViewDeps,\n): string {\n if (!args.spans || args.spans.length === 0) return \"\";\n\n const normalized = normalizeSpans(args.spans);\n if (normalized.length === 0) return \"\";\n\n const roots = buildTree(normalized);\n const flat = flattenTree(roots);\n\n // Compute relative scale\n let minStart = Infinity;\n let maxEnd = -Infinity;\n for (const node of flat) {\n const s = node.span.startTimeMs;\n const e = s + node.span.durationMs;\n if (s < minStart) minStart = s;\n if (e > maxEnd) maxEnd = e;\n }\n let totalDuration = maxEnd - minStart;\n if (totalDuration <= 0) totalDuration = 1;\n\n // Render rows\n const rows = flat\n .map((node) => {\n const { span, depth } = node;\n const indent = depth * 16;\n const minWidth = 0.5;\n let spanLeft = clamp(\n ((span.startTimeMs - minStart) / totalDuration) * 100,\n 0,\n 100,\n );\n // Nudge left so the min-width bar stays within bounds\n if (spanLeft + minWidth > 100) {\n spanLeft = 100 - minWidth;\n }\n const spanWidth = clamp(\n (span.durationMs / totalDuration) * 100,\n minWidth,\n 100 - spanLeft,\n );\n const tooltip = buildTooltip(span, deps.escapeHtml);\n const durationLabel = formatDuration(span.durationMs);\n\n return ` <div class=\"trace-view-row\">\n <div class=\"trace-view-name\" style=\"padding-left: ${indent}px\" title=\"${deps.escapeHtml(span.name)}\">\n <span class=\"trace-view-status-dot trace-view-status-${span.status}\"></span>\n ${deps.escapeHtml(span.name)}\n </div>\n <div class=\"trace-view-bar-container\">\n <div class=\"trace-view-bar trace-view-bar-${span.status}\" style=\"left: ${spanLeft.toFixed(2)}%; width: ${spanWidth.toFixed(2)}%\" title=\"${tooltip}\">${durationLabel}</div>\n </div>\n </div>`;\n })\n .join(\"\\n\");\n\n const axisEnd = formatDuration(maxEnd - minStart);\n\n return `<div class=\"trace-view collapsed\">\n <div class=\"trace-view-header\" role=\"button\" tabindex=\"0\" aria-expanded=\"false\">\n <span>Spans</span>\n <span class=\"trace-view-count\">${flat.length}</span>\n <span class=\"chevron\">&#9660;</span>\n </div>\n <div class=\"trace-view-content\">\n <div class=\"trace-view-axis\">\n <span>0ms</span>\n <span>${axisEnd}</span>\n </div>\n${rows}\n </div>\n</div>`;\n}\n","/**\n * Render a feature section (group of scenarios from same file) (fn(args, deps)).\n */\n\nimport type { TestCaseResult } from \"../../../types/test-result\";\nimport type { TestMetrics } from \"../../../history/types\";\nimport { slugify } from \"../../../converters/acl/ids.js\";\n\nexport interface RenderFeatureArgs {\n file: string;\n testCases: TestCaseResult[];\n metricsMap?: Map<string, TestMetrics>;\n}\n\nexport interface RenderFeatureDeps {\n escapeHtml: (str: string) => string;\n startCollapsed: boolean;\n renderScenario: (\n args: import(\"./scenario.js\").RenderScenarioArgs,\n deps: import(\"./scenario.js\").RenderScenarioDeps,\n ) => string;\n scenarioDeps: import(\"./scenario.js\").RenderScenarioDeps;\n}\n\nexport function renderFeature(\n args: RenderFeatureArgs,\n deps: RenderFeatureDeps,\n): string {\n const { file, testCases } = args;\n const passed = testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const collapsedClass = deps.startCollapsed ? \" collapsed\" : \"\";\n const ariaExpanded = !deps.startCollapsed;\n const featureSlug = `feature-${slugify(file)}`;\n\n const scenarios = testCases\n .map((tc) =>\n deps.renderScenario(\n { tc, metrics: args.metricsMap?.get(tc.id) },\n deps.scenarioDeps,\n ),\n )\n .join(\"\\n\");\n\n return `\n<div class=\"feature${collapsedClass}\" id=\"${featureSlug}\">\n <div class=\"feature-header\" role=\"button\" tabindex=\"0\" aria-expanded=\"${ariaExpanded}\">\n <button class=\"permalink-anchor\" onclick=\"copyPermalink('${featureSlug}')\" aria-label=\"Copy link to feature\" title=\"Copy link\">#</button>\n <div class=\"feature-info\">\n <div class=\"feature-title\">${deps.escapeHtml(featureName)}</div>\n <div class=\"feature-path\">${deps.escapeHtml(file)}</div>\n </div>\n <div class=\"feature-stats\">\n <span class=\"stat passed\">✓ ${passed}</span>\n <span class=\"stat failed\">✗ ${failed}</span>\n <span class=\"stat skipped\">○ ${skipped}</span>\n <span class=\"chevron\">▼</span>\n </div>\n </div>\n <div class=\"feature-content\">\n ${scenarios}\n </div>\n</div>`;\n}\n","/**\n * Build report body from run (fn(args, deps)).\n * Composes meta, summary, tag bar, and features; uses groupBy for feature grouping.\n */\n\nimport type { TestRunResult } from \"../../../types/test-result\";\nimport type { TestMetrics } from \"../../../history/types\";\nimport type { RenderTagBarArgs, RenderTagBarDeps } from \"./tag-bar.js\";\nimport type { RenderFailureSummaryArgs, RenderFailureSummaryDeps } from \"./failure-summary.js\";\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nexport interface BuildBodyArgs {\n run: TestRunResult;\n metricsMap?: Map<string, TestMetrics>;\n}\n\nexport interface BuildBodyDeps {\n renderMetaInfo: (\n args: import(\"./meta.js\").RenderMetaInfoArgs,\n deps: import(\"./meta.js\").RenderMetaInfoDeps,\n ) => string;\n renderSummary: (\n args: import(\"./summary.js\").RenderSummaryArgs,\n deps: import(\"./summary.js\").RenderSummaryDeps,\n ) => string;\n renderTagBar: (args: RenderTagBarArgs, deps: RenderTagBarDeps) => string;\n renderFeature: (\n args: import(\"./feature.js\").RenderFeatureArgs,\n deps: import(\"./feature.js\").RenderFeatureDeps,\n ) => string;\n renderFailureSummary: (\n args: RenderFailureSummaryArgs,\n deps: RenderFailureSummaryDeps,\n ) => string;\n metaDeps: import(\"./meta.js\").RenderMetaInfoDeps;\n summaryDeps: import(\"./summary.js\").RenderSummaryDeps;\n tagBarDeps: RenderTagBarDeps;\n featureDeps: import(\"./feature.js\").RenderFeatureDeps;\n failureSummaryDeps: RenderFailureSummaryDeps;\n}\n\nexport function buildBody(args: BuildBodyArgs, deps: BuildBodyDeps): string {\n const { run } = args;\n\n const parts: string[] = [];\n\n parts.push(\n deps.renderMetaInfo(\n {\n startedAtMs: run.startedAtMs,\n durationMs: run.durationMs,\n packageVersion: run.packageVersion,\n gitSha: run.gitSha,\n ciName: run.ci?.name,\n ciBranch: run.ci?.branch,\n ciUrl: run.ci?.url,\n ciCommitSha: run.ci?.commitSha,\n ciBuildNumber: run.ci?.buildNumber,\n },\n deps.metaDeps,\n ),\n );\n\n const total = run.testCases.length;\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n parts.push(\n deps.renderSummary(\n { total, passed, failed, skipped },\n deps.summaryDeps,\n ),\n );\n\n const allTags = [\n ...new Set(run.testCases.flatMap((tc) => tc.tags)),\n ].sort();\n parts.push(\n deps.renderTagBar(\n { tags: allTags, totalScenarios: total },\n deps.tagBarDeps,\n ),\n );\n\n const failedCases = run.testCases.filter((tc) => tc.status === \"failed\");\n if (failedCases.length > 0) {\n parts.push(\n deps.renderFailureSummary(\n { failedCases },\n deps.failureSummaryDeps,\n ),\n );\n }\n\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n for (const [file, testCases] of byFile) {\n parts.push(\n deps.renderFeature(\n { file, testCases, metricsMap: args.metricsMap },\n deps.featureDeps,\n ),\n );\n }\n\n return parts.join(\"\\n\");\n}\n","/**\n * Render failure summary block with deep links to failed scenarios (fn(args, deps)).\n */\n\nimport type { TestCaseResult } from \"../../../types/test-result\";\n\nexport interface RenderFailureSummaryArgs {\n failedCases: TestCaseResult[];\n}\n\nexport interface RenderFailureSummaryDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderFailureSummary(\n args: RenderFailureSummaryArgs,\n deps: RenderFailureSummaryDeps,\n): string {\n const { failedCases } = args;\n if (failedCases.length === 0) return \"\";\n\n const items = failedCases\n .map((tc) => {\n const name = deps.escapeHtml(tc.story.scenario);\n return `<li><a href=\"#scenario-${tc.id}\">${name}</a></li>`;\n })\n .join(\"\\n \");\n\n return `\n<div class=\"failure-summary\">\n <div class=\"failure-summary-header\">Failed (${failedCases.length})</div>\n <div class=\"failure-summary-note\">\n For review-grade output, generate a compare report with <code>compare --pr-summary</code>.\n </div>\n <ul>\n ${items}\n </ul>\n</div>`;\n}\n","/**\n * Render table of contents sidebar (fn(args, deps)).\n */\n\nimport type { TestRunResult, TestStatus } from \"../../../types/test-result.js\";\nimport { slugify } from \"../../../converters/acl/ids.js\";\n\nexport interface RenderTocArgs {\n run: TestRunResult;\n}\n\nexport interface RenderTocDeps {\n escapeHtml: (str: string) => string;\n getStatusIcon: (status: TestStatus) => string;\n}\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nexport function renderToc(args: RenderTocArgs, deps: RenderTocDeps): string {\n const { run } = args;\n if (run.testCases.length === 0) return \"\";\n\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n const features: string[] = [];\n\n for (const [file, testCases] of byFile) {\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const featureSlug = `feature-${slugify(file)}`;\n\n const scenarios = testCases\n .map((tc) => {\n const statusIcon = deps.getStatusIcon(tc.status);\n const statusClass = `status-${tc.status}`;\n const failedClass = tc.status === \"failed\" ? \" toc-failed\" : \"\";\n return `<a class=\"toc-scenario${failedClass}\" href=\"#scenario-${tc.id}\">\n <span class=\"toc-status ${statusClass}\">${statusIcon}</span>\n ${deps.escapeHtml(tc.story.scenario)}\n </a>`;\n })\n .join(\"\\n\");\n\n features.push(`<div class=\"toc-feature\">\n <button class=\"toc-feature-toggle\" aria-expanded=\"true\" onclick=\"this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') === 'true' ? 'false' : 'true'); this.nextElementSibling.style.display = this.getAttribute('aria-expanded') === 'true' ? '' : 'none'\" data-feature=\"#${featureSlug}\">\n ${deps.escapeHtml(featureName)}\n </button>\n <div class=\"toc-scenarios\">\n ${scenarios}\n </div>\n </div>`);\n }\n\n return `<nav class=\"toc-sidebar\" aria-label=\"Table of contents\">\n <div class=\"toc-header\">\n <span class=\"toc-title\">Contents</span>\n </div>\n <div class=\"toc-body\">\n ${features.join(\"\\n\")}\n </div>\n</nav>`;\n}\n","/**\n * HTML renderers and factory (fn(args, deps) pattern).\n * Exports all render functions and createHtmlFormatter.\n */\n\nimport type { DocEntry } from \"../../../types/story\";\nimport type { TestRunResult } from \"../../../types/test-result\";\nimport { escapeHtml, generateHtmlTemplate } from \"../template\";\nimport { CSS_STYLES } from \"../styles\";\nimport type { HtmlTheme } from \"../themes/types.js\";\nimport { resolveTheme, getCssOnlyThemes } from \"../themes/index.js\";\nimport { getStatusIcon } from \"./status\";\nimport { renderMetaInfo } from \"./meta\";\nimport { renderSummary } from \"./summary\";\nimport { renderTagBar } from \"./tag-bar\";\nimport { renderErrorBox } from \"./error-box\";\nimport { renderAttachments } from \"./attachments\";\nimport { renderDocEntry } from \"./doc-entries\";\nimport { renderSteps } from \"./steps\";\nimport { highlightStepParams } from \"./step-params\";\nimport { renderScenario } from \"./scenario\";\nimport { renderTraceView } from \"./trace-view\";\nimport { renderFeature } from \"./feature\";\nimport { buildBody } from \"./body\";\nimport { renderFailureSummary } from \"./failure-summary\";\nimport { renderToc } from \"./toc\";\n\n/** Options for HTML formatting (subset used by createHtmlFormatter) */\nexport interface HtmlFormatterOptions {\n title?: string;\n darkMode?: boolean;\n searchable?: boolean;\n startCollapsed?: boolean;\n embedScreenshots?: boolean;\n syntaxHighlighting?: boolean;\n mermaidEnabled?: boolean;\n markdownEnabled?: boolean;\n permalinkBaseUrl?: string;\n /** URL template for ticket links. Use {ticket} as placeholder. E.g., \"https://jira.example.com/browse/{ticket}\" */\n ticketUrlTemplate?: string;\n /** Show table of contents sidebar. Default: true */\n tocEnabled?: boolean;\n /** Theme name or custom theme object. Default: \"default\" */\n theme?: string | HtmlTheme;\n /** Include theme picker with all CSS-only themes embedded. Default: false */\n themePickerEnabled?: boolean;\n}\n\nfunction normalizeOptions(options: HtmlFormatterOptions = {}) {\n return {\n title: options.title ?? \"Test Results\",\n darkMode: options.darkMode ?? true,\n searchable: options.searchable ?? true,\n startCollapsed: options.startCollapsed ?? false,\n embedScreenshots: options.embedScreenshots ?? true,\n syntaxHighlighting: options.syntaxHighlighting ?? true,\n mermaidEnabled: options.mermaidEnabled ?? true,\n markdownEnabled: options.markdownEnabled ?? true,\n permalinkBaseUrl: options.permalinkBaseUrl,\n ticketUrlTemplate: options.ticketUrlTemplate,\n tocEnabled: options.tocEnabled ?? true,\n theme: options.theme ?? \"default\",\n themePickerEnabled: options.themePickerEnabled ?? false,\n };\n}\n\n/**\n * Factory: wire deps once, return { format(run) }.\n */\nexport function createHtmlFormatter(\n options: HtmlFormatterOptions = {},\n): { format(run: TestRunResult): string } {\n const opts = normalizeOptions(options);\n\n const docEntryDeps = {\n escapeHtml,\n syntaxHighlighting: opts.syntaxHighlighting,\n markdownEnabled: opts.markdownEnabled,\n mermaidEnabled: opts.mermaidEnabled,\n };\n\n const renderDocs = (\n docs: DocEntry[] | undefined,\n containerClass: string,\n ): string => {\n if (!docs || docs.length === 0) return \"\";\n const entries = docs.map((entry) => renderDocEntry(entry, docEntryDeps)).join(\"\");\n return `<div class=\"${containerClass}\">${entries}</div>`;\n };\n\n const stepsDeps = {\n escapeHtml,\n getStatusIcon,\n renderDocs,\n highlightStepParams: (text: string) =>\n highlightStepParams(text, { escapeHtml }),\n };\n\n const scenarioDeps = {\n escapeHtml,\n getStatusIcon,\n startCollapsed: opts.startCollapsed,\n renderSteps: (args: import(\"./steps.js\").RenderStepsArgs) =>\n renderSteps(args, stepsDeps),\n renderDocs,\n renderErrorBox: (\n args: import(\"./error-box.js\").RenderErrorBoxArgs,\n d: import(\"./error-box.js\").RenderErrorBoxDeps,\n ) => renderErrorBox(args, d),\n renderAttachments: (\n args: import(\"./attachments.js\").RenderAttachmentsArgs,\n d: import(\"./attachments.js\").RenderAttachmentsDeps,\n ) => renderAttachments(args, d),\n renderTraceView: (\n args: import(\"./trace-view.js\").RenderTraceViewArgs,\n d: import(\"./trace-view.js\").RenderTraceViewDeps,\n ) => renderTraceView(args, d),\n embedScreenshots: opts.embedScreenshots,\n permalinkBaseUrl: opts.permalinkBaseUrl,\n ticketUrlTemplate: opts.ticketUrlTemplate,\n };\n\n const featureDeps = {\n escapeHtml,\n startCollapsed: opts.startCollapsed,\n renderScenario: (args: import(\"./scenario.js\").RenderScenarioArgs) =>\n renderScenario(args, scenarioDeps),\n scenarioDeps,\n };\n\n const tagBarDeps = { escapeHtml };\n\n const tocDeps = {\n escapeHtml,\n getStatusIcon,\n };\n\n const bodyDeps = {\n renderMetaInfo,\n renderSummary,\n renderTagBar,\n renderFeature,\n renderFailureSummary,\n metaDeps: { escapeHtml },\n summaryDeps: {},\n tagBarDeps,\n featureDeps,\n failureSummaryDeps: { escapeHtml },\n };\n\n const theme = resolveTheme(opts.theme);\n\n return {\n format(run: TestRunResult): string {\n const bodyFn = theme.buildBody ?? buildBody;\n const body = bodyFn({ run }, bodyDeps);\n const templateFn = theme.generateTemplate ?? generateHtmlTemplate;\n\n // Only inject default TOC for themes that don't override body/template layout\n const isStructuralTheme = !!(theme.buildBody || theme.generateTemplate);\n const tocHtml = opts.tocEnabled && !isStructuralTheme ? renderToc({ run }, tocDeps) : undefined;\n\n let themePickerHtml: string | undefined;\n let additionalThemeCss: Array<{ name: string; label: string; css: string }> | undefined;\n\n if (opts.themePickerEnabled) {\n const cssOnlyThemes = getCssOnlyThemes();\n const pickerOptions = cssOnlyThemes\n .map(t => `<option value=\"${t.name}\"${t.name === theme.name ? ' selected' : ''}>${t.label}</option>`)\n .join('');\n themePickerHtml = `<select class=\"theme-picker\" aria-label=\"Select theme\">${pickerOptions}</select>`;\n additionalThemeCss = cssOnlyThemes\n .filter(t => t.name !== theme.name)\n .map(t => ({ name: t.name, label: t.label, css: t.css }));\n }\n\n return templateFn(\n opts.title,\n theme.css,\n body,\n {\n includeSearch: opts.searchable,\n includeDarkMode: opts.darkMode,\n syntaxHighlighting: opts.syntaxHighlighting,\n mermaidEnabled: opts.mermaidEnabled,\n markdownEnabled: opts.markdownEnabled,\n additionalJs: theme.additionalJs,\n additionalImports: theme.additionalImports,\n tocHtml,\n themePickerHtml,\n additionalThemeCss,\n activeThemeName: theme.name,\n },\n );\n },\n };\n}\n\nexport { renderMetaInfo } from \"./meta\";\nexport { renderSummary } from \"./summary\";\nexport { renderTagBar } from \"./tag-bar\";\nexport { renderErrorBox } from \"./error-box\";\nexport { renderAttachments } from \"./attachments\";\nexport {\n renderDocEntry,\n renderDocNote,\n renderDocTag,\n renderDocKv,\n renderDocCode,\n renderDocTable,\n renderDocLink,\n renderDocSection,\n renderDocMermaid,\n renderDocScreenshot,\n renderDocCustom,\n} from \"./doc-entries\";\nexport { highlightStepParams } from \"./step-params\";\nexport { renderSteps, renderStep } from \"./steps\";\nexport { renderScenario } from \"./scenario\";\nexport { renderTraceView } from \"./trace-view\";\nexport { renderFeature } from \"./feature\";\nexport { buildBody } from \"./body\";\nexport { renderFailureSummary } from \"./failure-summary\";\nexport { getStatusIcon } from \"./status\";\nexport type { DocEntryDeps } from \"./doc-entries\";\nexport type { RenderMetaInfoArgs, RenderMetaInfoDeps } from \"./meta\";\nexport type { RenderSummaryArgs, RenderSummaryDeps } from \"./summary\";\nexport type { RenderTagBarArgs, RenderTagBarDeps } from \"./tag-bar\";\nexport type { RenderErrorBoxArgs, RenderErrorBoxDeps } from \"./error-box\";\nexport type { RenderAttachmentsArgs, RenderAttachmentsDeps } from \"./attachments\";\nexport type { HighlightStepParamsDeps } from \"./step-params\";\nexport type { RenderStepsArgs, RenderStepsDeps } from \"./steps\";\nexport type { RenderScenarioArgs, RenderScenarioDeps } from \"./scenario\";\nexport type { RenderTraceViewArgs, RenderTraceViewDeps } from \"./trace-view\";\nexport type { RenderFeatureArgs, RenderFeatureDeps } from \"./feature\";\nexport type { BuildBodyArgs, BuildBodyDeps } from \"./body\";\nexport type { RenderFailureSummaryArgs, RenderFailureSummaryDeps } from \"./failure-summary\";\nexport { renderToc } from \"./toc\";\nexport type { RenderTocArgs, RenderTocDeps } from \"./toc\";\n","/**\n * HTML Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into a standalone HTML report.\n * Implemented via createHtmlFormatter (fn(args, deps) pattern).\n */\n\nimport type { TestRunResult } from \"../../types/test-result\";\nimport { createHtmlFormatter } from \"./renderers/index\";\n\nimport type { HtmlTheme } from \"./themes/types\";\n\n/** Options for HTML formatting */\nexport interface HtmlOptions {\n /** Report title. Default: \"Test Results\" */\n title?: string;\n /** Include dark mode toggle. Default: true */\n darkMode?: boolean;\n /** Include search/filter functionality. Default: true */\n searchable?: boolean;\n /** Start scenarios collapsed. Default: false */\n startCollapsed?: boolean;\n /** Embed screenshots inline (base64). Default: true */\n embedScreenshots?: boolean;\n /** Enable syntax highlighting for code blocks (via highlight.js CDN). Default: true */\n syntaxHighlighting?: boolean;\n /** Enable live Mermaid diagram rendering (via Mermaid.js CDN). Default: true */\n mermaidEnabled?: boolean;\n /** Enable Markdown parsing for section doc entries (via marked.js CDN). Default: true */\n markdownEnabled?: boolean;\n /** Base URL for source permalinks. E.g., \"https://github.com/user/repo/blob/main\" */\n permalinkBaseUrl?: string;\n /** URL template for ticket links. Use {ticket} as placeholder. E.g., \"https://jira.example.com/browse/{ticket}\" */\n ticketUrlTemplate?: string;\n /** Show table of contents sidebar. Default: true */\n tocEnabled?: boolean;\n /** Theme name or custom theme object. Default: \"default\" */\n theme?: string | HtmlTheme;\n /** Include theme picker with all CSS-only themes embedded. Default: false */\n themePickerEnabled?: boolean;\n}\n\n/**\n * HTML Formatter.\n *\n * Transforms TestRunResult into a standalone HTML report with:\n * - Dark/light mode toggle\n * - Search/filter functionality\n * - Collapsible features and scenarios\n * - Modern, accessible design\n *\n * Thin wrapper around createHtmlFormatter for backward compatibility.\n */\nexport class HtmlFormatter {\n private formatFn: (run: TestRunResult) => string;\n\n constructor(options: HtmlOptions = {}) {\n const wired = createHtmlFormatter(options);\n this.formatFn = wired.format.bind(wired);\n }\n\n /**\n * Format a test run into standalone HTML.\n *\n * @param run - Canonical test run result\n * @returns HTML string\n */\n format(run: TestRunResult): string {\n return this.formatFn(run);\n }\n}\n\nexport { createHtmlFormatter } from \"./renderers/index\";\nexport type { HtmlFormatterOptions } from \"./renderers/index\";\nexport { escapeHtml, generateHtmlTemplate } from \"./template\";\nexport { CSS_STYLES } from \"./styles\";\nexport {\n renderMetaInfo,\n renderSummary,\n renderErrorBox,\n renderAttachments,\n renderDocEntry,\n renderDocNote,\n renderDocTag,\n renderDocKv,\n renderDocCode,\n renderDocTable,\n renderDocLink,\n renderDocSection,\n renderDocMermaid,\n renderDocScreenshot,\n renderDocCustom,\n renderSteps,\n renderStep,\n renderScenario,\n renderFeature,\n renderFailureSummary,\n buildBody,\n getStatusIcon,\n} from \"./renderers/index\";\nexport { renderToc } from \"./renderers/toc\";\nexport type { HtmlTheme, HtmlThemeName } from \"./themes/index\";\nexport { resolveTheme, getAvailableThemes, getCssOnlyThemes } from \"./themes/index\";\n","/**\n * JUnit XML Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into JUnit XML format\n * for CI system integration.\n */\n\nimport type { DocEntry, StoryStep } from \"../types/story\";\nimport type { TestRunResult, TestCaseResult } from \"../types/test-result\";\n\n/** Options for JUnit XML formatting */\nexport interface JUnitOptions {\n /** Test suite name. Default: \"Test Suite\" */\n suiteName?: string;\n /** Include system-out/system-err. Default: true */\n includeOutput?: boolean;\n /** Pretty-print XML output. Default: true */\n pretty?: boolean;\n}\n\n/**\n * JUnit XML Formatter.\n *\n * Transforms TestRunResult into JUnit XML format for CI integrations.\n * Compatible with Jenkins, GitHub Actions, and other CI systems.\n */\nexport class JUnitFormatter {\n private options: Required<JUnitOptions>;\n\n constructor(options: JUnitOptions = {}) {\n this.options = {\n suiteName: options.suiteName ?? \"Test Suite\",\n includeOutput: options.includeOutput ?? true,\n pretty: options.pretty ?? true,\n };\n }\n\n /**\n * Format a test run into JUnit XML.\n *\n * @param run - Canonical test run result\n * @returns JUnit XML string\n */\n format(run: TestRunResult): string {\n const indent = this.options.pretty ? \" \" : \"\";\n const newline = this.options.pretty ? \"\\n\" : \"\";\n\n // Calculate totals\n const tests = run.testCases.length;\n const failures = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\"\n ).length;\n const errors = 0; // We don't distinguish errors from failures\n const time = (run.durationMs / 1000).toFixed(3);\n\n // Build XML\n const lines: string[] = [];\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n lines.push(\n `<testsuites name=\"${escapeXml(this.options.suiteName)}\" tests=\"${tests}\" failures=\"${failures}\" errors=\"${errors}\" skipped=\"${skipped}\" time=\"${time}\">`\n );\n\n // Group test cases by source file (one testsuite per file)\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n\n for (const [file, testCases] of byFile) {\n lines.push(...this.buildTestSuite(file, testCases, indent, newline));\n }\n\n lines.push(\"</testsuites>\");\n\n return lines.join(newline);\n }\n\n /**\n * Build a testsuite element for a file.\n */\n private buildTestSuite(\n file: string,\n testCases: TestCaseResult[],\n indent: string,\n newline: string\n ): string[] {\n const lines: string[] = [];\n\n const tests = testCases.length;\n const failures = testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\"\n ).length;\n const time = testCases\n .reduce((sum, tc) => sum + tc.durationMs, 0) / 1000;\n\n // Normalize path separators for consistent output across platforms\n const normalizedFile = file.replace(/\\\\/g, \"/\");\n\n lines.push(\n `${indent}<testsuite name=\"${escapeXml(normalizedFile)}\" tests=\"${tests}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\" time=\"${time.toFixed(3)}\">`\n );\n\n for (const tc of testCases) {\n lines.push(...this.buildTestCase(tc, indent + indent, newline));\n }\n\n lines.push(`${indent}</testsuite>`);\n\n return lines;\n }\n\n /**\n * Build a testcase element.\n */\n private buildTestCase(\n tc: TestCaseResult,\n indent: string,\n newline: string\n ): string[] {\n const lines: string[] = [];\n\n // Build classname from titlePath or sourceFile\n // Normalize path separators (Windows backslashes and Unix forward slashes)\n const classname = tc.titlePath.length > 0\n ? tc.titlePath.join(\".\")\n : tc.sourceFile\n .replace(/[\\\\/]+/g, \".\") // Replace path separators with dots\n .replace(/\\.[^.]+$/, \"\"); // Remove file extension\n\n const name = tc.story.scenario;\n const time = (tc.durationMs / 1000).toFixed(3);\n\n const hasFailure = tc.status === \"failed\";\n const hasSkipped = tc.status === \"skipped\" || tc.status === \"pending\";\n const hasOutput = this.options.includeOutput && tc.story.steps.length > 0;\n\n // Use full form if there's any content to include\n if (hasFailure || hasSkipped || hasOutput) {\n lines.push(\n `${indent}<testcase classname=\"${escapeXml(classname)}\" name=\"${escapeXml(name)}\" time=\"${time}\">`\n );\n\n if (hasFailure) {\n const message = tc.errorMessage\n ? escapeXml(tc.errorMessage.split(\"\\n\")[0])\n : \"Test failed\";\n lines.push(`${indent}${indent}<failure message=\"${message}\">`);\n if (tc.errorMessage) {\n lines.push(escapeXml(tc.errorMessage));\n }\n if (tc.errorStack) {\n lines.push(\"\");\n lines.push(escapeXml(tc.errorStack));\n }\n lines.push(`${indent}${indent}</failure>`);\n } else if (hasSkipped) {\n const message = tc.status === \"pending\" ? \"Test pending\" : \"Test skipped\";\n lines.push(`${indent}${indent}<skipped message=\"${message}\"/>`);\n }\n\n // Include system-out with step info and docs if requested\n if (hasOutput) {\n const output = this.buildSystemOut(tc);\n lines.push(`${indent}${indent}<system-out>${escapeXml(output)}</system-out>`);\n }\n\n lines.push(`${indent}</testcase>`);\n } else {\n // Self-closing tag for tests without content\n lines.push(\n `${indent}<testcase classname=\"${escapeXml(classname)}\" name=\"${escapeXml(name)}\" time=\"${time}\"/>`\n );\n }\n\n return lines;\n }\n\n /**\n * Build system-out content with steps and docs.\n */\n private buildSystemOut(tc: TestCaseResult): string {\n const outputLines: string[] = [];\n\n // Story-level docs\n if (tc.story.docs && tc.story.docs.length > 0) {\n for (const doc of tc.story.docs) {\n outputLines.push(this.renderDocEntry(doc));\n }\n outputLines.push(\"\");\n }\n\n // Steps with their docs\n for (const step of tc.story.steps) {\n outputLines.push(this.renderStep(step));\n }\n\n return outputLines.join(\"\\n\").trim();\n }\n\n /**\n * Render a step with its docs.\n */\n private renderStep(step: StoryStep): string {\n const lines: string[] = [];\n lines.push(`${step.keyword} ${step.text}`);\n\n // Step docs\n if (step.docs && step.docs.length > 0) {\n for (const doc of step.docs) {\n const rendered = this.renderDocEntry(doc, \" \");\n if (rendered) {\n lines.push(rendered);\n }\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Render a doc entry as plain text.\n */\n private renderDocEntry(entry: DocEntry, indent = \"\"): string {\n switch (entry.kind) {\n case \"note\":\n return `${indent}> ${entry.text}`;\n\n case \"tag\":\n return `${indent}Tags: ${entry.names.join(\", \")}`;\n\n case \"kv\": {\n const val = typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value);\n return `${indent}${entry.label}: ${val}`;\n }\n\n case \"code\": {\n const langLabel = entry.lang ? ` (${entry.lang})` : \"\";\n const header = entry.label ? `${indent}${entry.label}${langLabel}:\\n` : \"\";\n const codeLines = entry.content.split(\"\\n\").map((l) => `${indent} ${l}`).join(\"\\n\");\n return `${header}${codeLines}`;\n }\n\n case \"table\": {\n const lines: string[] = [];\n if (entry.label) {\n lines.push(`${indent}${entry.label}:`);\n }\n lines.push(`${indent}| ${entry.columns.join(\" | \")} |`);\n lines.push(`${indent}| ${entry.columns.map(() => \"---\").join(\" | \")} |`);\n for (const row of entry.rows) {\n lines.push(`${indent}| ${row.join(\" | \")} |`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"link\":\n return `${indent}${entry.label}: ${entry.url}`;\n\n case \"section\": {\n const lines: string[] = [];\n lines.push(`${indent}${entry.title}:`);\n for (const line of entry.markdown.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"mermaid\": {\n const lines: string[] = [];\n if (entry.title) {\n lines.push(`${indent}${entry.title}:`);\n }\n for (const line of entry.code.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"screenshot\":\n return `${indent}Screenshot: ${entry.alt ?? entry.path}`;\n\n case \"custom\": {\n const dataStr = JSON.stringify(entry.data, null, 2);\n const lines: string[] = [];\n lines.push(`${indent}[${entry.type}]:`);\n for (const line of dataStr.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n default:\n return \"\";\n }\n }\n}\n\n/**\n * Escape special XML characters.\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n\n/**\n * Group array items by a key function.\n */\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n","/**\n * Markdown Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into Markdown documentation.\n * Compatible with existing markdown output from framework reporters.\n */\n\nimport type { StoryStep, DocEntry } from \"../types/story\";\nimport type { TestRunResult, TestCaseResult, TestStatus } from \"../types/test-result\";\nimport type { MarkdownRenderers } from \"../types/options\";\n\n/** Options for Markdown formatting */\nexport interface MarkdownOptions {\n /** Report title. Default: \"User Stories\" */\n title?: string;\n /** Include status icons on scenarios. Default: true */\n includeStatusIcons?: boolean;\n /** Include metadata table (date, version). Default: true */\n includeMetadata?: boolean;\n /** Include error details for failed scenarios. Default: true */\n includeErrors?: boolean;\n /** Scenario heading level. Default: 3 */\n scenarioHeadingLevel?: 2 | 3 | 4;\n /** Step rendering style. Default: \"bullets\" */\n stepStyle?: \"bullets\" | \"gherkin\";\n /** Group scenarios by. Default: \"file\" */\n groupBy?: \"file\" | \"suite\" | \"none\";\n /** Sort scenarios. Default: \"source\" */\n sortScenarios?: \"alpha\" | \"source\" | \"none\";\n /** Suite path separator. Default: \" - \" */\n suiteSeparator?: string;\n /** Include YAML front-matter for machine parsing. Default: false */\n includeFrontMatter?: boolean;\n /** Include summary table (counts, duration). Default: false */\n includeSummaryTable?: boolean;\n /** Base URL for source permalinks. E.g., \"https://github.com/user/repo/blob\" */\n permalinkBaseUrl?: string;\n /** URL template for ticket links. Use {ticket} as placeholder */\n ticketUrlTemplate?: string;\n /** URL template for trace links. Use {traceId} as placeholder. E.g. \"https://grafana.example.com/explore?traceId={traceId}\" */\n traceUrlTemplate?: string;\n /** Include source links when permalinkBaseUrl is set. Default: true */\n includeSourceLinks?: boolean;\n /** Custom renderers for doc entries */\n customRenderers?: MarkdownRenderers;\n}\n\n/** Resolved options with all defaults */\ntype ResolvedMarkdownOptions = {\n title: string;\n includeStatusIcons: boolean;\n includeMetadata: boolean;\n includeErrors: boolean;\n scenarioHeadingLevel: 2 | 3 | 4;\n stepStyle: \"bullets\" | \"gherkin\";\n groupBy: \"file\" | \"suite\" | \"none\";\n sortScenarios: \"alpha\" | \"source\" | \"none\";\n suiteSeparator: string;\n includeFrontMatter: boolean;\n includeSummaryTable: boolean;\n permalinkBaseUrl?: string;\n ticketUrlTemplate?: string;\n traceUrlTemplate?: string;\n includeSourceLinks: boolean;\n customRenderers?: MarkdownRenderers;\n};\n\n/**\n * Markdown Formatter.\n *\n * Transforms TestRunResult into Markdown documentation that matches\n * the output format of existing framework reporters.\n */\nexport class MarkdownFormatter {\n private options: ResolvedMarkdownOptions;\n\n constructor(options: MarkdownOptions = {}) {\n this.options = {\n title: options.title ?? \"User Stories\",\n includeStatusIcons: options.includeStatusIcons ?? true,\n includeMetadata: options.includeMetadata ?? true,\n includeErrors: options.includeErrors ?? true,\n scenarioHeadingLevel: options.scenarioHeadingLevel ?? 3,\n stepStyle: options.stepStyle ?? \"bullets\",\n groupBy: options.groupBy ?? \"file\",\n sortScenarios: options.sortScenarios ?? \"source\",\n suiteSeparator: options.suiteSeparator ?? \" - \",\n includeFrontMatter: options.includeFrontMatter ?? false,\n includeSummaryTable: options.includeSummaryTable ?? false,\n permalinkBaseUrl: options.permalinkBaseUrl,\n ticketUrlTemplate: options.ticketUrlTemplate,\n traceUrlTemplate: options.traceUrlTemplate,\n includeSourceLinks: options.includeSourceLinks ?? true,\n customRenderers: options.customRenderers,\n };\n }\n\n /**\n * Format a test run into Markdown.\n *\n * @param run - Canonical test run result\n * @returns Markdown string\n */\n format(run: TestRunResult): string {\n const lines: string[] = [];\n\n // Front-matter\n if (this.options.includeFrontMatter) {\n this.renderFrontMatter(lines, run);\n }\n\n // Title\n lines.push(`# ${this.options.title}`);\n lines.push(\"\");\n\n // Metadata\n if (this.options.includeMetadata) {\n this.renderMetadata(lines, run);\n lines.push(\"\");\n }\n\n // Summary table\n if (this.options.includeSummaryTable) {\n this.renderSummaryTable(lines, run);\n lines.push(\"\");\n }\n\n // Render scenarios based on grouping\n switch (this.options.groupBy) {\n case \"none\":\n this.renderFlatList(lines, run.testCases);\n break;\n case \"suite\":\n this.renderBySuite(lines, run.testCases);\n break;\n case \"file\":\n default:\n this.renderByFile(lines, run.testCases);\n break;\n }\n\n // Custom footer\n if (this.options.customRenderers?.renderFooter) {\n const footer = this.options.customRenderers.renderFooter(run);\n if (footer) {\n lines.push(\"\");\n lines.push(footer);\n }\n }\n\n return lines.join(\"\\n\").trimEnd();\n }\n\n /**\n * Render YAML front-matter.\n */\n private renderFrontMatter(lines: string[], run: TestRunResult): void {\n const data: Record<string, unknown> = {\n title: this.options.title,\n generatedAt: new Date(run.startedAtMs).toISOString(),\n durationMs: run.durationMs,\n scenarios: run.testCases.length,\n passed: run.testCases.filter((tc) => tc.status === \"passed\").length,\n failed: run.testCases.filter((tc) => tc.status === \"failed\").length,\n skipped: run.testCases.filter((tc) => tc.status === \"skipped\").length,\n pending: run.testCases.filter((tc) => tc.status === \"pending\").length,\n };\n\n if (run.packageVersion) data.version = run.packageVersion;\n if (run.gitSha) data.gitSha = run.gitSha.length > 7 ? run.gitSha.slice(0, 7) : run.gitSha;\n if (run.coverage) data.coverage = run.coverage;\n\n lines.push(\"---\");\n for (const [key, value] of Object.entries(data)) {\n if (value === undefined) continue;\n if (typeof value === \"object\") {\n lines.push(`${key}:`);\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n lines.push(` ${k}: ${v}`);\n }\n } else {\n lines.push(`${key}: ${value}`);\n }\n }\n lines.push(\"---\");\n lines.push(\"\");\n }\n\n /**\n * Render summary table.\n */\n private renderSummaryTable(lines: string[], run: TestRunResult): void {\n const totalScenarios = run.testCases.length;\n const totalSteps = run.testCases.reduce((acc, tc) => acc + tc.story.steps.length, 0);\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter((tc) => tc.status === \"skipped\").length;\n const pending = run.testCases.filter((tc) => tc.status === \"pending\").length;\n\n lines.push(\"| Scenarios | Steps | Passed | Failed | Skipped | Pending | Duration |\");\n lines.push(\"| ---: | ---: | ---: | ---: | ---: | ---: | ---: |\");\n lines.push(`| ${totalScenarios} | ${totalSteps} | ${passed} | ${failed} | ${skipped} | ${pending} | ${this.formatDuration(run.durationMs)} |`);\n\n // Coverage summary if available\n if (run.coverage) {\n lines.push(\"\");\n lines.push(\"| Coverage | % |\");\n lines.push(\"| --- | ---: |\");\n if (run.coverage.statementsPct !== undefined) {\n lines.push(`| Statements | ${run.coverage.statementsPct}% |`);\n }\n if (run.coverage.branchesPct !== undefined) {\n lines.push(`| Branches | ${run.coverage.branchesPct}% |`);\n }\n if (run.coverage.functionsPct !== undefined) {\n lines.push(`| Functions | ${run.coverage.functionsPct}% |`);\n }\n if (run.coverage.linesPct !== undefined) {\n lines.push(`| Lines | ${run.coverage.linesPct}% |`);\n }\n }\n }\n\n /**\n * Format duration in human-readable form.\n */\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(2)}s`;\n }\n\n /**\n * Render metadata table.\n */\n private renderMetadata(lines: string[], run: TestRunResult): void {\n const rows: Array<[string, string]> = [];\n\n const startDate = new Date(run.startedAtMs);\n rows.push([\"Date\", startDate.toISOString()]);\n\n if (run.packageVersion) {\n rows.push([\"Version\", run.packageVersion]);\n }\n\n if (run.gitSha) {\n const shortSha = run.gitSha.length > 7 ? run.gitSha.slice(0, 7) : run.gitSha;\n rows.push([\"Git SHA\", shortSha]);\n }\n\n if (rows.length > 0) {\n lines.push(\"| Key | Value |\");\n lines.push(\"| --- | --- |\");\n for (const [key, value] of rows) {\n lines.push(`| ${key} | ${value} |`);\n }\n }\n }\n\n /**\n * Render scenarios grouped by file.\n */\n private renderByFile(lines: string[], testCases: TestCaseResult[]): void {\n const byFile = groupBy(testCases, (tc) => tc.sourceFile);\n\n for (const [file, fileTestCases] of byFile) {\n lines.push(`## ${file}`);\n lines.push(\"\");\n\n // Group by suite path within file\n this.renderSuiteGroups(lines, fileTestCases, 3);\n }\n }\n\n /**\n * Render scenarios grouped by suite path.\n */\n private renderBySuite(lines: string[], testCases: TestCaseResult[]): void {\n this.renderSuiteGroups(lines, testCases, 2);\n }\n\n /**\n * Render suite groups.\n */\n private renderSuiteGroups(\n lines: string[],\n testCases: TestCaseResult[],\n baseLevel: number\n ): void {\n const bySuite = groupBy(testCases, (tc) =>\n tc.titlePath.join(this.options.suiteSeparator)\n );\n\n // Sort suite groups\n const sortedSuites = this.sortSuiteGroups([...bySuite.entries()]);\n\n for (const [suitePath, suiteTestCases] of sortedSuites) {\n if (suitePath) {\n lines.push(`${\"#\".repeat(baseLevel)} ${suitePath}`);\n lines.push(\"\");\n }\n\n const sorted = this.sortScenarios(suiteTestCases);\n for (const tc of sorted) {\n this.renderScenario(lines, tc);\n }\n }\n }\n\n /**\n * Render flat list of scenarios.\n */\n private renderFlatList(lines: string[], testCases: TestCaseResult[]): void {\n const sorted = this.sortScenarios(testCases);\n for (const tc of sorted) {\n this.renderScenario(lines, tc);\n }\n }\n\n /**\n * Render a single scenario.\n */\n private renderScenario(lines: string[], tc: TestCaseResult): void {\n // Check for custom scenario header renderer\n if (this.options.customRenderers?.renderScenarioHeader) {\n const custom = this.options.customRenderers.renderScenarioHeader(tc);\n if (custom !== null) {\n lines.push(custom);\n lines.push(\"\");\n // Still render steps and docs after custom header\n this.renderScenarioBody(lines, tc);\n return;\n }\n }\n\n const headingPrefix = \"#\".repeat(this.options.scenarioHeadingLevel);\n\n // Status icon\n let icon = \"\";\n if (this.options.includeStatusIcons) {\n icon = this.getStatusIcon(tc.status) + \" \";\n }\n\n // Scenario heading\n lines.push(`${headingPrefix} ${icon}${tc.story.scenario}`);\n\n // Source link\n if (this.options.includeSourceLinks && this.options.permalinkBaseUrl && tc.sourceFile !== \"unknown\") {\n const permalink = this.buildPermalink(tc);\n lines.push(`Source: [${tc.sourceFile}](${permalink})`);\n }\n\n // Tags and tickets\n const meta: string[] = [];\n if (tc.tags.length > 0) {\n meta.push(`Tags: ${tc.tags.map((t) => `\\`${t}\\``).join(\", \")}`);\n }\n if (tc.story.tickets && tc.story.tickets.length > 0) {\n const ticketTemplate = this.options.ticketUrlTemplate;\n const ticketLinks = tc.story.tickets.map((t) => {\n if (t.url) {\n return `[${t.id}](${t.url})`;\n }\n if (ticketTemplate) {\n return `[${t.id}](${ticketTemplate.replace(\"{ticket}\", t.id)})`;\n }\n return `\\`${t.id}\\``;\n });\n meta.push(`Tickets: ${ticketLinks.join(\", \")}`);\n }\n // Trace context (injected by OTel bridge in story.init())\n const otelMeta = (tc.story.meta as Record<string, unknown> | undefined)\n ?.otel as { traceId?: string } | undefined;\n if (otelMeta?.traceId) {\n const traceTemplate = this.options.traceUrlTemplate;\n if (traceTemplate) {\n const url = traceTemplate.replace(/\\{traceId\\}/g, otelMeta.traceId);\n meta.push(\n `Trace: [${otelMeta.traceId.slice(0, 16)}…](${url})`,\n );\n } else {\n meta.push(`Trace: \\`${otelMeta.traceId}\\``);\n }\n }\n\n if (meta.length > 0) {\n lines.push(meta.join(\" | \"));\n }\n\n lines.push(\"\");\n\n this.renderScenarioBody(lines, tc);\n }\n\n /**\n * Render scenario body (docs, steps, errors).\n */\n private renderScenarioBody(lines: string[], tc: TestCaseResult): void {\n // Story-level docs\n if (tc.story.docs && tc.story.docs.length > 0) {\n for (const doc of tc.story.docs) {\n this.renderDocEntry(lines, doc);\n }\n }\n\n // Steps\n for (const step of tc.story.steps) {\n this.renderStep(lines, step);\n }\n\n // Error\n if (tc.status === \"failed\" && tc.errorMessage && this.options.includeErrors) {\n lines.push(\"**Failure**\");\n lines.push(\"\");\n lines.push(\"```text\");\n lines.push(tc.errorMessage);\n if (tc.errorStack) {\n lines.push(\"\");\n lines.push(tc.errorStack);\n }\n lines.push(\"```\");\n lines.push(\"\");\n }\n\n lines.push(\"\");\n }\n\n /**\n * Build permalink URL for a test case.\n */\n private buildPermalink(tc: TestCaseResult): string {\n const base = this.options.permalinkBaseUrl!.replace(/\\/$/, \"\");\n const file = tc.sourceFile;\n const line = tc.sourceLine > 0 ? `#L${tc.sourceLine}` : \"\";\n return `${base}/${file}${line}`;\n }\n\n /**\n * Render a step.\n */\n private renderStep(lines: string[], step: StoryStep): void {\n // Check for custom step renderer\n if (this.options.customRenderers?.renderStep) {\n const custom = this.options.customRenderers.renderStep(step);\n if (custom !== null) {\n lines.push(custom);\n // Still render step docs\n if (step.docs && step.docs.length > 0) {\n const indent = this.options.stepStyle === \"gherkin\" ? \"\" : \" \";\n for (const doc of step.docs) {\n this.renderDocEntry(lines, doc, indent);\n }\n }\n return;\n }\n }\n\n // Mode indicator\n let modeIndicator = \"\";\n if (step.mode === \"skip\") {\n modeIndicator = \" _(skipped)_\";\n } else if (step.mode === \"todo\") {\n modeIndicator = \" _(todo)_\";\n } else if (step.mode === \"fails\") {\n modeIndicator = \" _(expected to fail)_\";\n }\n\n if (this.options.stepStyle === \"gherkin\") {\n lines.push(`**${step.keyword}** ${step.text}${modeIndicator}`);\n } else {\n lines.push(`- **${step.keyword}** ${step.text}${modeIndicator}`);\n }\n\n // Render step docs\n if (step.docs && step.docs.length > 0) {\n const indent = this.options.stepStyle === \"gherkin\" ? \"\" : \" \";\n for (const doc of step.docs) {\n this.renderDocEntry(lines, doc, indent);\n }\n }\n }\n\n /**\n * Render a documentation entry.\n */\n private renderDocEntry(lines: string[], entry: DocEntry, indent = \"\"): void {\n // Check for custom doc entry renderer\n if (this.options.customRenderers?.renderDocEntry) {\n const custom = this.options.customRenderers.renderDocEntry(entry);\n if (custom !== null) {\n lines.push(`${indent}${custom}`);\n return;\n }\n }\n\n switch (entry.kind) {\n case \"note\":\n lines.push(`${indent}> ${entry.text}`);\n break;\n\n case \"tag\":\n lines.push(`${indent}${entry.names.map((n) => `\\`${n}\\``).join(\" \")}`);\n break;\n\n case \"kv\": {\n const val = typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value);\n lines.push(`${indent}- **${entry.label}:** ${val}`);\n break;\n }\n\n case \"code\":\n if (entry.label) {\n lines.push(`${indent}**${entry.label}**`);\n lines.push(`${indent}`);\n }\n lines.push(`${indent}\\`\\`\\`${entry.lang ?? \"\"}`);\n for (const line of (entry.content ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n lines.push(`${indent}`);\n break;\n\n case \"table\":\n if (entry.label) {\n lines.push(`${indent}**${entry.label}**`);\n lines.push(`${indent}`);\n }\n lines.push(`${indent}| ${entry.columns.join(\" | \")} |`);\n lines.push(`${indent}| ${entry.columns.map(() => \"---\").join(\" | \")} |`);\n for (const row of entry.rows) {\n lines.push(`${indent}| ${row.join(\" | \")} |`);\n }\n lines.push(`${indent}`);\n break;\n\n case \"link\":\n lines.push(`${indent}[${entry.label}](${entry.url})`);\n break;\n\n case \"section\":\n lines.push(`${indent}**${entry.title}**`);\n lines.push(`${indent}`);\n for (const line of (entry.markdown ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}`);\n break;\n\n case \"mermaid\":\n if (entry.title) {\n lines.push(`${indent}**${entry.title}**`);\n }\n lines.push(`${indent}\\`\\`\\`mermaid`);\n for (const line of (entry.code ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n break;\n\n case \"screenshot\":\n lines.push(`${indent}![${entry.alt ?? \"Screenshot\"}](${entry.path})`);\n break;\n\n case \"custom\":\n lines.push(`${indent}**[${entry.type}]**`);\n lines.push(`${indent}`);\n lines.push(`${indent}\\`\\`\\`json`);\n for (const line of JSON.stringify(entry.data ?? null, null, 2).split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n lines.push(`${indent}`);\n break;\n }\n\n // Render children with increased indentation\n if (entry.children && entry.children.length > 0) {\n const childIndent = indent + \" \";\n for (const child of entry.children) {\n this.renderDocEntry(lines, child, childIndent);\n }\n }\n }\n\n /**\n * Get status icon for a status.\n */\n private getStatusIcon(status: TestStatus): string {\n switch (status) {\n case \"passed\":\n return \"✅\";\n case \"failed\":\n return \"❌\";\n case \"skipped\":\n return \"⏩\";\n case \"pending\":\n return \"📝\";\n default:\n return \"⚠️\";\n }\n }\n\n /**\n * Sort scenarios based on options.\n */\n private sortScenarios(testCases: TestCaseResult[]): TestCaseResult[] {\n if (this.options.sortScenarios === \"alpha\") {\n return [...testCases].sort((a, b) =>\n a.story.scenario.localeCompare(b.story.scenario)\n );\n }\n if (this.options.sortScenarios === \"source\") {\n return [...testCases].sort(\n (a, b) => (a.story.sourceOrder ?? 0) - (b.story.sourceOrder ?? 0)\n );\n }\n return testCases;\n }\n\n /**\n * Sort suite groups.\n */\n private sortSuiteGroups(\n entries: [string, TestCaseResult[]][]\n ): [string, TestCaseResult[]][] {\n if (this.options.sortScenarios === \"alpha\") {\n return entries.sort(([a], [b]) => a.localeCompare(b));\n }\n if (this.options.sortScenarios === \"source\") {\n return entries.sort(([, a], [, b]) => {\n const minA = Math.min(...a.map((s) => s.story.sourceOrder ?? Infinity));\n const minB = Math.min(...b.map((s) => s.story.sourceOrder ?? Infinity));\n return minA - minB;\n });\n }\n return entries;\n }\n}\n\n/**\n * Group array items by a key function.\n */\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n","/**\n * Synthesize .feature file text and a line map from TestCaseResult[].\n *\n * Used to produce Source and GherkinDocument messages when real .feature\n * files don't exist (all test cases come from e.g. Jest/Vitest/Playwright).\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\n\n/** Map from scenario name → { scenarioLine, stepLines: Map<stepIndex, line> } */\nexport interface LineMap {\n featureLine: number;\n scenarios: Map<\n string,\n { scenarioLine: number; stepLines: Map<number, number> }\n >;\n /** Tag lines for each scenario, keyed by scenario name */\n scenarioTagLines: Map<string, number>;\n /** Feature-level tag line (if any) */\n featureTagLine?: number;\n}\n\nexport interface SynthesizedFeature {\n /** The synthesized .feature text */\n text: string;\n /** Line map for building the GherkinDocument AST */\n lineMap: LineMap;\n /** Extracted feature name */\n featureName: string;\n /** Feature-level tags (union of all scenario tags) */\n featureTags: string[];\n}\n\n/**\n * Extract a feature name from grouped test cases.\n *\n * Uses the first element of the first titlePath, or derives from the URI.\n */\nexport function extractFeatureName(\n testCases: TestCaseResult[],\n uri: string\n): string {\n for (const tc of testCases) {\n if (tc.titlePath.length > 0) {\n return tc.titlePath[0];\n }\n }\n // Fallback: derive from filename\n const basename = uri.replace(/^.*[\\\\/]/, \"\").replace(/\\.[^.]+$/, \"\");\n return basename\n .replace(/[-_]+/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Synthesize a .feature file from a group of test cases belonging to the same source file.\n */\nexport function synthesizeFeature(\n uri: string,\n testCases: TestCaseResult[]\n): SynthesizedFeature {\n const featureName = extractFeatureName(testCases, uri);\n\n // Collect feature-level tags (union of all scenario tags)\n const featureTagSet = new Set<string>();\n for (const tc of testCases) {\n for (const tag of tc.tags) {\n featureTagSet.add(tag);\n }\n }\n const featureTags = [...featureTagSet].sort();\n\n const lines: string[] = [];\n const lineMap: LineMap = {\n featureLine: 0,\n scenarios: new Map(),\n scenarioTagLines: new Map(),\n };\n\n let currentLine = 1;\n\n // Feature-level tags\n if (featureTags.length > 0) {\n lines.push(featureTags.map((t) => `@${t}`).join(\" \"));\n lineMap.featureTagLine = currentLine;\n currentLine++;\n }\n\n // Feature declaration\n lines.push(`Feature: ${featureName}`);\n lineMap.featureLine = currentLine;\n currentLine++;\n\n // Blank line after feature\n lines.push(\"\");\n currentLine++;\n\n for (const tc of testCases) {\n const scenario = tc.story.scenario;\n\n // Scenario tags\n if (tc.tags.length > 0) {\n lines.push(` ${tc.tags.map((t) => `@${t}`).join(\" \")}`);\n lineMap.scenarioTagLines.set(scenario, currentLine);\n currentLine++;\n }\n\n // Scenario declaration\n const scenarioLine = currentLine;\n lines.push(` Scenario: ${scenario}`);\n currentLine++;\n\n // Steps\n const stepLines = new Map<number, number>();\n for (let i = 0; i < tc.story.steps.length; i++) {\n const step = tc.story.steps[i];\n stepLines.set(i, currentLine);\n lines.push(` ${step.keyword} ${step.text}`);\n currentLine++;\n }\n\n lineMap.scenarios.set(scenario, { scenarioLine, stepLines });\n\n // Blank line between scenarios\n lines.push(\"\");\n currentLine++;\n }\n\n return {\n text: lines.join(\"\\n\"),\n lineMap,\n featureName,\n featureTags,\n };\n}\n","/**\n * Utility functions for Cucumber Messages NDJSON format.\n */\n\nimport { createHash } from \"node:crypto\";\nimport type {\n Timestamp,\n Duration,\n KeywordType,\n PickleStepType,\n TestStepResultStatus,\n} from \"../types/cucumber-messages\";\nimport type { TestStatus } from \"../types/test-result\";\nimport type { StepKeyword } from \"../types/story\";\n\n/**\n * Convert epoch milliseconds to protobuf Timestamp.\n */\nexport function msToTimestamp(ms: number): Timestamp {\n const seconds = Math.floor(ms / 1000);\n const nanos = Math.round((ms % 1000) * 1_000_000);\n return { seconds, nanos };\n}\n\n/**\n * Convert milliseconds to protobuf Duration.\n */\nexport function msToDuration(ms: number): Duration {\n const seconds = Math.floor(ms / 1000);\n const nanos = Math.round((ms % 1000) * 1_000_000);\n return { seconds, nanos };\n}\n\n/**\n * Map a StepKeyword to a Cucumber KeywordType.\n *\n * And/But/* inherit from the previous non-conjunction keyword type.\n */\nexport function keywordToKeywordType(keyword: StepKeyword): KeywordType {\n switch (keyword) {\n case \"Given\":\n return \"Context\";\n case \"When\":\n return \"Action\";\n case \"Then\":\n return \"Outcome\";\n case \"And\":\n case \"But\":\n return \"Conjunction\";\n default:\n return \"Unknown\";\n }\n}\n\n/**\n * Resolve the effective PickleStepType for a sequence of steps,\n * inheriting the previous non-conjunction type for And/But.\n *\n * Returns an array of resolved types, one per step.\n */\nexport function resolvePickleStepTypes(\n keywords: StepKeyword[]\n): PickleStepType[] {\n let lastNonConjunction: PickleStepType = \"Unknown\";\n return keywords.map((kw) => {\n const kt = keywordToKeywordType(kw);\n if (kt === \"Conjunction\") {\n return lastNonConjunction;\n }\n const resolved = keywordTypeToPickleStepType(kt);\n lastNonConjunction = resolved;\n return resolved;\n });\n}\n\n/**\n * Convert a non-conjunction KeywordType to PickleStepType.\n */\nfunction keywordTypeToPickleStepType(kt: KeywordType): PickleStepType {\n switch (kt) {\n case \"Context\":\n return \"Context\";\n case \"Action\":\n return \"Action\";\n case \"Outcome\":\n return \"Outcome\";\n default:\n return \"Unknown\";\n }\n}\n\n/**\n * Map TestStatus to Cucumber TestStepResultStatus.\n */\nexport function statusToCucumberStatus(\n status: TestStatus\n): TestStepResultStatus {\n switch (status) {\n case \"passed\":\n return \"PASSED\";\n case \"failed\":\n return \"FAILED\";\n case \"skipped\":\n return \"SKIPPED\";\n case \"pending\":\n return \"PENDING\";\n default:\n return \"UNKNOWN\";\n }\n}\n\n/**\n * Generate a deterministic ID using SHA-1.\n *\n * @param kind - Namespace to prevent collisions between entity types (e.g., \"pickle\", \"testCase\")\n * @param salt - Optional salt from options (idSalt)\n * @param parts - Strings to hash\n * @returns 36-character hex string (UUID-length without dashes)\n */\nexport function deterministicId(\n kind: string,\n salt: string,\n ...parts: string[]\n): string {\n const input = [salt, kind, ...parts].join(\"::\");\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 36);\n}\n","/**\n * Build a GherkinDocument envelope from synthesized feature data.\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\nimport type { DocEntry, StepKeyword, StoryStep } from \"../../types/story\";\nimport type {\n GherkinDocument,\n Feature,\n FeatureChild,\n Scenario,\n Step,\n Tag,\n Envelope,\n DocString,\n DataTable,\n TableRow,\n TableCell,\n} from \"../../types/cucumber-messages\";\nimport type { SynthesizedFeature } from \"./synthesize-feature\";\nimport { deterministicId, keywordToKeywordType } from \"../../utils/cucumber-messages\";\n\n/**\n * Build GherkinDocument + Source envelopes for a group of test cases from one file.\n */\nexport function buildGherkinDocumentEnvelopes(\n uri: string,\n testCases: TestCaseResult[],\n synthesized: SynthesizedFeature,\n salt: string\n): { sourceEnvelope: Envelope; gherkinDocumentEnvelope: Envelope } {\n const { lineMap, featureName, featureTags, text } = synthesized;\n\n // Build feature-level tags\n const featureTagNodes: Tag[] = featureTags.map((tag, i) => ({\n location: {\n line: lineMap.featureTagLine ?? 1,\n column: undefined,\n },\n name: `@${tag}`,\n id: deterministicId(\"featureTag\", salt, uri, tag),\n }));\n\n // Build children (scenarios)\n const children: FeatureChild[] = [];\n\n for (const tc of testCases) {\n const scenarioName = tc.story.scenario;\n const scenarioInfo = lineMap.scenarios.get(scenarioName);\n if (!scenarioInfo) continue;\n\n const scenarioId = deterministicId(\"scenario\", salt, uri, scenarioName);\n\n // Build scenario tags\n const scenarioTags: Tag[] = tc.tags.map((tag) => ({\n location: {\n line: lineMap.scenarioTagLines.get(scenarioName) ?? scenarioInfo.scenarioLine,\n },\n name: `@${tag}`,\n id: deterministicId(\"scenarioTag\", salt, uri, scenarioName, tag),\n }));\n\n // Build steps with keyword type tracking for And/But inheritance\n let lastNonConjunctionType: \"Context\" | \"Action\" | \"Outcome\" = \"Context\";\n const steps: Step[] = tc.story.steps.map((step, i) => {\n const keyword = step.keyword as StepKeyword;\n let kwType = keywordToKeywordType(keyword);\n\n if (kwType === \"Conjunction\") {\n kwType = lastNonConjunctionType;\n } else if (kwType === \"Context\" || kwType === \"Action\" || kwType === \"Outcome\") {\n lastNonConjunctionType = kwType;\n }\n\n const stepLine = scenarioInfo.stepLines.get(i) ?? 0;\n const astStep: Step = {\n location: { line: stepLine },\n keyword: `${keyword} `,\n keywordType: keywordToKeywordType(keyword),\n text: step.text,\n id: deterministicId(\"astStep\", salt, uri, scenarioName, String(i)),\n };\n\n // Convert doc entries to DocString/DataTable\n const { docString, dataTable } = buildStepArguments(step, stepLine);\n if (docString) astStep.docString = docString;\n if (dataTable) astStep.dataTable = dataTable;\n\n return astStep;\n });\n\n const scenario: Scenario = {\n location: { line: scenarioInfo.scenarioLine },\n tags: scenarioTags,\n keyword: \"Scenario\",\n name: scenarioName,\n description: \"\",\n steps,\n id: scenarioId,\n };\n\n children.push({ scenario });\n }\n\n const feature: Feature = {\n location: { line: lineMap.featureLine },\n tags: featureTagNodes,\n language: \"en\",\n keyword: \"Feature\",\n name: featureName,\n description: \"\",\n children,\n };\n\n const gherkinDocument: GherkinDocument = { uri, feature };\n\n return {\n sourceEnvelope: {\n source: {\n uri,\n data: text,\n mediaType: \"text/x.cucumber.gherkin+plain\",\n },\n },\n gherkinDocumentEnvelope: { gherkinDocument },\n };\n}\n\n/**\n * Convert step doc entries to DocString or DataTable for the GherkinDocument AST.\n *\n * Priority: first \"table\" doc → DataTable, else first \"code\"/\"note\"/\"section\"/\"mermaid\" → DocString.\n * Screenshots are handled as attachments, not arguments.\n */\nfunction buildStepArguments(\n step: StoryStep,\n stepLine: number\n): { docString?: DocString; dataTable?: DataTable } {\n if (!step.docs || step.docs.length === 0) return {};\n\n // Look for table first (takes priority)\n const tableDocs = step.docs.filter((d): d is Extract<DocEntry, { kind: \"table\" }> => d.kind === \"table\");\n if (tableDocs.length > 0) {\n const table = tableDocs[0];\n return { dataTable: buildDataTable(table, stepLine + 1) };\n }\n\n // Look for doc-string-like entries\n for (const doc of step.docs) {\n const ds = docEntryToDocString(doc, stepLine + 1);\n if (ds) return { docString: ds };\n }\n\n return {};\n}\n\nfunction docEntryToDocString(doc: DocEntry, line: number): DocString | undefined {\n switch (doc.kind) {\n case \"code\":\n return {\n location: { line },\n mediaType: doc.lang,\n content: doc.content,\n delimiter: '\"\"\"',\n };\n case \"note\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: doc.text,\n delimiter: '\"\"\"',\n };\n case \"section\":\n return {\n location: { line },\n mediaType: \"text/markdown\",\n content: doc.markdown,\n delimiter: '\"\"\"',\n };\n case \"mermaid\":\n return {\n location: { line },\n mediaType: \"text/x-mermaid\",\n content: doc.code,\n delimiter: '\"\"\"',\n };\n case \"kv\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: `${doc.label}: ${typeof doc.value === \"string\" ? doc.value : JSON.stringify(doc.value)}`,\n delimiter: '\"\"\"',\n };\n case \"link\":\n return {\n location: { line },\n mediaType: \"text/markdown\",\n content: `[${doc.label}](${doc.url})`,\n delimiter: '\"\"\"',\n };\n case \"custom\":\n return {\n location: { line },\n mediaType: \"application/json\",\n content: JSON.stringify(doc.data, null, 2),\n delimiter: '\"\"\"',\n };\n case \"tag\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: doc.names.map((n) => `@${n}`).join(\" \"),\n delimiter: '\"\"\"',\n };\n // screenshot and other kinds are not converted to doc strings\n default:\n return undefined;\n }\n}\n\nfunction buildDataTable(\n table: Extract<DocEntry, { kind: \"table\" }>,\n line: number\n): DataTable {\n const rows: TableRow[] = [];\n\n // Header row\n rows.push({\n location: { line },\n cells: table.columns.map((col) => ({\n location: { line },\n value: col,\n })),\n id: \"\",\n });\n\n // Data rows\n for (let r = 0; r < table.rows.length; r++) {\n const rowLine = line + 1 + r;\n rows.push({\n location: { line: rowLine },\n cells: table.rows[r].map((cell) => ({\n location: { line: rowLine },\n value: cell,\n })),\n id: \"\",\n });\n }\n\n return {\n location: { line },\n rows,\n };\n}\n","/**\n * Build Pickle envelopes from test cases and GherkinDocument data.\n *\n * Each TestCaseResult becomes one Pickle (the compiled, runnable scenario).\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\nimport type { DocEntry, StepKeyword, StoryStep } from \"../../types/story\";\nimport type {\n Pickle,\n PickleStep,\n PickleTag,\n PickleStepArgument,\n PickleDocString,\n PickleTable,\n PickleTableRow,\n Envelope,\n} from \"../../types/cucumber-messages\";\nimport {\n deterministicId,\n resolvePickleStepTypes,\n} from \"../../utils/cucumber-messages\";\n\n/**\n * Build Pickle envelopes for a group of test cases from one file.\n */\nexport function buildPickleEnvelopes(\n uri: string,\n testCases: TestCaseResult[],\n salt: string\n): Envelope[] {\n const envelopes: Envelope[] = [];\n\n for (const tc of testCases) {\n const scenarioName = tc.story.scenario;\n const pickleId = deterministicId(\"pickle\", salt, uri, scenarioName);\n const scenarioAstId = deterministicId(\"scenario\", salt, uri, scenarioName);\n\n // Resolve step types with And/But inheritance\n const keywords = tc.story.steps.map((s) => s.keyword as StepKeyword);\n const resolvedTypes = resolvePickleStepTypes(keywords);\n\n const pickleSteps: PickleStep[] = tc.story.steps.map((step, i) => {\n const ps: PickleStep = {\n astNodeIds: [\n deterministicId(\"astStep\", salt, uri, scenarioName, String(i)),\n ],\n id: deterministicId(\"pickleStep\", salt, uri, scenarioName, String(i)),\n type: resolvedTypes[i],\n text: step.text,\n };\n\n const argument = buildPickleStepArgument(step);\n if (argument) ps.argument = argument;\n\n return ps;\n });\n\n const pickleTags: PickleTag[] = tc.tags.map((tag) => ({\n name: `@${tag}`,\n astNodeId: deterministicId(\"scenarioTag\", salt, uri, scenarioName, tag),\n }));\n\n const pickle: Pickle = {\n id: pickleId,\n uri,\n name: scenarioName,\n language: \"en\",\n steps: pickleSteps,\n tags: pickleTags,\n astNodeIds: [scenarioAstId],\n };\n\n envelopes.push({ pickle });\n }\n\n return envelopes;\n}\n\n/**\n * Build a PickleStepArgument from step docs.\n * Priority: first table → PickleTable, else first doc-string-like → PickleDocString.\n */\nfunction buildPickleStepArgument(step: StoryStep): PickleStepArgument | undefined {\n if (!step.docs || step.docs.length === 0) return undefined;\n\n // Table takes priority\n const tableDocs = step.docs.filter(\n (d): d is Extract<DocEntry, { kind: \"table\" }> => d.kind === \"table\"\n );\n if (tableDocs.length > 0) {\n return { dataTable: buildPickleTable(tableDocs[0]) };\n }\n\n // Doc-string-like entries\n for (const doc of step.docs) {\n const ds = docEntryToPickleDocString(doc);\n if (ds) return { docString: ds };\n }\n\n return undefined;\n}\n\nfunction docEntryToPickleDocString(doc: DocEntry): PickleDocString | undefined {\n switch (doc.kind) {\n case \"code\":\n return { mediaType: doc.lang, content: doc.content };\n case \"note\":\n return { mediaType: \"text/plain\", content: doc.text };\n case \"section\":\n return { mediaType: \"text/markdown\", content: doc.markdown };\n case \"mermaid\":\n return { mediaType: \"text/x-mermaid\", content: doc.code };\n case \"kv\":\n return {\n mediaType: \"text/plain\",\n content: `${doc.label}: ${typeof doc.value === \"string\" ? doc.value : JSON.stringify(doc.value)}`,\n };\n case \"link\":\n return { mediaType: \"text/markdown\", content: `[${doc.label}](${doc.url})` };\n case \"custom\":\n return { mediaType: \"application/json\", content: JSON.stringify(doc.data, null, 2) };\n case \"tag\":\n return { mediaType: \"text/plain\", content: doc.names.map((n) => `@${n}`).join(\" \") };\n default:\n return undefined;\n }\n}\n\nfunction buildPickleTable(\n table: Extract<DocEntry, { kind: \"table\" }>\n): PickleTable {\n const rows: PickleTableRow[] = [];\n\n // Header row\n rows.push({\n cells: table.columns.map((col) => ({ value: col })),\n });\n\n // Data rows\n for (const row of table.rows) {\n rows.push({\n cells: row.map((cell) => ({ value: cell })),\n });\n }\n\n return { rows };\n}\n","/**\n * Build execution envelopes: TestCase, TestCaseStarted/Finished,\n * TestStepStarted/Finished, and Attachment messages.\n *\n * Attachment placement:\n * - Step-level doc screenshots → Attachment tied to that specific step\n * - Test-case level attachments → Attachment tied to failed step or last step\n * - All Attachment envelopes appear after the relevant TestStepFinished\n */\n\nimport type { TestCaseResult, TestRunResult } from \"../../types/test-result\";\nimport type { StoryStep } from \"../../types/story\";\nimport type {\n Envelope,\n TestCase,\n TestStep,\n TestCaseStarted,\n TestStepStarted,\n TestStepFinished,\n TestCaseFinished,\n TestRunStarted,\n TestRunFinished,\n CucumberAttachment,\n AttachmentContentEncoding,\n} from \"../../types/cucumber-messages\";\nimport {\n deterministicId,\n msToTimestamp,\n msToDuration,\n statusToCucumberStatus,\n} from \"../../utils/cucumber-messages\";\n\n/**\n * Build TestRunStarted envelope.\n */\nexport function buildTestRunStarted(run: TestRunResult): Envelope {\n return {\n testRunStarted: {\n timestamp: msToTimestamp(run.startedAtMs),\n },\n };\n}\n\n/**\n * Build TestRunFinished envelope.\n */\nexport function buildTestRunFinished(run: TestRunResult): Envelope {\n const allPassed = run.testCases.every((tc) => tc.status === \"passed\");\n return {\n testRunFinished: {\n timestamp: msToTimestamp(run.finishedAtMs),\n success: allPassed,\n },\n };\n}\n\n/**\n * Build all execution envelopes for a single test case:\n * TestCase, then for each attempt:\n * TestCaseStarted, [TestStepStarted + TestStepFinished + Attachment*]*, TestCaseFinished\n *\n * When tc.attempts exists with multiple entries, prior attempts are emitted\n * with willBeRetried=true, and the final attempt with willBeRetried=false.\n */\nexport function buildTestCaseExecutionEnvelopes(\n uri: string,\n tc: TestCaseResult,\n salt: string\n): Envelope[] {\n const envelopes: Envelope[] = [];\n const scenarioName = tc.story.scenario;\n\n const pickleId = deterministicId(\"pickle\", salt, uri, scenarioName);\n const testCaseId = deterministicId(\"testCase\", salt, uri, scenarioName);\n\n // Build test steps (shared across all attempts — the TestCase is the same)\n const testSteps: TestStep[] = tc.story.steps.map((_step, i) => ({\n id: deterministicId(\"testStep\", salt, uri, scenarioName, String(i)),\n pickleStepId: deterministicId(\n \"pickleStep\",\n salt,\n uri,\n scenarioName,\n String(i)\n ),\n stepDefinitionIds: [],\n }));\n\n // TestCase envelope (emitted once, before all attempts)\n const testCase: TestCase = {\n id: testCaseId,\n pickleId,\n testSteps,\n };\n envelopes.push({ testCase });\n\n // If there are explicit attempts, emit each one\n if (tc.attempts && tc.attempts.length > 1) {\n for (let a = 0; a < tc.attempts.length; a++) {\n const attempt = tc.attempts[a];\n const isLastAttempt = a === tc.attempts.length - 1;\n\n const attemptEnvelopes = buildAttemptEnvelopes({\n testCaseId,\n testSteps,\n tc,\n uri,\n scenarioName,\n salt,\n attemptNumber: attempt.attempt,\n attemptStatus: attempt.status,\n attemptDurationMs: attempt.durationMs,\n attemptErrorMessage: attempt.errorMessage,\n willBeRetried: !isLastAttempt,\n // Only emit attachments and doc screenshots on the final attempt\n emitAttachments: isLastAttempt,\n });\n envelopes.push(...attemptEnvelopes);\n }\n } else {\n // Single attempt (normal case)\n const attemptEnvelopes = buildAttemptEnvelopes({\n testCaseId,\n testSteps,\n tc,\n uri,\n scenarioName,\n salt,\n attemptNumber: tc.retry,\n attemptStatus: undefined, // Use tc.stepResults\n attemptDurationMs: undefined,\n attemptErrorMessage: undefined,\n willBeRetried: false,\n emitAttachments: true,\n });\n envelopes.push(...attemptEnvelopes);\n }\n\n return envelopes;\n}\n\ninterface AttemptParams {\n testCaseId: string;\n testSteps: TestStep[];\n tc: TestCaseResult;\n uri: string;\n scenarioName: string;\n salt: string;\n attemptNumber: number;\n attemptStatus: import(\"../../types/test-result.js\").TestStatus | undefined;\n attemptDurationMs: number | undefined;\n attemptErrorMessage: string | undefined;\n willBeRetried: boolean;\n emitAttachments: boolean;\n}\n\n/**\n * Build envelopes for a single attempt:\n * TestCaseStarted, [TestStepStarted, TestStepFinished, Attachment*]*, TestCaseFinished\n */\nfunction buildAttemptEnvelopes(params: AttemptParams): Envelope[] {\n const {\n testCaseId, testSteps, tc, uri, scenarioName, salt,\n attemptNumber, attemptStatus, attemptDurationMs, attemptErrorMessage,\n willBeRetried, emitAttachments,\n } = params;\n\n const envelopes: Envelope[] = [];\n const testCaseStartedId = deterministicId(\n \"testCaseStarted\",\n salt,\n uri,\n scenarioName,\n String(attemptNumber)\n );\n\n // TestCaseStarted\n envelopes.push({\n testCaseStarted: {\n id: testCaseStartedId,\n testCaseId,\n timestamp: msToTimestamp(0),\n attempt: attemptNumber,\n },\n });\n\n // Determine which step receives test-case-level attachments\n const tcAttachmentStepIndex = emitAttachments\n ? findTestCaseAttachmentStepIndex(tc)\n : -1;\n\n // Step execution envelopes\n let cumulativeMs = 0;\n\n for (let i = 0; i < testSteps.length; i++) {\n const testStep = testSteps[i];\n const storyStep = tc.story.steps[i];\n\n // Determine step status for this attempt\n let stepStatus: import(\"../../types/test-result.js\").TestStatus;\n let stepDurationMs: number;\n let stepErrorMessage: string | undefined;\n\n if (attemptStatus !== undefined) {\n // For prior attempts, derive step statuses from attempt-level status\n // (we don't have per-step data for prior attempts)\n stepStatus = attemptStatus === \"failed\" && i === tc.story.steps.length - 1\n ? \"failed\"\n : attemptStatus === \"failed\" && i < tc.story.steps.length - 1\n ? \"passed\"\n : attemptStatus;\n stepDurationMs = attemptDurationMs !== undefined\n ? attemptDurationMs / tc.story.steps.length\n : 0;\n stepErrorMessage = stepStatus === \"failed\" ? attemptErrorMessage : undefined;\n } else {\n // Final/only attempt: use actual step results\n const stepResult = tc.stepResults[i];\n stepStatus = stepResult?.status ?? \"passed\";\n stepDurationMs = stepResult?.durationMs ?? 0;\n // Include stack trace from test case if this is the failed step\n stepErrorMessage = stepResult?.errorMessage;\n if (stepStatus === \"failed\" && tc.errorStack && stepErrorMessage) {\n stepErrorMessage = stepErrorMessage + \"\\n\" + tc.errorStack;\n } else if (stepStatus === \"failed\" && tc.errorStack && !stepErrorMessage) {\n stepErrorMessage = tc.errorStack;\n }\n }\n\n // TestStepStarted\n envelopes.push({\n testStepStarted: {\n testCaseStartedId,\n testStepId: testStep.id,\n timestamp: msToTimestamp(cumulativeMs),\n },\n });\n\n // TestStepFinished\n cumulativeMs += stepDurationMs;\n envelopes.push({\n testStepFinished: {\n testCaseStartedId,\n testStepId: testStep.id,\n testStepResult: {\n duration: msToDuration(stepDurationMs),\n status: statusToCucumberStatus(stepStatus),\n message: stepErrorMessage,\n },\n timestamp: msToTimestamp(cumulativeMs),\n },\n });\n\n // Per-step doc screenshots (only on final attempt)\n if (emitAttachments) {\n const docAttachments = extractDocAttachments(storyStep);\n for (const att of docAttachments) {\n envelopes.push({\n attachment: {\n testCaseStartedId,\n testStepId: testStep.id,\n body: att.body,\n mediaType: att.mediaType,\n contentEncoding: att.contentEncoding,\n },\n });\n }\n }\n\n // Test-case level attachments (only on final attempt, on failed/last step)\n if (i === tcAttachmentStepIndex) {\n for (const att of tc.attachments) {\n envelopes.push({\n attachment: {\n testCaseStartedId,\n testStepId: testStep.id,\n body: att.body,\n mediaType: att.mediaType,\n contentEncoding: att.contentEncoding,\n },\n });\n }\n }\n }\n\n // TestCaseFinished\n envelopes.push({\n testCaseFinished: {\n testCaseStartedId,\n timestamp: msToTimestamp(cumulativeMs),\n willBeRetried,\n },\n });\n\n return envelopes;\n}\n\n/**\n * Extract attachments from a step's doc entries.\n *\n * Handles:\n * - screenshot docs with data: URIs → BASE64 attachment\n * - screenshot docs with other paths → IDENTITY attachment\n */\nfunction extractDocAttachments(\n step: StoryStep\n): Array<{ body: string; mediaType: string; contentEncoding: AttachmentContentEncoding }> {\n if (!step.docs) return [];\n\n const attachments: Array<{\n body: string;\n mediaType: string;\n contentEncoding: AttachmentContentEncoding;\n }> = [];\n\n for (const doc of step.docs) {\n if (doc.kind !== \"screenshot\") continue;\n\n // Parse data URI: data:image/png;base64,ABC123...\n const match = doc.path.match(/^data:([^;]+);base64,(.+)$/);\n if (match) {\n attachments.push({\n body: match[2],\n mediaType: match[1],\n contentEncoding: \"BASE64\",\n });\n } else {\n // Non-data-URI screenshot (file path or URL) → IDENTITY\n attachments.push({\n body: doc.path,\n mediaType: guessMediaType(doc.path),\n contentEncoding: \"IDENTITY\",\n });\n }\n }\n\n return attachments;\n}\n\n/**\n * Guess media type from a file path or URL.\n */\nfunction guessMediaType(path: string): string {\n const lower = path.toLowerCase();\n if (lower.endsWith(\".png\")) return \"image/png\";\n if (lower.endsWith(\".jpg\") || lower.endsWith(\".jpeg\")) return \"image/jpeg\";\n if (lower.endsWith(\".gif\")) return \"image/gif\";\n if (lower.endsWith(\".webp\")) return \"image/webp\";\n if (lower.endsWith(\".svg\")) return \"image/svg+xml\";\n return \"image/png\"; // Default assumption for screenshots\n}\n\n/**\n * Determine which step index should receive test-case-level attachments.\n * Attach to the failed step if one exists, otherwise the last step.\n * Returns -1 if there are no attachments.\n */\nfunction findTestCaseAttachmentStepIndex(tc: TestCaseResult): number {\n if (tc.attachments.length === 0) return -1;\n\n const failedIndex = tc.stepResults.findIndex((sr) => sr.status === \"failed\");\n if (failedIndex >= 0) return failedIndex;\n\n return tc.stepResults.length - 1;\n}\n","/**\n * CucumberMessagesFormatter — produces NDJSON compatible with @cucumber/html-formatter.\n *\n * Message stream order:\n * Meta → Source* → GherkinDocument* → Pickle* → TestRunStarted →\n * [TestCase, TestCaseStarted, TestStep*, TestCaseFinished]* → TestRunFinished\n */\n\nimport type { TestRunResult, TestCaseResult } from \"../../types/test-result\";\nimport type { Envelope, Meta } from \"../../types/cucumber-messages\";\nimport { synthesizeFeature } from \"./synthesize-feature\";\nimport { buildGherkinDocumentEnvelopes } from \"./build-gherkin-document\";\nimport { buildPickleEnvelopes } from \"./build-pickles\";\nimport {\n buildTestRunStarted,\n buildTestRunFinished,\n buildTestCaseExecutionEnvelopes,\n} from \"./build-execution\";\n\nexport interface CucumberMessagesOptions {\n /** Strategy for deriving Source.uri. Default: \"sourceFile\" */\n uriStrategy?: \"sourceFile\" | \"virtual\";\n /** Whether to emit Source/GherkinDocument for synthesized features. Default: true */\n includeSynthetics?: boolean;\n /** Salt for deterministic IDs. Default: \"\" */\n idSalt?: string;\n /** Tool metadata for Meta envelope */\n meta?: { toolName?: string; toolVersion?: string };\n}\n\nexport class CucumberMessagesFormatter {\n private options: Required<\n Pick<CucumberMessagesOptions, \"uriStrategy\" | \"includeSynthetics\" | \"idSalt\">\n > & { meta?: CucumberMessagesOptions[\"meta\"] };\n\n constructor(options: CucumberMessagesOptions = {}) {\n this.options = {\n uriStrategy: options.uriStrategy ?? \"sourceFile\",\n includeSynthetics: options.includeSynthetics ?? true,\n idSalt: options.idSalt ?? \"\",\n meta: options.meta,\n };\n }\n\n /**\n * Format a TestRunResult into an array of Envelope objects.\n */\n format(run: TestRunResult): Envelope[] {\n const envelopes: Envelope[] = [];\n const salt = this.options.idSalt;\n\n // 1. Meta envelope\n envelopes.push(this.buildMetaEnvelope(run));\n\n // Group test cases by source file\n const grouped = this.groupBySourceFile(run.testCases);\n\n // 2. Source + GherkinDocument + Pickle envelopes (definition phase)\n const allPickleEnvelopes: Envelope[] = [];\n\n for (const [uri, testCases] of grouped) {\n const synthesized = synthesizeFeature(uri, testCases);\n\n if (this.options.includeSynthetics) {\n const { sourceEnvelope, gherkinDocumentEnvelope } =\n buildGherkinDocumentEnvelopes(uri, testCases, synthesized, salt);\n envelopes.push(sourceEnvelope);\n envelopes.push(gherkinDocumentEnvelope);\n }\n\n const pickles = buildPickleEnvelopes(uri, testCases, salt);\n allPickleEnvelopes.push(...pickles);\n }\n\n // All pickles after all source/gherkin documents\n envelopes.push(...allPickleEnvelopes);\n\n // 3. TestRunStarted\n envelopes.push(buildTestRunStarted(run));\n\n // 4. Execution envelopes per test case\n for (const [uri, testCases] of grouped) {\n for (const tc of testCases) {\n const executionEnvelopes = buildTestCaseExecutionEnvelopes(\n uri,\n tc,\n salt\n );\n envelopes.push(...executionEnvelopes);\n }\n }\n\n // 5. TestRunFinished (always last)\n envelopes.push(buildTestRunFinished(run));\n\n return envelopes;\n }\n\n /**\n * Format as NDJSON string (one JSON line per envelope).\n */\n formatToString(run: TestRunResult): string {\n const envelopes = this.format(run);\n return envelopes.map((e) => JSON.stringify(e)).join(\"\\n\") + \"\\n\";\n }\n\n /**\n * Build the Meta envelope.\n */\n private buildMetaEnvelope(run: TestRunResult): Envelope {\n const meta: Meta = {\n protocolVersion: \"25.0.1\",\n implementation: {\n name: this.options.meta?.toolName ?? \"executable-stories\",\n version:\n this.options.meta?.toolVersion ?? run.packageVersion ?? \"0.0.0\",\n },\n runtime: {\n name: \"node.js\",\n version: process.version,\n },\n os: {\n name: process.platform,\n },\n cpu: {\n name: process.arch,\n },\n };\n\n return { meta };\n }\n\n /**\n * Group test cases by source file, preserving order.\n */\n private groupBySourceFile(\n testCases: TestCaseResult[]\n ): Map<string, TestCaseResult[]> {\n const grouped = new Map<string, TestCaseResult[]>();\n for (const tc of testCases) {\n const uri = tc.sourceFile;\n const existing = grouped.get(uri);\n if (existing) {\n existing.push(tc);\n } else {\n grouped.set(uri, [tc]);\n }\n }\n return grouped;\n }\n}\n","/**\n * CucumberHtmlFormatter — produces the official Cucumber HTML report.\n *\n * Thin wrapper: reuses CucumberMessagesFormatter to generate NDJSON envelopes,\n * then pipes them through @cucumber/html-formatter's CucumberHtmlStream.\n */\n\nimport { Readable, Writable } from \"node:stream\";\nimport { CucumberHtmlStream } from \"@cucumber/html-formatter\";\nimport { CucumberMessagesFormatter } from \"./cucumber-messages/index\";\nimport type { CucumberMessagesOptions } from \"./cucumber-messages/index\";\nimport type { TestRunResult } from \"../types/test-result\";\n\nexport interface CucumberHtmlOptions {\n /** Options forwarded to the underlying CucumberMessagesFormatter */\n messages?: CucumberMessagesOptions;\n}\n\nexport class CucumberHtmlFormatter {\n private messagesFormatter: CucumberMessagesFormatter;\n\n constructor(options: CucumberHtmlOptions = {}) {\n this.messagesFormatter = new CucumberMessagesFormatter(options.messages);\n }\n\n /**\n * Format a TestRunResult into official Cucumber HTML.\n *\n * Returns a Promise because CucumberHtmlStream is a Node.js Transform stream.\n */\n async format(run: TestRunResult): Promise<string> {\n return this.formatToString(run);\n }\n\n /**\n * Format a TestRunResult into official Cucumber HTML string.\n */\n async formatToString(run: TestRunResult): Promise<string> {\n // 1. Generate NDJSON envelopes as plain objects\n const envelopes = this.messagesFormatter.format(run);\n\n // 2. Create the Cucumber HTML stream\n const htmlStream = new CucumberHtmlStream();\n\n // 3. Collect output chunks\n const chunks: Buffer[] = [];\n const collector = new Writable({\n write(chunk, _encoding, callback) {\n chunks.push(Buffer.from(chunk));\n callback();\n },\n });\n\n // 4. Pipe HTML stream output to collector\n htmlStream.pipe(collector);\n\n // 5. Write each envelope through the stream\n for (const envelope of envelopes) {\n const accepted = htmlStream.write(envelope);\n if (!accepted) {\n await new Promise<void>((resolve) => htmlStream.once(\"drain\", resolve));\n }\n }\n\n // 6. End the stream and wait for completion\n await new Promise<void>((resolve, reject) => {\n collector.on(\"finish\", resolve);\n collector.on(\"error\", reject);\n htmlStream.end();\n });\n\n return Buffer.concat(chunks).toString(\"utf8\");\n }\n}\n","import type { DocEntry, NormalizedTicket, StoryStep } from \"./story\";\nimport type { Attachment, TestCaseResult, TestRunResult, TestStatus } from \"./test-result\";\n\nexport type ScenarioChangeKind =\n | \"added\"\n | \"removed\"\n | \"regressed\"\n | \"fixed\"\n | \"changed\"\n | \"unchanged\";\n\nexport interface ScenarioChangeFlags {\n status: boolean;\n steps: boolean;\n docs: boolean;\n tags: boolean;\n tickets: boolean;\n source: boolean;\n duration: boolean;\n attachments: boolean;\n error: boolean;\n titlePath: boolean;\n}\n\nexport interface ScenarioSnapshot {\n id: string;\n scenario: string;\n sourceFile: string;\n sourceLine: number;\n status: TestStatus;\n durationMs: number;\n tags: string[];\n titlePath: string[];\n steps: StoryStep[];\n docs: DocEntry[];\n tickets: NormalizedTicket[];\n attachments: Attachment[];\n errorMessage?: string;\n}\n\nexport interface ScenarioDiff {\n kind: ScenarioChangeKind;\n id: string;\n scenario: string;\n sourceFile: string;\n sourceLine: number;\n baseline?: ScenarioSnapshot;\n current?: ScenarioSnapshot;\n flags: ScenarioChangeFlags;\n changedFields: string[];\n durationDeltaMs?: number;\n}\n\nexport interface RunDiffSummary {\n totalBaseline: number;\n totalCurrent: number;\n added: number;\n removed: number;\n changed: number;\n regressed: number;\n fixed: number;\n unchanged: number;\n}\n\nexport interface RunDiffResult {\n baseline: TestRunResult;\n current: TestRunResult;\n summary: RunDiffSummary;\n scenarios: ScenarioDiff[];\n}\n\nexport type CompareFormat = \"html\" | \"markdown\";\n\nexport interface CompareFormatterOptions {\n title?: string;\n}\n\nexport function toScenarioSnapshot(tc: TestCaseResult): ScenarioSnapshot {\n return {\n id: tc.id,\n scenario: tc.story.scenario,\n sourceFile: tc.sourceFile,\n sourceLine: tc.sourceLine,\n status: tc.status,\n durationMs: tc.durationMs,\n tags: tc.tags,\n titlePath: tc.titlePath,\n steps: tc.story.steps,\n docs: tc.story.docs ?? [],\n tickets: tc.story.tickets ?? [],\n attachments: tc.attachments,\n errorMessage: tc.errorMessage,\n };\n}\n","import type { RunDiffResult, ScenarioDiff } from \"../types/compare\";\n\nfunction titleFor(kind: ScenarioDiff[\"kind\"]): string {\n switch (kind) {\n case \"regressed\":\n return \"Regressed\";\n case \"fixed\":\n return \"Fixed\";\n case \"added\":\n return \"Added\";\n case \"removed\":\n return \"Removed\";\n case \"changed\":\n return \"Changed\";\n default:\n return \"Unchanged\";\n }\n}\n\nfunction summarizeScenario(scenario: ScenarioDiff): string {\n const before = scenario.baseline?.status;\n const after = scenario.current?.status;\n const statusPart =\n before && after\n ? `status \\`${before}\\` -> \\`${after}\\``\n : before\n ? `removed from \\`${before}\\``\n : `new \\`${after}\\``;\n const fields =\n scenario.changedFields.length > 0\n ? `; changed ${scenario.changedFields.map((field) => `\\`${field}\\``).join(\", \")}`\n : \"\";\n return `- ${scenario.scenario} (\\`${scenario.sourceFile}:${scenario.sourceLine}\\`): ${statusPart}${fields}`;\n}\n\nfunction addSection(\n lines: string[],\n diff: RunDiffResult,\n kind: ScenarioDiff[\"kind\"],\n maxScenarios: number\n): void {\n const scenarios = diff.scenarios.filter((scenario) => scenario.kind === kind);\n if (scenarios.length === 0) return;\n\n lines.push(`### ${titleFor(kind)} (${scenarios.length})`);\n lines.push(\"\");\n for (const scenario of scenarios.slice(0, maxScenarios)) {\n lines.push(summarizeScenario(scenario));\n }\n if (scenarios.length > maxScenarios) {\n lines.push(`- ...and ${scenarios.length - maxScenarios} more`);\n }\n lines.push(\"\");\n}\n\nexport function createPrCommentSummary(\n diff: RunDiffResult,\n maxScenarios = 10\n): string {\n const lines: string[] = [];\n\n lines.push(\"## Executable Stories Review Summary\");\n lines.push(\"\");\n lines.push(\n `Priority signal: ${diff.summary.regressed} regressed, ${diff.summary.fixed} fixed, ${diff.summary.added} added, ${diff.summary.removed} removed, ${diff.summary.changed} changed.`\n );\n lines.push(\"\");\n\n if (diff.summary.regressed > 0) {\n lines.push(\"> Regressions detected. Review these first.\");\n lines.push(\"\");\n } else if (diff.summary.fixed > 0) {\n lines.push(\"> No regressions detected. Review fixed scenarios next.\");\n lines.push(\"\");\n } else {\n lines.push(\"> No regressions or fixes detected. Remaining changes are neutral.\");\n lines.push(\"\");\n }\n\n addSection(lines, diff, \"regressed\", maxScenarios);\n addSection(lines, diff, \"fixed\", maxScenarios);\n addSection(lines, diff, \"added\", maxScenarios);\n addSection(lines, diff, \"removed\", maxScenarios);\n addSection(lines, diff, \"changed\", maxScenarios);\n\n return lines.join(\"\\n\").trimEnd();\n}\n","import type { TestRunResult } from \"../types/test-result\";\n\nexport interface BaselineCandidate {\n file: string;\n run: TestRunResult;\n}\n\nfunction getCommit(run: TestRunResult): string | undefined {\n return run.ci?.commitSha ?? run.gitSha;\n}\n\nfunction getBranch(run: TestRunResult): string | undefined {\n return run.ci?.branch;\n}\n\nexport function pickAutoBaseline(\n currentRun: TestRunResult,\n candidates: BaselineCandidate[]\n): BaselineCandidate | undefined {\n const currentBranch = getBranch(currentRun);\n const currentCommit = getCommit(currentRun);\n\n return [...candidates].sort((a, b) => {\n const aSameBranch =\n Boolean(currentBranch && getBranch(a.run) && currentBranch === getBranch(a.run));\n const bSameBranch =\n Boolean(currentBranch && getBranch(b.run) && currentBranch === getBranch(b.run));\n if (aSameBranch !== bSameBranch) {\n return Number(bSameBranch) - Number(aSameBranch);\n }\n\n const aDifferentCommit =\n Boolean(currentCommit && getCommit(a.run) && currentCommit !== getCommit(a.run));\n const bDifferentCommit =\n Boolean(currentCommit && getCommit(b.run) && currentCommit !== getCommit(b.run));\n if (aDifferentCommit !== bDifferentCommit) {\n return Number(bDifferentCommit) - Number(aDifferentCommit);\n }\n\n const aOlder = a.run.startedAtMs < currentRun.startedAtMs;\n const bOlder = b.run.startedAtMs < currentRun.startedAtMs;\n if (aOlder !== bOlder) {\n return Number(bOlder) - Number(aOlder);\n }\n\n return b.run.startedAtMs - a.run.startedAtMs;\n })[0];\n}\n","import type { TestCaseResult, TestRunResult } from \"../types/test-result\";\nimport type {\n RunDiffResult,\n ScenarioChangeFlags,\n ScenarioChangeKind,\n ScenarioDiff,\n} from \"../types/compare\";\nimport { toScenarioSnapshot } from \"../types/compare\";\n\nfunction compareStringArrays(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((value, index) => value === b[index]);\n}\n\nfunction stableJson(value: unknown): string {\n return JSON.stringify(value);\n}\n\nfunction isFailedStatus(status: TestCaseResult[\"status\"]): boolean {\n return status === \"failed\";\n}\n\nfunction getPrimaryKind(\n baseline: TestCaseResult,\n current: TestCaseResult,\n hasChanges: boolean\n): ScenarioChangeKind {\n if (isFailedStatus(current.status) && !isFailedStatus(baseline.status)) {\n return \"regressed\";\n }\n if (!isFailedStatus(current.status) && isFailedStatus(baseline.status)) {\n return \"fixed\";\n }\n return hasChanges ? \"changed\" : \"unchanged\";\n}\n\nfunction buildFlags(\n baseline: TestCaseResult,\n current: TestCaseResult\n): ScenarioChangeFlags {\n const baselineDocs = baseline.story.docs ?? [];\n const currentDocs = current.story.docs ?? [];\n\n return {\n status: baseline.status !== current.status,\n steps: stableJson(baseline.story.steps) !== stableJson(current.story.steps),\n docs: stableJson(baselineDocs) !== stableJson(currentDocs),\n tags: !compareStringArrays(baseline.tags, current.tags),\n tickets: stableJson(baseline.story.tickets ?? []) !== stableJson(current.story.tickets ?? []),\n source:\n baseline.sourceFile !== current.sourceFile ||\n baseline.sourceLine !== current.sourceLine,\n duration: baseline.durationMs !== current.durationMs,\n attachments:\n stableJson(baseline.attachments) !== stableJson(current.attachments),\n error:\n (baseline.errorMessage ?? \"\") !== (current.errorMessage ?? \"\"),\n titlePath: !compareStringArrays(baseline.titlePath, current.titlePath),\n };\n}\n\nfunction sortDiffs(scenarios: ScenarioDiff[]): ScenarioDiff[] {\n const rank: Record<ScenarioChangeKind, number> = {\n regressed: 0,\n fixed: 1,\n added: 2,\n removed: 3,\n changed: 4,\n unchanged: 5,\n };\n\n return [...scenarios].sort((a, b) => {\n if (rank[a.kind] !== rank[b.kind]) {\n return rank[a.kind] - rank[b.kind];\n }\n if (a.sourceFile !== b.sourceFile) {\n return a.sourceFile.localeCompare(b.sourceFile);\n }\n if (a.sourceLine !== b.sourceLine) {\n return a.sourceLine - b.sourceLine;\n }\n return a.scenario.localeCompare(b.scenario);\n });\n}\n\nexport function diffRuns(\n baseline: TestRunResult,\n current: TestRunResult\n): RunDiffResult {\n const baselineById = new Map(baseline.testCases.map((tc) => [tc.id, tc]));\n const currentById = new Map(current.testCases.map((tc) => [tc.id, tc]));\n const ids = new Set([...baselineById.keys(), ...currentById.keys()]);\n\n const scenarios: ScenarioDiff[] = [];\n\n for (const id of ids) {\n const before = baselineById.get(id);\n const after = currentById.get(id);\n\n if (!before && after) {\n const flags: ScenarioChangeFlags = {\n status: true,\n steps: true,\n docs: true,\n tags: true,\n tickets: true,\n source: true,\n duration: true,\n attachments: true,\n error: Boolean(after.errorMessage),\n titlePath: true,\n };\n scenarios.push({\n kind: \"added\",\n id,\n scenario: after.story.scenario,\n sourceFile: after.sourceFile,\n sourceLine: after.sourceLine,\n current: toScenarioSnapshot(after),\n flags,\n changedFields: Object.entries(flags)\n .filter(([, changed]) => changed)\n .map(([field]) => field),\n });\n continue;\n }\n\n if (before && !after) {\n const flags: ScenarioChangeFlags = {\n status: true,\n steps: true,\n docs: true,\n tags: true,\n tickets: true,\n source: true,\n duration: true,\n attachments: true,\n error: Boolean(before.errorMessage),\n titlePath: true,\n };\n scenarios.push({\n kind: \"removed\",\n id,\n scenario: before.story.scenario,\n sourceFile: before.sourceFile,\n sourceLine: before.sourceLine,\n baseline: toScenarioSnapshot(before),\n flags,\n changedFields: Object.entries(flags)\n .filter(([, changed]) => changed)\n .map(([field]) => field),\n });\n continue;\n }\n\n if (!before || !after) {\n continue;\n }\n\n const flags = buildFlags(before, after);\n const changedFields = Object.entries(flags)\n .filter(([, changed]) => changed)\n .map(([field]) => field);\n const kind = getPrimaryKind(before, after, changedFields.length > 0);\n\n scenarios.push({\n kind,\n id,\n scenario: after.story.scenario,\n sourceFile: after.sourceFile,\n sourceLine: after.sourceLine,\n baseline: toScenarioSnapshot(before),\n current: toScenarioSnapshot(after),\n flags,\n changedFields,\n durationDeltaMs: after.durationMs - before.durationMs,\n });\n }\n\n const sorted = sortDiffs(scenarios);\n const summary = {\n totalBaseline: baseline.testCases.length,\n totalCurrent: current.testCases.length,\n added: sorted.filter((s) => s.kind === \"added\").length,\n removed: sorted.filter((s) => s.kind === \"removed\").length,\n changed: sorted.filter((s) => s.kind === \"changed\").length,\n regressed: sorted.filter((s) => s.kind === \"regressed\").length,\n fixed: sorted.filter((s) => s.kind === \"fixed\").length,\n unchanged: sorted.filter((s) => s.kind === \"unchanged\").length,\n };\n\n return {\n baseline,\n current,\n summary,\n scenarios: sorted,\n };\n}\n\nexport { createPrCommentSummary } from \"./pr-summary\";\nexport { pickAutoBaseline } from \"./auto-baseline\";\n","import type { RunDiffResult, ScenarioDiff, ScenarioSnapshot } from \"../types/compare\";\nimport type { DocEntry, StoryStep } from \"../types/story\";\nimport type { HtmlTheme } from \"./html/themes/types\";\nimport { resolveTheme } from \"./html/themes/index\";\n\nexport interface RunDiffHtmlOptions {\n title?: string;\n /** Theme name or custom theme object. Default: \"default\" */\n theme?: string | HtmlTheme;\n /** Enable dark mode toggle. Default: true */\n darkMode?: boolean;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\nfunction statusLabel(kind: ScenarioDiff[\"kind\"]): string {\n switch (kind) {\n case \"regressed\":\n return \"Regressed\";\n case \"fixed\":\n return \"Fixed\";\n case \"added\":\n return \"Added\";\n case \"removed\":\n return \"Removed\";\n case \"changed\":\n return \"Changed\";\n default:\n return \"Unchanged\";\n }\n}\n\nfunction formatStep(step: StoryStep): string {\n let content = `<strong>${escapeHtml(step.keyword)}</strong> ${escapeHtml(step.text)}`;\n if (step.mode && step.mode !== \"normal\") {\n content += ` <span class=\"field-pill\">${escapeHtml(step.mode)}</span>`;\n }\n if (step.docs && step.docs.length > 0) {\n content += `<ul class=\"doc-list\">${step.docs.map((d) => `<li>${formatDocEntry(d)}</li>`).join(\"\")}</ul>`;\n }\n return `<li>${content}</li>`;\n}\n\nfunction formatSteps(steps: StoryStep[]): string {\n if (steps.length === 0) return \"&nbsp;\";\n return `<ul class=\"step-list\">${steps.map(formatStep).join(\"\")}</ul>`;\n}\n\nfunction formatDocEntry(doc: DocEntry): string {\n switch (doc.kind) {\n case \"note\":\n return escapeHtml(doc.text);\n case \"tag\":\n return escapeHtml(doc.names.join(\", \"));\n case \"kv\":\n return `${escapeHtml(doc.label)}: ${escapeHtml(typeof doc.value === \"object\" && doc.value !== null ? JSON.stringify(doc.value) : String(doc.value))}`;\n case \"code\":\n return `${escapeHtml(doc.label)}${doc.lang ? ` (${escapeHtml(doc.lang)})` : \"\"}: <code>${escapeHtml(doc.content)}</code>`;\n case \"table\": {\n const header = `<tr>${doc.columns.map((c) => `<th>${escapeHtml(c)}</th>`).join(\"\")}</tr>`;\n const rows = doc.rows.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join(\"\")}</tr>`).join(\"\");\n return `${escapeHtml(doc.label)}<table>${header}${rows}</table>`;\n }\n case \"link\":\n return `${escapeHtml(doc.label)}: ${escapeHtml(doc.url)}`;\n case \"section\":\n return `${escapeHtml(doc.title)}: ${escapeHtml(doc.markdown)}`;\n case \"mermaid\":\n return `${escapeHtml(doc.title ?? \"mermaid diagram\")}: <code>${escapeHtml(doc.code)}</code>`;\n case \"screenshot\":\n return `${doc.alt ? `${escapeHtml(doc.alt)}: ` : \"\"}${escapeHtml(doc.path)}`;\n case \"custom\":\n return `${escapeHtml(doc.type)}: ${escapeHtml(JSON.stringify(doc.data))}`;\n }\n}\n\nfunction formatDocs(docs: DocEntry[]): string {\n if (docs.length === 0) return \"&nbsp;\";\n return `<ul class=\"doc-list\">${docs.map((d) => `<li>${formatDocEntry(d)}</li>`).join(\"\")}</ul>`;\n}\n\nfunction renderScenarioCard(scenario: ScenarioDiff): string {\n const before = scenario.baseline;\n const after = scenario.current;\n const durationDelta = scenario.durationDeltaMs\n ? `${scenario.durationDeltaMs > 0 ? \"+\" : \"\"}${scenario.durationDeltaMs}ms`\n : \"\";\n\n return `\n <article class=\"scenario-card\" data-kind=\"${scenario.kind}\" data-search=\"${escapeHtml(\n `${scenario.scenario} ${scenario.sourceFile} ${scenario.changedFields.join(\" \")}`\n ).toLowerCase()}\">\n <header class=\"scenario-header\">\n <div>\n <span class=\"kind-badge kind-${scenario.kind}\">${statusLabel(scenario.kind)}</span>\n <h3>${escapeHtml(scenario.scenario)}</h3>\n <p class=\"source\">${escapeHtml(`${scenario.sourceFile}:${scenario.sourceLine}`)}</p>\n </div>\n <div class=\"meta\">\n ${\n before && after\n ? `<div class=\"status-pair\"><span>${escapeHtml(before.status)}</span><span>&rarr;</span><span>${escapeHtml(after.status)}</span></div>`\n : before\n ? `<div class=\"status-pair\"><span>${escapeHtml(before.status)}</span><span>&rarr;</span><span>removed</span></div>`\n : `<div class=\"status-pair\"><span>new</span><span>&rarr;</span><span>${escapeHtml(after?.status ?? \"\")}</span></div>`\n }\n ${durationDelta ? `<div class=\"duration-delta\">${escapeHtml(durationDelta)}</div>` : \"\"}\n </div>\n </header>\n ${\n scenario.changedFields.length > 0\n ? `<div class=\"field-list\">${scenario.changedFields\n .map((field) => `<span class=\"field-pill\">${escapeHtml(field)}</span>`)\n .join(\"\")}</div>`\n : \"\"\n }\n ${\n before && after\n ? `<div class=\"comparison-grid\">\n <section>\n <h4>Baseline</h4>\n <dl>\n <dt>Tags</dt>\n <dd>${escapeHtml(before.tags.join(\", \")) || \"&nbsp;\"}</dd>\n <dt>Suite</dt>\n <dd>${escapeHtml(before.titlePath.join(\" > \")) || \"&nbsp;\"}</dd>\n <dt>Error</dt>\n <dd>${escapeHtml(before.errorMessage ?? \"\") || \"&nbsp;\"}</dd>\n ${scenario.flags.steps ? `<dt>Steps</dt><dd>${formatSteps(before.steps)}</dd>` : \"\"}\n ${scenario.flags.docs ? `<dt>Docs</dt><dd>${formatDocs(before.docs)}</dd>` : \"\"}\n ${scenario.flags.tickets ? `<dt>Tickets</dt><dd>${escapeHtml(before.tickets.map(t => t.id).join(\", \")) || \"&nbsp;\"}</dd>` : \"\"}\n </dl>\n </section>\n <section>\n <h4>Current</h4>\n <dl>\n <dt>Tags</dt>\n <dd>${escapeHtml(after.tags.join(\", \")) || \"&nbsp;\"}</dd>\n <dt>Suite</dt>\n <dd>${escapeHtml(after.titlePath.join(\" > \")) || \"&nbsp;\"}</dd>\n <dt>Error</dt>\n <dd>${escapeHtml(after.errorMessage ?? \"\") || \"&nbsp;\"}</dd>\n ${scenario.flags.steps ? `<dt>Steps</dt><dd>${formatSteps(after.steps)}</dd>` : \"\"}\n ${scenario.flags.docs ? `<dt>Docs</dt><dd>${formatDocs(after.docs)}</dd>` : \"\"}\n ${scenario.flags.tickets ? `<dt>Tickets</dt><dd>${escapeHtml(after.tickets.map(t => t.id).join(\", \")) || \"&nbsp;\"}</dd>` : \"\"}\n </dl>\n </section>\n </div>`\n : (() => {\n const snapshot = after ?? before;\n if (!snapshot) return \"\";\n const hasTags = snapshot.tags.length > 0;\n const hasTickets = snapshot.tickets.length > 0;\n const hasSteps = snapshot.steps.length > 0;\n const hasDocs = snapshot.docs.length > 0;\n if (!hasTags && !hasTickets && !hasSteps && !hasDocs) return \"\";\n return `<div class=\"snapshot-detail\">\n <dl>\n ${hasTags ? `<dt>Tags</dt><dd>${escapeHtml(snapshot.tags.join(\", \"))}</dd>` : \"\"}\n ${hasTickets ? `<dt>Tickets</dt><dd>${escapeHtml(snapshot.tickets.map(t => t.id).join(\", \"))}</dd>` : \"\"}\n ${hasSteps ? `<dt>Steps</dt><dd>${formatSteps(snapshot.steps)}</dd>` : \"\"}\n ${hasDocs ? `<dt>Docs</dt><dd>${formatDocs(snapshot.docs)}</dd>` : \"\"}\n </dl>\n </div>`;\n })()\n }\n </article>\n `;\n}\n\n/** Diff-specific CSS that references theme custom properties */\nconst DIFF_CSS = `\n /* Diff layout — uses theme custom properties */\n * { box-sizing: border-box; }\n body {\n margin: 0;\n font-family: var(--font-sans, Georgia, \"Iowan Old Style\", serif);\n background: var(--background);\n color: var(--foreground);\n }\n main { max-width: 1200px; margin: 0 auto; padding: 32px 20px 80px; }\n .diff-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n }\n .diff-header-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n }\n .theme-toggle {\n background: var(--secondary);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 8px 12px;\n cursor: pointer;\n font-size: 1.1rem;\n color: var(--foreground);\n }\n .theme-toggle:hover { background: var(--accent); }\n .hero { display: grid; gap: 16px; margin-bottom: 24px; }\n .hero-card, .summary-card, .toolbar, .scenario-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius, 18px);\n box-shadow: 0 1px 3px color-mix(in srgb, var(--foreground) 6%, transparent);\n }\n .hero-card { padding: 24px; }\n h1, h2, h3, h4, p { margin: 0; }\n .subtle { color: var(--muted-foreground); margin-top: 8px; }\n .summary-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 12px;\n margin: 20px 0 24px;\n }\n .priority-banner {\n padding: 18px 20px;\n margin-bottom: 20px;\n background: linear-gradient(135deg, color-mix(in srgb, var(--destructive) 9%, transparent), var(--card));\n }\n .summary-card { padding: 16px; }\n .summary-card strong { display: block; font-size: 1.8rem; }\n .toolbar {\n position: sticky;\n top: 12px;\n z-index: 2;\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n padding: 14px;\n margin-bottom: 20px;\n }\n .toolbar input {\n flex: 1 1 260px;\n border: 1px solid var(--border);\n border-radius: 999px;\n padding: 10px 14px;\n font: inherit;\n background: var(--background);\n color: var(--foreground);\n }\n .toolbar button {\n border: 1px solid var(--border);\n background: var(--secondary);\n border-radius: 999px;\n padding: 10px 14px;\n font: inherit;\n cursor: pointer;\n color: var(--foreground);\n }\n .toolbar button.active { background: var(--foreground); color: var(--background); }\n .scenario-list { display: grid; gap: 14px; }\n .scenario-card { padding: 18px; }\n .scenario-header {\n display: flex;\n justify-content: space-between;\n gap: 16px;\n align-items: flex-start;\n }\n .kind-badge, .field-pill {\n display: inline-flex;\n align-items: center;\n padding: 4px 10px;\n border-radius: 999px;\n font-size: 0.85rem;\n margin-right: 8px;\n margin-bottom: 8px;\n background: var(--secondary);\n }\n .kind-regressed { background: color-mix(in srgb, var(--destructive) 15%, transparent); color: var(--destructive); }\n .kind-fixed { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }\n .kind-added { background: color-mix(in srgb, var(--primary) 15%, transparent); color: var(--primary); }\n .kind-removed { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning); }\n .kind-changed { background: color-mix(in srgb, var(--ring, var(--primary)) 15%, transparent); color: var(--ring, var(--primary)); }\n .source, .meta, dd { color: var(--muted-foreground); }\n .status-pair { display: flex; gap: 8px; justify-content: flex-end; font-family: var(--font-mono, ui-monospace, monospace); }\n .duration-delta { margin-top: 8px; text-align: right; color: var(--muted-foreground); }\n .field-list { margin-top: 10px; }\n .comparison-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n gap: 12px;\n margin-top: 16px;\n }\n .comparison-grid section {\n background: color-mix(in srgb, var(--card) 60%, var(--background));\n border: 1px solid var(--border);\n border-radius: var(--radius, 14px);\n padding: 12px;\n }\n dl {\n margin: 10px 0 0;\n display: grid;\n grid-template-columns: minmax(70px, 90px) 1fr;\n gap: 8px 12px;\n }\n @media (max-width: 720px) {\n .scenario-header { flex-direction: column; }\n .status-pair, .duration-delta { text-align: left; justify-content: flex-start; }\n }\n`;\n\n/** Theme toggle JavaScript */\nconst JS_THEME_TOGGLE = `\nfunction getSystemTheme() {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\nfunction getEffectiveTheme() {\n var saved = localStorage.getItem('diff-theme');\n if (saved === 'dark' || saved === 'light') return saved;\n return getSystemTheme();\n}\nfunction toggleTheme() {\n var current = getEffectiveTheme();\n var next = current === 'dark' ? 'light' : 'dark';\n localStorage.setItem('diff-theme', next);\n applyTheme(next);\n}\nfunction applyTheme(theme) {\n document.documentElement.setAttribute('data-theme', theme);\n var btn = document.querySelector('.theme-toggle');\n if (btn) {\n btn.textContent = theme === 'dark' ? '\\\\u2600\\\\ufe0f' : '\\\\ud83c\\\\udf19';\n btn.setAttribute('aria-label', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');\n }\n}\n`;\n\nexport class RunDiffHtmlFormatter {\n private title: string;\n private theme: HtmlTheme;\n private darkMode: boolean;\n\n constructor(options: RunDiffHtmlOptions = {}) {\n this.title = options.title ?? \"Run Comparison\";\n this.theme = resolveTheme(options.theme ?? \"default\");\n this.darkMode = options.darkMode ?? true;\n }\n\n format(diff: RunDiffResult): string {\n const defaultFilter: \"all\" | \"regressed\" | \"fixed\" | \"added\" | \"removed\" | \"changed\" =\n diff.summary.regressed > 0\n ? \"regressed\"\n : diff.summary.fixed > 0\n ? \"fixed\"\n : \"all\";\n const scenarios = diff.scenarios\n .filter((scenario) => scenario.kind !== \"unchanged\")\n .map((scenario) => renderScenarioCard(scenario))\n .join(\"\\n\");\n\n const themeToggleHtml = this.darkMode\n ? `<div class=\"diff-header-actions\"><button type=\"button\" class=\"theme-toggle\" onclick=\"toggleTheme()\" aria-label=\"Toggle theme\"></button></div>`\n : \"\";\n\n const themeInitJs = this.darkMode\n ? `${JS_THEME_TOGGLE}\\napplyTheme(getEffectiveTheme());\\nwindow.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() { if (!localStorage.getItem('diff-theme')) applyTheme(getSystemTheme()); });`\n : \"\";\n\n const themeAttr = this.darkMode ? ' data-theme=\"light\"' : '';\n\n return `<!doctype html>\n<html lang=\"en\"${themeAttr}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(this.title)}</title>\n <style>\n ${this.theme.css}\n ${DIFF_CSS}\n </style>\n </head>\n <body>\n <main>\n <section class=\"hero\">\n <div class=\"hero-card\">\n <div class=\"diff-header\">\n <h1>${escapeHtml(this.title)}</h1>\n ${themeToggleHtml}\n </div>\n <p class=\"subtle\">Baseline ${escapeHtml(new Date(diff.baseline.startedAtMs).toISOString())} against current ${escapeHtml(new Date(diff.current.startedAtMs).toISOString())}</p>\n </div>\n </section>\n <section class=\"summary-grid\">\n <div class=\"summary-card\"><strong>${diff.summary.regressed}</strong><span>Regressed</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.fixed}</strong><span>Fixed</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.added}</strong><span>Added</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.removed}</strong><span>Removed</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.changed}</strong><span>Changed</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.unchanged}</strong><span>Unchanged</span></div>\n </section>\n <section class=\"hero-card priority-banner\">\n <h2>Priority Review</h2>\n <p class=\"subtle\">${\n diff.summary.regressed > 0\n ? `${diff.summary.regressed} regression(s) detected. The view is pre-filtered to regressions.`\n : diff.summary.fixed > 0\n ? `No regressions detected. The view is pre-filtered to fixed scenarios.`\n : \"No regressions or fixes detected. Review neutral changes as needed.\"\n }</p>\n </section>\n <section class=\"toolbar\">\n <input type=\"search\" placeholder=\"Filter by scenario, file, or changed field\" aria-label=\"Filter scenarios\" />\n <button type=\"button\" class=\"${defaultFilter === \"all\" ? \"active\" : \"\"}\" data-filter=\"all\">All</button>\n <button type=\"button\" class=\"${defaultFilter === \"regressed\" ? \"active\" : \"\"}\" data-filter=\"regressed\">Regressed</button>\n <button type=\"button\" class=\"${defaultFilter === \"fixed\" ? \"active\" : \"\"}\" data-filter=\"fixed\">Fixed</button>\n <button type=\"button\" data-filter=\"added\">Added</button>\n <button type=\"button\" data-filter=\"removed\">Removed</button>\n <button type=\"button\" data-filter=\"changed\">Changed</button>\n </section>\n <section class=\"scenario-list\">${scenarios || \"<div class=\\\"hero-card\\\"><p>No scenario changes detected.</p></div>\"}</section>\n </main>\n <script>\n ${themeInitJs}\n const input = document.querySelector('input[type=\"search\"]');\n const buttons = Array.from(document.querySelectorAll('[data-filter]'));\n const cards = Array.from(document.querySelectorAll('.scenario-card'));\n let activeFilter = '${defaultFilter}';\n function applyFilters() {\n const query = (input.value || '').trim().toLowerCase();\n cards.forEach((card) => {\n const kind = card.getAttribute('data-kind');\n const haystack = card.getAttribute('data-search') || '';\n const matchesFilter = activeFilter === 'all' || kind === activeFilter;\n const matchesSearch = !query || haystack.includes(query);\n card.style.display = matchesFilter && matchesSearch ? '' : 'none';\n });\n }\n input.addEventListener('input', applyFilters);\n buttons.forEach((button) => {\n button.addEventListener('click', () => {\n activeFilter = button.getAttribute('data-filter');\n buttons.forEach((candidate) => candidate.classList.toggle('active', candidate === button));\n applyFilters();\n });\n });\n applyFilters();\n </script>\n </body>\n</html>`;\n }\n}\n","import type { RunDiffResult, ScenarioDiff, ScenarioSnapshot } from \"../types/compare\";\nimport type { DocEntry, StoryStep } from \"../types/story\";\n\nexport interface RunDiffMarkdownOptions {\n title?: string;\n}\n\nfunction formatStatus(kind: ScenarioDiff[\"kind\"]): string {\n switch (kind) {\n case \"regressed\":\n return \"Regressed\";\n case \"fixed\":\n return \"Fixed\";\n case \"added\":\n return \"Added\";\n case \"removed\":\n return \"Removed\";\n case \"changed\":\n return \"Changed\";\n default:\n return \"Unchanged\";\n }\n}\n\nfunction formatDurationDelta(deltaMs?: number): string | null {\n if (deltaMs === undefined || deltaMs === 0) return null;\n return `${deltaMs > 0 ? \"+\" : \"\"}${deltaMs}ms`;\n}\n\nfunction renderScenario(lines: string[], scenario: ScenarioDiff): void {\n const before = scenario.baseline;\n const after = scenario.current;\n\n lines.push(`## ${formatStatus(scenario.kind)}: ${scenario.scenario}`);\n lines.push(\"\");\n lines.push(`- File: \\`${scenario.sourceFile}:${scenario.sourceLine}\\``);\n if (before && after) {\n lines.push(`- Status: \\`${before.status}\\` -> \\`${after.status}\\``);\n } else if (after) {\n lines.push(`- Status: new \\`${after.status}\\``);\n } else if (before) {\n lines.push(`- Status: removed \\`${before.status}\\``);\n }\n if (scenario.changedFields.length > 0) {\n lines.push(`- Changed: ${scenario.changedFields.map((field) => `\\`${field}\\``).join(\", \")}`);\n }\n const durationDelta = formatDurationDelta(scenario.durationDeltaMs);\n if (durationDelta) {\n lines.push(`- Duration delta: ${durationDelta}`);\n }\n lines.push(\"\");\n\n if (before && after) {\n lines.push(\"| Field | Baseline | Current |\");\n lines.push(\"| --- | --- | --- |\");\n lines.push(`| Scenario | ${escapeCell(before.scenario)} | ${escapeCell(after.scenario)} |`);\n lines.push(`| Tags | ${escapeCell(before.tags.join(\", \"))} | ${escapeCell(after.tags.join(\", \"))} |`);\n lines.push(`| Suite | ${escapeCell(before.titlePath.join(\" > \"))} | ${escapeCell(after.titlePath.join(\" > \"))} |`);\n lines.push(`| Error | ${escapeCell(before.errorMessage ?? \"\")} | ${escapeCell(after.errorMessage ?? \"\")} |`);\n if (scenario.flags.steps) {\n lines.push(`| Steps | ${escapeCell(formatSteps(before.steps))} | ${escapeCell(formatSteps(after.steps))} |`);\n }\n if (scenario.flags.docs) {\n lines.push(`| Docs | ${escapeCell(formatDocs(before.docs))} | ${escapeCell(formatDocs(after.docs))} |`);\n }\n if (scenario.flags.tickets) {\n lines.push(`| Tickets | ${escapeCell(before.tickets.map(t => t.id).join(\", \"))} | ${escapeCell(after.tickets.map(t => t.id).join(\", \"))} |`);\n }\n lines.push(\"\");\n } else {\n const snapshot = after ?? before;\n if (snapshot) {\n renderSnapshotDetail(lines, snapshot);\n }\n }\n}\n\nfunction renderSnapshotDetail(lines: string[], snapshot: ScenarioSnapshot): void {\n if (snapshot.tags.length > 0) {\n lines.push(`**Tags:** ${snapshot.tags.join(\", \")}`);\n lines.push(\"\");\n }\n if (snapshot.tickets.length > 0) {\n lines.push(`**Tickets:** ${snapshot.tickets.map(t => t.id).join(\", \")}`);\n lines.push(\"\");\n }\n if (snapshot.steps.length > 0) {\n lines.push(\"**Steps:**\");\n lines.push(\"\");\n for (const step of snapshot.steps) {\n lines.push(`- ${formatStep(step)}`);\n }\n lines.push(\"\");\n }\n if (snapshot.docs.length > 0) {\n lines.push(\"**Docs:**\");\n lines.push(\"\");\n for (const doc of snapshot.docs) {\n lines.push(`- ${formatDocEntry(doc)}`);\n }\n lines.push(\"\");\n }\n}\n\nfunction escapeCell(value: string): string {\n return value.replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"<br>\");\n}\n\nfunction formatStep(step: StoryStep): string {\n let result = `**${step.keyword}** ${step.text}`;\n if (step.mode && step.mode !== \"normal\") {\n result += ` [${step.mode}]`;\n }\n if (step.docs && step.docs.length > 0) {\n result += ` (${step.docs.map(formatDocEntry).join(\"; \")})`;\n }\n return result;\n}\n\nfunction formatSteps(steps: StoryStep[]): string {\n if (steps.length === 0) return \"(none)\";\n return steps.map(formatStep).join(\"; \");\n}\n\nfunction formatDocEntry(doc: DocEntry): string {\n switch (doc.kind) {\n case \"note\":\n return doc.text;\n case \"tag\":\n return doc.names.join(\", \");\n case \"kv\":\n return `${doc.label}: ${typeof doc.value === \"object\" && doc.value !== null ? JSON.stringify(doc.value) : String(doc.value)}`;\n case \"code\":\n return `${doc.label}${doc.lang ? ` (${doc.lang})` : \"\"}: \\`${doc.content}\\``;\n case \"table\":\n return `${doc.label}: [${doc.columns.join(\", \")}] ${doc.rows.map((row) => row.join(\", \")).join(\"; \")}`;\n case \"link\":\n return `${doc.label}: ${doc.url}`;\n case \"section\":\n return `${doc.title}: ${doc.markdown}`;\n case \"mermaid\":\n return `${doc.title ?? \"mermaid diagram\"}: \\`${doc.code}\\``;\n case \"screenshot\":\n return `${doc.alt ? `${doc.alt}: ` : \"\"}${doc.path}`;\n case \"custom\":\n return `${doc.type}: ${JSON.stringify(doc.data)}`;\n }\n}\n\nfunction formatDocs(docs: DocEntry[]): string {\n if (docs.length === 0) return \"(none)\";\n return docs.map(formatDocEntry).join(\"; \");\n}\n\nexport class RunDiffMarkdownFormatter {\n private title: string;\n\n constructor(options: RunDiffMarkdownOptions = {}) {\n this.title = options.title ?? \"Run Comparison\";\n }\n\n format(diff: RunDiffResult): string {\n const lines: string[] = [];\n\n lines.push(`# ${this.title}`);\n lines.push(\"\");\n lines.push(`Baseline: \\`${new Date(diff.baseline.startedAtMs).toISOString()}\\``);\n lines.push(`Current: \\`${new Date(diff.current.startedAtMs).toISOString()}\\``);\n lines.push(\"\");\n lines.push(\"## Review Priority\");\n lines.push(\"\");\n if (diff.summary.regressed > 0) {\n lines.push(`Review regressions first: ${diff.summary.regressed} scenario(s) got worse.`);\n } else if (diff.summary.fixed > 0) {\n lines.push(`No regressions detected. Review ${diff.summary.fixed} fixed scenario(s) next.`);\n } else {\n lines.push(\"No regressions or fixes detected. Remaining changes are neutral.\");\n }\n lines.push(\"\");\n lines.push(\"| Added | Removed | Regressed | Fixed | Changed | Unchanged |\");\n lines.push(\"| ---: | ---: | ---: | ---: | ---: | ---: |\");\n lines.push(\n `| ${diff.summary.added} | ${diff.summary.removed} | ${diff.summary.regressed} | ${diff.summary.fixed} | ${diff.summary.changed} | ${diff.summary.unchanged} |`\n );\n lines.push(\"\");\n\n for (const kind of [\"regressed\", \"fixed\", \"added\", \"removed\", \"changed\"] as const) {\n const scenarios = diff.scenarios.filter((scenario) => scenario.kind === kind);\n if (scenarios.length === 0) continue;\n lines.push(`## ${formatStatus(kind)} (${scenarios.length})`);\n lines.push(\"\");\n for (const scenario of scenarios) {\n renderScenario(lines, scenario);\n }\n }\n\n return lines.join(\"\\n\").trimEnd();\n }\n}\n","import type { SortTestCasesMode, Logger } from \"./types/options\";\nimport type { TestCaseResult } from \"./types/test-result\";\n\nexport interface SelectTestCasesArgs {\n testCases: TestCaseResult[];\n include?: string[];\n exclude?: string[];\n includeTags?: string[];\n excludeTags?: string[];\n sortTestCases?: SortTestCasesMode;\n}\n\nexport interface SelectTestCasesDeps {\n logger: Logger;\n}\n\nexport function matchesPattern(pattern: string, sourceFile: string): boolean {\n const normalizedPattern = pattern.replace(/\\\\/g, \"/\");\n const normalizedFile = sourceFile.replace(/\\\\/g, \"/\");\n\n const regexStr = normalizedPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*/g, \"{{GLOBSTAR}}\")\n .replace(/\\*/g, \"[^/]*\")\n .replace(/{{GLOBSTAR}}/g, \".*\");\n\n const regex = new RegExp(`^${regexStr}$`);\n return regex.test(normalizedFile);\n}\n\nfunction filterTestCasesByGlobs(\n testCases: TestCaseResult[],\n include: string[],\n exclude: string[],\n logger: Logger\n): TestCaseResult[] {\n if (include.length === 0 && exclude.length === 0) return testCases;\n\n const filtered: TestCaseResult[] = [];\n for (const tc of testCases) {\n const sourceFile = tc.sourceFile.replace(/\\\\/g, \"/\");\n\n if (include.length > 0) {\n const included = include.some((pattern) => matchesPattern(pattern, sourceFile));\n if (!included) continue;\n }\n\n if (exclude.length > 0) {\n const excluded = exclude.some((pattern) => matchesPattern(pattern, sourceFile));\n if (excluded) continue;\n }\n\n filtered.push(tc);\n }\n\n const dropped = testCases.length - filtered.length;\n if (dropped > 0) {\n logger.warn(\n `Filtered ${dropped} test case(s) by include/exclude globs (${filtered.length} included)`\n );\n }\n\n return filtered;\n}\n\nfunction filterTestCasesByTags(\n testCases: TestCaseResult[],\n includeTags: string[],\n excludeTags: string[],\n logger: Logger\n): TestCaseResult[] {\n if (includeTags.length === 0 && excludeTags.length === 0) return testCases;\n\n const filtered: TestCaseResult[] = [];\n for (const tc of testCases) {\n if (includeTags.length > 0) {\n const included = tc.tags.some((tag) => includeTags.includes(tag));\n if (!included) continue;\n }\n\n if (excludeTags.length > 0) {\n const excluded = tc.tags.some((tag) => excludeTags.includes(tag));\n if (excluded) continue;\n }\n\n filtered.push(tc);\n }\n\n const dropped = testCases.length - filtered.length;\n if (dropped > 0) {\n logger.warn(\n `Filtered ${dropped} test case(s) by include/exclude tags (${filtered.length} included)`\n );\n }\n\n return filtered;\n}\n\nfunction sortTestCases(\n testCases: TestCaseResult[],\n sortMode: SortTestCasesMode\n): TestCaseResult[] {\n if (sortMode === \"none\") return testCases;\n\n return [...testCases].sort((a, b) => {\n if (sortMode === \"id\") {\n return a.id.localeCompare(b.id);\n }\n\n if (a.sourceFile !== b.sourceFile) {\n return a.sourceFile.localeCompare(b.sourceFile);\n }\n if (a.sourceLine !== b.sourceLine) {\n return a.sourceLine - b.sourceLine;\n }\n if (a.story.scenario !== b.story.scenario) {\n return a.story.scenario.localeCompare(b.story.scenario);\n }\n return a.id.localeCompare(b.id);\n });\n}\n\nexport function selectTestCases(\n args: SelectTestCasesArgs,\n deps: SelectTestCasesDeps\n): TestCaseResult[] {\n const include = args.include ?? [];\n const exclude = args.exclude ?? [];\n const includeTags = args.includeTags ?? [];\n const excludeTags = args.excludeTags ?? [];\n const sortMode = args.sortTestCases ?? \"none\";\n\n let selected = filterTestCasesByGlobs(\n args.testCases,\n include,\n exclude,\n deps.logger\n );\n\n selected = filterTestCasesByTags(\n selected,\n includeTags,\n excludeTags,\n deps.logger\n );\n\n return sortTestCases(selected, sortMode);\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { scanHtmlAssets } from \"./scan-html-assets\";\nimport { copyAsset } from \"./copy-asset\";\n\nexport interface BundleOptions {\n /** If true, warn about missing assets instead of throwing. Default: false. */\n allowMissing?: boolean;\n}\n\nexport interface BundleResult {\n /** Number of assets successfully copied */\n copiedCount: number;\n /** Number of missing assets */\n missingCount: number;\n /** Paths of missing assets (original references) */\n missing: string[];\n}\n\n/**\n * Post-process an HTML report file: copy referenced local assets into\n * an `assets/` directory beside it and rewrite paths in the HTML.\n */\nexport function bundleAssets(\n htmlPath: string,\n options: BundleOptions = {},\n): BundleResult {\n const htmlDir = path.dirname(htmlPath);\n const assetsDir = path.join(htmlDir, \"assets\");\n\n let html = fs.readFileSync(htmlPath, \"utf8\");\n const refs = scanHtmlAssets(html);\n\n let copiedCount = 0;\n const missing: string[] = [];\n\n for (const ref of refs) {\n const absolutePath = path.resolve(htmlDir, ref);\n\n if (!fs.existsSync(absolutePath)) {\n missing.push(ref);\n continue;\n }\n\n const newRelPath = copyAsset(absolutePath, assetsDir);\n html = replaceAssetRef(html, ref, newRelPath);\n copiedCount++;\n }\n\n if (missing.length > 0 && !options.allowMissing) {\n throw new Error(\n `Missing asset${missing.length > 1 ? \"s\" : \"\"}: ${missing.join(\", \")}`,\n );\n }\n\n fs.writeFileSync(htmlPath, html, \"utf8\");\n\n return {\n copiedCount,\n missingCount: missing.length,\n missing,\n };\n}\n\n/**\n * Replace an asset reference only in bundleable contexts:\n * - src=\"...\" on <img>/<video> elements\n * - href=\"...\" on <a class=\"attachment\"> elements\n *\n * Ordinary doc links sharing the same path are left untouched.\n */\nfunction replaceAssetRef(html: string, original: string, replacement: string): string {\n const escaped = original.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n // Rewrite src= on <img> and <video>\n const srcPattern = new RegExp(\n `(<(?:img|video)\\\\b[^>]*?\\\\bsrc=[\"'])${escaped}([\"'])`,\n \"g\",\n );\n html = html.replace(srcPattern, `$1${replacement}$2`);\n\n // Rewrite href= on <a class=\"attachment\"> (class before href)\n const hrefClassFirst = new RegExp(\n `(<a\\\\b[^>]*?\\\\bclass=[\"']attachment[\"'][^>]*?\\\\bhref=[\"'])${escaped}([\"'])`,\n \"g\",\n );\n html = html.replace(hrefClassFirst, `$1${replacement}$2`);\n\n // Rewrite href= on <a class=\"attachment\"> (href before class)\n const hrefHrefFirst = new RegExp(\n `(<a\\\\b[^>]*?\\\\bhref=[\"'])${escaped}([\"'][^>]*?\\\\bclass=[\"']attachment[\"'])`,\n \"g\",\n );\n html = html.replace(hrefHrefFirst, `$1${replacement}$2`);\n\n return html;\n}\n","/**\n * Scan generated HTML for local asset references.\n *\n * Targets: src=\"...\" on img/video, href=\"...\" on a.attachment.\n * Skips: data: URIs, http/https URLs, empty strings, fragment-only refs.\n * Returns deduplicated list of local path strings.\n */\nexport function scanHtmlAssets(html: string): string[] {\n const seen = new Set<string>();\n\n // Only match src=\"...\" on <img> and <video> elements, and\n // href=\"...\" on <a class=\"attachment\"> elements.\n // This avoids treating ordinary doc links as bundleable assets.\n const patterns: RegExp[] = [\n /<(?:img|video)\\b[^>]*?\\bsrc=[\"']([^\"']+)[\"']/g,\n /<a\\b[^>]*?\\bclass=[\"']attachment[\"'][^>]*?\\bhref=[\"']([^\"']+)[\"']/g,\n /<a\\b[^>]*?\\bhref=[\"']([^\"']+)[\"'][^>]*?\\bclass=[\"']attachment[\"']/g,\n ];\n\n for (const pattern of patterns) {\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(html)) !== null) {\n const ref = match[1];\n if (isLocalAssetRef(ref) && !seen.has(ref)) {\n seen.add(ref);\n }\n }\n }\n\n return [...seen];\n}\n\nfunction isLocalAssetRef(ref: string): boolean {\n if (!ref) return false;\n if (ref.startsWith(\"data:\")) return false;\n if (ref.startsWith(\"http://\") || ref.startsWith(\"https://\")) return false;\n if (ref.startsWith(\"#\")) return false;\n return true;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\n\n/**\n * Copy a source file into assetsDir with a content-hashed filename.\n *\n * Returns the relative path from the HTML file's directory to the copied asset\n * (e.g. \"assets/video-3f2c1a7b.webm\").\n *\n * Idempotent: if the destination already exists, skips the copy.\n */\nexport function copyAsset(sourcePath: string, assetsDir: string): string {\n if (!fs.existsSync(assetsDir)) {\n fs.mkdirSync(assetsDir, { recursive: true });\n }\n\n const content = fs.readFileSync(sourcePath);\n const hash = crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 8);\n\n const ext = path.extname(sourcePath);\n const baseName = sanitize(path.basename(sourcePath, ext));\n const destName = `${baseName}-${hash}${ext}`;\n const destPath = path.join(assetsDir, destName);\n\n if (!fs.existsSync(destPath)) {\n fs.copyFileSync(sourcePath, destPath);\n }\n\n return `assets/${destName}`;\n}\n\n/** Replace non-alphanumeric/hyphen/dot characters with hyphens, collapse runs. */\nfunction sanitize(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9._-]/g, \"-\")\n .replace(/-{2,}/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n","/**\n * NDJSON-to-TestRunResult parser.\n *\n * Parses Cucumber Messages NDJSON (one JSON envelope per line) back into\n * a TestRunResult suitable for rendering by HtmlFormatter or other formatters.\n *\n * This is the NDJSON compat path: it produces a minimal but sufficient\n * TestRunResult. Fields not present in the NDJSON stream are given\n * sensible defaults.\n */\n\nimport type { StoryMeta, StoryStep, StepKeyword, DocEntry, DocPhase } from \"../types/story\";\nimport type {\n TestRunResult,\n TestCaseResult,\n TestCaseAttempt,\n StepResult,\n TestStatus,\n Attachment,\n} from \"../types/test-result\";\nimport type {\n Envelope,\n Pickle,\n PickleStep,\n GherkinDocument,\n Source,\n TestCase,\n TestCaseStarted,\n TestCaseFinished,\n TestStepFinished,\n TestStepResultStatus,\n CucumberAttachment,\n Timestamp,\n} from \"../types/cucumber-messages\";\n\n// ============================================================================\n// Internal index types\n// ============================================================================\n\ninterface PickleIndex {\n pickle: Pickle;\n uri: string;\n}\n\ninterface StepDefinition {\n keyword: StepKeyword;\n text: string;\n}\n\ninterface TestCaseIndex {\n testCase: TestCase;\n pickleId: string;\n}\n\ninterface TestCaseStartedIndex {\n testCaseStarted: TestCaseStarted;\n testCaseId: string;\n}\n\ninterface TestCaseFinishedIndex {\n testCaseStartedId: string;\n willBeRetried: boolean;\n}\n\ninterface StepResultAccumulator {\n testStepId: string;\n status: TestStatus;\n durationMs: number;\n errorMessage?: string;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Parse an NDJSON string into a TestRunResult.\n *\n * @param ndjson - NDJSON string (one JSON envelope per line)\n * @returns TestRunResult reconstructed from the envelopes\n */\nexport function parseNdjson(ndjson: string): TestRunResult {\n const lines = ndjson.trim().split(\"\\n\").filter(Boolean);\n const envelopes: Envelope[] = lines.map((line) => JSON.parse(line));\n return parseEnvelopes(envelopes);\n}\n\n/**\n * Parse an array of Envelope objects into a TestRunResult.\n *\n * @param envelopes - Array of Cucumber Messages envelopes\n * @returns TestRunResult reconstructed from the envelopes\n */\nexport function parseEnvelopes(envelopes: Envelope[]): TestRunResult {\n // Indexes for cross-referencing\n const sources = new Map<string, Source>(); // uri → Source\n const gherkinDocs = new Map<string, GherkinDocument>(); // uri → GherkinDocument\n const pickles = new Map<string, PickleIndex>(); // pickleId → { pickle, uri }\n const testCases = new Map<string, TestCaseIndex>(); // testCaseId → { testCase, pickleId }\n const testCaseStarteds = new Map<string, TestCaseStartedIndex>(); // testCaseStartedId → { ... }\n const testCaseFinisheds = new Map<string, TestCaseFinishedIndex>(); // testCaseStartedId → { ... }\n const testCaseIdToStartedIds = new Map<string, string[]>(); // testCaseId → testCaseStartedId[]\n const stepResults = new Map<string, StepResultAccumulator[]>(); // testCaseStartedId → step results\n const attachments = new Map<string, CucumberAttachment[]>(); // testCaseStartedId → attachments\n\n let startedAtMs = 0;\n let finishedAtMs = 0;\n let success = true;\n let toolName = \"unknown\";\n let toolVersion = \"0.0.0\";\n\n // Single-pass index building\n for (const envelope of envelopes) {\n if (\"meta\" in envelope) {\n toolName = envelope.meta.implementation.name;\n toolVersion = envelope.meta.implementation.version;\n }\n\n if (\"source\" in envelope) {\n sources.set(envelope.source.uri, envelope.source);\n }\n\n if (\"gherkinDocument\" in envelope) {\n const doc = envelope.gherkinDocument;\n gherkinDocs.set(doc.uri, doc);\n }\n\n if (\"pickle\" in envelope) {\n const p = envelope.pickle;\n pickles.set(p.id, { pickle: p, uri: p.uri });\n }\n\n if (\"testCase\" in envelope) {\n const tc = envelope.testCase;\n testCases.set(tc.id, { testCase: tc, pickleId: tc.pickleId });\n }\n\n if (\"testCaseStarted\" in envelope) {\n const tcs = envelope.testCaseStarted;\n testCaseStarteds.set(tcs.id, {\n testCaseStarted: tcs,\n testCaseId: tcs.testCaseId,\n });\n stepResults.set(tcs.id, []);\n attachments.set(tcs.id, []);\n // Group by testCaseId for retry detection\n const existing = testCaseIdToStartedIds.get(tcs.testCaseId) ?? [];\n existing.push(tcs.id);\n testCaseIdToStartedIds.set(tcs.testCaseId, existing);\n }\n\n if (\"testCaseFinished\" in envelope) {\n const tcf = envelope.testCaseFinished;\n testCaseFinisheds.set(tcf.testCaseStartedId, {\n testCaseStartedId: tcf.testCaseStartedId,\n willBeRetried: tcf.willBeRetried,\n });\n }\n\n if (\"testStepFinished\" in envelope) {\n const tsf = envelope.testStepFinished;\n const results = stepResults.get(tsf.testCaseStartedId);\n if (results) {\n results.push({\n testStepId: tsf.testStepId,\n status: cucumberStatusToTestStatus(tsf.testStepResult.status),\n durationMs: durationToMs(tsf.testStepResult.duration),\n errorMessage: tsf.testStepResult.message,\n });\n }\n }\n\n if (\"attachment\" in envelope) {\n const att = envelope.attachment;\n const list = attachments.get(att.testCaseStartedId);\n if (list) {\n list.push(att);\n }\n }\n\n if (\"testRunStarted\" in envelope) {\n startedAtMs = timestampToMs(envelope.testRunStarted.timestamp);\n }\n\n if (\"testRunFinished\" in envelope) {\n finishedAtMs = timestampToMs(envelope.testRunFinished.timestamp);\n success = envelope.testRunFinished.success;\n }\n }\n\n // Reconstruct TestCaseResults (one per TestCase, grouping retries)\n const testCaseResults: TestCaseResult[] = [];\n\n for (const [testCaseId, tcIndex] of testCases) {\n const pickleIndex = pickles.get(tcIndex.pickleId);\n if (!pickleIndex) continue;\n\n const pickle = pickleIndex.pickle;\n const uri = pickleIndex.uri;\n\n // Get all started IDs for this test case (sorted by attempt number)\n const startedIds = testCaseIdToStartedIds.get(testCaseId) ?? [];\n if (startedIds.length === 0) continue;\n\n // Sort by attempt number\n const sortedStarteds = startedIds\n .map((id) => testCaseStarteds.get(id)!)\n .filter(Boolean)\n .sort((a, b) => a.testCaseStarted.attempt - b.testCaseStarted.attempt);\n\n // The final attempt is the last one (or the one with willBeRetried=false)\n const finalStarted = sortedStarteds[sortedStarteds.length - 1];\n const finalStartedId = finalStarted.testCaseStarted.id;\n\n // Build step-to-result mapping via testStepId\n const testStepIdToIndex = new Map<string, number>();\n for (let i = 0; i < tcIndex.testCase.testSteps.length; i++) {\n testStepIdToIndex.set(tcIndex.testCase.testSteps[i].id, i);\n }\n\n // Reconstruct StorySteps from pickle steps + gherkin doc\n const storySteps = reconstructStorySteps(pickle, uri, gherkinDocs);\n\n // Use final attempt's step results\n const tcStepResults = stepResults.get(finalStartedId) ?? [];\n const tcAttachments = attachments.get(finalStartedId) ?? [];\n\n // Reconstruct StepResults in order\n const orderedStepResults: StepResult[] = storySteps.map((_, i) => ({\n index: i,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n\n for (const sr of tcStepResults) {\n const stepIndex = testStepIdToIndex.get(sr.testStepId);\n if (stepIndex !== undefined && stepIndex < orderedStepResults.length) {\n orderedStepResults[stepIndex] = {\n index: stepIndex,\n status: sr.status,\n durationMs: sr.durationMs,\n errorMessage: sr.errorMessage,\n };\n }\n }\n\n // Derive overall status from step results\n const overallStatus = deriveOverallStatus(orderedStepResults);\n const totalDurationMs = orderedStepResults.reduce(\n (sum, sr) => sum + sr.durationMs,\n 0\n );\n\n // Reconstruct tags\n const tags = pickle.tags.map((t) => t.name.replace(/^@/, \"\"));\n\n // Reconstruct attachments (from final attempt)\n const resolvedAttachments: Attachment[] = tcAttachments.map((att) => ({\n name: att.mediaType,\n mediaType: att.mediaType,\n body: att.body,\n contentEncoding: att.contentEncoding,\n }));\n\n // Extract titlePath from feature name + scenario name\n const featureName = extractFeatureName(uri, gherkinDocs);\n const titlePath = featureName\n ? [featureName, pickle.name]\n : [pickle.name];\n\n // Build StoryMeta\n const story: StoryMeta = {\n scenario: pickle.name,\n steps: storySteps,\n tags: tags.length > 0 ? tags : undefined,\n };\n\n // Build error info\n const failedStep = orderedStepResults.find((sr) => sr.status === \"failed\");\n\n // Build attempts array if there are retries\n let attempts: TestCaseAttempt[] | undefined;\n if (sortedStarteds.length > 1) {\n attempts = sortedStarteds.map((started) => {\n const sId = started.testCaseStarted.id;\n const sStepResults = stepResults.get(sId) ?? [];\n const attemptStatus = deriveOverallStatus(\n buildOrderedStepResults(sStepResults, testStepIdToIndex, storySteps.length)\n );\n const attemptDurationMs = sStepResults.reduce(\n (sum, sr) => sum + sr.durationMs, 0\n );\n const attemptFailedStep = sStepResults.find((sr) => sr.status === \"failed\");\n return {\n attempt: started.testCaseStarted.attempt,\n status: attemptStatus,\n durationMs: attemptDurationMs,\n errorMessage: attemptFailedStep?.errorMessage,\n };\n });\n }\n\n const testCaseResult: TestCaseResult = {\n id: tcIndex.testCase.id,\n story,\n sourceFile: uri,\n sourceLine: 1,\n status: overallStatus,\n durationMs: totalDurationMs,\n errorMessage: failedStep?.errorMessage,\n attachments: resolvedAttachments,\n stepResults: orderedStepResults,\n titlePath,\n retry: finalStarted.testCaseStarted.attempt,\n retries: sortedStarteds.length > 1 ? sortedStarteds.length - 1 : 0,\n tags,\n attempts,\n };\n\n testCaseResults.push(testCaseResult);\n }\n\n const durationMs =\n finishedAtMs > 0 && startedAtMs > 0\n ? finishedAtMs - startedAtMs\n : testCaseResults.reduce((sum, tc) => sum + tc.durationMs, 0);\n\n return {\n testCases: testCaseResults,\n startedAtMs,\n finishedAtMs,\n durationMs,\n projectRoot: \"\",\n runId: \"\",\n packageVersion: toolVersion !== \"0.0.0\" ? toolVersion : undefined,\n };\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction cucumberStatusToTestStatus(status: TestStepResultStatus): TestStatus {\n switch (status) {\n case \"PASSED\":\n return \"passed\";\n case \"FAILED\":\n return \"failed\";\n case \"SKIPPED\":\n return \"skipped\";\n case \"PENDING\":\n case \"UNDEFINED\":\n return \"pending\";\n default:\n return \"skipped\";\n }\n}\n\nfunction timestampToMs(ts: Timestamp): number {\n return ts.seconds * 1000 + Math.round(ts.nanos / 1_000_000);\n}\n\nfunction durationToMs(d: { seconds: number; nanos: number }): number {\n return d.seconds * 1000 + Math.round(d.nanos / 1_000_000);\n}\n\n/**\n * Reconstruct StorySteps from pickle steps, using GherkinDocument for keywords.\n */\nfunction reconstructStorySteps(\n pickle: Pickle,\n uri: string,\n gherkinDocs: Map<string, GherkinDocument>\n): StoryStep[] {\n const doc = gherkinDocs.get(uri);\n\n // Build AST step ID → keyword map from GherkinDocument\n const astStepKeywords = new Map<string, string>();\n if (doc) {\n for (const child of doc.feature.children) {\n const scenario = child.scenario ?? child.background;\n if (scenario) {\n for (const step of scenario.steps) {\n astStepKeywords.set(step.id, step.keyword.trim());\n }\n }\n }\n }\n\n return pickle.steps.map((ps) => {\n // Look up the keyword from the AST step\n let keyword: StepKeyword = \"Given\";\n if (ps.astNodeIds.length > 0) {\n const astKeyword = astStepKeywords.get(ps.astNodeIds[0]);\n if (astKeyword && isStepKeyword(astKeyword)) {\n keyword = astKeyword;\n }\n }\n\n // Fallback: derive keyword from pickle step type\n if (!ps.astNodeIds.length || !astStepKeywords.has(ps.astNodeIds[0])) {\n keyword = pickleStepTypeToKeyword(ps.type);\n }\n\n const step: StoryStep = {\n keyword,\n text: ps.text,\n };\n\n // Reconstruct docs from pickle step argument\n const docs = pickleStepArgumentToDocs(ps);\n if (docs.length > 0) {\n step.docs = docs;\n }\n\n return step;\n });\n}\n\nfunction isStepKeyword(s: string): s is StepKeyword {\n return [\"Given\", \"When\", \"Then\", \"And\", \"But\"].includes(s);\n}\n\nfunction pickleStepTypeToKeyword(\n type: string\n): StepKeyword {\n switch (type) {\n case \"Context\":\n return \"Given\";\n case \"Action\":\n return \"When\";\n case \"Outcome\":\n return \"Then\";\n default:\n return \"Given\";\n }\n}\n\nfunction extractFeatureName(\n uri: string,\n gherkinDocs: Map<string, GherkinDocument>\n): string | undefined {\n const doc = gherkinDocs.get(uri);\n if (doc) {\n return doc.feature.name;\n }\n return undefined;\n}\n\nfunction deriveOverallStatus(stepResults: StepResult[]): TestStatus {\n if (stepResults.some((sr) => sr.status === \"failed\")) return \"failed\";\n if (stepResults.every((sr) => sr.status === \"skipped\")) return \"skipped\";\n if (stepResults.some((sr) => sr.status === \"pending\")) return \"pending\";\n if (stepResults.every((sr) => sr.status === \"passed\")) return \"passed\";\n return \"passed\";\n}\n\n/**\n * Build ordered step results from accumulated results for a specific attempt.\n */\nfunction buildOrderedStepResults(\n accumulators: StepResultAccumulator[],\n testStepIdToIndex: Map<string, number>,\n stepCount: number\n): StepResult[] {\n const results: StepResult[] = Array.from({ length: stepCount }, (_, i) => ({\n index: i,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n\n for (const sr of accumulators) {\n const stepIndex = testStepIdToIndex.get(sr.testStepId);\n if (stepIndex !== undefined && stepIndex < results.length) {\n results[stepIndex] = {\n index: stepIndex,\n status: sr.status,\n durationMs: sr.durationMs,\n errorMessage: sr.errorMessage,\n };\n }\n }\n\n return results;\n}\n\n/**\n * Convert a pickle step's argument (DocString/DataTable) back to DocEntry[].\n */\nfunction pickleStepArgumentToDocs(ps: PickleStep): DocEntry[] {\n if (!ps.argument) return [];\n const docs: DocEntry[] = [];\n const phase: DocPhase = \"static\";\n\n if (ps.argument.dataTable) {\n const table = ps.argument.dataTable;\n if (table.rows.length > 0) {\n const columns = table.rows[0].cells.map((c) => c.value);\n const rows = table.rows.slice(1).map((r) => r.cells.map((c) => c.value));\n docs.push({\n kind: \"table\",\n label: \"\",\n columns,\n rows,\n phase,\n });\n }\n }\n\n if (ps.argument.docString) {\n const ds = ps.argument.docString;\n const mediaType = ds.mediaType ?? \"text/plain\";\n\n if (mediaType === \"text/plain\") {\n docs.push({\n kind: \"note\",\n text: ds.content,\n phase,\n });\n } else if (mediaType === \"text/markdown\") {\n docs.push({\n kind: \"section\",\n title: \"\",\n markdown: ds.content,\n phase,\n });\n } else if (mediaType === \"text/x-mermaid\") {\n docs.push({\n kind: \"mermaid\",\n code: ds.content,\n phase,\n });\n } else if (mediaType === \"application/json\") {\n try {\n docs.push({\n kind: \"custom\",\n type: \"json\",\n data: JSON.parse(ds.content),\n phase,\n });\n } catch {\n docs.push({\n kind: \"code\",\n label: \"\",\n content: ds.content,\n lang: \"json\",\n phase,\n });\n }\n } else {\n // Default: treat as code block with mediaType as lang\n docs.push({\n kind: \"code\",\n label: \"\",\n content: ds.content,\n lang: mediaType,\n phase,\n });\n }\n }\n\n return docs;\n}\n","/**\n * No-dependency ANSI escape sequence stripper.\n */\n\n/** Strip ANSI escape sequences. Regex: \\x1B\\[[0-?]*[ -/]*[@-~] */\nexport function stripAnsi(text: string): string {\n // eslint-disable-next-line no-control-regex\n return text.replace(/\\x1B\\[[0-?]*[ -/]*[@-~]/g, \"\");\n}\n","/**\n * Slack webhook notifier using Block Kit.\n *\n * Follows the fn(args, deps) pattern.\n * Never logs the webhook URL.\n */\n\nimport type { NotificationSummary } from \"./types\";\nimport { stripAnsi } from \"./ansi-strip\";\n\n/** Arguments for sendSlackNotification. */\nexport interface SlackNotificationArgs {\n summary: NotificationSummary;\n webhookUrl: string;\n maxFailedTests?: number;\n}\n\n/** Injectable dependencies for sendSlackNotification. */\nexport interface SlackNotificationDeps {\n fetch: typeof globalThis.fetch;\n logger: { warn(msg: string): void };\n}\n\n/** Result of a notification send attempt. */\nexport interface NotificationResult {\n ok: boolean;\n error?: string;\n}\n\n/** Truncate text to a maximum length, appending ellipsis if needed. */\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - 3) + \"...\";\n}\n\n/** Format milliseconds as a human-readable duration string. */\nfunction formatDuration(ms: number): string {\n const seconds = ms / 1000;\n if (seconds < 60) return `${seconds.toFixed(1)}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}m ${remainingSeconds.toFixed(0)}s`;\n}\n\n/** Build Slack Block Kit payload from a notification summary. */\nfunction buildSlackPayload(\n summary: NotificationSummary,\n maxFailedTests: number,\n): Record<string, unknown> {\n const allPassed = summary.failed === 0;\n const emoji = allPassed ? \":white_check_mark:\" : \":x:\";\n const statusText = allPassed ? \"Passed\" : \"Failed\";\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const blocks: any[] = [];\n\n // Header block\n blocks.push({\n type: \"header\",\n text: {\n type: \"plain_text\",\n text: `${emoji} Test Results: ${summary.passed} passed, ${summary.failed} failed`,\n emoji: true,\n },\n });\n\n // Summary fields section\n blocks.push({\n type: \"section\",\n fields: [\n { type: \"mrkdwn\", text: `*Total:* ${summary.total}` },\n { type: \"mrkdwn\", text: `*Passed:* ${summary.passed}` },\n { type: \"mrkdwn\", text: `*Failed:* ${summary.failed}` },\n { type: \"mrkdwn\", text: `*Skipped:* ${summary.skipped}` },\n { type: \"mrkdwn\", text: `*Duration:* ${formatDuration(summary.durationMs)}` },\n { type: \"mrkdwn\", text: `*Status:* ${statusText}` },\n ],\n });\n\n // Failed tests section\n if (summary.failedTests.length > 0) {\n const displayedTests = summary.failedTests.slice(0, maxFailedTests);\n const lines = displayedTests.map((t) => {\n const name = t.name;\n if (t.error) {\n const cleanError = truncate(stripAnsi(t.error), 500);\n return `*${name}*\\n\\`\\`\\`${cleanError}\\`\\`\\``;\n }\n return `*${name}*`;\n });\n\n let text = lines.join(\"\\n\\n\");\n if (summary.failedTests.length > maxFailedTests) {\n text += `\\n\\n_...and ${summary.failedTests.length - maxFailedTests} more_`;\n }\n\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text,\n },\n });\n }\n\n // CI context block\n if (summary.ci) {\n const elements: Array<{ type: string; text: string }> = [];\n\n if (summary.ci.displayName) {\n elements.push({ type: \"mrkdwn\", text: `*CI:* ${summary.ci.displayName}` });\n }\n if (summary.ci.branch) {\n elements.push({ type: \"mrkdwn\", text: `*Branch:* ${summary.ci.branch}` });\n }\n if (summary.ci.commitSha) {\n elements.push({ type: \"mrkdwn\", text: `*Commit:* ${summary.ci.commitSha.slice(0, 7)}` });\n }\n if (summary.ci.buildNumber) {\n elements.push({ type: \"mrkdwn\", text: `*Build:* #${summary.ci.buildNumber}` });\n }\n\n if (elements.length > 0) {\n blocks.push({\n type: \"context\",\n elements,\n });\n }\n }\n\n // Actions block with \"View Report\" button\n if (summary.reportUrl) {\n blocks.push({\n type: \"actions\",\n elements: [\n {\n type: \"button\",\n text: {\n type: \"plain_text\",\n text: \"View Report\",\n emoji: true,\n },\n url: summary.reportUrl,\n action_id: \"view_report\",\n },\n ],\n });\n }\n\n return { blocks };\n}\n\n/**\n * Send a Slack notification via incoming webhook.\n *\n * Never throws. Returns `{ ok, error? }`.\n * Never logs the webhook URL.\n */\nexport async function sendSlackNotification(\n args: SlackNotificationArgs,\n deps: SlackNotificationDeps,\n): Promise<NotificationResult> {\n const { summary, webhookUrl, maxFailedTests = 5 } = args;\n const { fetch, logger } = deps;\n\n const payload = buildSlackPayload(summary, maxFailedTests);\n\n try {\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const requestId = response.headers.get(\"x-request-id\") ?? undefined;\n let bodyText = \"\";\n try {\n bodyText = await response.text();\n } catch {\n // ignore body read errors\n }\n const truncatedBody = truncate(bodyText, 200);\n const idPart = requestId ? ` x-request-id=${requestId}` : \"\";\n const errorMsg = `Slack notifier failed: HTTP ${response.status}${idPart} ${truncatedBody}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const errorMsg = `Slack notifier failed: ${msg}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n}\n","/**\n * Microsoft Teams webhook notifier using Adaptive Cards.\n *\n * Follows the fn(args, deps) pattern.\n * Never logs the webhook URL.\n */\n\nimport type { NotificationSummary } from \"./types\";\nimport { stripAnsi } from \"./ansi-strip\";\n\n/** Arguments for sendTeamsNotification. */\nexport interface TeamsNotificationArgs {\n summary: NotificationSummary;\n webhookUrl: string;\n maxFailedTests?: number;\n}\n\n/** Injectable dependencies for sendTeamsNotification. */\nexport interface TeamsNotificationDeps {\n fetch: typeof globalThis.fetch;\n logger: { warn(msg: string): void };\n}\n\n/** Result of a notification send attempt. */\nexport interface TeamsNotificationResult {\n ok: boolean;\n error?: string;\n}\n\n/** Truncate text to a maximum length, appending ellipsis if needed. */\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - 3) + \"...\";\n}\n\n/** Format milliseconds as a human-readable duration string. */\nfunction formatDuration(ms: number): string {\n const seconds = ms / 1000;\n if (seconds < 60) return `${seconds.toFixed(1)}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}m ${remainingSeconds.toFixed(0)}s`;\n}\n\n/** Build an Adaptive Card payload from a notification summary. */\nfunction buildTeamsPayload(\n summary: NotificationSummary,\n maxFailedTests: number,\n): Record<string, unknown> {\n const allPassed = summary.failed === 0;\n const statusEmoji = allPassed ? \"\\u2705\" : \"\\u274C\"; // check mark / cross mark\n const statusColor = allPassed ? \"good\" : \"attention\";\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const bodyItems: any[] = [];\n\n // Header with status\n bodyItems.push({\n type: \"TextBlock\",\n size: \"Large\",\n weight: \"Bolder\",\n text: `${statusEmoji} Test Results`,\n color: statusColor,\n });\n\n // FactSet with summary\n bodyItems.push({\n type: \"FactSet\",\n facts: [\n { title: \"Total\", value: String(summary.total) },\n { title: \"Passed\", value: String(summary.passed) },\n { title: \"Failed\", value: String(summary.failed) },\n { title: \"Skipped\", value: String(summary.skipped) },\n { title: \"Duration\", value: formatDuration(summary.durationMs) },\n ],\n });\n\n // Failed tests container\n if (summary.failedTests.length > 0) {\n const displayedTests = summary.failedTests.slice(0, maxFailedTests);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const failedItems: any[] = [\n {\n type: \"TextBlock\",\n text: \"Failed Tests\",\n weight: \"Bolder\",\n spacing: \"Medium\",\n },\n ];\n\n for (const t of displayedTests) {\n failedItems.push({\n type: \"TextBlock\",\n text: `**${t.name}**`,\n wrap: true,\n });\n if (t.error) {\n const cleanError = truncate(stripAnsi(t.error), 500);\n failedItems.push({\n type: \"TextBlock\",\n text: cleanError,\n wrap: true,\n fontType: \"Monospace\",\n size: \"Small\",\n color: \"Attention\",\n });\n }\n }\n\n if (summary.failedTests.length > maxFailedTests) {\n failedItems.push({\n type: \"TextBlock\",\n text: `...and ${summary.failedTests.length - maxFailedTests} more`,\n isSubtle: true,\n spacing: \"Small\",\n });\n }\n\n bodyItems.push({\n type: \"Container\",\n items: failedItems,\n });\n }\n\n // CI info facts\n if (summary.ci) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ciFacts: any[] = [];\n\n if (summary.ci.displayName) {\n ciFacts.push({ title: \"CI\", value: summary.ci.displayName });\n }\n if (summary.ci.branch) {\n ciFacts.push({ title: \"Branch\", value: summary.ci.branch });\n }\n if (summary.ci.commitSha) {\n ciFacts.push({ title: \"Commit\", value: summary.ci.commitSha.slice(0, 7) });\n }\n if (summary.ci.buildNumber) {\n ciFacts.push({ title: \"Build\", value: `#${summary.ci.buildNumber}` });\n }\n\n if (ciFacts.length > 0) {\n bodyItems.push({\n type: \"FactSet\",\n facts: ciFacts,\n separator: true,\n });\n }\n }\n\n // Build the Adaptive Card\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const card: Record<string, any> = {\n type: \"AdaptiveCard\",\n $schema: \"http://adaptivecards.io/schemas/adaptive-card.json\",\n version: \"1.4\",\n body: bodyItems,\n };\n\n // \"View Report\" action\n if (summary.reportUrl) {\n card.actions = [\n {\n type: \"Action.OpenUrl\",\n title: \"View Report\",\n url: summary.reportUrl,\n },\n ];\n }\n\n return {\n type: \"message\",\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: card,\n },\n ],\n };\n}\n\n/**\n * Send a Teams notification via incoming webhook.\n *\n * Never throws. Returns `{ ok, error? }`.\n * Never logs the webhook URL.\n */\nexport async function sendTeamsNotification(\n args: TeamsNotificationArgs,\n deps: TeamsNotificationDeps,\n): Promise<TeamsNotificationResult> {\n const { summary, webhookUrl, maxFailedTests = 5 } = args;\n const { fetch, logger } = deps;\n\n const payload = buildTeamsPayload(summary, maxFailedTests);\n\n try {\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const requestId = response.headers.get(\"x-request-id\") ?? undefined;\n let bodyText = \"\";\n try {\n bodyText = await response.text();\n } catch {\n // ignore body read errors\n }\n const truncatedBody = truncate(bodyText, 200);\n const idPart = requestId ? ` x-request-id=${requestId}` : \"\";\n const errorMsg = `Teams notifier failed: HTTP ${response.status}${idPart} ${truncatedBody}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const errorMsg = `Teams notifier failed: ${msg}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n}\n","/**\n * HMAC-SHA256 signing for generic webhook payloads.\n *\n * Pure function, isolated for testing.\n * Uses node:crypto — zero new dependencies.\n */\n\nimport { createHmac } from \"node:crypto\";\n\n/** Result of HMAC signing. */\nexport interface HmacSignResult {\n /** Signature in format \"sha256=<hex>\" (GitHub-style, widely recognized) */\n signature: string;\n /** ISO 8601 timestamp, only present when includeTimestamp is true */\n timestamp?: string;\n}\n\n/**\n * Compute HMAC-SHA256 signature for a request body.\n *\n * When `includeTimestamp` is true, the signed input is `\"<timestamp>.<body>\"` and the\n * timestamp is returned for the caller to emit as a header.\n */\nexport function signBody(args: {\n body: string;\n secret: string;\n includeTimestamp?: boolean;\n /** Injectable for deterministic testing */\n timestamp?: string;\n}): HmacSignResult {\n let input: string;\n let timestamp: string | undefined;\n\n if (args.includeTimestamp) {\n timestamp = args.timestamp ?? new Date().toISOString();\n input = `${timestamp}.${args.body}`;\n } else {\n input = args.body;\n }\n\n const hex = createHmac(\"sha256\", args.secret)\n .update(input, \"utf8\")\n .digest(\"hex\");\n\n return {\n signature: `sha256=${hex}`,\n timestamp,\n };\n}\n","/**\n * Generic webhook notifier.\n *\n * Sends a versioned JSON envelope to arbitrary HTTP endpoints with optional\n * HMAC-SHA256 signing. Follows the fn(args, deps) pattern.\n *\n * Never throws. Never logs the webhook URL.\n */\n\nimport type { NotificationSummary } from \"./types\";\nimport type { GenericWebhookNotifierOptions, WebhookPayload } from \"./types\";\nimport { signBody } from \"./hmac\";\n\n/** Arguments for sendWebhookNotification. */\nexport interface WebhookNotificationArgs {\n summary: NotificationSummary;\n options: GenericWebhookNotifierOptions;\n maxFailedTests?: number;\n}\n\n/** Injectable dependencies for sendWebhookNotification. */\nexport interface WebhookNotificationDeps {\n fetch: typeof globalThis.fetch;\n logger: { warn(msg: string): void };\n}\n\n/** Result of a webhook send attempt. */\nexport interface WebhookNotificationResult {\n ok: boolean;\n error?: string;\n}\n\n/**\n * Send a notification to a generic webhook endpoint.\n *\n * Never throws. Returns `{ ok, error? }`.\n * Never logs the webhook URL.\n */\nexport async function sendWebhookNotification(\n args: WebhookNotificationArgs,\n deps: WebhookNotificationDeps,\n): Promise<WebhookNotificationResult> {\n const { summary, options } = args;\n const { fetch, logger } = deps;\n\n // Build versioned envelope\n const payload: WebhookPayload = {\n schemaVersion: 1,\n event: \"test_run_finished\",\n summary,\n };\n const body = JSON.stringify(payload);\n\n // Build headers\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n\n // Apply user-provided headers (can override Content-Type)\n if (options.headers) {\n for (const [key, value] of Object.entries(options.headers)) {\n headers[key] = value;\n }\n }\n\n // HMAC signing — headers always override user-supplied values\n if (options.signer) {\n const { secret, header, includeTimestamp, timestampHeader } = options.signer;\n const result = signBody({ body, secret, includeTimestamp });\n headers[header] = result.signature;\n if (result.timestamp) {\n headers[timestampHeader ?? \"X-Timestamp\"] = result.timestamp;\n }\n }\n\n try {\n const response = await fetch(options.url, {\n method: options.method ?? \"POST\",\n headers,\n body,\n });\n\n if (!response.ok) {\n const requestId = response.headers.get(\"x-request-id\") ?? undefined;\n let snippet = \"\";\n try {\n snippet = (await response.text()).slice(0, 200);\n } catch {\n /* ignore */\n }\n const idPart = requestId ? ` x-request-id=${requestId}` : \"\";\n const errorMsg = `webhook: HTTP ${response.status}${idPart} ${snippet}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const errorMsg = `webhook: ${msg}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n}\n","/**\n * Notification orchestrator.\n *\n * Builds a NotificationSummary from a TestRunResult and dispatches\n * to configured notifiers (Slack, Teams, generic webhooks) based on condition.\n *\n * Env fallback and defaults are resolved internally so CLI and reporters\n * behave identically with zero duplication.\n *\n * Follows the fn(args, deps) pattern.\n * Never throws. Never logs webhook URLs.\n */\n\nimport type { TestRunResult } from \"../types/test-result\";\nimport type { CIInfo } from \"../types/ci\";\nimport type { RawCIInfo } from \"../types/raw\";\nimport type { NotificationSummary, NotifyCondition, GenericWebhookNotifierOptions } from \"./types\";\nimport { sendSlackNotification } from \"./slack\";\nimport { sendTeamsNotification } from \"./teams\";\nimport { sendWebhookNotification } from \"./webhook\";\n\n/** Arguments for sendNotifications. */\nexport interface SendNotificationsArgs {\n run: TestRunResult;\n /** Notification config from FormatterOptions.notification (or CLI-assembled equivalent) */\n notification?: {\n slackWebhookUrl?: string;\n teamsWebhookUrl?: string;\n condition?: NotifyCondition;\n reportUrl?: string;\n maxFailedTests?: number;\n webhooks?: GenericWebhookNotifierOptions[];\n };\n}\n\n/** Injectable dependencies for sendNotifications. */\nexport interface SendNotificationsDeps {\n fetch?: typeof globalThis.fetch;\n logger: { warn(msg: string): void };\n toCIInfo: (raw?: RawCIInfo) => CIInfo | undefined;\n env?: Record<string, string | undefined>;\n}\n\n/** Build a NotificationSummary from a TestRunResult. */\nfunction buildSummary(\n run: TestRunResult,\n reportUrl: string | undefined,\n toCIInfo: SendNotificationsDeps[\"toCIInfo\"],\n): NotificationSummary {\n let passed = 0;\n let failed = 0;\n let skipped = 0;\n\n const failedTests: NotificationSummary[\"failedTests\"] = [];\n\n for (const tc of run.testCases) {\n switch (tc.status) {\n case \"passed\":\n passed++;\n break;\n case \"failed\":\n failed++;\n failedTests.push({\n testId: tc.id,\n name: tc.story.scenario,\n error: tc.errorMessage,\n });\n break;\n case \"skipped\":\n case \"pending\":\n skipped++;\n break;\n }\n }\n\n // Derive CI info: use typed CIInfo from run.ci if available\n // run.ci is the legacy CIInfo shape { name, url?, buildNumber? }\n // Cast to RawCIInfo to pass through the converter\n let ci: CIInfo | undefined;\n if (run.ci) {\n ci = toCIInfo(run.ci as unknown as RawCIInfo);\n }\n\n return {\n total: run.testCases.length,\n passed,\n failed,\n skipped,\n durationMs: run.durationMs,\n failedTests,\n ci,\n reportUrl,\n };\n}\n\n/** Check if a notifier should fire given condition and failure count. */\nfunction shouldNotify(condition: NotifyCondition, failedCount: number): boolean {\n if (condition === \"never\") return false;\n if (condition === \"on-failure\" && failedCount === 0) return false;\n return true;\n}\n\n/**\n * Send notifications to all configured channels.\n *\n * Resolves env fallbacks and defaults internally.\n * Never throws. Logs warnings for failures.\n * Never logs webhook URLs.\n */\nexport async function sendNotifications(\n args: SendNotificationsArgs,\n deps: SendNotificationsDeps,\n): Promise<void> {\n const { run, notification } = args;\n const { logger, toCIInfo } = deps;\n const env = deps.env ?? process.env;\n\n // Guard: if fetch is unavailable, warn and bail\n if (!deps.fetch) {\n logger.warn(\"notifications: skipped (fetch unavailable)\");\n return;\n }\n const fetch = deps.fetch;\n\n // Resolve env fallbacks + defaults\n const slackWebhookUrl = notification?.slackWebhookUrl ?? env.SLACK_WEBHOOK_URL;\n const teamsWebhookUrl = notification?.teamsWebhookUrl ?? env.TEAMS_WEBHOOK_URL;\n const globalCondition: NotifyCondition = notification?.condition ?? \"on-failure\";\n const reportUrl = notification?.reportUrl;\n const maxFailedTests = notification?.maxFailedTests ?? 5;\n const webhooks = notification?.webhooks ?? [];\n\n // Nothing configured → early return\n if (!slackWebhookUrl && !teamsWebhookUrl && webhooks.length === 0) {\n return;\n }\n\n const summary = buildSummary(run, reportUrl, toCIInfo);\n\n // Dispatch to configured notifiers concurrently\n const promises: Promise<void>[] = [];\n\n if (slackWebhookUrl && shouldNotify(globalCondition, summary.failed)) {\n promises.push(\n sendSlackNotification(\n { summary, webhookUrl: slackWebhookUrl, maxFailedTests },\n { fetch, logger },\n ).then(() => undefined),\n );\n }\n\n if (teamsWebhookUrl && shouldNotify(globalCondition, summary.failed)) {\n promises.push(\n sendTeamsNotification(\n { summary, webhookUrl: teamsWebhookUrl, maxFailedTests },\n { fetch, logger },\n ).then(() => undefined),\n );\n }\n\n // Generic webhooks — per-webhook condition override\n for (const webhook of webhooks) {\n const effectiveCondition = webhook.condition ?? globalCondition;\n if (!shouldNotify(effectiveCondition, summary.failed)) continue;\n\n promises.push(\n sendWebhookNotification(\n { summary, options: webhook, maxFailedTests },\n { fetch, logger },\n ).then(() => undefined),\n );\n }\n\n // Wait for all, never throw\n await Promise.allSettled(promises);\n}\n","/**\n * Typed CI provider and canonical CI info.\n *\n * RawCIInfo.name = legacy transport string (kept for backward compat + schema).\n * CIInfo.displayName = canonical display name for downstream consumers.\n *\n * All downstream code (HTML meta, notifications, history) uses CIInfo via mappers.\n */\n\nimport type { RawCIInfo } from \"./raw\";\n\nexport type CIProvider =\n | \"github\"\n | \"gitlab\"\n | \"circleci\"\n | \"jenkins\"\n | \"azure\"\n | \"buildkite\"\n | \"travis\"\n | \"unknown\";\n\nexport interface CIInfo {\n provider: CIProvider;\n displayName: string;\n url?: string;\n buildNumber?: string;\n branch?: string;\n commitSha?: string;\n prNumber?: string;\n}\n\nconst DISPLAY_NAMES: Record<CIProvider, string> = {\n github: \"GitHub Actions\",\n gitlab: \"GitLab CI\",\n circleci: \"CircleCI\",\n jenkins: \"Jenkins\",\n azure: \"Azure DevOps\",\n buildkite: \"Buildkite\",\n travis: \"Travis CI\",\n unknown: \"CI\",\n};\n\nconst NAME_TO_PROVIDER: Record<string, CIProvider> = {\n github: \"github\",\n gitlab: \"gitlab\",\n circleci: \"circleci\",\n jenkins: \"jenkins\",\n azure: \"azure\",\n buildkite: \"buildkite\",\n travis: \"travis\",\n ci: \"unknown\",\n};\n\n/** Convert RawCIInfo (legacy transport) to canonical CIInfo. */\nexport function toCIInfo(raw?: RawCIInfo): CIInfo | undefined {\n if (!raw) return undefined;\n\n const provider: CIProvider =\n raw.provider ?? NAME_TO_PROVIDER[raw.name] ?? \"unknown\";\n\n return {\n provider,\n displayName: DISPLAY_NAMES[provider],\n url: raw.url,\n buildNumber: raw.buildNumber,\n branch: raw.branch,\n commitSha: raw.commitSha,\n prNumber: raw.prNumber,\n };\n}\n\n/** Convert canonical CIInfo back to RawCIInfo (for serialization). */\nexport function toRawCIInfo(ci?: CIInfo): RawCIInfo | undefined {\n if (!ci) return undefined;\n\n return {\n name: ci.provider === \"unknown\" ? \"ci\" : ci.provider,\n provider: ci.provider,\n url: ci.url,\n buildNumber: ci.buildNumber,\n branch: ci.branch,\n commitSha: ci.commitSha,\n prNumber: ci.prNumber,\n };\n}\n","/**\n * History store: load, save, update (fn(args, deps) pattern).\n */\n\nimport type { TestRunResult } from \"../types/test-result\";\nimport type { HistoryEntry, HistoryStore, TestHistory } from \"./types\";\n\n// ============================================================================\n// Load\n// ============================================================================\n\nexport interface LoadHistoryArgs {\n filePath: string;\n}\n\nexport interface LoadHistoryDeps {\n readFile: (path: string) => string | undefined;\n logger: { warn(msg: string): void };\n}\n\nfunction emptyStore(): HistoryStore {\n return { version: 1, maxRuns: 10, tests: {}, lastUpdated: 0 };\n}\n\nexport function loadHistory(args: LoadHistoryArgs, deps: LoadHistoryDeps): HistoryStore {\n const content = deps.readFile(args.filePath);\n if (content === undefined) {\n return emptyStore();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n deps.logger.warn(`Failed to parse history file: ${args.filePath}`);\n return emptyStore();\n }\n\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as Record<string, unknown>).version !== 1\n ) {\n deps.logger.warn(\n `Unknown history version in ${args.filePath}, expected version 1`,\n );\n return emptyStore();\n }\n\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.tests !== \"object\" || obj.tests === null || Array.isArray(obj.tests)) {\n deps.logger.warn(\n `Malformed history store in ${args.filePath}: tests must be a non-null object`,\n );\n return emptyStore();\n }\n\n return parsed as HistoryStore;\n}\n\n// ============================================================================\n// Save\n// ============================================================================\n\nexport interface SaveHistoryArgs {\n filePath: string;\n store: HistoryStore;\n}\n\nexport interface SaveHistoryDeps {\n writeFile: (path: string, content: string) => void;\n}\n\nexport function saveHistory(args: SaveHistoryArgs, deps: SaveHistoryDeps): void {\n deps.writeFile(args.filePath, JSON.stringify(args.store, null, 2));\n}\n\n// ============================================================================\n// Update (pure function, no deps)\n// ============================================================================\n\nexport interface UpdateHistoryArgs {\n store: HistoryStore;\n run: TestRunResult;\n maxRuns: number;\n}\n\nexport function updateHistory(args: UpdateHistoryArgs): HistoryStore {\n const { store, run, maxRuns } = args;\n const newTests: Record<string, TestHistory> = { ...store.tests };\n\n for (const tc of run.testCases) {\n const entry: HistoryEntry = {\n runId: run.runId,\n timestamp: run.startedAtMs,\n status: tc.status,\n durationMs: tc.durationMs,\n ci: run.ci\n ? {\n provider: undefined,\n branch: run.ci.branch,\n commitSha: run.ci.commitSha,\n }\n : undefined,\n };\n\n const existing = newTests[tc.id];\n if (existing) {\n const updatedEntries = [...existing.entries, entry];\n // Trim per-test entries to maxRuns (keep latest)\n const trimmed =\n updatedEntries.length > maxRuns\n ? updatedEntries.slice(updatedEntries.length - maxRuns)\n : updatedEntries;\n newTests[tc.id] = {\n ...existing,\n testName: tc.story.scenario,\n sourceFile: tc.sourceFile,\n sourceLine: tc.sourceLine,\n entries: trimmed,\n };\n } else {\n newTests[tc.id] = {\n testId: tc.id,\n testName: tc.story.scenario,\n sourceFile: tc.sourceFile,\n sourceLine: tc.sourceLine,\n entries: [entry],\n };\n }\n }\n\n return {\n version: 1,\n maxRuns,\n tests: newTests,\n lastUpdated: Date.now(),\n };\n}\n","/**\n * List scenarios from a test run (fn(args, deps) pattern).\n * Produces text table or JSON output.\n */\n\nimport type { TestCaseResult } from \"./types/test-result\";\n\nexport interface ListScenariosArgs {\n testCases: TestCaseResult[];\n format: \"text\" | \"json\";\n}\n\nexport type ListScenariosDeps = Record<string, never>;\n\nconst STATUS_ICONS: Record<string, string> = {\n passed: \"\\u2705\",\n failed: \"\\u274C\",\n skipped: \"\\u23ED\\uFE0F\",\n pending: \"\\u23F3\",\n};\n\nexport function listScenarios(\n args: ListScenariosArgs,\n _deps: ListScenariosDeps,\n): string {\n const { testCases, format } = args;\n\n if (format === \"json\") {\n const items = testCases.map((tc) => ({\n scenario: tc.story.scenario,\n status: tc.status,\n sourceFile: tc.sourceFile,\n sourceLine: tc.sourceLine,\n tags: tc.tags,\n id: tc.id,\n }));\n return JSON.stringify(items, null, 2);\n }\n\n // Text format\n if (testCases.length === 0) {\n return \"No scenarios found.\";\n }\n\n const sorted = [...testCases].sort((a, b) => {\n const rank = { failed: 0, pending: 1, skipped: 2, passed: 3 };\n if (rank[a.status] !== rank[b.status]) {\n return rank[a.status] - rank[b.status];\n }\n if (a.sourceFile !== b.sourceFile) {\n return a.sourceFile.localeCompare(b.sourceFile);\n }\n if (a.sourceLine !== b.sourceLine) {\n return a.sourceLine - b.sourceLine;\n }\n return a.story.scenario.localeCompare(b.story.scenario);\n });\n\n const summary = {\n total: sorted.length,\n failed: sorted.filter((tc) => tc.status === \"failed\").length,\n pending: sorted.filter((tc) => tc.status === \"pending\").length,\n skipped: sorted.filter((tc) => tc.status === \"skipped\").length,\n passed: sorted.filter((tc) => tc.status === \"passed\").length,\n };\n\n const lines = [\n `Review summary: ${summary.failed} failed, ${summary.pending} pending, ${summary.skipped} skipped, ${summary.passed} passed (${summary.total} total)`,\n summary.failed > 0\n ? \"Priority: failed scenarios are listed first for review.\"\n : \"Priority: no failed scenarios detected.\",\n \"\",\n ...sorted.map((tc) => {\n const icon = STATUS_ICONS[tc.status] ?? \"?\";\n const status = tc.status.padEnd(7);\n const scenario = tc.story.scenario;\n const location = `${tc.sourceFile}:${tc.sourceLine}`;\n const tags = tc.tags.length > 0 ? tc.tags.join(\", \") : \"\";\n return `${icon} ${status} ${scenario} ${location}${tags ? ` ${tags}` : \"\"}`;\n }),\n ];\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA82DtB,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4Dd,IAAM,iBAA4B;AAAA,EACvC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,WAAW;AAAA,EACX,cAAc;AAChB;;;ACjlEO,IAAM,gBAA2B;AAAA,EACtC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuqDP;;;ACvqDO,IAAM,eAA0B;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2rDtB,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqDd,IAAM,iBAA4B;AAAA,EACvC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,WAAW;AAAA,EACX,cAAc;AAChB;;;ACn5DO,IAAM,eAA0B;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["fs","path","path","path","groupBy","main","escapeHtml","escapeHtml","groupBy","groupBy","groupBy","groupBy","basename","createHash","path","resolve","flags","escapeHtml","renderScenario","formatSteps","formatDocs","formatStep","formatDocEntry","fs","path","fs","path","extractFeatureName","formatDuration","truncate","formatDuration","toCIInfo","resolve","text","obj"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/validation/schema-validator.ts","../schemas/raw-run.schema.json","../src/converters/synthesize.ts","../src/converters/acl/status.ts","../src/converters/acl/ids.ts","../src/converters/acl/steps.ts","../src/converters/acl/attachments.ts","../src/converters/acl/index.ts","../src/converters/acl/validate.ts","../src/index.ts","../src/converters/acl/lines.ts","../src/formatters/cucumber-json.ts","../src/formatters/html/template.ts","../src/formatters/html/styles.ts","../src/formatters/html/themes/default.ts","../src/formatters/html/themes/corporate.ts","../src/formatters/html/themes/terminal.ts","../src/formatters/html/themes/minimal.ts","../src/formatters/html/themes/dashboard.ts","../src/formatters/html/themes/playful.ts","../src/formatters/html/themes/index.ts","../src/formatters/html/renderers/status.ts","../src/formatters/html/renderers/meta.ts","../src/formatters/html/renderers/summary.ts","../src/formatters/html/renderers/tag-bar.ts","../src/formatters/html/renderers/error-box.ts","../src/formatters/html/renderers/attachments.ts","../src/formatters/html/renderers/doc-entries.ts","../src/formatters/html/renderers/steps.ts","../src/formatters/html/renderers/step-params.ts","../src/history/sample-policy.ts","../src/formatters/html/renderers/scenario.ts","../src/formatters/html/renderers/trace-view.ts","../src/formatters/html/renderers/feature.ts","../src/formatters/html/renderers/body.ts","../src/formatters/html/renderers/failure-summary.ts","../src/formatters/html/renderers/toc.ts","../src/formatters/html/renderers/index.ts","../src/formatters/html/index.ts","../src/formatters/junit-xml.ts","../src/formatters/markdown.ts","../src/formatters/cucumber-messages/synthesize-feature.ts","../src/utils/cucumber-messages.ts","../src/formatters/cucumber-messages/build-gherkin-document.ts","../src/formatters/cucumber-messages/build-pickles.ts","../src/formatters/cucumber-messages/build-execution.ts","../src/formatters/cucumber-messages/index.ts","../src/formatters/cucumber-html.ts","../src/types/compare.ts","../src/compare/pr-summary.ts","../src/compare/auto-baseline.ts","../src/compare/index.ts","../src/formatters/run-diff-html.ts","../src/formatters/run-diff-markdown.ts","../src/select-test-cases.ts","../src/bundler/bundle-assets.ts","../src/bundler/scan-html-assets.ts","../src/bundler/copy-asset.ts","../src/formatters/astro.ts","../src/formatters/astro-assets.ts","../src/converters/ndjson-parser.ts","../src/notifiers/ansi-strip.ts","../src/notifiers/slack.ts","../src/notifiers/teams.ts","../src/notifiers/hmac.ts","../src/notifiers/webhook.ts","../src/notifiers/index.ts","../src/types/ci.ts","../src/history/history-store.ts","../src/list-scenarios.ts","../src/init-astro.ts"],"sourcesContent":["/**\n * executable-stories CLI\n *\n * Reads raw test results as JSON and generates reports.\n *\n * Usage:\n * executable-stories format run.json --format html,markdown\n * executable-stories format --stdin --format html\n * executable-stories validate run.json\n */\n\nimport { parseArgs } from \"node:util\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { validateRawRun } from \"./validation/schema-validator\";\nimport { synthesizeStories } from \"./converters/synthesize\";\nimport { canonicalizeRun } from \"./converters/acl\";\nimport { assertValidRun } from \"./converters/acl/validate\";\n// eslint-disable-next-line no-restricted-imports -- ReportGenerator and compare helpers currently live in the package entrypoint.\nimport {\n ReportGenerator,\n createPrCommentSummary,\n generateRunComparison,\n} from \"./index.js\";\nimport { parseNdjson } from \"./converters/ndjson-parser\";\nimport type { OutputFormat } from \"./types/options\";\nimport { sendNotifications } from \"./notifiers\";\nimport { toCIInfo } from \"./types/ci\";\nimport type { NotifyCondition, GenericWebhookNotifierOptions, WebhookSignerHmac } from \"./notifiers/types\";\nimport { loadHistory, saveHistory, updateHistory } from \"./history\";\nimport { pickAutoBaseline } from \"./compare/auto-baseline\";\nimport { listScenarios } from \"./list-scenarios\";\nimport { selectTestCases } from \"./select-test-cases\";\nimport type { RawRun } from \"./types/raw\";\nimport type { TestRunResult } from \"./types/test-result\";\nimport { initAstro as initAstroFn } from \"./init-astro\";\n\n// ============================================================================\n// Exit Codes\n// ============================================================================\n\nconst EXIT_SUCCESS = 0;\nconst EXIT_SCHEMA_VALIDATION = 1;\nconst EXIT_CANONICAL_VALIDATION = 2;\nconst EXIT_GENERATION = 3;\nconst EXIT_USAGE = 4;\n\n// ============================================================================\n// CLI Argument Parsing\n// ============================================================================\n\nconst HELP_TEXT = `\nexecutable-stories — Generate reports from test results JSON.\n\nUSAGE\n executable-stories format <file> [options]\n executable-stories format --stdin [options]\n executable-stories compare <baseline-file> <current-file> [options]\n executable-stories list <file> [options]\n executable-stories validate <file>\n executable-stories validate --stdin\n executable-stories init-astro [directory]\n\nSUBCOMMANDS\n format Read raw test results and generate reports\n compare Compare two runs and generate a diff report\n list List scenarios from a test run (text table or JSON)\n validate Validate a JSON file against the schema (no output generated)\n init-astro Scaffold an Astro Starlight docs site for story output\n\nOPTIONS\n --format <formats> Comma-separated formats (default: html)\n astro Starlight-compatible Markdown (for Astro docs sites)\n html Custom HTML report (accessible, dark mode, mermaid)\n cucumber-html Official Cucumber HTML report\n markdown Markdown documentation\n junit JUnit XML\n cucumber-json Cucumber JSON\n cucumber-messages Raw NDJSON (Cucumber Messages)\n --input-type <type> Input type: raw, canonical, or ndjson (default: raw)\n --output-dir <dir> Output directory (default: reports)\n --output-name <name> Base filename (default: index)\n --output-name-timestamp Append run timestamp (UTC seconds) to output filename for before/after diffs\n --sort-test-cases <mode> Sort scenarios deterministically: id, source, none (default: none)\n --include <globs> Comma-separated globs to include test cases by sourceFile (e.g. \"**/*.Story*.cs\")\n --exclude <globs> Comma-separated globs to exclude test cases by sourceFile (e.g. \"**/obj/**\")\n --include-tags <tags> Comma-separated tags to include test cases (any match)\n --exclude-tags <tags> Comma-separated tags to exclude test cases (any match)\n --synthesize-stories Synthesize story metadata for plain test results (default)\n --no-synthesize-stories Disable story synthesis (strict mode)\n --html-title <title> HTML report title (default: Test Results)\n --html-theme <name> HTML theme (default, corporate, terminal, minimal, dashboard, playful)\n --html-no-syntax-highlighting Disable syntax highlighting in HTML (enabled by default)\n --html-no-mermaid Disable mermaid diagrams in HTML (enabled by default)\n --html-no-markdown Disable markdown parsing in HTML (enabled by default)\n --html-permalink-base-url <url> Base URL for source permalinks in HTML (e.g. \"https://github.com/org/repo/blob/main\")\n --html-no-toc Disable table of contents sidebar in HTML (enabled by default)\n --html-theme-picker Include theme picker in HTML report (embeds all CSS-only themes)\n --html-ticket-url-template <url> URL template for ticket links in HTML (use {ticket} as placeholder)\n --asset-mode <mode> Asset bundling: \"none\" (default) or \"copy\"\n --allow-missing-assets Warn on missing assets instead of failing\n --stdin Read JSON from stdin instead of file\n --json-summary Print machine-parsable JSON summary\n --baseline <path|auto> Compare baseline file, or auto-pick a prior run for compare\n --baseline-dir <dir> Directory to scan when --baseline auto is used\n --pr-summary Print a PR-friendly markdown summary after compare\n --pr-summary-file <path> Write the PR-friendly markdown summary to a file\n --emit-canonical <path> Write canonical JSON to given path\n --help Show this help message\n\nLIST\n list prints one scenario per line (text by default, JSON with --json-summary)\n list supports --include-tags, --exclude-tags for filtering\n list supports --input-type and --stdin\n\nCOMPARE\n compare supports --format html,markdown\n compare uses the same --input-type for both baseline and current files\n\nINIT-ASTRO\n executable-stories init-astro [directory] Scaffold into directory (default: ./story-docs)\n --force Overwrite existing directory\n\nNOTIFICATIONS\n --slack-webhook <url> Slack incoming webhook URL (fallback: SLACK_WEBHOOK_URL env var)\n --teams-webhook <url> Teams incoming webhook URL (fallback: TEAMS_WEBHOOK_URL env var)\n --notify <condition> When to send: always, on-failure, never (default: on-failure)\n --report-url <url> URL to link in notification messages\n --max-failed-tests <n> Max failed tests to show in notifications (default: 5)\n\nGENERIC WEBHOOK\n --webhook-url <url> Generic webhook URL (repeatable for multiple endpoints)\n --webhook-header <Key: Value> Custom request header (repeatable)\n --webhook-method <POST|PUT> HTTP method (default: POST)\n --webhook-hmac-secret <s> HMAC-SHA256 signing secret\n --webhook-hmac-header <name> Signature header name (default: X-Signature)\n --webhook-hmac-timestamp Include timestamp in HMAC signing\n Note: all --webhook-url entries share the same method/headers/signing options.\n\nHISTORY\n --history-file <path> Path to JSON history file (enables tracking)\n --max-history-runs <n> Max runs to keep in history per test (default: 10)\n\nEXIT CODES\n 0 Success\n 1 Schema validation failure\n 2 Canonical validation failure\n 3 Formatter/generation failure\n 4 Bad arguments / usage error\n`.trim();\n\ninterface CliArgs {\n subcommand: \"format\" | \"compare\" | \"list\" | \"validate\";\n inputFile?: string;\n baselineFile?: string;\n currentFile?: string;\n baselineMode: \"explicit\" | \"auto\";\n baselineDir?: string;\n stdin: boolean;\n formats: OutputFormat[];\n inputType: \"raw\" | \"canonical\" | \"ndjson\";\n outputDir: string;\n outputName: string;\n outputNameTimestamp: boolean;\n sortTestCases: \"id\" | \"source\" | \"none\";\n include: string[];\n exclude: string[];\n includeTags: string[];\n excludeTags: string[];\n synthesizeStories: boolean;\n htmlTitle: string;\n htmlTheme: string;\n htmlNoSyntaxHighlighting: boolean;\n htmlNoMermaid: boolean;\n htmlNoMarkdown: boolean;\n htmlPermalinkBaseUrl?: string;\n htmlTicketUrlTemplate?: string;\n htmlNoToc: boolean;\n htmlThemePicker: boolean;\n jsonSummary: boolean;\n emitCanonical?: string;\n slackWebhook?: string;\n teamsWebhook?: string;\n notify: NotifyCondition;\n reportUrl?: string;\n maxFailedTests: number;\n historyFile?: string;\n maxHistoryRuns: number;\n webhookUrls: string[];\n webhookHeaders: Record<string, string>;\n webhookMethod: \"POST\" | \"PUT\";\n webhookHmacSecret?: string;\n webhookHmacHeader: string;\n webhookHmacTimestamp: boolean;\n assetMode: \"none\" | \"copy\";\n allowMissingAssets: boolean;\n prSummary: boolean;\n prSummaryFile?: string;\n}\n\nfunction parseCliArgs(argv: string[]): CliArgs {\n // Strip node + script path\n const args = argv.slice(2);\n\n if (args.length === 0 || args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(HELP_TEXT);\n process.exit(EXIT_SUCCESS);\n }\n\n const subcommand = args[0];\n if (subcommand !== \"format\" && subcommand !== \"compare\" && subcommand !== \"list\" && subcommand !== \"validate\" && subcommand !== \"init-astro\") {\n console.error(`Unknown subcommand: \"${subcommand}\". Use \"format\", \"compare\", \"list\", \"validate\", or \"init-astro\".`);\n process.exit(EXIT_USAGE);\n }\n\n // Handle init-astro early (no parseArgs needed)\n if (subcommand === \"init-astro\") {\n const initArgs = args.slice(1);\n const targetDir = initArgs.find((a) => !a.startsWith(\"--\")) ?? \"./story-docs\";\n const force = initArgs.includes(\"--force\");\n\n try {\n const result = initAstroFn({ targetDir, force });\n console.log(`Scaffolded Astro Starlight project at ${result.targetDir}`);\n console.log(\"\");\n console.log(\"Next steps:\");\n console.log(` cd ${result.targetDir}`);\n console.log(\" pnpm install # or npm install\");\n console.log(\" pnpm dev # start the dev server\");\n console.log(\"\");\n console.log(\"Generate story docs with:\");\n console.log(` executable-stories format run.json --format astro --output-dir ${result.targetDir}/src/content/docs/stories --asset-mode copy`);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n console.error(`Error: ${(err as Error).message}`);\n process.exit(EXIT_USAGE);\n }\n }\n\n // Parse remaining args with node:util parseArgs\n const { values, positionals } = parseArgs({\n args: args.slice(1),\n options: {\n format: { type: \"string\", default: \"html\" },\n baseline: { type: \"string\" },\n \"baseline-dir\": { type: \"string\" },\n \"input-type\": { type: \"string\", default: \"raw\" },\n \"output-dir\": { type: \"string\", default: \"reports\" },\n \"output-name\": { type: \"string\", default: \"index\" },\n \"output-name-timestamp\": { type: \"boolean\", default: false },\n \"sort-test-cases\": { type: \"string\", default: \"none\" },\n include: { type: \"string\" },\n exclude: { type: \"string\" },\n \"include-tags\": { type: \"string\" },\n \"exclude-tags\": { type: \"string\" },\n \"synthesize-stories\": { type: \"boolean\", default: true },\n \"no-synthesize-stories\": { type: \"boolean\", default: false },\n \"html-title\": { type: \"string\", default: \"Test Results\" },\n \"html-theme\": { type: \"string\", default: \"default\" },\n \"html-no-syntax-highlighting\": { type: \"boolean\", default: false },\n \"html-no-mermaid\": { type: \"boolean\", default: false },\n \"html-no-markdown\": { type: \"boolean\", default: false },\n \"html-permalink-base-url\": { type: \"string\" },\n \"html-ticket-url-template\": { type: \"string\" },\n \"html-no-toc\": { type: \"boolean\", default: false },\n \"html-theme-picker\": { type: \"boolean\", default: false },\n stdin: { type: \"boolean\", default: false },\n \"json-summary\": { type: \"boolean\", default: false },\n \"emit-canonical\": { type: \"string\" },\n \"slack-webhook\": { type: \"string\" },\n \"teams-webhook\": { type: \"string\" },\n notify: { type: \"string\", default: \"on-failure\" },\n \"report-url\": { type: \"string\" },\n \"max-failed-tests\": { type: \"string\" },\n \"history-file\": { type: \"string\" },\n \"max-history-runs\": { type: \"string\" },\n \"webhook-url\": { type: \"string\", multiple: true },\n \"webhook-header\": { type: \"string\", multiple: true },\n \"webhook-method\": { type: \"string\" },\n \"webhook-hmac-secret\": { type: \"string\" },\n \"webhook-hmac-header\": { type: \"string\" },\n \"webhook-hmac-timestamp\": { type: \"boolean\", default: false },\n \"asset-mode\": { type: \"string\", default: \"none\" },\n \"allow-missing-assets\": { type: \"boolean\", default: false },\n \"pr-summary\": { type: \"boolean\", default: false },\n \"pr-summary-file\": { type: \"string\" },\n help: { type: \"boolean\", default: false },\n },\n allowPositionals: true,\n strict: true,\n });\n\n if (values.help) {\n console.log(HELP_TEXT);\n process.exit(EXIT_SUCCESS);\n }\n\n const useStdin = values.stdin as boolean;\n const baselineValue = values.baseline as string | undefined;\n const baselineMode = baselineValue === \"auto\" ? \"auto\" : \"explicit\";\n const inputFile = subcommand === \"compare\" ? undefined : positionals[0];\n const baselineFile =\n subcommand === \"compare\"\n ? baselineMode === \"auto\"\n ? baselineValue && baselineValue !== \"auto\"\n ? baselineValue\n : positionals.length > 1\n ? positionals[0]\n : undefined\n : baselineValue && baselineValue !== \"auto\"\n ? baselineValue\n : positionals.length > 1\n ? positionals[0]\n : undefined\n : undefined;\n const currentFile =\n subcommand === \"compare\"\n ? positionals.length > 1\n ? positionals[1]\n : positionals[0]\n : undefined;\n\n if (subcommand === \"compare\") {\n if (useStdin) {\n console.error(\"Error: compare does not support --stdin. Pass baseline and current files.\");\n process.exit(EXIT_USAGE);\n }\n if (!currentFile) {\n console.error(\"Error: compare requires <current-file>, and either <baseline-file> or --baseline auto.\");\n process.exit(EXIT_USAGE);\n }\n if (baselineMode === \"explicit\" && !baselineFile) {\n console.error(\"Error: compare requires <baseline-file> and <current-file>, or use --baseline auto.\");\n process.exit(EXIT_USAGE);\n }\n } else if (!useStdin && !inputFile) {\n console.error(\"Error: No input file specified. Use a positional argument or --stdin.\");\n process.exit(EXIT_USAGE);\n }\n\n const inputType = values[\"input-type\"] as string;\n if (inputType !== \"raw\" && inputType !== \"canonical\" && inputType !== \"ndjson\") {\n console.error(`Error: --input-type must be \"raw\", \"canonical\", or \"ndjson\", got \"${inputType}\".`);\n process.exit(EXIT_USAGE);\n }\n\n // Parse comma-separated formats\n const validFormats = new Set([\"astro\", \"html\", \"markdown\", \"junit\", \"cucumber-json\", \"cucumber-messages\", \"cucumber-html\"]);\n const formatStr = values.format as string;\n const formats = formatStr.split(\",\").map((f) => f.trim()) as OutputFormat[];\n for (const f of formats) {\n if (!validFormats.has(f)) {\n console.error(`Error: Unknown format \"${f}\". Valid: astro, html, markdown, junit, cucumber-json, cucumber-messages, cucumber-html.`);\n process.exit(EXIT_USAGE);\n }\n }\n\n // Validate --html-theme\n const htmlTheme = values[\"html-theme\"] as string;\n const validThemes = new Set([\"default\", \"corporate\", \"terminal\", \"minimal\", \"dashboard\", \"playful\"]);\n if (!validThemes.has(htmlTheme)) {\n console.error(`Error: Unknown theme \"${htmlTheme}\". Valid: ${[...validThemes].join(\", \")}.`);\n process.exit(EXIT_USAGE);\n }\n\n const noSynthesize = values[\"no-synthesize-stories\"] as boolean;\n\n const parseGlobs = (v: string | undefined): string[] =>\n v ? v.split(\",\").map((s) => s.trim()).filter(Boolean) : [];\n\n // Validate --notify\n const notifyValue = values.notify as string;\n const validNotifyConditions = new Set([\"always\", \"on-failure\", \"never\"]);\n if (!validNotifyConditions.has(notifyValue)) {\n console.error(`Error: --notify must be \"always\", \"on-failure\", or \"never\", got \"${notifyValue}\".`);\n process.exit(EXIT_USAGE);\n }\n\n // Parse --max-failed-tests\n const maxFailedTestsStr = values[\"max-failed-tests\"] as string | undefined;\n const maxFailedTests = maxFailedTestsStr ? parseInt(maxFailedTestsStr, 10) : 5;\n if (maxFailedTestsStr && (isNaN(maxFailedTests) || maxFailedTests < 0)) {\n console.error(`Error: --max-failed-tests must be a non-negative integer, got \"${maxFailedTestsStr}\".`);\n process.exit(EXIT_USAGE);\n }\n\n // Slack/Teams webhook: CLI flag (env fallback is handled inside sendNotifications)\n const slackWebhook = values[\"slack-webhook\"] as string | undefined;\n const teamsWebhook = values[\"teams-webhook\"] as string | undefined;\n\n // Parse --webhook-url (repeatable)\n const webhookUrls = (values[\"webhook-url\"] as string[] | undefined) ?? [];\n\n // Parse --webhook-header (repeatable) \"Key: Value\"\n const webhookHeaders: Record<string, string> = {};\n const rawHeaders = (values[\"webhook-header\"] as string[] | undefined) ?? [];\n for (const h of rawHeaders) {\n const colonIdx = h.indexOf(\":\");\n if (colonIdx <= 0) {\n console.error(`Warning: ignoring invalid --webhook-header \"${h}\" (expected \"Key: Value\")`);\n continue;\n }\n const key = h.slice(0, colonIdx).trim();\n const value = h.slice(colonIdx + 1).trim();\n if (!key) {\n console.error(`Warning: ignoring --webhook-header with empty key`);\n continue;\n }\n webhookHeaders[key] = value;\n }\n\n // Parse --webhook-method\n const webhookMethodRaw = values[\"webhook-method\"] as string | undefined;\n let webhookMethod: \"POST\" | \"PUT\" = \"POST\";\n if (webhookMethodRaw) {\n const upper = webhookMethodRaw.toUpperCase();\n if (upper !== \"POST\" && upper !== \"PUT\") {\n console.error(`Error: --webhook-method must be \"POST\" or \"PUT\", got \"${webhookMethodRaw}\".`);\n process.exit(EXIT_USAGE);\n }\n webhookMethod = upper as \"POST\" | \"PUT\";\n }\n\n // Parse --max-history-runs\n const maxHistoryRunsStr = values[\"max-history-runs\"] as string | undefined;\n const maxHistoryRuns = maxHistoryRunsStr ? parseInt(maxHistoryRunsStr, 10) : 10;\n if (maxHistoryRunsStr && (isNaN(maxHistoryRuns) || maxHistoryRuns < 1)) {\n console.error(`Error: --max-history-runs must be a positive integer, got \"${maxHistoryRunsStr}\".`);\n process.exit(EXIT_USAGE);\n }\n\n const sortTestCasesRaw = values[\"sort-test-cases\"] as string;\n const validSortModes = new Set([\"id\", \"source\", \"none\"]);\n if (!validSortModes.has(sortTestCasesRaw)) {\n console.error(`Error: --sort-test-cases must be id, source, or none, got \"${sortTestCasesRaw}\".`);\n process.exit(EXIT_USAGE);\n }\n\n const assetModeRaw = values[\"asset-mode\"] as string;\n const validAssetModes = new Set([\"none\", \"copy\"]);\n if (!validAssetModes.has(assetModeRaw)) {\n console.error(`Error: --asset-mode must be \"none\" or \"copy\", got \"${assetModeRaw}\".`);\n process.exit(EXIT_USAGE);\n }\n\n return {\n subcommand: subcommand as \"format\" | \"compare\" | \"list\" | \"validate\",\n inputFile,\n baselineFile,\n currentFile,\n baselineMode,\n baselineDir: values[\"baseline-dir\"] as string | undefined,\n stdin: useStdin,\n formats,\n inputType: inputType as \"raw\" | \"canonical\" | \"ndjson\",\n outputDir: values[\"output-dir\"] as string,\n outputName: values[\"output-name\"] as string,\n outputNameTimestamp: values[\"output-name-timestamp\"] as boolean,\n sortTestCases: sortTestCasesRaw as \"id\" | \"source\" | \"none\",\n include: parseGlobs(values.include as string | undefined),\n exclude: parseGlobs(values.exclude as string | undefined),\n includeTags: parseGlobs(values[\"include-tags\"] as string | undefined),\n excludeTags: parseGlobs(values[\"exclude-tags\"] as string | undefined),\n synthesizeStories: !noSynthesize,\n htmlTitle: values[\"html-title\"] as string,\n htmlTheme: values[\"html-theme\"] as string,\n htmlNoSyntaxHighlighting: values[\"html-no-syntax-highlighting\"] as boolean,\n htmlNoMermaid: values[\"html-no-mermaid\"] as boolean,\n htmlNoMarkdown: values[\"html-no-markdown\"] as boolean,\n htmlPermalinkBaseUrl: values[\"html-permalink-base-url\"] as string | undefined,\n htmlTicketUrlTemplate: values[\"html-ticket-url-template\"] as string | undefined,\n htmlNoToc: values[\"html-no-toc\"] as boolean,\n htmlThemePicker: values[\"html-theme-picker\"] as boolean,\n jsonSummary: values[\"json-summary\"] as boolean,\n emitCanonical: values[\"emit-canonical\"] as string | undefined,\n slackWebhook,\n teamsWebhook,\n notify: notifyValue as NotifyCondition,\n reportUrl: values[\"report-url\"] as string | undefined,\n maxFailedTests,\n historyFile: values[\"history-file\"] as string | undefined,\n maxHistoryRuns,\n webhookUrls,\n webhookHeaders,\n webhookMethod,\n webhookHmacSecret: values[\"webhook-hmac-secret\"] as string | undefined,\n webhookHmacHeader: (values[\"webhook-hmac-header\"] as string | undefined) ?? \"X-Signature\",\n webhookHmacTimestamp: values[\"webhook-hmac-timestamp\"] as boolean,\n assetMode: assetModeRaw as \"none\" | \"copy\",\n allowMissingAssets: values[\"allow-missing-assets\"] as boolean,\n prSummary: values[\"pr-summary\"] as boolean,\n prSummaryFile: values[\"pr-summary-file\"] as string | undefined,\n };\n}\n\n// ============================================================================\n// Input Reading\n// ============================================================================\n\nasync function readInput(args: CliArgs): Promise<string> {\n if (args.stdin) {\n return readStdin();\n }\n const filePath = path.resolve(args.inputFile!);\n if (!fs.existsSync(filePath)) {\n console.error(`Error: File not found: ${filePath}`);\n process.exit(EXIT_USAGE);\n }\n return fs.readFileSync(filePath, \"utf8\");\n}\n\nfunction readFileInput(filePath: string): string {\n const resolved = path.resolve(filePath);\n if (!fs.existsSync(resolved)) {\n console.error(`Error: File not found: ${resolved}`);\n process.exit(EXIT_USAGE);\n }\n return fs.readFileSync(resolved, \"utf8\");\n}\n\nfunction readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: string[] = [];\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => chunks.push(chunk as string));\n process.stdin.on(\"end\", () => resolve(chunks.join(\"\")));\n process.stdin.on(\"error\", reject);\n });\n}\n\n// ============================================================================\n// Validation Pipeline\n// ============================================================================\n\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: Invalid JSON — ${msg}`);\n process.exit(EXIT_USAGE);\n }\n}\n\nfunction tryParseJson(text: string): unknown | undefined {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeRunFromJsonData(data: unknown, args: CliArgs): {\n run: TestRunResult;\n droppedMissingStory: number;\n} {\n if (args.inputType === \"canonical\") {\n try {\n assertValidRun(data as TestRunResult);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Canonical validation failed:\\n${msg}`);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n\n return { run: data as TestRunResult, droppedMissingStory: 0 };\n }\n\n const obj = data as Record<string, unknown>;\n if (obj.schemaVersion !== 1) {\n console.error(`Unsupported schemaVersion ${obj.schemaVersion}. Supported: 1.`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n const schemaResult = validateRawRun(data);\n if (!schemaResult.valid) {\n console.error(\"Schema validation failed:\");\n for (const err of schemaResult.errors) {\n console.error(` ${err}`);\n }\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n let raw = data as RawRun;\n let droppedMissingStory = 0;\n\n if (args.synthesizeStories) {\n raw = synthesizeStories(raw);\n } else {\n const before = raw.testCases.length;\n const withStory = raw.testCases.filter((tc) => tc.story != null).length;\n droppedMissingStory = before - withStory;\n }\n\n const canonical = canonicalizeRun(raw);\n try {\n assertValidRun(canonical);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Canonical validation failed:\\n${msg}`);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n\n return { run: canonical, droppedMissingStory };\n}\n\nfunction normalizeRunFromText(text: string, args: CliArgs): {\n run: TestRunResult;\n droppedMissingStory: number;\n} {\n if (args.inputType === \"ndjson\") {\n try {\n return { run: parseNdjson(text), droppedMissingStory: 0 };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`NDJSON parse failed: ${msg}`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n }\n\n return normalizeRunFromJsonData(parseJson(text), args);\n}\n\nfunction applySelection(run: TestRunResult, args: CliArgs): TestRunResult {\n const testCases = selectTestCases(\n {\n testCases: run.testCases,\n include: args.include,\n exclude: args.exclude,\n includeTags: args.includeTags,\n excludeTags: args.excludeTags,\n sortTestCases: args.sortTestCases,\n },\n { logger: console }\n );\n\n return { ...run, testCases };\n}\n\nfunction tryNormalizeRunFromText(\n text: string,\n args: CliArgs\n): TestRunResult | undefined {\n if (args.inputType === \"ndjson\") {\n try {\n return parseNdjson(text);\n } catch {\n return undefined;\n }\n }\n\n const data = tryParseJson(text);\n if (data === undefined) return undefined;\n\n if (args.inputType === \"canonical\") {\n try {\n assertValidRun(data as TestRunResult);\n return data as TestRunResult;\n } catch {\n return undefined;\n }\n }\n\n const obj = data as Record<string, unknown>;\n if (obj.schemaVersion !== 1) return undefined;\n\n const schemaResult = validateRawRun(data);\n if (!schemaResult.valid) return undefined;\n\n let raw = data as RawRun;\n if (args.synthesizeStories) {\n raw = synthesizeStories(raw);\n }\n\n const canonical = canonicalizeRun(raw);\n try {\n assertValidRun(canonical);\n return canonical;\n } catch {\n return undefined;\n }\n}\n\nfunction listBaselineCandidates(currentFile: string, args: CliArgs): string[] {\n const baselineDir = path.resolve(args.baselineDir ?? path.dirname(currentFile));\n const currentResolved = path.resolve(currentFile);\n\n if (!fs.existsSync(baselineDir)) {\n console.error(`Error: baseline directory not found: ${baselineDir}`);\n process.exit(EXIT_USAGE);\n }\n\n const entries = fs.readdirSync(baselineDir, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isFile())\n .map((entry) => path.join(baselineDir, entry.name))\n .filter((candidate) => path.resolve(candidate) !== currentResolved)\n .filter((candidate) =>\n args.inputType === \"ndjson\" ? candidate.endsWith(\".ndjson\") : candidate.endsWith(\".json\")\n );\n}\n\nfunction resolveBaselineAuto(\n currentFile: string,\n currentRun: TestRunResult,\n args: CliArgs\n): string {\n const candidates = listBaselineCandidates(currentFile, args);\n const comparable: Array<{ file: string; run: TestRunResult }> = [];\n\n for (const candidate of candidates) {\n const run = tryNormalizeRunFromText(fs.readFileSync(candidate, \"utf8\"), args);\n if (run) {\n comparable.push({ file: candidate, run });\n }\n }\n\n if (comparable.length === 0) {\n console.error(\n `Error: no compatible baseline files found in ${path.resolve(args.baselineDir ?? path.dirname(currentFile))}.`\n );\n process.exit(EXIT_USAGE);\n }\n\n const picked = pickAutoBaseline(currentRun, comparable);\n if (!picked) {\n console.error(\"Error: unable to choose an automatic baseline.\");\n process.exit(EXIT_USAGE);\n }\n return picked.file;\n}\n\n// ============================================================================\n// Main\n// ============================================================================\n\nasync function main() {\n const args = parseCliArgs(process.argv);\n const startMs = Date.now();\n\n if (args.subcommand === \"compare\") {\n const currentText = readFileInput(args.currentFile!);\n const current = applySelection(normalizeRunFromText(currentText, args).run, args);\n const baselineFile =\n args.baselineMode === \"auto\"\n ? resolveBaselineAuto(args.currentFile!, current, args)\n : args.baselineFile!;\n const baselineText = readFileInput(baselineFile);\n const baseline = applySelection(normalizeRunFromText(baselineText, args).run, args);\n\n try {\n const result = await generateCompareReports(baseline, current, baselineFile, args);\n printCompareResult(result, args, startMs);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Comparison failed: ${msg}`);\n process.exit(EXIT_GENERATION);\n }\n }\n\n if (args.subcommand === \"list\") {\n const text = await readInput(args);\n const run = applySelection(normalizeRunFromText(text, args).run, args);\n\n // Use --json-summary to get JSON output for list command\n const outputFormat: \"text\" | \"json\" = args.jsonSummary ? \"json\" : \"text\";\n const output = listScenarios({ testCases: run.testCases, format: outputFormat }, {});\n console.log(output);\n process.exit(EXIT_SUCCESS);\n }\n\n // Read input\n const text = await readInput(args);\n\n // === NDJSON input pipeline ===\n if (args.inputType === \"ndjson\") {\n if (args.subcommand === \"validate\") {\n // Validate each line is valid JSON with exactly one envelope field\n const lines = text.trim().split(\"\\n\").filter(Boolean);\n const validKeys = new Set([\n \"meta\", \"source\", \"gherkinDocument\", \"pickle\",\n \"testRunStarted\", \"testCase\", \"testCaseStarted\",\n \"testStepStarted\", \"testStepFinished\", \"testCaseFinished\",\n \"testRunFinished\", \"attachment\",\n ]);\n for (let i = 0; i < lines.length; i++) {\n try {\n const obj = JSON.parse(lines[i]);\n const keys = Object.keys(obj);\n if (keys.length !== 1 || !validKeys.has(keys[0])) {\n console.error(`Line ${i + 1}: invalid envelope (keys: ${keys.join(\", \")})`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n } catch {\n console.error(`Line ${i + 1}: invalid JSON`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n }\n console.log(`Valid NDJSON (${lines.length} envelopes).`);\n process.exit(EXIT_SUCCESS);\n }\n\n // Parse NDJSON → TestRunResult\n let run;\n try {\n run = parseNdjson(text);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`NDJSON parse failed: ${msg}`);\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n // Emit canonical if requested\n if (args.emitCanonical) {\n const outPath = path.resolve(args.emitCanonical);\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\n fs.writeFileSync(outPath, JSON.stringify(run, null, 2), \"utf8\");\n }\n\n try {\n const result = await generateReports(run, args);\n await dispatchNotifications(run, args);\n runHistoryPipeline(run, args);\n printResult(result, args, startMs);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Generation failed: ${msg}`);\n process.exit(EXIT_GENERATION);\n }\n }\n\n const data = parseJson(text);\n\n if (args.subcommand === \"validate\") {\n // Validate-only mode\n if (args.inputType === \"canonical\") {\n try {\n assertValidRun(data as TestRunResult);\n console.log(\"Valid canonical TestRunResult.\");\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(msg);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n }\n\n // Check schemaVersion\n const obj = data as Record<string, unknown>;\n if (obj.schemaVersion !== 1) {\n console.error(\n `Unsupported schemaVersion ${obj.schemaVersion}. Supported: 1.`\n );\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n const result = validateRawRun(data);\n if (!result.valid) {\n console.error(\"Schema validation failed:\");\n for (const err of result.errors) {\n console.error(` ${err}`);\n }\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n console.log(\"Valid RawRun (schemaVersion 1).\");\n process.exit(EXIT_SUCCESS);\n }\n\n // === format subcommand ===\n\n if (args.inputType === \"canonical\") {\n // Skip schema validation, go straight to canonical validation\n try {\n assertValidRun(data as TestRunResult);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Canonical validation failed:\\n${msg}`);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n\n const run = data as TestRunResult;\n\n // Emit canonical if requested\n if (args.emitCanonical) {\n const outPath = path.resolve(args.emitCanonical);\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\n fs.writeFileSync(outPath, JSON.stringify(run, null, 2), \"utf8\");\n }\n\n try {\n const result = await generateReports(run, args);\n await dispatchNotifications(run, args);\n runHistoryPipeline(run, args);\n printResult(result, args, startMs);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Generation failed: ${msg}`);\n process.exit(EXIT_GENERATION);\n }\n }\n\n // Raw input pipeline\n // 1. Check schemaVersion\n const obj = data as Record<string, unknown>;\n if (obj.schemaVersion !== 1) {\n console.error(\n `Unsupported schemaVersion ${obj.schemaVersion}. Supported: 1.`\n );\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n // 2. Ajv schema validation\n const schemaResult = validateRawRun(data);\n if (!schemaResult.valid) {\n console.error(\"Schema validation failed:\");\n for (const err of schemaResult.errors) {\n console.error(` ${err}`);\n }\n process.exit(EXIT_SCHEMA_VALIDATION);\n }\n\n // 3. Synthesize stories (optional)\n let raw = data as RawRun;\n let droppedMissingStory = 0;\n\n if (args.synthesizeStories) {\n raw = synthesizeStories(raw);\n } else {\n // Count and warn about dropped test cases\n const before = raw.testCases.length;\n const withStory = raw.testCases.filter(\n (tc) => tc.story != null\n ).length;\n droppedMissingStory = before - withStory;\n if (droppedMissingStory > 0) {\n console.error(\n `Dropped ${droppedMissingStory} test cases missing story (use --synthesize-stories to include)`\n );\n }\n }\n\n // 4. Canonicalize\n const canonical = canonicalizeRun(raw);\n\n // 5. Assert canonical validity\n try {\n assertValidRun(canonical);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Canonical validation failed:\\n${msg}`);\n process.exit(EXIT_CANONICAL_VALIDATION);\n }\n\n // Emit canonical if requested\n if (args.emitCanonical) {\n const outPath = path.resolve(args.emitCanonical);\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\n fs.writeFileSync(outPath, JSON.stringify(canonical, null, 2), \"utf8\");\n }\n\n // 6. Generate reports\n try {\n const result = await generateReports(canonical, args, droppedMissingStory);\n await dispatchNotifications(canonical, args);\n runHistoryPipeline(canonical, args);\n printResult(result, args, startMs, droppedMissingStory);\n process.exit(EXIT_SUCCESS);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Generation failed: ${msg}`);\n process.exit(EXIT_GENERATION);\n }\n}\n\n// ============================================================================\n// Notifications\n// ============================================================================\n\nasync function dispatchNotifications(run: TestRunResult, args: CliArgs): Promise<void> {\n // Build generic webhook configs from CLI flags\n const webhooks: GenericWebhookNotifierOptions[] = args.webhookUrls.map((url) => {\n const opts: GenericWebhookNotifierOptions = { url };\n if (Object.keys(args.webhookHeaders).length > 0) {\n opts.headers = { ...args.webhookHeaders };\n }\n if (args.webhookMethod !== \"POST\") {\n opts.method = args.webhookMethod;\n }\n if (args.webhookHmacSecret) {\n const signer: WebhookSignerHmac = {\n type: \"hmac-sha256\",\n secret: args.webhookHmacSecret,\n header: args.webhookHmacHeader,\n };\n if (args.webhookHmacTimestamp) {\n signer.includeTimestamp = true;\n }\n opts.signer = signer;\n }\n return opts;\n });\n\n await sendNotifications(\n {\n run,\n notification: {\n slackWebhookUrl: args.slackWebhook,\n teamsWebhookUrl: args.teamsWebhook,\n condition: args.notify,\n reportUrl: args.reportUrl,\n maxFailedTests: args.maxFailedTests,\n webhooks: webhooks.length > 0 ? webhooks : undefined,\n },\n },\n {\n fetch: globalThis.fetch,\n logger: console,\n toCIInfo,\n },\n );\n}\n\n// ============================================================================\n// History Pipeline\n// ============================================================================\n\nfunction runHistoryPipeline(run: TestRunResult, args: CliArgs): void {\n if (!args.historyFile) return;\n\n const historyPath = path.resolve(args.historyFile);\n\n // Load existing history\n const store = loadHistory(\n { filePath: historyPath },\n {\n readFile: (p: string) => {\n try {\n return fs.readFileSync(p, \"utf8\");\n } catch {\n return undefined;\n }\n },\n logger: console,\n },\n );\n\n // Update history with current run\n const updated = updateHistory({\n store,\n run,\n maxRuns: args.maxHistoryRuns,\n });\n\n // Save updated history\n const dir = path.dirname(historyPath);\n fs.mkdirSync(dir, { recursive: true });\n saveHistory(\n { filePath: historyPath, store: updated },\n { writeFile: (p: string, content: string) => fs.writeFileSync(p, content, \"utf8\") },\n );\n\n // Compute metrics (log summary for CLI users)\n let metricsCount = 0;\n for (const testId of Object.keys(updated.tests)) {\n const history = updated.tests[testId];\n if (history.entries.length >= 3) {\n metricsCount++;\n }\n }\n if (metricsCount > 0) {\n console.error(`History updated: ${historyPath} (${Object.keys(updated.tests).length} tests tracked)`);\n }\n}\n\n// ============================================================================\n// Report Generation\n// ============================================================================\n\ninterface CliResult {\n files: string[];\n counts: { passed: number; failed: number; skipped: number; pending: number };\n}\n\ninterface CompareCliResult {\n files: string[];\n baselineFile: string;\n summary: {\n added: number;\n removed: number;\n changed: number;\n regressed: number;\n fixed: number;\n unchanged: number;\n };\n prSummary?: string;\n}\n\nasync function generateReports(\n run: TestRunResult,\n args: CliArgs,\n _droppedMissingStory = 0\n): Promise<CliResult> {\n const generator = new ReportGenerator({\n include: args.include,\n exclude: args.exclude,\n includeTags: args.includeTags,\n excludeTags: args.excludeTags,\n formats: args.formats,\n outputDir: args.outputDir,\n outputName: args.outputName,\n outputNameTimestamp: args.outputNameTimestamp,\n sortTestCases: args.sortTestCases,\n html: {\n title: args.htmlTitle,\n theme: args.htmlTheme,\n syntaxHighlighting: !args.htmlNoSyntaxHighlighting,\n mermaidEnabled: !args.htmlNoMermaid,\n markdownEnabled: !args.htmlNoMarkdown,\n permalinkBaseUrl: args.htmlPermalinkBaseUrl,\n ticketUrlTemplate: args.htmlTicketUrlTemplate,\n tocEnabled: !args.htmlNoToc,\n themePickerEnabled: args.htmlThemePicker,\n },\n assetMode: args.assetMode,\n allowMissingAssets: args.allowMissingAssets,\n });\n\n const resultMap = await generator.generate(run);\n\n // Collect all generated file paths\n const files: string[] = [];\n for (const paths of resultMap.values()) {\n files.push(...paths);\n }\n\n // Count statuses\n const counts = { passed: 0, failed: 0, skipped: 0, pending: 0 };\n for (const tc of run.testCases) {\n const status = tc.status as keyof typeof counts;\n if (status in counts) {\n counts[status]++;\n }\n }\n\n return { files, counts };\n}\n\nasync function generateCompareReports(\n baseline: TestRunResult,\n current: TestRunResult,\n baselineFile: string,\n args: CliArgs\n): Promise<CompareCliResult> {\n const unsupportedCompareFormats = args.formats.filter(\n (format) => format !== \"html\" && format !== \"markdown\"\n );\n if (unsupportedCompareFormats.length > 0) {\n throw new Error(\n `compare supports only \"html\" and \"markdown\" formats (unsupported: ${unsupportedCompareFormats.join(\", \")})`\n );\n }\n const compareFormats = args.formats as (\"html\" | \"markdown\")[];\n\n const result = await generateRunComparison({\n baseline,\n current,\n formats: compareFormats,\n outputDir: args.outputDir,\n outputName: args.outputName,\n title: args.htmlTitle,\n });\n\n return {\n files: result.files,\n baselineFile,\n summary: result.diff.summary,\n prSummary: args.prSummary || args.prSummaryFile ? createPrCommentSummary(result.diff) : undefined,\n };\n}\n\nfunction printResult(\n result: CliResult,\n args: CliArgs,\n startMs: number,\n droppedMissingStory = 0\n) {\n const durationMs = Date.now() - startMs;\n\n if (args.jsonSummary) {\n const summary: Record<string, unknown> = {\n files: result.files,\n counts: result.counts,\n durationMs,\n };\n if (droppedMissingStory > 0) {\n summary.droppedMissingStory = droppedMissingStory;\n }\n console.log(JSON.stringify(summary, null, 2));\n } else {\n for (const f of result.files) {\n console.log(f);\n }\n }\n}\n\nfunction printCompareResult(\n result: CompareCliResult,\n args: CliArgs,\n startMs: number\n) {\n const durationMs = Date.now() - startMs;\n\n if (result.prSummary && args.prSummaryFile) {\n const outputPath = path.resolve(args.prSummaryFile);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, result.prSummary, \"utf8\");\n }\n\n if (args.jsonSummary) {\n console.log(\n JSON.stringify(\n {\n files: result.files,\n baselineFile: result.baselineFile,\n diff: result.summary,\n prSummary: result.prSummary,\n durationMs,\n },\n null,\n 2\n )\n );\n return;\n }\n\n for (const f of result.files) {\n console.log(f);\n }\n console.log(`baseline: ${result.baselineFile}`);\n if (result.prSummary && args.prSummary) {\n console.log(\"\");\n console.log(result.prSummary);\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(EXIT_USAGE);\n});\n","/**\n * JSON Schema validation for RawRun using Ajv.\n *\n * Validates raw JSON input against the raw-run.schema.json schema\n * before it enters the ACL pipeline.\n */\n\nimport Ajv from \"ajv/dist/2020.js\";\nimport schema from \"../../schemas/raw-run.schema.json\" with { type: \"json\" };\n\n/** Validation result with JSON-path-based error messages */\nexport interface SchemaValidationResult {\n valid: boolean;\n errors: string[];\n}\n\n// Compile once, reuse across calls\nconst ajv = new Ajv({ allErrors: true });\nconst validate = ajv.compile(schema);\n\n/**\n * Validate raw JSON data against the RawRun schema.\n *\n * Returns JSON-path-based error messages for easy debugging\n * from any language.\n */\nexport function validateRawRun(data: unknown): SchemaValidationResult {\n const valid = validate(data);\n\n if (valid) {\n return { valid: true, errors: [] };\n }\n\n const errors = (validate.errors ?? []).map((err: { instancePath?: string; message?: string; keyword?: string; params?: Record<string, unknown> }) => {\n const path = err.instancePath || \"/\";\n const message = err.message ?? \"unknown error\";\n\n if (err.keyword === \"additionalProperties\") {\n const extra = (err.params as { additionalProperty?: string })\n .additionalProperty;\n return `${path}: ${message} — '${extra}'`;\n }\n\n if (err.keyword === \"enum\") {\n const allowed = (err.params as { allowedValues?: unknown[] })\n .allowedValues;\n return `${path}: ${message} — allowed: ${JSON.stringify(allowed)}`;\n }\n\n return `${path}: ${message}`;\n });\n\n return { valid: false, errors };\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://executable-stories.dev/schemas/raw-run.schema.json\",\n \"title\": \"RawRun\",\n \"description\": \"Raw test run data from any test framework. Feed this to the executable-stories CLI to generate HTML, Markdown, JUnit XML, and Cucumber JSON reports.\",\n \"type\": \"object\",\n \"$ref\": \"#/$defs/RawRun\",\n \"$defs\": {\n \"RawRun\": {\n \"type\": \"object\",\n \"description\": \"Top-level raw run containing all test cases and run metadata.\",\n \"properties\": {\n \"schemaVersion\": {\n \"type\": \"integer\",\n \"const\": 1,\n \"description\": \"Schema version for forward compatibility. Must be 1.\"\n },\n \"testCases\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/RawTestCase\" },\n \"description\": \"All test cases from the run.\"\n },\n \"startedAtMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Run start time as Unix epoch milliseconds.\"\n },\n \"finishedAtMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Run finish time as Unix epoch milliseconds.\"\n },\n \"projectRoot\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"Absolute path to the project root directory. Used for resolving relative attachment paths.\"\n },\n \"packageVersion\": {\n \"type\": \"string\",\n \"description\": \"Version of the package under test.\"\n },\n \"gitSha\": {\n \"type\": \"string\",\n \"description\": \"Git commit SHA at the time of the run.\"\n },\n \"ci\": {\n \"$ref\": \"#/$defs/RawCIInfo\"\n },\n \"meta\": {\n \"type\": \"object\",\n \"description\": \"Arbitrary runner-specific metadata. Escape hatch for data not covered by the schema.\"\n }\n },\n \"required\": [\"schemaVersion\", \"testCases\", \"projectRoot\"],\n \"additionalProperties\": false\n },\n \"RawTestCase\": {\n \"type\": \"object\",\n \"description\": \"A single test case result. Only 'status' is required — everything else is optional to support plain unit tests without BDD metadata.\",\n \"properties\": {\n \"externalId\": {\n \"type\": \"string\",\n \"description\": \"Framework's native test ID (e.g., Jest test path, Playwright test ID).\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Human-readable test title/name.\"\n },\n \"titlePath\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Full title path from describe/suite blocks down to the test name.\"\n },\n \"story\": {\n \"$ref\": \"#/$defs/StoryMeta\"\n },\n \"sourceFile\": {\n \"type\": \"string\",\n \"description\": \"Path to the source file containing this test (relative to projectRoot or absolute).\"\n },\n \"sourceLine\": {\n \"type\": \"integer\",\n \"minimum\": 1,\n \"description\": \"Line number in the source file (1-based).\"\n },\n \"status\": {\n \"$ref\": \"#/$defs/RawStatus\"\n },\n \"durationMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Test duration in milliseconds.\"\n },\n \"error\": {\n \"type\": \"object\",\n \"description\": \"Error information if the test failed.\",\n \"properties\": {\n \"message\": {\n \"type\": \"string\",\n \"description\": \"Error message.\"\n },\n \"stack\": {\n \"type\": \"string\",\n \"description\": \"Error stack trace.\"\n }\n },\n \"additionalProperties\": false\n },\n \"stepEvents\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/RawStepEvent\" },\n \"description\": \"Step-level execution events from the framework (if available).\"\n },\n \"attachments\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/RawAttachment\" },\n \"description\": \"Attachments such as screenshots, logs, or other artifacts.\"\n },\n \"meta\": {\n \"type\": \"object\",\n \"description\": \"Arbitrary framework-specific metadata. Escape hatch for data not covered by the schema.\"\n },\n \"retry\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"description\": \"Retry attempt number (0-based). 0 means first attempt.\"\n },\n \"retries\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"description\": \"Total number of retries configured for this test.\"\n },\n \"projectName\": {\n \"type\": \"string\",\n \"description\": \"Project name (e.g., Playwright project like 'chromium', 'firefox').\"\n }\n },\n \"required\": [\"status\"],\n \"additionalProperties\": false\n },\n \"RawStatus\": {\n \"type\": \"string\",\n \"enum\": [\"pass\", \"fail\", \"skip\", \"todo\", \"pending\", \"timeout\", \"interrupted\", \"unknown\"],\n \"description\": \"Permissive test status from any framework. Mapped to canonical status (passed/failed/skipped/pending) during canonicalization.\"\n },\n \"StoryMeta\": {\n \"type\": \"object\",\n \"description\": \"BDD story metadata for a test case. Contains the scenario title, steps, tags, and documentation.\",\n \"properties\": {\n \"scenario\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"description\": \"The scenario title (typically the test name).\"\n },\n \"steps\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/StoryStep\" },\n \"description\": \"BDD steps in this scenario. Can be empty or omitted for progressive enrichment.\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Tags for filtering and categorization (e.g., ['smoke', 'auth']).\"\n },\n \"tickets\": {\n \"type\": \"array\",\n \"items\": {\n \"oneOf\": [\n { \"type\": \"string\" },\n {\n \"type\": \"object\",\n \"properties\": {\n \"id\": { \"type\": \"string\" },\n \"url\": { \"type\": \"string\" }\n },\n \"required\": [\"id\"],\n \"additionalProperties\": false\n }\n ]\n },\n \"description\": \"Ticket/issue references. Each item is either a string ID or an object with id and optional url.\"\n },\n \"meta\": {\n \"type\": \"object\",\n \"description\": \"User-defined metadata for this story.\"\n },\n \"suitePath\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Parent describe/suite names for hierarchical grouping.\"\n },\n \"docs\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/DocEntry\" },\n \"description\": \"Story-level documentation entries (before any steps).\"\n },\n \"sourceOrder\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"description\": \"Order in which the story was defined in source (for stable sorting).\"\n }\n },\n \"required\": [\"scenario\"],\n \"additionalProperties\": false\n },\n \"StoryStep\": {\n \"type\": \"object\",\n \"description\": \"A single BDD step in a scenario.\",\n \"properties\": {\n \"keyword\": {\n \"$ref\": \"#/$defs/StepKeyword\"\n },\n \"text\": {\n \"type\": \"string\",\n \"description\": \"The step description text.\"\n },\n \"mode\": {\n \"$ref\": \"#/$defs/StepMode\"\n },\n \"docs\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/DocEntry\" },\n \"description\": \"Rich documentation entries attached to this step.\"\n },\n \"id\": {\n \"type\": \"string\",\n \"description\": \"Unique step identifier within the scenario (e.g., 'step-0').\"\n },\n \"wrapped\": {\n \"type\": \"boolean\",\n \"description\": \"Whether this step wraps a function call (Fn/Expect pattern).\"\n },\n \"durationMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Step-level duration in milliseconds (from startTimer/endTimer).\"\n }\n },\n \"required\": [\"keyword\", \"text\"],\n \"additionalProperties\": false\n },\n \"StepKeyword\": {\n \"type\": \"string\",\n \"description\": \"BDD step keyword. Case-insensitive — 'given' and 'Given' are both accepted. Normalized to title case during processing.\",\n \"pattern\": \"^[Gg]iven$|^[Ww]hen$|^[Tt]hen$|^[Aa]nd$|^[Bb]ut$\"\n },\n \"StepMode\": {\n \"type\": \"string\",\n \"enum\": [\"normal\", \"skip\", \"only\", \"todo\", \"fails\", \"concurrent\"],\n \"description\": \"Step execution mode for documentation rendering.\"\n },\n \"DocPhase\": {\n \"type\": \"string\",\n \"enum\": [\"static\", \"runtime\"],\n \"description\": \"When the doc entry was added — 'static' at definition time, 'runtime' during execution.\"\n },\n \"DocEntry\": {\n \"description\": \"A documentation entry attached to a story or step. Discriminated union on the 'kind' field.\",\n \"oneOf\": [\n {\n \"type\": \"object\",\n \"description\": \"Free-text note.\",\n \"properties\": {\n \"kind\": { \"const\": \"note\" },\n \"text\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"text\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Tag(s) for categorization.\",\n \"properties\": {\n \"kind\": { \"const\": \"tag\" },\n \"names\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"names\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Key-value pair.\",\n \"properties\": {\n \"kind\": { \"const\": \"kv\" },\n \"label\": { \"type\": \"string\" },\n \"value\": {},\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"label\", \"value\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Code block with optional language.\",\n \"properties\": {\n \"kind\": { \"const\": \"code\" },\n \"label\": { \"type\": \"string\" },\n \"content\": { \"type\": \"string\" },\n \"lang\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"label\", \"content\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Markdown table.\",\n \"properties\": {\n \"kind\": { \"const\": \"table\" },\n \"label\": { \"type\": \"string\" },\n \"columns\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n },\n \"rows\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n }\n },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"label\", \"columns\", \"rows\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Hyperlink.\",\n \"properties\": {\n \"kind\": { \"const\": \"link\" },\n \"label\": { \"type\": \"string\" },\n \"url\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"label\", \"url\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Titled section with markdown content.\",\n \"properties\": {\n \"kind\": { \"const\": \"section\" },\n \"title\": { \"type\": \"string\" },\n \"markdown\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"title\", \"markdown\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Mermaid diagram.\",\n \"properties\": {\n \"kind\": { \"const\": \"mermaid\" },\n \"code\": { \"type\": \"string\" },\n \"title\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"code\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Screenshot reference.\",\n \"properties\": {\n \"kind\": { \"const\": \"screenshot\" },\n \"path\": { \"type\": \"string\" },\n \"alt\": { \"type\": \"string\" },\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"path\", \"phase\"],\n \"additionalProperties\": false\n },\n {\n \"type\": \"object\",\n \"description\": \"Custom documentation entry with arbitrary data.\",\n \"properties\": {\n \"kind\": { \"const\": \"custom\" },\n \"type\": { \"type\": \"string\" },\n \"data\": {},\n \"phase\": { \"$ref\": \"#/$defs/DocPhase\" },\n \"children\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/DocEntry\" }, \"description\": \"Nested child doc entries for grouping.\" }\n },\n \"required\": [\"kind\", \"type\", \"data\", \"phase\"],\n \"additionalProperties\": false\n }\n ]\n },\n \"RawAttachment\": {\n \"type\": \"object\",\n \"description\": \"A test attachment (screenshot, log, artifact). In the MVP schema, only path-based attachments are supported (no inline base64).\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Human-readable name for the attachment.\"\n },\n \"mediaType\": {\n \"type\": \"string\",\n \"description\": \"MIME type (e.g., 'image/png', 'text/plain', 'application/json').\"\n },\n \"path\": {\n \"type\": \"string\",\n \"description\": \"File path (relative to projectRoot or absolute).\"\n }\n },\n \"required\": [\"name\", \"mediaType\", \"path\"],\n \"additionalProperties\": false\n },\n \"RawStepEvent\": {\n \"type\": \"object\",\n \"description\": \"Step-level execution event from the framework. Optional — most frameworks don't provide step-level data.\",\n \"properties\": {\n \"index\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"description\": \"Step index (0-based).\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Step title/description.\"\n },\n \"status\": {\n \"$ref\": \"#/$defs/RawStatus\"\n },\n \"durationMs\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"description\": \"Step duration in milliseconds.\"\n },\n \"errorMessage\": {\n \"type\": \"string\",\n \"description\": \"Error message if the step failed.\"\n }\n },\n \"additionalProperties\": false\n },\n \"RawCIInfo\": {\n \"type\": \"object\",\n \"description\": \"CI environment information.\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"CI provider name (e.g., 'GitHub Actions', 'Jenkins', 'CircleCI').\"\n },\n \"url\": {\n \"type\": \"string\",\n \"description\": \"URL to the CI build/run.\"\n },\n \"buildNumber\": {\n \"type\": \"string\",\n \"description\": \"CI build number or run ID.\"\n }\n },\n \"required\": [\"name\"],\n \"additionalProperties\": false\n }\n }\n}\n","/**\n * Story synthesis for plain test results.\n *\n * Fills in missing story metadata so that test cases without BDD steps\n * are not discarded by the ACL's `.filter((tc) => tc.story != null)`.\n */\n\nimport type { StepKeyword, StoryStep } from \"../types/story\";\nimport type { RawRun, RawTestCase } from \"../types/raw\";\n\n/** Mapping from lowercase keyword to canonical title case */\nconst KEYWORD_MAP: Record<string, StepKeyword> = {\n given: \"Given\",\n when: \"When\",\n then: \"Then\",\n and: \"And\",\n but: \"But\",\n};\n\n/**\n * Normalize a step keyword to title case.\n * \"given\" → \"Given\", \"THEN\" is not a valid keyword and will pass through.\n */\nfunction normalizeKeyword(keyword: string): StepKeyword {\n return KEYWORD_MAP[keyword.toLowerCase()] ?? (keyword as StepKeyword);\n}\n\n/**\n * Normalize all step keywords in a StoryStep array to title case.\n */\nfunction normalizeStepKeywords(steps: StoryStep[]): StoryStep[] {\n return steps.map((step) => ({\n ...step,\n keyword: normalizeKeyword(step.keyword),\n }));\n}\n\n/**\n * Derive a scenario name from a raw test case.\n */\nfunction deriveScenarioName(tc: RawTestCase): string {\n if (tc.title) return tc.title;\n if (tc.titlePath && tc.titlePath.length > 0) {\n return tc.titlePath[tc.titlePath.length - 1];\n }\n return \"Untitled\";\n}\n\n/**\n * Synthesize story metadata for test cases that are missing it.\n *\n * Rules:\n * - If `story` is missing entirely:\n * - `scenario` = title (or last element of titlePath, or \"Untitled\")\n * - `steps` = `[{ keyword: \"Then\", text: scenario }]`\n * - If `story` is present but `steps` is empty/missing:\n * - `steps` = `[{ keyword: \"Then\", text: story.scenario }]`\n * - Normalize keyword casing in all steps: \"given\" → \"Given\", etc.\n *\n * This is a pure function that returns a new RawRun.\n */\nexport function synthesizeStories(raw: RawRun): RawRun {\n return {\n ...raw,\n testCases: raw.testCases.map(synthesizeTestCase),\n };\n}\n\nfunction synthesizeTestCase(tc: RawTestCase): RawTestCase {\n if (tc.story == null) {\n // No story at all — synthesize from test title\n const scenario = deriveScenarioName(tc);\n return {\n ...tc,\n story: {\n scenario,\n steps: [{ keyword: \"Then\", text: scenario }],\n },\n };\n }\n\n // Story exists — check if steps are missing/empty\n const steps = tc.story.steps;\n if (!steps || steps.length === 0) {\n return {\n ...tc,\n story: {\n ...tc.story,\n steps: [{ keyword: \"Then\", text: tc.story.scenario }],\n },\n };\n }\n\n // Steps exist — just normalize keyword casing\n return {\n ...tc,\n story: {\n ...tc.story,\n steps: normalizeStepKeywords(steps),\n },\n };\n}\n","/**\n * Status mapping from raw framework statuses to canonical TestStatus.\n */\n\nimport type { RawStatus } from \"../../types/raw\";\nimport type { TestStatus } from \"../../types/test-result\";\n\n/** Map raw status to canonical TestStatus */\nconst STATUS_MAP: Record<RawStatus, TestStatus> = {\n pass: \"passed\",\n fail: \"failed\",\n skip: \"skipped\",\n pending: \"pending\",\n todo: \"pending\", // Map todo → pending\n timeout: \"failed\", // Map timeout → failed\n interrupted: \"failed\", // Map interrupted → failed\n unknown: \"skipped\", // Safest default\n};\n\n/**\n * Convert a raw status to canonical TestStatus.\n *\n * @param raw - The raw status from a framework\n * @returns The canonical TestStatus\n */\nexport function normalizeStatus(raw: RawStatus): TestStatus {\n return STATUS_MAP[raw] ?? \"skipped\";\n}\n\n/**\n * Convert a string to RawStatus, with fallback to \"unknown\".\n *\n * @param status - Any status string\n * @returns A valid RawStatus\n */\nexport function parseRawStatus(status: string | undefined): RawStatus {\n if (!status) return \"unknown\";\n const lower = status.toLowerCase();\n\n // Direct matches\n if (lower === \"pass\" || lower === \"passed\") return \"pass\";\n if (lower === \"fail\" || lower === \"failed\") return \"fail\";\n if (lower === \"skip\" || lower === \"skipped\") return \"skip\";\n if (lower === \"pending\") return \"pending\";\n if (lower === \"todo\") return \"todo\";\n if (lower === \"timeout\" || lower === \"timedout\" || lower === \"timed_out\") return \"timeout\";\n if (lower === \"interrupted\") return \"interrupted\";\n\n return \"unknown\";\n}\n","/**\n * ID generation and slug helpers for deterministic, Cucumber-compatible IDs.\n */\n\nimport { createHash } from \"node:crypto\";\n\n/**\n * Generate a deterministic test case ID from source file and scenario name.\n *\n * @param sourceFile - The source file path\n * @param scenario - The scenario name\n * @returns A 12-character hex ID\n */\nexport function generateTestCaseId(sourceFile: string, scenario: string): string {\n const input = `${sourceFile}::${scenario}`;\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 12);\n}\n\n/**\n * Generate a deterministic run ID from timestamp and project root.\n *\n * @param startedAtMs - Run start timestamp\n * @param projectRoot - Project root directory\n * @returns A 16-character hex ID\n */\nexport function generateRunId(startedAtMs: number, projectRoot: string): string {\n const input = `${startedAtMs}::${projectRoot}`;\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 16);\n}\n\n/**\n * Slugify a string for Cucumber JSON IDs.\n *\n * Converts to lowercase, replaces path separators/spaces with hyphens,\n * removes other special chars, and trims leading/trailing hyphens.\n *\n * @param text - The text to slugify\n * @returns A URL-safe slug\n */\nexport function slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[/\\\\]+/g, \"-\") // Convert path separators to hyphens\n .replace(/[^\\w\\s-]/g, \"\") // Remove other special characters\n .replace(/[\\s_]+/g, \"-\") // Replace spaces and underscores with hyphens\n .replace(/-+/g, \"-\") // Remove consecutive hyphens\n .replace(/^-+|-+$/g, \"\"); // Trim leading/trailing hyphens\n}\n\n/**\n * Generate a Cucumber-compatible feature ID from file path.\n *\n * Uses the full path (without extension) to ensure uniqueness for files\n * with the same basename in different directories.\n *\n * @param uri - The feature file URI/path\n * @returns A slugified feature ID\n */\nexport function generateFeatureId(uri: string): string {\n // Use full path without extension for uniqueness\n const pathWithoutExt = uri.replace(/\\.[^.]+$/, \"\");\n return slugify(pathWithoutExt);\n}\n\n/**\n * Generate a Cucumber-compatible scenario ID.\n *\n * Format: feature-id;scenario-name\n *\n * @param featureId - The feature ID\n * @param scenarioName - The scenario name\n * @returns A Cucumber-compatible scenario ID\n */\nexport function generateScenarioId(featureId: string, scenarioName: string): string {\n return `${featureId};${slugify(scenarioName)}`;\n}\n","/**\n * Step fallback rules for deriving step results from scenario status.\n *\n * When frameworks don't provide step-level results, we derive them\n * from the overall scenario status using these rules.\n */\n\nimport type { StoryStep } from \"../../types/story\";\nimport type { TestStatus, StepResult } from \"../../types/test-result\";\n\n/**\n * Derive step results from story steps and scenario status.\n *\n * Rules:\n * - Passed: All steps are passed\n * - Skipped/Pending: All steps are skipped/pending\n * - Failed: Steps up to failure are passed, failing step is failed, rest are skipped\n * (Heuristic: last step is the failure, or use error info if available)\n *\n * @param steps - Story steps with keywords and text\n * @param scenarioStatus - Overall scenario status\n * @param error - Optional error information to help identify failing step\n * @returns Array of step results\n */\nexport function deriveStepResults(\n steps: StoryStep[],\n scenarioStatus: TestStatus,\n error?: { message?: string; stack?: string }\n): StepResult[] {\n if (steps.length === 0) {\n return [];\n }\n\n // Passed: all steps passed\n if (scenarioStatus === \"passed\") {\n return steps.map((_, index) => ({\n index,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n }\n\n // Skipped or Pending: all steps have same status\n if (scenarioStatus === \"skipped\" || scenarioStatus === \"pending\") {\n return steps.map((_, index) => ({\n index,\n status: scenarioStatus,\n durationMs: 0,\n }));\n }\n\n // Failed: identify failing step and mark accordingly\n const failingIndex = findFailingStepIndex(steps, error);\n\n return steps.map((_, index) => {\n if (index < failingIndex) {\n // Steps before failure are passed\n return { index, status: \"passed\" as TestStatus, durationMs: 0 };\n } else if (index === failingIndex) {\n // Failing step\n return {\n index,\n status: \"failed\" as TestStatus,\n durationMs: 0,\n errorMessage: error?.message,\n };\n } else {\n // Steps after failure are skipped\n return { index, status: \"skipped\" as TestStatus, durationMs: 0 };\n }\n });\n}\n\n/**\n * Attempt to identify which step failed based on error information.\n *\n * Strategies:\n * 1. Look for step text in error message/stack\n * 2. Default to last step if no match found\n *\n * @param steps - Story steps\n * @param error - Error information\n * @returns Index of the failing step (0-based)\n */\nfunction findFailingStepIndex(\n steps: StoryStep[],\n error?: { message?: string; stack?: string }\n): number {\n if (!error || steps.length === 0) {\n // Default: last step failed\n return steps.length - 1;\n }\n\n const errorText = `${error.message ?? \"\"} ${error.stack ?? \"\"}`.toLowerCase();\n\n // Try to find a step mentioned in the error\n for (let i = 0; i < steps.length; i++) {\n const stepText = steps[i].text.toLowerCase();\n if (errorText.includes(stepText)) {\n return i;\n }\n }\n\n // Default: last step failed\n return steps.length - 1;\n}\n\n/**\n * Merge raw step events with derived step results.\n *\n * When we have partial step data from the framework, merge it with\n * the derived results, preferring actual data over derived.\n *\n * @param derived - Derived step results from fallback rules\n * @param events - Raw step events from framework (if any)\n * @returns Merged step results\n */\nexport function mergeStepResults(\n derived: StepResult[],\n events?: Array<{\n index?: number;\n status?: string;\n durationMs?: number;\n errorMessage?: string;\n }>\n): StepResult[] {\n if (!events || events.length === 0) {\n return derived;\n }\n\n // Create a map of actual results by index\n const actualByIndex = new Map<number, (typeof events)[0]>();\n for (const event of events) {\n if (event.index !== undefined) {\n actualByIndex.set(event.index, event);\n }\n }\n\n return derived.map((step) => {\n const actual = actualByIndex.get(step.index);\n if (!actual) {\n return step;\n }\n\n return {\n index: step.index,\n status: normalizeStepStatus(actual.status) ?? step.status,\n durationMs: actual.durationMs ?? step.durationMs,\n errorMessage: actual.errorMessage ?? step.errorMessage,\n };\n });\n}\n\n/**\n * Normalize step status string to TestStatus.\n */\nfunction normalizeStepStatus(status?: string): TestStatus | undefined {\n if (!status) return undefined;\n\n const lower = status.toLowerCase();\n if (lower === \"pass\" || lower === \"passed\") return \"passed\";\n if (lower === \"fail\" || lower === \"failed\") return \"failed\";\n if (lower === \"skip\" || lower === \"skipped\") return \"skipped\";\n if (lower === \"pending\") return \"pending\";\n\n return undefined;\n}\n","/**\n * Attachment resolution: embed vs link decision.\n *\n * Attachments can either be embedded inline (base64) or linked to\n * external files based on size thresholds.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { RawAttachment } from \"../../types/raw\";\nimport type { Attachment } from \"../../types/test-result\";\n\n/** Default max embed size: 512KB */\nconst DEFAULT_MAX_EMBED_BYTES = 512 * 1024;\n\n/** Options for attachment resolution */\nexport interface AttachmentOptions {\n /** Max bytes before attachment becomes external link. Default: 512KB */\n maxEmbedBytes?: number;\n /** Directory for external attachments */\n externalDir?: string;\n /** Project root for relative paths */\n projectRoot?: string;\n}\n\n/**\n * Resolve a raw attachment to a canonical attachment.\n *\n * Decision logic:\n * 1. If body is already provided, use it (check size for encoding decision)\n * 2. If path is provided, read file and decide embed vs link\n * 3. For large files, return a URL reference instead of embedding\n *\n * @param raw - Raw attachment from framework\n * @param options - Resolution options\n * @returns Resolved canonical attachment\n */\nexport function resolveAttachment(\n raw: RawAttachment,\n options: AttachmentOptions = {}\n): Attachment {\n const maxBytes = options.maxEmbedBytes ?? DEFAULT_MAX_EMBED_BYTES;\n\n // If we already have a body, use it\n if (raw.body) {\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: raw.body,\n contentEncoding: raw.encoding ?? \"BASE64\",\n };\n }\n\n // If we have a path, read and potentially embed\n if (raw.path) {\n return resolveFromPath(raw, maxBytes, options);\n }\n\n // No body or path - return empty attachment\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: \"\",\n contentEncoding: \"IDENTITY\",\n };\n}\n\n/**\n * Resolve attachment from file path.\n */\nfunction resolveFromPath(\n raw: RawAttachment,\n maxBytes: number,\n options: AttachmentOptions\n): Attachment {\n const filePath = raw.path!;\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(options.projectRoot ?? process.cwd(), filePath);\n\n // Check if file exists\n if (!fs.existsSync(absolutePath)) {\n // File doesn't exist - return path as URL reference\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: filePath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Get file size\n const stats = fs.statSync(absolutePath);\n const byteLength = raw.byteLength ?? stats.size;\n\n // If too large, return as URL reference\n if (byteLength > maxBytes) {\n // Copy to external dir if specified\n if (options.externalDir) {\n const destPath = copyToExternalDir(absolutePath, options.externalDir);\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: destPath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Return relative path as URL\n const relativePath = options.projectRoot\n ? path.relative(options.projectRoot, absolutePath)\n : filePath;\n\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: relativePath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Embed as base64\n const content = fs.readFileSync(absolutePath);\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: content.toString(\"base64\"),\n contentEncoding: \"BASE64\",\n };\n}\n\n/**\n * Copy file to external directory and return the destination path.\n */\nfunction copyToExternalDir(sourcePath: string, externalDir: string): string {\n // Ensure external dir exists\n if (!fs.existsSync(externalDir)) {\n fs.mkdirSync(externalDir, { recursive: true });\n }\n\n const filename = path.basename(sourcePath);\n const destPath = path.join(externalDir, filename);\n\n // Handle filename conflicts\n let finalPath = destPath;\n let counter = 1;\n while (fs.existsSync(finalPath)) {\n const ext = path.extname(filename);\n const base = path.basename(filename, ext);\n finalPath = path.join(externalDir, `${base}-${counter}${ext}`);\n counter++;\n }\n\n fs.copyFileSync(sourcePath, finalPath);\n return finalPath;\n}\n\n/**\n * Resolve multiple attachments.\n *\n * @param attachments - Raw attachments array\n * @param options - Resolution options\n * @returns Resolved canonical attachments\n */\nexport function resolveAttachments(\n attachments: RawAttachment[] | undefined,\n options: AttachmentOptions = {}\n): Attachment[] {\n if (!attachments || attachments.length === 0) {\n return [];\n }\n\n return attachments.map((raw) => resolveAttachment(raw, options));\n}\n","/**\n * Anti-Corruption Layer (ACL) - Layer 2.\n *\n * Transforms permissive RawRun data from framework adapters into\n * strict canonical TestRunResult for formatters.\n */\n\nimport type { StoryMeta, NormalizedTicket } from \"../../types/story\";\nimport type { RawRun, RawTestCase } from \"../../types/raw\";\nimport type {\n TestRunResult,\n TestCaseResult,\n TestStatus,\n} from \"../../types/test-result\";\nimport type { CanonicalizeOptions } from \"../../types/options\";\nimport { normalizeStatus } from \"./status\";\nimport { generateTestCaseId, generateRunId } from \"./ids\";\nimport { deriveStepResults, mergeStepResults } from \"./steps\";\nimport { resolveAttachments } from \"./attachments\";\n\n/**\n * Canonicalize a raw run into a strict TestRunResult.\n *\n * This is the main entry point for the ACL. It:\n * - Enforces required fields with defaults\n * - Normalizes statuses to TestStatus enum\n * - Applies step fallback rules\n * - Resolves attachments (embed vs link)\n * - Generates deterministic IDs\n *\n * @param raw - Raw run data from a framework adapter\n * @param options - Canonicalization options\n * @returns Strict canonical TestRunResult\n */\nexport function canonicalizeRun(\n raw: RawRun,\n options: CanonicalizeOptions = {}\n): TestRunResult {\n const now = Date.now();\n const startedAtMs = raw.startedAtMs ?? options.defaults?.startedAtMs ?? now;\n const finishedAtMs = raw.finishedAtMs ?? options.defaults?.finishedAtMs ?? now;\n\n const runId = generateRunId(startedAtMs, raw.projectRoot);\n\n const testCases = raw.testCases\n .filter((tc) => tc.story != null)\n .map((tc) => canonicalizeTestCase(tc, options, raw.projectRoot));\n\n return {\n testCases,\n startedAtMs,\n finishedAtMs,\n durationMs: finishedAtMs - startedAtMs,\n projectRoot: raw.projectRoot,\n runId,\n packageVersion: raw.packageVersion,\n gitSha: raw.gitSha,\n ci: raw.ci,\n };\n}\n\n/**\n * Canonicalize a single test case.\n */\nfunction canonicalizeTestCase(\n raw: RawTestCase,\n options: CanonicalizeOptions,\n projectRoot: string\n): TestCaseResult {\n const story = raw.story!;\n const sourceFile = raw.sourceFile ?? \"unknown\";\n const scenario = story.scenario ?? raw.title ?? \"Unknown Scenario\";\n\n // Generate deterministic ID\n const id = generateTestCaseId(sourceFile, scenario);\n\n // Normalize status\n const status = normalizeStatus(raw.status);\n\n // Derive step results\n const derivedSteps = deriveStepResults(story.steps ?? [], status, raw.error);\n const stepResults = mergeStepResults(\n derivedSteps,\n raw.stepEvents?.map((e) => ({\n index: e.index,\n status: e.status,\n durationMs: e.durationMs,\n errorMessage: e.errorMessage,\n }))\n );\n\n // Resolve attachments\n const attachments = resolveAttachments(raw.attachments, {\n maxEmbedBytes: options.attachments?.maxEmbedBytes,\n externalDir: options.attachments?.externalDir,\n projectRoot,\n });\n\n // Normalize tags\n const tags = normalizeTags(story);\n\n // Normalize tickets (raw JSON may have plain strings)\n if (story.tickets) {\n story.tickets = normalizeTickets(story.tickets as unknown as (string | NormalizedTicket)[]);\n }\n\n // Build title path\n const titlePath = buildTitlePath(raw, story);\n\n return {\n id,\n story,\n sourceFile,\n sourceLine: raw.sourceLine ?? 1,\n status,\n durationMs: raw.durationMs ?? 0,\n errorMessage: raw.error?.message,\n errorStack: raw.error?.stack,\n attachments,\n stepResults,\n titlePath,\n projectName: raw.projectName,\n retry: raw.retry ?? 0,\n retries: raw.retries ?? 0,\n tags,\n };\n}\n\n/**\n * Normalize tags from story metadata.\n *\n * - Ensures array format\n * - Deduplicates\n * - Sorts alphabetically\n */\nfunction normalizeTags(story: StoryMeta): string[] {\n const tags = story.tags ?? [];\n return [...new Set(tags)].sort();\n}\n\n/**\n * Normalize raw tickets to NormalizedTicket objects.\n *\n * Raw JSON may contain plain strings (from language packages or older adapters)\n * or objects with {id, url}. This ensures a uniform shape for formatters.\n */\nfunction normalizeTickets(raw: (string | NormalizedTicket)[]): NormalizedTicket[] {\n return raw.map((t) => (typeof t === \"string\" ? { id: t } : t));\n}\n\n/**\n * Build title path from raw test case and story metadata.\n *\n * Prefers story.suitePath if available, falls back to raw.titlePath.\n */\nfunction buildTitlePath(raw: RawTestCase, story: StoryMeta): string[] {\n if (story.suitePath && story.suitePath.length > 0) {\n return story.suitePath;\n }\n\n if (raw.titlePath && raw.titlePath.length > 0) {\n // Exclude the last element (test name) if titlePath includes it\n const withoutTestName = raw.titlePath.slice(0, -1);\n return withoutTestName.length > 0 ? withoutTestName : [];\n }\n\n return [];\n}\n\n// Re-export helpers for advanced usage\nexport { normalizeStatus } from \"./status\";\nexport { generateTestCaseId, generateRunId, slugify } from \"./ids\";\nexport { deriveStepResults, mergeStepResults } from \"./steps\";\nexport { resolveAttachment, resolveAttachments } from \"./attachments\";\n","/**\n * Validation helpers for canonical TestRunResult.\n *\n * Used in tests to verify ACL output meets all invariants.\n */\n\nimport type { TestRunResult, TestCaseResult } from \"../../types/test-result\";\n\n/** Validation result */\nexport interface ValidationResult {\n /** Whether the run is valid */\n valid: boolean;\n /** List of validation errors */\n errors: string[];\n}\n\n/**\n * Validate a canonical TestRunResult.\n *\n * Checks:\n * - All required fields are present\n * - stepResults length matches story.steps length\n * - stepResults indexes are valid and unique\n * - Durations are non-negative\n * - Timestamps are valid\n *\n * @param run - The TestRunResult to validate\n * @returns Validation result with errors if any\n */\nexport function validateCanonicalRun(run: TestRunResult): ValidationResult {\n const errors: string[] = [];\n\n // Validate run-level fields\n if (!run.runId) {\n errors.push(\"Run missing runId\");\n }\n\n if (!run.projectRoot) {\n errors.push(\"Run missing projectRoot\");\n }\n\n if (typeof run.startedAtMs !== \"number\" || run.startedAtMs < 0) {\n errors.push(`Invalid startedAtMs: ${run.startedAtMs}`);\n }\n\n if (typeof run.finishedAtMs !== \"number\" || run.finishedAtMs < 0) {\n errors.push(`Invalid finishedAtMs: ${run.finishedAtMs}`);\n }\n\n if (run.finishedAtMs < run.startedAtMs) {\n errors.push(`finishedAtMs (${run.finishedAtMs}) < startedAtMs (${run.startedAtMs})`);\n }\n\n if (typeof run.durationMs !== \"number\" || run.durationMs < 0) {\n errors.push(`Invalid durationMs: ${run.durationMs}`);\n }\n\n if (!Array.isArray(run.testCases)) {\n errors.push(\"testCases is not an array\");\n } else {\n // Validate each test case\n for (let i = 0; i < run.testCases.length; i++) {\n const tcErrors = validateTestCase(run.testCases[i], i);\n errors.push(...tcErrors);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Validate a single test case.\n */\nfunction validateTestCase(tc: TestCaseResult, index: number): string[] {\n const errors: string[] = [];\n const prefix = `TestCase[${index}]`;\n\n // Required fields\n if (!tc.id) {\n errors.push(`${prefix}: missing id`);\n }\n\n if (!tc.story) {\n errors.push(`${prefix}: missing story`);\n return errors; // Can't validate further without story\n }\n\n if (!tc.sourceFile) {\n errors.push(`${prefix}: missing sourceFile`);\n }\n\n if (typeof tc.sourceLine !== \"number\" || tc.sourceLine < 1) {\n errors.push(`${prefix}: invalid sourceLine ${tc.sourceLine}`);\n }\n\n // Status must be valid enum value\n const validStatuses = [\"passed\", \"failed\", \"skipped\", \"pending\"];\n if (!validStatuses.includes(tc.status)) {\n errors.push(`${prefix}: invalid status \"${tc.status}\"`);\n }\n\n // Duration must be non-negative\n if (typeof tc.durationMs !== \"number\" || tc.durationMs < 0) {\n errors.push(`${prefix}: invalid durationMs ${tc.durationMs}`);\n }\n\n // Retry fields\n if (typeof tc.retry !== \"number\" || tc.retry < 0) {\n errors.push(`${prefix}: invalid retry ${tc.retry}`);\n }\n\n if (typeof tc.retries !== \"number\" || tc.retries < 0) {\n errors.push(`${prefix}: invalid retries ${tc.retries}`);\n }\n\n // Arrays must be arrays\n if (!Array.isArray(tc.attachments)) {\n errors.push(`${prefix}: attachments is not an array`);\n }\n\n if (!Array.isArray(tc.titlePath)) {\n errors.push(`${prefix}: titlePath is not an array`);\n }\n\n if (!Array.isArray(tc.tags)) {\n errors.push(`${prefix}: tags is not an array`);\n }\n\n // Validate stepResults\n if (!Array.isArray(tc.stepResults)) {\n errors.push(`${prefix}: stepResults is not an array`);\n } else {\n const stepsCount = tc.story.steps?.length ?? 0;\n\n // stepResults length should match story.steps length\n if (tc.stepResults.length !== stepsCount) {\n errors.push(\n `${prefix}: stepResults.length (${tc.stepResults.length}) !== story.steps.length (${stepsCount})`\n );\n }\n\n // Validate each step result\n const seenIndexes = new Set<number>();\n for (const sr of tc.stepResults) {\n // Index must be valid\n if (typeof sr.index !== \"number\" || sr.index < 0 || sr.index >= stepsCount) {\n errors.push(`${prefix}: invalid stepResult index ${sr.index}`);\n }\n\n // Index must be unique\n if (seenIndexes.has(sr.index)) {\n errors.push(`${prefix}: duplicate stepResult index ${sr.index}`);\n }\n seenIndexes.add(sr.index);\n\n // Status must be valid\n if (!validStatuses.includes(sr.status)) {\n errors.push(`${prefix}: invalid stepResult status \"${sr.status}\" at index ${sr.index}`);\n }\n\n // Duration must be non-negative\n if (typeof sr.durationMs !== \"number\" || sr.durationMs < 0) {\n errors.push(`${prefix}: invalid stepResult durationMs ${sr.durationMs} at index ${sr.index}`);\n }\n }\n }\n\n return errors;\n}\n\n/**\n * Assert that a run is valid, throwing if not.\n *\n * Useful in tests.\n *\n * @param run - The TestRunResult to validate\n * @throws Error if validation fails\n */\nexport function assertValidRun(run: TestRunResult): void {\n const result = validateCanonicalRun(run);\n if (!result.valid) {\n throw new Error(`Invalid TestRunResult:\\n${result.errors.join(\"\\n\")}`);\n }\n}\n","/**\n * @executable-stories/formatters\n *\n * Cucumber-compatible report formats (JSON, HTML, JUnit, Markdown)\n * for Jest, Vitest, and Playwright test results.\n *\n * Architecture:\n * - Layer 1: Framework Adapters (adaptJestRun, adaptVitestRun, adaptPlaywrightRun)\n * - Layer 2: Anti-Corruption Layer (canonicalizeRun)\n * - Layer 3: Formatters (CucumberJsonFormatter, HtmlFormatter, JUnitFormatter, MarkdownFormatter)\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport * as fsPromises from \"node:fs/promises\";\nimport type { TestRunResult, TestCaseResult } from \"./types/test-result\";\nimport type {\n FormatterOptions,\n ResolvedFormatterOptions,\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n Logger,\n WriteFile,\n CanonicalizeOptions,\n SortTestCasesMode,\n} from \"./types/options\";\nimport type { RawRun } from \"./types/raw\";\nimport type { RunDiffResult } from \"./types/compare\";\n\nimport { canonicalizeRun } from \"./converters/acl/index\";\nimport { CucumberJsonFormatter } from \"./formatters/cucumber-json\";\nimport { HtmlFormatter } from \"./formatters/html/index\";\nimport { JUnitFormatter } from \"./formatters/junit-xml\";\nimport { MarkdownFormatter } from \"./formatters/markdown\";\nimport { CucumberMessagesFormatter } from \"./formatters/cucumber-messages/index\";\nimport { CucumberHtmlFormatter } from \"./formatters/cucumber-html\";\nimport { diffRuns } from \"./compare/index\";\nimport { RunDiffHtmlFormatter } from \"./formatters/run-diff-html\";\nimport { RunDiffMarkdownFormatter } from \"./formatters/run-diff-markdown\";\nimport { matchesPattern, selectTestCases } from \"./select-test-cases\";\nimport { bundleAssets } from \"./bundler/bundle-assets\";\nimport { AstroFormatter } from \"./formatters/astro\";\nimport { copyMarkdownAssets } from \"./formatters/astro-assets\";\n\n// Import adapters for convenience functions\nimport { adaptJestRun } from \"./converters/adapters/jest\";\nimport { adaptVitestRun } from \"./converters/adapters/vitest\";\nimport { adaptPlaywrightRun } from \"./converters/adapters/playwright\";\n\n// ============================================================================\n// Type Exports\n// ============================================================================\n\n// Story types (shared vocabulary for all adapters)\nexport type {\n StepKeyword,\n StepMode,\n DocPhase,\n DocEntry,\n StoryStep,\n StoryMeta,\n NormalizedTicket,\n} from \"./types/story\";\nexport { STORY_META_KEY } from \"./types/story\";\n\n// OTel span types (trace waterfall rendering)\nexport type { OtelSpan, OtelAttributeValue } from \"./types/otel\";\n\n// Canonical types (Layer 2 output - what formatters accept)\nexport type {\n TestStatus,\n StepResult,\n Attachment,\n TestCaseResult,\n TestCaseAttempt,\n CIInfo,\n CoverageSummary,\n TestRunResult,\n} from \"./types/test-result\";\n\n// Raw types (Layer 1 - for adapter authors)\nexport type {\n RawStatus,\n RawAttachment,\n RawStepEvent,\n RawTestCase,\n RawCIInfo,\n RawRun,\n} from \"./types/raw\";\n\n// Cucumber JSON types (Layer 3 output)\nexport type {\n IJsonTag,\n IJsonDocString,\n IJsonTableRow,\n IJsonDataTable,\n IJsonStepArgument,\n IJsonEmbedding,\n IJsonStepResult,\n IJsonStep,\n IJsonScenario,\n IJsonFeature,\n} from \"./types/cucumber-json\";\n\n// Options types\nexport type {\n CanonicalizeOptions,\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n OutputConfig,\n Logger,\n WriteFile,\n MarkdownFormatterOptions,\n MarkdownRenderers,\n FormatterOptions,\n ResolvedFormatterOptions,\n SortTestCasesMode,\n} from \"./types/options\";\nexport type {\n ScenarioChangeKind,\n ScenarioChangeFlags,\n ScenarioSnapshot,\n ScenarioDiff,\n RunDiffSummary,\n RunDiffResult,\n CompareFormat,\n CompareFormatterOptions,\n} from \"./types/compare\";\n\n// Theme types\nexport type { HtmlTheme, HtmlThemeName } from \"./formatters/html/themes/index\";\nexport { resolveTheme, getAvailableThemes, getCssOnlyThemes } from \"./formatters/html/themes/index\";\n\n// ============================================================================\n// ACL Exports\n// ============================================================================\n\nexport { canonicalizeRun } from \"./converters/acl/index\";\n\n/** @internal */\nexport { normalizeStatus } from \"./converters/acl/index\";\n/** @internal */\nexport { generateTestCaseId } from \"./converters/acl/index\";\n/** @internal */\nexport { generateRunId } from \"./converters/acl/index\";\n/** @internal */\nexport { slugify } from \"./converters/acl/index\";\n/** @internal */\nexport { deriveStepResults } from \"./converters/acl/index\";\n/** @internal */\nexport { mergeStepResults } from \"./converters/acl/index\";\n/** @internal */\nexport { resolveAttachment } from \"./converters/acl/index\";\n/** @internal */\nexport { resolveAttachments } from \"./converters/acl/index\";\n\nexport {\n validateCanonicalRun,\n assertValidRun,\n type ValidationResult,\n} from \"./converters/acl/validate\";\n\n// ============================================================================\n// Formatter Exports\n// ============================================================================\n\nexport {\n CucumberJsonFormatter,\n type CucumberJsonOptions,\n} from \"./formatters/cucumber-json\";\n\nexport {\n HtmlFormatter,\n type HtmlOptions,\n} from \"./formatters/html/index\";\n\nexport {\n JUnitFormatter,\n type JUnitOptions,\n} from \"./formatters/junit-xml\";\n\nexport {\n MarkdownFormatter,\n type MarkdownOptions,\n} from \"./formatters/markdown\";\n\nexport {\n AstroFormatter,\n type AstroFormatterOptions as AstroFormatterOpts,\n type StarlightBadge,\n} from \"./formatters/astro\";\n\nexport {\n copyMarkdownAssets,\n rewriteAssetPaths,\n type AstroAssetResult,\n type CopyMarkdownAssetsOptions,\n} from \"./formatters/astro-assets\";\n\nexport {\n CucumberMessagesFormatter,\n type CucumberMessagesOptions,\n} from \"./formatters/cucumber-messages/index\";\n\nexport {\n CucumberHtmlFormatter,\n type CucumberHtmlOptions,\n} from \"./formatters/cucumber-html\";\n\nexport {\n RunDiffHtmlFormatter,\n type RunDiffHtmlOptions,\n} from \"./formatters/run-diff-html\";\n\nexport {\n RunDiffMarkdownFormatter,\n type RunDiffMarkdownOptions,\n} from \"./formatters/run-diff-markdown\";\n\n// ============================================================================\n// NDJSON Parser (compat path: NDJSON → TestRunResult)\n// ============================================================================\n\nexport { parseNdjson, parseEnvelopes } from \"./converters/ndjson-parser\";\n\n// ============================================================================\n// Utility Exports\n// ============================================================================\n\n/** @internal */\nexport { readGitSha } from \"./utils/git-info\";\n/** @internal */\nexport { findGitDir } from \"./utils/git-info\";\n/** @internal */\nexport { readBranchName } from \"./utils/git-info\";\n/** @internal */\nexport { formatDuration } from \"./utils/duration\";\n/** @internal */\nexport { msToNanoseconds } from \"./utils/duration\";\n/** @internal */\nexport { nanosecondsToMs } from \"./utils/duration\";\n/** @internal */\nexport { readPackageVersion } from \"./utils/metadata\";\n/** @internal */\nexport { clearVersionCache } from \"./utils/metadata\";\nexport { detectCI } from \"./utils/ci-detect\";\nexport {\n tryGetActiveOtelContext,\n resolveTraceUrl,\n type OtelTraceContext,\n} from \"./utils/otel-detect\";\n\n// ============================================================================\n// Notifier Exports\n// ============================================================================\n\nexport { sendNotifications } from \"./notifiers/index\";\nexport { sendSlackNotification } from \"./notifiers/slack\";\nexport { sendTeamsNotification } from \"./notifiers/teams\";\nexport { sendWebhookNotification } from \"./notifiers/webhook\";\nexport { signBody } from \"./notifiers/hmac\";\nexport { stripAnsi } from \"./notifiers/ansi-strip\";\nexport type { NotificationSummary, NotifyCondition, GenericWebhookNotifierOptions, WebhookSignerHmac, WebhookPayload } from \"./notifiers/types\";\n\n// ============================================================================\n// CI Type Exports\n// ============================================================================\n\nexport type { CIProvider, CIInfo as TypedCIInfo } from \"./types/ci\";\nexport { toCIInfo, toRawCIInfo } from \"./types/ci\";\n\n// ============================================================================\n// History Exports\n// ============================================================================\n\nexport {\n loadHistory,\n saveHistory,\n updateHistory,\n calculateFlakiness,\n detectPerformanceTrend,\n calculateStability,\n computeTestMetrics,\n MIN_PERF_SAMPLES,\n MIN_METRIC_SAMPLES,\n MIN_FLAKINESS_SAMPLES,\n hasSufficientHistory,\n} from \"./history/index\";\n\nexport type {\n HistoryEntry,\n TestHistory,\n HistoryStore,\n StabilityGrade,\n FlakinessLevel,\n PerformanceTrend,\n TestMetrics,\n} from \"./history/index\";\n\n// ============================================================================\n// List Scenarios\n// ============================================================================\n\nexport { listScenarios } from \"./list-scenarios\";\nexport type { ListScenariosArgs, ListScenariosDeps } from \"./list-scenarios\";\n\n// ============================================================================\n// ReportGenerator Types (fn(args, deps) pattern)\n// ============================================================================\n\n/** Arguments for generate function */\nexport interface GenerateArgs {\n /** Canonical test run result */\n run: TestRunResult;\n /** Optional options override */\n options?: FormatterOptions;\n}\n\n/** Dependencies for generate function (injectable for testing) */\nexport interface GenerateDeps {\n /** Logger for warnings */\n logger: Logger;\n /** File writer function */\n writeFile: WriteFile;\n}\n\n/** Result of generate function: Map of format to array of file paths */\nexport type GenerateResult = Map<OutputFormat, string[]>;\n\nexport interface GenerateCompareResult {\n files: string[];\n diff: RunDiffResult;\n}\n\n/** Extension map for output formats */\nconst FORMAT_EXTENSIONS: Record<OutputFormat, string> = {\n astro: \".md\",\n markdown: \".md\",\n html: \".html\",\n \"cucumber-html\": \".cucumber.html\",\n junit: \".junit.xml\",\n \"cucumber-json\": \".cucumber.json\",\n \"cucumber-messages\": \".ndjson\",\n};\n\n/** Known test file extensions to strip for colocated naming */\nconst TEST_EXTENSIONS = [\n \".test.ts\", \".test.tsx\", \".spec.ts\", \".spec.tsx\",\n \".test.js\", \".spec.js\", \".story.test.ts\", \".story.spec.ts\",\n];\n\n// ============================================================================\n// Pure Functions for Output Routing\n// ============================================================================\n\n/**\n * Find the first matching rule for a source file.\n */\nfunction findMatchingRule(\n sourceFile: string,\n rules: OutputRule[]\n): OutputRule | undefined {\n for (const rule of rules) {\n if (matchesPattern(rule.match, sourceFile)) {\n return rule;\n }\n }\n return undefined;\n}\n\n/**\n * Normalize path to posix format (forward slashes).\n */\nfunction toPosix(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Compute output path for a test case based on mode and settings.\n */\nfunction computeOutputPath(\n sourceFile: string,\n format: OutputFormat,\n mode: OutputMode,\n colocatedStyle: ColocatedStyle,\n baseOutputDir: string,\n outputName: string,\n outputNameSuffix?: string\n): string {\n const ext = FORMAT_EXTENSIONS[format];\n const effectiveName = outputName + (outputNameSuffix ?? \"\");\n\n if (mode === \"aggregated\") {\n // Aggregated: single file in outputDir\n return toPosix(path.join(baseOutputDir, `${effectiveName}${ext}`));\n }\n\n // Colocated mode - normalize source file to posix first\n const normalizedSource = toPosix(sourceFile);\n const dirOfSource = path.posix.dirname(normalizedSource);\n let baseName = path.posix.basename(normalizedSource);\n\n // Strip test extension\n for (const testExt of TEST_EXTENSIONS) {\n if (baseName.endsWith(testExt)) {\n baseName = baseName.slice(0, -testExt.length);\n break;\n }\n }\n\n const fileName = `${baseName}.${effectiveName}${ext}`;\n\n if (colocatedStyle === \"adjacent\") {\n // Adjacent: write next to source file (ignores outputDir)\n return toPosix(path.posix.join(dirOfSource, fileName));\n }\n\n // Mirrored: preserve directory structure under outputDir\n return toPosix(path.posix.join(baseOutputDir, dirOfSource, fileName));\n}\n\n/**\n * Group test cases by their computed output path.\n */\nfunction groupTestCasesByOutput(\n testCases: TestCaseResult[],\n format: OutputFormat,\n options: ResolvedFormatterOptions,\n logger: Logger,\n outputNameSuffix?: string\n): Map<string, TestCaseResult[]> {\n const groups = new Map<string, TestCaseResult[]>();\n const rules = options.output.rules;\n const defaultMode = options.output.mode;\n const defaultColocatedStyle = options.output.colocatedStyle;\n const defaultFormats = options.formats;\n const defaultOutputDir = options.outputDir;\n const defaultOutputName = options.outputName;\n\n for (const tc of testCases) {\n const sourceFile = tc.sourceFile;\n\n // Check if colocated mode but missing sourceFile\n if (defaultMode === \"colocated\" && sourceFile === \"unknown\") {\n logger.warn(\n `Test case \"${tc.story.scenario}\" missing sourceFile, falling back to aggregated`\n );\n }\n\n // Find matching rule\n const rule = findMatchingRule(sourceFile, rules);\n\n // Determine effective settings (first match wins, fall back to defaults)\n const mode = rule?.mode ?? defaultMode;\n const colocatedStyle = rule?.colocatedStyle ?? defaultColocatedStyle;\n const formats = rule?.formats ?? defaultFormats;\n const outputDir = rule?.outputDir ?? defaultOutputDir;\n const outputName = rule?.outputName ?? options.output.outputName ?? defaultOutputName;\n\n // Warn if rule sets both adjacent style and outputDir\n if (\n rule &&\n rule.colocatedStyle === \"adjacent\" &&\n rule.outputDir !== undefined\n ) {\n logger.warn(\n `Rule for \"${rule.match}\" sets both colocatedStyle: \"adjacent\" and outputDir. outputDir will be ignored for adjacent mode.`\n );\n }\n\n // Skip if format not in effective formats\n if (!formats.includes(format)) {\n continue;\n }\n\n // Handle missing sourceFile in colocated mode\n const effectiveMode =\n mode === \"colocated\" && sourceFile === \"unknown\" ? \"aggregated\" : mode;\n\n const outputPath = computeOutputPath(\n sourceFile,\n format,\n effectiveMode,\n colocatedStyle,\n outputDir,\n outputName,\n outputNameSuffix\n );\n\n const existing = groups.get(outputPath);\n if (existing) {\n existing.push(tc);\n } else {\n groups.set(outputPath, [tc]);\n }\n }\n\n return groups;\n}\n\n// ============================================================================\n// ReportGenerator\n// ============================================================================\n\n/**\n * High-level report generator that combines multiple formatters.\n *\n * Accepts ONLY canonical TestRunResult - use adapters + canonicalizeRun first.\n *\n * Supports output routing:\n * - Aggregated: All test cases in a single file\n * - Colocated mirrored: Files mirrored under outputDir preserving directory structure\n * - Colocated adjacent: Files written next to source files\n * - Rule-based: Different routing based on source file patterns\n */\nexport class ReportGenerator {\n private options: ResolvedFormatterOptions;\n private deps: GenerateDeps;\n\n constructor(options: FormatterOptions = {}, deps?: Partial<GenerateDeps>) {\n this.options = this.resolveOptions(options);\n this.deps = {\n logger: deps?.logger ?? console,\n writeFile: deps?.writeFile ?? ((p, c) => fsPromises.writeFile(p, c, \"utf8\")),\n };\n }\n\n /**\n * Resolve options with defaults.\n */\n private resolveOptions(options: FormatterOptions): ResolvedFormatterOptions {\n return {\n include: options.include ?? [],\n exclude: options.exclude ?? [],\n includeTags: options.includeTags ?? [],\n excludeTags: options.excludeTags ?? [],\n formats: options.formats ?? [\"cucumber-json\"],\n outputDir: options.outputDir ?? \"reports\",\n outputName: options.outputName ?? \"index\",\n outputNameTimestamp: options.outputNameTimestamp ?? false,\n sortTestCases: options.sortTestCases ?? \"none\",\n output: {\n mode: options.output?.mode ?? \"aggregated\",\n colocatedStyle: options.output?.colocatedStyle ?? \"mirrored\",\n rules: options.output?.rules ?? [],\n outputName: options.output?.outputName,\n },\n cucumberJson: {\n pretty: options.cucumberJson?.pretty ?? false,\n },\n cucumberMessages: {\n uriStrategy: options.cucumberMessages?.uriStrategy ?? \"sourceFile\",\n includeSynthetics: options.cucumberMessages?.includeSynthetics ?? true,\n idSalt: options.cucumberMessages?.idSalt ?? \"\",\n meta: options.cucumberMessages?.meta,\n },\n html: {\n title: options.html?.title ?? \"Test Results\",\n darkMode: options.html?.darkMode ?? true,\n searchable: options.html?.searchable ?? true,\n startCollapsed: options.html?.startCollapsed ?? false,\n embedScreenshots: options.html?.embedScreenshots ?? true,\n syntaxHighlighting: options.html?.syntaxHighlighting ?? true,\n mermaidEnabled: options.html?.mermaidEnabled ?? true,\n markdownEnabled: options.html?.markdownEnabled ?? true,\n permalinkBaseUrl: options.html?.permalinkBaseUrl,\n ticketUrlTemplate: options.html?.ticketUrlTemplate,\n theme: options.html?.theme ?? \"default\",\n tocEnabled: options.html?.tocEnabled ?? true,\n themePickerEnabled: options.html?.themePickerEnabled ?? false,\n },\n junit: {\n suiteName: options.junit?.suiteName ?? \"Test Suite\",\n includeOutput: options.junit?.includeOutput ?? true,\n },\n markdown: {\n title: options.markdown?.title ?? \"User Stories\",\n includeStatusIcons: options.markdown?.includeStatusIcons ?? true,\n includeMetadata: options.markdown?.includeMetadata ?? true,\n includeErrors: options.markdown?.includeErrors ?? true,\n scenarioHeadingLevel: options.markdown?.scenarioHeadingLevel ?? 3,\n stepStyle: options.markdown?.stepStyle ?? \"bullets\",\n groupBy: options.markdown?.groupBy ?? \"file\",\n sortScenarios: options.markdown?.sortScenarios ?? \"source\",\n suiteSeparator: options.markdown?.suiteSeparator ?? \" - \",\n includeFrontMatter: options.markdown?.includeFrontMatter ?? false,\n includeSummaryTable: options.markdown?.includeSummaryTable ?? false,\n permalinkBaseUrl: options.markdown?.permalinkBaseUrl,\n ticketUrlTemplate: options.markdown?.ticketUrlTemplate,\n traceUrlTemplate: options.markdown?.traceUrlTemplate,\n includeSourceLinks: options.markdown?.includeSourceLinks ?? true,\n customRenderers: options.markdown?.customRenderers,\n },\n astro: {\n assetsDir: options.astro?.assetsDir ?? \"public/stories/assets\",\n assetsBaseUrl: options.astro?.assetsBaseUrl ?? \"/stories/assets\",\n markdown: {\n title: options.astro?.markdown?.title ?? \"User Stories\",\n includeStatusIcons: options.astro?.markdown?.includeStatusIcons ?? true,\n includeErrors: options.astro?.markdown?.includeErrors ?? true,\n scenarioHeadingLevel: options.astro?.markdown?.scenarioHeadingLevel ?? 3,\n groupBy: options.astro?.markdown?.groupBy ?? \"file\",\n sortScenarios: options.astro?.markdown?.sortScenarios ?? \"source\",\n suiteSeparator: options.astro?.markdown?.suiteSeparator ?? \" - \",\n includeSourceLinks: options.astro?.markdown?.includeSourceLinks ?? true,\n permalinkBaseUrl: options.astro?.markdown?.permalinkBaseUrl,\n ticketUrlTemplate: options.astro?.markdown?.ticketUrlTemplate,\n traceUrlTemplate: options.astro?.markdown?.traceUrlTemplate,\n customRenderers: options.astro?.markdown?.customRenderers,\n },\n },\n assetMode: options.assetMode ?? \"none\",\n allowMissingAssets: options.allowMissingAssets ?? false,\n };\n }\n\n /**\n * Generate reports for a test run.\n *\n * @param run - Canonical TestRunResult (use canonicalizeRun to create from RawRun)\n * @returns Map of output format to generated file paths\n */\n async generate(run: TestRunResult): Promise<GenerateResult> {\n const testCases = selectTestCases(\n {\n testCases: run.testCases,\n include: this.options.include,\n exclude: this.options.exclude,\n includeTags: this.options.includeTags,\n excludeTags: this.options.excludeTags,\n sortTestCases: this.options.sortTestCases,\n },\n { logger: this.deps.logger }\n );\n\n const filteredRun: TestRunResult = { ...run, testCases };\n\n const results: GenerateResult = new Map();\n\n for (const format of this.options.formats) {\n const paths = await this.generateFormat(filteredRun, format);\n results.set(format, paths);\n }\n\n if (this.options.assetMode === \"copy\") {\n const htmlPaths = results.get(\"html\");\n if (htmlPaths) {\n for (const htmlPath of htmlPaths) {\n bundleAssets(htmlPath, {\n allowMissing: this.options.allowMissingAssets,\n });\n }\n }\n\n const astroPaths = results.get(\"astro\");\n if (astroPaths) {\n for (const mdPath of astroPaths) {\n const content = await fsPromises.readFile(mdPath, \"utf8\");\n const mdDir = path.dirname(mdPath);\n // assetsDir is resolved from CWD (same as outputDir), not relative to outputDir\n const assetsDir = path.resolve(this.options.astro.assetsDir);\n const result = copyMarkdownAssets({\n markdown: content,\n markdownDir: mdDir,\n assetsDir,\n assetsBaseUrl: this.options.astro.assetsBaseUrl,\n allowMissing: this.options.allowMissingAssets,\n });\n if (result.copiedCount > 0 || result.missingCount > 0) {\n await this.deps.writeFile(mdPath, result.markdown);\n }\n }\n }\n }\n\n return results;\n }\n\n /**\n * Generate reports for a single format.\n */\n private async generateFormat(\n run: TestRunResult,\n format: OutputFormat\n ): Promise<string[]> {\n const outputNameSuffix = this.options.outputNameTimestamp\n ? `-${Math.floor(run.startedAtMs / 1000)}`\n : undefined;\n\n // Group test cases by output path\n const groups = groupTestCasesByOutput(\n run.testCases,\n format,\n this.options,\n this.deps.logger,\n outputNameSuffix\n );\n\n // Handle empty runs in aggregated mode - write a single empty file\n if (groups.size === 0 && this.options.output.mode === \"aggregated\") {\n const ext = FORMAT_EXTENSIONS[format];\n const effectiveName = this.options.outputName + (outputNameSuffix ?? \"\");\n const outputPath = toPosix(path.join(this.options.outputDir, `${effectiveName}${ext}`));\n const content = await this.formatContent(run, format);\n const dir = path.dirname(outputPath);\n await fsPromises.mkdir(dir, { recursive: true });\n await this.deps.writeFile(outputPath, content);\n return [outputPath];\n }\n\n const writtenPaths: string[] = [];\n\n for (const [outputPath, testCases] of groups) {\n // Create a run with just these test cases\n const groupRun: TestRunResult = {\n ...run,\n testCases,\n };\n\n // Format content\n const content = await this.formatContent(groupRun, format);\n\n // Ensure directory exists\n const dir = path.dirname(outputPath);\n await fsPromises.mkdir(dir, { recursive: true });\n\n // Write file\n await this.deps.writeFile(outputPath, content);\n writtenPaths.push(outputPath);\n }\n\n return writtenPaths;\n }\n\n /**\n * Format content for a specific format.\n */\n private formatContent(run: TestRunResult, format: OutputFormat): string | Promise<string> {\n switch (format) {\n case \"cucumber-json\": {\n const formatter = new CucumberJsonFormatter({\n pretty: this.options.cucumberJson.pretty,\n });\n return formatter.formatToString(run);\n }\n\n case \"html\": {\n const formatter = new HtmlFormatter({\n title: this.options.html.title,\n theme: this.options.html.theme,\n darkMode: this.options.html.darkMode,\n searchable: this.options.html.searchable,\n startCollapsed: this.options.html.startCollapsed,\n embedScreenshots: this.options.html.embedScreenshots,\n syntaxHighlighting: this.options.html.syntaxHighlighting,\n mermaidEnabled: this.options.html.mermaidEnabled,\n markdownEnabled: this.options.html.markdownEnabled,\n permalinkBaseUrl: this.options.html.permalinkBaseUrl,\n ticketUrlTemplate: this.options.html.ticketUrlTemplate,\n tocEnabled: this.options.html.tocEnabled,\n themePickerEnabled: this.options.html.themePickerEnabled,\n });\n return formatter.format(run);\n }\n\n case \"cucumber-html\": {\n const formatter = new CucumberHtmlFormatter({\n messages: {\n uriStrategy: this.options.cucumberMessages.uriStrategy,\n includeSynthetics: this.options.cucumberMessages.includeSynthetics,\n idSalt: this.options.cucumberMessages.idSalt,\n meta: this.options.cucumberMessages.meta,\n },\n });\n return formatter.formatToString(run);\n }\n\n case \"junit\": {\n const formatter = new JUnitFormatter({\n suiteName: this.options.junit.suiteName,\n includeOutput: this.options.junit.includeOutput,\n });\n return formatter.format(run);\n }\n\n case \"cucumber-messages\": {\n const formatter = new CucumberMessagesFormatter({\n uriStrategy: this.options.cucumberMessages.uriStrategy,\n includeSynthetics: this.options.cucumberMessages.includeSynthetics,\n idSalt: this.options.cucumberMessages.idSalt,\n meta: this.options.cucumberMessages.meta,\n });\n return formatter.formatToString(run);\n }\n\n case \"astro\": {\n const formatter = new AstroFormatter({\n assetsBaseUrl: this.options.astro.assetsBaseUrl,\n markdown: this.options.astro.markdown,\n });\n return formatter.format(run);\n }\n\n case \"markdown\": {\n const formatter = new MarkdownFormatter({\n title: this.options.markdown.title,\n includeStatusIcons: this.options.markdown.includeStatusIcons,\n includeMetadata: this.options.markdown.includeMetadata,\n includeErrors: this.options.markdown.includeErrors,\n scenarioHeadingLevel: this.options.markdown.scenarioHeadingLevel,\n stepStyle: this.options.markdown.stepStyle,\n groupBy: this.options.markdown.groupBy,\n sortScenarios: this.options.markdown.sortScenarios,\n suiteSeparator: this.options.markdown.suiteSeparator,\n includeFrontMatter: this.options.markdown.includeFrontMatter,\n includeSummaryTable: this.options.markdown.includeSummaryTable,\n permalinkBaseUrl: this.options.markdown.permalinkBaseUrl,\n ticketUrlTemplate: this.options.markdown.ticketUrlTemplate,\n traceUrlTemplate: this.options.markdown.traceUrlTemplate,\n includeSourceLinks: this.options.markdown.includeSourceLinks,\n customRenderers: this.options.markdown.customRenderers,\n });\n return formatter.format(run);\n }\n\n default:\n throw new Error(`Unknown format: ${format}`);\n }\n }\n}\n\n/**\n * Factory function to create a ReportGenerator with dependency injection.\n *\n * Useful for testing and custom configurations.\n */\nexport function createReportGenerator(\n options?: FormatterOptions,\n deps?: Partial<GenerateDeps>\n): ReportGenerator {\n return new ReportGenerator(options, deps);\n}\n\nexport async function generateRunComparison(args: {\n baseline: TestRunResult;\n current: TestRunResult;\n formats: Array<\"html\" | \"markdown\">;\n outputDir?: string;\n outputName?: string;\n title?: string;\n}): Promise<GenerateCompareResult> {\n const outputDir = args.outputDir ?? \"reports\";\n const outputName = args.outputName ?? \"test-results-diff\";\n const diff = diffRuns(args.baseline, args.current);\n const files: string[] = [];\n\n await fsPromises.mkdir(outputDir, { recursive: true });\n\n for (const format of args.formats) {\n const ext = format === \"html\" ? \".html\" : \".md\";\n const outputPath = toPosix(path.join(outputDir, `${outputName}${ext}`));\n const content =\n format === \"html\"\n ? new RunDiffHtmlFormatter({ title: args.title }).format(diff)\n : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);\n await fsPromises.writeFile(outputPath, content, \"utf8\");\n files.push(outputPath);\n }\n\n return { files, diff };\n}\n\nexport { diffRuns } from \"./compare/index\";\nexport { createPrCommentSummary } from \"./compare/index\";\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n// Re-export adapters\nexport { adaptJestRun, adaptVitestRun, adaptPlaywrightRun };\n\n// ============================================================================\n// Bundler Exports\n// ============================================================================\n\nexport { bundleAssets } from \"./bundler/bundle-assets\";\nexport type { BundleOptions, BundleResult } from \"./bundler/bundle-assets\";\n\n// Re-export adapter types\nexport type {\n JestTestResult,\n JestFileResult,\n JestAggregatedResult,\n StoryFileReport,\n JestAdapterOptions,\n VitestState,\n VitestSerializedError,\n VitestTestResult,\n VitestTestCase,\n VitestTestModule,\n VitestAdapterOptions,\n PlaywrightStatus,\n PlaywrightError,\n PlaywrightAttachment,\n PlaywrightTestResult,\n PlaywrightAnnotation,\n PlaywrightLocation,\n PlaywrightTestCase,\n PlaywrightAdapterOptions,\n} from \"./converters/adapters/index\";\n\n/**\n * Normalize Jest results to canonical TestRunResult.\n *\n * Combines adaptJestRun + canonicalizeRun.\n */\nexport function normalizeJestResults(\n jestResults: Parameters<typeof adaptJestRun>[0],\n storyReports: Parameters<typeof adaptJestRun>[1],\n adapterOptions?: Parameters<typeof adaptJestRun>[2],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptJestRun(jestResults, storyReports, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n\n/**\n * Normalize Vitest results to canonical TestRunResult.\n *\n * Combines adaptVitestRun + canonicalizeRun.\n */\nexport function normalizeVitestResults(\n testModules: Parameters<typeof adaptVitestRun>[0],\n adapterOptions?: Parameters<typeof adaptVitestRun>[1],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptVitestRun(testModules, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n\n/**\n * Normalize Playwright results to canonical TestRunResult.\n *\n * Combines adaptPlaywrightRun + canonicalizeRun.\n */\nexport function normalizePlaywrightResults(\n testResults: Parameters<typeof adaptPlaywrightRun>[0],\n adapterOptions?: Parameters<typeof adaptPlaywrightRun>[1],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptPlaywrightRun(testResults, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n","/**\n * Deterministic line number generation for Cucumber JSON compatibility.\n *\n * When actual line numbers aren't available, we generate deterministic\n * line numbers based on position in the file.\n */\n\n/**\n * Generate deterministic line numbers for scenarios in a file.\n *\n * Starts at line 2 (after feature declaration) and increments by\n * steps count + buffer for each scenario.\n *\n * @param scenarioIndex - Index of the scenario in the file (0-based)\n * @param stepsCount - Number of steps in the scenario\n * @param prevEndLine - End line of previous scenario (default 1)\n * @returns Deterministic line number\n */\nexport function generateScenarioLine(\n scenarioIndex: number,\n stepsCount: number,\n prevEndLine: number = 1\n): number {\n // Each scenario needs:\n // - 1 line for scenario declaration\n // - n lines for steps\n // - 1 blank line after\n const linesPerScenario = 1 + stepsCount + 1;\n\n if (scenarioIndex === 0) {\n // First scenario starts at line 3 (Feature on 1, blank on 2)\n return 3;\n }\n\n return prevEndLine + linesPerScenario;\n}\n\n/**\n * Generate step line numbers for a scenario.\n *\n * @param scenarioLine - The scenario's line number\n * @param stepIndex - Index of the step (0-based)\n * @returns Line number for the step\n */\nexport function generateStepLine(scenarioLine: number, stepIndex: number): number {\n // Steps start on the line after the scenario declaration\n return scenarioLine + 1 + stepIndex;\n}\n\n/**\n * Context for tracking line numbers across multiple scenarios.\n */\nexport interface LineContext {\n /** Current line number */\n currentLine: number;\n}\n\n/**\n * Create a new line context starting at line 1.\n */\nexport function createLineContext(): LineContext {\n return { currentLine: 1 };\n}\n\n/**\n * Advance line context past a feature declaration.\n *\n * @param ctx - Line context\n * @returns Updated line context\n */\nexport function advancePastFeature(ctx: LineContext): LineContext {\n // Feature keyword on line 1, blank line after\n return { currentLine: 3 };\n}\n\n/**\n * Get the current scenario line and advance past it.\n *\n * @param ctx - Line context\n * @param stepsCount - Number of steps in the scenario\n * @returns Tuple of [scenarioLine, updatedContext]\n */\nexport function advancePastScenario(\n ctx: LineContext,\n stepsCount: number\n): [number, LineContext] {\n const scenarioLine = ctx.currentLine;\n // Advance past: scenario keyword + steps + blank line\n const newLine = scenarioLine + 1 + stepsCount + 1;\n return [scenarioLine, { currentLine: newLine }];\n}\n","/**\n * Cucumber JSON Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into Cucumber JSON format\n * compatible with cucumber-js v11.x output.\n */\n\nimport type { StoryStep, DocEntry } from \"../types/story\";\nimport type {\n TestRunResult,\n TestCaseResult,\n StepResult,\n Attachment,\n} from \"../types/test-result\";\nimport type {\n IJsonFeature,\n IJsonScenario,\n IJsonStep,\n IJsonTag,\n IJsonEmbedding,\n IJsonStepResult,\n} from \"../types/cucumber-json\";\nimport { slugify, generateFeatureId, generateScenarioId } from \"../converters/acl/ids\";\nimport { createLineContext, advancePastFeature, advancePastScenario, generateStepLine } from \"../converters/acl/lines\";\n\n/** Options for Cucumber JSON formatting */\nexport interface CucumberJsonOptions {\n /** Pretty-print JSON output. Default: false */\n pretty?: boolean;\n /** Include trailing space in keywords. Default: true */\n keywordSpacing?: boolean;\n}\n\n/**\n * Cucumber JSON Formatter.\n *\n * Transforms TestRunResult into an array of IJsonFeature objects\n * that match the Cucumber JSON format specification.\n */\nexport class CucumberJsonFormatter {\n private options: Required<CucumberJsonOptions>;\n\n constructor(options: CucumberJsonOptions = {}) {\n this.options = {\n pretty: options.pretty ?? false,\n keywordSpacing: options.keywordSpacing ?? true,\n };\n }\n\n /**\n * Format a test run into Cucumber JSON features.\n *\n * Groups test cases by source file (one feature per file).\n *\n * @param run - Canonical test run result\n * @returns Array of Cucumber JSON features\n */\n format(run: TestRunResult): IJsonFeature[] {\n // Group test cases by source file\n const byFile = new Map<string, TestCaseResult[]>();\n for (const tc of run.testCases) {\n const file = tc.sourceFile;\n const existing = byFile.get(file);\n if (existing) {\n existing.push(tc);\n } else {\n byFile.set(file, [tc]);\n }\n }\n\n // Build features\n const features: IJsonFeature[] = [];\n for (const [uri, testCases] of byFile) {\n features.push(this.buildFeature(uri, testCases));\n }\n\n return features;\n }\n\n /**\n * Format and serialize to JSON string.\n *\n * @param run - Canonical test run result\n * @returns JSON string\n */\n formatToString(run: TestRunResult): string {\n const features = this.format(run);\n return this.options.pretty\n ? JSON.stringify(features, null, 2)\n : JSON.stringify(features);\n }\n\n /**\n * Build a single feature from test cases in the same file.\n */\n private buildFeature(uri: string, testCases: TestCaseResult[]): IJsonFeature {\n const featureName = this.extractFeatureName(uri, testCases);\n const featureId = generateFeatureId(uri);\n\n // Extract feature-level tags (union of all scenario tags)\n const featureTags = this.extractFeatureTags(testCases);\n\n // Build scenarios with deterministic line numbers\n let lineCtx = createLineContext();\n lineCtx = advancePastFeature(lineCtx);\n\n const elements: IJsonScenario[] = [];\n for (const tc of testCases) {\n const [scenarioLine, nextCtx] = advancePastScenario(lineCtx, tc.story.steps.length);\n elements.push(this.buildScenario(tc, featureId, scenarioLine));\n lineCtx = nextCtx;\n }\n\n return {\n description: \"\",\n elements,\n id: featureId,\n keyword: \"Feature\",\n line: 1,\n name: featureName,\n tags: featureTags,\n uri,\n };\n }\n\n /**\n * Extract feature name from URI or test cases.\n *\n * Uses the top-level suite path if available, otherwise file name.\n */\n private extractFeatureName(uri: string, testCases: TestCaseResult[]): string {\n // Try to get common suite path prefix\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n\n if (suitePaths.length > 0) {\n // Use the first element of the first suite path as feature name\n const firstPath = suitePaths[0];\n if (firstPath.length > 0) {\n return firstPath[0];\n }\n }\n\n // Fall back to filename without extension\n const filename = uri.split(\"/\").pop() ?? uri;\n return filename.replace(/\\.[^.]+$/, \"\").replace(/[._-]/g, \" \");\n }\n\n /**\n * Extract feature-level tags from all test cases.\n */\n private extractFeatureTags(testCases: TestCaseResult[]): IJsonTag[] {\n // Collect all unique tags\n const allTags = new Set<string>();\n for (const tc of testCases) {\n for (const tag of tc.tags) {\n allTags.add(tag);\n }\n }\n\n // Convert to IJsonTag format (with @ prefix if not present)\n return [...allTags].sort().map((tag) => ({\n name: tag.startsWith(\"@\") ? tag : `@${tag}`,\n line: 1,\n }));\n }\n\n /**\n * Build a scenario from a test case.\n */\n private buildScenario(\n tc: TestCaseResult,\n featureId: string,\n scenarioLine: number\n ): IJsonScenario {\n const scenarioName = tc.story.scenario;\n const scenarioId = generateScenarioId(featureId, scenarioName);\n\n // Build steps\n const steps = this.buildSteps(tc, scenarioLine);\n\n // Build scenario tags\n const tags: IJsonTag[] = tc.tags.map((tag) => ({\n name: tag.startsWith(\"@\") ? tag : `@${tag}`,\n line: scenarioLine,\n }));\n\n return {\n description: \"\",\n id: scenarioId,\n keyword: \"Scenario\",\n line: scenarioLine,\n name: scenarioName,\n steps,\n tags,\n type: \"scenario\",\n };\n }\n\n /**\n * Build steps from story steps and step results.\n */\n private buildSteps(tc: TestCaseResult, scenarioLine: number): IJsonStep[] {\n const storySteps = tc.story.steps;\n const stepResults = tc.stepResults;\n\n // Create a map of step results by index\n const resultsByIndex = new Map<number, StepResult>();\n for (const sr of stepResults) {\n resultsByIndex.set(sr.index, sr);\n }\n\n // Check if any step failed (used to decide if last step gets attachments)\n const hasFailedStep = stepResults.some(sr => sr.status === \"failed\");\n\n const totalSteps = storySteps.length;\n return storySteps.map((step, index) => {\n const stepResult = resultsByIndex.get(index);\n const stepLine = generateStepLine(scenarioLine, index);\n const isLastStep = index === totalSteps - 1;\n return this.buildStep(step, stepResult, stepLine, index, tc.attachments, isLastStep, hasFailedStep);\n });\n }\n\n /**\n * Build a single step.\n */\n private buildStep(\n step: StoryStep,\n result: StepResult | undefined,\n line: number,\n index: number,\n attachments: Attachment[],\n isLastStep: boolean,\n hasFailedStep: boolean\n ): IJsonStep {\n // Keyword with optional trailing space\n const keyword = this.options.keywordSpacing\n ? `${step.keyword} `\n : step.keyword;\n\n // Build result\n const stepResult = this.buildStepResult(result);\n\n // Build embeddings for attachments (attach to failed step, or last step if no failure)\n const embeddings = this.buildEmbeddings(attachments, result, isLastStep, hasFailedStep);\n\n // Add screenshot docs as embeddings (always include with their step)\n const screenshotEmbeddings = this.buildScreenshotEmbeddings(step);\n embeddings.push(...screenshotEmbeddings);\n\n const jsonStep: IJsonStep = {\n keyword,\n line,\n name: step.text,\n result: stepResult,\n };\n\n // Only include optional fields if they have values\n if (embeddings.length > 0) {\n jsonStep.embeddings = embeddings;\n }\n\n // Add step arguments (doc strings from docs, data tables)\n const args = this.buildStepArguments(step);\n if (args.length > 0) {\n jsonStep.arguments = args;\n }\n\n return jsonStep;\n }\n\n /**\n * Build embeddings from screenshot doc entries.\n */\n private buildScreenshotEmbeddings(step: StoryStep): IJsonEmbedding[] {\n if (!step.docs) {\n return [];\n }\n\n const embeddings: IJsonEmbedding[] = [];\n\n for (const doc of step.docs) {\n if (doc.kind !== \"screenshot\" || !doc.path.startsWith(\"data:\")) {\n continue;\n }\n\n // Parse data URI: data:image/png;base64,ABC123...\n const match = doc.path.match(/^data:([^;]+);base64,(.+)$/);\n if (match) {\n embeddings.push({\n data: match[2],\n mime_type: match[1],\n name: doc.alt,\n });\n }\n }\n\n return embeddings;\n }\n\n /**\n * Build step result.\n */\n private buildStepResult(result: StepResult | undefined): IJsonStepResult {\n if (!result) {\n return {\n status: \"undefined\",\n duration: 0,\n };\n }\n\n // Map canonical status to Cucumber status\n const statusMap: Record<string, IJsonStepResult[\"status\"]> = {\n passed: \"passed\",\n failed: \"failed\",\n skipped: \"skipped\",\n pending: \"pending\",\n };\n\n const stepResult: IJsonStepResult = {\n status: statusMap[result.status] ?? \"undefined\",\n // Duration in nanoseconds (Cucumber uses nanoseconds)\n duration: result.durationMs * 1_000_000,\n };\n\n if (result.errorMessage) {\n stepResult.error_message = result.errorMessage;\n }\n\n return stepResult;\n }\n\n /**\n * Build embeddings (attachments) for a step.\n *\n * Cucumber convention: attach to the failing step, or last step if no failure.\n */\n private buildEmbeddings(\n attachments: Attachment[],\n result: StepResult | undefined,\n isLastStep: boolean,\n hasFailedStep: boolean\n ): IJsonEmbedding[] {\n const isFailed = result?.status === \"failed\";\n\n // If this step failed, attach here\n if (isFailed) {\n // Continue to attach\n } else if (isLastStep && !hasFailedStep) {\n // No failed step in scenario, attach to last step\n } else {\n // Not a failed step, and either not last or there's a failed step elsewhere\n return [];\n }\n\n // Only include BASE64 attachments - IDENTITY attachments are URLs/paths, not embeddable data\n const base64Attachments = attachments.filter(\n (att) => att.contentEncoding === \"BASE64\"\n );\n\n if (base64Attachments.length === 0) {\n return [];\n }\n\n return base64Attachments.map((att) => ({\n data: att.body,\n mime_type: att.mediaType,\n name: att.name,\n }));\n }\n\n /**\n * Build step arguments from step docs.\n *\n * Converts doc entries to Cucumber step arguments (doc strings, data tables).\n */\n private buildStepArguments(step: StoryStep): Array<{ doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> }> {\n if (!step.docs || step.docs.length === 0) {\n return [];\n }\n\n const args: Array<{ doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> }> = [];\n\n for (const doc of step.docs) {\n const arg = this.docEntryToArgument(doc);\n if (arg) {\n args.push(arg);\n }\n }\n\n return args;\n }\n\n /**\n * Convert a doc entry to a Cucumber step argument.\n */\n private docEntryToArgument(doc: DocEntry): { doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> } | null {\n switch (doc.kind) {\n case \"code\":\n return {\n doc_string: {\n content: doc.content,\n content_type: doc.lang,\n line: 0,\n },\n };\n\n case \"table\":\n return {\n rows: [\n { cells: doc.columns },\n ...doc.rows.map((row) => ({ cells: row })),\n ],\n };\n\n case \"note\":\n return {\n doc_string: {\n content: doc.text,\n content_type: \"text/plain\",\n line: 0,\n },\n };\n\n case \"mermaid\":\n return {\n doc_string: {\n content: doc.code,\n content_type: \"text/x-mermaid\",\n line: 0,\n },\n };\n\n case \"section\":\n return {\n doc_string: {\n content: `# ${doc.title}\\n\\n${doc.markdown}`,\n content_type: \"text/markdown\",\n line: 0,\n },\n };\n\n case \"link\":\n return {\n doc_string: {\n content: `[${doc.label}](${doc.url})`,\n content_type: \"text/markdown\",\n line: 0,\n },\n };\n\n case \"kv\": {\n const value = typeof doc.value === \"string\"\n ? doc.value\n : JSON.stringify(doc.value, null, 2);\n return {\n doc_string: {\n content: `${doc.label}: ${value}`,\n content_type: \"text/plain\",\n line: 0,\n },\n };\n }\n\n case \"tag\":\n return {\n doc_string: {\n content: doc.names.map((n) => `@${n}`).join(\" \"),\n content_type: \"text/plain\",\n line: 0,\n },\n };\n\n case \"custom\":\n return {\n doc_string: {\n content: JSON.stringify(doc.data, null, 2),\n content_type: \"application/json\",\n line: 0,\n },\n };\n\n case \"screenshot\":\n // Screenshots are handled as embeddings, not arguments\n return null;\n\n default:\n return null;\n }\n }\n}\n","/**\n * HTML Report Template.\n *\n * Generates the JavaScript for interactivity (theme toggle, search, collapse).\n */\n\n/** Theme-related JavaScript (only included when darkMode is enabled) */\nconst JS_THEME = `\n// Theme management\nfunction getSystemTheme() {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nfunction getEffectiveTheme() {\n const saved = localStorage.getItem('theme');\n if (saved === 'dark' || saved === 'light') return saved;\n return getSystemTheme();\n}\n\nfunction toggleTheme() {\n const current = getEffectiveTheme();\n const next = current === 'dark' ? 'light' : 'dark';\n localStorage.setItem('theme', next);\n applyTheme(next);\n}\n\nfunction applyTheme(theme) {\n document.documentElement.setAttribute('data-theme', theme);\n updateThemeIcon(theme);\n}\n\nfunction updateThemeIcon(theme) {\n const btn = document.querySelector('.theme-toggle');\n if (btn) {\n btn.textContent = theme === 'dark' ? '\\\\u2600\\\\ufe0f' : '\\\\ud83c\\\\udf19';\n btn.setAttribute('aria-label', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');\n }\n}\n\nfunction initTheme() {\n const theme = getEffectiveTheme();\n applyTheme(theme);\n\n // Listen for system theme changes\n window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {\n if (!localStorage.getItem('theme')) {\n applyTheme(e.matches ? 'dark' : 'light');\n }\n });\n}\n`;\n\n/** Core JavaScript (always included) */\nconst JS_CORE = `\n// Filter state\nvar activeTags = new Set();\nvar activeStatus = null;\nvar activeDetailLevel = 'full';\n\n// Search functionality\nfunction initSearch() {\n var input = document.querySelector('.search-input');\n if (!input) return;\n\n var debounceTimer;\n input.addEventListener('input', function() {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(function() {\n applyAllFilters();\n }, 150);\n });\n\n // Clear search on Escape\n input.addEventListener('keydown', function(e) {\n if (e.key === 'Escape') {\n e.target.value = '';\n applyAllFilters();\n }\n });\n}\n\n// Tag filter\nfunction initTagFilter() {\n var toggleBtn = document.querySelector('.tag-bar-toggle');\n var tagBar = document.querySelector('.tag-bar');\n if (toggleBtn && tagBar) {\n toggleBtn.addEventListener('click', function() {\n var isCollapsed = tagBar.classList.toggle('tag-bar-collapsed');\n toggleBtn.setAttribute('aria-expanded', String(!isCollapsed));\n });\n }\n\n document.querySelectorAll('.tag-pill').forEach(function(pill) {\n pill.addEventListener('click', function() {\n var tag = pill.dataset.tag;\n if (activeTags.has(tag)) {\n activeTags.delete(tag);\n pill.classList.remove('active');\n pill.setAttribute('aria-pressed', 'false');\n } else {\n activeTags.add(tag);\n pill.classList.add('active');\n pill.setAttribute('aria-pressed', 'true');\n }\n updateTagBarState();\n applyAllFilters();\n });\n });\n\n var clearBtn = document.querySelector('.tag-bar-clear');\n if (clearBtn) {\n clearBtn.addEventListener('click', function(e) {\n e.stopPropagation();\n activeTags.clear();\n document.querySelectorAll('.tag-pill.active').forEach(function(p) {\n p.classList.remove('active');\n p.setAttribute('aria-pressed', 'false');\n });\n updateTagBarState();\n applyAllFilters();\n });\n }\n}\n\nfunction updateTagBarState() {\n var clearBtn = document.querySelector('.tag-bar-clear');\n var countBadge = document.querySelector('.tag-bar-count');\n if (clearBtn) {\n clearBtn.style.display = activeTags.size > 0 ? '' : 'none';\n }\n if (countBadge) {\n countBadge.textContent = activeTags.size > 0 ? activeTags.size + ' selected' : '';\n }\n}\n\n// Status filter (clickable summary cards)\nfunction initStatusFilter() {\n document.querySelectorAll('.summary-card').forEach(function(card) {\n card.style.cursor = 'pointer';\n if (!card.classList.contains('passed') && !card.classList.contains('failed') && !card.classList.contains('skipped')) {\n card.addEventListener('click', function() {\n activeStatus = null;\n document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });\n applyAllFilters();\n });\n return;\n }\n card.addEventListener('click', function() {\n var status = card.classList.contains('passed') ? 'passed' :\n card.classList.contains('failed') ? 'failed' : 'skipped';\n if (activeStatus === status) {\n activeStatus = null;\n card.classList.remove('status-active');\n } else {\n activeStatus = status;\n document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });\n card.classList.add('status-active');\n }\n applyAllFilters();\n });\n });\n}\n\n// Unified filter: composes search + tags + status\nfunction applyAllFilters() {\n var searchInput = document.querySelector('.search-input');\n var searchQuery = searchInput ? searchInput.value.toLowerCase().trim() : '';\n var features = document.querySelectorAll('.feature');\n var visibleCount = 0;\n var totalCount = 0;\n\n features.forEach(function(feature) {\n var scenarios = feature.querySelectorAll('.scenario');\n var featureVisible = 0;\n\n scenarios.forEach(function(scenario) {\n totalCount++;\n var title = (scenario.querySelector('.scenario-title') || {}).textContent || '';\n title = title.toLowerCase();\n var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.toLowerCase(); });\n var steps = Array.from(scenario.querySelectorAll('.step-text')).map(function(s) { return s.textContent.toLowerCase(); });\n var statusEl = scenario.querySelector('.status-icon');\n var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :\n statusEl && statusEl.classList.contains('status-failed') ? 'failed' :\n statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';\n\n var matchesSearch = !searchQuery ||\n title.includes(searchQuery) ||\n tags.some(function(t) { return t.includes(searchQuery); }) ||\n steps.some(function(s) { return s.includes(searchQuery); });\n\n var matchesTags = activeTags.size === 0 ||\n tags.some(function(t) { return activeTags.has(t); });\n\n var matchesStatus = !activeStatus ||\n status === activeStatus ||\n (activeStatus === 'skipped' && status === 'pending');\n\n var visible = matchesSearch && matchesTags && matchesStatus;\n scenario.style.display = visible ? '' : 'none';\n if (visible) { visibleCount++; featureVisible++; }\n });\n\n feature.style.display = featureVisible > 0 ? '' : 'none';\n });\n\n updateFilterResults(visibleCount, totalCount);\n syncTocVisibility();\n writeUrlState();\n}\n\nfunction updateFilterResults(visible, total) {\n var el = document.querySelector('.filter-results');\n if (!el) return;\n var searchInput = document.querySelector('.search-input');\n var isFiltering = activeTags.size > 0 || activeStatus ||\n (searchInput && searchInput.value.trim().length > 0);\n el.style.display = isFiltering ? '' : 'none';\n var vc = el.querySelector('.visible-count');\n var tc = el.querySelector('.total-count');\n if (vc) vc.textContent = visible;\n if (tc) tc.textContent = total;\n}\n\n// Keyboard navigation\nvar focusedScenarioIndex = -1;\n\nfunction getVisibleScenarios() {\n return Array.from(document.querySelectorAll('.scenario')).filter(function(s) {\n return s.style.display !== 'none' && s.closest('.feature').style.display !== 'none';\n });\n}\n\nfunction focusScenario(index) {\n var scenarios = getVisibleScenarios();\n if (scenarios.length === 0) return;\n\n // Remove previous focus\n var prev = document.querySelector('.scenario-focused');\n if (prev) prev.classList.remove('scenario-focused');\n\n // Wrap around\n if (index < 0) index = scenarios.length - 1;\n if (index >= scenarios.length) index = 0;\n focusedScenarioIndex = index;\n\n var scenario = scenarios[index];\n scenario.classList.add('scenario-focused');\n scenario.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n}\n\nfunction showShortcutsOverlay() {\n if (document.querySelector('.shortcuts-overlay')) return;\n var overlay = document.createElement('div');\n overlay.className = 'shortcuts-overlay';\n overlay.innerHTML = '<div class=\"shortcuts-modal\">' +\n '<div class=\"shortcuts-title\">Keyboard Shortcuts</div>' +\n '<div class=\"shortcuts-grid\">' +\n '<kbd>j</kbd><span>Next scenario</span>' +\n '<kbd>k</kbd><span>Previous scenario</span>' +\n '<kbd>Enter</kbd><span>Expand/collapse scenario</span>' +\n '<kbd>Escape</kbd><span>Collapse scenario / close</span>' +\n '<kbd>/</kbd><span>Focus search</span>' +\n '<kbd>?</kbd><span>Toggle this help</span>' +\n '<kbd>e</kbd><span>Expand all</span>' +\n '<kbd>c</kbd><span>Collapse all</span>' +\n '<kbd>t</kbd><span>Toggle table of contents</span>' +\n '</div></div>';\n overlay.addEventListener('click', function(ev) {\n if (ev.target === overlay) hideShortcutsOverlay();\n });\n document.body.appendChild(overlay);\n}\n\nfunction hideShortcutsOverlay() {\n var overlay = document.querySelector('.shortcuts-overlay');\n if (overlay) overlay.remove();\n}\n\nfunction initKeyboardShortcuts() {\n document.addEventListener('keydown', function(e) {\n var tag = e.target.tagName;\n if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') {\n if (e.key === 'Escape') {\n e.target.blur();\n if (e.target.classList.contains('search-input')) {\n e.target.value = '';\n applyAllFilters();\n }\n }\n return;\n }\n\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n\n switch (e.key) {\n case 'j':\n e.preventDefault();\n focusScenario(focusedScenarioIndex + 1);\n break;\n case 'k':\n e.preventDefault();\n focusScenario(focusedScenarioIndex - 1);\n break;\n case 'Enter':\n e.preventDefault();\n var scenarios = getVisibleScenarios();\n if (focusedScenarioIndex >= 0 && focusedScenarioIndex < scenarios.length) {\n var s = scenarios[focusedScenarioIndex];\n var h = s.querySelector('.scenario-header');\n if (h) toggleCollapse(h, s);\n }\n break;\n case 'Escape':\n if (document.querySelector('.shortcuts-overlay')) {\n hideShortcutsOverlay();\n } else {\n var scenarios2 = getVisibleScenarios();\n if (focusedScenarioIndex >= 0 && focusedScenarioIndex < scenarios2.length) {\n var sc = scenarios2[focusedScenarioIndex];\n if (!sc.classList.contains('collapsed')) {\n sc.classList.add('collapsed');\n var sh = sc.querySelector('.scenario-header');\n if (sh) sh.setAttribute('aria-expanded', 'false');\n }\n }\n }\n break;\n case '/':\n e.preventDefault();\n var input = document.querySelector('.search-input');\n if (input) input.focus();\n break;\n case '?':\n e.preventDefault();\n if (document.querySelector('.shortcuts-overlay')) {\n hideShortcutsOverlay();\n } else {\n showShortcutsOverlay();\n }\n break;\n case 'e':\n e.preventDefault();\n expandAll();\n break;\n case 'c':\n e.preventDefault();\n collapseAll();\n break;\n case 't':\n e.preventDefault();\n if (typeof toggleToc === 'function') toggleToc();\n break;\n }\n });\n}\n\n// Collapse/expand functionality\nfunction toggleCollapse(header, container) {\n container?.classList.toggle('collapsed');\n const isCollapsed = container?.classList.contains('collapsed');\n header.setAttribute('aria-expanded', !isCollapsed);\n}\n\nfunction initCollapse() {\n document.querySelectorAll('.feature-header').forEach(header => {\n header.addEventListener('click', () => {\n toggleCollapse(header, header.closest('.feature'));\n });\n header.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse(header, header.closest('.feature'));\n }\n });\n });\n\n document.querySelectorAll('.scenario-header').forEach(header => {\n header.addEventListener('click', () => {\n toggleCollapse(header, header.closest('.scenario'));\n });\n header.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse(header, header.closest('.scenario'));\n }\n });\n });\n\n document.querySelectorAll('.trace-view-header').forEach(header => {\n header.addEventListener('click', () => {\n toggleCollapse(header, header.closest('.trace-view'));\n });\n header.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse(header, header.closest('.trace-view'));\n }\n });\n });\n}\n\nfunction expandAll() {\n document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {\n el.classList.remove('collapsed');\n const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');\n header?.setAttribute('aria-expanded', 'true');\n });\n}\n\nfunction collapseAll() {\n document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {\n el.classList.add('collapsed');\n const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');\n header?.setAttribute('aria-expanded', 'false');\n });\n}\n\n// Detail level toggle\nfunction toggleDetailLevel() {\n activeDetailLevel = activeDetailLevel === 'full' ? 'minimal' : 'full';\n document.documentElement.setAttribute('data-detail-level', activeDetailLevel);\n updateDetailToggle();\n writeUrlState();\n}\n\nfunction updateDetailToggle() {\n var btn = document.querySelector('.detail-toggle');\n if (btn) {\n btn.textContent = activeDetailLevel === 'full' ? '\\\\ud83d\\\\udccb' : '\\\\ud83d\\\\udcc4';\n btn.setAttribute('aria-label', activeDetailLevel === 'full' ? 'Hide documentation (minimal)' : 'Show documentation (full)');\n btn.title = activeDetailLevel === 'full' ? 'Showing full detail' : 'Showing minimal detail';\n }\n}\n\nfunction initDetailLevel() {\n updateDetailToggle();\n}\n\n// URL state sync for shareable URLs\nfunction readUrlState() {\n var params = new URLSearchParams(window.location.search);\n\n var search = params.get('search');\n if (search) {\n var input = document.querySelector('.search-input');\n if (input) input.value = search;\n }\n\n var tags = params.get('tags');\n if (tags) {\n tags.split(',').forEach(function(tag) {\n var pill = document.querySelector('.tag-pill[data-tag=\"' + tag + '\"]');\n if (pill) {\n activeTags.add(tag);\n pill.classList.add('active');\n pill.setAttribute('aria-pressed', 'true');\n }\n });\n updateTagBarState();\n }\n\n var status = params.get('status');\n if (status && ['passed', 'failed', 'skipped'].indexOf(status) !== -1) {\n activeStatus = status;\n var card = document.querySelector('.summary-card.' + status);\n if (card) card.classList.add('status-active');\n }\n\n var detail = params.get('detail');\n if (detail === 'minimal' || detail === 'full') {\n activeDetailLevel = detail;\n document.documentElement.setAttribute('data-detail-level', detail);\n updateDetailToggle();\n }\n}\n\nfunction writeUrlState() {\n var params = new URLSearchParams();\n var input = document.querySelector('.search-input');\n var search = input ? input.value.trim() : '';\n if (search) params.set('search', search);\n if (activeTags.size > 0) params.set('tags', Array.from(activeTags).sort().join(','));\n if (activeStatus) params.set('status', activeStatus);\n if (activeDetailLevel !== 'full') params.set('detail', activeDetailLevel);\n\n var qs = params.toString();\n var url = window.location.pathname + (qs ? '?' + qs : '');\n history.replaceState(null, '', url);\n}\n\n// Permalink copy\nfunction copyPermalink(anchorId) {\n var url = location.origin + location.pathname + location.search + '#' + anchorId;\n navigator.clipboard.writeText(url).then(function() {\n var el = document.getElementById(anchorId);\n if (el) showCopyToast(el);\n });\n}\n\nfunction showCopyToast(el) {\n var existing = el.querySelector('.copy-toast');\n if (existing) existing.remove();\n var toast = document.createElement('span');\n toast.className = 'copy-toast';\n toast.textContent = 'Copied!';\n var header = el.querySelector('.feature-header, .scenario-header');\n if (header) {\n header.style.position = 'relative';\n header.appendChild(toast);\n }\n setTimeout(function() { toast.remove(); }, 1500);\n}\n\n// Copy scenario as markdown\nfunction copyScenarioAsMarkdown(scenarioId) {\n var scenario = document.getElementById(scenarioId);\n if (!scenario) return;\n\n var title = (scenario.querySelector('.scenario-name') || {}).textContent || '';\n var steps = scenario.querySelectorAll('.step, .step.continuation');\n var lines = ['### Scenario: ' + title.trim(), ''];\n\n steps.forEach(function(step) {\n var keyword = step.getAttribute('data-keyword') || '';\n var text = step.getAttribute('data-text') || '';\n lines.push('- **' + keyword + '** ' + text);\n });\n\n var errorBox = scenario.querySelector('.error-message');\n if (errorBox) {\n var errorText = errorBox.textContent || '';\n lines.push('');\n lines.push('> **Error:** ' + errorText.trim());\n }\n\n var md = lines.join('\\\\n');\n navigator.clipboard.writeText(md).then(function() {\n showCopyToast(scenario);\n });\n}\n\n// Hash scroll on load\nfunction initHashScroll() {\n if (!location.hash) return;\n var target = document.querySelector(location.hash);\n if (!target) return;\n var feature = target.closest('.feature');\n if (feature && feature.classList.contains('collapsed')) {\n feature.classList.remove('collapsed');\n var fh = feature.querySelector('.feature-header');\n if (fh) fh.setAttribute('aria-expanded', 'true');\n }\n if (target.classList.contains('collapsed')) {\n target.classList.remove('collapsed');\n var sh = target.querySelector('.scenario-header');\n if (sh) sh.setAttribute('aria-expanded', 'true');\n }\n setTimeout(function() {\n target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n target.classList.add('hash-highlight');\n }, 100);\n}\n\n// Table of contents\nfunction toggleToc() {\n var sidebar = document.querySelector('.toc-sidebar');\n var wrapper = document.querySelector('.report-layout');\n if (!sidebar || !wrapper) return;\n var isMobile = window.matchMedia('(max-width: 767px)').matches;\n if (isMobile) {\n sidebar.classList.toggle('toc-mobile-open');\n } else {\n wrapper.classList.toggle('toc-hidden');\n var hidden = wrapper.classList.contains('toc-hidden');\n localStorage.setItem('toc-visible', String(!hidden));\n }\n}\n\nfunction initToc() {\n var sidebar = document.querySelector('.toc-sidebar');\n if (!sidebar) return;\n\n var saved = localStorage.getItem('toc-visible');\n var wrapper = document.querySelector('.report-layout');\n if (saved === 'false' && wrapper) {\n wrapper.classList.add('toc-hidden');\n }\n\n // Active tracking via IntersectionObserver\n var observer = new IntersectionObserver(function(entries) {\n entries.forEach(function(entry) {\n if (entry.isIntersecting) {\n var id = entry.target.id;\n if (!id) return;\n document.querySelectorAll('.toc-scenario, .toc-feature-toggle').forEach(function(el) {\n el.classList.remove('toc-active');\n });\n var tocLink = sidebar.querySelector('a[href=\"#' + id + '\"]');\n if (tocLink) tocLink.classList.add('toc-active');\n }\n });\n }, { rootMargin: '-10% 0px -80% 0px' });\n\n document.querySelectorAll('.feature, .scenario').forEach(function(el) {\n if (el.id) observer.observe(el);\n });\n\n // Click navigation: expand collapsed parents\n sidebar.querySelectorAll('.toc-scenario').forEach(function(link) {\n link.addEventListener('click', function(e) {\n var hash = link.getAttribute('href');\n if (!hash) return;\n var target = document.querySelector(hash);\n if (!target) return;\n var feature = target.closest('.feature');\n if (feature && feature.classList.contains('collapsed')) {\n feature.classList.remove('collapsed');\n var fh = feature.querySelector('.feature-header');\n if (fh) fh.setAttribute('aria-expanded', 'true');\n }\n if (target.classList.contains('collapsed')) {\n target.classList.remove('collapsed');\n var sh = target.querySelector('.scenario-header');\n if (sh) sh.setAttribute('aria-expanded', 'true');\n }\n });\n });\n}\n\n// Theme picker\nfunction initThemePicker() {\n var picker = document.querySelector('.theme-picker');\n if (!picker) return;\n\n var saved = localStorage.getItem('report-theme');\n if (saved) {\n picker.value = saved;\n switchReportTheme(saved);\n }\n\n picker.addEventListener('change', function(e) {\n switchReportTheme(e.target.value);\n localStorage.setItem('report-theme', e.target.value);\n });\n}\n\nfunction switchReportTheme(name) {\n document.querySelectorAll('style[data-theme-name]').forEach(function(s) {\n s.disabled = s.dataset.themeName !== name;\n });\n}\n\n// Sync TOC visibility with filters\nfunction syncTocVisibility() {\n var sidebar = document.querySelector('.toc-sidebar');\n if (!sidebar) return;\n\n sidebar.querySelectorAll('.toc-scenario').forEach(function(link) {\n var href = link.getAttribute('href');\n if (!href) return;\n var target = document.querySelector(href);\n link.style.display = (target && target.style.display !== 'none') ? '' : 'none';\n });\n\n sidebar.querySelectorAll('.toc-feature').forEach(function(feature) {\n var visibleScenarios = feature.querySelectorAll('.toc-scenario');\n var anyVisible = Array.from(visibleScenarios).some(function(s) {\n return s.style.display !== 'none';\n });\n feature.style.display = anyVisible ? '' : 'none';\n });\n}\n`;\n\n/** Options for HTML template generation */\nexport interface HtmlTemplateOptions {\n includeSearch?: boolean;\n includeDarkMode?: boolean;\n syntaxHighlighting?: boolean;\n mermaidEnabled?: boolean;\n markdownEnabled?: boolean;\n /** Additional inline JS injected after core JS (used by themes). */\n additionalJs?: string;\n /** Additional ESM import statements for CDN libraries (used by themes). */\n additionalImports?: string[];\n /** Pre-rendered TOC sidebar HTML. Placed as sibling of .container inside .report-layout. */\n tocHtml?: string;\n /** Pre-rendered theme picker HTML (select element). */\n themePickerHtml?: string;\n /** Additional theme CSS blocks to embed (for theme picker). */\n additionalThemeCss?: Array<{ name: string; label: string; css: string }>;\n /** Name of the currently active theme (for data-theme-name attribute). */\n activeThemeName?: string;\n}\n\n/** JavaScript for markdown parsing (used as a function body string in the ESM module) */\nconst JS_MARKDOWN_FN = `\nfunction parseMarkdownSections(marked) {\n // Configure marked for safe output\n marked.setOptions({\n breaks: true,\n gfm: true\n });\n\n document.querySelectorAll('.doc-section-content[data-markdown]').forEach(el => {\n const encoded = el.getAttribute('data-markdown');\n if (!encoded) return;\n\n try {\n const markdown = decodeURIComponent(atob(encoded));\n // Use marked.parse and sanitize by escaping script tags\n let html = marked.parse(markdown);\n // Basic XSS prevention - remove script tags\n html = html.replace(/<script\\\\b[^<]*(?:(?!<\\\\/script>)<[^<]*)*<\\\\/script>/gi, '');\n el.innerHTML = html;\n el.removeAttribute('data-markdown');\n } catch (e) {\n console.warn('Failed to parse markdown:', e);\n }\n });\n}\n`;\n\n/** Generate the inline JavaScript for the report (non-CDN parts) */\nfunction generateScript(options: HtmlTemplateOptions): string {\n const initCalls: string[] = [];\n\n if (options.includeDarkMode) {\n initCalls.push('initTheme();');\n }\n initCalls.push('readUrlState();');\n initCalls.push('initSearch();');\n initCalls.push('initTagFilter();');\n initCalls.push('initStatusFilter();');\n initCalls.push('initKeyboardShortcuts();');\n initCalls.push('initCollapse();');\n initCalls.push('initDetailLevel();');\n initCalls.push('applyAllFilters();');\n initCalls.push('initHashScroll();');\n initCalls.push('initToc();');\n initCalls.push('initThemePicker();');\n\n const initScript = `\n// Initialize on load\ndocument.addEventListener('DOMContentLoaded', () => {\n ${initCalls.join('\\n ')}\n});\n`;\n\n let script = options.includeDarkMode ? JS_THEME : '';\n script += JS_CORE;\n if (options.additionalJs) {\n script += options.additionalJs;\n }\n script += initScript;\n\n return script;\n}\n\n/** Generate ESM module script that imports CDN libraries and initializes them */\nfunction generateEsmScript(options: HtmlTemplateOptions): string {\n const imports: string[] = [];\n const initCalls: string[] = [];\n\n if (options.syntaxHighlighting) {\n imports.push('import hljs from \"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js\";');\n initCalls.push('hljs.highlightAll();');\n }\n\n if (options.mermaidEnabled) {\n imports.push('import mermaid from \"https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs\";');\n initCalls.push(\"mermaid.initialize({ startOnLoad: false, theme: 'neutral' });\");\n initCalls.push('await mermaid.run({ querySelector: \".mermaid\" });');\n }\n\n if (options.markdownEnabled) {\n imports.push('import { marked } from \"https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js\";');\n initCalls.push('parseMarkdownSections(marked);');\n }\n\n if (options.additionalImports) {\n imports.push(...options.additionalImports);\n }\n\n if (imports.length === 0) return '';\n\n let script = imports.join('\\n ');\n if (options.markdownEnabled) {\n script += '\\n' + JS_MARKDOWN_FN;\n }\n script += '\\n ' + initCalls.join('\\n ');\n\n return `\\n <script type=\"module\">\\n ${script}\\n </script>`;\n}\n\n/**\n * Generate the HTML template for the report.\n */\nexport function generateHtmlTemplate(\n title: string,\n styles: string,\n body: string,\n options: HtmlTemplateOptions = {}\n): string {\n const {\n includeSearch = true,\n includeDarkMode = true,\n syntaxHighlighting = true,\n mermaidEnabled = true,\n markdownEnabled = true,\n } = options;\n\n const script = generateScript(options);\n\n // Set initial theme to light; initTheme() will update based on system/localStorage\n const themeAttr = includeDarkMode ? ' data-theme=\"light\"' : '';\n\n // CDN stylesheet resources for optional features\n const cdnStyles: string[] = [];\n\n if (syntaxHighlighting) {\n cdnStyles.push('<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css\">');\n cdnStyles.push('<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css\" media=\"(prefers-color-scheme: dark)\">');\n }\n\n const cdnStylesHtml = cdnStyles.length > 0 ? '\\n ' + cdnStyles.join('\\n ') : '';\n const esmScriptHtml = generateEsmScript(options);\n\n const additionalThemeStyles = (options.additionalThemeCss ?? [])\n .map(t => `<style data-theme-name=\"${escapeHtml(t.name)}\" disabled>${t.css}</style>`)\n .join('\\n ');\n\n return `<!DOCTYPE html>\n<html lang=\"en\"${themeAttr} data-detail-level=\"full\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"color-scheme\" content=\"light dark\">\n <title>${escapeHtml(title)}</title>${cdnStylesHtml}\n <style${options.additionalThemeCss ? ` data-theme-name=\"${escapeHtml(options.activeThemeName ?? 'default')}\"` : ''}>${styles}</style>\n ${additionalThemeStyles}\n</head>\n<body>\n <div class=\"report-layout\">\n ${options.tocHtml ?? ''}\n <div class=\"main-content\">\n <div class=\"container\">\n <header class=\"header\">\n <h1>${escapeHtml(title)}</h1>\n <div class=\"header-actions\">\n <button type=\"button\" class=\"toc-toggle\" onclick=\"toggleToc()\" aria-label=\"Toggle table of contents\" title=\"Toggle contents\">&#x2630;</button>\n ${includeSearch ? '<input type=\"text\" class=\"search-input\" placeholder=\"Search scenarios...\" aria-label=\"Search scenarios\">' : ''}\n <button type=\"button\" class=\"detail-toggle\" onclick=\"toggleDetailLevel()\" aria-label=\"Toggle detail level\" title=\"Toggle documentation detail\"></button>\n ${options.themePickerHtml ?? ''}\n ${includeDarkMode ? '<button type=\"button\" class=\"theme-toggle\" onclick=\"toggleTheme()\" aria-label=\"Toggle theme\"></button>' : ''}\n </div>\n </header>\n ${body}\n </div>\n </div>\n </div>\n <script>${script}</script>${esmScriptHtml}\n</body>\n</html>`;\n}\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n","/**\n * HTML Report Styles.\n *\n * Modern, clean CSS inspired by shadcn/ui base theme with Cucumber branding.\n * Supports dark/light mode via CSS custom properties and prefers-color-scheme.\n */\n\nexport const CSS_STYLES = `\n/* ============================================================================\n Google Fonts Import - IBM Plex for refined typography\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Default)\n Cucumber-branded shadcn/ui base theme\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"IBM Plex Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"IBM Plex Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors (shadcn base with cucumber accent) */\n --background: hsl(0 0% 100%);\n --foreground: hsl(0 0% 9%);\n --card: hsl(0 0% 100%);\n --card-foreground: hsl(0 0% 9%);\n --popover: hsl(0 0% 100%);\n --popover-foreground: hsl(0 0% 9%);\n\n /* Cucumber green as primary */\n --primary: hsl(145 63% 42%);\n --primary-foreground: hsl(0 0% 100%);\n\n --secondary: hsl(0 0% 96.5%);\n --secondary-foreground: hsl(0 0% 9%);\n --muted: hsl(0 0% 96.5%);\n --muted-foreground: hsl(0 0% 45%);\n --accent: hsl(0 0% 96.5%);\n --accent-foreground: hsl(0 0% 9%);\n --destructive: hsl(0 84% 60%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 90%);\n --input: hsl(0 0% 90%);\n --ring: hsl(145 63% 42%);\n --radius: 0.5rem;\n\n /* Shadows - refined for depth */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.03);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.06);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.07), 0 2px 4px -2px rgb(0 0 0 / 0.05);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.05);\n\n /* Status colors - cucumber-harmonized */\n --success: hsl(145 63% 42%);\n --success-light: hsl(145 55% 96%);\n --success-border: hsl(145 55% 88%);\n --error: hsl(0 72% 51%);\n --error-light: hsl(0 86% 97%);\n --error-border: hsl(0 72% 92%);\n --warning: hsl(38 92% 50%);\n --warning-light: hsl(48 100% 96%);\n --warning-border: hsl(48 96% 88%);\n --pending: hsl(262 60% 55%);\n --pending-light: hsl(262 55% 97%);\n --pending-border: hsl(262 55% 90%);\n\n /* Cucumber-specific */\n --keyword-color: hsl(145 63% 32%);\n --tag-bg: hsl(145 55% 95%);\n --tag-color: hsl(145 63% 30%);\n --tag-border: hsl(145 55% 85%);\n --step-param-color: hsl(220 70% 50%);\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: hsl(0 0% 98%);\n --accordion-content-bg: hsl(0 0% 98.5%);\n}\n\n/* ============================================================================\n Dark Mode - Cucumber branded\n ============================================================================ */\n[data-theme=\"dark\"] {\n --background: hsl(0 0% 6%);\n --foreground: hsl(0 0% 95%);\n --card: hsl(0 0% 9%);\n --card-foreground: hsl(0 0% 95%);\n --popover: hsl(0 0% 9%);\n --popover-foreground: hsl(0 0% 95%);\n\n /* Cucumber green stays vibrant in dark mode */\n --primary: hsl(145 63% 50%);\n --primary-foreground: hsl(0 0% 6%);\n\n --secondary: hsl(0 0% 13%);\n --secondary-foreground: hsl(0 0% 95%);\n --muted: hsl(0 0% 13%);\n --muted-foreground: hsl(0 0% 55%);\n --accent: hsl(0 0% 13%);\n --accent-foreground: hsl(0 0% 95%);\n --destructive: hsl(0 72% 55%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 16%);\n --input: hsl(0 0% 16%);\n --ring: hsl(145 63% 50%);\n\n /* Shadows (subtle for dark mode) */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.35);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.35);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.45), 0 4px 6px -4px rgb(0 0 0 / 0.35);\n\n /* Status colors (dark mode) */\n --success: hsl(145 63% 55%);\n --success-light: hsl(145 35% 14%);\n --success-border: hsl(145 35% 22%);\n --error: hsl(0 72% 60%);\n --error-light: hsl(0 35% 14%);\n --error-border: hsl(0 35% 22%);\n --warning: hsl(38 92% 55%);\n --warning-light: hsl(38 35% 14%);\n --warning-border: hsl(38 35% 22%);\n --pending: hsl(262 60% 65%);\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n\n /* Cucumber-specific (dark) */\n --keyword-color: hsl(145 63% 60%);\n --tag-bg: hsl(145 35% 14%);\n --tag-color: hsl(145 63% 60%);\n --tag-border: hsl(145 35% 22%);\n --step-param-color: hsl(220 70% 70%);\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: hsl(0 0% 11%);\n --accordion-content-bg: hsl(0 0% 7%);\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: hsl(0 0% 6%);\n --foreground: hsl(0 0% 95%);\n --card: hsl(0 0% 9%);\n --card-foreground: hsl(0 0% 95%);\n --popover: hsl(0 0% 9%);\n --popover-foreground: hsl(0 0% 95%);\n --primary: hsl(145 63% 50%);\n --primary-foreground: hsl(0 0% 6%);\n --secondary: hsl(0 0% 13%);\n --secondary-foreground: hsl(0 0% 95%);\n --muted: hsl(0 0% 13%);\n --muted-foreground: hsl(0 0% 55%);\n --accent: hsl(0 0% 13%);\n --accent-foreground: hsl(0 0% 95%);\n --destructive: hsl(0 72% 55%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 16%);\n --input: hsl(0 0% 16%);\n --ring: hsl(145 63% 50%);\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.35);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.35);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.45), 0 4px 6px -4px rgb(0 0 0 / 0.35);\n --success: hsl(145 63% 55%);\n --success-light: hsl(145 35% 14%);\n --success-border: hsl(145 35% 22%);\n --error: hsl(0 72% 60%);\n --error-light: hsl(0 35% 14%);\n --error-border: hsl(0 35% 22%);\n --warning: hsl(38 92% 55%);\n --warning-light: hsl(38 35% 14%);\n --warning-border: hsl(38 35% 22%);\n --pending: hsl(262 60% 65%);\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n --keyword-color: hsl(145 63% 60%);\n --tag-bg: hsl(145 35% 14%);\n --tag-color: hsl(145 63% 60%);\n --tag-border: hsl(145 35% 22%);\n --step-param-color: hsl(220 70% 70%);\n --accordion-header-hover: hsl(0 0% 11%);\n --accordion-content-bg: hsl(0 0% 7%);\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 14px;\n line-height: 1.6;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout\n ============================================================================ */\n.container {\n max-width: 1100px;\n margin: 0 auto;\n padding: 1.25rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 2rem 2.5rem;\n }\n}\n\n/* ============================================================================\n Header - shadcn style\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1.25rem;\n margin-bottom: 1.5rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-size: 1.375rem;\n font-weight: 600;\n letter-spacing: -0.02em;\n color: var(--foreground);\n}\n\n.header-actions {\n display: flex;\n gap: 0.625rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle - shadcn button style\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Search Input - shadcn input style\n ============================================================================ */\n.search-input {\n height: 2.25rem;\n padding: 0 0.875rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.875rem;\n width: 220px;\n transition: all 0.15s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 3px hsl(145 63% 42% / 0.1);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 260px;\n }\n}\n\n/* ============================================================================\n Meta Info - clean card style\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 1.75rem;\n margin-bottom: 1.25rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 500;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Summary Cards - tinted card style\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 1rem 1.25rem;\n transition: all 0.15s ease;\n}\n\n.summary-card:hover {\n box-shadow: var(--shadow-sm);\n}\n\n.summary-card .label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.375rem;\n}\n\n.summary-card .value {\n font-size: 2rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n font-family: var(--font-sans);\n}\n\n/* Passed - green tint */\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed - red tint */\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped - amber tint */\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending - purple tint */\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 1rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.6875rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--destructive, #dc2626);\n background: var(--destructive-light, #fef2f2);\n border: 1px solid var(--destructive-border, #fecaca);\n cursor: pointer;\n padding: 0.25rem 0.75rem;\n border-radius: var(--radius);\n transition: all 0.15s ease;\n}\n\n.tag-bar-clear:hover {\n background: var(--destructive-border, #fecaca);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.5rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.75rem;\n font-weight: 500;\n padding: 0.25rem 0.625rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.tag-pill:hover {\n background: var(--success-border);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n margin-bottom: 1rem;\n font-weight: 500;\n}\n\n/* ============================================================================\n Feature Sections - shadcn accordion style\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.625rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 0.875rem 1rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 600;\n font-size: 0.9375rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 0.8125rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.625rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios - nested accordion style\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n margin-bottom: 0.5rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.75rem 1rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 500;\n font-size: 0.875rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.375rem;\n}\n\n.tag {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.scenario-duration {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.75rem 1rem 1rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons - refined\n ============================================================================ */\n.status-icon {\n font-size: 0.875rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps - story-like flow\n ============================================================================ */\n.steps {\n margin-top: 0.25rem;\n padding: 0.25rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.5rem;\n padding: 0.375rem 0;\n font-size: 0.8125rem;\n align-items: baseline;\n line-height: 1.5;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1rem;\n text-align: center;\n font-size: 0.75rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 52px;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* Indent continuation keywords (And, But, *) to show they belong to previous step */\n.step.continuation {\n padding-left: 1.25rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 500;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.7;\n}\n\n/* ============================================================================\n Error Display - alert style\n ============================================================================ */\n.error-box {\n margin-top: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments - badge style\n ============================================================================ */\n.attachments {\n margin-top: 0.75rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n font-size: 0.75rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.15s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon - smooth rotation\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars - subtle styling\n ============================================================================ */\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States - cucumber ring color\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection - cucumber tinted\n ============================================================================ */\n::selection {\n background: hsl(145 63% 42% / 0.15);\n color: inherit;\n}\n\n/* ============================================================================\n Animations - smooth reveals\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.2s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.02s; }\n.feature:nth-child(3) { animation-delay: 0.04s; }\n.feature:nth-child(4) { animation-delay: 0.06s; }\n.feature:nth-child(5) { animation-delay: 0.08s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #e5e5e5;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body {\n font-size: 12px;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.75rem;\n padding: 0.75rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.5rem;\n margin-top: 0.25rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0.5rem;\n background: var(--muted);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n font-size: 0.8125rem;\n line-height: 1.5;\n color: var(--foreground);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 0.5rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 0.375rem;\n font-size: 0.8125rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.125rem 0.375rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.5rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.75rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.5rem 0.75rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.375rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.8125rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.15s ease;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"→\";\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.5rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-size: 0.8125rem;\n line-height: 1.6;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n margin-top: 1em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.25rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.8125rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.125em 0.375em;\n background: var(--muted);\n border-radius: 3px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.75em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 2px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.5em 1em;\n border-left: 3px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.8125rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 2px);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid (displayed as code)\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"◇ \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.5rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.375rem;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.375rem 0.75rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Children\n ============================================================================ */\n.doc-children {\n margin-left: 1rem;\n padding-left: 1rem;\n border-left: 2px solid var(--border);\n margin-top: 0.25rem;\n}\n\n/* ============================================================================\n Trace View - OTel span waterfall\n ============================================================================ */\n.trace-view {\n margin-top: 0.75rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-size: 0.8125rem;\n font-weight: 500;\n color: var(--foreground);\n transition: background-color 0.15s ease;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 1px solid var(--border);\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.375rem;\n margin-bottom: 0.375rem;\n border-bottom: 1px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.1875rem 0;\n font-size: 0.75rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1.25rem;\n background: var(--muted);\n border-radius: 2px;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 2px;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.375rem;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: white;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75em; font-weight: 600; margin-left: 4px; vertical-align: middle; }\n.badge-grade { color: #fff; }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #2196F3; }\n.badge-grade-C { background: #FF9800; }\n.badge-grade-D { background: #f44336; }\n.badge-grade-F { background: #9E0000; }\n.badge-flaky { background: #FF9800; color: #fff; }\n.badge-perf { font-size: 0.7em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* Failure summary */\n.failure-summary {\n margin: 1rem 0;\n padding: 0.75rem 1rem;\n border: 1px solid var(--error);\n border-radius: 0.5rem;\n background: color-mix(in srgb, var(--error) 8%, transparent);\n}\n.failure-summary-header {\n font-weight: 600;\n font-size: 0.875rem;\n color: var(--error);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n.failure-summary li a {\n font-size: 0.8125rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* Source permalink */\n.source-link {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Detail Level Toggle\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.detail-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n\n/* ============================================================================\n Permalink Anchors\n ============================================================================ */\n.permalink-anchor {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n border: none;\n background: none;\n color: var(--muted-foreground);\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.15s ease;\n font-size: 0.875rem;\n font-weight: 600;\n padding: 0;\n flex-shrink: 0;\n}\n\n.feature-header:hover .permalink-anchor,\n.scenario-header:hover .permalink-anchor,\n.permalink-anchor:focus-visible {\n opacity: 1;\n}\n\n.permalink-anchor:hover {\n color: var(--primary);\n}\n\n.copy-toast {\n position: absolute;\n right: 0.5rem;\n top: 50%;\n transform: translateY(-50%);\n background: var(--foreground);\n color: var(--background);\n padding: 0.25rem 0.5rem;\n border-radius: var(--radius);\n font-size: 0.75rem;\n font-weight: 500;\n pointer-events: none;\n animation: fadeOut 1.5s ease forwards;\n z-index: 10;\n}\n\n@keyframes fadeOut {\n 0%, 70% { opacity: 1; }\n 100% { opacity: 0; }\n}\n\n.hash-highlight {\n animation: hashPulse 2s ease;\n}\n\n@keyframes hashPulse {\n 0%, 100% { background: transparent; }\n 20% { background: color-mix(in srgb, var(--primary) 12%, transparent); }\n}\n\n.scenario-actions {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n flex-shrink: 0;\n}\n\n.copy-scenario-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n border: none;\n background: none;\n color: var(--muted-foreground);\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.15s ease;\n font-size: 0.875rem;\n padding: 0;\n flex-shrink: 0;\n}\n\n.scenario-header:hover .copy-scenario-btn,\n.copy-scenario-btn:focus-visible {\n opacity: 1;\n}\n\n.copy-scenario-btn:hover {\n color: var(--primary);\n}\n\n/* ============================================================================\n Keyboard Navigation\n ============================================================================ */\n.scenario-focused {\n border-left: 2px solid var(--primary);\n}\n\n.shortcuts-overlay {\n position: fixed;\n inset: 0;\n background: rgb(0 0 0 / 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n}\n\n.shortcuts-modal {\n background: var(--card);\n color: var(--card-foreground);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) * 2);\n padding: 1.5rem 2rem;\n max-width: 400px;\n width: 90vw;\n box-shadow: var(--shadow-md, 0 4px 12px rgb(0 0 0 / 0.15));\n}\n\n.shortcuts-title {\n font-weight: 600;\n font-size: 1.125rem;\n margin-bottom: 1rem;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid var(--border);\n}\n\n.shortcuts-grid {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 0.5rem 1rem;\n align-items: center;\n}\n\n.shortcuts-grid kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.75rem;\n padding: 0.125rem 0.375rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) * 0.5);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.shortcuts-grid span {\n font-size: 0.875rem;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Table of Contents Sidebar\n ============================================================================ */\n.report-layout {\n display: flex;\n min-height: 100vh;\n}\n\n.report-layout.toc-hidden .toc-sidebar {\n display: none;\n}\n\n.main-content {\n flex: 1;\n min-width: 0;\n}\n\n.toc-sidebar {\n width: 260px;\n flex-shrink: 0;\n position: sticky;\n top: 0;\n height: 100vh;\n overflow-y: auto;\n border-right: 1px solid var(--border);\n background: var(--card);\n padding: 1rem 0;\n font-size: 0.8125rem;\n}\n\n.toc-header {\n padding: 0 1rem 0.75rem;\n border-bottom: 1px solid var(--border);\n margin-bottom: 0.5rem;\n}\n\n.toc-title {\n font-weight: 600;\n font-size: 0.875rem;\n color: var(--foreground);\n text-decoration: none;\n cursor: pointer;\n}\n\na.toc-title:hover {\n color: var(--primary);\n}\n\n.toc-feature {\n margin-bottom: 0.25rem;\n}\n\n.toc-feature-toggle {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 0.375rem 1rem;\n border: none;\n background: none;\n text-align: left;\n cursor: pointer;\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--foreground);\n font-family: var(--font-sans);\n}\n\n.toc-feature-toggle:hover {\n background: var(--accent);\n}\n\n.toc-feature-toggle[aria-expanded=\"false\"] + .toc-scenarios {\n display: none;\n}\n\n.toc-scenarios {\n display: flex;\n flex-direction: column;\n}\n\n.toc-scenario {\n display: flex;\n align-items: baseline;\n gap: 0.375rem;\n padding: 0.25rem 1rem 0.25rem 1.5rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-size: 0.8125rem;\n line-height: 1.4;\n border-left: 2px solid transparent;\n transition: all 0.1s ease;\n}\n\n.toc-scenario:hover {\n color: var(--foreground);\n background: var(--accent);\n}\n\n.toc-scenario.toc-active {\n color: var(--foreground);\n border-left-color: var(--primary);\n font-weight: 500;\n}\n\n.toc-scenario.toc-failed {\n border-left-color: var(--error, var(--destructive));\n}\n\n.toc-status {\n flex-shrink: 0;\n font-size: 0.75rem;\n}\n\n.toc-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.toc-toggle:hover {\n background: var(--accent);\n}\n\n/* Mobile: overlay sidebar */\n@media (max-width: 767px) {\n .toc-sidebar {\n position: fixed;\n left: 0;\n top: 0;\n z-index: 50;\n box-shadow: var(--shadow-sm, 0 1px 3px rgb(0 0 0 / 0.1));\n transform: translateX(-100%);\n transition: transform 0.2s ease;\n }\n\n .toc-sidebar.toc-mobile-open {\n transform: translateX(0);\n }\n}\n\n/* ============================================================================\n Theme Picker\n ============================================================================ */\n.theme-picker {\n height: 2.25rem;\n padding: 0 0.5rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-size: 0.8125rem;\n font-family: var(--font-sans);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.theme-picker:hover {\n background: var(--accent);\n}\n\n.theme-picker:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n`;\n","/**\n * Default theme — wraps the existing CSS_STYLES.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\nimport { CSS_STYLES } from \"../styles.js\";\n\nexport const defaultTheme: HtmlTheme = {\n name: \"default\",\n label: \"Default\",\n css: CSS_STYLES,\n};\n","/**\n * Corporate theme — editorial/magazine feel with serif typography and navy palette.\n * Two-pane layout: fixed sidebar with TOC navigation + main content area.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\nimport type { BuildBodyArgs, BuildBodyDeps } from \"../renderers/body.js\";\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nfunction corporateBuildBody(args: BuildBodyArgs, deps: BuildBodyDeps): string {\n const { run } = args;\n\n // --- Build sidebar content ---\n const total = run.testCases.length;\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n\n const tocItems: string[] = [];\n let featureIndex = 0;\n for (const [file, testCases] of byFile) {\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const fPassed = testCases.filter((tc) => tc.status === \"passed\").length;\n const fFailed = testCases.filter((tc) => tc.status === \"failed\").length;\n const statusDot = fFailed > 0 ? \"dot-failed\" : \"dot-passed\";\n\n tocItems.push(\n `<a class=\"toc-item\" href=\"#corporate-feature-${featureIndex}\" data-feature-index=\"${featureIndex}\">` +\n `<span class=\"toc-dot ${statusDot}\"></span>` +\n `<span class=\"toc-label\">${escapeForAttr(featureName)}</span>` +\n `<span class=\"toc-count\">${fPassed}/${testCases.length}</span>` +\n `</a>`,\n );\n featureIndex++;\n }\n\n const passRate = total > 0 ? Math.round((passed / total) * 100) : 0;\n\n const sidebar = `\n<nav class=\"toc\">\n <div class=\"toc-header\">\n <a href=\"#\" class=\"toc-title\" onclick=\"window.scrollTo({top:0,behavior:'smooth'});return false;\">Test Report</a>\n <div class=\"toc-stats\">\n <div class=\"toc-stat-row\">\n <span class=\"toc-stat-label\">Total</span>\n <span class=\"toc-stat-value\">${total}</span>\n </div>\n <div class=\"toc-stat-row\">\n <span class=\"toc-stat-label\">Passed</span>\n <span class=\"toc-stat-value toc-stat-passed\">${passed}</span>\n </div>\n <div class=\"toc-stat-row\">\n <span class=\"toc-stat-label\">Failed</span>\n <span class=\"toc-stat-value toc-stat-failed\">${failed}</span>\n </div>\n <div class=\"toc-stat-row\">\n <span class=\"toc-stat-label\">Skipped</span>\n <span class=\"toc-stat-value toc-stat-skipped\">${skipped}</span>\n </div>\n <div class=\"toc-progress\">\n <div class=\"toc-progress-bar\" style=\"width: ${passRate}%\"></div>\n </div>\n <div class=\"toc-pass-rate\">${passRate}% pass rate</div>\n </div>\n </div>\n <div class=\"toc-nav\">\n <div class=\"toc-nav-label\">Features</div>\n ${tocItems.join(\"\\n \")}\n </div>\n</nav>`;\n\n // --- Build main content ---\n const mainParts: string[] = [];\n\n mainParts.push(\n deps.renderMetaInfo(\n {\n startedAtMs: run.startedAtMs,\n durationMs: run.durationMs,\n packageVersion: run.packageVersion,\n gitSha: run.gitSha,\n ciName: run.ci?.name,\n ciBranch: run.ci?.branch,\n ciUrl: run.ci?.url,\n ciCommitSha: run.ci?.commitSha,\n ciBuildNumber: run.ci?.buildNumber,\n },\n deps.metaDeps,\n ),\n );\n\n mainParts.push(\n deps.renderSummary(\n { total, passed, failed, skipped },\n deps.summaryDeps,\n ),\n );\n\n const allTags = [\n ...new Set(run.testCases.flatMap((tc) => tc.tags)),\n ].sort();\n mainParts.push(\n deps.renderTagBar(\n { tags: allTags, totalScenarios: total },\n deps.tagBarDeps,\n ),\n );\n\n const failedCases = run.testCases.filter((tc) => tc.status === \"failed\");\n if (failedCases.length > 0) {\n mainParts.push(\n deps.renderFailureSummary(\n { failedCases },\n deps.failureSummaryDeps,\n ),\n );\n }\n\n featureIndex = 0;\n for (const [file, testCases] of byFile) {\n const featureHtml = deps.renderFeature(\n { file, testCases, metricsMap: args.metricsMap },\n deps.featureDeps,\n );\n // Wrap each feature with an ID anchor for sidebar navigation\n mainParts.push(\n `<div id=\"corporate-feature-${featureIndex}\">${featureHtml}</div>`,\n );\n featureIndex++;\n }\n\n return `<div class=\"corporate-layout\">${sidebar}<main class=\"corporate-main\">${mainParts.join(\"\\n\")}</main></div>`;\n}\n\n/** Minimal HTML-safe escaping for attribute values in sidebar */\nfunction escapeForAttr(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\nconst CORPORATE_CSS = `\n/* ============================================================================\n Google Fonts Import — Playfair Display, Source Serif 4, DM Sans\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Playfair+Display:wght@400;500;600;700&family=Source+Serif+4:wght@400;500;600&display=swap');\n\n/* ============================================================================\n CSS Custom Properties — Light Mode (Default)\n Navy palette with editorial serif typography\n ============================================================================ */\n:root {\n /* Typography */\n --font-heading: \"Playfair Display\", Georgia, \"Times New Roman\", serif;\n --font-body: \"Source Serif 4\", Georgia, \"Times New Roman\", serif;\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"DM Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors — warm ivory background, navy accents */\n --background: #faf9f7;\n --foreground: #1a202c;\n --card: #ffffff;\n --card-foreground: #1a202c;\n --popover: #ffffff;\n --popover-foreground: #1a202c;\n\n /* Navy as primary */\n --primary: #1a365d;\n --primary-foreground: #ffffff;\n\n --secondary: #f0ede8;\n --secondary-foreground: #1a202c;\n --muted: #f0ede8;\n --muted-foreground: #64748b;\n --accent: #eee9e0;\n --accent-foreground: #1a202c;\n --destructive: #b91c1c;\n --destructive-foreground: #ffffff;\n --border: #d6d0c4;\n --input: #d6d0c4;\n --ring: #1a365d;\n --radius: 0.375rem;\n\n /* Shadows */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.04);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.04);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.06), 0 2px 4px -2px rgb(0 0 0 / 0.04);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.07), 0 4px 6px -4px rgb(0 0 0 / 0.04);\n\n /* Status colors */\n --success: #166534;\n --success-light: #f0fdf4;\n --success-border: #bbf7d0;\n --error: #b91c1c;\n --error-light: #fef2f2;\n --error-border: #fecaca;\n --warning: #a16207;\n --warning-light: #fefce8;\n --warning-border: #fef08a;\n --pending: #6d28d9;\n --pending-light: #f5f3ff;\n --pending-border: #ddd6fe;\n\n /* Theme-specific */\n --keyword-color: #1a365d;\n --tag-bg: #eff6ff;\n --tag-color: #1e40af;\n --tag-border: #bfdbfe;\n --step-param-color: #7c3aed;\n\n /* Accordion/Collapsible */\n --accordion-header-hover: #f5f2ed;\n --accordion-content-bg: #faf8f5;\n}\n\n/* ============================================================================\n Dark Mode — Navy palette\n ============================================================================ */\n[data-theme=\"dark\"] {\n --font-heading: \"Playfair Display\", Georgia, \"Times New Roman\", serif;\n --font-body: \"Source Serif 4\", Georgia, \"Times New Roman\", serif;\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"DM Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #111827;\n --foreground: #f1f5f9;\n --card: #1e293b;\n --card-foreground: #f1f5f9;\n --popover: #1e293b;\n --popover-foreground: #f1f5f9;\n\n --primary: #93c5fd;\n --primary-foreground: #0f172a;\n\n --secondary: #1e293b;\n --secondary-foreground: #f1f5f9;\n --muted: #1e293b;\n --muted-foreground: #94a3b8;\n --accent: #1e293b;\n --accent-foreground: #f1f5f9;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #334155;\n --input: #334155;\n --ring: #93c5fd;\n\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);\n\n --success: #4ade80;\n --success-light: hsl(145 30% 12%);\n --success-border: hsl(145 30% 22%);\n --error: #f87171;\n --error-light: hsl(0 30% 12%);\n --error-border: hsl(0 30% 22%);\n --warning: #fbbf24;\n --warning-light: hsl(38 30% 12%);\n --warning-border: hsl(38 30% 22%);\n --pending: #a78bfa;\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n\n --keyword-color: #93c5fd;\n --tag-bg: hsl(220 40% 15%);\n --tag-color: #93c5fd;\n --tag-border: hsl(220 30% 25%);\n --step-param-color: #c4b5fd;\n\n --accordion-header-hover: #253347;\n --accordion-content-bg: #162033;\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: #111827;\n --foreground: #f1f5f9;\n --card: #1e293b;\n --card-foreground: #f1f5f9;\n --popover: #1e293b;\n --popover-foreground: #f1f5f9;\n --primary: #93c5fd;\n --primary-foreground: #0f172a;\n --secondary: #1e293b;\n --secondary-foreground: #f1f5f9;\n --muted: #1e293b;\n --muted-foreground: #94a3b8;\n --accent: #1e293b;\n --accent-foreground: #f1f5f9;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #334155;\n --input: #334155;\n --ring: #93c5fd;\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);\n --success: #4ade80;\n --success-light: hsl(145 30% 12%);\n --success-border: hsl(145 30% 22%);\n --error: #f87171;\n --error-light: hsl(0 30% 12%);\n --error-border: hsl(0 30% 22%);\n --warning: #fbbf24;\n --warning-light: hsl(38 30% 12%);\n --warning-border: hsl(38 30% 22%);\n --pending: #a78bfa;\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n --keyword-color: #93c5fd;\n --tag-bg: hsl(220 40% 15%);\n --tag-color: #93c5fd;\n --tag-border: hsl(220 30% 25%);\n --step-param-color: #c4b5fd;\n --accordion-header-hover: #253347;\n --accordion-content-bg: #162033;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-body);\n font-size: 14px;\n line-height: 1.7;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Corporate Two-Pane Layout\n ============================================================================ */\n.corporate-layout {\n display: flex;\n min-height: 100vh;\n}\n\n/* ============================================================================\n Sidebar / Table of Contents\n ============================================================================ */\n.toc {\n position: fixed;\n top: 0;\n left: 0;\n width: 260px;\n height: 100vh;\n overflow-y: auto;\n background: var(--card);\n border-right: 1px solid var(--border);\n display: flex;\n flex-direction: column;\n z-index: 20;\n}\n\n.toc-header {\n padding: 1.5rem 1.25rem 1rem;\n border-bottom: 1px solid var(--border);\n}\n\n.toc-title {\n font-family: var(--font-heading);\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--primary);\n letter-spacing: -0.01em;\n margin-bottom: 1rem;\n}\n\n.toc-stats {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n\n.toc-stat-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-family: var(--font-sans);\n font-size: 0.75rem;\n}\n\n.toc-stat-label {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.toc-stat-value {\n font-weight: 600;\n color: var(--foreground);\n font-family: var(--font-mono);\n}\n\n.toc-stat-passed { color: var(--success); }\n.toc-stat-failed { color: var(--error); }\n.toc-stat-skipped { color: var(--warning); }\n\n.toc-progress {\n height: 4px;\n background: var(--muted);\n border-radius: 2px;\n margin-top: 0.5rem;\n overflow: hidden;\n}\n\n.toc-progress-bar {\n height: 100%;\n background: var(--success);\n border-radius: 2px;\n transition: width 0.3s ease;\n}\n\n.toc-pass-rate {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-align: right;\n margin-top: 0.25rem;\n}\n\n.toc-nav {\n flex: 1;\n overflow-y: auto;\n padding: 0.75rem 0;\n}\n\n.toc-nav-label {\n font-family: var(--font-sans);\n font-size: 0.625rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n padding: 0.5rem 1.25rem 0.375rem;\n}\n\n.toc-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 1.25rem;\n text-decoration: none;\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n font-weight: 400;\n transition: all 0.15s ease;\n border-left: 2px solid transparent;\n}\n\n.toc-item:hover {\n background: var(--accent);\n color: var(--primary);\n}\n\n.toc-item.active {\n background: var(--accent);\n border-left-color: var(--primary);\n color: var(--primary);\n font-weight: 500;\n}\n\n.toc-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.toc-dot.dot-passed {\n background: var(--success);\n}\n\n.toc-dot.dot-failed {\n background: var(--error);\n}\n\n.toc-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.toc-count {\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n flex-shrink: 0;\n}\n\n/* ============================================================================\n Main Content Area\n ============================================================================ */\n.corporate-main {\n margin-left: 260px;\n flex: 1;\n min-width: 0;\n}\n\n.container {\n max-width: 960px;\n margin: 0 auto;\n padding: 1.5rem 2rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 2rem 2.5rem;\n }\n}\n\n/* ============================================================================\n Responsive — collapse sidebar on small screens\n ============================================================================ */\n@media (max-width: 860px) {\n .toc {\n display: none;\n }\n\n .corporate-main {\n margin-left: 0;\n }\n}\n\n/* ============================================================================\n Header — editorial style\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1.25rem;\n margin-bottom: 1.5rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-family: var(--font-heading);\n font-size: 1.5rem;\n font-weight: 600;\n letter-spacing: -0.02em;\n color: var(--primary);\n}\n\n.header-actions {\n display: flex;\n gap: 0.625rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Search Input\n ============================================================================ */\n.search-input {\n height: 2.25rem;\n padding: 0 0.875rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.875rem;\n width: 220px;\n transition: all 0.15s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 3px hsl(220 60% 50% / 0.1);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 260px;\n }\n}\n\n/* ============================================================================\n Meta Info\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 1.75rem;\n margin-bottom: 1.25rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.8125rem;\n font-family: var(--font-body);\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 500;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Summary Cards\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 1rem 1.25rem;\n transition: all 0.15s ease;\n}\n\n.summary-card:hover {\n box-shadow: var(--shadow-sm);\n}\n\n.summary-card .label {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.375rem;\n}\n\n.summary-card .value {\n font-family: var(--font-heading);\n font-size: 2rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n}\n\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n.summary-card.status-active {\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 1rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.6875rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--destructive, #dc2626);\n background: var(--destructive-light, #fef2f2);\n border: 1px solid var(--destructive-border, #fecaca);\n cursor: pointer;\n padding: 0.25rem 0.75rem;\n border-radius: var(--radius);\n transition: all 0.15s ease;\n}\n\n.tag-bar-clear:hover {\n background: var(--destructive-border, #fecaca);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.5rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n padding: 0.25rem 0.625rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.tag-pill:hover {\n background: var(--success-border);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n margin-bottom: 1rem;\n font-weight: 500;\n}\n\n/* ============================================================================\n Feature Sections — editorial card style\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.75rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 1rem 1.25rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-family: var(--font-heading);\n font-weight: 600;\n font-size: 1.0625rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 0.8125rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.75rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n margin-bottom: 0.5rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.75rem 1rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 500;\n font-size: 0.875rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n font-family: var(--font-body);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.375rem;\n}\n\n.tag {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n}\n\n.scenario-duration {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.75rem 1rem 1rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons — colored dots instead of emoji\n ============================================================================ */\n.status-icon {\n font-size: 0.875rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps\n ============================================================================ */\n.steps {\n margin-top: 0.25rem;\n padding: 0.25rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.5rem;\n padding: 0.375rem 0;\n font-size: 0.8125rem;\n align-items: baseline;\n line-height: 1.6;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1rem;\n text-align: center;\n font-size: 0.75rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 52px;\n font-family: var(--font-sans);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.02em;\n}\n\n.step.continuation {\n padding-left: 1.25rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n font-family: var(--font-body);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 500;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.7;\n}\n\n/* ============================================================================\n Error Display\n ============================================================================ */\n.error-box {\n margin-top: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments\n ============================================================================ */\n.attachments {\n margin-top: 0.75rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n font-size: 0.75rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.15s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars\n ============================================================================ */\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection\n ============================================================================ */\n::selection {\n background: hsl(220 60% 50% / 0.15);\n color: inherit;\n}\n\n/* ============================================================================\n Animations\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.2s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.02s; }\n.feature:nth-child(3) { animation-delay: 0.04s; }\n.feature:nth-child(4) { animation-delay: 0.06s; }\n.feature:nth-child(5) { animation-delay: 0.08s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #e5e5e5;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body {\n font-size: 12px;\n }\n\n .corporate-layout {\n display: block;\n }\n\n .toc {\n display: none;\n }\n\n .corporate-main {\n margin-left: 0;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries — Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.75rem;\n padding: 0.75rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.5rem;\n margin-top: 0.25rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n/* ============================================================================\n Documentation Entries — Note\n ============================================================================ */\n.doc-note {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0.5rem;\n background: var(--muted);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n font-size: 0.8125rem;\n line-height: 1.6;\n color: var(--foreground);\n font-family: var(--font-body);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries — Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 0.5rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n}\n\n/* ============================================================================\n Documentation Entries — Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 0.375rem;\n font-size: 0.8125rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-sans);\n font-size: 0.75rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries — Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-family: var(--font-sans);\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.125rem 0.375rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries — Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.5rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.75rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.5rem 0.75rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--foreground);\n font-family: var(--font-sans);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries — Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.375rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.8125rem;\n color: var(--primary);\n text-decoration: none;\n font-family: var(--font-body);\n transition: color 0.15s ease;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"\\\\2192\";\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Documentation Entries — Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.5rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-family: var(--font-heading);\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-size: 0.8125rem;\n line-height: 1.7;\n white-space: pre-wrap;\n color: var(--foreground);\n font-family: var(--font-body);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n font-family: var(--font-heading);\n margin-top: 1em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.25rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.8125rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.125em 0.375em;\n background: var(--muted);\n border-radius: 3px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.75em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 2px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.5em 1em;\n border-left: 3px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.8125rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 2px);\n}\n\n/* ============================================================================\n Documentation Entries — Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-family: var(--font-sans);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"\\\\25C7 \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries — Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.5rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.375rem;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-style: italic;\n font-family: var(--font-body);\n}\n\n/* ============================================================================\n Documentation Entries — Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.375rem 0.75rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-family: var(--font-sans);\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View\n ============================================================================ */\n.trace-view {\n margin-top: 0.75rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n font-weight: 500;\n color: var(--foreground);\n transition: background-color 0.15s ease;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 1px solid var(--border);\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.375rem;\n margin-bottom: 0.375rem;\n border-bottom: 1px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.1875rem 0;\n font-size: 0.75rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1.25rem;\n background: var(--muted);\n border-radius: 2px;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 2px;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.375rem;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: white;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75em; font-weight: 600; margin-left: 4px; vertical-align: middle; font-family: var(--font-sans); }\n.badge-grade { color: #fff; }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #2196F3; }\n.badge-grade-C { background: #FF9800; }\n.badge-grade-D { background: #f44336; }\n.badge-grade-F { background: #9E0000; }\n.badge-flaky { background: #FF9800; color: #fff; }\n.badge-perf { font-size: 0.7em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* ============================================================================\n Failure summary\n ============================================================================ */\n.failure-summary {\n margin: 1rem 0;\n padding: 0.75rem 1rem;\n border: 1px solid var(--error);\n border-radius: var(--radius);\n background: color-mix(in srgb, var(--error) 8%, transparent);\n}\n.failure-summary-header {\n font-family: var(--font-heading);\n font-weight: 600;\n font-size: 0.875rem;\n color: var(--error);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n.failure-summary li a {\n font-family: var(--font-body);\n font-size: 0.8125rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* ============================================================================\n Source permalink\n ============================================================================ */\n.source-link {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Detail Level Toggle\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.detail-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n`;\n\nconst CORPORATE_JS = `\n// Sidebar TOC navigation — highlight active section on scroll, click to smooth-scroll\n(function() {\n var tocItems = document.querySelectorAll('.toc-item');\n if (!tocItems.length) return;\n\n var featureAnchors = [];\n tocItems.forEach(function(item) {\n var href = item.getAttribute('href');\n if (href) {\n var el = document.querySelector(href);\n if (el) featureAnchors.push({ el: el, tocItem: item });\n }\n });\n\n // Click handler — smooth scroll\n tocItems.forEach(function(item) {\n item.addEventListener('click', function(e) {\n e.preventDefault();\n var href = item.getAttribute('href');\n if (!href) return;\n var target = document.querySelector(href);\n if (target) {\n target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }\n });\n });\n\n // Scroll handler — highlight active section\n var ticking = false;\n function onScroll() {\n if (ticking) return;\n ticking = true;\n requestAnimationFrame(function() {\n var scrollY = window.scrollY || document.documentElement.scrollTop;\n var viewportH = window.innerHeight;\n var activeIdx = -1;\n\n for (var i = featureAnchors.length - 1; i >= 0; i--) {\n var rect = featureAnchors[i].el.getBoundingClientRect();\n if (rect.top <= viewportH * 0.3) {\n activeIdx = i;\n break;\n }\n }\n\n tocItems.forEach(function(item) { item.classList.remove('active'); });\n if (activeIdx >= 0) {\n featureAnchors[activeIdx].tocItem.classList.add('active');\n }\n\n ticking = false;\n });\n }\n\n window.addEventListener('scroll', onScroll, { passive: true });\n onScroll();\n})();\n`;\n\nexport const corporateTheme: HtmlTheme = {\n name: \"corporate\",\n label: \"Corporate\",\n css: CORPORATE_CSS,\n buildBody: corporateBuildBody,\n additionalJs: CORPORATE_JS,\n};\n","/**\n * Terminal theme — green-on-dark hacker aesthetic.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\n\nexport const terminalTheme: HtmlTheme = {\n name: \"terminal\",\n label: \"Terminal\",\n css: `\n/* ============================================================================\n Google Fonts Import - JetBrains Mono for terminal typography\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Always dark-feeling)\n Terminal theme: green-on-dark, high density, no rounding\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Light terminal — paper-white with green accents */\n --background: #f5f5f0;\n --foreground: #1a1a1a;\n --card: #eaeae5;\n --card-foreground: #1a1a1a;\n --popover: #eaeae5;\n --popover-foreground: #1a1a1a;\n\n /* Green primary, darker for light bg */\n --primary: #008a45;\n --primary-foreground: #f5f5f0;\n\n --secondary: #e0e0d8;\n --secondary-foreground: #1a1a1a;\n --muted: #e8e8e2;\n --muted-foreground: #666660;\n --accent: #e0e0d8;\n --accent-foreground: #1a1a1a;\n --destructive: #cc2222;\n --destructive-foreground: #f5f5f0;\n --border: #c8c8c0;\n --input: #c8c8c0;\n --ring: #008a45;\n --radius: 0;\n\n /* No shadows — flat terminal look */\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n\n /* Status colors — legible on light bg */\n --success: #008a45;\n --success-light: #008a4512;\n --success-border: #008a4533;\n --error: #cc2222;\n --error-light: #cc222212;\n --error-border: #cc222233;\n --warning: #b87800;\n --warning-light: #b8780012;\n --warning-border: #b8780033;\n --pending: #0088a0;\n --pending-light: #0088a012;\n --pending-border: #0088a033;\n\n /* Terminal-specific */\n --keyword-color: #008a45;\n --tag-bg: #008a4515;\n --tag-color: #008a45;\n --tag-border: #008a4533;\n --step-param-color: #0088a0;\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: #e0e0d8;\n --accordion-content-bg: #ebebeb;\n}\n\n/* ============================================================================\n Dark Mode — classic green-on-black terminal\n ============================================================================ */\n[data-theme=\"dark\"] {\n --font-sans: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #0a0a0a;\n --foreground: #d4d4d4;\n --card: #111111;\n --card-foreground: #d4d4d4;\n --popover: #111111;\n --popover-foreground: #d4d4d4;\n\n --primary: #00d26a;\n --primary-foreground: #0a0a0a;\n\n --secondary: #1a1a1a;\n --secondary-foreground: #d4d4d4;\n --muted: #1a1a1a;\n --muted-foreground: #6b6b6b;\n --accent: #1a1a1a;\n --accent-foreground: #d4d4d4;\n --destructive: #ff4444;\n --destructive-foreground: #0a0a0a;\n --border: #2a2a2a;\n --input: #2a2a2a;\n --ring: #00d26a;\n\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n\n --success: #00d26a;\n --success-light: #00d26a12;\n --success-border: #00d26a33;\n --error: #ff4444;\n --error-light: #ff444412;\n --error-border: #ff444433;\n --warning: #ffaa00;\n --warning-light: #ffaa0012;\n --warning-border: #ffaa0033;\n --pending: #00bcd4;\n --pending-light: #00bcd412;\n --pending-border: #00bcd433;\n\n --keyword-color: #00d26a;\n --tag-bg: #00d26a15;\n --tag-color: #00d26a;\n --tag-border: #00d26a33;\n --step-param-color: #00bcd4;\n\n --accordion-header-hover: #1a1a1a;\n --accordion-content-bg: #0e0e0e;\n}\n\n/* Auto dark mode — same values (always dark) */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --font-sans: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #0a0a0a;\n --foreground: #d4d4d4;\n --card: #111111;\n --card-foreground: #d4d4d4;\n --popover: #111111;\n --popover-foreground: #d4d4d4;\n --primary: #00d26a;\n --primary-foreground: #0a0a0a;\n --secondary: #1a1a1a;\n --secondary-foreground: #d4d4d4;\n --muted: #1a1a1a;\n --muted-foreground: #6b6b6b;\n --accent: #1a1a1a;\n --accent-foreground: #d4d4d4;\n --destructive: #ff4444;\n --destructive-foreground: #0a0a0a;\n --border: #2a2a2a;\n --input: #2a2a2a;\n --ring: #00d26a;\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n --success: #00d26a;\n --success-light: #00d26a12;\n --success-border: #00d26a33;\n --error: #ff4444;\n --error-light: #ff444412;\n --error-border: #ff444433;\n --warning: #ffaa00;\n --warning-light: #ffaa0012;\n --warning-border: #ffaa0033;\n --pending: #00bcd4;\n --pending-light: #00bcd412;\n --pending-border: #00bcd433;\n --keyword-color: #00d26a;\n --tag-bg: #00d26a15;\n --tag-color: #00d26a;\n --tag-border: #00d26a33;\n --step-param-color: #00bcd4;\n --accordion-header-hover: #1a1a1a;\n --accordion-content-bg: #0e0e0e;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 13px;\n line-height: 1.5;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout — compact, high density\n ============================================================================ */\n.container {\n max-width: 1200px;\n margin: 0 auto;\n padding: 0.75rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 1rem 1.5rem;\n }\n}\n\n/* ============================================================================\n Header\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 0.75rem;\n margin-bottom: 0.75rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-size: 1.125rem;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: var(--primary);\n}\n\n.header h1::before {\n content: \"> \";\n color: var(--muted-foreground);\n}\n\n.header-actions {\n display: flex;\n gap: 0.5rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--primary);\n font-size: 0.875rem;\n transition: all 0.1s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--primary);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n/* ============================================================================\n Search Input\n ============================================================================ */\n.search-input {\n height: 2rem;\n padding: 0 0.75rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n width: 220px;\n transition: all 0.1s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--primary);\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 280px;\n }\n}\n\n/* ============================================================================\n Meta Info\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.125rem 1.5rem;\n margin-bottom: 0.75rem;\n padding: 0.5rem 0.75rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.75rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 600;\n color: var(--primary);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n/* ============================================================================\n Summary Cards\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0.5rem;\n margin-bottom: 0.75rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.625rem 0.75rem;\n transition: border-color 0.1s ease;\n}\n\n.summary-card:hover {\n border-color: var(--muted-foreground);\n}\n\n.summary-card .label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.25rem;\n}\n\n.summary-card .value {\n font-size: 1.75rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n font-family: var(--font-sans);\n}\n\n/* Passed — green */\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed — red */\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped — amber */\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending — cyan */\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 1px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.625rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.15s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--destructive);\n background: var(--error-light);\n border: 1px solid var(--error-border);\n cursor: pointer;\n padding: 0.125rem 0.625rem;\n border-radius: var(--radius);\n transition: all 0.1s ease;\n}\n\n.tag-bar-clear:hover {\n background: var(--error-border);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 1px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.375rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.1s ease;\n}\n\n.tag-pill:hover {\n background: var(--success-border);\n}\n\n.tag-pill:focus-visible {\n outline: 1px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n font-weight: 500;\n}\n\n/* ============================================================================\n Feature Sections\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.375rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 0.5rem 0.75rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.1s ease;\n gap: 0.75rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 600;\n font-size: 0.8125rem;\n color: var(--primary);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.0625rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.75rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.1875rem;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.375rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.25rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.375rem 0.75rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.1s ease;\n gap: 0.75rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-weight: 500;\n font-size: 0.8125rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-top: 0.25rem;\n}\n\n.tag {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n}\n\n.scenario-duration {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.5rem 0.75rem 0.625rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons\n ============================================================================ */\n.status-icon {\n font-size: 0.8125rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps — compact terminal flow\n ============================================================================ */\n.steps {\n margin-top: 0.125rem;\n padding: 0.125rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.375rem;\n padding: 0.1875rem 0;\n font-size: 0.75rem;\n align-items: baseline;\n line-height: 1.4;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 0.875rem;\n text-align: center;\n font-size: 0.6875rem;\n}\n\n.step-keyword {\n font-weight: 700;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 48px;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n/* Indent continuation keywords (And, But, *) */\n.step.continuation {\n padding-left: 1rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: normal;\n font-weight: 600;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.625rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.6;\n}\n\n/* ============================================================================\n Error Display\n ============================================================================ */\n.error-box {\n margin-top: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--error-light);\n border-radius: var(--radius);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments\n ============================================================================ */\n.attachments {\n margin-top: 0.5rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.1s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--primary);\n border-color: var(--primary);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.375rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.375rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.15s ease;\n font-size: 0.6875rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars — thin green track\n ============================================================================ */\n::-webkit-scrollbar {\n width: 4px;\n height: 4px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 0;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--primary);\n}\n\n/* ============================================================================\n Focus States\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n/* ============================================================================\n Selection — green tinted\n ============================================================================ */\n::selection {\n background: #00d26a33;\n color: inherit;\n}\n\n/* ============================================================================\n Animations — minimal, fast\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.feature {\n animation: fadeIn 0.1s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.01s; }\n.feature:nth-child(3) { animation-delay: 0.02s; }\n.feature:nth-child(4) { animation-delay: 0.03s; }\n.feature:nth-child(5) { animation-delay: 0.04s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #ccc;\n --muted: #f0f0f0;\n --muted-foreground: #555;\n --primary: #006633;\n --keyword-color: #006633;\n }\n\n body {\n font-size: 11px;\n color: black;\n background: white;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.5rem;\n padding: 0.5rem;\n background: var(--accordion-content-bg);\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.25rem;\n margin-top: 0.125rem;\n margin-bottom: 0.375rem;\n padding: 0.375rem 0.625rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: var(--radius);\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.375rem 0.625rem;\n margin-bottom: 0.375rem;\n background: var(--muted);\n border-left: 2px solid var(--primary);\n border-radius: var(--radius);\n font-size: 0.75rem;\n line-height: 1.4;\n color: var(--foreground);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-bottom: 0.375rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.375rem;\n margin-bottom: 0.25rem;\n font-size: 0.75rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--primary);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.doc-kv-label::after {\n content: \":\";\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.5625rem;\n font-weight: 600;\n padding: 0.0625rem 0.3125rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: var(--radius);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.5rem 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.375rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.25rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.3125rem 0.625rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--primary);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.25rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-size: 0.75rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.1s ease;\n}\n\n.doc-link a:hover {\n color: var(--foreground);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \">\";\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.375rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.5rem 0.625rem;\n background: var(--card);\n font-size: 0.75rem;\n line-height: 1.5;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n margin-top: 0.75em;\n margin-bottom: 0.375em;\n font-weight: 700;\n line-height: 1.3;\n color: var(--primary);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.8125rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.75rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.375em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.375em 0;\n padding-left: 1.25em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.125em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.0625em 0.25em;\n background: var(--muted);\n border-radius: var(--radius);\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.5em 0;\n padding: 0.5em;\n background: var(--muted);\n border-radius: var(--radius);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.5em 0;\n padding: 0.375em 0.75em;\n border-left: 2px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 0.75em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.5em 0;\n border-collapse: collapse;\n font-size: 0.75rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.375em 0.625em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: var(--radius);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"> \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.5rem 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.375rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.25rem;\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-style: normal;\n}\n\n.doc-screenshot-caption::before {\n content: \"# \";\n color: var(--primary);\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.25rem 0.625rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-size: 0.625rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.5rem 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View - OTel span waterfall\n ============================================================================ */\n.trace-view {\n margin-top: 0.5rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.625rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--foreground);\n transition: background-color 0.1s ease;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 1px solid var(--border);\n padding: 0.375rem 0.625rem;\n background: var(--accordion-content-bg);\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.5625rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.25rem;\n margin-bottom: 0.25rem;\n border-bottom: 1px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.125rem 0;\n font-size: 0.6875rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 0;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1rem;\n background: var(--muted);\n border-radius: 0;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 0;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.25rem;\n font-size: 0.5625rem;\n font-family: var(--font-mono);\n color: var(--primary-foreground);\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 1px 5px; border-radius: 0; font-size: 0.6875em; font-weight: 600; margin-left: 4px; vertical-align: middle; }\n.badge-grade { color: var(--primary-foreground); }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #2196F3; }\n.badge-grade-C { background: #FF9800; }\n.badge-grade-D { background: #f44336; }\n.badge-grade-F { background: #9E0000; }\n.badge-flaky { background: #FF9800; color: var(--primary-foreground); }\n.badge-perf { font-size: 0.65em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* Failure summary */\n.failure-summary {\n margin: 0.5rem 0;\n padding: 0.5rem 0.75rem;\n border: 1px solid var(--error);\n border-radius: var(--radius);\n background: var(--error-light);\n}\n.failure-summary-header {\n font-weight: 700;\n font-size: 0.8125rem;\n color: var(--error);\n margin-bottom: 0.375rem;\n}\n.failure-summary-note {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n}\n.failure-summary li a {\n font-size: 0.75rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* Source permalink */\n.source-link {\n font-size: 0.625rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--primary);\n}\n\n/* ============================================================================\n Detail Level Toggle\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 0.875rem;\n transition: all 0.1s ease;\n}\n\n.detail-toggle:hover {\n background: var(--accent);\n border-color: var(--primary);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px var(--primary);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n`,\n};\n","/**\n * Minimal theme — zen-like typography-first aesthetic.\n *\n * Noto Serif Display headings, DM Sans body, warm neutrals, teal accent.\n * No cards, no shadows — just typography and space.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\n\nexport const minimalTheme: HtmlTheme = {\n name: \"minimal\",\n label: \"Minimal\",\n css: `\n/* ============================================================================\n Google Fonts Import - Noto Serif Display + DM Sans\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+Display:wght@400;500;600;700&family=DM+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Default)\n Warm neutral palette with teal accent\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"DM Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors — warm neutrals */\n --background: #fdfcfa;\n --foreground: #2d2d2d;\n --card: #fdfcfa;\n --card-foreground: #2d2d2d;\n --popover: #fdfcfa;\n --popover-foreground: #2d2d2d;\n\n /* Teal primary */\n --primary: #2a9d8f;\n --primary-foreground: #ffffff;\n\n --secondary: #f5f3ef;\n --secondary-foreground: #2d2d2d;\n --muted: #f5f3ef;\n --muted-foreground: #8a8680;\n --accent: #f0ece6;\n --accent-foreground: #2d2d2d;\n --destructive: #c1554d;\n --destructive-foreground: #ffffff;\n --border: #e8e4de;\n --input: #e8e4de;\n --ring: #2a9d8f;\n --radius: 0.25rem;\n\n /* Shadows — nearly invisible for minimal aesthetic */\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n\n /* Status colors — muted, warm tones */\n --success: #2a9d8f;\n --success-light: #f0faf8;\n --success-border: #c4e8e3;\n --error: #c1554d;\n --error-light: #fdf5f4;\n --error-border: #e8c5c2;\n --warning: #c68a19;\n --warning-light: #fdf8ed;\n --warning-border: #e8d9b0;\n --pending: #7c6daa;\n --pending-light: #f7f5fb;\n --pending-border: #d5cfea;\n\n /* Theme-specific */\n --keyword-color: #1f7a6e;\n --tag-bg: #f0faf8;\n --tag-color: #1f7a6e;\n --tag-border: #c4e8e3;\n --step-param-color: #4a7fb5;\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: #f5f3ef;\n --accordion-content-bg: #fdfcfa;\n}\n\n/* ============================================================================\n Dark Mode\n ============================================================================ */\n[data-theme=\"dark\"] {\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"DM Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #1a1a18;\n --foreground: #e0ddd8;\n --card: #1a1a18;\n --card-foreground: #e0ddd8;\n --popover: #1a1a18;\n --popover-foreground: #e0ddd8;\n\n --primary: #3dbcad;\n --primary-foreground: #1a1a18;\n\n --secondary: #262622;\n --secondary-foreground: #e0ddd8;\n --muted: #262622;\n --muted-foreground: #8a8680;\n --accent: #2e2e2a;\n --accent-foreground: #e0ddd8;\n --destructive: #d4706a;\n --destructive-foreground: #ffffff;\n --border: #3a3836;\n --input: #3a3836;\n --ring: #3dbcad;\n --radius: 0.25rem;\n\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n\n --success: #3dbcad;\n --success-light: #1c2b28;\n --success-border: #2a4a44;\n --error: #d4706a;\n --error-light: #2b1c1c;\n --error-border: #4a2a28;\n --warning: #d4a033;\n --warning-light: #2b2618;\n --warning-border: #4a3e22;\n --pending: #9688c0;\n --pending-light: #221e2e;\n --pending-border: #3a3450;\n\n --keyword-color: #4ed4c4;\n --tag-bg: #1c2b28;\n --tag-color: #4ed4c4;\n --tag-border: #2a4a44;\n --step-param-color: #7aade0;\n\n --accordion-header-hover: #262622;\n --accordion-content-bg: #1e1e1c;\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: #1a1a18;\n --foreground: #e0ddd8;\n --card: #1a1a18;\n --card-foreground: #e0ddd8;\n --popover: #1a1a18;\n --popover-foreground: #e0ddd8;\n --primary: #3dbcad;\n --primary-foreground: #1a1a18;\n --secondary: #262622;\n --secondary-foreground: #e0ddd8;\n --muted: #262622;\n --muted-foreground: #8a8680;\n --accent: #2e2e2a;\n --accent-foreground: #e0ddd8;\n --destructive: #d4706a;\n --destructive-foreground: #ffffff;\n --border: #3a3836;\n --input: #3a3836;\n --ring: #3dbcad;\n --shadow-xs: none;\n --shadow-sm: none;\n --shadow: none;\n --shadow-md: none;\n --success: #3dbcad;\n --success-light: #1c2b28;\n --success-border: #2a4a44;\n --error: #d4706a;\n --error-light: #2b1c1c;\n --error-border: #4a2a28;\n --warning: #d4a033;\n --warning-light: #2b2618;\n --warning-border: #4a3e22;\n --pending: #9688c0;\n --pending-light: #221e2e;\n --pending-border: #3a3450;\n --keyword-color: #4ed4c4;\n --tag-bg: #1c2b28;\n --tag-color: #4ed4c4;\n --tag-border: #2a4a44;\n --step-param-color: #7aade0;\n --accordion-header-hover: #262622;\n --accordion-content-bg: #1e1e1c;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 15px;\n line-height: 1.8;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout — single-column centered, generous whitespace\n ============================================================================ */\n.container {\n max-width: 680px;\n margin: 0 auto;\n padding: 2rem 1.5rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 3rem 2rem;\n }\n}\n\n/* ============================================================================\n Header — serif heading, minimal rule\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 1.5rem;\n padding-bottom: 1.5rem;\n margin-bottom: 2rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-family: \"Noto Serif Display\", Georgia, \"Times New Roman\", serif;\n font-size: 1.75rem;\n font-weight: 500;\n letter-spacing: -0.02em;\n color: var(--foreground);\n white-space: nowrap;\n}\n\n.header-actions {\n display: flex;\n gap: 0.625rem;\n align-items: center;\n flex-shrink: 0;\n}\n\n/* ============================================================================\n Theme Toggle\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: transparent;\n cursor: pointer;\n color: var(--muted-foreground);\n font-size: 0.875rem;\n transition: color 0.2s ease;\n}\n\n.theme-toggle:hover {\n color: var(--foreground);\n background: transparent;\n border-color: var(--foreground);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Search Input\n ============================================================================ */\n.search-input {\n height: 2rem;\n padding: 0 0.75rem;\n border: none;\n border-bottom: 1px solid var(--border);\n border-radius: 0;\n background: transparent;\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.875rem;\n width: 200px;\n transition: border-color 0.2s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--primary);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 240px;\n }\n}\n\n/* ============================================================================\n Meta Info — understated inline text\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 1.5rem;\n margin-bottom: 2rem;\n padding: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 500;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Summary Cards — flat, typographic counters\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0;\n margin-bottom: 2.5rem;\n border-top: 1px solid var(--border);\n border-bottom: 1px solid var(--border);\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: transparent;\n border: none;\n border-radius: 0;\n padding: 1rem 1rem;\n border-right: 1px solid var(--border);\n transition: none;\n}\n\n.summary-card:last-child {\n border-right: none;\n}\n\n.summary-card:hover {\n box-shadow: none;\n}\n\n.summary-card .label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.25rem;\n}\n\n.summary-card .value {\n font-family: \"Noto Serif Display\", Georgia, serif;\n font-size: 2rem;\n font-weight: 400;\n letter-spacing: -0.02em;\n line-height: 1.1;\n}\n\n/* Passed — teal text only */\n.summary-card.passed {\n background: transparent;\n border-color: var(--border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed — red text only */\n.summary-card.failed {\n background: transparent;\n border-color: var(--border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped — amber text only */\n.summary-card.skipped {\n background: transparent;\n border-color: var(--border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending — purple text only */\n.summary-card.pending {\n background: transparent;\n border-color: var(--border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 1.5rem;\n padding: 0.75rem 0;\n background: transparent;\n border: none;\n border-bottom: 1px solid var(--border);\n border-radius: 0;\n position: sticky;\n top: 0;\n z-index: 10;\n backdrop-filter: blur(8px);\n background: color-mix(in srgb, var(--background) 90%, transparent);\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.6875rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--destructive);\n background: transparent;\n border: none;\n border-bottom: 1px solid var(--destructive);\n cursor: pointer;\n padding: 0.125rem 0;\n border-radius: 0;\n transition: opacity 0.15s ease;\n}\n\n.tag-bar-clear:hover {\n opacity: 0.7;\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.5rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.75rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: transparent;\n color: var(--muted-foreground);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.tag-pill:hover {\n color: var(--primary);\n border-color: var(--primary);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: none;\n border-bottom: 2px solid var(--ring);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n margin-bottom: 1.5rem;\n font-weight: 400;\n font-style: italic;\n}\n\n/* ============================================================================\n Feature Sections — no card, separated by large rules\n ============================================================================ */\n.feature {\n background: transparent;\n border: none;\n border-radius: 0;\n margin-bottom: 0;\n padding-bottom: 2rem;\n border-bottom: 2px solid var(--border);\n overflow: visible;\n}\n\n.feature:last-child {\n border-bottom: none;\n padding-bottom: 0;\n}\n\n.feature + .feature {\n padding-top: 2rem;\n}\n\n.feature-header {\n padding: 0.75rem 0;\n background: transparent;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: none;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: transparent;\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-family: \"Noto Serif Display\", Georgia, \"Times New Roman\", serif;\n font-weight: 500;\n font-size: 1.25rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 0.8125rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.5rem 0 0 0;\n border-top: none;\n background: transparent;\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios — left-border accent, no card\n ============================================================================ */\n.scenario {\n background: transparent;\n border: none;\n border-left: 3px solid var(--border);\n border-radius: 0;\n margin-bottom: 1.25rem;\n padding-left: 1rem;\n overflow: visible;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.5rem 0;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: none;\n gap: 1rem;\n}\n\n.scenario-header:hover {\n background: transparent;\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 500;\n font-size: 0.9375rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.375rem;\n}\n\n.tag {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: transparent;\n color: var(--muted-foreground);\n border: none;\n border-radius: 0;\n font-family: var(--font-mono);\n}\n\n.tag::before {\n content: \"#\";\n opacity: 0.5;\n}\n\n.scenario-duration {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.25rem 0 0.5rem;\n border-top: none;\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* Status-based left border for scenarios */\n.scenario:has(.status-passed) {\n border-left-color: var(--success);\n}\n\n.scenario:has(.status-failed) {\n border-left-color: var(--error);\n}\n\n.scenario:has(.status-skipped) {\n border-left-color: var(--warning);\n}\n\n.scenario:has(.status-pending) {\n border-left-color: var(--pending);\n}\n\n/* ============================================================================\n Status Icons\n ============================================================================ */\n.status-icon {\n font-size: 0.875rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps — generous line-height, quiet styling\n ============================================================================ */\n.steps {\n margin-top: 0.25rem;\n padding: 0.25rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.5rem;\n padding: 0.25rem 0;\n font-size: 0.875rem;\n align-items: baseline;\n line-height: 1.7;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1rem;\n text-align: center;\n font-size: 0.75rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 52px;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n/* Indent continuation keywords (And, But, *) */\n.step.continuation {\n padding-left: 1.25rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 500;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.5;\n}\n\n/* ============================================================================\n Error Display — left-border accent, minimal\n ============================================================================ */\n.error-box {\n margin-top: 0.75rem;\n padding: 0.75rem 1rem;\n background: transparent;\n border-radius: 0;\n border: none;\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments\n ============================================================================ */\n.attachments {\n margin-top: 0.75rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.25rem 0;\n background: transparent;\n border: none;\n border-bottom: 1px solid var(--border);\n border-radius: 0;\n font-size: 0.75rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: color 0.15s ease;\n}\n\n.attachment:hover {\n background: transparent;\n color: var(--primary);\n border-color: var(--primary);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars — hidden track\n ============================================================================ */\n::-webkit-scrollbar {\n width: 4px;\n height: 4px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 2px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection\n ============================================================================ */\n::selection {\n background: color-mix(in srgb, var(--primary) 15%, transparent);\n color: inherit;\n}\n\n/* ============================================================================\n Animations — subtle fade only\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.feature {\n animation: fadeIn 0.3s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.03s; }\n.feature:nth-child(3) { animation-delay: 0.06s; }\n.feature:nth-child(4) { animation-delay: 0.09s; }\n.feature:nth-child(5) { animation-delay: 0.12s; }\n\n/* ============================================================================\n Print Styles — optimized by default\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #d4d4d4;\n --muted: #f5f5f5;\n --muted-foreground: #555;\n }\n\n body {\n font-size: 11pt;\n line-height: 1.6;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .header h1 {\n font-size: 18pt;\n }\n\n .feature {\n page-break-inside: avoid;\n animation: none;\n border-bottom: 1pt solid #d4d4d4;\n }\n\n .scenario {\n page-break-inside: avoid;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n\n .summary-card .value {\n font-size: 16pt;\n }\n\n .step {\n font-size: 10pt;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 1rem;\n padding: 0.75rem 0;\n background: transparent;\n border-radius: 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.5rem;\n margin-top: 0.25rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0 0.5rem 0.75rem;\n background: transparent;\n border-left: 2px solid var(--primary);\n border-radius: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.5rem 0 0.5rem 0.75rem;\n margin-bottom: 0.5rem;\n background: transparent;\n border-left: 2px solid var(--muted-foreground);\n border-radius: 0;\n font-size: 0.875rem;\n line-height: 1.7;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 0.5rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: transparent;\n color: var(--tag-color);\n border: none;\n border-radius: 0;\n font-family: var(--font-mono);\n}\n\n.doc-tag-item::before {\n content: \"#\";\n opacity: 0.5;\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 0.375rem;\n font-size: 0.875rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.75rem;\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.375rem 0;\n background: transparent;\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: transparent;\n color: var(--muted-foreground);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.75rem 0;\n background: transparent;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.75rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.8125rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.5rem 0.75rem;\n text-align: left;\n border-bottom: 1px solid var(--border);\n}\n\n.doc-table th {\n background: transparent;\n font-weight: 600;\n color: var(--foreground);\n border-bottom: 2px solid var(--border);\n}\n\n.doc-table td {\n background: transparent;\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accent);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.375rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.875rem;\n color: var(--primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.15s ease;\n}\n\n.doc-link a:hover {\n border-bottom-color: var(--primary);\n text-decoration: none;\n}\n\n.doc-link a::before {\n content: \"\\\\2192\";\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.75rem;\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.375rem 0;\n background: transparent;\n border-bottom: 1px solid var(--border);\n font-family: \"Noto Serif Display\", Georgia, serif;\n font-size: 0.9375rem;\n font-weight: 500;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.75rem 0;\n background: transparent;\n font-size: 0.875rem;\n line-height: 1.7;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n font-family: \"Noto Serif Display\", Georgia, serif;\n margin-top: 1.25em;\n margin-bottom: 0.5em;\n font-weight: 500;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.25rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.8125rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n border-bottom-color: var(--primary);\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.125em 0.375em;\n background: var(--muted);\n border-radius: 2px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.75em;\n background: var(--muted);\n border-radius: var(--radius);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.5em 1em;\n border-left: 2px solid var(--primary);\n background: transparent;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1.5em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.875rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border-bottom: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: transparent;\n font-weight: 600;\n border-bottom: 2px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: var(--radius);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.75rem;\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.375rem 0;\n background: transparent;\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"\\\\25C7 \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.75rem 0;\n background: transparent;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.75rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.375rem;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.75rem;\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.375rem 0;\n background: transparent;\n border-bottom: 1px solid var(--border);\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.75rem 0;\n background: transparent;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View\n ============================================================================ */\n.trace-view {\n margin-top: 0.75rem;\n border: none;\n border-top: 1px solid var(--border);\n border-radius: 0;\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0;\n background: transparent;\n cursor: pointer;\n user-select: none;\n font-size: 0.8125rem;\n font-weight: 500;\n color: var(--foreground);\n transition: none;\n}\n\n.trace-view-header:hover {\n background: transparent;\n}\n\n.trace-view-count {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: transparent;\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: var(--radius);\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 1px solid var(--border);\n padding: 0.5rem 0;\n background: transparent;\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.375rem;\n margin-bottom: 0.375rem;\n border-bottom: 1px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.1875rem 0;\n font-size: 0.75rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1.25rem;\n background: var(--muted);\n border-radius: 2px;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 2px;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.375rem;\n font-size: 0.625rem;\n font-family: var(--font-mono);\n color: white;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 2px 6px; border-radius: 2px; font-size: 0.75em; font-weight: 600; margin-left: 4px; vertical-align: middle; }\n.badge-grade { color: #fff; }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #4a7fb5; }\n.badge-grade-C { background: #c68a19; }\n.badge-grade-D { background: #c1554d; }\n.badge-grade-F { background: #8a2020; }\n.badge-flaky { background: #c68a19; color: #fff; }\n.badge-perf { font-size: 0.7em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* Failure summary */\n.failure-summary {\n margin: 1.5rem 0;\n padding: 0.75rem 0 0.75rem 1rem;\n border: none;\n border-left: 3px solid var(--error);\n border-radius: 0;\n background: transparent;\n}\n.failure-summary-header {\n font-weight: 600;\n font-size: 0.875rem;\n color: var(--error);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n.failure-summary li a {\n font-size: 0.8125rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* Source permalink */\n.source-link {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Detail Level Toggle\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: transparent;\n cursor: pointer;\n color: var(--muted-foreground);\n font-size: 0.875rem;\n transition: color 0.15s ease;\n}\n\n.detail-toggle:hover {\n color: var(--foreground);\n background: transparent;\n border-color: var(--foreground);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n`,\n};\n","/**\n * Dashboard theme — Grafana/Datadog-inspired, data-dense, two-column layout.\n * Dark by default with DM Sans for UI and JetBrains Mono for metrics/timing.\n * Structural theme: overrides both CSS and HTML body via buildBody.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\nimport type { BuildBodyArgs, BuildBodyDeps } from \"../renderers/body.js\";\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nfunction dashboardBuildBody(args: BuildBodyArgs, deps: BuildBodyDeps): string {\n const { run } = args;\n\n const total = run.testCases.length;\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n const passRate = total > 0 ? Math.round((passed / total) * 100) : 0;\n\n const allTags = [...new Set(run.testCases.flatMap((tc) => tc.tags))].sort();\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n\n // Build sidebar feature tree items\n const treeItems: string[] = [];\n let featureIndex = 0;\n for (const [file, testCases] of byFile) {\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const fPassed = testCases.filter((tc) => tc.status === \"passed\").length;\n const fFailed = testCases.filter((tc) => tc.status === \"failed\").length;\n const statusClass =\n fFailed > 0 ? \"error\" : fPassed === testCases.length ? \"success\" : \"warning\";\n\n treeItems.push(\n `<button type=\"button\" class=\"db-tree-item\" data-feature-index=\"${featureIndex}\" title=\"${file}\">\n <span class=\"db-tree-dot db-tree-dot--${statusClass}\"></span>\n <span class=\"db-tree-name\">${featureName}</span>\n <span class=\"db-tree-count\">${testCases.length}</span>\n </button>`,\n );\n featureIndex++;\n }\n\n // Build tag chips\n const tagChips = allTags\n .map(\n (tag) =>\n `<span class=\"db-tag-chip\">${tag}</span>`,\n )\n .join(\"\\n \");\n\n // Build run info\n const startDate = run.startedAtMs\n ? new Date(run.startedAtMs).toLocaleString()\n : \"N/A\";\n const durationSec =\n run.durationMs != null ? (run.durationMs / 1000).toFixed(1) : \"N/A\";\n\n // Build main content using deps (preserves all default classes)\n const mainParts: string[] = [];\n\n // Tag bar\n mainParts.push(\n deps.renderTagBar(\n { tags: allTags, totalScenarios: total },\n deps.tagBarDeps,\n ),\n );\n\n // Failure summary\n const failedCases = run.testCases.filter((tc) => tc.status === \"failed\");\n if (failedCases.length > 0) {\n mainParts.push(\n deps.renderFailureSummary({ failedCases }, deps.failureSummaryDeps),\n );\n }\n\n // Features\n for (const [file, testCases] of byFile) {\n mainParts.push(\n deps.renderFeature(\n { file, testCases, metricsMap: args.metricsMap },\n deps.featureDeps,\n ),\n );\n }\n\n const sidebar = `\n<aside class=\"db-sidebar\">\n <div class=\"db-sidebar-header\">\n <div class=\"db-logo\">Test Report</div>\n <div class=\"db-run-info\">\n <span class=\"db-run-date\">${startDate}</span>\n <span class=\"db-run-duration\">${durationSec}s</span>\n </div>\n </div>\n\n <div class=\"db-metrics\">\n <div class=\"db-metric db-metric--success\">\n <div class=\"db-metric-value\">${passed}</div>\n <div class=\"db-metric-label\">Passed</div>\n </div>\n <div class=\"db-metric db-metric--error\">\n <div class=\"db-metric-value\">${failed}</div>\n <div class=\"db-metric-label\">Failed</div>\n </div>\n <div class=\"db-metric db-metric--warning\">\n <div class=\"db-metric-value\">${skipped}</div>\n <div class=\"db-metric-label\">Skipped</div>\n </div>\n <div class=\"db-metric db-metric--info\">\n <div class=\"db-metric-value\">${passRate}%</div>\n <div class=\"db-metric-label\">Pass Rate</div>\n </div>\n </div>\n\n <div class=\"db-section\">\n <div class=\"db-section-title\">Features</div>\n <div class=\"db-tree\">\n ${treeItems.join(\"\\n \")}\n </div>\n </div>\n\n ${\n allTags.length > 0\n ? `<div class=\"db-section\">\n <div class=\"db-section-title\">Tags</div>\n <div class=\"db-tag-chips\">\n ${tagChips}\n </div>\n </div>`\n : \"\"\n }\n</aside>`;\n\n const main = `\n<div class=\"db-main\">\n ${mainParts.join(\"\\n \")}\n</div>`;\n\n return `<div class=\"dashboard-layout\">${sidebar}${main}</div>`;\n}\n\nconst DASHBOARD_CSS = `\n/* ============================================================================\n Google Fonts Import - DM Sans + JetBrains Mono\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Dark Mode (Default for Dashboard)\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors - dark by default */\n --background: #111318;\n --foreground: #e1e4ea;\n --card: #1a1d24;\n --card-foreground: #e1e4ea;\n --popover: #1a1d24;\n --popover-foreground: #e1e4ea;\n\n /* Primary - vibrant blue */\n --primary: #3b82f6;\n --primary-foreground: #ffffff;\n\n --secondary: #1e2028;\n --secondary-foreground: #c8ccd4;\n --muted: #1e2028;\n --muted-foreground: #6b7280;\n --accent: #252830;\n --accent-foreground: #e1e4ea;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #2a2d35;\n --input: #2a2d35;\n --ring: #3b82f6;\n --radius: 0.375rem;\n\n /* Shadows */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.4);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.45);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.4);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);\n\n /* Status colors - vibrant */\n --success: #10b981;\n --success-light: rgba(16, 185, 129, 0.1);\n --success-border: rgba(16, 185, 129, 0.25);\n --error: #ef4444;\n --error-light: rgba(239, 68, 68, 0.1);\n --error-border: rgba(239, 68, 68, 0.25);\n --warning: #f59e0b;\n --warning-light: rgba(245, 158, 11, 0.1);\n --warning-border: rgba(245, 158, 11, 0.25);\n --pending: #8b5cf6;\n --pending-light: rgba(139, 92, 246, 0.1);\n --pending-border: rgba(139, 92, 246, 0.25);\n\n /* Dashboard-specific */\n --keyword-color: #10b981;\n --tag-bg: rgba(59, 130, 246, 0.1);\n --tag-color: #60a5fa;\n --tag-border: rgba(59, 130, 246, 0.25);\n --step-param-color: #60a5fa;\n\n /* Accordion/Collapsible */\n --accordion-header-hover: #1e2028;\n --accordion-content-bg: #15171e;\n}\n\n/* Dark mode explicit */\n[data-theme=\"dark\"] {\n --font-sans: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #111318;\n --foreground: #e1e4ea;\n --card: #1a1d24;\n --card-foreground: #e1e4ea;\n --popover: #1a1d24;\n --popover-foreground: #e1e4ea;\n\n --primary: #3b82f6;\n --primary-foreground: #ffffff;\n\n --secondary: #1e2028;\n --secondary-foreground: #c8ccd4;\n --muted: #1e2028;\n --muted-foreground: #6b7280;\n --accent: #252830;\n --accent-foreground: #e1e4ea;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #2a2d35;\n --input: #2a2d35;\n --ring: #3b82f6;\n --radius: 0.375rem;\n\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.4);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.45);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.4);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);\n\n --success: #10b981;\n --success-light: rgba(16, 185, 129, 0.1);\n --success-border: rgba(16, 185, 129, 0.25);\n --error: #ef4444;\n --error-light: rgba(239, 68, 68, 0.1);\n --error-border: rgba(239, 68, 68, 0.25);\n --warning: #f59e0b;\n --warning-light: rgba(245, 158, 11, 0.1);\n --warning-border: rgba(245, 158, 11, 0.25);\n --pending: #8b5cf6;\n --pending-light: rgba(139, 92, 246, 0.1);\n --pending-border: rgba(139, 92, 246, 0.25);\n\n --keyword-color: #10b981;\n --tag-bg: rgba(59, 130, 246, 0.1);\n --tag-color: #60a5fa;\n --tag-border: rgba(59, 130, 246, 0.25);\n --step-param-color: #60a5fa;\n\n --accordion-header-hover: #1e2028;\n --accordion-content-bg: #15171e;\n}\n\n/* Light mode */\n[data-theme=\"light\"] {\n --background: #f8f9fb;\n --foreground: #1a1d24;\n --card: #ffffff;\n --card-foreground: #1a1d24;\n --popover: #ffffff;\n --popover-foreground: #1a1d24;\n\n --primary: #2563eb;\n --primary-foreground: #ffffff;\n\n --secondary: #f1f3f5;\n --secondary-foreground: #374151;\n --muted: #f1f3f5;\n --muted-foreground: #6b7280;\n --accent: #e8ebef;\n --accent-foreground: #1a1d24;\n --destructive: #dc2626;\n --destructive-foreground: #ffffff;\n --border: #e2e5ea;\n --input: #e2e5ea;\n --ring: #2563eb;\n\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.04);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.06);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.07), 0 2px 4px -2px rgb(0 0 0 / 0.05);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.05);\n\n --success: #059669;\n --success-light: rgba(5, 150, 105, 0.08);\n --success-border: rgba(5, 150, 105, 0.2);\n --error: #dc2626;\n --error-light: rgba(220, 38, 38, 0.08);\n --error-border: rgba(220, 38, 38, 0.2);\n --warning: #d97706;\n --warning-light: rgba(217, 119, 6, 0.08);\n --warning-border: rgba(217, 119, 6, 0.2);\n --pending: #7c3aed;\n --pending-light: rgba(124, 58, 237, 0.08);\n --pending-border: rgba(124, 58, 237, 0.2);\n\n --keyword-color: #059669;\n --tag-bg: rgba(37, 99, 235, 0.08);\n --tag-color: #2563eb;\n --tag-border: rgba(37, 99, 235, 0.2);\n --step-param-color: #2563eb;\n\n --accordion-header-hover: #f1f3f5;\n --accordion-content-bg: #fafbfc;\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: #111318;\n --foreground: #e1e4ea;\n --card: #1a1d24;\n --card-foreground: #e1e4ea;\n --popover: #1a1d24;\n --popover-foreground: #e1e4ea;\n --primary: #3b82f6;\n --primary-foreground: #ffffff;\n --secondary: #1e2028;\n --secondary-foreground: #c8ccd4;\n --muted: #1e2028;\n --muted-foreground: #6b7280;\n --accent: #252830;\n --accent-foreground: #e1e4ea;\n --destructive: #ef4444;\n --destructive-foreground: #ffffff;\n --border: #2a2d35;\n --input: #2a2d35;\n --ring: #3b82f6;\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.4);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.45);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.4);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);\n --success: #10b981;\n --success-light: rgba(16, 185, 129, 0.1);\n --success-border: rgba(16, 185, 129, 0.25);\n --error: #ef4444;\n --error-light: rgba(239, 68, 68, 0.1);\n --error-border: rgba(239, 68, 68, 0.25);\n --warning: #f59e0b;\n --warning-light: rgba(245, 158, 11, 0.1);\n --warning-border: rgba(245, 158, 11, 0.25);\n --pending: #8b5cf6;\n --pending-light: rgba(139, 92, 246, 0.1);\n --pending-border: rgba(139, 92, 246, 0.25);\n --keyword-color: #10b981;\n --tag-bg: rgba(59, 130, 246, 0.1);\n --tag-color: #60a5fa;\n --tag-border: rgba(59, 130, 246, 0.25);\n --step-param-color: #60a5fa;\n --accordion-header-hover: #1e2028;\n --accordion-content-bg: #15171e;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 13px;\n line-height: 1.5;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout - Container wraps the dashboard\n ============================================================================ */\n.container {\n max-width: 100%;\n margin: 0;\n padding: 0;\n}\n\n/* ============================================================================\n Dashboard Two-Column Layout\n ============================================================================ */\n.dashboard-layout {\n display: grid;\n grid-template-columns: 280px 1fr;\n min-height: 100vh;\n}\n\n@media (max-width: 900px) {\n .dashboard-layout {\n grid-template-columns: 1fr;\n }\n}\n\n/* ============================================================================\n Sidebar\n ============================================================================ */\n.db-sidebar {\n background: var(--card);\n border-right: 1px solid var(--border);\n padding: 0;\n overflow-y: auto;\n position: sticky;\n top: 0;\n height: 100vh;\n display: flex;\n flex-direction: column;\n}\n\n@media (max-width: 900px) {\n .db-sidebar {\n position: relative;\n height: auto;\n border-right: none;\n border-bottom: 1px solid var(--border);\n }\n}\n\n.db-sidebar-header {\n padding: 1.25rem 1rem 1rem;\n border-bottom: 1px solid var(--border);\n}\n\n.db-logo {\n font-size: 0.9375rem;\n font-weight: 700;\n color: var(--foreground);\n letter-spacing: -0.02em;\n}\n\n.db-run-info {\n display: flex;\n gap: 0.75rem;\n margin-top: 0.375rem;\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n}\n\n.db-run-duration {\n color: var(--primary);\n font-weight: 500;\n}\n\n/* ============================================================================\n Metrics Grid\n ============================================================================ */\n.db-metrics {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--border);\n}\n\n.db-metric {\n padding: 0.625rem 0.75rem;\n border-radius: var(--radius);\n border: 1px solid var(--border);\n background: var(--background);\n}\n\n.db-metric-value {\n font-size: 1.375rem;\n font-weight: 700;\n font-family: var(--font-mono);\n line-height: 1.1;\n letter-spacing: -0.03em;\n}\n\n.db-metric-label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-top: 0.125rem;\n}\n\n.db-metric--success .db-metric-value { color: var(--success); }\n.db-metric--success { border-color: var(--success-border); background: var(--success-light); }\n.db-metric--error .db-metric-value { color: var(--error); }\n.db-metric--error { border-color: var(--error-border); background: var(--error-light); }\n.db-metric--warning .db-metric-value { color: var(--warning); }\n.db-metric--warning { border-color: var(--warning-border); background: var(--warning-light); }\n.db-metric--info .db-metric-value { color: var(--primary); }\n.db-metric--info { border-color: var(--tag-border); background: var(--tag-bg); }\n\n/* ============================================================================\n Sidebar Sections\n ============================================================================ */\n.db-section {\n padding: 0.75rem 0;\n border-bottom: 1px solid var(--border);\n flex: 1;\n overflow-y: auto;\n}\n\n.db-section:last-child {\n border-bottom: none;\n flex: none;\n}\n\n.db-section-title {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--muted-foreground);\n font-weight: 600;\n padding: 0 1rem 0.5rem;\n}\n\n/* ============================================================================\n Feature Tree\n ============================================================================ */\n.db-tree {\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.db-tree-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4375rem 1rem;\n background: none;\n border: none;\n width: 100%;\n text-align: left;\n cursor: pointer;\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n transition: background-color 0.1s ease;\n}\n\n.db-tree-item:hover {\n background: var(--accent);\n}\n\n.db-tree-item.active {\n background: var(--accent);\n border-left: 2px solid var(--primary);\n padding-left: calc(1rem - 2px);\n}\n\n.db-tree-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.db-tree-dot--success { background: var(--success); }\n.db-tree-dot--error { background: var(--error); }\n.db-tree-dot--warning { background: var(--warning); }\n\n.db-tree-name {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.db-tree-count {\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n flex-shrink: 0;\n}\n\n/* ============================================================================\n Tag Chips (sidebar)\n ============================================================================ */\n.db-tag-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n padding: 0 1rem;\n}\n\n.db-tag-chip {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Main Content Area\n ============================================================================ */\n.db-main {\n padding: 1.25rem 1.5rem;\n overflow-y: auto;\n max-width: 1000px;\n}\n\n@media (max-width: 900px) {\n .db-main {\n padding: 1rem;\n }\n}\n\n/* ============================================================================\n Header - hidden in dashboard (info is in sidebar)\n ============================================================================ */\n.header {\n display: none;\n}\n\n/* ============================================================================\n Meta Info - hidden in dashboard (info is in sidebar)\n ============================================================================ */\n.meta-info {\n display: none;\n}\n\n/* ============================================================================\n Summary Cards - hidden in dashboard (metrics in sidebar)\n ============================================================================ */\n.summary {\n display: none;\n}\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 0.75rem;\n padding: 0.625rem 0.875rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.tag-bar-count {\n font-size: 0.625rem;\n font-weight: 600;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--error);\n background: var(--error-light);\n border: 1px solid var(--error-border);\n cursor: pointer;\n padding: 0.1875rem 0.625rem;\n border-radius: var(--radius);\n transition: all 0.15s ease;\n}\n\n.tag-bar-clear:hover {\n background: var(--error-border);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.375rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.1875rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.tag-pill:hover {\n background: var(--accent);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n margin-bottom: 0.75rem;\n font-weight: 500;\n}\n\n/* ============================================================================\n Failure Summary\n ============================================================================ */\n.failure-summary {\n margin-bottom: 0.75rem;\n padding: 0.75rem 0.875rem;\n background: var(--error-light);\n border: 1px solid var(--error-border);\n border-radius: var(--radius);\n}\n\n.failure-summary-title {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--error);\n margin-bottom: 0.375rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.failure-summary-list {\n list-style: none;\n padding: 0;\n}\n\n.failure-summary-list li {\n padding: 0.25rem 0;\n}\n\n.failure-summary-list a {\n font-size: 0.8125rem;\n color: var(--error);\n text-decoration: none;\n transition: opacity 0.15s ease;\n}\n\n.failure-summary-list a:hover {\n opacity: 0.8;\n text-decoration: underline;\n}\n\n/* ============================================================================\n Feature Sections - compact card style\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.5rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 0.625rem 0.875rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.1s ease;\n gap: 0.75rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 600;\n font-size: 0.8125rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.0625rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.75rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.1875rem;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.5rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios - compact nested style\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n margin-bottom: 0.375rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.5rem 0.75rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.1s ease;\n gap: 0.75rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-weight: 500;\n font-size: 0.8125rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-top: 0.25rem;\n}\n\n.tag {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.scenario-right {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n\n.scenario-duration {\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.5rem 0.75rem 0.75rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons\n ============================================================================ */\n.status-icon {\n font-size: 0.8125rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps\n ============================================================================ */\n.steps {\n margin-top: 0.125rem;\n padding: 0.125rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.375rem;\n padding: 0.25rem 0;\n font-size: 0.75rem;\n align-items: baseline;\n line-height: 1.5;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 0.875rem;\n text-align: center;\n font-size: 0.6875rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 48px;\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.step.continuation {\n padding-left: 1rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 500;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.625rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.6;\n}\n\n/* ============================================================================\n Error Display\n ============================================================================ */\n.error-box {\n margin-top: 0.5rem;\n padding: 0.625rem 0.75rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 1px);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments\n ============================================================================ */\n.attachments {\n margin-top: 0.5rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.1s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.375rem;\n border-radius: calc(var(--radius) - 1px);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.375rem;\n border-radius: calc(var(--radius) - 1px);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.6875rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars - thin dark\n ============================================================================ */\n::-webkit-scrollbar {\n width: 5px;\n height: 5px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection\n ============================================================================ */\n::selection {\n background: rgba(59, 130, 246, 0.2);\n color: inherit;\n}\n\n/* ============================================================================\n Animations - subtle\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-2px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.15s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.02s; }\n.feature:nth-child(3) { animation-delay: 0.04s; }\n.feature:nth-child(4) { animation-delay: 0.06s; }\n.feature:nth-child(5) { animation-delay: 0.08s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #e5e5e5;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body { font-size: 11px; }\n .container { max-width: 100%; padding: 0; }\n\n .dashboard-layout { grid-template-columns: 1fr; }\n .db-sidebar { display: none; }\n\n .header-actions,\n .tag-bar,\n .filter-results { display: none !important; }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content { display: block; }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.5rem;\n padding: 0.5rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 1px);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.25rem;\n margin-top: 0.125rem;\n margin-bottom: 0.375rem;\n padding: 0.375rem 0.625rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.375rem 0.625rem;\n margin-bottom: 0.375rem;\n background: var(--muted);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0;\n font-size: 0.75rem;\n line-height: 1.5;\n color: var(--foreground);\n}\n\n.doc-note:last-child { margin-bottom: 0; }\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-bottom: 0.375rem;\n}\n\n.doc-tag:last-child { margin-bottom: 0; }\n\n.doc-tag-item {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.375rem;\n margin-bottom: 0.25rem;\n font-size: 0.75rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child { margin-bottom: 0; }\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.doc-code:last-child { margin-bottom: 0; }\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.5625rem;\n font-weight: 500;\n padding: 0.0625rem 0.3125rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.375rem;\n}\n\n.doc-table:last-child { margin-bottom: 0; }\n\n.doc-table-label {\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.25rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.375rem 0.625rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.25rem;\n}\n\n.doc-link:last-child { margin-bottom: 0; }\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-size: 0.75rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.1s ease;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"\\\\2192\";\n font-size: 0.6875rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.doc-section:last-child { margin-bottom: 0; }\n\n.doc-section-title {\n padding: 0.375rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.625rem;\n background: var(--card);\n font-size: 0.75rem;\n line-height: 1.5;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content { white-space: normal; }\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n margin-top: 0.75em;\n margin-bottom: 0.375em;\n font-weight: 600;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child { margin-top: 0; }\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.8125rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.75rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p { margin: 0.375em 0; }\n.doc-section-parsed .doc-section-content p:first-child { margin-top: 0; }\n.doc-section-parsed .doc-section-content p:last-child { margin-bottom: 0; }\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol { margin: 0.375em 0; padding-left: 1.25em; }\n.doc-section-parsed .doc-section-content li { margin: 0.125em 0; }\n\n.doc-section-parsed .doc-section-content a { color: var(--primary); text-decoration: none; }\n.doc-section-parsed .doc-section-content a:hover { text-decoration: underline; }\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.0625em 0.25em;\n background: var(--muted);\n border-radius: 3px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.5em 0;\n padding: 0.625em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 1px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code { padding: 0; background: none; }\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.5em 0;\n padding: 0.375em 0.75em;\n border-left: 3px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n}\n\n.doc-section-parsed .doc-section-content blockquote p { margin: 0; }\n\n.doc-section-parsed .doc-section-content hr {\n margin: 0.75em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.5em 0;\n border-collapse: collapse;\n font-size: 0.75rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.375em 0.625em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 1px);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child { margin-bottom: 0; }\n\n.doc-mermaid-title {\n padding: 0.25rem 0.625rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.6875rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"\\\\25C7 \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot { margin-bottom: 0.375rem; }\n.doc-screenshot:last-child { margin-bottom: 0; }\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.25rem;\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.375rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.doc-custom:last-child { margin-bottom: 0; }\n\n.doc-custom-type {\n padding: 0.25rem 0.625rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-size: 0.625rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.625rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.6875rem;\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View\n ============================================================================ */\n.trace-view {\n margin-top: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 1px);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.625rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--foreground);\n transition: background-color 0.1s ease;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n border-radius: 9999px;\n}\n\n/* ============================================================================\n Search Input\n ============================================================================ */\n.search-input {\n height: 2rem;\n padding: 0 0.75rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.8125rem;\n width: 200px;\n transition: all 0.1s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n/* ============================================================================\n Theme Toggle\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 0.875rem;\n transition: all 0.1s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Summary Card (kept for JS filter compatibility)\n ============================================================================ */\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.75rem 1rem;\n transition: all 0.1s ease;\n}\n\n.summary-card .label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.25rem;\n}\n\n.summary-card .value {\n font-size: 1.5rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n font-family: var(--font-mono);\n}\n\n.summary-card.passed { background: var(--success-light); border-color: var(--success-border); }\n.summary-card.passed .value { color: var(--success); }\n.summary-card.failed { background: var(--error-light); border-color: var(--error-border); }\n.summary-card.failed .value { color: var(--error); }\n.summary-card.skipped { background: var(--warning-light); border-color: var(--warning-border); }\n.summary-card.skipped .value { color: var(--warning); }\n.summary-card.pending { background: var(--pending-light); border-color: var(--pending-border); }\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Retry info\n ============================================================================ */\n.retry-info {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n background: var(--warning-light);\n color: var(--warning);\n border: 1px solid var(--warning-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Metrics badge (sparkline-like trend indicator)\n ============================================================================ */\n.metrics-badge {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.0625rem 0.375rem;\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.metrics-badge.improving {\n background: var(--success-light);\n color: var(--success);\n border: 1px solid var(--success-border);\n}\n\n.metrics-badge.degrading {\n background: var(--error-light);\n color: var(--error);\n border: 1px solid var(--error-border);\n}\n\n.metrics-badge.stable {\n background: var(--muted);\n color: var(--muted-foreground);\n border: 1px solid var(--border);\n}\n`;\n\nconst DASHBOARD_JS = `\n(function() {\n // Feature tree navigation: click to scroll\n var treeItems = document.querySelectorAll('.db-tree-item');\n var features = document.querySelectorAll('.db-main .feature');\n\n treeItems.forEach(function(item) {\n item.addEventListener('click', function() {\n var index = parseInt(item.getAttribute('data-feature-index'), 10);\n var target = features[index];\n if (target) {\n target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n // Expand if collapsed\n if (target.classList.contains('collapsed')) {\n target.classList.remove('collapsed');\n var header = target.querySelector('.feature-header');\n if (header) header.setAttribute('aria-expanded', 'true');\n }\n }\n // Mark active\n treeItems.forEach(function(t) { t.classList.remove('active'); });\n item.classList.add('active');\n });\n });\n\n // Highlight active feature in sidebar on scroll\n var mainEl = document.querySelector('.db-main');\n if (mainEl && features.length > 0) {\n var onScroll = function() {\n var scrollTop = mainEl.scrollTop || window.scrollY;\n var activeIndex = 0;\n features.forEach(function(f, i) {\n var rect = f.getBoundingClientRect();\n if (rect.top <= 120) {\n activeIndex = i;\n }\n });\n treeItems.forEach(function(t, i) {\n if (i === activeIndex) {\n t.classList.add('active');\n } else {\n t.classList.remove('active');\n }\n });\n };\n\n // Listen on both the main element and window for scroll\n mainEl.addEventListener('scroll', onScroll, { passive: true });\n window.addEventListener('scroll', onScroll, { passive: true });\n }\n})();\n`;\n\nexport const dashboardTheme: HtmlTheme = {\n name: \"dashboard\",\n label: \"Dashboard\",\n css: DASHBOARD_CSS,\n buildBody: dashboardBuildBody,\n additionalJs: DASHBOARD_JS,\n};\n","/**\n * Playful theme — warm pastels, rounded corners, cheerful aesthetic.\n * Nunito font, WCAG AAA contrast, color-blind safe palette.\n */\n\nimport type { HtmlTheme } from \"./types.js\";\n\nexport const playfulTheme: HtmlTheme = {\n name: \"playful\",\n label: \"Playful\",\n css: `\n/* ============================================================================\n Google Fonts Import - Nunito for playful headings\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700;800&family=Source+Sans+3:wght@400;500;600&family=Source+Code+Pro:wght@400;500&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Default)\n Warm pastel palette with coral accent\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"Source Sans 3\", \"Nunito\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"Source Code Pro\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors — warm cream background, brown foreground */\n --background: #fef7f0;\n --foreground: #3d3229;\n --card: #ffffff;\n --card-foreground: #3d3229;\n --popover: #ffffff;\n --popover-foreground: #3d3229;\n\n /* Coral/salmon as primary */\n --primary: #e07a5f;\n --primary-foreground: #ffffff;\n\n --secondary: #fdf0e6;\n --secondary-foreground: #3d3229;\n --muted: #f5ebe0;\n --muted-foreground: #6b5b4e;\n --accent: #fdf0e6;\n --accent-foreground: #3d3229;\n --destructive: #c44536;\n --destructive-foreground: #ffffff;\n --border: #e6d5c3;\n --input: #e6d5c3;\n --ring: #e07a5f;\n --radius: 1rem;\n\n /* Shadows — soft and warm */\n --shadow-xs: 0 1px 2px 0 rgb(61 50 41 / 0.04);\n --shadow-sm: 0 1px 4px 0 rgb(61 50 41 / 0.06), 0 1px 2px -1px rgb(61 50 41 / 0.04);\n --shadow: 0 4px 8px -2px rgb(61 50 41 / 0.08), 0 2px 4px -2px rgb(61 50 41 / 0.04);\n --shadow-md: 0 10px 20px -4px rgb(61 50 41 / 0.1), 0 4px 8px -4px rgb(61 50 41 / 0.05);\n\n /* Status colors — color-blind safe, cheerful */\n --success: #2d8659;\n --success-light: #eef8f0;\n --success-border: #b8e0c8;\n --error: #c44536;\n --error-light: #fdf0ee;\n --error-border: #f0c4be;\n --warning: #c77d18;\n --warning-light: #fdf5e6;\n --warning-border: #f0d8a8;\n --pending: #7c5cbf;\n --pending-light: #f4f0fa;\n --pending-border: #d4c8ec;\n\n /* Playful-specific */\n --keyword-color: #b05740;\n --tag-bg: #fdf0e6;\n --tag-color: #b05740;\n --tag-border: #f0cdb8;\n --step-param-color: #5b7fc7;\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: #fdf5ed;\n --accordion-content-bg: #fdf8f3;\n}\n\n/* ============================================================================\n Dark Mode — warm dark palette\n ============================================================================ */\n[data-theme=\"dark\"] {\n --font-sans: \"Source Sans 3\", \"Nunito\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"Source Code Pro\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n --background: #1e1814;\n --foreground: #f0e6dc;\n --card: #2a2118;\n --card-foreground: #f0e6dc;\n --popover: #2a2118;\n --popover-foreground: #f0e6dc;\n\n --primary: #e8957d;\n --primary-foreground: #1e1814;\n\n --secondary: #332920;\n --secondary-foreground: #f0e6dc;\n --muted: #332920;\n --muted-foreground: #a89585;\n --accent: #332920;\n --accent-foreground: #f0e6dc;\n --destructive: #e05a4c;\n --destructive-foreground: #ffffff;\n --border: #4a3d32;\n --input: #4a3d32;\n --ring: #e8957d;\n --radius: 1rem;\n\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.25);\n --shadow-sm: 0 1px 4px 0 rgb(0 0 0 / 0.35), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n --shadow: 0 4px 8px -2px rgb(0 0 0 / 0.35), 0 2px 4px -2px rgb(0 0 0 / 0.3);\n --shadow-md: 0 10px 20px -4px rgb(0 0 0 / 0.4), 0 4px 8px -4px rgb(0 0 0 / 0.3);\n\n --success: #5ab87e;\n --success-light: #1e2e22;\n --success-border: #2d5e3e;\n --error: #e05a4c;\n --error-light: #2e1e1c;\n --error-border: #6b302a;\n --warning: #e0a63e;\n --warning-light: #2e2518;\n --warning-border: #6b5120;\n --pending: #a68be0;\n --pending-light: #241e30;\n --pending-border: #4a3870;\n\n --keyword-color: #e8957d;\n --tag-bg: #332920;\n --tag-color: #e8957d;\n --tag-border: #5a4535;\n --step-param-color: #8aade0;\n\n --accordion-header-hover: #332920;\n --accordion-content-bg: #241e18;\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: #1e1814;\n --foreground: #f0e6dc;\n --card: #2a2118;\n --card-foreground: #f0e6dc;\n --popover: #2a2118;\n --popover-foreground: #f0e6dc;\n --primary: #e8957d;\n --primary-foreground: #1e1814;\n --secondary: #332920;\n --secondary-foreground: #f0e6dc;\n --muted: #332920;\n --muted-foreground: #a89585;\n --accent: #332920;\n --accent-foreground: #f0e6dc;\n --destructive: #e05a4c;\n --destructive-foreground: #ffffff;\n --border: #4a3d32;\n --input: #4a3d32;\n --ring: #e8957d;\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.25);\n --shadow-sm: 0 1px 4px 0 rgb(0 0 0 / 0.35), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n --shadow: 0 4px 8px -2px rgb(0 0 0 / 0.35), 0 2px 4px -2px rgb(0 0 0 / 0.3);\n --shadow-md: 0 10px 20px -4px rgb(0 0 0 / 0.4), 0 4px 8px -4px rgb(0 0 0 / 0.3);\n --success: #5ab87e;\n --success-light: #1e2e22;\n --success-border: #2d5e3e;\n --error: #e05a4c;\n --error-light: #2e1e1c;\n --error-border: #6b302a;\n --warning: #e0a63e;\n --warning-light: #2e2518;\n --warning-border: #6b5120;\n --pending: #a68be0;\n --pending-light: #241e30;\n --pending-border: #4a3870;\n --keyword-color: #e8957d;\n --tag-bg: #332920;\n --tag-color: #e8957d;\n --tag-border: #5a4535;\n --step-param-color: #8aade0;\n --accordion-header-hover: #332920;\n --accordion-content-bg: #241e18;\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 15px;\n line-height: 1.7;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nh1, h2, h3, h4, h5, h6,\n.feature-title,\n.header h1 {\n font-family: \"Nunito\", var(--font-sans);\n font-weight: 700;\n}\n\n/* ============================================================================\n Layout\n ============================================================================ */\n.container {\n max-width: 1100px;\n margin: 0 auto;\n padding: 1.5rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 2.5rem 3rem;\n }\n}\n\n/* ============================================================================\n Header — playful style\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1.5rem;\n margin-bottom: 2rem;\n border-bottom: 2px solid var(--border);\n}\n\n.header h1 {\n font-size: 1.75rem;\n font-weight: 800;\n letter-spacing: -0.02em;\n color: var(--foreground);\n}\n\n.header-actions {\n display: flex;\n gap: 0.75rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle — large touch target\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.75rem;\n height: 2.75rem;\n border: 2px solid var(--border);\n border-radius: var(--radius);\n background: var(--card);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1.125rem;\n transition: all 0.2s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--primary);\n transform: scale(1.05);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring);\n}\n\n/* ============================================================================\n Search Input — rounded, large touch target\n ============================================================================ */\n.search-input {\n height: 2.75rem;\n padding: 0 1rem;\n border: 2px solid var(--border);\n border-radius: var(--radius);\n background: var(--card);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.9375rem;\n width: 220px;\n transition: all 0.2s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 4px rgb(224 122 95 / 0.15);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 280px;\n }\n}\n\n/* ============================================================================\n Meta Info — warm card\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem 2rem;\n margin-bottom: 1.5rem;\n padding: 1rem 1.25rem;\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.875rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 600;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n/* ============================================================================\n Summary Cards — pastel tinted with hover animation\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n margin-bottom: 2rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: var(--radius);\n padding: 1.25rem 1.5rem;\n transition: all 0.25s ease;\n cursor: default;\n}\n\n.summary-card:hover {\n box-shadow: var(--shadow-md);\n transform: translateY(-3px);\n}\n\n.summary-card .label {\n font-family: \"Nunito\", var(--font-sans);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--muted-foreground);\n font-weight: 700;\n margin-bottom: 0.5rem;\n}\n\n.summary-card .value {\n font-family: \"Nunito\", var(--font-sans);\n font-size: 2.25rem;\n font-weight: 800;\n letter-spacing: -0.03em;\n line-height: 1.1;\n}\n\n/* Passed — green tint */\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed — red tint */\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped — amber tint */\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending — purple tint */\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Tag Filter Bar\n ============================================================================ */\n.tag-bar {\n margin-bottom: 1.25rem;\n padding: 1rem 1.25rem;\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: var(--radius);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tag-bar-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.tag-bar-toggle {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n color: inherit;\n font: inherit;\n}\n\n.tag-bar-toggle:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n border-radius: var(--radius);\n}\n\n.tag-bar-label {\n font-family: \"Nunito\", var(--font-sans);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--muted-foreground);\n font-weight: 700;\n}\n\n.tag-bar-count {\n font-size: 0.75rem;\n font-weight: 700;\n color: var(--primary);\n}\n\n.tag-bar-chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s ease;\n flex-shrink: 0;\n}\n\n.tag-bar-collapsed .tag-bar-chevron {\n transform: rotate(0deg);\n}\n\n.tag-bar:not(.tag-bar-collapsed) .tag-bar-chevron {\n transform: rotate(180deg);\n}\n\n.tag-bar-clear {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--destructive);\n background: var(--error-light);\n border: 2px solid var(--error-border);\n cursor: pointer;\n padding: 0.375rem 0.875rem;\n border-radius: var(--radius);\n transition: all 0.2s ease;\n min-height: 2.25rem;\n}\n\n.tag-bar-clear:hover {\n background: var(--error-border);\n}\n\n.tag-bar-clear:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-bar-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n max-height: 200px;\n overflow-y: auto;\n margin-top: 0.625rem;\n}\n\n.tag-bar-collapsed .tag-bar-pills {\n display: none;\n}\n\n.tag-pill {\n font-size: 0.8125rem;\n font-weight: 600;\n padding: 0.375rem 0.75rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 2px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n cursor: pointer;\n transition: all 0.2s ease;\n min-height: 2rem;\n}\n\n.tag-pill:hover {\n background: var(--success-border);\n transform: scale(1.05);\n}\n\n.tag-pill:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.tag-pill.active {\n background: var(--primary);\n color: var(--primary-foreground);\n border-color: var(--primary);\n}\n\n/* ============================================================================\n Summary Card Status Filter\n ============================================================================ */\n.summary-card.status-active {\n box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring);\n}\n\n/* ============================================================================\n Filter Results Counter\n ============================================================================ */\n.filter-results {\n text-align: center;\n font-size: 0.875rem;\n color: var(--muted-foreground);\n margin-bottom: 1.25rem;\n font-weight: 600;\n}\n\n/* ============================================================================\n Feature Sections — rounded accordion\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.875rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 1rem 1.25rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.2s ease;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 700;\n font-size: 1.0625rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.1875rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 0.875rem;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.75rem;\n border-top: 2px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios — pastel nested cards\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n margin-bottom: 0.625rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.875rem 1.25rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.2s ease;\n gap: 1rem;\n min-height: 2.75rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n font-size: 0.9375rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.5rem;\n}\n\n.tag {\n font-size: 0.75rem;\n font-weight: 600;\n padding: 0.1875rem 0.625rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 2px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.scenario-duration {\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.875rem 1.25rem 1.25rem;\n border-top: 2px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons — cheerful\n ============================================================================ */\n.status-icon {\n font-size: 1rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps — individual pastel cards\n ============================================================================ */\n.steps {\n margin-top: 0.375rem;\n padding: 0.25rem 0;\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n}\n\n.step {\n display: flex;\n gap: 0.625rem;\n padding: 0.625rem 0.875rem;\n font-size: 0.875rem;\n align-items: baseline;\n line-height: 1.6;\n background: var(--accordion-content-bg);\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 4px);\n transition: border-color 0.2s ease;\n}\n\n.step:hover {\n border-color: var(--primary);\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1.125rem;\n text-align: center;\n font-size: 0.8125rem;\n}\n\n.step-keyword {\n font-weight: 700;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 56px;\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n/* Indent continuation keywords (And, But, *) to show they belong to previous step */\n.step.continuation {\n padding-left: 2rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 600;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-param {\n font-style: italic;\n font-weight: 600;\n color: var(--step-param-color);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.75rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.7;\n}\n\n/* ============================================================================\n Error Display — alert style\n ============================================================================ */\n.error-box {\n margin-top: 0.875rem;\n padding: 1rem 1.25rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 2px);\n border: 2px solid var(--error-border);\n border-left: 4px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments — badge style\n ============================================================================ */\n.attachments {\n margin-top: 0.875rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.625rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.5rem 0.875rem;\n background: var(--muted);\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n font-size: 0.8125rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.2s ease;\n min-height: 2.25rem;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.625rem;\n border-radius: calc(var(--radius) - 2px);\n border: 2px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.625rem;\n border-radius: calc(var(--radius) - 2px);\n border: 2px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon — smooth rotation\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.8125rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars — rounded, warm\n ============================================================================ */\n::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 4px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States — coral ring\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring);\n}\n\n/* ============================================================================\n Selection — warm coral tint\n ============================================================================ */\n::selection {\n background: rgb(224 122 95 / 0.2);\n color: inherit;\n}\n\n/* ============================================================================\n Animations — playful reveals\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.3s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.03s; }\n.feature:nth-child(3) { animation-delay: 0.06s; }\n.feature:nth-child(4) { animation-delay: 0.09s; }\n.feature:nth-child(5) { animation-delay: 0.12s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: #1a1a1a;\n --card: white;\n --border: #ddd;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body {\n font-size: 12px;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions,\n .tag-bar,\n .filter-results {\n display: none !important;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .step {\n background: none;\n border: 1px solid #ddd;\n }\n\n .summary-card:hover {\n transform: none;\n box-shadow: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.875rem;\n padding: 0.875rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 2px);\n border: 2px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.75rem;\n margin-top: 0.375rem;\n margin-bottom: 0.625rem;\n padding: 0.625rem 0.875rem;\n background: var(--accordion-content-bg);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.625rem 0.875rem;\n margin-bottom: 0.625rem;\n background: var(--muted);\n border-left: 4px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n font-size: 0.875rem;\n line-height: 1.6;\n color: var(--foreground);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n margin-bottom: 0.625rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.75rem;\n font-weight: 600;\n padding: 0.1875rem 0.625rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 2px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.625rem;\n margin-bottom: 0.5rem;\n font-size: 0.875rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 700;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.625rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.5rem 0.875rem;\n background: var(--muted);\n border-bottom: 2px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.6875rem;\n font-weight: 700;\n padding: 0.1875rem 0.5rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.875rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.625rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--muted-foreground);\n margin-bottom: 0.5rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n font-size: 0.8125rem;\n font-family: var(--font-mono);\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.625rem 0.875rem;\n text-align: left;\n border-bottom: 2px solid var(--border);\n border-right: 2px solid var(--border);\n}\n\n.doc-table th:last-child,\n.doc-table td:last-child {\n border-right: none;\n}\n\n.doc-table tr:last-child td {\n border-bottom: none;\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 700;\n color: var(--foreground);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.5rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.875rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.2s ease;\n font-weight: 600;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"\\\\2192\";\n font-size: 0.8125rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.625rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.625rem 0.875rem;\n background: var(--muted);\n border-bottom: 2px solid var(--border);\n font-family: \"Nunito\", var(--font-sans);\n font-size: 0.875rem;\n font-weight: 700;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.875rem;\n background: var(--card);\n font-size: 0.875rem;\n line-height: 1.7;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n font-family: \"Nunito\", var(--font-sans);\n margin-top: 1em;\n margin-bottom: 0.5em;\n font-weight: 700;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.375rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.1875rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1.0625rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.875rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.15em 0.4em;\n background: var(--muted);\n border-radius: 6px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.875em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 2px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.625em 1.125em;\n border-left: 4px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1em 0;\n border: none;\n border-top: 2px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.875rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border: 2px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 700;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 2px);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.625rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.5rem 0.875rem;\n background: var(--muted);\n border-bottom: 2px solid var(--border);\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"\\\\25C7 \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.875rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.625rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.5rem;\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.625rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.5rem 0.875rem;\n background: var(--warning-light);\n border-bottom: 2px solid var(--warning-border);\n font-size: 0.75rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.875rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n line-height: 1.7;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Trace View - OTel span waterfall\n ============================================================================ */\n.trace-view {\n margin-top: 0.875rem;\n border: 2px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.trace-view-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.625rem 0.875rem;\n background: var(--card);\n cursor: pointer;\n user-select: none;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--foreground);\n transition: background-color 0.2s ease;\n min-height: 2.75rem;\n}\n\n.trace-view-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.trace-view-count {\n font-size: 0.75rem;\n font-weight: 700;\n padding: 0.1875rem 0.625rem;\n background: var(--success-light);\n color: var(--success);\n border: 2px solid var(--success-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.trace-view-content {\n border-top: 2px solid var(--border);\n padding: 0.625rem 0.875rem;\n background: var(--accordion-content-bg);\n}\n\n.trace-view.collapsed .trace-view-content {\n display: none;\n}\n\n.trace-view-axis {\n display: flex;\n justify-content: space-between;\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n color: var(--muted-foreground);\n padding-bottom: 0.5rem;\n margin-bottom: 0.5rem;\n border-bottom: 2px solid var(--border);\n}\n\n.trace-view-row {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n padding: 0.25rem 0;\n font-size: 0.8125rem;\n}\n\n.trace-view-name {\n width: 35%;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--foreground);\n}\n\n.trace-view-status-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.trace-view-status-ok { background: var(--success); }\n.trace-view-status-error { background: var(--error); }\n.trace-view-status-unset { background: var(--muted-foreground); }\n\n.trace-view-bar-container {\n flex: 1;\n position: relative;\n height: 1.5rem;\n background: var(--muted);\n border-radius: 6px;\n}\n\n.trace-view-bar {\n position: absolute;\n top: 0;\n height: 100%;\n border-radius: 6px;\n min-width: 2px;\n display: flex;\n align-items: center;\n padding: 0 0.5rem;\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n color: white;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.trace-view-bar-ok { background: var(--success); }\n.trace-view-bar-error { background: var(--error); }\n.trace-view-bar-unset { background: var(--muted-foreground); }\n\n@media print {\n .trace-view.collapsed .trace-view-content {\n display: block;\n }\n}\n\n/* ============================================================================\n History metric badges\n ============================================================================ */\n.badge { display: inline-block; padding: 3px 8px; border-radius: 8px; font-size: 0.75em; font-weight: 700; margin-left: 4px; vertical-align: middle; }\n.badge-grade { color: #fff; }\n.badge-grade-A { background: var(--success); }\n.badge-grade-B { background: #2196F3; }\n.badge-grade-C { background: #FF9800; }\n.badge-grade-D { background: #f44336; }\n.badge-grade-F { background: #9E0000; }\n.badge-flaky { background: #FF9800; color: #fff; }\n.badge-perf { font-size: 0.7em; }\n.badge-perf-improving { color: var(--success); }\n.badge-perf-regressing { color: var(--error); }\n\n/* Failure summary */\n.failure-summary {\n margin: 1.25rem 0;\n padding: 1rem 1.25rem;\n border: 2px solid var(--error);\n border-radius: var(--radius);\n background: color-mix(in srgb, var(--error) 8%, transparent);\n}\n.failure-summary-header {\n font-family: \"Nunito\", var(--font-sans);\n font-weight: 700;\n font-size: 0.9375rem;\n color: var(--error);\n margin-bottom: 0.625rem;\n}\n.failure-summary-note {\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n margin-bottom: 0.625rem;\n}\n.failure-summary-note code {\n font-family: var(--font-mono);\n font-size: 0.8125rem;\n}\n.failure-summary ul {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n}\n.failure-summary li a {\n font-size: 0.875rem;\n color: var(--foreground);\n text-decoration: none;\n}\n.failure-summary li a:hover {\n text-decoration: underline;\n color: var(--error);\n}\n\n/* Source permalink */\n.source-link {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n text-decoration: none;\n font-family: var(--font-mono);\n}\n.source-link:hover {\n text-decoration: underline;\n color: var(--foreground);\n}\n\n/* ============================================================================\n Detail Level Toggle — large touch target\n ============================================================================ */\n.detail-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.75rem;\n height: 2.75rem;\n border: 2px solid var(--border);\n border-radius: var(--radius);\n background: var(--card);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1.125rem;\n transition: all 0.2s ease;\n}\n\n.detail-toggle:hover {\n background: var(--accent);\n border-color: var(--primary);\n transform: scale(1.05);\n}\n\n.detail-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--background), 0 0 0 5px var(--ring);\n}\n\n[data-detail-level=\"minimal\"] .story-docs,\n[data-detail-level=\"minimal\"] .step-docs {\n display: none;\n}\n`,\n};\n","/**\n * Theme registry — resolves theme names to theme objects.\n */\n\nimport type { HtmlTheme, HtmlThemeName } from \"./types.js\";\nimport { defaultTheme } from \"./default.js\";\nimport { corporateTheme } from \"./corporate.js\";\nimport { terminalTheme } from \"./terminal.js\";\nimport { minimalTheme } from \"./minimal.js\";\nimport { dashboardTheme } from \"./dashboard.js\";\nimport { playfulTheme } from \"./playful.js\";\n\nconst THEME_REGISTRY = new Map<string, HtmlTheme>([\n [\"default\", defaultTheme],\n [\"corporate\", corporateTheme],\n [\"terminal\", terminalTheme],\n [\"minimal\", minimalTheme],\n [\"dashboard\", dashboardTheme],\n [\"playful\", playfulTheme],\n]);\n\n/** Resolve a theme by name or pass through a custom theme object. */\nexport function resolveTheme(nameOrTheme: string | HtmlTheme): HtmlTheme {\n if (typeof nameOrTheme === \"object\") return nameOrTheme;\n const theme = THEME_REGISTRY.get(nameOrTheme);\n if (!theme) {\n throw new Error(\n `Unknown theme: \"${nameOrTheme}\". Available: ${[...THEME_REGISTRY.keys()].join(\", \")}`,\n );\n }\n return theme;\n}\n\n/** List available built-in theme names. */\nexport function getAvailableThemes(): string[] {\n return [...THEME_REGISTRY.keys()];\n}\n\n/** Get all themes that only use CSS (no custom body/template overrides). */\nexport function getCssOnlyThemes(): HtmlTheme[] {\n return [...THEME_REGISTRY.values()].filter(\n (theme) => !theme.buildBody && !theme.generateTemplate,\n );\n}\n\nexport type { HtmlTheme, HtmlThemeName } from \"./types.js\";\n","/**\n * Pure helper: map test status to display icon.\n * Used by scenario and steps renderers; inject via deps for testability.\n */\n\nimport type { TestStatus } from \"../../../types/test-result\";\n\nexport type GetStatusIcon = (status: TestStatus) => string;\n\nexport function getStatusIcon(status: TestStatus): string {\n switch (status) {\n case \"passed\":\n return \"✓\";\n case \"failed\":\n return \"✗\";\n case \"skipped\":\n return \"○\";\n case \"pending\":\n return \"◔\";\n default:\n return \"?\";\n }\n}\n","/**\n * Render meta info section (fn(args, deps)).\n */\n\nexport interface RenderMetaInfoArgs {\n startedAtMs: number;\n durationMs: number;\n packageVersion?: string;\n gitSha?: string;\n ciName?: string;\n ciBranch?: string;\n ciUrl?: string;\n ciCommitSha?: string;\n ciBuildNumber?: string;\n}\n\nexport interface RenderMetaInfoDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderMetaInfo(\n args: RenderMetaInfoArgs,\n deps: RenderMetaInfoDeps,\n): string {\n const items: string[] = [];\n\n const startDate = new Date(args.startedAtMs);\n items.push(`<dt>Started:</dt><dd>${startDate.toISOString()}</dd>`);\n\n const duration = (args.durationMs / 1000).toFixed(2);\n items.push(`<dt>Duration:</dt><dd>${duration}s</dd>`);\n\n if (args.packageVersion) {\n items.push(`<dt>Version:</dt><dd>${deps.escapeHtml(args.packageVersion)}</dd>`);\n }\n\n if (args.gitSha) {\n const shortSha =\n args.gitSha.length > 7 ? args.gitSha.slice(0, 7) : args.gitSha;\n items.push(`<dt>Git:</dt><dd>${deps.escapeHtml(shortSha)}</dd>`);\n }\n\n if (args.ciName) {\n // When URL and build number are present, render build number as a link\n if (args.ciUrl && args.ciBuildNumber) {\n items.push(\n `<dt>CI:</dt><dd>${deps.escapeHtml(args.ciName)} <a href=\"${deps.escapeHtml(args.ciUrl)}\">#${deps.escapeHtml(args.ciBuildNumber)}</a></dd>`,\n );\n } else {\n items.push(`<dt>CI:</dt><dd>${deps.escapeHtml(args.ciName)}</dd>`);\n }\n }\n\n if (args.ciBranch) {\n items.push(`<dt>Branch:</dt><dd>${deps.escapeHtml(args.ciBranch)}</dd>`);\n }\n\n if (args.ciCommitSha) {\n const shortSha =\n args.ciCommitSha.length > 7\n ? args.ciCommitSha.slice(0, 7)\n : args.ciCommitSha;\n items.push(\n `<dt>Commit:</dt><dd title=\"${deps.escapeHtml(args.ciCommitSha)}\">${deps.escapeHtml(shortSha)}</dd>`,\n );\n }\n\n return `<dl class=\"meta-info\">${items.join(\"\")}</dl>`;\n}\n","/**\n * Render summary cards section (fn(args, deps)).\n * No deps: pure counts to HTML.\n */\n\nexport interface RenderSummaryArgs {\n total: number;\n passed: number;\n failed: number;\n skipped: number;\n}\n\nexport interface RenderSummaryDeps {\n // No dependencies; structure only\n}\n\nexport function renderSummary(\n args: RenderSummaryArgs,\n _deps: RenderSummaryDeps,\n): string {\n const { total, passed, failed, skipped } = args;\n return `\n<div class=\"summary\">\n <div class=\"summary-card\">\n <div class=\"label\">Total</div>\n <div class=\"value\">${total}</div>\n </div>\n <div class=\"summary-card passed\">\n <div class=\"label\">Passed</div>\n <div class=\"value\">${passed}</div>\n </div>\n <div class=\"summary-card failed\">\n <div class=\"label\">Failed</div>\n <div class=\"value\">${failed}</div>\n </div>\n <div class=\"summary-card skipped\">\n <div class=\"label\">Skipped</div>\n <div class=\"value\">${skipped}</div>\n </div>\n</div>`;\n}\n","/**\n * Render tag filter bar (fn(args, deps)).\n * Displays a collapsible tag bar with clickable tag pills for filtering scenarios,\n * ARIA attributes for accessibility, and a results counter.\n */\n\nexport interface RenderTagBarArgs {\n tags: string[];\n totalScenarios: number;\n}\n\nexport interface RenderTagBarDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderTagBar(\n args: RenderTagBarArgs,\n deps: RenderTagBarDeps,\n): string {\n const { tags, totalScenarios } = args;\n\n if (tags.length === 0) return \"\";\n\n const pills = tags\n .map(\n (tag) =>\n `<button type=\"button\" class=\"tag-pill\" data-tag=\"${deps.escapeHtml(tag)}\" aria-pressed=\"false\">${deps.escapeHtml(tag)}</button>`,\n )\n .join(\"\\n \");\n\n return `\n<div class=\"tag-bar tag-bar-collapsed\">\n <div class=\"tag-bar-header\">\n <button type=\"button\" class=\"tag-bar-toggle\" aria-expanded=\"false\" aria-controls=\"tag-pills-region\">\n <span class=\"tag-bar-label\">Filter by tag</span>\n <span class=\"tag-bar-count\" aria-live=\"polite\"></span>\n <svg class=\"tag-bar-chevron\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M4 6l4 4 4-4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"tag-bar-clear\" aria-label=\"Clear all tag filters\" style=\"display:none\">Clear all</button>\n </div>\n <div id=\"tag-pills-region\" class=\"tag-bar-pills\" role=\"group\" aria-label=\"Tag filters\">\n ${pills}\n </div>\n</div>\n<div class=\"filter-results\" style=\"display:none\" aria-live=\"polite\">\n Showing <span class=\"visible-count\">0</span> of <span class=\"total-count\">${totalScenarios}</span> scenarios\n</div>`;\n}\n","/**\n * Render error box (fn(args, deps)).\n */\n\nexport interface RenderErrorBoxArgs {\n message: string;\n stack?: string;\n}\n\nexport interface RenderErrorBoxDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderErrorBox(\n args: RenderErrorBoxArgs,\n deps: RenderErrorBoxDeps,\n): string {\n const body =\n args.stack != null\n ? `${deps.escapeHtml(args.message)}\\n\\n${deps.escapeHtml(args.stack)}`\n : deps.escapeHtml(args.message);\n return `<div class=\"error-box\">${body}</div>`;\n}\n","/**\n * Render attachments section (fn(args, deps)).\n */\n\nimport type { Attachment } from \"../../../types/test-result\";\n\nexport interface RenderAttachmentsArgs {\n attachments: Attachment[];\n}\n\nexport interface RenderAttachmentsDeps {\n escapeHtml: (str: string) => string;\n embedScreenshots: boolean;\n}\n\nexport function renderAttachments(\n args: RenderAttachmentsArgs,\n deps: RenderAttachmentsDeps,\n): string {\n if (args.attachments.length === 0) {\n return \"\";\n }\n\n const items = args.attachments.map((att) => {\n const isImage = att.mediaType.startsWith(\"image/\");\n const isVideo = att.mediaType.startsWith(\"video/\");\n const isBase64 = att.contentEncoding === \"BASE64\";\n\n if (isImage && deps.embedScreenshots && isBase64) {\n return `\n<div class=\"attachment\">\n ${deps.escapeHtml(att.name)}\n <img class=\"attachment-image\" src=\"data:${att.mediaType};base64,${att.body}\" alt=\"${deps.escapeHtml(att.name)}\" />\n</div>`;\n }\n\n if (isVideo && deps.embedScreenshots) {\n const src = isBase64\n ? `data:${att.mediaType};base64,${att.body}`\n : att.body;\n return `\n<div class=\"attachment\">\n ${deps.escapeHtml(att.name)}\n <video class=\"attachment-video\" controls src=\"${deps.escapeHtml(src)}\"></video>\n</div>`;\n }\n\n const href = isBase64\n ? `data:${att.mediaType};base64,${att.body}`\n : att.body;\n\n return `<a class=\"attachment\" href=\"${deps.escapeHtml(href)}\">${deps.escapeHtml(att.name)}</a>`;\n });\n\n return `<div class=\"attachments\">${items.join(\"\")}</div>`;\n}\n","/**\n * Render doc entries (fn(args, deps)).\n * One function per doc kind + dispatcher renderDocEntry.\n */\n\nimport type { DocEntry } from \"../../../types/story\";\n\nexport interface DocEntryDeps {\n escapeHtml: (str: string) => string;\n syntaxHighlighting: boolean;\n markdownEnabled: boolean;\n mermaidEnabled: boolean;\n}\n\nexport function renderDocNote(\n entry: Extract<DocEntry, { kind: \"note\" }>,\n deps: DocEntryDeps,\n): string {\n return `<div class=\"doc-note\">${deps.escapeHtml(entry.text)}</div>`;\n}\n\nexport function renderDocTag(\n entry: Extract<DocEntry, { kind: \"tag\" }>,\n deps: DocEntryDeps,\n): string {\n const tags = entry.names\n .map((t) => `<span class=\"doc-tag-item\">${deps.escapeHtml(t)}</span>`)\n .join(\"\");\n return `<div class=\"doc-tag\">${tags}</div>`;\n}\n\nexport function renderDocKv(\n entry: Extract<DocEntry, { kind: \"kv\" }>,\n deps: DocEntryDeps,\n): string {\n const valueStr =\n typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value, null, 2);\n return `<div class=\"doc-kv\">\n <span class=\"doc-kv-label\">${deps.escapeHtml(entry.label)}:</span>\n <span class=\"doc-kv-value\">${deps.escapeHtml(valueStr)}</span>\n</div>`;\n}\n\nexport function renderDocCode(\n entry: Extract<DocEntry, { kind: \"code\" }>,\n deps: DocEntryDeps,\n): string {\n const langBadge = entry.lang\n ? `<span class=\"doc-code-lang\">${deps.escapeHtml(entry.lang)}</span>`\n : \"\";\n const langClass =\n deps.syntaxHighlighting && entry.lang\n ? ` class=\"language-${deps.escapeHtml(entry.lang)}\"`\n : \"\";\n return `<div class=\"doc-code\">\n <div class=\"doc-code-header\">\n <span class=\"doc-code-label\">${deps.escapeHtml(entry.label)}</span>\n ${langBadge}\n </div>\n <pre class=\"doc-code-content\"><code${langClass}>${deps.escapeHtml(entry.content)}</code></pre>\n</div>`;\n}\n\nexport function renderDocTable(\n entry: Extract<DocEntry, { kind: \"table\" }>,\n deps: DocEntryDeps,\n): string {\n const headers = entry.columns\n .map((c) => `<th>${deps.escapeHtml(c)}</th>`)\n .join(\"\");\n const rows = entry.rows\n .map((r) =>\n `<tr>${r.map((c) => `<td>${deps.escapeHtml(c)}</td>`).join(\"\")}</tr>`,\n )\n .join(\"\");\n return `<div class=\"doc-table\">\n <div class=\"doc-table-label\">${deps.escapeHtml(entry.label)}</div>\n <table>\n <thead><tr>${headers}</tr></thead>\n <tbody>${rows}</tbody>\n </table>\n</div>`;\n}\n\nexport function renderDocLink(\n entry: Extract<DocEntry, { kind: \"link\" }>,\n deps: DocEntryDeps,\n): string {\n return `<div class=\"doc-link\">\n <a href=\"${deps.escapeHtml(entry.url)}\" target=\"_blank\" rel=\"noopener noreferrer\">${deps.escapeHtml(entry.label)}</a>\n</div>`;\n}\n\nexport function renderDocSection(\n entry: Extract<DocEntry, { kind: \"section\" }>,\n deps: DocEntryDeps,\n): string {\n if (deps.markdownEnabled) {\n const encodedMarkdown = btoa(encodeURIComponent(entry.markdown));\n return `<div class=\"doc-section doc-section-parsed\">\n <div class=\"doc-section-title\">${deps.escapeHtml(entry.title)}</div>\n <div class=\"doc-section-content\" data-markdown=\"${encodedMarkdown}\"></div>\n</div>`;\n }\n return `<div class=\"doc-section\">\n <div class=\"doc-section-title\">${deps.escapeHtml(entry.title)}</div>\n <pre class=\"doc-section-content\">${deps.escapeHtml(entry.markdown)}</pre>\n</div>`;\n}\n\nexport function renderDocMermaid(\n entry: Extract<DocEntry, { kind: \"mermaid\" }>,\n deps: DocEntryDeps,\n): string {\n const title = entry.title\n ? `<div class=\"doc-mermaid-title\">${deps.escapeHtml(entry.title)}</div>`\n : \"\";\n\n if (deps.mermaidEnabled) {\n return `<div class=\"doc-mermaid doc-mermaid-live\">\n ${title}\n <pre class=\"mermaid\">${deps.escapeHtml(entry.code)}</pre>\n</div>`;\n }\n return `<div class=\"doc-mermaid\">\n ${title}\n <pre class=\"doc-mermaid-code\"><code>${deps.escapeHtml(entry.code)}</code></pre>\n</div>`;\n}\n\nexport function renderDocScreenshot(\n entry: Extract<DocEntry, { kind: \"screenshot\" }>,\n deps: DocEntryDeps,\n): string {\n const alt = entry.alt ?? \"Screenshot\";\n const src = entry.path;\n return `<div class=\"doc-screenshot\">\n <img src=\"${deps.escapeHtml(src)}\" alt=\"${deps.escapeHtml(alt)}\" class=\"doc-screenshot-img\" />\n ${entry.alt ? `<div class=\"doc-screenshot-caption\">${deps.escapeHtml(entry.alt)}</div>` : \"\"}\n</div>`;\n}\n\nexport function renderDocCustom(\n entry: Extract<DocEntry, { kind: \"custom\" }>,\n deps: DocEntryDeps,\n): string {\n const dataStr = JSON.stringify(entry.data, null, 2);\n return `<div class=\"doc-custom\">\n <div class=\"doc-custom-type\">${deps.escapeHtml(entry.type)}</div>\n <pre class=\"doc-custom-data\"><code>${deps.escapeHtml(dataStr)}</code></pre>\n</div>`;\n}\n\nexport function renderDocEntry(entry: DocEntry, deps: DocEntryDeps): string {\n let html: string;\n switch (entry.kind) {\n case \"note\":\n html = renderDocNote(entry, deps);\n break;\n case \"tag\":\n html = renderDocTag(entry, deps);\n break;\n case \"kv\":\n html = renderDocKv(entry, deps);\n break;\n case \"code\":\n html = renderDocCode(entry, deps);\n break;\n case \"table\":\n html = renderDocTable(entry, deps);\n break;\n case \"link\":\n html = renderDocLink(entry, deps);\n break;\n case \"section\":\n html = renderDocSection(entry, deps);\n break;\n case \"mermaid\":\n html = renderDocMermaid(entry, deps);\n break;\n case \"screenshot\":\n html = renderDocScreenshot(entry, deps);\n break;\n case \"custom\":\n html = renderDocCustom(entry, deps);\n break;\n default:\n html = \"\";\n }\n\n if (entry.children && entry.children.length > 0) {\n const childrenHtml = entry.children\n .map((child) => renderDocEntry(child, deps))\n .join(\"\");\n html += `<div class=\"doc-children\">${childrenHtml}</div>`;\n }\n\n return html;\n}\n","/**\n * Render steps list (fn(args, deps)).\n */\n\nimport type { DocEntry, StoryStep } from \"../../../types/story\";\nimport type { StepResult } from \"../../../types/test-result\";\n\nconst CONTINUATION_KEYWORDS = [\"And\", \"But\", \"*\"];\n\nexport interface RenderStepsArgs {\n steps: StoryStep[];\n stepResults: StepResult[];\n}\n\nexport interface RenderStepsDeps {\n escapeHtml: (str: string) => string;\n getStatusIcon: (status: import(\"../../../types/test-result.js\").TestStatus) => string;\n renderDocs: (docs: DocEntry[] | undefined, containerClass: string) => string;\n highlightStepParams?: (text: string) => string;\n}\n\nexport function renderStep(\n step: StoryStep,\n stepResult: StepResult | undefined,\n index: number,\n deps: RenderStepsDeps,\n): string {\n const statusIcon = stepResult ? deps.getStatusIcon(stepResult.status) : \"○\";\n const statusClass = stepResult ? `status-${stepResult.status}` : \"\";\n const duration =\n stepResult && stepResult.durationMs > 0\n ? `${stepResult.durationMs}ms`\n : \"\";\n\n const keywordTrimmed = step.keyword.trim();\n const isContinuation = CONTINUATION_KEYWORDS.includes(keywordTrimmed);\n const stepClass = isContinuation ? \"step continuation\" : \"step\";\n\n const stepDocs = deps.renderDocs(step.docs, \"step-docs\");\n\n const textHtml = deps.highlightStepParams\n ? deps.highlightStepParams(step.text)\n : deps.escapeHtml(step.text);\n\n return `<div class=\"${stepClass}\" data-keyword=\"${deps.escapeHtml(keywordTrimmed)}\" data-text=\"${deps.escapeHtml(step.text)}\">\n <span class=\"step-status ${statusClass}\">${statusIcon}</span>\n <span class=\"step-keyword\">${deps.escapeHtml(step.keyword)}</span>\n <span class=\"step-text\">${textHtml}</span>\n <span class=\"step-duration\">${duration}</span>\n</div>${stepDocs}`;\n}\n\nexport function renderSteps(\n args: RenderStepsArgs,\n deps: RenderStepsDeps,\n): string {\n const stepsHtml = args.steps\n .map((step, index) => {\n const stepResult = args.stepResults.find((sr) => sr.index === index);\n return renderStep(step, stepResult, index, deps);\n })\n .join(\"\");\n return `<div class=\"steps\">${stepsHtml}</div>`;\n}\n","/**\n * Highlight step parameters (quoted strings, standalone numbers) in step text.\n * Pure function following fn(args, deps) pattern.\n */\n\nexport interface HighlightStepParamsDeps {\n escapeHtml: (str: string) => string;\n}\n\n/**\n * Regex matches:\n * - `\"[^\"]*\"` — double-quoted strings (matched first, so numbers inside quotes are part of the string)\n * - `(?<![\\w.])\\d+(?:\\.\\d+)?(?![\\w.])` — standalone numbers with dot-aware boundaries\n */\nconst STEP_PARAM_PATTERN = /\"[^\"]*\"|(?<![\\w.\\-])\\d+(?:\\.\\d+)?(?![\\w.\\-])/g;\n\nexport function highlightStepParams(\n text: string,\n deps: HighlightStepParamsDeps,\n): string {\n const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));\n\n if (matches.length === 0) {\n return deps.escapeHtml(text);\n }\n\n let result = \"\";\n let lastIndex = 0;\n\n for (const match of matches) {\n const matchStart = match.index;\n const matchEnd = matchStart + match[0].length;\n\n // Append escaped plain text before this match\n if (matchStart > lastIndex) {\n result += deps.escapeHtml(text.slice(lastIndex, matchStart));\n }\n\n // Wrap the matched param in a span (also escape its content)\n result += `<span class=\"step-param\">${deps.escapeHtml(match[0])}</span>`;\n\n lastIndex = matchEnd;\n }\n\n // Append any remaining plain text after the last match\n if (lastIndex < text.length) {\n result += deps.escapeHtml(text.slice(lastIndex));\n }\n\n return result;\n}\n","/**\n * Centralized sample-size policy for history metrics.\n *\n * Easy to tune without hunting across files.\n */\n\n/** Minimum duration-bearing entries for performance trend analysis. */\nexport const MIN_PERF_SAMPLES = 6;\n\n/** Minimum entries before showing badges/metrics in reports. */\nexport const MIN_METRIC_SAMPLES = 5;\n\n/** Minimum entries for flakiness calculation (below this → \"stable\"). */\nexport const MIN_FLAKINESS_SAMPLES = 3;\n\n/** Check whether an array meets the minimum sample threshold. */\nexport function hasSufficientHistory(\n entries: unknown[],\n min: number,\n): boolean {\n return entries.length >= min;\n}\n","/**\n * Render a scenario element (fn(args, deps)).\n */\n\nimport type { DocEntry, NormalizedTicket } from \"../../../types/story\";\nimport type { TestCaseResult } from \"../../../types/test-result\";\nimport type { TestMetrics } from \"../../../history/types\";\nimport { MIN_METRIC_SAMPLES } from \"../../../history/sample-policy\";\n\nexport interface RenderScenarioArgs {\n tc: TestCaseResult;\n metrics?: TestMetrics;\n}\n\nexport interface RenderScenarioDeps {\n escapeHtml: (str: string) => string;\n getStatusIcon: (status: import(\"../../../types/test-result.js\").TestStatus) => string;\n startCollapsed: boolean;\n renderSteps: (\n args: import(\"./steps.js\").RenderStepsArgs,\n deps: import(\"./steps.js\").RenderStepsDeps,\n ) => string;\n renderDocs: (docs: DocEntry[] | undefined, containerClass: string) => string;\n renderErrorBox: (\n args: import(\"./error-box.js\").RenderErrorBoxArgs,\n deps: import(\"./error-box.js\").RenderErrorBoxDeps,\n ) => string;\n renderAttachments: (\n args: import(\"./attachments.js\").RenderAttachmentsArgs,\n deps: import(\"./attachments.js\").RenderAttachmentsDeps,\n ) => string;\n renderTraceView: (\n args: import(\"./trace-view.js\").RenderTraceViewArgs,\n deps: import(\"./trace-view.js\").RenderTraceViewDeps,\n ) => string;\n embedScreenshots: boolean;\n permalinkBaseUrl?: string;\n ticketUrlTemplate?: string;\n}\n\nfunction renderTicket(\n ticket: NormalizedTicket,\n template: string | undefined,\n escapeHtml: (s: string) => string,\n): string {\n const url = ticket.url ?? (template ? template.replace(\"{ticket}\", ticket.id) : undefined);\n if (url) {\n return `<a class=\"tag ticket-tag\" href=\"${escapeHtml(url)}\" target=\"_blank\" rel=\"noopener noreferrer\">${escapeHtml(ticket.id)}</a>`;\n }\n return `<span class=\"tag ticket-tag\">${escapeHtml(ticket.id)}</span>`;\n}\n\nexport function renderScenario(\n args: RenderScenarioArgs,\n deps: RenderScenarioDeps,\n): string {\n const { tc } = args;\n const statusIcon = deps.getStatusIcon(tc.status);\n const statusClass = `status-${tc.status}`;\n const duration =\n tc.durationMs > 0 ? `${(tc.durationMs / 1000).toFixed(2)}s` : \"\";\n\n const tags = tc.tags\n .map((t) => `<span class=\"tag\">${deps.escapeHtml(t)}</span>`)\n .join(\"\");\n\n const tickets = (tc.story.tickets ?? [])\n .map((t) => renderTicket(t, deps.ticketUrlTemplate, deps.escapeHtml))\n .join(\"\");\n\n // Trace badge from OTel bridge\n const otelMeta = (tc.story.meta as Record<string, unknown> | undefined)\n ?.otel as { traceId?: string } | undefined;\n let traceBadge = \"\";\n if (otelMeta?.traceId) {\n const shortId = otelMeta.traceId.slice(0, 16);\n // Look for a \"View Trace\" link in story-level docs for the URL\n const traceLink = tc.story.docs?.find(\n (d): d is Extract<typeof d, { kind: \"link\" }> =>\n d.kind === \"link\" && d.label === \"View Trace\",\n );\n if (traceLink) {\n traceBadge = `<a class=\"tag trace-tag\" href=\"${deps.escapeHtml(traceLink.url)}\" title=\"${deps.escapeHtml(otelMeta.traceId)}\" target=\"_blank\" rel=\"noopener\">${deps.escapeHtml(shortId)}…</a>`;\n } else {\n traceBadge = `<span class=\"tag trace-tag\" title=\"${deps.escapeHtml(otelMeta.traceId)}\">${deps.escapeHtml(shortId)}…</span>`;\n }\n }\n\n // History metric badges\n let metricBadges = \"\";\n const { metrics } = args;\n if (metrics && metrics.sampleSize >= MIN_METRIC_SAMPLES) {\n const grade = metrics.stabilityGrade;\n metricBadges += `<span class=\"badge badge-grade badge-grade-${grade}\" title=\"Pass rate: ${(metrics.passRate * 100).toFixed(0)}% (${metrics.sampleSize} runs)\">${grade}</span>`;\n\n if (metrics.flakinessLevel !== \"stable\") {\n metricBadges += `<span class=\"badge badge-flaky\">${metrics.flakinessLevel}</span>`;\n }\n\n if (metrics.performanceTrend !== \"stable\") {\n const arrow = metrics.performanceTrend === \"improving\" ? \"\\u2191\" : \"\\u2193\";\n metricBadges += `<span class=\"badge badge-perf badge-perf-${metrics.performanceTrend}\">${arrow} ${metrics.performanceTrend}</span>`;\n }\n }\n\n const storyDocs = deps.renderDocs(tc.story.docs, \"story-docs\");\n const steps = deps.renderSteps(\n { steps: tc.story.steps, stepResults: tc.stepResults },\n {\n escapeHtml: deps.escapeHtml,\n getStatusIcon: deps.getStatusIcon,\n renderDocs: deps.renderDocs,\n },\n );\n const error =\n tc.status === \"failed\" && tc.errorMessage\n ? deps.renderErrorBox(\n { message: tc.errorMessage, stack: tc.errorStack },\n { escapeHtml: deps.escapeHtml },\n )\n : \"\";\n const attachments = deps.renderAttachments(\n { attachments: tc.attachments },\n {\n escapeHtml: deps.escapeHtml,\n embedScreenshots: deps.embedScreenshots,\n },\n );\n\n const traceView = deps.renderTraceView(\n { spans: tc.story.otelSpans },\n { escapeHtml: deps.escapeHtml },\n );\n\n // Source permalink\n let sourceLink = \"\";\n if (deps.permalinkBaseUrl && tc.sourceFile && tc.sourceFile !== \"unknown\") {\n const fragment = tc.sourceLine > 0 ? `#L${tc.sourceLine}` : \"\";\n const href = `${deps.permalinkBaseUrl}/${tc.sourceFile}${fragment}`;\n const label = `${tc.sourceFile}${tc.sourceLine > 0 ? `:${tc.sourceLine}` : \"\"}`;\n sourceLink = `<a class=\"source-link\" href=\"${deps.escapeHtml(href)}\" target=\"_blank\" rel=\"noopener\">${deps.escapeHtml(label)}</a>`;\n }\n\n const collapsedClass = deps.startCollapsed ? \" collapsed\" : \"\";\n const ariaExpanded = !deps.startCollapsed;\n\n return `\n<div class=\"scenario${collapsedClass}\" id=\"scenario-${tc.id}\">\n <div class=\"scenario-header\" role=\"button\" tabindex=\"0\" aria-expanded=\"${ariaExpanded}\">\n <div class=\"scenario-info\">\n <div class=\"scenario-title\">\n <span class=\"status-icon ${statusClass}\">${statusIcon}</span>\n <span class=\"scenario-name\">${deps.escapeHtml(tc.story.scenario)}</span>\n </div>\n <div class=\"scenario-meta\">${tags}${tickets}${sourceLink}${traceBadge}${metricBadges}</div>\n </div>\n <div class=\"scenario-actions\">\n <button class=\"copy-scenario-btn\" onclick=\"copyScenarioAsMarkdown('scenario-${tc.id}')\" aria-label=\"Copy scenario as markdown\" title=\"Copy as Markdown\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg></button>\n <button class=\"permalink-anchor\" onclick=\"copyPermalink('scenario-${tc.id}')\" aria-label=\"Copy link to scenario\" title=\"Copy link\">#</button>\n <span class=\"scenario-duration\">${duration}</span>\n </div>\n </div>\n <div class=\"scenario-content\">\n ${storyDocs}\n ${steps}\n ${error}\n ${attachments}\n ${traceView}\n </div>\n</div>`;\n}\n","/**\n * Render an OTel trace waterfall (fn(args, deps)).\n */\n\nimport type { OtelSpan } from \"../../../types/otel\";\n\nexport interface RenderTraceViewArgs {\n spans: OtelSpan[] | undefined;\n}\n\nexport interface RenderTraceViewDeps {\n escapeHtml: (str: string) => string;\n}\n\ninterface NormalizedSpan {\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n durationMs: number;\n status: \"ok\" | \"error\" | \"unset\";\n statusMessage?: string;\n attributes?: Record<string, unknown>;\n}\n\ninterface TreeNode {\n span: NormalizedSpan;\n children: TreeNode[];\n depth: number;\n}\n\nconst VALID_STATUSES = new Set<string>([\"ok\", \"error\", \"unset\"]);\nconst TOOLTIP_MAX_LENGTH = 4096;\n\nfunction safeStatus(status: string): \"ok\" | \"error\" | \"unset\" {\n return VALID_STATUSES.has(status) ? (status as \"ok\" | \"error\" | \"unset\") : \"unset\";\n}\n\nfunction formatDuration(ms: number): string {\n if (ms >= 1000) return `${(ms / 1000).toFixed(2)}s`;\n return `${ms.toFixed(1)}ms`;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction normalizeSpans(spans: OtelSpan[]): NormalizedSpan[] {\n const result: NormalizedSpan[] = [];\n for (const span of spans) {\n if (!span || typeof span !== \"object\") continue;\n if (typeof span.spanId !== \"string\" || typeof span.name !== \"string\") continue;\n\n let startTimeMs: number;\n let durationMs: number;\n\n if (span.startTimeMs != null && span.durationMs != null) {\n startTimeMs = span.startTimeMs;\n durationMs = span.durationMs;\n } else if (span.startTimeUnixNano != null && span.endTimeUnixNano != null) {\n startTimeMs = span.startTimeUnixNano / 1e6;\n durationMs = (span.endTimeUnixNano - span.startTimeUnixNano) / 1e6;\n } else {\n continue;\n }\n\n durationMs = Math.max(0, durationMs);\n if (!isFinite(startTimeMs) || !isFinite(durationMs)) continue;\n\n result.push({\n spanId: span.spanId,\n parentSpanId: span.parentSpanId,\n name: span.name,\n startTimeMs,\n durationMs,\n status: safeStatus(span.status),\n statusMessage: span.statusMessage,\n attributes: span.attributes,\n });\n }\n return result;\n}\n\nfunction buildTree(spans: NormalizedSpan[]): TreeNode[] {\n const byId = new Map<string, TreeNode>();\n for (const span of spans) {\n let key = span.spanId;\n if (byId.has(key)) {\n let suffix = 2;\n while (byId.has(`${span.spanId}__dup${suffix}`)) suffix++;\n key = `${span.spanId}__dup${suffix}`;\n }\n byId.set(key, { span: { ...span, spanId: key }, children: [], depth: 0 });\n }\n\n const roots: TreeNode[] = [];\n for (const node of byId.values()) {\n const parentId = node.span.parentSpanId;\n const parent = parentId ? byId.get(parentId) : undefined;\n if (parent && parent !== node) {\n parent.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n // Sort children by startTimeMs within each parent\n for (const node of byId.values()) {\n node.children.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);\n }\n roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);\n\n // Assign depths via DFS with cycle guard\n const visited = new Set<string>();\n function assignDepth(node: TreeNode, depth: number): void {\n if (visited.has(node.span.spanId)) return;\n visited.add(node.span.spanId);\n node.depth = depth;\n for (const child of node.children) {\n assignDepth(child, depth + 1);\n }\n }\n for (const root of roots) {\n assignDepth(root, 0);\n }\n\n // Promote any unvisited nodes to roots (handles cycles like A→B, B→A)\n for (const node of byId.values()) {\n if (!visited.has(node.span.spanId)) {\n node.children = [];\n roots.push(node);\n assignDepth(node, 0);\n }\n }\n roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);\n\n return roots;\n}\n\nfunction flattenTree(roots: TreeNode[]): TreeNode[] {\n const result: TreeNode[] = [];\n function walk(node: TreeNode): void {\n result.push(node);\n for (const child of node.children) {\n walk(child);\n }\n }\n for (const root of roots) {\n walk(root);\n }\n return result;\n}\n\nfunction buildTooltip(\n span: NormalizedSpan,\n escapeHtml: (s: string) => string,\n): string {\n const parts: string[] = [];\n parts.push(`${span.name} (${formatDuration(span.durationMs)})`);\n\n if (span.statusMessage) {\n parts.push(`Status: ${span.statusMessage}`);\n }\n\n if (span.attributes) {\n const keys = Object.keys(span.attributes).sort();\n for (const key of keys) {\n const val = span.attributes[key];\n const formatted = Array.isArray(val)\n ? `[${val.map((v) => String(v)).join(\", \")}]`\n : String(val);\n parts.push(`${key}=${formatted}`);\n }\n }\n\n let text = parts.join(\"\\n\");\n if (text.length > TOOLTIP_MAX_LENGTH) {\n text = text.slice(0, TOOLTIP_MAX_LENGTH - 3) + \"...\";\n }\n\n return escapeHtml(text);\n}\n\nexport function renderTraceView(\n args: RenderTraceViewArgs,\n deps: RenderTraceViewDeps,\n): string {\n if (!args.spans || args.spans.length === 0) return \"\";\n\n const normalized = normalizeSpans(args.spans);\n if (normalized.length === 0) return \"\";\n\n const roots = buildTree(normalized);\n const flat = flattenTree(roots);\n\n // Compute relative scale\n let minStart = Infinity;\n let maxEnd = -Infinity;\n for (const node of flat) {\n const s = node.span.startTimeMs;\n const e = s + node.span.durationMs;\n if (s < minStart) minStart = s;\n if (e > maxEnd) maxEnd = e;\n }\n let totalDuration = maxEnd - minStart;\n if (totalDuration <= 0) totalDuration = 1;\n\n // Render rows\n const rows = flat\n .map((node) => {\n const { span, depth } = node;\n const indent = depth * 16;\n const minWidth = 0.5;\n let spanLeft = clamp(\n ((span.startTimeMs - minStart) / totalDuration) * 100,\n 0,\n 100,\n );\n // Nudge left so the min-width bar stays within bounds\n if (spanLeft + minWidth > 100) {\n spanLeft = 100 - minWidth;\n }\n const spanWidth = clamp(\n (span.durationMs / totalDuration) * 100,\n minWidth,\n 100 - spanLeft,\n );\n const tooltip = buildTooltip(span, deps.escapeHtml);\n const durationLabel = formatDuration(span.durationMs);\n\n return ` <div class=\"trace-view-row\">\n <div class=\"trace-view-name\" style=\"padding-left: ${indent}px\" title=\"${deps.escapeHtml(span.name)}\">\n <span class=\"trace-view-status-dot trace-view-status-${span.status}\"></span>\n ${deps.escapeHtml(span.name)}\n </div>\n <div class=\"trace-view-bar-container\">\n <div class=\"trace-view-bar trace-view-bar-${span.status}\" style=\"left: ${spanLeft.toFixed(2)}%; width: ${spanWidth.toFixed(2)}%\" title=\"${tooltip}\">${durationLabel}</div>\n </div>\n </div>`;\n })\n .join(\"\\n\");\n\n const axisEnd = formatDuration(maxEnd - minStart);\n\n return `<div class=\"trace-view collapsed\">\n <div class=\"trace-view-header\" role=\"button\" tabindex=\"0\" aria-expanded=\"false\">\n <span>Spans</span>\n <span class=\"trace-view-count\">${flat.length}</span>\n <span class=\"chevron\">&#9660;</span>\n </div>\n <div class=\"trace-view-content\">\n <div class=\"trace-view-axis\">\n <span>0ms</span>\n <span>${axisEnd}</span>\n </div>\n${rows}\n </div>\n</div>`;\n}\n","/**\n * Render a feature section (group of scenarios from same file) (fn(args, deps)).\n */\n\nimport type { TestCaseResult } from \"../../../types/test-result\";\nimport type { TestMetrics } from \"../../../history/types\";\nimport { slugify } from \"../../../converters/acl/ids.js\";\n\nexport interface RenderFeatureArgs {\n file: string;\n testCases: TestCaseResult[];\n metricsMap?: Map<string, TestMetrics>;\n}\n\nexport interface RenderFeatureDeps {\n escapeHtml: (str: string) => string;\n startCollapsed: boolean;\n renderScenario: (\n args: import(\"./scenario.js\").RenderScenarioArgs,\n deps: import(\"./scenario.js\").RenderScenarioDeps,\n ) => string;\n scenarioDeps: import(\"./scenario.js\").RenderScenarioDeps;\n}\n\nexport function renderFeature(\n args: RenderFeatureArgs,\n deps: RenderFeatureDeps,\n): string {\n const { file, testCases } = args;\n const passed = testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const collapsedClass = deps.startCollapsed ? \" collapsed\" : \"\";\n const ariaExpanded = !deps.startCollapsed;\n const featureSlug = `feature-${slugify(file)}`;\n\n const scenarios = testCases\n .map((tc) =>\n deps.renderScenario(\n { tc, metrics: args.metricsMap?.get(tc.id) },\n deps.scenarioDeps,\n ),\n )\n .join(\"\\n\");\n\n return `\n<div class=\"feature${collapsedClass}\" id=\"${featureSlug}\">\n <div class=\"feature-header\" role=\"button\" tabindex=\"0\" aria-expanded=\"${ariaExpanded}\">\n <button class=\"permalink-anchor\" onclick=\"copyPermalink('${featureSlug}')\" aria-label=\"Copy link to feature\" title=\"Copy link\">#</button>\n <div class=\"feature-info\">\n <div class=\"feature-title\">${deps.escapeHtml(featureName)}</div>\n <div class=\"feature-path\">${deps.escapeHtml(file)}</div>\n </div>\n <div class=\"feature-stats\">\n <span class=\"stat passed\">✓ ${passed}</span>\n <span class=\"stat failed\">✗ ${failed}</span>\n <span class=\"stat skipped\">○ ${skipped}</span>\n <span class=\"chevron\">▼</span>\n </div>\n </div>\n <div class=\"feature-content\">\n ${scenarios}\n </div>\n</div>`;\n}\n","/**\n * Build report body from run (fn(args, deps)).\n * Composes meta, summary, tag bar, and features; uses groupBy for feature grouping.\n */\n\nimport type { TestRunResult } from \"../../../types/test-result\";\nimport type { TestMetrics } from \"../../../history/types\";\nimport type { RenderTagBarArgs, RenderTagBarDeps } from \"./tag-bar.js\";\nimport type { RenderFailureSummaryArgs, RenderFailureSummaryDeps } from \"./failure-summary.js\";\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nexport interface BuildBodyArgs {\n run: TestRunResult;\n metricsMap?: Map<string, TestMetrics>;\n}\n\nexport interface BuildBodyDeps {\n renderMetaInfo: (\n args: import(\"./meta.js\").RenderMetaInfoArgs,\n deps: import(\"./meta.js\").RenderMetaInfoDeps,\n ) => string;\n renderSummary: (\n args: import(\"./summary.js\").RenderSummaryArgs,\n deps: import(\"./summary.js\").RenderSummaryDeps,\n ) => string;\n renderTagBar: (args: RenderTagBarArgs, deps: RenderTagBarDeps) => string;\n renderFeature: (\n args: import(\"./feature.js\").RenderFeatureArgs,\n deps: import(\"./feature.js\").RenderFeatureDeps,\n ) => string;\n renderFailureSummary: (\n args: RenderFailureSummaryArgs,\n deps: RenderFailureSummaryDeps,\n ) => string;\n metaDeps: import(\"./meta.js\").RenderMetaInfoDeps;\n summaryDeps: import(\"./summary.js\").RenderSummaryDeps;\n tagBarDeps: RenderTagBarDeps;\n featureDeps: import(\"./feature.js\").RenderFeatureDeps;\n failureSummaryDeps: RenderFailureSummaryDeps;\n}\n\nexport function buildBody(args: BuildBodyArgs, deps: BuildBodyDeps): string {\n const { run } = args;\n\n const parts: string[] = [];\n\n parts.push(\n deps.renderMetaInfo(\n {\n startedAtMs: run.startedAtMs,\n durationMs: run.durationMs,\n packageVersion: run.packageVersion,\n gitSha: run.gitSha,\n ciName: run.ci?.name,\n ciBranch: run.ci?.branch,\n ciUrl: run.ci?.url,\n ciCommitSha: run.ci?.commitSha,\n ciBuildNumber: run.ci?.buildNumber,\n },\n deps.metaDeps,\n ),\n );\n\n const total = run.testCases.length;\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n parts.push(\n deps.renderSummary(\n { total, passed, failed, skipped },\n deps.summaryDeps,\n ),\n );\n\n const allTags = [\n ...new Set(run.testCases.flatMap((tc) => tc.tags)),\n ].sort();\n parts.push(\n deps.renderTagBar(\n { tags: allTags, totalScenarios: total },\n deps.tagBarDeps,\n ),\n );\n\n const failedCases = run.testCases.filter((tc) => tc.status === \"failed\");\n if (failedCases.length > 0) {\n parts.push(\n deps.renderFailureSummary(\n { failedCases },\n deps.failureSummaryDeps,\n ),\n );\n }\n\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n for (const [file, testCases] of byFile) {\n parts.push(\n deps.renderFeature(\n { file, testCases, metricsMap: args.metricsMap },\n deps.featureDeps,\n ),\n );\n }\n\n return parts.join(\"\\n\");\n}\n","/**\n * Render failure summary block with deep links to failed scenarios (fn(args, deps)).\n */\n\nimport type { TestCaseResult } from \"../../../types/test-result\";\n\nexport interface RenderFailureSummaryArgs {\n failedCases: TestCaseResult[];\n}\n\nexport interface RenderFailureSummaryDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderFailureSummary(\n args: RenderFailureSummaryArgs,\n deps: RenderFailureSummaryDeps,\n): string {\n const { failedCases } = args;\n if (failedCases.length === 0) return \"\";\n\n const items = failedCases\n .map((tc) => {\n const name = deps.escapeHtml(tc.story.scenario);\n return `<li><a href=\"#scenario-${tc.id}\">${name}</a></li>`;\n })\n .join(\"\\n \");\n\n return `\n<div class=\"failure-summary\">\n <div class=\"failure-summary-header\">Failed (${failedCases.length})</div>\n <div class=\"failure-summary-note\">\n For review-grade output, generate a compare report with <code>compare --pr-summary</code>.\n </div>\n <ul>\n ${items}\n </ul>\n</div>`;\n}\n","/**\n * Render table of contents sidebar (fn(args, deps)).\n */\n\nimport type { TestRunResult, TestStatus } from \"../../../types/test-result.js\";\nimport { slugify } from \"../../../converters/acl/ids.js\";\n\nexport interface RenderTocArgs {\n run: TestRunResult;\n}\n\nexport interface RenderTocDeps {\n escapeHtml: (str: string) => string;\n getStatusIcon: (status: TestStatus) => string;\n}\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nexport function renderToc(args: RenderTocArgs, deps: RenderTocDeps): string {\n const { run } = args;\n if (run.testCases.length === 0) return \"\";\n\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n const features: string[] = [];\n\n for (const [file, testCases] of byFile) {\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const featureSlug = `feature-${slugify(file)}`;\n\n const scenarios = testCases\n .map((tc) => {\n const statusIcon = deps.getStatusIcon(tc.status);\n const statusClass = `status-${tc.status}`;\n const failedClass = tc.status === \"failed\" ? \" toc-failed\" : \"\";\n return `<a class=\"toc-scenario${failedClass}\" href=\"#scenario-${tc.id}\">\n <span class=\"toc-status ${statusClass}\">${statusIcon}</span>\n ${deps.escapeHtml(tc.story.scenario)}\n </a>`;\n })\n .join(\"\\n\");\n\n features.push(`<div class=\"toc-feature\">\n <button class=\"toc-feature-toggle\" aria-expanded=\"true\" onclick=\"this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') === 'true' ? 'false' : 'true'); this.nextElementSibling.style.display = this.getAttribute('aria-expanded') === 'true' ? '' : 'none'\" data-feature=\"#${featureSlug}\">\n ${deps.escapeHtml(featureName)}\n </button>\n <div class=\"toc-scenarios\">\n ${scenarios}\n </div>\n </div>`);\n }\n\n return `<nav class=\"toc-sidebar\" aria-label=\"Table of contents\">\n <div class=\"toc-header\">\n <a href=\"#\" class=\"toc-title\" onclick=\"window.scrollTo({top:0,behavior:'smooth'});return false;\">Contents</a>\n </div>\n <div class=\"toc-body\">\n ${features.join(\"\\n\")}\n </div>\n</nav>`;\n}\n","/**\n * HTML renderers and factory (fn(args, deps) pattern).\n * Exports all render functions and createHtmlFormatter.\n */\n\nimport type { DocEntry } from \"../../../types/story\";\nimport type { TestRunResult } from \"../../../types/test-result\";\nimport { escapeHtml, generateHtmlTemplate } from \"../template\";\nimport { CSS_STYLES } from \"../styles\";\nimport type { HtmlTheme } from \"../themes/types.js\";\nimport { resolveTheme, getCssOnlyThemes } from \"../themes/index.js\";\nimport { getStatusIcon } from \"./status\";\nimport { renderMetaInfo } from \"./meta\";\nimport { renderSummary } from \"./summary\";\nimport { renderTagBar } from \"./tag-bar\";\nimport { renderErrorBox } from \"./error-box\";\nimport { renderAttachments } from \"./attachments\";\nimport { renderDocEntry } from \"./doc-entries\";\nimport { renderSteps } from \"./steps\";\nimport { highlightStepParams } from \"./step-params\";\nimport { renderScenario } from \"./scenario\";\nimport { renderTraceView } from \"./trace-view\";\nimport { renderFeature } from \"./feature\";\nimport { buildBody } from \"./body\";\nimport { renderFailureSummary } from \"./failure-summary\";\nimport { renderToc } from \"./toc\";\n\n/** Options for HTML formatting (subset used by createHtmlFormatter) */\nexport interface HtmlFormatterOptions {\n title?: string;\n darkMode?: boolean;\n searchable?: boolean;\n startCollapsed?: boolean;\n embedScreenshots?: boolean;\n syntaxHighlighting?: boolean;\n mermaidEnabled?: boolean;\n markdownEnabled?: boolean;\n permalinkBaseUrl?: string;\n /** URL template for ticket links. Use {ticket} as placeholder. E.g., \"https://jira.example.com/browse/{ticket}\" */\n ticketUrlTemplate?: string;\n /** Show table of contents sidebar. Default: true */\n tocEnabled?: boolean;\n /** Theme name or custom theme object. Default: \"default\" */\n theme?: string | HtmlTheme;\n /** Include theme picker with all CSS-only themes embedded. Default: false */\n themePickerEnabled?: boolean;\n}\n\nfunction normalizeOptions(options: HtmlFormatterOptions = {}) {\n return {\n title: options.title ?? \"Test Results\",\n darkMode: options.darkMode ?? true,\n searchable: options.searchable ?? true,\n startCollapsed: options.startCollapsed ?? false,\n embedScreenshots: options.embedScreenshots ?? true,\n syntaxHighlighting: options.syntaxHighlighting ?? true,\n mermaidEnabled: options.mermaidEnabled ?? true,\n markdownEnabled: options.markdownEnabled ?? true,\n permalinkBaseUrl: options.permalinkBaseUrl,\n ticketUrlTemplate: options.ticketUrlTemplate,\n tocEnabled: options.tocEnabled ?? true,\n theme: options.theme ?? \"default\",\n themePickerEnabled: options.themePickerEnabled ?? false,\n };\n}\n\n/**\n * Factory: wire deps once, return { format(run) }.\n */\nexport function createHtmlFormatter(\n options: HtmlFormatterOptions = {},\n): { format(run: TestRunResult): string } {\n const opts = normalizeOptions(options);\n\n const docEntryDeps = {\n escapeHtml,\n syntaxHighlighting: opts.syntaxHighlighting,\n markdownEnabled: opts.markdownEnabled,\n mermaidEnabled: opts.mermaidEnabled,\n };\n\n const renderDocs = (\n docs: DocEntry[] | undefined,\n containerClass: string,\n ): string => {\n if (!docs || docs.length === 0) return \"\";\n const entries = docs.map((entry) => renderDocEntry(entry, docEntryDeps)).join(\"\");\n return `<div class=\"${containerClass}\">${entries}</div>`;\n };\n\n const stepsDeps = {\n escapeHtml,\n getStatusIcon,\n renderDocs,\n highlightStepParams: (text: string) =>\n highlightStepParams(text, { escapeHtml }),\n };\n\n const scenarioDeps = {\n escapeHtml,\n getStatusIcon,\n startCollapsed: opts.startCollapsed,\n renderSteps: (args: import(\"./steps.js\").RenderStepsArgs) =>\n renderSteps(args, stepsDeps),\n renderDocs,\n renderErrorBox: (\n args: import(\"./error-box.js\").RenderErrorBoxArgs,\n d: import(\"./error-box.js\").RenderErrorBoxDeps,\n ) => renderErrorBox(args, d),\n renderAttachments: (\n args: import(\"./attachments.js\").RenderAttachmentsArgs,\n d: import(\"./attachments.js\").RenderAttachmentsDeps,\n ) => renderAttachments(args, d),\n renderTraceView: (\n args: import(\"./trace-view.js\").RenderTraceViewArgs,\n d: import(\"./trace-view.js\").RenderTraceViewDeps,\n ) => renderTraceView(args, d),\n embedScreenshots: opts.embedScreenshots,\n permalinkBaseUrl: opts.permalinkBaseUrl,\n ticketUrlTemplate: opts.ticketUrlTemplate,\n };\n\n const featureDeps = {\n escapeHtml,\n startCollapsed: opts.startCollapsed,\n renderScenario: (args: import(\"./scenario.js\").RenderScenarioArgs) =>\n renderScenario(args, scenarioDeps),\n scenarioDeps,\n };\n\n const tagBarDeps = { escapeHtml };\n\n const tocDeps = {\n escapeHtml,\n getStatusIcon,\n };\n\n const bodyDeps = {\n renderMetaInfo,\n renderSummary,\n renderTagBar,\n renderFeature,\n renderFailureSummary,\n metaDeps: { escapeHtml },\n summaryDeps: {},\n tagBarDeps,\n featureDeps,\n failureSummaryDeps: { escapeHtml },\n };\n\n const theme = resolveTheme(opts.theme);\n\n return {\n format(run: TestRunResult): string {\n const bodyFn = theme.buildBody ?? buildBody;\n const body = bodyFn({ run }, bodyDeps);\n const templateFn = theme.generateTemplate ?? generateHtmlTemplate;\n\n // Only inject default TOC for themes that don't override body/template layout\n const isStructuralTheme = !!(theme.buildBody || theme.generateTemplate);\n const tocHtml = opts.tocEnabled && !isStructuralTheme ? renderToc({ run }, tocDeps) : undefined;\n\n let themePickerHtml: string | undefined;\n let additionalThemeCss: Array<{ name: string; label: string; css: string }> | undefined;\n\n if (opts.themePickerEnabled) {\n const cssOnlyThemes = getCssOnlyThemes();\n const pickerOptions = cssOnlyThemes\n .map(t => `<option value=\"${t.name}\"${t.name === theme.name ? ' selected' : ''}>${t.label}</option>`)\n .join('');\n themePickerHtml = `<select class=\"theme-picker\" aria-label=\"Select theme\">${pickerOptions}</select>`;\n additionalThemeCss = cssOnlyThemes\n .filter(t => t.name !== theme.name)\n .map(t => ({ name: t.name, label: t.label, css: t.css }));\n }\n\n return templateFn(\n opts.title,\n theme.css,\n body,\n {\n includeSearch: opts.searchable,\n includeDarkMode: opts.darkMode,\n syntaxHighlighting: opts.syntaxHighlighting,\n mermaidEnabled: opts.mermaidEnabled,\n markdownEnabled: opts.markdownEnabled,\n additionalJs: theme.additionalJs,\n additionalImports: theme.additionalImports,\n tocHtml,\n themePickerHtml,\n additionalThemeCss,\n activeThemeName: theme.name,\n },\n );\n },\n };\n}\n\nexport { renderMetaInfo } from \"./meta\";\nexport { renderSummary } from \"./summary\";\nexport { renderTagBar } from \"./tag-bar\";\nexport { renderErrorBox } from \"./error-box\";\nexport { renderAttachments } from \"./attachments\";\nexport {\n renderDocEntry,\n renderDocNote,\n renderDocTag,\n renderDocKv,\n renderDocCode,\n renderDocTable,\n renderDocLink,\n renderDocSection,\n renderDocMermaid,\n renderDocScreenshot,\n renderDocCustom,\n} from \"./doc-entries\";\nexport { highlightStepParams } from \"./step-params\";\nexport { renderSteps, renderStep } from \"./steps\";\nexport { renderScenario } from \"./scenario\";\nexport { renderTraceView } from \"./trace-view\";\nexport { renderFeature } from \"./feature\";\nexport { buildBody } from \"./body\";\nexport { renderFailureSummary } from \"./failure-summary\";\nexport { getStatusIcon } from \"./status\";\nexport type { DocEntryDeps } from \"./doc-entries\";\nexport type { RenderMetaInfoArgs, RenderMetaInfoDeps } from \"./meta\";\nexport type { RenderSummaryArgs, RenderSummaryDeps } from \"./summary\";\nexport type { RenderTagBarArgs, RenderTagBarDeps } from \"./tag-bar\";\nexport type { RenderErrorBoxArgs, RenderErrorBoxDeps } from \"./error-box\";\nexport type { RenderAttachmentsArgs, RenderAttachmentsDeps } from \"./attachments\";\nexport type { HighlightStepParamsDeps } from \"./step-params\";\nexport type { RenderStepsArgs, RenderStepsDeps } from \"./steps\";\nexport type { RenderScenarioArgs, RenderScenarioDeps } from \"./scenario\";\nexport type { RenderTraceViewArgs, RenderTraceViewDeps } from \"./trace-view\";\nexport type { RenderFeatureArgs, RenderFeatureDeps } from \"./feature\";\nexport type { BuildBodyArgs, BuildBodyDeps } from \"./body\";\nexport type { RenderFailureSummaryArgs, RenderFailureSummaryDeps } from \"./failure-summary\";\nexport { renderToc } from \"./toc\";\nexport type { RenderTocArgs, RenderTocDeps } from \"./toc\";\n","/**\n * HTML Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into a standalone HTML report.\n * Implemented via createHtmlFormatter (fn(args, deps) pattern).\n */\n\nimport type { TestRunResult } from \"../../types/test-result\";\nimport { createHtmlFormatter } from \"./renderers/index\";\n\nimport type { HtmlTheme } from \"./themes/types\";\n\n/** Options for HTML formatting */\nexport interface HtmlOptions {\n /** Report title. Default: \"Test Results\" */\n title?: string;\n /** Include dark mode toggle. Default: true */\n darkMode?: boolean;\n /** Include search/filter functionality. Default: true */\n searchable?: boolean;\n /** Start scenarios collapsed. Default: false */\n startCollapsed?: boolean;\n /** Embed screenshots inline (base64). Default: true */\n embedScreenshots?: boolean;\n /** Enable syntax highlighting for code blocks (via highlight.js CDN). Default: true */\n syntaxHighlighting?: boolean;\n /** Enable live Mermaid diagram rendering (via Mermaid.js CDN). Default: true */\n mermaidEnabled?: boolean;\n /** Enable Markdown parsing for section doc entries (via marked.js CDN). Default: true */\n markdownEnabled?: boolean;\n /** Base URL for source permalinks. E.g., \"https://github.com/user/repo/blob/main\" */\n permalinkBaseUrl?: string;\n /** URL template for ticket links. Use {ticket} as placeholder. E.g., \"https://jira.example.com/browse/{ticket}\" */\n ticketUrlTemplate?: string;\n /** Show table of contents sidebar. Default: true */\n tocEnabled?: boolean;\n /** Theme name or custom theme object. Default: \"default\" */\n theme?: string | HtmlTheme;\n /** Include theme picker with all CSS-only themes embedded. Default: false */\n themePickerEnabled?: boolean;\n}\n\n/**\n * HTML Formatter.\n *\n * Transforms TestRunResult into a standalone HTML report with:\n * - Dark/light mode toggle\n * - Search/filter functionality\n * - Collapsible features and scenarios\n * - Modern, accessible design\n *\n * Thin wrapper around createHtmlFormatter for backward compatibility.\n */\nexport class HtmlFormatter {\n private formatFn: (run: TestRunResult) => string;\n\n constructor(options: HtmlOptions = {}) {\n const wired = createHtmlFormatter(options);\n this.formatFn = wired.format.bind(wired);\n }\n\n /**\n * Format a test run into standalone HTML.\n *\n * @param run - Canonical test run result\n * @returns HTML string\n */\n format(run: TestRunResult): string {\n return this.formatFn(run);\n }\n}\n\nexport { createHtmlFormatter } from \"./renderers/index\";\nexport type { HtmlFormatterOptions } from \"./renderers/index\";\nexport { escapeHtml, generateHtmlTemplate } from \"./template\";\nexport { CSS_STYLES } from \"./styles\";\nexport {\n renderMetaInfo,\n renderSummary,\n renderErrorBox,\n renderAttachments,\n renderDocEntry,\n renderDocNote,\n renderDocTag,\n renderDocKv,\n renderDocCode,\n renderDocTable,\n renderDocLink,\n renderDocSection,\n renderDocMermaid,\n renderDocScreenshot,\n renderDocCustom,\n renderSteps,\n renderStep,\n renderScenario,\n renderFeature,\n renderFailureSummary,\n buildBody,\n getStatusIcon,\n} from \"./renderers/index\";\nexport { renderToc } from \"./renderers/toc\";\nexport type { HtmlTheme, HtmlThemeName } from \"./themes/index\";\nexport { resolveTheme, getAvailableThemes, getCssOnlyThemes } from \"./themes/index\";\n","/**\n * JUnit XML Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into JUnit XML format\n * for CI system integration.\n */\n\nimport type { DocEntry, StoryStep } from \"../types/story\";\nimport type { TestRunResult, TestCaseResult } from \"../types/test-result\";\n\n/** Options for JUnit XML formatting */\nexport interface JUnitOptions {\n /** Test suite name. Default: \"Test Suite\" */\n suiteName?: string;\n /** Include system-out/system-err. Default: true */\n includeOutput?: boolean;\n /** Pretty-print XML output. Default: true */\n pretty?: boolean;\n}\n\n/**\n * JUnit XML Formatter.\n *\n * Transforms TestRunResult into JUnit XML format for CI integrations.\n * Compatible with Jenkins, GitHub Actions, and other CI systems.\n */\nexport class JUnitFormatter {\n private options: Required<JUnitOptions>;\n\n constructor(options: JUnitOptions = {}) {\n this.options = {\n suiteName: options.suiteName ?? \"Test Suite\",\n includeOutput: options.includeOutput ?? true,\n pretty: options.pretty ?? true,\n };\n }\n\n /**\n * Format a test run into JUnit XML.\n *\n * @param run - Canonical test run result\n * @returns JUnit XML string\n */\n format(run: TestRunResult): string {\n const indent = this.options.pretty ? \" \" : \"\";\n const newline = this.options.pretty ? \"\\n\" : \"\";\n\n // Calculate totals\n const tests = run.testCases.length;\n const failures = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\"\n ).length;\n const errors = 0; // We don't distinguish errors from failures\n const time = (run.durationMs / 1000).toFixed(3);\n\n // Build XML\n const lines: string[] = [];\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n lines.push(\n `<testsuites name=\"${escapeXml(this.options.suiteName)}\" tests=\"${tests}\" failures=\"${failures}\" errors=\"${errors}\" skipped=\"${skipped}\" time=\"${time}\">`\n );\n\n // Group test cases by source file (one testsuite per file)\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n\n for (const [file, testCases] of byFile) {\n lines.push(...this.buildTestSuite(file, testCases, indent, newline));\n }\n\n lines.push(\"</testsuites>\");\n\n return lines.join(newline);\n }\n\n /**\n * Build a testsuite element for a file.\n */\n private buildTestSuite(\n file: string,\n testCases: TestCaseResult[],\n indent: string,\n newline: string\n ): string[] {\n const lines: string[] = [];\n\n const tests = testCases.length;\n const failures = testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\"\n ).length;\n const time = testCases\n .reduce((sum, tc) => sum + tc.durationMs, 0) / 1000;\n\n // Normalize path separators for consistent output across platforms\n const normalizedFile = file.replace(/\\\\/g, \"/\");\n\n lines.push(\n `${indent}<testsuite name=\"${escapeXml(normalizedFile)}\" tests=\"${tests}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\" time=\"${time.toFixed(3)}\">`\n );\n\n for (const tc of testCases) {\n lines.push(...this.buildTestCase(tc, indent + indent, newline));\n }\n\n lines.push(`${indent}</testsuite>`);\n\n return lines;\n }\n\n /**\n * Build a testcase element.\n */\n private buildTestCase(\n tc: TestCaseResult,\n indent: string,\n newline: string\n ): string[] {\n const lines: string[] = [];\n\n // Build classname from titlePath or sourceFile\n // Normalize path separators (Windows backslashes and Unix forward slashes)\n const classname = tc.titlePath.length > 0\n ? tc.titlePath.join(\".\")\n : tc.sourceFile\n .replace(/[\\\\/]+/g, \".\") // Replace path separators with dots\n .replace(/\\.[^.]+$/, \"\"); // Remove file extension\n\n const name = tc.story.scenario;\n const time = (tc.durationMs / 1000).toFixed(3);\n\n const hasFailure = tc.status === \"failed\";\n const hasSkipped = tc.status === \"skipped\" || tc.status === \"pending\";\n const hasOutput = this.options.includeOutput && tc.story.steps.length > 0;\n\n // Use full form if there's any content to include\n if (hasFailure || hasSkipped || hasOutput) {\n lines.push(\n `${indent}<testcase classname=\"${escapeXml(classname)}\" name=\"${escapeXml(name)}\" time=\"${time}\">`\n );\n\n if (hasFailure) {\n const message = tc.errorMessage\n ? escapeXml(tc.errorMessage.split(\"\\n\")[0])\n : \"Test failed\";\n lines.push(`${indent}${indent}<failure message=\"${message}\">`);\n if (tc.errorMessage) {\n lines.push(escapeXml(tc.errorMessage));\n }\n if (tc.errorStack) {\n lines.push(\"\");\n lines.push(escapeXml(tc.errorStack));\n }\n lines.push(`${indent}${indent}</failure>`);\n } else if (hasSkipped) {\n const message = tc.status === \"pending\" ? \"Test pending\" : \"Test skipped\";\n lines.push(`${indent}${indent}<skipped message=\"${message}\"/>`);\n }\n\n // Include system-out with step info and docs if requested\n if (hasOutput) {\n const output = this.buildSystemOut(tc);\n lines.push(`${indent}${indent}<system-out>${escapeXml(output)}</system-out>`);\n }\n\n lines.push(`${indent}</testcase>`);\n } else {\n // Self-closing tag for tests without content\n lines.push(\n `${indent}<testcase classname=\"${escapeXml(classname)}\" name=\"${escapeXml(name)}\" time=\"${time}\"/>`\n );\n }\n\n return lines;\n }\n\n /**\n * Build system-out content with steps and docs.\n */\n private buildSystemOut(tc: TestCaseResult): string {\n const outputLines: string[] = [];\n\n // Story-level docs\n if (tc.story.docs && tc.story.docs.length > 0) {\n for (const doc of tc.story.docs) {\n outputLines.push(this.renderDocEntry(doc));\n }\n outputLines.push(\"\");\n }\n\n // Steps with their docs\n for (const step of tc.story.steps) {\n outputLines.push(this.renderStep(step));\n }\n\n return outputLines.join(\"\\n\").trim();\n }\n\n /**\n * Render a step with its docs.\n */\n private renderStep(step: StoryStep): string {\n const lines: string[] = [];\n lines.push(`${step.keyword} ${step.text}`);\n\n // Step docs\n if (step.docs && step.docs.length > 0) {\n for (const doc of step.docs) {\n const rendered = this.renderDocEntry(doc, \" \");\n if (rendered) {\n lines.push(rendered);\n }\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Render a doc entry as plain text.\n */\n private renderDocEntry(entry: DocEntry, indent = \"\"): string {\n switch (entry.kind) {\n case \"note\":\n return `${indent}> ${entry.text}`;\n\n case \"tag\":\n return `${indent}Tags: ${entry.names.join(\", \")}`;\n\n case \"kv\": {\n const val = typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value);\n return `${indent}${entry.label}: ${val}`;\n }\n\n case \"code\": {\n const langLabel = entry.lang ? ` (${entry.lang})` : \"\";\n const header = entry.label ? `${indent}${entry.label}${langLabel}:\\n` : \"\";\n const codeLines = entry.content.split(\"\\n\").map((l) => `${indent} ${l}`).join(\"\\n\");\n return `${header}${codeLines}`;\n }\n\n case \"table\": {\n const lines: string[] = [];\n if (entry.label) {\n lines.push(`${indent}${entry.label}:`);\n }\n lines.push(`${indent}| ${entry.columns.join(\" | \")} |`);\n lines.push(`${indent}| ${entry.columns.map(() => \"---\").join(\" | \")} |`);\n for (const row of entry.rows) {\n lines.push(`${indent}| ${row.join(\" | \")} |`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"link\":\n return `${indent}${entry.label}: ${entry.url}`;\n\n case \"section\": {\n const lines: string[] = [];\n lines.push(`${indent}${entry.title}:`);\n for (const line of entry.markdown.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"mermaid\": {\n const lines: string[] = [];\n if (entry.title) {\n lines.push(`${indent}${entry.title}:`);\n }\n for (const line of entry.code.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"screenshot\":\n return `${indent}Screenshot: ${entry.alt ?? entry.path}`;\n\n case \"custom\": {\n const dataStr = JSON.stringify(entry.data, null, 2);\n const lines: string[] = [];\n lines.push(`${indent}[${entry.type}]:`);\n for (const line of dataStr.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n default:\n return \"\";\n }\n }\n}\n\n/**\n * Escape special XML characters.\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n\n/**\n * Group array items by a key function.\n */\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n","/**\n * Markdown Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into Markdown documentation.\n * Compatible with existing markdown output from framework reporters.\n */\n\nimport type { StoryStep, DocEntry } from \"../types/story\";\nimport type { TestRunResult, TestCaseResult, TestStatus } from \"../types/test-result\";\nimport type { MarkdownRenderers } from \"../types/options\";\n\n/** Options for Markdown formatting */\nexport interface MarkdownOptions {\n /** Report title. Default: \"User Stories\" */\n title?: string;\n /** Include status icons on scenarios. Default: true */\n includeStatusIcons?: boolean;\n /** Include metadata table (date, version). Default: true */\n includeMetadata?: boolean;\n /** Include error details for failed scenarios. Default: true */\n includeErrors?: boolean;\n /** Scenario heading level. Default: 3 */\n scenarioHeadingLevel?: 2 | 3 | 4;\n /** Step rendering style. Default: \"bullets\" */\n stepStyle?: \"bullets\" | \"gherkin\";\n /** Group scenarios by. Default: \"file\" */\n groupBy?: \"file\" | \"suite\" | \"none\";\n /** Sort scenarios. Default: \"source\" */\n sortScenarios?: \"alpha\" | \"source\" | \"none\";\n /** Suite path separator. Default: \" - \" */\n suiteSeparator?: string;\n /** Include YAML front-matter for machine parsing. Default: false */\n includeFrontMatter?: boolean;\n /** Include summary table (counts, duration). Default: false */\n includeSummaryTable?: boolean;\n /** Base URL for source permalinks. E.g., \"https://github.com/user/repo/blob\" */\n permalinkBaseUrl?: string;\n /** URL template for ticket links. Use {ticket} as placeholder */\n ticketUrlTemplate?: string;\n /** URL template for trace links. Use {traceId} as placeholder. E.g. \"https://grafana.example.com/explore?traceId={traceId}\" */\n traceUrlTemplate?: string;\n /** Include source links when permalinkBaseUrl is set. Default: true */\n includeSourceLinks?: boolean;\n /** Custom renderers for doc entries */\n customRenderers?: MarkdownRenderers;\n}\n\n/** Resolved options with all defaults */\ntype ResolvedMarkdownOptions = {\n title: string;\n includeStatusIcons: boolean;\n includeMetadata: boolean;\n includeErrors: boolean;\n scenarioHeadingLevel: 2 | 3 | 4;\n stepStyle: \"bullets\" | \"gherkin\";\n groupBy: \"file\" | \"suite\" | \"none\";\n sortScenarios: \"alpha\" | \"source\" | \"none\";\n suiteSeparator: string;\n includeFrontMatter: boolean;\n includeSummaryTable: boolean;\n permalinkBaseUrl?: string;\n ticketUrlTemplate?: string;\n traceUrlTemplate?: string;\n includeSourceLinks: boolean;\n customRenderers?: MarkdownRenderers;\n};\n\n/**\n * Markdown Formatter.\n *\n * Transforms TestRunResult into Markdown documentation that matches\n * the output format of existing framework reporters.\n */\nexport class MarkdownFormatter {\n private options: ResolvedMarkdownOptions;\n\n constructor(options: MarkdownOptions = {}) {\n this.options = {\n title: options.title ?? \"User Stories\",\n includeStatusIcons: options.includeStatusIcons ?? true,\n includeMetadata: options.includeMetadata ?? true,\n includeErrors: options.includeErrors ?? true,\n scenarioHeadingLevel: options.scenarioHeadingLevel ?? 3,\n stepStyle: options.stepStyle ?? \"bullets\",\n groupBy: options.groupBy ?? \"file\",\n sortScenarios: options.sortScenarios ?? \"source\",\n suiteSeparator: options.suiteSeparator ?? \" - \",\n includeFrontMatter: options.includeFrontMatter ?? false,\n includeSummaryTable: options.includeSummaryTable ?? false,\n permalinkBaseUrl: options.permalinkBaseUrl,\n ticketUrlTemplate: options.ticketUrlTemplate,\n traceUrlTemplate: options.traceUrlTemplate,\n includeSourceLinks: options.includeSourceLinks ?? true,\n customRenderers: options.customRenderers,\n };\n }\n\n /**\n * Format a test run into Markdown.\n *\n * @param run - Canonical test run result\n * @returns Markdown string\n */\n format(run: TestRunResult): string {\n const lines: string[] = [];\n\n // Front-matter\n if (this.options.includeFrontMatter) {\n this.renderFrontMatter(lines, run);\n }\n\n // Title\n lines.push(`# ${this.options.title}`);\n lines.push(\"\");\n\n // Metadata\n if (this.options.includeMetadata) {\n this.renderMetadata(lines, run);\n lines.push(\"\");\n }\n\n // Summary table\n if (this.options.includeSummaryTable) {\n this.renderSummaryTable(lines, run);\n lines.push(\"\");\n }\n\n // Render scenarios based on grouping\n switch (this.options.groupBy) {\n case \"none\":\n this.renderFlatList(lines, run.testCases);\n break;\n case \"suite\":\n this.renderBySuite(lines, run.testCases);\n break;\n case \"file\":\n default:\n this.renderByFile(lines, run.testCases);\n break;\n }\n\n // Custom footer\n if (this.options.customRenderers?.renderFooter) {\n const footer = this.options.customRenderers.renderFooter(run);\n if (footer) {\n lines.push(\"\");\n lines.push(footer);\n }\n }\n\n return lines.join(\"\\n\").trimEnd();\n }\n\n /**\n * Render YAML front-matter.\n */\n private renderFrontMatter(lines: string[], run: TestRunResult): void {\n const data: Record<string, unknown> = {\n title: this.options.title,\n generatedAt: new Date(run.startedAtMs).toISOString(),\n durationMs: run.durationMs,\n scenarios: run.testCases.length,\n passed: run.testCases.filter((tc) => tc.status === \"passed\").length,\n failed: run.testCases.filter((tc) => tc.status === \"failed\").length,\n skipped: run.testCases.filter((tc) => tc.status === \"skipped\").length,\n pending: run.testCases.filter((tc) => tc.status === \"pending\").length,\n };\n\n if (run.packageVersion) data.version = run.packageVersion;\n if (run.gitSha) data.gitSha = run.gitSha.length > 7 ? run.gitSha.slice(0, 7) : run.gitSha;\n if (run.coverage) data.coverage = run.coverage;\n\n lines.push(\"---\");\n for (const [key, value] of Object.entries(data)) {\n if (value === undefined) continue;\n if (typeof value === \"object\") {\n lines.push(`${key}:`);\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n lines.push(` ${k}: ${v}`);\n }\n } else {\n lines.push(`${key}: ${value}`);\n }\n }\n lines.push(\"---\");\n lines.push(\"\");\n }\n\n /**\n * Render summary table.\n */\n private renderSummaryTable(lines: string[], run: TestRunResult): void {\n const totalScenarios = run.testCases.length;\n const totalSteps = run.testCases.reduce((acc, tc) => acc + tc.story.steps.length, 0);\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter((tc) => tc.status === \"skipped\").length;\n const pending = run.testCases.filter((tc) => tc.status === \"pending\").length;\n\n lines.push(\"| Scenarios | Steps | Passed | Failed | Skipped | Pending | Duration |\");\n lines.push(\"| ---: | ---: | ---: | ---: | ---: | ---: | ---: |\");\n lines.push(`| ${totalScenarios} | ${totalSteps} | ${passed} | ${failed} | ${skipped} | ${pending} | ${this.formatDuration(run.durationMs)} |`);\n\n // Coverage summary if available\n if (run.coverage) {\n lines.push(\"\");\n lines.push(\"| Coverage | % |\");\n lines.push(\"| --- | ---: |\");\n if (run.coverage.statementsPct !== undefined) {\n lines.push(`| Statements | ${run.coverage.statementsPct}% |`);\n }\n if (run.coverage.branchesPct !== undefined) {\n lines.push(`| Branches | ${run.coverage.branchesPct}% |`);\n }\n if (run.coverage.functionsPct !== undefined) {\n lines.push(`| Functions | ${run.coverage.functionsPct}% |`);\n }\n if (run.coverage.linesPct !== undefined) {\n lines.push(`| Lines | ${run.coverage.linesPct}% |`);\n }\n }\n }\n\n /**\n * Format duration in human-readable form.\n */\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(2)}s`;\n }\n\n /**\n * Render metadata table.\n */\n private renderMetadata(lines: string[], run: TestRunResult): void {\n const rows: Array<[string, string]> = [];\n\n const startDate = new Date(run.startedAtMs);\n rows.push([\"Date\", startDate.toISOString()]);\n\n if (run.packageVersion) {\n rows.push([\"Version\", run.packageVersion]);\n }\n\n if (run.gitSha) {\n const shortSha = run.gitSha.length > 7 ? run.gitSha.slice(0, 7) : run.gitSha;\n rows.push([\"Git SHA\", shortSha]);\n }\n\n if (rows.length > 0) {\n lines.push(\"| Key | Value |\");\n lines.push(\"| --- | --- |\");\n for (const [key, value] of rows) {\n lines.push(`| ${key} | ${value} |`);\n }\n }\n }\n\n /**\n * Render scenarios grouped by file.\n */\n private renderByFile(lines: string[], testCases: TestCaseResult[]): void {\n const byFile = groupBy(testCases, (tc) => tc.sourceFile);\n\n for (const [file, fileTestCases] of byFile) {\n lines.push(`## ${file}`);\n lines.push(\"\");\n\n // Group by suite path within file\n this.renderSuiteGroups(lines, fileTestCases, 3);\n }\n }\n\n /**\n * Render scenarios grouped by suite path.\n */\n private renderBySuite(lines: string[], testCases: TestCaseResult[]): void {\n this.renderSuiteGroups(lines, testCases, 2);\n }\n\n /**\n * Render suite groups.\n */\n private renderSuiteGroups(\n lines: string[],\n testCases: TestCaseResult[],\n baseLevel: number\n ): void {\n const bySuite = groupBy(testCases, (tc) =>\n tc.titlePath.join(this.options.suiteSeparator)\n );\n\n // Sort suite groups\n const sortedSuites = this.sortSuiteGroups([...bySuite.entries()]);\n\n for (const [suitePath, suiteTestCases] of sortedSuites) {\n if (suitePath) {\n lines.push(`${\"#\".repeat(baseLevel)} ${suitePath}`);\n lines.push(\"\");\n }\n\n const sorted = this.sortScenarios(suiteTestCases);\n for (const tc of sorted) {\n this.renderScenario(lines, tc);\n }\n }\n }\n\n /**\n * Render flat list of scenarios.\n */\n private renderFlatList(lines: string[], testCases: TestCaseResult[]): void {\n const sorted = this.sortScenarios(testCases);\n for (const tc of sorted) {\n this.renderScenario(lines, tc);\n }\n }\n\n /**\n * Render a single scenario.\n */\n private renderScenario(lines: string[], tc: TestCaseResult): void {\n // Check for custom scenario header renderer\n if (this.options.customRenderers?.renderScenarioHeader) {\n const custom = this.options.customRenderers.renderScenarioHeader(tc);\n if (custom !== null) {\n lines.push(custom);\n lines.push(\"\");\n // Still render steps and docs after custom header\n this.renderScenarioBody(lines, tc);\n return;\n }\n }\n\n const headingPrefix = \"#\".repeat(this.options.scenarioHeadingLevel);\n\n // Status icon\n let icon = \"\";\n if (this.options.includeStatusIcons) {\n icon = this.getStatusIcon(tc.status) + \" \";\n }\n\n // Scenario heading\n lines.push(`${headingPrefix} ${icon}${tc.story.scenario}`);\n\n // Source link\n if (this.options.includeSourceLinks && this.options.permalinkBaseUrl && tc.sourceFile !== \"unknown\") {\n const permalink = this.buildPermalink(tc);\n lines.push(`Source: [${tc.sourceFile}](${permalink})`);\n }\n\n // Tags and tickets\n const meta: string[] = [];\n if (tc.tags.length > 0) {\n meta.push(`Tags: ${tc.tags.map((t) => `\\`${t}\\``).join(\", \")}`);\n }\n if (tc.story.tickets && tc.story.tickets.length > 0) {\n const ticketTemplate = this.options.ticketUrlTemplate;\n const ticketLinks = tc.story.tickets.map((t) => {\n if (t.url) {\n return `[${t.id}](${t.url})`;\n }\n if (ticketTemplate) {\n return `[${t.id}](${ticketTemplate.replace(\"{ticket}\", t.id)})`;\n }\n return `\\`${t.id}\\``;\n });\n meta.push(`Tickets: ${ticketLinks.join(\", \")}`);\n }\n // Trace context (injected by OTel bridge in story.init())\n const otelMeta = (tc.story.meta as Record<string, unknown> | undefined)\n ?.otel as { traceId?: string } | undefined;\n if (otelMeta?.traceId) {\n const traceTemplate = this.options.traceUrlTemplate;\n if (traceTemplate) {\n const url = traceTemplate.replace(/\\{traceId\\}/g, otelMeta.traceId);\n meta.push(\n `Trace: [${otelMeta.traceId.slice(0, 16)}…](${url})`,\n );\n } else {\n meta.push(`Trace: \\`${otelMeta.traceId}\\``);\n }\n }\n\n if (meta.length > 0) {\n lines.push(meta.join(\" | \"));\n }\n\n lines.push(\"\");\n\n this.renderScenarioBody(lines, tc);\n }\n\n /**\n * Render scenario body (docs, steps, errors).\n */\n private renderScenarioBody(lines: string[], tc: TestCaseResult): void {\n // Story-level docs\n if (tc.story.docs && tc.story.docs.length > 0) {\n for (const doc of tc.story.docs) {\n this.renderDocEntry(lines, doc);\n }\n }\n\n // Steps\n for (const step of tc.story.steps) {\n this.renderStep(lines, step);\n }\n\n // Error\n if (tc.status === \"failed\" && tc.errorMessage && this.options.includeErrors) {\n lines.push(\"**Failure**\");\n lines.push(\"\");\n lines.push(\"```text\");\n lines.push(tc.errorMessage);\n if (tc.errorStack) {\n lines.push(\"\");\n lines.push(tc.errorStack);\n }\n lines.push(\"```\");\n lines.push(\"\");\n }\n\n lines.push(\"\");\n }\n\n /**\n * Build permalink URL for a test case.\n */\n private buildPermalink(tc: TestCaseResult): string {\n const base = this.options.permalinkBaseUrl!.replace(/\\/$/, \"\");\n const file = tc.sourceFile;\n const line = tc.sourceLine > 0 ? `#L${tc.sourceLine}` : \"\";\n return `${base}/${file}${line}`;\n }\n\n /**\n * Render a step.\n */\n private renderStep(lines: string[], step: StoryStep): void {\n // Check for custom step renderer\n if (this.options.customRenderers?.renderStep) {\n const custom = this.options.customRenderers.renderStep(step);\n if (custom !== null) {\n lines.push(custom);\n // Still render step docs\n if (step.docs && step.docs.length > 0) {\n const indent = this.options.stepStyle === \"gherkin\" ? \"\" : \" \";\n for (const doc of step.docs) {\n this.renderDocEntry(lines, doc, indent);\n }\n }\n return;\n }\n }\n\n // Mode indicator\n let modeIndicator = \"\";\n if (step.mode === \"skip\") {\n modeIndicator = \" _(skipped)_\";\n } else if (step.mode === \"todo\") {\n modeIndicator = \" _(todo)_\";\n } else if (step.mode === \"fails\") {\n modeIndicator = \" _(expected to fail)_\";\n }\n\n if (this.options.stepStyle === \"gherkin\") {\n lines.push(`**${step.keyword}** ${step.text}${modeIndicator}`);\n } else {\n lines.push(`- **${step.keyword}** ${step.text}${modeIndicator}`);\n }\n\n // Render step docs\n if (step.docs && step.docs.length > 0) {\n const indent = this.options.stepStyle === \"gherkin\" ? \"\" : \" \";\n for (const doc of step.docs) {\n this.renderDocEntry(lines, doc, indent);\n }\n }\n }\n\n /**\n * Render a documentation entry.\n */\n private renderDocEntry(lines: string[], entry: DocEntry, indent = \"\"): void {\n // Check for custom doc entry renderer\n if (this.options.customRenderers?.renderDocEntry) {\n const custom = this.options.customRenderers.renderDocEntry(entry);\n if (custom !== null) {\n lines.push(`${indent}${custom}`);\n return;\n }\n }\n\n switch (entry.kind) {\n case \"note\":\n lines.push(`${indent}> ${entry.text}`);\n break;\n\n case \"tag\":\n lines.push(`${indent}${entry.names.map((n) => `\\`${n}\\``).join(\" \")}`);\n break;\n\n case \"kv\": {\n const val = typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value);\n lines.push(`${indent}- **${entry.label}:** ${val}`);\n break;\n }\n\n case \"code\":\n if (entry.label) {\n lines.push(`${indent}**${entry.label}**`);\n lines.push(`${indent}`);\n }\n lines.push(`${indent}\\`\\`\\`${entry.lang ?? \"\"}`);\n for (const line of (entry.content ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n lines.push(`${indent}`);\n break;\n\n case \"table\":\n if (entry.label) {\n lines.push(`${indent}**${entry.label}**`);\n lines.push(`${indent}`);\n }\n lines.push(`${indent}| ${entry.columns.join(\" | \")} |`);\n lines.push(`${indent}| ${entry.columns.map(() => \"---\").join(\" | \")} |`);\n for (const row of entry.rows) {\n lines.push(`${indent}| ${row.join(\" | \")} |`);\n }\n lines.push(`${indent}`);\n break;\n\n case \"link\":\n lines.push(`${indent}[${entry.label}](${entry.url})`);\n break;\n\n case \"section\":\n lines.push(`${indent}**${entry.title}**`);\n lines.push(`${indent}`);\n for (const line of (entry.markdown ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}`);\n break;\n\n case \"mermaid\":\n if (entry.title) {\n lines.push(`${indent}**${entry.title}**`);\n }\n lines.push(`${indent}\\`\\`\\`mermaid`);\n for (const line of (entry.code ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n break;\n\n case \"screenshot\":\n lines.push(`${indent}![${entry.alt ?? \"Screenshot\"}](${entry.path})`);\n break;\n\n case \"custom\":\n lines.push(`${indent}**[${entry.type}]**`);\n lines.push(`${indent}`);\n lines.push(`${indent}\\`\\`\\`json`);\n for (const line of JSON.stringify(entry.data ?? null, null, 2).split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n lines.push(`${indent}`);\n break;\n }\n\n // Render children with increased indentation\n if (entry.children && entry.children.length > 0) {\n const childIndent = indent + \" \";\n for (const child of entry.children) {\n this.renderDocEntry(lines, child, childIndent);\n }\n }\n }\n\n /**\n * Get status icon for a status.\n */\n private getStatusIcon(status: TestStatus): string {\n switch (status) {\n case \"passed\":\n return \"✅\";\n case \"failed\":\n return \"❌\";\n case \"skipped\":\n return \"⏩\";\n case \"pending\":\n return \"📝\";\n default:\n return \"⚠️\";\n }\n }\n\n /**\n * Sort scenarios based on options.\n */\n private sortScenarios(testCases: TestCaseResult[]): TestCaseResult[] {\n if (this.options.sortScenarios === \"alpha\") {\n return [...testCases].sort((a, b) =>\n a.story.scenario.localeCompare(b.story.scenario)\n );\n }\n if (this.options.sortScenarios === \"source\") {\n return [...testCases].sort(\n (a, b) => (a.story.sourceOrder ?? 0) - (b.story.sourceOrder ?? 0)\n );\n }\n return testCases;\n }\n\n /**\n * Sort suite groups.\n */\n private sortSuiteGroups(\n entries: [string, TestCaseResult[]][]\n ): [string, TestCaseResult[]][] {\n if (this.options.sortScenarios === \"alpha\") {\n return entries.sort(([a], [b]) => a.localeCompare(b));\n }\n if (this.options.sortScenarios === \"source\") {\n return entries.sort(([, a], [, b]) => {\n const minA = Math.min(...a.map((s) => s.story.sourceOrder ?? Infinity));\n const minB = Math.min(...b.map((s) => s.story.sourceOrder ?? Infinity));\n return minA - minB;\n });\n }\n return entries;\n }\n}\n\n/**\n * Group array items by a key function.\n */\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n","/**\n * Synthesize .feature file text and a line map from TestCaseResult[].\n *\n * Used to produce Source and GherkinDocument messages when real .feature\n * files don't exist (all test cases come from e.g. Jest/Vitest/Playwright).\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\n\n/** Map from scenario name → { scenarioLine, stepLines: Map<stepIndex, line> } */\nexport interface LineMap {\n featureLine: number;\n scenarios: Map<\n string,\n { scenarioLine: number; stepLines: Map<number, number> }\n >;\n /** Tag lines for each scenario, keyed by scenario name */\n scenarioTagLines: Map<string, number>;\n /** Feature-level tag line (if any) */\n featureTagLine?: number;\n}\n\nexport interface SynthesizedFeature {\n /** The synthesized .feature text */\n text: string;\n /** Line map for building the GherkinDocument AST */\n lineMap: LineMap;\n /** Extracted feature name */\n featureName: string;\n /** Feature-level tags (union of all scenario tags) */\n featureTags: string[];\n}\n\n/**\n * Extract a feature name from grouped test cases.\n *\n * Uses the first element of the first titlePath, or derives from the URI.\n */\nexport function extractFeatureName(\n testCases: TestCaseResult[],\n uri: string\n): string {\n for (const tc of testCases) {\n if (tc.titlePath.length > 0) {\n return tc.titlePath[0];\n }\n }\n // Fallback: derive from filename\n const basename = uri.replace(/^.*[\\\\/]/, \"\").replace(/\\.[^.]+$/, \"\");\n return basename\n .replace(/[-_]+/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Synthesize a .feature file from a group of test cases belonging to the same source file.\n */\nexport function synthesizeFeature(\n uri: string,\n testCases: TestCaseResult[]\n): SynthesizedFeature {\n const featureName = extractFeatureName(testCases, uri);\n\n // Collect feature-level tags (union of all scenario tags)\n const featureTagSet = new Set<string>();\n for (const tc of testCases) {\n for (const tag of tc.tags) {\n featureTagSet.add(tag);\n }\n }\n const featureTags = [...featureTagSet].sort();\n\n const lines: string[] = [];\n const lineMap: LineMap = {\n featureLine: 0,\n scenarios: new Map(),\n scenarioTagLines: new Map(),\n };\n\n let currentLine = 1;\n\n // Feature-level tags\n if (featureTags.length > 0) {\n lines.push(featureTags.map((t) => `@${t}`).join(\" \"));\n lineMap.featureTagLine = currentLine;\n currentLine++;\n }\n\n // Feature declaration\n lines.push(`Feature: ${featureName}`);\n lineMap.featureLine = currentLine;\n currentLine++;\n\n // Blank line after feature\n lines.push(\"\");\n currentLine++;\n\n for (const tc of testCases) {\n const scenario = tc.story.scenario;\n\n // Scenario tags\n if (tc.tags.length > 0) {\n lines.push(` ${tc.tags.map((t) => `@${t}`).join(\" \")}`);\n lineMap.scenarioTagLines.set(scenario, currentLine);\n currentLine++;\n }\n\n // Scenario declaration\n const scenarioLine = currentLine;\n lines.push(` Scenario: ${scenario}`);\n currentLine++;\n\n // Steps\n const stepLines = new Map<number, number>();\n for (let i = 0; i < tc.story.steps.length; i++) {\n const step = tc.story.steps[i];\n stepLines.set(i, currentLine);\n lines.push(` ${step.keyword} ${step.text}`);\n currentLine++;\n }\n\n lineMap.scenarios.set(scenario, { scenarioLine, stepLines });\n\n // Blank line between scenarios\n lines.push(\"\");\n currentLine++;\n }\n\n return {\n text: lines.join(\"\\n\"),\n lineMap,\n featureName,\n featureTags,\n };\n}\n","/**\n * Utility functions for Cucumber Messages NDJSON format.\n */\n\nimport { createHash } from \"node:crypto\";\nimport type {\n Timestamp,\n Duration,\n KeywordType,\n PickleStepType,\n TestStepResultStatus,\n} from \"../types/cucumber-messages\";\nimport type { TestStatus } from \"../types/test-result\";\nimport type { StepKeyword } from \"../types/story\";\n\n/**\n * Convert epoch milliseconds to protobuf Timestamp.\n */\nexport function msToTimestamp(ms: number): Timestamp {\n const seconds = Math.floor(ms / 1000);\n const nanos = Math.round((ms % 1000) * 1_000_000);\n return { seconds, nanos };\n}\n\n/**\n * Convert milliseconds to protobuf Duration.\n */\nexport function msToDuration(ms: number): Duration {\n const seconds = Math.floor(ms / 1000);\n const nanos = Math.round((ms % 1000) * 1_000_000);\n return { seconds, nanos };\n}\n\n/**\n * Map a StepKeyword to a Cucumber KeywordType.\n *\n * And/But/* inherit from the previous non-conjunction keyword type.\n */\nexport function keywordToKeywordType(keyword: StepKeyword): KeywordType {\n switch (keyword) {\n case \"Given\":\n return \"Context\";\n case \"When\":\n return \"Action\";\n case \"Then\":\n return \"Outcome\";\n case \"And\":\n case \"But\":\n return \"Conjunction\";\n default:\n return \"Unknown\";\n }\n}\n\n/**\n * Resolve the effective PickleStepType for a sequence of steps,\n * inheriting the previous non-conjunction type for And/But.\n *\n * Returns an array of resolved types, one per step.\n */\nexport function resolvePickleStepTypes(\n keywords: StepKeyword[]\n): PickleStepType[] {\n let lastNonConjunction: PickleStepType = \"Unknown\";\n return keywords.map((kw) => {\n const kt = keywordToKeywordType(kw);\n if (kt === \"Conjunction\") {\n return lastNonConjunction;\n }\n const resolved = keywordTypeToPickleStepType(kt);\n lastNonConjunction = resolved;\n return resolved;\n });\n}\n\n/**\n * Convert a non-conjunction KeywordType to PickleStepType.\n */\nfunction keywordTypeToPickleStepType(kt: KeywordType): PickleStepType {\n switch (kt) {\n case \"Context\":\n return \"Context\";\n case \"Action\":\n return \"Action\";\n case \"Outcome\":\n return \"Outcome\";\n default:\n return \"Unknown\";\n }\n}\n\n/**\n * Map TestStatus to Cucumber TestStepResultStatus.\n */\nexport function statusToCucumberStatus(\n status: TestStatus\n): TestStepResultStatus {\n switch (status) {\n case \"passed\":\n return \"PASSED\";\n case \"failed\":\n return \"FAILED\";\n case \"skipped\":\n return \"SKIPPED\";\n case \"pending\":\n return \"PENDING\";\n default:\n return \"UNKNOWN\";\n }\n}\n\n/**\n * Generate a deterministic ID using SHA-1.\n *\n * @param kind - Namespace to prevent collisions between entity types (e.g., \"pickle\", \"testCase\")\n * @param salt - Optional salt from options (idSalt)\n * @param parts - Strings to hash\n * @returns 36-character hex string (UUID-length without dashes)\n */\nexport function deterministicId(\n kind: string,\n salt: string,\n ...parts: string[]\n): string {\n const input = [salt, kind, ...parts].join(\"::\");\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 36);\n}\n","/**\n * Build a GherkinDocument envelope from synthesized feature data.\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\nimport type { DocEntry, StepKeyword, StoryStep } from \"../../types/story\";\nimport type {\n GherkinDocument,\n Feature,\n FeatureChild,\n Scenario,\n Step,\n Tag,\n Envelope,\n DocString,\n DataTable,\n TableRow,\n TableCell,\n} from \"../../types/cucumber-messages\";\nimport type { SynthesizedFeature } from \"./synthesize-feature\";\nimport { deterministicId, keywordToKeywordType } from \"../../utils/cucumber-messages\";\n\n/**\n * Build GherkinDocument + Source envelopes for a group of test cases from one file.\n */\nexport function buildGherkinDocumentEnvelopes(\n uri: string,\n testCases: TestCaseResult[],\n synthesized: SynthesizedFeature,\n salt: string\n): { sourceEnvelope: Envelope; gherkinDocumentEnvelope: Envelope } {\n const { lineMap, featureName, featureTags, text } = synthesized;\n\n // Build feature-level tags\n const featureTagNodes: Tag[] = featureTags.map((tag, i) => ({\n location: {\n line: lineMap.featureTagLine ?? 1,\n column: undefined,\n },\n name: `@${tag}`,\n id: deterministicId(\"featureTag\", salt, uri, tag),\n }));\n\n // Build children (scenarios)\n const children: FeatureChild[] = [];\n\n for (const tc of testCases) {\n const scenarioName = tc.story.scenario;\n const scenarioInfo = lineMap.scenarios.get(scenarioName);\n if (!scenarioInfo) continue;\n\n const scenarioId = deterministicId(\"scenario\", salt, uri, scenarioName);\n\n // Build scenario tags\n const scenarioTags: Tag[] = tc.tags.map((tag) => ({\n location: {\n line: lineMap.scenarioTagLines.get(scenarioName) ?? scenarioInfo.scenarioLine,\n },\n name: `@${tag}`,\n id: deterministicId(\"scenarioTag\", salt, uri, scenarioName, tag),\n }));\n\n // Build steps with keyword type tracking for And/But inheritance\n let lastNonConjunctionType: \"Context\" | \"Action\" | \"Outcome\" = \"Context\";\n const steps: Step[] = tc.story.steps.map((step, i) => {\n const keyword = step.keyword as StepKeyword;\n let kwType = keywordToKeywordType(keyword);\n\n if (kwType === \"Conjunction\") {\n kwType = lastNonConjunctionType;\n } else if (kwType === \"Context\" || kwType === \"Action\" || kwType === \"Outcome\") {\n lastNonConjunctionType = kwType;\n }\n\n const stepLine = scenarioInfo.stepLines.get(i) ?? 0;\n const astStep: Step = {\n location: { line: stepLine },\n keyword: `${keyword} `,\n keywordType: keywordToKeywordType(keyword),\n text: step.text,\n id: deterministicId(\"astStep\", salt, uri, scenarioName, String(i)),\n };\n\n // Convert doc entries to DocString/DataTable\n const { docString, dataTable } = buildStepArguments(step, stepLine);\n if (docString) astStep.docString = docString;\n if (dataTable) astStep.dataTable = dataTable;\n\n return astStep;\n });\n\n const scenario: Scenario = {\n location: { line: scenarioInfo.scenarioLine },\n tags: scenarioTags,\n keyword: \"Scenario\",\n name: scenarioName,\n description: \"\",\n steps,\n id: scenarioId,\n };\n\n children.push({ scenario });\n }\n\n const feature: Feature = {\n location: { line: lineMap.featureLine },\n tags: featureTagNodes,\n language: \"en\",\n keyword: \"Feature\",\n name: featureName,\n description: \"\",\n children,\n };\n\n const gherkinDocument: GherkinDocument = { uri, feature };\n\n return {\n sourceEnvelope: {\n source: {\n uri,\n data: text,\n mediaType: \"text/x.cucumber.gherkin+plain\",\n },\n },\n gherkinDocumentEnvelope: { gherkinDocument },\n };\n}\n\n/**\n * Convert step doc entries to DocString or DataTable for the GherkinDocument AST.\n *\n * Priority: first \"table\" doc → DataTable, else first \"code\"/\"note\"/\"section\"/\"mermaid\" → DocString.\n * Screenshots are handled as attachments, not arguments.\n */\nfunction buildStepArguments(\n step: StoryStep,\n stepLine: number\n): { docString?: DocString; dataTable?: DataTable } {\n if (!step.docs || step.docs.length === 0) return {};\n\n // Look for table first (takes priority)\n const tableDocs = step.docs.filter((d): d is Extract<DocEntry, { kind: \"table\" }> => d.kind === \"table\");\n if (tableDocs.length > 0) {\n const table = tableDocs[0];\n return { dataTable: buildDataTable(table, stepLine + 1) };\n }\n\n // Look for doc-string-like entries\n for (const doc of step.docs) {\n const ds = docEntryToDocString(doc, stepLine + 1);\n if (ds) return { docString: ds };\n }\n\n return {};\n}\n\nfunction docEntryToDocString(doc: DocEntry, line: number): DocString | undefined {\n switch (doc.kind) {\n case \"code\":\n return {\n location: { line },\n mediaType: doc.lang,\n content: doc.content,\n delimiter: '\"\"\"',\n };\n case \"note\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: doc.text,\n delimiter: '\"\"\"',\n };\n case \"section\":\n return {\n location: { line },\n mediaType: \"text/markdown\",\n content: doc.markdown,\n delimiter: '\"\"\"',\n };\n case \"mermaid\":\n return {\n location: { line },\n mediaType: \"text/x-mermaid\",\n content: doc.code,\n delimiter: '\"\"\"',\n };\n case \"kv\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: `${doc.label}: ${typeof doc.value === \"string\" ? doc.value : JSON.stringify(doc.value)}`,\n delimiter: '\"\"\"',\n };\n case \"link\":\n return {\n location: { line },\n mediaType: \"text/markdown\",\n content: `[${doc.label}](${doc.url})`,\n delimiter: '\"\"\"',\n };\n case \"custom\":\n return {\n location: { line },\n mediaType: \"application/json\",\n content: JSON.stringify(doc.data, null, 2),\n delimiter: '\"\"\"',\n };\n case \"tag\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: doc.names.map((n) => `@${n}`).join(\" \"),\n delimiter: '\"\"\"',\n };\n // screenshot and other kinds are not converted to doc strings\n default:\n return undefined;\n }\n}\n\nfunction buildDataTable(\n table: Extract<DocEntry, { kind: \"table\" }>,\n line: number\n): DataTable {\n const rows: TableRow[] = [];\n\n // Header row\n rows.push({\n location: { line },\n cells: table.columns.map((col) => ({\n location: { line },\n value: col,\n })),\n id: \"\",\n });\n\n // Data rows\n for (let r = 0; r < table.rows.length; r++) {\n const rowLine = line + 1 + r;\n rows.push({\n location: { line: rowLine },\n cells: table.rows[r].map((cell) => ({\n location: { line: rowLine },\n value: cell,\n })),\n id: \"\",\n });\n }\n\n return {\n location: { line },\n rows,\n };\n}\n","/**\n * Build Pickle envelopes from test cases and GherkinDocument data.\n *\n * Each TestCaseResult becomes one Pickle (the compiled, runnable scenario).\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\nimport type { DocEntry, StepKeyword, StoryStep } from \"../../types/story\";\nimport type {\n Pickle,\n PickleStep,\n PickleTag,\n PickleStepArgument,\n PickleDocString,\n PickleTable,\n PickleTableRow,\n Envelope,\n} from \"../../types/cucumber-messages\";\nimport {\n deterministicId,\n resolvePickleStepTypes,\n} from \"../../utils/cucumber-messages\";\n\n/**\n * Build Pickle envelopes for a group of test cases from one file.\n */\nexport function buildPickleEnvelopes(\n uri: string,\n testCases: TestCaseResult[],\n salt: string\n): Envelope[] {\n const envelopes: Envelope[] = [];\n\n for (const tc of testCases) {\n const scenarioName = tc.story.scenario;\n const pickleId = deterministicId(\"pickle\", salt, uri, scenarioName);\n const scenarioAstId = deterministicId(\"scenario\", salt, uri, scenarioName);\n\n // Resolve step types with And/But inheritance\n const keywords = tc.story.steps.map((s) => s.keyword as StepKeyword);\n const resolvedTypes = resolvePickleStepTypes(keywords);\n\n const pickleSteps: PickleStep[] = tc.story.steps.map((step, i) => {\n const ps: PickleStep = {\n astNodeIds: [\n deterministicId(\"astStep\", salt, uri, scenarioName, String(i)),\n ],\n id: deterministicId(\"pickleStep\", salt, uri, scenarioName, String(i)),\n type: resolvedTypes[i],\n text: step.text,\n };\n\n const argument = buildPickleStepArgument(step);\n if (argument) ps.argument = argument;\n\n return ps;\n });\n\n const pickleTags: PickleTag[] = tc.tags.map((tag) => ({\n name: `@${tag}`,\n astNodeId: deterministicId(\"scenarioTag\", salt, uri, scenarioName, tag),\n }));\n\n const pickle: Pickle = {\n id: pickleId,\n uri,\n name: scenarioName,\n language: \"en\",\n steps: pickleSteps,\n tags: pickleTags,\n astNodeIds: [scenarioAstId],\n };\n\n envelopes.push({ pickle });\n }\n\n return envelopes;\n}\n\n/**\n * Build a PickleStepArgument from step docs.\n * Priority: first table → PickleTable, else first doc-string-like → PickleDocString.\n */\nfunction buildPickleStepArgument(step: StoryStep): PickleStepArgument | undefined {\n if (!step.docs || step.docs.length === 0) return undefined;\n\n // Table takes priority\n const tableDocs = step.docs.filter(\n (d): d is Extract<DocEntry, { kind: \"table\" }> => d.kind === \"table\"\n );\n if (tableDocs.length > 0) {\n return { dataTable: buildPickleTable(tableDocs[0]) };\n }\n\n // Doc-string-like entries\n for (const doc of step.docs) {\n const ds = docEntryToPickleDocString(doc);\n if (ds) return { docString: ds };\n }\n\n return undefined;\n}\n\nfunction docEntryToPickleDocString(doc: DocEntry): PickleDocString | undefined {\n switch (doc.kind) {\n case \"code\":\n return { mediaType: doc.lang, content: doc.content };\n case \"note\":\n return { mediaType: \"text/plain\", content: doc.text };\n case \"section\":\n return { mediaType: \"text/markdown\", content: doc.markdown };\n case \"mermaid\":\n return { mediaType: \"text/x-mermaid\", content: doc.code };\n case \"kv\":\n return {\n mediaType: \"text/plain\",\n content: `${doc.label}: ${typeof doc.value === \"string\" ? doc.value : JSON.stringify(doc.value)}`,\n };\n case \"link\":\n return { mediaType: \"text/markdown\", content: `[${doc.label}](${doc.url})` };\n case \"custom\":\n return { mediaType: \"application/json\", content: JSON.stringify(doc.data, null, 2) };\n case \"tag\":\n return { mediaType: \"text/plain\", content: doc.names.map((n) => `@${n}`).join(\" \") };\n default:\n return undefined;\n }\n}\n\nfunction buildPickleTable(\n table: Extract<DocEntry, { kind: \"table\" }>\n): PickleTable {\n const rows: PickleTableRow[] = [];\n\n // Header row\n rows.push({\n cells: table.columns.map((col) => ({ value: col })),\n });\n\n // Data rows\n for (const row of table.rows) {\n rows.push({\n cells: row.map((cell) => ({ value: cell })),\n });\n }\n\n return { rows };\n}\n","/**\n * Build execution envelopes: TestCase, TestCaseStarted/Finished,\n * TestStepStarted/Finished, and Attachment messages.\n *\n * Attachment placement:\n * - Step-level doc screenshots → Attachment tied to that specific step\n * - Test-case level attachments → Attachment tied to failed step or last step\n * - All Attachment envelopes appear after the relevant TestStepFinished\n */\n\nimport type { TestCaseResult, TestRunResult } from \"../../types/test-result\";\nimport type { StoryStep } from \"../../types/story\";\nimport type {\n Envelope,\n TestCase,\n TestStep,\n TestCaseStarted,\n TestStepStarted,\n TestStepFinished,\n TestCaseFinished,\n TestRunStarted,\n TestRunFinished,\n CucumberAttachment,\n AttachmentContentEncoding,\n} from \"../../types/cucumber-messages\";\nimport {\n deterministicId,\n msToTimestamp,\n msToDuration,\n statusToCucumberStatus,\n} from \"../../utils/cucumber-messages\";\n\n/**\n * Build TestRunStarted envelope.\n */\nexport function buildTestRunStarted(run: TestRunResult): Envelope {\n return {\n testRunStarted: {\n timestamp: msToTimestamp(run.startedAtMs),\n },\n };\n}\n\n/**\n * Build TestRunFinished envelope.\n */\nexport function buildTestRunFinished(run: TestRunResult): Envelope {\n const allPassed = run.testCases.every((tc) => tc.status === \"passed\");\n return {\n testRunFinished: {\n timestamp: msToTimestamp(run.finishedAtMs),\n success: allPassed,\n },\n };\n}\n\n/**\n * Build all execution envelopes for a single test case:\n * TestCase, then for each attempt:\n * TestCaseStarted, [TestStepStarted + TestStepFinished + Attachment*]*, TestCaseFinished\n *\n * When tc.attempts exists with multiple entries, prior attempts are emitted\n * with willBeRetried=true, and the final attempt with willBeRetried=false.\n */\nexport function buildTestCaseExecutionEnvelopes(\n uri: string,\n tc: TestCaseResult,\n salt: string\n): Envelope[] {\n const envelopes: Envelope[] = [];\n const scenarioName = tc.story.scenario;\n\n const pickleId = deterministicId(\"pickle\", salt, uri, scenarioName);\n const testCaseId = deterministicId(\"testCase\", salt, uri, scenarioName);\n\n // Build test steps (shared across all attempts — the TestCase is the same)\n const testSteps: TestStep[] = tc.story.steps.map((_step, i) => ({\n id: deterministicId(\"testStep\", salt, uri, scenarioName, String(i)),\n pickleStepId: deterministicId(\n \"pickleStep\",\n salt,\n uri,\n scenarioName,\n String(i)\n ),\n stepDefinitionIds: [],\n }));\n\n // TestCase envelope (emitted once, before all attempts)\n const testCase: TestCase = {\n id: testCaseId,\n pickleId,\n testSteps,\n };\n envelopes.push({ testCase });\n\n // If there are explicit attempts, emit each one\n if (tc.attempts && tc.attempts.length > 1) {\n for (let a = 0; a < tc.attempts.length; a++) {\n const attempt = tc.attempts[a];\n const isLastAttempt = a === tc.attempts.length - 1;\n\n const attemptEnvelopes = buildAttemptEnvelopes({\n testCaseId,\n testSteps,\n tc,\n uri,\n scenarioName,\n salt,\n attemptNumber: attempt.attempt,\n attemptStatus: attempt.status,\n attemptDurationMs: attempt.durationMs,\n attemptErrorMessage: attempt.errorMessage,\n willBeRetried: !isLastAttempt,\n // Only emit attachments and doc screenshots on the final attempt\n emitAttachments: isLastAttempt,\n });\n envelopes.push(...attemptEnvelopes);\n }\n } else {\n // Single attempt (normal case)\n const attemptEnvelopes = buildAttemptEnvelopes({\n testCaseId,\n testSteps,\n tc,\n uri,\n scenarioName,\n salt,\n attemptNumber: tc.retry,\n attemptStatus: undefined, // Use tc.stepResults\n attemptDurationMs: undefined,\n attemptErrorMessage: undefined,\n willBeRetried: false,\n emitAttachments: true,\n });\n envelopes.push(...attemptEnvelopes);\n }\n\n return envelopes;\n}\n\ninterface AttemptParams {\n testCaseId: string;\n testSteps: TestStep[];\n tc: TestCaseResult;\n uri: string;\n scenarioName: string;\n salt: string;\n attemptNumber: number;\n attemptStatus: import(\"../../types/test-result.js\").TestStatus | undefined;\n attemptDurationMs: number | undefined;\n attemptErrorMessage: string | undefined;\n willBeRetried: boolean;\n emitAttachments: boolean;\n}\n\n/**\n * Build envelopes for a single attempt:\n * TestCaseStarted, [TestStepStarted, TestStepFinished, Attachment*]*, TestCaseFinished\n */\nfunction buildAttemptEnvelopes(params: AttemptParams): Envelope[] {\n const {\n testCaseId, testSteps, tc, uri, scenarioName, salt,\n attemptNumber, attemptStatus, attemptDurationMs, attemptErrorMessage,\n willBeRetried, emitAttachments,\n } = params;\n\n const envelopes: Envelope[] = [];\n const testCaseStartedId = deterministicId(\n \"testCaseStarted\",\n salt,\n uri,\n scenarioName,\n String(attemptNumber)\n );\n\n // TestCaseStarted\n envelopes.push({\n testCaseStarted: {\n id: testCaseStartedId,\n testCaseId,\n timestamp: msToTimestamp(0),\n attempt: attemptNumber,\n },\n });\n\n // Determine which step receives test-case-level attachments\n const tcAttachmentStepIndex = emitAttachments\n ? findTestCaseAttachmentStepIndex(tc)\n : -1;\n\n // Step execution envelopes\n let cumulativeMs = 0;\n\n for (let i = 0; i < testSteps.length; i++) {\n const testStep = testSteps[i];\n const storyStep = tc.story.steps[i];\n\n // Determine step status for this attempt\n let stepStatus: import(\"../../types/test-result.js\").TestStatus;\n let stepDurationMs: number;\n let stepErrorMessage: string | undefined;\n\n if (attemptStatus !== undefined) {\n // For prior attempts, derive step statuses from attempt-level status\n // (we don't have per-step data for prior attempts)\n stepStatus = attemptStatus === \"failed\" && i === tc.story.steps.length - 1\n ? \"failed\"\n : attemptStatus === \"failed\" && i < tc.story.steps.length - 1\n ? \"passed\"\n : attemptStatus;\n stepDurationMs = attemptDurationMs !== undefined\n ? attemptDurationMs / tc.story.steps.length\n : 0;\n stepErrorMessage = stepStatus === \"failed\" ? attemptErrorMessage : undefined;\n } else {\n // Final/only attempt: use actual step results\n const stepResult = tc.stepResults[i];\n stepStatus = stepResult?.status ?? \"passed\";\n stepDurationMs = stepResult?.durationMs ?? 0;\n // Include stack trace from test case if this is the failed step\n stepErrorMessage = stepResult?.errorMessage;\n if (stepStatus === \"failed\" && tc.errorStack && stepErrorMessage) {\n stepErrorMessage = stepErrorMessage + \"\\n\" + tc.errorStack;\n } else if (stepStatus === \"failed\" && tc.errorStack && !stepErrorMessage) {\n stepErrorMessage = tc.errorStack;\n }\n }\n\n // TestStepStarted\n envelopes.push({\n testStepStarted: {\n testCaseStartedId,\n testStepId: testStep.id,\n timestamp: msToTimestamp(cumulativeMs),\n },\n });\n\n // TestStepFinished\n cumulativeMs += stepDurationMs;\n envelopes.push({\n testStepFinished: {\n testCaseStartedId,\n testStepId: testStep.id,\n testStepResult: {\n duration: msToDuration(stepDurationMs),\n status: statusToCucumberStatus(stepStatus),\n message: stepErrorMessage,\n },\n timestamp: msToTimestamp(cumulativeMs),\n },\n });\n\n // Per-step doc screenshots (only on final attempt)\n if (emitAttachments) {\n const docAttachments = extractDocAttachments(storyStep);\n for (const att of docAttachments) {\n envelopes.push({\n attachment: {\n testCaseStartedId,\n testStepId: testStep.id,\n body: att.body,\n mediaType: att.mediaType,\n contentEncoding: att.contentEncoding,\n },\n });\n }\n }\n\n // Test-case level attachments (only on final attempt, on failed/last step)\n if (i === tcAttachmentStepIndex) {\n for (const att of tc.attachments) {\n envelopes.push({\n attachment: {\n testCaseStartedId,\n testStepId: testStep.id,\n body: att.body,\n mediaType: att.mediaType,\n contentEncoding: att.contentEncoding,\n },\n });\n }\n }\n }\n\n // TestCaseFinished\n envelopes.push({\n testCaseFinished: {\n testCaseStartedId,\n timestamp: msToTimestamp(cumulativeMs),\n willBeRetried,\n },\n });\n\n return envelopes;\n}\n\n/**\n * Extract attachments from a step's doc entries.\n *\n * Handles:\n * - screenshot docs with data: URIs → BASE64 attachment\n * - screenshot docs with other paths → IDENTITY attachment\n */\nfunction extractDocAttachments(\n step: StoryStep\n): Array<{ body: string; mediaType: string; contentEncoding: AttachmentContentEncoding }> {\n if (!step.docs) return [];\n\n const attachments: Array<{\n body: string;\n mediaType: string;\n contentEncoding: AttachmentContentEncoding;\n }> = [];\n\n for (const doc of step.docs) {\n if (doc.kind !== \"screenshot\") continue;\n\n // Parse data URI: data:image/png;base64,ABC123...\n const match = doc.path.match(/^data:([^;]+);base64,(.+)$/);\n if (match) {\n attachments.push({\n body: match[2],\n mediaType: match[1],\n contentEncoding: \"BASE64\",\n });\n } else {\n // Non-data-URI screenshot (file path or URL) → IDENTITY\n attachments.push({\n body: doc.path,\n mediaType: guessMediaType(doc.path),\n contentEncoding: \"IDENTITY\",\n });\n }\n }\n\n return attachments;\n}\n\n/**\n * Guess media type from a file path or URL.\n */\nfunction guessMediaType(path: string): string {\n const lower = path.toLowerCase();\n if (lower.endsWith(\".png\")) return \"image/png\";\n if (lower.endsWith(\".jpg\") || lower.endsWith(\".jpeg\")) return \"image/jpeg\";\n if (lower.endsWith(\".gif\")) return \"image/gif\";\n if (lower.endsWith(\".webp\")) return \"image/webp\";\n if (lower.endsWith(\".svg\")) return \"image/svg+xml\";\n return \"image/png\"; // Default assumption for screenshots\n}\n\n/**\n * Determine which step index should receive test-case-level attachments.\n * Attach to the failed step if one exists, otherwise the last step.\n * Returns -1 if there are no attachments.\n */\nfunction findTestCaseAttachmentStepIndex(tc: TestCaseResult): number {\n if (tc.attachments.length === 0) return -1;\n\n const failedIndex = tc.stepResults.findIndex((sr) => sr.status === \"failed\");\n if (failedIndex >= 0) return failedIndex;\n\n return tc.stepResults.length - 1;\n}\n","/**\n * CucumberMessagesFormatter — produces NDJSON compatible with @cucumber/html-formatter.\n *\n * Message stream order:\n * Meta → Source* → GherkinDocument* → Pickle* → TestRunStarted →\n * [TestCase, TestCaseStarted, TestStep*, TestCaseFinished]* → TestRunFinished\n */\n\nimport type { TestRunResult, TestCaseResult } from \"../../types/test-result\";\nimport type { Envelope, Meta } from \"../../types/cucumber-messages\";\nimport { synthesizeFeature } from \"./synthesize-feature\";\nimport { buildGherkinDocumentEnvelopes } from \"./build-gherkin-document\";\nimport { buildPickleEnvelopes } from \"./build-pickles\";\nimport {\n buildTestRunStarted,\n buildTestRunFinished,\n buildTestCaseExecutionEnvelopes,\n} from \"./build-execution\";\n\nexport interface CucumberMessagesOptions {\n /** Strategy for deriving Source.uri. Default: \"sourceFile\" */\n uriStrategy?: \"sourceFile\" | \"virtual\";\n /** Whether to emit Source/GherkinDocument for synthesized features. Default: true */\n includeSynthetics?: boolean;\n /** Salt for deterministic IDs. Default: \"\" */\n idSalt?: string;\n /** Tool metadata for Meta envelope */\n meta?: { toolName?: string; toolVersion?: string };\n}\n\nexport class CucumberMessagesFormatter {\n private options: Required<\n Pick<CucumberMessagesOptions, \"uriStrategy\" | \"includeSynthetics\" | \"idSalt\">\n > & { meta?: CucumberMessagesOptions[\"meta\"] };\n\n constructor(options: CucumberMessagesOptions = {}) {\n this.options = {\n uriStrategy: options.uriStrategy ?? \"sourceFile\",\n includeSynthetics: options.includeSynthetics ?? true,\n idSalt: options.idSalt ?? \"\",\n meta: options.meta,\n };\n }\n\n /**\n * Format a TestRunResult into an array of Envelope objects.\n */\n format(run: TestRunResult): Envelope[] {\n const envelopes: Envelope[] = [];\n const salt = this.options.idSalt;\n\n // 1. Meta envelope\n envelopes.push(this.buildMetaEnvelope(run));\n\n // Group test cases by source file\n const grouped = this.groupBySourceFile(run.testCases);\n\n // 2. Source + GherkinDocument + Pickle envelopes (definition phase)\n const allPickleEnvelopes: Envelope[] = [];\n\n for (const [uri, testCases] of grouped) {\n const synthesized = synthesizeFeature(uri, testCases);\n\n if (this.options.includeSynthetics) {\n const { sourceEnvelope, gherkinDocumentEnvelope } =\n buildGherkinDocumentEnvelopes(uri, testCases, synthesized, salt);\n envelopes.push(sourceEnvelope);\n envelopes.push(gherkinDocumentEnvelope);\n }\n\n const pickles = buildPickleEnvelopes(uri, testCases, salt);\n allPickleEnvelopes.push(...pickles);\n }\n\n // All pickles after all source/gherkin documents\n envelopes.push(...allPickleEnvelopes);\n\n // 3. TestRunStarted\n envelopes.push(buildTestRunStarted(run));\n\n // 4. Execution envelopes per test case\n for (const [uri, testCases] of grouped) {\n for (const tc of testCases) {\n const executionEnvelopes = buildTestCaseExecutionEnvelopes(\n uri,\n tc,\n salt\n );\n envelopes.push(...executionEnvelopes);\n }\n }\n\n // 5. TestRunFinished (always last)\n envelopes.push(buildTestRunFinished(run));\n\n return envelopes;\n }\n\n /**\n * Format as NDJSON string (one JSON line per envelope).\n */\n formatToString(run: TestRunResult): string {\n const envelopes = this.format(run);\n return envelopes.map((e) => JSON.stringify(e)).join(\"\\n\") + \"\\n\";\n }\n\n /**\n * Build the Meta envelope.\n */\n private buildMetaEnvelope(run: TestRunResult): Envelope {\n const meta: Meta = {\n protocolVersion: \"25.0.1\",\n implementation: {\n name: this.options.meta?.toolName ?? \"executable-stories\",\n version:\n this.options.meta?.toolVersion ?? run.packageVersion ?? \"0.0.0\",\n },\n runtime: {\n name: \"node.js\",\n version: process.version,\n },\n os: {\n name: process.platform,\n },\n cpu: {\n name: process.arch,\n },\n };\n\n return { meta };\n }\n\n /**\n * Group test cases by source file, preserving order.\n */\n private groupBySourceFile(\n testCases: TestCaseResult[]\n ): Map<string, TestCaseResult[]> {\n const grouped = new Map<string, TestCaseResult[]>();\n for (const tc of testCases) {\n const uri = tc.sourceFile;\n const existing = grouped.get(uri);\n if (existing) {\n existing.push(tc);\n } else {\n grouped.set(uri, [tc]);\n }\n }\n return grouped;\n }\n}\n","/**\n * CucumberHtmlFormatter — produces the official Cucumber HTML report.\n *\n * Thin wrapper: reuses CucumberMessagesFormatter to generate NDJSON envelopes,\n * then pipes them through @cucumber/html-formatter's CucumberHtmlStream.\n */\n\nimport { Readable, Writable } from \"node:stream\";\nimport { CucumberHtmlStream } from \"@cucumber/html-formatter\";\nimport { CucumberMessagesFormatter } from \"./cucumber-messages/index\";\nimport type { CucumberMessagesOptions } from \"./cucumber-messages/index\";\nimport type { TestRunResult } from \"../types/test-result\";\n\nexport interface CucumberHtmlOptions {\n /** Options forwarded to the underlying CucumberMessagesFormatter */\n messages?: CucumberMessagesOptions;\n}\n\nexport class CucumberHtmlFormatter {\n private messagesFormatter: CucumberMessagesFormatter;\n\n constructor(options: CucumberHtmlOptions = {}) {\n this.messagesFormatter = new CucumberMessagesFormatter(options.messages);\n }\n\n /**\n * Format a TestRunResult into official Cucumber HTML.\n *\n * Returns a Promise because CucumberHtmlStream is a Node.js Transform stream.\n */\n async format(run: TestRunResult): Promise<string> {\n return this.formatToString(run);\n }\n\n /**\n * Format a TestRunResult into official Cucumber HTML string.\n */\n async formatToString(run: TestRunResult): Promise<string> {\n // 1. Generate NDJSON envelopes as plain objects\n const envelopes = this.messagesFormatter.format(run);\n\n // 2. Create the Cucumber HTML stream\n const htmlStream = new CucumberHtmlStream();\n\n // 3. Collect output chunks\n const chunks: Buffer[] = [];\n const collector = new Writable({\n write(chunk, _encoding, callback) {\n chunks.push(Buffer.from(chunk));\n callback();\n },\n });\n\n // 4. Pipe HTML stream output to collector\n htmlStream.pipe(collector);\n\n // 5. Write each envelope through the stream\n for (const envelope of envelopes) {\n const accepted = htmlStream.write(envelope);\n if (!accepted) {\n await new Promise<void>((resolve) => htmlStream.once(\"drain\", resolve));\n }\n }\n\n // 6. End the stream and wait for completion\n await new Promise<void>((resolve, reject) => {\n collector.on(\"finish\", resolve);\n collector.on(\"error\", reject);\n htmlStream.end();\n });\n\n return Buffer.concat(chunks).toString(\"utf8\");\n }\n}\n","import type { DocEntry, NormalizedTicket, StoryStep } from \"./story\";\nimport type { Attachment, TestCaseResult, TestRunResult, TestStatus } from \"./test-result\";\n\nexport type ScenarioChangeKind =\n | \"added\"\n | \"removed\"\n | \"regressed\"\n | \"fixed\"\n | \"changed\"\n | \"unchanged\";\n\nexport interface ScenarioChangeFlags {\n status: boolean;\n steps: boolean;\n docs: boolean;\n tags: boolean;\n tickets: boolean;\n source: boolean;\n duration: boolean;\n attachments: boolean;\n error: boolean;\n titlePath: boolean;\n}\n\nexport interface ScenarioSnapshot {\n id: string;\n scenario: string;\n sourceFile: string;\n sourceLine: number;\n status: TestStatus;\n durationMs: number;\n tags: string[];\n titlePath: string[];\n steps: StoryStep[];\n docs: DocEntry[];\n tickets: NormalizedTicket[];\n attachments: Attachment[];\n errorMessage?: string;\n}\n\nexport interface ScenarioDiff {\n kind: ScenarioChangeKind;\n id: string;\n scenario: string;\n sourceFile: string;\n sourceLine: number;\n baseline?: ScenarioSnapshot;\n current?: ScenarioSnapshot;\n flags: ScenarioChangeFlags;\n changedFields: string[];\n durationDeltaMs?: number;\n}\n\nexport interface RunDiffSummary {\n totalBaseline: number;\n totalCurrent: number;\n added: number;\n removed: number;\n changed: number;\n regressed: number;\n fixed: number;\n unchanged: number;\n}\n\nexport interface RunDiffResult {\n baseline: TestRunResult;\n current: TestRunResult;\n summary: RunDiffSummary;\n scenarios: ScenarioDiff[];\n}\n\nexport type CompareFormat = \"html\" | \"markdown\";\n\nexport interface CompareFormatterOptions {\n title?: string;\n}\n\nexport function toScenarioSnapshot(tc: TestCaseResult): ScenarioSnapshot {\n return {\n id: tc.id,\n scenario: tc.story.scenario,\n sourceFile: tc.sourceFile,\n sourceLine: tc.sourceLine,\n status: tc.status,\n durationMs: tc.durationMs,\n tags: tc.tags,\n titlePath: tc.titlePath,\n steps: tc.story.steps,\n docs: tc.story.docs ?? [],\n tickets: tc.story.tickets ?? [],\n attachments: tc.attachments,\n errorMessage: tc.errorMessage,\n };\n}\n","import type { RunDiffResult, ScenarioDiff } from \"../types/compare\";\n\nfunction titleFor(kind: ScenarioDiff[\"kind\"]): string {\n switch (kind) {\n case \"regressed\":\n return \"Regressed\";\n case \"fixed\":\n return \"Fixed\";\n case \"added\":\n return \"Added\";\n case \"removed\":\n return \"Removed\";\n case \"changed\":\n return \"Changed\";\n default:\n return \"Unchanged\";\n }\n}\n\nfunction summarizeScenario(scenario: ScenarioDiff): string {\n const before = scenario.baseline?.status;\n const after = scenario.current?.status;\n const statusPart =\n before && after\n ? `status \\`${before}\\` -> \\`${after}\\``\n : before\n ? `removed from \\`${before}\\``\n : `new \\`${after}\\``;\n const fields =\n scenario.changedFields.length > 0\n ? `; changed ${scenario.changedFields.map((field) => `\\`${field}\\``).join(\", \")}`\n : \"\";\n return `- ${scenario.scenario} (\\`${scenario.sourceFile}:${scenario.sourceLine}\\`): ${statusPart}${fields}`;\n}\n\nfunction addSection(\n lines: string[],\n diff: RunDiffResult,\n kind: ScenarioDiff[\"kind\"],\n maxScenarios: number\n): void {\n const scenarios = diff.scenarios.filter((scenario) => scenario.kind === kind);\n if (scenarios.length === 0) return;\n\n lines.push(`### ${titleFor(kind)} (${scenarios.length})`);\n lines.push(\"\");\n for (const scenario of scenarios.slice(0, maxScenarios)) {\n lines.push(summarizeScenario(scenario));\n }\n if (scenarios.length > maxScenarios) {\n lines.push(`- ...and ${scenarios.length - maxScenarios} more`);\n }\n lines.push(\"\");\n}\n\nexport function createPrCommentSummary(\n diff: RunDiffResult,\n maxScenarios = 10\n): string {\n const lines: string[] = [];\n\n lines.push(\"## Executable Stories Review Summary\");\n lines.push(\"\");\n lines.push(\n `Priority signal: ${diff.summary.regressed} regressed, ${diff.summary.fixed} fixed, ${diff.summary.added} added, ${diff.summary.removed} removed, ${diff.summary.changed} changed.`\n );\n lines.push(\"\");\n\n if (diff.summary.regressed > 0) {\n lines.push(\"> Regressions detected. Review these first.\");\n lines.push(\"\");\n } else if (diff.summary.fixed > 0) {\n lines.push(\"> No regressions detected. Review fixed scenarios next.\");\n lines.push(\"\");\n } else {\n lines.push(\"> No regressions or fixes detected. Remaining changes are neutral.\");\n lines.push(\"\");\n }\n\n addSection(lines, diff, \"regressed\", maxScenarios);\n addSection(lines, diff, \"fixed\", maxScenarios);\n addSection(lines, diff, \"added\", maxScenarios);\n addSection(lines, diff, \"removed\", maxScenarios);\n addSection(lines, diff, \"changed\", maxScenarios);\n\n return lines.join(\"\\n\").trimEnd();\n}\n","import type { TestRunResult } from \"../types/test-result\";\n\nexport interface BaselineCandidate {\n file: string;\n run: TestRunResult;\n}\n\nfunction getCommit(run: TestRunResult): string | undefined {\n return run.ci?.commitSha ?? run.gitSha;\n}\n\nfunction getBranch(run: TestRunResult): string | undefined {\n return run.ci?.branch;\n}\n\nexport function pickAutoBaseline(\n currentRun: TestRunResult,\n candidates: BaselineCandidate[]\n): BaselineCandidate | undefined {\n const currentBranch = getBranch(currentRun);\n const currentCommit = getCommit(currentRun);\n\n return [...candidates].sort((a, b) => {\n const aSameBranch =\n Boolean(currentBranch && getBranch(a.run) && currentBranch === getBranch(a.run));\n const bSameBranch =\n Boolean(currentBranch && getBranch(b.run) && currentBranch === getBranch(b.run));\n if (aSameBranch !== bSameBranch) {\n return Number(bSameBranch) - Number(aSameBranch);\n }\n\n const aDifferentCommit =\n Boolean(currentCommit && getCommit(a.run) && currentCommit !== getCommit(a.run));\n const bDifferentCommit =\n Boolean(currentCommit && getCommit(b.run) && currentCommit !== getCommit(b.run));\n if (aDifferentCommit !== bDifferentCommit) {\n return Number(bDifferentCommit) - Number(aDifferentCommit);\n }\n\n const aOlder = a.run.startedAtMs < currentRun.startedAtMs;\n const bOlder = b.run.startedAtMs < currentRun.startedAtMs;\n if (aOlder !== bOlder) {\n return Number(bOlder) - Number(aOlder);\n }\n\n return b.run.startedAtMs - a.run.startedAtMs;\n })[0];\n}\n","import type { TestCaseResult, TestRunResult } from \"../types/test-result\";\nimport type {\n RunDiffResult,\n ScenarioChangeFlags,\n ScenarioChangeKind,\n ScenarioDiff,\n} from \"../types/compare\";\nimport { toScenarioSnapshot } from \"../types/compare\";\n\nfunction compareStringArrays(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((value, index) => value === b[index]);\n}\n\nfunction stableJson(value: unknown): string {\n return JSON.stringify(value);\n}\n\nfunction isFailedStatus(status: TestCaseResult[\"status\"]): boolean {\n return status === \"failed\";\n}\n\nfunction getPrimaryKind(\n baseline: TestCaseResult,\n current: TestCaseResult,\n hasChanges: boolean\n): ScenarioChangeKind {\n if (isFailedStatus(current.status) && !isFailedStatus(baseline.status)) {\n return \"regressed\";\n }\n if (!isFailedStatus(current.status) && isFailedStatus(baseline.status)) {\n return \"fixed\";\n }\n return hasChanges ? \"changed\" : \"unchanged\";\n}\n\nfunction buildFlags(\n baseline: TestCaseResult,\n current: TestCaseResult\n): ScenarioChangeFlags {\n const baselineDocs = baseline.story.docs ?? [];\n const currentDocs = current.story.docs ?? [];\n\n return {\n status: baseline.status !== current.status,\n steps: stableJson(baseline.story.steps) !== stableJson(current.story.steps),\n docs: stableJson(baselineDocs) !== stableJson(currentDocs),\n tags: !compareStringArrays(baseline.tags, current.tags),\n tickets: stableJson(baseline.story.tickets ?? []) !== stableJson(current.story.tickets ?? []),\n source:\n baseline.sourceFile !== current.sourceFile ||\n baseline.sourceLine !== current.sourceLine,\n duration: baseline.durationMs !== current.durationMs,\n attachments:\n stableJson(baseline.attachments) !== stableJson(current.attachments),\n error:\n (baseline.errorMessage ?? \"\") !== (current.errorMessage ?? \"\"),\n titlePath: !compareStringArrays(baseline.titlePath, current.titlePath),\n };\n}\n\nfunction sortDiffs(scenarios: ScenarioDiff[]): ScenarioDiff[] {\n const rank: Record<ScenarioChangeKind, number> = {\n regressed: 0,\n fixed: 1,\n added: 2,\n removed: 3,\n changed: 4,\n unchanged: 5,\n };\n\n return [...scenarios].sort((a, b) => {\n if (rank[a.kind] !== rank[b.kind]) {\n return rank[a.kind] - rank[b.kind];\n }\n if (a.sourceFile !== b.sourceFile) {\n return a.sourceFile.localeCompare(b.sourceFile);\n }\n if (a.sourceLine !== b.sourceLine) {\n return a.sourceLine - b.sourceLine;\n }\n return a.scenario.localeCompare(b.scenario);\n });\n}\n\nexport function diffRuns(\n baseline: TestRunResult,\n current: TestRunResult\n): RunDiffResult {\n const baselineById = new Map(baseline.testCases.map((tc) => [tc.id, tc]));\n const currentById = new Map(current.testCases.map((tc) => [tc.id, tc]));\n const ids = new Set([...baselineById.keys(), ...currentById.keys()]);\n\n const scenarios: ScenarioDiff[] = [];\n\n for (const id of ids) {\n const before = baselineById.get(id);\n const after = currentById.get(id);\n\n if (!before && after) {\n const flags: ScenarioChangeFlags = {\n status: true,\n steps: true,\n docs: true,\n tags: true,\n tickets: true,\n source: true,\n duration: true,\n attachments: true,\n error: Boolean(after.errorMessage),\n titlePath: true,\n };\n scenarios.push({\n kind: \"added\",\n id,\n scenario: after.story.scenario,\n sourceFile: after.sourceFile,\n sourceLine: after.sourceLine,\n current: toScenarioSnapshot(after),\n flags,\n changedFields: Object.entries(flags)\n .filter(([, changed]) => changed)\n .map(([field]) => field),\n });\n continue;\n }\n\n if (before && !after) {\n const flags: ScenarioChangeFlags = {\n status: true,\n steps: true,\n docs: true,\n tags: true,\n tickets: true,\n source: true,\n duration: true,\n attachments: true,\n error: Boolean(before.errorMessage),\n titlePath: true,\n };\n scenarios.push({\n kind: \"removed\",\n id,\n scenario: before.story.scenario,\n sourceFile: before.sourceFile,\n sourceLine: before.sourceLine,\n baseline: toScenarioSnapshot(before),\n flags,\n changedFields: Object.entries(flags)\n .filter(([, changed]) => changed)\n .map(([field]) => field),\n });\n continue;\n }\n\n if (!before || !after) {\n continue;\n }\n\n const flags = buildFlags(before, after);\n const changedFields = Object.entries(flags)\n .filter(([, changed]) => changed)\n .map(([field]) => field);\n const kind = getPrimaryKind(before, after, changedFields.length > 0);\n\n scenarios.push({\n kind,\n id,\n scenario: after.story.scenario,\n sourceFile: after.sourceFile,\n sourceLine: after.sourceLine,\n baseline: toScenarioSnapshot(before),\n current: toScenarioSnapshot(after),\n flags,\n changedFields,\n durationDeltaMs: after.durationMs - before.durationMs,\n });\n }\n\n const sorted = sortDiffs(scenarios);\n const summary = {\n totalBaseline: baseline.testCases.length,\n totalCurrent: current.testCases.length,\n added: sorted.filter((s) => s.kind === \"added\").length,\n removed: sorted.filter((s) => s.kind === \"removed\").length,\n changed: sorted.filter((s) => s.kind === \"changed\").length,\n regressed: sorted.filter((s) => s.kind === \"regressed\").length,\n fixed: sorted.filter((s) => s.kind === \"fixed\").length,\n unchanged: sorted.filter((s) => s.kind === \"unchanged\").length,\n };\n\n return {\n baseline,\n current,\n summary,\n scenarios: sorted,\n };\n}\n\nexport { createPrCommentSummary } from \"./pr-summary\";\nexport { pickAutoBaseline } from \"./auto-baseline\";\n","import type { RunDiffResult, ScenarioDiff, ScenarioSnapshot } from \"../types/compare\";\nimport type { DocEntry, StoryStep } from \"../types/story\";\nimport type { HtmlTheme } from \"./html/themes/types\";\nimport { resolveTheme } from \"./html/themes/index\";\n\nexport interface RunDiffHtmlOptions {\n title?: string;\n /** Theme name or custom theme object. Default: \"default\" */\n theme?: string | HtmlTheme;\n /** Enable dark mode toggle. Default: true */\n darkMode?: boolean;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\nfunction statusLabel(kind: ScenarioDiff[\"kind\"]): string {\n switch (kind) {\n case \"regressed\":\n return \"Regressed\";\n case \"fixed\":\n return \"Fixed\";\n case \"added\":\n return \"Added\";\n case \"removed\":\n return \"Removed\";\n case \"changed\":\n return \"Changed\";\n default:\n return \"Unchanged\";\n }\n}\n\nfunction formatStep(step: StoryStep): string {\n let content = `<strong>${escapeHtml(step.keyword)}</strong> ${escapeHtml(step.text)}`;\n if (step.mode && step.mode !== \"normal\") {\n content += ` <span class=\"field-pill\">${escapeHtml(step.mode)}</span>`;\n }\n if (step.docs && step.docs.length > 0) {\n content += `<ul class=\"doc-list\">${step.docs.map((d) => `<li>${formatDocEntry(d)}</li>`).join(\"\")}</ul>`;\n }\n return `<li>${content}</li>`;\n}\n\nfunction formatSteps(steps: StoryStep[]): string {\n if (steps.length === 0) return \"&nbsp;\";\n return `<ul class=\"step-list\">${steps.map(formatStep).join(\"\")}</ul>`;\n}\n\nfunction formatDocEntry(doc: DocEntry): string {\n switch (doc.kind) {\n case \"note\":\n return escapeHtml(doc.text);\n case \"tag\":\n return escapeHtml(doc.names.join(\", \"));\n case \"kv\":\n return `${escapeHtml(doc.label)}: ${escapeHtml(typeof doc.value === \"object\" && doc.value !== null ? JSON.stringify(doc.value) : String(doc.value))}`;\n case \"code\":\n return `${escapeHtml(doc.label)}${doc.lang ? ` (${escapeHtml(doc.lang)})` : \"\"}: <code>${escapeHtml(doc.content)}</code>`;\n case \"table\": {\n const header = `<tr>${doc.columns.map((c) => `<th>${escapeHtml(c)}</th>`).join(\"\")}</tr>`;\n const rows = doc.rows.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join(\"\")}</tr>`).join(\"\");\n return `${escapeHtml(doc.label)}<table>${header}${rows}</table>`;\n }\n case \"link\":\n return `${escapeHtml(doc.label)}: ${escapeHtml(doc.url)}`;\n case \"section\":\n return `${escapeHtml(doc.title)}: ${escapeHtml(doc.markdown)}`;\n case \"mermaid\":\n return `${escapeHtml(doc.title ?? \"mermaid diagram\")}: <code>${escapeHtml(doc.code)}</code>`;\n case \"screenshot\":\n return `${doc.alt ? `${escapeHtml(doc.alt)}: ` : \"\"}${escapeHtml(doc.path)}`;\n case \"custom\":\n return `${escapeHtml(doc.type)}: ${escapeHtml(JSON.stringify(doc.data))}`;\n }\n}\n\nfunction formatDocs(docs: DocEntry[]): string {\n if (docs.length === 0) return \"&nbsp;\";\n return `<ul class=\"doc-list\">${docs.map((d) => `<li>${formatDocEntry(d)}</li>`).join(\"\")}</ul>`;\n}\n\nfunction renderScenarioCard(scenario: ScenarioDiff): string {\n const before = scenario.baseline;\n const after = scenario.current;\n const durationDelta = scenario.durationDeltaMs\n ? `${scenario.durationDeltaMs > 0 ? \"+\" : \"\"}${scenario.durationDeltaMs}ms`\n : \"\";\n\n return `\n <article class=\"scenario-card\" data-kind=\"${scenario.kind}\" data-search=\"${escapeHtml(\n `${scenario.scenario} ${scenario.sourceFile} ${scenario.changedFields.join(\" \")}`\n ).toLowerCase()}\">\n <header class=\"scenario-header\">\n <div>\n <span class=\"kind-badge kind-${scenario.kind}\">${statusLabel(scenario.kind)}</span>\n <h3>${escapeHtml(scenario.scenario)}</h3>\n <p class=\"source\">${escapeHtml(`${scenario.sourceFile}:${scenario.sourceLine}`)}</p>\n </div>\n <div class=\"meta\">\n ${\n before && after\n ? `<div class=\"status-pair\"><span>${escapeHtml(before.status)}</span><span>&rarr;</span><span>${escapeHtml(after.status)}</span></div>`\n : before\n ? `<div class=\"status-pair\"><span>${escapeHtml(before.status)}</span><span>&rarr;</span><span>removed</span></div>`\n : `<div class=\"status-pair\"><span>new</span><span>&rarr;</span><span>${escapeHtml(after?.status ?? \"\")}</span></div>`\n }\n ${durationDelta ? `<div class=\"duration-delta\">${escapeHtml(durationDelta)}</div>` : \"\"}\n </div>\n </header>\n ${\n scenario.changedFields.length > 0\n ? `<div class=\"field-list\">${scenario.changedFields\n .map((field) => `<span class=\"field-pill\">${escapeHtml(field)}</span>`)\n .join(\"\")}</div>`\n : \"\"\n }\n ${\n before && after\n ? `<div class=\"comparison-grid\">\n <section>\n <h4>Baseline</h4>\n <dl>\n <dt>Tags</dt>\n <dd>${escapeHtml(before.tags.join(\", \")) || \"&nbsp;\"}</dd>\n <dt>Suite</dt>\n <dd>${escapeHtml(before.titlePath.join(\" > \")) || \"&nbsp;\"}</dd>\n <dt>Error</dt>\n <dd>${escapeHtml(before.errorMessage ?? \"\") || \"&nbsp;\"}</dd>\n ${scenario.flags.steps ? `<dt>Steps</dt><dd>${formatSteps(before.steps)}</dd>` : \"\"}\n ${scenario.flags.docs ? `<dt>Docs</dt><dd>${formatDocs(before.docs)}</dd>` : \"\"}\n ${scenario.flags.tickets ? `<dt>Tickets</dt><dd>${escapeHtml(before.tickets.map(t => t.id).join(\", \")) || \"&nbsp;\"}</dd>` : \"\"}\n </dl>\n </section>\n <section>\n <h4>Current</h4>\n <dl>\n <dt>Tags</dt>\n <dd>${escapeHtml(after.tags.join(\", \")) || \"&nbsp;\"}</dd>\n <dt>Suite</dt>\n <dd>${escapeHtml(after.titlePath.join(\" > \")) || \"&nbsp;\"}</dd>\n <dt>Error</dt>\n <dd>${escapeHtml(after.errorMessage ?? \"\") || \"&nbsp;\"}</dd>\n ${scenario.flags.steps ? `<dt>Steps</dt><dd>${formatSteps(after.steps)}</dd>` : \"\"}\n ${scenario.flags.docs ? `<dt>Docs</dt><dd>${formatDocs(after.docs)}</dd>` : \"\"}\n ${scenario.flags.tickets ? `<dt>Tickets</dt><dd>${escapeHtml(after.tickets.map(t => t.id).join(\", \")) || \"&nbsp;\"}</dd>` : \"\"}\n </dl>\n </section>\n </div>`\n : (() => {\n const snapshot = after ?? before;\n if (!snapshot) return \"\";\n const hasTags = snapshot.tags.length > 0;\n const hasTickets = snapshot.tickets.length > 0;\n const hasSteps = snapshot.steps.length > 0;\n const hasDocs = snapshot.docs.length > 0;\n if (!hasTags && !hasTickets && !hasSteps && !hasDocs) return \"\";\n return `<div class=\"snapshot-detail\">\n <dl>\n ${hasTags ? `<dt>Tags</dt><dd>${escapeHtml(snapshot.tags.join(\", \"))}</dd>` : \"\"}\n ${hasTickets ? `<dt>Tickets</dt><dd>${escapeHtml(snapshot.tickets.map(t => t.id).join(\", \"))}</dd>` : \"\"}\n ${hasSteps ? `<dt>Steps</dt><dd>${formatSteps(snapshot.steps)}</dd>` : \"\"}\n ${hasDocs ? `<dt>Docs</dt><dd>${formatDocs(snapshot.docs)}</dd>` : \"\"}\n </dl>\n </div>`;\n })()\n }\n </article>\n `;\n}\n\n/** Diff-specific CSS that references theme custom properties */\nconst DIFF_CSS = `\n /* Diff layout — uses theme custom properties */\n * { box-sizing: border-box; }\n body {\n margin: 0;\n font-family: var(--font-sans, Georgia, \"Iowan Old Style\", serif);\n background: var(--background);\n color: var(--foreground);\n }\n main { max-width: 1200px; margin: 0 auto; padding: 32px 20px 80px; }\n .diff-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n }\n .diff-header-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n }\n .theme-toggle {\n background: var(--secondary);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 8px 12px;\n cursor: pointer;\n font-size: 1.1rem;\n color: var(--foreground);\n }\n .theme-toggle:hover { background: var(--accent); }\n .hero { display: grid; gap: 16px; margin-bottom: 24px; }\n .hero-card, .summary-card, .toolbar, .scenario-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius, 18px);\n box-shadow: 0 1px 3px color-mix(in srgb, var(--foreground) 6%, transparent);\n }\n .hero-card { padding: 24px; }\n h1, h2, h3, h4, p { margin: 0; }\n .subtle { color: var(--muted-foreground); margin-top: 8px; }\n .summary-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 12px;\n margin: 20px 0 24px;\n }\n .priority-banner {\n padding: 18px 20px;\n margin-bottom: 20px;\n background: linear-gradient(135deg, color-mix(in srgb, var(--destructive) 9%, transparent), var(--card));\n }\n .summary-card { padding: 16px; }\n .summary-card strong { display: block; font-size: 1.8rem; }\n .toolbar {\n position: sticky;\n top: 12px;\n z-index: 2;\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n padding: 14px;\n margin-bottom: 20px;\n }\n .toolbar input {\n flex: 1 1 260px;\n border: 1px solid var(--border);\n border-radius: 999px;\n padding: 10px 14px;\n font: inherit;\n background: var(--background);\n color: var(--foreground);\n }\n .toolbar button {\n border: 1px solid var(--border);\n background: var(--secondary);\n border-radius: 999px;\n padding: 10px 14px;\n font: inherit;\n cursor: pointer;\n color: var(--foreground);\n }\n .toolbar button.active { background: var(--foreground); color: var(--background); }\n .scenario-list { display: grid; gap: 14px; }\n .scenario-card { padding: 18px; }\n .scenario-header {\n display: flex;\n justify-content: space-between;\n gap: 16px;\n align-items: flex-start;\n }\n .kind-badge, .field-pill {\n display: inline-flex;\n align-items: center;\n padding: 4px 10px;\n border-radius: 999px;\n font-size: 0.85rem;\n margin-right: 8px;\n margin-bottom: 8px;\n background: var(--secondary);\n }\n .kind-regressed { background: color-mix(in srgb, var(--destructive) 15%, transparent); color: var(--destructive); }\n .kind-fixed { background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }\n .kind-added { background: color-mix(in srgb, var(--primary) 15%, transparent); color: var(--primary); }\n .kind-removed { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning); }\n .kind-changed { background: color-mix(in srgb, var(--ring, var(--primary)) 15%, transparent); color: var(--ring, var(--primary)); }\n .source, .meta, dd { color: var(--muted-foreground); }\n .status-pair { display: flex; gap: 8px; justify-content: flex-end; font-family: var(--font-mono, ui-monospace, monospace); }\n .duration-delta { margin-top: 8px; text-align: right; color: var(--muted-foreground); }\n .field-list { margin-top: 10px; }\n .comparison-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n gap: 12px;\n margin-top: 16px;\n }\n .comparison-grid section {\n background: color-mix(in srgb, var(--card) 60%, var(--background));\n border: 1px solid var(--border);\n border-radius: var(--radius, 14px);\n padding: 12px;\n }\n dl {\n margin: 10px 0 0;\n display: grid;\n grid-template-columns: minmax(70px, 90px) 1fr;\n gap: 8px 12px;\n }\n @media (max-width: 720px) {\n .scenario-header { flex-direction: column; }\n .status-pair, .duration-delta { text-align: left; justify-content: flex-start; }\n }\n`;\n\n/** Theme toggle JavaScript */\nconst JS_THEME_TOGGLE = `\nfunction getSystemTheme() {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\nfunction getEffectiveTheme() {\n var saved = localStorage.getItem('diff-theme');\n if (saved === 'dark' || saved === 'light') return saved;\n return getSystemTheme();\n}\nfunction toggleTheme() {\n var current = getEffectiveTheme();\n var next = current === 'dark' ? 'light' : 'dark';\n localStorage.setItem('diff-theme', next);\n applyTheme(next);\n}\nfunction applyTheme(theme) {\n document.documentElement.setAttribute('data-theme', theme);\n var btn = document.querySelector('.theme-toggle');\n if (btn) {\n btn.textContent = theme === 'dark' ? '\\\\u2600\\\\ufe0f' : '\\\\ud83c\\\\udf19';\n btn.setAttribute('aria-label', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');\n }\n}\n`;\n\nexport class RunDiffHtmlFormatter {\n private title: string;\n private theme: HtmlTheme;\n private darkMode: boolean;\n\n constructor(options: RunDiffHtmlOptions = {}) {\n this.title = options.title ?? \"Run Comparison\";\n this.theme = resolveTheme(options.theme ?? \"default\");\n this.darkMode = options.darkMode ?? true;\n }\n\n format(diff: RunDiffResult): string {\n const defaultFilter: \"all\" | \"regressed\" | \"fixed\" | \"added\" | \"removed\" | \"changed\" =\n diff.summary.regressed > 0\n ? \"regressed\"\n : diff.summary.fixed > 0\n ? \"fixed\"\n : \"all\";\n const scenarios = diff.scenarios\n .filter((scenario) => scenario.kind !== \"unchanged\")\n .map((scenario) => renderScenarioCard(scenario))\n .join(\"\\n\");\n\n const themeToggleHtml = this.darkMode\n ? `<div class=\"diff-header-actions\"><button type=\"button\" class=\"theme-toggle\" onclick=\"toggleTheme()\" aria-label=\"Toggle theme\"></button></div>`\n : \"\";\n\n const themeInitJs = this.darkMode\n ? `${JS_THEME_TOGGLE}\\napplyTheme(getEffectiveTheme());\\nwindow.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() { if (!localStorage.getItem('diff-theme')) applyTheme(getSystemTheme()); });`\n : \"\";\n\n const themeAttr = this.darkMode ? ' data-theme=\"light\"' : '';\n\n return `<!doctype html>\n<html lang=\"en\"${themeAttr}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(this.title)}</title>\n <style>\n ${this.theme.css}\n ${DIFF_CSS}\n </style>\n </head>\n <body>\n <main>\n <section class=\"hero\">\n <div class=\"hero-card\">\n <div class=\"diff-header\">\n <h1>${escapeHtml(this.title)}</h1>\n ${themeToggleHtml}\n </div>\n <p class=\"subtle\">Baseline ${escapeHtml(new Date(diff.baseline.startedAtMs).toISOString())} against current ${escapeHtml(new Date(diff.current.startedAtMs).toISOString())}</p>\n </div>\n </section>\n <section class=\"summary-grid\">\n <div class=\"summary-card\"><strong>${diff.summary.regressed}</strong><span>Regressed</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.fixed}</strong><span>Fixed</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.added}</strong><span>Added</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.removed}</strong><span>Removed</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.changed}</strong><span>Changed</span></div>\n <div class=\"summary-card\"><strong>${diff.summary.unchanged}</strong><span>Unchanged</span></div>\n </section>\n <section class=\"hero-card priority-banner\">\n <h2>Priority Review</h2>\n <p class=\"subtle\">${\n diff.summary.regressed > 0\n ? `${diff.summary.regressed} regression(s) detected. The view is pre-filtered to regressions.`\n : diff.summary.fixed > 0\n ? `No regressions detected. The view is pre-filtered to fixed scenarios.`\n : \"No regressions or fixes detected. Review neutral changes as needed.\"\n }</p>\n </section>\n <section class=\"toolbar\">\n <input type=\"search\" placeholder=\"Filter by scenario, file, or changed field\" aria-label=\"Filter scenarios\" />\n <button type=\"button\" class=\"${defaultFilter === \"all\" ? \"active\" : \"\"}\" data-filter=\"all\">All</button>\n <button type=\"button\" class=\"${defaultFilter === \"regressed\" ? \"active\" : \"\"}\" data-filter=\"regressed\">Regressed</button>\n <button type=\"button\" class=\"${defaultFilter === \"fixed\" ? \"active\" : \"\"}\" data-filter=\"fixed\">Fixed</button>\n <button type=\"button\" data-filter=\"added\">Added</button>\n <button type=\"button\" data-filter=\"removed\">Removed</button>\n <button type=\"button\" data-filter=\"changed\">Changed</button>\n </section>\n <section class=\"scenario-list\">${scenarios || \"<div class=\\\"hero-card\\\"><p>No scenario changes detected.</p></div>\"}</section>\n </main>\n <script>\n ${themeInitJs}\n const input = document.querySelector('input[type=\"search\"]');\n const buttons = Array.from(document.querySelectorAll('[data-filter]'));\n const cards = Array.from(document.querySelectorAll('.scenario-card'));\n let activeFilter = '${defaultFilter}';\n function applyFilters() {\n const query = (input.value || '').trim().toLowerCase();\n cards.forEach((card) => {\n const kind = card.getAttribute('data-kind');\n const haystack = card.getAttribute('data-search') || '';\n const matchesFilter = activeFilter === 'all' || kind === activeFilter;\n const matchesSearch = !query || haystack.includes(query);\n card.style.display = matchesFilter && matchesSearch ? '' : 'none';\n });\n }\n input.addEventListener('input', applyFilters);\n buttons.forEach((button) => {\n button.addEventListener('click', () => {\n activeFilter = button.getAttribute('data-filter');\n buttons.forEach((candidate) => candidate.classList.toggle('active', candidate === button));\n applyFilters();\n });\n });\n applyFilters();\n </script>\n </body>\n</html>`;\n }\n}\n","import type { RunDiffResult, ScenarioDiff, ScenarioSnapshot } from \"../types/compare\";\nimport type { DocEntry, StoryStep } from \"../types/story\";\n\nexport interface RunDiffMarkdownOptions {\n title?: string;\n}\n\nfunction formatStatus(kind: ScenarioDiff[\"kind\"]): string {\n switch (kind) {\n case \"regressed\":\n return \"Regressed\";\n case \"fixed\":\n return \"Fixed\";\n case \"added\":\n return \"Added\";\n case \"removed\":\n return \"Removed\";\n case \"changed\":\n return \"Changed\";\n default:\n return \"Unchanged\";\n }\n}\n\nfunction formatDurationDelta(deltaMs?: number): string | null {\n if (deltaMs === undefined || deltaMs === 0) return null;\n return `${deltaMs > 0 ? \"+\" : \"\"}${deltaMs}ms`;\n}\n\nfunction renderScenario(lines: string[], scenario: ScenarioDiff): void {\n const before = scenario.baseline;\n const after = scenario.current;\n\n lines.push(`## ${formatStatus(scenario.kind)}: ${scenario.scenario}`);\n lines.push(\"\");\n lines.push(`- File: \\`${scenario.sourceFile}:${scenario.sourceLine}\\``);\n if (before && after) {\n lines.push(`- Status: \\`${before.status}\\` -> \\`${after.status}\\``);\n } else if (after) {\n lines.push(`- Status: new \\`${after.status}\\``);\n } else if (before) {\n lines.push(`- Status: removed \\`${before.status}\\``);\n }\n if (scenario.changedFields.length > 0) {\n lines.push(`- Changed: ${scenario.changedFields.map((field) => `\\`${field}\\``).join(\", \")}`);\n }\n const durationDelta = formatDurationDelta(scenario.durationDeltaMs);\n if (durationDelta) {\n lines.push(`- Duration delta: ${durationDelta}`);\n }\n lines.push(\"\");\n\n if (before && after) {\n lines.push(\"| Field | Baseline | Current |\");\n lines.push(\"| --- | --- | --- |\");\n lines.push(`| Scenario | ${escapeCell(before.scenario)} | ${escapeCell(after.scenario)} |`);\n lines.push(`| Tags | ${escapeCell(before.tags.join(\", \"))} | ${escapeCell(after.tags.join(\", \"))} |`);\n lines.push(`| Suite | ${escapeCell(before.titlePath.join(\" > \"))} | ${escapeCell(after.titlePath.join(\" > \"))} |`);\n lines.push(`| Error | ${escapeCell(before.errorMessage ?? \"\")} | ${escapeCell(after.errorMessage ?? \"\")} |`);\n if (scenario.flags.steps) {\n lines.push(`| Steps | ${escapeCell(formatSteps(before.steps))} | ${escapeCell(formatSteps(after.steps))} |`);\n }\n if (scenario.flags.docs) {\n lines.push(`| Docs | ${escapeCell(formatDocs(before.docs))} | ${escapeCell(formatDocs(after.docs))} |`);\n }\n if (scenario.flags.tickets) {\n lines.push(`| Tickets | ${escapeCell(before.tickets.map(t => t.id).join(\", \"))} | ${escapeCell(after.tickets.map(t => t.id).join(\", \"))} |`);\n }\n lines.push(\"\");\n } else {\n const snapshot = after ?? before;\n if (snapshot) {\n renderSnapshotDetail(lines, snapshot);\n }\n }\n}\n\nfunction renderSnapshotDetail(lines: string[], snapshot: ScenarioSnapshot): void {\n if (snapshot.tags.length > 0) {\n lines.push(`**Tags:** ${snapshot.tags.join(\", \")}`);\n lines.push(\"\");\n }\n if (snapshot.tickets.length > 0) {\n lines.push(`**Tickets:** ${snapshot.tickets.map(t => t.id).join(\", \")}`);\n lines.push(\"\");\n }\n if (snapshot.steps.length > 0) {\n lines.push(\"**Steps:**\");\n lines.push(\"\");\n for (const step of snapshot.steps) {\n lines.push(`- ${formatStep(step)}`);\n }\n lines.push(\"\");\n }\n if (snapshot.docs.length > 0) {\n lines.push(\"**Docs:**\");\n lines.push(\"\");\n for (const doc of snapshot.docs) {\n lines.push(`- ${formatDocEntry(doc)}`);\n }\n lines.push(\"\");\n }\n}\n\nfunction escapeCell(value: string): string {\n return value.replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"<br>\");\n}\n\nfunction formatStep(step: StoryStep): string {\n let result = `**${step.keyword}** ${step.text}`;\n if (step.mode && step.mode !== \"normal\") {\n result += ` [${step.mode}]`;\n }\n if (step.docs && step.docs.length > 0) {\n result += ` (${step.docs.map(formatDocEntry).join(\"; \")})`;\n }\n return result;\n}\n\nfunction formatSteps(steps: StoryStep[]): string {\n if (steps.length === 0) return \"(none)\";\n return steps.map(formatStep).join(\"; \");\n}\n\nfunction formatDocEntry(doc: DocEntry): string {\n switch (doc.kind) {\n case \"note\":\n return doc.text;\n case \"tag\":\n return doc.names.join(\", \");\n case \"kv\":\n return `${doc.label}: ${typeof doc.value === \"object\" && doc.value !== null ? JSON.stringify(doc.value) : String(doc.value)}`;\n case \"code\":\n return `${doc.label}${doc.lang ? ` (${doc.lang})` : \"\"}: \\`${doc.content}\\``;\n case \"table\":\n return `${doc.label}: [${doc.columns.join(\", \")}] ${doc.rows.map((row) => row.join(\", \")).join(\"; \")}`;\n case \"link\":\n return `${doc.label}: ${doc.url}`;\n case \"section\":\n return `${doc.title}: ${doc.markdown}`;\n case \"mermaid\":\n return `${doc.title ?? \"mermaid diagram\"}: \\`${doc.code}\\``;\n case \"screenshot\":\n return `${doc.alt ? `${doc.alt}: ` : \"\"}${doc.path}`;\n case \"custom\":\n return `${doc.type}: ${JSON.stringify(doc.data)}`;\n }\n}\n\nfunction formatDocs(docs: DocEntry[]): string {\n if (docs.length === 0) return \"(none)\";\n return docs.map(formatDocEntry).join(\"; \");\n}\n\nexport class RunDiffMarkdownFormatter {\n private title: string;\n\n constructor(options: RunDiffMarkdownOptions = {}) {\n this.title = options.title ?? \"Run Comparison\";\n }\n\n format(diff: RunDiffResult): string {\n const lines: string[] = [];\n\n lines.push(`# ${this.title}`);\n lines.push(\"\");\n lines.push(`Baseline: \\`${new Date(diff.baseline.startedAtMs).toISOString()}\\``);\n lines.push(`Current: \\`${new Date(diff.current.startedAtMs).toISOString()}\\``);\n lines.push(\"\");\n lines.push(\"## Review Priority\");\n lines.push(\"\");\n if (diff.summary.regressed > 0) {\n lines.push(`Review regressions first: ${diff.summary.regressed} scenario(s) got worse.`);\n } else if (diff.summary.fixed > 0) {\n lines.push(`No regressions detected. Review ${diff.summary.fixed} fixed scenario(s) next.`);\n } else {\n lines.push(\"No regressions or fixes detected. Remaining changes are neutral.\");\n }\n lines.push(\"\");\n lines.push(\"| Added | Removed | Regressed | Fixed | Changed | Unchanged |\");\n lines.push(\"| ---: | ---: | ---: | ---: | ---: | ---: |\");\n lines.push(\n `| ${diff.summary.added} | ${diff.summary.removed} | ${diff.summary.regressed} | ${diff.summary.fixed} | ${diff.summary.changed} | ${diff.summary.unchanged} |`\n );\n lines.push(\"\");\n\n for (const kind of [\"regressed\", \"fixed\", \"added\", \"removed\", \"changed\"] as const) {\n const scenarios = diff.scenarios.filter((scenario) => scenario.kind === kind);\n if (scenarios.length === 0) continue;\n lines.push(`## ${formatStatus(kind)} (${scenarios.length})`);\n lines.push(\"\");\n for (const scenario of scenarios) {\n renderScenario(lines, scenario);\n }\n }\n\n return lines.join(\"\\n\").trimEnd();\n }\n}\n","import type { SortTestCasesMode, Logger } from \"./types/options\";\nimport type { TestCaseResult } from \"./types/test-result\";\n\nexport interface SelectTestCasesArgs {\n testCases: TestCaseResult[];\n include?: string[];\n exclude?: string[];\n includeTags?: string[];\n excludeTags?: string[];\n sortTestCases?: SortTestCasesMode;\n}\n\nexport interface SelectTestCasesDeps {\n logger: Logger;\n}\n\nexport function matchesPattern(pattern: string, sourceFile: string): boolean {\n const normalizedPattern = pattern.replace(/\\\\/g, \"/\");\n const normalizedFile = sourceFile.replace(/\\\\/g, \"/\");\n\n const regexStr = normalizedPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*/g, \"{{GLOBSTAR}}\")\n .replace(/\\*/g, \"[^/]*\")\n .replace(/{{GLOBSTAR}}/g, \".*\");\n\n const regex = new RegExp(`^${regexStr}$`);\n return regex.test(normalizedFile);\n}\n\nfunction filterTestCasesByGlobs(\n testCases: TestCaseResult[],\n include: string[],\n exclude: string[],\n logger: Logger\n): TestCaseResult[] {\n if (include.length === 0 && exclude.length === 0) return testCases;\n\n const filtered: TestCaseResult[] = [];\n for (const tc of testCases) {\n const sourceFile = tc.sourceFile.replace(/\\\\/g, \"/\");\n\n if (include.length > 0) {\n const included = include.some((pattern) => matchesPattern(pattern, sourceFile));\n if (!included) continue;\n }\n\n if (exclude.length > 0) {\n const excluded = exclude.some((pattern) => matchesPattern(pattern, sourceFile));\n if (excluded) continue;\n }\n\n filtered.push(tc);\n }\n\n const dropped = testCases.length - filtered.length;\n if (dropped > 0) {\n logger.warn(\n `Filtered ${dropped} test case(s) by include/exclude globs (${filtered.length} included)`\n );\n }\n\n return filtered;\n}\n\nfunction filterTestCasesByTags(\n testCases: TestCaseResult[],\n includeTags: string[],\n excludeTags: string[],\n logger: Logger\n): TestCaseResult[] {\n if (includeTags.length === 0 && excludeTags.length === 0) return testCases;\n\n const filtered: TestCaseResult[] = [];\n for (const tc of testCases) {\n if (includeTags.length > 0) {\n const included = tc.tags.some((tag) => includeTags.includes(tag));\n if (!included) continue;\n }\n\n if (excludeTags.length > 0) {\n const excluded = tc.tags.some((tag) => excludeTags.includes(tag));\n if (excluded) continue;\n }\n\n filtered.push(tc);\n }\n\n const dropped = testCases.length - filtered.length;\n if (dropped > 0) {\n logger.warn(\n `Filtered ${dropped} test case(s) by include/exclude tags (${filtered.length} included)`\n );\n }\n\n return filtered;\n}\n\nfunction sortTestCases(\n testCases: TestCaseResult[],\n sortMode: SortTestCasesMode\n): TestCaseResult[] {\n if (sortMode === \"none\") return testCases;\n\n return [...testCases].sort((a, b) => {\n if (sortMode === \"id\") {\n return a.id.localeCompare(b.id);\n }\n\n if (a.sourceFile !== b.sourceFile) {\n return a.sourceFile.localeCompare(b.sourceFile);\n }\n if (a.sourceLine !== b.sourceLine) {\n return a.sourceLine - b.sourceLine;\n }\n if (a.story.scenario !== b.story.scenario) {\n return a.story.scenario.localeCompare(b.story.scenario);\n }\n return a.id.localeCompare(b.id);\n });\n}\n\nexport function selectTestCases(\n args: SelectTestCasesArgs,\n deps: SelectTestCasesDeps\n): TestCaseResult[] {\n const include = args.include ?? [];\n const exclude = args.exclude ?? [];\n const includeTags = args.includeTags ?? [];\n const excludeTags = args.excludeTags ?? [];\n const sortMode = args.sortTestCases ?? \"none\";\n\n let selected = filterTestCasesByGlobs(\n args.testCases,\n include,\n exclude,\n deps.logger\n );\n\n selected = filterTestCasesByTags(\n selected,\n includeTags,\n excludeTags,\n deps.logger\n );\n\n return sortTestCases(selected, sortMode);\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { scanHtmlAssets } from \"./scan-html-assets\";\nimport { copyAsset } from \"./copy-asset\";\n\nexport interface BundleOptions {\n /** If true, warn about missing assets instead of throwing. Default: false. */\n allowMissing?: boolean;\n}\n\nexport interface BundleResult {\n /** Number of assets successfully copied */\n copiedCount: number;\n /** Number of missing assets */\n missingCount: number;\n /** Paths of missing assets (original references) */\n missing: string[];\n}\n\n/**\n * Post-process an HTML report file: copy referenced local assets into\n * an `assets/` directory beside it and rewrite paths in the HTML.\n */\nexport function bundleAssets(\n htmlPath: string,\n options: BundleOptions = {},\n): BundleResult {\n const htmlDir = path.dirname(htmlPath);\n const assetsDir = path.join(htmlDir, \"assets\");\n\n let html = fs.readFileSync(htmlPath, \"utf8\");\n const refs = scanHtmlAssets(html);\n\n let copiedCount = 0;\n const missing: string[] = [];\n\n for (const ref of refs) {\n const absolutePath = path.resolve(htmlDir, ref);\n\n if (!fs.existsSync(absolutePath)) {\n missing.push(ref);\n continue;\n }\n\n const newRelPath = copyAsset(absolutePath, assetsDir);\n html = replaceAssetRef(html, ref, newRelPath);\n copiedCount++;\n }\n\n if (missing.length > 0 && !options.allowMissing) {\n throw new Error(\n `Missing asset${missing.length > 1 ? \"s\" : \"\"}: ${missing.join(\", \")}`,\n );\n }\n\n fs.writeFileSync(htmlPath, html, \"utf8\");\n\n return {\n copiedCount,\n missingCount: missing.length,\n missing,\n };\n}\n\n/**\n * Replace an asset reference only in bundleable contexts:\n * - src=\"...\" on <img>/<video> elements\n * - href=\"...\" on <a class=\"attachment\"> elements\n *\n * Ordinary doc links sharing the same path are left untouched.\n */\nfunction replaceAssetRef(html: string, original: string, replacement: string): string {\n const escaped = original.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n // Rewrite src= on <img> and <video>\n const srcPattern = new RegExp(\n `(<(?:img|video)\\\\b[^>]*?\\\\bsrc=[\"'])${escaped}([\"'])`,\n \"g\",\n );\n html = html.replace(srcPattern, `$1${replacement}$2`);\n\n // Rewrite href= on <a class=\"attachment\"> (class before href)\n const hrefClassFirst = new RegExp(\n `(<a\\\\b[^>]*?\\\\bclass=[\"']attachment[\"'][^>]*?\\\\bhref=[\"'])${escaped}([\"'])`,\n \"g\",\n );\n html = html.replace(hrefClassFirst, `$1${replacement}$2`);\n\n // Rewrite href= on <a class=\"attachment\"> (href before class)\n const hrefHrefFirst = new RegExp(\n `(<a\\\\b[^>]*?\\\\bhref=[\"'])${escaped}([\"'][^>]*?\\\\bclass=[\"']attachment[\"'])`,\n \"g\",\n );\n html = html.replace(hrefHrefFirst, `$1${replacement}$2`);\n\n return html;\n}\n","/**\n * Scan generated HTML for local asset references.\n *\n * Targets: src=\"...\" on img/video, href=\"...\" on a.attachment.\n * Skips: data: URIs, http/https URLs, empty strings, fragment-only refs.\n * Returns deduplicated list of local path strings.\n */\nexport function scanHtmlAssets(html: string): string[] {\n const seen = new Set<string>();\n\n // Only match src=\"...\" on <img> and <video> elements, and\n // href=\"...\" on <a class=\"attachment\"> elements.\n // This avoids treating ordinary doc links as bundleable assets.\n const patterns: RegExp[] = [\n /<(?:img|video)\\b[^>]*?\\bsrc=[\"']([^\"']+)[\"']/g,\n /<a\\b[^>]*?\\bclass=[\"']attachment[\"'][^>]*?\\bhref=[\"']([^\"']+)[\"']/g,\n /<a\\b[^>]*?\\bhref=[\"']([^\"']+)[\"'][^>]*?\\bclass=[\"']attachment[\"']/g,\n ];\n\n for (const pattern of patterns) {\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(html)) !== null) {\n const ref = match[1];\n if (isLocalAssetRef(ref) && !seen.has(ref)) {\n seen.add(ref);\n }\n }\n }\n\n return [...seen];\n}\n\nfunction isLocalAssetRef(ref: string): boolean {\n if (!ref) return false;\n if (ref.startsWith(\"data:\")) return false;\n if (ref.startsWith(\"http://\") || ref.startsWith(\"https://\")) return false;\n if (ref.startsWith(\"#\")) return false;\n return true;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\n\n/**\n * Copy a source file into assetsDir with a content-hashed filename.\n *\n * Returns the relative path from the HTML file's directory to the copied asset\n * (e.g. \"assets/video-3f2c1a7b.webm\").\n *\n * Idempotent: if the destination already exists, skips the copy.\n */\nexport function copyAsset(sourcePath: string, assetsDir: string): string {\n if (!fs.existsSync(assetsDir)) {\n fs.mkdirSync(assetsDir, { recursive: true });\n }\n\n const content = fs.readFileSync(sourcePath);\n const hash = crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 8);\n\n const ext = path.extname(sourcePath);\n const baseName = sanitize(path.basename(sourcePath, ext));\n const destName = `${baseName}-${hash}${ext}`;\n const destPath = path.join(assetsDir, destName);\n\n if (!fs.existsSync(destPath)) {\n fs.copyFileSync(sourcePath, destPath);\n }\n\n return `assets/${destName}`;\n}\n\n/** Replace non-alphanumeric/hyphen/dot characters with hyphens, collapse runs. */\nfunction sanitize(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9._-]/g, \"-\")\n .replace(/-{2,}/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n","/**\n * Astro/Starlight Formatter - Layer 3.\n *\n * Wraps the MarkdownFormatter to produce Starlight-compatible .md files\n * with proper YAML frontmatter (title, description, sidebar.badge).\n */\n\nimport { MarkdownFormatter } from \"./markdown\";\nimport type { MarkdownOptions } from \"./markdown\";\nimport type { TestRunResult, TestCaseResult } from \"../types/test-result\";\n\nexport interface StarlightBadge {\n text: string;\n variant: \"success\" | \"danger\" | \"caution\" | \"note\" | \"tip\";\n}\n\nexport interface AstroFormatterOptions {\n assetsBaseUrl?: string;\n markdown?: Omit<MarkdownOptions, \"includeFrontMatter\" | \"includeSummaryTable\" | \"includeMetadata\" | \"stepStyle\">;\n}\n\nexport class AstroFormatter {\n private markdownFormatter: MarkdownFormatter;\n private title: string;\n\n constructor(options: AstroFormatterOptions = {}) {\n this.title = options.markdown?.title ?? \"User Stories\";\n this.markdownFormatter = new MarkdownFormatter({\n ...options.markdown,\n title: this.title,\n stepStyle: \"gherkin\",\n includeFrontMatter: false,\n includeSummaryTable: false,\n includeMetadata: false,\n });\n }\n\n format(run: TestRunResult): string {\n const markdown = this.markdownFormatter.format(run);\n // Strip the h1 title — Starlight renders its own from frontmatter\n const body = markdown.replace(/^# .+\\n\\n?/, \"\");\n const frontmatter = this.buildFrontmatter(run);\n return `${frontmatter}\\n${body}`;\n }\n\n private buildFrontmatter(run: TestRunResult): string {\n const badge = AstroFormatter.computeBadge(run.testCases);\n const count = run.testCases.length;\n const description = `${count} scenario${count !== 1 ? \"s\" : \"\"} — ${badge.text.toLowerCase()}`;\n const lines = [\n \"---\",\n `title: ${this.title}`,\n `description: ${description}`,\n \"sidebar:\",\n \" badge:\",\n ` text: ${badge.text}`,\n ` variant: ${badge.variant}`,\n \"---\",\n ];\n return lines.join(\"\\n\");\n }\n\n static computeBadge(testCases: Pick<TestCaseResult, \"status\">[]): StarlightBadge {\n const statuses = new Set(testCases.map((tc) => tc.status));\n if (statuses.has(\"failed\")) return { text: \"Failed\", variant: \"danger\" };\n if (statuses.has(\"pending\")) return { text: \"Pending\", variant: \"caution\" };\n if (statuses.has(\"skipped\") && !statuses.has(\"passed\")) return { text: \"Skipped\", variant: \"caution\" };\n return { text: \"Passed\", variant: \"success\" };\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { copyAsset } from \"../bundler/copy-asset\";\n\nexport interface AstroAssetResult {\n markdown: string;\n copiedCount: number;\n missingCount: number;\n missing: string[];\n}\n\nexport interface CopyMarkdownAssetsOptions {\n markdown: string;\n markdownDir: string;\n assetsDir: string;\n assetsBaseUrl: string;\n allowMissing?: boolean;\n}\n\nconst SKIP_PREFIXES = [\"http://\", \"https://\", \"data:\", \"#\"];\n\nfunction isLocalPath(src: string): boolean {\n const trimmed = src.trim();\n if (SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {\n return false;\n }\n\n // Root-relative, protocol-relative, and absolute filesystem paths are not\n // relative to the markdown file and should not be copied from markdownDir.\n return !path.posix.isAbsolute(trimmed) && !path.win32.isAbsolute(trimmed);\n}\n\n/** Strip fenced code blocks and inline code spans so their contents aren't treated as real references. */\nfunction stripCodeContent(markdown: string): string {\n // Strip fenced code blocks (allow leading whitespace for indented fences in lists/quotes)\n let result = markdown.replace(/^[ \\t]*(`{3,}|~{3,})[^\\n]*\\n[\\s\\S]*?^[ \\t]*\\1\\s*$/gm, \"\");\n // Strip inline code spans — backreference ensures opening and closing delimiters match\n result = result.replace(/(`+)(?:(?!\\1).)+\\1/g, \"\");\n // Strip HTML code/pre blocks so literal snippets are not scanned as assets\n result = result.replace(/<pre\\b[^>]*>[\\s\\S]*?<\\/pre>/gi, \"\");\n result = result.replace(/<code\\b[^>]*>[\\s\\S]*?<\\/code>/gi, \"\");\n return result;\n}\n\n/**\n * Scan markdown for local asset references (images, videos).\n * Returns an array of unique local paths found.\n * Ignores references inside fenced code blocks.\n */\nexport function scanMarkdownAssets(markdown: string): string[] {\n const found = new Set<string>();\n const stripped = stripCodeContent(markdown);\n\n // Markdown image syntax: ![alt](path) or ![alt](path \"title\")\n const mdImageRe = /!\\[[^\\]]*\\]\\(([^)\"'\\s]+)(?:\\s+[\"'][^\"']*[\"'])?\\s*\\)/g;\n let match: RegExpExecArray | null;\n while ((match = mdImageRe.exec(stripped)) !== null) {\n const src = match[1].trim();\n if (isLocalPath(src)) {\n found.add(src);\n }\n }\n\n // HTML tags: <img src=\"...\">, <source src=\"...\">, <video src=\"...\">\n const htmlSrcRe = /<(?:img|source|video)[^>]+\\bsrc=[\"']([^\"']+)[\"'][^>]*>/gi;\n while ((match = htmlSrcRe.exec(stripped)) !== null) {\n const src = match[1].trim();\n if (isLocalPath(src)) {\n found.add(src);\n }\n }\n\n return Array.from(found);\n}\n\n/**\n * Split markdown into alternating [prose, code, prose, code, ...] segments.\n * Fenced code blocks and inline code spans are returned verbatim;\n * only prose segments are rewritten.\n */\nfunction splitByCode(markdown: string): string[] {\n // Match fenced code blocks (with optional indentation), HTML code blocks, or inline code spans.\n const codeRe =\n /^[ \\t]*(`{3,}|~{3,})[^\\n]*\\n[\\s\\S]*?^[ \\t]*\\1\\s*$|<pre\\b[^>]*>[\\s\\S]*?<\\/pre>|<code\\b[^>]*>[\\s\\S]*?<\\/code>|(`+)(?:(?!\\2).)+\\2/gim;\n const segments: string[] = [];\n let lastIndex = 0;\n\n for (const match of markdown.matchAll(codeRe)) {\n if (match.index! > lastIndex) {\n segments.push(markdown.slice(lastIndex, match.index!));\n }\n segments.push(match[0]); // code — preserved as-is\n lastIndex = match.index! + match[0].length;\n }\n if (lastIndex < markdown.length) {\n segments.push(markdown.slice(lastIndex));\n }\n return segments;\n}\n\n/** Returns true if segment is a code block or inline code span. */\nfunction isCode(segment: string): boolean {\n const trimmed = segment.trimStart();\n return trimmed.startsWith(\"`\") || trimmed.startsWith(\"~\") || trimmed.startsWith(\"<pre\") || trimmed.startsWith(\"<code\");\n}\n\n/** Rewrite asset paths in a single prose (non-fenced) segment. */\nfunction rewriteProseSegment(\n prose: string,\n assetsBaseUrl: string,\n pathMap?: Map<string, string>,\n): string {\n let result = prose;\n\n // Rewrite markdown image syntax: ![alt](path) or ![alt](path \"title\")\n result = result.replace(\n /(!\\[[^\\]]*\\]\\()([^)\"'\\s]+)((?:\\s+[\"'][^\"']*[\"'])?\\s*\\))/g,\n (full, pre, src, post) => {\n const trimmed = src.trim();\n if (!isLocalPath(trimmed)) return full;\n if (pathMap) {\n const mapped = pathMap.get(trimmed);\n if (mapped === undefined) return full;\n return `${pre}${assetsBaseUrl}/${mapped}${post}`;\n }\n return `${pre}${assetsBaseUrl}/${trimmed}${post}`;\n },\n );\n\n // Rewrite HTML src attributes in img/source/video tags\n result = result.replace(\n /(<(?:img|source|video)[^>]+\\bsrc=[\"'])([^\"']+)([\"'][^>]*>)/gi,\n (full, pre, src, post) => {\n const trimmed = src.trim();\n if (!isLocalPath(trimmed)) return full;\n if (pathMap) {\n const mapped = pathMap.get(trimmed);\n if (mapped === undefined) return full;\n return `${pre}${assetsBaseUrl}/${mapped}${post}`;\n }\n return `${pre}${assetsBaseUrl}/${trimmed}${post}`;\n },\n );\n\n return result;\n}\n\n/**\n * Rewrite local asset paths in markdown using a path map or a base URL.\n * Paths not present in the pathMap are left unchanged.\n * Content inside fenced code blocks and inline code spans is never rewritten.\n */\nexport function rewriteAssetPaths(\n markdown: string,\n assetsBaseUrl: string,\n pathMap?: Map<string, string>,\n): string {\n return splitByCode(markdown)\n .map((seg) => (isCode(seg) ? seg : rewriteProseSegment(seg, assetsBaseUrl, pathMap)))\n .join(\"\");\n}\n\n/**\n * Full pipeline: scan markdown for local asset refs, copy them to assetsDir\n * with content-hashed names, and rewrite the paths in the markdown.\n */\nexport function copyMarkdownAssets(options: CopyMarkdownAssetsOptions): AstroAssetResult {\n const {\n markdown,\n markdownDir,\n assetsDir,\n assetsBaseUrl,\n allowMissing = false,\n } = options;\n\n const refs = scanMarkdownAssets(markdown);\n const pathMap = new Map<string, string>();\n const missing: string[] = [];\n\n for (const ref of refs) {\n const absPath = path.resolve(markdownDir, ref);\n if (!fs.existsSync(absPath)) {\n if (!allowMissing) {\n throw new Error(`Asset not found: ${absPath}`);\n }\n missing.push(ref);\n continue;\n }\n // copyAsset returns \"assets/<hashed-name>\"\n const relativeCopied = copyAsset(absPath, assetsDir);\n // Strip leading \"assets/\" prefix since assetsBaseUrl already points there\n const fileName = relativeCopied.replace(/^assets\\//, \"\");\n pathMap.set(ref, fileName);\n }\n\n const rewritten = rewriteAssetPaths(markdown, assetsBaseUrl, pathMap);\n\n return {\n markdown: rewritten,\n copiedCount: pathMap.size,\n missingCount: missing.length,\n missing,\n };\n}\n","/**\n * NDJSON-to-TestRunResult parser.\n *\n * Parses Cucumber Messages NDJSON (one JSON envelope per line) back into\n * a TestRunResult suitable for rendering by HtmlFormatter or other formatters.\n *\n * This is the NDJSON compat path: it produces a minimal but sufficient\n * TestRunResult. Fields not present in the NDJSON stream are given\n * sensible defaults.\n */\n\nimport type { StoryMeta, StoryStep, StepKeyword, DocEntry, DocPhase } from \"../types/story\";\nimport type {\n TestRunResult,\n TestCaseResult,\n TestCaseAttempt,\n StepResult,\n TestStatus,\n Attachment,\n} from \"../types/test-result\";\nimport type {\n Envelope,\n Pickle,\n PickleStep,\n GherkinDocument,\n Source,\n TestCase,\n TestCaseStarted,\n TestCaseFinished,\n TestStepFinished,\n TestStepResultStatus,\n CucumberAttachment,\n Timestamp,\n} from \"../types/cucumber-messages\";\n\n// ============================================================================\n// Internal index types\n// ============================================================================\n\ninterface PickleIndex {\n pickle: Pickle;\n uri: string;\n}\n\ninterface StepDefinition {\n keyword: StepKeyword;\n text: string;\n}\n\ninterface TestCaseIndex {\n testCase: TestCase;\n pickleId: string;\n}\n\ninterface TestCaseStartedIndex {\n testCaseStarted: TestCaseStarted;\n testCaseId: string;\n}\n\ninterface TestCaseFinishedIndex {\n testCaseStartedId: string;\n willBeRetried: boolean;\n}\n\ninterface StepResultAccumulator {\n testStepId: string;\n status: TestStatus;\n durationMs: number;\n errorMessage?: string;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Parse an NDJSON string into a TestRunResult.\n *\n * @param ndjson - NDJSON string (one JSON envelope per line)\n * @returns TestRunResult reconstructed from the envelopes\n */\nexport function parseNdjson(ndjson: string): TestRunResult {\n const lines = ndjson.trim().split(\"\\n\").filter(Boolean);\n const envelopes: Envelope[] = lines.map((line) => JSON.parse(line));\n return parseEnvelopes(envelopes);\n}\n\n/**\n * Parse an array of Envelope objects into a TestRunResult.\n *\n * @param envelopes - Array of Cucumber Messages envelopes\n * @returns TestRunResult reconstructed from the envelopes\n */\nexport function parseEnvelopes(envelopes: Envelope[]): TestRunResult {\n // Indexes for cross-referencing\n const sources = new Map<string, Source>(); // uri → Source\n const gherkinDocs = new Map<string, GherkinDocument>(); // uri → GherkinDocument\n const pickles = new Map<string, PickleIndex>(); // pickleId → { pickle, uri }\n const testCases = new Map<string, TestCaseIndex>(); // testCaseId → { testCase, pickleId }\n const testCaseStarteds = new Map<string, TestCaseStartedIndex>(); // testCaseStartedId → { ... }\n const testCaseFinisheds = new Map<string, TestCaseFinishedIndex>(); // testCaseStartedId → { ... }\n const testCaseIdToStartedIds = new Map<string, string[]>(); // testCaseId → testCaseStartedId[]\n const stepResults = new Map<string, StepResultAccumulator[]>(); // testCaseStartedId → step results\n const attachments = new Map<string, CucumberAttachment[]>(); // testCaseStartedId → attachments\n\n let startedAtMs = 0;\n let finishedAtMs = 0;\n let success = true;\n let toolName = \"unknown\";\n let toolVersion = \"0.0.0\";\n\n // Single-pass index building\n for (const envelope of envelopes) {\n if (\"meta\" in envelope) {\n toolName = envelope.meta.implementation.name;\n toolVersion = envelope.meta.implementation.version;\n }\n\n if (\"source\" in envelope) {\n sources.set(envelope.source.uri, envelope.source);\n }\n\n if (\"gherkinDocument\" in envelope) {\n const doc = envelope.gherkinDocument;\n gherkinDocs.set(doc.uri, doc);\n }\n\n if (\"pickle\" in envelope) {\n const p = envelope.pickle;\n pickles.set(p.id, { pickle: p, uri: p.uri });\n }\n\n if (\"testCase\" in envelope) {\n const tc = envelope.testCase;\n testCases.set(tc.id, { testCase: tc, pickleId: tc.pickleId });\n }\n\n if (\"testCaseStarted\" in envelope) {\n const tcs = envelope.testCaseStarted;\n testCaseStarteds.set(tcs.id, {\n testCaseStarted: tcs,\n testCaseId: tcs.testCaseId,\n });\n stepResults.set(tcs.id, []);\n attachments.set(tcs.id, []);\n // Group by testCaseId for retry detection\n const existing = testCaseIdToStartedIds.get(tcs.testCaseId) ?? [];\n existing.push(tcs.id);\n testCaseIdToStartedIds.set(tcs.testCaseId, existing);\n }\n\n if (\"testCaseFinished\" in envelope) {\n const tcf = envelope.testCaseFinished;\n testCaseFinisheds.set(tcf.testCaseStartedId, {\n testCaseStartedId: tcf.testCaseStartedId,\n willBeRetried: tcf.willBeRetried,\n });\n }\n\n if (\"testStepFinished\" in envelope) {\n const tsf = envelope.testStepFinished;\n const results = stepResults.get(tsf.testCaseStartedId);\n if (results) {\n results.push({\n testStepId: tsf.testStepId,\n status: cucumberStatusToTestStatus(tsf.testStepResult.status),\n durationMs: durationToMs(tsf.testStepResult.duration),\n errorMessage: tsf.testStepResult.message,\n });\n }\n }\n\n if (\"attachment\" in envelope) {\n const att = envelope.attachment;\n const list = attachments.get(att.testCaseStartedId);\n if (list) {\n list.push(att);\n }\n }\n\n if (\"testRunStarted\" in envelope) {\n startedAtMs = timestampToMs(envelope.testRunStarted.timestamp);\n }\n\n if (\"testRunFinished\" in envelope) {\n finishedAtMs = timestampToMs(envelope.testRunFinished.timestamp);\n success = envelope.testRunFinished.success;\n }\n }\n\n // Reconstruct TestCaseResults (one per TestCase, grouping retries)\n const testCaseResults: TestCaseResult[] = [];\n\n for (const [testCaseId, tcIndex] of testCases) {\n const pickleIndex = pickles.get(tcIndex.pickleId);\n if (!pickleIndex) continue;\n\n const pickle = pickleIndex.pickle;\n const uri = pickleIndex.uri;\n\n // Get all started IDs for this test case (sorted by attempt number)\n const startedIds = testCaseIdToStartedIds.get(testCaseId) ?? [];\n if (startedIds.length === 0) continue;\n\n // Sort by attempt number\n const sortedStarteds = startedIds\n .map((id) => testCaseStarteds.get(id)!)\n .filter(Boolean)\n .sort((a, b) => a.testCaseStarted.attempt - b.testCaseStarted.attempt);\n\n // The final attempt is the last one (or the one with willBeRetried=false)\n const finalStarted = sortedStarteds[sortedStarteds.length - 1];\n const finalStartedId = finalStarted.testCaseStarted.id;\n\n // Build step-to-result mapping via testStepId\n const testStepIdToIndex = new Map<string, number>();\n for (let i = 0; i < tcIndex.testCase.testSteps.length; i++) {\n testStepIdToIndex.set(tcIndex.testCase.testSteps[i].id, i);\n }\n\n // Reconstruct StorySteps from pickle steps + gherkin doc\n const storySteps = reconstructStorySteps(pickle, uri, gherkinDocs);\n\n // Use final attempt's step results\n const tcStepResults = stepResults.get(finalStartedId) ?? [];\n const tcAttachments = attachments.get(finalStartedId) ?? [];\n\n // Reconstruct StepResults in order\n const orderedStepResults: StepResult[] = storySteps.map((_, i) => ({\n index: i,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n\n for (const sr of tcStepResults) {\n const stepIndex = testStepIdToIndex.get(sr.testStepId);\n if (stepIndex !== undefined && stepIndex < orderedStepResults.length) {\n orderedStepResults[stepIndex] = {\n index: stepIndex,\n status: sr.status,\n durationMs: sr.durationMs,\n errorMessage: sr.errorMessage,\n };\n }\n }\n\n // Derive overall status from step results\n const overallStatus = deriveOverallStatus(orderedStepResults);\n const totalDurationMs = orderedStepResults.reduce(\n (sum, sr) => sum + sr.durationMs,\n 0\n );\n\n // Reconstruct tags\n const tags = pickle.tags.map((t) => t.name.replace(/^@/, \"\"));\n\n // Reconstruct attachments (from final attempt)\n const resolvedAttachments: Attachment[] = tcAttachments.map((att) => ({\n name: att.mediaType,\n mediaType: att.mediaType,\n body: att.body,\n contentEncoding: att.contentEncoding,\n }));\n\n // Extract titlePath from feature name + scenario name\n const featureName = extractFeatureName(uri, gherkinDocs);\n const titlePath = featureName\n ? [featureName, pickle.name]\n : [pickle.name];\n\n // Build StoryMeta\n const story: StoryMeta = {\n scenario: pickle.name,\n steps: storySteps,\n tags: tags.length > 0 ? tags : undefined,\n };\n\n // Build error info\n const failedStep = orderedStepResults.find((sr) => sr.status === \"failed\");\n\n // Build attempts array if there are retries\n let attempts: TestCaseAttempt[] | undefined;\n if (sortedStarteds.length > 1) {\n attempts = sortedStarteds.map((started) => {\n const sId = started.testCaseStarted.id;\n const sStepResults = stepResults.get(sId) ?? [];\n const attemptStatus = deriveOverallStatus(\n buildOrderedStepResults(sStepResults, testStepIdToIndex, storySteps.length)\n );\n const attemptDurationMs = sStepResults.reduce(\n (sum, sr) => sum + sr.durationMs, 0\n );\n const attemptFailedStep = sStepResults.find((sr) => sr.status === \"failed\");\n return {\n attempt: started.testCaseStarted.attempt,\n status: attemptStatus,\n durationMs: attemptDurationMs,\n errorMessage: attemptFailedStep?.errorMessage,\n };\n });\n }\n\n const testCaseResult: TestCaseResult = {\n id: tcIndex.testCase.id,\n story,\n sourceFile: uri,\n sourceLine: 1,\n status: overallStatus,\n durationMs: totalDurationMs,\n errorMessage: failedStep?.errorMessage,\n attachments: resolvedAttachments,\n stepResults: orderedStepResults,\n titlePath,\n retry: finalStarted.testCaseStarted.attempt,\n retries: sortedStarteds.length > 1 ? sortedStarteds.length - 1 : 0,\n tags,\n attempts,\n };\n\n testCaseResults.push(testCaseResult);\n }\n\n const durationMs =\n finishedAtMs > 0 && startedAtMs > 0\n ? finishedAtMs - startedAtMs\n : testCaseResults.reduce((sum, tc) => sum + tc.durationMs, 0);\n\n return {\n testCases: testCaseResults,\n startedAtMs,\n finishedAtMs,\n durationMs,\n projectRoot: \"\",\n runId: \"\",\n packageVersion: toolVersion !== \"0.0.0\" ? toolVersion : undefined,\n };\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction cucumberStatusToTestStatus(status: TestStepResultStatus): TestStatus {\n switch (status) {\n case \"PASSED\":\n return \"passed\";\n case \"FAILED\":\n return \"failed\";\n case \"SKIPPED\":\n return \"skipped\";\n case \"PENDING\":\n case \"UNDEFINED\":\n return \"pending\";\n default:\n return \"skipped\";\n }\n}\n\nfunction timestampToMs(ts: Timestamp): number {\n return ts.seconds * 1000 + Math.round(ts.nanos / 1_000_000);\n}\n\nfunction durationToMs(d: { seconds: number; nanos: number }): number {\n return d.seconds * 1000 + Math.round(d.nanos / 1_000_000);\n}\n\n/**\n * Reconstruct StorySteps from pickle steps, using GherkinDocument for keywords.\n */\nfunction reconstructStorySteps(\n pickle: Pickle,\n uri: string,\n gherkinDocs: Map<string, GherkinDocument>\n): StoryStep[] {\n const doc = gherkinDocs.get(uri);\n\n // Build AST step ID → keyword map from GherkinDocument\n const astStepKeywords = new Map<string, string>();\n if (doc) {\n for (const child of doc.feature.children) {\n const scenario = child.scenario ?? child.background;\n if (scenario) {\n for (const step of scenario.steps) {\n astStepKeywords.set(step.id, step.keyword.trim());\n }\n }\n }\n }\n\n return pickle.steps.map((ps) => {\n // Look up the keyword from the AST step\n let keyword: StepKeyword = \"Given\";\n if (ps.astNodeIds.length > 0) {\n const astKeyword = astStepKeywords.get(ps.astNodeIds[0]);\n if (astKeyword && isStepKeyword(astKeyword)) {\n keyword = astKeyword;\n }\n }\n\n // Fallback: derive keyword from pickle step type\n if (!ps.astNodeIds.length || !astStepKeywords.has(ps.astNodeIds[0])) {\n keyword = pickleStepTypeToKeyword(ps.type);\n }\n\n const step: StoryStep = {\n keyword,\n text: ps.text,\n };\n\n // Reconstruct docs from pickle step argument\n const docs = pickleStepArgumentToDocs(ps);\n if (docs.length > 0) {\n step.docs = docs;\n }\n\n return step;\n });\n}\n\nfunction isStepKeyword(s: string): s is StepKeyword {\n return [\"Given\", \"When\", \"Then\", \"And\", \"But\"].includes(s);\n}\n\nfunction pickleStepTypeToKeyword(\n type: string\n): StepKeyword {\n switch (type) {\n case \"Context\":\n return \"Given\";\n case \"Action\":\n return \"When\";\n case \"Outcome\":\n return \"Then\";\n default:\n return \"Given\";\n }\n}\n\nfunction extractFeatureName(\n uri: string,\n gherkinDocs: Map<string, GherkinDocument>\n): string | undefined {\n const doc = gherkinDocs.get(uri);\n if (doc) {\n return doc.feature.name;\n }\n return undefined;\n}\n\nfunction deriveOverallStatus(stepResults: StepResult[]): TestStatus {\n if (stepResults.some((sr) => sr.status === \"failed\")) return \"failed\";\n if (stepResults.every((sr) => sr.status === \"skipped\")) return \"skipped\";\n if (stepResults.some((sr) => sr.status === \"pending\")) return \"pending\";\n if (stepResults.every((sr) => sr.status === \"passed\")) return \"passed\";\n return \"passed\";\n}\n\n/**\n * Build ordered step results from accumulated results for a specific attempt.\n */\nfunction buildOrderedStepResults(\n accumulators: StepResultAccumulator[],\n testStepIdToIndex: Map<string, number>,\n stepCount: number\n): StepResult[] {\n const results: StepResult[] = Array.from({ length: stepCount }, (_, i) => ({\n index: i,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n\n for (const sr of accumulators) {\n const stepIndex = testStepIdToIndex.get(sr.testStepId);\n if (stepIndex !== undefined && stepIndex < results.length) {\n results[stepIndex] = {\n index: stepIndex,\n status: sr.status,\n durationMs: sr.durationMs,\n errorMessage: sr.errorMessage,\n };\n }\n }\n\n return results;\n}\n\n/**\n * Convert a pickle step's argument (DocString/DataTable) back to DocEntry[].\n */\nfunction pickleStepArgumentToDocs(ps: PickleStep): DocEntry[] {\n if (!ps.argument) return [];\n const docs: DocEntry[] = [];\n const phase: DocPhase = \"static\";\n\n if (ps.argument.dataTable) {\n const table = ps.argument.dataTable;\n if (table.rows.length > 0) {\n const columns = table.rows[0].cells.map((c) => c.value);\n const rows = table.rows.slice(1).map((r) => r.cells.map((c) => c.value));\n docs.push({\n kind: \"table\",\n label: \"\",\n columns,\n rows,\n phase,\n });\n }\n }\n\n if (ps.argument.docString) {\n const ds = ps.argument.docString;\n const mediaType = ds.mediaType ?? \"text/plain\";\n\n if (mediaType === \"text/plain\") {\n docs.push({\n kind: \"note\",\n text: ds.content,\n phase,\n });\n } else if (mediaType === \"text/markdown\") {\n docs.push({\n kind: \"section\",\n title: \"\",\n markdown: ds.content,\n phase,\n });\n } else if (mediaType === \"text/x-mermaid\") {\n docs.push({\n kind: \"mermaid\",\n code: ds.content,\n phase,\n });\n } else if (mediaType === \"application/json\") {\n try {\n docs.push({\n kind: \"custom\",\n type: \"json\",\n data: JSON.parse(ds.content),\n phase,\n });\n } catch {\n docs.push({\n kind: \"code\",\n label: \"\",\n content: ds.content,\n lang: \"json\",\n phase,\n });\n }\n } else {\n // Default: treat as code block with mediaType as lang\n docs.push({\n kind: \"code\",\n label: \"\",\n content: ds.content,\n lang: mediaType,\n phase,\n });\n }\n }\n\n return docs;\n}\n","/**\n * No-dependency ANSI escape sequence stripper.\n */\n\n/** Strip ANSI escape sequences. Regex: \\x1B\\[[0-?]*[ -/]*[@-~] */\nexport function stripAnsi(text: string): string {\n // eslint-disable-next-line no-control-regex\n return text.replace(/\\x1B\\[[0-?]*[ -/]*[@-~]/g, \"\");\n}\n","/**\n * Slack webhook notifier using Block Kit.\n *\n * Follows the fn(args, deps) pattern.\n * Never logs the webhook URL.\n */\n\nimport type { NotificationSummary } from \"./types\";\nimport { stripAnsi } from \"./ansi-strip\";\n\n/** Arguments for sendSlackNotification. */\nexport interface SlackNotificationArgs {\n summary: NotificationSummary;\n webhookUrl: string;\n maxFailedTests?: number;\n}\n\n/** Injectable dependencies for sendSlackNotification. */\nexport interface SlackNotificationDeps {\n fetch: typeof globalThis.fetch;\n logger: { warn(msg: string): void };\n}\n\n/** Result of a notification send attempt. */\nexport interface NotificationResult {\n ok: boolean;\n error?: string;\n}\n\n/** Truncate text to a maximum length, appending ellipsis if needed. */\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - 3) + \"...\";\n}\n\n/** Format milliseconds as a human-readable duration string. */\nfunction formatDuration(ms: number): string {\n const seconds = ms / 1000;\n if (seconds < 60) return `${seconds.toFixed(1)}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}m ${remainingSeconds.toFixed(0)}s`;\n}\n\n/** Build Slack Block Kit payload from a notification summary. */\nfunction buildSlackPayload(\n summary: NotificationSummary,\n maxFailedTests: number,\n): Record<string, unknown> {\n const allPassed = summary.failed === 0;\n const emoji = allPassed ? \":white_check_mark:\" : \":x:\";\n const statusText = allPassed ? \"Passed\" : \"Failed\";\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const blocks: any[] = [];\n\n // Header block\n blocks.push({\n type: \"header\",\n text: {\n type: \"plain_text\",\n text: `${emoji} Test Results: ${summary.passed} passed, ${summary.failed} failed`,\n emoji: true,\n },\n });\n\n // Summary fields section\n blocks.push({\n type: \"section\",\n fields: [\n { type: \"mrkdwn\", text: `*Total:* ${summary.total}` },\n { type: \"mrkdwn\", text: `*Passed:* ${summary.passed}` },\n { type: \"mrkdwn\", text: `*Failed:* ${summary.failed}` },\n { type: \"mrkdwn\", text: `*Skipped:* ${summary.skipped}` },\n { type: \"mrkdwn\", text: `*Duration:* ${formatDuration(summary.durationMs)}` },\n { type: \"mrkdwn\", text: `*Status:* ${statusText}` },\n ],\n });\n\n // Failed tests section\n if (summary.failedTests.length > 0) {\n const displayedTests = summary.failedTests.slice(0, maxFailedTests);\n const lines = displayedTests.map((t) => {\n const name = t.name;\n if (t.error) {\n const cleanError = truncate(stripAnsi(t.error), 500);\n return `*${name}*\\n\\`\\`\\`${cleanError}\\`\\`\\``;\n }\n return `*${name}*`;\n });\n\n let text = lines.join(\"\\n\\n\");\n if (summary.failedTests.length > maxFailedTests) {\n text += `\\n\\n_...and ${summary.failedTests.length - maxFailedTests} more_`;\n }\n\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text,\n },\n });\n }\n\n // CI context block\n if (summary.ci) {\n const elements: Array<{ type: string; text: string }> = [];\n\n if (summary.ci.displayName) {\n elements.push({ type: \"mrkdwn\", text: `*CI:* ${summary.ci.displayName}` });\n }\n if (summary.ci.branch) {\n elements.push({ type: \"mrkdwn\", text: `*Branch:* ${summary.ci.branch}` });\n }\n if (summary.ci.commitSha) {\n elements.push({ type: \"mrkdwn\", text: `*Commit:* ${summary.ci.commitSha.slice(0, 7)}` });\n }\n if (summary.ci.buildNumber) {\n elements.push({ type: \"mrkdwn\", text: `*Build:* #${summary.ci.buildNumber}` });\n }\n\n if (elements.length > 0) {\n blocks.push({\n type: \"context\",\n elements,\n });\n }\n }\n\n // Actions block with \"View Report\" button\n if (summary.reportUrl) {\n blocks.push({\n type: \"actions\",\n elements: [\n {\n type: \"button\",\n text: {\n type: \"plain_text\",\n text: \"View Report\",\n emoji: true,\n },\n url: summary.reportUrl,\n action_id: \"view_report\",\n },\n ],\n });\n }\n\n return { blocks };\n}\n\n/**\n * Send a Slack notification via incoming webhook.\n *\n * Never throws. Returns `{ ok, error? }`.\n * Never logs the webhook URL.\n */\nexport async function sendSlackNotification(\n args: SlackNotificationArgs,\n deps: SlackNotificationDeps,\n): Promise<NotificationResult> {\n const { summary, webhookUrl, maxFailedTests = 5 } = args;\n const { fetch, logger } = deps;\n\n const payload = buildSlackPayload(summary, maxFailedTests);\n\n try {\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const requestId = response.headers.get(\"x-request-id\") ?? undefined;\n let bodyText = \"\";\n try {\n bodyText = await response.text();\n } catch {\n // ignore body read errors\n }\n const truncatedBody = truncate(bodyText, 200);\n const idPart = requestId ? ` x-request-id=${requestId}` : \"\";\n const errorMsg = `Slack notifier failed: HTTP ${response.status}${idPart} ${truncatedBody}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const errorMsg = `Slack notifier failed: ${msg}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n}\n","/**\n * Microsoft Teams webhook notifier using Adaptive Cards.\n *\n * Follows the fn(args, deps) pattern.\n * Never logs the webhook URL.\n */\n\nimport type { NotificationSummary } from \"./types\";\nimport { stripAnsi } from \"./ansi-strip\";\n\n/** Arguments for sendTeamsNotification. */\nexport interface TeamsNotificationArgs {\n summary: NotificationSummary;\n webhookUrl: string;\n maxFailedTests?: number;\n}\n\n/** Injectable dependencies for sendTeamsNotification. */\nexport interface TeamsNotificationDeps {\n fetch: typeof globalThis.fetch;\n logger: { warn(msg: string): void };\n}\n\n/** Result of a notification send attempt. */\nexport interface TeamsNotificationResult {\n ok: boolean;\n error?: string;\n}\n\n/** Truncate text to a maximum length, appending ellipsis if needed. */\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - 3) + \"...\";\n}\n\n/** Format milliseconds as a human-readable duration string. */\nfunction formatDuration(ms: number): string {\n const seconds = ms / 1000;\n if (seconds < 60) return `${seconds.toFixed(1)}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}m ${remainingSeconds.toFixed(0)}s`;\n}\n\n/** Build an Adaptive Card payload from a notification summary. */\nfunction buildTeamsPayload(\n summary: NotificationSummary,\n maxFailedTests: number,\n): Record<string, unknown> {\n const allPassed = summary.failed === 0;\n const statusEmoji = allPassed ? \"\\u2705\" : \"\\u274C\"; // check mark / cross mark\n const statusColor = allPassed ? \"good\" : \"attention\";\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const bodyItems: any[] = [];\n\n // Header with status\n bodyItems.push({\n type: \"TextBlock\",\n size: \"Large\",\n weight: \"Bolder\",\n text: `${statusEmoji} Test Results`,\n color: statusColor,\n });\n\n // FactSet with summary\n bodyItems.push({\n type: \"FactSet\",\n facts: [\n { title: \"Total\", value: String(summary.total) },\n { title: \"Passed\", value: String(summary.passed) },\n { title: \"Failed\", value: String(summary.failed) },\n { title: \"Skipped\", value: String(summary.skipped) },\n { title: \"Duration\", value: formatDuration(summary.durationMs) },\n ],\n });\n\n // Failed tests container\n if (summary.failedTests.length > 0) {\n const displayedTests = summary.failedTests.slice(0, maxFailedTests);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const failedItems: any[] = [\n {\n type: \"TextBlock\",\n text: \"Failed Tests\",\n weight: \"Bolder\",\n spacing: \"Medium\",\n },\n ];\n\n for (const t of displayedTests) {\n failedItems.push({\n type: \"TextBlock\",\n text: `**${t.name}**`,\n wrap: true,\n });\n if (t.error) {\n const cleanError = truncate(stripAnsi(t.error), 500);\n failedItems.push({\n type: \"TextBlock\",\n text: cleanError,\n wrap: true,\n fontType: \"Monospace\",\n size: \"Small\",\n color: \"Attention\",\n });\n }\n }\n\n if (summary.failedTests.length > maxFailedTests) {\n failedItems.push({\n type: \"TextBlock\",\n text: `...and ${summary.failedTests.length - maxFailedTests} more`,\n isSubtle: true,\n spacing: \"Small\",\n });\n }\n\n bodyItems.push({\n type: \"Container\",\n items: failedItems,\n });\n }\n\n // CI info facts\n if (summary.ci) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ciFacts: any[] = [];\n\n if (summary.ci.displayName) {\n ciFacts.push({ title: \"CI\", value: summary.ci.displayName });\n }\n if (summary.ci.branch) {\n ciFacts.push({ title: \"Branch\", value: summary.ci.branch });\n }\n if (summary.ci.commitSha) {\n ciFacts.push({ title: \"Commit\", value: summary.ci.commitSha.slice(0, 7) });\n }\n if (summary.ci.buildNumber) {\n ciFacts.push({ title: \"Build\", value: `#${summary.ci.buildNumber}` });\n }\n\n if (ciFacts.length > 0) {\n bodyItems.push({\n type: \"FactSet\",\n facts: ciFacts,\n separator: true,\n });\n }\n }\n\n // Build the Adaptive Card\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const card: Record<string, any> = {\n type: \"AdaptiveCard\",\n $schema: \"http://adaptivecards.io/schemas/adaptive-card.json\",\n version: \"1.4\",\n body: bodyItems,\n };\n\n // \"View Report\" action\n if (summary.reportUrl) {\n card.actions = [\n {\n type: \"Action.OpenUrl\",\n title: \"View Report\",\n url: summary.reportUrl,\n },\n ];\n }\n\n return {\n type: \"message\",\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: card,\n },\n ],\n };\n}\n\n/**\n * Send a Teams notification via incoming webhook.\n *\n * Never throws. Returns `{ ok, error? }`.\n * Never logs the webhook URL.\n */\nexport async function sendTeamsNotification(\n args: TeamsNotificationArgs,\n deps: TeamsNotificationDeps,\n): Promise<TeamsNotificationResult> {\n const { summary, webhookUrl, maxFailedTests = 5 } = args;\n const { fetch, logger } = deps;\n\n const payload = buildTeamsPayload(summary, maxFailedTests);\n\n try {\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const requestId = response.headers.get(\"x-request-id\") ?? undefined;\n let bodyText = \"\";\n try {\n bodyText = await response.text();\n } catch {\n // ignore body read errors\n }\n const truncatedBody = truncate(bodyText, 200);\n const idPart = requestId ? ` x-request-id=${requestId}` : \"\";\n const errorMsg = `Teams notifier failed: HTTP ${response.status}${idPart} ${truncatedBody}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const errorMsg = `Teams notifier failed: ${msg}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n}\n","/**\n * HMAC-SHA256 signing for generic webhook payloads.\n *\n * Pure function, isolated for testing.\n * Uses node:crypto — zero new dependencies.\n */\n\nimport { createHmac } from \"node:crypto\";\n\n/** Result of HMAC signing. */\nexport interface HmacSignResult {\n /** Signature in format \"sha256=<hex>\" (GitHub-style, widely recognized) */\n signature: string;\n /** ISO 8601 timestamp, only present when includeTimestamp is true */\n timestamp?: string;\n}\n\n/**\n * Compute HMAC-SHA256 signature for a request body.\n *\n * When `includeTimestamp` is true, the signed input is `\"<timestamp>.<body>\"` and the\n * timestamp is returned for the caller to emit as a header.\n */\nexport function signBody(args: {\n body: string;\n secret: string;\n includeTimestamp?: boolean;\n /** Injectable for deterministic testing */\n timestamp?: string;\n}): HmacSignResult {\n let input: string;\n let timestamp: string | undefined;\n\n if (args.includeTimestamp) {\n timestamp = args.timestamp ?? new Date().toISOString();\n input = `${timestamp}.${args.body}`;\n } else {\n input = args.body;\n }\n\n const hex = createHmac(\"sha256\", args.secret)\n .update(input, \"utf8\")\n .digest(\"hex\");\n\n return {\n signature: `sha256=${hex}`,\n timestamp,\n };\n}\n","/**\n * Generic webhook notifier.\n *\n * Sends a versioned JSON envelope to arbitrary HTTP endpoints with optional\n * HMAC-SHA256 signing. Follows the fn(args, deps) pattern.\n *\n * Never throws. Never logs the webhook URL.\n */\n\nimport type { NotificationSummary } from \"./types\";\nimport type { GenericWebhookNotifierOptions, WebhookPayload } from \"./types\";\nimport { signBody } from \"./hmac\";\n\n/** Arguments for sendWebhookNotification. */\nexport interface WebhookNotificationArgs {\n summary: NotificationSummary;\n options: GenericWebhookNotifierOptions;\n maxFailedTests?: number;\n}\n\n/** Injectable dependencies for sendWebhookNotification. */\nexport interface WebhookNotificationDeps {\n fetch: typeof globalThis.fetch;\n logger: { warn(msg: string): void };\n}\n\n/** Result of a webhook send attempt. */\nexport interface WebhookNotificationResult {\n ok: boolean;\n error?: string;\n}\n\n/**\n * Send a notification to a generic webhook endpoint.\n *\n * Never throws. Returns `{ ok, error? }`.\n * Never logs the webhook URL.\n */\nexport async function sendWebhookNotification(\n args: WebhookNotificationArgs,\n deps: WebhookNotificationDeps,\n): Promise<WebhookNotificationResult> {\n const { summary, options } = args;\n const { fetch, logger } = deps;\n\n // Build versioned envelope\n const payload: WebhookPayload = {\n schemaVersion: 1,\n event: \"test_run_finished\",\n summary,\n };\n const body = JSON.stringify(payload);\n\n // Build headers\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n\n // Apply user-provided headers (can override Content-Type)\n if (options.headers) {\n for (const [key, value] of Object.entries(options.headers)) {\n headers[key] = value;\n }\n }\n\n // HMAC signing — headers always override user-supplied values\n if (options.signer) {\n const { secret, header, includeTimestamp, timestampHeader } = options.signer;\n const result = signBody({ body, secret, includeTimestamp });\n headers[header] = result.signature;\n if (result.timestamp) {\n headers[timestampHeader ?? \"X-Timestamp\"] = result.timestamp;\n }\n }\n\n try {\n const response = await fetch(options.url, {\n method: options.method ?? \"POST\",\n headers,\n body,\n });\n\n if (!response.ok) {\n const requestId = response.headers.get(\"x-request-id\") ?? undefined;\n let snippet = \"\";\n try {\n snippet = (await response.text()).slice(0, 200);\n } catch {\n /* ignore */\n }\n const idPart = requestId ? ` x-request-id=${requestId}` : \"\";\n const errorMsg = `webhook: HTTP ${response.status}${idPart} ${snippet}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const errorMsg = `webhook: ${msg}`;\n logger.warn(errorMsg);\n return { ok: false, error: errorMsg };\n }\n}\n","/**\n * Notification orchestrator.\n *\n * Builds a NotificationSummary from a TestRunResult and dispatches\n * to configured notifiers (Slack, Teams, generic webhooks) based on condition.\n *\n * Env fallback and defaults are resolved internally so CLI and reporters\n * behave identically with zero duplication.\n *\n * Follows the fn(args, deps) pattern.\n * Never throws. Never logs webhook URLs.\n */\n\nimport type { TestRunResult } from \"../types/test-result\";\nimport type { CIInfo } from \"../types/ci\";\nimport type { RawCIInfo } from \"../types/raw\";\nimport type { NotificationSummary, NotifyCondition, GenericWebhookNotifierOptions } from \"./types\";\nimport { sendSlackNotification } from \"./slack\";\nimport { sendTeamsNotification } from \"./teams\";\nimport { sendWebhookNotification } from \"./webhook\";\n\n/** Arguments for sendNotifications. */\nexport interface SendNotificationsArgs {\n run: TestRunResult;\n /** Notification config from FormatterOptions.notification (or CLI-assembled equivalent) */\n notification?: {\n slackWebhookUrl?: string;\n teamsWebhookUrl?: string;\n condition?: NotifyCondition;\n reportUrl?: string;\n maxFailedTests?: number;\n webhooks?: GenericWebhookNotifierOptions[];\n };\n}\n\n/** Injectable dependencies for sendNotifications. */\nexport interface SendNotificationsDeps {\n fetch?: typeof globalThis.fetch;\n logger: { warn(msg: string): void };\n toCIInfo: (raw?: RawCIInfo) => CIInfo | undefined;\n env?: Record<string, string | undefined>;\n}\n\n/** Build a NotificationSummary from a TestRunResult. */\nfunction buildSummary(\n run: TestRunResult,\n reportUrl: string | undefined,\n toCIInfo: SendNotificationsDeps[\"toCIInfo\"],\n): NotificationSummary {\n let passed = 0;\n let failed = 0;\n let skipped = 0;\n\n const failedTests: NotificationSummary[\"failedTests\"] = [];\n\n for (const tc of run.testCases) {\n switch (tc.status) {\n case \"passed\":\n passed++;\n break;\n case \"failed\":\n failed++;\n failedTests.push({\n testId: tc.id,\n name: tc.story.scenario,\n error: tc.errorMessage,\n });\n break;\n case \"skipped\":\n case \"pending\":\n skipped++;\n break;\n }\n }\n\n // Derive CI info: use typed CIInfo from run.ci if available\n // run.ci is the legacy CIInfo shape { name, url?, buildNumber? }\n // Cast to RawCIInfo to pass through the converter\n let ci: CIInfo | undefined;\n if (run.ci) {\n ci = toCIInfo(run.ci as unknown as RawCIInfo);\n }\n\n return {\n total: run.testCases.length,\n passed,\n failed,\n skipped,\n durationMs: run.durationMs,\n failedTests,\n ci,\n reportUrl,\n };\n}\n\n/** Check if a notifier should fire given condition and failure count. */\nfunction shouldNotify(condition: NotifyCondition, failedCount: number): boolean {\n if (condition === \"never\") return false;\n if (condition === \"on-failure\" && failedCount === 0) return false;\n return true;\n}\n\n/**\n * Send notifications to all configured channels.\n *\n * Resolves env fallbacks and defaults internally.\n * Never throws. Logs warnings for failures.\n * Never logs webhook URLs.\n */\nexport async function sendNotifications(\n args: SendNotificationsArgs,\n deps: SendNotificationsDeps,\n): Promise<void> {\n const { run, notification } = args;\n const { logger, toCIInfo } = deps;\n const env = deps.env ?? process.env;\n\n // Guard: if fetch is unavailable, warn and bail\n if (!deps.fetch) {\n logger.warn(\"notifications: skipped (fetch unavailable)\");\n return;\n }\n const fetch = deps.fetch;\n\n // Resolve env fallbacks + defaults\n const slackWebhookUrl = notification?.slackWebhookUrl ?? env.SLACK_WEBHOOK_URL;\n const teamsWebhookUrl = notification?.teamsWebhookUrl ?? env.TEAMS_WEBHOOK_URL;\n const globalCondition: NotifyCondition = notification?.condition ?? \"on-failure\";\n const reportUrl = notification?.reportUrl;\n const maxFailedTests = notification?.maxFailedTests ?? 5;\n const webhooks = notification?.webhooks ?? [];\n\n // Nothing configured → early return\n if (!slackWebhookUrl && !teamsWebhookUrl && webhooks.length === 0) {\n return;\n }\n\n const summary = buildSummary(run, reportUrl, toCIInfo);\n\n // Dispatch to configured notifiers concurrently\n const promises: Promise<void>[] = [];\n\n if (slackWebhookUrl && shouldNotify(globalCondition, summary.failed)) {\n promises.push(\n sendSlackNotification(\n { summary, webhookUrl: slackWebhookUrl, maxFailedTests },\n { fetch, logger },\n ).then(() => undefined),\n );\n }\n\n if (teamsWebhookUrl && shouldNotify(globalCondition, summary.failed)) {\n promises.push(\n sendTeamsNotification(\n { summary, webhookUrl: teamsWebhookUrl, maxFailedTests },\n { fetch, logger },\n ).then(() => undefined),\n );\n }\n\n // Generic webhooks — per-webhook condition override\n for (const webhook of webhooks) {\n const effectiveCondition = webhook.condition ?? globalCondition;\n if (!shouldNotify(effectiveCondition, summary.failed)) continue;\n\n promises.push(\n sendWebhookNotification(\n { summary, options: webhook, maxFailedTests },\n { fetch, logger },\n ).then(() => undefined),\n );\n }\n\n // Wait for all, never throw\n await Promise.allSettled(promises);\n}\n","/**\n * Typed CI provider and canonical CI info.\n *\n * RawCIInfo.name = legacy transport string (kept for backward compat + schema).\n * CIInfo.displayName = canonical display name for downstream consumers.\n *\n * All downstream code (HTML meta, notifications, history) uses CIInfo via mappers.\n */\n\nimport type { RawCIInfo } from \"./raw\";\n\nexport type CIProvider =\n | \"github\"\n | \"gitlab\"\n | \"circleci\"\n | \"jenkins\"\n | \"azure\"\n | \"buildkite\"\n | \"travis\"\n | \"unknown\";\n\nexport interface CIInfo {\n provider: CIProvider;\n displayName: string;\n url?: string;\n buildNumber?: string;\n branch?: string;\n commitSha?: string;\n prNumber?: string;\n}\n\nconst DISPLAY_NAMES: Record<CIProvider, string> = {\n github: \"GitHub Actions\",\n gitlab: \"GitLab CI\",\n circleci: \"CircleCI\",\n jenkins: \"Jenkins\",\n azure: \"Azure DevOps\",\n buildkite: \"Buildkite\",\n travis: \"Travis CI\",\n unknown: \"CI\",\n};\n\nconst NAME_TO_PROVIDER: Record<string, CIProvider> = {\n github: \"github\",\n gitlab: \"gitlab\",\n circleci: \"circleci\",\n jenkins: \"jenkins\",\n azure: \"azure\",\n buildkite: \"buildkite\",\n travis: \"travis\",\n ci: \"unknown\",\n};\n\n/** Convert RawCIInfo (legacy transport) to canonical CIInfo. */\nexport function toCIInfo(raw?: RawCIInfo): CIInfo | undefined {\n if (!raw) return undefined;\n\n const provider: CIProvider =\n raw.provider ?? NAME_TO_PROVIDER[raw.name] ?? \"unknown\";\n\n return {\n provider,\n displayName: DISPLAY_NAMES[provider],\n url: raw.url,\n buildNumber: raw.buildNumber,\n branch: raw.branch,\n commitSha: raw.commitSha,\n prNumber: raw.prNumber,\n };\n}\n\n/** Convert canonical CIInfo back to RawCIInfo (for serialization). */\nexport function toRawCIInfo(ci?: CIInfo): RawCIInfo | undefined {\n if (!ci) return undefined;\n\n return {\n name: ci.provider === \"unknown\" ? \"ci\" : ci.provider,\n provider: ci.provider,\n url: ci.url,\n buildNumber: ci.buildNumber,\n branch: ci.branch,\n commitSha: ci.commitSha,\n prNumber: ci.prNumber,\n };\n}\n","/**\n * History store: load, save, update (fn(args, deps) pattern).\n */\n\nimport type { TestRunResult } from \"../types/test-result\";\nimport type { HistoryEntry, HistoryStore, TestHistory } from \"./types\";\n\n// ============================================================================\n// Load\n// ============================================================================\n\nexport interface LoadHistoryArgs {\n filePath: string;\n}\n\nexport interface LoadHistoryDeps {\n readFile: (path: string) => string | undefined;\n logger: { warn(msg: string): void };\n}\n\nfunction emptyStore(): HistoryStore {\n return { version: 1, maxRuns: 10, tests: {}, lastUpdated: 0 };\n}\n\nexport function loadHistory(args: LoadHistoryArgs, deps: LoadHistoryDeps): HistoryStore {\n const content = deps.readFile(args.filePath);\n if (content === undefined) {\n return emptyStore();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n deps.logger.warn(`Failed to parse history file: ${args.filePath}`);\n return emptyStore();\n }\n\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as Record<string, unknown>).version !== 1\n ) {\n deps.logger.warn(\n `Unknown history version in ${args.filePath}, expected version 1`,\n );\n return emptyStore();\n }\n\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.tests !== \"object\" || obj.tests === null || Array.isArray(obj.tests)) {\n deps.logger.warn(\n `Malformed history store in ${args.filePath}: tests must be a non-null object`,\n );\n return emptyStore();\n }\n\n return parsed as HistoryStore;\n}\n\n// ============================================================================\n// Save\n// ============================================================================\n\nexport interface SaveHistoryArgs {\n filePath: string;\n store: HistoryStore;\n}\n\nexport interface SaveHistoryDeps {\n writeFile: (path: string, content: string) => void;\n}\n\nexport function saveHistory(args: SaveHistoryArgs, deps: SaveHistoryDeps): void {\n deps.writeFile(args.filePath, JSON.stringify(args.store, null, 2));\n}\n\n// ============================================================================\n// Update (pure function, no deps)\n// ============================================================================\n\nexport interface UpdateHistoryArgs {\n store: HistoryStore;\n run: TestRunResult;\n maxRuns: number;\n}\n\nexport function updateHistory(args: UpdateHistoryArgs): HistoryStore {\n const { store, run, maxRuns } = args;\n const newTests: Record<string, TestHistory> = { ...store.tests };\n\n for (const tc of run.testCases) {\n const entry: HistoryEntry = {\n runId: run.runId,\n timestamp: run.startedAtMs,\n status: tc.status,\n durationMs: tc.durationMs,\n ci: run.ci\n ? {\n provider: undefined,\n branch: run.ci.branch,\n commitSha: run.ci.commitSha,\n }\n : undefined,\n };\n\n const existing = newTests[tc.id];\n if (existing) {\n const updatedEntries = [...existing.entries, entry];\n // Trim per-test entries to maxRuns (keep latest)\n const trimmed =\n updatedEntries.length > maxRuns\n ? updatedEntries.slice(updatedEntries.length - maxRuns)\n : updatedEntries;\n newTests[tc.id] = {\n ...existing,\n testName: tc.story.scenario,\n sourceFile: tc.sourceFile,\n sourceLine: tc.sourceLine,\n entries: trimmed,\n };\n } else {\n newTests[tc.id] = {\n testId: tc.id,\n testName: tc.story.scenario,\n sourceFile: tc.sourceFile,\n sourceLine: tc.sourceLine,\n entries: [entry],\n };\n }\n }\n\n return {\n version: 1,\n maxRuns,\n tests: newTests,\n lastUpdated: Date.now(),\n };\n}\n","/**\n * List scenarios from a test run (fn(args, deps) pattern).\n * Produces text table or JSON output.\n */\n\nimport type { TestCaseResult } from \"./types/test-result\";\n\nexport interface ListScenariosArgs {\n testCases: TestCaseResult[];\n format: \"text\" | \"json\";\n}\n\nexport type ListScenariosDeps = Record<string, never>;\n\nconst STATUS_ICONS: Record<string, string> = {\n passed: \"\\u2705\",\n failed: \"\\u274C\",\n skipped: \"\\u23ED\\uFE0F\",\n pending: \"\\u23F3\",\n};\n\nexport function listScenarios(\n args: ListScenariosArgs,\n _deps: ListScenariosDeps,\n): string {\n const { testCases, format } = args;\n\n if (format === \"json\") {\n const items = testCases.map((tc) => ({\n scenario: tc.story.scenario,\n status: tc.status,\n sourceFile: tc.sourceFile,\n sourceLine: tc.sourceLine,\n tags: tc.tags,\n id: tc.id,\n }));\n return JSON.stringify(items, null, 2);\n }\n\n // Text format\n if (testCases.length === 0) {\n return \"No scenarios found.\";\n }\n\n const sorted = [...testCases].sort((a, b) => {\n const rank = { failed: 0, pending: 1, skipped: 2, passed: 3 };\n if (rank[a.status] !== rank[b.status]) {\n return rank[a.status] - rank[b.status];\n }\n if (a.sourceFile !== b.sourceFile) {\n return a.sourceFile.localeCompare(b.sourceFile);\n }\n if (a.sourceLine !== b.sourceLine) {\n return a.sourceLine - b.sourceLine;\n }\n return a.story.scenario.localeCompare(b.story.scenario);\n });\n\n const summary = {\n total: sorted.length,\n failed: sorted.filter((tc) => tc.status === \"failed\").length,\n pending: sorted.filter((tc) => tc.status === \"pending\").length,\n skipped: sorted.filter((tc) => tc.status === \"skipped\").length,\n passed: sorted.filter((tc) => tc.status === \"passed\").length,\n };\n\n const lines = [\n `Review summary: ${summary.failed} failed, ${summary.pending} pending, ${summary.skipped} skipped, ${summary.passed} passed (${summary.total} total)`,\n summary.failed > 0\n ? \"Priority: failed scenarios are listed first for review.\"\n : \"Priority: no failed scenarios detected.\",\n \"\",\n ...sorted.map((tc) => {\n const icon = STATUS_ICONS[tc.status] ?? \"?\";\n const status = tc.status.padEnd(7);\n const scenario = tc.story.scenario;\n const location = `${tc.sourceFile}:${tc.sourceLine}`;\n const tags = tc.tags.length > 0 ? tc.tags.join(\", \") : \"\";\n return `${icon} ${status} ${scenario} ${location}${tags ? ` ${tags}` : \"\"}`;\n }),\n ];\n\n return lines.join(\"\\n\");\n}\n","/**\n * Scaffold an Astro Starlight project from template.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface InitAstroOptions {\n targetDir?: string;\n force?: boolean;\n}\n\nexport interface InitAstroResult {\n targetDir: string;\n}\n\nexport function initAstro(options: InitAstroOptions = {}): InitAstroResult {\n const targetDir = options.targetDir ?? \"./story-docs\";\n const force = options.force ?? false;\n\n if (fs.existsSync(targetDir)) {\n const entries = fs.readdirSync(targetDir);\n if (entries.length > 0 && !force) {\n throw new Error(\n `Directory \"${targetDir}\" already exists and is not empty. Use --force to overwrite.`,\n );\n }\n }\n\n // Template is at: <package-root>/templates/astro-starlight/\n const templateDir = path.resolve(__dirname, \"..\", \"templates\", \"astro-starlight\");\n\n if (!fs.existsSync(templateDir)) {\n throw new Error(\n `Template directory not found at ${templateDir}. Ensure the package is installed correctly.`,\n );\n }\n\n copyDirRecursive(templateDir, targetDir);\n return { targetDir };\n}\n\nfunction copyDirRecursive(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n const entries = fs.readdirSync(src, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n copyDirRecursive(srcPath, destPath);\n } else {\n fs.copyFileSync(srcPath, destPath);\n }\n }\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA82DtB,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4Dd,IAAM,iBAA4B;AAAA,EACvC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,WAAW;AAAA,EACX,cAAc;AAChB;;;ACjlEO,IAAM,gBAA2B;AAAA,EACtC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuqDP;;;ACvqDO,IAAM,eAA0B;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2rDtB,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqDd,IAAM,iBAA4B;AAAA,EACvC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,WAAW;AAAA,EACX,cAAc;AAChB;;;ACn5DO,IAAM,eAA0B;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8sDP;;;AC5sDA,IAAM,iBAAiB,oBAAI,IAAuB;AAAA,EAChD,CAAC,WAAW,YAAY;AAAA,EACxB,CAAC,aAAa,cAAc;AAAA,EAC5B,CAAC,YAAY,aAAa;AAAA,EAC1B,CAAC,WAAW,YAAY;AAAA,EACxB,CAAC,aAAa,cAAc;AAAA,EAC5B,CAAC,WAAW,YAAY;AAC1B,CAAC;AAGM,SAAS,aAAa,aAA4C;AACvE,MAAI,OAAO,gBAAgB,SAAU,QAAO;AAC5C,QAAM,QAAQ,eAAe,IAAI,WAAW;AAC5C,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,mBAAmB,WAAW,iBAAiB,CAAC,GAAG,eAAe,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,IACtF;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,mBAAgC;AAC9C,SAAO,CAAC,GAAG,eAAe,OAAO,CAAC,EAAE;AAAA,IAClC,CAAC,UAAU,CAAC,MAAM,aAAa,CAAC,MAAM;AAAA,EACxC;AACF;;;AClCO,SAAS,cAAc,QAA4B;AACxD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;ACFO,SAAS,eACd,MACA,MACQ;AACR,QAAM,QAAkB,CAAC;AAEzB,QAAM,YAAY,IAAI,KAAK,KAAK,WAAW;AAC3C,QAAM,KAAK,wBAAwB,UAAU,YAAY,CAAC,OAAO;AAEjE,QAAM,YAAY,KAAK,aAAa,KAAM,QAAQ,CAAC;AACnD,QAAM,KAAK,yBAAyB,QAAQ,QAAQ;AAEpD,MAAI,KAAK,gBAAgB;AACvB,UAAM,KAAK,wBAAwB,KAAK,WAAW,KAAK,cAAc,CAAC,OAAO;AAAA,EAChF;AAEA,MAAI,KAAK,QAAQ;AACf,UAAM,WACJ,KAAK,OAAO,SAAS,IAAI,KAAK,OAAO,MAAM,GAAG,CAAC,IAAI,KAAK;AAC1D,UAAM,KAAK,oBAAoB,KAAK,WAAW,QAAQ,CAAC,OAAO;AAAA,EACjE;AAEA,MAAI,KAAK,QAAQ;AAEf,QAAI,KAAK,SAAS,KAAK,eAAe;AACpC,YAAM;AAAA,QACJ,mBAAmB,KAAK,WAAW,KAAK,MAAM,CAAC,aAAa,KAAK,WAAW,KAAK,KAAK,CAAC,MAAM,KAAK,WAAW,KAAK,aAAa,CAAC;AAAA,MAClI;AAAA,IACF,OAAO;AACL,YAAM,KAAK,mBAAmB,KAAK,WAAW,KAAK,MAAM,CAAC,OAAO;AAAA,IACnE;AAAA,EACF;AAEA,MAAI,KAAK,UAAU;AACjB,UAAM,KAAK,uBAAuB,KAAK,WAAW,KAAK,QAAQ,CAAC,OAAO;AAAA,EACzE;AAEA,MAAI,KAAK,aAAa;AACpB,UAAM,WACJ,KAAK,YAAY,SAAS,IACtB,KAAK,YAAY,MAAM,GAAG,CAAC,IAC3B,KAAK;AACX,UAAM;AAAA,MACJ,8BAA8B,KAAK,WAAW,KAAK,WAAW,CAAC,KAAK,KAAK,WAAW,QAAQ,CAAC;AAAA,IAC/F;AAAA,EACF;AAEA,SAAO,yBAAyB,MAAM,KAAK,EAAE,CAAC;AAChD;;;ACpDO,SAAS,cACd,MACA,OACQ;AACR,QAAM,EAAE,OAAO,QAAQ,QAAQ,QAAQ,IAAI;AAC3C,SAAO;AAAA;AAAA;AAAA;AAAA,yBAIgB,KAAK;AAAA;AAAA;AAAA;AAAA,yBAIL,MAAM;AAAA;AAAA;AAAA;AAAA,yBAIN,MAAM;AAAA;AAAA;AAAA;AAAA,yBAIN,OAAO;AAAA;AAAA;AAGhC;;;ACzBO,SAAS,aACd,MACA,MACQ;AACR,QAAM,EAAE,MAAM,eAAe,IAAI;AAEjC,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,QAAQ,KACX;AAAA,IACC,CAAC,QACC,oDAAoD,KAAK,WAAW,GAAG,CAAC,0BAA0B,KAAK,WAAW,GAAG,CAAC;AAAA,EAC1H,EACC,KAAK,YAAY;AAEpB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAaH,KAAK;AAAA;AAAA;AAAA;AAAA,8EAImE,cAAc;AAAA;AAE5F;;;ACpCO,SAAS,eACd,MACA,MACQ;AACR,QAAM,OACJ,KAAK,SAAS,OACV,GAAG,KAAK,WAAW,KAAK,OAAO,CAAC;AAAA;AAAA,EAAO,KAAK,WAAW,KAAK,KAAK,CAAC,KAClE,KAAK,WAAW,KAAK,OAAO;AAClC,SAAO,0BAA0B,IAAI;AACvC;;;ACPO,SAAS,kBACd,MACA,MACQ;AACR,MAAI,KAAK,YAAY,WAAW,GAAG;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,KAAK,YAAY,IAAI,CAAC,QAAQ;AAC1C,UAAM,UAAU,IAAI,UAAU,WAAW,QAAQ;AACjD,UAAM,UAAU,IAAI,UAAU,WAAW,QAAQ;AACjD,UAAM,WAAW,IAAI,oBAAoB;AAEzC,QAAI,WAAW,KAAK,oBAAoB,UAAU;AAChD,aAAO;AAAA;AAAA,IAET,KAAK,WAAW,IAAI,IAAI,CAAC;AAAA,4CACe,IAAI,SAAS,WAAW,IAAI,IAAI,UAAU,KAAK,WAAW,IAAI,IAAI,CAAC;AAAA;AAAA,IAE3G;AAEA,QAAI,WAAW,KAAK,kBAAkB;AACpC,YAAM,MAAM,WACR,QAAQ,IAAI,SAAS,WAAW,IAAI,IAAI,KACxC,IAAI;AACR,aAAO;AAAA;AAAA,IAET,KAAK,WAAW,IAAI,IAAI,CAAC;AAAA,kDACqB,KAAK,WAAW,GAAG,CAAC;AAAA;AAAA,IAElE;AAEA,UAAM,OAAO,WACT,QAAQ,IAAI,SAAS,WAAW,IAAI,IAAI,KACxC,IAAI;AAER,WAAO,+BAA+B,KAAK,WAAW,IAAI,CAAC,KAAK,KAAK,WAAW,IAAI,IAAI,CAAC;AAAA,EAC3F,CAAC;AAED,SAAO,4BAA4B,MAAM,KAAK,EAAE,CAAC;AACnD;;;ACzCO,SAAS,cACd,OACA,MACQ;AACR,SAAO,yBAAyB,KAAK,WAAW,MAAM,IAAI,CAAC;AAC7D;AAEO,SAAS,aACd,OACA,MACQ;AACR,QAAM,OAAO,MAAM,MAChB,IAAI,CAAC,MAAM,8BAA8B,KAAK,WAAW,CAAC,CAAC,SAAS,EACpE,KAAK,EAAE;AACV,SAAO,wBAAwB,IAAI;AACrC;AAEO,SAAS,YACd,OACA,MACQ;AACR,QAAM,WACJ,OAAO,MAAM,UAAU,WACnB,MAAM,QACN,KAAK,UAAU,MAAM,OAAO,MAAM,CAAC;AACzC,SAAO;AAAA,+BACsB,KAAK,WAAW,MAAM,KAAK,CAAC;AAAA,+BAC5B,KAAK,WAAW,QAAQ,CAAC;AAAA;AAExD;AAEO,SAAS,cACd,OACA,MACQ;AACR,QAAM,YAAY,MAAM,OACpB,+BAA+B,KAAK,WAAW,MAAM,IAAI,CAAC,YAC1D;AACJ,QAAM,YACJ,KAAK,sBAAsB,MAAM,OAC7B,oBAAoB,KAAK,WAAW,MAAM,IAAI,CAAC,MAC/C;AACN,SAAO;AAAA;AAAA,mCAE0B,KAAK,WAAW,MAAM,KAAK,CAAC;AAAA,MACzD,SAAS;AAAA;AAAA,uCAEwB,SAAS,IAAI,KAAK,WAAW,MAAM,OAAO,CAAC;AAAA;AAElF;AAEO,SAAS,eACd,OACA,MACQ;AACR,QAAM,UAAU,MAAM,QACnB,IAAI,CAAC,MAAM,OAAO,KAAK,WAAW,CAAC,CAAC,OAAO,EAC3C,KAAK,EAAE;AACV,QAAM,OAAO,MAAM,KAChB;AAAA,IAAI,CAAC,MACJ,OAAO,EAAE,IAAI,CAAC,MAAM,OAAO,KAAK,WAAW,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,EAChE,EACC,KAAK,EAAE;AACV,SAAO;AAAA,iCACwB,KAAK,WAAW,MAAM,KAAK,CAAC;AAAA;AAAA,iBAE5C,OAAO;AAAA,aACX,IAAI;AAAA;AAAA;AAGjB;AAEO,SAAS,cACd,OACA,MACQ;AACR,SAAO;AAAA,aACI,KAAK,WAAW,MAAM,GAAG,CAAC,+CAA+C,KAAK,WAAW,MAAM,KAAK,CAAC;AAAA;AAElH;AAEO,SAAS,iBACd,OACA,MACQ;AACR,MAAI,KAAK,iBAAiB;AACxB,UAAM,kBAAkB,KAAK,mBAAmB,MAAM,QAAQ,CAAC;AAC/D,WAAO;AAAA,mCACwB,KAAK,WAAW,MAAM,KAAK,CAAC;AAAA,oDACX,eAAe;AAAA;AAAA,EAEjE;AACA,SAAO;AAAA,mCAC0B,KAAK,WAAW,MAAM,KAAK,CAAC;AAAA,qCAC1B,KAAK,WAAW,MAAM,QAAQ,CAAC;AAAA;AAEpE;AAEO,SAAS,iBACd,OACA,MACQ;AACR,QAAM,QAAQ,MAAM,QAChB,kCAAkC,KAAK,WAAW,MAAM,KAAK,CAAC,WAC9D;AAEJ,MAAI,KAAK,gBAAgB;AACvB,WAAO;AAAA,IACP,KAAK;AAAA,yBACgB,KAAK,WAAW,MAAM,IAAI,CAAC;AAAA;AAAA,EAElD;AACA,SAAO;AAAA,IACL,KAAK;AAAA,wCAC+B,KAAK,WAAW,MAAM,IAAI,CAAC;AAAA;AAEnE;AAEO,SAAS,oBACd,OACA,MACQ;AACR,QAAM,MAAM,MAAM,OAAO;AACzB,QAAM,MAAM,MAAM;AAClB,SAAO;AAAA,cACK,KAAK,WAAW,GAAG,CAAC,UAAU,KAAK,WAAW,GAAG,CAAC;AAAA,IAC5D,MAAM,MAAM,uCAAuC,KAAK,WAAW,MAAM,GAAG,CAAC,WAAW,EAAE;AAAA;AAE9F;AAEO,SAAS,gBACd,OACA,MACQ;AACR,QAAM,UAAU,KAAK,UAAU,MAAM,MAAM,MAAM,CAAC;AAClD,SAAO;AAAA,iCACwB,KAAK,WAAW,MAAM,IAAI,CAAC;AAAA,uCACrB,KAAK,WAAW,OAAO,CAAC;AAAA;AAE/D;AAEO,SAAS,eAAe,OAAiB,MAA4B;AAC1E,MAAI;AACJ,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,cAAc,OAAO,IAAI;AAChC;AAAA,IACF,KAAK;AACH,aAAO,aAAa,OAAO,IAAI;AAC/B;AAAA,IACF,KAAK;AACH,aAAO,YAAY,OAAO,IAAI;AAC9B;AAAA,IACF,KAAK;AACH,aAAO,cAAc,OAAO,IAAI;AAChC;AAAA,IACF,KAAK;AACH,aAAO,eAAe,OAAO,IAAI;AACjC;AAAA,IACF,KAAK;AACH,aAAO,cAAc,OAAO,IAAI;AAChC;AAAA,IACF,KAAK;AACH,aAAO,iBAAiB,OAAO,IAAI;AACnC;AAAA,IACF,KAAK;AACH,aAAO,iBAAiB,OAAO,IAAI;AACnC;AAAA,IACF,KAAK;AACH,aAAO,oBAAoB,OAAO,IAAI;AACtC;AAAA,IACF,KAAK;AACH,aAAO,gBAAgB,OAAO,IAAI;AAClC;AAAA,IACF;AACE,aAAO;AAAA,EACX;AAEA,MAAI,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAC/C,UAAM,eAAe,MAAM,SACxB,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC,EAC1C,KAAK,EAAE;AACV,YAAQ,6BAA6B,YAAY;AAAA,EACnD;AAEA,SAAO;AACT;;;ACjMA,IAAM,wBAAwB,CAAC,OAAO,OAAO,GAAG;AAczC,SAAS,WACd,MACA,YACA,OACA,MACQ;AACR,QAAM,aAAa,aAAa,KAAK,cAAc,WAAW,MAAM,IAAI;AACxE,QAAM,cAAc,aAAa,UAAU,WAAW,MAAM,KAAK;AACjE,QAAM,WACJ,cAAc,WAAW,aAAa,IAClC,GAAG,WAAW,UAAU,OACxB;AAEN,QAAM,iBAAiB,KAAK,QAAQ,KAAK;AACzC,QAAM,iBAAiB,sBAAsB,SAAS,cAAc;AACpE,QAAM,YAAY,iBAAiB,sBAAsB;AAEzD,QAAM,WAAW,KAAK,WAAW,KAAK,MAAM,WAAW;AAEvD,QAAM,WAAW,KAAK,sBAClB,KAAK,oBAAoB,KAAK,IAAI,IAClC,KAAK,WAAW,KAAK,IAAI;AAE7B,SAAO,eAAe,SAAS,mBAAmB,KAAK,WAAW,cAAc,CAAC,gBAAgB,KAAK,WAAW,KAAK,IAAI,CAAC;AAAA,6BAChG,WAAW,KAAK,UAAU;AAAA,+BACxB,KAAK,WAAW,KAAK,OAAO,CAAC;AAAA,4BAChC,QAAQ;AAAA,gCACJ,QAAQ;AAAA,QAChC,QAAQ;AAChB;AAEO,SAAS,YACd,MACA,MACQ;AACR,QAAM,YAAY,KAAK,MACpB,IAAI,CAAC,MAAM,UAAU;AACpB,UAAM,aAAa,KAAK,YAAY,KAAK,CAAC,OAAO,GAAG,UAAU,KAAK;AACnE,WAAO,WAAW,MAAM,YAAY,OAAO,IAAI;AAAA,EACjD,CAAC,EACA,KAAK,EAAE;AACV,SAAO,sBAAsB,SAAS;AACxC;;;ACjDA,IAAM,qBAAqB;AAEpB,SAAS,oBACd,MACA,MACQ;AACR,QAAM,UAAU,MAAM,KAAK,KAAK,SAAS,kBAAkB,CAAC;AAE5D,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,KAAK,WAAW,IAAI;AAAA,EAC7B;AAEA,MAAI,SAAS;AACb,MAAI,YAAY;AAEhB,aAAW,SAAS,SAAS;AAC3B,UAAM,aAAa,MAAM;AACzB,UAAM,WAAW,aAAa,MAAM,CAAC,EAAE;AAGvC,QAAI,aAAa,WAAW;AAC1B,gBAAU,KAAK,WAAW,KAAK,MAAM,WAAW,UAAU,CAAC;AAAA,IAC7D;AAGA,cAAU,4BAA4B,KAAK,WAAW,MAAM,CAAC,CAAC,CAAC;AAE/D,gBAAY;AAAA,EACd;AAGA,MAAI,YAAY,KAAK,QAAQ;AAC3B,cAAU,KAAK,WAAW,KAAK,MAAM,SAAS,CAAC;AAAA,EACjD;AAEA,SAAO;AACT;;;ACxCO,IAAM,qBAAqB;;;AC8BlC,SAAS,aACP,QACA,UACAC,aACQ;AACR,QAAM,MAAM,OAAO,QAAQ,WAAW,SAAS,QAAQ,YAAY,OAAO,EAAE,IAAI;AAChF,MAAI,KAAK;AACP,WAAO,mCAAmCA,YAAW,GAAG,CAAC,+CAA+CA,YAAW,OAAO,EAAE,CAAC;AAAA,EAC/H;AACA,SAAO,gCAAgCA,YAAW,OAAO,EAAE,CAAC;AAC9D;AAEO,SAAS,eACd,MACA,MACQ;AACR,QAAM,EAAE,GAAG,IAAI;AACf,QAAM,aAAa,KAAK,cAAc,GAAG,MAAM;AAC/C,QAAM,cAAc,UAAU,GAAG,MAAM;AACvC,QAAM,WACJ,GAAG,aAAa,IAAI,IAAI,GAAG,aAAa,KAAM,QAAQ,CAAC,CAAC,MAAM;AAEhE,QAAM,OAAO,GAAG,KACb,IAAI,CAAC,MAAM,qBAAqB,KAAK,WAAW,CAAC,CAAC,SAAS,EAC3D,KAAK,EAAE;AAEV,QAAM,WAAW,GAAG,MAAM,WAAW,CAAC,GACnC,IAAI,CAAC,MAAM,aAAa,GAAG,KAAK,mBAAmB,KAAK,UAAU,CAAC,EACnE,KAAK,EAAE;AAGV,QAAM,WAAY,GAAG,MAAM,MACvB;AACJ,MAAI,aAAa;AACjB,MAAI,UAAU,SAAS;AACrB,UAAM,UAAU,SAAS,QAAQ,MAAM,GAAG,EAAE;AAE5C,UAAM,YAAY,GAAG,MAAM,MAAM;AAAA,MAC/B,CAAC,MACC,EAAE,SAAS,UAAU,EAAE,UAAU;AAAA,IACrC;AACA,QAAI,WAAW;AACb,mBAAa,kCAAkC,KAAK,WAAW,UAAU,GAAG,CAAC,YAAY,KAAK,WAAW,SAAS,OAAO,CAAC,oCAAoC,KAAK,WAAW,OAAO,CAAC;AAAA,IACxL,OAAO;AACL,mBAAa,sCAAsC,KAAK,WAAW,SAAS,OAAO,CAAC,KAAK,KAAK,WAAW,OAAO,CAAC;AAAA,IACnH;AAAA,EACF;AAGA,MAAI,eAAe;AACnB,QAAM,EAAE,QAAQ,IAAI;AACpB,MAAI,WAAW,QAAQ,cAAc,oBAAoB;AACvD,UAAM,QAAQ,QAAQ;AACtB,oBAAgB,8CAA8C,KAAK,wBAAwB,QAAQ,WAAW,KAAK,QAAQ,CAAC,CAAC,MAAM,QAAQ,UAAU,WAAW,KAAK;AAErK,QAAI,QAAQ,mBAAmB,UAAU;AACvC,sBAAgB,mCAAmC,QAAQ,cAAc;AAAA,IAC3E;AAEA,QAAI,QAAQ,qBAAqB,UAAU;AACzC,YAAM,QAAQ,QAAQ,qBAAqB,cAAc,WAAW;AACpE,sBAAgB,4CAA4C,QAAQ,gBAAgB,KAAK,KAAK,IAAI,QAAQ,gBAAgB;AAAA,IAC5H;AAAA,EACF;AAEA,QAAM,YAAY,KAAK,WAAW,GAAG,MAAM,MAAM,YAAY;AAC7D,QAAM,QAAQ,KAAK;AAAA,IACjB,EAAE,OAAO,GAAG,MAAM,OAAO,aAAa,GAAG,YAAY;AAAA,IACrD;AAAA,MACE,YAAY,KAAK;AAAA,MACjB,eAAe,KAAK;AAAA,MACpB,YAAY,KAAK;AAAA,IACnB;AAAA,EACF;AACA,QAAM,QACJ,GAAG,WAAW,YAAY,GAAG,eACzB,KAAK;AAAA,IACH,EAAE,SAAS,GAAG,cAAc,OAAO,GAAG,WAAW;AAAA,IACjD,EAAE,YAAY,KAAK,WAAW;AAAA,EAChC,IACA;AACN,QAAM,cAAc,KAAK;AAAA,IACvB,EAAE,aAAa,GAAG,YAAY;AAAA,IAC9B;AAAA,MACE,YAAY,KAAK;AAAA,MACjB,kBAAkB,KAAK;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,YAAY,KAAK;AAAA,IACrB,EAAE,OAAO,GAAG,MAAM,UAAU;AAAA,IAC5B,EAAE,YAAY,KAAK,WAAW;AAAA,EAChC;AAGA,MAAI,aAAa;AACjB,MAAI,KAAK,oBAAoB,GAAG,cAAc,GAAG,eAAe,WAAW;AACzE,UAAM,WAAW,GAAG,aAAa,IAAI,KAAK,GAAG,UAAU,KAAK;AAC5D,UAAM,OAAO,GAAG,KAAK,gBAAgB,IAAI,GAAG,UAAU,GAAG,QAAQ;AACjE,UAAM,QAAQ,GAAG,GAAG,UAAU,GAAG,GAAG,aAAa,IAAI,IAAI,GAAG,UAAU,KAAK,EAAE;AAC7E,iBAAa,gCAAgC,KAAK,WAAW,IAAI,CAAC,oCAAoC,KAAK,WAAW,KAAK,CAAC;AAAA,EAC9H;AAEA,QAAM,iBAAiB,KAAK,iBAAiB,eAAe;AAC5D,QAAM,eAAe,CAAC,KAAK;AAE3B,SAAO;AAAA,sBACa,cAAc,kBAAkB,GAAG,EAAE;AAAA,2EACgB,YAAY;AAAA;AAAA;AAAA,mCAGpD,WAAW,KAAK,UAAU;AAAA,sCACvB,KAAK,WAAW,GAAG,MAAM,QAAQ,CAAC;AAAA;AAAA,mCAErC,IAAI,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY;AAAA;AAAA;AAAA,oFAGN,GAAG,EAAE;AAAA,0EACf,GAAG,EAAE;AAAA,wCACvC,QAAQ;AAAA;AAAA;AAAA;AAAA,MAI1C,SAAS;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,WAAW;AAAA,MACX,SAAS;AAAA;AAAA;AAGf;;;AC3IA,IAAM,iBAAiB,oBAAI,IAAY,CAAC,MAAM,SAAS,OAAO,CAAC;AAC/D,IAAM,qBAAqB;AAE3B,SAAS,WAAW,QAA0C;AAC5D,SAAO,eAAe,IAAI,MAAM,IAAK,SAAsC;AAC7E;AAEA,SAAS,eAAe,IAAoB;AAC1C,MAAI,MAAM,IAAM,QAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AAChD,SAAO,GAAG,GAAG,QAAQ,CAAC,CAAC;AACzB;AAEA,SAAS,MAAM,OAAe,KAAa,KAAqB;AAC9D,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAC3C;AAEA,SAAS,eAAe,OAAqC;AAC3D,QAAM,SAA2B,CAAC;AAClC,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,QAAI,OAAO,KAAK,WAAW,YAAY,OAAO,KAAK,SAAS,SAAU;AAEtE,QAAI;AACJ,QAAI;AAEJ,QAAI,KAAK,eAAe,QAAQ,KAAK,cAAc,MAAM;AACvD,oBAAc,KAAK;AACnB,mBAAa,KAAK;AAAA,IACpB,WAAW,KAAK,qBAAqB,QAAQ,KAAK,mBAAmB,MAAM;AACzE,oBAAc,KAAK,oBAAoB;AACvC,oBAAc,KAAK,kBAAkB,KAAK,qBAAqB;AAAA,IACjE,OAAO;AACL;AAAA,IACF;AAEA,iBAAa,KAAK,IAAI,GAAG,UAAU;AACnC,QAAI,CAAC,SAAS,WAAW,KAAK,CAAC,SAAS,UAAU,EAAG;AAErD,WAAO,KAAK;AAAA,MACV,QAAQ,KAAK;AAAA,MACb,cAAc,KAAK;AAAA,MACnB,MAAM,KAAK;AAAA,MACX;AAAA,MACA;AAAA,MACA,QAAQ,WAAW,KAAK,MAAM;AAAA,MAC9B,eAAe,KAAK;AAAA,MACpB,YAAY,KAAK;AAAA,IACnB,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,UAAU,OAAqC;AACtD,QAAM,OAAO,oBAAI,IAAsB;AACvC,aAAW,QAAQ,OAAO;AACxB,QAAI,MAAM,KAAK;AACf,QAAI,KAAK,IAAI,GAAG,GAAG;AACjB,UAAI,SAAS;AACb,aAAO,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,MAAM,EAAE,EAAG;AACjD,YAAM,GAAG,KAAK,MAAM,QAAQ,MAAM;AAAA,IACpC;AACA,SAAK,IAAI,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,IAAI,GAAG,UAAU,CAAC,GAAG,OAAO,EAAE,CAAC;AAAA,EAC1E;AAEA,QAAM,QAAoB,CAAC;AAC3B,aAAW,QAAQ,KAAK,OAAO,GAAG;AAChC,UAAM,WAAW,KAAK,KAAK;AAC3B,UAAM,SAAS,WAAW,KAAK,IAAI,QAAQ,IAAI;AAC/C,QAAI,UAAU,WAAW,MAAM;AAC7B,aAAO,SAAS,KAAK,IAAI;AAAA,IAC3B,OAAO;AACL,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AAGA,aAAW,QAAQ,KAAK,OAAO,GAAG;AAChC,SAAK,SAAS,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW;AAAA,EACtE;AACA,QAAM,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW;AAG5D,QAAM,UAAU,oBAAI,IAAY;AAChC,WAAS,YAAY,MAAgB,OAAqB;AACxD,QAAI,QAAQ,IAAI,KAAK,KAAK,MAAM,EAAG;AACnC,YAAQ,IAAI,KAAK,KAAK,MAAM;AAC5B,SAAK,QAAQ;AACb,eAAW,SAAS,KAAK,UAAU;AACjC,kBAAY,OAAO,QAAQ,CAAC;AAAA,IAC9B;AAAA,EACF;AACA,aAAW,QAAQ,OAAO;AACxB,gBAAY,MAAM,CAAC;AAAA,EACrB;AAGA,aAAW,QAAQ,KAAK,OAAO,GAAG;AAChC,QAAI,CAAC,QAAQ,IAAI,KAAK,KAAK,MAAM,GAAG;AAClC,WAAK,WAAW,CAAC;AACjB,YAAM,KAAK,IAAI;AACf,kBAAY,MAAM,CAAC;AAAA,IACrB;AAAA,EACF;AACA,QAAM,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW;AAE5D,SAAO;AACT;AAEA,SAAS,YAAY,OAA+B;AAClD,QAAM,SAAqB,CAAC;AAC5B,WAAS,KAAK,MAAsB;AAClC,WAAO,KAAK,IAAI;AAChB,eAAW,SAAS,KAAK,UAAU;AACjC,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AACA,aAAW,QAAQ,OAAO;AACxB,SAAK,IAAI;AAAA,EACX;AACA,SAAO;AACT;AAEA,SAAS,aACP,MACAC,aACQ;AACR,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,GAAG,KAAK,IAAI,KAAK,eAAe,KAAK,UAAU,CAAC,GAAG;AAE9D,MAAI,KAAK,eAAe;AACtB,UAAM,KAAK,WAAW,KAAK,aAAa,EAAE;AAAA,EAC5C;AAEA,MAAI,KAAK,YAAY;AACnB,UAAM,OAAO,OAAO,KAAK,KAAK,UAAU,EAAE,KAAK;AAC/C,eAAW,OAAO,MAAM;AACtB,YAAM,MAAM,KAAK,WAAW,GAAG;AAC/B,YAAM,YAAY,MAAM,QAAQ,GAAG,IAC/B,IAAI,IAAI,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,MACxC,OAAO,GAAG;AACd,YAAM,KAAK,GAAG,GAAG,IAAI,SAAS,EAAE;AAAA,IAClC;AAAA,EACF;AAEA,MAAI,OAAO,MAAM,KAAK,IAAI;AAC1B,MAAI,KAAK,SAAS,oBAAoB;AACpC,WAAO,KAAK,MAAM,GAAG,qBAAqB,CAAC,IAAI;AAAA,EACjD;AAEA,SAAOA,YAAW,IAAI;AACxB;AAEO,SAAS,gBACd,MACA,MACQ;AACR,MAAI,CAAC,KAAK,SAAS,KAAK,MAAM,WAAW,EAAG,QAAO;AAEnD,QAAM,aAAa,eAAe,KAAK,KAAK;AAC5C,MAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,QAAM,QAAQ,UAAU,UAAU;AAClC,QAAM,OAAO,YAAY,KAAK;AAG9B,MAAI,WAAW;AACf,MAAI,SAAS;AACb,aAAW,QAAQ,MAAM;AACvB,UAAM,IAAI,KAAK,KAAK;AACpB,UAAM,IAAI,IAAI,KAAK,KAAK;AACxB,QAAI,IAAI,SAAU,YAAW;AAC7B,QAAI,IAAI,OAAQ,UAAS;AAAA,EAC3B;AACA,MAAI,gBAAgB,SAAS;AAC7B,MAAI,iBAAiB,EAAG,iBAAgB;AAGxC,QAAM,OAAO,KACV,IAAI,CAAC,SAAS;AACb,UAAM,EAAE,MAAM,MAAM,IAAI;AACxB,UAAM,SAAS,QAAQ;AACvB,UAAM,WAAW;AACjB,QAAI,WAAW;AAAA,OACX,KAAK,cAAc,YAAY,gBAAiB;AAAA,MAClD;AAAA,MACA;AAAA,IACF;AAEA,QAAI,WAAW,WAAW,KAAK;AAC7B,iBAAW,MAAM;AAAA,IACnB;AACA,UAAM,YAAY;AAAA,MACf,KAAK,aAAa,gBAAiB;AAAA,MACpC;AAAA,MACA,MAAM;AAAA,IACR;AACA,UAAM,UAAU,aAAa,MAAM,KAAK,UAAU;AAClD,UAAM,gBAAgB,eAAe,KAAK,UAAU;AAEpD,WAAO;AAAA,0DAC6C,MAAM,cAAc,KAAK,WAAW,KAAK,IAAI,CAAC;AAAA,+DACzC,KAAK,MAAM;AAAA,UAChE,KAAK,WAAW,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA,oDAGgB,KAAK,MAAM,kBAAkB,SAAS,QAAQ,CAAC,CAAC,aAAa,UAAU,QAAQ,CAAC,CAAC,aAAa,OAAO,KAAK,aAAa;AAAA;AAAA;AAAA,EAGvK,CAAC,EACA,KAAK,IAAI;AAEZ,QAAM,UAAU,eAAe,SAAS,QAAQ;AAEhD,SAAO;AAAA;AAAA;AAAA,qCAG4B,KAAK,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAMlC,OAAO;AAAA;AAAA,EAEnB,IAAI;AAAA;AAAA;AAGN;;;AC1OO,SAAS,cACd,MACA,MACQ;AACR,QAAM,EAAE,MAAM,UAAU,IAAI;AAC5B,QAAM,SAAS,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AAChE,QAAM,SAAS,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AAChE,QAAM,UAAU,UAAU;AAAA,IACxB,CAAC,OAAO,GAAG,WAAW,aAAa,GAAG,WAAW;AAAA,EACnD,EAAE;AAEF,QAAM,aAAa,UAChB,IAAI,CAAC,OAAO,GAAG,SAAS,EACxB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7B,QAAM,cACJ,WAAW,SAAS,KAAK,WAAW,CAAC,EAAE,SAAS,IAC5C,WAAW,CAAC,EAAE,CAAC,IACf,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,QAAQ,YAAY,EAAE,KAAK;AAExD,QAAM,iBAAiB,KAAK,iBAAiB,eAAe;AAC5D,QAAM,eAAe,CAAC,KAAK;AAC3B,QAAM,cAAc,WAAW,QAAQ,IAAI,CAAC;AAE5C,QAAM,YAAY,UACf;AAAA,IAAI,CAAC,OACJ,KAAK;AAAA,MACH,EAAE,IAAI,SAAS,KAAK,YAAY,IAAI,GAAG,EAAE,EAAE;AAAA,MAC3C,KAAK;AAAA,IACP;AAAA,EACF,EACC,KAAK,IAAI;AAEZ,SAAO;AAAA,qBACY,cAAc,SAAS,WAAW;AAAA,0EACmB,YAAY;AAAA,+DACvB,WAAW;AAAA;AAAA,mCAEvC,KAAK,WAAW,WAAW,CAAC;AAAA,kCAC7B,KAAK,WAAW,IAAI,CAAC;AAAA;AAAA;AAAA,yCAGnB,MAAM;AAAA,yCACN,MAAM;AAAA,0CACL,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAKtC,SAAS;AAAA;AAAA;AAGf;;;ACjEA,SAASC,SAAc,OAAY,OAAoC;AACrE,QAAM,MAAM,oBAAI,IAAY;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,MAAM,IAAI;AACtB,UAAM,WAAW,IAAI,IAAI,GAAG;AAC5B,QAAI,UAAU;AACZ,eAAS,KAAK,IAAI;AAAA,IACpB,OAAO;AACL,UAAI,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAgCO,SAAS,UAAU,MAAqB,MAA6B;AAC1E,QAAM,EAAE,IAAI,IAAI;AAEhB,QAAM,QAAkB,CAAC;AAEzB,QAAM;AAAA,IACJ,KAAK;AAAA,MACH;AAAA,QACE,aAAa,IAAI;AAAA,QACjB,YAAY,IAAI;AAAA,QAChB,gBAAgB,IAAI;AAAA,QACpB,QAAQ,IAAI;AAAA,QACZ,QAAQ,IAAI,IAAI;AAAA,QAChB,UAAU,IAAI,IAAI;AAAA,QAClB,OAAO,IAAI,IAAI;AAAA,QACf,aAAa,IAAI,IAAI;AAAA,QACrB,eAAe,IAAI,IAAI;AAAA,MACzB;AAAA,MACA,KAAK;AAAA,IACP;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,UAAU;AAC5B,QAAM,SAAS,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AACpE,QAAM,SAAS,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AACpE,QAAM,UAAU,IAAI,UAAU;AAAA,IAC5B,CAAC,OAAO,GAAG,WAAW,aAAa,GAAG,WAAW;AAAA,EACnD,EAAE;AACF,QAAM;AAAA,IACJ,KAAK;AAAA,MACH,EAAE,OAAO,QAAQ,QAAQ,QAAQ;AAAA,MACjC,KAAK;AAAA,IACP;AAAA,EACF;AAEA,QAAM,UAAU;AAAA,IACd,GAAG,IAAI,IAAI,IAAI,UAAU,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;AAAA,EACnD,EAAE,KAAK;AACP,QAAM;AAAA,IACJ,KAAK;AAAA,MACH,EAAE,MAAM,SAAS,gBAAgB,MAAM;AAAA,MACvC,KAAK;AAAA,IACP;AAAA,EACF;AAEA,QAAM,cAAc,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ;AACvE,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM;AAAA,MACJ,KAAK;AAAA,QACH,EAAE,YAAY;AAAA,QACd,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAASA,SAAQ,IAAI,WAAW,CAAC,OAAO,GAAG,UAAU;AAC3D,aAAW,CAAC,MAAM,SAAS,KAAK,QAAQ;AACtC,UAAM;AAAA,MACJ,KAAK;AAAA,QACH,EAAE,MAAM,WAAW,YAAY,KAAK,WAAW;AAAA,QAC/C,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC1GO,SAAS,qBACd,MACA,MACQ;AACR,QAAM,EAAE,YAAY,IAAI;AACxB,MAAI,YAAY,WAAW,EAAG,QAAO;AAErC,QAAM,QAAQ,YACX,IAAI,CAAC,OAAO;AACX,UAAM,OAAO,KAAK,WAAW,GAAG,MAAM,QAAQ;AAC9C,WAAO,0BAA0B,GAAG,EAAE,KAAK,IAAI;AAAA,EACjD,CAAC,EACA,KAAK,UAAU;AAElB,SAAO;AAAA;AAAA,gDAEuC,YAAY,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,QAK1D,KAAK;AAAA;AAAA;AAGb;;;ACtBA,SAASC,SAAc,OAAY,OAAoC;AACrE,QAAM,MAAM,oBAAI,IAAY;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,MAAM,IAAI;AACtB,UAAM,WAAW,IAAI,IAAI,GAAG;AAC5B,QAAI,UAAU;AACZ,eAAS,KAAK,IAAI;AAAA,IACpB,OAAO;AACL,UAAI,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,UAAU,MAAqB,MAA6B;AAC1E,QAAM,EAAE,IAAI,IAAI;AAChB,MAAI,IAAI,UAAU,WAAW,EAAG,QAAO;AAEvC,QAAM,SAASA,SAAQ,IAAI,WAAW,CAAC,OAAO,GAAG,UAAU;AAC3D,QAAM,WAAqB,CAAC;AAE5B,aAAW,CAAC,MAAM,SAAS,KAAK,QAAQ;AACtC,UAAM,aAAa,UAChB,IAAI,CAAC,OAAO,GAAG,SAAS,EACxB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7B,UAAM,cACJ,WAAW,SAAS,KAAK,WAAW,CAAC,EAAE,SAAS,IAC5C,WAAW,CAAC,EAAE,CAAC,IACf,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,QAAQ,YAAY,EAAE,KAAK;AAExD,UAAM,cAAc,WAAW,QAAQ,IAAI,CAAC;AAE5C,UAAM,YAAY,UACf,IAAI,CAAC,OAAO;AACX,YAAM,aAAa,KAAK,cAAc,GAAG,MAAM;AAC/C,YAAM,cAAc,UAAU,GAAG,MAAM;AACvC,YAAM,cAAc,GAAG,WAAW,WAAW,gBAAgB;AAC7D,aAAO,yBAAyB,WAAW,qBAAqB,GAAG,EAAE;AAAA,oCACzC,WAAW,KAAK,UAAU;AAAA,YAClD,KAAK,WAAW,GAAG,MAAM,QAAQ,CAAC;AAAA;AAAA,IAExC,CAAC,EACA,KAAK,IAAI;AAEZ,aAAS,KAAK;AAAA,mSACiR,WAAW;AAAA,UACpS,KAAK,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA,UAG5B,SAAS;AAAA;AAAA,WAER;AAAA,EACT;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAKH,SAAS,KAAK,IAAI,CAAC;AAAA;AAAA;AAGzB;;;AC9BA,SAAS,iBAAiB,UAAgC,CAAC,GAAG;AAC5D,SAAO;AAAA,IACL,OAAO,QAAQ,SAAS;AAAA,IACxB,UAAU,QAAQ,YAAY;AAAA,IAC9B,YAAY,QAAQ,cAAc;AAAA,IAClC,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,kBAAkB,QAAQ,oBAAoB;AAAA,IAC9C,oBAAoB,QAAQ,sBAAsB;AAAA,IAClD,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,kBAAkB,QAAQ;AAAA,IAC1B,mBAAmB,QAAQ;AAAA,IAC3B,YAAY,QAAQ,cAAc;AAAA,IAClC,OAAO,QAAQ,SAAS;AAAA,IACxB,oBAAoB,QAAQ,sBAAsB;AAAA,EACpD;AACF;AAKO,SAAS,oBACd,UAAgC,CAAC,GACO;AACxC,QAAM,OAAO,iBAAiB,OAAO;AAErC,QAAM,eAAe;AAAA,IACnB;AAAA,IACA,oBAAoB,KAAK;AAAA,IACzB,iBAAiB,KAAK;AAAA,IACtB,gBAAgB,KAAK;AAAA,EACvB;AAEA,QAAM,aAAa,CACjB,MACA,mBACW;AACX,QAAI,CAAC,QAAQ,KAAK,WAAW,EAAG,QAAO;AACvC,UAAM,UAAU,KAAK,IAAI,CAAC,UAAU,eAAe,OAAO,YAAY,CAAC,EAAE,KAAK,EAAE;AAChF,WAAO,eAAe,cAAc,KAAK,OAAO;AAAA,EAClD;AAEA,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,qBAAqB,CAAC,SACpB,oBAAoB,MAAM,EAAE,WAAW,CAAC;AAAA,EAC5C;AAEA,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA,gBAAgB,KAAK;AAAA,IACrB,aAAa,CAAC,SACZ,YAAY,MAAM,SAAS;AAAA,IAC7B;AAAA,IACA,gBAAgB,CACd,MACA,MACG,eAAe,MAAM,CAAC;AAAA,IAC3B,mBAAmB,CACjB,MACA,MACG,kBAAkB,MAAM,CAAC;AAAA,IAC9B,iBAAiB,CACf,MACA,MACG,gBAAgB,MAAM,CAAC;AAAA,IAC5B,kBAAkB,KAAK;AAAA,IACvB,kBAAkB,KAAK;AAAA,IACvB,mBAAmB,KAAK;AAAA,EAC1B;AAEA,QAAM,cAAc;AAAA,IAClB;AAAA,IACA,gBAAgB,KAAK;AAAA,IACrB,gBAAgB,CAAC,SACf,eAAe,MAAM,YAAY;AAAA,IACnC;AAAA,EACF;AAEA,QAAM,aAAa,EAAE,WAAW;AAEhC,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,EACF;AAEA,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,EAAE,WAAW;AAAA,IACvB,aAAa,CAAC;AAAA,IACd;AAAA,IACA;AAAA,IACA,oBAAoB,EAAE,WAAW;AAAA,EACnC;AAEA,QAAM,QAAQ,aAAa,KAAK,KAAK;AAErC,SAAO;AAAA,IACL,OAAO,KAA4B;AACjC,YAAM,SAAS,MAAM,aAAa;AAClC,YAAM,OAAO,OAAO,EAAE,IAAI,GAAG,QAAQ;AACrC,YAAM,aAAa,MAAM,oBAAoB;AAG7C,YAAM,oBAAoB,CAAC,EAAE,MAAM,aAAa,MAAM;AACtD,YAAM,UAAU,KAAK,cAAc,CAAC,oBAAoB,UAAU,EAAE,IAAI,GAAG,OAAO,IAAI;AAEtF,UAAI;AACJ,UAAI;AAEJ,UAAI,KAAK,oBAAoB;AAC3B,cAAM,gBAAgB,iBAAiB;AACvC,cAAM,gBAAgB,cACnB,IAAI,OAAK,kBAAkB,EAAE,IAAI,IAAI,EAAE,SAAS,MAAM,OAAO,cAAc,EAAE,IAAI,EAAE,KAAK,WAAW,EACnG,KAAK,EAAE;AACV,0BAAkB,0DAA0D,aAAa;AACzF,6BAAqB,cAClB,OAAO,OAAK,EAAE,SAAS,MAAM,IAAI,EACjC,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,OAAO,KAAK,EAAE,IAAI,EAAE;AAAA,MAC5D;AAEA,aAAO;AAAA,QACL,KAAK;AAAA,QACL,MAAM;AAAA,QACN;AAAA,QACA;AAAA,UACE,eAAe,KAAK;AAAA,UACpB,iBAAiB,KAAK;AAAA,UACtB,oBAAoB,KAAK;AAAA,UACzB,gBAAgB,KAAK;AAAA,UACrB,iBAAiB,KAAK;AAAA,UACtB,cAAc,MAAM;AAAA,UACpB,mBAAmB,MAAM;AAAA,UACzB;AAAA,UACA;AAAA,UACA;AAAA,UACA,iBAAiB,MAAM;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC/IO,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EAER,YAAY,UAAuB,CAAC,GAAG;AACrC,UAAM,QAAQ,oBAAoB,OAAO;AACzC,SAAK,WAAW,MAAM,OAAO,KAAK,KAAK;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,KAA4B;AACjC,WAAO,KAAK,SAAS,GAAG;AAAA,EAC1B;AACF;;;AC5CO,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EAER,YAAY,UAAwB,CAAC,GAAG;AACtC,SAAK,UAAU;AAAA,MACb,WAAW,QAAQ,aAAa;AAAA,MAChC,eAAe,QAAQ,iBAAiB;AAAA,MACxC,QAAQ,QAAQ,UAAU;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,KAA4B;AACjC,UAAM,SAAS,KAAK,QAAQ,SAAS,OAAO;AAC5C,UAAM,UAAU,KAAK,QAAQ,SAAS,OAAO;AAG7C,UAAM,QAAQ,IAAI,UAAU;AAC5B,UAAM,WAAW,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AACtE,UAAM,UAAU,IAAI,UAAU;AAAA,MAC5B,CAAC,OAAO,GAAG,WAAW,aAAa,GAAG,WAAW;AAAA,IACnD,EAAE;AACF,UAAM,SAAS;AACf,UAAM,QAAQ,IAAI,aAAa,KAAM,QAAQ,CAAC;AAG9C,UAAM,QAAkB,CAAC;AACzB,UAAM,KAAK,wCAAwC;AACnD,UAAM;AAAA,MACJ,qBAAqB,UAAU,KAAK,QAAQ,SAAS,CAAC,YAAY,KAAK,eAAe,QAAQ,aAAa,MAAM,cAAc,OAAO,WAAW,IAAI;AAAA,IACvJ;AAGA,UAAM,SAASC,SAAQ,IAAI,WAAW,CAAC,OAAO,GAAG,UAAU;AAE3D,eAAW,CAAC,MAAM,SAAS,KAAK,QAAQ;AACtC,YAAM,KAAK,GAAG,KAAK,eAAe,MAAM,WAAW,QAAQ,OAAO,CAAC;AAAA,IACrE;AAEA,UAAM,KAAK,eAAe;AAE1B,WAAO,MAAM,KAAK,OAAO;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKQ,eACN,MACA,WACA,QACA,SACU;AACV,UAAM,QAAkB,CAAC;AAEzB,UAAM,QAAQ,UAAU;AACxB,UAAM,WAAW,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AAClE,UAAM,UAAU,UAAU;AAAA,MACxB,CAAC,OAAO,GAAG,WAAW,aAAa,GAAG,WAAW;AAAA,IACnD,EAAE;AACF,UAAM,OAAO,UACV,OAAO,CAAC,KAAK,OAAO,MAAM,GAAG,YAAY,CAAC,IAAI;AAGjD,UAAM,iBAAiB,KAAK,QAAQ,OAAO,GAAG;AAE9C,UAAM;AAAA,MACJ,GAAG,MAAM,oBAAoB,UAAU,cAAc,CAAC,YAAY,KAAK,eAAe,QAAQ,yBAAyB,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC;AAAA,IAC1J;AAEA,eAAW,MAAM,WAAW;AAC1B,YAAM,KAAK,GAAG,KAAK,cAAc,IAAI,SAAS,QAAQ,OAAO,CAAC;AAAA,IAChE;AAEA,UAAM,KAAK,GAAG,MAAM,cAAc;AAElC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,cACN,IACA,QACA,SACU;AACV,UAAM,QAAkB,CAAC;AAIzB,UAAM,YAAY,GAAG,UAAU,SAAS,IACpC,GAAG,UAAU,KAAK,GAAG,IACrB,GAAG,WACA,QAAQ,WAAW,GAAG,EACtB,QAAQ,YAAY,EAAE;AAE7B,UAAM,OAAO,GAAG,MAAM;AACtB,UAAM,QAAQ,GAAG,aAAa,KAAM,QAAQ,CAAC;AAE7C,UAAM,aAAa,GAAG,WAAW;AACjC,UAAM,aAAa,GAAG,WAAW,aAAa,GAAG,WAAW;AAC5D,UAAM,YAAY,KAAK,QAAQ,iBAAiB,GAAG,MAAM,MAAM,SAAS;AAGxE,QAAI,cAAc,cAAc,WAAW;AACzC,YAAM;AAAA,QACJ,GAAG,MAAM,wBAAwB,UAAU,SAAS,CAAC,WAAW,UAAU,IAAI,CAAC,WAAW,IAAI;AAAA,MAChG;AAEA,UAAI,YAAY;AACd,cAAM,UAAU,GAAG,eACf,UAAU,GAAG,aAAa,MAAM,IAAI,EAAE,CAAC,CAAC,IACxC;AACJ,cAAM,KAAK,GAAG,MAAM,GAAG,MAAM,qBAAqB,OAAO,IAAI;AAC7D,YAAI,GAAG,cAAc;AACnB,gBAAM,KAAK,UAAU,GAAG,YAAY,CAAC;AAAA,QACvC;AACA,YAAI,GAAG,YAAY;AACjB,gBAAM,KAAK,EAAE;AACb,gBAAM,KAAK,UAAU,GAAG,UAAU,CAAC;AAAA,QACrC;AACA,cAAM,KAAK,GAAG,MAAM,GAAG,MAAM,YAAY;AAAA,MAC3C,WAAW,YAAY;AACrB,cAAM,UAAU,GAAG,WAAW,YAAY,iBAAiB;AAC3D,cAAM,KAAK,GAAG,MAAM,GAAG,MAAM,qBAAqB,OAAO,KAAK;AAAA,MAChE;AAGA,UAAI,WAAW;AACb,cAAM,SAAS,KAAK,eAAe,EAAE;AACrC,cAAM,KAAK,GAAG,MAAM,GAAG,MAAM,eAAe,UAAU,MAAM,CAAC,eAAe;AAAA,MAC9E;AAEA,YAAM,KAAK,GAAG,MAAM,aAAa;AAAA,IACnC,OAAO;AAEL,YAAM;AAAA,QACJ,GAAG,MAAM,wBAAwB,UAAU,SAAS,CAAC,WAAW,UAAU,IAAI,CAAC,WAAW,IAAI;AAAA,MAChG;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,IAA4B;AACjD,UAAM,cAAwB,CAAC;AAG/B,QAAI,GAAG,MAAM,QAAQ,GAAG,MAAM,KAAK,SAAS,GAAG;AAC7C,iBAAW,OAAO,GAAG,MAAM,MAAM;AAC/B,oBAAY,KAAK,KAAK,eAAe,GAAG,CAAC;AAAA,MAC3C;AACA,kBAAY,KAAK,EAAE;AAAA,IACrB;AAGA,eAAW,QAAQ,GAAG,MAAM,OAAO;AACjC,kBAAY,KAAK,KAAK,WAAW,IAAI,CAAC;AAAA,IACxC;AAEA,WAAO,YAAY,KAAK,IAAI,EAAE,KAAK;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW,MAAyB;AAC1C,UAAM,QAAkB,CAAC;AACzB,UAAM,KAAK,GAAG,KAAK,OAAO,IAAI,KAAK,IAAI,EAAE;AAGzC,QAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;AACrC,iBAAW,OAAO,KAAK,MAAM;AAC3B,cAAM,WAAW,KAAK,eAAe,KAAK,IAAI;AAC9C,YAAI,UAAU;AACZ,gBAAM,KAAK,QAAQ;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAiB,SAAS,IAAY;AAC3D,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,eAAO,GAAG,MAAM,KAAK,MAAM,IAAI;AAAA,MAEjC,KAAK;AACH,eAAO,GAAG,MAAM,SAAS,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,MAEjD,KAAK,MAAM;AACT,cAAM,MAAM,OAAO,MAAM,UAAU,WAC/B,MAAM,QACN,KAAK,UAAU,MAAM,KAAK;AAC9B,eAAO,GAAG,MAAM,GAAG,MAAM,KAAK,KAAK,GAAG;AAAA,MACxC;AAAA,MAEA,KAAK,QAAQ;AACX,cAAM,YAAY,MAAM,OAAO,KAAK,MAAM,IAAI,MAAM;AACpD,cAAM,SAAS,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,KAAK,GAAG,SAAS;AAAA,IAAQ;AACxE,cAAM,YAAY,MAAM,QAAQ,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,GAAG,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI;AACnF,eAAO,GAAG,MAAM,GAAG,SAAS;AAAA,MAC9B;AAAA,MAEA,KAAK,SAAS;AACZ,cAAM,QAAkB,CAAC;AACzB,YAAI,MAAM,OAAO;AACf,gBAAM,KAAK,GAAG,MAAM,GAAG,MAAM,KAAK,GAAG;AAAA,QACvC;AACA,cAAM,KAAK,GAAG,MAAM,KAAK,MAAM,QAAQ,KAAK,KAAK,CAAC,IAAI;AACtD,cAAM,KAAK,GAAG,MAAM,KAAK,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,KAAK,KAAK,CAAC,IAAI;AACvE,mBAAW,OAAO,MAAM,MAAM;AAC5B,gBAAM,KAAK,GAAG,MAAM,KAAK,IAAI,KAAK,KAAK,CAAC,IAAI;AAAA,QAC9C;AACA,eAAO,MAAM,KAAK,IAAI;AAAA,MACxB;AAAA,MAEA,KAAK;AACH,eAAO,GAAG,MAAM,GAAG,MAAM,KAAK,KAAK,MAAM,GAAG;AAAA,MAE9C,KAAK,WAAW;AACd,cAAM,QAAkB,CAAC;AACzB,cAAM,KAAK,GAAG,MAAM,GAAG,MAAM,KAAK,GAAG;AACrC,mBAAW,QAAQ,MAAM,SAAS,MAAM,IAAI,GAAG;AAC7C,gBAAM,KAAK,GAAG,MAAM,KAAK,IAAI,EAAE;AAAA,QACjC;AACA,eAAO,MAAM,KAAK,IAAI;AAAA,MACxB;AAAA,MAEA,KAAK,WAAW;AACd,cAAM,QAAkB,CAAC;AACzB,YAAI,MAAM,OAAO;AACf,gBAAM,KAAK,GAAG,MAAM,GAAG,MAAM,KAAK,GAAG;AAAA,QACvC;AACA,mBAAW,QAAQ,MAAM,KAAK,MAAM,IAAI,GAAG;AACzC,gBAAM,KAAK,GAAG,MAAM,KAAK,IAAI,EAAE;AAAA,QACjC;AACA,eAAO,MAAM,KAAK,IAAI;AAAA,MACxB;AAAA,MAEA,KAAK;AACH,eAAO,GAAG,MAAM,eAAe,MAAM,OAAO,MAAM,IAAI;AAAA,MAExD,KAAK,UAAU;AACb,cAAM,UAAU,KAAK,UAAU,MAAM,MAAM,MAAM,CAAC;AAClD,cAAM,QAAkB,CAAC;AACzB,cAAM,KAAK,GAAG,MAAM,IAAI,MAAM,IAAI,IAAI;AACtC,mBAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,gBAAM,KAAK,GAAG,MAAM,KAAK,IAAI,EAAE;AAAA,QACjC;AACA,eAAO,MAAM,KAAK,IAAI;AAAA,MACxB;AAAA,MAEA;AACE,eAAO;AAAA,IACX;AAAA,EACF;AACF;AAKA,SAAS,UAAU,KAAqB;AACtC,SAAO,IACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAC3B;AAKA,SAASA,SAAc,OAAY,OAAoC;AACrE,QAAM,MAAM,oBAAI,IAAY;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,MAAM,IAAI;AACtB,UAAM,WAAW,IAAI,IAAI,GAAG;AAC5B,QAAI,UAAU;AACZ,eAAS,KAAK,IAAI;AAAA,IACpB,OAAO;AACL,UAAI,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;;;AC5PO,IAAM,oBAAN,MAAwB;AAAA,EACrB;AAAA,EAER,YAAY,UAA2B,CAAC,GAAG;AACzC,SAAK,UAAU;AAAA,MACb,OAAO,QAAQ,SAAS;AAAA,MACxB,oBAAoB,QAAQ,sBAAsB;AAAA,MAClD,iBAAiB,QAAQ,mBAAmB;AAAA,MAC5C,eAAe,QAAQ,iBAAiB;AAAA,MACxC,sBAAsB,QAAQ,wBAAwB;AAAA,MACtD,WAAW,QAAQ,aAAa;AAAA,MAChC,SAAS,QAAQ,WAAW;AAAA,MAC5B,eAAe,QAAQ,iBAAiB;AAAA,MACxC,gBAAgB,QAAQ,kBAAkB;AAAA,MAC1C,oBAAoB,QAAQ,sBAAsB;AAAA,MAClD,qBAAqB,QAAQ,uBAAuB;AAAA,MACpD,kBAAkB,QAAQ;AAAA,MAC1B,mBAAmB,QAAQ;AAAA,MAC3B,kBAAkB,QAAQ;AAAA,MAC1B,oBAAoB,QAAQ,sBAAsB;AAAA,MAClD,iBAAiB,QAAQ;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,KAA4B;AACjC,UAAM,QAAkB,CAAC;AAGzB,QAAI,KAAK,QAAQ,oBAAoB;AACnC,WAAK,kBAAkB,OAAO,GAAG;AAAA,IACnC;AAGA,UAAM,KAAK,KAAK,KAAK,QAAQ,KAAK,EAAE;AACpC,UAAM,KAAK,EAAE;AAGb,QAAI,KAAK,QAAQ,iBAAiB;AAChC,WAAK,eAAe,OAAO,GAAG;AAC9B,YAAM,KAAK,EAAE;AAAA,IACf;AAGA,QAAI,KAAK,QAAQ,qBAAqB;AACpC,WAAK,mBAAmB,OAAO,GAAG;AAClC,YAAM,KAAK,EAAE;AAAA,IACf;AAGA,YAAQ,KAAK,QAAQ,SAAS;AAAA,MAC5B,KAAK;AACH,aAAK,eAAe,OAAO,IAAI,SAAS;AACxC;AAAA,MACF,KAAK;AACH,aAAK,cAAc,OAAO,IAAI,SAAS;AACvC;AAAA,MACF,KAAK;AAAA,MACL;AACE,aAAK,aAAa,OAAO,IAAI,SAAS;AACtC;AAAA,IACJ;AAGA,QAAI,KAAK,QAAQ,iBAAiB,cAAc;AAC9C,YAAM,SAAS,KAAK,QAAQ,gBAAgB,aAAa,GAAG;AAC5D,UAAI,QAAQ;AACV,cAAM,KAAK,EAAE;AACb,cAAM,KAAK,MAAM;AAAA,MACnB;AAAA,IACF;AAEA,WAAO,MAAM,KAAK,IAAI,EAAE,QAAQ;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,OAAiB,KAA0B;AACnE,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK,QAAQ;AAAA,MACpB,aAAa,IAAI,KAAK,IAAI,WAAW,EAAE,YAAY;AAAA,MACnD,YAAY,IAAI;AAAA,MAChB,WAAW,IAAI,UAAU;AAAA,MACzB,QAAQ,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AAAA,MAC7D,QAAQ,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AAAA,MAC7D,SAAS,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,SAAS,EAAE;AAAA,MAC/D,SAAS,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,SAAS,EAAE;AAAA,IACjE;AAEA,QAAI,IAAI,eAAgB,MAAK,UAAU,IAAI;AAC3C,QAAI,IAAI,OAAQ,MAAK,SAAS,IAAI,OAAO,SAAS,IAAI,IAAI,OAAO,MAAM,GAAG,CAAC,IAAI,IAAI;AACnF,QAAI,IAAI,SAAU,MAAK,WAAW,IAAI;AAEtC,UAAM,KAAK,KAAK;AAChB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,UAAI,UAAU,OAAW;AACzB,UAAI,OAAO,UAAU,UAAU;AAC7B,cAAM,KAAK,GAAG,GAAG,GAAG;AACpB,mBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrE,gBAAM,KAAK,KAAK,CAAC,KAAK,CAAC,EAAE;AAAA,QAC3B;AAAA,MACF,OAAO;AACL,cAAM,KAAK,GAAG,GAAG,KAAK,KAAK,EAAE;AAAA,MAC/B;AAAA,IACF;AACA,UAAM,KAAK,KAAK;AAChB,UAAM,KAAK,EAAE;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,OAAiB,KAA0B;AACpE,UAAM,iBAAiB,IAAI,UAAU;AACrC,UAAM,aAAa,IAAI,UAAU,OAAO,CAAC,KAAK,OAAO,MAAM,GAAG,MAAM,MAAM,QAAQ,CAAC;AACnF,UAAM,SAAS,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AACpE,UAAM,SAAS,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AACpE,UAAM,UAAU,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,SAAS,EAAE;AACtE,UAAM,UAAU,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,SAAS,EAAE;AAEtE,UAAM,KAAK,wEAAwE;AACnF,UAAM,KAAK,oDAAoD;AAC/D,UAAM,KAAK,KAAK,cAAc,MAAM,UAAU,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO,MAAM,OAAO,MAAM,KAAK,eAAe,IAAI,UAAU,CAAC,IAAI;AAG7I,QAAI,IAAI,UAAU;AAChB,YAAM,KAAK,EAAE;AACb,YAAM,KAAK,kBAAkB;AAC7B,YAAM,KAAK,gBAAgB;AAC3B,UAAI,IAAI,SAAS,kBAAkB,QAAW;AAC5C,cAAM,KAAK,kBAAkB,IAAI,SAAS,aAAa,KAAK;AAAA,MAC9D;AACA,UAAI,IAAI,SAAS,gBAAgB,QAAW;AAC1C,cAAM,KAAK,gBAAgB,IAAI,SAAS,WAAW,KAAK;AAAA,MAC1D;AACA,UAAI,IAAI,SAAS,iBAAiB,QAAW;AAC3C,cAAM,KAAK,iBAAiB,IAAI,SAAS,YAAY,KAAK;AAAA,MAC5D;AACA,UAAI,IAAI,SAAS,aAAa,QAAW;AACvC,cAAM,KAAK,aAAa,IAAI,SAAS,QAAQ,KAAK;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,IAAoB;AACzC,QAAI,KAAK,IAAM,QAAO,GAAG,EAAE;AAC3B,WAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAiB,KAA0B;AAChE,UAAM,OAAgC,CAAC;AAEvC,UAAM,YAAY,IAAI,KAAK,IAAI,WAAW;AAC1C,SAAK,KAAK,CAAC,QAAQ,UAAU,YAAY,CAAC,CAAC;AAE3C,QAAI,IAAI,gBAAgB;AACtB,WAAK,KAAK,CAAC,WAAW,IAAI,cAAc,CAAC;AAAA,IAC3C;AAEA,QAAI,IAAI,QAAQ;AACd,YAAM,WAAW,IAAI,OAAO,SAAS,IAAI,IAAI,OAAO,MAAM,GAAG,CAAC,IAAI,IAAI;AACtE,WAAK,KAAK,CAAC,WAAW,QAAQ,CAAC;AAAA,IACjC;AAEA,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,KAAK,iBAAiB;AAC5B,YAAM,KAAK,eAAe;AAC1B,iBAAW,CAAC,KAAK,KAAK,KAAK,MAAM;AAC/B,cAAM,KAAK,KAAK,GAAG,MAAM,KAAK,IAAI;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,OAAiB,WAAmC;AACvE,UAAM,SAASC,SAAQ,WAAW,CAAC,OAAO,GAAG,UAAU;AAEvD,eAAW,CAAC,MAAM,aAAa,KAAK,QAAQ;AAC1C,YAAM,KAAK,MAAM,IAAI,EAAE;AACvB,YAAM,KAAK,EAAE;AAGb,WAAK,kBAAkB,OAAO,eAAe,CAAC;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,OAAiB,WAAmC;AACxE,SAAK,kBAAkB,OAAO,WAAW,CAAC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKQ,kBACN,OACA,WACA,WACM;AACN,UAAM,UAAUA;AAAA,MAAQ;AAAA,MAAW,CAAC,OAClC,GAAG,UAAU,KAAK,KAAK,QAAQ,cAAc;AAAA,IAC/C;AAGA,UAAM,eAAe,KAAK,gBAAgB,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC;AAEhE,eAAW,CAAC,WAAW,cAAc,KAAK,cAAc;AACtD,UAAI,WAAW;AACb,cAAM,KAAK,GAAG,IAAI,OAAO,SAAS,CAAC,IAAI,SAAS,EAAE;AAClD,cAAM,KAAK,EAAE;AAAA,MACf;AAEA,YAAM,SAAS,KAAK,cAAc,cAAc;AAChD,iBAAW,MAAM,QAAQ;AACvB,aAAK,eAAe,OAAO,EAAE;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAiB,WAAmC;AACzE,UAAM,SAAS,KAAK,cAAc,SAAS;AAC3C,eAAW,MAAM,QAAQ;AACvB,WAAK,eAAe,OAAO,EAAE;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAiB,IAA0B;AAEhE,QAAI,KAAK,QAAQ,iBAAiB,sBAAsB;AACtD,YAAM,SAAS,KAAK,QAAQ,gBAAgB,qBAAqB,EAAE;AACnE,UAAI,WAAW,MAAM;AACnB,cAAM,KAAK,MAAM;AACjB,cAAM,KAAK,EAAE;AAEb,aAAK,mBAAmB,OAAO,EAAE;AACjC;AAAA,MACF;AAAA,IACF;AAEA,UAAM,gBAAgB,IAAI,OAAO,KAAK,QAAQ,oBAAoB;AAGlE,QAAI,OAAO;AACX,QAAI,KAAK,QAAQ,oBAAoB;AACnC,aAAO,KAAK,cAAc,GAAG,MAAM,IAAI;AAAA,IACzC;AAGA,UAAM,KAAK,GAAG,aAAa,IAAI,IAAI,GAAG,GAAG,MAAM,QAAQ,EAAE;AAGzD,QAAI,KAAK,QAAQ,sBAAsB,KAAK,QAAQ,oBAAoB,GAAG,eAAe,WAAW;AACnG,YAAM,YAAY,KAAK,eAAe,EAAE;AACxC,YAAM,KAAK,YAAY,GAAG,UAAU,KAAK,SAAS,GAAG;AAAA,IACvD;AAGA,UAAM,OAAiB,CAAC;AACxB,QAAI,GAAG,KAAK,SAAS,GAAG;AACtB,WAAK,KAAK,SAAS,GAAG,KAAK,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,IAChE;AACA,QAAI,GAAG,MAAM,WAAW,GAAG,MAAM,QAAQ,SAAS,GAAG;AACnD,YAAM,iBAAiB,KAAK,QAAQ;AACpC,YAAM,cAAc,GAAG,MAAM,QAAQ,IAAI,CAAC,MAAM;AAC9C,YAAI,EAAE,KAAK;AACT,iBAAO,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG;AAAA,QAC3B;AACA,YAAI,gBAAgB;AAClB,iBAAO,IAAI,EAAE,EAAE,KAAK,eAAe,QAAQ,YAAY,EAAE,EAAE,CAAC;AAAA,QAC9D;AACA,eAAO,KAAK,EAAE,EAAE;AAAA,MAClB,CAAC;AACD,WAAK,KAAK,YAAY,YAAY,KAAK,IAAI,CAAC,EAAE;AAAA,IAChD;AAEA,UAAM,WAAY,GAAG,MAAM,MACvB;AACJ,QAAI,UAAU,SAAS;AACrB,YAAM,gBAAgB,KAAK,QAAQ;AACnC,UAAI,eAAe;AACjB,cAAM,MAAM,cAAc,QAAQ,gBAAgB,SAAS,OAAO;AAClE,aAAK;AAAA,UACH,WAAW,SAAS,QAAQ,MAAM,GAAG,EAAE,CAAC,WAAM,GAAG;AAAA,QACnD;AAAA,MACF,OAAO;AACL,aAAK,KAAK,YAAY,SAAS,OAAO,IAAI;AAAA,MAC5C;AAAA,IACF;AAEA,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,KAAK,KAAK,KAAK,KAAK,CAAC;AAAA,IAC7B;AAEA,UAAM,KAAK,EAAE;AAEb,SAAK,mBAAmB,OAAO,EAAE;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,OAAiB,IAA0B;AAEpE,QAAI,GAAG,MAAM,QAAQ,GAAG,MAAM,KAAK,SAAS,GAAG;AAC7C,iBAAW,OAAO,GAAG,MAAM,MAAM;AAC/B,aAAK,eAAe,OAAO,GAAG;AAAA,MAChC;AAAA,IACF;AAGA,eAAW,QAAQ,GAAG,MAAM,OAAO;AACjC,WAAK,WAAW,OAAO,IAAI;AAAA,IAC7B;AAGA,QAAI,GAAG,WAAW,YAAY,GAAG,gBAAgB,KAAK,QAAQ,eAAe;AAC3E,YAAM,KAAK,aAAa;AACxB,YAAM,KAAK,EAAE;AACb,YAAM,KAAK,SAAS;AACpB,YAAM,KAAK,GAAG,YAAY;AAC1B,UAAI,GAAG,YAAY;AACjB,cAAM,KAAK,EAAE;AACb,cAAM,KAAK,GAAG,UAAU;AAAA,MAC1B;AACA,YAAM,KAAK,KAAK;AAChB,YAAM,KAAK,EAAE;AAAA,IACf;AAEA,UAAM,KAAK,EAAE;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,IAA4B;AACjD,UAAM,OAAO,KAAK,QAAQ,iBAAkB,QAAQ,OAAO,EAAE;AAC7D,UAAM,OAAO,GAAG;AAChB,UAAM,OAAO,GAAG,aAAa,IAAI,KAAK,GAAG,UAAU,KAAK;AACxD,WAAO,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW,OAAiB,MAAuB;AAEzD,QAAI,KAAK,QAAQ,iBAAiB,YAAY;AAC5C,YAAM,SAAS,KAAK,QAAQ,gBAAgB,WAAW,IAAI;AAC3D,UAAI,WAAW,MAAM;AACnB,cAAM,KAAK,MAAM;AAEjB,YAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;AACrC,gBAAM,SAAS,KAAK,QAAQ,cAAc,YAAY,KAAK;AAC3D,qBAAW,OAAO,KAAK,MAAM;AAC3B,iBAAK,eAAe,OAAO,KAAK,MAAM;AAAA,UACxC;AAAA,QACF;AACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,gBAAgB;AACpB,QAAI,KAAK,SAAS,QAAQ;AACxB,sBAAgB;AAAA,IAClB,WAAW,KAAK,SAAS,QAAQ;AAC/B,sBAAgB;AAAA,IAClB,WAAW,KAAK,SAAS,SAAS;AAChC,sBAAgB;AAAA,IAClB;AAEA,QAAI,KAAK,QAAQ,cAAc,WAAW;AACxC,YAAM,KAAK,KAAK,KAAK,OAAO,MAAM,KAAK,IAAI,GAAG,aAAa,EAAE;AAAA,IAC/D,OAAO;AACL,YAAM,KAAK,OAAO,KAAK,OAAO,MAAM,KAAK,IAAI,GAAG,aAAa,EAAE;AAAA,IACjE;AAGA,QAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;AACrC,YAAM,SAAS,KAAK,QAAQ,cAAc,YAAY,KAAK;AAC3D,iBAAW,OAAO,KAAK,MAAM;AAC3B,aAAK,eAAe,OAAO,KAAK,MAAM;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAiB,OAAiB,SAAS,IAAU;AAE1E,QAAI,KAAK,QAAQ,iBAAiB,gBAAgB;AAChD,YAAM,SAAS,KAAK,QAAQ,gBAAgB,eAAe,KAAK;AAChE,UAAI,WAAW,MAAM;AACnB,cAAM,KAAK,GAAG,MAAM,GAAG,MAAM,EAAE;AAC/B;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,cAAM,KAAK,GAAG,MAAM,KAAK,MAAM,IAAI,EAAE;AACrC;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,GAAG,MAAM,GAAG,MAAM,MAAM,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE;AACrE;AAAA,MAEF,KAAK,MAAM;AACT,cAAM,MAAM,OAAO,MAAM,UAAU,WAC/B,MAAM,QACN,KAAK,UAAU,MAAM,KAAK;AAC9B,cAAM,KAAK,GAAG,MAAM,OAAO,MAAM,KAAK,OAAO,GAAG,EAAE;AAClD;AAAA,MACF;AAAA,MAEA,KAAK;AACH,YAAI,MAAM,OAAO;AACf,gBAAM,KAAK,GAAG,MAAM,KAAK,MAAM,KAAK,IAAI;AACxC,gBAAM,KAAK,GAAG,MAAM,EAAE;AAAA,QACxB;AACA,cAAM,KAAK,GAAG,MAAM,SAAS,MAAM,QAAQ,EAAE,EAAE;AAC/C,mBAAW,SAAS,MAAM,WAAW,IAAI,MAAM,IAAI,GAAG;AACpD,gBAAM,KAAK,GAAG,MAAM,GAAG,IAAI,EAAE;AAAA,QAC/B;AACA,cAAM,KAAK,GAAG,MAAM,QAAQ;AAC5B,cAAM,KAAK,GAAG,MAAM,EAAE;AACtB;AAAA,MAEF,KAAK;AACH,YAAI,MAAM,OAAO;AACf,gBAAM,KAAK,GAAG,MAAM,KAAK,MAAM,KAAK,IAAI;AACxC,gBAAM,KAAK,GAAG,MAAM,EAAE;AAAA,QACxB;AACA,cAAM,KAAK,GAAG,MAAM,KAAK,MAAM,QAAQ,KAAK,KAAK,CAAC,IAAI;AACtD,cAAM,KAAK,GAAG,MAAM,KAAK,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,KAAK,KAAK,CAAC,IAAI;AACvE,mBAAW,OAAO,MAAM,MAAM;AAC5B,gBAAM,KAAK,GAAG,MAAM,KAAK,IAAI,KAAK,KAAK,CAAC,IAAI;AAAA,QAC9C;AACA,cAAM,KAAK,GAAG,MAAM,EAAE;AACtB;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,GAAG,MAAM,IAAI,MAAM,KAAK,KAAK,MAAM,GAAG,GAAG;AACpD;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,GAAG,MAAM,KAAK,MAAM,KAAK,IAAI;AACxC,cAAM,KAAK,GAAG,MAAM,EAAE;AACtB,mBAAW,SAAS,MAAM,YAAY,IAAI,MAAM,IAAI,GAAG;AACrD,gBAAM,KAAK,GAAG,MAAM,GAAG,IAAI,EAAE;AAAA,QAC/B;AACA,cAAM,KAAK,GAAG,MAAM,EAAE;AACtB;AAAA,MAEF,KAAK;AACH,YAAI,MAAM,OAAO;AACf,gBAAM,KAAK,GAAG,MAAM,KAAK,MAAM,KAAK,IAAI;AAAA,QAC1C;AACA,cAAM,KAAK,GAAG,MAAM,eAAe;AACnC,mBAAW,SAAS,MAAM,QAAQ,IAAI,MAAM,IAAI,GAAG;AACjD,gBAAM,KAAK,GAAG,MAAM,GAAG,IAAI,EAAE;AAAA,QAC/B;AACA,cAAM,KAAK,GAAG,MAAM,QAAQ;AAC5B;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,GAAG,MAAM,KAAK,MAAM,OAAO,YAAY,KAAK,MAAM,IAAI,GAAG;AACpE;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,GAAG,MAAM,MAAM,MAAM,IAAI,KAAK;AACzC,cAAM,KAAK,GAAG,MAAM,EAAE;AACtB,cAAM,KAAK,GAAG,MAAM,YAAY;AAChC,mBAAW,QAAQ,KAAK,UAAU,MAAM,QAAQ,MAAM,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG;AAC1E,gBAAM,KAAK,GAAG,MAAM,GAAG,IAAI,EAAE;AAAA,QAC/B;AACA,cAAM,KAAK,GAAG,MAAM,QAAQ;AAC5B,cAAM,KAAK,GAAG,MAAM,EAAE;AACtB;AAAA,IACJ;AAGA,QAAI,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAC/C,YAAM,cAAc,SAAS;AAC7B,iBAAW,SAAS,MAAM,UAAU;AAClC,aAAK,eAAe,OAAO,OAAO,WAAW;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,QAA4B;AAChD,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,WAA+C;AACnE,QAAI,KAAK,QAAQ,kBAAkB,SAAS;AAC1C,aAAO,CAAC,GAAG,SAAS,EAAE;AAAA,QAAK,CAAC,GAAG,MAC7B,EAAE,MAAM,SAAS,cAAc,EAAE,MAAM,QAAQ;AAAA,MACjD;AAAA,IACF;AACA,QAAI,KAAK,QAAQ,kBAAkB,UAAU;AAC3C,aAAO,CAAC,GAAG,SAAS,EAAE;AAAA,QACpB,CAAC,GAAG,OAAO,EAAE,MAAM,eAAe,MAAM,EAAE,MAAM,eAAe;AAAA,MACjE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBACN,SAC8B;AAC9B,QAAI,KAAK,QAAQ,kBAAkB,SAAS;AAC1C,aAAO,QAAQ,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAAA,IACtD;AACA,QAAI,KAAK,QAAQ,kBAAkB,UAAU;AAC3C,aAAO,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM;AACpC,cAAM,OAAO,KAAK,IAAI,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,eAAe,QAAQ,CAAC;AACtE,cAAM,OAAO,KAAK,IAAI,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,eAAe,QAAQ,CAAC;AACtE,eAAO,OAAO;AAAA,MAChB,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;AAKA,SAASA,SAAc,OAAY,OAAoC;AACrE,QAAM,MAAM,oBAAI,IAAY;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,MAAM,IAAI;AACtB,UAAM,WAAW,IAAI,IAAI,GAAG;AAC5B,QAAI,UAAU;AACZ,eAAS,KAAK,IAAI;AAAA,IACpB,OAAO;AACL,UAAI,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;;;AC1mBO,SAAS,mBACd,WACA,KACQ;AACR,aAAW,MAAM,WAAW;AAC1B,QAAI,GAAG,UAAU,SAAS,GAAG;AAC3B,aAAO,GAAG,UAAU,CAAC;AAAA,IACvB;AAAA,EACF;AAEA,QAAMC,YAAW,IAAI,QAAQ,YAAY,EAAE,EAAE,QAAQ,YAAY,EAAE;AACnE,SAAOA,UACJ,QAAQ,UAAU,GAAG,EACrB,QAAQ,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC;AAC5C;AAKO,SAAS,kBACd,KACA,WACoB;AACpB,QAAM,cAAc,mBAAmB,WAAW,GAAG;AAGrD,QAAM,gBAAgB,oBAAI,IAAY;AACtC,aAAW,MAAM,WAAW;AAC1B,eAAW,OAAO,GAAG,MAAM;AACzB,oBAAc,IAAI,GAAG;AAAA,IACvB;AAAA,EACF;AACA,QAAM,cAAc,CAAC,GAAG,aAAa,EAAE,KAAK;AAE5C,QAAM,QAAkB,CAAC;AACzB,QAAM,UAAmB;AAAA,IACvB,aAAa;AAAA,IACb,WAAW,oBAAI,IAAI;AAAA,IACnB,kBAAkB,oBAAI,IAAI;AAAA,EAC5B;AAEA,MAAI,cAAc;AAGlB,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,KAAK,YAAY,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC;AACpD,YAAQ,iBAAiB;AACzB;AAAA,EACF;AAGA,QAAM,KAAK,YAAY,WAAW,EAAE;AACpC,UAAQ,cAAc;AACtB;AAGA,QAAM,KAAK,EAAE;AACb;AAEA,aAAW,MAAM,WAAW;AAC1B,UAAM,WAAW,GAAG,MAAM;AAG1B,QAAI,GAAG,KAAK,SAAS,GAAG;AACtB,YAAM,KAAK,KAAK,GAAG,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC,EAAE;AACvD,cAAQ,iBAAiB,IAAI,UAAU,WAAW;AAClD;AAAA,IACF;AAGA,UAAM,eAAe;AACrB,UAAM,KAAK,eAAe,QAAQ,EAAE;AACpC;AAGA,UAAM,YAAY,oBAAI,IAAoB;AAC1C,aAAS,IAAI,GAAG,IAAI,GAAG,MAAM,MAAM,QAAQ,KAAK;AAC9C,YAAM,OAAO,GAAG,MAAM,MAAM,CAAC;AAC7B,gBAAU,IAAI,GAAG,WAAW;AAC5B,YAAM,KAAK,OAAO,KAAK,OAAO,IAAI,KAAK,IAAI,EAAE;AAC7C;AAAA,IACF;AAEA,YAAQ,UAAU,IAAI,UAAU,EAAE,cAAc,UAAU,CAAC;AAG3D,UAAM,KAAK,EAAE;AACb;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,MAAM,KAAK,IAAI;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AClIA,SAAS,cAAAC,mBAAkB;AAcpB,SAAS,cAAc,IAAuB;AACnD,QAAM,UAAU,KAAK,MAAM,KAAK,GAAI;AACpC,QAAM,QAAQ,KAAK,MAAO,KAAK,MAAQ,GAAS;AAChD,SAAO,EAAE,SAAS,MAAM;AAC1B;AAKO,SAAS,aAAa,IAAsB;AACjD,QAAM,UAAU,KAAK,MAAM,KAAK,GAAI;AACpC,QAAM,QAAQ,KAAK,MAAO,KAAK,MAAQ,GAAS;AAChD,SAAO,EAAE,SAAS,MAAM;AAC1B;AAOO,SAAS,qBAAqB,SAAmC;AACtE,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAQO,SAAS,uBACd,UACkB;AAClB,MAAI,qBAAqC;AACzC,SAAO,SAAS,IAAI,CAAC,OAAO;AAC1B,UAAM,KAAK,qBAAqB,EAAE;AAClC,QAAI,OAAO,eAAe;AACxB,aAAO;AAAA,IACT;AACA,UAAM,WAAW,4BAA4B,EAAE;AAC/C,yBAAqB;AACrB,WAAO;AAAA,EACT,CAAC;AACH;AAKA,SAAS,4BAA4B,IAAiC;AACpE,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAKO,SAAS,uBACd,QACsB;AACtB,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAUO,SAAS,gBACd,MACA,SACG,OACK;AACR,QAAM,QAAQ,CAAC,MAAM,MAAM,GAAG,KAAK,EAAE,KAAK,IAAI;AAC9C,SAAOA,YAAW,MAAM,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACnE;;;ACrGO,SAAS,8BACd,KACA,WACA,aACA,MACiE;AACjE,QAAM,EAAE,SAAS,aAAa,aAAa,KAAK,IAAI;AAGpD,QAAM,kBAAyB,YAAY,IAAI,CAAC,KAAK,OAAO;AAAA,IAC1D,UAAU;AAAA,MACR,MAAM,QAAQ,kBAAkB;AAAA,MAChC,QAAQ;AAAA,IACV;AAAA,IACA,MAAM,IAAI,GAAG;AAAA,IACb,IAAI,gBAAgB,cAAc,MAAM,KAAK,GAAG;AAAA,EAClD,EAAE;AAGF,QAAM,WAA2B,CAAC;AAElC,aAAW,MAAM,WAAW;AAC1B,UAAM,eAAe,GAAG,MAAM;AAC9B,UAAM,eAAe,QAAQ,UAAU,IAAI,YAAY;AACvD,QAAI,CAAC,aAAc;AAEnB,UAAM,aAAa,gBAAgB,YAAY,MAAM,KAAK,YAAY;AAGtE,UAAM,eAAsB,GAAG,KAAK,IAAI,CAAC,SAAS;AAAA,MAChD,UAAU;AAAA,QACR,MAAM,QAAQ,iBAAiB,IAAI,YAAY,KAAK,aAAa;AAAA,MACnE;AAAA,MACA,MAAM,IAAI,GAAG;AAAA,MACb,IAAI,gBAAgB,eAAe,MAAM,KAAK,cAAc,GAAG;AAAA,IACjE,EAAE;AAGF,QAAI,yBAA2D;AAC/D,UAAM,QAAgB,GAAG,MAAM,MAAM,IAAI,CAAC,MAAM,MAAM;AACpD,YAAM,UAAU,KAAK;AACrB,UAAI,SAAS,qBAAqB,OAAO;AAEzC,UAAI,WAAW,eAAe;AAC5B,iBAAS;AAAA,MACX,WAAW,WAAW,aAAa,WAAW,YAAY,WAAW,WAAW;AAC9E,iCAAyB;AAAA,MAC3B;AAEA,YAAM,WAAW,aAAa,UAAU,IAAI,CAAC,KAAK;AAClD,YAAM,UAAgB;AAAA,QACpB,UAAU,EAAE,MAAM,SAAS;AAAA,QAC3B,SAAS,GAAG,OAAO;AAAA,QACnB,aAAa,qBAAqB,OAAO;AAAA,QACzC,MAAM,KAAK;AAAA,QACX,IAAI,gBAAgB,WAAW,MAAM,KAAK,cAAc,OAAO,CAAC,CAAC;AAAA,MACnE;AAGA,YAAM,EAAE,WAAW,UAAU,IAAI,mBAAmB,MAAM,QAAQ;AAClE,UAAI,UAAW,SAAQ,YAAY;AACnC,UAAI,UAAW,SAAQ,YAAY;AAEnC,aAAO;AAAA,IACT,CAAC;AAED,UAAM,WAAqB;AAAA,MACzB,UAAU,EAAE,MAAM,aAAa,aAAa;AAAA,MAC5C,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,MACb;AAAA,MACA,IAAI;AAAA,IACN;AAEA,aAAS,KAAK,EAAE,SAAS,CAAC;AAAA,EAC5B;AAEA,QAAM,UAAmB;AAAA,IACvB,UAAU,EAAE,MAAM,QAAQ,YAAY;AAAA,IACtC,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,aAAa;AAAA,IACb;AAAA,EACF;AAEA,QAAM,kBAAmC,EAAE,KAAK,QAAQ;AAExD,SAAO;AAAA,IACL,gBAAgB;AAAA,MACd,QAAQ;AAAA,QACN;AAAA,QACA,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,yBAAyB,EAAE,gBAAgB;AAAA,EAC7C;AACF;AAQA,SAAS,mBACP,MACA,UACkD;AAClD,MAAI,CAAC,KAAK,QAAQ,KAAK,KAAK,WAAW,EAAG,QAAO,CAAC;AAGlD,QAAM,YAAY,KAAK,KAAK,OAAO,CAAC,MAAiD,EAAE,SAAS,OAAO;AACvG,MAAI,UAAU,SAAS,GAAG;AACxB,UAAM,QAAQ,UAAU,CAAC;AACzB,WAAO,EAAE,WAAW,eAAe,OAAO,WAAW,CAAC,EAAE;AAAA,EAC1D;AAGA,aAAW,OAAO,KAAK,MAAM;AAC3B,UAAM,KAAK,oBAAoB,KAAK,WAAW,CAAC;AAChD,QAAI,GAAI,QAAO,EAAE,WAAW,GAAG;AAAA,EACjC;AAEA,SAAO,CAAC;AACV;AAEA,SAAS,oBAAoB,KAAe,MAAqC;AAC/E,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAO;AAAA,QACL,UAAU,EAAE,KAAK;AAAA,QACjB,WAAW,IAAI;AAAA,QACf,SAAS,IAAI;AAAA,QACb,WAAW;AAAA,MACb;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,EAAE,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,SAAS,IAAI;AAAA,QACb,WAAW;AAAA,MACb;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,EAAE,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,SAAS,IAAI;AAAA,QACb,WAAW;AAAA,MACb;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,EAAE,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,SAAS,IAAI;AAAA,QACb,WAAW;AAAA,MACb;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,EAAE,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,SAAS,GAAG,IAAI,KAAK,KAAK,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ,KAAK,UAAU,IAAI,KAAK,CAAC;AAAA,QAC/F,WAAW;AAAA,MACb;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,EAAE,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,SAAS,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG;AAAA,QAClC,WAAW;AAAA,MACb;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,EAAE,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,SAAS,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC;AAAA,QACzC,WAAW;AAAA,MACb;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,EAAE,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,SAAS,IAAI,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,EAAE,KAAK,GAAG;AAAA,QAC/C,WAAW;AAAA,MACb;AAAA;AAAA,IAEF;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,eACP,OACA,MACW;AACX,QAAM,OAAmB,CAAC;AAG1B,OAAK,KAAK;AAAA,IACR,UAAU,EAAE,KAAK;AAAA,IACjB,OAAO,MAAM,QAAQ,IAAI,CAAC,SAAS;AAAA,MACjC,UAAU,EAAE,KAAK;AAAA,MACjB,OAAO;AAAA,IACT,EAAE;AAAA,IACF,IAAI;AAAA,EACN,CAAC;AAGD,WAAS,IAAI,GAAG,IAAI,MAAM,KAAK,QAAQ,KAAK;AAC1C,UAAM,UAAU,OAAO,IAAI;AAC3B,SAAK,KAAK;AAAA,MACR,UAAU,EAAE,MAAM,QAAQ;AAAA,MAC1B,OAAO,MAAM,KAAK,CAAC,EAAE,IAAI,CAAC,UAAU;AAAA,QAClC,UAAU,EAAE,MAAM,QAAQ;AAAA,QAC1B,OAAO;AAAA,MACT,EAAE;AAAA,MACF,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,UAAU,EAAE,KAAK;AAAA,IACjB;AAAA,EACF;AACF;;;ACnOO,SAAS,qBACd,KACA,WACA,MACY;AACZ,QAAM,YAAwB,CAAC;AAE/B,aAAW,MAAM,WAAW;AAC1B,UAAM,eAAe,GAAG,MAAM;AAC9B,UAAM,WAAW,gBAAgB,UAAU,MAAM,KAAK,YAAY;AAClE,UAAM,gBAAgB,gBAAgB,YAAY,MAAM,KAAK,YAAY;AAGzE,UAAM,WAAW,GAAG,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,OAAsB;AACnE,UAAM,gBAAgB,uBAAuB,QAAQ;AAErD,UAAM,cAA4B,GAAG,MAAM,MAAM,IAAI,CAAC,MAAM,MAAM;AAChE,YAAM,KAAiB;AAAA,QACrB,YAAY;AAAA,UACV,gBAAgB,WAAW,MAAM,KAAK,cAAc,OAAO,CAAC,CAAC;AAAA,QAC/D;AAAA,QACA,IAAI,gBAAgB,cAAc,MAAM,KAAK,cAAc,OAAO,CAAC,CAAC;AAAA,QACpE,MAAM,cAAc,CAAC;AAAA,QACrB,MAAM,KAAK;AAAA,MACb;AAEA,YAAM,WAAW,wBAAwB,IAAI;AAC7C,UAAI,SAAU,IAAG,WAAW;AAE5B,aAAO;AAAA,IACT,CAAC;AAED,UAAM,aAA0B,GAAG,KAAK,IAAI,CAAC,SAAS;AAAA,MACpD,MAAM,IAAI,GAAG;AAAA,MACb,WAAW,gBAAgB,eAAe,MAAM,KAAK,cAAc,GAAG;AAAA,IACxE,EAAE;AAEF,UAAM,SAAiB;AAAA,MACrB,IAAI;AAAA,MACJ;AAAA,MACA,MAAM;AAAA,MACN,UAAU;AAAA,MACV,OAAO;AAAA,MACP,MAAM;AAAA,MACN,YAAY,CAAC,aAAa;AAAA,IAC5B;AAEA,cAAU,KAAK,EAAE,OAAO,CAAC;AAAA,EAC3B;AAEA,SAAO;AACT;AAMA,SAAS,wBAAwB,MAAiD;AAChF,MAAI,CAAC,KAAK,QAAQ,KAAK,KAAK,WAAW,EAAG,QAAO;AAGjD,QAAM,YAAY,KAAK,KAAK;AAAA,IAC1B,CAAC,MAAiD,EAAE,SAAS;AAAA,EAC/D;AACA,MAAI,UAAU,SAAS,GAAG;AACxB,WAAO,EAAE,WAAW,iBAAiB,UAAU,CAAC,CAAC,EAAE;AAAA,EACrD;AAGA,aAAW,OAAO,KAAK,MAAM;AAC3B,UAAM,KAAK,0BAA0B,GAAG;AACxC,QAAI,GAAI,QAAO,EAAE,WAAW,GAAG;AAAA,EACjC;AAEA,SAAO;AACT;AAEA,SAAS,0BAA0B,KAA4C;AAC7E,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAO,EAAE,WAAW,IAAI,MAAM,SAAS,IAAI,QAAQ;AAAA,IACrD,KAAK;AACH,aAAO,EAAE,WAAW,cAAc,SAAS,IAAI,KAAK;AAAA,IACtD,KAAK;AACH,aAAO,EAAE,WAAW,iBAAiB,SAAS,IAAI,SAAS;AAAA,IAC7D,KAAK;AACH,aAAO,EAAE,WAAW,kBAAkB,SAAS,IAAI,KAAK;AAAA,IAC1D,KAAK;AACH,aAAO;AAAA,QACL,WAAW;AAAA,QACX,SAAS,GAAG,IAAI,KAAK,KAAK,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ,KAAK,UAAU,IAAI,KAAK,CAAC;AAAA,MACjG;AAAA,IACF,KAAK;AACH,aAAO,EAAE,WAAW,iBAAiB,SAAS,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI;AAAA,IAC7E,KAAK;AACH,aAAO,EAAE,WAAW,oBAAoB,SAAS,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,EAAE;AAAA,IACrF,KAAK;AACH,aAAO,EAAE,WAAW,cAAc,SAAS,IAAI,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,EAAE,KAAK,GAAG,EAAE;AAAA,IACrF;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,iBACP,OACa;AACb,QAAM,OAAyB,CAAC;AAGhC,OAAK,KAAK;AAAA,IACR,OAAO,MAAM,QAAQ,IAAI,CAAC,SAAS,EAAE,OAAO,IAAI,EAAE;AAAA,EACpD,CAAC;AAGD,aAAW,OAAO,MAAM,MAAM;AAC5B,SAAK,KAAK;AAAA,MACR,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,OAAO,KAAK,EAAE;AAAA,IAC5C,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,KAAK;AAChB;;;AChHO,SAAS,oBAAoB,KAA8B;AAChE,SAAO;AAAA,IACL,gBAAgB;AAAA,MACd,WAAW,cAAc,IAAI,WAAW;AAAA,IAC1C;AAAA,EACF;AACF;AAKO,SAAS,qBAAqB,KAA8B;AACjE,QAAM,YAAY,IAAI,UAAU,MAAM,CAAC,OAAO,GAAG,WAAW,QAAQ;AACpE,SAAO;AAAA,IACL,iBAAiB;AAAA,MACf,WAAW,cAAc,IAAI,YAAY;AAAA,MACzC,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAUO,SAAS,gCACd,KACA,IACA,MACY;AACZ,QAAM,YAAwB,CAAC;AAC/B,QAAM,eAAe,GAAG,MAAM;AAE9B,QAAM,WAAW,gBAAgB,UAAU,MAAM,KAAK,YAAY;AAClE,QAAM,aAAa,gBAAgB,YAAY,MAAM,KAAK,YAAY;AAGtE,QAAM,YAAwB,GAAG,MAAM,MAAM,IAAI,CAAC,OAAO,OAAO;AAAA,IAC9D,IAAI,gBAAgB,YAAY,MAAM,KAAK,cAAc,OAAO,CAAC,CAAC;AAAA,IAClE,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,CAAC;AAAA,IACV;AAAA,IACA,mBAAmB,CAAC;AAAA,EACtB,EAAE;AAGF,QAAM,WAAqB;AAAA,IACzB,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,EACF;AACA,YAAU,KAAK,EAAE,SAAS,CAAC;AAG3B,MAAI,GAAG,YAAY,GAAG,SAAS,SAAS,GAAG;AACzC,aAAS,IAAI,GAAG,IAAI,GAAG,SAAS,QAAQ,KAAK;AAC3C,YAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,YAAM,gBAAgB,MAAM,GAAG,SAAS,SAAS;AAEjD,YAAM,mBAAmB,sBAAsB;AAAA,QAC7C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,QAAQ;AAAA,QACvB,eAAe,QAAQ;AAAA,QACvB,mBAAmB,QAAQ;AAAA,QAC3B,qBAAqB,QAAQ;AAAA,QAC7B,eAAe,CAAC;AAAA;AAAA,QAEhB,iBAAiB;AAAA,MACnB,CAAC;AACD,gBAAU,KAAK,GAAG,gBAAgB;AAAA,IACpC;AAAA,EACF,OAAO;AAEL,UAAM,mBAAmB,sBAAsB;AAAA,MAC7C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe,GAAG;AAAA,MAClB,eAAe;AAAA;AAAA,MACf,mBAAmB;AAAA,MACnB,qBAAqB;AAAA,MACrB,eAAe;AAAA,MACf,iBAAiB;AAAA,IACnB,CAAC;AACD,cAAU,KAAK,GAAG,gBAAgB;AAAA,EACpC;AAEA,SAAO;AACT;AAqBA,SAAS,sBAAsB,QAAmC;AAChE,QAAM;AAAA,IACJ;AAAA,IAAY;AAAA,IAAW;AAAA,IAAI;AAAA,IAAK;AAAA,IAAc;AAAA,IAC9C;AAAA,IAAe;AAAA,IAAe;AAAA,IAAmB;AAAA,IACjD;AAAA,IAAe;AAAA,EACjB,IAAI;AAEJ,QAAM,YAAwB,CAAC;AAC/B,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,aAAa;AAAA,EACtB;AAGA,YAAU,KAAK;AAAA,IACb,iBAAiB;AAAA,MACf,IAAI;AAAA,MACJ;AAAA,MACA,WAAW,cAAc,CAAC;AAAA,MAC1B,SAAS;AAAA,IACX;AAAA,EACF,CAAC;AAGD,QAAM,wBAAwB,kBAC1B,gCAAgC,EAAE,IAClC;AAGJ,MAAI,eAAe;AAEnB,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,WAAW,UAAU,CAAC;AAC5B,UAAM,YAAY,GAAG,MAAM,MAAM,CAAC;AAGlC,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,kBAAkB,QAAW;AAG/B,mBAAa,kBAAkB,YAAY,MAAM,GAAG,MAAM,MAAM,SAAS,IACrE,WACA,kBAAkB,YAAY,IAAI,GAAG,MAAM,MAAM,SAAS,IACxD,WACA;AACN,uBAAiB,sBAAsB,SACnC,oBAAoB,GAAG,MAAM,MAAM,SACnC;AACJ,yBAAmB,eAAe,WAAW,sBAAsB;AAAA,IACrE,OAAO;AAEL,YAAM,aAAa,GAAG,YAAY,CAAC;AACnC,mBAAa,YAAY,UAAU;AACnC,uBAAiB,YAAY,cAAc;AAE3C,yBAAmB,YAAY;AAC/B,UAAI,eAAe,YAAY,GAAG,cAAc,kBAAkB;AAChE,2BAAmB,mBAAmB,OAAO,GAAG;AAAA,MAClD,WAAW,eAAe,YAAY,GAAG,cAAc,CAAC,kBAAkB;AACxE,2BAAmB,GAAG;AAAA,MACxB;AAAA,IACF;AAGA,cAAU,KAAK;AAAA,MACb,iBAAiB;AAAA,QACf;AAAA,QACA,YAAY,SAAS;AAAA,QACrB,WAAW,cAAc,YAAY;AAAA,MACvC;AAAA,IACF,CAAC;AAGD,oBAAgB;AAChB,cAAU,KAAK;AAAA,MACb,kBAAkB;AAAA,QAChB;AAAA,QACA,YAAY,SAAS;AAAA,QACrB,gBAAgB;AAAA,UACd,UAAU,aAAa,cAAc;AAAA,UACrC,QAAQ,uBAAuB,UAAU;AAAA,UACzC,SAAS;AAAA,QACX;AAAA,QACA,WAAW,cAAc,YAAY;AAAA,MACvC;AAAA,IACF,CAAC;AAGD,QAAI,iBAAiB;AACnB,YAAM,iBAAiB,sBAAsB,SAAS;AACtD,iBAAW,OAAO,gBAAgB;AAChC,kBAAU,KAAK;AAAA,UACb,YAAY;AAAA,YACV;AAAA,YACA,YAAY,SAAS;AAAA,YACrB,MAAM,IAAI;AAAA,YACV,WAAW,IAAI;AAAA,YACf,iBAAiB,IAAI;AAAA,UACvB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,MAAM,uBAAuB;AAC/B,iBAAW,OAAO,GAAG,aAAa;AAChC,kBAAU,KAAK;AAAA,UACb,YAAY;AAAA,YACV;AAAA,YACA,YAAY,SAAS;AAAA,YACrB,MAAM,IAAI;AAAA,YACV,WAAW,IAAI;AAAA,YACf,iBAAiB,IAAI;AAAA,UACvB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,YAAU,KAAK;AAAA,IACb,kBAAkB;AAAA,MAChB;AAAA,MACA,WAAW,cAAc,YAAY;AAAA,MACrC;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;AASA,SAAS,sBACP,MACwF;AACxF,MAAI,CAAC,KAAK,KAAM,QAAO,CAAC;AAExB,QAAM,cAID,CAAC;AAEN,aAAW,OAAO,KAAK,MAAM;AAC3B,QAAI,IAAI,SAAS,aAAc;AAG/B,UAAM,QAAQ,IAAI,KAAK,MAAM,4BAA4B;AACzD,QAAI,OAAO;AACT,kBAAY,KAAK;AAAA,QACf,MAAM,MAAM,CAAC;AAAA,QACb,WAAW,MAAM,CAAC;AAAA,QAClB,iBAAiB;AAAA,MACnB,CAAC;AAAA,IACH,OAAO;AAEL,kBAAY,KAAK;AAAA,QACf,MAAM,IAAI;AAAA,QACV,WAAW,eAAe,IAAI,IAAI;AAAA,QAClC,iBAAiB;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,eAAeC,OAAsB;AAC5C,QAAM,QAAQA,MAAK,YAAY;AAC/B,MAAI,MAAM,SAAS,MAAM,EAAG,QAAO;AACnC,MAAI,MAAM,SAAS,MAAM,KAAK,MAAM,SAAS,OAAO,EAAG,QAAO;AAC9D,MAAI,MAAM,SAAS,MAAM,EAAG,QAAO;AACnC,MAAI,MAAM,SAAS,OAAO,EAAG,QAAO;AACpC,MAAI,MAAM,SAAS,MAAM,EAAG,QAAO;AACnC,SAAO;AACT;AAOA,SAAS,gCAAgC,IAA4B;AACnE,MAAI,GAAG,YAAY,WAAW,EAAG,QAAO;AAExC,QAAM,cAAc,GAAG,YAAY,UAAU,CAAC,OAAO,GAAG,WAAW,QAAQ;AAC3E,MAAI,eAAe,EAAG,QAAO;AAE7B,SAAO,GAAG,YAAY,SAAS;AACjC;;;AC9UO,IAAM,4BAAN,MAAgC;AAAA,EAC7B;AAAA,EAIR,YAAY,UAAmC,CAAC,GAAG;AACjD,SAAK,UAAU;AAAA,MACb,aAAa,QAAQ,eAAe;AAAA,MACpC,mBAAmB,QAAQ,qBAAqB;AAAA,MAChD,QAAQ,QAAQ,UAAU;AAAA,MAC1B,MAAM,QAAQ;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,KAAgC;AACrC,UAAM,YAAwB,CAAC;AAC/B,UAAM,OAAO,KAAK,QAAQ;AAG1B,cAAU,KAAK,KAAK,kBAAkB,GAAG,CAAC;AAG1C,UAAM,UAAU,KAAK,kBAAkB,IAAI,SAAS;AAGpD,UAAM,qBAAiC,CAAC;AAExC,eAAW,CAAC,KAAK,SAAS,KAAK,SAAS;AACtC,YAAM,cAAc,kBAAkB,KAAK,SAAS;AAEpD,UAAI,KAAK,QAAQ,mBAAmB;AAClC,cAAM,EAAE,gBAAgB,wBAAwB,IAC9C,8BAA8B,KAAK,WAAW,aAAa,IAAI;AACjE,kBAAU,KAAK,cAAc;AAC7B,kBAAU,KAAK,uBAAuB;AAAA,MACxC;AAEA,YAAM,UAAU,qBAAqB,KAAK,WAAW,IAAI;AACzD,yBAAmB,KAAK,GAAG,OAAO;AAAA,IACpC;AAGA,cAAU,KAAK,GAAG,kBAAkB;AAGpC,cAAU,KAAK,oBAAoB,GAAG,CAAC;AAGvC,eAAW,CAAC,KAAK,SAAS,KAAK,SAAS;AACtC,iBAAW,MAAM,WAAW;AAC1B,cAAM,qBAAqB;AAAA,UACzB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,kBAAU,KAAK,GAAG,kBAAkB;AAAA,MACtC;AAAA,IACF;AAGA,cAAU,KAAK,qBAAqB,GAAG,CAAC;AAExC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,KAA4B;AACzC,UAAM,YAAY,KAAK,OAAO,GAAG;AACjC,WAAO,UAAU,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,KAA8B;AACtD,UAAM,OAAa;AAAA,MACjB,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,QACd,MAAM,KAAK,QAAQ,MAAM,YAAY;AAAA,QACrC,SACE,KAAK,QAAQ,MAAM,eAAe,IAAI,kBAAkB;AAAA,MAC5D;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS,QAAQ;AAAA,MACnB;AAAA,MACA,IAAI;AAAA,QACF,MAAM,QAAQ;AAAA,MAChB;AAAA,MACA,KAAK;AAAA,QACH,MAAM,QAAQ;AAAA,MAChB;AAAA,IACF;AAEA,WAAO,EAAE,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKQ,kBACN,WAC+B;AAC/B,UAAM,UAAU,oBAAI,IAA8B;AAClD,eAAW,MAAM,WAAW;AAC1B,YAAM,MAAM,GAAG;AACf,YAAM,WAAW,QAAQ,IAAI,GAAG;AAChC,UAAI,UAAU;AACZ,iBAAS,KAAK,EAAE;AAAA,MAClB,OAAO;AACL,gBAAQ,IAAI,KAAK,CAAC,EAAE,CAAC;AAAA,MACvB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;AC/IA,SAAmB,gBAAgB;AACnC,SAAS,0BAA0B;AAU5B,IAAM,wBAAN,MAA4B;AAAA,EACzB;AAAA,EAER,YAAY,UAA+B,CAAC,GAAG;AAC7C,SAAK,oBAAoB,IAAI,0BAA0B,QAAQ,QAAQ;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAO,KAAqC;AAChD,WAAO,KAAK,eAAe,GAAG;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,KAAqC;AAExD,UAAM,YAAY,KAAK,kBAAkB,OAAO,GAAG;AAGnD,UAAM,aAAa,IAAI,mBAAmB;AAG1C,UAAM,SAAmB,CAAC;AAC1B,UAAM,YAAY,IAAI,SAAS;AAAA,MAC7B,MAAM,OAAO,WAAW,UAAU;AAChC,eAAO,KAAK,OAAO,KAAK,KAAK,CAAC;AAC9B,iBAAS;AAAA,MACX;AAAA,IACF,CAAC;AAGD,eAAW,KAAK,SAAS;AAGzB,eAAW,YAAY,WAAW;AAChC,YAAM,WAAW,WAAW,MAAM,QAAQ;AAC1C,UAAI,CAAC,UAAU;AACb,cAAM,IAAI,QAAc,CAACC,aAAY,WAAW,KAAK,SAASA,QAAO,CAAC;AAAA,MACxE;AAAA,IACF;AAGA,UAAM,IAAI,QAAc,CAACA,UAAS,WAAW;AAC3C,gBAAU,GAAG,UAAUA,QAAO;AAC9B,gBAAU,GAAG,SAAS,MAAM;AAC5B,iBAAW,IAAI;AAAA,IACjB,CAAC;AAED,WAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAAA,EAC9C;AACF;;;ACIO,SAAS,mBAAmB,IAAsC;AACvE,SAAO;AAAA,IACL,IAAI,GAAG;AAAA,IACP,UAAU,GAAG,MAAM;AAAA,IACnB,YAAY,GAAG;AAAA,IACf,YAAY,GAAG;AAAA,IACf,QAAQ,GAAG;AAAA,IACX,YAAY,GAAG;AAAA,IACf,MAAM,GAAG;AAAA,IACT,WAAW,GAAG;AAAA,IACd,OAAO,GAAG,MAAM;AAAA,IAChB,MAAM,GAAG,MAAM,QAAQ,CAAC;AAAA,IACxB,SAAS,GAAG,MAAM,WAAW,CAAC;AAAA,IAC9B,aAAa,GAAG;AAAA,IAChB,cAAc,GAAG;AAAA,EACnB;AACF;;;AC3FA,SAAS,SAAS,MAAoC;AACpD,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,kBAAkB,UAAgC;AACzD,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,aACJ,UAAU,QACN,YAAY,MAAM,WAAW,KAAK,OAClC,SACE,kBAAkB,MAAM,OACxB,SAAS,KAAK;AACtB,QAAM,SACJ,SAAS,cAAc,SAAS,IAC5B,aAAa,SAAS,cAAc,IAAI,CAAC,UAAU,KAAK,KAAK,IAAI,EAAE,KAAK,IAAI,CAAC,KAC7E;AACN,SAAO,KAAK,SAAS,QAAQ,OAAO,SAAS,UAAU,IAAI,SAAS,UAAU,QAAQ,UAAU,GAAG,MAAM;AAC3G;AAEA,SAAS,WACP,OACA,MACA,MACA,cACM;AACN,QAAM,YAAY,KAAK,UAAU,OAAO,CAAC,aAAa,SAAS,SAAS,IAAI;AAC5E,MAAI,UAAU,WAAW,EAAG;AAE5B,QAAM,KAAK,OAAO,SAAS,IAAI,CAAC,KAAK,UAAU,MAAM,GAAG;AACxD,QAAM,KAAK,EAAE;AACb,aAAW,YAAY,UAAU,MAAM,GAAG,YAAY,GAAG;AACvD,UAAM,KAAK,kBAAkB,QAAQ,CAAC;AAAA,EACxC;AACA,MAAI,UAAU,SAAS,cAAc;AACnC,UAAM,KAAK,YAAY,UAAU,SAAS,YAAY,OAAO;AAAA,EAC/D;AACA,QAAM,KAAK,EAAE;AACf;AAEO,SAAS,uBACd,MACA,eAAe,IACP;AACR,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,sCAAsC;AACjD,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ,oBAAoB,KAAK,QAAQ,SAAS,eAAe,KAAK,QAAQ,KAAK,WAAW,KAAK,QAAQ,KAAK,WAAW,KAAK,QAAQ,OAAO,aAAa,KAAK,QAAQ,OAAO;AAAA,EAC1K;AACA,QAAM,KAAK,EAAE;AAEb,MAAI,KAAK,QAAQ,YAAY,GAAG;AAC9B,UAAM,KAAK,6CAA6C;AACxD,UAAM,KAAK,EAAE;AAAA,EACf,WAAW,KAAK,QAAQ,QAAQ,GAAG;AACjC,UAAM,KAAK,yDAAyD;AACpE,UAAM,KAAK,EAAE;AAAA,EACf,OAAO;AACL,UAAM,KAAK,oEAAoE;AAC/E,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,aAAW,OAAO,MAAM,aAAa,YAAY;AACjD,aAAW,OAAO,MAAM,SAAS,YAAY;AAC7C,aAAW,OAAO,MAAM,SAAS,YAAY;AAC7C,aAAW,OAAO,MAAM,WAAW,YAAY;AAC/C,aAAW,OAAO,MAAM,WAAW,YAAY;AAE/C,SAAO,MAAM,KAAK,IAAI,EAAE,QAAQ;AAClC;;;AC/EA,SAAS,UAAU,KAAwC;AACzD,SAAO,IAAI,IAAI,aAAa,IAAI;AAClC;AAEA,SAAS,UAAU,KAAwC;AACzD,SAAO,IAAI,IAAI;AACjB;AAEO,SAAS,iBACd,YACA,YAC+B;AAC/B,QAAM,gBAAgB,UAAU,UAAU;AAC1C,QAAM,gBAAgB,UAAU,UAAU;AAE1C,SAAO,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,GAAG,MAAM;AACpC,UAAM,cACJ,QAAQ,iBAAiB,UAAU,EAAE,GAAG,KAAK,kBAAkB,UAAU,EAAE,GAAG,CAAC;AACjF,UAAM,cACJ,QAAQ,iBAAiB,UAAU,EAAE,GAAG,KAAK,kBAAkB,UAAU,EAAE,GAAG,CAAC;AACjF,QAAI,gBAAgB,aAAa;AAC/B,aAAO,OAAO,WAAW,IAAI,OAAO,WAAW;AAAA,IACjD;AAEA,UAAM,mBACJ,QAAQ,iBAAiB,UAAU,EAAE,GAAG,KAAK,kBAAkB,UAAU,EAAE,GAAG,CAAC;AACjF,UAAM,mBACJ,QAAQ,iBAAiB,UAAU,EAAE,GAAG,KAAK,kBAAkB,UAAU,EAAE,GAAG,CAAC;AACjF,QAAI,qBAAqB,kBAAkB;AACzC,aAAO,OAAO,gBAAgB,IAAI,OAAO,gBAAgB;AAAA,IAC3D;AAEA,UAAM,SAAS,EAAE,IAAI,cAAc,WAAW;AAC9C,UAAM,SAAS,EAAE,IAAI,cAAc,WAAW;AAC9C,QAAI,WAAW,QAAQ;AACrB,aAAO,OAAO,MAAM,IAAI,OAAO,MAAM;AAAA,IACvC;AAEA,WAAO,EAAE,IAAI,cAAc,EAAE,IAAI;AAAA,EACnC,CAAC,EAAE,CAAC;AACN;;;ACtCA,SAAS,oBAAoB,GAAa,GAAsB;AAC9D,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,SAAO,EAAE,MAAM,CAAC,OAAO,UAAU,UAAU,EAAE,KAAK,CAAC;AACrD;AAEA,SAAS,WAAW,OAAwB;AAC1C,SAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAS,eAAe,QAA2C;AACjE,SAAO,WAAW;AACpB;AAEA,SAAS,eACP,UACA,SACA,YACoB;AACpB,MAAI,eAAe,QAAQ,MAAM,KAAK,CAAC,eAAe,SAAS,MAAM,GAAG;AACtE,WAAO;AAAA,EACT;AACA,MAAI,CAAC,eAAe,QAAQ,MAAM,KAAK,eAAe,SAAS,MAAM,GAAG;AACtE,WAAO;AAAA,EACT;AACA,SAAO,aAAa,YAAY;AAClC;AAEA,SAAS,WACP,UACA,SACqB;AACrB,QAAM,eAAe,SAAS,MAAM,QAAQ,CAAC;AAC7C,QAAM,cAAc,QAAQ,MAAM,QAAQ,CAAC;AAE3C,SAAO;AAAA,IACL,QAAQ,SAAS,WAAW,QAAQ;AAAA,IACpC,OAAO,WAAW,SAAS,MAAM,KAAK,MAAM,WAAW,QAAQ,MAAM,KAAK;AAAA,IAC1E,MAAM,WAAW,YAAY,MAAM,WAAW,WAAW;AAAA,IACzD,MAAM,CAAC,oBAAoB,SAAS,MAAM,QAAQ,IAAI;AAAA,IACtD,SAAS,WAAW,SAAS,MAAM,WAAW,CAAC,CAAC,MAAM,WAAW,QAAQ,MAAM,WAAW,CAAC,CAAC;AAAA,IAC5F,QACE,SAAS,eAAe,QAAQ,cAChC,SAAS,eAAe,QAAQ;AAAA,IAClC,UAAU,SAAS,eAAe,QAAQ;AAAA,IAC1C,aACE,WAAW,SAAS,WAAW,MAAM,WAAW,QAAQ,WAAW;AAAA,IACrE,QACG,SAAS,gBAAgB,SAAS,QAAQ,gBAAgB;AAAA,IAC7D,WAAW,CAAC,oBAAoB,SAAS,WAAW,QAAQ,SAAS;AAAA,EACvE;AACF;AAEA,SAAS,UAAU,WAA2C;AAC5D,QAAM,OAA2C;AAAA,IAC/C,WAAW;AAAA,IACX,OAAO;AAAA,IACP,OAAO;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAEA,SAAO,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM;AACnC,QAAI,KAAK,EAAE,IAAI,MAAM,KAAK,EAAE,IAAI,GAAG;AACjC,aAAO,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE,IAAI;AAAA,IACnC;AACA,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,WAAW,cAAc,EAAE,UAAU;AAAA,IAChD;AACA,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,WAAO,EAAE,SAAS,cAAc,EAAE,QAAQ;AAAA,EAC5C,CAAC;AACH;AAEO,SAAS,SACd,UACA,SACe;AACf,QAAM,eAAe,IAAI,IAAI,SAAS,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;AACxE,QAAM,cAAc,IAAI,IAAI,QAAQ,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;AACtE,QAAM,MAAM,oBAAI,IAAI,CAAC,GAAG,aAAa,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC;AAEnE,QAAM,YAA4B,CAAC;AAEnC,aAAW,MAAM,KAAK;AACpB,UAAM,SAAS,aAAa,IAAI,EAAE;AAClC,UAAM,QAAQ,YAAY,IAAI,EAAE;AAEhC,QAAI,CAAC,UAAU,OAAO;AACpB,YAAMC,SAA6B;AAAA,QACjC,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,aAAa;AAAA,QACb,OAAO,QAAQ,MAAM,YAAY;AAAA,QACjC,WAAW;AAAA,MACb;AACA,gBAAU,KAAK;AAAA,QACb,MAAM;AAAA,QACN;AAAA,QACA,UAAU,MAAM,MAAM;AAAA,QACtB,YAAY,MAAM;AAAA,QAClB,YAAY,MAAM;AAAA,QAClB,SAAS,mBAAmB,KAAK;AAAA,QACjC,OAAAA;AAAA,QACA,eAAe,OAAO,QAAQA,MAAK,EAChC,OAAO,CAAC,CAAC,EAAE,OAAO,MAAM,OAAO,EAC/B,IAAI,CAAC,CAAC,KAAK,MAAM,KAAK;AAAA,MAC3B,CAAC;AACD;AAAA,IACF;AAEA,QAAI,UAAU,CAAC,OAAO;AACpB,YAAMA,SAA6B;AAAA,QACjC,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,aAAa;AAAA,QACb,OAAO,QAAQ,OAAO,YAAY;AAAA,QAClC,WAAW;AAAA,MACb;AACA,gBAAU,KAAK;AAAA,QACb,MAAM;AAAA,QACN;AAAA,QACA,UAAU,OAAO,MAAM;AAAA,QACvB,YAAY,OAAO;AAAA,QACnB,YAAY,OAAO;AAAA,QACnB,UAAU,mBAAmB,MAAM;AAAA,QACnC,OAAAA;AAAA,QACA,eAAe,OAAO,QAAQA,MAAK,EAChC,OAAO,CAAC,CAAC,EAAE,OAAO,MAAM,OAAO,EAC/B,IAAI,CAAC,CAAC,KAAK,MAAM,KAAK;AAAA,MAC3B,CAAC;AACD;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,CAAC,OAAO;AACrB;AAAA,IACF;AAEA,UAAM,QAAQ,WAAW,QAAQ,KAAK;AACtC,UAAM,gBAAgB,OAAO,QAAQ,KAAK,EACvC,OAAO,CAAC,CAAC,EAAE,OAAO,MAAM,OAAO,EAC/B,IAAI,CAAC,CAAC,KAAK,MAAM,KAAK;AACzB,UAAM,OAAO,eAAe,QAAQ,OAAO,cAAc,SAAS,CAAC;AAEnE,cAAU,KAAK;AAAA,MACb;AAAA,MACA;AAAA,MACA,UAAU,MAAM,MAAM;AAAA,MACtB,YAAY,MAAM;AAAA,MAClB,YAAY,MAAM;AAAA,MAClB,UAAU,mBAAmB,MAAM;AAAA,MACnC,SAAS,mBAAmB,KAAK;AAAA,MACjC;AAAA,MACA;AAAA,MACA,iBAAiB,MAAM,aAAa,OAAO;AAAA,IAC7C,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,UAAU,SAAS;AAClC,QAAM,UAAU;AAAA,IACd,eAAe,SAAS,UAAU;AAAA,IAClC,cAAc,QAAQ,UAAU;AAAA,IAChC,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE;AAAA,IAChD,SAAS,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE;AAAA,IACpD,SAAS,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE;AAAA,IACpD,WAAW,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE;AAAA,IACxD,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE;AAAA,IAChD,WAAW,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE;AAAA,EAC1D;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,EACb;AACF;;;ACxLA,SAASC,YAAW,OAAuB;AACzC,SAAO,MACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ;AAC3B;AAEA,SAAS,YAAY,MAAoC;AACvD,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,WAAW,MAAyB;AAC3C,MAAI,UAAU,WAAWA,YAAW,KAAK,OAAO,CAAC,aAAaA,YAAW,KAAK,IAAI,CAAC;AACnF,MAAI,KAAK,QAAQ,KAAK,SAAS,UAAU;AACvC,eAAW,6BAA6BA,YAAW,KAAK,IAAI,CAAC;AAAA,EAC/D;AACA,MAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;AACrC,eAAW,wBAAwB,KAAK,KAAK,IAAI,CAAC,MAAM,OAAO,eAAe,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,EACnG;AACA,SAAO,OAAO,OAAO;AACvB;AAEA,SAAS,YAAY,OAA4B;AAC/C,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,yBAAyB,MAAM,IAAI,UAAU,EAAE,KAAK,EAAE,CAAC;AAChE;AAEA,SAAS,eAAe,KAAuB;AAC7C,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAOA,YAAW,IAAI,IAAI;AAAA,IAC5B,KAAK;AACH,aAAOA,YAAW,IAAI,MAAM,KAAK,IAAI,CAAC;AAAA,IACxC,KAAK;AACH,aAAO,GAAGA,YAAW,IAAI,KAAK,CAAC,KAAKA,YAAW,OAAO,IAAI,UAAU,YAAY,IAAI,UAAU,OAAO,KAAK,UAAU,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC;AAAA,IACrJ,KAAK;AACH,aAAO,GAAGA,YAAW,IAAI,KAAK,CAAC,GAAG,IAAI,OAAO,KAAKA,YAAW,IAAI,IAAI,CAAC,MAAM,EAAE,WAAWA,YAAW,IAAI,OAAO,CAAC;AAAA,IAClH,KAAK,SAAS;AACZ,YAAM,SAAS,OAAO,IAAI,QAAQ,IAAI,CAAC,MAAM,OAAOA,YAAW,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;AAClF,YAAM,OAAO,IAAI,KAAK,IAAI,CAAC,QAAQ,OAAO,IAAI,IAAI,CAAC,SAAS,OAAOA,YAAW,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE;AACpH,aAAO,GAAGA,YAAW,IAAI,KAAK,CAAC,UAAU,MAAM,GAAG,IAAI;AAAA,IACxD;AAAA,IACA,KAAK;AACH,aAAO,GAAGA,YAAW,IAAI,KAAK,CAAC,KAAKA,YAAW,IAAI,GAAG,CAAC;AAAA,IACzD,KAAK;AACH,aAAO,GAAGA,YAAW,IAAI,KAAK,CAAC,KAAKA,YAAW,IAAI,QAAQ,CAAC;AAAA,IAC9D,KAAK;AACH,aAAO,GAAGA,YAAW,IAAI,SAAS,iBAAiB,CAAC,WAAWA,YAAW,IAAI,IAAI,CAAC;AAAA,IACrF,KAAK;AACH,aAAO,GAAG,IAAI,MAAM,GAAGA,YAAW,IAAI,GAAG,CAAC,OAAO,EAAE,GAAGA,YAAW,IAAI,IAAI,CAAC;AAAA,IAC5E,KAAK;AACH,aAAO,GAAGA,YAAW,IAAI,IAAI,CAAC,KAAKA,YAAW,KAAK,UAAU,IAAI,IAAI,CAAC,CAAC;AAAA,EAC3E;AACF;AAEA,SAAS,WAAW,MAA0B;AAC5C,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,SAAO,wBAAwB,KAAK,IAAI,CAAC,MAAM,OAAO,eAAe,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;AAC1F;AAEA,SAAS,mBAAmB,UAAgC;AAC1D,QAAM,SAAS,SAAS;AACxB,QAAM,QAAQ,SAAS;AACvB,QAAM,gBAAgB,SAAS,kBAC3B,GAAG,SAAS,kBAAkB,IAAI,MAAM,EAAE,GAAG,SAAS,eAAe,OACrE;AAEJ,SAAO;AAAA,gDACuC,SAAS,IAAI,kBAAkBA;AAAA,IACzE,GAAG,SAAS,QAAQ,IAAI,SAAS,UAAU,IAAI,SAAS,cAAc,KAAK,GAAG,CAAC;AAAA,EACjF,EAAE,YAAY,CAAC;AAAA;AAAA;AAAA,yCAGsB,SAAS,IAAI,KAAK,YAAY,SAAS,IAAI,CAAC;AAAA,gBACrEA,YAAW,SAAS,QAAQ,CAAC;AAAA,8BACfA,YAAW,GAAG,SAAS,UAAU,IAAI,SAAS,UAAU,EAAE,CAAC;AAAA;AAAA;AAAA,YAI7E,UAAU,QACN,kCAAkCA,YAAW,OAAO,MAAM,CAAC,mCAAmCA,YAAW,MAAM,MAAM,CAAC,kBACtH,SACE,kCAAkCA,YAAW,OAAO,MAAM,CAAC,yDAC3D,qEAAqEA,YAAW,OAAO,UAAU,EAAE,CAAC,eAC5G;AAAA,YACE,gBAAgB,+BAA+BA,YAAW,aAAa,CAAC,WAAW,EAAE;AAAA;AAAA;AAAA,QAIzF,SAAS,cAAc,SAAS,IAC5B,2BAA2B,SAAS,cACjC,IAAI,CAAC,UAAU,4BAA4BA,YAAW,KAAK,CAAC,SAAS,EACrE,KAAK,EAAE,CAAC,WACX,EACN;AAAA,QAEE,UAAU,QACN;AAAA;AAAA;AAAA;AAAA;AAAA,wBAKYA,YAAW,OAAO,KAAK,KAAK,IAAI,CAAC,KAAK,QAAQ;AAAA;AAAA,wBAE9CA,YAAW,OAAO,UAAU,KAAK,KAAK,CAAC,KAAK,QAAQ;AAAA;AAAA,wBAEpDA,YAAW,OAAO,gBAAgB,EAAE,KAAK,QAAQ;AAAA,oBACrD,SAAS,MAAM,QAAQ,qBAAqB,YAAY,OAAO,KAAK,CAAC,UAAU,EAAE;AAAA,oBACjF,SAAS,MAAM,OAAO,oBAAoB,WAAW,OAAO,IAAI,CAAC,UAAU,EAAE;AAAA,oBAC7E,SAAS,MAAM,UAAU,uBAAuBA,YAAW,OAAO,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC,KAAK,QAAQ,UAAU,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAOxHA,YAAW,MAAM,KAAK,KAAK,IAAI,CAAC,KAAK,QAAQ;AAAA;AAAA,wBAE7CA,YAAW,MAAM,UAAU,KAAK,KAAK,CAAC,KAAK,QAAQ;AAAA;AAAA,wBAEnDA,YAAW,MAAM,gBAAgB,EAAE,KAAK,QAAQ;AAAA,oBACpD,SAAS,MAAM,QAAQ,qBAAqB,YAAY,MAAM,KAAK,CAAC,UAAU,EAAE;AAAA,oBAChF,SAAS,MAAM,OAAO,oBAAoB,WAAW,MAAM,IAAI,CAAC,UAAU,EAAE;AAAA,oBAC5E,SAAS,MAAM,UAAU,uBAAuBA,YAAW,MAAM,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC,KAAK,QAAQ,UAAU,EAAE;AAAA;AAAA;AAAA,uBAIlI,MAAM;AACL,UAAM,WAAW,SAAS;AAC1B,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,UAAU,SAAS,KAAK,SAAS;AACvC,UAAM,aAAa,SAAS,QAAQ,SAAS;AAC7C,UAAM,WAAW,SAAS,MAAM,SAAS;AACzC,UAAM,UAAU,SAAS,KAAK,SAAS;AACvC,QAAI,CAAC,WAAW,CAAC,cAAc,CAAC,YAAY,CAAC,QAAS,QAAO;AAC7D,WAAO;AAAA;AAAA,oBAED,UAAU,oBAAoBA,YAAW,SAAS,KAAK,KAAK,IAAI,CAAC,CAAC,UAAU,EAAE;AAAA,oBAC9E,aAAa,uBAAuBA,YAAW,SAAS,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC,CAAC,UAAU,EAAE;AAAA,oBACtG,WAAW,qBAAqB,YAAY,SAAS,KAAK,CAAC,UAAU,EAAE;AAAA,oBACvE,UAAU,oBAAoB,WAAW,SAAS,IAAI,CAAC,UAAU,EAAE;AAAA;AAAA;AAAA,EAG3E,GAAG,CACT;AAAA;AAAA;AAGN;AAGA,IAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuIjB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBjB,IAAM,uBAAN,MAA2B;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,UAA8B,CAAC,GAAG;AAC5C,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,QAAQ,aAAa,QAAQ,SAAS,SAAS;AACpD,SAAK,WAAW,QAAQ,YAAY;AAAA,EACtC;AAAA,EAEA,OAAO,MAA6B;AAClC,UAAM,gBACJ,KAAK,QAAQ,YAAY,IACrB,cACA,KAAK,QAAQ,QAAQ,IACnB,UACA;AACR,UAAM,YAAY,KAAK,UACpB,OAAO,CAAC,aAAa,SAAS,SAAS,WAAW,EAClD,IAAI,CAAC,aAAa,mBAAmB,QAAQ,CAAC,EAC9C,KAAK,IAAI;AAEZ,UAAM,kBAAkB,KAAK,WACzB,kJACA;AAEJ,UAAM,cAAc,KAAK,WACrB,GAAG,eAAe;AAAA;AAAA,wKAClB;AAEJ,UAAM,YAAY,KAAK,WAAW,wBAAwB;AAE1D,WAAO;AAAA,iBACM,SAAS;AAAA;AAAA;AAAA;AAAA,aAIbA,YAAW,KAAK,KAAK,CAAC;AAAA;AAAA,QAE3B,KAAK,MAAM,GAAG;AAAA,QACd,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAQEA,YAAW,KAAK,KAAK,CAAC;AAAA,cAC1B,eAAe;AAAA;AAAA,uCAEUA,YAAW,IAAI,KAAK,KAAK,SAAS,WAAW,EAAE,YAAY,CAAC,CAAC,oBAAoBA,YAAW,IAAI,KAAK,KAAK,QAAQ,WAAW,EAAE,YAAY,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,4CAIxI,KAAK,QAAQ,SAAS;AAAA,4CACtB,KAAK,QAAQ,KAAK;AAAA,4CAClB,KAAK,QAAQ,KAAK;AAAA,4CAClB,KAAK,QAAQ,OAAO;AAAA,4CACpB,KAAK,QAAQ,OAAO;AAAA,4CACpB,KAAK,QAAQ,SAAS;AAAA;AAAA;AAAA;AAAA,4BAKxD,KAAK,QAAQ,YAAY,IACrB,GAAG,KAAK,QAAQ,SAAS,sEACzB,KAAK,QAAQ,QAAQ,IACnB,0EACA,qEACR;AAAA;AAAA;AAAA;AAAA,uCAI+B,kBAAkB,QAAQ,WAAW,EAAE;AAAA,uCACvC,kBAAkB,cAAc,WAAW,EAAE;AAAA,uCAC7C,kBAAkB,UAAU,WAAW,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,uCAKzC,aAAa,mEAAqE;AAAA;AAAA;AAAA,QAGjH,WAAW;AAAA;AAAA;AAAA;AAAA,4BAIS,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBvC;AACF;;;AC3bA,SAAS,aAAa,MAAoC;AACxD,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,oBAAoB,SAAiC;AAC5D,MAAI,YAAY,UAAa,YAAY,EAAG,QAAO;AACnD,SAAO,GAAG,UAAU,IAAI,MAAM,EAAE,GAAG,OAAO;AAC5C;AAEA,SAASC,gBAAe,OAAiB,UAA8B;AACrE,QAAM,SAAS,SAAS;AACxB,QAAM,QAAQ,SAAS;AAEvB,QAAM,KAAK,MAAM,aAAa,SAAS,IAAI,CAAC,KAAK,SAAS,QAAQ,EAAE;AACpE,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,aAAa,SAAS,UAAU,IAAI,SAAS,UAAU,IAAI;AACtE,MAAI,UAAU,OAAO;AACnB,UAAM,KAAK,eAAe,OAAO,MAAM,WAAW,MAAM,MAAM,IAAI;AAAA,EACpE,WAAW,OAAO;AAChB,UAAM,KAAK,mBAAmB,MAAM,MAAM,IAAI;AAAA,EAChD,WAAW,QAAQ;AACjB,UAAM,KAAK,uBAAuB,OAAO,MAAM,IAAI;AAAA,EACrD;AACA,MAAI,SAAS,cAAc,SAAS,GAAG;AACrC,UAAM,KAAK,cAAc,SAAS,cAAc,IAAI,CAAC,UAAU,KAAK,KAAK,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EAC7F;AACA,QAAM,gBAAgB,oBAAoB,SAAS,eAAe;AAClE,MAAI,eAAe;AACjB,UAAM,KAAK,qBAAqB,aAAa,EAAE;AAAA,EACjD;AACA,QAAM,KAAK,EAAE;AAEb,MAAI,UAAU,OAAO;AACnB,UAAM,KAAK,gCAAgC;AAC3C,UAAM,KAAK,qBAAqB;AAChC,UAAM,KAAK,gBAAgB,WAAW,OAAO,QAAQ,CAAC,MAAM,WAAW,MAAM,QAAQ,CAAC,IAAI;AAC1F,UAAM,KAAK,YAAY,WAAW,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,MAAM,WAAW,MAAM,KAAK,KAAK,IAAI,CAAC,CAAC,IAAI;AACpG,UAAM,KAAK,aAAa,WAAW,OAAO,UAAU,KAAK,KAAK,CAAC,CAAC,MAAM,WAAW,MAAM,UAAU,KAAK,KAAK,CAAC,CAAC,IAAI;AACjH,UAAM,KAAK,aAAa,WAAW,OAAO,gBAAgB,EAAE,CAAC,MAAM,WAAW,MAAM,gBAAgB,EAAE,CAAC,IAAI;AAC3G,QAAI,SAAS,MAAM,OAAO;AACxB,YAAM,KAAK,aAAa,WAAWC,aAAY,OAAO,KAAK,CAAC,CAAC,MAAM,WAAWA,aAAY,MAAM,KAAK,CAAC,CAAC,IAAI;AAAA,IAC7G;AACA,QAAI,SAAS,MAAM,MAAM;AACvB,YAAM,KAAK,YAAY,WAAWC,YAAW,OAAO,IAAI,CAAC,CAAC,MAAM,WAAWA,YAAW,MAAM,IAAI,CAAC,CAAC,IAAI;AAAA,IACxG;AACA,QAAI,SAAS,MAAM,SAAS;AAC1B,YAAM,KAAK,eAAe,WAAW,OAAO,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC,CAAC,MAAM,WAAW,MAAM,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC,CAAC,IAAI;AAAA,IAC7I;AACA,UAAM,KAAK,EAAE;AAAA,EACf,OAAO;AACL,UAAM,WAAW,SAAS;AAC1B,QAAI,UAAU;AACZ,2BAAqB,OAAO,QAAQ;AAAA,IACtC;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,OAAiB,UAAkC;AAC/E,MAAI,SAAS,KAAK,SAAS,GAAG;AAC5B,UAAM,KAAK,aAAa,SAAS,KAAK,KAAK,IAAI,CAAC,EAAE;AAClD,UAAM,KAAK,EAAE;AAAA,EACf;AACA,MAAI,SAAS,QAAQ,SAAS,GAAG;AAC/B,UAAM,KAAK,gBAAgB,SAAS,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AACvE,UAAM,KAAK,EAAE;AAAA,EACf;AACA,MAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,UAAM,KAAK,YAAY;AACvB,UAAM,KAAK,EAAE;AACb,eAAW,QAAQ,SAAS,OAAO;AACjC,YAAM,KAAK,KAAKC,YAAW,IAAI,CAAC,EAAE;AAAA,IACpC;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AACA,MAAI,SAAS,KAAK,SAAS,GAAG;AAC5B,UAAM,KAAK,WAAW;AACtB,UAAM,KAAK,EAAE;AACb,eAAW,OAAO,SAAS,MAAM;AAC/B,YAAM,KAAK,KAAKC,gBAAe,GAAG,CAAC,EAAE;AAAA,IACvC;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AACF;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,MAAM,QAAQ,OAAO,KAAK,EAAE,QAAQ,OAAO,MAAM;AAC1D;AAEA,SAASD,YAAW,MAAyB;AAC3C,MAAI,SAAS,KAAK,KAAK,OAAO,MAAM,KAAK,IAAI;AAC7C,MAAI,KAAK,QAAQ,KAAK,SAAS,UAAU;AACvC,cAAU,KAAK,KAAK,IAAI;AAAA,EAC1B;AACA,MAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;AACrC,cAAU,KAAK,KAAK,KAAK,IAAIC,eAAc,EAAE,KAAK,IAAI,CAAC;AAAA,EACzD;AACA,SAAO;AACT;AAEA,SAASH,aAAY,OAA4B;AAC/C,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,MAAM,IAAIE,WAAU,EAAE,KAAK,IAAI;AACxC;AAEA,SAASC,gBAAe,KAAuB;AAC7C,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAO,IAAI;AAAA,IACb,KAAK;AACH,aAAO,IAAI,MAAM,KAAK,IAAI;AAAA,IAC5B,KAAK;AACH,aAAO,GAAG,IAAI,KAAK,KAAK,OAAO,IAAI,UAAU,YAAY,IAAI,UAAU,OAAO,KAAK,UAAU,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,CAAC;AAAA,IAC7H,KAAK;AACH,aAAO,GAAG,IAAI,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,EAAE,OAAO,IAAI,OAAO;AAAA,IAC1E,KAAK;AACH,aAAO,GAAG,IAAI,KAAK,MAAM,IAAI,QAAQ,KAAK,IAAI,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC,QAAQ,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,IACtG,KAAK;AACH,aAAO,GAAG,IAAI,KAAK,KAAK,IAAI,GAAG;AAAA,IACjC,KAAK;AACH,aAAO,GAAG,IAAI,KAAK,KAAK,IAAI,QAAQ;AAAA,IACtC,KAAK;AACH,aAAO,GAAG,IAAI,SAAS,iBAAiB,OAAO,IAAI,IAAI;AAAA,IACzD,KAAK;AACH,aAAO,GAAG,IAAI,MAAM,GAAG,IAAI,GAAG,OAAO,EAAE,GAAG,IAAI,IAAI;AAAA,IACpD,KAAK;AACH,aAAO,GAAG,IAAI,IAAI,KAAK,KAAK,UAAU,IAAI,IAAI,CAAC;AAAA,EACnD;AACF;AAEA,SAASF,YAAW,MAA0B;AAC5C,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,SAAO,KAAK,IAAIE,eAAc,EAAE,KAAK,IAAI;AAC3C;AAEO,IAAM,2BAAN,MAA+B;AAAA,EAC5B;AAAA,EAER,YAAY,UAAkC,CAAC,GAAG;AAChD,SAAK,QAAQ,QAAQ,SAAS;AAAA,EAChC;AAAA,EAEA,OAAO,MAA6B;AAClC,UAAM,QAAkB,CAAC;AAEzB,UAAM,KAAK,KAAK,KAAK,KAAK,EAAE;AAC5B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,eAAe,IAAI,KAAK,KAAK,SAAS,WAAW,EAAE,YAAY,CAAC,IAAI;AAC/E,UAAM,KAAK,cAAc,IAAI,KAAK,KAAK,QAAQ,WAAW,EAAE,YAAY,CAAC,IAAI;AAC7E,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,oBAAoB;AAC/B,UAAM,KAAK,EAAE;AACb,QAAI,KAAK,QAAQ,YAAY,GAAG;AAC9B,YAAM,KAAK,6BAA6B,KAAK,QAAQ,SAAS,yBAAyB;AAAA,IACzF,WAAW,KAAK,QAAQ,QAAQ,GAAG;AACjC,YAAM,KAAK,mCAAmC,KAAK,QAAQ,KAAK,0BAA0B;AAAA,IAC5F,OAAO;AACL,YAAM,KAAK,kEAAkE;AAAA,IAC/E;AACA,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,+DAA+D;AAC1E,UAAM,KAAK,6CAA6C;AACxD,UAAM;AAAA,MACJ,KAAK,KAAK,QAAQ,KAAK,MAAM,KAAK,QAAQ,OAAO,MAAM,KAAK,QAAQ,SAAS,MAAM,KAAK,QAAQ,KAAK,MAAM,KAAK,QAAQ,OAAO,MAAM,KAAK,QAAQ,SAAS;AAAA,IAC7J;AACA,UAAM,KAAK,EAAE;AAEb,eAAW,QAAQ,CAAC,aAAa,SAAS,SAAS,WAAW,SAAS,GAAY;AACjF,YAAM,YAAY,KAAK,UAAU,OAAO,CAAC,aAAa,SAAS,SAAS,IAAI;AAC5E,UAAI,UAAU,WAAW,EAAG;AAC5B,YAAM,KAAK,MAAM,aAAa,IAAI,CAAC,KAAK,UAAU,MAAM,GAAG;AAC3D,YAAM,KAAK,EAAE;AACb,iBAAW,YAAY,WAAW;AAChC,QAAAJ,gBAAe,OAAO,QAAQ;AAAA,MAChC;AAAA,IACF;AAEA,WAAO,MAAM,KAAK,IAAI,EAAE,QAAQ;AAAA,EAClC;AACF;;;ACtLO,SAAS,eAAe,SAAiB,YAA6B;AAC3E,QAAM,oBAAoB,QAAQ,QAAQ,OAAO,GAAG;AACpD,QAAM,iBAAiB,WAAW,QAAQ,OAAO,GAAG;AAEpD,QAAM,WAAW,kBACd,QAAQ,qBAAqB,MAAM,EACnC,QAAQ,SAAS,cAAc,EAC/B,QAAQ,OAAO,OAAO,EACtB,QAAQ,iBAAiB,IAAI;AAEhC,QAAM,QAAQ,IAAI,OAAO,IAAI,QAAQ,GAAG;AACxC,SAAO,MAAM,KAAK,cAAc;AAClC;AAEA,SAAS,uBACP,WACA,SACA,SACA,QACkB;AAClB,MAAI,QAAQ,WAAW,KAAK,QAAQ,WAAW,EAAG,QAAO;AAEzD,QAAM,WAA6B,CAAC;AACpC,aAAW,MAAM,WAAW;AAC1B,UAAM,aAAa,GAAG,WAAW,QAAQ,OAAO,GAAG;AAEnD,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,WAAW,QAAQ,KAAK,CAAC,YAAY,eAAe,SAAS,UAAU,CAAC;AAC9E,UAAI,CAAC,SAAU;AAAA,IACjB;AAEA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,WAAW,QAAQ,KAAK,CAAC,YAAY,eAAe,SAAS,UAAU,CAAC;AAC9E,UAAI,SAAU;AAAA,IAChB;AAEA,aAAS,KAAK,EAAE;AAAA,EAClB;AAEA,QAAM,UAAU,UAAU,SAAS,SAAS;AAC5C,MAAI,UAAU,GAAG;AACf,WAAO;AAAA,MACL,YAAY,OAAO,2CAA2C,SAAS,MAAM;AAAA,IAC/E;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,sBACP,WACA,aACA,aACA,QACkB;AAClB,MAAI,YAAY,WAAW,KAAK,YAAY,WAAW,EAAG,QAAO;AAEjE,QAAM,WAA6B,CAAC;AACpC,aAAW,MAAM,WAAW;AAC1B,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,WAAW,GAAG,KAAK,KAAK,CAAC,QAAQ,YAAY,SAAS,GAAG,CAAC;AAChE,UAAI,CAAC,SAAU;AAAA,IACjB;AAEA,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,WAAW,GAAG,KAAK,KAAK,CAAC,QAAQ,YAAY,SAAS,GAAG,CAAC;AAChE,UAAI,SAAU;AAAA,IAChB;AAEA,aAAS,KAAK,EAAE;AAAA,EAClB;AAEA,QAAM,UAAU,UAAU,SAAS,SAAS;AAC5C,MAAI,UAAU,GAAG;AACf,WAAO;AAAA,MACL,YAAY,OAAO,0CAA0C,SAAS,MAAM;AAAA,IAC9E;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,cACP,WACA,UACkB;AAClB,MAAI,aAAa,OAAQ,QAAO;AAEhC,SAAO,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM;AACnC,QAAI,aAAa,MAAM;AACrB,aAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,IAChC;AAEA,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,WAAW,cAAc,EAAE,UAAU;AAAA,IAChD;AACA,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,QAAI,EAAE,MAAM,aAAa,EAAE,MAAM,UAAU;AACzC,aAAO,EAAE,MAAM,SAAS,cAAc,EAAE,MAAM,QAAQ;AAAA,IACxD;AACA,WAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,EAChC,CAAC;AACH;AAEO,SAAS,gBACd,MACA,MACkB;AAClB,QAAM,UAAU,KAAK,WAAW,CAAC;AACjC,QAAM,UAAU,KAAK,WAAW,CAAC;AACjC,QAAM,cAAc,KAAK,eAAe,CAAC;AACzC,QAAM,cAAc,KAAK,eAAe,CAAC;AACzC,QAAM,WAAW,KAAK,iBAAiB;AAEvC,MAAI,WAAW;AAAA,IACb,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA,KAAK;AAAA,EACP;AAEA,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK;AAAA,EACP;AAEA,SAAO,cAAc,UAAU,QAAQ;AACzC;;;ACnJA,YAAYK,SAAQ;AACpB,YAAYC,WAAU;;;ACMf,SAAS,eAAe,MAAwB;AACrD,QAAM,OAAO,oBAAI,IAAY;AAK7B,QAAM,WAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,WAAW,UAAU;AAC9B,QAAI;AACJ,YAAQ,QAAQ,QAAQ,KAAK,IAAI,OAAO,MAAM;AAC5C,YAAM,MAAM,MAAM,CAAC;AACnB,UAAI,gBAAgB,GAAG,KAAK,CAAC,KAAK,IAAI,GAAG,GAAG;AAC1C,aAAK,IAAI,GAAG;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,SAAO,CAAC,GAAG,IAAI;AACjB;AAEA,SAAS,gBAAgB,KAAsB;AAC7C,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AACpC,MAAI,IAAI,WAAW,SAAS,KAAK,IAAI,WAAW,UAAU,EAAG,QAAO;AACpE,MAAI,IAAI,WAAW,GAAG,EAAG,QAAO;AAChC,SAAO;AACT;;;ACtCA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAY,YAAY;AAUjB,SAAS,UAAU,YAAoB,WAA2B;AACvE,MAAI,CAAI,eAAW,SAAS,GAAG;AAC7B,IAAG,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC7C;AAEA,QAAM,UAAa,iBAAa,UAAU;AAC1C,QAAM,OAAc,kBAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AAEjF,QAAM,MAAW,cAAQ,UAAU;AACnC,QAAM,WAAW,SAAc,eAAS,YAAY,GAAG,CAAC;AACxD,QAAM,WAAW,GAAG,QAAQ,IAAI,IAAI,GAAG,GAAG;AAC1C,QAAM,WAAgB,WAAK,WAAW,QAAQ;AAE9C,MAAI,CAAI,eAAW,QAAQ,GAAG;AAC5B,IAAG,iBAAa,YAAY,QAAQ;AAAA,EACtC;AAEA,SAAO,UAAU,QAAQ;AAC3B;AAGA,SAAS,SAAS,MAAsB;AACtC,SAAO,KACJ,QAAQ,oBAAoB,GAAG,EAC/B,QAAQ,UAAU,GAAG,EACrB,QAAQ,UAAU,EAAE;AACzB;;;AFfO,SAAS,aACd,UACA,UAAyB,CAAC,GACZ;AACd,QAAM,UAAe,cAAQ,QAAQ;AACrC,QAAM,YAAiB,WAAK,SAAS,QAAQ;AAE7C,MAAI,OAAU,iBAAa,UAAU,MAAM;AAC3C,QAAM,OAAO,eAAe,IAAI;AAEhC,MAAI,cAAc;AAClB,QAAM,UAAoB,CAAC;AAE3B,aAAW,OAAO,MAAM;AACtB,UAAM,eAAoB,cAAQ,SAAS,GAAG;AAE9C,QAAI,CAAI,eAAW,YAAY,GAAG;AAChC,cAAQ,KAAK,GAAG;AAChB;AAAA,IACF;AAEA,UAAM,aAAa,UAAU,cAAc,SAAS;AACpD,WAAO,gBAAgB,MAAM,KAAK,UAAU;AAC5C;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,KAAK,CAAC,QAAQ,cAAc;AAC/C,UAAM,IAAI;AAAA,MACR,gBAAgB,QAAQ,SAAS,IAAI,MAAM,EAAE,KAAK,QAAQ,KAAK,IAAI,CAAC;AAAA,IACtE;AAAA,EACF;AAEA,EAAG,kBAAc,UAAU,MAAM,MAAM;AAEvC,SAAO;AAAA,IACL;AAAA,IACA,cAAc,QAAQ;AAAA,IACtB;AAAA,EACF;AACF;AASA,SAAS,gBAAgB,MAAc,UAAkB,aAA6B;AACpF,QAAM,UAAU,SAAS,QAAQ,uBAAuB,MAAM;AAG9D,QAAM,aAAa,IAAI;AAAA,IACrB,uCAAuC,OAAO;AAAA,IAC9C;AAAA,EACF;AACA,SAAO,KAAK,QAAQ,YAAY,KAAK,WAAW,IAAI;AAGpD,QAAM,iBAAiB,IAAI;AAAA,IACzB,6DAA6D,OAAO;AAAA,IACpE;AAAA,EACF;AACA,SAAO,KAAK,QAAQ,gBAAgB,KAAK,WAAW,IAAI;AAGxD,QAAM,gBAAgB,IAAI;AAAA,IACxB,4BAA4B,OAAO;AAAA,IACnC;AAAA,EACF;AACA,SAAO,KAAK,QAAQ,eAAe,KAAK,WAAW,IAAI;AAEvD,SAAO;AACT;;;AG3EO,IAAM,iBAAN,MAAM,gBAAe;AAAA,EAClB;AAAA,EACA;AAAA,EAER,YAAY,UAAiC,CAAC,GAAG;AAC/C,SAAK,QAAQ,QAAQ,UAAU,SAAS;AACxC,SAAK,oBAAoB,IAAI,kBAAkB;AAAA,MAC7C,GAAG,QAAQ;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,WAAW;AAAA,MACX,oBAAoB;AAAA,MACpB,qBAAqB;AAAA,MACrB,iBAAiB;AAAA,IACnB,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,KAA4B;AACjC,UAAM,WAAW,KAAK,kBAAkB,OAAO,GAAG;AAElD,UAAM,OAAO,SAAS,QAAQ,cAAc,EAAE;AAC9C,UAAM,cAAc,KAAK,iBAAiB,GAAG;AAC7C,WAAO,GAAG,WAAW;AAAA,EAAK,IAAI;AAAA,EAChC;AAAA,EAEQ,iBAAiB,KAA4B;AACnD,UAAM,QAAQ,gBAAe,aAAa,IAAI,SAAS;AACvD,UAAM,QAAQ,IAAI,UAAU;AAC5B,UAAM,cAAc,GAAG,KAAK,YAAY,UAAU,IAAI,MAAM,EAAE,WAAM,MAAM,KAAK,YAAY,CAAC;AAC5F,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,UAAU,KAAK,KAAK;AAAA,MACpB,gBAAgB,WAAW;AAAA,MAC3B;AAAA,MACA;AAAA,MACA,aAAa,MAAM,IAAI;AAAA,MACvB,gBAAgB,MAAM,OAAO;AAAA,MAC7B;AAAA,IACF;AACA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAAA,EAEA,OAAO,aAAa,WAA6D;AAC/E,UAAM,WAAW,IAAI,IAAI,UAAU,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;AACzD,QAAI,SAAS,IAAI,QAAQ,EAAG,QAAO,EAAE,MAAM,UAAU,SAAS,SAAS;AACvE,QAAI,SAAS,IAAI,SAAS,EAAG,QAAO,EAAE,MAAM,WAAW,SAAS,UAAU;AAC1E,QAAI,SAAS,IAAI,SAAS,KAAK,CAAC,SAAS,IAAI,QAAQ,EAAG,QAAO,EAAE,MAAM,WAAW,SAAS,UAAU;AACrG,WAAO,EAAE,MAAM,UAAU,SAAS,UAAU;AAAA,EAC9C;AACF;;;ACrEA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAkBtB,IAAM,gBAAgB,CAAC,WAAW,YAAY,SAAS,GAAG;AAE1D,SAAS,YAAY,KAAsB;AACzC,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,cAAc,KAAK,CAAC,WAAW,QAAQ,WAAW,MAAM,CAAC,GAAG;AAC9D,WAAO;AAAA,EACT;AAIA,SAAO,CAAM,YAAM,WAAW,OAAO,KAAK,CAAM,YAAM,WAAW,OAAO;AAC1E;AAGA,SAAS,iBAAiB,UAA0B;AAElD,MAAI,SAAS,SAAS,QAAQ,uDAAuD,EAAE;AAEvF,WAAS,OAAO,QAAQ,uBAAuB,EAAE;AAEjD,WAAS,OAAO,QAAQ,iCAAiC,EAAE;AAC3D,WAAS,OAAO,QAAQ,mCAAmC,EAAE;AAC7D,SAAO;AACT;AAOO,SAAS,mBAAmB,UAA4B;AAC7D,QAAM,QAAQ,oBAAI,IAAY;AAC9B,QAAM,WAAW,iBAAiB,QAAQ;AAG1C,QAAM,YAAY;AAClB,MAAI;AACJ,UAAQ,QAAQ,UAAU,KAAK,QAAQ,OAAO,MAAM;AAClD,UAAM,MAAM,MAAM,CAAC,EAAE,KAAK;AAC1B,QAAI,YAAY,GAAG,GAAG;AACpB,YAAM,IAAI,GAAG;AAAA,IACf;AAAA,EACF;AAGA,QAAM,YAAY;AAClB,UAAQ,QAAQ,UAAU,KAAK,QAAQ,OAAO,MAAM;AAClD,UAAM,MAAM,MAAM,CAAC,EAAE,KAAK;AAC1B,QAAI,YAAY,GAAG,GAAG;AACpB,YAAM,IAAI,GAAG;AAAA,IACf;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,KAAK;AACzB;AAOA,SAAS,YAAY,UAA4B;AAE/C,QAAM,SACJ;AACF,QAAM,WAAqB,CAAC;AAC5B,MAAI,YAAY;AAEhB,aAAW,SAAS,SAAS,SAAS,MAAM,GAAG;AAC7C,QAAI,MAAM,QAAS,WAAW;AAC5B,eAAS,KAAK,SAAS,MAAM,WAAW,MAAM,KAAM,CAAC;AAAA,IACvD;AACA,aAAS,KAAK,MAAM,CAAC,CAAC;AACtB,gBAAY,MAAM,QAAS,MAAM,CAAC,EAAE;AAAA,EACtC;AACA,MAAI,YAAY,SAAS,QAAQ;AAC/B,aAAS,KAAK,SAAS,MAAM,SAAS,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AAGA,SAAS,OAAO,SAA0B;AACxC,QAAM,UAAU,QAAQ,UAAU;AAClC,SAAO,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,MAAM,KAAK,QAAQ,WAAW,OAAO;AACvH;AAGA,SAAS,oBACP,OACA,eACA,SACQ;AACR,MAAI,SAAS;AAGb,WAAS,OAAO;AAAA,IACd;AAAA,IACA,CAAC,MAAM,KAAK,KAAK,SAAS;AACxB,YAAM,UAAU,IAAI,KAAK;AACzB,UAAI,CAAC,YAAY,OAAO,EAAG,QAAO;AAClC,UAAI,SAAS;AACX,cAAM,SAAS,QAAQ,IAAI,OAAO;AAClC,YAAI,WAAW,OAAW,QAAO;AACjC,eAAO,GAAG,GAAG,GAAG,aAAa,IAAI,MAAM,GAAG,IAAI;AAAA,MAChD;AACA,aAAO,GAAG,GAAG,GAAG,aAAa,IAAI,OAAO,GAAG,IAAI;AAAA,IACjD;AAAA,EACF;AAGA,WAAS,OAAO;AAAA,IACd;AAAA,IACA,CAAC,MAAM,KAAK,KAAK,SAAS;AACxB,YAAM,UAAU,IAAI,KAAK;AACzB,UAAI,CAAC,YAAY,OAAO,EAAG,QAAO;AAClC,UAAI,SAAS;AACX,cAAM,SAAS,QAAQ,IAAI,OAAO;AAClC,YAAI,WAAW,OAAW,QAAO;AACjC,eAAO,GAAG,GAAG,GAAG,aAAa,IAAI,MAAM,GAAG,IAAI;AAAA,MAChD;AACA,aAAO,GAAG,GAAG,GAAG,aAAa,IAAI,OAAO,GAAG,IAAI;AAAA,IACjD;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,kBACd,UACA,eACA,SACQ;AACR,SAAO,YAAY,QAAQ,EACxB,IAAI,CAAC,QAAS,OAAO,GAAG,IAAI,MAAM,oBAAoB,KAAK,eAAe,OAAO,CAAE,EACnF,KAAK,EAAE;AACZ;AAMO,SAAS,mBAAmB,SAAsD;AACvF,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EACjB,IAAI;AAEJ,QAAM,OAAO,mBAAmB,QAAQ;AACxC,QAAM,UAAU,oBAAI,IAAoB;AACxC,QAAM,UAAoB,CAAC;AAE3B,aAAW,OAAO,MAAM;AACtB,UAAM,UAAe,cAAQ,aAAa,GAAG;AAC7C,QAAI,CAAI,eAAW,OAAO,GAAG;AAC3B,UAAI,CAAC,cAAc;AACjB,cAAM,IAAI,MAAM,oBAAoB,OAAO,EAAE;AAAA,MAC/C;AACA,cAAQ,KAAK,GAAG;AAChB;AAAA,IACF;AAEA,UAAM,iBAAiB,UAAU,SAAS,SAAS;AAEnD,UAAM,WAAW,eAAe,QAAQ,aAAa,EAAE;AACvD,YAAQ,IAAI,KAAK,QAAQ;AAAA,EAC3B;AAEA,QAAM,YAAY,kBAAkB,UAAU,eAAe,OAAO;AAEpE,SAAO;AAAA,IACL,UAAU;AAAA,IACV,aAAa,QAAQ;AAAA,IACrB,cAAc,QAAQ;AAAA,IACtB;AAAA,EACF;AACF;;;AC1HO,SAAS,YAAY,QAA+B;AACzD,QAAM,QAAQ,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AACtD,QAAM,YAAwB,MAAM,IAAI,CAAC,SAAS,KAAK,MAAM,IAAI,CAAC;AAClE,SAAO,eAAe,SAAS;AACjC;AAQO,SAAS,eAAe,WAAsC;AAEnE,QAAM,UAAU,oBAAI,IAAoB;AACxC,QAAM,cAAc,oBAAI,IAA6B;AACrD,QAAM,UAAU,oBAAI,IAAyB;AAC7C,QAAM,YAAY,oBAAI,IAA2B;AACjD,QAAM,mBAAmB,oBAAI,IAAkC;AAC/D,QAAM,oBAAoB,oBAAI,IAAmC;AACjE,QAAM,yBAAyB,oBAAI,IAAsB;AACzD,QAAM,cAAc,oBAAI,IAAqC;AAC7D,QAAM,cAAc,oBAAI,IAAkC;AAE1D,MAAI,cAAc;AAClB,MAAI,eAAe;AACnB,MAAI,UAAU;AACd,MAAI,WAAW;AACf,MAAI,cAAc;AAGlB,aAAW,YAAY,WAAW;AAChC,QAAI,UAAU,UAAU;AACtB,iBAAW,SAAS,KAAK,eAAe;AACxC,oBAAc,SAAS,KAAK,eAAe;AAAA,IAC7C;AAEA,QAAI,YAAY,UAAU;AACxB,cAAQ,IAAI,SAAS,OAAO,KAAK,SAAS,MAAM;AAAA,IAClD;AAEA,QAAI,qBAAqB,UAAU;AACjC,YAAM,MAAM,SAAS;AACrB,kBAAY,IAAI,IAAI,KAAK,GAAG;AAAA,IAC9B;AAEA,QAAI,YAAY,UAAU;AACxB,YAAM,IAAI,SAAS;AACnB,cAAQ,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAG,KAAK,EAAE,IAAI,CAAC;AAAA,IAC7C;AAEA,QAAI,cAAc,UAAU;AAC1B,YAAM,KAAK,SAAS;AACpB,gBAAU,IAAI,GAAG,IAAI,EAAE,UAAU,IAAI,UAAU,GAAG,SAAS,CAAC;AAAA,IAC9D;AAEA,QAAI,qBAAqB,UAAU;AACjC,YAAM,MAAM,SAAS;AACrB,uBAAiB,IAAI,IAAI,IAAI;AAAA,QAC3B,iBAAiB;AAAA,QACjB,YAAY,IAAI;AAAA,MAClB,CAAC;AACD,kBAAY,IAAI,IAAI,IAAI,CAAC,CAAC;AAC1B,kBAAY,IAAI,IAAI,IAAI,CAAC,CAAC;AAE1B,YAAM,WAAW,uBAAuB,IAAI,IAAI,UAAU,KAAK,CAAC;AAChE,eAAS,KAAK,IAAI,EAAE;AACpB,6BAAuB,IAAI,IAAI,YAAY,QAAQ;AAAA,IACrD;AAEA,QAAI,sBAAsB,UAAU;AAClC,YAAM,MAAM,SAAS;AACrB,wBAAkB,IAAI,IAAI,mBAAmB;AAAA,QAC3C,mBAAmB,IAAI;AAAA,QACvB,eAAe,IAAI;AAAA,MACrB,CAAC;AAAA,IACH;AAEA,QAAI,sBAAsB,UAAU;AAClC,YAAM,MAAM,SAAS;AACrB,YAAM,UAAU,YAAY,IAAI,IAAI,iBAAiB;AACrD,UAAI,SAAS;AACX,gBAAQ,KAAK;AAAA,UACX,YAAY,IAAI;AAAA,UAChB,QAAQ,2BAA2B,IAAI,eAAe,MAAM;AAAA,UAC5D,YAAY,aAAa,IAAI,eAAe,QAAQ;AAAA,UACpD,cAAc,IAAI,eAAe;AAAA,QACnC,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,gBAAgB,UAAU;AAC5B,YAAM,MAAM,SAAS;AACrB,YAAM,OAAO,YAAY,IAAI,IAAI,iBAAiB;AAClD,UAAI,MAAM;AACR,aAAK,KAAK,GAAG;AAAA,MACf;AAAA,IACF;AAEA,QAAI,oBAAoB,UAAU;AAChC,oBAAc,cAAc,SAAS,eAAe,SAAS;AAAA,IAC/D;AAEA,QAAI,qBAAqB,UAAU;AACjC,qBAAe,cAAc,SAAS,gBAAgB,SAAS;AAC/D,gBAAU,SAAS,gBAAgB;AAAA,IACrC;AAAA,EACF;AAGA,QAAM,kBAAoC,CAAC;AAE3C,aAAW,CAAC,YAAY,OAAO,KAAK,WAAW;AAC7C,UAAM,cAAc,QAAQ,IAAI,QAAQ,QAAQ;AAChD,QAAI,CAAC,YAAa;AAElB,UAAM,SAAS,YAAY;AAC3B,UAAM,MAAM,YAAY;AAGxB,UAAM,aAAa,uBAAuB,IAAI,UAAU,KAAK,CAAC;AAC9D,QAAI,WAAW,WAAW,EAAG;AAG7B,UAAM,iBAAiB,WACpB,IAAI,CAAC,OAAO,iBAAiB,IAAI,EAAE,CAAE,EACrC,OAAO,OAAO,EACd,KAAK,CAAC,GAAG,MAAM,EAAE,gBAAgB,UAAU,EAAE,gBAAgB,OAAO;AAGvE,UAAM,eAAe,eAAe,eAAe,SAAS,CAAC;AAC7D,UAAM,iBAAiB,aAAa,gBAAgB;AAGpD,UAAM,oBAAoB,oBAAI,IAAoB;AAClD,aAAS,IAAI,GAAG,IAAI,QAAQ,SAAS,UAAU,QAAQ,KAAK;AAC1D,wBAAkB,IAAI,QAAQ,SAAS,UAAU,CAAC,EAAE,IAAI,CAAC;AAAA,IAC3D;AAGA,UAAM,aAAa,sBAAsB,QAAQ,KAAK,WAAW;AAGjE,UAAM,gBAAgB,YAAY,IAAI,cAAc,KAAK,CAAC;AAC1D,UAAM,gBAAgB,YAAY,IAAI,cAAc,KAAK,CAAC;AAG1D,UAAM,qBAAmC,WAAW,IAAI,CAAC,GAAG,OAAO;AAAA,MACjE,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,IACd,EAAE;AAEF,eAAW,MAAM,eAAe;AAC9B,YAAM,YAAY,kBAAkB,IAAI,GAAG,UAAU;AACrD,UAAI,cAAc,UAAa,YAAY,mBAAmB,QAAQ;AACpE,2BAAmB,SAAS,IAAI;AAAA,UAC9B,OAAO;AAAA,UACP,QAAQ,GAAG;AAAA,UACX,YAAY,GAAG;AAAA,UACf,cAAc,GAAG;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAGA,UAAM,gBAAgB,oBAAoB,kBAAkB;AAC5D,UAAM,kBAAkB,mBAAmB;AAAA,MACzC,CAAC,KAAK,OAAO,MAAM,GAAG;AAAA,MACtB;AAAA,IACF;AAGA,UAAM,OAAO,OAAO,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK,QAAQ,MAAM,EAAE,CAAC;AAG5D,UAAM,sBAAoC,cAAc,IAAI,CAAC,SAAS;AAAA,MACpE,MAAM,IAAI;AAAA,MACV,WAAW,IAAI;AAAA,MACf,MAAM,IAAI;AAAA,MACV,iBAAiB,IAAI;AAAA,IACvB,EAAE;AAGF,UAAM,cAAcC,oBAAmB,KAAK,WAAW;AACvD,UAAM,YAAY,cACd,CAAC,aAAa,OAAO,IAAI,IACzB,CAAC,OAAO,IAAI;AAGhB,UAAM,QAAmB;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,OAAO;AAAA,MACP,MAAM,KAAK,SAAS,IAAI,OAAO;AAAA,IACjC;AAGA,UAAM,aAAa,mBAAmB,KAAK,CAAC,OAAO,GAAG,WAAW,QAAQ;AAGzE,QAAI;AACJ,QAAI,eAAe,SAAS,GAAG;AAC7B,iBAAW,eAAe,IAAI,CAAC,YAAY;AACzC,cAAM,MAAM,QAAQ,gBAAgB;AACpC,cAAM,eAAe,YAAY,IAAI,GAAG,KAAK,CAAC;AAC9C,cAAM,gBAAgB;AAAA,UACpB,wBAAwB,cAAc,mBAAmB,WAAW,MAAM;AAAA,QAC5E;AACA,cAAM,oBAAoB,aAAa;AAAA,UACrC,CAAC,KAAK,OAAO,MAAM,GAAG;AAAA,UAAY;AAAA,QACpC;AACA,cAAM,oBAAoB,aAAa,KAAK,CAAC,OAAO,GAAG,WAAW,QAAQ;AAC1E,eAAO;AAAA,UACL,SAAS,QAAQ,gBAAgB;AAAA,UACjC,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,cAAc,mBAAmB;AAAA,QACnC;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,iBAAiC;AAAA,MACrC,IAAI,QAAQ,SAAS;AAAA,MACrB;AAAA,MACA,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,cAAc,YAAY;AAAA,MAC1B,aAAa;AAAA,MACb,aAAa;AAAA,MACb;AAAA,MACA,OAAO,aAAa,gBAAgB;AAAA,MACpC,SAAS,eAAe,SAAS,IAAI,eAAe,SAAS,IAAI;AAAA,MACjE;AAAA,MACA;AAAA,IACF;AAEA,oBAAgB,KAAK,cAAc;AAAA,EACrC;AAEA,QAAM,aACJ,eAAe,KAAK,cAAc,IAC9B,eAAe,cACf,gBAAgB,OAAO,CAAC,KAAK,OAAO,MAAM,GAAG,YAAY,CAAC;AAEhE,SAAO;AAAA,IACL,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,OAAO;AAAA,IACP,gBAAgB,gBAAgB,UAAU,cAAc;AAAA,EAC1D;AACF;AAMA,SAAS,2BAA2B,QAA0C;AAC5E,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,cAAc,IAAuB;AAC5C,SAAO,GAAG,UAAU,MAAO,KAAK,MAAM,GAAG,QAAQ,GAAS;AAC5D;AAEA,SAAS,aAAa,GAA+C;AACnE,SAAO,EAAE,UAAU,MAAO,KAAK,MAAM,EAAE,QAAQ,GAAS;AAC1D;AAKA,SAAS,sBACP,QACA,KACA,aACa;AACb,QAAM,MAAM,YAAY,IAAI,GAAG;AAG/B,QAAM,kBAAkB,oBAAI,IAAoB;AAChD,MAAI,KAAK;AACP,eAAW,SAAS,IAAI,QAAQ,UAAU;AACxC,YAAM,WAAW,MAAM,YAAY,MAAM;AACzC,UAAI,UAAU;AACZ,mBAAW,QAAQ,SAAS,OAAO;AACjC,0BAAgB,IAAI,KAAK,IAAI,KAAK,QAAQ,KAAK,CAAC;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,OAAO,MAAM,IAAI,CAAC,OAAO;AAE9B,QAAI,UAAuB;AAC3B,QAAI,GAAG,WAAW,SAAS,GAAG;AAC5B,YAAM,aAAa,gBAAgB,IAAI,GAAG,WAAW,CAAC,CAAC;AACvD,UAAI,cAAc,cAAc,UAAU,GAAG;AAC3C,kBAAU;AAAA,MACZ;AAAA,IACF;AAGA,QAAI,CAAC,GAAG,WAAW,UAAU,CAAC,gBAAgB,IAAI,GAAG,WAAW,CAAC,CAAC,GAAG;AACnE,gBAAU,wBAAwB,GAAG,IAAI;AAAA,IAC3C;AAEA,UAAM,OAAkB;AAAA,MACtB;AAAA,MACA,MAAM,GAAG;AAAA,IACX;AAGA,UAAM,OAAO,yBAAyB,EAAE;AACxC,QAAI,KAAK,SAAS,GAAG;AACnB,WAAK,OAAO;AAAA,IACd;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,cAAc,GAA6B;AAClD,SAAO,CAAC,SAAS,QAAQ,QAAQ,OAAO,KAAK,EAAE,SAAS,CAAC;AAC3D;AAEA,SAAS,wBACP,MACa;AACb,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAASA,oBACP,KACA,aACoB;AACpB,QAAM,MAAM,YAAY,IAAI,GAAG;AAC/B,MAAI,KAAK;AACP,WAAO,IAAI,QAAQ;AAAA,EACrB;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,aAAuC;AAClE,MAAI,YAAY,KAAK,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAG,QAAO;AAC7D,MAAI,YAAY,MAAM,CAAC,OAAO,GAAG,WAAW,SAAS,EAAG,QAAO;AAC/D,MAAI,YAAY,KAAK,CAAC,OAAO,GAAG,WAAW,SAAS,EAAG,QAAO;AAC9D,MAAI,YAAY,MAAM,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAG,QAAO;AAC9D,SAAO;AACT;AAKA,SAAS,wBACP,cACA,mBACA,WACc;AACd,QAAM,UAAwB,MAAM,KAAK,EAAE,QAAQ,UAAU,GAAG,CAAC,GAAG,OAAO;AAAA,IACzE,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,YAAY;AAAA,EACd,EAAE;AAEF,aAAW,MAAM,cAAc;AAC7B,UAAM,YAAY,kBAAkB,IAAI,GAAG,UAAU;AACrD,QAAI,cAAc,UAAa,YAAY,QAAQ,QAAQ;AACzD,cAAQ,SAAS,IAAI;AAAA,QACnB,OAAO;AAAA,QACP,QAAQ,GAAG;AAAA,QACX,YAAY,GAAG;AAAA,QACf,cAAc,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,yBAAyB,IAA4B;AAC5D,MAAI,CAAC,GAAG,SAAU,QAAO,CAAC;AAC1B,QAAM,OAAmB,CAAC;AAC1B,QAAM,QAAkB;AAExB,MAAI,GAAG,SAAS,WAAW;AACzB,UAAM,QAAQ,GAAG,SAAS;AAC1B,QAAI,MAAM,KAAK,SAAS,GAAG;AACzB,YAAM,UAAU,MAAM,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK;AACtD,YAAM,OAAO,MAAM,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACvE,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,GAAG,SAAS,WAAW;AACzB,UAAM,KAAK,GAAG,SAAS;AACvB,UAAM,YAAY,GAAG,aAAa;AAElC,QAAI,cAAc,cAAc;AAC9B,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,MAAM,GAAG;AAAA,QACT;AAAA,MACF,CAAC;AAAA,IACH,WAAW,cAAc,iBAAiB;AACxC,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,QACP,UAAU,GAAG;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH,WAAW,cAAc,kBAAkB;AACzC,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,MAAM,GAAG;AAAA,QACT;AAAA,MACF,CAAC;AAAA,IACH,WAAW,cAAc,oBAAoB;AAC3C,UAAI;AACF,aAAK,KAAK;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM,KAAK,MAAM,GAAG,OAAO;AAAA,UAC3B;AAAA,QACF,CAAC;AAAA,MACH,QAAQ;AACN,aAAK,KAAK;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,SAAS,GAAG;AAAA,UACZ,MAAM;AAAA,UACN;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,OAAO;AAEL,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,QACP,SAAS,GAAG;AAAA,QACZ,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;AC7iBO,SAAS,UAAU,MAAsB;AAE9C,SAAO,KAAK,QAAQ,4BAA4B,EAAE;AACpD;;;ACsBA,SAAS,SAAS,MAAc,QAAwB;AACtD,MAAI,KAAK,UAAU,OAAQ,QAAO;AAClC,SAAO,KAAK,MAAM,GAAG,SAAS,CAAC,IAAI;AACrC;AAGA,SAASC,gBAAe,IAAoB;AAC1C,QAAM,UAAU,KAAK;AACrB,MAAI,UAAU,GAAI,QAAO,GAAG,QAAQ,QAAQ,CAAC,CAAC;AAC9C,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,QAAM,mBAAmB,UAAU;AACnC,SAAO,GAAG,OAAO,KAAK,iBAAiB,QAAQ,CAAC,CAAC;AACnD;AAGA,SAAS,kBACP,SACA,gBACyB;AACzB,QAAM,YAAY,QAAQ,WAAW;AACrC,QAAM,QAAQ,YAAY,uBAAuB;AACjD,QAAM,aAAa,YAAY,WAAW;AAG1C,QAAM,SAAgB,CAAC;AAGvB,SAAO,KAAK;AAAA,IACV,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,MAAM,GAAG,KAAK,kBAAkB,QAAQ,MAAM,YAAY,QAAQ,MAAM;AAAA,MACxE,OAAO;AAAA,IACT;AAAA,EACF,CAAC;AAGD,SAAO,KAAK;AAAA,IACV,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,EAAE,MAAM,UAAU,MAAM,YAAY,QAAQ,KAAK,GAAG;AAAA,MACpD,EAAE,MAAM,UAAU,MAAM,aAAa,QAAQ,MAAM,GAAG;AAAA,MACtD,EAAE,MAAM,UAAU,MAAM,aAAa,QAAQ,MAAM,GAAG;AAAA,MACtD,EAAE,MAAM,UAAU,MAAM,cAAc,QAAQ,OAAO,GAAG;AAAA,MACxD,EAAE,MAAM,UAAU,MAAM,eAAeA,gBAAe,QAAQ,UAAU,CAAC,GAAG;AAAA,MAC5E,EAAE,MAAM,UAAU,MAAM,aAAa,UAAU,GAAG;AAAA,IACpD;AAAA,EACF,CAAC;AAGD,MAAI,QAAQ,YAAY,SAAS,GAAG;AAClC,UAAM,iBAAiB,QAAQ,YAAY,MAAM,GAAG,cAAc;AAClE,UAAM,QAAQ,eAAe,IAAI,CAAC,MAAM;AACtC,YAAM,OAAO,EAAE;AACf,UAAI,EAAE,OAAO;AACX,cAAM,aAAa,SAAS,UAAU,EAAE,KAAK,GAAG,GAAG;AACnD,eAAO,IAAI,IAAI;AAAA,QAAY,UAAU;AAAA,MACvC;AACA,aAAO,IAAI,IAAI;AAAA,IACjB,CAAC;AAED,QAAI,OAAO,MAAM,KAAK,MAAM;AAC5B,QAAI,QAAQ,YAAY,SAAS,gBAAgB;AAC/C,cAAQ;AAAA;AAAA,UAAe,QAAQ,YAAY,SAAS,cAAc;AAAA,IACpE;AAEA,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,QACJ,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MAAI,QAAQ,IAAI;AACd,UAAM,WAAkD,CAAC;AAEzD,QAAI,QAAQ,GAAG,aAAa;AAC1B,eAAS,KAAK,EAAE,MAAM,UAAU,MAAM,SAAS,QAAQ,GAAG,WAAW,GAAG,CAAC;AAAA,IAC3E;AACA,QAAI,QAAQ,GAAG,QAAQ;AACrB,eAAS,KAAK,EAAE,MAAM,UAAU,MAAM,aAAa,QAAQ,GAAG,MAAM,GAAG,CAAC;AAAA,IAC1E;AACA,QAAI,QAAQ,GAAG,WAAW;AACxB,eAAS,KAAK,EAAE,MAAM,UAAU,MAAM,aAAa,QAAQ,GAAG,UAAU,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC;AAAA,IACzF;AACA,QAAI,QAAQ,GAAG,aAAa;AAC1B,eAAS,KAAK,EAAE,MAAM,UAAU,MAAM,aAAa,QAAQ,GAAG,WAAW,GAAG,CAAC;AAAA,IAC/E;AAEA,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,QAAQ,WAAW;AACrB,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,UACA,KAAK,QAAQ;AAAA,UACb,WAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,OAAO;AAClB;AAQA,eAAsB,sBACpB,MACA,MAC6B;AAC7B,QAAM,EAAE,SAAS,YAAY,iBAAiB,EAAE,IAAI;AACpD,QAAM,EAAE,OAAO,OAAO,IAAI;AAE1B,QAAM,UAAU,kBAAkB,SAAS,cAAc;AAEzD,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,YAAY;AAAA,MACvC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,IAC9B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC1D,UAAI,WAAW;AACf,UAAI;AACF,mBAAW,MAAM,SAAS,KAAK;AAAA,MACjC,QAAQ;AAAA,MAER;AACA,YAAM,gBAAgB,SAAS,UAAU,GAAG;AAC5C,YAAM,SAAS,YAAY,iBAAiB,SAAS,KAAK;AAC1D,YAAM,WAAW,+BAA+B,SAAS,MAAM,GAAG,MAAM,IAAI,aAAa;AACzF,aAAO,KAAK,QAAQ;AACpB,aAAO,EAAE,IAAI,OAAO,OAAO,SAAS;AAAA,IACtC;AAEA,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,WAAW,0BAA0B,GAAG;AAC9C,WAAO,KAAK,QAAQ;AACpB,WAAO,EAAE,IAAI,OAAO,OAAO,SAAS;AAAA,EACtC;AACF;;;ACtKA,SAASC,UAAS,MAAc,QAAwB;AACtD,MAAI,KAAK,UAAU,OAAQ,QAAO;AAClC,SAAO,KAAK,MAAM,GAAG,SAAS,CAAC,IAAI;AACrC;AAGA,SAASC,gBAAe,IAAoB;AAC1C,QAAM,UAAU,KAAK;AACrB,MAAI,UAAU,GAAI,QAAO,GAAG,QAAQ,QAAQ,CAAC,CAAC;AAC9C,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,QAAM,mBAAmB,UAAU;AACnC,SAAO,GAAG,OAAO,KAAK,iBAAiB,QAAQ,CAAC,CAAC;AACnD;AAGA,SAAS,kBACP,SACA,gBACyB;AACzB,QAAM,YAAY,QAAQ,WAAW;AACrC,QAAM,cAAc,YAAY,WAAW;AAC3C,QAAM,cAAc,YAAY,SAAS;AAGzC,QAAM,YAAmB,CAAC;AAG1B,YAAU,KAAK;AAAA,IACb,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,MAAM,GAAG,WAAW;AAAA,IACpB,OAAO;AAAA,EACT,CAAC;AAGD,YAAU,KAAK;AAAA,IACb,MAAM;AAAA,IACN,OAAO;AAAA,MACL,EAAE,OAAO,SAAS,OAAO,OAAO,QAAQ,KAAK,EAAE;AAAA,MAC/C,EAAE,OAAO,UAAU,OAAO,OAAO,QAAQ,MAAM,EAAE;AAAA,MACjD,EAAE,OAAO,UAAU,OAAO,OAAO,QAAQ,MAAM,EAAE;AAAA,MACjD,EAAE,OAAO,WAAW,OAAO,OAAO,QAAQ,OAAO,EAAE;AAAA,MACnD,EAAE,OAAO,YAAY,OAAOA,gBAAe,QAAQ,UAAU,EAAE;AAAA,IACjE;AAAA,EACF,CAAC;AAGD,MAAI,QAAQ,YAAY,SAAS,GAAG;AAClC,UAAM,iBAAiB,QAAQ,YAAY,MAAM,GAAG,cAAc;AAElE,UAAM,cAAqB;AAAA,MACzB;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,MACX;AAAA,IACF;AAEA,eAAW,KAAK,gBAAgB;AAC9B,kBAAY,KAAK;AAAA,QACf,MAAM;AAAA,QACN,MAAM,KAAK,EAAE,IAAI;AAAA,QACjB,MAAM;AAAA,MACR,CAAC;AACD,UAAI,EAAE,OAAO;AACX,cAAM,aAAaD,UAAS,UAAU,EAAE,KAAK,GAAG,GAAG;AACnD,oBAAY,KAAK;AAAA,UACf,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,MAAM;AAAA,UACN,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,QAAQ,YAAY,SAAS,gBAAgB;AAC/C,kBAAY,KAAK;AAAA,QACf,MAAM;AAAA,QACN,MAAM,UAAU,QAAQ,YAAY,SAAS,cAAc;AAAA,QAC3D,UAAU;AAAA,QACV,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAGA,MAAI,QAAQ,IAAI;AAEd,UAAM,UAAiB,CAAC;AAExB,QAAI,QAAQ,GAAG,aAAa;AAC1B,cAAQ,KAAK,EAAE,OAAO,MAAM,OAAO,QAAQ,GAAG,YAAY,CAAC;AAAA,IAC7D;AACA,QAAI,QAAQ,GAAG,QAAQ;AACrB,cAAQ,KAAK,EAAE,OAAO,UAAU,OAAO,QAAQ,GAAG,OAAO,CAAC;AAAA,IAC5D;AACA,QAAI,QAAQ,GAAG,WAAW;AACxB,cAAQ,KAAK,EAAE,OAAO,UAAU,OAAO,QAAQ,GAAG,UAAU,MAAM,GAAG,CAAC,EAAE,CAAC;AAAA,IAC3E;AACA,QAAI,QAAQ,GAAG,aAAa;AAC1B,cAAQ,KAAK,EAAE,OAAO,SAAS,OAAO,IAAI,QAAQ,GAAG,WAAW,GAAG,CAAC;AAAA,IACtE;AAEA,QAAI,QAAQ,SAAS,GAAG;AACtB,gBAAU,KAAK;AAAA,QACb,MAAM;AAAA,QACN,OAAO;AAAA,QACP,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAIA,QAAM,OAA4B;AAAA,IAChC,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,EACR;AAGA,MAAI,QAAQ,WAAW;AACrB,SAAK,UAAU;AAAA,MACb;AAAA,QACE,MAAM;AAAA,QACN,OAAO;AAAA,QACP,KAAK,QAAQ;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,MACX;AAAA,QACE,aAAa;AAAA,QACb,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAQA,eAAsB,sBACpB,MACA,MACkC;AAClC,QAAM,EAAE,SAAS,YAAY,iBAAiB,EAAE,IAAI;AACpD,QAAM,EAAE,OAAO,OAAO,IAAI;AAE1B,QAAM,UAAU,kBAAkB,SAAS,cAAc;AAEzD,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,YAAY;AAAA,MACvC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,IAC9B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC1D,UAAI,WAAW;AACf,UAAI;AACF,mBAAW,MAAM,SAAS,KAAK;AAAA,MACjC,QAAQ;AAAA,MAER;AACA,YAAM,gBAAgBA,UAAS,UAAU,GAAG;AAC5C,YAAM,SAAS,YAAY,iBAAiB,SAAS,KAAK;AAC1D,YAAM,WAAW,+BAA+B,SAAS,MAAM,GAAG,MAAM,IAAI,aAAa;AACzF,aAAO,KAAK,QAAQ;AACpB,aAAO,EAAE,IAAI,OAAO,OAAO,SAAS;AAAA,IACtC;AAEA,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,WAAW,0BAA0B,GAAG;AAC9C,WAAO,KAAK,QAAQ;AACpB,WAAO,EAAE,IAAI,OAAO,OAAO,SAAS;AAAA,EACtC;AACF;;;AC3NA,SAAS,kBAAkB;AAgBpB,SAAS,SAAS,MAMN;AACjB,MAAI;AACJ,MAAI;AAEJ,MAAI,KAAK,kBAAkB;AACzB,gBAAY,KAAK,cAAa,oBAAI,KAAK,GAAE,YAAY;AACrD,YAAQ,GAAG,SAAS,IAAI,KAAK,IAAI;AAAA,EACnC,OAAO;AACL,YAAQ,KAAK;AAAA,EACf;AAEA,QAAM,MAAM,WAAW,UAAU,KAAK,MAAM,EACzC,OAAO,OAAO,MAAM,EACpB,OAAO,KAAK;AAEf,SAAO;AAAA,IACL,WAAW,UAAU,GAAG;AAAA,IACxB;AAAA,EACF;AACF;;;ACVA,eAAsB,wBACpB,MACA,MACoC;AACpC,QAAM,EAAE,SAAS,QAAQ,IAAI;AAC7B,QAAM,EAAE,OAAO,OAAO,IAAI;AAG1B,QAAM,UAA0B;AAAA,IAC9B,eAAe;AAAA,IACf,OAAO;AAAA,IACP;AAAA,EACF;AACA,QAAM,OAAO,KAAK,UAAU,OAAO;AAGnC,QAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAG7E,MAAI,QAAQ,SAAS;AACnB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAC1D,cAAQ,GAAG,IAAI;AAAA,IACjB;AAAA,EACF;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,EAAE,QAAQ,QAAQ,kBAAkB,gBAAgB,IAAI,QAAQ;AACtE,UAAM,SAAS,SAAS,EAAE,MAAM,QAAQ,iBAAiB,CAAC;AAC1D,YAAQ,MAAM,IAAI,OAAO;AACzB,QAAI,OAAO,WAAW;AACpB,cAAQ,mBAAmB,aAAa,IAAI,OAAO;AAAA,IACrD;AAAA,EACF;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,QAAQ,KAAK;AAAA,MACxC,QAAQ,QAAQ,UAAU;AAAA,MAC1B;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC1D,UAAI,UAAU;AACd,UAAI;AACF,mBAAW,MAAM,SAAS,KAAK,GAAG,MAAM,GAAG,GAAG;AAAA,MAChD,QAAQ;AAAA,MAER;AACA,YAAM,SAAS,YAAY,iBAAiB,SAAS,KAAK;AAC1D,YAAM,WAAW,iBAAiB,SAAS,MAAM,GAAG,MAAM,IAAI,OAAO;AACrE,aAAO,KAAK,QAAQ;AACpB,aAAO,EAAE,IAAI,OAAO,OAAO,SAAS;AAAA,IACtC;AAEA,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,WAAW,YAAY,GAAG;AAChC,WAAO,KAAK,QAAQ;AACpB,WAAO,EAAE,IAAI,OAAO,OAAO,SAAS;AAAA,EACtC;AACF;;;ACzDA,SAAS,aACP,KACA,WACAE,WACqB;AACrB,MAAI,SAAS;AACb,MAAI,SAAS;AACb,MAAI,UAAU;AAEd,QAAM,cAAkD,CAAC;AAEzD,aAAW,MAAM,IAAI,WAAW;AAC9B,YAAQ,GAAG,QAAQ;AAAA,MACjB,KAAK;AACH;AACA;AAAA,MACF,KAAK;AACH;AACA,oBAAY,KAAK;AAAA,UACf,QAAQ,GAAG;AAAA,UACX,MAAM,GAAG,MAAM;AAAA,UACf,OAAO,GAAG;AAAA,QACZ,CAAC;AACD;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH;AACA;AAAA,IACJ;AAAA,EACF;AAKA,MAAI;AACJ,MAAI,IAAI,IAAI;AACV,SAAKA,UAAS,IAAI,EAA0B;AAAA,EAC9C;AAEA,SAAO;AAAA,IACL,OAAO,IAAI,UAAU;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,IAAI;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAGA,SAAS,aAAa,WAA4B,aAA8B;AAC9E,MAAI,cAAc,QAAS,QAAO;AAClC,MAAI,cAAc,gBAAgB,gBAAgB,EAAG,QAAO;AAC5D,SAAO;AACT;AASA,eAAsB,kBACpB,MACA,MACe;AACf,QAAM,EAAE,KAAK,aAAa,IAAI;AAC9B,QAAM,EAAE,QAAQ,UAAAA,UAAS,IAAI;AAC7B,QAAM,MAAM,KAAK,OAAO,QAAQ;AAGhC,MAAI,CAAC,KAAK,OAAO;AACf,WAAO,KAAK,4CAA4C;AACxD;AAAA,EACF;AACA,QAAM,QAAQ,KAAK;AAGnB,QAAM,kBAAkB,cAAc,mBAAmB,IAAI;AAC7D,QAAM,kBAAkB,cAAc,mBAAmB,IAAI;AAC7D,QAAM,kBAAmC,cAAc,aAAa;AACpE,QAAM,YAAY,cAAc;AAChC,QAAM,iBAAiB,cAAc,kBAAkB;AACvD,QAAM,WAAW,cAAc,YAAY,CAAC;AAG5C,MAAI,CAAC,mBAAmB,CAAC,mBAAmB,SAAS,WAAW,GAAG;AACjE;AAAA,EACF;AAEA,QAAM,UAAU,aAAa,KAAK,WAAWA,SAAQ;AAGrD,QAAM,WAA4B,CAAC;AAEnC,MAAI,mBAAmB,aAAa,iBAAiB,QAAQ,MAAM,GAAG;AACpE,aAAS;AAAA,MACP;AAAA,QACE,EAAE,SAAS,YAAY,iBAAiB,eAAe;AAAA,QACvD,EAAE,OAAO,OAAO;AAAA,MAClB,EAAE,KAAK,MAAM,MAAS;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,mBAAmB,aAAa,iBAAiB,QAAQ,MAAM,GAAG;AACpE,aAAS;AAAA,MACP;AAAA,QACE,EAAE,SAAS,YAAY,iBAAiB,eAAe;AAAA,QACvD,EAAE,OAAO,OAAO;AAAA,MAClB,EAAE,KAAK,MAAM,MAAS;AAAA,IACxB;AAAA,EACF;AAGA,aAAW,WAAW,UAAU;AAC9B,UAAM,qBAAqB,QAAQ,aAAa;AAChD,QAAI,CAAC,aAAa,oBAAoB,QAAQ,MAAM,EAAG;AAEvD,aAAS;AAAA,MACP;AAAA,QACE,EAAE,SAAS,SAAS,SAAS,eAAe;AAAA,QAC5C,EAAE,OAAO,OAAO;AAAA,MAClB,EAAE,KAAK,MAAM,MAAS;AAAA,IACxB;AAAA,EACF;AAGA,QAAM,QAAQ,WAAW,QAAQ;AACnC;;;AChJA,IAAM,gBAA4C;AAAA,EAChD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,SAAS;AACX;AAEA,IAAM,mBAA+C;AAAA,EACnD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,IAAI;AACN;AAGO,SAAS,SAAS,KAAqC;AAC5D,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,WACJ,IAAI,YAAY,iBAAiB,IAAI,IAAI,KAAK;AAEhD,SAAO;AAAA,IACL;AAAA,IACA,aAAa,cAAc,QAAQ;AAAA,IACnC,KAAK,IAAI;AAAA,IACT,aAAa,IAAI;AAAA,IACjB,QAAQ,IAAI;AAAA,IACZ,WAAW,IAAI;AAAA,IACf,UAAU,IAAI;AAAA,EAChB;AACF;;;ACjDA,SAAS,aAA2B;AAClC,SAAO,EAAE,SAAS,GAAG,SAAS,IAAI,OAAO,CAAC,GAAG,aAAa,EAAE;AAC9D;AAEO,SAAS,YAAY,MAAuB,MAAqC;AACtF,QAAM,UAAU,KAAK,SAAS,KAAK,QAAQ;AAC3C,MAAI,YAAY,QAAW;AACzB,WAAO,WAAW;AAAA,EACpB;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AACN,SAAK,OAAO,KAAK,iCAAiC,KAAK,QAAQ,EAAE;AACjE,WAAO,WAAW;AAAA,EACpB;AAEA,MACE,OAAO,WAAW,YAClB,WAAW,QACV,OAAmC,YAAY,GAChD;AACA,SAAK,OAAO;AAAA,MACV,8BAA8B,KAAK,QAAQ;AAAA,IAC7C;AACA,WAAO,WAAW;AAAA,EACpB;AAEA,QAAM,MAAM;AACZ,MAAI,OAAO,IAAI,UAAU,YAAY,IAAI,UAAU,QAAQ,MAAM,QAAQ,IAAI,KAAK,GAAG;AACnF,SAAK,OAAO;AAAA,MACV,8BAA8B,KAAK,QAAQ;AAAA,IAC7C;AACA,WAAO,WAAW;AAAA,EACpB;AAEA,SAAO;AACT;AAeO,SAAS,YAAY,MAAuB,MAA6B;AAC9E,OAAK,UAAU,KAAK,UAAU,KAAK,UAAU,KAAK,OAAO,MAAM,CAAC,CAAC;AACnE;AAYO,SAAS,cAAc,MAAuC;AACnE,QAAM,EAAE,OAAO,KAAK,QAAQ,IAAI;AAChC,QAAM,WAAwC,EAAE,GAAG,MAAM,MAAM;AAE/D,aAAW,MAAM,IAAI,WAAW;AAC9B,UAAM,QAAsB;AAAA,MAC1B,OAAO,IAAI;AAAA,MACX,WAAW,IAAI;AAAA,MACf,QAAQ,GAAG;AAAA,MACX,YAAY,GAAG;AAAA,MACf,IAAI,IAAI,KACJ;AAAA,QACE,UAAU;AAAA,QACV,QAAQ,IAAI,GAAG;AAAA,QACf,WAAW,IAAI,GAAG;AAAA,MACpB,IACA;AAAA,IACN;AAEA,UAAM,WAAW,SAAS,GAAG,EAAE;AAC/B,QAAI,UAAU;AACZ,YAAM,iBAAiB,CAAC,GAAG,SAAS,SAAS,KAAK;AAElD,YAAM,UACJ,eAAe,SAAS,UACpB,eAAe,MAAM,eAAe,SAAS,OAAO,IACpD;AACN,eAAS,GAAG,EAAE,IAAI;AAAA,QAChB,GAAG;AAAA,QACH,UAAU,GAAG,MAAM;AAAA,QACnB,YAAY,GAAG;AAAA,QACf,YAAY,GAAG;AAAA,QACf,SAAS;AAAA,MACX;AAAA,IACF,OAAO;AACL,eAAS,GAAG,EAAE,IAAI;AAAA,QAChB,QAAQ,GAAG;AAAA,QACX,UAAU,GAAG,MAAM;AAAA,QACnB,YAAY,GAAG;AAAA,QACf,YAAY,GAAG;AAAA,QACf,SAAS,CAAC,KAAK;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,OAAO;AAAA,IACP,aAAa,KAAK,IAAI;AAAA,EACxB;AACF;;;AC5HA,IAAM,eAAuC;AAAA,EAC3C,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AAEO,SAAS,cACd,MACA,OACQ;AACR,QAAM,EAAE,WAAW,OAAO,IAAI;AAE9B,MAAI,WAAW,QAAQ;AACrB,UAAM,QAAQ,UAAU,IAAI,CAAC,QAAQ;AAAA,MACnC,UAAU,GAAG,MAAM;AAAA,MACnB,QAAQ,GAAG;AAAA,MACX,YAAY,GAAG;AAAA,MACf,YAAY,GAAG;AAAA,MACf,MAAM,GAAG;AAAA,MACT,IAAI,GAAG;AAAA,IACT,EAAE;AACF,WAAO,KAAK,UAAU,OAAO,MAAM,CAAC;AAAA,EACtC;AAGA,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM;AAC3C,UAAM,OAAO,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,EAAE;AAC5D,QAAI,KAAK,EAAE,MAAM,MAAM,KAAK,EAAE,MAAM,GAAG;AACrC,aAAO,KAAK,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM;AAAA,IACvC;AACA,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,WAAW,cAAc,EAAE,UAAU;AAAA,IAChD;AACA,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,WAAO,EAAE,MAAM,SAAS,cAAc,EAAE,MAAM,QAAQ;AAAA,EACxD,CAAC;AAED,QAAM,UAAU;AAAA,IACd,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AAAA,IACtD,SAAS,OAAO,OAAO,CAAC,OAAO,GAAG,WAAW,SAAS,EAAE;AAAA,IACxD,SAAS,OAAO,OAAO,CAAC,OAAO,GAAG,WAAW,SAAS,EAAE;AAAA,IACxD,QAAQ,OAAO,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AAAA,EACxD;AAEA,QAAM,QAAQ;AAAA,IACZ,mBAAmB,QAAQ,MAAM,YAAY,QAAQ,OAAO,aAAa,QAAQ,OAAO,aAAa,QAAQ,MAAM,YAAY,QAAQ,KAAK;AAAA,IAC5I,QAAQ,SAAS,IACb,4DACA;AAAA,IACJ;AAAA,IACA,GAAG,OAAO,IAAI,CAAC,OAAO;AACtB,YAAM,OAAO,aAAa,GAAG,MAAM,KAAK;AACxC,YAAM,SAAS,GAAG,OAAO,OAAO,CAAC;AACjC,YAAM,WAAW,GAAG,MAAM;AAC1B,YAAM,WAAW,GAAG,GAAG,UAAU,IAAI,GAAG,UAAU;AAClD,YAAM,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,KAAK,IAAI,IAAI;AACvD,aAAO,GAAG,IAAI,IAAI,MAAM,KAAK,QAAQ,KAAK,QAAQ,GAAG,OAAO,KAAK,IAAI,KAAK,EAAE;AAAA,IAC5E,CAAC;AAAA,EACH;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;A5DiQA,IAAM,oBAAkD;AAAA,EACtD,OAAO;AAAA,EACP,UAAU;AAAA,EACV,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,qBAAqB;AACvB;AAGA,IAAM,kBAAkB;AAAA,EACtB;AAAA,EAAY;AAAA,EAAa;AAAA,EAAY;AAAA,EACrC;AAAA,EAAY;AAAA,EAAY;AAAA,EAAkB;AAC5C;AASA,SAAS,iBACP,YACA,OACwB;AACxB,aAAW,QAAQ,OAAO;AACxB,QAAI,eAAe,KAAK,OAAO,UAAU,GAAG;AAC1C,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,QAAQ,GAAmB;AAClC,SAAO,EAAE,QAAQ,OAAO,GAAG;AAC7B;AAKA,SAAS,kBACP,YACA,QACA,MACA,gBACA,eACA,YACA,kBACQ;AACR,QAAM,MAAM,kBAAkB,MAAM;AACpC,QAAM,gBAAgB,cAAc,oBAAoB;AAExD,MAAI,SAAS,cAAc;AAEzB,WAAO,QAAa,WAAK,eAAe,GAAG,aAAa,GAAG,GAAG,EAAE,CAAC;AAAA,EACnE;AAGA,QAAM,mBAAmB,QAAQ,UAAU;AAC3C,QAAM,cAAmB,YAAM,QAAQ,gBAAgB;AACvD,MAAI,WAAgB,YAAM,SAAS,gBAAgB;AAGnD,aAAW,WAAW,iBAAiB;AACrC,QAAI,SAAS,SAAS,OAAO,GAAG;AAC9B,iBAAW,SAAS,MAAM,GAAG,CAAC,QAAQ,MAAM;AAC5C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,GAAG,QAAQ,IAAI,aAAa,GAAG,GAAG;AAEnD,MAAI,mBAAmB,YAAY;AAEjC,WAAO,QAAa,YAAM,KAAK,aAAa,QAAQ,CAAC;AAAA,EACvD;AAGA,SAAO,QAAa,YAAM,KAAK,eAAe,aAAa,QAAQ,CAAC;AACtE;AAKA,SAAS,uBACP,WACA,QACA,SACA,QACA,kBAC+B;AAC/B,QAAM,SAAS,oBAAI,IAA8B;AACjD,QAAM,QAAQ,QAAQ,OAAO;AAC7B,QAAM,cAAc,QAAQ,OAAO;AACnC,QAAM,wBAAwB,QAAQ,OAAO;AAC7C,QAAM,iBAAiB,QAAQ;AAC/B,QAAM,mBAAmB,QAAQ;AACjC,QAAM,oBAAoB,QAAQ;AAElC,aAAW,MAAM,WAAW;AAC1B,UAAM,aAAa,GAAG;AAGtB,QAAI,gBAAgB,eAAe,eAAe,WAAW;AAC3D,aAAO;AAAA,QACL,cAAc,GAAG,MAAM,QAAQ;AAAA,MACjC;AAAA,IACF;AAGA,UAAM,OAAO,iBAAiB,YAAY,KAAK;AAG/C,UAAM,OAAO,MAAM,QAAQ;AAC3B,UAAM,iBAAiB,MAAM,kBAAkB;AAC/C,UAAM,UAAU,MAAM,WAAW;AACjC,UAAM,YAAY,MAAM,aAAa;AACrC,UAAM,aAAa,MAAM,cAAc,QAAQ,OAAO,cAAc;AAGpE,QACE,QACA,KAAK,mBAAmB,cACxB,KAAK,cAAc,QACnB;AACA,aAAO;AAAA,QACL,aAAa,KAAK,KAAK;AAAA,MACzB;AAAA,IACF;AAGA,QAAI,CAAC,QAAQ,SAAS,MAAM,GAAG;AAC7B;AAAA,IACF;AAGA,UAAM,gBACJ,SAAS,eAAe,eAAe,YAAY,eAAe;AAEpE,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,WAAW,OAAO,IAAI,UAAU;AACtC,QAAI,UAAU;AACZ,eAAS,KAAK,EAAE;AAAA,IAClB,OAAO;AACL,aAAO,IAAI,YAAY,CAAC,EAAE,CAAC;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO;AACT;AAiBO,IAAM,kBAAN,MAAsB;AAAA,EACnB;AAAA,EACA;AAAA,EAER,YAAY,UAA4B,CAAC,GAAG,MAA8B;AACxE,SAAK,UAAU,KAAK,eAAe,OAAO;AAC1C,SAAK,OAAO;AAAA,MACV,QAAQ,MAAM,UAAU;AAAA,MACxB,WAAW,MAAM,cAAc,CAAC,GAAG,MAAiB,qBAAU,GAAG,GAAG,MAAM;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,SAAqD;AAC1E,WAAO;AAAA,MACL,SAAS,QAAQ,WAAW,CAAC;AAAA,MAC7B,SAAS,QAAQ,WAAW,CAAC;AAAA,MAC7B,aAAa,QAAQ,eAAe,CAAC;AAAA,MACrC,aAAa,QAAQ,eAAe,CAAC;AAAA,MACrC,SAAS,QAAQ,WAAW,CAAC,eAAe;AAAA,MAC5C,WAAW,QAAQ,aAAa;AAAA,MAChC,YAAY,QAAQ,cAAc;AAAA,MAClC,qBAAqB,QAAQ,uBAAuB;AAAA,MACpD,eAAe,QAAQ,iBAAiB;AAAA,MACxC,QAAQ;AAAA,QACN,MAAM,QAAQ,QAAQ,QAAQ;AAAA,QAC9B,gBAAgB,QAAQ,QAAQ,kBAAkB;AAAA,QAClD,OAAO,QAAQ,QAAQ,SAAS,CAAC;AAAA,QACjC,YAAY,QAAQ,QAAQ;AAAA,MAC9B;AAAA,MACA,cAAc;AAAA,QACZ,QAAQ,QAAQ,cAAc,UAAU;AAAA,MAC1C;AAAA,MACA,kBAAkB;AAAA,QAChB,aAAa,QAAQ,kBAAkB,eAAe;AAAA,QACtD,mBAAmB,QAAQ,kBAAkB,qBAAqB;AAAA,QAClE,QAAQ,QAAQ,kBAAkB,UAAU;AAAA,QAC5C,MAAM,QAAQ,kBAAkB;AAAA,MAClC;AAAA,MACA,MAAM;AAAA,QACJ,OAAO,QAAQ,MAAM,SAAS;AAAA,QAC9B,UAAU,QAAQ,MAAM,YAAY;AAAA,QACpC,YAAY,QAAQ,MAAM,cAAc;AAAA,QACxC,gBAAgB,QAAQ,MAAM,kBAAkB;AAAA,QAChD,kBAAkB,QAAQ,MAAM,oBAAoB;AAAA,QACpD,oBAAoB,QAAQ,MAAM,sBAAsB;AAAA,QACxD,gBAAgB,QAAQ,MAAM,kBAAkB;AAAA,QAChD,iBAAiB,QAAQ,MAAM,mBAAmB;AAAA,QAClD,kBAAkB,QAAQ,MAAM;AAAA,QAChC,mBAAmB,QAAQ,MAAM;AAAA,QACjC,OAAO,QAAQ,MAAM,SAAS;AAAA,QAC9B,YAAY,QAAQ,MAAM,cAAc;AAAA,QACxC,oBAAoB,QAAQ,MAAM,sBAAsB;AAAA,MAC1D;AAAA,MACA,OAAO;AAAA,QACL,WAAW,QAAQ,OAAO,aAAa;AAAA,QACvC,eAAe,QAAQ,OAAO,iBAAiB;AAAA,MACjD;AAAA,MACA,UAAU;AAAA,QACR,OAAO,QAAQ,UAAU,SAAS;AAAA,QAClC,oBAAoB,QAAQ,UAAU,sBAAsB;AAAA,QAC5D,iBAAiB,QAAQ,UAAU,mBAAmB;AAAA,QACtD,eAAe,QAAQ,UAAU,iBAAiB;AAAA,QAClD,sBAAsB,QAAQ,UAAU,wBAAwB;AAAA,QAChE,WAAW,QAAQ,UAAU,aAAa;AAAA,QAC1C,SAAS,QAAQ,UAAU,WAAW;AAAA,QACtC,eAAe,QAAQ,UAAU,iBAAiB;AAAA,QAClD,gBAAgB,QAAQ,UAAU,kBAAkB;AAAA,QACpD,oBAAoB,QAAQ,UAAU,sBAAsB;AAAA,QAC5D,qBAAqB,QAAQ,UAAU,uBAAuB;AAAA,QAC9D,kBAAkB,QAAQ,UAAU;AAAA,QACpC,mBAAmB,QAAQ,UAAU;AAAA,QACrC,kBAAkB,QAAQ,UAAU;AAAA,QACpC,oBAAoB,QAAQ,UAAU,sBAAsB;AAAA,QAC5D,iBAAiB,QAAQ,UAAU;AAAA,MACrC;AAAA,MACA,OAAO;AAAA,QACL,WAAW,QAAQ,OAAO,aAAa;AAAA,QACvC,eAAe,QAAQ,OAAO,iBAAiB;AAAA,QAC/C,UAAU;AAAA,UACR,OAAO,QAAQ,OAAO,UAAU,SAAS;AAAA,UACzC,oBAAoB,QAAQ,OAAO,UAAU,sBAAsB;AAAA,UACnE,eAAe,QAAQ,OAAO,UAAU,iBAAiB;AAAA,UACzD,sBAAsB,QAAQ,OAAO,UAAU,wBAAwB;AAAA,UACvE,SAAS,QAAQ,OAAO,UAAU,WAAW;AAAA,UAC7C,eAAe,QAAQ,OAAO,UAAU,iBAAiB;AAAA,UACzD,gBAAgB,QAAQ,OAAO,UAAU,kBAAkB;AAAA,UAC3D,oBAAoB,QAAQ,OAAO,UAAU,sBAAsB;AAAA,UACnE,kBAAkB,QAAQ,OAAO,UAAU;AAAA,UAC3C,mBAAmB,QAAQ,OAAO,UAAU;AAAA,UAC5C,kBAAkB,QAAQ,OAAO,UAAU;AAAA,UAC3C,iBAAiB,QAAQ,OAAO,UAAU;AAAA,QAC5C;AAAA,MACF;AAAA,MACA,WAAW,QAAQ,aAAa;AAAA,MAChC,oBAAoB,QAAQ,sBAAsB;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SAAS,KAA6C;AAC1D,UAAM,YAAY;AAAA,MAChB;AAAA,QACE,WAAW,IAAI;AAAA,QACf,SAAS,KAAK,QAAQ;AAAA,QACtB,SAAS,KAAK,QAAQ;AAAA,QACtB,aAAa,KAAK,QAAQ;AAAA,QAC1B,aAAa,KAAK,QAAQ;AAAA,QAC1B,eAAe,KAAK,QAAQ;AAAA,MAC9B;AAAA,MACA,EAAE,QAAQ,KAAK,KAAK,OAAO;AAAA,IAC7B;AAEA,UAAM,cAA6B,EAAE,GAAG,KAAK,UAAU;AAEvD,UAAM,UAA0B,oBAAI,IAAI;AAExC,eAAW,UAAU,KAAK,QAAQ,SAAS;AACzC,YAAM,QAAQ,MAAM,KAAK,eAAe,aAAa,MAAM;AAC3D,cAAQ,IAAI,QAAQ,KAAK;AAAA,IAC3B;AAEA,QAAI,KAAK,QAAQ,cAAc,QAAQ;AACrC,YAAM,YAAY,QAAQ,IAAI,MAAM;AACpC,UAAI,WAAW;AACb,mBAAW,YAAY,WAAW;AAChC,uBAAa,UAAU;AAAA,YACrB,cAAc,KAAK,QAAQ;AAAA,UAC7B,CAAC;AAAA,QACH;AAAA,MACF;AAEA,YAAM,aAAa,QAAQ,IAAI,OAAO;AACtC,UAAI,YAAY;AACd,mBAAW,UAAU,YAAY;AAC/B,gBAAM,UAAU,MAAiB,oBAAS,QAAQ,MAAM;AACxD,gBAAM,QAAa,cAAQ,MAAM;AAEjC,gBAAM,YAAiB,cAAQ,KAAK,QAAQ,MAAM,SAAS;AAC3D,gBAAM,SAAS,mBAAmB;AAAA,YAChC,UAAU;AAAA,YACV,aAAa;AAAA,YACb;AAAA,YACA,eAAe,KAAK,QAAQ,MAAM;AAAA,YAClC,cAAc,KAAK,QAAQ;AAAA,UAC7B,CAAC;AACD,cAAI,OAAO,cAAc,KAAK,OAAO,eAAe,GAAG;AACrD,kBAAM,KAAK,KAAK,UAAU,QAAQ,OAAO,QAAQ;AAAA,UACnD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eACZ,KACA,QACmB;AACnB,UAAM,mBAAmB,KAAK,QAAQ,sBAClC,IAAI,KAAK,MAAM,IAAI,cAAc,GAAI,CAAC,KACtC;AAGJ,UAAM,SAAS;AAAA,MACb,IAAI;AAAA,MACJ;AAAA,MACA,KAAK;AAAA,MACL,KAAK,KAAK;AAAA,MACV;AAAA,IACF;AAGA,QAAI,OAAO,SAAS,KAAK,KAAK,QAAQ,OAAO,SAAS,cAAc;AAClE,YAAM,MAAM,kBAAkB,MAAM;AACpC,YAAM,gBAAgB,KAAK,QAAQ,cAAc,oBAAoB;AACrE,YAAM,aAAa,QAAa,WAAK,KAAK,QAAQ,WAAW,GAAG,aAAa,GAAG,GAAG,EAAE,CAAC;AACtF,YAAM,UAAU,MAAM,KAAK,cAAc,KAAK,MAAM;AACpD,YAAM,MAAW,cAAQ,UAAU;AACnC,YAAiB,iBAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAC/C,YAAM,KAAK,KAAK,UAAU,YAAY,OAAO;AAC7C,aAAO,CAAC,UAAU;AAAA,IACpB;AAEA,UAAM,eAAyB,CAAC;AAEhC,eAAW,CAAC,YAAY,SAAS,KAAK,QAAQ;AAE5C,YAAM,WAA0B;AAAA,QAC9B,GAAG;AAAA,QACH;AAAA,MACF;AAGA,YAAM,UAAU,MAAM,KAAK,cAAc,UAAU,MAAM;AAGzD,YAAM,MAAW,cAAQ,UAAU;AACnC,YAAiB,iBAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAG/C,YAAM,KAAK,KAAK,UAAU,YAAY,OAAO;AAC7C,mBAAa,KAAK,UAAU;AAAA,IAC9B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,KAAoB,QAAgD;AACxF,YAAQ,QAAQ;AAAA,MACd,KAAK,iBAAiB;AACpB,cAAM,YAAY,IAAI,sBAAsB;AAAA,UAC1C,QAAQ,KAAK,QAAQ,aAAa;AAAA,QACpC,CAAC;AACD,eAAO,UAAU,eAAe,GAAG;AAAA,MACrC;AAAA,MAEA,KAAK,QAAQ;AACX,cAAM,YAAY,IAAI,cAAc;AAAA,UAClC,OAAO,KAAK,QAAQ,KAAK;AAAA,UACzB,OAAO,KAAK,QAAQ,KAAK;AAAA,UACzB,UAAU,KAAK,QAAQ,KAAK;AAAA,UAC5B,YAAY,KAAK,QAAQ,KAAK;AAAA,UAC9B,gBAAgB,KAAK,QAAQ,KAAK;AAAA,UAClC,kBAAkB,KAAK,QAAQ,KAAK;AAAA,UACpC,oBAAoB,KAAK,QAAQ,KAAK;AAAA,UACtC,gBAAgB,KAAK,QAAQ,KAAK;AAAA,UAClC,iBAAiB,KAAK,QAAQ,KAAK;AAAA,UACnC,kBAAkB,KAAK,QAAQ,KAAK;AAAA,UACpC,mBAAmB,KAAK,QAAQ,KAAK;AAAA,UACrC,YAAY,KAAK,QAAQ,KAAK;AAAA,UAC9B,oBAAoB,KAAK,QAAQ,KAAK;AAAA,QACxC,CAAC;AACD,eAAO,UAAU,OAAO,GAAG;AAAA,MAC7B;AAAA,MAEA,KAAK,iBAAiB;AACpB,cAAM,YAAY,IAAI,sBAAsB;AAAA,UAC1C,UAAU;AAAA,YACR,aAAa,KAAK,QAAQ,iBAAiB;AAAA,YAC3C,mBAAmB,KAAK,QAAQ,iBAAiB;AAAA,YACjD,QAAQ,KAAK,QAAQ,iBAAiB;AAAA,YACtC,MAAM,KAAK,QAAQ,iBAAiB;AAAA,UACtC;AAAA,QACF,CAAC;AACD,eAAO,UAAU,eAAe,GAAG;AAAA,MACrC;AAAA,MAEA,KAAK,SAAS;AACZ,cAAM,YAAY,IAAI,eAAe;AAAA,UACnC,WAAW,KAAK,QAAQ,MAAM;AAAA,UAC9B,eAAe,KAAK,QAAQ,MAAM;AAAA,QACpC,CAAC;AACD,eAAO,UAAU,OAAO,GAAG;AAAA,MAC7B;AAAA,MAEA,KAAK,qBAAqB;AACxB,cAAM,YAAY,IAAI,0BAA0B;AAAA,UAC9C,aAAa,KAAK,QAAQ,iBAAiB;AAAA,UAC3C,mBAAmB,KAAK,QAAQ,iBAAiB;AAAA,UACjD,QAAQ,KAAK,QAAQ,iBAAiB;AAAA,UACtC,MAAM,KAAK,QAAQ,iBAAiB;AAAA,QACtC,CAAC;AACD,eAAO,UAAU,eAAe,GAAG;AAAA,MACrC;AAAA,MAEA,KAAK,SAAS;AACZ,cAAM,YAAY,IAAI,eAAe;AAAA,UACnC,eAAe,KAAK,QAAQ,MAAM;AAAA,UAClC,UAAU,KAAK,QAAQ,MAAM;AAAA,QAC/B,CAAC;AACD,eAAO,UAAU,OAAO,GAAG;AAAA,MAC7B;AAAA,MAEA,KAAK,YAAY;AACf,cAAM,YAAY,IAAI,kBAAkB;AAAA,UACtC,OAAO,KAAK,QAAQ,SAAS;AAAA,UAC7B,oBAAoB,KAAK,QAAQ,SAAS;AAAA,UAC1C,iBAAiB,KAAK,QAAQ,SAAS;AAAA,UACvC,eAAe,KAAK,QAAQ,SAAS;AAAA,UACrC,sBAAsB,KAAK,QAAQ,SAAS;AAAA,UAC5C,WAAW,KAAK,QAAQ,SAAS;AAAA,UACjC,SAAS,KAAK,QAAQ,SAAS;AAAA,UAC/B,eAAe,KAAK,QAAQ,SAAS;AAAA,UACrC,gBAAgB,KAAK,QAAQ,SAAS;AAAA,UACtC,oBAAoB,KAAK,QAAQ,SAAS;AAAA,UAC1C,qBAAqB,KAAK,QAAQ,SAAS;AAAA,UAC3C,kBAAkB,KAAK,QAAQ,SAAS;AAAA,UACxC,mBAAmB,KAAK,QAAQ,SAAS;AAAA,UACzC,kBAAkB,KAAK,QAAQ,SAAS;AAAA,UACxC,oBAAoB,KAAK,QAAQ,SAAS;AAAA,UAC1C,iBAAiB,KAAK,QAAQ,SAAS;AAAA,QACzC,CAAC;AACD,eAAO,UAAU,OAAO,GAAG;AAAA,MAC7B;AAAA,MAEA;AACE,cAAM,IAAI,MAAM,mBAAmB,MAAM,EAAE;AAAA,IAC/C;AAAA,EACF;AACF;AAcA,eAAsB,sBAAsB,MAOT;AACjC,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,aAAa,KAAK,cAAc;AACtC,QAAM,OAAO,SAAS,KAAK,UAAU,KAAK,OAAO;AACjD,QAAM,QAAkB,CAAC;AAEzB,QAAiB,iBAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAErD,aAAW,UAAU,KAAK,SAAS;AACjC,UAAM,MAAM,WAAW,SAAS,UAAU;AAC1C,UAAM,aAAa,QAAa,WAAK,WAAW,GAAG,UAAU,GAAG,GAAG,EAAE,CAAC;AACtE,UAAM,UACJ,WAAW,SACP,IAAI,qBAAqB,EAAE,OAAO,KAAK,MAAM,CAAC,EAAE,OAAO,IAAI,IAC3D,IAAI,yBAAyB,EAAE,OAAO,KAAK,MAAM,CAAC,EAAE,OAAO,IAAI;AACrE,UAAiB,qBAAU,YAAY,SAAS,MAAM;AACtD,UAAM,KAAK,UAAU;AAAA,EACvB;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;;;A6Dv2BA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,SAAS,qBAAqB;AAE9B,IAAM,YAAiB,cAAQ,cAAc,YAAY,GAAG,CAAC;AAWtD,SAAS,UAAU,UAA4B,CAAC,GAAoB;AACzE,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,QAAQ,QAAQ,SAAS;AAE/B,MAAO,eAAW,SAAS,GAAG;AAC5B,UAAM,UAAa,gBAAY,SAAS;AACxC,QAAI,QAAQ,SAAS,KAAK,CAAC,OAAO;AAChC,YAAM,IAAI;AAAA,QACR,cAAc,SAAS;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,cAAmB,cAAQ,WAAW,MAAM,aAAa,iBAAiB;AAEhF,MAAI,CAAI,eAAW,WAAW,GAAG;AAC/B,UAAM,IAAI;AAAA,MACR,mCAAmC,WAAW;AAAA,IAChD;AAAA,EACF;AAEA,mBAAiB,aAAa,SAAS;AACvC,SAAO,EAAE,UAAU;AACrB;AAEA,SAAS,iBAAiB,KAAa,MAAoB;AACzD,EAAG,cAAU,MAAM,EAAE,WAAW,KAAK,CAAC;AACtC,QAAM,UAAa,gBAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAC3D,aAAW,SAAS,SAAS;AAC3B,UAAM,UAAe,WAAK,KAAK,MAAM,IAAI;AACzC,UAAM,WAAgB,WAAK,MAAM,MAAM,IAAI;AAC3C,QAAI,MAAM,YAAY,GAAG;AACvB,uBAAiB,SAAS,QAAQ;AAAA,IACpC,OAAO;AACL,MAAG,iBAAa,SAAS,QAAQ;AAAA,IACnC;AAAA,EACF;AACF;;;AvEfA,IAAM,eAAe;AACrB,IAAM,yBAAyB;AAC/B,IAAM,4BAA4B;AAClC,IAAM,kBAAkB;AACxB,IAAM,aAAa;AAMnB,IAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkGhB,KAAK;AAmDP,SAAS,aAAa,MAAyB;AAE7C,QAAM,OAAO,KAAK,MAAM,CAAC;AAEzB,MAAI,KAAK,WAAW,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;AACvE,YAAQ,IAAI,SAAS;AACrB,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAEA,QAAM,aAAa,KAAK,CAAC;AACzB,MAAI,eAAe,YAAY,eAAe,aAAa,eAAe,UAAU,eAAe,cAAc,eAAe,cAAc;AAC5I,YAAQ,MAAM,wBAAwB,UAAU,kEAAkE;AAClH,YAAQ,KAAK,UAAU;AAAA,EACzB;AAGA,MAAI,eAAe,cAAc;AAC/B,UAAM,WAAW,KAAK,MAAM,CAAC;AAC7B,UAAM,YAAY,SAAS,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,IAAI,CAAC,KAAK;AAC/D,UAAM,QAAQ,SAAS,SAAS,SAAS;AAEzC,QAAI;AACF,YAAM,SAAS,UAAY,EAAE,WAAW,MAAM,CAAC;AAC/C,cAAQ,IAAI,yCAAyC,OAAO,SAAS,EAAE;AACvE,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,aAAa;AACzB,cAAQ,IAAI,QAAQ,OAAO,SAAS,EAAE;AACtC,cAAQ,IAAI,oCAAoC;AAChD,cAAQ,IAAI,0CAA0C;AACtD,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,2BAA2B;AACvC,cAAQ,IAAI,oEAAoE,OAAO,SAAS,6CAA6C;AAC7I,cAAQ,KAAK,YAAY;AAAA,IAC3B,SAAS,KAAK;AACZ,cAAQ,MAAM,UAAW,IAAc,OAAO,EAAE;AAChD,cAAQ,KAAK,UAAU;AAAA,IACzB;AAAA,EACF;AAGA,QAAM,EAAE,QAAQ,YAAY,IAAI,UAAU;AAAA,IACxC,MAAM,KAAK,MAAM,CAAC;AAAA,IAClB,SAAS;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,MAC1C,UAAU,EAAE,MAAM,SAAS;AAAA,MAC3B,gBAAgB,EAAE,MAAM,SAAS;AAAA,MACjC,cAAc,EAAE,MAAM,UAAU,SAAS,MAAM;AAAA,MAC/C,cAAc,EAAE,MAAM,UAAU,SAAS,UAAU;AAAA,MACnD,eAAe,EAAE,MAAM,UAAU,SAAS,QAAQ;AAAA,MAClD,yBAAyB,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAC3D,mBAAmB,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,MACrD,SAAS,EAAE,MAAM,SAAS;AAAA,MAC1B,SAAS,EAAE,MAAM,SAAS;AAAA,MAC1B,gBAAgB,EAAE,MAAM,SAAS;AAAA,MACjC,gBAAgB,EAAE,MAAM,SAAS;AAAA,MACjC,sBAAsB,EAAE,MAAM,WAAW,SAAS,KAAK;AAAA,MACvD,yBAAyB,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAC3D,cAAc,EAAE,MAAM,UAAU,SAAS,eAAe;AAAA,MACxD,cAAc,EAAE,MAAM,UAAU,SAAS,UAAU;AAAA,MACnD,+BAA+B,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACjE,mBAAmB,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACrD,oBAAoB,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACtD,2BAA2B,EAAE,MAAM,SAAS;AAAA,MAC5C,4BAA4B,EAAE,MAAM,SAAS;AAAA,MAC7C,eAAe,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACjD,qBAAqB,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACvD,OAAO,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACzC,gBAAgB,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAClD,kBAAkB,EAAE,MAAM,SAAS;AAAA,MACnC,iBAAiB,EAAE,MAAM,SAAS;AAAA,MAClC,iBAAiB,EAAE,MAAM,SAAS;AAAA,MAClC,QAAQ,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,MAChD,cAAc,EAAE,MAAM,SAAS;AAAA,MAC/B,oBAAoB,EAAE,MAAM,SAAS;AAAA,MACrC,gBAAgB,EAAE,MAAM,SAAS;AAAA,MACjC,oBAAoB,EAAE,MAAM,SAAS;AAAA,MACrC,eAAe,EAAE,MAAM,UAAU,UAAU,KAAK;AAAA,MAChD,kBAAkB,EAAE,MAAM,UAAU,UAAU,KAAK;AAAA,MACnD,kBAAkB,EAAE,MAAM,SAAS;AAAA,MACnC,uBAAuB,EAAE,MAAM,SAAS;AAAA,MACxC,uBAAuB,EAAE,MAAM,SAAS;AAAA,MACxC,0BAA0B,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAC5D,cAAc,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,MAChD,wBAAwB,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAC1D,cAAc,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAChD,mBAAmB,EAAE,MAAM,SAAS;AAAA,MACpC,MAAM,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,IAC1C;AAAA,IACA,kBAAkB;AAAA,IAClB,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,OAAO,MAAM;AACf,YAAQ,IAAI,SAAS;AACrB,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAEA,QAAM,WAAW,OAAO;AACxB,QAAM,gBAAgB,OAAO;AAC7B,QAAM,eAAe,kBAAkB,SAAS,SAAS;AACzD,QAAM,YAAY,eAAe,YAAY,SAAY,YAAY,CAAC;AACtE,QAAM,eACJ,eAAe,YACX,iBAAiB,SACf,iBAAiB,kBAAkB,SACjC,gBACA,YAAY,SAAS,IACnB,YAAY,CAAC,IACb,SACJ,iBAAiB,kBAAkB,SACjC,gBACA,YAAY,SAAS,IACnB,YAAY,CAAC,IACb,SACN;AACN,QAAM,cACJ,eAAe,YACX,YAAY,SAAS,IACnB,YAAY,CAAC,IACb,YAAY,CAAC,IACf;AAEN,MAAI,eAAe,WAAW;AAC5B,QAAI,UAAU;AACZ,cAAQ,MAAM,2EAA2E;AACzF,cAAQ,KAAK,UAAU;AAAA,IACzB;AACA,QAAI,CAAC,aAAa;AAChB,cAAQ,MAAM,wFAAwF;AACtG,cAAQ,KAAK,UAAU;AAAA,IACzB;AACA,QAAI,iBAAiB,cAAc,CAAC,cAAc;AAChD,cAAQ,MAAM,qFAAqF;AACnG,cAAQ,KAAK,UAAU;AAAA,IACzB;AAAA,EACF,WAAW,CAAC,YAAY,CAAC,WAAW;AAClC,YAAQ,MAAM,uEAAuE;AACrF,YAAQ,KAAK,UAAU;AAAA,EACzB;AAEA,QAAM,YAAY,OAAO,YAAY;AACrC,MAAI,cAAc,SAAS,cAAc,eAAe,cAAc,UAAU;AAC9E,YAAQ,MAAM,qEAAqE,SAAS,IAAI;AAChG,YAAQ,KAAK,UAAU;AAAA,EACzB;AAGA,QAAM,eAAe,oBAAI,IAAI,CAAC,SAAS,QAAQ,YAAY,SAAS,iBAAiB,qBAAqB,eAAe,CAAC;AAC1H,QAAM,YAAY,OAAO;AACzB,QAAM,UAAU,UAAU,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACxD,aAAW,KAAK,SAAS;AACvB,QAAI,CAAC,aAAa,IAAI,CAAC,GAAG;AACxB,cAAQ,MAAM,0BAA0B,CAAC,0FAA0F;AACnI,cAAQ,KAAK,UAAU;AAAA,IACzB;AAAA,EACF;AAGA,QAAM,YAAY,OAAO,YAAY;AACrC,QAAM,cAAc,oBAAI,IAAI,CAAC,WAAW,aAAa,YAAY,WAAW,aAAa,SAAS,CAAC;AACnG,MAAI,CAAC,YAAY,IAAI,SAAS,GAAG;AAC/B,YAAQ,MAAM,yBAAyB,SAAS,aAAa,CAAC,GAAG,WAAW,EAAE,KAAK,IAAI,CAAC,GAAG;AAC3F,YAAQ,KAAK,UAAU;AAAA,EACzB;AAEA,QAAM,eAAe,OAAO,uBAAuB;AAEnD,QAAM,aAAa,CAAC,MAClB,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IAAI,CAAC;AAG3D,QAAM,cAAc,OAAO;AAC3B,QAAM,wBAAwB,oBAAI,IAAI,CAAC,UAAU,cAAc,OAAO,CAAC;AACvE,MAAI,CAAC,sBAAsB,IAAI,WAAW,GAAG;AAC3C,YAAQ,MAAM,oEAAoE,WAAW,IAAI;AACjG,YAAQ,KAAK,UAAU;AAAA,EACzB;AAGA,QAAM,oBAAoB,OAAO,kBAAkB;AACnD,QAAM,iBAAiB,oBAAoB,SAAS,mBAAmB,EAAE,IAAI;AAC7E,MAAI,sBAAsB,MAAM,cAAc,KAAK,iBAAiB,IAAI;AACtE,YAAQ,MAAM,kEAAkE,iBAAiB,IAAI;AACrG,YAAQ,KAAK,UAAU;AAAA,EACzB;AAGA,QAAM,eAAe,OAAO,eAAe;AAC3C,QAAM,eAAe,OAAO,eAAe;AAG3C,QAAM,cAAe,OAAO,aAAa,KAA8B,CAAC;AAGxE,QAAM,iBAAyC,CAAC;AAChD,QAAM,aAAc,OAAO,gBAAgB,KAA8B,CAAC;AAC1E,aAAW,KAAK,YAAY;AAC1B,UAAM,WAAW,EAAE,QAAQ,GAAG;AAC9B,QAAI,YAAY,GAAG;AACjB,cAAQ,MAAM,+CAA+C,CAAC,2BAA2B;AACzF;AAAA,IACF;AACA,UAAM,MAAM,EAAE,MAAM,GAAG,QAAQ,EAAE,KAAK;AACtC,UAAM,QAAQ,EAAE,MAAM,WAAW,CAAC,EAAE,KAAK;AACzC,QAAI,CAAC,KAAK;AACR,cAAQ,MAAM,mDAAmD;AACjE;AAAA,IACF;AACA,mBAAe,GAAG,IAAI;AAAA,EACxB;AAGA,QAAM,mBAAmB,OAAO,gBAAgB;AAChD,MAAI,gBAAgC;AACpC,MAAI,kBAAkB;AACpB,UAAM,QAAQ,iBAAiB,YAAY;AAC3C,QAAI,UAAU,UAAU,UAAU,OAAO;AACvC,cAAQ,MAAM,yDAAyD,gBAAgB,IAAI;AAC3F,cAAQ,KAAK,UAAU;AAAA,IACzB;AACA,oBAAgB;AAAA,EAClB;AAGA,QAAM,oBAAoB,OAAO,kBAAkB;AACnD,QAAM,iBAAiB,oBAAoB,SAAS,mBAAmB,EAAE,IAAI;AAC7E,MAAI,sBAAsB,MAAM,cAAc,KAAK,iBAAiB,IAAI;AACtE,YAAQ,MAAM,8DAA8D,iBAAiB,IAAI;AACjG,YAAQ,KAAK,UAAU;AAAA,EACzB;AAEA,QAAM,mBAAmB,OAAO,iBAAiB;AACjD,QAAM,iBAAiB,oBAAI,IAAI,CAAC,MAAM,UAAU,MAAM,CAAC;AACvD,MAAI,CAAC,eAAe,IAAI,gBAAgB,GAAG;AACzC,YAAQ,MAAM,8DAA8D,gBAAgB,IAAI;AAChG,YAAQ,KAAK,UAAU;AAAA,EACzB;AAEA,QAAM,eAAe,OAAO,YAAY;AACxC,QAAM,kBAAkB,oBAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;AAChD,MAAI,CAAC,gBAAgB,IAAI,YAAY,GAAG;AACtC,YAAQ,MAAM,sDAAsD,YAAY,IAAI;AACpF,YAAQ,KAAK,UAAU;AAAA,EACzB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,cAAc;AAAA,IAClC,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,WAAW,OAAO,YAAY;AAAA,IAC9B,YAAY,OAAO,aAAa;AAAA,IAChC,qBAAqB,OAAO,uBAAuB;AAAA,IACnD,eAAe;AAAA,IACf,SAAS,WAAW,OAAO,OAA6B;AAAA,IACxD,SAAS,WAAW,OAAO,OAA6B;AAAA,IACxD,aAAa,WAAW,OAAO,cAAc,CAAuB;AAAA,IACpE,aAAa,WAAW,OAAO,cAAc,CAAuB;AAAA,IACpE,mBAAmB,CAAC;AAAA,IACpB,WAAW,OAAO,YAAY;AAAA,IAC9B,WAAW,OAAO,YAAY;AAAA,IAC9B,0BAA0B,OAAO,6BAA6B;AAAA,IAC9D,eAAe,OAAO,iBAAiB;AAAA,IACvC,gBAAgB,OAAO,kBAAkB;AAAA,IACzC,sBAAsB,OAAO,yBAAyB;AAAA,IACtD,uBAAuB,OAAO,0BAA0B;AAAA,IACxD,WAAW,OAAO,aAAa;AAAA,IAC/B,iBAAiB,OAAO,mBAAmB;AAAA,IAC3C,aAAa,OAAO,cAAc;AAAA,IAClC,eAAe,OAAO,gBAAgB;AAAA,IACtC;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,WAAW,OAAO,YAAY;AAAA,IAC9B;AAAA,IACA,aAAa,OAAO,cAAc;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,OAAO,qBAAqB;AAAA,IAC/C,mBAAoB,OAAO,qBAAqB,KAA4B;AAAA,IAC5E,sBAAsB,OAAO,wBAAwB;AAAA,IACrD,WAAW;AAAA,IACX,oBAAoB,OAAO,sBAAsB;AAAA,IACjD,WAAW,OAAO,YAAY;AAAA,IAC9B,eAAe,OAAO,iBAAiB;AAAA,EACzC;AACF;AAMA,eAAe,UAAU,MAAgC;AACvD,MAAI,KAAK,OAAO;AACd,WAAO,UAAU;AAAA,EACnB;AACA,QAAM,WAAgB,cAAQ,KAAK,SAAU;AAC7C,MAAI,CAAI,eAAW,QAAQ,GAAG;AAC5B,YAAQ,MAAM,0BAA0B,QAAQ,EAAE;AAClD,YAAQ,KAAK,UAAU;AAAA,EACzB;AACA,SAAU,iBAAa,UAAU,MAAM;AACzC;AAEA,SAAS,cAAc,UAA0B;AAC/C,QAAM,WAAgB,cAAQ,QAAQ;AACtC,MAAI,CAAI,eAAW,QAAQ,GAAG;AAC5B,YAAQ,MAAM,0BAA0B,QAAQ,EAAE;AAClD,YAAQ,KAAK,UAAU;AAAA,EACzB;AACA,SAAU,iBAAa,UAAU,MAAM;AACzC;AAEA,SAAS,YAA6B;AACpC,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,UAAM,SAAmB,CAAC;AAC1B,YAAQ,MAAM,YAAY,MAAM;AAChC,YAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU,OAAO,KAAK,KAAe,CAAC;AAChE,YAAQ,MAAM,GAAG,OAAO,MAAMA,SAAQ,OAAO,KAAK,EAAE,CAAC,CAAC;AACtD,YAAQ,MAAM,GAAG,SAAS,MAAM;AAAA,EAClC,CAAC;AACH;AAMA,SAAS,UAAU,MAAuB;AACxC,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,MAAM,8BAAyB,GAAG,EAAE;AAC5C,YAAQ,KAAK,UAAU;AAAA,EACzB;AACF;AAEA,SAAS,aAAa,MAAmC;AACvD,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,yBAAyB,MAAe,MAG/C;AACA,MAAI,KAAK,cAAc,aAAa;AAClC,QAAI;AACF,qBAAe,IAAqB;AAAA,IACtC,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,MAAM;AAAA,EAAiC,GAAG,EAAE;AACpD,cAAQ,KAAK,yBAAyB;AAAA,IACxC;AAEA,WAAO,EAAE,KAAK,MAAuB,qBAAqB,EAAE;AAAA,EAC9D;AAEA,QAAM,MAAM;AACZ,MAAI,IAAI,kBAAkB,GAAG;AAC3B,YAAQ,MAAM,6BAA6B,IAAI,aAAa,iBAAiB;AAC7E,YAAQ,KAAK,sBAAsB;AAAA,EACrC;AAEA,QAAM,eAAe,eAAe,IAAI;AACxC,MAAI,CAAC,aAAa,OAAO;AACvB,YAAQ,MAAM,2BAA2B;AACzC,eAAW,OAAO,aAAa,QAAQ;AACrC,cAAQ,MAAM,KAAK,GAAG,EAAE;AAAA,IAC1B;AACA,YAAQ,KAAK,sBAAsB;AAAA,EACrC;AAEA,MAAI,MAAM;AACV,MAAI,sBAAsB;AAE1B,MAAI,KAAK,mBAAmB;AAC1B,UAAM,kBAAkB,GAAG;AAAA,EAC7B,OAAO;AACL,UAAM,SAAS,IAAI,UAAU;AAC7B,UAAM,YAAY,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,SAAS,IAAI,EAAE;AACjE,0BAAsB,SAAS;AAAA,EACjC;AAEA,QAAM,YAAY,gBAAgB,GAAG;AACrC,MAAI;AACF,mBAAe,SAAS;AAAA,EAC1B,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,MAAM;AAAA,EAAiC,GAAG,EAAE;AACpD,YAAQ,KAAK,yBAAyB;AAAA,EACxC;AAEA,SAAO,EAAE,KAAK,WAAW,oBAAoB;AAC/C;AAEA,SAAS,qBAAqB,MAAc,MAG1C;AACA,MAAI,KAAK,cAAc,UAAU;AAC/B,QAAI;AACF,aAAO,EAAE,KAAK,YAAY,IAAI,GAAG,qBAAqB,EAAE;AAAA,IAC1D,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,MAAM,wBAAwB,GAAG,EAAE;AAC3C,cAAQ,KAAK,sBAAsB;AAAA,IACrC;AAAA,EACF;AAEA,SAAO,yBAAyB,UAAU,IAAI,GAAG,IAAI;AACvD;AAEA,SAAS,eAAe,KAAoB,MAA8B;AACxE,QAAM,YAAY;AAAA,IAChB;AAAA,MACE,WAAW,IAAI;AAAA,MACf,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,aAAa,KAAK;AAAA,MAClB,aAAa,KAAK;AAAA,MAClB,eAAe,KAAK;AAAA,IACtB;AAAA,IACA,EAAE,QAAQ,QAAQ;AAAA,EACpB;AAEA,SAAO,EAAE,GAAG,KAAK,UAAU;AAC7B;AAEA,SAAS,wBACP,MACA,MAC2B;AAC3B,MAAI,KAAK,cAAc,UAAU;AAC/B,QAAI;AACF,aAAO,YAAY,IAAI;AAAA,IACzB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,OAAO,aAAa,IAAI;AAC9B,MAAI,SAAS,OAAW,QAAO;AAE/B,MAAI,KAAK,cAAc,aAAa;AAClC,QAAI;AACF,qBAAe,IAAqB;AACpC,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,MAAM;AACZ,MAAI,IAAI,kBAAkB,EAAG,QAAO;AAEpC,QAAM,eAAe,eAAe,IAAI;AACxC,MAAI,CAAC,aAAa,MAAO,QAAO;AAEhC,MAAI,MAAM;AACV,MAAI,KAAK,mBAAmB;AAC1B,UAAM,kBAAkB,GAAG;AAAA,EAC7B;AAEA,QAAM,YAAY,gBAAgB,GAAG;AACrC,MAAI;AACF,mBAAe,SAAS;AACxB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,uBAAuB,aAAqB,MAAyB;AAC5E,QAAM,cAAmB,cAAQ,KAAK,eAAoB,cAAQ,WAAW,CAAC;AAC9E,QAAM,kBAAuB,cAAQ,WAAW;AAEhD,MAAI,CAAI,eAAW,WAAW,GAAG;AAC/B,YAAQ,MAAM,wCAAwC,WAAW,EAAE;AACnE,YAAQ,KAAK,UAAU;AAAA,EACzB;AAEA,QAAM,UAAa,gBAAY,aAAa,EAAE,eAAe,KAAK,CAAC;AACnE,SAAO,QACJ,OAAO,CAAC,UAAU,MAAM,OAAO,CAAC,EAChC,IAAI,CAAC,UAAe,WAAK,aAAa,MAAM,IAAI,CAAC,EACjD,OAAO,CAAC,cAAmB,cAAQ,SAAS,MAAM,eAAe,EACjE;AAAA,IAAO,CAAC,cACP,KAAK,cAAc,WAAW,UAAU,SAAS,SAAS,IAAI,UAAU,SAAS,OAAO;AAAA,EAC1F;AACJ;AAEA,SAAS,oBACP,aACA,YACA,MACQ;AACR,QAAM,aAAa,uBAAuB,aAAa,IAAI;AAC3D,QAAM,aAA0D,CAAC;AAEjE,aAAW,aAAa,YAAY;AAClC,UAAM,MAAM,wBAA2B,iBAAa,WAAW,MAAM,GAAG,IAAI;AAC5E,QAAI,KAAK;AACP,iBAAW,KAAK,EAAE,MAAM,WAAW,IAAI,CAAC;AAAA,IAC1C;AAAA,EACF;AAEA,MAAI,WAAW,WAAW,GAAG;AAC3B,YAAQ;AAAA,MACN,gDAAqD,cAAQ,KAAK,eAAoB,cAAQ,WAAW,CAAC,CAAC;AAAA,IAC7G;AACA,YAAQ,KAAK,UAAU;AAAA,EACzB;AAEA,QAAM,SAAS,iBAAiB,YAAY,UAAU;AACtD,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,gDAAgD;AAC9D,YAAQ,KAAK,UAAU;AAAA,EACzB;AACA,SAAO,OAAO;AAChB;AAMA,eAAe,OAAO;AACpB,QAAM,OAAO,aAAa,QAAQ,IAAI;AACtC,QAAM,UAAU,KAAK,IAAI;AAEzB,MAAI,KAAK,eAAe,WAAW;AACjC,UAAM,cAAc,cAAc,KAAK,WAAY;AACnD,UAAM,UAAU,eAAe,qBAAqB,aAAa,IAAI,EAAE,KAAK,IAAI;AAChF,UAAM,eACJ,KAAK,iBAAiB,SAClB,oBAAoB,KAAK,aAAc,SAAS,IAAI,IACpD,KAAK;AACX,UAAM,eAAe,cAAc,YAAY;AAC/C,UAAM,WAAW,eAAe,qBAAqB,cAAc,IAAI,EAAE,KAAK,IAAI;AAElF,QAAI;AACF,YAAM,SAAS,MAAM,uBAAuB,UAAU,SAAS,cAAc,IAAI;AACjF,yBAAmB,QAAQ,MAAM,OAAO;AACxC,cAAQ,KAAK,YAAY;AAAA,IAC3B,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,MAAM,sBAAsB,GAAG,EAAE;AACzC,cAAQ,KAAK,eAAe;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,KAAK,eAAe,QAAQ;AAC9B,UAAMC,QAAO,MAAM,UAAU,IAAI;AACjC,UAAM,MAAM,eAAe,qBAAqBA,OAAM,IAAI,EAAE,KAAK,IAAI;AAGrE,UAAM,eAAgC,KAAK,cAAc,SAAS;AAClE,UAAM,SAAS,cAAc,EAAE,WAAW,IAAI,WAAW,QAAQ,aAAa,GAAG,CAAC,CAAC;AACnF,YAAQ,IAAI,MAAM;AAClB,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAGA,QAAM,OAAO,MAAM,UAAU,IAAI;AAGjC,MAAI,KAAK,cAAc,UAAU;AAC/B,QAAI,KAAK,eAAe,YAAY;AAElC,YAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AACpD,YAAM,YAAY,oBAAI,IAAI;AAAA,QACxB;AAAA,QAAQ;AAAA,QAAU;AAAA,QAAmB;AAAA,QACrC;AAAA,QAAkB;AAAA,QAAY;AAAA,QAC9B;AAAA,QAAmB;AAAA,QAAoB;AAAA,QACvC;AAAA,QAAmB;AAAA,MACrB,CAAC;AACD,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI;AACF,gBAAMC,OAAM,KAAK,MAAM,MAAM,CAAC,CAAC;AAC/B,gBAAM,OAAO,OAAO,KAAKA,IAAG;AAC5B,cAAI,KAAK,WAAW,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,CAAC,GAAG;AAChD,oBAAQ,MAAM,QAAQ,IAAI,CAAC,6BAA6B,KAAK,KAAK,IAAI,CAAC,GAAG;AAC1E,oBAAQ,KAAK,sBAAsB;AAAA,UACrC;AAAA,QACF,QAAQ;AACN,kBAAQ,MAAM,QAAQ,IAAI,CAAC,gBAAgB;AAC3C,kBAAQ,KAAK,sBAAsB;AAAA,QACrC;AAAA,MACF;AACA,cAAQ,IAAI,iBAAiB,MAAM,MAAM,cAAc;AACvD,cAAQ,KAAK,YAAY;AAAA,IAC3B;AAGA,QAAI;AACJ,QAAI;AACF,YAAM,YAAY,IAAI;AAAA,IACxB,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,MAAM,wBAAwB,GAAG,EAAE;AAC3C,cAAQ,KAAK,sBAAsB;AAAA,IACrC;AAGA,QAAI,KAAK,eAAe;AACtB,YAAM,UAAe,cAAQ,KAAK,aAAa;AAC/C,MAAG,cAAe,cAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,MAAG,kBAAc,SAAS,KAAK,UAAU,KAAK,MAAM,CAAC,GAAG,MAAM;AAAA,IAChE;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,gBAAgB,KAAK,IAAI;AAC9C,YAAM,sBAAsB,KAAK,IAAI;AACrC,yBAAmB,KAAK,IAAI;AAC5B,kBAAY,QAAQ,MAAM,OAAO;AACjC,cAAQ,KAAK,YAAY;AAAA,IAC3B,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,MAAM,sBAAsB,GAAG,EAAE;AACzC,cAAQ,KAAK,eAAe;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,OAAO,UAAU,IAAI;AAE3B,MAAI,KAAK,eAAe,YAAY;AAElC,QAAI,KAAK,cAAc,aAAa;AAClC,UAAI;AACF,uBAAe,IAAqB;AACpC,gBAAQ,IAAI,gCAAgC;AAC5C,gBAAQ,KAAK,YAAY;AAAA,MAC3B,SAAS,KAAK;AACZ,cAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,gBAAQ,MAAM,GAAG;AACjB,gBAAQ,KAAK,yBAAyB;AAAA,MACxC;AAAA,IACF;AAGA,UAAMA,OAAM;AACZ,QAAIA,KAAI,kBAAkB,GAAG;AAC3B,cAAQ;AAAA,QACN,6BAA6BA,KAAI,aAAa;AAAA,MAChD;AACA,cAAQ,KAAK,sBAAsB;AAAA,IACrC;AAEA,UAAM,SAAS,eAAe,IAAI;AAClC,QAAI,CAAC,OAAO,OAAO;AACjB,cAAQ,MAAM,2BAA2B;AACzC,iBAAW,OAAO,OAAO,QAAQ;AAC/B,gBAAQ,MAAM,KAAK,GAAG,EAAE;AAAA,MAC1B;AACA,cAAQ,KAAK,sBAAsB;AAAA,IACrC;AAEA,YAAQ,IAAI,iCAAiC;AAC7C,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAIA,MAAI,KAAK,cAAc,aAAa;AAElC,QAAI;AACF,qBAAe,IAAqB;AAAA,IACtC,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,MAAM;AAAA,EAAiC,GAAG,EAAE;AACpD,cAAQ,KAAK,yBAAyB;AAAA,IACxC;AAEA,UAAM,MAAM;AAGZ,QAAI,KAAK,eAAe;AACtB,YAAM,UAAe,cAAQ,KAAK,aAAa;AAC/C,MAAG,cAAe,cAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,MAAG,kBAAc,SAAS,KAAK,UAAU,KAAK,MAAM,CAAC,GAAG,MAAM;AAAA,IAChE;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,gBAAgB,KAAK,IAAI;AAC9C,YAAM,sBAAsB,KAAK,IAAI;AACrC,yBAAmB,KAAK,IAAI;AAC5B,kBAAY,QAAQ,MAAM,OAAO;AACjC,cAAQ,KAAK,YAAY;AAAA,IAC3B,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,MAAM,sBAAsB,GAAG,EAAE;AACzC,cAAQ,KAAK,eAAe;AAAA,IAC9B;AAAA,EACF;AAIA,QAAM,MAAM;AACZ,MAAI,IAAI,kBAAkB,GAAG;AAC3B,YAAQ;AAAA,MACN,6BAA6B,IAAI,aAAa;AAAA,IAChD;AACA,YAAQ,KAAK,sBAAsB;AAAA,EACrC;AAGA,QAAM,eAAe,eAAe,IAAI;AACxC,MAAI,CAAC,aAAa,OAAO;AACvB,YAAQ,MAAM,2BAA2B;AACzC,eAAW,OAAO,aAAa,QAAQ;AACrC,cAAQ,MAAM,KAAK,GAAG,EAAE;AAAA,IAC1B;AACA,YAAQ,KAAK,sBAAsB;AAAA,EACrC;AAGA,MAAI,MAAM;AACV,MAAI,sBAAsB;AAE1B,MAAI,KAAK,mBAAmB;AAC1B,UAAM,kBAAkB,GAAG;AAAA,EAC7B,OAAO;AAEL,UAAM,SAAS,IAAI,UAAU;AAC7B,UAAM,YAAY,IAAI,UAAU;AAAA,MAC9B,CAAC,OAAO,GAAG,SAAS;AAAA,IACtB,EAAE;AACF,0BAAsB,SAAS;AAC/B,QAAI,sBAAsB,GAAG;AAC3B,cAAQ;AAAA,QACN,WAAW,mBAAmB;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,gBAAgB,GAAG;AAGrC,MAAI;AACF,mBAAe,SAAS;AAAA,EAC1B,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,MAAM;AAAA,EAAiC,GAAG,EAAE;AACpD,YAAQ,KAAK,yBAAyB;AAAA,EACxC;AAGA,MAAI,KAAK,eAAe;AACtB,UAAM,UAAe,cAAQ,KAAK,aAAa;AAC/C,IAAG,cAAe,cAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,IAAG,kBAAc,SAAS,KAAK,UAAU,WAAW,MAAM,CAAC,GAAG,MAAM;AAAA,EACtE;AAGA,MAAI;AACF,UAAM,SAAS,MAAM,gBAAgB,WAAW,MAAM,mBAAmB;AACzE,UAAM,sBAAsB,WAAW,IAAI;AAC3C,uBAAmB,WAAW,IAAI;AAClC,gBAAY,QAAQ,MAAM,SAAS,mBAAmB;AACtD,YAAQ,KAAK,YAAY;AAAA,EAC3B,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,MAAM,sBAAsB,GAAG,EAAE;AACzC,YAAQ,KAAK,eAAe;AAAA,EAC9B;AACF;AAMA,eAAe,sBAAsB,KAAoB,MAA8B;AAErF,QAAM,WAA4C,KAAK,YAAY,IAAI,CAAC,QAAQ;AAC9E,UAAM,OAAsC,EAAE,IAAI;AAClD,QAAI,OAAO,KAAK,KAAK,cAAc,EAAE,SAAS,GAAG;AAC/C,WAAK,UAAU,EAAE,GAAG,KAAK,eAAe;AAAA,IAC1C;AACA,QAAI,KAAK,kBAAkB,QAAQ;AACjC,WAAK,SAAS,KAAK;AAAA,IACrB;AACA,QAAI,KAAK,mBAAmB;AAC1B,YAAM,SAA4B;AAAA,QAChC,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,MACf;AACA,UAAI,KAAK,sBAAsB;AAC7B,eAAO,mBAAmB;AAAA,MAC5B;AACA,WAAK,SAAS;AAAA,IAChB;AACA,WAAO;AAAA,EACT,CAAC;AAED,QAAM;AAAA,IACJ;AAAA,MACE;AAAA,MACA,cAAc;AAAA,QACZ,iBAAiB,KAAK;AAAA,QACtB,iBAAiB,KAAK;AAAA,QACtB,WAAW,KAAK;AAAA,QAChB,WAAW,KAAK;AAAA,QAChB,gBAAgB,KAAK;AAAA,QACrB,UAAU,SAAS,SAAS,IAAI,WAAW;AAAA,MAC7C;AAAA,IACF;AAAA,IACA;AAAA,MACE,OAAO,WAAW;AAAA,MAClB,QAAQ;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,mBAAmB,KAAoB,MAAqB;AACnE,MAAI,CAAC,KAAK,YAAa;AAEvB,QAAM,cAAmB,cAAQ,KAAK,WAAW;AAGjD,QAAM,QAAQ;AAAA,IACZ,EAAE,UAAU,YAAY;AAAA,IACxB;AAAA,MACE,UAAU,CAAC,MAAc;AACvB,YAAI;AACF,iBAAU,iBAAa,GAAG,MAAM;AAAA,QAClC,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AAGA,QAAM,UAAU,cAAc;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,SAAS,KAAK;AAAA,EAChB,CAAC;AAGD,QAAM,MAAW,cAAQ,WAAW;AACpC,EAAG,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACrC;AAAA,IACE,EAAE,UAAU,aAAa,OAAO,QAAQ;AAAA,IACxC,EAAE,WAAW,CAAC,GAAW,YAAuB,kBAAc,GAAG,SAAS,MAAM,EAAE;AAAA,EACpF;AAGA,MAAI,eAAe;AACnB,aAAW,UAAU,OAAO,KAAK,QAAQ,KAAK,GAAG;AAC/C,UAAM,UAAU,QAAQ,MAAM,MAAM;AACpC,QAAI,QAAQ,QAAQ,UAAU,GAAG;AAC/B;AAAA,IACF;AAAA,EACF;AACA,MAAI,eAAe,GAAG;AACpB,YAAQ,MAAM,oBAAoB,WAAW,KAAK,OAAO,KAAK,QAAQ,KAAK,EAAE,MAAM,iBAAiB;AAAA,EACtG;AACF;AAyBA,eAAe,gBACb,KACA,MACA,uBAAuB,GACH;AACpB,QAAM,YAAY,IAAI,gBAAgB;AAAA,IACpC,SAAS,KAAK;AAAA,IACd,SAAS,KAAK;AAAA,IACd,aAAa,KAAK;AAAA,IAClB,aAAa,KAAK;AAAA,IAClB,SAAS,KAAK;AAAA,IACd,WAAW,KAAK;AAAA,IAChB,YAAY,KAAK;AAAA,IACjB,qBAAqB,KAAK;AAAA,IAC1B,eAAe,KAAK;AAAA,IACpB,MAAM;AAAA,MACJ,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,oBAAoB,CAAC,KAAK;AAAA,MAC1B,gBAAgB,CAAC,KAAK;AAAA,MACtB,iBAAiB,CAAC,KAAK;AAAA,MACvB,kBAAkB,KAAK;AAAA,MACvB,mBAAmB,KAAK;AAAA,MACxB,YAAY,CAAC,KAAK;AAAA,MAClB,oBAAoB,KAAK;AAAA,IAC3B;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,oBAAoB,KAAK;AAAA,EAC3B,CAAC;AAED,QAAM,YAAY,MAAM,UAAU,SAAS,GAAG;AAG9C,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,UAAU,OAAO,GAAG;AACtC,UAAM,KAAK,GAAG,KAAK;AAAA,EACrB;AAGA,QAAM,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,EAAE;AAC9D,aAAW,MAAM,IAAI,WAAW;AAC9B,UAAM,SAAS,GAAG;AAClB,QAAI,UAAU,QAAQ;AACpB,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,OAAO;AACzB;AAEA,eAAe,uBACb,UACA,SACA,cACA,MAC2B;AAC3B,QAAM,4BAA4B,KAAK,QAAQ;AAAA,IAC7C,CAAC,WAAW,WAAW,UAAU,WAAW;AAAA,EAC9C;AACA,MAAI,0BAA0B,SAAS,GAAG;AACxC,UAAM,IAAI;AAAA,MACR,qEAAqE,0BAA0B,KAAK,IAAI,CAAC;AAAA,IAC3G;AAAA,EACF;AACA,QAAM,iBAAiB,KAAK;AAE5B,QAAM,SAAS,MAAM,sBAAsB;AAAA,IACzC;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,WAAW,KAAK;AAAA,IAChB,YAAY,KAAK;AAAA,IACjB,OAAO,KAAK;AAAA,EACd,CAAC;AAED,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd;AAAA,IACA,SAAS,OAAO,KAAK;AAAA,IACrB,WAAW,KAAK,aAAa,KAAK,gBAAgB,uBAAuB,OAAO,IAAI,IAAI;AAAA,EAC1F;AACF;AAEA,SAAS,YACP,QACA,MACA,SACA,sBAAsB,GACtB;AACA,QAAM,aAAa,KAAK,IAAI,IAAI;AAEhC,MAAI,KAAK,aAAa;AACpB,UAAM,UAAmC;AAAA,MACvC,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf;AAAA,IACF;AACA,QAAI,sBAAsB,GAAG;AAC3B,cAAQ,sBAAsB;AAAA,IAChC;AACA,YAAQ,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,EAC9C,OAAO;AACL,eAAW,KAAK,OAAO,OAAO;AAC5B,cAAQ,IAAI,CAAC;AAAA,IACf;AAAA,EACF;AACF;AAEA,SAAS,mBACP,QACA,MACA,SACA;AACA,QAAM,aAAa,KAAK,IAAI,IAAI;AAEhC,MAAI,OAAO,aAAa,KAAK,eAAe;AAC1C,UAAM,aAAkB,cAAQ,KAAK,aAAa;AAClD,IAAG,cAAe,cAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,IAAG,kBAAc,YAAY,OAAO,WAAW,MAAM;AAAA,EACvD;AAEA,MAAI,KAAK,aAAa;AACpB,YAAQ;AAAA,MACN,KAAK;AAAA,QACH;AAAA,UACE,OAAO,OAAO;AAAA,UACd,cAAc,OAAO;AAAA,UACrB,MAAM,OAAO;AAAA,UACb,WAAW,OAAO;AAAA,UAClB;AAAA,QACF;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAEA,aAAW,KAAK,OAAO,OAAO;AAC5B,YAAQ,IAAI,CAAC;AAAA,EACf;AACA,UAAQ,IAAI,aAAa,OAAO,YAAY,EAAE;AAC9C,MAAI,OAAO,aAAa,KAAK,WAAW;AACtC,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,OAAO,SAAS;AAAA,EAC9B;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,UAAU;AACzB,CAAC;","names":["fs","path","path","path","groupBy","main","escapeHtml","escapeHtml","groupBy","groupBy","groupBy","groupBy","basename","createHash","path","resolve","flags","escapeHtml","renderScenario","formatSteps","formatDocs","formatStep","formatDocEntry","fs","path","fs","path","fs","path","extractFeatureName","formatDuration","truncate","formatDuration","toCIInfo","fs","path","resolve","text","obj"]}