executable-stories-formatters 0.7.7 → 0.7.8
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 +111 -21
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +31 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.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/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/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/adapters/jest.ts","../src/converters/adapters/vitest.ts","../src/converters/adapters/playwright.ts","../src/types/story.ts","../src/converters/acl/validate.ts","../src/converters/ndjson-parser.ts","../src/utils/git-info.ts","../src/utils/duration.ts","../src/utils/metadata.ts","../src/utils/ci-detect.ts","../src/utils/otel-detect.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/history/flakiness.ts","../src/history/performance.ts","../src/history/stability.ts","../src/history/metrics.ts","../src/list-scenarios.ts"],"sourcesContent":["/**\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 * 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 * 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\">☰</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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\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\">▼</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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\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}`);\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 { 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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\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 \" \";\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 \" \";\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>→</span><span>${escapeHtml(after.status)}</span></div>`\n : before\n ? `<div class=\"status-pair\"><span>${escapeHtml(before.status)}</span><span>→</span><span>removed</span></div>`\n : `<div class=\"status-pair\"><span>new</span><span>→</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(\", \")) || \" \"}</dd>\n <dt>Suite</dt>\n <dd>${escapeHtml(before.titlePath.join(\" > \")) || \" \"}</dd>\n <dt>Error</dt>\n <dd>${escapeHtml(before.errorMessage ?? \"\") || \" \"}</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(\", \")) || \" \"}</dd>` : \"\"}\n </dl>\n </section>\n <section>\n <h4>Current</h4>\n <dl>\n <dt>Tags</dt>\n <dd>${escapeHtml(after.tags.join(\", \")) || \" \"}</dd>\n <dt>Suite</dt>\n <dd>${escapeHtml(after.titlePath.join(\" > \")) || \" \"}</dd>\n <dt>Error</dt>\n <dd>${escapeHtml(after.errorMessage ?? \"\") || \" \"}</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(\", \")) || \" \"}</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:  or \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:  or \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 * Jest Adapter - Layer 1.\n *\n * Transforms Jest test results and story reports into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"../../types/raw\";\n\n/** Jest test result shape (subset of what Jest provides) */\nexport interface JestTestResult {\n fullName: string;\n status: \"passed\" | \"failed\" | \"pending\" | \"todo\";\n duration?: number;\n failureMessages?: string[];\n}\n\n/** Jest file result shape */\nexport interface JestFileResult {\n testFilePath: string;\n testResults: JestTestResult[];\n}\n\n/** Jest aggregated result shape */\nexport interface JestAggregatedResult {\n testResults: JestFileResult[];\n startTime?: number;\n}\n\n/** Story file report written by story.init() */\nexport interface StoryFileReport {\n testFilePath: string;\n scenarios: StoryMeta[];\n}\n\n/** Options for Jest adapter */\nexport interface JestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n}\n\n/**\n * Map Jest status to RawStatus.\n */\nfunction mapJestStatus(status: JestTestResult[\"status\"]): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"pending\";\n case \"todo\":\n return \"todo\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Jest results and story reports to RawRun.\n *\n * @param jestResults - Jest aggregated results\n * @param storyReports - Story reports from story.init()\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptJestRun(\n jestResults: JestAggregatedResult,\n storyReports: StoryFileReport[],\n options: JestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n\n // Build map of Jest results by file for lookup\n const fileResultsMap = new Map<string, JestFileResult>();\n for (const fileResult of jestResults.testResults) {\n fileResultsMap.set(fileResult.testFilePath, fileResult);\n }\n\n // Process each story report\n for (const report of storyReports) {\n const fileResult = fileResultsMap.get(report.testFilePath);\n\n for (const meta of report.scenarios) {\n if (!meta?.scenario) continue;\n\n // Find matching Jest test result\n const matchingTest = findMatchingJestTest(fileResult, meta);\n\n testCases.push({\n externalId: matchingTest?.fullName,\n title: meta.scenario,\n titlePath: meta.suitePath\n ? [...meta.suitePath, meta.scenario]\n : [meta.scenario],\n story: meta,\n sourceFile: report.testFilePath,\n sourceLine: undefined, // Jest doesn't provide line numbers\n status: matchingTest ? mapJestStatus(matchingTest.status) : \"unknown\",\n durationMs: matchingTest?.duration,\n error: matchingTest?.failureMessages?.length\n ? { message: matchingTest.failureMessages.join(\"\\n\") }\n : undefined,\n stepEvents: undefined, // Jest doesn't provide step-level results\n attachments: undefined, // Jest doesn't capture attachments\n meta: { jestStatus: matchingTest?.status },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: jestResults.startTime,\n finishedAtMs: Date.now(),\n projectRoot: options.projectRoot ?? process.cwd(),\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Find matching Jest test result for a story.\n *\n * Matches by constructing fullName from suitePath and scenario.\n */\nfunction findMatchingJestTest(\n fileResult: JestFileResult | undefined,\n meta: StoryMeta\n): JestTestResult | undefined {\n if (!fileResult) return undefined;\n\n // Construct expected fullName\n const expectedFullName = meta.suitePath\n ? [...meta.suitePath, meta.scenario].join(\" > \")\n : meta.scenario;\n\n return fileResult.testResults.find((test) => test.fullName === expectedFullName);\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Vitest Adapter - Layer 1.\n *\n * Transforms Vitest test results into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"../../types/raw\";\n\n/** Vitest test state */\nexport type VitestState = \"passed\" | \"failed\" | \"skipped\" | \"pending\";\n\n/** Vitest serialized error */\nexport interface VitestSerializedError {\n message?: string;\n stack?: string;\n diff?: string;\n}\n\n/** Vitest test result shape */\nexport interface VitestTestResult {\n state?: VitestState;\n duration?: number;\n errors?: VitestSerializedError[];\n}\n\n/** Vitest test case shape (minimal) */\nexport interface VitestTestCase {\n name: string;\n meta: () => Record<string, unknown>;\n result: () => VitestTestResult | undefined;\n}\n\n/** Vitest test module shape (minimal) */\nexport interface VitestTestModule {\n moduleId?: string;\n relativeModuleId?: string;\n children?: {\n allTests: () => Iterable<VitestTestCase>;\n };\n}\n\n/** Options for Vitest adapter */\nexport interface VitestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n}\n\n/**\n * Map Vitest state to RawStatus.\n */\nfunction mapVitestStatus(state?: VitestState): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"pending\":\n return \"pending\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Vitest test modules to RawRun.\n *\n * @param testModules - Vitest test modules\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptVitestRun(\n testModules: ReadonlyArray<VitestTestModule>,\n options: VitestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const mod of testModules) {\n const collection = mod.children;\n if (!collection) continue;\n\n // Get module path\n const moduleId = mod.moduleId ?? mod.relativeModuleId ?? \"\";\n const sourcePath = moduleId.startsWith(\"/\")\n ? moduleId\n : `${projectRoot}/${moduleId}`;\n\n for (const test of collection.allTests()) {\n const meta = test.meta();\n const story = meta?.[\"story\"] as StoryMeta | undefined;\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n const result = test.result();\n const state = result?.state;\n const errors = state === \"failed\" && result ? result.errors : undefined;\n\n testCases.push({\n externalId: test.name,\n title: story.scenario,\n titlePath: story.suitePath\n ? [...story.suitePath, story.scenario]\n : [story.scenario],\n story,\n sourceFile: sourcePath,\n sourceLine: undefined, // Vitest doesn't provide line numbers easily\n status: mapVitestStatus(state),\n durationMs: result?.duration,\n error: errors?.length\n ? {\n message: formatVitestError(errors[0]),\n stack: errors[0].stack,\n }\n : undefined,\n stepEvents: undefined, // Vitest doesn't provide step-level results\n attachments: undefined, // Vitest doesn't capture attachments\n meta: { vitestState: state },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Format Vitest error message.\n */\nfunction formatVitestError(error: VitestSerializedError): string {\n const parts: string[] = [];\n\n if (error.message) {\n parts.push(error.message);\n }\n\n if (error.diff) {\n parts.push(\"\", error.diff);\n }\n\n return parts.join(\"\\n\") || \"Unknown error\";\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Playwright Adapter - Layer 1.\n *\n * Transforms Playwright test results into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus, RawAttachment } from \"../../types/raw\";\n\n/** Playwright test status */\nexport type PlaywrightStatus =\n | \"passed\"\n | \"failed\"\n | \"skipped\"\n | \"timedOut\"\n | \"interrupted\";\n\n/** Playwright test error */\nexport interface PlaywrightError {\n message?: string;\n stack?: string;\n}\n\n/** Playwright attachment */\nexport interface PlaywrightAttachment {\n name: string;\n contentType: string;\n path?: string;\n body?: Buffer;\n}\n\n/** Playwright test result */\nexport interface PlaywrightTestResult {\n status: PlaywrightStatus;\n duration: number;\n errors: PlaywrightError[];\n attachments: PlaywrightAttachment[];\n retry: number;\n}\n\n/** Playwright test case annotation */\nexport interface PlaywrightAnnotation {\n type: string;\n description?: string;\n}\n\n/** Playwright test location */\nexport interface PlaywrightLocation {\n file: string;\n line: number;\n column: number;\n}\n\n/** Playwright test case shape (minimal) */\nexport interface PlaywrightTestCase {\n title: string;\n titlePath: () => string[];\n annotations: PlaywrightAnnotation[];\n location: PlaywrightLocation;\n retries: number;\n}\n\n/** Options for Playwright adapter */\nexport interface PlaywrightAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n /** Playwright project name */\n projectName?: string;\n}\n\n/**\n * Map Playwright status to RawStatus.\n */\nfunction mapPlaywrightStatus(status: PlaywrightStatus): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"timedOut\":\n return \"timeout\";\n case \"interrupted\":\n return \"interrupted\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Playwright test results to RawRun.\n *\n * @param testResults - Array of [testCase, result] tuples\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptPlaywrightRun(\n testResults: Array<[PlaywrightTestCase, PlaywrightTestResult]>,\n options: PlaywrightAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const [test, result] of testResults) {\n // Find story-meta annotation\n const storyAnnotation = test.annotations.find((a) => a.type === \"story-meta\");\n if (!storyAnnotation?.description) continue;\n\n let story: StoryMeta;\n try {\n story = JSON.parse(storyAnnotation.description);\n } catch {\n continue; // Skip if annotation is not valid JSON\n }\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n // Convert attachments\n const attachments = convertAttachments(result.attachments);\n\n // Get error info\n const error = result.errors?.length\n ? {\n message: result.errors[0].message,\n stack: result.errors[0].stack,\n }\n : undefined;\n\n testCases.push({\n externalId: test.titlePath().join(\" > \"),\n title: story.scenario,\n titlePath: test.titlePath(),\n story,\n sourceFile: test.location.file,\n sourceLine: test.location.line,\n status: mapPlaywrightStatus(result.status),\n durationMs: result.duration,\n error,\n stepEvents: undefined, // Playwright could provide step info, but we don't capture it yet\n attachments,\n meta: {\n playwrightStatus: result.status,\n column: test.location.column,\n },\n retry: result.retry,\n retries: test.retries,\n projectName: options.projectName,\n });\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Convert Playwright attachments to raw attachments.\n */\nfunction convertAttachments(\n attachments: PlaywrightAttachment[]\n): RawAttachment[] {\n return attachments.map((att) => ({\n name: att.name,\n mediaType: att.contentType,\n path: att.path,\n body: att.body ? att.body.toString(\"base64\") : undefined,\n encoding: att.body ? \"BASE64\" as const : undefined,\n byteLength: att.body?.length,\n }));\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Story types — the shared vocabulary for all framework adapters.\n *\n * These types were previously in executable-stories-core.\n * They now live in formatters so every adapter can import them\n * from the same place that defines RawRun (the output contract).\n */\n\nimport type { OtelSpan } from \"./otel\";\n\n// ============================================================================\n// Step Keywords\n// ============================================================================\n\n/** BDD step keywords for scenario documentation */\nexport type StepKeyword = \"Given\" | \"When\" | \"Then\" | \"And\" | \"But\";\n\n/** Step execution mode for docs rendering */\nexport type StepMode = \"normal\" | \"skip\" | \"only\" | \"todo\" | \"fails\" | \"concurrent\";\n\n// ============================================================================\n// Doc Entry Types\n// ============================================================================\n\n/** Phase tracks when the doc entry was added */\nexport type DocPhase = \"static\" | \"runtime\";\n\n/** Union type for all documentation entry kinds */\nexport type DocEntry =\n | { kind: \"note\"; text: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"tag\"; names: string[]; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"kv\"; label: string; value: unknown; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"code\"; label: string; content: string; lang?: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"table\"; label: string; columns: string[]; rows: string[][]; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"link\"; label: string; url: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"section\"; title: string; markdown: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"mermaid\"; code: string; title?: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"screenshot\"; path: string; alt?: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"custom\"; type: string; data: unknown; phase: DocPhase; children?: DocEntry[] };\n\n// ============================================================================\n// Story Step\n// ============================================================================\n\n/**\n * A single step in a scenario with its documentation entries.\n */\nexport interface StoryStep {\n /** Stable internal ID (auto-generated at creation, e.g., \"step-0\") */\n id?: string;\n /** The BDD keyword (Given, When, Then, And, But) */\n keyword: StepKeyword;\n /** The step description text */\n text: string;\n /** Step execution mode for docs rendering */\n mode?: StepMode;\n /** Rich documentation entries attached to this step */\n docs?: DocEntry[];\n /** Opt-in step duration in milliseconds */\n durationMs?: number;\n /** Whether this step wrapped a function body (step.fn / step.step) vs a text marker */\n wrapped?: boolean;\n}\n\n// ============================================================================\n// Ticket Reference\n// ============================================================================\n\n/** A ticket reference with an optional direct URL */\nexport interface NormalizedTicket {\n /** Ticket identifier (e.g., \"JIRA-123\", \"PAY-1042\") */\n id: string;\n /** Direct URL to the ticket (overrides ticketUrlTemplate) */\n url?: string;\n}\n\n// ============================================================================\n// Story Metadata\n// ============================================================================\n\n/**\n * Metadata for a complete scenario, attached to test metadata.\n * Used by reporters to generate documentation.\n */\nexport interface StoryMeta {\n /** The scenario title (from test name) */\n scenario: string;\n /** All steps in this scenario */\n steps: StoryStep[];\n /** Tags for filtering and categorization */\n tags?: string[];\n /** Ticket/issue references (normalized to array) */\n tickets?: NormalizedTicket[];\n /** User-defined metadata */\n meta?: Record<string, unknown>;\n /** Parent describe/suite names for hierarchical grouping */\n suitePath?: string[];\n /** Story-level docs (before any steps) */\n docs?: DocEntry[];\n /** Order in which story.init() was called (for source ordering) */\n sourceOrder?: number;\n /** OTel spans from autotel for trace waterfall rendering */\n otelSpans?: OtelSpan[];\n}\n\n/** Key used to store StoryMeta in test metadata */\nexport const STORY_META_KEY = \"story\";\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 * 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 * Git information utilities.\n *\n * Read git SHA and other info from the local repository.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/**\n * Read the current git SHA.\n *\n * Checks environment variables first (for CI), then reads from .git directory.\n *\n * @param cwd - Working directory to start search from\n * @returns Git SHA or undefined if not in a git repo\n */\nexport function readGitSha(cwd: string = process.cwd()): string | undefined {\n // Check CI environment variables first\n const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;\n if (envSha) return envSha;\n\n // Find .git directory\n const gitDir = findGitDir(cwd);\n if (!gitDir) return undefined;\n\n try {\n const headPath = path.join(gitDir, \"HEAD\");\n const head = fs.readFileSync(headPath, \"utf8\").trim();\n\n // If HEAD is a direct SHA (detached HEAD state)\n if (!head.startsWith(\"ref:\")) {\n return head;\n }\n\n // HEAD points to a ref, resolve it\n const refPath = head.replace(\"ref:\", \"\").trim();\n const refFile = path.join(gitDir, refPath);\n\n if (fs.existsSync(refFile)) {\n return fs.readFileSync(refFile, \"utf8\").trim();\n }\n\n // Check packed-refs\n const packedRefs = path.join(gitDir, \"packed-refs\");\n if (fs.existsSync(packedRefs)) {\n const content = fs.readFileSync(packedRefs, \"utf8\");\n for (const line of content.split(\"\\n\")) {\n if (!line || line.startsWith(\"#\") || line.startsWith(\"^\")) continue;\n const [sha, ref] = line.split(\" \");\n if (ref === refPath) return sha;\n }\n }\n\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Find the .git directory starting from a given directory.\n *\n * @param start - Directory to start search from\n * @returns Path to .git directory or undefined if not found\n */\nexport function findGitDir(start: string): string | undefined {\n let current = start;\n while (true) {\n const candidate = path.join(current, \".git\");\n if (fs.existsSync(candidate)) {\n // Handle git worktrees where .git is a file\n const stat = fs.statSync(candidate);\n if (stat.isFile()) {\n const content = fs.readFileSync(candidate, \"utf8\").trim();\n const match = content.match(/^gitdir: (.+)$/);\n if (match) {\n return path.resolve(current, match[1]);\n }\n }\n return candidate;\n }\n const parent = path.dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n}\n\n/**\n * Read the current branch name.\n *\n * @param cwd - Working directory\n * @returns Branch name or undefined\n */\nexport function readBranchName(cwd: string = process.cwd()): string | undefined {\n // Check CI environment variables first\n const envBranch = process.env.GITHUB_REF_NAME || process.env.CI_COMMIT_REF_NAME;\n if (envBranch) return envBranch;\n\n const gitDir = findGitDir(cwd);\n if (!gitDir) return undefined;\n\n try {\n const headPath = path.join(gitDir, \"HEAD\");\n const head = fs.readFileSync(headPath, \"utf8\").trim();\n\n if (head.startsWith(\"ref:\")) {\n const refPath = head.replace(\"ref:\", \"\").trim();\n // Extract branch name from refs/heads/branch-name\n const match = refPath.match(/^refs\\/heads\\/(.+)$/);\n return match ? match[1] : refPath;\n }\n\n // Detached HEAD\n return undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Duration formatting utilities.\n */\n\n/**\n * Format milliseconds as human-readable duration.\n *\n * @param ms - Duration in milliseconds\n * @returns Formatted string (e.g., \"123 ms\", \"1.5 s\", \"2m 30s\")\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`;\n }\n\n if (ms < 60000) {\n return `${(ms / 1000).toFixed(2)} s`;\n }\n\n const minutes = Math.floor(ms / 60000);\n const seconds = Math.floor((ms % 60000) / 1000);\n\n if (minutes < 60) {\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n return `${hours}h ${remainingMinutes}m`;\n}\n\n/**\n * Convert milliseconds to nanoseconds.\n *\n * Used for Cucumber JSON format which expects nanoseconds.\n *\n * @param ms - Duration in milliseconds\n * @returns Duration in nanoseconds\n */\nexport function msToNanoseconds(ms: number): number {\n return Math.round(ms * 1_000_000);\n}\n\n/**\n * Convert nanoseconds to milliseconds.\n *\n * @param ns - Duration in nanoseconds\n * @returns Duration in milliseconds\n */\nexport function nanosecondsToMs(ns: number): number {\n return ns / 1_000_000;\n}\n","/**\n * Metadata utilities for reading package information.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/** Cache for package versions by root directory */\nconst versionCache = new Map<string, string | undefined>();\n\n/**\n * Read the package version from the closest package.json.\n *\n * Results are cached by root directory.\n *\n * @param root - Directory to start searching from\n * @returns Package version string or undefined if not found\n */\nexport function readPackageVersion(root: string): string | undefined {\n if (versionCache.has(root)) {\n return versionCache.get(root);\n }\n\n const version = findPackageVersion(root);\n versionCache.set(root, version);\n return version;\n}\n\n/**\n * Find package.json version by walking up the directory tree.\n */\nfunction findPackageVersion(startDir: string): string | undefined {\n let current = path.resolve(startDir);\n\n while (true) {\n const pkgPath = path.join(current, \"package.json\");\n try {\n if (fs.existsSync(pkgPath)) {\n const raw = fs.readFileSync(pkgPath, \"utf8\");\n const parsed = JSON.parse(raw) as { version?: string };\n return parsed.version;\n }\n } catch {\n // Continue searching\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n // Reached root\n return undefined;\n }\n current = parent;\n }\n}\n\n/**\n * Clear the package version cache.\n * Useful for testing.\n */\nexport function clearVersionCache(): void {\n versionCache.clear();\n}\n","/**\n * CI environment auto-detection utility.\n *\n * Detects known CI providers from environment variables and populates\n * branch, commit SHA, PR number, build number, and URL metadata.\n *\n * Precedence (checked in order, first match wins):\n * 1. TF_BUILD=True -> azure\n * 2. BUILDKITE=true -> buildkite\n * 3. GITHUB_ACTIONS=true -> github\n * 4. GITLAB_CI=true -> gitlab\n * 5. CIRCLECI=true -> circleci\n * 6. JENKINS_URL defined -> jenkins\n * 7. TRAVIS=true -> travis\n * 8. CI=true -> unknown (generic fallback; note: fires in dev shells too)\n * 9. Nothing -> undefined (not in CI)\n */\n\nimport type { RawCIInfo } from \"../types/raw\";\n\n/**\n * Detect CI environment from process.env.\n * Returns undefined when not running in CI.\n */\nexport function detectCI(\n env: Record<string, string | undefined> = process.env,\n): RawCIInfo | undefined {\n // 1. Azure DevOps (TF_BUILD is \"True\" with capital T)\n if (env.TF_BUILD === \"True\") {\n const branch = env.BUILD_SOURCEBRANCH?.replace(/^refs\\/heads\\//, \"\");\n\n // URL requires all three components\n const serverUri = env.SYSTEM_TEAMFOUNDATIONSERVERURI;\n const teamProject = env.SYSTEM_TEAMPROJECT;\n const buildId = env.BUILD_BUILDID;\n const url =\n serverUri && teamProject && buildId\n ? `${serverUri}${teamProject}/_build/results?buildId=${buildId}`\n : undefined;\n\n return {\n name: \"azure\",\n provider: \"azure\",\n buildNumber: buildId,\n url,\n branch,\n commitSha: env.BUILD_SOURCEVERSION,\n prNumber: env.SYSTEM_PULLREQUEST_PULLREQUESTID,\n };\n }\n\n // 2. Buildkite\n if (env.BUILDKITE === \"true\") {\n const prRaw = env.BUILDKITE_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"buildkite\",\n provider: \"buildkite\",\n buildNumber: env.BUILDKITE_BUILD_NUMBER,\n url: env.BUILDKITE_BUILD_URL,\n branch: env.BUILDKITE_BRANCH,\n commitSha: env.BUILDKITE_COMMIT,\n prNumber,\n };\n }\n\n // 3. GitHub Actions\n if (env.GITHUB_ACTIONS === \"true\") {\n const url =\n env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : undefined;\n\n // GITHUB_HEAD_REF is set on pull_request events; GITHUB_REF_NAME is always set\n const branch = env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME;\n\n // Parse PR number from GITHUB_REF: refs/pull/<n>/merge or refs/pull/<n>/head\n const prMatch = env.GITHUB_REF?.match(/^refs\\/pull\\/(\\d+)\\/(merge|head)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"github\",\n provider: \"github\",\n buildNumber: env.GITHUB_RUN_NUMBER,\n url,\n branch,\n commitSha: env.GITHUB_SHA,\n prNumber,\n };\n }\n\n // 4. GitLab CI\n if (env.GITLAB_CI === \"true\") {\n return {\n name: \"gitlab\",\n provider: \"gitlab\",\n buildNumber: env.CI_PIPELINE_IID,\n url: env.CI_PIPELINE_URL,\n branch: env.CI_COMMIT_REF_NAME,\n commitSha: env.CI_COMMIT_SHA,\n prNumber: env.CI_MERGE_REQUEST_IID,\n };\n }\n\n // 5. CircleCI\n if (env.CIRCLECI === \"true\") {\n // Parse PR number from trailing digits of CIRCLE_PULL_REQUEST URL\n // e.g. https://github.com/org/repo/pull/42 -> \"42\"\n const prUrl = env.CIRCLE_PULL_REQUEST;\n const prMatch = prUrl?.match(/\\/(\\d+)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"circleci\",\n provider: \"circleci\",\n buildNumber: env.CIRCLE_BUILD_NUM,\n url: env.CIRCLE_BUILD_URL,\n branch: env.CIRCLE_BRANCH,\n commitSha: env.CIRCLE_SHA1,\n prNumber,\n };\n }\n\n // 6. Jenkins (detected by JENKINS_URL being defined, any value)\n if (env.JENKINS_URL !== undefined) {\n return {\n name: \"jenkins\",\n provider: \"jenkins\",\n buildNumber: env.BUILD_NUMBER,\n url: env.BUILD_URL,\n branch: env.GIT_BRANCH,\n commitSha: env.GIT_COMMIT,\n };\n }\n\n // 7. Travis CI\n if (env.TRAVIS === \"true\") {\n const prRaw = env.TRAVIS_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"travis\",\n provider: \"travis\",\n buildNumber: env.TRAVIS_BUILD_NUMBER,\n url: env.TRAVIS_BUILD_WEB_URL,\n branch: env.TRAVIS_BRANCH,\n commitSha: env.TRAVIS_COMMIT,\n prNumber,\n };\n }\n\n // 8. Generic CI fallback (note: CI=true fires in many dev shells too)\n if (env.CI === \"true\") {\n return {\n name: \"ci\",\n provider: \"unknown\",\n };\n }\n\n // 9. Not in CI\n return undefined;\n}\n","/**\n * OTel trace context detection.\n *\n * Detects an active OpenTelemetry span via `@opentelemetry/api` (optional peer).\n * Uses `createRequire` so the import is lazy and never breaks if OTel isn't installed.\n */\n\nimport { createRequire } from \"node:module\";\n\nexport interface OtelTraceContext {\n traceId: string;\n spanId: string;\n}\n\n/**\n * Build a require function that works in both ESM and CJS bundles.\n * In ESM, import.meta.url is defined. In CJS (e.g. tsup output), it is\n * shimmed to undefined — fall back to __filename which CJS always has.\n */\nfunction getRequire(): NodeRequire {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const url = import.meta.url\n ?? (typeof __filename !== \"undefined\" ? `file://${__filename}` : undefined);\n if (!url) throw new Error(\"Cannot determine module URL\");\n return createRequire(url);\n}\n\n/**\n * Try to read trace context from the currently-active OTel span.\n * Returns `undefined` when `@opentelemetry/api` is not installed or no span is active.\n */\nexport function tryGetActiveOtelContext(): OtelTraceContext | undefined {\n try {\n const api = getRequire()(\"@opentelemetry/api\");\n const span = api.trace?.getActiveSpan?.();\n if (!span) return undefined;\n const ctx = span.spanContext?.();\n if (!ctx?.traceId || ctx.traceId === \"00000000000000000000000000000000\")\n return undefined;\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Replace `{traceId}` in a URL template.\n * Returns `undefined` when no template is provided.\n */\nexport function resolveTraceUrl(\n template: string | undefined,\n traceId: string,\n): string | undefined {\n if (!template) return undefined;\n return template.replace(/\\{traceId\\}/g, traceId);\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 * Flakiness calculation based on status transitions.\n */\n\nimport type { HistoryEntry, FlakinessLevel } from \"./types\";\nimport { MIN_FLAKINESS_SAMPLES } from \"./sample-policy\";\n\nexport interface FlakinessResult {\n flakinessLevel: FlakinessLevel;\n flakinessScore: number;\n failureRate: number;\n longestPassStreak: number;\n longestFailStreak: number;\n}\n\nexport function calculateFlakiness(args: {\n entries: HistoryEntry[];\n}): FlakinessResult {\n const { entries } = args;\n\n // Only count pass/fail entries (exclude skipped/pending)\n const countable = entries.filter(\n (e) => e.status === \"passed\" || e.status === \"failed\",\n );\n\n if (countable.length < MIN_FLAKINESS_SAMPLES) {\n return {\n flakinessLevel: \"stable\",\n flakinessScore: 0,\n failureRate: 0,\n longestPassStreak: countable.length,\n longestFailStreak: 0,\n };\n }\n\n // Count transitions (consecutive entries with different status)\n let transitions = 0;\n for (let i = 1; i < countable.length; i++) {\n if (countable[i].status !== countable[i - 1].status) {\n transitions++;\n }\n }\n\n const transitionScore = transitions / (countable.length - 1);\n const failures = countable.filter((e) => e.status === \"failed\").length;\n const failureRate = failures / countable.length;\n\n // Calculate streaks\n let longestPassStreak = 0;\n let longestFailStreak = 0;\n let currentPassStreak = 0;\n let currentFailStreak = 0;\n\n for (const e of countable) {\n if (e.status === \"passed\") {\n currentPassStreak++;\n currentFailStreak = 0;\n if (currentPassStreak > longestPassStreak) {\n longestPassStreak = currentPassStreak;\n }\n } else {\n currentFailStreak++;\n currentPassStreak = 0;\n if (currentFailStreak > longestFailStreak) {\n longestFailStreak = currentFailStreak;\n }\n }\n }\n\n // Classification\n let flakinessLevel: FlakinessLevel;\n if (\n transitionScore > 0.5 ||\n (transitionScore > 0.3 && failureRate > 0.2)\n ) {\n flakinessLevel = \"flaky\";\n } else if (transitionScore > 0.2 || failureRate > 0.3) {\n flakinessLevel = \"unstable\";\n } else {\n flakinessLevel = \"stable\";\n }\n\n return {\n flakinessLevel,\n flakinessScore: transitionScore,\n failureRate,\n longestPassStreak,\n longestFailStreak,\n };\n}\n","/**\n * Performance trend detection via half-split comparison.\n */\n\nimport type { HistoryEntry, PerformanceTrend } from \"./types\";\nimport { MIN_PERF_SAMPLES } from \"./sample-policy\";\n\nexport interface PerformanceResult {\n trend: PerformanceTrend;\n avgDurationMs: number;\n}\n\nexport function detectPerformanceTrend(args: {\n entries: HistoryEntry[];\n}): PerformanceResult {\n const { entries } = args;\n\n // Filter out entries with no meaningful duration data\n const countable = entries.filter(\n (e) => e.status !== \"skipped\" && e.status !== \"pending\",\n );\n\n if (countable.length === 0) {\n return { trend: \"stable\", avgDurationMs: 0 };\n }\n\n const avgAll =\n countable.reduce((sum, e) => sum + e.durationMs, 0) / countable.length;\n\n if (countable.length < MIN_PERF_SAMPLES) {\n return { trend: \"stable\", avgDurationMs: avgAll };\n }\n\n // Split into earlier half and recent half\n const mid = Math.floor(countable.length / 2);\n const earlier = countable.slice(0, mid);\n const recent = countable.slice(mid);\n\n const earlierAvg =\n earlier.reduce((sum, e) => sum + e.durationMs, 0) / earlier.length;\n const recentAvg =\n recent.reduce((sum, e) => sum + e.durationMs, 0) / recent.length;\n\n let trend: PerformanceTrend;\n if (earlierAvg === 0) {\n trend = \"stable\";\n } else {\n const change = (recentAvg - earlierAvg) / earlierAvg;\n if (change > 0.1) {\n trend = \"regressing\";\n } else if (change < -0.1) {\n trend = \"improving\";\n } else {\n trend = \"stable\";\n }\n }\n\n return { trend, avgDurationMs: avgAll };\n}\n","/**\n * Stability grade calculation (composite score).\n */\n\nimport type { StabilityGrade } from \"./types\";\n\nexport interface CalculateStabilityArgs {\n passRate: number;\n flakinessScore: number;\n longestPassStreak: number;\n sampleSize: number;\n}\n\nexport function calculateStability(args: CalculateStabilityArgs): StabilityGrade {\n const { passRate, flakinessScore, longestPassStreak, sampleSize } = args;\n\n const inverseFlakiness = 1 - flakinessScore;\n const streakNorm = longestPassStreak / Math.min(sampleSize, 10);\n\n const score = passRate * 0.6 + inverseFlakiness * 0.3 + streakNorm * 0.1;\n\n if (score >= 0.95) return \"A\";\n if (score >= 0.85) return \"B\";\n if (score >= 0.70) return \"C\";\n if (score >= 0.50) return \"D\";\n return \"F\";\n}\n","/**\n * Composite test metrics from history entries.\n */\n\nimport type { HistoryEntry, TestMetrics } from \"./types\";\nimport { calculateFlakiness } from \"./flakiness\";\nimport { detectPerformanceTrend } from \"./performance\";\nimport { calculateStability } from \"./stability\";\n\nexport function computeTestMetrics(args: {\n testId: string;\n entries: HistoryEntry[];\n}): TestMetrics {\n const { testId, entries } = args;\n\n const flakiness = calculateFlakiness({ entries });\n const perf = detectPerformanceTrend({ entries });\n\n // Calculate pass rate (excluding skipped/pending)\n const countable = entries.filter(\n (e) => e.status === \"passed\" || e.status === \"failed\",\n );\n const passRate =\n countable.length > 0\n ? countable.filter((e) => e.status === \"passed\").length / countable.length\n : 1;\n\n const stabilityGrade = calculateStability({\n passRate,\n flakinessScore: flakiness.flakinessScore,\n longestPassStreak: flakiness.longestPassStreak,\n sampleSize: entries.length,\n });\n\n // Calculate consecutive failures from the tail end\n let consecutiveFailures = 0;\n for (let i = entries.length - 1; i >= 0; i--) {\n if (entries[i].status === \"failed\") {\n consecutiveFailures++;\n } else {\n break;\n }\n }\n\n return {\n testId,\n flakinessLevel: flakiness.flakinessLevel,\n flakinessScore: flakiness.flakinessScore,\n failureRate: flakiness.failureRate,\n stabilityGrade,\n performanceTrend: perf.trend,\n avgDurationMs: perf.avgDurationMs,\n passRate,\n longestPassStreak: flakiness.longestPassStreak,\n consecutiveFailures,\n sampleSize: entries.length,\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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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;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,QAAQ,MAAM,QAAQ;AAC3B;AAEA,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4uDP;;;AC/uDA,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;AAEA,SAAS,mBAAmB,MAAqB,MAA6B;AAC5E,QAAM,EAAE,IAAI,IAAI;AAEhB,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,WAAW,QAAQ,IAAI,KAAK,MAAO,SAAS,QAAS,GAAG,IAAI;AAElE,QAAM,UAAU,CAAC,GAAG,IAAI,IAAI,IAAI,UAAU,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,KAAK;AAC1E,QAAM,SAASA,SAAQ,IAAI,WAAW,CAAC,OAAO,GAAG,UAAU;AAG3D,QAAM,YAAsB,CAAC;AAC7B,MAAI,eAAe;AACnB,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,UAAU,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AACjE,UAAM,UAAU,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ,EAAE;AACjE,UAAM,cACJ,UAAU,IAAI,UAAU,YAAY,UAAU,SAAS,YAAY;AAErE,cAAU;AAAA,MACR,kEAAkE,YAAY,YAAY,IAAI;AAAA,gDACpD,WAAW;AAAA,qCACtB,WAAW;AAAA,sCACV,UAAU,MAAM;AAAA;AAAA,IAElD;AACA;AAAA,EACF;AAGA,QAAM,WAAW,QACd;AAAA,IACC,CAAC,QACC,6BAA6B,GAAG;AAAA,EACpC,EACC,KAAK,cAAc;AAGtB,QAAM,YAAY,IAAI,cAClB,IAAI,KAAK,IAAI,WAAW,EAAE,eAAe,IACzC;AACJ,QAAM,cACJ,IAAI,cAAc,QAAQ,IAAI,aAAa,KAAM,QAAQ,CAAC,IAAI;AAGhE,QAAM,YAAsB,CAAC;AAG7B,YAAU;AAAA,IACR,KAAK;AAAA,MACH,EAAE,MAAM,SAAS,gBAAgB,MAAM;AAAA,MACvC,KAAK;AAAA,IACP;AAAA,EACF;AAGA,QAAM,cAAc,IAAI,UAAU,OAAO,CAAC,OAAO,GAAG,WAAW,QAAQ;AACvE,MAAI,YAAY,SAAS,GAAG;AAC1B,cAAU;AAAA,MACR,KAAK,qBAAqB,EAAE,YAAY,GAAG,KAAK,kBAAkB;AAAA,IACpE;AAAA,EACF;AAGA,aAAW,CAAC,MAAM,SAAS,KAAK,QAAQ;AACtC,cAAU;AAAA,MACR,KAAK;AAAA,QACH,EAAE,MAAM,WAAW,YAAY,KAAK,WAAW;AAAA,QAC/C,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,kCAKgB,SAAS;AAAA,sCACL,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAMZ,MAAM;AAAA;AAAA;AAAA;AAAA,qCAIN,MAAM;AAAA;AAAA;AAAA;AAAA,qCAIN,OAAO;AAAA;AAAA;AAAA;AAAA,qCAIP,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQrC,UAAU,KAAK,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA,IAK9B,QAAQ,SAAS,IACb;AAAA;AAAA;AAAA,QAGA,QAAQ;AAAA;AAAA,YAGR,EACN;AAAA;AAGA,QAAM,OAAO;AAAA;AAAA,IAEX,UAAU,KAAK,MAAM,CAAC;AAAA;AAGxB,SAAO,iCAAiC,OAAO,GAAG,IAAI;AACxD;AAEA,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;AAGO,SAAS,qBAA+B;AAC7C,SAAO,CAAC,GAAG,eAAe,KAAK,CAAC;AAClC;AAGO,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;;;AC3CO,IAAM,mBAAmB;AAGzB,IAAM,qBAAqB;AAG3B,IAAM,wBAAwB;AAG9B,SAAS,qBACd,SACA,KACS;AACT,SAAO,QAAQ,UAAU;AAC3B;;;ACmBA,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;;;AC7EA,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;;;AC3JA,SAAS,cAAc,QAA6C;AAClE,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,aACd,aACA,cACA,UAA8B,CAAC,GACvB;AACR,QAAM,YAA2B,CAAC;AAGlC,QAAM,iBAAiB,oBAAI,IAA4B;AACvD,aAAW,cAAc,YAAY,aAAa;AAChD,mBAAe,IAAI,WAAW,cAAc,UAAU;AAAA,EACxD;AAGA,aAAW,UAAU,cAAc;AACjC,UAAM,aAAa,eAAe,IAAI,OAAO,YAAY;AAEzD,eAAW,QAAQ,OAAO,WAAW;AACnC,UAAI,CAAC,MAAM,SAAU;AAGrB,YAAM,eAAe,qBAAqB,YAAY,IAAI;AAE1D,gBAAU,KAAK;AAAA,QACb,YAAY,cAAc;AAAA,QAC1B,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK,YACZ,CAAC,GAAG,KAAK,WAAW,KAAK,QAAQ,IACjC,CAAC,KAAK,QAAQ;AAAA,QAClB,OAAO;AAAA,QACP,YAAY,OAAO;AAAA,QACnB,YAAY;AAAA;AAAA,QACZ,QAAQ,eAAe,cAAc,aAAa,MAAM,IAAI;AAAA,QAC5D,YAAY,cAAc;AAAA,QAC1B,OAAO,cAAc,iBAAiB,SAClC,EAAE,SAAS,aAAa,gBAAgB,KAAK,IAAI,EAAE,IACnD;AAAA,QACJ,YAAY;AAAA;AAAA,QACZ,aAAa;AAAA;AAAA,QACb,MAAM,EAAE,YAAY,cAAc,OAAO;AAAA,QACzC,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,YAAY;AAAA,IACzB,cAAc,KAAK,IAAI;AAAA,IACvB,aAAa,QAAQ,eAAe,QAAQ,IAAI;AAAA,IAChD,gBAAgB,QAAQ;AAAA,IACxB,QAAQ,QAAQ;AAAA,IAChB,IAAI,SAAS;AAAA,EACf;AACF;AAOA,SAAS,qBACP,YACA,MAC4B;AAC5B,MAAI,CAAC,WAAY,QAAO;AAGxB,QAAM,mBAAmB,KAAK,YAC1B,CAAC,GAAG,KAAK,WAAW,KAAK,QAAQ,EAAE,KAAK,KAAK,IAC7C,KAAK;AAET,SAAO,WAAW,YAAY,KAAK,CAAC,SAAS,KAAK,aAAa,gBAAgB;AACjF;AAKA,SAAS,WAAW;AAClB,MAAI,QAAQ,IAAI,mBAAmB,QAAQ;AACzC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,oBAC9C,GAAG,QAAQ,IAAI,iBAAiB,IAAI,QAAQ,IAAI,iBAAiB,iBAAiB,QAAQ,IAAI,aAAa,KAC3G;AAAA,MACJ,aAAa,QAAQ,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,QAAQ,IAAI,aAAa;AAC3B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,QAAQ,IAAI;AAAA,MACjB,aAAa,QAAQ,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,QAAQ,IAAI,IAAI;AAClB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK;AAAA,MACL,aAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO;AACT;;;AC1HA,SAAS,gBAAgB,OAAgC;AACvD,UAAQ,OAAO;AAAA,IACb,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;AASO,SAAS,eACd,aACA,UAAgC,CAAC,GACzB;AACR,QAAM,YAA2B,CAAC;AAClC,QAAM,cAAc,QAAQ,eAAe,QAAQ,IAAI;AAEvD,aAAW,OAAO,aAAa;AAC7B,UAAM,aAAa,IAAI;AACvB,QAAI,CAAC,WAAY;AAGjB,UAAM,WAAW,IAAI,YAAY,IAAI,oBAAoB;AACzD,UAAM,aAAa,SAAS,WAAW,GAAG,IACtC,WACA,GAAG,WAAW,IAAI,QAAQ;AAE9B,eAAW,QAAQ,WAAW,SAAS,GAAG;AACxC,YAAM,OAAO,KAAK,KAAK;AACvB,YAAM,QAAQ,OAAO,OAAO;AAE5B,UAAI,CAAC,OAAO,YAAY,CAAC,MAAM,QAAQ,MAAM,KAAK,EAAG;AAErD,YAAM,SAAS,KAAK,OAAO;AAC3B,YAAM,QAAQ,QAAQ;AACtB,YAAM,SAAS,UAAU,YAAY,SAAS,OAAO,SAAS;AAE9D,gBAAU,KAAK;AAAA,QACb,YAAY,KAAK;AAAA,QACjB,OAAO,MAAM;AAAA,QACb,WAAW,MAAM,YACb,CAAC,GAAG,MAAM,WAAW,MAAM,QAAQ,IACnC,CAAC,MAAM,QAAQ;AAAA,QACnB;AAAA,QACA,YAAY;AAAA,QACZ,YAAY;AAAA;AAAA,QACZ,QAAQ,gBAAgB,KAAK;AAAA,QAC7B,YAAY,QAAQ;AAAA,QACpB,OAAO,QAAQ,SACX;AAAA,UACE,SAAS,kBAAkB,OAAO,CAAC,CAAC;AAAA,UACpC,OAAO,OAAO,CAAC,EAAE;AAAA,QACnB,IACA;AAAA,QACJ,YAAY;AAAA;AAAA,QACZ,aAAa;AAAA;AAAA,QACb,MAAM,EAAE,aAAa,MAAM;AAAA,QAC3B,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,cAAc,KAAK,IAAI;AAAA,IACvB;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB,QAAQ,QAAQ;AAAA,IAChB,IAAIC,UAAS;AAAA,EACf;AACF;AAKA,SAAS,kBAAkB,OAAsC;AAC/D,QAAM,QAAkB,CAAC;AAEzB,MAAI,MAAM,SAAS;AACjB,UAAM,KAAK,MAAM,OAAO;AAAA,EAC1B;AAEA,MAAI,MAAM,MAAM;AACd,UAAM,KAAK,IAAI,MAAM,IAAI;AAAA,EAC3B;AAEA,SAAO,MAAM,KAAK,IAAI,KAAK;AAC7B;AAKA,SAASA,YAAW;AAClB,MAAI,QAAQ,IAAI,mBAAmB,QAAQ;AACzC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,oBAC9C,GAAG,QAAQ,IAAI,iBAAiB,IAAI,QAAQ,IAAI,iBAAiB,iBAAiB,QAAQ,IAAI,aAAa,KAC3G;AAAA,MACJ,aAAa,QAAQ,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,QAAQ,IAAI,aAAa;AAC3B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,QAAQ,IAAI;AAAA,MACjB,aAAa,QAAQ,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,QAAQ,IAAI,IAAI;AAClB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK;AAAA,MACL,aAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO;AACT;;;ACjHA,SAAS,oBAAoB,QAAqC;AAChE,UAAQ,QAAQ;AAAA,IACd,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;AASO,SAAS,mBACd,aACA,UAAoC,CAAC,GAC7B;AACR,QAAM,YAA2B,CAAC;AAClC,QAAM,cAAc,QAAQ,eAAe,QAAQ,IAAI;AAEvD,aAAW,CAAC,MAAM,MAAM,KAAK,aAAa;AAExC,UAAM,kBAAkB,KAAK,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY;AAC5E,QAAI,CAAC,iBAAiB,YAAa;AAEnC,QAAI;AACJ,QAAI;AACF,cAAQ,KAAK,MAAM,gBAAgB,WAAW;AAAA,IAChD,QAAQ;AACN;AAAA,IACF;AAEA,QAAI,CAAC,OAAO,YAAY,CAAC,MAAM,QAAQ,MAAM,KAAK,EAAG;AAGrD,UAAM,cAAc,mBAAmB,OAAO,WAAW;AAGzD,UAAM,QAAQ,OAAO,QAAQ,SACzB;AAAA,MACE,SAAS,OAAO,OAAO,CAAC,EAAE;AAAA,MAC1B,OAAO,OAAO,OAAO,CAAC,EAAE;AAAA,IAC1B,IACA;AAEJ,cAAU,KAAK;AAAA,MACb,YAAY,KAAK,UAAU,EAAE,KAAK,KAAK;AAAA,MACvC,OAAO,MAAM;AAAA,MACb,WAAW,KAAK,UAAU;AAAA,MAC1B;AAAA,MACA,YAAY,KAAK,SAAS;AAAA,MAC1B,YAAY,KAAK,SAAS;AAAA,MAC1B,QAAQ,oBAAoB,OAAO,MAAM;AAAA,MACzC,YAAY,OAAO;AAAA,MACnB;AAAA,MACA,YAAY;AAAA;AAAA,MACZ;AAAA,MACA,MAAM;AAAA,QACJ,kBAAkB,OAAO;AAAA,QACzB,QAAQ,KAAK,SAAS;AAAA,MACxB;AAAA,MACA,OAAO,OAAO;AAAA,MACd,SAAS,KAAK;AAAA,MACd,aAAa,QAAQ;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,cAAc,KAAK,IAAI;AAAA,IACvB;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB,QAAQ,QAAQ;AAAA,IAChB,IAAIC,UAAS;AAAA,EACf;AACF;AAKA,SAAS,mBACP,aACiB;AACjB,SAAO,YAAY,IAAI,CAAC,SAAS;AAAA,IAC/B,MAAM,IAAI;AAAA,IACV,WAAW,IAAI;AAAA,IACf,MAAM,IAAI;AAAA,IACV,MAAM,IAAI,OAAO,IAAI,KAAK,SAAS,QAAQ,IAAI;AAAA,IAC/C,UAAU,IAAI,OAAO,WAAoB;AAAA,IACzC,YAAY,IAAI,MAAM;AAAA,EACxB,EAAE;AACJ;AAKA,SAASA,YAAW;AAClB,MAAI,QAAQ,IAAI,mBAAmB,QAAQ;AACzC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,oBAC9C,GAAG,QAAQ,IAAI,iBAAiB,IAAI,QAAQ,IAAI,iBAAiB,iBAAiB,QAAQ,IAAI,aAAa,KAC3G;AAAA,MACJ,aAAa,QAAQ,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,QAAQ,IAAI,aAAa;AAC3B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK,QAAQ,IAAI;AAAA,MACjB,aAAa,QAAQ,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,QAAQ,IAAI,IAAI;AAClB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK;AAAA,MACL,aAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO;AACT;;;AC7GO,IAAM,iBAAiB;;;AC7EvB,SAAS,qBAAqB,KAAsC;AACzE,QAAM,SAAmB,CAAC;AAG1B,MAAI,CAAC,IAAI,OAAO;AACd,WAAO,KAAK,mBAAmB;AAAA,EACjC;AAEA,MAAI,CAAC,IAAI,aAAa;AACpB,WAAO,KAAK,yBAAyB;AAAA,EACvC;AAEA,MAAI,OAAO,IAAI,gBAAgB,YAAY,IAAI,cAAc,GAAG;AAC9D,WAAO,KAAK,wBAAwB,IAAI,WAAW,EAAE;AAAA,EACvD;AAEA,MAAI,OAAO,IAAI,iBAAiB,YAAY,IAAI,eAAe,GAAG;AAChE,WAAO,KAAK,yBAAyB,IAAI,YAAY,EAAE;AAAA,EACzD;AAEA,MAAI,IAAI,eAAe,IAAI,aAAa;AACtC,WAAO,KAAK,iBAAiB,IAAI,YAAY,oBAAoB,IAAI,WAAW,GAAG;AAAA,EACrF;AAEA,MAAI,OAAO,IAAI,eAAe,YAAY,IAAI,aAAa,GAAG;AAC5D,WAAO,KAAK,uBAAuB,IAAI,UAAU,EAAE;AAAA,EACrD;AAEA,MAAI,CAAC,MAAM,QAAQ,IAAI,SAAS,GAAG;AACjC,WAAO,KAAK,2BAA2B;AAAA,EACzC,OAAO;AAEL,aAAS,IAAI,GAAG,IAAI,IAAI,UAAU,QAAQ,KAAK;AAC7C,YAAM,WAAW,iBAAiB,IAAI,UAAU,CAAC,GAAG,CAAC;AACrD,aAAO,KAAK,GAAG,QAAQ;AAAA,IACzB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB;AAAA,EACF;AACF;AAKA,SAAS,iBAAiB,IAAoB,OAAyB;AACrE,QAAM,SAAmB,CAAC;AAC1B,QAAM,SAAS,YAAY,KAAK;AAGhC,MAAI,CAAC,GAAG,IAAI;AACV,WAAO,KAAK,GAAG,MAAM,cAAc;AAAA,EACrC;AAEA,MAAI,CAAC,GAAG,OAAO;AACb,WAAO,KAAK,GAAG,MAAM,iBAAiB;AACtC,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,GAAG,YAAY;AAClB,WAAO,KAAK,GAAG,MAAM,sBAAsB;AAAA,EAC7C;AAEA,MAAI,OAAO,GAAG,eAAe,YAAY,GAAG,aAAa,GAAG;AAC1D,WAAO,KAAK,GAAG,MAAM,wBAAwB,GAAG,UAAU,EAAE;AAAA,EAC9D;AAGA,QAAM,gBAAgB,CAAC,UAAU,UAAU,WAAW,SAAS;AAC/D,MAAI,CAAC,cAAc,SAAS,GAAG,MAAM,GAAG;AACtC,WAAO,KAAK,GAAG,MAAM,qBAAqB,GAAG,MAAM,GAAG;AAAA,EACxD;AAGA,MAAI,OAAO,GAAG,eAAe,YAAY,GAAG,aAAa,GAAG;AAC1D,WAAO,KAAK,GAAG,MAAM,wBAAwB,GAAG,UAAU,EAAE;AAAA,EAC9D;AAGA,MAAI,OAAO,GAAG,UAAU,YAAY,GAAG,QAAQ,GAAG;AAChD,WAAO,KAAK,GAAG,MAAM,mBAAmB,GAAG,KAAK,EAAE;AAAA,EACpD;AAEA,MAAI,OAAO,GAAG,YAAY,YAAY,GAAG,UAAU,GAAG;AACpD,WAAO,KAAK,GAAG,MAAM,qBAAqB,GAAG,OAAO,EAAE;AAAA,EACxD;AAGA,MAAI,CAAC,MAAM,QAAQ,GAAG,WAAW,GAAG;AAClC,WAAO,KAAK,GAAG,MAAM,+BAA+B;AAAA,EACtD;AAEA,MAAI,CAAC,MAAM,QAAQ,GAAG,SAAS,GAAG;AAChC,WAAO,KAAK,GAAG,MAAM,6BAA6B;AAAA,EACpD;AAEA,MAAI,CAAC,MAAM,QAAQ,GAAG,IAAI,GAAG;AAC3B,WAAO,KAAK,GAAG,MAAM,wBAAwB;AAAA,EAC/C;AAGA,MAAI,CAAC,MAAM,QAAQ,GAAG,WAAW,GAAG;AAClC,WAAO,KAAK,GAAG,MAAM,+BAA+B;AAAA,EACtD,OAAO;AACL,UAAM,aAAa,GAAG,MAAM,OAAO,UAAU;AAG7C,QAAI,GAAG,YAAY,WAAW,YAAY;AACxC,aAAO;AAAA,QACL,GAAG,MAAM,yBAAyB,GAAG,YAAY,MAAM,6BAA6B,UAAU;AAAA,MAChG;AAAA,IACF;AAGA,UAAM,cAAc,oBAAI,IAAY;AACpC,eAAW,MAAM,GAAG,aAAa;AAE/B,UAAI,OAAO,GAAG,UAAU,YAAY,GAAG,QAAQ,KAAK,GAAG,SAAS,YAAY;AAC1E,eAAO,KAAK,GAAG,MAAM,8BAA8B,GAAG,KAAK,EAAE;AAAA,MAC/D;AAGA,UAAI,YAAY,IAAI,GAAG,KAAK,GAAG;AAC7B,eAAO,KAAK,GAAG,MAAM,gCAAgC,GAAG,KAAK,EAAE;AAAA,MACjE;AACA,kBAAY,IAAI,GAAG,KAAK;AAGxB,UAAI,CAAC,cAAc,SAAS,GAAG,MAAM,GAAG;AACtC,eAAO,KAAK,GAAG,MAAM,gCAAgC,GAAG,MAAM,cAAc,GAAG,KAAK,EAAE;AAAA,MACxF;AAGA,UAAI,OAAO,GAAG,eAAe,YAAY,GAAG,aAAa,GAAG;AAC1D,eAAO,KAAK,GAAG,MAAM,mCAAmC,GAAG,UAAU,aAAa,GAAG,KAAK,EAAE;AAAA,MAC9F;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAUO,SAAS,eAAe,KAA0B;AACvD,QAAM,SAAS,qBAAqB,GAAG;AACvC,MAAI,CAAC,OAAO,OAAO;AACjB,UAAM,IAAI,MAAM;AAAA,EAA2B,OAAO,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,EACvE;AACF;;;ACzGO,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;;;AC5iBA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAUf,SAAS,WAAW,MAAc,QAAQ,IAAI,GAAuB;AAE1E,QAAM,SAAS,QAAQ,IAAI,cAAc,QAAQ,IAAI,cAAc,QAAQ,IAAI;AAC/E,MAAI,OAAQ,QAAO;AAGnB,QAAM,SAAS,WAAW,GAAG;AAC7B,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI;AACF,UAAM,WAAgB,WAAK,QAAQ,MAAM;AACzC,UAAM,OAAU,iBAAa,UAAU,MAAM,EAAE,KAAK;AAGpD,QAAI,CAAC,KAAK,WAAW,MAAM,GAAG;AAC5B,aAAO;AAAA,IACT;AAGA,UAAM,UAAU,KAAK,QAAQ,QAAQ,EAAE,EAAE,KAAK;AAC9C,UAAM,UAAe,WAAK,QAAQ,OAAO;AAEzC,QAAO,eAAW,OAAO,GAAG;AAC1B,aAAU,iBAAa,SAAS,MAAM,EAAE,KAAK;AAAA,IAC/C;AAGA,UAAM,aAAkB,WAAK,QAAQ,aAAa;AAClD,QAAO,eAAW,UAAU,GAAG;AAC7B,YAAM,UAAa,iBAAa,YAAY,MAAM;AAClD,iBAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,YAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG,EAAG;AAC3D,cAAM,CAAC,KAAK,GAAG,IAAI,KAAK,MAAM,GAAG;AACjC,YAAI,QAAQ,QAAS,QAAO;AAAA,MAC9B;AAAA,IACF;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,WAAW,OAAmC;AAC5D,MAAI,UAAU;AACd,SAAO,MAAM;AACX,UAAM,YAAiB,WAAK,SAAS,MAAM;AAC3C,QAAO,eAAW,SAAS,GAAG;AAE5B,YAAM,OAAU,aAAS,SAAS;AAClC,UAAI,KAAK,OAAO,GAAG;AACjB,cAAM,UAAa,iBAAa,WAAW,MAAM,EAAE,KAAK;AACxD,cAAM,QAAQ,QAAQ,MAAM,gBAAgB;AAC5C,YAAI,OAAO;AACT,iBAAY,cAAQ,SAAS,MAAM,CAAC,CAAC;AAAA,QACvC;AAAA,MACF;AACA,aAAO;AAAA,IACT;AACA,UAAM,SAAc,cAAQ,OAAO;AACnC,QAAI,WAAW,QAAS,QAAO;AAC/B,cAAU;AAAA,EACZ;AACF;AAQO,SAAS,eAAe,MAAc,QAAQ,IAAI,GAAuB;AAE9E,QAAM,YAAY,QAAQ,IAAI,mBAAmB,QAAQ,IAAI;AAC7D,MAAI,UAAW,QAAO;AAEtB,QAAM,SAAS,WAAW,GAAG;AAC7B,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI;AACF,UAAM,WAAgB,WAAK,QAAQ,MAAM;AACzC,UAAM,OAAU,iBAAa,UAAU,MAAM,EAAE,KAAK;AAEpD,QAAI,KAAK,WAAW,MAAM,GAAG;AAC3B,YAAM,UAAU,KAAK,QAAQ,QAAQ,EAAE,EAAE,KAAK;AAE9C,YAAM,QAAQ,QAAQ,MAAM,qBAAqB;AACjD,aAAO,QAAQ,MAAM,CAAC,IAAI;AAAA,IAC5B;AAGA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC5GO,SAASC,gBAAe,IAAoB;AACjD,MAAI,KAAK,KAAM;AACb,WAAO,GAAG,KAAK,MAAM,EAAE,CAAC;AAAA,EAC1B;AAEA,MAAI,KAAK,KAAO;AACd,WAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AAAA,EAClC;AAEA,QAAM,UAAU,KAAK,MAAM,KAAK,GAAK;AACrC,QAAM,UAAU,KAAK,MAAO,KAAK,MAAS,GAAI;AAE9C,MAAI,UAAU,IAAI;AAChB,WAAO,UAAU,IAAI,GAAG,OAAO,KAAK,OAAO,MAAM,GAAG,OAAO;AAAA,EAC7D;AAEA,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,QAAM,mBAAmB,UAAU;AACnC,SAAO,GAAG,KAAK,KAAK,gBAAgB;AACtC;AAUO,SAAS,gBAAgB,IAAoB;AAClD,SAAO,KAAK,MAAM,KAAK,GAAS;AAClC;AAQO,SAAS,gBAAgB,IAAoB;AAClD,SAAO,KAAK;AACd;;;AC/CA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAGtB,IAAM,eAAe,oBAAI,IAAgC;AAUlD,SAAS,mBAAmB,MAAkC;AACnE,MAAI,aAAa,IAAI,IAAI,GAAG;AAC1B,WAAO,aAAa,IAAI,IAAI;AAAA,EAC9B;AAEA,QAAM,UAAU,mBAAmB,IAAI;AACvC,eAAa,IAAI,MAAM,OAAO;AAC9B,SAAO;AACT;AAKA,SAAS,mBAAmB,UAAsC;AAChE,MAAI,UAAe,cAAQ,QAAQ;AAEnC,SAAO,MAAM;AACX,UAAM,UAAe,WAAK,SAAS,cAAc;AACjD,QAAI;AACF,UAAO,eAAW,OAAO,GAAG;AAC1B,cAAM,MAAS,iBAAa,SAAS,MAAM;AAC3C,cAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,eAAO,OAAO;AAAA,MAChB;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,UAAM,SAAc,cAAQ,OAAO;AACnC,QAAI,WAAW,SAAS;AAEtB,aAAO;AAAA,IACT;AACA,cAAU;AAAA,EACZ;AACF;AAMO,SAAS,oBAA0B;AACxC,eAAa,MAAM;AACrB;;;ACrCO,SAASC,UACd,MAA0C,QAAQ,KAC3B;AAEvB,MAAI,IAAI,aAAa,QAAQ;AAC3B,UAAM,SAAS,IAAI,oBAAoB,QAAQ,kBAAkB,EAAE;AAGnE,UAAM,YAAY,IAAI;AACtB,UAAM,cAAc,IAAI;AACxB,UAAM,UAAU,IAAI;AACpB,UAAM,MACJ,aAAa,eAAe,UACxB,GAAG,SAAS,GAAG,WAAW,2BAA2B,OAAO,KAC5D;AAEN,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,WAAW,IAAI;AAAA,MACf,UAAU,IAAI;AAAA,IAChB;AAAA,EACF;AAGA,MAAI,IAAI,cAAc,QAAQ;AAC5B,UAAM,QAAQ,IAAI;AAClB,UAAM,WAAW,SAAS,UAAU,UAAU,QAAQ;AAEtD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,IAAI,mBAAmB,QAAQ;AACjC,UAAM,MACJ,IAAI,qBAAqB,IAAI,qBAAqB,IAAI,gBAClD,GAAG,IAAI,iBAAiB,IAAI,IAAI,iBAAiB,iBAAiB,IAAI,aAAa,KACnF;AAGN,UAAM,SAAS,IAAI,mBAAmB,IAAI;AAG1C,UAAM,UAAU,IAAI,YAAY,MAAM,mCAAmC;AACzE,UAAM,WAAW,UAAU,QAAQ,CAAC,IAAI;AAExC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB;AAAA,MACA;AAAA,MACA,WAAW,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,IAAI,cAAc,QAAQ;AAC5B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf,UAAU,IAAI;AAAA,IAChB;AAAA,EACF;AAGA,MAAI,IAAI,aAAa,QAAQ;AAG3B,UAAM,QAAQ,IAAI;AAClB,UAAM,UAAU,OAAO,MAAM,UAAU;AACvC,UAAM,WAAW,UAAU,QAAQ,CAAC,IAAI;AAExC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,IAAI,gBAAgB,QAAW;AACjC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAGA,MAAI,IAAI,WAAW,QAAQ;AACzB,UAAM,QAAQ,IAAI;AAClB,UAAM,WAAW,SAAS,UAAU,UAAU,QAAQ;AAEtD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,IAAI,OAAO,QAAQ;AACrB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAGA,SAAO;AACT;;;AC3JA,SAAS,qBAAqB;AAY9B,SAAS,aAA0B;AAEjC,QAAM,MAAM,YAAY,QAClB,OAAO,eAAe,cAAc,UAAU,UAAU,KAAK;AACnE,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,6BAA6B;AACvD,SAAO,cAAc,GAAG;AAC1B;AAMO,SAAS,0BAAwD;AACtE,MAAI;AACF,UAAM,MAAM,WAAW,EAAE,oBAAoB;AAC7C,UAAM,OAAO,IAAI,OAAO,gBAAgB;AACxC,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,MAAM,KAAK,cAAc;AAC/B,QAAI,CAAC,KAAK,WAAW,IAAI,YAAY;AACnC,aAAO;AACT,WAAO,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,OAAO;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,gBACd,UACA,SACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,SAAS,QAAQ,gBAAgB,OAAO;AACjD;;;AClDO,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;AAGO,SAAS,YAAY,IAAoC;AAC9D,MAAI,CAAC,GAAI,QAAO;AAEhB,SAAO;AAAA,IACL,MAAM,GAAG,aAAa,YAAY,OAAO,GAAG;AAAA,IAC5C,UAAU,GAAG;AAAA,IACb,KAAK,GAAG;AAAA,IACR,aAAa,GAAG;AAAA,IAChB,QAAQ,GAAG;AAAA,IACX,WAAW,GAAG;AAAA,IACd,UAAU,GAAG;AAAA,EACf;AACF;;;AChEA,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;;;AC3HO,SAAS,mBAAmB,MAEf;AAClB,QAAM,EAAE,QAAQ,IAAI;AAGpB,QAAM,YAAY,QAAQ;AAAA,IACxB,CAAC,MAAM,EAAE,WAAW,YAAY,EAAE,WAAW;AAAA,EAC/C;AAEA,MAAI,UAAU,SAAS,uBAAuB;AAC5C,WAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,mBAAmB,UAAU;AAAA,MAC7B,mBAAmB;AAAA,IACrB;AAAA,EACF;AAGA,MAAI,cAAc;AAClB,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,QAAI,UAAU,CAAC,EAAE,WAAW,UAAU,IAAI,CAAC,EAAE,QAAQ;AACnD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,kBAAkB,eAAe,UAAU,SAAS;AAC1D,QAAM,WAAW,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EAAE;AAChE,QAAM,cAAc,WAAW,UAAU;AAGzC,MAAI,oBAAoB;AACxB,MAAI,oBAAoB;AACxB,MAAI,oBAAoB;AACxB,MAAI,oBAAoB;AAExB,aAAW,KAAK,WAAW;AACzB,QAAI,EAAE,WAAW,UAAU;AACzB;AACA,0BAAoB;AACpB,UAAI,oBAAoB,mBAAmB;AACzC,4BAAoB;AAAA,MACtB;AAAA,IACF,OAAO;AACL;AACA,0BAAoB;AACpB,UAAI,oBAAoB,mBAAmB;AACzC,4BAAoB;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAGA,MAAI;AACJ,MACE,kBAAkB,OACjB,kBAAkB,OAAO,cAAc,KACxC;AACA,qBAAiB;AAAA,EACnB,WAAW,kBAAkB,OAAO,cAAc,KAAK;AACrD,qBAAiB;AAAA,EACnB,OAAO;AACL,qBAAiB;AAAA,EACnB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC7EO,SAAS,uBAAuB,MAEjB;AACpB,QAAM,EAAE,QAAQ,IAAI;AAGpB,QAAM,YAAY,QAAQ;AAAA,IACxB,CAAC,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW;AAAA,EAChD;AAEA,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,EAAE,OAAO,UAAU,eAAe,EAAE;AAAA,EAC7C;AAEA,QAAM,SACJ,UAAU,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC,IAAI,UAAU;AAElE,MAAI,UAAU,SAAS,kBAAkB;AACvC,WAAO,EAAE,OAAO,UAAU,eAAe,OAAO;AAAA,EAClD;AAGA,QAAM,MAAM,KAAK,MAAM,UAAU,SAAS,CAAC;AAC3C,QAAM,UAAU,UAAU,MAAM,GAAG,GAAG;AACtC,QAAM,SAAS,UAAU,MAAM,GAAG;AAElC,QAAM,aACJ,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC,IAAI,QAAQ;AAC9D,QAAM,YACJ,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC,IAAI,OAAO;AAE5D,MAAI;AACJ,MAAI,eAAe,GAAG;AACpB,YAAQ;AAAA,EACV,OAAO;AACL,UAAM,UAAU,YAAY,cAAc;AAC1C,QAAI,SAAS,KAAK;AAChB,cAAQ;AAAA,IACV,WAAW,SAAS,MAAM;AACxB,cAAQ;AAAA,IACV,OAAO;AACL,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,eAAe,OAAO;AACxC;;;AC7CO,SAAS,mBAAmB,MAA8C;AAC/E,QAAM,EAAE,UAAU,gBAAgB,mBAAmB,WAAW,IAAI;AAEpE,QAAM,mBAAmB,IAAI;AAC7B,QAAM,aAAa,oBAAoB,KAAK,IAAI,YAAY,EAAE;AAE9D,QAAM,QAAQ,WAAW,MAAM,mBAAmB,MAAM,aAAa;AAErE,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,SAAS,IAAM,QAAO;AAC1B,MAAI,SAAS,IAAM,QAAO;AAC1B,SAAO;AACT;;;ACjBO,SAAS,mBAAmB,MAGnB;AACd,QAAM,EAAE,QAAQ,QAAQ,IAAI;AAE5B,QAAM,YAAY,mBAAmB,EAAE,QAAQ,CAAC;AAChD,QAAM,OAAO,uBAAuB,EAAE,QAAQ,CAAC;AAG/C,QAAM,YAAY,QAAQ;AAAA,IACxB,CAAC,MAAM,EAAE,WAAW,YAAY,EAAE,WAAW;AAAA,EAC/C;AACA,QAAM,WACJ,UAAU,SAAS,IACf,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EAAE,SAAS,UAAU,SAClE;AAEN,QAAM,iBAAiB,mBAAmB;AAAA,IACxC;AAAA,IACA,gBAAgB,UAAU;AAAA,IAC1B,mBAAmB,UAAU;AAAA,IAC7B,YAAY,QAAQ;AAAA,EACtB,CAAC;AAGD,MAAI,sBAAsB;AAC1B,WAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,QAAI,QAAQ,CAAC,EAAE,WAAW,UAAU;AAClC;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,UAAU;AAAA,IAC1B,gBAAgB,UAAU;AAAA,IAC1B,aAAa,UAAU;AAAA,IACvB;AAAA,IACA,kBAAkB,KAAK;AAAA,IACvB,eAAe,KAAK;AAAA,IACpB;AAAA,IACA,mBAAmB,UAAU;AAAA,IAC7B;AAAA,IACA,YAAY,QAAQ;AAAA,EACtB;AACF;;;AC3CA,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;;;A9EiQA,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;AAOO,SAAS,sBACd,SACA,MACiB;AACjB,SAAO,IAAI,gBAAgB,SAAS,IAAI;AAC1C;AAEA,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;AA+CO,SAAS,qBACd,aACA,cACA,gBACA,qBACe;AACf,QAAM,MAAc,aAAa,aAAa,cAAc,cAAc;AAC1E,SAAO,gBAAgB,KAAK,mBAAmB;AACjD;AAOO,SAAS,uBACd,aACA,gBACA,qBACe;AACf,QAAM,MAAc,eAAe,aAAa,cAAc;AAC9D,SAAO,gBAAgB,KAAK,mBAAmB;AACjD;AAOO,SAAS,2BACd,aACA,gBACA,qBACe;AACf,QAAM,MAAc,mBAAmB,aAAa,cAAc;AAClE,SAAO,gBAAgB,KAAK,mBAAmB;AACjD;","names":["path","groupBy","escapeHtml","escapeHtml","groupBy","groupBy","groupBy","groupBy","basename","createHash","path","resolve","flags","escapeHtml","renderScenario","formatSteps","formatDocs","formatStep","formatDocEntry","fs","path","fs","path","fs","path","detectCI","detectCI","extractFeatureName","fs","path","formatDuration","fs","path","detectCI","formatDuration","truncate","formatDuration","toCIInfo"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.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/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/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/adapters/jest.ts","../src/converters/adapters/vitest.ts","../src/converters/adapters/playwright.ts","../src/types/story.ts","../src/converters/acl/validate.ts","../src/converters/ndjson-parser.ts","../src/utils/git-info.ts","../src/utils/duration.ts","../src/utils/metadata.ts","../src/utils/ci-detect.ts","../src/utils/otel-detect.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/history/flakiness.ts","../src/history/performance.ts","../src/history/stability.ts","../src/history/metrics.ts","../src/list-scenarios.ts"],"sourcesContent":["/**\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// Formatter plugin types\nexport type { Formatter, ExecutableStoriesConfig } from \"./types/formatter\";\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 * 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 * 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\">☰</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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\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\">▼</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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\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}`);\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 { 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, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\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 \" \";\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 \" \";\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>→</span><span>${escapeHtml(after.status)}</span></div>`\n : before\n ? `<div class=\"status-pair\"><span>${escapeHtml(before.status)}</span><span>→</span><span>removed</span></div>`\n : `<div class=\"status-pair\"><span>new</span><span>→</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(\", \")) || \" \"}</dd>\n <dt>Suite</dt>\n <dd>${escapeHtml(before.titlePath.join(\" > \")) || \" \"}</dd>\n <dt>Error</dt>\n <dd>${escapeHtml(before.errorMessage ?? \"\") || \" \"}</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(\", \")) || \" \"}</dd>` : \"\"}\n </dl>\n </section>\n <section>\n <h4>Current</h4>\n <dl>\n <dt>Tags</dt>\n <dd>${escapeHtml(after.tags.join(\", \")) || \" \"}</dd>\n <dt>Suite</dt>\n <dd>${escapeHtml(after.titlePath.join(\" > \")) || \" \"}</dd>\n <dt>Error</dt>\n <dd>${escapeHtml(after.errorMessage ?? \"\") || \" \"}</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(\", \")) || \" \"}</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:  or \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:  or \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 * Jest Adapter - Layer 1.\n *\n * Transforms Jest test results and story reports into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"../../types/raw\";\n\n/** Jest test result shape (subset of what Jest provides) */\nexport interface JestTestResult {\n fullName: string;\n status: \"passed\" | \"failed\" | \"pending\" | \"todo\";\n duration?: number;\n failureMessages?: string[];\n}\n\n/** Jest file result shape */\nexport interface JestFileResult {\n testFilePath: string;\n testResults: JestTestResult[];\n}\n\n/** Jest aggregated result shape */\nexport interface JestAggregatedResult {\n testResults: JestFileResult[];\n startTime?: number;\n}\n\n/** Story file report written by story.init() */\nexport interface StoryFileReport {\n testFilePath: string;\n scenarios: StoryMeta[];\n}\n\n/** Options for Jest adapter */\nexport interface JestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n}\n\n/**\n * Map Jest status to RawStatus.\n */\nfunction mapJestStatus(status: JestTestResult[\"status\"]): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"pending\";\n case \"todo\":\n return \"todo\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Jest results and story reports to RawRun.\n *\n * @param jestResults - Jest aggregated results\n * @param storyReports - Story reports from story.init()\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptJestRun(\n jestResults: JestAggregatedResult,\n storyReports: StoryFileReport[],\n options: JestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n\n // Build map of Jest results by file for lookup\n const fileResultsMap = new Map<string, JestFileResult>();\n for (const fileResult of jestResults.testResults) {\n fileResultsMap.set(fileResult.testFilePath, fileResult);\n }\n\n // Process each story report\n for (const report of storyReports) {\n const fileResult = fileResultsMap.get(report.testFilePath);\n\n for (const meta of report.scenarios) {\n if (!meta?.scenario) continue;\n\n // Find matching Jest test result\n const matchingTest = findMatchingJestTest(fileResult, meta);\n\n testCases.push({\n externalId: matchingTest?.fullName,\n title: meta.scenario,\n titlePath: meta.suitePath\n ? [...meta.suitePath, meta.scenario]\n : [meta.scenario],\n story: meta,\n sourceFile: report.testFilePath,\n sourceLine: undefined, // Jest doesn't provide line numbers\n status: matchingTest ? mapJestStatus(matchingTest.status) : \"unknown\",\n durationMs: matchingTest?.duration,\n error: matchingTest?.failureMessages?.length\n ? { message: matchingTest.failureMessages.join(\"\\n\") }\n : undefined,\n stepEvents: undefined, // Jest doesn't provide step-level results\n attachments: undefined, // Jest doesn't capture attachments\n meta: { jestStatus: matchingTest?.status },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: jestResults.startTime,\n finishedAtMs: Date.now(),\n projectRoot: options.projectRoot ?? process.cwd(),\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Find matching Jest test result for a story.\n *\n * Matches by constructing fullName from suitePath and scenario.\n */\nfunction findMatchingJestTest(\n fileResult: JestFileResult | undefined,\n meta: StoryMeta\n): JestTestResult | undefined {\n if (!fileResult) return undefined;\n\n // Construct expected fullName\n const expectedFullName = meta.suitePath\n ? [...meta.suitePath, meta.scenario].join(\" > \")\n : meta.scenario;\n\n return fileResult.testResults.find((test) => test.fullName === expectedFullName);\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Vitest Adapter - Layer 1.\n *\n * Transforms Vitest test results into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"../../types/raw\";\n\n/** Vitest test state */\nexport type VitestState = \"passed\" | \"failed\" | \"skipped\" | \"pending\";\n\n/** Vitest serialized error */\nexport interface VitestSerializedError {\n message?: string;\n stack?: string;\n diff?: string;\n}\n\n/** Vitest test result shape */\nexport interface VitestTestResult {\n state?: VitestState;\n duration?: number;\n errors?: VitestSerializedError[];\n}\n\n/** Vitest test case shape (minimal) */\nexport interface VitestTestCase {\n name: string;\n meta: () => Record<string, unknown>;\n result: () => VitestTestResult | undefined;\n}\n\n/** Vitest test module shape (minimal) */\nexport interface VitestTestModule {\n moduleId?: string;\n relativeModuleId?: string;\n children?: {\n allTests: () => Iterable<VitestTestCase>;\n };\n}\n\n/** Options for Vitest adapter */\nexport interface VitestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n}\n\n/**\n * Map Vitest state to RawStatus.\n */\nfunction mapVitestStatus(state?: VitestState): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"pending\":\n return \"pending\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Vitest test modules to RawRun.\n *\n * @param testModules - Vitest test modules\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptVitestRun(\n testModules: ReadonlyArray<VitestTestModule>,\n options: VitestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const mod of testModules) {\n const collection = mod.children;\n if (!collection) continue;\n\n // Get module path\n const moduleId = mod.moduleId ?? mod.relativeModuleId ?? \"\";\n const sourcePath = moduleId.startsWith(\"/\")\n ? moduleId\n : `${projectRoot}/${moduleId}`;\n\n for (const test of collection.allTests()) {\n const meta = test.meta();\n const story = meta?.[\"story\"] as StoryMeta | undefined;\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n const result = test.result();\n const state = result?.state;\n const errors = state === \"failed\" && result ? result.errors : undefined;\n\n testCases.push({\n externalId: test.name,\n title: story.scenario,\n titlePath: story.suitePath\n ? [...story.suitePath, story.scenario]\n : [story.scenario],\n story,\n sourceFile: sourcePath,\n sourceLine: undefined, // Vitest doesn't provide line numbers easily\n status: mapVitestStatus(state),\n durationMs: result?.duration,\n error: errors?.length\n ? {\n message: formatVitestError(errors[0]),\n stack: errors[0].stack,\n }\n : undefined,\n stepEvents: undefined, // Vitest doesn't provide step-level results\n attachments: undefined, // Vitest doesn't capture attachments\n meta: { vitestState: state },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Format Vitest error message.\n */\nfunction formatVitestError(error: VitestSerializedError): string {\n const parts: string[] = [];\n\n if (error.message) {\n parts.push(error.message);\n }\n\n if (error.diff) {\n parts.push(\"\", error.diff);\n }\n\n return parts.join(\"\\n\") || \"Unknown error\";\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Playwright Adapter - Layer 1.\n *\n * Transforms Playwright test results into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus, RawAttachment } from \"../../types/raw\";\n\n/** Playwright test status */\nexport type PlaywrightStatus =\n | \"passed\"\n | \"failed\"\n | \"skipped\"\n | \"timedOut\"\n | \"interrupted\";\n\n/** Playwright test error */\nexport interface PlaywrightError {\n message?: string;\n stack?: string;\n}\n\n/** Playwright attachment */\nexport interface PlaywrightAttachment {\n name: string;\n contentType: string;\n path?: string;\n body?: Buffer;\n}\n\n/** Playwright test result */\nexport interface PlaywrightTestResult {\n status: PlaywrightStatus;\n duration: number;\n errors: PlaywrightError[];\n attachments: PlaywrightAttachment[];\n retry: number;\n}\n\n/** Playwright test case annotation */\nexport interface PlaywrightAnnotation {\n type: string;\n description?: string;\n}\n\n/** Playwright test location */\nexport interface PlaywrightLocation {\n file: string;\n line: number;\n column: number;\n}\n\n/** Playwright test case shape (minimal) */\nexport interface PlaywrightTestCase {\n title: string;\n titlePath: () => string[];\n annotations: PlaywrightAnnotation[];\n location: PlaywrightLocation;\n retries: number;\n}\n\n/** Options for Playwright adapter */\nexport interface PlaywrightAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n /** Playwright project name */\n projectName?: string;\n}\n\n/**\n * Map Playwright status to RawStatus.\n */\nfunction mapPlaywrightStatus(status: PlaywrightStatus): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"timedOut\":\n return \"timeout\";\n case \"interrupted\":\n return \"interrupted\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Playwright test results to RawRun.\n *\n * @param testResults - Array of [testCase, result] tuples\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptPlaywrightRun(\n testResults: Array<[PlaywrightTestCase, PlaywrightTestResult]>,\n options: PlaywrightAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const [test, result] of testResults) {\n // Find story-meta annotation\n const storyAnnotation = test.annotations.find((a) => a.type === \"story-meta\");\n if (!storyAnnotation?.description) continue;\n\n let story: StoryMeta;\n try {\n story = JSON.parse(storyAnnotation.description);\n } catch {\n continue; // Skip if annotation is not valid JSON\n }\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n // Convert attachments\n const attachments = convertAttachments(result.attachments);\n\n // Get error info\n const error = result.errors?.length\n ? {\n message: result.errors[0].message,\n stack: result.errors[0].stack,\n }\n : undefined;\n\n testCases.push({\n externalId: test.titlePath().join(\" > \"),\n title: story.scenario,\n titlePath: test.titlePath(),\n story,\n sourceFile: test.location.file,\n sourceLine: test.location.line,\n status: mapPlaywrightStatus(result.status),\n durationMs: result.duration,\n error,\n stepEvents: undefined, // Playwright could provide step info, but we don't capture it yet\n attachments,\n meta: {\n playwrightStatus: result.status,\n column: test.location.column,\n },\n retry: result.retry,\n retries: test.retries,\n projectName: options.projectName,\n });\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Convert Playwright attachments to raw attachments.\n */\nfunction convertAttachments(\n attachments: PlaywrightAttachment[]\n): RawAttachment[] {\n return attachments.map((att) => ({\n name: att.name,\n mediaType: att.contentType,\n path: att.path,\n body: att.body ? att.body.toString(\"base64\") : undefined,\n encoding: att.body ? \"BASE64\" as const : undefined,\n byteLength: att.body?.length,\n }));\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Story types — the shared vocabulary for all framework adapters.\n *\n * These types were previously in executable-stories-core.\n * They now live in formatters so every adapter can import them\n * from the same place that defines RawRun (the output contract).\n */\n\nimport type { OtelSpan } from \"./otel\";\n\n// ============================================================================\n// Step Keywords\n// ============================================================================\n\n/** BDD step keywords for scenario documentation */\nexport type StepKeyword = \"Given\" | \"When\" | \"Then\" | \"And\" | \"But\";\n\n/** Step execution mode for docs rendering */\nexport type StepMode = \"normal\" | \"skip\" | \"only\" | \"todo\" | \"fails\" | \"concurrent\";\n\n// ============================================================================\n// Doc Entry Types\n// ============================================================================\n\n/** Phase tracks when the doc entry was added */\nexport type DocPhase = \"static\" | \"runtime\";\n\n/** Union type for all documentation entry kinds */\nexport type DocEntry =\n | { kind: \"note\"; text: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"tag\"; names: string[]; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"kv\"; label: string; value: unknown; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"code\"; label: string; content: string; lang?: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"table\"; label: string; columns: string[]; rows: string[][]; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"link\"; label: string; url: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"section\"; title: string; markdown: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"mermaid\"; code: string; title?: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"screenshot\"; path: string; alt?: string; phase: DocPhase; children?: DocEntry[] }\n | { kind: \"custom\"; type: string; data: unknown; phase: DocPhase; children?: DocEntry[] };\n\n// ============================================================================\n// Story Step\n// ============================================================================\n\n/**\n * A single step in a scenario with its documentation entries.\n */\nexport interface StoryStep {\n /** Stable internal ID (auto-generated at creation, e.g., \"step-0\") */\n id?: string;\n /** The BDD keyword (Given, When, Then, And, But) */\n keyword: StepKeyword;\n /** The step description text */\n text: string;\n /** Step execution mode for docs rendering */\n mode?: StepMode;\n /** Rich documentation entries attached to this step */\n docs?: DocEntry[];\n /** Opt-in step duration in milliseconds */\n durationMs?: number;\n /** Whether this step wrapped a function body (step.fn / step.step) vs a text marker */\n wrapped?: boolean;\n}\n\n// ============================================================================\n// Ticket Reference\n// ============================================================================\n\n/** A ticket reference with an optional direct URL */\nexport interface NormalizedTicket {\n /** Ticket identifier (e.g., \"JIRA-123\", \"PAY-1042\") */\n id: string;\n /** Direct URL to the ticket (overrides ticketUrlTemplate) */\n url?: string;\n}\n\n// ============================================================================\n// Story Metadata\n// ============================================================================\n\n/**\n * Metadata for a complete scenario, attached to test metadata.\n * Used by reporters to generate documentation.\n */\nexport interface StoryMeta {\n /** The scenario title (from test name) */\n scenario: string;\n /** All steps in this scenario */\n steps: StoryStep[];\n /** Tags for filtering and categorization */\n tags?: string[];\n /** Ticket/issue references (normalized to array) */\n tickets?: NormalizedTicket[];\n /** User-defined metadata */\n meta?: Record<string, unknown>;\n /** Parent describe/suite names for hierarchical grouping */\n suitePath?: string[];\n /** Story-level docs (before any steps) */\n docs?: DocEntry[];\n /** Order in which story.init() was called (for source ordering) */\n sourceOrder?: number;\n /** OTel spans from autotel for trace waterfall rendering */\n otelSpans?: OtelSpan[];\n}\n\n/** Key used to store StoryMeta in test metadata */\nexport const STORY_META_KEY = \"story\";\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 * 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 * Git information utilities.\n *\n * Read git SHA and other info from the local repository.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/**\n * Read the current git SHA.\n *\n * Checks environment variables first (for CI), then reads from .git directory.\n *\n * @param cwd - Working directory to start search from\n * @returns Git SHA or undefined if not in a git repo\n */\nexport function readGitSha(cwd: string = process.cwd()): string | undefined {\n // Check CI environment variables first\n const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;\n if (envSha) return envSha;\n\n // Find .git directory\n const gitDir = findGitDir(cwd);\n if (!gitDir) return undefined;\n\n try {\n const headPath = path.join(gitDir, \"HEAD\");\n const head = fs.readFileSync(headPath, \"utf8\").trim();\n\n // If HEAD is a direct SHA (detached HEAD state)\n if (!head.startsWith(\"ref:\")) {\n return head;\n }\n\n // HEAD points to a ref, resolve it\n const refPath = head.replace(\"ref:\", \"\").trim();\n const refFile = path.join(gitDir, refPath);\n\n if (fs.existsSync(refFile)) {\n return fs.readFileSync(refFile, \"utf8\").trim();\n }\n\n // Check packed-refs\n const packedRefs = path.join(gitDir, \"packed-refs\");\n if (fs.existsSync(packedRefs)) {\n const content = fs.readFileSync(packedRefs, \"utf8\");\n for (const line of content.split(\"\\n\")) {\n if (!line || line.startsWith(\"#\") || line.startsWith(\"^\")) continue;\n const [sha, ref] = line.split(\" \");\n if (ref === refPath) return sha;\n }\n }\n\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Find the .git directory starting from a given directory.\n *\n * @param start - Directory to start search from\n * @returns Path to .git directory or undefined if not found\n */\nexport function findGitDir(start: string): string | undefined {\n let current = start;\n while (true) {\n const candidate = path.join(current, \".git\");\n if (fs.existsSync(candidate)) {\n // Handle git worktrees where .git is a file\n const stat = fs.statSync(candidate);\n if (stat.isFile()) {\n const content = fs.readFileSync(candidate, \"utf8\").trim();\n const match = content.match(/^gitdir: (.+)$/);\n if (match) {\n return path.resolve(current, match[1]);\n }\n }\n return candidate;\n }\n const parent = path.dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n}\n\n/**\n * Read the current branch name.\n *\n * @param cwd - Working directory\n * @returns Branch name or undefined\n */\nexport function readBranchName(cwd: string = process.cwd()): string | undefined {\n // Check CI environment variables first\n const envBranch = process.env.GITHUB_REF_NAME || process.env.CI_COMMIT_REF_NAME;\n if (envBranch) return envBranch;\n\n const gitDir = findGitDir(cwd);\n if (!gitDir) return undefined;\n\n try {\n const headPath = path.join(gitDir, \"HEAD\");\n const head = fs.readFileSync(headPath, \"utf8\").trim();\n\n if (head.startsWith(\"ref:\")) {\n const refPath = head.replace(\"ref:\", \"\").trim();\n // Extract branch name from refs/heads/branch-name\n const match = refPath.match(/^refs\\/heads\\/(.+)$/);\n return match ? match[1] : refPath;\n }\n\n // Detached HEAD\n return undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Duration formatting utilities.\n */\n\n/**\n * Format milliseconds as human-readable duration.\n *\n * @param ms - Duration in milliseconds\n * @returns Formatted string (e.g., \"123 ms\", \"1.5 s\", \"2m 30s\")\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`;\n }\n\n if (ms < 60000) {\n return `${(ms / 1000).toFixed(2)} s`;\n }\n\n const minutes = Math.floor(ms / 60000);\n const seconds = Math.floor((ms % 60000) / 1000);\n\n if (minutes < 60) {\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n return `${hours}h ${remainingMinutes}m`;\n}\n\n/**\n * Convert milliseconds to nanoseconds.\n *\n * Used for Cucumber JSON format which expects nanoseconds.\n *\n * @param ms - Duration in milliseconds\n * @returns Duration in nanoseconds\n */\nexport function msToNanoseconds(ms: number): number {\n return Math.round(ms * 1_000_000);\n}\n\n/**\n * Convert nanoseconds to milliseconds.\n *\n * @param ns - Duration in nanoseconds\n * @returns Duration in milliseconds\n */\nexport function nanosecondsToMs(ns: number): number {\n return ns / 1_000_000;\n}\n","/**\n * Metadata utilities for reading package information.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/** Cache for package versions by root directory */\nconst versionCache = new Map<string, string | undefined>();\n\n/**\n * Read the package version from the closest package.json.\n *\n * Results are cached by root directory.\n *\n * @param root - Directory to start searching from\n * @returns Package version string or undefined if not found\n */\nexport function readPackageVersion(root: string): string | undefined {\n if (versionCache.has(root)) {\n return versionCache.get(root);\n }\n\n const version = findPackageVersion(root);\n versionCache.set(root, version);\n return version;\n}\n\n/**\n * Find package.json version by walking up the directory tree.\n */\nfunction findPackageVersion(startDir: string): string | undefined {\n let current = path.resolve(startDir);\n\n while (true) {\n const pkgPath = path.join(current, \"package.json\");\n try {\n if (fs.existsSync(pkgPath)) {\n const raw = fs.readFileSync(pkgPath, \"utf8\");\n const parsed = JSON.parse(raw) as { version?: string };\n return parsed.version;\n }\n } catch {\n // Continue searching\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n // Reached root\n return undefined;\n }\n current = parent;\n }\n}\n\n/**\n * Clear the package version cache.\n * Useful for testing.\n */\nexport function clearVersionCache(): void {\n versionCache.clear();\n}\n","/**\n * CI environment auto-detection utility.\n *\n * Detects known CI providers from environment variables and populates\n * branch, commit SHA, PR number, build number, and URL metadata.\n *\n * Precedence (checked in order, first match wins):\n * 1. TF_BUILD=True -> azure\n * 2. BUILDKITE=true -> buildkite\n * 3. GITHUB_ACTIONS=true -> github\n * 4. GITLAB_CI=true -> gitlab\n * 5. CIRCLECI=true -> circleci\n * 6. JENKINS_URL defined -> jenkins\n * 7. TRAVIS=true -> travis\n * 8. CI=true -> unknown (generic fallback; note: fires in dev shells too)\n * 9. Nothing -> undefined (not in CI)\n */\n\nimport type { RawCIInfo } from \"../types/raw\";\n\n/**\n * Detect CI environment from process.env.\n * Returns undefined when not running in CI.\n */\nexport function detectCI(\n env: Record<string, string | undefined> = process.env,\n): RawCIInfo | undefined {\n // 1. Azure DevOps (TF_BUILD is \"True\" with capital T)\n if (env.TF_BUILD === \"True\") {\n const branch = env.BUILD_SOURCEBRANCH?.replace(/^refs\\/heads\\//, \"\");\n\n // URL requires all three components\n const serverUri = env.SYSTEM_TEAMFOUNDATIONSERVERURI;\n const teamProject = env.SYSTEM_TEAMPROJECT;\n const buildId = env.BUILD_BUILDID;\n const url =\n serverUri && teamProject && buildId\n ? `${serverUri}${teamProject}/_build/results?buildId=${buildId}`\n : undefined;\n\n return {\n name: \"azure\",\n provider: \"azure\",\n buildNumber: buildId,\n url,\n branch,\n commitSha: env.BUILD_SOURCEVERSION,\n prNumber: env.SYSTEM_PULLREQUEST_PULLREQUESTID,\n };\n }\n\n // 2. Buildkite\n if (env.BUILDKITE === \"true\") {\n const prRaw = env.BUILDKITE_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"buildkite\",\n provider: \"buildkite\",\n buildNumber: env.BUILDKITE_BUILD_NUMBER,\n url: env.BUILDKITE_BUILD_URL,\n branch: env.BUILDKITE_BRANCH,\n commitSha: env.BUILDKITE_COMMIT,\n prNumber,\n };\n }\n\n // 3. GitHub Actions\n if (env.GITHUB_ACTIONS === \"true\") {\n const url =\n env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : undefined;\n\n // GITHUB_HEAD_REF is set on pull_request events; GITHUB_REF_NAME is always set\n const branch = env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME;\n\n // Parse PR number from GITHUB_REF: refs/pull/<n>/merge or refs/pull/<n>/head\n const prMatch = env.GITHUB_REF?.match(/^refs\\/pull\\/(\\d+)\\/(merge|head)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"github\",\n provider: \"github\",\n buildNumber: env.GITHUB_RUN_NUMBER,\n url,\n branch,\n commitSha: env.GITHUB_SHA,\n prNumber,\n };\n }\n\n // 4. GitLab CI\n if (env.GITLAB_CI === \"true\") {\n return {\n name: \"gitlab\",\n provider: \"gitlab\",\n buildNumber: env.CI_PIPELINE_IID,\n url: env.CI_PIPELINE_URL,\n branch: env.CI_COMMIT_REF_NAME,\n commitSha: env.CI_COMMIT_SHA,\n prNumber: env.CI_MERGE_REQUEST_IID,\n };\n }\n\n // 5. CircleCI\n if (env.CIRCLECI === \"true\") {\n // Parse PR number from trailing digits of CIRCLE_PULL_REQUEST URL\n // e.g. https://github.com/org/repo/pull/42 -> \"42\"\n const prUrl = env.CIRCLE_PULL_REQUEST;\n const prMatch = prUrl?.match(/\\/(\\d+)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"circleci\",\n provider: \"circleci\",\n buildNumber: env.CIRCLE_BUILD_NUM,\n url: env.CIRCLE_BUILD_URL,\n branch: env.CIRCLE_BRANCH,\n commitSha: env.CIRCLE_SHA1,\n prNumber,\n };\n }\n\n // 6. Jenkins (detected by JENKINS_URL being defined, any value)\n if (env.JENKINS_URL !== undefined) {\n return {\n name: \"jenkins\",\n provider: \"jenkins\",\n buildNumber: env.BUILD_NUMBER,\n url: env.BUILD_URL,\n branch: env.GIT_BRANCH,\n commitSha: env.GIT_COMMIT,\n };\n }\n\n // 7. Travis CI\n if (env.TRAVIS === \"true\") {\n const prRaw = env.TRAVIS_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"travis\",\n provider: \"travis\",\n buildNumber: env.TRAVIS_BUILD_NUMBER,\n url: env.TRAVIS_BUILD_WEB_URL,\n branch: env.TRAVIS_BRANCH,\n commitSha: env.TRAVIS_COMMIT,\n prNumber,\n };\n }\n\n // 8. Generic CI fallback (note: CI=true fires in many dev shells too)\n if (env.CI === \"true\") {\n return {\n name: \"ci\",\n provider: \"unknown\",\n };\n }\n\n // 9. Not in CI\n return undefined;\n}\n","/**\n * OTel trace context detection.\n *\n * Detects an active OpenTelemetry span via `@opentelemetry/api` (optional peer).\n * Uses `createRequire` so the import is lazy and never breaks if OTel isn't installed.\n */\n\nimport { createRequire } from \"node:module\";\n\nexport interface OtelTraceContext {\n traceId: string;\n spanId: string;\n}\n\n/**\n * Build a require function that works in both ESM and CJS bundles.\n * In ESM, import.meta.url is defined. In CJS (e.g. tsup output), it is\n * shimmed to undefined — fall back to __filename which CJS always has.\n */\nfunction getRequire(): NodeRequire {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const url = import.meta.url\n ?? (typeof __filename !== \"undefined\" ? `file://${__filename}` : undefined);\n if (!url) throw new Error(\"Cannot determine module URL\");\n return createRequire(url);\n}\n\n/**\n * Try to read trace context from the currently-active OTel span.\n * Returns `undefined` when `@opentelemetry/api` is not installed or no span is active.\n */\nexport function tryGetActiveOtelContext(): OtelTraceContext | undefined {\n try {\n const api = getRequire()(\"@opentelemetry/api\");\n const span = api.trace?.getActiveSpan?.();\n if (!span) return undefined;\n const ctx = span.spanContext?.();\n if (!ctx?.traceId || ctx.traceId === \"00000000000000000000000000000000\")\n return undefined;\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Replace `{traceId}` in a URL template.\n * Returns `undefined` when no template is provided.\n */\nexport function resolveTraceUrl(\n template: string | undefined,\n traceId: string,\n): string | undefined {\n if (!template) return undefined;\n return template.replace(/\\{traceId\\}/g, traceId);\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 * Flakiness calculation based on status transitions.\n */\n\nimport type { HistoryEntry, FlakinessLevel } from \"./types\";\nimport { MIN_FLAKINESS_SAMPLES } from \"./sample-policy\";\n\nexport interface FlakinessResult {\n flakinessLevel: FlakinessLevel;\n flakinessScore: number;\n failureRate: number;\n longestPassStreak: number;\n longestFailStreak: number;\n}\n\nexport function calculateFlakiness(args: {\n entries: HistoryEntry[];\n}): FlakinessResult {\n const { entries } = args;\n\n // Only count pass/fail entries (exclude skipped/pending)\n const countable = entries.filter(\n (e) => e.status === \"passed\" || e.status === \"failed\",\n );\n\n if (countable.length < MIN_FLAKINESS_SAMPLES) {\n return {\n flakinessLevel: \"stable\",\n flakinessScore: 0,\n failureRate: 0,\n longestPassStreak: countable.length,\n longestFailStreak: 0,\n };\n }\n\n // Count transitions (consecutive entries with different status)\n let transitions = 0;\n for (let i = 1; i < countable.length; i++) {\n if (countable[i].status !== countable[i - 1].status) {\n transitions++;\n }\n }\n\n const transitionScore = transitions / (countable.length - 1);\n const failures = countable.filter((e) => e.status === \"failed\").length;\n const failureRate = failures / countable.length;\n\n // Calculate streaks\n let longestPassStreak = 0;\n let longestFailStreak = 0;\n let currentPassStreak = 0;\n let currentFailStreak = 0;\n\n for (const e of countable) {\n if (e.status === \"passed\") {\n currentPassStreak++;\n currentFailStreak = 0;\n if (currentPassStreak > longestPassStreak) {\n longestPassStreak = currentPassStreak;\n }\n } else {\n currentFailStreak++;\n currentPassStreak = 0;\n if (currentFailStreak > longestFailStreak) {\n longestFailStreak = currentFailStreak;\n }\n }\n }\n\n // Classification\n let flakinessLevel: FlakinessLevel;\n if (\n transitionScore > 0.5 ||\n (transitionScore > 0.3 && failureRate > 0.2)\n ) {\n flakinessLevel = \"flaky\";\n } else if (transitionScore > 0.2 || failureRate > 0.3) {\n flakinessLevel = \"unstable\";\n } else {\n flakinessLevel = \"stable\";\n }\n\n return {\n flakinessLevel,\n flakinessScore: transitionScore,\n failureRate,\n longestPassStreak,\n longestFailStreak,\n };\n}\n","/**\n * Performance trend detection via half-split comparison.\n */\n\nimport type { HistoryEntry, PerformanceTrend } from \"./types\";\nimport { MIN_PERF_SAMPLES } from \"./sample-policy\";\n\nexport interface PerformanceResult {\n trend: PerformanceTrend;\n avgDurationMs: number;\n}\n\nexport function detectPerformanceTrend(args: {\n entries: HistoryEntry[];\n}): PerformanceResult {\n const { entries } = args;\n\n // Filter out entries with no meaningful duration data\n const countable = entries.filter(\n (e) => e.status !== \"skipped\" && e.status !== \"pending\",\n );\n\n if (countable.length === 0) {\n return { trend: \"stable\", avgDurationMs: 0 };\n }\n\n const avgAll =\n countable.reduce((sum, e) => sum + e.durationMs, 0) / countable.length;\n\n if (countable.length < MIN_PERF_SAMPLES) {\n return { trend: \"stable\", avgDurationMs: avgAll };\n }\n\n // Split into earlier half and recent half\n const mid = Math.floor(countable.length / 2);\n const earlier = countable.slice(0, mid);\n const recent = countable.slice(mid);\n\n const earlierAvg =\n earlier.reduce((sum, e) => sum + e.durationMs, 0) / earlier.length;\n const recentAvg =\n recent.reduce((sum, e) => sum + e.durationMs, 0) / recent.length;\n\n let trend: PerformanceTrend;\n if (earlierAvg === 0) {\n trend = \"stable\";\n } else {\n const change = (recentAvg - earlierAvg) / earlierAvg;\n if (change > 0.1) {\n trend = \"regressing\";\n } else if (change < -0.1) {\n trend = \"improving\";\n } else {\n trend = \"stable\";\n }\n }\n\n return { trend, avgDurationMs: avgAll };\n}\n","/**\n * Stability grade calculation (composite score).\n */\n\nimport type { StabilityGrade } from \"./types\";\n\nexport interface CalculateStabilityArgs {\n passRate: number;\n flakinessScore: number;\n longestPassStreak: number;\n sampleSize: number;\n}\n\nexport function calculateStability(args: CalculateStabilityArgs): StabilityGrade {\n const { passRate, flakinessScore, longestPassStreak, sampleSize } = args;\n\n const inverseFlakiness = 1 - flakinessScore;\n const streakNorm = longestPassStreak / Math.min(sampleSize, 10);\n\n const score = passRate * 0.6 + inverseFlakiness * 0.3 + streakNorm * 0.1;\n\n if (score >= 0.95) return \"A\";\n if (score >= 0.85) return \"B\";\n if (score >= 0.70) return \"C\";\n if (score >= 0.50) return \"D\";\n return \"F\";\n}\n","/**\n * Composite test metrics from history entries.\n */\n\nimport type { HistoryEntry, TestMetrics } from \"./types\";\nimport { calculateFlakiness } from \"./flakiness\";\nimport { detectPerformanceTrend } from \"./performance\";\nimport { calculateStability } from \"./stability\";\n\nexport function computeTestMetrics(args: {\n testId: string;\n entries: HistoryEntry[];\n}): TestMetrics {\n const { testId, entries } = args;\n\n const flakiness = calculateFlakiness({ entries });\n const perf = detectPerformanceTrend({ entries });\n\n // Calculate pass rate (excluding skipped/pending)\n const countable = entries.filter(\n (e) => e.status === \"passed\" || e.status === \"failed\",\n );\n const passRate =\n countable.length > 0\n ? countable.filter((e) => e.status === \"passed\").length / countable.length\n : 1;\n\n const stabilityGrade = calculateStability({\n passRate,\n flakinessScore: flakiness.flakinessScore,\n longestPassStreak: flakiness.longestPassStreak,\n sampleSize: entries.length,\n });\n\n // Calculate consecutive failures from the tail end\n let consecutiveFailures = 0;\n for (let i = entries.length - 1; i >= 0; i--) {\n if (entries[i].status === \"failed\") {\n consecutiveFailures++;\n } else {\n break;\n }\n }\n\n return {\n testId,\n flakinessLevel: flakiness.flakinessLevel,\n flakinessScore: flakiness.flakinessScore,\n failureRate: flakiness.failureRate,\n stabilityGrade,\n performanceTrend: perf.trend,\n avgDurationMs: perf.avgDurationMs,\n passRate,\n longestPassStreak: flakiness.longestPassStreak,\n consecutiveFailures,\n sampleSize: entries.length,\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\" | \"csv\" | \"markdown-table\";\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 if (format === \"csv\") {\n const header = \"id,scenario,status,sourceFile,sourceLine,tags\";\n const rows = testCases.map((tc) => {\n const fields = [\n tc.id,\n tc.story.scenario,\n tc.status,\n tc.sourceFile,\n String(tc.sourceLine),\n tc.tags.join(\" \"),\n ];\n return fields\n .map((f) => {\n if (f.includes(\",\") || f.includes('\"') || f.includes(\"\\n\")) {\n return `\"${f.replace(/\"/g, '\"\"')}\"`;\n }\n return f;\n })\n .join(\",\");\n });\n return [header, ...rows].join(\"\\n\");\n }\n\n if (format === \"markdown-table\") {\n const header = \"| Status | Scenario | Location | Tags |\";\n const divider = \"|--------|----------|----------|------|\";\n const rows = testCases.map((tc) => {\n const icon = STATUS_ICONS[tc.status] ?? \"?\";\n const location = `${tc.sourceFile}:${tc.sourceLine}`;\n const tags = tc.tags.map((t) => `@${t}`).join(\" \");\n return `| ${icon} | ${tc.story.scenario} | ${location} | ${tags} |`;\n });\n return [header, divider, ...rows].join(\"\\n\");\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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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;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,QAAQ,MAAM,QAAQ;AAC3B;AAEA,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;AAA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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;AA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","names":["path","groupBy","escapeHtml","escapeHtml","groupBy","groupBy","groupBy","groupBy","basename","createHash","path","resolve","flags","escapeHtml","renderScenario","formatSteps","formatDocs","formatStep","formatDocEntry","fs","path","fs","path","fs","path","detectCI","detectCI","extractFeatureName","fs","path","formatDuration","fs","path","detectCI","formatDuration","truncate","formatDuration","toCIInfo"]}
|