@c2l2c/backstage-plugin-dora-metrics 0.4.2 → 0.5.0

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/README.md CHANGED
@@ -84,6 +84,13 @@ app:
84
84
  collection:
85
85
  initialDays: 30
86
86
 
87
+ # Optional: base URL of the GitHub instance (default: https://github.com).
88
+ # Set this for GitHub Enterprise. The REST API base URL is derived automatically:
89
+ # github.com → https://api.github.com
90
+ # *.ghe.com (Cloud) → https://api.<host> (e.g. https://api.va.ghe.com)
91
+ # other (GHE Server)→ https://<host>/api/v3
92
+ githubUrl: https://github.com
93
+
87
94
  # Define the environments / branches to track.
88
95
  # Exactly ONE environment may set isProduction: true.
89
96
  environments:
@@ -0,0 +1,105 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "app": {
6
+ "type": "object",
7
+ "properties": {
8
+ "doraMetrics": {
9
+ "type": "object",
10
+ "properties": {
11
+ "debug": {
12
+ "type": "boolean",
13
+ "description": "Enable debug mode — uses mock data instead of the GitHub API. Useful for local development and screenshots.",
14
+ "visibility": "frontend"
15
+ },
16
+ "githubUrl": {
17
+ "type": "string",
18
+ "description": "Base URL of the GitHub instance to use. Defaults to \"https://github.com\". Set this to a GitHub Enterprise URL (e.g. \"https://xyz.ghe.com\") to point all API requests and links at that instance. The REST API base URL is derived automatically: - github.com → https://api.github.com - *.ghe.com (Cloud) → https://api.<host> - other (GHE Server) → https://<host>/api/v3",
19
+ "visibility": "frontend"
20
+ },
21
+ "environments": {
22
+ "type": "array",
23
+ "items": {
24
+ "type": "object",
25
+ "properties": {
26
+ "name": {
27
+ "type": "string",
28
+ "description": "Display name shown in the UI (e.g. \"Production\").",
29
+ "visibility": "frontend"
30
+ },
31
+ "branch": {
32
+ "type": "string",
33
+ "description": "Branch(es) to track for this environment. Accepts: - A single branch name: \"main\" - Comma-separated names: \"main,master\" - A JavaScript regex string: \"^(main|master)$\"\n\nWhen multiple branches match, PRs are fetched from all of them and deduplicated by PR number before metrics are computed.",
34
+ "visibility": "frontend"
35
+ },
36
+ "isProduction": {
37
+ "type": "boolean",
38
+ "description": "Mark this environment as the production environment. Only one environment across the entire list may set this to true. Production environments unlock Change Failure Rate, MTTR, and Hotfix count metrics.",
39
+ "visibility": "frontend"
40
+ },
41
+ "label": {
42
+ "type": "string",
43
+ "description": "Label(s) on PRs that represent production failures (hotfixes). Defaults to \"hotfix\". Accepts the same three formats as `branch`: - Single label: \"hotfix\" - Comma-separated: \"hotfix,fix,bug\" - JavaScript regex: \"^(hotfix|fix)$\"",
44
+ "visibility": "frontend"
45
+ },
46
+ "featureLabel": {
47
+ "type": "string",
48
+ "description": "Label on PRs that represent planned feature deployments.",
49
+ "visibility": "frontend"
50
+ }
51
+ },
52
+ "required": [
53
+ "name",
54
+ "branch"
55
+ ]
56
+ },
57
+ "description": "Environments to track. At most one environment may have isProduction: true.",
58
+ "visibility": "frontend"
59
+ },
60
+ "collection": {
61
+ "type": "object",
62
+ "properties": {
63
+ "initialDays": {
64
+ "type": "number",
65
+ "description": "Number of days of history to fetch on initial load. Defaults to 30.",
66
+ "visibility": "frontend"
67
+ }
68
+ },
69
+ "visibility": "frontend"
70
+ },
71
+ "targets": {
72
+ "type": "object",
73
+ "properties": {
74
+ "deploymentFrequency": {
75
+ "type": "number",
76
+ "description": "Target deployments per week (higher is better). Defaults to 7.",
77
+ "visibility": "frontend"
78
+ },
79
+ "leadTime": {
80
+ "type": "number",
81
+ "description": "Target lead time in hours (lower is better). Defaults to 24.",
82
+ "visibility": "frontend"
83
+ },
84
+ "changeFailureRate": {
85
+ "type": "number",
86
+ "description": "Target change failure rate as a percentage (lower is better). Defaults to 15.",
87
+ "visibility": "frontend"
88
+ },
89
+ "mttr": {
90
+ "type": "number",
91
+ "description": "Target mean time to restore in hours (lower is better). Defaults to 1.",
92
+ "visibility": "frontend"
93
+ }
94
+ },
95
+ "description": "Default performance targets used for rating calculations. These can be overridden per entity via the dora-metrics/targets annotation.",
96
+ "visibility": "frontend"
97
+ }
98
+ },
99
+ "description": "DORA metrics configuration.",
100
+ "visibility": "frontend"
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
@@ -30,6 +30,12 @@ function toPrDetail(pr, durationHours) {
30
30
  function isRegexPattern(pattern) {
31
31
  return /[|^$[\](){}?+*\\]/.test(pattern);
32
32
  }
33
+ function deriveApiBaseUrl(webUrl) {
34
+ const { protocol, host } = new URL(webUrl);
35
+ if (host === "github.com") return "https://api.github.com";
36
+ if (host.endsWith(".ghe.com")) return `${protocol}//api.${host}`;
37
+ return `${protocol}//${host}/api/v3`;
38
+ }
33
39
  function matchesLabel(prLabels, labelPattern) {
34
40
  if (!labelPattern) return false;
35
41
  const names = prLabels.map((l) => l.name);
@@ -40,10 +46,10 @@ function matchesLabel(prLabels, labelPattern) {
40
46
  const patterns = labelPattern.split(",").map((l) => l.trim().toLowerCase()).filter(Boolean);
41
47
  return names.some((n) => patterns.includes(n.toLowerCase()));
42
48
  }
43
- async function resolveBranches(owner, repo, branchPattern, fetchFn) {
49
+ async function resolveBranches(owner, repo, branchPattern, apiBaseUrl, fetchFn) {
44
50
  if (isRegexPattern(branchPattern)) {
45
51
  const repoBranches = await fetchFn(
46
- `https://api.github.com/repos/${owner}/${repo}/branches?per_page=100`
52
+ `${apiBaseUrl}/repos/${owner}/${repo}/branches?per_page=100`
47
53
  );
48
54
  const regex = new RegExp(branchPattern);
49
55
  return repoBranches.map((b) => b.name).filter((name) => regex.test(name));
@@ -60,6 +66,8 @@ class DoraMetricsClient {
60
66
  getConfig() {
61
67
  const appCfg = this.configApi.getConfig("app");
62
68
  const cfg = appCfg.getOptionalConfig("doraMetrics");
69
+ const githubUrl = (cfg?.getOptionalString("githubUrl") ?? "https://github.com").replace(/\/+$/, "");
70
+ const apiBaseUrl = deriveApiBaseUrl(githubUrl);
63
71
  const rawEnvs = cfg?.getOptionalConfigArray("environments") ?? [];
64
72
  const environments = rawEnvs.map((e) => ({
65
73
  name: e.getString("name"),
@@ -81,7 +89,7 @@ class DoraMetricsClient {
81
89
  changeFailureRate: targetsCfg?.getOptionalNumber("changeFailureRate") ?? 15,
82
90
  mttr: targetsCfg?.getOptionalNumber("mttr") ?? 1
83
91
  };
84
- return { environments, initialDays, targets };
92
+ return { environments, initialDays, targets, githubUrl, apiBaseUrl };
85
93
  }
86
94
  getEnvironments() {
87
95
  return this.getConfig().environments;
@@ -89,6 +97,9 @@ class DoraMetricsClient {
89
97
  getDefaultDays() {
90
98
  return this.getConfig().initialDays;
91
99
  }
100
+ getGithubBaseUrl() {
101
+ return this.getConfig().githubUrl;
102
+ }
92
103
  getTargets() {
93
104
  return this.getConfig().targets;
94
105
  }
@@ -110,15 +121,15 @@ class DoraMetricsClient {
110
121
  * Fetch all merged PRs for an environment, resolving branch patterns and
111
122
  * deduplicating across multiple branches by PR number.
112
123
  */
113
- async fetchMergedPRs(owner, repo, env, cutoff, token) {
124
+ async fetchMergedPRs(owner, repo, env, cutoff, token, apiBaseUrl) {
114
125
  const boundFetch = (url) => this.fetchWithAuth(url, token);
115
- const branches = await resolveBranches(owner, repo, env.branch, boundFetch);
126
+ const branches = await resolveBranches(owner, repo, env.branch, apiBaseUrl, boundFetch);
116
127
  const allByNumber = /* @__PURE__ */ new Map();
117
128
  await Promise.all(
118
129
  branches.map(async (branch) => {
119
130
  let hasMore = true;
120
131
  for (let page = 1; hasMore; page++) {
121
- const url = `https://api.github.com/repos/${owner}/${repo}/pulls?state=closed&base=${encodeURIComponent(branch)}&per_page=100&page=${page}&sort=updated&direction=desc`;
132
+ const url = `${apiBaseUrl}/repos/${owner}/${repo}/pulls?state=closed&base=${encodeURIComponent(branch)}&per_page=100&page=${page}&sort=updated&direction=desc`;
122
133
  const prs = await boundFetch(url);
123
134
  for (const pr of prs) {
124
135
  if (pr.merged_at && new Date(pr.merged_at) >= cutoff) {
@@ -136,14 +147,14 @@ class DoraMetricsClient {
136
147
  }
137
148
  async getMetrics(projectSlug, env, days, targetsOverride) {
138
149
  const token = await this.githubAuthApi.getAccessToken("repo");
139
- const { targets: configTargets } = this.getConfig();
150
+ const { targets: configTargets, apiBaseUrl } = this.getConfig();
140
151
  const targets = { ...configTargets, ...targetsOverride };
141
152
  const [owner, repo] = projectSlug.split("/");
142
153
  if (!owner || !repo) {
143
154
  throw new Error(`Invalid project slug "${projectSlug}". Expected "owner/repo".`);
144
155
  }
145
156
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
146
- const mergedPRs = await this.fetchMergedPRs(owner, repo, env, cutoff, token);
157
+ const mergedPRs = await this.fetchMergedPRs(owner, repo, env, cutoff, token, apiBaseUrl);
147
158
  const deploymentsPerWeek = days > 0 ? mergedPRs.length / days * 7 : 0;
148
159
  const deploymentFrequency = {
149
160
  value: Math.round(deploymentsPerWeek * 100) / 100,
@@ -213,10 +224,11 @@ class DoraMetricsClient {
213
224
  }
214
225
  async getHistory(projectSlug, env, days) {
215
226
  const token = await this.githubAuthApi.getAccessToken("repo");
227
+ const { apiBaseUrl } = this.getConfig();
216
228
  const [owner, repo] = projectSlug.split("/");
217
229
  if (!owner || !repo) throw new Error(`Invalid project slug "${projectSlug}".`);
218
230
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
219
- const mergedPRs = await this.fetchMergedPRs(owner, repo, env, cutoff, token);
231
+ const mergedPRs = await this.fetchMergedPRs(owner, repo, env, cutoff, token, apiBaseUrl);
220
232
  const bucketDays = Math.max(1, Math.round(days / 7));
221
233
  const numBuckets = Math.max(2, Math.ceil(days / bucketDays));
222
234
  const buckets = [];
@@ -1 +1 @@
1
- {"version":3,"file":"DoraMetricsClient.esm.js","sources":["../../src/api/DoraMetricsClient.ts"],"sourcesContent":["import { ConfigApi, OAuthApi } from '@backstage/core-plugin-api';\nimport {\n DoraEnvironment,\n DoraHistoryPoint,\n DoraMetrics,\n DoraMetricValue,\n DoraMetricsApi,\n DoraRating,\n DoraTargets,\n PrDetail,\n} from './types';\n\ninterface GitHubPR {\n number: number;\n title: string;\n html_url: string;\n created_at: string;\n updated_at: string;\n merged_at: string | null;\n closed_at: string | null;\n labels: Array<{ name: string }>;\n user: { login: string; avatar_url: string } | null;\n}\n\n/**\n * Compute a DORA rating based on the configured target.\n *\n * lowerIsBetter (lead time, CFR, MTTR, hotfix count):\n * elite = value <= target\n * high = value <= target * 2\n * medium = value <= target * 4\n * low = value > target * 4\n *\n * higherIsBetter (deployment frequency):\n * elite = value >= target\n * high = value >= target * 0.7\n * medium = value >= target * 0.4\n * low = value < target * 0.4\n *\n * Special case — target = 0 (hotfix count ideal = 0):\n * elite=0, high≤2, medium≤5, low>5\n */\nfunction computeRating(value: number, target: number, lowerIsBetter: boolean): DoraRating {\n if (lowerIsBetter) {\n if (target === 0) {\n if (value === 0) return 'elite';\n if (value <= 2) return 'high';\n if (value <= 5) return 'medium';\n return 'low';\n }\n if (value <= target) return 'elite';\n if (value <= target * 2) return 'high';\n if (value <= target * 4) return 'medium';\n return 'low';\n }\n if (value >= target) return 'elite';\n if (value >= target * 0.7) return 'high';\n if (value >= target * 0.4) return 'medium';\n return 'low';\n}\n\nfunction toPrDetail(pr: GitHubPR, durationHours: number): PrDetail {\n return {\n number: pr.number,\n title: pr.title,\n url: pr.html_url,\n durationHours: Math.round(durationHours * 10) / 10,\n mergedAt: pr.merged_at!,\n author: pr.user?.login,\n authorAvatar: pr.user?.avatar_url,\n };\n}\n\n/**\n * Determine whether a pattern string is a regex (vs a plain name or\n * comma-separated list of names). A string is treated as a regex if it\n * contains any regex meta-character: | ^ $ [ ] ( ) { } ? + * \\\n */\nfunction isRegexPattern(pattern: string): boolean {\n return /[|^$[\\](){}?+*\\\\]/.test(pattern);\n}\n\n/**\n * Return true if any of the PR's labels match the label pattern.\n *\n * Supports the same three formats as branch patterns:\n * - Single name: \"hotfix\"\n * - Comma-separated: \"hotfix,fix,bug\"\n * - JavaScript regex: \"^(hotfix|fix)$\"\n */\nfunction matchesLabel(prLabels: Array<{ name: string }>, labelPattern: string): boolean {\n if (!labelPattern) return false;\n const names = prLabels.map(l => l.name);\n if (isRegexPattern(labelPattern)) {\n const regex = new RegExp(labelPattern, 'i');\n return names.some(n => regex.test(n));\n }\n const patterns = labelPattern.split(',').map(l => l.trim().toLowerCase()).filter(Boolean);\n return names.some(n => patterns.includes(n.toLowerCase()));\n}\n\n/**\n * Resolve a branch pattern to a concrete list of branch names.\n *\n * - Single name \"main\" → [\"main\"]\n * - Comma-sep \"main,master\" → [\"main\", \"master\"]\n * - Regex \"^(main|master)$\"→ fetched from the repo's branch list and filtered\n */\nasync function resolveBranches(\n owner: string,\n repo: string,\n branchPattern: string,\n fetchFn: <T>(url: string) => Promise<T>,\n): Promise<string[]> {\n if (isRegexPattern(branchPattern)) {\n const repoBranches = await fetchFn<Array<{ name: string }>>(\n `https://api.github.com/repos/${owner}/${repo}/branches?per_page=100`,\n );\n const regex = new RegExp(branchPattern);\n return repoBranches.map(b => b.name).filter(name => regex.test(name));\n }\n // Comma-separated or single branch name\n return branchPattern.split(',').map(b => b.trim()).filter(Boolean);\n}\n\nexport class DoraMetricsClient implements DoraMetricsApi {\n private readonly githubAuthApi: OAuthApi;\n private readonly configApi: ConfigApi;\n\n constructor(githubAuthApi: OAuthApi, configApi: ConfigApi) {\n this.githubAuthApi = githubAuthApi;\n this.configApi = configApi;\n }\n\n private getConfig() {\n const appCfg = this.configApi.getConfig('app');\n const cfg = appCfg.getOptionalConfig('doraMetrics');\n\n const rawEnvs = cfg?.getOptionalConfigArray('environments') ?? [];\n const environments: DoraEnvironment[] = rawEnvs.map(e => ({\n name: e.getString('name'),\n branch: e.getString('branch'),\n isProduction: e.getOptionalBoolean('isProduction') ?? false,\n label: e.getOptionalString('label') ?? 'hotfix',\n }));\n\n // Validate: at most one isProduction environment\n const prodEnvs = environments.filter(e => e.isProduction);\n if (prodEnvs.length > 1) {\n throw new Error(\n `DORA metrics config error: only one environment may have isProduction: true, ` +\n `but found ${prodEnvs.length}: ${prodEnvs.map(e => e.name).join(', ')}`,\n );\n }\n\n const initialDays =\n cfg?.getOptionalConfig('collection')?.getOptionalNumber('initialDays') ?? 30;\n\n const targetsCfg = cfg?.getOptionalConfig('targets');\n const targets: DoraTargets = {\n deploymentFrequency: targetsCfg?.getOptionalNumber('deploymentFrequency') ?? 7,\n leadTime: targetsCfg?.getOptionalNumber('leadTime') ?? 24,\n changeFailureRate: targetsCfg?.getOptionalNumber('changeFailureRate') ?? 15,\n mttr: targetsCfg?.getOptionalNumber('mttr') ?? 1,\n };\n\n return { environments, initialDays, targets };\n }\n\n getEnvironments(): DoraEnvironment[] {\n return this.getConfig().environments;\n }\n\n getDefaultDays(): number {\n return this.getConfig().initialDays;\n }\n\n getTargets(): DoraTargets {\n return this.getConfig().targets;\n }\n\n private async fetchWithAuth<T>(url: string, token: string): Promise<T> {\n const response = await fetch(url, {\n headers: {\n Authorization: `token ${token}`,\n Accept: 'application/vnd.github.v3+json',\n },\n });\n\n if (!response.ok) {\n throw new Error(\n `GitHub API request failed: ${response.status} ${response.statusText} — ${url}`,\n );\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Fetch all merged PRs for an environment, resolving branch patterns and\n * deduplicating across multiple branches by PR number.\n */\n private async fetchMergedPRs(\n owner: string,\n repo: string,\n env: DoraEnvironment,\n cutoff: Date,\n token: string,\n ): Promise<GitHubPR[]> {\n const boundFetch = <T>(url: string) => this.fetchWithAuth<T>(url, token);\n\n const branches = await resolveBranches(owner, repo, env.branch, boundFetch);\n\n const allByNumber = new Map<number, GitHubPR>();\n\n await Promise.all(\n branches.map(async branch => {\n let hasMore = true;\n for (let page = 1; hasMore; page++) {\n const url =\n `https://api.github.com/repos/${owner}/${repo}/pulls` +\n `?state=closed&base=${encodeURIComponent(branch)}&per_page=100&page=${page}&sort=updated&direction=desc`;\n const prs = await boundFetch<GitHubPR[]>(url);\n\n for (const pr of prs) {\n if (pr.merged_at && new Date(pr.merged_at) >= cutoff) {\n // Keep first occurrence — branches may share PRs\n if (!allByNumber.has(pr.number)) {\n allByNumber.set(pr.number, pr);\n }\n }\n }\n\n // PRs are sorted by updated_at desc. Since merged_at <= updated_at,\n // once the oldest updated_at on a page is before cutoff, no subsequent\n // page can contain a PR merged within the window — stop early.\n const oldestUpdatedMs = prs.length > 0\n ? Math.min(...prs.map(pr => new Date(pr.updated_at).getTime()))\n : 0;\n hasMore = prs.length >= 100 && oldestUpdatedMs >= cutoff.getTime();\n }\n }),\n );\n\n return Array.from(allByNumber.values());\n }\n\n async getMetrics(\n projectSlug: string,\n env: DoraEnvironment,\n days: number,\n targetsOverride?: Partial<DoraTargets>,\n ): Promise<DoraMetrics> {\n const token = await this.githubAuthApi.getAccessToken('repo');\n const { targets: configTargets } = this.getConfig();\n const targets: DoraTargets = { ...configTargets, ...targetsOverride };\n\n const [owner, repo] = projectSlug.split('/');\n if (!owner || !repo) {\n throw new Error(`Invalid project slug \"${projectSlug}\". Expected \"owner/repo\".`);\n }\n\n const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);\n const mergedPRs = await this.fetchMergedPRs(owner, repo, env, cutoff, token);\n\n // --- Deployment Frequency ---\n const deploymentsPerWeek = days > 0 ? (mergedPRs.length / days) * 7 : 0;\n\n const deploymentFrequency: DoraMetricValue = {\n value: Math.round(deploymentsPerWeek * 100) / 100,\n unit: 'per week',\n rating: computeRating(deploymentsPerWeek, targets.deploymentFrequency, false),\n target: targets.deploymentFrequency,\n // All merged PRs sorted newest-first — used in expanded view date table\n slowestPRs: [...mergedPRs]\n .sort((a, b) => new Date(b.merged_at!).getTime() - new Date(a.merged_at!).getTime())\n .map(pr => toPrDetail(pr,\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) / (1000 * 60 * 60)\n )),\n };\n\n // --- Lead Time ---\n const prsWithLeadTime = mergedPRs\n .map(pr => ({\n pr,\n durationHours:\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) /\n (1000 * 60 * 60),\n }))\n .sort((a, b) => b.durationHours - a.durationHours);\n\n const avgLeadTimeHours =\n prsWithLeadTime.length > 0\n ? prsWithLeadTime.reduce((sum, p) => sum + p.durationHours, 0) / prsWithLeadTime.length\n : 0;\n\n const leadTime: DoraMetricValue = {\n value: Math.round(avgLeadTimeHours * 10) / 10,\n unit: 'hours',\n rating: computeRating(avgLeadTimeHours, targets.leadTime, true),\n target: targets.leadTime,\n slowestPRs: prsWithLeadTime\n .slice(0, 10)\n .map(p => toPrDetail(p.pr, p.durationHours)),\n };\n\n // --- Production-only metrics ---\n if (!env.isProduction) {\n return {\n deploymentFrequency,\n leadTime,\n changeFailureRate: null,\n mttr: null,\n numberOfHotfixes: null,\n };\n }\n\n const hotfixPRs = mergedPRs.filter(pr =>\n matchesLabel(pr.labels ?? [], env.label),\n );\n\n // Number of Hotfixes — sorted newest-first for the expanded list\n const numberOfHotfixes: DoraMetricValue = {\n value: hotfixPRs.length,\n unit: 'PRs',\n rating: computeRating(hotfixPRs.length, 0, true),\n target: 0,\n slowestPRs: [...hotfixPRs]\n .sort((a, b) => new Date(b.merged_at!).getTime() - new Date(a.merged_at!).getTime())\n .map(pr => toPrDetail(pr,\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) / (1000 * 60 * 60)\n )),\n };\n\n // Change Failure Rate = fix PRs / all merged PRs (DORA standard: always 0–100%)\n const changeFailureRatePct =\n mergedPRs.length > 0 ? (hotfixPRs.length / mergedPRs.length) * 100 : 0;\n\n const changeFailureRate: DoraMetricValue = {\n value: Math.round(changeFailureRatePct * 10) / 10,\n unit: '%',\n rating: computeRating(changeFailureRatePct, targets.changeFailureRate, true),\n target: targets.changeFailureRate,\n };\n\n // MTTR\n const hotfixWithDuration = hotfixPRs\n .map(pr => ({\n pr,\n durationHours:\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) /\n (1000 * 60 * 60),\n }))\n .sort((a, b) => b.durationHours - a.durationHours);\n\n const avgMttrHours =\n hotfixWithDuration.length > 0\n ? hotfixWithDuration.reduce((sum, p) => sum + p.durationHours, 0) /\n hotfixWithDuration.length\n : 0;\n\n const mttr: DoraMetricValue = {\n value: Math.round(avgMttrHours * 10) / 10,\n unit: 'hours',\n rating: computeRating(avgMttrHours, targets.mttr, true),\n target: targets.mttr,\n slowestPRs: hotfixWithDuration\n .slice(0, 10)\n .map(p => toPrDetail(p.pr, p.durationHours)),\n };\n\n return { deploymentFrequency, leadTime, changeFailureRate, mttr, numberOfHotfixes };\n }\n\n async getHistory(\n projectSlug: string,\n env: DoraEnvironment,\n days: number,\n ): Promise<DoraHistoryPoint[]> {\n const token = await this.githubAuthApi.getAccessToken('repo');\n const [owner, repo] = projectSlug.split('/');\n if (!owner || !repo) throw new Error(`Invalid project slug \"${projectSlug}\".`);\n\n const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);\n const mergedPRs = await this.fetchMergedPRs(owner, repo, env, cutoff, token);\n\n // Scale bucket size so we always get ~7 data points regardless of date range\n const bucketDays = Math.max(1, Math.round(days / 7));\n const numBuckets = Math.max(2, Math.ceil(days / bucketDays));\n const buckets: DoraHistoryPoint[] = [];\n\n for (let w = numBuckets - 1; w >= 0; w--) {\n const weekEnd = new Date(Date.now() - w * bucketDays * 24 * 60 * 60 * 1000);\n const weekStart = new Date(weekEnd.getTime() - bucketDays * 24 * 60 * 60 * 1000);\n\n const weekPRs = mergedPRs.filter(pr => {\n const t = new Date(pr.merged_at!).getTime();\n return t >= weekStart.getTime() && t < weekEnd.getTime();\n });\n\n const leadTimes = weekPRs.map(pr =>\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) / (1000 * 60 * 60),\n );\n const avgLeadTime =\n leadTimes.length > 0 ? leadTimes.reduce((a, b) => a + b, 0) / leadTimes.length : 0;\n\n let changeFailureRate: number | undefined;\n let mttrHours: number | undefined;\n\n if (env.isProduction) {\n const hotfixPRs = weekPRs.filter(pr => matchesLabel(pr.labels, env.label));\n changeFailureRate =\n weekPRs.length > 0 ? Math.round((hotfixPRs.length / weekPRs.length) * 1000) / 10 : 0;\n const hotfixDurations = hotfixPRs.map(pr =>\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) / (1000 * 60 * 60),\n );\n mttrHours =\n hotfixDurations.length > 0\n ? Math.round((hotfixDurations.reduce((a, b) => a + b, 0) / hotfixDurations.length) * 10) / 10\n : 0;\n }\n\n // Label with bucket end (capped at now) so all views show \"today\" as the last label.\n const labelDate = new Date(Math.min(weekEnd.getTime(), Date.now()));\n buckets.push({\n weekLabel: labelDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),\n bucketMidMs: Math.round((weekStart.getTime() + weekEnd.getTime()) / 2),\n deploymentCount: weekPRs.length,\n leadTimeHours: Math.round(avgLeadTime * 10) / 10,\n changeFailureRate,\n mttrHours,\n });\n }\n\n return buckets;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DoraMetricsClient.esm.js","sources":["../../src/api/DoraMetricsClient.ts"],"sourcesContent":["import { ConfigApi, OAuthApi } from '@backstage/core-plugin-api';\nimport {\n DoraEnvironment,\n DoraHistoryPoint,\n DoraMetrics,\n DoraMetricValue,\n DoraMetricsApi,\n DoraRating,\n DoraTargets,\n PrDetail,\n} from './types';\n\ninterface GitHubPR {\n number: number;\n title: string;\n html_url: string;\n created_at: string;\n updated_at: string;\n merged_at: string | null;\n closed_at: string | null;\n labels: Array<{ name: string }>;\n user: { login: string; avatar_url: string } | null;\n}\n\n/**\n * Compute a DORA rating based on the configured target.\n *\n * lowerIsBetter (lead time, CFR, MTTR, hotfix count):\n * elite = value <= target\n * high = value <= target * 2\n * medium = value <= target * 4\n * low = value > target * 4\n *\n * higherIsBetter (deployment frequency):\n * elite = value >= target\n * high = value >= target * 0.7\n * medium = value >= target * 0.4\n * low = value < target * 0.4\n *\n * Special case — target = 0 (hotfix count ideal = 0):\n * elite=0, high≤2, medium≤5, low>5\n */\nfunction computeRating(value: number, target: number, lowerIsBetter: boolean): DoraRating {\n if (lowerIsBetter) {\n if (target === 0) {\n if (value === 0) return 'elite';\n if (value <= 2) return 'high';\n if (value <= 5) return 'medium';\n return 'low';\n }\n if (value <= target) return 'elite';\n if (value <= target * 2) return 'high';\n if (value <= target * 4) return 'medium';\n return 'low';\n }\n if (value >= target) return 'elite';\n if (value >= target * 0.7) return 'high';\n if (value >= target * 0.4) return 'medium';\n return 'low';\n}\n\nfunction toPrDetail(pr: GitHubPR, durationHours: number): PrDetail {\n return {\n number: pr.number,\n title: pr.title,\n url: pr.html_url,\n durationHours: Math.round(durationHours * 10) / 10,\n mergedAt: pr.merged_at!,\n author: pr.user?.login,\n authorAvatar: pr.user?.avatar_url,\n };\n}\n\n/**\n * Determine whether a pattern string is a regex (vs a plain name or\n * comma-separated list of names). A string is treated as a regex if it\n * contains any regex meta-character: | ^ $ [ ] ( ) { } ? + * \\\n */\nfunction isRegexPattern(pattern: string): boolean {\n return /[|^$[\\](){}?+*\\\\]/.test(pattern);\n}\n\n/**\n * Derive the GitHub REST API base URL from a web base URL.\n * - github.com → https://api.github.com\n * - *.ghe.com (Cloud) → https://api.<host>\n * - other (GHE Server) → https://<host>/api/v3\n */\nfunction deriveApiBaseUrl(webUrl: string): string {\n const { protocol, host } = new URL(webUrl);\n if (host === 'github.com') return 'https://api.github.com';\n if (host.endsWith('.ghe.com')) return `${protocol}//api.${host}`;\n return `${protocol}//${host}/api/v3`;\n}\n\n/**\n * Return true if any of the PR's labels match the label pattern.\n *\n * Supports the same three formats as branch patterns:\n * - Single name: \"hotfix\"\n * - Comma-separated: \"hotfix,fix,bug\"\n * - JavaScript regex: \"^(hotfix|fix)$\"\n */\nfunction matchesLabel(prLabels: Array<{ name: string }>, labelPattern: string): boolean {\n if (!labelPattern) return false;\n const names = prLabels.map(l => l.name);\n if (isRegexPattern(labelPattern)) {\n const regex = new RegExp(labelPattern, 'i');\n return names.some(n => regex.test(n));\n }\n const patterns = labelPattern.split(',').map(l => l.trim().toLowerCase()).filter(Boolean);\n return names.some(n => patterns.includes(n.toLowerCase()));\n}\n\n/**\n * Resolve a branch pattern to a concrete list of branch names.\n *\n * - Single name \"main\" → [\"main\"]\n * - Comma-sep \"main,master\" → [\"main\", \"master\"]\n * - Regex \"^(main|master)$\"→ fetched from the repo's branch list and filtered\n */\nasync function resolveBranches(\n owner: string,\n repo: string,\n branchPattern: string,\n apiBaseUrl: string,\n fetchFn: <T>(url: string) => Promise<T>,\n): Promise<string[]> {\n if (isRegexPattern(branchPattern)) {\n const repoBranches = await fetchFn<Array<{ name: string }>>(\n `${apiBaseUrl}/repos/${owner}/${repo}/branches?per_page=100`,\n );\n const regex = new RegExp(branchPattern);\n return repoBranches.map(b => b.name).filter(name => regex.test(name));\n }\n // Comma-separated or single branch name\n return branchPattern.split(',').map(b => b.trim()).filter(Boolean);\n}\n\nexport class DoraMetricsClient implements DoraMetricsApi {\n private readonly githubAuthApi: OAuthApi;\n private readonly configApi: ConfigApi;\n\n constructor(githubAuthApi: OAuthApi, configApi: ConfigApi) {\n this.githubAuthApi = githubAuthApi;\n this.configApi = configApi;\n }\n\n private getConfig() {\n const appCfg = this.configApi.getConfig('app');\n const cfg = appCfg.getOptionalConfig('doraMetrics');\n\n const githubUrl = (cfg?.getOptionalString('githubUrl') ?? 'https://github.com').replace(/\\/+$/, '');\n const apiBaseUrl = deriveApiBaseUrl(githubUrl);\n\n const rawEnvs = cfg?.getOptionalConfigArray('environments') ?? [];\n const environments: DoraEnvironment[] = rawEnvs.map(e => ({\n name: e.getString('name'),\n branch: e.getString('branch'),\n isProduction: e.getOptionalBoolean('isProduction') ?? false,\n label: e.getOptionalString('label') ?? 'hotfix',\n }));\n\n // Validate: at most one isProduction environment\n const prodEnvs = environments.filter(e => e.isProduction);\n if (prodEnvs.length > 1) {\n throw new Error(\n `DORA metrics config error: only one environment may have isProduction: true, ` +\n `but found ${prodEnvs.length}: ${prodEnvs.map(e => e.name).join(', ')}`,\n );\n }\n\n const initialDays =\n cfg?.getOptionalConfig('collection')?.getOptionalNumber('initialDays') ?? 30;\n\n const targetsCfg = cfg?.getOptionalConfig('targets');\n const targets: DoraTargets = {\n deploymentFrequency: targetsCfg?.getOptionalNumber('deploymentFrequency') ?? 7,\n leadTime: targetsCfg?.getOptionalNumber('leadTime') ?? 24,\n changeFailureRate: targetsCfg?.getOptionalNumber('changeFailureRate') ?? 15,\n mttr: targetsCfg?.getOptionalNumber('mttr') ?? 1,\n };\n\n return { environments, initialDays, targets, githubUrl, apiBaseUrl };\n }\n\n getEnvironments(): DoraEnvironment[] {\n return this.getConfig().environments;\n }\n\n getDefaultDays(): number {\n return this.getConfig().initialDays;\n }\n\n\n getGithubBaseUrl(): string {\n return this.getConfig().githubUrl;\n }\n getTargets(): DoraTargets {\n return this.getConfig().targets;\n }\n\n private async fetchWithAuth<T>(url: string, token: string): Promise<T> {\n const response = await fetch(url, {\n headers: {\n Authorization: `token ${token}`,\n Accept: 'application/vnd.github.v3+json',\n },\n });\n\n if (!response.ok) {\n throw new Error(\n `GitHub API request failed: ${response.status} ${response.statusText} — ${url}`,\n );\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Fetch all merged PRs for an environment, resolving branch patterns and\n * deduplicating across multiple branches by PR number.\n */\n private async fetchMergedPRs(\n owner: string,\n repo: string,\n env: DoraEnvironment,\n cutoff: Date,\n token: string,\n apiBaseUrl: string,\n ): Promise<GitHubPR[]> {\n const boundFetch = <T>(url: string) => this.fetchWithAuth<T>(url, token);\n\n const branches = await resolveBranches(owner, repo, env.branch, apiBaseUrl, boundFetch);\n\n const allByNumber = new Map<number, GitHubPR>();\n\n await Promise.all(\n branches.map(async branch => {\n let hasMore = true;\n for (let page = 1; hasMore; page++) {\n const url =\n `${apiBaseUrl}/repos/${owner}/${repo}/pulls` +\n `?state=closed&base=${encodeURIComponent(branch)}&per_page=100&page=${page}&sort=updated&direction=desc`;\n const prs = await boundFetch<GitHubPR[]>(url);\n\n for (const pr of prs) {\n if (pr.merged_at && new Date(pr.merged_at) >= cutoff) {\n // Keep first occurrence — branches may share PRs\n if (!allByNumber.has(pr.number)) {\n allByNumber.set(pr.number, pr);\n }\n }\n }\n\n // PRs are sorted by updated_at desc. Since merged_at <= updated_at,\n // once the oldest updated_at on a page is before cutoff, no subsequent\n // page can contain a PR merged within the window — stop early.\n const oldestUpdatedMs = prs.length > 0\n ? Math.min(...prs.map(pr => new Date(pr.updated_at).getTime()))\n : 0;\n hasMore = prs.length >= 100 && oldestUpdatedMs >= cutoff.getTime();\n }\n }),\n );\n\n return Array.from(allByNumber.values());\n }\n\n async getMetrics(\n projectSlug: string,\n env: DoraEnvironment,\n days: number,\n targetsOverride?: Partial<DoraTargets>,\n ): Promise<DoraMetrics> {\n const token = await this.githubAuthApi.getAccessToken('repo');\n const { targets: configTargets, apiBaseUrl } = this.getConfig();\n const targets: DoraTargets = { ...configTargets, ...targetsOverride };\n\n const [owner, repo] = projectSlug.split('/');\n if (!owner || !repo) {\n throw new Error(`Invalid project slug \"${projectSlug}\". Expected \"owner/repo\".`);\n }\n\n const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);\n const mergedPRs = await this.fetchMergedPRs(owner, repo, env, cutoff, token, apiBaseUrl);\n\n // --- Deployment Frequency ---\n const deploymentsPerWeek = days > 0 ? (mergedPRs.length / days) * 7 : 0;\n\n const deploymentFrequency: DoraMetricValue = {\n value: Math.round(deploymentsPerWeek * 100) / 100,\n unit: 'per week',\n rating: computeRating(deploymentsPerWeek, targets.deploymentFrequency, false),\n target: targets.deploymentFrequency,\n // All merged PRs sorted newest-first — used in expanded view date table\n slowestPRs: [...mergedPRs]\n .sort((a, b) => new Date(b.merged_at!).getTime() - new Date(a.merged_at!).getTime())\n .map(pr => toPrDetail(pr,\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) / (1000 * 60 * 60)\n )),\n };\n\n // --- Lead Time ---\n const prsWithLeadTime = mergedPRs\n .map(pr => ({\n pr,\n durationHours:\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) /\n (1000 * 60 * 60),\n }))\n .sort((a, b) => b.durationHours - a.durationHours);\n\n const avgLeadTimeHours =\n prsWithLeadTime.length > 0\n ? prsWithLeadTime.reduce((sum, p) => sum + p.durationHours, 0) / prsWithLeadTime.length\n : 0;\n\n const leadTime: DoraMetricValue = {\n value: Math.round(avgLeadTimeHours * 10) / 10,\n unit: 'hours',\n rating: computeRating(avgLeadTimeHours, targets.leadTime, true),\n target: targets.leadTime,\n slowestPRs: prsWithLeadTime\n .slice(0, 10)\n .map(p => toPrDetail(p.pr, p.durationHours)),\n };\n\n // --- Production-only metrics ---\n if (!env.isProduction) {\n return {\n deploymentFrequency,\n leadTime,\n changeFailureRate: null,\n mttr: null,\n numberOfHotfixes: null,\n };\n }\n\n const hotfixPRs = mergedPRs.filter(pr =>\n matchesLabel(pr.labels ?? [], env.label),\n );\n\n // Number of Hotfixes — sorted newest-first for the expanded list\n const numberOfHotfixes: DoraMetricValue = {\n value: hotfixPRs.length,\n unit: 'PRs',\n rating: computeRating(hotfixPRs.length, 0, true),\n target: 0,\n slowestPRs: [...hotfixPRs]\n .sort((a, b) => new Date(b.merged_at!).getTime() - new Date(a.merged_at!).getTime())\n .map(pr => toPrDetail(pr,\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) / (1000 * 60 * 60)\n )),\n };\n\n // Change Failure Rate = fix PRs / all merged PRs (DORA standard: always 0–100%)\n const changeFailureRatePct =\n mergedPRs.length > 0 ? (hotfixPRs.length / mergedPRs.length) * 100 : 0;\n\n const changeFailureRate: DoraMetricValue = {\n value: Math.round(changeFailureRatePct * 10) / 10,\n unit: '%',\n rating: computeRating(changeFailureRatePct, targets.changeFailureRate, true),\n target: targets.changeFailureRate,\n };\n\n // MTTR\n const hotfixWithDuration = hotfixPRs\n .map(pr => ({\n pr,\n durationHours:\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) /\n (1000 * 60 * 60),\n }))\n .sort((a, b) => b.durationHours - a.durationHours);\n\n const avgMttrHours =\n hotfixWithDuration.length > 0\n ? hotfixWithDuration.reduce((sum, p) => sum + p.durationHours, 0) /\n hotfixWithDuration.length\n : 0;\n\n const mttr: DoraMetricValue = {\n value: Math.round(avgMttrHours * 10) / 10,\n unit: 'hours',\n rating: computeRating(avgMttrHours, targets.mttr, true),\n target: targets.mttr,\n slowestPRs: hotfixWithDuration\n .slice(0, 10)\n .map(p => toPrDetail(p.pr, p.durationHours)),\n };\n\n return { deploymentFrequency, leadTime, changeFailureRate, mttr, numberOfHotfixes };\n }\n\n async getHistory(\n projectSlug: string,\n env: DoraEnvironment,\n days: number,\n ): Promise<DoraHistoryPoint[]> {\n const token = await this.githubAuthApi.getAccessToken('repo');\n const { apiBaseUrl } = this.getConfig();\n const [owner, repo] = projectSlug.split('/');\n if (!owner || !repo) throw new Error(`Invalid project slug \"${projectSlug}\".`);\n\n const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);\n const mergedPRs = await this.fetchMergedPRs(owner, repo, env, cutoff, token, apiBaseUrl);\n\n // Scale bucket size so we always get ~7 data points regardless of date range\n const bucketDays = Math.max(1, Math.round(days / 7));\n const numBuckets = Math.max(2, Math.ceil(days / bucketDays));\n const buckets: DoraHistoryPoint[] = [];\n\n for (let w = numBuckets - 1; w >= 0; w--) {\n const weekEnd = new Date(Date.now() - w * bucketDays * 24 * 60 * 60 * 1000);\n const weekStart = new Date(weekEnd.getTime() - bucketDays * 24 * 60 * 60 * 1000);\n\n const weekPRs = mergedPRs.filter(pr => {\n const t = new Date(pr.merged_at!).getTime();\n return t >= weekStart.getTime() && t < weekEnd.getTime();\n });\n\n const leadTimes = weekPRs.map(pr =>\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) / (1000 * 60 * 60),\n );\n const avgLeadTime =\n leadTimes.length > 0 ? leadTimes.reduce((a, b) => a + b, 0) / leadTimes.length : 0;\n\n let changeFailureRate: number | undefined;\n let mttrHours: number | undefined;\n\n if (env.isProduction) {\n const hotfixPRs = weekPRs.filter(pr => matchesLabel(pr.labels, env.label));\n changeFailureRate =\n weekPRs.length > 0 ? Math.round((hotfixPRs.length / weekPRs.length) * 1000) / 10 : 0;\n const hotfixDurations = hotfixPRs.map(pr =>\n (new Date(pr.merged_at!).getTime() - new Date(pr.created_at).getTime()) / (1000 * 60 * 60),\n );\n mttrHours =\n hotfixDurations.length > 0\n ? Math.round((hotfixDurations.reduce((a, b) => a + b, 0) / hotfixDurations.length) * 10) / 10\n : 0;\n }\n\n // Label with bucket end (capped at now) so all views show \"today\" as the last label.\n const labelDate = new Date(Math.min(weekEnd.getTime(), Date.now()));\n buckets.push({\n weekLabel: labelDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),\n bucketMidMs: Math.round((weekStart.getTime() + weekEnd.getTime()) / 2),\n deploymentCount: weekPRs.length,\n leadTimeHours: Math.round(avgLeadTime * 10) / 10,\n changeFailureRate,\n mttrHours,\n });\n }\n\n return buckets;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -36,6 +36,9 @@ class MockDoraMetricsClient {
36
36
  getTargets() {
37
37
  return MOCK_TARGETS;
38
38
  }
39
+ getGithubBaseUrl() {
40
+ return "https://github.com";
41
+ }
39
42
  async getMetrics(_projectSlug, env, _days, _targetsOverride) {
40
43
  await new Promise((r) => setTimeout(r, 600));
41
44
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"MockDoraMetricsClient.esm.js","sources":["../../src/api/MockDoraMetricsClient.ts"],"sourcesContent":["import { DoraEnvironment, DoraHistoryPoint, DoraMetrics, DoraMetricsApi, DoraTargets } from './types';\n\nconst MOCK_ENVIRONMENTS: DoraEnvironment[] = [\n { name: 'Production', branch: 'main', isProduction: true, label: 'hotfix' },\n { name: 'Staging', branch: 'staging', isProduction: false, label: '' },\n];\n\nconst MOCK_TARGETS: DoraTargets = {\n deploymentFrequency: 7,\n leadTime: 24,\n changeFailureRate: 15,\n mttr: 1,\n};\n\nconst MOCK_PRS = [\n { number: 142, title: 'feat: redesign dashboard layout', url: 'https://github.com/example/repo/pull/142', durationHours: 31.2, mergedAt: '2026-03-15T14:22:00Z', author: 'alice', authorAvatar: 'https://avatars.githubusercontent.com/u/1?v=4' },\n { number: 138, title: 'fix: resolve memory leak in worker thread', url: 'https://github.com/example/repo/pull/138', durationHours: 22.5, mergedAt: '2026-03-12T09:10:00Z', author: 'bob', authorAvatar: 'https://avatars.githubusercontent.com/u/2?v=4' },\n { number: 135, title: 'feat: add export to CSV functionality', url: 'https://github.com/example/repo/pull/135', durationHours: 18.8, mergedAt: '2026-03-10T16:45:00Z', author: 'carol', authorAvatar: 'https://avatars.githubusercontent.com/u/3?v=4' },\n { number: 131, title: 'chore: upgrade dependencies to latest', url: 'https://github.com/example/repo/pull/131', durationHours: 14.1, mergedAt: '2026-03-08T11:30:00Z', author: 'dave', authorAvatar: 'https://avatars.githubusercontent.com/u/4?v=4' },\n { number: 128, title: 'feat: implement rate limiting middleware', url: 'https://github.com/example/repo/pull/128', durationHours: 9.6, mergedAt: '2026-03-05T13:20:00Z', author: 'alice', authorAvatar: 'https://avatars.githubusercontent.com/u/1?v=4' },\n];\n\nconst MOCK_HOTFIX_PRS = [\n { number: 140, title: 'hotfix: fix broken auth token refresh', url: 'https://github.com/example/repo/pull/140', durationHours: 1.2, mergedAt: '2026-03-14T03:45:00Z', author: 'bob', authorAvatar: 'https://avatars.githubusercontent.com/u/2?v=4' },\n { number: 133, title: 'hotfix: patch SQL injection in search query', url: 'https://github.com/example/repo/pull/133', durationHours: 0.8, mergedAt: '2026-03-09T22:10:00Z', author: 'carol', authorAvatar: 'https://avatars.githubusercontent.com/u/3?v=4' },\n];\n\nconst MOCK_HISTORY: DoraHistoryPoint[] = [\n { weekLabel: 'Feb 17', bucketMidMs: new Date('2026-02-20').getTime(), deploymentCount: 3, leadTimeHours: 24.1, changeFailureRate: 0, mttrHours: 0 },\n { weekLabel: 'Feb 24', bucketMidMs: new Date('2026-02-27').getTime(), deploymentCount: 5, leadTimeHours: 19.8, changeFailureRate: 20, mttrHours: 1.2 },\n { weekLabel: 'Mar 3', bucketMidMs: new Date('2026-03-06').getTime(), deploymentCount: 4, leadTimeHours: 22.3, changeFailureRate: 0, mttrHours: 0 },\n { weekLabel: 'Mar 10', bucketMidMs: new Date('2026-03-13').getTime(), deploymentCount: 6, leadTimeHours: 16.5, changeFailureRate: 16.7, mttrHours: 0.9 },\n { weekLabel: 'Mar 17', bucketMidMs: new Date('2026-03-20').getTime(), deploymentCount: 5, leadTimeHours: 18.4, changeFailureRate: 0, mttrHours: 0 },\n];\n\nexport class MockDoraMetricsClient implements DoraMetricsApi {\n getEnvironments(): DoraEnvironment[] {\n return MOCK_ENVIRONMENTS;\n }\n\n getDefaultDays(): number {\n return 30;\n }\n\n getTargets(): DoraTargets {\n return MOCK_TARGETS;\n }\n\n async getMetrics(\n _projectSlug: string,\n env: DoraEnvironment,\n _days: number,\n _targetsOverride?: Partial<DoraTargets>,\n ): Promise<DoraMetrics> {\n await new Promise(r => setTimeout(r, 600)); // simulate network delay\n\n return {\n deploymentFrequency: {\n value: 4.2,\n unit: 'per week',\n rating: 'high',\n target: 7,\n slowestPRs: MOCK_PRS,\n },\n leadTime: {\n value: 18.4,\n unit: 'hours',\n rating: 'elite',\n target: 24,\n slowestPRs: MOCK_PRS,\n },\n changeFailureRate: env.isProduction\n ? { value: 8.3, unit: '%', rating: 'high', target: 15 }\n : null,\n mttr: env.isProduction\n ? { value: 0.8, unit: 'hours', rating: 'elite', target: 1, slowestPRs: MOCK_HOTFIX_PRS }\n : null,\n numberOfHotfixes: env.isProduction\n ? { value: 2, unit: 'PRs', rating: 'high', target: 0, slowestPRs: MOCK_HOTFIX_PRS }\n : null,\n };\n }\n\n async getHistory(\n _projectSlug: string,\n env: DoraEnvironment,\n _days: number,\n ): Promise<DoraHistoryPoint[]> {\n await new Promise(r => setTimeout(r, 400));\n\n return MOCK_HISTORY.map(point => ({\n ...point,\n changeFailureRate: env.isProduction ? point.changeFailureRate : undefined,\n mttrHours: env.isProduction ? point.mttrHours : undefined,\n }));\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MockDoraMetricsClient.esm.js","sources":["../../src/api/MockDoraMetricsClient.ts"],"sourcesContent":["import { DoraEnvironment, DoraHistoryPoint, DoraMetrics, DoraMetricsApi, DoraTargets } from './types';\n\nconst MOCK_ENVIRONMENTS: DoraEnvironment[] = [\n { name: 'Production', branch: 'main', isProduction: true, label: 'hotfix' },\n { name: 'Staging', branch: 'staging', isProduction: false, label: '' },\n];\n\nconst MOCK_TARGETS: DoraTargets = {\n deploymentFrequency: 7,\n leadTime: 24,\n changeFailureRate: 15,\n mttr: 1,\n};\n\nconst MOCK_PRS = [\n { number: 142, title: 'feat: redesign dashboard layout', url: 'https://github.com/example/repo/pull/142', durationHours: 31.2, mergedAt: '2026-03-15T14:22:00Z', author: 'alice', authorAvatar: 'https://avatars.githubusercontent.com/u/1?v=4' },\n { number: 138, title: 'fix: resolve memory leak in worker thread', url: 'https://github.com/example/repo/pull/138', durationHours: 22.5, mergedAt: '2026-03-12T09:10:00Z', author: 'bob', authorAvatar: 'https://avatars.githubusercontent.com/u/2?v=4' },\n { number: 135, title: 'feat: add export to CSV functionality', url: 'https://github.com/example/repo/pull/135', durationHours: 18.8, mergedAt: '2026-03-10T16:45:00Z', author: 'carol', authorAvatar: 'https://avatars.githubusercontent.com/u/3?v=4' },\n { number: 131, title: 'chore: upgrade dependencies to latest', url: 'https://github.com/example/repo/pull/131', durationHours: 14.1, mergedAt: '2026-03-08T11:30:00Z', author: 'dave', authorAvatar: 'https://avatars.githubusercontent.com/u/4?v=4' },\n { number: 128, title: 'feat: implement rate limiting middleware', url: 'https://github.com/example/repo/pull/128', durationHours: 9.6, mergedAt: '2026-03-05T13:20:00Z', author: 'alice', authorAvatar: 'https://avatars.githubusercontent.com/u/1?v=4' },\n];\n\nconst MOCK_HOTFIX_PRS = [\n { number: 140, title: 'hotfix: fix broken auth token refresh', url: 'https://github.com/example/repo/pull/140', durationHours: 1.2, mergedAt: '2026-03-14T03:45:00Z', author: 'bob', authorAvatar: 'https://avatars.githubusercontent.com/u/2?v=4' },\n { number: 133, title: 'hotfix: patch SQL injection in search query', url: 'https://github.com/example/repo/pull/133', durationHours: 0.8, mergedAt: '2026-03-09T22:10:00Z', author: 'carol', authorAvatar: 'https://avatars.githubusercontent.com/u/3?v=4' },\n];\n\nconst MOCK_HISTORY: DoraHistoryPoint[] = [\n { weekLabel: 'Feb 17', bucketMidMs: new Date('2026-02-20').getTime(), deploymentCount: 3, leadTimeHours: 24.1, changeFailureRate: 0, mttrHours: 0 },\n { weekLabel: 'Feb 24', bucketMidMs: new Date('2026-02-27').getTime(), deploymentCount: 5, leadTimeHours: 19.8, changeFailureRate: 20, mttrHours: 1.2 },\n { weekLabel: 'Mar 3', bucketMidMs: new Date('2026-03-06').getTime(), deploymentCount: 4, leadTimeHours: 22.3, changeFailureRate: 0, mttrHours: 0 },\n { weekLabel: 'Mar 10', bucketMidMs: new Date('2026-03-13').getTime(), deploymentCount: 6, leadTimeHours: 16.5, changeFailureRate: 16.7, mttrHours: 0.9 },\n { weekLabel: 'Mar 17', bucketMidMs: new Date('2026-03-20').getTime(), deploymentCount: 5, leadTimeHours: 18.4, changeFailureRate: 0, mttrHours: 0 },\n];\n\nexport class MockDoraMetricsClient implements DoraMetricsApi {\n getEnvironments(): DoraEnvironment[] {\n return MOCK_ENVIRONMENTS;\n }\n\n getDefaultDays(): number {\n return 30;\n }\n\n getTargets(): DoraTargets {\n return MOCK_TARGETS;\n }\n\n getGithubBaseUrl(): string {\n return 'https://github.com';\n }\n\n async getMetrics(\n _projectSlug: string,\n env: DoraEnvironment,\n _days: number,\n _targetsOverride?: Partial<DoraTargets>,\n ): Promise<DoraMetrics> {\n await new Promise(r => setTimeout(r, 600)); // simulate network delay\n\n return {\n deploymentFrequency: {\n value: 4.2,\n unit: 'per week',\n rating: 'high',\n target: 7,\n slowestPRs: MOCK_PRS,\n },\n leadTime: {\n value: 18.4,\n unit: 'hours',\n rating: 'elite',\n target: 24,\n slowestPRs: MOCK_PRS,\n },\n changeFailureRate: env.isProduction\n ? { value: 8.3, unit: '%', rating: 'high', target: 15 }\n : null,\n mttr: env.isProduction\n ? { value: 0.8, unit: 'hours', rating: 'elite', target: 1, slowestPRs: MOCK_HOTFIX_PRS }\n : null,\n numberOfHotfixes: env.isProduction\n ? { value: 2, unit: 'PRs', rating: 'high', target: 0, slowestPRs: MOCK_HOTFIX_PRS }\n : null,\n };\n }\n\n async getHistory(\n _projectSlug: string,\n env: DoraEnvironment,\n _days: number,\n ): Promise<DoraHistoryPoint[]> {\n await new Promise(r => setTimeout(r, 400));\n\n return MOCK_HISTORY.map(point => ({\n ...point,\n changeFailureRate: env.isProduction ? point.changeFailureRate : undefined,\n mttrHours: env.isProduction ? point.mttrHours : undefined,\n }));\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.esm.js","sources":["../../src/api/types.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\n\nexport type DoraRating = 'elite' | 'high' | 'medium' | 'low';\n\nexport interface PrDetail {\n number: number;\n title: string;\n url: string;\n durationHours: number;\n mergedAt: string;\n author?: string;\n authorAvatar?: string;\n}\n\nexport interface DoraMetricValue {\n value: number;\n unit: string;\n rating: DoraRating;\n target: number;\n slowestPRs?: PrDetail[];\n}\n\nexport interface DoraEnvironment {\n name: string;\n /**\n * Raw branch pattern as configured — a single branch name, a comma-separated\n * list (\"main,master\"), or a JavaScript regex string (\"^(main|master)$\").\n * The client resolves this to a concrete list of branches at query time.\n */\n branch: string;\n isProduction: boolean;\n label: string;\n}\n\nexport interface DoraTargets {\n deploymentFrequency: number;\n leadTime: number;\n changeFailureRate: number;\n mttr: number;\n}\n\nexport interface DoraMetrics {\n deploymentFrequency: DoraMetricValue;\n leadTime: DoraMetricValue;\n /** null for non-production environments */\n changeFailureRate: DoraMetricValue | null;\n /** null for non-production environments */\n mttr: DoraMetricValue | null;\n /** null for non-production environments */\n numberOfHotfixes: DoraMetricValue | null;\n}\n\nexport interface DoraHistoryPoint {\n weekLabel: string; // e.g. \"Mar 10\"\n bucketMidMs: number; // timestamp (ms) of bucket midpoint — for chart x-axis alignment\n deploymentCount: number; // total PRs merged that week\n leadTimeHours: number; // avg lead time that week (0 if no PRs)\n changeFailureRate?: number; // % (prod only)\n mttrHours?: number; // avg (prod only)\n}\n\nexport interface DoraMetricsApi {\n getEnvironments(): DoraEnvironment[];\n getDefaultDays(): number;\n getTargets(): DoraTargets;\n getMetrics(\n projectSlug: string,\n env: DoraEnvironment,\n days: number,\n targetsOverride?: Partial<DoraTargets>,\n ): Promise<DoraMetrics>;\n getHistory(\n projectSlug: string,\n env: DoraEnvironment,\n days: number,\n ): Promise<DoraHistoryPoint[]>;\n}\n\nexport const doraMetricsApiRef = createApiRef<DoraMetricsApi>({\n id: 'plugin.dora-metrics.service',\n});\n"],"names":[],"mappings":";;AA8EO,MAAM,oBAAoB,YAAA,CAA6B;AAAA,EAC5D,EAAA,EAAI;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"types.esm.js","sources":["../../src/api/types.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\n\nexport type DoraRating = 'elite' | 'high' | 'medium' | 'low';\n\nexport interface PrDetail {\n number: number;\n title: string;\n url: string;\n durationHours: number;\n mergedAt: string;\n author?: string;\n authorAvatar?: string;\n}\n\nexport interface DoraMetricValue {\n value: number;\n unit: string;\n rating: DoraRating;\n target: number;\n slowestPRs?: PrDetail[];\n}\n\nexport interface DoraEnvironment {\n name: string;\n /**\n * Raw branch pattern as configured — a single branch name, a comma-separated\n * list (\"main,master\"), or a JavaScript regex string (\"^(main|master)$\").\n * The client resolves this to a concrete list of branches at query time.\n */\n branch: string;\n isProduction: boolean;\n label: string;\n}\n\nexport interface DoraTargets {\n deploymentFrequency: number;\n leadTime: number;\n changeFailureRate: number;\n mttr: number;\n}\n\nexport interface DoraMetrics {\n deploymentFrequency: DoraMetricValue;\n leadTime: DoraMetricValue;\n /** null for non-production environments */\n changeFailureRate: DoraMetricValue | null;\n /** null for non-production environments */\n mttr: DoraMetricValue | null;\n /** null for non-production environments */\n numberOfHotfixes: DoraMetricValue | null;\n}\n\nexport interface DoraHistoryPoint {\n weekLabel: string; // e.g. \"Mar 10\"\n bucketMidMs: number; // timestamp (ms) of bucket midpoint — for chart x-axis alignment\n deploymentCount: number; // total PRs merged that week\n leadTimeHours: number; // avg lead time that week (0 if no PRs)\n changeFailureRate?: number; // % (prod only)\n mttrHours?: number; // avg (prod only)\n}\n\nexport interface DoraMetricsApi {\n getEnvironments(): DoraEnvironment[];\n getDefaultDays(): number;\n getTargets(): DoraTargets;\n /** Web base URL of the configured GitHub instance (e.g. \"https://github.com\"). */\n getGithubBaseUrl(): string;\n getMetrics(\n projectSlug: string,\n env: DoraEnvironment,\n days: number,\n targetsOverride?: Partial<DoraTargets>,\n ): Promise<DoraMetrics>;\n getHistory(\n projectSlug: string,\n env: DoraEnvironment,\n days: number,\n ): Promise<DoraHistoryPoint[]>;\n}\n\nexport const doraMetricsApiRef = createApiRef<DoraMetricsApi>({\n id: 'plugin.dora-metrics.service',\n});\n"],"names":[],"mappings":";;AAgFO,MAAM,oBAAoB,YAAA,CAA6B;AAAA,EAC5D,EAAA,EAAI;AACN,CAAC;;;;"}
@@ -526,6 +526,7 @@ function DetailedOverlay({
526
526
  }) {
527
527
  const theme = useTheme();
528
528
  const isDark = theme.palette.type === "dark";
529
+ const githubBaseUrl = useApi(doraMetricsApiRef).getGithubBaseUrl();
529
530
  const [hoveredPrNumber, setHoveredPrNumber] = useState(null);
530
531
  const prRowRefs = useRef(/* @__PURE__ */ new Map());
531
532
  useEffect(() => {
@@ -707,7 +708,7 @@ function DetailedOverlay({
707
708
  } }, pr.author[0].toUpperCase()), /* @__PURE__ */ React.createElement(
708
709
  "a",
709
710
  {
710
- href: `https://github.com/${pr.author}`,
711
+ href: `${githubBaseUrl}/${pr.author}`,
711
712
  target: "_blank",
712
713
  rel: "noopener noreferrer",
713
714
  style: {
@@ -1 +1 @@
1
- {"version":3,"file":"DoraMetricsContent.esm.js","sources":["../../src/components/DoraMetricsContent.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { useTheme } from '@material-ui/core/styles';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { InfoCard } from '@backstage/core-components';\nimport { doraMetricsApiRef } from '../api/types';\nimport { niceHourTicks, niceCountTicks } from '../api/chartUtils';\nimport type { DoraEnvironment, DoraHistoryPoint, DoraMetrics, DoraMetricValue, DoraTargets, PrDetail } from '../api/types';\nimport { RatingBadge } from './ui/badge';\nimport { Card, CardLabel, CardFooter, CardDescription } from './ui/card';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from './ui/select';\nimport { ExternalLink } from 'lucide-react';\n\nconst ANNOTATION_PROJECT_SLUG = 'github.com/project-slug';\n/** Override the global environments list for this specific entity. JSON array of DoraEnvironment objects. */\nconst ANNOTATION_ENVIRONMENTS = 'dora-metrics/environments';\n/** Override the global performance targets for this specific entity. JSON object matching DoraTargets. */\nconst ANNOTATION_TARGETS = 'dora-metrics/targets';\n\n/** Parse entity annotation overrides. Returns undefined for malformed JSON to fail gracefully. */\nfunction parseAnnotationJson<T>(raw: string | undefined): T | undefined {\n if (!raw) return undefined;\n try { return JSON.parse(raw) as T; } catch { return undefined; }\n}\n\nconst DATE_RANGE_OPTIONS = [\n { value: '7', label: 'Last 7 days' },\n { value: '14', label: 'Last 14 days' },\n { value: '30', label: 'Last 30 days' },\n { value: '60', label: 'Last 60 days' },\n { value: '90', label: 'Last 90 days' },\n];\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\nfunction formatDuration(hours: number): string {\n const totalMinutes = Math.round(hours * 60);\n const d = Math.floor(totalMinutes / (24 * 60));\n const h = Math.floor((totalMinutes % (24 * 60)) / 60);\n const m = totalMinutes % 60;\n if (d > 0) return h > 0 ? `${d}d ${h}h` : `${d}d`;\n if (h > 0) return m > 0 ? `${h}h ${m}m` : `${h}h`;\n return `${m}m`;\n}\n\n// Red → Orange → Yellow → Lime → Green gradient (slowest to fastest)\nconst DURATION_GRADIENT = ['#f87171', '#fb923c', '#fbbf24', '#a3e635', '#4ade80'];\n\nfunction durationColor(index: number, total: number): string {\n if (total <= 1) return DURATION_GRADIENT[0];\n const step = (DURATION_GRADIENT.length - 1) / (total - 1);\n return DURATION_GRADIENT[Math.round(index * step)];\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\n// Period-over-period delta — compares last bucket vs second-to-last bucket\nfunction buildDeltaLabel(\n values: number[],\n unit: string,\n lowerIsBetter: boolean,\n): { text: string; tooltip: string; good: boolean } | null {\n if (values.length < 2) return null;\n const curr = values[values.length - 1];\n const prev = values[values.length - 2];\n const diff = curr - prev;\n if (Math.abs(diff) < 0.001) return null;\n const good = lowerIsBetter ? diff < 0 : diff > 0;\n const arrow = diff > 0 ? '↑' : '↓';\n\n const fmt = (v: number) =>\n unit === 'hours' ? formatDuration(v) :\n unit === '%' ? `${Math.round(v * 10) / 10}%` :\n unit === 'per week' ? `${Math.round(v * 10) / 10}/wk` :\n `${Math.round(v * 10) / 10} ${unit}`;\n\n const direction = good ? 'improving' : 'worsening';\n return {\n text: `${arrow} ${fmt(Math.abs(diff))} ${direction}`,\n tooltip: `Latest period: ${fmt(curr)} · Previous period: ${fmt(prev)}\\nEach period = one time bucket (~1/7th of your selected date range)`,\n good,\n };\n}\n\n// ─── Loading overlay ────────────────────────────────────────────────────────\n\nconst LOADING_MESSAGES = [\n 'Establishing routes…',\n 'Syncing catalog…',\n 'Loading plugins…',\n 'Fetching PR data…',\n 'Reading entity graph…',\n];\nconst STAGES = ['FETCH', 'PARSE', 'SCORE', 'RENDER'] as const;\nconst FONT_IMPACT = '\"Impact\",\"Haettenschweiler\",\"Franklin Gothic Heavy\",\"Arial Black\",sans-serif';\nconst FONT_MONO = '\"JetBrains Mono\",\"Fira Code\",\"Consolas\",monospace';\n\nfunction DoraLoadingOverlay() {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n\n const [activeStage, setActiveStage] = useState(0);\n const [msgKey, setMsgKey] = useState(0);\n const [msgIdx, setMsgIdx] = useState(0);\n\n useEffect(() => {\n const st = setInterval(() => setActiveStage(s => (s + 1) % STAGES.length), 800);\n return () => clearInterval(st);\n }, []);\n\n useEffect(() => {\n const mt = setInterval(() => { setMsgIdx(i => (i + 1) % LOADING_MESSAGES.length); setMsgKey(k => k + 1); }, 2200);\n return () => clearInterval(mt);\n }, []);\n\n const textShadow = isDark\n ? '3px 3px 0 #C24E00, 6px 6px 0 #902800, 9px 9px 0 #5A1800, 12px 12px 0 rgba(0,0,0,0.55), 0 0 60px rgba(250,100,0,0.45)'\n : '3px 3px 0 #C24E00, 6px 6px 0 #902800, 9px 9px 0 rgba(80,20,0,0.28), 0 0 32px rgba(250,100,0,0.22)';\n\n const mutedColor = isDark ? '#3f3f46' : '#a1a1aa';\n const subtitleColor = isDark ? '#52525b' : '#71717a';\n const borderColor = isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.08)';\n\n return (\n <>\n {/* Frosted glass backdrop — blurs whatever is behind */}\n <div aria-hidden style={{\n position: 'fixed', inset: 0, zIndex: 900,\n backdropFilter: 'blur(12px)',\n WebkitBackdropFilter: 'blur(12px)',\n background: isDark ? 'rgba(6,6,9,0.65)' : 'rgba(253,252,251,0.70)',\n }} />\n\n {/* Centered card */}\n <div style={{\n position: 'fixed', zIndex: 901,\n top: '50%', left: '50%',\n transform: 'translateX(-50%) translateY(-50%)',\n display: 'flex', flexDirection: 'column', alignItems: 'center',\n padding: '48px 56px 40px',\n borderRadius: 20,\n background: isDark ? 'rgba(9,9,12,0.82)' : 'rgba(255,255,255,0.88)',\n border: `1px solid ${borderColor}`,\n boxShadow: isDark\n ? '0 0 0 1px rgba(250,100,0,0.06), 0 32px 80px rgba(0,0,0,0.65)'\n : '0 32px 80px rgba(0,0,0,0.12)',\n backdropFilter: 'blur(20px)',\n WebkitBackdropFilter: 'blur(20px)',\n animation: 'dlsCardIn 0.35s cubic-bezier(0.34,1.2,0.64,1) both',\n overflow: 'visible',\n }}>\n\n {/* Breathing ambient glow — inside card */}\n <div aria-hidden style={{\n position: 'absolute', width: 500, height: 360,\n left: '50%', top: '50%',\n transform: 'translateX(-50%) translateY(-50%)',\n background: `radial-gradient(ellipse at center, ${isDark ? 'rgba(250,100,0,0.08)' : 'rgba(250,100,0,0.05)'} 0%, transparent 70%)`,\n animation: 'dlsBreath 3.2s ease-in-out infinite',\n pointerEvents: 'none', borderRadius: 20,\n }} />\n\n {/* DORA wordmark */}\n <div style={{ display: 'flex', lineHeight: 1, marginBottom: 6 }}>\n {['D','O','R','A'].map((letter, i) => (\n <span key={i} style={{\n fontFamily: FONT_IMPACT, fontWeight: 900, fontSize: 110,\n color: '#FA6400', letterSpacing: '0.04em', display: 'inline-block',\n transform: 'skewX(-6deg)', textShadow,\n WebkitTextStroke: '0.5px #C24E00',\n animation: `dlsLetterDrop 0.6s cubic-bezier(0.34,1.45,0.64,1) ${i * 0.1}s both`,\n }}>\n {letter}\n </span>\n ))}\n </div>\n\n {/* Subtitle */}\n <span style={{ fontFamily: 'Inter,system-ui,sans-serif', fontSize: 13, fontWeight: 400,\n letterSpacing: '0.06em', color: subtitleColor, marginBottom: 32,\n animation: 'dlsFadeUp 0.4s ease 0.55s both' }}>\n Metrics\n </span>\n\n {/* Pipeline stages */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 0, marginBottom: 20, animation: 'dlsFadeUp 0.4s ease 0.7s both' }}>\n {STAGES.map((stage, i) => {\n const isActive = i === activeStage;\n const isComplete = i < activeStage;\n return (\n <div key={stage} style={{ display: 'flex', alignItems: 'center', gap: 0 }}>\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5, width: 64 }}>\n <div style={{ position: 'relative', width: 12, height: 12 }}>\n {isActive && (\n <div style={{ position: 'absolute', inset: -6, borderRadius: '50%',\n border: '1.5px solid rgba(250,100,0,0.5)', animation: 'dlsPulse 1s ease-out infinite' }} />\n )}\n <div style={{\n width: 12, height: 12, borderRadius: '50%',\n background: (isActive || isComplete) ? '#FA6400' : (isDark ? '#27272a' : '#e4e4e7'),\n border: `2px solid ${(isActive || isComplete) ? '#C24E00' : (isDark ? '#3f3f46' : '#d4d4d8')}`,\n boxShadow: isActive ? '0 0 14px rgba(250,100,0,0.7)' : 'none',\n transition: 'background 0.3s, box-shadow 0.3s',\n }} />\n </div>\n <span style={{ fontFamily: 'Inter,system-ui,sans-serif', fontSize: 8, fontWeight: 700,\n letterSpacing: '0.12em', textTransform: 'uppercase',\n color: isActive ? '#FA6400' : isComplete ? mutedColor : mutedColor,\n transition: 'color 0.3s' }}>\n {stage}\n </span>\n </div>\n {i < STAGES.length - 1 && (\n <div style={{ width: 32, height: 2, marginBottom: 14, position: 'relative', overflow: 'hidden',\n background: isDark ? '#27272a' : '#e4e4e7', borderRadius: 2 }}>\n <div style={{\n position: 'absolute', inset: 0, borderRadius: 2, background: '#FA6400',\n transformOrigin: 'left center',\n transform: `scaleX(${i < activeStage ? 1 : isActive ? 0.5 : 0})`,\n transition: 'transform 0.4s ease',\n }} />\n </div>\n )}\n </div>\n );\n })}\n </div>\n\n {/* Status message */}\n <span key={msgKey} style={{ fontFamily: FONT_MONO, fontSize: 11,\n color: mutedColor, letterSpacing: '0.04em', animation: 'dlsFadeUp 0.3s ease both' }}>\n {LOADING_MESSAGES[msgIdx]}\n </span>\n\n </div>{/* end card */}\n\n <style>{`\n @keyframes dlsLetterDrop {\n from { opacity: 0; transform: skewX(-6deg) translateY(-32px) scale(1.1); }\n 65% { transform: skewX(-6deg) translateY(6px) scale(0.95); }\n to { opacity: 1; transform: skewX(-6deg) translateY(0) scale(1); }\n }\n @keyframes dlsFadeUp {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes dlsBreath {\n 0%, 100% { transform: translateX(-50%) translateY(-50%) scale(0.85); opacity: 0.6; }\n 50% { transform: translateX(-50%) translateY(-50%) scale(1.2); opacity: 1; }\n }\n @keyframes dlsPulse {\n from { transform: scale(0.6); opacity: 0.9; }\n to { transform: scale(2.2); opacity: 0; }\n }\n @keyframes dlsCardIn {\n from { opacity: 0; transform: translateX(-50%) translateY(-46%); }\n to { opacity: 1; transform: translateX(-50%) translateY(-50%); }\n }\n `}</style>\n </>\n );\n}\n\n// ─── Compact sparkline (with hover tooltip + date axis) ────────────────────\n\nfunction CompactSparkline({\n values,\n weekLabels,\n color,\n type,\n formatValue,\n height = 36,\n}: {\n values: number[];\n weekLabels: string[];\n color: string;\n type: 'bar' | 'line';\n formatValue: (v: number) => string;\n height?: number;\n}) {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n const [hoverIdx, setHoverIdx] = useState<number | null>(null);\n\n const VW = 400; // viewBox units\n const mutedBorder = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)';\n const tooltipBg = isDark ? '#1c1c20' : '#ffffff';\n const tooltipBdr = isDark ? '#3f3f46' : '#e4e4e7';\n const tooltipText = isDark ? '#ededef' : '#111114';\n const font = 'Inter,system-ui,sans-serif';\n\n const handleMouseMove = (e: React.MouseEvent<SVGSVGElement>) => {\n const rect = e.currentTarget.getBoundingClientRect();\n const relX = (e.clientX - rect.left) / rect.width;\n setHoverIdx(Math.max(0, Math.min(values.length - 1, Math.round(relX * (values.length - 1)))));\n };\n\n if (type === 'bar') {\n const max = Math.max(...values, 1);\n const gap = 4;\n const barW = (VW - gap * (values.length - 1)) / values.length;\n\n const hoverX = hoverIdx !== null\n ? hoverIdx * (barW + gap) + barW / 2\n : null;\n const tooltipX = hoverX !== null ? Math.min(hoverX - 34, VW - 72) : 0;\n\n return (\n <div>\n <svg viewBox={`0 0 ${VW} ${height}`} width=\"100%\" style={{ display: 'block', overflow: 'visible' }}\n onMouseMove={handleMouseMove} onMouseLeave={() => setHoverIdx(null)}>\n {values.map((v, i) => {\n const h = Math.max(2, (v / max) * (height - 4));\n return (\n <rect key={i} x={i * (barW + gap)} y={height - h} width={barW} height={h} rx={1.5}\n fill={color} opacity={i === hoverIdx ? 1 : 0.3 + (i / values.length) * 0.55} />\n );\n })}\n <line x1={0} y1={height - 0.5} x2={VW} y2={height - 0.5} stroke={mutedBorder} strokeWidth={1} />\n {hoverIdx !== null && hoverX !== null && (\n <>\n <rect x={tooltipX} y={2} width={72} height={22} rx={4} fill={tooltipBg} stroke={tooltipBdr} strokeWidth={1} />\n <text x={tooltipX + 36} y={17} textAnchor=\"middle\" fill={tooltipText} fontSize={9} fontWeight={600} fontFamily={font}>\n {weekLabels[hoverIdx]}: {formatValue(values[hoverIdx])}\n </text>\n </>\n )}\n </svg>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 3 }}>\n <span style={{ fontSize: 9, color: isDark ? '#3f3f46' : '#a1a1aa' }}>{weekLabels[0]}</span>\n <span style={{ fontSize: 9, color: isDark ? '#3f3f46' : '#a1a1aa' }}>{weekLabels[weekLabels.length - 1]}</span>\n </div>\n </div>\n );\n }\n\n if (values.length < 2) return null;\n const max = Math.max(...values, 0.01);\n const min = Math.min(...values);\n const range = max - min || max || 1;\n const pad = 3;\n const pts = values.map((v, i): [number, number] => [\n (i / (values.length - 1)) * (VW - pad * 2) + pad,\n (height - pad * 2) - ((v - min) / range) * (height - pad * 2 - 2) + pad,\n ]);\n const d = pts.map(([x, y], i) => `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`).join(' ');\n const area = `${d} L${pts[pts.length-1][0]},${height} L${pts[0][0]},${height} Z`;\n const hoverPt = hoverIdx !== null ? pts[hoverIdx] : null;\n const tooltipX2 = hoverPt ? Math.min(hoverPt[0] - 34, VW - 72) : 0;\n\n return (\n <div>\n <svg viewBox={`0 0 ${VW} ${height}`} width=\"100%\" style={{ display: 'block', overflow: 'visible' }}\n onMouseMove={handleMouseMove} onMouseLeave={() => setHoverIdx(null)}>\n <path d={area} fill={color} opacity={0.07} />\n <path d={d} fill=\"none\" stroke={color} strokeWidth={1.5} strokeLinejoin=\"round\" strokeLinecap=\"round\" />\n {pts.map(([x, y], i) => (\n <circle key={i} cx={x} cy={y} r={i === hoverIdx ? 3.5 : i === pts.length - 1 ? 2.5 : 0} fill={color} />\n ))}\n <line x1={0} y1={height - 0.5} x2={VW} y2={height - 0.5} stroke={mutedBorder} strokeWidth={1} />\n {hoverPt && hoverIdx !== null && (\n <>\n <line x1={hoverPt[0]} y1={0} x2={hoverPt[0]} y2={height} stroke={color} strokeWidth={1} strokeDasharray=\"2 2\" opacity={0.4} />\n <rect x={tooltipX2} y={2} width={72} height={22} rx={4} fill={tooltipBg} stroke={tooltipBdr} strokeWidth={1} />\n <text x={tooltipX2 + 36} y={17} textAnchor=\"middle\" fill={tooltipText} fontSize={9} fontWeight={600} fontFamily={font}>\n {weekLabels[hoverIdx]}: {formatValue(values[hoverIdx])}\n </text>\n </>\n )}\n </svg>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 3 }}>\n <span style={{ fontSize: 9, color: isDark ? '#3f3f46' : '#a1a1aa' }}>{weekLabels[0]}</span>\n <span style={{ fontSize: 9, color: isDark ? '#3f3f46' : '#a1a1aa' }}>{weekLabels[weekLabels.length - 1]}</span>\n </div>\n </div>\n );\n}\n\n// ─── Detailed interactive chart ────────────────────────────────────────────\n\ninterface ChartTooltipState {\n x: number;\n y: number;\n primary: string;\n secondary?: string;\n}\n\nfunction DetailedChart({\n values,\n weekLabels,\n bucketMidMs,\n color,\n type,\n formatValue,\n tickUnit,\n prs,\n days,\n hoveredPrNumber,\n onPrHover,\n}: {\n values: number[];\n weekLabels: string[];\n bucketMidMs: number[];\n color: string;\n type: 'bar' | 'line';\n formatValue: (v: number) => string;\n tickUnit?: 'hours' | 'count';\n prs?: PrDetail[];\n days: number;\n hoveredPrNumber?: number | null;\n onPrHover?: (num: number | null) => void;\n}) {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n const [hoverIdx, setHoverIdx] = useState<number | null>(null);\n const [tooltip, setTooltip] = useState<ChartTooltipState | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const W = 800, H = 260;\n const PAD = { top: 16, right: 16, bottom: 36, left: 56 };\n const cW = W - PAD.left - PAD.right;\n const cH = H - PAD.top - PAD.bottom;\n\n // Y scale: nice tick-aligned range so axis labels are clean intervals.\n const prMaxDuration = prs && prs.length > 0 ? Math.max(...prs.map(p => p.durationHours)) : 0;\n const rawDataMax = Math.max(...values.filter(v => v > 0), prMaxDuration, 0.01);\n const yTicks = tickUnit === 'hours'\n ? niceHourTicks(rawDataMax)\n : tickUnit === 'count'\n ? niceCountTicks(rawDataMax)\n : [0, 0.25, 0.5, 0.75, 1].map(t => t * rawDataMax);\n const dataMin = 0;\n const dataMax = yTicks[yTicks.length - 1];\n const dataRange = dataMax - dataMin || 1;\n const yScale = (v: number) => cH - ((v - dataMin) / dataRange) * cH;\n\n // Shared timestamp-based x-axis\n const cutoffMs = Date.now() - days * 24 * 60 * 60 * 1000;\n const rangeMs = days * 24 * 60 * 60 * 1000;\n const xTime = (ms: number) => Math.max(0, Math.min(cW, ((ms - cutoffMs) / rangeMs) * cW));\n\n const xIdx = (i: number) => bucketMidMs.length === values.length\n ? xTime(bucketMidMs[i])\n : (values.length > 1 ? (i / (values.length - 1)) * cW : cW / 2);\n\n const pts: [number, number][] = values.map((v, i) => [PAD.left + xIdx(i), PAD.top + yScale(v)]);\n const linePath = pts.map(([x, y], i) => `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`).join(' ');\n const areaPath = pts.length > 1\n ? `${linePath} L${pts[pts.length-1][0]},${PAD.top+cH} L${pts[0][0]},${PAD.top+cH} Z`\n : '';\n\n const gap = 6;\n const barW = values.length > 1 ? (cW - gap * (values.length - 1)) / values.length : cW;\n\n const gridColor = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)';\n const axisColor = isDark ? '#3f3f46' : '#d4d4d8';\n const labelColor = isDark ? '#52525b' : '#a1a1aa';\n const font = 'Inter,system-ui,sans-serif';\n const gradId = `dg_${color.replace('#','')}`;\n\n // Find the nearest bucket index for a given timestamp\n const nearestBucket = (ms: number): number => {\n if (bucketMidMs.length === 0) return 0;\n let best = 0, bestDist = Infinity;\n bucketMidMs.forEach((mid, i) => {\n const d = Math.abs(ms - mid);\n if (d < bestDist) { bestDist = d; best = i; }\n });\n return best;\n };\n\n const handleSvgMouseMove = (e: React.MouseEvent<SVGSVGElement>) => {\n const rect = e.currentTarget.getBoundingClientRect();\n const relX = (e.clientX - rect.left - PAD.left * (rect.width / W)) / (rect.width * cW / W);\n const idx = Math.max(0, Math.min(values.length - 1, Math.round(relX * (values.length - 1))));\n setHoverIdx(idx);\n const cRect = containerRef.current?.getBoundingClientRect();\n const x = cRect ? e.clientX - cRect.left : 0;\n const y = cRect ? e.clientY - cRect.top : 0;\n setTooltip({ x, y, primary: formatValue(values[idx]), secondary: weekLabels[idx] });\n };\n\n return (\n <div ref={containerRef} style={{ position: 'relative' }}>\n <svg viewBox={`0 0 ${W} ${H}`} width=\"100%\" style={{ display: 'block' }}\n onMouseMove={handleSvgMouseMove}\n onMouseLeave={() => { setHoverIdx(null); setTooltip(null); }}>\n <defs>\n <linearGradient id={gradId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={color} stopOpacity={0.28} />\n <stop offset=\"100%\" stopColor={color} stopOpacity={0} />\n </linearGradient>\n {/* Clip PR dots to the chart area */}\n <clipPath id={`prClip_${gradId}`}>\n <rect x={PAD.left} y={PAD.top - 8} width={cW} height={cH + 16} />\n </clipPath>\n </defs>\n\n {/* Grid + Y-axis */}\n {yTicks.map((v, i) => {\n const y = PAD.top + yScale(v);\n return (\n <g key={i}>\n {i > 0 && <line x1={PAD.left} y1={y} x2={PAD.left+cW} y2={y} stroke={gridColor} strokeWidth={1} strokeDasharray=\"3 4\" />}\n <text x={PAD.left - 6} y={y + 4} textAnchor=\"end\" fill={labelColor} fontSize={10} fontFamily={font}>\n {formatValue(v)}\n </text>\n </g>\n );\n })}\n <line x1={PAD.left} y1={PAD.top+cH} x2={PAD.left+cW} y2={PAD.top+cH} stroke={axisColor} strokeWidth={1} />\n\n {/* Chart body */}\n {type === 'bar' ? (\n values.map((v, i) => {\n const bH = Math.max(2, ((v - dataMin) / dataRange) * cH);\n const x = PAD.left + i * (barW + gap);\n return (\n <rect key={i} x={x} y={PAD.top+cH-bH} width={barW} height={bH} rx={3}\n fill={color} opacity={i === hoverIdx ? 0.95 : 0.4 + (i / Math.max(values.length - 1, 1)) * 0.45} />\n );\n })\n ) : (\n <>\n {/* When prs are provided, show scatter-only (no line/area/bucket dots).\n For CFR (no prs), keep a minimal line + non-zero bucket dots. */}\n {!prs && areaPath && <path d={areaPath} fill={`url(#${gradId})`} />}\n {!prs && <path d={linePath} fill=\"none\" stroke={color} strokeWidth={2} strokeLinejoin=\"round\" strokeLinecap=\"round\" />}\n {!prs && pts.map(([x, y], i) => values[i] > 0 && (\n <circle key={i} cx={x} cy={y}\n r={i === hoverIdx ? 5.5 : 4}\n fill={isDark ? '#0f0f12' : '#ffffff'} stroke={color}\n strokeWidth={i === hoverIdx ? 2.5 : 1.5} />\n ))}\n </>\n )}\n\n {/* PR data-point dots — x at bucket midpoint, y at actual duration (clamped to chart) */}\n {prs && type === 'line' && (\n <g clipPath={`url(#prClip_${gradId})`}>\n {prs.map((pr, dotIdx) => {\n const mergedMs = new Date(pr.mergedAt).getTime();\n const bIdx = nearestBucket(mergedMs);\n // Small deterministic jitter within the bucket to avoid overlap\n const jitter = ((pr.number * 17 + dotIdx * 11) % 13 - 6) * 5;\n const px = PAD.left + xIdx(bIdx) + jitter;\n // Clamp y: PR may be faster or slower than bucket avg; always visible\n const rawPy = PAD.top + yScale(pr.durationHours);\n const py = Math.max(PAD.top + 4, Math.min(PAD.top + cH - 4, rawPy));\n const isHov = pr.number === hoveredPrNumber;\n\n // Thin connector from dot to the bucket trend point\n const trendPt = pts[bIdx];\n\n return (\n <g key={pr.number} style={{ cursor: 'pointer' }}\n onMouseEnter={e => {\n e.stopPropagation();\n const cRect = containerRef.current?.getBoundingClientRect();\n const x = cRect ? e.clientX - cRect.left : 0;\n const y = cRect ? e.clientY - cRect.top : 0;\n setTooltip({ x, y,\n primary: `#${pr.number}: ${pr.title}`,\n secondary: `${formatValue(pr.durationHours)} · ${new Date(pr.mergedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}` });\n onPrHover?.(pr.number);\n }}\n onMouseLeave={() => { setTooltip(null); onPrHover?.(null); }}>\n {/* connector removed — no bucket-average dot to connect to */}\n <circle cx={px} cy={py} r={isHov ? 11 : 7} fill={color} opacity={isHov ? 0.2 : 0.1} />\n <circle cx={px} cy={py} r={isHov ? 5.5 : 3.5}\n fill={isHov ? color : (isDark ? '#1a1a20' : '#fff')}\n stroke={color} strokeWidth={isHov ? 0 : 1.5}\n opacity={isHov ? 1 : 0.8} />\n </g>\n );\n })}\n </g>\n )}\n\n {/* Hover crosshair */}\n {hoverIdx !== null && type === 'line' && pts[hoverIdx] && (\n <>\n <line x1={pts[hoverIdx][0]} y1={PAD.top} x2={pts[hoverIdx][0]} y2={PAD.top+cH}\n stroke={color} strokeWidth={1} strokeDasharray=\"3 3\" opacity={0.3} />\n <line x1={PAD.left} y1={pts[hoverIdx][1]} x2={PAD.left+cW} y2={pts[hoverIdx][1]}\n stroke={color} strokeWidth={1} strokeDasharray=\"3 3\" opacity={0.2} />\n </>\n )}\n\n {/* X-axis labels */}\n {weekLabels.map((lbl, i) => {\n const total = weekLabels.length;\n const step = Math.max(1, Math.floor(total / 5));\n if (i !== 0 && i !== total - 1 && i % step !== 0) return null;\n const x = type === 'bar'\n ? PAD.left + i * (barW + gap) + barW / 2\n : PAD.left + xIdx(i);\n return (\n <text key={i} x={x} y={H - 8} textAnchor=\"middle\" fill={labelColor} fontSize={10} fontFamily={font}>\n {lbl}\n </text>\n );\n })}\n </svg>\n\n {/* Absolute tooltip — positioned relative to the chart container */}\n {tooltip && (() => {\n const TW = 220;\n const cW2 = containerRef.current?.offsetWidth ?? 800;\n const nearRight = tooltip.x + TW + 12 > cW2;\n return (\n <div style={{\n position: 'absolute',\n left: nearRight ? tooltip.x - TW - 8 : tooltip.x + 8,\n top: tooltip.y - 36,\n zIndex: 9999, pointerEvents: 'none',\n background: isDark ? '#18181b' : '#ffffff',\n border: `1px solid ${isDark ? '#3f3f46' : '#e4e4e7'}`,\n borderRadius: 7, padding: '7px 10px',\n boxShadow: isDark ? '0 6px 20px rgba(0,0,0,0.55)' : '0 6px 20px rgba(0,0,0,0.10)',\n width: TW,\n }}>\n <div style={{ fontSize: 12, fontWeight: 600, color: isDark ? '#ededef' : '#111114',\n fontFamily: font, lineHeight: 1.4 }}>\n {tooltip.primary}\n </div>\n {tooltip.secondary && (\n <div style={{ fontSize: 11, color: isDark ? '#71717a' : '#a1a1aa', fontFamily: font, marginTop: 2 }}>\n {tooltip.secondary}\n </div>\n )}\n </div>\n );\n })()}\n </div>\n );\n}\n\n// ─── Detailed overlay ──────────────────────────────────────────────────────\n\ninterface ExpandedCardData {\n title: string;\n metric: DoraMetricValue;\n description: string;\n lowerIsBetter: boolean;\n sparklineValues: number[];\n sparklineType: 'bar' | 'line';\n sparklineColor: string;\n tickUnit?: 'hours' | 'count';\n weekLabels: string[];\n bucketMidMs: number[];\n prs?: PrDetail[];\n /** Override the section header above the PR list (default: \"All deployments\"). */\n prListLabel?: string;\n}\n\nfunction DetailedOverlay({\n data,\n days,\n onClose,\n}: {\n data: ExpandedCardData;\n days: number;\n onClose: () => void;\n}) {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n const [hoveredPrNumber, setHoveredPrNumber] = useState<number | null>(null);\n const prRowRefs = useRef<Map<number, HTMLDivElement>>(new Map());\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose]);\n\n // Auto-scroll to the highlighted PR row when hover comes from the chart\n useEffect(() => {\n if (hoveredPrNumber !== null) {\n const el = prRowRefs.current.get(hoveredPrNumber);\n if (el) el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n }\n }, [hoveredPrNumber]);\n\n const { title, metric, description, lowerIsBetter, sparklineValues, sparklineType, sparklineColor, tickUnit, weekLabels, bucketMidMs, prs, prListLabel } = data;\n const isDeployFreq = sparklineType === 'bar';\n const displayValue = metric.unit === 'hours' ? formatDuration(metric.value) : String(metric.value);\n const displayUnit = metric.unit === 'hours' ? '' : metric.unit;\n const formatVal = (v: number) => metric.unit === 'hours' ? formatDuration(v) : `${Math.round(v * 10) / 10}`;\n const borderColor = isDark ? '#27272a' : '#e4e4e7';\n const mutedColor = isDark ? '#52525b' : '#a1a1aa';\n const targetLabel = lowerIsBetter\n ? `Target: < ${metric.unit === 'hours' ? formatDuration(metric.target) : metric.target} ${displayUnit}`\n : `Target: ≥ ${metric.target} ${displayUnit}`;\n\n // Group deployment PRs by date (for the date table)\n const prsByDate = isDeployFreq && prs ? prs.reduce((acc, pr) => {\n const date = new Date(pr.mergedAt).toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });\n if (!acc[date]) acc[date] = [];\n acc[date].push(pr);\n return acc;\n }, {} as Record<string, PrDetail[]>) : {};\n const sortedDates = Object.keys(prsByDate).sort(\n (a, b) => new Date(prsByDate[b][0].mergedAt).getTime() - new Date(prsByDate[a][0].mergedAt).getTime()\n );\n\n return (\n <>\n {/* Backdrop */}\n <div onClick={onClose} style={{\n position: 'fixed', inset: 0, zIndex: 499,\n background: isDark ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.3)',\n backdropFilter: 'blur(4px)',\n }} />\n\n {/* Panel */}\n <div style={{\n position: 'fixed',\n top: '50%', left: '50%',\n transform: 'translateX(-50%) translateY(-50%)',\n width: 'min(92vw, 960px)',\n maxHeight: 'calc(100vh - 80px)',\n zIndex: 500,\n background: isDark ? '#09090c' : '#fafafa',\n border: `1px solid ${borderColor}`,\n borderRadius: 14,\n display: 'flex', flexDirection: 'column',\n overflow: 'hidden',\n animation: 'detailExpandIn 0.2s cubic-bezier(0.34,1.2,0.64,1)',\n boxShadow: isDark ? '0 24px 64px rgba(0,0,0,0.7)' : '0 24px 64px rgba(0,0,0,0.18)',\n }}>\n {/* Header */}\n <div style={{\n display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start',\n padding: '20px 24px 16px',\n borderBottom: `1px solid ${borderColor}`,\n flexShrink: 0,\n }}>\n <div>\n <div style={{ fontSize: 10, fontWeight: 700, color: mutedColor, textTransform: 'uppercase', letterSpacing: '0.12em', marginBottom: 8 }}>\n {title} · {weekLabels[0]} – {weekLabels[weekLabels.length - 1]}\n </div>\n <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>\n <span style={{ fontSize: 52, fontWeight: 700, color: theme.palette.text.primary, lineHeight: 1, letterSpacing: '-0.02em' }}>\n {displayValue}\n </span>\n {displayUnit && (\n <span style={{ fontSize: 18, color: mutedColor, fontWeight: 500 }}>{displayUnit}</span>\n )}\n <RatingBadge rating={metric.rating} />\n </div>\n <div style={{ fontSize: 12, color: mutedColor, marginTop: 6 }}>\n {description} · <span style={{ fontStyle: 'italic' }}>{targetLabel}</span>\n </div>\n </div>\n <button type=\"button\" onClick={onClose} style={{\n all: 'unset', cursor: 'pointer', width: 30, height: 30,\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n borderRadius: '50%', background: isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)',\n color: mutedColor, fontSize: 18, fontWeight: 300, transition: 'all 0.12s', flexShrink: 0,\n }}\n onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.1)'; }}\n onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)'; }}>\n ✕\n </button>\n </div>\n\n {/* Body — scrollable */}\n <div style={{ flex: 1, overflow: 'auto', padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 20 }}>\n\n {/* Chart */}\n {weekLabels.length >= 2 ? (\n <DetailedChart\n values={sparklineValues} weekLabels={weekLabels}\n bucketMidMs={bucketMidMs}\n color={sparklineColor} type={sparklineType}\n tickUnit={tickUnit}\n formatValue={formatVal} prs={isDeployFreq ? undefined : prs} days={days}\n hoveredPrNumber={hoveredPrNumber} onPrHover={setHoveredPrNumber}\n />\n ) : (\n <div style={{ color: mutedColor, fontSize: 13, textAlign: 'center', padding: '48px 0' }}>\n Not enough data — select a wider date range.\n </div>\n )}\n\n {/* === Deployment Frequency: PRs grouped by date === */}\n {isDeployFreq && prs && prs.length > 0 && (\n <div>\n <div style={{ fontSize: 10, fontWeight: 700, color: mutedColor, textTransform: 'uppercase',\n letterSpacing: '0.12em', marginBottom: 12,\n borderTop: `1px solid ${borderColor}`, paddingTop: 12 }}>\n {prListLabel ?? 'All deployments'} · {prs.length} PR{prs.length !== 1 ? 's' : ''}\n </div>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>\n {sortedDates.map(date => (\n <div key={date}>\n {/* Date header */}\n <div style={{\n fontSize: 11, fontWeight: 700, color: sparklineColor,\n marginBottom: 6, display: 'flex', alignItems: 'center', gap: 8,\n }}>\n <span>{date}</span>\n <span style={{ color: mutedColor, fontWeight: 500 }}>· {prsByDate[date].length} PR{prsByDate[date].length > 1 ? 's' : ''}</span>\n </div>\n {/* PR rows */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4, paddingLeft: 12,\n borderLeft: `2px solid ${sparklineColor}22` }}>\n {prsByDate[date].map(pr => (\n <div key={pr.number} style={{\n display: 'flex', flexDirection: 'column', gap: 5,\n padding: '7px 10px', borderRadius: 7,\n background: isDark ? '#0d0d10' : '#f7f7f9',\n border: `1px solid ${borderColor}`,\n transition: 'background 0.12s',\n }}>\n {/* Row 1: number + title + lead time + link */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ fontSize: 10, fontWeight: 700, color: mutedColor, flexShrink: 0, minWidth: 36 }}>\n #{pr.number}\n </span>\n <span style={{ fontSize: 12, color: theme.palette.text.primary, flex: 1,\n whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} title={pr.title}>\n {pr.title}\n </span>\n <span style={{ fontSize: 10, color: mutedColor, flexShrink: 0 }}>\n {formatDuration(pr.durationHours)} lead\n </span>\n <a href={pr.url} target=\"_blank\" rel=\"noopener noreferrer\"\n style={{ color: mutedColor, flexShrink: 0 }}>\n <ExternalLink size={11} />\n </a>\n </div>\n {/* Row 2: author badge */}\n {pr.author && (\n <div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>\n {pr.authorAvatar ? (\n <img src={pr.authorAvatar} alt={pr.author}\n style={{ width: 16, height: 16, borderRadius: '50%', flexShrink: 0,\n border: `1px solid ${isDark ? '#3f3f46' : '#e4e4e7'}` }} />\n ) : (\n <div style={{ width: 16, height: 16, borderRadius: '50%', flexShrink: 0,\n background: isDark ? '#27272a' : '#e4e4e7',\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n fontSize: 8, fontWeight: 700, color: mutedColor }}>\n {pr.author[0].toUpperCase()}\n </div>\n )}\n <a href={`https://github.com/${pr.author}`} target=\"_blank\" rel=\"noopener noreferrer\"\n style={{ fontSize: 10, fontWeight: 500, color: mutedColor,\n textDecoration: 'none', letterSpacing: '0.01em' }}\n onMouseEnter={e => { (e.currentTarget as HTMLElement).style.color = '#FA6400'; }}\n onMouseLeave={e => { (e.currentTarget as HTMLElement).style.color = mutedColor; }}>\n @{pr.author}\n </a>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* === Lead Time / MTTR: duration-ranked PR list with hover linking === */}\n {!isDeployFreq && prs && prs.length > 0 && (\n <div>\n <div style={{ fontSize: 10, fontWeight: 700, color: mutedColor, textTransform: 'uppercase',\n letterSpacing: '0.12em', marginBottom: 10,\n borderTop: `1px solid ${borderColor}`, paddingTop: 12 }}>\n {title.toLowerCase().includes('restore') ? 'Slowest Hotfix PRs' : 'Slowest PRs'}\n {' '}· hover a row to highlight on chart\n </div>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>\n {prs.map((pr, i) => {\n const col = durationColor(i, prs.length);\n const maxDur = prs[0].durationHours;\n const barPct = maxDur > 0 ? (pr.durationHours / maxDur) * 100 : 0;\n const isHov = pr.number === hoveredPrNumber;\n return (\n <div key={pr.number}\n ref={(el) => {\n if (el) prRowRefs.current.set(pr.number, el);\n else prRowRefs.current.delete(pr.number);\n }}\n onMouseEnter={() => setHoveredPrNumber(pr.number)}\n onMouseLeave={() => setHoveredPrNumber(null)}\n style={{\n padding: '8px 12px', borderRadius: 8,\n background: isHov\n ? (isDark ? '#18181f' : '#f0f0ff')\n : (isDark ? '#111114' : '#f4f4f6'),\n border: `1px solid ${isHov ? `${sparklineColor}55` : borderColor}`,\n display: 'flex', flexDirection: 'column', gap: 5,\n transition: 'background 0.12s, border-color 0.12s',\n cursor: 'default',\n }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>\n <span style={{ fontSize: 10, fontWeight: 700, color: col, flexShrink: 0 }}>#{pr.number}</span>\n <span style={{ fontSize: 12, color: theme.palette.text.primary, flex: 1,\n whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} title={pr.title}>\n {pr.title}\n </span>\n <span style={{ fontSize: 10, color: mutedColor, flexShrink: 0 }}>\n {new Date(pr.mergedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\n </span>\n <a href={pr.url} target=\"_blank\" rel=\"noopener noreferrer\"\n style={{ color: mutedColor, flexShrink: 0, transition: 'color 0.12s' }}>\n <ExternalLink size={11} />\n </a>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <AnimatedBar widthPct={barPct} color={col} />\n <span style={{ fontSize: 11, fontWeight: 700, color: col, minWidth: 52, textAlign: 'right', flexShrink: 0 }}>\n {formatDuration(pr.durationHours)}\n </span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n </div>\n <style>{`\n @keyframes detailExpandIn {\n from { opacity: 0; transform: translateX(-50%) translateY(-46%); }\n to { opacity: 1; transform: translateX(-50%) translateY(-50%); }\n }\n `}</style>\n </>\n );\n}\n\n// ─── Animated bar ──────────────────────────────────────────────────────────\n\nfunction AnimatedBar({ widthPct, color }: { widthPct: number; color: string }) {\n const [width, setWidth] = useState(0);\n const ref = useRef<HTMLDivElement>(null);\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n\n useEffect(() => {\n const timer = setTimeout(() => setWidth(widthPct), 80);\n return () => clearTimeout(timer);\n }, [widthPct]);\n\n return (\n <div\n ref={ref}\n style={{\n background: isDark ? '#1a1a1e' : '#f0f0f2',\n borderRadius: 4,\n height: 5,\n overflow: 'hidden',\n flex: 1,\n minWidth: 40,\n }}\n >\n <div\n style={{\n height: '100%',\n width: `${width}%`,\n background: color,\n borderRadius: 4,\n transition: 'width 0.55s cubic-bezier(0.4, 0, 0.2, 1)',\n boxShadow: `0 0 6px ${color}88`,\n }}\n />\n </div>\n );\n}\n\n// ─── Metric cards ──────────────────────────────────────────────────────────\n\nfunction MetricCard({\n title,\n metric,\n description,\n lowerIsBetter = false,\n sparklineData,\n sparklineType = 'line',\n sparklineColor,\n weekLabels,\n onExpand,\n expandLabel,\n}: {\n title: string;\n metric: DoraMetricValue;\n description: string;\n lowerIsBetter?: boolean;\n sparklineData?: number[];\n sparklineType?: 'bar' | 'line';\n sparklineColor?: string;\n weekLabels?: string[];\n onExpand?: () => void;\n /** Persistent label shown in the card footer (instead of the hover-only \"expand ↗\"). */\n expandLabel?: string;\n}) {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n const [hovered, setHovered] = useState(false);\n\n const displayValue = metric.unit === 'hours' ? formatDuration(metric.value) : metric.value;\n const displayUnit = metric.unit === 'hours' ? '' : metric.unit;\n const targetValue = metric.unit === 'hours' ? formatDuration(metric.target) : metric.target;\n const targetLabel = lowerIsBetter\n ? `Target: < ${targetValue} ${displayUnit}`.trim()\n : `Target: ≥ ${targetValue} ${displayUnit}`.trim();\n\n const hasSparkline = sparklineData && sparklineData.length >= 2 && weekLabels && weekLabels.length >= 2;\n const delta = hasSparkline ? buildDeltaLabel(sparklineData!, metric.unit, lowerIsBetter) : null;\n const formatVal = (v: number) => metric.unit === 'hours' ? formatDuration(v) : `${Math.round(v * 10) / 10}`;\n\n return (\n <Card\n style={{ cursor: onExpand ? 'pointer' : undefined }}\n onClick={onExpand}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8 }}>\n <CardLabel>{title}</CardLabel>\n {onExpand && (\n <span style={{\n fontSize: 9, fontWeight: 600, color: isDark ? '#3f3f46' : '#d4d4d8',\n opacity: hovered ? 1 : 0, transition: 'opacity 0.15s',\n letterSpacing: '0.05em', textTransform: 'uppercase', flexShrink: 0,\n }}>\n expand ↗\n </span>\n )}\n </div>\n {/* Right-aligned column: delta trend + direction hint stacked */}\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4 }}>\n {delta && (\n <span title={delta.tooltip} style={{\n fontSize: 10, fontWeight: 600, cursor: 'help',\n color: delta.good ? '#4ade80' : '#f87171',\n background: delta.good\n ? (isDark ? 'rgba(74,222,128,0.08)' : 'rgba(74,222,128,0.12)')\n : (isDark ? 'rgba(248,113,113,0.08)' : 'rgba(248,113,113,0.12)'),\n padding: '2px 8px', borderRadius: 4, whiteSpace: 'nowrap',\n }}>\n {delta.text}\n </span>\n )}\n <span style={{\n fontSize: 10, fontWeight: 600,\n color: isDark ? '#3f3f46' : '#d4d4d8',\n whiteSpace: 'nowrap',\n }}>\n {lowerIsBetter ? '↓ lower = better' : '↑ higher = better'}\n </span>\n </div>\n {/* Value row: big number + unit */}\n <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>\n <span style={{ fontSize: 36, fontWeight: 700, color: isDark ? '#f4f4f5' : '#111114', lineHeight: 1 }}>\n {displayValue}\n </span>\n {displayUnit && (\n <span style={{ fontSize: 14, color: isDark ? '#71717a' : '#71717a', fontWeight: 500 }}>\n {displayUnit}\n </span>\n )}\n </div>\n <RatingBadge rating={metric.rating} />\n {hasSparkline && (\n <div style={{ marginTop: 2 }}>\n <CompactSparkline\n values={sparklineData!}\n weekLabels={weekLabels!}\n color={sparklineColor ?? '#FA6400'}\n type={sparklineType}\n formatValue={formatVal}\n />\n </div>\n )}\n <CardDescription>{description}</CardDescription>\n <CardFooter>\n <span style={{ fontWeight: 600, color: isDark ? '#a1a1aa' : '#52525b' }}>\n {targetLabel}\n </span>\n {expandLabel && onExpand && (\n <span style={{\n fontSize: 10, fontWeight: 700,\n color: '#FA6400',\n letterSpacing: '0.04em',\n marginLeft: 'auto',\n }}>\n {expandLabel}\n </span>\n )}\n </CardFooter>\n </Card>\n );\n}\n\nfunction NaCard({ title, description }: { title: string; description: string }) {\n return (\n <Card>\n <CardLabel>{title}</CardLabel>\n <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>\n <span style={{ fontSize: 36, fontWeight: 700, color: '#52525b', lineHeight: 1 }}>N/A</span>\n </div>\n <CardDescription muted>{description}</CardDescription>\n </Card>\n );\n}\n\n// ─── Filter label ──────────────────────────────────────────────────────────\n\nfunction FilterLabel({ children }: { children: React.ReactNode }) {\n const theme = useTheme();\n return (\n <div style={{ fontSize: 11, fontWeight: 600, color: theme.palette.text.primary, marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.06em' }}>\n {children}\n </div>\n );\n}\n\n// ─── Main component ────────────────────────────────────────────────────────\n\nexport function DoraMetricsContent() {\n const { entity } = useEntity();\n const doraApi = useApi(doraMetricsApiRef);\n const muiTheme = useTheme();\n const textPrimary = muiTheme.palette.text.primary;\n const textSecondary = muiTheme.palette.text.secondary;\n\n const projectSlug = entity.metadata.annotations?.[ANNOTATION_PROJECT_SLUG] ?? '';\n const annotations = entity.metadata.annotations ?? {};\n\n // Per-repo overrides from catalog-info.yaml annotations\n const annotationEnvs = parseAnnotationJson<DoraEnvironment[]>(annotations[ANNOTATION_ENVIRONMENTS]);\n const annotationTargets = parseAnnotationJson<Partial<DoraTargets>>(annotations[ANNOTATION_TARGETS]);\n\n const environments: DoraEnvironment[] = annotationEnvs ?? doraApi.getEnvironments();\n const defaultEnv = environments[0];\n const defaultDays = doraApi.getDefaultDays();\n\n // Use environment name as the selection key (branch can be a multi-value pattern)\n const [selectedEnvName, setSelectedEnvName] = useState<string>(defaultEnv?.name ?? '');\n const [selectedDays, setSelectedDays] = useState<string>(String(defaultDays));\n\n const selectedEnv = environments.find(e => e.name === selectedEnvName) ?? defaultEnv;\n const days = parseInt(selectedDays, 10);\n\n const [metrics, setMetrics] = useState<DoraMetrics | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const [history, setHistory] = useState<DoraHistoryPoint[] | null>(null);\n const [expanded, setExpanded] = useState<ExpandedCardData | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n\n if (!projectSlug) {\n setError(new Error(`Entity is missing the \"${ANNOTATION_PROJECT_SLUG}\" annotation.`));\n setLoading(false);\n return () => { cancelled = true; };\n }\n if (!selectedEnv) {\n setError(new Error('No environments configured for DORA metrics.'));\n setLoading(false);\n return () => { cancelled = true; };\n }\n\n setLoading(true);\n setError(null);\n setHistory(null);\n\n doraApi\n .getMetrics(projectSlug, selectedEnv, days, annotationTargets ?? undefined)\n .then(data => {\n if (!cancelled) { setMetrics(data); setLoading(false); }\n })\n .catch((err: Error) => {\n if (!cancelled) { setError(err); setLoading(false); }\n });\n\n doraApi\n .getHistory(projectSlug, selectedEnv, days)\n .then(data => { if (!cancelled) setHistory(data); })\n .catch(() => { /* history is optional — fail silently */ });\n\n return () => { cancelled = true; };\n // selectedEnv?.name is intentional — avoids re-render loops from object identity changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [doraApi, projectSlug, selectedEnv?.name, days, annotationTargets]);\n\n if (loading) return <DoraLoadingOverlay />;\n\n if (error) {\n return (\n <InfoCard title=\"DORA Metrics\">\n <div style={{ padding: 24, color: '#f87171' }}>\n <span style={{ fontWeight: 600 }}>Error: </span>{error.message}\n </div>\n </InfoCard>\n );\n }\n\n if (!metrics) return null;\n\n return (\n <InfoCard title=\" \">\n <div style={{ padding: '4px 0', position: 'relative' }}>\n {/* Detailed overlay */}\n {expanded && (\n <DetailedOverlay data={expanded} days={days} onClose={() => setExpanded(null)} />\n )}\n\n {/* Title */}\n <div style={{ marginBottom: 16 }}>\n <div style={{ fontSize: 22, fontWeight: 700, color: textPrimary, letterSpacing: '-0.02em' }}>DORA Metrics</div>\n {selectedEnv && (\n <div style={{ fontSize: 12, color: textSecondary, marginTop: 5 }}>\n Showing metrics for{' '}\n <strong style={{ color: textPrimary }}>{selectedEnv.name}</strong>\n {' '}—{' '}\n <span style={{ fontFamily: 'monospace' }}>{selectedEnv.branch}</span>\n </div>\n )}\n </div>\n\n {/* Filters row — left aligned */}\n <div style={{ display: 'flex', alignItems: 'flex-end', gap: 16, flexWrap: 'wrap', marginBottom: 20 }}>\n {environments.length > 1 && (\n <div>\n <FilterLabel>Environment</FilterLabel>\n <Select value={selectedEnvName} onValueChange={setSelectedEnvName}>\n <SelectTrigger><SelectValue placeholder=\"Select environment\" /></SelectTrigger>\n <SelectContent>\n {environments.map(env => (\n <SelectItem key={env.name} value={env.name}>\n {env.name} ({env.branch})\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n\n <div>\n <FilterLabel>Date Range</FilterLabel>\n <Select value={selectedDays} onValueChange={setSelectedDays}>\n <SelectTrigger><SelectValue placeholder=\"Select date range\" /></SelectTrigger>\n <SelectContent>\n {DATE_RANGE_OPTIONS.map(opt => (\n <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {/* Metric cards — sparklines inline, click to expand */}\n {(() => {\n const wkLabels = history?.map(h => h.weekLabel) ?? [];\n const deployData = history?.map(h => h.deploymentCount) ?? [];\n const leadData = history?.map(h => h.leadTimeHours) ?? [];\n const cfrData = history?.map(h => h.changeFailureRate ?? 0) ?? [];\n const mttrData = history?.map(h => h.mttrHours ?? 0) ?? [];\n\n const bmMs = history?.map(h => h.bucketMidMs) ?? [];\n\n const mkExpand = (\n title: string, metric: DoraMetricValue, description: string,\n lowerIsBetter: boolean, sparklineValues: number[],\n sparklineType: 'bar' | 'line', sparklineColor: string, prs?: PrDetail[],\n prListLabel?: string, tickUnit?: 'hours' | 'count',\n ) => () => setExpanded({\n title, metric, description, lowerIsBetter,\n sparklineValues, sparklineType, sparklineColor, tickUnit,\n weekLabels: wkLabels, bucketMidMs: bmMs, prs, prListLabel,\n });\n\n // Per-bucket hotfix counts derived from hotfix PRs + bucket midpoints.\n const hotfixSparkData: number[] = bmMs.map(() => 0);\n if (bmMs.length > 0 && metrics?.numberOfHotfixes?.slowestPRs) {\n for (const pr of metrics.numberOfHotfixes.slowestPRs) {\n const t = new Date(pr.mergedAt).getTime();\n let bestIdx = 0, bestDist = Infinity;\n bmMs.forEach((mid, i) => { const d = Math.abs(t - mid); if (d < bestDist) { bestDist = d; bestIdx = i; } });\n hotfixSparkData[bestIdx]++;\n }\n }\n\n return (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>\n <MetricCard\n title=\"Deployment Frequency\"\n metric={metrics.deploymentFrequency}\n description=\"How often code is deployed to this branch\"\n sparklineData={deployData}\n sparklineType=\"bar\"\n sparklineColor=\"#FA6400\"\n weekLabels={wkLabels}\n onExpand={mkExpand('Deployment Frequency', metrics.deploymentFrequency,\n 'How often code is deployed to this branch', false, deployData, 'bar', '#FA6400',\n metrics.deploymentFrequency.slowestPRs, undefined, 'count')}\n />\n <MetricCard\n title=\"Lead Time for Changes\"\n metric={metrics.leadTime}\n description=\"Average time from PR creation to merge\"\n lowerIsBetter\n sparklineData={leadData}\n sparklineType=\"line\"\n sparklineColor=\"#818cf8\"\n weekLabels={wkLabels}\n onExpand={mkExpand('Lead Time for Changes', metrics.leadTime,\n 'Average time from PR creation to merge', true, leadData, 'line', '#818cf8', metrics.leadTime.slowestPRs, undefined, 'hours')}\n />\n {metrics.changeFailureRate !== null ? (\n <MetricCard\n title=\"Change Failure Rate\"\n metric={metrics.changeFailureRate}\n description=\"% of all merged PRs that were hotfixes\"\n lowerIsBetter\n sparklineData={cfrData}\n sparklineType=\"line\"\n sparklineColor=\"#f87171\"\n weekLabels={wkLabels}\n onExpand={mkExpand('Change Failure Rate', metrics.changeFailureRate,\n '% of all merged PRs that were hotfixes', true, cfrData, 'line', '#f87171', undefined, undefined, 'count')}\n />\n ) : (\n <NaCard title=\"Change Failure Rate\" description=\"Only tracked on production branches\" />\n )}\n {metrics.mttr !== null ? (\n <MetricCard\n title=\"Mean Time to Restore\"\n metric={metrics.mttr}\n description=\"Avg time from hotfix PR open to merge\"\n lowerIsBetter\n sparklineData={mttrData}\n sparklineType=\"line\"\n sparklineColor=\"#fbbf24\"\n weekLabels={wkLabels}\n onExpand={mkExpand('Mean Time to Restore', metrics.mttr,\n 'Avg time from hotfix PR open to merge', true, mttrData, 'line', '#fbbf24', metrics.mttr.slowestPRs, undefined, 'hours')}\n />\n ) : (\n <NaCard title=\"Mean Time to Restore\" description=\"Only tracked on production branches\" />\n )}\n {metrics.numberOfHotfixes !== null ? (\n <MetricCard\n title=\"Hotfixes to Production\"\n metric={metrics.numberOfHotfixes}\n description={`PRs labeled '${selectedEnv?.label ?? 'hotfix'}' merged to production`}\n lowerIsBetter\n expandLabel={metrics.numberOfHotfixes.value > 0 ? `View ${metrics.numberOfHotfixes.value} PR${metrics.numberOfHotfixes.value !== 1 ? 's' : ''} →` : undefined}\n onExpand={mkExpand(\n 'Hotfixes to Production',\n metrics.numberOfHotfixes,\n `PRs labeled '${selectedEnv?.label ?? 'hotfix'}' merged to production`,\n true,\n hotfixSparkData,\n 'bar',\n '#f87171',\n metrics.numberOfHotfixes.slowestPRs,\n 'All hotfixes',\n 'count',\n )}\n />\n ) : (\n <NaCard title=\"Hotfixes to Production\" description=\"Only tracked on production branches\" />\n )}\n </div>\n );\n })()}\n\n </div>\n </InfoCard>\n );\n}\n"],"names":["max"],"mappings":";;;;;;;;;;;;AAmBA,MAAM,uBAAA,GAA0B,yBAAA;AAEhC,MAAM,uBAAA,GAA0B,2BAAA;AAEhC,MAAM,kBAAA,GAAqB,sBAAA;AAG3B,SAAS,oBAAuB,GAAA,EAAwC;AACtE,EAAA,IAAI,CAAC,KAAK,OAAO,MAAA;AACjB,EAAA,IAAI;AAAE,IAAA,OAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,EAAQ,CAAA,CAAA,MAAQ;AAAE,IAAA,OAAO,MAAA;AAAA,EAAW;AACjE;AAEA,MAAM,kBAAA,GAAqB;AAAA,EACzB,EAAE,KAAA,EAAO,GAAA,EAAM,KAAA,EAAO,aAAA,EAAc;AAAA,EACpC,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,cAAA,EAAe;AAAA,EACrC,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,cAAA,EAAe;AAAA,EACrC,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,cAAA,EAAe;AAAA,EACrC,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,cAAA;AACxB,CAAA;AAIA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,EAAE,CAAA;AAC1C,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,YAAA,IAAgB,KAAK,EAAA,CAAG,CAAA;AAC7C,EAAA,MAAM,IAAI,IAAA,CAAK,KAAA,CAAO,YAAA,IAAgB,EAAA,GAAK,MAAO,EAAE,CAAA;AACpD,EAAA,MAAM,IAAI,YAAA,GAAe,EAAA;AACzB,EAAA,IAAI,CAAA,GAAI,CAAA,EAAG,OAAO,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,CAAA,CAAA,GAAM,CAAA,EAAG,CAAC,CAAA,CAAA,CAAA;AAC9C,EAAA,IAAI,CAAA,GAAI,CAAA,EAAG,OAAO,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,CAAA,CAAA,GAAM,CAAA,EAAG,CAAC,CAAA,CAAA,CAAA;AAC9C,EAAA,OAAO,GAAG,CAAC,CAAA,CAAA,CAAA;AACb;AAGA,MAAM,oBAAoB,CAAC,SAAA,EAAW,SAAA,EAAW,SAAA,EAAW,WAAW,SAAS,CAAA;AAEhF,SAAS,aAAA,CAAc,OAAe,KAAA,EAAuB;AAC3D,EAAA,IAAI,KAAA,IAAS,CAAA,EAAG,OAAO,iBAAA,CAAkB,CAAC,CAAA;AAC1C,EAAA,MAAM,IAAA,GAAA,CAAQ,iBAAA,CAAkB,MAAA,GAAS,CAAA,KAAM,KAAA,GAAQ,CAAA,CAAA;AACvD,EAAA,OAAO,iBAAA,CAAkB,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,IAAI,CAAC,CAAA;AACnD;AAKA,SAAS,eAAA,CACP,MAAA,EACA,IAAA,EACA,aAAA,EACyD;AACzD,EAAA,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,OAAO,IAAA;AAC9B,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,CAAC,CAAA;AACrC,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,CAAC,CAAA;AACrC,EAAA,MAAM,OAAO,IAAA,GAAO,IAAA;AACpB,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,GAAI,MAAO,OAAO,IAAA;AACnC,EAAA,MAAM,IAAA,GAAO,aAAA,GAAgB,IAAA,GAAO,CAAA,GAAI,IAAA,GAAO,CAAA;AAC/C,EAAA,MAAM,KAAA,GAAQ,IAAA,GAAO,CAAA,GAAI,QAAA,GAAM,QAAA;AAE/B,EAAA,MAAM,MAAM,CAAC,CAAA,KACX,IAAA,KAAS,OAAA,GAAa,eAAe,CAAC,CAAA,GACtC,IAAA,KAAS,GAAA,GAAa,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,EAAE,CAAA,CAAA,CAAA,GAChD,IAAA,KAAS,UAAA,GAAa,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,EAAE,CAAA,GAAA,CAAA,GAC1B,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,GAAI,EAAE,CAAA,GAAI,EAAE,IAAI,IAAI,CAAA,CAAA;AAE1D,EAAA,MAAM,SAAA,GAAY,OAAO,WAAA,GAAc,WAAA;AACvC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAC,CAAC,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAAA,IAClD,OAAA,EAAS,kBAAkB,GAAA,CAAI,IAAI,CAAC,CAAA,uBAAA,EAAuB,GAAA,CAAI,IAAI,CAAC;AAAA,kEAAA,CAAA;AAAA,IACpE;AAAA,GACF;AACF;AAIA,MAAM,gBAAA,GAAmB;AAAA,EACvB,2BAAA;AAAA,EACA,uBAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AAAA,EACA;AACF,CAAA;AACA,MAAM,MAAA,GAAS,CAAC,OAAA,EAAS,OAAA,EAAS,SAAS,QAAQ,CAAA;AACnD,MAAM,WAAA,GAAc,8EAAA;AACpB,MAAM,SAAA,GAAc,mDAAA;AAEpB,SAAS,kBAAA,GAAqB;AAC5B,EAAA,MAAM,QAAS,QAAA,EAAS;AACxB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AAEtC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,CAAC,CAAA;AAChD,EAAA,MAAM,CAAC,MAAA,EAAS,SAAS,CAAA,GAAa,SAAS,CAAC,CAAA;AAChD,EAAA,MAAM,CAAC,MAAA,EAAS,SAAS,CAAA,GAAa,SAAS,CAAC,CAAA;AAEhD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,EAAA,GAAK,WAAA,CAAY,MAAM,cAAA,CAAe,CAAA,CAAA,KAAA,CAAM,IAAI,CAAA,IAAK,MAAA,CAAO,MAAM,CAAA,EAAG,GAAG,CAAA;AAC9E,IAAA,OAAO,MAAM,cAAc,EAAE,CAAA;AAAA,EAC/B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,EAAA,GAAK,YAAY,MAAM;AAAE,MAAA,SAAA,CAAU,CAAA,CAAA,KAAA,CAAM,CAAA,GAAI,CAAA,IAAK,gBAAA,CAAiB,MAAM,CAAA;AAAG,MAAA,SAAA,CAAU,CAAA,CAAA,KAAK,IAAI,CAAC,CAAA;AAAA,IAAG,GAAG,IAAI,CAAA;AAChH,IAAA,OAAO,MAAM,cAAc,EAAE,CAAA;AAAA,EAC/B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,UAAA,GAAa,SACf,sHAAA,GACA,mGAAA;AAEJ,EAAA,MAAM,UAAA,GAAgB,SAAS,SAAA,GAAY,SAAA;AAC3C,EAAA,MAAM,aAAA,GAAgB,SAAS,SAAA,GAAY,SAAA;AAC3C,EAAA,MAAM,WAAA,GAAgB,SAAS,wBAAA,GAA2B,kBAAA;AAE1D,EAAA,uBACE,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBAEE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAW,MAAC,KAAA,EAAO;AAAA,IACtB,QAAA,EAAU,OAAA;AAAA,IAAS,KAAA,EAAO,CAAA;AAAA,IAAG,MAAA,EAAQ,GAAA;AAAA,IACrC,cAAA,EAAgB,YAAA;AAAA,IAChB,oBAAA,EAAsB,YAAA;AAAA,IACtB,UAAA,EAAY,SAAS,kBAAA,GAAqB;AAAA,GAC5C,EAAG,CAAA,kBAGH,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IACV,QAAA,EAAU,OAAA;AAAA,IAAS,MAAA,EAAQ,GAAA;AAAA,IAC3B,GAAA,EAAK,KAAA;AAAA,IAAO,IAAA,EAAM,KAAA;AAAA,IAClB,SAAA,EAAW,mCAAA;AAAA,IACX,OAAA,EAAS,MAAA;AAAA,IAAQ,aAAA,EAAe,QAAA;AAAA,IAAU,UAAA,EAAY,QAAA;AAAA,IACtD,OAAA,EAAS,gBAAA;AAAA,IACT,YAAA,EAAc,EAAA;AAAA,IACd,UAAA,EAAY,SAAS,mBAAA,GAAsB,wBAAA;AAAA,IAC3C,MAAA,EAAQ,aAAa,WAAW,CAAA,CAAA;AAAA,IAChC,SAAA,EAAW,SACP,8DAAA,GACA,8BAAA;AAAA,IACJ,cAAA,EAAgB,YAAA;AAAA,IAChB,oBAAA,EAAsB,YAAA;AAAA,IACtB,SAAA,EAAW,oDAAA;AAAA,IACX,QAAA,EAAU;AAAA,GACZ,EAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAW,MAAC,KAAA,EAAO;AAAA,IACtB,QAAA,EAAU,UAAA;AAAA,IAAY,KAAA,EAAO,GAAA;AAAA,IAAK,MAAA,EAAQ,GAAA;AAAA,IAC1C,IAAA,EAAM,KAAA;AAAA,IAAO,GAAA,EAAK,KAAA;AAAA,IAClB,SAAA,EAAW,mCAAA;AAAA,IACX,UAAA,EAAY,CAAA,mCAAA,EAAsC,MAAA,GAAS,sBAAA,GAAyB,sBAAsB,CAAA,qBAAA,CAAA;AAAA,IAC1G,SAAA,EAAW,qCAAA;AAAA,IACX,aAAA,EAAe,MAAA;AAAA,IAAQ,YAAA,EAAc;AAAA,GACvC,EAAG,CAAA,kBAGD,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,CAAA,EAAG,YAAA,EAAc,CAAA,EAAE,EAAA,EAC3D,CAAC,GAAA,EAAI,GAAA,EAAI,GAAA,EAAI,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,MAAA,EAAQ,CAAA,qBAC9B,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO;AAAA,IACnB,UAAA,EAAY,WAAA;AAAA,IAAa,UAAA,EAAY,GAAA;AAAA,IAAK,QAAA,EAAU,GAAA;AAAA,IACpD,KAAA,EAAO,SAAA;AAAA,IAAW,aAAA,EAAe,QAAA;AAAA,IAAU,OAAA,EAAS,cAAA;AAAA,IACpD,SAAA,EAAW,cAAA;AAAA,IAAgB,UAAA;AAAA,IAC3B,gBAAA,EAAkB,eAAA;AAAA,IAClB,SAAA,EAAW,CAAA,kDAAA,EAAqD,CAAA,GAAI,GAAG,CAAA,MAAA;AAAA,OAEtE,MACH,CACD,CACH,CAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,IAAE,UAAA,EAAY,4BAAA;AAAA,IAA8B,QAAA,EAAU,EAAA;AAAA,IAAI,UAAA,EAAY,GAAA;AAAA,IACjF,aAAA,EAAe,QAAA;AAAA,IAAU,KAAA,EAAO,aAAA;AAAA,IAAe,YAAA,EAAc,EAAA;AAAA,IAC7D,SAAA,EAAW;AAAA,GAAiC,EAAA,EAAG,SAEjD,CAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,KAAK,CAAA,EAAG,YAAA,EAAc,IAAI,SAAA,EAAW,+BAAA,MACvF,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,EAAO,CAAA,KAAM;AACxB,IAAA,MAAM,WAAa,CAAA,KAAM,WAAA;AACzB,IAAA,MAAM,aAAa,CAAA,GAAI,WAAA;AACvB,IAAA,2CACG,KAAA,EAAA,EAAI,GAAA,EAAK,OAAO,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,UAAA,EAAY,UAAU,GAAA,EAAK,CAAA,sBACpE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,OAAA,EAAS,QAAQ,aAAA,EAAe,QAAA,EAAU,UAAA,EAAY,QAAA,EAAU,KAAK,CAAA,EAAG,KAAA,EAAO,IAAG,EAAA,kBAC9F,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,UAAU,UAAA,EAAY,KAAA,EAAO,IAAI,MAAA,EAAQ,EAAA,MACpD,QAAA,oBACC,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,MAAE,QAAA,EAAU,UAAA;AAAA,MAAY,KAAA,EAAO,EAAA;AAAA,MAAI,YAAA,EAAc,KAAA;AAAA,MAC3D,MAAA,EAAQ,iCAAA;AAAA,MAAmC,SAAA,EAAW;AAAA,KAAgC,EAAG,CAAA,kBAE7F,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,MACV,KAAA,EAAO,EAAA;AAAA,MAAI,MAAA,EAAQ,EAAA;AAAA,MAAI,YAAA,EAAc,KAAA;AAAA,MACrC,UAAA,EAAa,QAAA,IAAY,UAAA,GAAc,SAAA,GAAa,SAAS,SAAA,GAAY,SAAA;AAAA,MACzE,QAAQ,CAAA,UAAA,EAAc,QAAA,IAAY,aAAc,SAAA,GAAa,MAAA,GAAS,YAAY,SAAU,CAAA,CAAA;AAAA,MAC5F,SAAA,EAAW,WAAW,8BAAA,GAAiC,MAAA;AAAA,MACvD,UAAA,EAAY;AAAA,KACd,EAAG,CACL,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,MAAE,UAAA,EAAY,4BAAA;AAAA,MAA8B,QAAA,EAAU,CAAA;AAAA,MAAG,UAAA,EAAY,GAAA;AAAA,MAChF,aAAA,EAAe,QAAA;AAAA,MAAU,aAAA,EAAe,WAAA;AAAA,MACxC,KAAA,EAAO,QAAA,GAAW,SAAA,GAAY,UAAA,GAAa,UAAA,GAAa,UAAA;AAAA,MACxD,UAAA,EAAY;AAAA,KAAa,EAAA,EACxB,KACH,CACF,CAAA,EACC,CAAA,GAAI,OAAO,MAAA,GAAS,CAAA,oBACnB,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,MAAE,KAAA,EAAO,EAAA;AAAA,MAAI,MAAA,EAAQ,CAAA;AAAA,MAAG,YAAA,EAAc,EAAA;AAAA,MAAI,QAAA,EAAU,UAAA;AAAA,MAAY,QAAA,EAAU,QAAA;AAAA,MACpF,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,MAAW,YAAA,EAAc;AAAA,KAAE,EAAA,kBAC5D,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,MACV,QAAA,EAAU,UAAA;AAAA,MAAY,KAAA,EAAO,CAAA;AAAA,MAAG,YAAA,EAAc,CAAA;AAAA,MAAG,UAAA,EAAY,SAAA;AAAA,MAC7D,eAAA,EAAiB,aAAA;AAAA,MACjB,WAAW,CAAA,OAAA,EAAU,CAAA,GAAI,cAAc,CAAA,GAAI,QAAA,GAAW,MAAM,CAAC,CAAA,CAAA,CAAA;AAAA,MAC7D,UAAA,EAAY;AAAA,KACd,EAAG,CACL,CAEJ,CAAA;AAAA,EAEJ,CAAC,CACH,CAAA,sCAGC,MAAA,EAAA,EAAK,GAAA,EAAK,QAAQ,KAAA,EAAO;AAAA,IAAE,UAAA,EAAY,SAAA;AAAA,IAAW,QAAA,EAAU,EAAA;AAAA,IAC3D,KAAA,EAAO,UAAA;AAAA,IAAY,aAAA,EAAe,QAAA;AAAA,IAAU,SAAA,EAAW;AAAA,OACtD,gBAAA,CAAiB,MAAM,CAC1B,CAEF,CAAA,sCAEC,OAAA,EAAA,IAAA,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAsBN,CACJ,CAAA;AAEJ;AAIA,SAAS,gBAAA,CAAiB;AAAA,EACxB,MAAA;AAAA,EACA,UAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA,GAAS;AACX,CAAA,EAOG;AACD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAwB,IAAI,CAAA;AAE5D,EAAA,MAAM,EAAA,GAAK,GAAA;AACX,EAAA,MAAM,WAAA,GAAc,SAAS,wBAAA,GAA2B,kBAAA;AACxD,EAAA,MAAM,SAAA,GAAc,SAAS,SAAA,GAAY,SAAA;AACzC,EAAA,MAAM,UAAA,GAAc,SAAS,SAAA,GAAY,SAAA;AACzC,EAAA,MAAM,WAAA,GAAc,SAAS,SAAA,GAAY,SAAA;AACzC,EAAA,MAAM,IAAA,GAAO,4BAAA;AAEb,EAAA,MAAM,eAAA,GAAkB,CAAC,CAAA,KAAuC;AAC9D,IAAA,MAAM,IAAA,GAAO,CAAA,CAAE,aAAA,CAAc,qBAAA,EAAsB;AACnD,IAAA,MAAM,IAAA,GAAA,CAAQ,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,QAAQ,IAAA,CAAK,KAAA;AAC5C,IAAA,WAAA,CAAY,KAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,OAAO,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,QAAQ,MAAA,CAAO,MAAA,GAAS,CAAA,CAAE,CAAC,CAAC,CAAC,CAAA;AAAA,EAC9F,CAAA;AAEA,EAAA,IAAI,SAAS,KAAA,EAAO;AAClB,IAAA,MAAMA,IAAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,QAAQ,CAAC,CAAA;AACjC,IAAA,MAAM,GAAA,GAAM,CAAA;AACZ,IAAA,MAAM,QAAQ,EAAA,GAAK,GAAA,IAAO,MAAA,CAAO,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA;AAEvD,IAAA,MAAM,SAAS,QAAA,KAAa,IAAA,GACxB,YAAY,IAAA,GAAO,GAAA,CAAA,GAAO,OAAO,CAAA,GACjC,IAAA;AACJ,IAAA,MAAM,QAAA,GAAW,WAAW,IAAA,GAAO,IAAA,CAAK,IAAI,MAAA,GAAS,EAAA,EAAI,EAAA,GAAK,EAAE,CAAA,GAAI,CAAA;AAEpE,IAAA,2CACG,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAAI,OAAA,EAAS,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,QAAI,KAAA,EAAM,MAAA;AAAA,QAAO,KAAA,EAAO,EAAE,OAAA,EAAS,OAAA,EAAS,UAAU,SAAA,EAAU;AAAA,QAC/F,WAAA,EAAa,eAAA;AAAA,QAAiB,YAAA,EAAc,MAAM,WAAA,CAAY,IAAI;AAAA,OAAA;AAAA,MACjE,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM;AACpB,QAAA,MAAM,IAAI,IAAA,CAAK,GAAA,CAAI,GAAI,CAAA,GAAIA,IAAAA,IAAQ,SAAS,CAAA,CAAE,CAAA;AAC9C,QAAA,uBACE,KAAA,CAAA,aAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YAAK,GAAA,EAAK,CAAA;AAAA,YAAG,CAAA,EAAG,KAAK,IAAA,GAAO,GAAA,CAAA;AAAA,YAAM,GAAG,MAAA,GAAS,CAAA;AAAA,YAAG,KAAA,EAAO,IAAA;AAAA,YAAM,MAAA,EAAQ,CAAA;AAAA,YAAG,EAAA,EAAI,GAAA;AAAA,YAC5E,IAAA,EAAM,KAAA;AAAA,YAAO,SAAS,CAAA,KAAM,QAAA,GAAW,IAAI,GAAA,GAAO,CAAA,GAAI,OAAO,MAAA,GAAU;AAAA;AAAA,SAAM;AAAA,MAEnF,CAAC,CAAA;AAAA,sBACD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,EAAA,EAAI,CAAA,EAAG,IAAI,MAAA,GAAS,GAAA,EAAK,EAAA,EAAI,EAAA,EAAI,IAAI,MAAA,GAAS,GAAA,EAAK,MAAA,EAAQ,WAAA,EAAa,aAAa,CAAA,EAAG,CAAA;AAAA,MAC7F,QAAA,KAAa,QAAQ,MAAA,KAAW,IAAA,8EAE7B,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,CAAA,EAAG,OAAO,EAAA,EAAI,MAAA,EAAQ,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,IAAA,EAAM,WAAW,MAAA,EAAQ,UAAA,EAAY,WAAA,EAAa,CAAA,EAAG,CAAA,kBAC5G,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,QAAA,GAAW,IAAI,CAAA,EAAG,EAAA,EAAI,YAAW,QAAA,EAAS,IAAA,EAAM,WAAA,EAAa,QAAA,EAAU,CAAA,EAAG,UAAA,EAAY,KAAK,UAAA,EAAY,IAAA,EAAA,EAC7G,UAAA,CAAW,QAAQ,CAAA,EAAE,IAAA,EAAG,YAAY,MAAA,CAAO,QAAQ,CAAC,CACvD,CACF;AAAA,KAEJ,sCACC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,gBAAgB,eAAA,EAAiB,SAAA,EAAW,GAAE,EAAA,kBAC3E,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,CAAA,EAAG,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MAAc,UAAA,CAAW,CAAC,CAAE,CAAA,kBACpF,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,CAAA,EAAG,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MAAc,UAAA,CAAW,UAAA,CAAW,SAAS,CAAC,CAAE,CAC1G,CACF,CAAA;AAAA,EAEJ;AAEA,EAAA,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,OAAO,IAAA;AAC9B,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,QAAQ,IAAI,CAAA;AACpC,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,MAAM,CAAA;AAC9B,EAAA,MAAM,KAAA,GAAQ,GAAA,GAAM,GAAA,IAAO,GAAA,IAAO,CAAA;AAClC,EAAA,MAAM,GAAA,GAAM,CAAA;AACZ,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,GAAA,CAAI,CAAC,GAAG,CAAA,KAAwB;AAAA,IAChD,KAAK,MAAA,CAAO,MAAA,GAAS,CAAA,CAAA,IAAO,EAAA,GAAK,MAAM,CAAA,CAAA,GAAK,GAAA;AAAA,IAC5C,MAAA,GAAS,MAAM,CAAA,GAAA,CAAO,CAAA,GAAI,OAAO,KAAA,IAAU,MAAA,GAAS,GAAA,GAAM,CAAA,GAAI,CAAA,CAAA,GAAK;AAAA,GACrE,CAAA;AACD,EAAA,MAAM,CAAA,GAAI,GAAA,CAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,GAAA,GAAM,GAAG,CAAA,EAAG,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,EAAI,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAClG,EAAA,MAAM,IAAA,GAAO,GAAG,CAAC,CAAA,EAAA,EAAK,IAAI,GAAA,CAAI,MAAA,GAAO,CAAC,CAAA,CAAE,CAAC,CAAC,CAAA,CAAA,EAAI,MAAM,KAAK,GAAA,CAAI,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAA,EAAA,CAAA;AAC5E,EAAA,MAAM,OAAA,GAAU,QAAA,KAAa,IAAA,GAAO,GAAA,CAAI,QAAQ,CAAA,GAAI,IAAA;AACpD,EAAA,MAAM,SAAA,GAAY,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAA,GAAI,EAAA,EAAI,EAAA,GAAK,EAAE,CAAA,GAAI,CAAA;AAEjE,EAAA,2CACG,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAAI,OAAA,EAAS,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,MAAI,KAAA,EAAM,MAAA;AAAA,MAAO,KAAA,EAAO,EAAE,OAAA,EAAS,OAAA,EAAS,UAAU,SAAA,EAAU;AAAA,MAC/F,WAAA,EAAa,eAAA;AAAA,MAAiB,YAAA,EAAc,MAAM,WAAA,CAAY,IAAI;AAAA,KAAA;AAAA,wCACjE,MAAA,EAAA,EAAK,CAAA,EAAG,MAAM,IAAA,EAAM,KAAA,EAAO,SAAS,IAAA,EAAM,CAAA;AAAA,oBAC3C,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAM,IAAA,EAAK,MAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,WAAA,EAAa,GAAA,EAAK,cAAA,EAAe,OAAA,EAAQ,aAAA,EAAc,OAAA,EAAQ,CAAA;AAAA,IACrG,GAAA,CAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA,qBAChB,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,GAAA,EAAK,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,CAAA,EAAG,CAAA,KAAM,QAAA,GAAW,GAAA,GAAM,CAAA,KAAM,GAAA,CAAI,MAAA,GAAS,CAAA,GAAI,GAAA,GAAM,CAAA,EAAG,IAAA,EAAM,KAAA,EAAO,CACtG,CAAA;AAAA,oBACD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,EAAA,EAAI,CAAA,EAAG,IAAI,MAAA,GAAS,GAAA,EAAK,EAAA,EAAI,EAAA,EAAI,IAAI,MAAA,GAAS,GAAA,EAAK,MAAA,EAAQ,WAAA,EAAa,aAAa,CAAA,EAAG,CAAA;AAAA,IAC7F,OAAA,IAAW,QAAA,KAAa,IAAA,oBACvB,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,sCACG,MAAA,EAAA,EAAK,EAAA,EAAI,OAAA,CAAQ,CAAC,GAAG,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,OAAA,CAAQ,CAAC,CAAA,EAAG,EAAA,EAAI,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,WAAA,EAAa,CAAA,EAAG,eAAA,EAAgB,OAAM,OAAA,EAAS,GAAA,EAAK,CAAA,kBAC5H,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,SAAA,EAAW,CAAA,EAAG,CAAA,EAAG,OAAO,EAAA,EAAI,MAAA,EAAQ,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,IAAA,EAAM,SAAA,EAAW,MAAA,EAAQ,YAAY,WAAA,EAAa,CAAA,EAAG,CAAA,kBAC7G,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,SAAA,GAAY,EAAA,EAAI,CAAA,EAAG,IAAI,UAAA,EAAW,QAAA,EAAS,IAAA,EAAM,WAAA,EAAa,QAAA,EAAU,CAAA,EAAG,UAAA,EAAY,GAAA,EAAK,YAAY,IAAA,EAAA,EAC9G,UAAA,CAAW,QAAQ,CAAA,EAAE,MAAG,WAAA,CAAY,MAAA,CAAO,QAAQ,CAAC,CACvD,CACF;AAAA,GAEJ,sCACC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,gBAAgB,eAAA,EAAiB,SAAA,EAAW,GAAE,EAAA,kBAC3E,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,CAAA,EAAG,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MAAc,UAAA,CAAW,CAAC,CAAE,CAAA,kBACpF,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,CAAA,EAAG,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MAAc,UAAA,CAAW,UAAA,CAAW,SAAS,CAAC,CAAE,CAC1G,CACF,CAAA;AAEJ;AAWA,SAAS,aAAA,CAAc;AAAA,EACrB,MAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,eAAA;AAAA,EACA;AACF,CAAA,EAYG;AACD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAwB,IAAI,CAAA;AAC5D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAmC,IAAI,CAAA;AACrE,EAAA,MAAM,YAAA,GAAe,OAAuB,IAAI,CAAA;AAEhD,EAAA,MAAM,CAAA,GAAI,KAAK,CAAA,GAAI,GAAA;AACnB,EAAA,MAAM,GAAA,GAAM,EAAE,GAAA,EAAK,EAAA,EAAI,OAAO,EAAA,EAAI,MAAA,EAAQ,EAAA,EAAI,IAAA,EAAM,EAAA,EAAG;AACvD,EAAA,MAAM,EAAA,GAAK,CAAA,GAAI,GAAA,CAAI,IAAA,GAAO,GAAA,CAAI,KAAA;AAC9B,EAAA,MAAM,EAAA,GAAK,CAAA,GAAI,GAAA,CAAI,GAAA,GAAM,GAAA,CAAI,MAAA;AAG7B,EAAA,MAAM,aAAA,GAAgB,GAAA,IAAO,GAAA,CAAI,MAAA,GAAS,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,GAAA,CAAI,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,aAAa,CAAC,CAAA,GAAI,CAAA;AAC3F,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,GAAG,MAAA,CAAO,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,GAAI,CAAC,CAAA,EAAG,aAAA,EAAe,IAAI,CAAA;AAC7E,EAAA,MAAM,MAAA,GAAS,aAAa,OAAA,GACxB,aAAA,CAAc,UAAU,CAAA,GACxB,QAAA,KAAa,UACb,cAAA,CAAe,UAAU,IACzB,CAAC,CAAA,EAAG,MAAM,GAAA,EAAK,IAAA,EAAM,CAAC,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,GAAI,UAAU,CAAA;AACnD,EAAA,MAAM,OAAA,GAAU,CAAA;AAChB,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,CAAC,CAAA;AACxC,EAAA,MAAM,SAAA,GAAY,UAAU,OAAA,IAAW,CAAA;AACvC,EAAA,MAAM,SAAS,CAAC,CAAA,KAAc,EAAA,GAAA,CAAO,CAAA,GAAI,WAAW,SAAA,GAAa,EAAA;AAGjE,EAAA,MAAM,WAAW,IAAA,CAAK,GAAA,KAAQ,IAAA,GAAO,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AACpD,EAAA,MAAM,OAAA,GAAW,IAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AACvC,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,KAAe,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,EAAA,EAAA,CAAM,EAAA,GAAK,QAAA,IAAY,OAAA,GAAW,EAAE,CAAC,CAAA;AAExF,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAc,WAAA,CAAY,WAAW,MAAA,CAAO,MAAA,GACtD,MAAM,WAAA,CAAY,CAAC,CAAC,CAAA,GACnB,MAAA,CAAO,SAAS,CAAA,GAAK,CAAA,IAAK,OAAO,MAAA,GAAS,CAAA,CAAA,GAAM,KAAK,EAAA,GAAK,CAAA;AAE/D,EAAA,MAAM,MAA0B,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,EAAG,MAAM,CAAC,GAAA,CAAI,IAAA,GAAO,IAAA,CAAK,CAAC,CAAA,EAAG,GAAA,CAAI,MAAM,MAAA,CAAO,CAAC,CAAC,CAAC,CAAA;AAC9F,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,GAAA,GAAM,GAAG,CAAA,EAAG,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,EAAI,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACzG,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,MAAA,GAAS,CAAA,GAC1B,CAAA,EAAG,QAAQ,CAAA,EAAA,EAAK,GAAA,CAAI,GAAA,CAAI,MAAA,GAAO,CAAC,CAAA,CAAE,CAAC,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,GAAA,GAAI,EAAE,CAAA,EAAA,EAAK,GAAA,CAAI,CAAC,CAAA,CAAE,CAAC,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,GAAA,GAAI,EAAE,CAAA,EAAA,CAAA,GAC9E,EAAA;AAEJ,EAAA,MAAM,GAAA,GAAO,CAAA;AACb,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,GAAS,CAAA,GAAA,CAAK,EAAA,GAAK,OAAO,MAAA,CAAO,MAAA,GAAS,CAAA,CAAA,IAAM,MAAA,CAAO,MAAA,GAAS,EAAA;AAEpF,EAAA,MAAM,SAAA,GAAa,SAAS,wBAAA,GAA2B,kBAAA;AACvD,EAAA,MAAM,SAAA,GAAa,SAAS,SAAA,GAAY,SAAA;AACxC,EAAA,MAAM,UAAA,GAAa,SAAS,SAAA,GAAY,SAAA;AACxC,EAAA,MAAM,IAAA,GAAa,4BAAA;AACnB,EAAA,MAAM,SAAa,CAAA,GAAA,EAAM,KAAA,CAAM,OAAA,CAAQ,GAAA,EAAI,EAAE,CAAC,CAAA,CAAA;AAG9C,EAAA,MAAM,aAAA,GAAgB,CAAC,EAAA,KAAuB;AAC5C,IAAA,IAAI,WAAA,CAAY,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AACrC,IAAA,IAAI,IAAA,GAAO,GAAG,QAAA,GAAW,QAAA;AACzB,IAAA,WAAA,CAAY,OAAA,CAAQ,CAAC,GAAA,EAAK,CAAA,KAAM;AAC9B,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAA,GAAK,GAAG,CAAA;AAC3B,MAAA,IAAI,IAAI,QAAA,EAAU;AAAE,QAAA,QAAA,GAAW,CAAA;AAAG,QAAA,IAAA,GAAO,CAAA;AAAA,MAAG;AAAA,IAC9C,CAAC,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,kBAAA,GAAqB,CAAC,CAAA,KAAuC;AACjE,IAAA,MAAM,IAAA,GAAO,CAAA,CAAE,aAAA,CAAc,qBAAA,EAAsB;AACnD,IAAA,MAAM,IAAA,GAAA,CAAQ,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,IAAA,GAAO,GAAA,CAAI,IAAA,IAAQ,IAAA,CAAK,KAAA,GAAQ,CAAA,CAAA,KAAO,IAAA,CAAK,KAAA,GAAQ,EAAA,GAAK,CAAA,CAAA;AACxF,IAAA,MAAM,MAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,MAAM,IAAA,IAAQ,MAAA,CAAO,MAAA,GAAS,CAAA,CAAE,CAAC,CAAC,CAAA;AAC5F,IAAA,WAAA,CAAY,GAAG,CAAA;AACf,IAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,OAAA,EAAS,qBAAA,EAAsB;AAC1D,IAAA,MAAM,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAM,IAAA,GAAO,CAAA;AAC3C,IAAA,MAAM,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAM,GAAA,GAAO,CAAA;AAC3C,IAAA,UAAA,CAAW,EAAE,CAAA,EAAG,CAAA,EAAG,OAAA,EAAS,WAAA,CAAY,MAAA,CAAO,GAAG,CAAC,CAAA,EAAG,SAAA,EAAW,UAAA,CAAW,GAAG,GAAG,CAAA;AAAA,EACpF,CAAA;AAEA,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,SAAI,GAAA,EAAK,YAAA,EAAc,OAAO,EAAE,QAAA,EAAU,YAAW,EAAA,kBACpD,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAAI,OAAA,EAAS,CAAA,IAAA,EAAO,CAAC,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA;AAAA,MAAI,KAAA,EAAM,MAAA;AAAA,MAAO,KAAA,EAAO,EAAE,OAAA,EAAS,OAAA,EAAQ;AAAA,MACpE,WAAA,EAAa,kBAAA;AAAA,MACb,cAAc,MAAM;AAAE,QAAA,WAAA,CAAY,IAAI,CAAA;AAAG,QAAA,UAAA,CAAW,IAAI,CAAA;AAAA,MAAG;AAAA,KAAA;AAAA,oBAC3D,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,gBAAA,EAAA,EAAe,EAAA,EAAI,QAAQ,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,IAAG,GAAA,EAAA,kBAClD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,MAAA,EAAO,IAAA,EAAK,SAAA,EAAW,KAAA,EAAO,WAAA,EAAa,IAAA,EAAM,CAAA,kBACvD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,MAAA,EAAO,MAAA,EAAO,WAAW,KAAA,EAAO,WAAA,EAAa,CAAA,EAAG,CACxD,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,cAAS,EAAA,EAAI,CAAA,OAAA,EAAU,MAAM,CAAA,CAAA,EAAA,kBAC5B,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,GAAG,GAAA,CAAI,IAAA,EAAM,CAAA,EAAG,GAAA,CAAI,GAAA,GAAM,CAAA,EAAG,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,EAAA,GAAK,EAAA,EAAI,CACjE,CACF,CAAA;AAAA,IAGC,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM;AACpB,MAAA,MAAM,CAAA,GAAI,GAAA,CAAI,GAAA,GAAM,MAAA,CAAO,CAAC,CAAA;AAC5B,MAAA,uBACE,KAAA,CAAA,aAAA,CAAC,OAAE,GAAA,EAAK,CAAA,EAAA,EACL,IAAI,CAAA,oBAAK,KAAA,CAAA,aAAA,CAAC,UAAK,EAAA,EAAI,GAAA,CAAI,MAAM,EAAA,EAAI,CAAA,EAAG,IAAI,GAAA,CAAI,IAAA,GAAK,IAAI,EAAA,EAAI,CAAA,EAAG,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAa,CAAA,EAAG,iBAAgB,KAAA,EAAM,CAAA,sCACrH,MAAA,EAAA,EAAK,CAAA,EAAG,IAAI,IAAA,GAAO,CAAA,EAAG,GAAG,CAAA,GAAI,CAAA,EAAG,YAAW,KAAA,EAAM,IAAA,EAAM,YAAY,QAAA,EAAU,EAAA,EAAI,YAAY,IAAA,EAAA,EAC3F,WAAA,CAAY,CAAC,CAChB,CACF,CAAA;AAAA,IAEJ,CAAC,CAAA;AAAA,oBACD,KAAA,CAAA,aAAA,CAAC,UAAK,EAAA,EAAI,GAAA,CAAI,MAAM,EAAA,EAAI,GAAA,CAAI,MAAI,EAAA,EAAI,EAAA,EAAI,IAAI,IAAA,GAAK,EAAA,EAAI,IAAI,GAAA,CAAI,GAAA,GAAI,IAAI,MAAA,EAAQ,SAAA,EAAW,aAAa,CAAA,EAAG,CAAA;AAAA,IAGvG,SAAS,KAAA,GACR,MAAA,CAAO,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM;AACnB,MAAA,MAAM,KAAK,IAAA,CAAK,GAAA,CAAI,IAAK,CAAA,GAAI,OAAA,IAAW,YAAa,EAAE,CAAA;AACvD,MAAA,MAAM,CAAA,GAAK,GAAA,CAAI,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,GAAA,CAAA;AAClC,MAAA,uBACE,KAAA,CAAA,aAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UAAK,GAAA,EAAK,CAAA;AAAA,UAAG,CAAA;AAAA,UAAM,CAAA,EAAG,GAAA,CAAI,GAAA,GAAI,EAAA,GAAG,EAAA;AAAA,UAAI,KAAA,EAAO,IAAA;AAAA,UAAM,MAAA,EAAQ,EAAA;AAAA,UAAI,EAAA,EAAI,CAAA;AAAA,UACjE,IAAA,EAAM,KAAA;AAAA,UAAO,OAAA,EAAS,CAAA,KAAM,QAAA,GAAW,IAAA,GAAO,GAAA,GAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,CAAC,CAAA,GAAK;AAAA;AAAA,OAAM;AAAA,IAEvG,CAAC,oBAED,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EAGG,CAAC,OAAO,QAAA,oBAAY,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,QAAA,EAAU,MAAM,CAAA,KAAA,EAAQ,MAAM,KAAK,CAAA,EAChE,CAAC,uBAAO,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,GAAG,QAAA,EAAU,IAAA,EAAK,QAAO,MAAA,EAAQ,KAAA,EAAO,aAAa,CAAA,EAAG,cAAA,EAAe,SAAQ,aAAA,EAAc,OAAA,EAAQ,GACnH,CAAC,GAAA,IAAO,IAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,GAAG,CAAA,KAAM,MAAA,CAAO,CAAC,CAAA,GAAI,CAAA,oBAC1C,KAAA,CAAA,aAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QAAO,GAAA,EAAK,CAAA;AAAA,QAAG,EAAA,EAAI,CAAA;AAAA,QAAG,EAAA,EAAI,CAAA;AAAA,QACzB,CAAA,EAAG,CAAA,KAAM,QAAA,GAAW,GAAA,GAAM,CAAA;AAAA,QAC1B,IAAA,EAAM,SAAS,SAAA,GAAY,SAAA;AAAA,QAAW,MAAA,EAAQ,KAAA;AAAA,QAC9C,WAAA,EAAa,CAAA,KAAM,QAAA,GAAW,GAAA,GAAM;AAAA;AAAA,KACvC,CACH,CAAA;AAAA,IAID,GAAA,IAAO,IAAA,KAAS,MAAA,oBACf,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,QAAA,EAAU,CAAA,YAAA,EAAe,MAAM,CAAA,CAAA,CAAA,EAAA,EAC/B,GAAA,CAAI,GAAA,CAAI,CAAC,IAAI,MAAA,KAAW;AACvB,MAAA,MAAM,WAAW,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,EAAE,OAAA,EAAQ;AAC/C,MAAA,MAAM,IAAA,GAAO,cAAc,QAAQ,CAAA;AAEnC,MAAA,MAAM,WAAW,EAAA,CAAG,MAAA,GAAS,KAAK,MAAA,GAAS,EAAA,IAAM,KAAK,CAAA,IAAK,CAAA;AAC3D,MAAA,MAAM,EAAA,GAAK,GAAA,CAAI,IAAA,GAAO,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA;AAEnC,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,GAAA,GAAM,MAAA,CAAO,GAAG,aAAa,CAAA;AAC/C,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAA,GAAM,EAAA,GAAK,CAAA,EAAG,KAAK,CAAC,CAAA;AAClE,MAAA,MAAM,KAAA,GAAQ,GAAG,MAAA,KAAW,eAAA;AAG5B,MAAgB,IAAI,IAAI;AAExB,MAAA,uBACE,KAAA,CAAA,aAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UAAE,KAAK,EAAA,CAAG,MAAA;AAAA,UAAQ,KAAA,EAAO,EAAE,MAAA,EAAQ,SAAA,EAAU;AAAA,UAC5C,cAAc,CAAA,CAAA,KAAK;AACjB,YAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,YAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,OAAA,EAAS,qBAAA,EAAsB;AAC1D,YAAA,MAAM,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAM,IAAA,GAAO,CAAA;AAC3C,YAAA,MAAM,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAM,GAAA,GAAO,CAAA;AAC3C,YAAA,UAAA,CAAW;AAAA,cAAE,CAAA;AAAA,cAAG,CAAA;AAAA,cACd,SAAS,CAAA,CAAA,EAAI,EAAA,CAAG,MAAM,CAAA,EAAA,EAAK,GAAG,KAAK,CAAA,CAAA;AAAA,cACnC,SAAA,EAAW,GAAG,WAAA,CAAY,EAAA,CAAG,aAAa,CAAC,CAAA,MAAA,EAAM,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,CAAA,CAAE,kBAAA,CAAmB,SAAS,EAAE,KAAA,EAAO,SAAS,GAAA,EAAK,SAAA,EAAW,CAAC,CAAA;AAAA,aAAI,CAAA;AAC5I,YAAA,SAAA,GAAY,GAAG,MAAM,CAAA;AAAA,UACvB,CAAA;AAAA,UACA,cAAc,MAAM;AAAE,YAAA,UAAA,CAAW,IAAI,CAAA;AAAG,YAAA,SAAA,GAAY,IAAI,CAAA;AAAA,UAAG;AAAA,SAAA;AAAA,wBAE3D,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAI,EAAA,EAAI,IAAI,EAAA,EAAI,CAAA,EAAG,KAAA,GAAQ,EAAA,GAAK,GAAG,IAAA,EAAM,KAAA,EAAO,OAAA,EAAS,KAAA,GAAQ,MAAM,GAAA,EAAK,CAAA;AAAA,wBACpF,KAAA,CAAA,aAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAAO,EAAA,EAAI,EAAA;AAAA,YAAI,EAAA,EAAI,EAAA;AAAA,YAAI,CAAA,EAAG,QAAQ,GAAA,GAAM,GAAA;AAAA,YACvC,IAAA,EAAM,KAAA,GAAQ,KAAA,GAAS,MAAA,GAAS,SAAA,GAAY,MAAA;AAAA,YAC5C,MAAA,EAAQ,KAAA;AAAA,YAAO,WAAA,EAAa,QAAQ,CAAA,GAAI,GAAA;AAAA,YACxC,OAAA,EAAS,QAAQ,CAAA,GAAI;AAAA;AAAA;AAAK,OAC9B;AAAA,IAEJ,CAAC,CACH,CAAA;AAAA,IAID,aAAa,IAAA,IAAQ,IAAA,KAAS,UAAU,GAAA,CAAI,QAAQ,qBACnD,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBACE,KAAA,CAAA,aAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QAAK,EAAA,EAAI,GAAA,CAAI,QAAQ,CAAA,CAAE,CAAC,CAAA;AAAA,QAAG,IAAI,GAAA,CAAI,GAAA;AAAA,QAAK,EAAA,EAAI,GAAA,CAAI,QAAQ,CAAA,CAAE,CAAC,CAAA;AAAA,QAAG,EAAA,EAAI,IAAI,GAAA,GAAI,EAAA;AAAA,QACzE,MAAA,EAAQ,KAAA;AAAA,QAAO,WAAA,EAAa,CAAA;AAAA,QAAG,eAAA,EAAgB,KAAA;AAAA,QAAM,OAAA,EAAS;AAAA;AAAA,KAAK,kBACrE,KAAA,CAAA,aAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QAAK,IAAI,GAAA,CAAI,IAAA;AAAA,QAAM,EAAA,EAAI,GAAA,CAAI,QAAQ,CAAA,CAAE,CAAC,CAAA;AAAA,QAAG,EAAA,EAAI,IAAI,IAAA,GAAK,EAAA;AAAA,QAAI,EAAA,EAAI,GAAA,CAAI,QAAQ,CAAA,CAAE,CAAC,CAAA;AAAA,QAC5E,MAAA,EAAQ,KAAA;AAAA,QAAO,WAAA,EAAa,CAAA;AAAA,QAAG,eAAA,EAAgB,KAAA;AAAA,QAAM,OAAA,EAAS;AAAA;AAAA,KAClE,CAAA;AAAA,IAID,UAAA,CAAW,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,KAAM;AAC1B,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAA;AACzB,MAAA,MAAM,IAAA,GAAQ,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,KAAA,GAAQ,CAAC,CAAC,CAAA;AAC/C,MAAA,IAAI,CAAA,KAAM,KAAK,CAAA,KAAM,KAAA,GAAQ,KAAK,CAAA,GAAI,IAAA,KAAS,GAAG,OAAO,IAAA;AACzD,MAAA,MAAM,CAAA,GAAI,IAAA,KAAS,KAAA,GACf,GAAA,CAAI,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,GAAA,CAAA,GAAO,IAAA,GAAO,CAAA,GACrC,GAAA,CAAI,IAAA,GAAO,KAAK,CAAC,CAAA;AACrB,MAAA,2CACG,MAAA,EAAA,EAAK,GAAA,EAAK,CAAA,EAAG,CAAA,EAAM,GAAG,CAAA,GAAI,CAAA,EAAG,UAAA,EAAW,QAAA,EAAS,MAAM,UAAA,EAAY,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,QAC3F,GACH,CAAA;AAAA,IAEJ,CAAC;AAAA,GACH,EAGC,YAAY,MAAM;AACjB,IAAA,MAAM,EAAA,GAAK,GAAA;AACX,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,EAAS,WAAA,IAAe,GAAA;AACjD,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,CAAA,GAAI,EAAA,GAAK,EAAA,GAAK,GAAA;AACxC,IAAA,uBACE,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,MACV,QAAA,EAAU,UAAA;AAAA,MACV,MAAM,SAAA,GAAY,OAAA,CAAQ,IAAI,EAAA,GAAK,CAAA,GAAI,QAAQ,CAAA,GAAI,CAAA;AAAA,MACnD,GAAA,EAAK,QAAQ,CAAA,GAAI,EAAA;AAAA,MACjB,MAAA,EAAQ,IAAA;AAAA,MAAM,aAAA,EAAe,MAAA;AAAA,MAC7B,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,MACjC,MAAA,EAAQ,CAAA,UAAA,EAAa,MAAA,GAAS,SAAA,GAAY,SAAS,CAAA,CAAA;AAAA,MACnD,YAAA,EAAc,CAAA;AAAA,MAAG,OAAA,EAAS,UAAA;AAAA,MAC1B,SAAA,EAAW,SAAS,6BAAA,GAAgC,6BAAA;AAAA,MACpD,KAAA,EAAO;AAAA,KACT,EAAA,kBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,MAAE,QAAA,EAAU,EAAA;AAAA,MAAI,UAAA,EAAY,GAAA;AAAA,MAAK,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA;AAAA,MACvE,UAAA,EAAY,IAAA;AAAA,MAAM,UAAA,EAAY;AAAA,KAAI,EAAA,EACjC,QAAQ,OACX,CAAA,EACC,QAAQ,SAAA,oBACP,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,IAAI,KAAA,EAAO,MAAA,GAAS,SAAA,GAAY,SAAA,EAAW,UAAA,EAAY,IAAA,EAAM,WAAW,CAAA,EAAE,EAAA,EAC/F,OAAA,CAAQ,SACX,CAEJ,CAAA;AAAA,EAEJ,IACF,CAAA;AAEJ;AAoBA,SAAS,eAAA,CAAgB;AAAA,EACvB,IAAA;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAAA,EAIG;AACD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAwB,IAAI,CAAA;AAC1E,EAAA,MAAM,SAAA,GAAY,MAAA,iBAAoC,IAAI,GAAA,EAAK,CAAA;AAE/D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAqB;AAAE,MAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,QAAA,EAAU,OAAA,EAAQ;AAAA,IAAG,CAAA;AACzE,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,KAAK,CAAA;AACxC,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,KAAK,CAAA;AAAA,EAC1D,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAGZ,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,oBAAoB,IAAA,EAAM;AAC5B,MAAA,MAAM,EAAA,GAAK,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA;AAChD,MAAA,IAAI,EAAA,KAAO,cAAA,CAAe,EAAE,UAAU,QAAA,EAAU,KAAA,EAAO,WAAW,CAAA;AAAA,IACpE;AAAA,EACF,CAAA,EAAG,CAAC,eAAe,CAAC,CAAA;AAEpB,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,WAAA,EAAa,aAAA,EAAe,eAAA,EAAiB,aAAA,EAAe,cAAA,EAAgB,QAAA,EAAU,UAAA,EAAY,WAAA,EAAa,GAAA,EAAK,aAAY,GAAI,IAAA;AAC3J,EAAA,MAAM,eAAgB,aAAA,KAAkB,KAAA;AACxC,EAAA,MAAM,YAAA,GAAgB,MAAA,CAAO,IAAA,KAAS,OAAA,GAAU,cAAA,CAAe,OAAO,KAAK,CAAA,GAAI,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AAClG,EAAA,MAAM,WAAA,GAAgB,MAAA,CAAO,IAAA,KAAS,OAAA,GAAU,KAAK,MAAA,CAAO,IAAA;AAC5D,EAAA,MAAM,SAAA,GAAgB,CAAC,CAAA,KAAc,MAAA,CAAO,SAAS,OAAA,GAAU,cAAA,CAAe,CAAC,CAAA,GAAI,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,EAAE,CAAA,CAAA;AAC7G,EAAA,MAAM,WAAA,GAAgB,SAAS,SAAA,GAAY,SAAA;AAC3C,EAAA,MAAM,UAAA,GAAgB,SAAS,SAAA,GAAY,SAAA;AAC3C,EAAA,MAAM,cAAgB,aAAA,GAClB,CAAA,UAAA,EAAa,OAAO,IAAA,KAAS,OAAA,GAAU,eAAe,MAAA,CAAO,MAAM,IAAI,MAAA,CAAO,MAAM,IAAI,WAAW,CAAA,CAAA,GACnG,kBAAa,MAAA,CAAO,MAAM,IAAI,WAAW,CAAA,CAAA;AAG7C,EAAA,MAAM,YAAY,YAAA,IAAgB,GAAA,GAAM,IAAI,MAAA,CAAO,CAAC,KAAK,EAAA,KAAO;AAC9D,IAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,CAAA,CAAE,kBAAA,CAAmB,OAAA,EAAS,EAAE,SAAS,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,GAAA,EAAK,WAAW,CAAA;AACnH,IAAA,IAAI,CAAC,GAAA,CAAI,IAAI,GAAG,GAAA,CAAI,IAAI,IAAI,EAAC;AAC7B,IAAA,GAAA,CAAI,IAAI,CAAA,CAAE,IAAA,CAAK,EAAE,CAAA;AACjB,IAAA,OAAO,GAAA;AAAA,EACT,CAAA,EAAG,EAAgC,CAAA,GAAI,EAAC;AACxC,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,CAAE,IAAA;AAAA,IACzC,CAAC,GAAG,CAAA,KAAM,IAAI,KAAK,SAAA,CAAU,CAAC,CAAA,CAAE,CAAC,CAAA,CAAE,QAAQ,EAAE,OAAA,EAAQ,GAAI,IAAI,IAAA,CAAK,SAAA,CAAU,CAAC,EAAE,CAAC,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA;AAAQ,GACtG;AAEA,EAAA,uBACE,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBAEE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAA,EAAS,SAAS,KAAA,EAAO;AAAA,IAC5B,QAAA,EAAU,OAAA;AAAA,IAAS,KAAA,EAAO,CAAA;AAAA,IAAG,MAAA,EAAQ,GAAA;AAAA,IACrC,UAAA,EAAY,SAAS,iBAAA,GAAoB,iBAAA;AAAA,IACzC,cAAA,EAAgB;AAAA,GAClB,EAAG,CAAA,kBAGH,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IACV,QAAA,EAAU,OAAA;AAAA,IACV,GAAA,EAAK,KAAA;AAAA,IAAO,IAAA,EAAM,KAAA;AAAA,IAClB,SAAA,EAAW,mCAAA;AAAA,IACX,KAAA,EAAO,kBAAA;AAAA,IACP,SAAA,EAAW,oBAAA;AAAA,IACX,MAAA,EAAQ,GAAA;AAAA,IACR,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,IACjC,MAAA,EAAQ,aAAa,WAAW,CAAA,CAAA;AAAA,IAChC,YAAA,EAAc,EAAA;AAAA,IACd,OAAA,EAAS,MAAA;AAAA,IAAQ,aAAA,EAAe,QAAA;AAAA,IAChC,QAAA,EAAU,QAAA;AAAA,IACV,SAAA,EAAW,mDAAA;AAAA,IACX,SAAA,EAAW,SAAS,6BAAA,GAAgC;AAAA,GACtD,EAAA,kBAEE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IACV,OAAA,EAAS,MAAA;AAAA,IAAQ,cAAA,EAAgB,eAAA;AAAA,IAAiB,UAAA,EAAY,YAAA;AAAA,IAC9D,OAAA,EAAS,gBAAA;AAAA,IACT,YAAA,EAAc,aAAa,WAAW,CAAA,CAAA;AAAA,IACtC,UAAA,EAAY;AAAA,GACd,EAAA,kBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,YAAY,aAAA,EAAe,WAAA,EAAa,aAAA,EAAe,QAAA,EAAU,YAAA,EAAc,CAAA,MAChI,KAAA,EAAM,QAAA,EAAI,UAAA,CAAW,CAAC,CAAA,EAAE,UAAA,EAAI,WAAW,UAAA,CAAW,MAAA,GAAS,CAAC,CAC/D,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,UAAA,EAAY,KAAK,CAAA,EAAG,QAAA,EAAU,MAAA,EAAO,EAAA,kBAC9E,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,OAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,OAAA,EAAS,UAAA,EAAY,CAAA,EAAG,aAAA,EAAe,WAAU,EAAA,EACtH,YACH,CAAA,EACC,WAAA,oBACC,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,OAAO,EAAE,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,UAAA,EAAY,UAAA,EAAY,KAAI,EAAA,EAAI,WAAY,CAAA,kBAElF,KAAA,CAAA,aAAA,CAAC,WAAA,EAAA,EAAY,MAAA,EAAQ,OAAO,MAAA,EAAQ,CACtC,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,UAAA,EAAY,SAAA,EAAW,CAAA,MACvD,WAAA,EAAY,QAAA,kBAAG,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,SAAA,EAAW,QAAA,EAAS,EAAA,EAAI,WAAY,CACrE,CACF,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MAAO,IAAA,EAAK,QAAA;AAAA,MAAS,OAAA,EAAS,OAAA;AAAA,MAAS,KAAA,EAAO;AAAA,QAC7C,GAAA,EAAK,OAAA;AAAA,QAAS,MAAA,EAAQ,SAAA;AAAA,QAAW,KAAA,EAAO,EAAA;AAAA,QAAI,MAAA,EAAQ,EAAA;AAAA,QACpD,OAAA,EAAS,MAAA;AAAA,QAAQ,UAAA,EAAY,QAAA;AAAA,QAAU,cAAA,EAAgB,QAAA;AAAA,QACvD,YAAA,EAAc,KAAA;AAAA,QAAO,UAAA,EAAY,SAAS,wBAAA,GAA2B,kBAAA;AAAA,QACrE,KAAA,EAAO,UAAA;AAAA,QAAY,QAAA,EAAU,EAAA;AAAA,QAAI,UAAA,EAAY,GAAA;AAAA,QAAK,UAAA,EAAY,WAAA;AAAA,QAAa,UAAA,EAAY;AAAA,OACzF;AAAA,MACE,cAAc,CAAA,CAAA,KAAK;AAAE,QAAC,CAAA,CAAE,aAAA,CAA8B,KAAA,CAAM,UAAA,GAAa,SAAS,wBAAA,GAA2B,iBAAA;AAAA,MAAmB,CAAA;AAAA,MAChI,cAAc,CAAA,CAAA,KAAK;AAAE,QAAC,CAAA,CAAE,aAAA,CAA8B,KAAA,CAAM,UAAA,GAAa,SAAS,wBAAA,GAA2B,kBAAA;AAAA,MAAoB;AAAA,KAAA;AAAA,IAAG;AAAA,GAGxI,mBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,IAAA,EAAM,GAAG,QAAA,EAAU,MAAA,EAAQ,SAAS,WAAA,EAAa,OAAA,EAAS,QAAQ,aAAA,EAAe,QAAA,EAAU,KAAK,EAAA,EAAG,EAAA,EAG9G,UAAA,CAAW,MAAA,IAAU,CAAA,mBACpB,KAAA,CAAA,aAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,MAAA,EAAQ,eAAA;AAAA,MAAiB,UAAA;AAAA,MACzB,WAAA;AAAA,MACA,KAAA,EAAO,cAAA;AAAA,MAAgB,IAAA,EAAM,aAAA;AAAA,MAC7B,QAAA;AAAA,MACA,WAAA,EAAa,SAAA;AAAA,MAAW,GAAA,EAAK,eAAe,MAAA,GAAY,GAAA;AAAA,MAAK,IAAA;AAAA,MAC7D,eAAA;AAAA,MAAkC,SAAA,EAAW;AAAA;AAAA,GAC/C,mBAEA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAO,UAAA,EAAY,QAAA,EAAU,EAAA,EAAI,SAAA,EAAW,QAAA,EAAU,OAAA,EAAS,UAAS,EAAA,EAAG,mDAEzF,CAAA,EAID,YAAA,IAAgB,GAAA,IAAO,GAAA,CAAI,MAAA,GAAS,CAAA,oBACnC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IAAE,QAAA,EAAU,EAAA;AAAA,IAAI,UAAA,EAAY,GAAA;AAAA,IAAK,KAAA,EAAO,UAAA;AAAA,IAAY,aAAA,EAAe,WAAA;AAAA,IAC7E,aAAA,EAAe,QAAA;AAAA,IAAU,YAAA,EAAc,EAAA;AAAA,IACvC,SAAA,EAAW,aAAa,WAAW,CAAA,CAAA;AAAA,IAAI,UAAA,EAAY;AAAA,GAAG,EAAA,EACrD,WAAA,IAAe,iBAAA,EAAkB,QAAA,EAAI,IAAI,MAAA,EAAO,KAAA,EAAI,GAAA,CAAI,MAAA,KAAW,CAAA,GAAI,GAAA,GAAM,EAChF,CAAA,sCACC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,aAAA,EAAe,QAAA,EAAU,GAAA,EAAK,IAAG,EAAA,EAC7D,WAAA,CAAY,GAAA,CAAI,CAAA,IAAA,yCACd,KAAA,EAAA,EAAI,GAAA,EAAK,IAAA,EAAA,kBAER,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,IACV,QAAA,EAAU,EAAA;AAAA,IAAI,UAAA,EAAY,GAAA;AAAA,IAAK,KAAA,EAAO,cAAA;AAAA,IACtC,YAAA,EAAc,CAAA;AAAA,IAAG,OAAA,EAAS,MAAA;AAAA,IAAQ,UAAA,EAAY,QAAA;AAAA,IAAU,GAAA,EAAK;AAAA,GAC/D,EAAA,kBACE,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAA,EAAM,IAAK,CAAA,kBACZ,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,KAAA,EAAO,UAAA,EAAY,UAAA,EAAY,KAAI,EAAA,EAAG,OAAA,EAAG,SAAA,CAAU,IAAI,CAAA,CAAE,MAAA,EAAO,KAAA,EAAI,SAAA,CAAU,IAAI,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,GAAA,GAAM,EAAG,CAC3H,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,IAAE,OAAA,EAAS,MAAA;AAAA,IAAQ,aAAA,EAAe,QAAA;AAAA,IAAU,GAAA,EAAK,CAAA;AAAA,IAAG,WAAA,EAAa,EAAA;AAAA,IAC3E,UAAA,EAAY,aAAa,cAAc,CAAA,EAAA;AAAA,GAAK,EAAA,EAC3C,SAAA,CAAU,IAAI,CAAA,CAAE,GAAA,CAAI,CAAA,EAAA,qBACnB,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,EAAA,CAAG,MAAA,EAAQ,KAAA,EAAO;AAAA,IAC1B,OAAA,EAAS,MAAA;AAAA,IAAQ,aAAA,EAAe,QAAA;AAAA,IAAU,GAAA,EAAK,CAAA;AAAA,IAC/C,OAAA,EAAS,UAAA;AAAA,IAAY,YAAA,EAAc,CAAA;AAAA,IACnC,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,IACjC,MAAA,EAAQ,aAAa,WAAW,CAAA,CAAA;AAAA,IAChC,UAAA,EAAY;AAAA,GACd,EAAA,kBAEE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,CAAA,EAAE,EAAA,kBAC1D,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,UAAA,EAAY,YAAY,CAAA,EAAG,QAAA,EAAU,EAAA,EAAG,EAAA,EAAG,KAC5F,EAAA,CAAG,MACP,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,IAAE,QAAA,EAAU,EAAA;AAAA,IAAI,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,OAAA;AAAA,IAAS,IAAA,EAAM,CAAA;AAAA,IACpE,UAAA,EAAY,QAAA;AAAA,IAAU,QAAA,EAAU,QAAA;AAAA,IAAU,YAAA,EAAc;AAAA,GAAW,EAAG,OAAO,EAAA,CAAG,KAAA,EAAA,EAC/E,GAAG,KACN,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,KAAA,EAAO,UAAA,EAAY,UAAA,EAAY,CAAA,EAAE,EAAA,EAC3D,eAAe,EAAA,CAAG,aAAa,CAAA,EAAE,OACpC,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MAAE,MAAM,EAAA,CAAG,GAAA;AAAA,MAAK,MAAA,EAAO,QAAA;AAAA,MAAS,GAAA,EAAI,qBAAA;AAAA,MACnC,KAAA,EAAO,EAAE,KAAA,EAAO,UAAA,EAAY,YAAY,CAAA;AAAE,KAAA;AAAA,oBAC1C,KAAA,CAAA,aAAA,CAAC,YAAA,EAAA,EAAa,IAAA,EAAM,EAAA,EAAI;AAAA,GAE5B,CAAA,EAEC,EAAA,CAAG,MAAA,oBACF,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,YAAY,QAAA,EAAU,GAAA,EAAK,CAAA,EAAE,EAAA,EACzD,GAAG,YAAA,mBACF,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAAI,KAAK,EAAA,CAAG,YAAA;AAAA,MAAc,KAAK,EAAA,CAAG,MAAA;AAAA,MACjC,KAAA,EAAO;AAAA,QAAE,KAAA,EAAO,EAAA;AAAA,QAAI,MAAA,EAAQ,EAAA;AAAA,QAAI,YAAA,EAAc,KAAA;AAAA,QAAO,UAAA,EAAY,CAAA;AAAA,QAC/D,MAAA,EAAQ,CAAA,UAAA,EAAa,MAAA,GAAS,SAAA,GAAY,SAAS,CAAA;AAAA;AAAG;AAAA,GAAG,mBAE7D,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IAAE,KAAA,EAAO,EAAA;AAAA,IAAI,MAAA,EAAQ,EAAA;AAAA,IAAI,YAAA,EAAc,KAAA;AAAA,IAAO,UAAA,EAAY,CAAA;AAAA,IACpE,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,IACjC,OAAA,EAAS,MAAA;AAAA,IAAQ,UAAA,EAAY,QAAA;AAAA,IAAU,cAAA,EAAgB,QAAA;AAAA,IACvD,QAAA,EAAU,CAAA;AAAA,IAAG,UAAA,EAAY,GAAA;AAAA,IAAK,KAAA,EAAO;AAAA,OACpC,EAAA,CAAG,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAChB,CAAA,kBAEF,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MAAE,IAAA,EAAM,CAAA,mBAAA,EAAsB,EAAA,CAAG,MAAM,CAAA,CAAA;AAAA,MAAI,MAAA,EAAO,QAAA;AAAA,MAAS,GAAA,EAAI,qBAAA;AAAA,MAC9D,KAAA,EAAO;AAAA,QAAE,QAAA,EAAU,EAAA;AAAA,QAAI,UAAA,EAAY,GAAA;AAAA,QAAK,KAAA,EAAO,UAAA;AAAA,QAC7C,cAAA,EAAgB,MAAA;AAAA,QAAQ,aAAA,EAAe;AAAA,OAAS;AAAA,MAClD,cAAc,CAAA,CAAA,KAAK;AAAE,QAAC,CAAA,CAAE,aAAA,CAA8B,KAAA,CAAM,KAAA,GAAQ,SAAA;AAAA,MAAW,CAAA;AAAA,MAC/E,cAAc,CAAA,CAAA,KAAK;AAAE,QAAC,CAAA,CAAE,aAAA,CAA8B,KAAA,CAAM,KAAA,GAAQ,UAAA;AAAA,MAAY;AAAA,KAAA;AAAA,IAAG,GAAA;AAAA,IACjF,EAAA,CAAG;AAAA,GAET,CAEJ,CACD,CACH,CACF,CACD,CACH,CACF,CAAA,EAID,CAAC,YAAA,IAAgB,GAAA,IAAO,IAAI,MAAA,GAAS,CAAA,wCACnC,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,IAAE,QAAA,EAAU,EAAA;AAAA,IAAI,UAAA,EAAY,GAAA;AAAA,IAAK,KAAA,EAAO,UAAA;AAAA,IAAY,aAAA,EAAe,WAAA;AAAA,IAC7E,aAAA,EAAe,QAAA;AAAA,IAAU,YAAA,EAAc,EAAA;AAAA,IACvC,SAAA,EAAW,aAAa,WAAW,CAAA,CAAA;AAAA,IAAI,UAAA,EAAY;AAAA,GAAG,EAAA,EACrD,KAAA,CAAM,WAAA,EAAY,CAAE,QAAA,CAAS,SAAS,CAAA,GAAI,oBAAA,GAAuB,aAAA,EACjE,GAAA,EAAI,wCACP,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,aAAA,EAAe,QAAA,EAAU,GAAA,EAAK,CAAA,EAAE,EAAA,EAC5D,GAAA,CAAI,GAAA,CAAI,CAAC,EAAA,EAAI,CAAA,KAAM;AAClB,IAAA,MAAM,GAAA,GAAS,aAAA,CAAc,CAAA,EAAG,GAAA,CAAI,MAAM,CAAA;AAC1C,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,CAAC,CAAA,CAAE,aAAA;AACtB,IAAA,MAAM,SAAS,MAAA,GAAS,CAAA,GAAK,EAAA,CAAG,aAAA,GAAgB,SAAU,GAAA,GAAM,CAAA;AAChE,IAAA,MAAM,KAAA,GAAS,GAAG,MAAA,KAAW,eAAA;AAC7B,IAAA,uBACE,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAAI,KAAK,EAAA,CAAG,MAAA;AAAA,QACX,GAAA,EAAK,CAAC,EAAA,KAAO;AACX,UAAA,IAAI,IAAI,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,EAAA,CAAG,QAAQ,EAAE,CAAA;AAAA,eACtC,SAAA,CAAU,OAAA,CAAQ,MAAA,CAAO,EAAA,CAAG,MAAM,CAAA;AAAA,QACzC,CAAA;AAAA,QACA,YAAA,EAAc,MAAM,kBAAA,CAAmB,EAAA,CAAG,MAAM,CAAA;AAAA,QAChD,YAAA,EAAc,MAAM,kBAAA,CAAmB,IAAI,CAAA;AAAA,QAC3C,KAAA,EAAO;AAAA,UACL,OAAA,EAAS,UAAA;AAAA,UAAY,YAAA,EAAc,CAAA;AAAA,UACnC,YAAY,KAAA,GACP,MAAA,GAAS,SAAA,GAAY,SAAA,GACrB,SAAS,SAAA,GAAY,SAAA;AAAA,UAC1B,QAAQ,CAAA,UAAA,EAAa,KAAA,GAAQ,CAAA,EAAG,cAAc,OAAO,WAAW,CAAA,CAAA;AAAA,UAChE,OAAA,EAAS,MAAA;AAAA,UAAQ,aAAA,EAAe,QAAA;AAAA,UAAU,GAAA,EAAK,CAAA;AAAA,UAC/C,UAAA,EAAY,sCAAA;AAAA,UACZ,MAAA,EAAQ;AAAA;AACV,OAAA;AAAA,sBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,CAAA,EAAG,QAAA,EAAU,CAAA,EAAE,EAAA,sCACtE,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,KAAK,UAAA,EAAY,CAAA,EAAE,EAAA,EAAG,GAAA,EAAE,EAAA,CAAG,MAAO,CAAA,kBACvF,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,QAAE,QAAA,EAAU,EAAA;AAAA,QAAI,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,OAAA;AAAA,QAAS,IAAA,EAAM,CAAA;AAAA,QACpE,UAAA,EAAY,QAAA;AAAA,QAAU,QAAA,EAAU,QAAA;AAAA,QAAU,YAAA,EAAc;AAAA,OAAW,EAAG,KAAA,EAAO,EAAA,CAAG,KAAA,EAAA,EAC/E,GAAG,KACN,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,OAAO,UAAA,EAAY,UAAA,EAAY,CAAA,EAAE,EAAA,EAC3D,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,EAAE,kBAAA,CAAmB,OAAA,EAAS,EAAE,KAAA,EAAO,OAAA,EAAS,GAAA,EAAK,SAAA,EAAW,CACvF,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UAAE,MAAM,EAAA,CAAG,GAAA;AAAA,UAAK,MAAA,EAAO,QAAA;AAAA,UAAS,GAAA,EAAI,qBAAA;AAAA,UACnC,OAAO,EAAE,KAAA,EAAO,YAAY,UAAA,EAAY,CAAA,EAAG,YAAY,aAAA;AAAc,SAAA;AAAA,wBACrE,KAAA,CAAA,aAAA,CAAC,YAAA,EAAA,EAAa,IAAA,EAAM,EAAA,EAAI;AAAA,OAE5B,CAAA;AAAA,0CACC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,QAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,CAAA,sBACxD,KAAA,CAAA,aAAA,CAAC,WAAA,EAAA,EAAY,UAAU,MAAA,EAAQ,KAAA,EAAO,KAAK,CAAA,kBAC3C,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,YAAY,GAAA,EAAK,KAAA,EAAO,KAAK,QAAA,EAAU,EAAA,EAAI,SAAA,EAAW,OAAA,EAAS,YAAY,CAAA,EAAE,EAAA,EACvG,eAAe,EAAA,CAAG,aAAa,CAClC,CACF;AAAA,KACF;AAAA,EAEJ,CAAC,CACH,CACF,CAEJ,CACF,CAAA,sCACC,OAAA,EAAA,IAAA,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAKN,CACJ,CAAA;AAEJ;AAIA,SAAS,WAAA,CAAY,EAAE,QAAA,EAAU,KAAA,EAAM,EAAwC;AAC7E,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,CAAC,CAAA;AACpC,EAAA,MAAM,GAAA,GAAM,OAAuB,IAAI,CAAA;AACvC,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AAEtC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,QAAA,CAAS,QAAQ,GAAG,EAAE,CAAA;AACrD,IAAA,OAAO,MAAM,aAAa,KAAK,CAAA;AAAA,EACjC,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,uBACE,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACL,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,QACjC,YAAA,EAAc,CAAA;AAAA,QACd,MAAA,EAAQ,CAAA;AAAA,QACR,QAAA,EAAU,QAAA;AAAA,QACV,IAAA,EAAM,CAAA;AAAA,QACN,QAAA,EAAU;AAAA;AACZ,KAAA;AAAA,oBAEA,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO;AAAA,UACL,MAAA,EAAQ,MAAA;AAAA,UACR,KAAA,EAAO,GAAG,KAAK,CAAA,CAAA,CAAA;AAAA,UACf,UAAA,EAAY,KAAA;AAAA,UACZ,YAAA,EAAc,CAAA;AAAA,UACd,UAAA,EAAY,0CAAA;AAAA,UACZ,SAAA,EAAW,WAAW,KAAK,CAAA,EAAA;AAAA;AAC7B;AAAA;AACF,GACF;AAEJ;AAIA,SAAS,UAAA,CAAW;AAAA,EAClB,KAAA;AAAA,EACA,MAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA,GAAgB,KAAA;AAAA,EAChB,aAAA;AAAA,EACA,aAAA,GAAgB,MAAA;AAAA,EAChB,cAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAYG;AACD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAE5C,EAAA,MAAM,YAAA,GAAe,OAAO,IAAA,KAAS,OAAA,GAAU,eAAe,MAAA,CAAO,KAAK,IAAI,MAAA,CAAO,KAAA;AACrF,EAAA,MAAM,WAAA,GAAe,MAAA,CAAO,IAAA,KAAS,OAAA,GAAU,KAAK,MAAA,CAAO,IAAA;AAC3D,EAAA,MAAM,WAAA,GAAe,OAAO,IAAA,KAAS,OAAA,GAAU,eAAe,MAAA,CAAO,MAAM,IAAI,MAAA,CAAO,MAAA;AACtF,EAAA,MAAM,WAAA,GAAe,aAAA,GACjB,CAAA,UAAA,EAAa,WAAW,IAAI,WAAW,CAAA,CAAA,CAAG,IAAA,EAAK,GAC/C,CAAA,eAAA,EAAa,WAAW,CAAA,CAAA,EAAI,WAAW,GAAG,IAAA,EAAK;AAEnD,EAAA,MAAM,eAAe,aAAA,IAAiB,aAAA,CAAc,UAAU,CAAA,IAAK,UAAA,IAAc,WAAW,MAAA,IAAU,CAAA;AACtG,EAAA,MAAM,QAAQ,YAAA,GAAe,eAAA,CAAgB,eAAgB,MAAA,CAAO,IAAA,EAAM,aAAa,CAAA,GAAI,IAAA;AAC3F,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAc,MAAA,CAAO,SAAS,OAAA,GAAU,cAAA,CAAe,CAAC,CAAA,GAAI,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,EAAE,CAAA,CAAA;AAEzG,EAAA,uBACE,KAAA,CAAA,aAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAA,GAAW,YAAY,MAAA,EAAU;AAAA,MAClD,OAAA,EAAS,QAAA;AAAA,MACT,YAAA,EAAc,MAAM,UAAA,CAAW,IAAI,CAAA;AAAA,MACnC,YAAA,EAAc,MAAM,UAAA,CAAW,KAAK;AAAA,KAAA;AAAA,oBAEpC,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,cAAA,EAAgB,iBAAiB,UAAA,EAAY,YAAA,EAAc,KAAK,CAAA,EAAE,EAAA,sCAC9F,SAAA,EAAA,IAAA,EAAW,KAAM,GACjB,QAAA,oBACC,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,MACX,QAAA,EAAU,CAAA;AAAA,MAAG,UAAA,EAAY,GAAA;AAAA,MAAK,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA;AAAA,MAC1D,OAAA,EAAS,UAAU,CAAA,GAAI,CAAA;AAAA,MAAG,UAAA,EAAY,eAAA;AAAA,MACtC,aAAA,EAAe,QAAA;AAAA,MAAU,aAAA,EAAe,WAAA;AAAA,MAAa,UAAA,EAAY;AAAA,KACnE,EAAA,EAAG,eAEH,CAEJ,CAAA;AAAA,wCAEC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,eAAe,QAAA,EAAU,UAAA,EAAY,YAAY,GAAA,EAAK,CAAA,MAClF,KAAA,oBACC,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,KAAA,CAAM,SAAS,KAAA,EAAO;AAAA,MACjC,QAAA,EAAU,EAAA;AAAA,MAAI,UAAA,EAAY,GAAA;AAAA,MAAK,MAAA,EAAQ,MAAA;AAAA,MACvC,KAAA,EAAO,KAAA,CAAM,IAAA,GAAO,SAAA,GAAY,SAAA;AAAA,MAChC,YAAY,KAAA,CAAM,IAAA,GACb,SAAS,uBAAA,GAA0B,uBAAA,GACnC,SAAS,wBAAA,GAA2B,wBAAA;AAAA,MACzC,OAAA,EAAS,SAAA;AAAA,MAAW,YAAA,EAAc,CAAA;AAAA,MAAG,UAAA,EAAY;AAAA,SAEhD,KAAA,CAAM,IACT,CAAA,kBAEF,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,MACX,QAAA,EAAU,EAAA;AAAA,MAAI,UAAA,EAAY,GAAA;AAAA,MAC1B,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA;AAAA,MAC5B,UAAA,EAAY;AAAA,KACd,EAAA,EACG,aAAA,GAAgB,uBAAA,GAAqB,wBACxC,CACF,CAAA;AAAA,wCAEC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,YAAY,UAAA,EAAY,GAAA,EAAK,GAAE,EAAA,kBAC5D,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,UAAA,EAAY,KAAK,KAAA,EAAO,MAAA,GAAS,YAAY,SAAA,EAAW,UAAA,EAAY,GAAE,EAAA,EAChG,YACH,GACC,WAAA,oBACC,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,EAAW,YAAY,GAAA,EAAI,EAAA,EACjF,WACH,CAEJ,CAAA;AAAA,oBACA,KAAA,CAAA,aAAA,CAAC,WAAA,EAAA,EAAY,MAAA,EAAQ,MAAA,CAAO,MAAA,EAAQ,CAAA;AAAA,IACnC,gCACC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,SAAA,EAAW,GAAE,EAAA,kBACzB,KAAA,CAAA,aAAA;AAAA,MAAC,gBAAA;AAAA,MAAA;AAAA,QACC,MAAA,EAAQ,aAAA;AAAA,QACR,UAAA;AAAA,QACA,OAAO,cAAA,IAAkB,SAAA;AAAA,QACzB,IAAA,EAAM,aAAA;AAAA,QACN,WAAA,EAAa;AAAA;AAAA,KAEjB,CAAA;AAAA,oBAEF,KAAA,CAAA,aAAA,CAAC,uBAAiB,WAAY,CAAA;AAAA,wCAC7B,UAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,YAAY,GAAA,EAAK,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MACzD,WACH,CAAA,EACC,eAAe,QAAA,oBACd,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,MACX,QAAA,EAAU,EAAA;AAAA,MAAI,UAAA,EAAY,GAAA;AAAA,MAC1B,KAAA,EAAO,SAAA;AAAA,MACP,aAAA,EAAe,QAAA;AAAA,MACf,UAAA,EAAY;AAAA,KACd,EAAA,EACG,WACH,CAEJ;AAAA,GACF;AAEJ;AAEA,SAAS,MAAA,CAAO,EAAE,KAAA,EAAO,WAAA,EAAY,EAA2C;AAC9E,EAAA,2CACG,IAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,EAAW,KAAM,mBAClB,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,UAAA,EAAY,YAAY,GAAA,EAAK,CAAA,sBAC1D,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,WAAW,UAAA,EAAY,CAAA,EAAE,EAAA,EAAG,KAAG,CACtF,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,mBAAgB,KAAA,EAAK,IAAA,EAAA,EAAE,WAAY,CACtC,CAAA;AAEJ;AAIA,SAAS,WAAA,CAAY,EAAE,QAAA,EAAS,EAAkC;AAChE,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,UAAA,EAAY,KAAK,KAAA,EAAO,KAAA,CAAM,QAAQ,IAAA,CAAK,OAAA,EAAS,cAAc,CAAA,EAAG,aAAA,EAAe,aAAa,aAAA,EAAe,QAAA,MACzI,QACH,CAAA;AAEJ;AAIO,SAAS,kBAAA,GAAqB;AACnC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,OAAA,GAAU,OAAO,iBAAiB,CAAA;AACxC,EAAA,MAAM,WAAW,QAAA,EAAS;AAC1B,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,OAAA,CAAQ,IAAA,CAAK,OAAA;AAC1C,EAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,OAAA,CAAQ,IAAA,CAAK,SAAA;AAE5C,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,GAAc,uBAAuB,CAAA,IAAK,EAAA;AAC9E,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AAGpD,EAAA,MAAM,cAAA,GAAiB,mBAAA,CAAuC,WAAA,CAAY,uBAAuB,CAAC,CAAA;AAClG,EAAA,MAAM,iBAAA,GAAoB,mBAAA,CAA0C,WAAA,CAAY,kBAAkB,CAAC,CAAA;AAEnG,EAAA,MAAM,YAAA,GAAkC,cAAA,IAAkB,OAAA,CAAQ,eAAA,EAAgB;AAClF,EAAA,MAAM,UAAA,GAAa,aAAa,CAAC,CAAA;AACjC,EAAA,MAAM,WAAA,GAAc,QAAQ,cAAA,EAAe;AAG3C,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,IAAI,QAAA,CAAiB,UAAA,EAAY,QAAQ,EAAE,CAAA;AACrF,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,IAAI,QAAA,CAAiB,MAAA,CAAO,WAAW,CAAC,CAAA;AAE5E,EAAA,MAAM,cAAc,YAAA,CAAa,IAAA,CAAK,OAAK,CAAA,CAAE,IAAA,KAAS,eAAe,CAAA,IAAK,UAAA;AAC1E,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,YAAA,EAAc,EAAE,CAAA;AAEtC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAA6B,IAAI,CAAA;AAC/D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAoC,IAAI,CAAA;AACtE,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAkC,IAAI,CAAA;AAEtE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,QAAA,CAAS,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,uBAAuB,eAAe,CAAC,CAAA;AACpF,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,OAAO,MAAM;AAAE,QAAA,SAAA,GAAY,IAAA;AAAA,MAAM,CAAA;AAAA,IACnC;AACA,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,QAAA,CAAS,IAAI,KAAA,CAAM,8CAA8C,CAAC,CAAA;AAClE,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,OAAO,MAAM;AAAE,QAAA,SAAA,GAAY,IAAA;AAAA,MAAM,CAAA;AAAA,IACnC;AAEA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,UAAA,CAAW,IAAI,CAAA;AAEf,IAAA,OAAA,CACG,UAAA,CAAW,aAAa,WAAA,EAAa,IAAA,EAAM,qBAAqB,MAAS,CAAA,CACzE,KAAK,CAAA,IAAA,KAAQ;AACZ,MAAA,IAAI,CAAC,SAAA,EAAW;AAAE,QAAA,UAAA,CAAW,IAAI,CAAA;AAAG,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAAG;AAAA,IACzD,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAe;AACrB,MAAA,IAAI,CAAC,SAAA,EAAW;AAAE,QAAA,QAAA,CAAS,GAAG,CAAA;AAAG,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAAG;AAAA,IACtD,CAAC,CAAA;AAEH,IAAA,OAAA,CACG,WAAW,WAAA,EAAa,WAAA,EAAa,IAAI,CAAA,CACzC,KAAK,CAAA,IAAA,KAAQ;AAAE,MAAA,IAAI,CAAC,SAAA,EAAW,UAAA,CAAW,IAAI,CAAA;AAAA,IAAG,CAAC,CAAA,CAClD,KAAA,CAAM,MAAM;AAAA,IAA4C,CAAC,CAAA;AAE5D,IAAA,OAAO,MAAM;AAAE,MAAA,SAAA,GAAY,IAAA;AAAA,IAAM,CAAA;AAAA,EAGnC,CAAA,EAAG,CAAC,OAAA,EAAS,WAAA,EAAa,aAAa,IAAA,EAAM,IAAA,EAAM,iBAAiB,CAAC,CAAA;AAErE,EAAA,IAAI,OAAA,EAAS,uBAAO,KAAA,CAAA,aAAA,CAAC,kBAAA,EAAA,IAAmB,CAAA;AAExC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,uBACE,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,cAAA,EAAA,kBACd,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,OAAA,EAAS,EAAA,EAAI,KAAA,EAAO,SAAA,sBAChC,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,UAAA,EAAY,GAAA,MAAO,SAAO,CAAA,EAAQ,KAAA,CAAM,OACzD,CACF,CAAA;AAAA,EAEJ;AAEA,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,YAAS,KAAA,EAAM,GAAA,EAAA,sCACb,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,OAAA,EAAS,UAAU,UAAA,EAAW,EAAA,EAElD,4BACC,KAAA,CAAA,aAAA,CAAC,eAAA,EAAA,EAAgB,MAAM,QAAA,EAAU,IAAA,EAAY,SAAS,MAAM,WAAA,CAAY,IAAI,CAAA,EAAG,CAAA,sCAIhF,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,YAAA,EAAc,EAAA,sBAC1B,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,QAAA,EAAU,IAAI,UAAA,EAAY,GAAA,EAAK,OAAO,WAAA,EAAa,aAAA,EAAe,WAAU,EAAA,EAAG,cAAY,GACxG,WAAA,oBACC,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,KAAA,EAAO,eAAe,SAAA,EAAW,CAAA,MAAK,qBAAA,EAC5C,GAAA,sCACnB,QAAA,EAAA,EAAO,KAAA,EAAO,EAAE,KAAA,EAAO,WAAA,MAAgB,WAAA,CAAY,IAAK,GACxD,GAAA,EAAI,QAAA,EAAE,qBACP,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,OAAO,EAAE,UAAA,EAAY,aAAY,EAAA,EAAI,WAAA,CAAY,MAAO,CAChE,CAEJ,mBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,OAAA,EAAS,QAAQ,UAAA,EAAY,UAAA,EAAY,KAAK,EAAA,EAAI,QAAA,EAAU,QAAQ,YAAA,EAAc,EAAA,MAC7F,YAAA,CAAa,MAAA,GAAS,qBACrB,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,sCACE,WAAA,EAAA,IAAA,EAAY,aAAW,mBACxB,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,OAAO,eAAA,EAAiB,aAAA,EAAe,sCAC7C,KAAA,CAAA,aAAA,CAAC,aAAA,EAAA,IAAA,sCAAe,WAAA,EAAA,EAAY,WAAA,EAAY,sBAAqB,CAAE,CAAA,sCAC9D,aAAA,EAAA,IAAA,EACE,YAAA,CAAa,IAAI,CAAA,GAAA,qBAChB,KAAA,CAAA,aAAA,CAAC,cAAW,GAAA,EAAK,GAAA,CAAI,MAAM,KAAA,EAAO,GAAA,CAAI,QACnC,GAAA,CAAI,IAAA,EAAK,MAAG,GAAA,CAAI,MAAA,EAAO,GAC1B,CACD,CACH,CACF,CACF,CAAA,sCAGD,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,mBAAY,YAAU,CAAA,sCACtB,MAAA,EAAA,EAAO,KAAA,EAAO,cAAc,aAAA,EAAe,eAAA,EAAA,sCACzC,aAAA,EAAA,IAAA,kBAAc,KAAA,CAAA,aAAA,CAAC,eAAY,WAAA,EAAY,mBAAA,EAAoB,CAAE,CAAA,kBAC9D,KAAA,CAAA,aAAA,CAAC,qBACE,kBAAA,CAAmB,GAAA,CAAI,yBACtB,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,KAAK,GAAA,CAAI,KAAA,EAAO,OAAO,GAAA,CAAI,KAAA,EAAA,EAAQ,IAAI,KAAM,CAC1D,CACH,CACF,CACF,CACF,CAAA,EAAA,CAGE,MAAM;AACN,IAAA,MAAM,WAAa,OAAA,EAAS,GAAA,CAAI,OAAK,CAAA,CAAE,SAAS,KAAK,EAAC;AACtD,IAAA,MAAM,aAAa,OAAA,EAAS,GAAA,CAAI,OAAK,CAAA,CAAE,eAAe,KAAK,EAAC;AAC5D,IAAA,MAAM,WAAa,OAAA,EAAS,GAAA,CAAI,OAAK,CAAA,CAAE,aAAa,KAAK,EAAC;AAC1D,IAAA,MAAM,OAAA,GAAa,SAAS,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,iBAAA,IAAqB,CAAC,KAAK,EAAC;AACnE,IAAA,MAAM,QAAA,GAAa,SAAS,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,SAAA,IAAa,CAAC,KAAK,EAAC;AAE3D,IAAA,MAAM,OAAO,OAAA,EAAS,GAAA,CAAI,OAAK,CAAA,CAAE,WAAW,KAAK,EAAC;AAElD,IAAA,MAAM,QAAA,GAAW,CACf,KAAA,EAAe,MAAA,EAAyB,WAAA,EACxC,aAAA,EAAwB,eAAA,EACxB,aAAA,EAA+B,cAAA,EAAwB,GAAA,EACvD,WAAA,EAAsB,QAAA,KACnB,MAAM,WAAA,CAAY;AAAA,MACrB,KAAA;AAAA,MAAO,MAAA;AAAA,MAAQ,WAAA;AAAA,MAAa,aAAA;AAAA,MAC5B,eAAA;AAAA,MAAiB,aAAA;AAAA,MAAe,cAAA;AAAA,MAAgB,QAAA;AAAA,MAChD,UAAA,EAAY,QAAA;AAAA,MAAU,WAAA,EAAa,IAAA;AAAA,MAAM,GAAA;AAAA,MAAK;AAAA,KAC/C,CAAA;AAGD,IAAA,MAAM,eAAA,GAA4B,IAAA,CAAK,GAAA,CAAI,MAAM,CAAC,CAAA;AAClD,IAAA,IAAI,IAAA,CAAK,MAAA,GAAS,CAAA,IAAK,OAAA,EAAS,kBAAkB,UAAA,EAAY;AAC5D,MAAA,KAAA,MAAW,EAAA,IAAM,OAAA,CAAQ,gBAAA,CAAiB,UAAA,EAAY;AACpD,QAAA,MAAM,IAAI,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,EAAE,OAAA,EAAQ;AACxC,QAAA,IAAI,OAAA,GAAU,GAAG,QAAA,GAAW,QAAA;AAC5B,QAAA,IAAA,CAAK,OAAA,CAAQ,CAAC,GAAA,EAAK,CAAA,KAAM;AAAE,UAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,GAAI,GAAG,CAAA;AAAG,UAAA,IAAI,IAAI,QAAA,EAAU;AAAE,YAAA,QAAA,GAAW,CAAA;AAAG,YAAA,OAAA,GAAU,CAAA;AAAA,UAAG;AAAA,QAAE,CAAC,CAAA;AAC1G,QAAA,eAAA,CAAgB,OAAO,CAAA,EAAA;AAAA,MACzB;AAAA,IACF;AAEA,IAAA,uBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,QAAQ,QAAA,EAAU,MAAA,EAAQ,GAAA,EAAK,EAAA,EAAG,EAAA,kBACvD,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,sBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,mBAAA;AAAA,QAChB,WAAA,EAAY,2CAAA;AAAA,QACZ,aAAA,EAAe,UAAA;AAAA,QACf,aAAA,EAAc,KAAA;AAAA,QACd,cAAA,EAAe,SAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,QAAA,EAAU,QAAA;AAAA,UAAS,sBAAA;AAAA,UAAwB,OAAA,CAAQ,mBAAA;AAAA,UACjD,2CAAA;AAAA,UAA6C,KAAA;AAAA,UAAO,UAAA;AAAA,UAAY,KAAA;AAAA,UAAO,SAAA;AAAA,UACvE,QAAQ,mBAAA,CAAoB,UAAA;AAAA,UAAY,MAAA;AAAA,UAAW;AAAA;AAAO;AAAA,KAC9D,kBACA,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,uBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,QAAA;AAAA,QAChB,WAAA,EAAY,wCAAA;AAAA,QACZ,aAAA,EAAa,IAAA;AAAA,QACb,aAAA,EAAe,QAAA;AAAA,QACf,aAAA,EAAc,MAAA;AAAA,QACd,cAAA,EAAe,SAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,QAAA,EAAU,QAAA;AAAA,UAAS,uBAAA;AAAA,UAAyB,OAAA,CAAQ,QAAA;AAAA,UAClD,wCAAA;AAAA,UAA0C,IAAA;AAAA,UAAM,QAAA;AAAA,UAAU,MAAA;AAAA,UAAQ,SAAA;AAAA,UAAW,QAAQ,QAAA,CAAS,UAAA;AAAA,UAAY,MAAA;AAAA,UAAW;AAAA;AAAO;AAAA,KAChI,EACC,OAAA,CAAQ,iBAAA,KAAsB,IAAA,mBAC7B,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,qBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,iBAAA;AAAA,QAChB,WAAA,EAAY,wCAAA;AAAA,QACZ,aAAA,EAAa,IAAA;AAAA,QACb,aAAA,EAAe,OAAA;AAAA,QACf,aAAA,EAAc,MAAA;AAAA,QACd,cAAA,EAAe,SAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,QAAA,EAAU,QAAA;AAAA,UAAS,qBAAA;AAAA,UAAuB,OAAA,CAAQ,iBAAA;AAAA,UAChD,wCAAA;AAAA,UAA0C,IAAA;AAAA,UAAM,OAAA;AAAA,UAAS,MAAA;AAAA,UAAQ,SAAA;AAAA,UAAW,MAAA;AAAA,UAAW,MAAA;AAAA,UAAW;AAAA;AAAO;AAAA,KAC7G,mBAEA,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,KAAA,EAAM,qBAAA,EAAsB,aAAY,qCAAA,EAAsC,CAAA,EAEvF,OAAA,CAAQ,IAAA,KAAS,IAAA,mBAChB,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,sBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,IAAA;AAAA,QAChB,WAAA,EAAY,uCAAA;AAAA,QACZ,aAAA,EAAa,IAAA;AAAA,QACb,aAAA,EAAe,QAAA;AAAA,QACf,aAAA,EAAc,MAAA;AAAA,QACd,cAAA,EAAe,SAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,QAAA,EAAU,QAAA;AAAA,UAAS,sBAAA;AAAA,UAAwB,OAAA,CAAQ,IAAA;AAAA,UACjD,uCAAA;AAAA,UAAyC,IAAA;AAAA,UAAM,QAAA;AAAA,UAAU,MAAA;AAAA,UAAQ,SAAA;AAAA,UAAW,QAAQ,IAAA,CAAK,UAAA;AAAA,UAAY,MAAA;AAAA,UAAW;AAAA;AAAO;AAAA,KAC3H,mBAEA,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,KAAA,EAAM,sBAAA,EAAuB,aAAY,qCAAA,EAAsC,CAAA,EAExF,OAAA,CAAQ,gBAAA,KAAqB,IAAA,mBAC5B,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,wBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,gBAAA;AAAA,QAChB,WAAA,EAAa,CAAA,aAAA,EAAgB,WAAA,EAAa,KAAA,IAAS,QAAQ,CAAA,sBAAA,CAAA;AAAA,QAC3D,aAAA,EAAa,IAAA;AAAA,QACb,aAAa,OAAA,CAAQ,gBAAA,CAAiB,KAAA,GAAQ,CAAA,GAAI,QAAQ,OAAA,CAAQ,gBAAA,CAAiB,KAAK,CAAA,GAAA,EAAM,QAAQ,gBAAA,CAAiB,KAAA,KAAU,CAAA,GAAI,GAAA,GAAM,EAAE,CAAA,OAAA,CAAA,GAAO,MAAA;AAAA,QACpJ,QAAA,EAAU,QAAA;AAAA,UACR,wBAAA;AAAA,UACA,OAAA,CAAQ,gBAAA;AAAA,UACR,CAAA,aAAA,EAAgB,WAAA,EAAa,KAAA,IAAS,QAAQ,CAAA,sBAAA,CAAA;AAAA,UAC9C,IAAA;AAAA,UACA,eAAA;AAAA,UACA,KAAA;AAAA,UACA,SAAA;AAAA,UACA,QAAQ,gBAAA,CAAiB,UAAA;AAAA,UACzB,cAAA;AAAA,UACA;AAAA;AACF;AAAA,wBAGF,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,OAAM,wBAAA,EAAyB,WAAA,EAAY,uCAAsC,CAE7F,CAAA;AAAA,EAEJ,CAAA,GAEF,CACF,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"DoraMetricsContent.esm.js","sources":["../../src/components/DoraMetricsContent.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { useTheme } from '@material-ui/core/styles';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { InfoCard } from '@backstage/core-components';\nimport { doraMetricsApiRef } from '../api/types';\nimport { niceHourTicks, niceCountTicks } from '../api/chartUtils';\nimport type { DoraEnvironment, DoraHistoryPoint, DoraMetrics, DoraMetricValue, DoraTargets, PrDetail } from '../api/types';\nimport { RatingBadge } from './ui/badge';\nimport { Card, CardLabel, CardFooter, CardDescription } from './ui/card';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from './ui/select';\nimport { ExternalLink } from 'lucide-react';\n\nconst ANNOTATION_PROJECT_SLUG = 'github.com/project-slug';\n/** Override the global environments list for this specific entity. JSON array of DoraEnvironment objects. */\nconst ANNOTATION_ENVIRONMENTS = 'dora-metrics/environments';\n/** Override the global performance targets for this specific entity. JSON object matching DoraTargets. */\nconst ANNOTATION_TARGETS = 'dora-metrics/targets';\n\n/** Parse entity annotation overrides. Returns undefined for malformed JSON to fail gracefully. */\nfunction parseAnnotationJson<T>(raw: string | undefined): T | undefined {\n if (!raw) return undefined;\n try { return JSON.parse(raw) as T; } catch { return undefined; }\n}\n\nconst DATE_RANGE_OPTIONS = [\n { value: '7', label: 'Last 7 days' },\n { value: '14', label: 'Last 14 days' },\n { value: '30', label: 'Last 30 days' },\n { value: '60', label: 'Last 60 days' },\n { value: '90', label: 'Last 90 days' },\n];\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\nfunction formatDuration(hours: number): string {\n const totalMinutes = Math.round(hours * 60);\n const d = Math.floor(totalMinutes / (24 * 60));\n const h = Math.floor((totalMinutes % (24 * 60)) / 60);\n const m = totalMinutes % 60;\n if (d > 0) return h > 0 ? `${d}d ${h}h` : `${d}d`;\n if (h > 0) return m > 0 ? `${h}h ${m}m` : `${h}h`;\n return `${m}m`;\n}\n\n// Red → Orange → Yellow → Lime → Green gradient (slowest to fastest)\nconst DURATION_GRADIENT = ['#f87171', '#fb923c', '#fbbf24', '#a3e635', '#4ade80'];\n\nfunction durationColor(index: number, total: number): string {\n if (total <= 1) return DURATION_GRADIENT[0];\n const step = (DURATION_GRADIENT.length - 1) / (total - 1);\n return DURATION_GRADIENT[Math.round(index * step)];\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\n// Period-over-period delta — compares last bucket vs second-to-last bucket\nfunction buildDeltaLabel(\n values: number[],\n unit: string,\n lowerIsBetter: boolean,\n): { text: string; tooltip: string; good: boolean } | null {\n if (values.length < 2) return null;\n const curr = values[values.length - 1];\n const prev = values[values.length - 2];\n const diff = curr - prev;\n if (Math.abs(diff) < 0.001) return null;\n const good = lowerIsBetter ? diff < 0 : diff > 0;\n const arrow = diff > 0 ? '↑' : '↓';\n\n const fmt = (v: number) =>\n unit === 'hours' ? formatDuration(v) :\n unit === '%' ? `${Math.round(v * 10) / 10}%` :\n unit === 'per week' ? `${Math.round(v * 10) / 10}/wk` :\n `${Math.round(v * 10) / 10} ${unit}`;\n\n const direction = good ? 'improving' : 'worsening';\n return {\n text: `${arrow} ${fmt(Math.abs(diff))} ${direction}`,\n tooltip: `Latest period: ${fmt(curr)} · Previous period: ${fmt(prev)}\\nEach period = one time bucket (~1/7th of your selected date range)`,\n good,\n };\n}\n\n// ─── Loading overlay ────────────────────────────────────────────────────────\n\nconst LOADING_MESSAGES = [\n 'Establishing routes…',\n 'Syncing catalog…',\n 'Loading plugins…',\n 'Fetching PR data…',\n 'Reading entity graph…',\n];\nconst STAGES = ['FETCH', 'PARSE', 'SCORE', 'RENDER'] as const;\nconst FONT_IMPACT = '\"Impact\",\"Haettenschweiler\",\"Franklin Gothic Heavy\",\"Arial Black\",sans-serif';\nconst FONT_MONO = '\"JetBrains Mono\",\"Fira Code\",\"Consolas\",monospace';\n\nfunction DoraLoadingOverlay() {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n\n const [activeStage, setActiveStage] = useState(0);\n const [msgKey, setMsgKey] = useState(0);\n const [msgIdx, setMsgIdx] = useState(0);\n\n useEffect(() => {\n const st = setInterval(() => setActiveStage(s => (s + 1) % STAGES.length), 800);\n return () => clearInterval(st);\n }, []);\n\n useEffect(() => {\n const mt = setInterval(() => { setMsgIdx(i => (i + 1) % LOADING_MESSAGES.length); setMsgKey(k => k + 1); }, 2200);\n return () => clearInterval(mt);\n }, []);\n\n const textShadow = isDark\n ? '3px 3px 0 #C24E00, 6px 6px 0 #902800, 9px 9px 0 #5A1800, 12px 12px 0 rgba(0,0,0,0.55), 0 0 60px rgba(250,100,0,0.45)'\n : '3px 3px 0 #C24E00, 6px 6px 0 #902800, 9px 9px 0 rgba(80,20,0,0.28), 0 0 32px rgba(250,100,0,0.22)';\n\n const mutedColor = isDark ? '#3f3f46' : '#a1a1aa';\n const subtitleColor = isDark ? '#52525b' : '#71717a';\n const borderColor = isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.08)';\n\n return (\n <>\n {/* Frosted glass backdrop — blurs whatever is behind */}\n <div aria-hidden style={{\n position: 'fixed', inset: 0, zIndex: 900,\n backdropFilter: 'blur(12px)',\n WebkitBackdropFilter: 'blur(12px)',\n background: isDark ? 'rgba(6,6,9,0.65)' : 'rgba(253,252,251,0.70)',\n }} />\n\n {/* Centered card */}\n <div style={{\n position: 'fixed', zIndex: 901,\n top: '50%', left: '50%',\n transform: 'translateX(-50%) translateY(-50%)',\n display: 'flex', flexDirection: 'column', alignItems: 'center',\n padding: '48px 56px 40px',\n borderRadius: 20,\n background: isDark ? 'rgba(9,9,12,0.82)' : 'rgba(255,255,255,0.88)',\n border: `1px solid ${borderColor}`,\n boxShadow: isDark\n ? '0 0 0 1px rgba(250,100,0,0.06), 0 32px 80px rgba(0,0,0,0.65)'\n : '0 32px 80px rgba(0,0,0,0.12)',\n backdropFilter: 'blur(20px)',\n WebkitBackdropFilter: 'blur(20px)',\n animation: 'dlsCardIn 0.35s cubic-bezier(0.34,1.2,0.64,1) both',\n overflow: 'visible',\n }}>\n\n {/* Breathing ambient glow — inside card */}\n <div aria-hidden style={{\n position: 'absolute', width: 500, height: 360,\n left: '50%', top: '50%',\n transform: 'translateX(-50%) translateY(-50%)',\n background: `radial-gradient(ellipse at center, ${isDark ? 'rgba(250,100,0,0.08)' : 'rgba(250,100,0,0.05)'} 0%, transparent 70%)`,\n animation: 'dlsBreath 3.2s ease-in-out infinite',\n pointerEvents: 'none', borderRadius: 20,\n }} />\n\n {/* DORA wordmark */}\n <div style={{ display: 'flex', lineHeight: 1, marginBottom: 6 }}>\n {['D','O','R','A'].map((letter, i) => (\n <span key={i} style={{\n fontFamily: FONT_IMPACT, fontWeight: 900, fontSize: 110,\n color: '#FA6400', letterSpacing: '0.04em', display: 'inline-block',\n transform: 'skewX(-6deg)', textShadow,\n WebkitTextStroke: '0.5px #C24E00',\n animation: `dlsLetterDrop 0.6s cubic-bezier(0.34,1.45,0.64,1) ${i * 0.1}s both`,\n }}>\n {letter}\n </span>\n ))}\n </div>\n\n {/* Subtitle */}\n <span style={{ fontFamily: 'Inter,system-ui,sans-serif', fontSize: 13, fontWeight: 400,\n letterSpacing: '0.06em', color: subtitleColor, marginBottom: 32,\n animation: 'dlsFadeUp 0.4s ease 0.55s both' }}>\n Metrics\n </span>\n\n {/* Pipeline stages */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 0, marginBottom: 20, animation: 'dlsFadeUp 0.4s ease 0.7s both' }}>\n {STAGES.map((stage, i) => {\n const isActive = i === activeStage;\n const isComplete = i < activeStage;\n return (\n <div key={stage} style={{ display: 'flex', alignItems: 'center', gap: 0 }}>\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5, width: 64 }}>\n <div style={{ position: 'relative', width: 12, height: 12 }}>\n {isActive && (\n <div style={{ position: 'absolute', inset: -6, borderRadius: '50%',\n border: '1.5px solid rgba(250,100,0,0.5)', animation: 'dlsPulse 1s ease-out infinite' }} />\n )}\n <div style={{\n width: 12, height: 12, borderRadius: '50%',\n background: (isActive || isComplete) ? '#FA6400' : (isDark ? '#27272a' : '#e4e4e7'),\n border: `2px solid ${(isActive || isComplete) ? '#C24E00' : (isDark ? '#3f3f46' : '#d4d4d8')}`,\n boxShadow: isActive ? '0 0 14px rgba(250,100,0,0.7)' : 'none',\n transition: 'background 0.3s, box-shadow 0.3s',\n }} />\n </div>\n <span style={{ fontFamily: 'Inter,system-ui,sans-serif', fontSize: 8, fontWeight: 700,\n letterSpacing: '0.12em', textTransform: 'uppercase',\n color: isActive ? '#FA6400' : isComplete ? mutedColor : mutedColor,\n transition: 'color 0.3s' }}>\n {stage}\n </span>\n </div>\n {i < STAGES.length - 1 && (\n <div style={{ width: 32, height: 2, marginBottom: 14, position: 'relative', overflow: 'hidden',\n background: isDark ? '#27272a' : '#e4e4e7', borderRadius: 2 }}>\n <div style={{\n position: 'absolute', inset: 0, borderRadius: 2, background: '#FA6400',\n transformOrigin: 'left center',\n transform: `scaleX(${i < activeStage ? 1 : isActive ? 0.5 : 0})`,\n transition: 'transform 0.4s ease',\n }} />\n </div>\n )}\n </div>\n );\n })}\n </div>\n\n {/* Status message */}\n <span key={msgKey} style={{ fontFamily: FONT_MONO, fontSize: 11,\n color: mutedColor, letterSpacing: '0.04em', animation: 'dlsFadeUp 0.3s ease both' }}>\n {LOADING_MESSAGES[msgIdx]}\n </span>\n\n </div>{/* end card */}\n\n <style>{`\n @keyframes dlsLetterDrop {\n from { opacity: 0; transform: skewX(-6deg) translateY(-32px) scale(1.1); }\n 65% { transform: skewX(-6deg) translateY(6px) scale(0.95); }\n to { opacity: 1; transform: skewX(-6deg) translateY(0) scale(1); }\n }\n @keyframes dlsFadeUp {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes dlsBreath {\n 0%, 100% { transform: translateX(-50%) translateY(-50%) scale(0.85); opacity: 0.6; }\n 50% { transform: translateX(-50%) translateY(-50%) scale(1.2); opacity: 1; }\n }\n @keyframes dlsPulse {\n from { transform: scale(0.6); opacity: 0.9; }\n to { transform: scale(2.2); opacity: 0; }\n }\n @keyframes dlsCardIn {\n from { opacity: 0; transform: translateX(-50%) translateY(-46%); }\n to { opacity: 1; transform: translateX(-50%) translateY(-50%); }\n }\n `}</style>\n </>\n );\n}\n\n// ─── Compact sparkline (with hover tooltip + date axis) ────────────────────\n\nfunction CompactSparkline({\n values,\n weekLabels,\n color,\n type,\n formatValue,\n height = 36,\n}: {\n values: number[];\n weekLabels: string[];\n color: string;\n type: 'bar' | 'line';\n formatValue: (v: number) => string;\n height?: number;\n}) {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n const [hoverIdx, setHoverIdx] = useState<number | null>(null);\n\n const VW = 400; // viewBox units\n const mutedBorder = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)';\n const tooltipBg = isDark ? '#1c1c20' : '#ffffff';\n const tooltipBdr = isDark ? '#3f3f46' : '#e4e4e7';\n const tooltipText = isDark ? '#ededef' : '#111114';\n const font = 'Inter,system-ui,sans-serif';\n\n const handleMouseMove = (e: React.MouseEvent<SVGSVGElement>) => {\n const rect = e.currentTarget.getBoundingClientRect();\n const relX = (e.clientX - rect.left) / rect.width;\n setHoverIdx(Math.max(0, Math.min(values.length - 1, Math.round(relX * (values.length - 1)))));\n };\n\n if (type === 'bar') {\n const max = Math.max(...values, 1);\n const gap = 4;\n const barW = (VW - gap * (values.length - 1)) / values.length;\n\n const hoverX = hoverIdx !== null\n ? hoverIdx * (barW + gap) + barW / 2\n : null;\n const tooltipX = hoverX !== null ? Math.min(hoverX - 34, VW - 72) : 0;\n\n return (\n <div>\n <svg viewBox={`0 0 ${VW} ${height}`} width=\"100%\" style={{ display: 'block', overflow: 'visible' }}\n onMouseMove={handleMouseMove} onMouseLeave={() => setHoverIdx(null)}>\n {values.map((v, i) => {\n const h = Math.max(2, (v / max) * (height - 4));\n return (\n <rect key={i} x={i * (barW + gap)} y={height - h} width={barW} height={h} rx={1.5}\n fill={color} opacity={i === hoverIdx ? 1 : 0.3 + (i / values.length) * 0.55} />\n );\n })}\n <line x1={0} y1={height - 0.5} x2={VW} y2={height - 0.5} stroke={mutedBorder} strokeWidth={1} />\n {hoverIdx !== null && hoverX !== null && (\n <>\n <rect x={tooltipX} y={2} width={72} height={22} rx={4} fill={tooltipBg} stroke={tooltipBdr} strokeWidth={1} />\n <text x={tooltipX + 36} y={17} textAnchor=\"middle\" fill={tooltipText} fontSize={9} fontWeight={600} fontFamily={font}>\n {weekLabels[hoverIdx]}: {formatValue(values[hoverIdx])}\n </text>\n </>\n )}\n </svg>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 3 }}>\n <span style={{ fontSize: 9, color: isDark ? '#3f3f46' : '#a1a1aa' }}>{weekLabels[0]}</span>\n <span style={{ fontSize: 9, color: isDark ? '#3f3f46' : '#a1a1aa' }}>{weekLabels[weekLabels.length - 1]}</span>\n </div>\n </div>\n );\n }\n\n if (values.length < 2) return null;\n const max = Math.max(...values, 0.01);\n const min = Math.min(...values);\n const range = max - min || max || 1;\n const pad = 3;\n const pts = values.map((v, i): [number, number] => [\n (i / (values.length - 1)) * (VW - pad * 2) + pad,\n (height - pad * 2) - ((v - min) / range) * (height - pad * 2 - 2) + pad,\n ]);\n const d = pts.map(([x, y], i) => `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`).join(' ');\n const area = `${d} L${pts[pts.length-1][0]},${height} L${pts[0][0]},${height} Z`;\n const hoverPt = hoverIdx !== null ? pts[hoverIdx] : null;\n const tooltipX2 = hoverPt ? Math.min(hoverPt[0] - 34, VW - 72) : 0;\n\n return (\n <div>\n <svg viewBox={`0 0 ${VW} ${height}`} width=\"100%\" style={{ display: 'block', overflow: 'visible' }}\n onMouseMove={handleMouseMove} onMouseLeave={() => setHoverIdx(null)}>\n <path d={area} fill={color} opacity={0.07} />\n <path d={d} fill=\"none\" stroke={color} strokeWidth={1.5} strokeLinejoin=\"round\" strokeLinecap=\"round\" />\n {pts.map(([x, y], i) => (\n <circle key={i} cx={x} cy={y} r={i === hoverIdx ? 3.5 : i === pts.length - 1 ? 2.5 : 0} fill={color} />\n ))}\n <line x1={0} y1={height - 0.5} x2={VW} y2={height - 0.5} stroke={mutedBorder} strokeWidth={1} />\n {hoverPt && hoverIdx !== null && (\n <>\n <line x1={hoverPt[0]} y1={0} x2={hoverPt[0]} y2={height} stroke={color} strokeWidth={1} strokeDasharray=\"2 2\" opacity={0.4} />\n <rect x={tooltipX2} y={2} width={72} height={22} rx={4} fill={tooltipBg} stroke={tooltipBdr} strokeWidth={1} />\n <text x={tooltipX2 + 36} y={17} textAnchor=\"middle\" fill={tooltipText} fontSize={9} fontWeight={600} fontFamily={font}>\n {weekLabels[hoverIdx]}: {formatValue(values[hoverIdx])}\n </text>\n </>\n )}\n </svg>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 3 }}>\n <span style={{ fontSize: 9, color: isDark ? '#3f3f46' : '#a1a1aa' }}>{weekLabels[0]}</span>\n <span style={{ fontSize: 9, color: isDark ? '#3f3f46' : '#a1a1aa' }}>{weekLabels[weekLabels.length - 1]}</span>\n </div>\n </div>\n );\n}\n\n// ─── Detailed interactive chart ────────────────────────────────────────────\n\ninterface ChartTooltipState {\n x: number;\n y: number;\n primary: string;\n secondary?: string;\n}\n\nfunction DetailedChart({\n values,\n weekLabels,\n bucketMidMs,\n color,\n type,\n formatValue,\n tickUnit,\n prs,\n days,\n hoveredPrNumber,\n onPrHover,\n}: {\n values: number[];\n weekLabels: string[];\n bucketMidMs: number[];\n color: string;\n type: 'bar' | 'line';\n formatValue: (v: number) => string;\n tickUnit?: 'hours' | 'count';\n prs?: PrDetail[];\n days: number;\n hoveredPrNumber?: number | null;\n onPrHover?: (num: number | null) => void;\n}) {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n const [hoverIdx, setHoverIdx] = useState<number | null>(null);\n const [tooltip, setTooltip] = useState<ChartTooltipState | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const W = 800, H = 260;\n const PAD = { top: 16, right: 16, bottom: 36, left: 56 };\n const cW = W - PAD.left - PAD.right;\n const cH = H - PAD.top - PAD.bottom;\n\n // Y scale: nice tick-aligned range so axis labels are clean intervals.\n const prMaxDuration = prs && prs.length > 0 ? Math.max(...prs.map(p => p.durationHours)) : 0;\n const rawDataMax = Math.max(...values.filter(v => v > 0), prMaxDuration, 0.01);\n const yTicks = tickUnit === 'hours'\n ? niceHourTicks(rawDataMax)\n : tickUnit === 'count'\n ? niceCountTicks(rawDataMax)\n : [0, 0.25, 0.5, 0.75, 1].map(t => t * rawDataMax);\n const dataMin = 0;\n const dataMax = yTicks[yTicks.length - 1];\n const dataRange = dataMax - dataMin || 1;\n const yScale = (v: number) => cH - ((v - dataMin) / dataRange) * cH;\n\n // Shared timestamp-based x-axis\n const cutoffMs = Date.now() - days * 24 * 60 * 60 * 1000;\n const rangeMs = days * 24 * 60 * 60 * 1000;\n const xTime = (ms: number) => Math.max(0, Math.min(cW, ((ms - cutoffMs) / rangeMs) * cW));\n\n const xIdx = (i: number) => bucketMidMs.length === values.length\n ? xTime(bucketMidMs[i])\n : (values.length > 1 ? (i / (values.length - 1)) * cW : cW / 2);\n\n const pts: [number, number][] = values.map((v, i) => [PAD.left + xIdx(i), PAD.top + yScale(v)]);\n const linePath = pts.map(([x, y], i) => `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`).join(' ');\n const areaPath = pts.length > 1\n ? `${linePath} L${pts[pts.length-1][0]},${PAD.top+cH} L${pts[0][0]},${PAD.top+cH} Z`\n : '';\n\n const gap = 6;\n const barW = values.length > 1 ? (cW - gap * (values.length - 1)) / values.length : cW;\n\n const gridColor = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)';\n const axisColor = isDark ? '#3f3f46' : '#d4d4d8';\n const labelColor = isDark ? '#52525b' : '#a1a1aa';\n const font = 'Inter,system-ui,sans-serif';\n const gradId = `dg_${color.replace('#','')}`;\n\n // Find the nearest bucket index for a given timestamp\n const nearestBucket = (ms: number): number => {\n if (bucketMidMs.length === 0) return 0;\n let best = 0, bestDist = Infinity;\n bucketMidMs.forEach((mid, i) => {\n const d = Math.abs(ms - mid);\n if (d < bestDist) { bestDist = d; best = i; }\n });\n return best;\n };\n\n const handleSvgMouseMove = (e: React.MouseEvent<SVGSVGElement>) => {\n const rect = e.currentTarget.getBoundingClientRect();\n const relX = (e.clientX - rect.left - PAD.left * (rect.width / W)) / (rect.width * cW / W);\n const idx = Math.max(0, Math.min(values.length - 1, Math.round(relX * (values.length - 1))));\n setHoverIdx(idx);\n const cRect = containerRef.current?.getBoundingClientRect();\n const x = cRect ? e.clientX - cRect.left : 0;\n const y = cRect ? e.clientY - cRect.top : 0;\n setTooltip({ x, y, primary: formatValue(values[idx]), secondary: weekLabels[idx] });\n };\n\n return (\n <div ref={containerRef} style={{ position: 'relative' }}>\n <svg viewBox={`0 0 ${W} ${H}`} width=\"100%\" style={{ display: 'block' }}\n onMouseMove={handleSvgMouseMove}\n onMouseLeave={() => { setHoverIdx(null); setTooltip(null); }}>\n <defs>\n <linearGradient id={gradId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={color} stopOpacity={0.28} />\n <stop offset=\"100%\" stopColor={color} stopOpacity={0} />\n </linearGradient>\n {/* Clip PR dots to the chart area */}\n <clipPath id={`prClip_${gradId}`}>\n <rect x={PAD.left} y={PAD.top - 8} width={cW} height={cH + 16} />\n </clipPath>\n </defs>\n\n {/* Grid + Y-axis */}\n {yTicks.map((v, i) => {\n const y = PAD.top + yScale(v);\n return (\n <g key={i}>\n {i > 0 && <line x1={PAD.left} y1={y} x2={PAD.left+cW} y2={y} stroke={gridColor} strokeWidth={1} strokeDasharray=\"3 4\" />}\n <text x={PAD.left - 6} y={y + 4} textAnchor=\"end\" fill={labelColor} fontSize={10} fontFamily={font}>\n {formatValue(v)}\n </text>\n </g>\n );\n })}\n <line x1={PAD.left} y1={PAD.top+cH} x2={PAD.left+cW} y2={PAD.top+cH} stroke={axisColor} strokeWidth={1} />\n\n {/* Chart body */}\n {type === 'bar' ? (\n values.map((v, i) => {\n const bH = Math.max(2, ((v - dataMin) / dataRange) * cH);\n const x = PAD.left + i * (barW + gap);\n return (\n <rect key={i} x={x} y={PAD.top+cH-bH} width={barW} height={bH} rx={3}\n fill={color} opacity={i === hoverIdx ? 0.95 : 0.4 + (i / Math.max(values.length - 1, 1)) * 0.45} />\n );\n })\n ) : (\n <>\n {/* When prs are provided, show scatter-only (no line/area/bucket dots).\n For CFR (no prs), keep a minimal line + non-zero bucket dots. */}\n {!prs && areaPath && <path d={areaPath} fill={`url(#${gradId})`} />}\n {!prs && <path d={linePath} fill=\"none\" stroke={color} strokeWidth={2} strokeLinejoin=\"round\" strokeLinecap=\"round\" />}\n {!prs && pts.map(([x, y], i) => values[i] > 0 && (\n <circle key={i} cx={x} cy={y}\n r={i === hoverIdx ? 5.5 : 4}\n fill={isDark ? '#0f0f12' : '#ffffff'} stroke={color}\n strokeWidth={i === hoverIdx ? 2.5 : 1.5} />\n ))}\n </>\n )}\n\n {/* PR data-point dots — x at bucket midpoint, y at actual duration (clamped to chart) */}\n {prs && type === 'line' && (\n <g clipPath={`url(#prClip_${gradId})`}>\n {prs.map((pr, dotIdx) => {\n const mergedMs = new Date(pr.mergedAt).getTime();\n const bIdx = nearestBucket(mergedMs);\n // Small deterministic jitter within the bucket to avoid overlap\n const jitter = ((pr.number * 17 + dotIdx * 11) % 13 - 6) * 5;\n const px = PAD.left + xIdx(bIdx) + jitter;\n // Clamp y: PR may be faster or slower than bucket avg; always visible\n const rawPy = PAD.top + yScale(pr.durationHours);\n const py = Math.max(PAD.top + 4, Math.min(PAD.top + cH - 4, rawPy));\n const isHov = pr.number === hoveredPrNumber;\n\n // Thin connector from dot to the bucket trend point\n const trendPt = pts[bIdx];\n\n return (\n <g key={pr.number} style={{ cursor: 'pointer' }}\n onMouseEnter={e => {\n e.stopPropagation();\n const cRect = containerRef.current?.getBoundingClientRect();\n const x = cRect ? e.clientX - cRect.left : 0;\n const y = cRect ? e.clientY - cRect.top : 0;\n setTooltip({ x, y,\n primary: `#${pr.number}: ${pr.title}`,\n secondary: `${formatValue(pr.durationHours)} · ${new Date(pr.mergedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}` });\n onPrHover?.(pr.number);\n }}\n onMouseLeave={() => { setTooltip(null); onPrHover?.(null); }}>\n {/* connector removed — no bucket-average dot to connect to */}\n <circle cx={px} cy={py} r={isHov ? 11 : 7} fill={color} opacity={isHov ? 0.2 : 0.1} />\n <circle cx={px} cy={py} r={isHov ? 5.5 : 3.5}\n fill={isHov ? color : (isDark ? '#1a1a20' : '#fff')}\n stroke={color} strokeWidth={isHov ? 0 : 1.5}\n opacity={isHov ? 1 : 0.8} />\n </g>\n );\n })}\n </g>\n )}\n\n {/* Hover crosshair */}\n {hoverIdx !== null && type === 'line' && pts[hoverIdx] && (\n <>\n <line x1={pts[hoverIdx][0]} y1={PAD.top} x2={pts[hoverIdx][0]} y2={PAD.top+cH}\n stroke={color} strokeWidth={1} strokeDasharray=\"3 3\" opacity={0.3} />\n <line x1={PAD.left} y1={pts[hoverIdx][1]} x2={PAD.left+cW} y2={pts[hoverIdx][1]}\n stroke={color} strokeWidth={1} strokeDasharray=\"3 3\" opacity={0.2} />\n </>\n )}\n\n {/* X-axis labels */}\n {weekLabels.map((lbl, i) => {\n const total = weekLabels.length;\n const step = Math.max(1, Math.floor(total / 5));\n if (i !== 0 && i !== total - 1 && i % step !== 0) return null;\n const x = type === 'bar'\n ? PAD.left + i * (barW + gap) + barW / 2\n : PAD.left + xIdx(i);\n return (\n <text key={i} x={x} y={H - 8} textAnchor=\"middle\" fill={labelColor} fontSize={10} fontFamily={font}>\n {lbl}\n </text>\n );\n })}\n </svg>\n\n {/* Absolute tooltip — positioned relative to the chart container */}\n {tooltip && (() => {\n const TW = 220;\n const cW2 = containerRef.current?.offsetWidth ?? 800;\n const nearRight = tooltip.x + TW + 12 > cW2;\n return (\n <div style={{\n position: 'absolute',\n left: nearRight ? tooltip.x - TW - 8 : tooltip.x + 8,\n top: tooltip.y - 36,\n zIndex: 9999, pointerEvents: 'none',\n background: isDark ? '#18181b' : '#ffffff',\n border: `1px solid ${isDark ? '#3f3f46' : '#e4e4e7'}`,\n borderRadius: 7, padding: '7px 10px',\n boxShadow: isDark ? '0 6px 20px rgba(0,0,0,0.55)' : '0 6px 20px rgba(0,0,0,0.10)',\n width: TW,\n }}>\n <div style={{ fontSize: 12, fontWeight: 600, color: isDark ? '#ededef' : '#111114',\n fontFamily: font, lineHeight: 1.4 }}>\n {tooltip.primary}\n </div>\n {tooltip.secondary && (\n <div style={{ fontSize: 11, color: isDark ? '#71717a' : '#a1a1aa', fontFamily: font, marginTop: 2 }}>\n {tooltip.secondary}\n </div>\n )}\n </div>\n );\n })()}\n </div>\n );\n}\n\n// ─── Detailed overlay ──────────────────────────────────────────────────────\n\ninterface ExpandedCardData {\n title: string;\n metric: DoraMetricValue;\n description: string;\n lowerIsBetter: boolean;\n sparklineValues: number[];\n sparklineType: 'bar' | 'line';\n sparklineColor: string;\n tickUnit?: 'hours' | 'count';\n weekLabels: string[];\n bucketMidMs: number[];\n prs?: PrDetail[];\n /** Override the section header above the PR list (default: \"All deployments\"). */\n prListLabel?: string;\n}\n\nfunction DetailedOverlay({\n data,\n days,\n onClose,\n}: {\n data: ExpandedCardData;\n days: number;\n onClose: () => void;\n}) {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n const githubBaseUrl = useApi(doraMetricsApiRef).getGithubBaseUrl();\n const [hoveredPrNumber, setHoveredPrNumber] = useState<number | null>(null);\n const prRowRefs = useRef<Map<number, HTMLDivElement>>(new Map());\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose]);\n\n // Auto-scroll to the highlighted PR row when hover comes from the chart\n useEffect(() => {\n if (hoveredPrNumber !== null) {\n const el = prRowRefs.current.get(hoveredPrNumber);\n if (el) el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n }\n }, [hoveredPrNumber]);\n\n const { title, metric, description, lowerIsBetter, sparklineValues, sparklineType, sparklineColor, tickUnit, weekLabels, bucketMidMs, prs, prListLabel } = data;\n const isDeployFreq = sparklineType === 'bar';\n const displayValue = metric.unit === 'hours' ? formatDuration(metric.value) : String(metric.value);\n const displayUnit = metric.unit === 'hours' ? '' : metric.unit;\n const formatVal = (v: number) => metric.unit === 'hours' ? formatDuration(v) : `${Math.round(v * 10) / 10}`;\n const borderColor = isDark ? '#27272a' : '#e4e4e7';\n const mutedColor = isDark ? '#52525b' : '#a1a1aa';\n const targetLabel = lowerIsBetter\n ? `Target: < ${metric.unit === 'hours' ? formatDuration(metric.target) : metric.target} ${displayUnit}`\n : `Target: ≥ ${metric.target} ${displayUnit}`;\n\n // Group deployment PRs by date (for the date table)\n const prsByDate = isDeployFreq && prs ? prs.reduce((acc, pr) => {\n const date = new Date(pr.mergedAt).toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });\n if (!acc[date]) acc[date] = [];\n acc[date].push(pr);\n return acc;\n }, {} as Record<string, PrDetail[]>) : {};\n const sortedDates = Object.keys(prsByDate).sort(\n (a, b) => new Date(prsByDate[b][0].mergedAt).getTime() - new Date(prsByDate[a][0].mergedAt).getTime()\n );\n\n return (\n <>\n {/* Backdrop */}\n <div onClick={onClose} style={{\n position: 'fixed', inset: 0, zIndex: 499,\n background: isDark ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.3)',\n backdropFilter: 'blur(4px)',\n }} />\n\n {/* Panel */}\n <div style={{\n position: 'fixed',\n top: '50%', left: '50%',\n transform: 'translateX(-50%) translateY(-50%)',\n width: 'min(92vw, 960px)',\n maxHeight: 'calc(100vh - 80px)',\n zIndex: 500,\n background: isDark ? '#09090c' : '#fafafa',\n border: `1px solid ${borderColor}`,\n borderRadius: 14,\n display: 'flex', flexDirection: 'column',\n overflow: 'hidden',\n animation: 'detailExpandIn 0.2s cubic-bezier(0.34,1.2,0.64,1)',\n boxShadow: isDark ? '0 24px 64px rgba(0,0,0,0.7)' : '0 24px 64px rgba(0,0,0,0.18)',\n }}>\n {/* Header */}\n <div style={{\n display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start',\n padding: '20px 24px 16px',\n borderBottom: `1px solid ${borderColor}`,\n flexShrink: 0,\n }}>\n <div>\n <div style={{ fontSize: 10, fontWeight: 700, color: mutedColor, textTransform: 'uppercase', letterSpacing: '0.12em', marginBottom: 8 }}>\n {title} · {weekLabels[0]} – {weekLabels[weekLabels.length - 1]}\n </div>\n <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>\n <span style={{ fontSize: 52, fontWeight: 700, color: theme.palette.text.primary, lineHeight: 1, letterSpacing: '-0.02em' }}>\n {displayValue}\n </span>\n {displayUnit && (\n <span style={{ fontSize: 18, color: mutedColor, fontWeight: 500 }}>{displayUnit}</span>\n )}\n <RatingBadge rating={metric.rating} />\n </div>\n <div style={{ fontSize: 12, color: mutedColor, marginTop: 6 }}>\n {description} · <span style={{ fontStyle: 'italic' }}>{targetLabel}</span>\n </div>\n </div>\n <button type=\"button\" onClick={onClose} style={{\n all: 'unset', cursor: 'pointer', width: 30, height: 30,\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n borderRadius: '50%', background: isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)',\n color: mutedColor, fontSize: 18, fontWeight: 300, transition: 'all 0.12s', flexShrink: 0,\n }}\n onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.1)'; }}\n onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)'; }}>\n ✕\n </button>\n </div>\n\n {/* Body — scrollable */}\n <div style={{ flex: 1, overflow: 'auto', padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 20 }}>\n\n {/* Chart */}\n {weekLabels.length >= 2 ? (\n <DetailedChart\n values={sparklineValues} weekLabels={weekLabels}\n bucketMidMs={bucketMidMs}\n color={sparklineColor} type={sparklineType}\n tickUnit={tickUnit}\n formatValue={formatVal} prs={isDeployFreq ? undefined : prs} days={days}\n hoveredPrNumber={hoveredPrNumber} onPrHover={setHoveredPrNumber}\n />\n ) : (\n <div style={{ color: mutedColor, fontSize: 13, textAlign: 'center', padding: '48px 0' }}>\n Not enough data — select a wider date range.\n </div>\n )}\n\n {/* === Deployment Frequency: PRs grouped by date === */}\n {isDeployFreq && prs && prs.length > 0 && (\n <div>\n <div style={{ fontSize: 10, fontWeight: 700, color: mutedColor, textTransform: 'uppercase',\n letterSpacing: '0.12em', marginBottom: 12,\n borderTop: `1px solid ${borderColor}`, paddingTop: 12 }}>\n {prListLabel ?? 'All deployments'} · {prs.length} PR{prs.length !== 1 ? 's' : ''}\n </div>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>\n {sortedDates.map(date => (\n <div key={date}>\n {/* Date header */}\n <div style={{\n fontSize: 11, fontWeight: 700, color: sparklineColor,\n marginBottom: 6, display: 'flex', alignItems: 'center', gap: 8,\n }}>\n <span>{date}</span>\n <span style={{ color: mutedColor, fontWeight: 500 }}>· {prsByDate[date].length} PR{prsByDate[date].length > 1 ? 's' : ''}</span>\n </div>\n {/* PR rows */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4, paddingLeft: 12,\n borderLeft: `2px solid ${sparklineColor}22` }}>\n {prsByDate[date].map(pr => (\n <div key={pr.number} style={{\n display: 'flex', flexDirection: 'column', gap: 5,\n padding: '7px 10px', borderRadius: 7,\n background: isDark ? '#0d0d10' : '#f7f7f9',\n border: `1px solid ${borderColor}`,\n transition: 'background 0.12s',\n }}>\n {/* Row 1: number + title + lead time + link */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ fontSize: 10, fontWeight: 700, color: mutedColor, flexShrink: 0, minWidth: 36 }}>\n #{pr.number}\n </span>\n <span style={{ fontSize: 12, color: theme.palette.text.primary, flex: 1,\n whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} title={pr.title}>\n {pr.title}\n </span>\n <span style={{ fontSize: 10, color: mutedColor, flexShrink: 0 }}>\n {formatDuration(pr.durationHours)} lead\n </span>\n <a href={pr.url} target=\"_blank\" rel=\"noopener noreferrer\"\n style={{ color: mutedColor, flexShrink: 0 }}>\n <ExternalLink size={11} />\n </a>\n </div>\n {/* Row 2: author badge */}\n {pr.author && (\n <div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>\n {pr.authorAvatar ? (\n <img src={pr.authorAvatar} alt={pr.author}\n style={{ width: 16, height: 16, borderRadius: '50%', flexShrink: 0,\n border: `1px solid ${isDark ? '#3f3f46' : '#e4e4e7'}` }} />\n ) : (\n <div style={{ width: 16, height: 16, borderRadius: '50%', flexShrink: 0,\n background: isDark ? '#27272a' : '#e4e4e7',\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n fontSize: 8, fontWeight: 700, color: mutedColor }}>\n {pr.author[0].toUpperCase()}\n </div>\n )}\n <a href={`${githubBaseUrl}/${pr.author}`} target=\"_blank\" rel=\"noopener noreferrer\"\n style={{ fontSize: 10, fontWeight: 500, color: mutedColor,\n textDecoration: 'none', letterSpacing: '0.01em' }}\n onMouseEnter={e => { (e.currentTarget as HTMLElement).style.color = '#FA6400'; }}\n onMouseLeave={e => { (e.currentTarget as HTMLElement).style.color = mutedColor; }}>\n @{pr.author}\n </a>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* === Lead Time / MTTR: duration-ranked PR list with hover linking === */}\n {!isDeployFreq && prs && prs.length > 0 && (\n <div>\n <div style={{ fontSize: 10, fontWeight: 700, color: mutedColor, textTransform: 'uppercase',\n letterSpacing: '0.12em', marginBottom: 10,\n borderTop: `1px solid ${borderColor}`, paddingTop: 12 }}>\n {title.toLowerCase().includes('restore') ? 'Slowest Hotfix PRs' : 'Slowest PRs'}\n {' '}· hover a row to highlight on chart\n </div>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>\n {prs.map((pr, i) => {\n const col = durationColor(i, prs.length);\n const maxDur = prs[0].durationHours;\n const barPct = maxDur > 0 ? (pr.durationHours / maxDur) * 100 : 0;\n const isHov = pr.number === hoveredPrNumber;\n return (\n <div key={pr.number}\n ref={(el) => {\n if (el) prRowRefs.current.set(pr.number, el);\n else prRowRefs.current.delete(pr.number);\n }}\n onMouseEnter={() => setHoveredPrNumber(pr.number)}\n onMouseLeave={() => setHoveredPrNumber(null)}\n style={{\n padding: '8px 12px', borderRadius: 8,\n background: isHov\n ? (isDark ? '#18181f' : '#f0f0ff')\n : (isDark ? '#111114' : '#f4f4f6'),\n border: `1px solid ${isHov ? `${sparklineColor}55` : borderColor}`,\n display: 'flex', flexDirection: 'column', gap: 5,\n transition: 'background 0.12s, border-color 0.12s',\n cursor: 'default',\n }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>\n <span style={{ fontSize: 10, fontWeight: 700, color: col, flexShrink: 0 }}>#{pr.number}</span>\n <span style={{ fontSize: 12, color: theme.palette.text.primary, flex: 1,\n whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} title={pr.title}>\n {pr.title}\n </span>\n <span style={{ fontSize: 10, color: mutedColor, flexShrink: 0 }}>\n {new Date(pr.mergedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\n </span>\n <a href={pr.url} target=\"_blank\" rel=\"noopener noreferrer\"\n style={{ color: mutedColor, flexShrink: 0, transition: 'color 0.12s' }}>\n <ExternalLink size={11} />\n </a>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <AnimatedBar widthPct={barPct} color={col} />\n <span style={{ fontSize: 11, fontWeight: 700, color: col, minWidth: 52, textAlign: 'right', flexShrink: 0 }}>\n {formatDuration(pr.durationHours)}\n </span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n </div>\n <style>{`\n @keyframes detailExpandIn {\n from { opacity: 0; transform: translateX(-50%) translateY(-46%); }\n to { opacity: 1; transform: translateX(-50%) translateY(-50%); }\n }\n `}</style>\n </>\n );\n}\n\n// ─── Animated bar ──────────────────────────────────────────────────────────\n\nfunction AnimatedBar({ widthPct, color }: { widthPct: number; color: string }) {\n const [width, setWidth] = useState(0);\n const ref = useRef<HTMLDivElement>(null);\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n\n useEffect(() => {\n const timer = setTimeout(() => setWidth(widthPct), 80);\n return () => clearTimeout(timer);\n }, [widthPct]);\n\n return (\n <div\n ref={ref}\n style={{\n background: isDark ? '#1a1a1e' : '#f0f0f2',\n borderRadius: 4,\n height: 5,\n overflow: 'hidden',\n flex: 1,\n minWidth: 40,\n }}\n >\n <div\n style={{\n height: '100%',\n width: `${width}%`,\n background: color,\n borderRadius: 4,\n transition: 'width 0.55s cubic-bezier(0.4, 0, 0.2, 1)',\n boxShadow: `0 0 6px ${color}88`,\n }}\n />\n </div>\n );\n}\n\n// ─── Metric cards ──────────────────────────────────────────────────────────\n\nfunction MetricCard({\n title,\n metric,\n description,\n lowerIsBetter = false,\n sparklineData,\n sparklineType = 'line',\n sparklineColor,\n weekLabels,\n onExpand,\n expandLabel,\n}: {\n title: string;\n metric: DoraMetricValue;\n description: string;\n lowerIsBetter?: boolean;\n sparklineData?: number[];\n sparklineType?: 'bar' | 'line';\n sparklineColor?: string;\n weekLabels?: string[];\n onExpand?: () => void;\n /** Persistent label shown in the card footer (instead of the hover-only \"expand ↗\"). */\n expandLabel?: string;\n}) {\n const theme = useTheme();\n const isDark = theme.palette.type === 'dark';\n const [hovered, setHovered] = useState(false);\n\n const displayValue = metric.unit === 'hours' ? formatDuration(metric.value) : metric.value;\n const displayUnit = metric.unit === 'hours' ? '' : metric.unit;\n const targetValue = metric.unit === 'hours' ? formatDuration(metric.target) : metric.target;\n const targetLabel = lowerIsBetter\n ? `Target: < ${targetValue} ${displayUnit}`.trim()\n : `Target: ≥ ${targetValue} ${displayUnit}`.trim();\n\n const hasSparkline = sparklineData && sparklineData.length >= 2 && weekLabels && weekLabels.length >= 2;\n const delta = hasSparkline ? buildDeltaLabel(sparklineData!, metric.unit, lowerIsBetter) : null;\n const formatVal = (v: number) => metric.unit === 'hours' ? formatDuration(v) : `${Math.round(v * 10) / 10}`;\n\n return (\n <Card\n style={{ cursor: onExpand ? 'pointer' : undefined }}\n onClick={onExpand}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8 }}>\n <CardLabel>{title}</CardLabel>\n {onExpand && (\n <span style={{\n fontSize: 9, fontWeight: 600, color: isDark ? '#3f3f46' : '#d4d4d8',\n opacity: hovered ? 1 : 0, transition: 'opacity 0.15s',\n letterSpacing: '0.05em', textTransform: 'uppercase', flexShrink: 0,\n }}>\n expand ↗\n </span>\n )}\n </div>\n {/* Right-aligned column: delta trend + direction hint stacked */}\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4 }}>\n {delta && (\n <span title={delta.tooltip} style={{\n fontSize: 10, fontWeight: 600, cursor: 'help',\n color: delta.good ? '#4ade80' : '#f87171',\n background: delta.good\n ? (isDark ? 'rgba(74,222,128,0.08)' : 'rgba(74,222,128,0.12)')\n : (isDark ? 'rgba(248,113,113,0.08)' : 'rgba(248,113,113,0.12)'),\n padding: '2px 8px', borderRadius: 4, whiteSpace: 'nowrap',\n }}>\n {delta.text}\n </span>\n )}\n <span style={{\n fontSize: 10, fontWeight: 600,\n color: isDark ? '#3f3f46' : '#d4d4d8',\n whiteSpace: 'nowrap',\n }}>\n {lowerIsBetter ? '↓ lower = better' : '↑ higher = better'}\n </span>\n </div>\n {/* Value row: big number + unit */}\n <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>\n <span style={{ fontSize: 36, fontWeight: 700, color: isDark ? '#f4f4f5' : '#111114', lineHeight: 1 }}>\n {displayValue}\n </span>\n {displayUnit && (\n <span style={{ fontSize: 14, color: isDark ? '#71717a' : '#71717a', fontWeight: 500 }}>\n {displayUnit}\n </span>\n )}\n </div>\n <RatingBadge rating={metric.rating} />\n {hasSparkline && (\n <div style={{ marginTop: 2 }}>\n <CompactSparkline\n values={sparklineData!}\n weekLabels={weekLabels!}\n color={sparklineColor ?? '#FA6400'}\n type={sparklineType}\n formatValue={formatVal}\n />\n </div>\n )}\n <CardDescription>{description}</CardDescription>\n <CardFooter>\n <span style={{ fontWeight: 600, color: isDark ? '#a1a1aa' : '#52525b' }}>\n {targetLabel}\n </span>\n {expandLabel && onExpand && (\n <span style={{\n fontSize: 10, fontWeight: 700,\n color: '#FA6400',\n letterSpacing: '0.04em',\n marginLeft: 'auto',\n }}>\n {expandLabel}\n </span>\n )}\n </CardFooter>\n </Card>\n );\n}\n\nfunction NaCard({ title, description }: { title: string; description: string }) {\n return (\n <Card>\n <CardLabel>{title}</CardLabel>\n <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>\n <span style={{ fontSize: 36, fontWeight: 700, color: '#52525b', lineHeight: 1 }}>N/A</span>\n </div>\n <CardDescription muted>{description}</CardDescription>\n </Card>\n );\n}\n\n// ─── Filter label ──────────────────────────────────────────────────────────\n\nfunction FilterLabel({ children }: { children: React.ReactNode }) {\n const theme = useTheme();\n return (\n <div style={{ fontSize: 11, fontWeight: 600, color: theme.palette.text.primary, marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.06em' }}>\n {children}\n </div>\n );\n}\n\n// ─── Main component ────────────────────────────────────────────────────────\n\nexport function DoraMetricsContent() {\n const { entity } = useEntity();\n const doraApi = useApi(doraMetricsApiRef);\n const muiTheme = useTheme();\n const textPrimary = muiTheme.palette.text.primary;\n const textSecondary = muiTheme.palette.text.secondary;\n\n const projectSlug = entity.metadata.annotations?.[ANNOTATION_PROJECT_SLUG] ?? '';\n const annotations = entity.metadata.annotations ?? {};\n\n // Per-repo overrides from catalog-info.yaml annotations\n const annotationEnvs = parseAnnotationJson<DoraEnvironment[]>(annotations[ANNOTATION_ENVIRONMENTS]);\n const annotationTargets = parseAnnotationJson<Partial<DoraTargets>>(annotations[ANNOTATION_TARGETS]);\n\n const environments: DoraEnvironment[] = annotationEnvs ?? doraApi.getEnvironments();\n const defaultEnv = environments[0];\n const defaultDays = doraApi.getDefaultDays();\n\n // Use environment name as the selection key (branch can be a multi-value pattern)\n const [selectedEnvName, setSelectedEnvName] = useState<string>(defaultEnv?.name ?? '');\n const [selectedDays, setSelectedDays] = useState<string>(String(defaultDays));\n\n const selectedEnv = environments.find(e => e.name === selectedEnvName) ?? defaultEnv;\n const days = parseInt(selectedDays, 10);\n\n const [metrics, setMetrics] = useState<DoraMetrics | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const [history, setHistory] = useState<DoraHistoryPoint[] | null>(null);\n const [expanded, setExpanded] = useState<ExpandedCardData | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n\n if (!projectSlug) {\n setError(new Error(`Entity is missing the \"${ANNOTATION_PROJECT_SLUG}\" annotation.`));\n setLoading(false);\n return () => { cancelled = true; };\n }\n if (!selectedEnv) {\n setError(new Error('No environments configured for DORA metrics.'));\n setLoading(false);\n return () => { cancelled = true; };\n }\n\n setLoading(true);\n setError(null);\n setHistory(null);\n\n doraApi\n .getMetrics(projectSlug, selectedEnv, days, annotationTargets ?? undefined)\n .then(data => {\n if (!cancelled) { setMetrics(data); setLoading(false); }\n })\n .catch((err: Error) => {\n if (!cancelled) { setError(err); setLoading(false); }\n });\n\n doraApi\n .getHistory(projectSlug, selectedEnv, days)\n .then(data => { if (!cancelled) setHistory(data); })\n .catch(() => { /* history is optional — fail silently */ });\n\n return () => { cancelled = true; };\n // selectedEnv?.name is intentional — avoids re-render loops from object identity changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [doraApi, projectSlug, selectedEnv?.name, days, annotationTargets]);\n\n if (loading) return <DoraLoadingOverlay />;\n\n if (error) {\n return (\n <InfoCard title=\"DORA Metrics\">\n <div style={{ padding: 24, color: '#f87171' }}>\n <span style={{ fontWeight: 600 }}>Error: </span>{error.message}\n </div>\n </InfoCard>\n );\n }\n\n if (!metrics) return null;\n\n return (\n <InfoCard title=\" \">\n <div style={{ padding: '4px 0', position: 'relative' }}>\n {/* Detailed overlay */}\n {expanded && (\n <DetailedOverlay data={expanded} days={days} onClose={() => setExpanded(null)} />\n )}\n\n {/* Title */}\n <div style={{ marginBottom: 16 }}>\n <div style={{ fontSize: 22, fontWeight: 700, color: textPrimary, letterSpacing: '-0.02em' }}>DORA Metrics</div>\n {selectedEnv && (\n <div style={{ fontSize: 12, color: textSecondary, marginTop: 5 }}>\n Showing metrics for{' '}\n <strong style={{ color: textPrimary }}>{selectedEnv.name}</strong>\n {' '}—{' '}\n <span style={{ fontFamily: 'monospace' }}>{selectedEnv.branch}</span>\n </div>\n )}\n </div>\n\n {/* Filters row — left aligned */}\n <div style={{ display: 'flex', alignItems: 'flex-end', gap: 16, flexWrap: 'wrap', marginBottom: 20 }}>\n {environments.length > 1 && (\n <div>\n <FilterLabel>Environment</FilterLabel>\n <Select value={selectedEnvName} onValueChange={setSelectedEnvName}>\n <SelectTrigger><SelectValue placeholder=\"Select environment\" /></SelectTrigger>\n <SelectContent>\n {environments.map(env => (\n <SelectItem key={env.name} value={env.name}>\n {env.name} ({env.branch})\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n\n <div>\n <FilterLabel>Date Range</FilterLabel>\n <Select value={selectedDays} onValueChange={setSelectedDays}>\n <SelectTrigger><SelectValue placeholder=\"Select date range\" /></SelectTrigger>\n <SelectContent>\n {DATE_RANGE_OPTIONS.map(opt => (\n <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {/* Metric cards — sparklines inline, click to expand */}\n {(() => {\n const wkLabels = history?.map(h => h.weekLabel) ?? [];\n const deployData = history?.map(h => h.deploymentCount) ?? [];\n const leadData = history?.map(h => h.leadTimeHours) ?? [];\n const cfrData = history?.map(h => h.changeFailureRate ?? 0) ?? [];\n const mttrData = history?.map(h => h.mttrHours ?? 0) ?? [];\n\n const bmMs = history?.map(h => h.bucketMidMs) ?? [];\n\n const mkExpand = (\n title: string, metric: DoraMetricValue, description: string,\n lowerIsBetter: boolean, sparklineValues: number[],\n sparklineType: 'bar' | 'line', sparklineColor: string, prs?: PrDetail[],\n prListLabel?: string, tickUnit?: 'hours' | 'count',\n ) => () => setExpanded({\n title, metric, description, lowerIsBetter,\n sparklineValues, sparklineType, sparklineColor, tickUnit,\n weekLabels: wkLabels, bucketMidMs: bmMs, prs, prListLabel,\n });\n\n // Per-bucket hotfix counts derived from hotfix PRs + bucket midpoints.\n const hotfixSparkData: number[] = bmMs.map(() => 0);\n if (bmMs.length > 0 && metrics?.numberOfHotfixes?.slowestPRs) {\n for (const pr of metrics.numberOfHotfixes.slowestPRs) {\n const t = new Date(pr.mergedAt).getTime();\n let bestIdx = 0, bestDist = Infinity;\n bmMs.forEach((mid, i) => { const d = Math.abs(t - mid); if (d < bestDist) { bestDist = d; bestIdx = i; } });\n hotfixSparkData[bestIdx]++;\n }\n }\n\n return (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>\n <MetricCard\n title=\"Deployment Frequency\"\n metric={metrics.deploymentFrequency}\n description=\"How often code is deployed to this branch\"\n sparklineData={deployData}\n sparklineType=\"bar\"\n sparklineColor=\"#FA6400\"\n weekLabels={wkLabels}\n onExpand={mkExpand('Deployment Frequency', metrics.deploymentFrequency,\n 'How often code is deployed to this branch', false, deployData, 'bar', '#FA6400',\n metrics.deploymentFrequency.slowestPRs, undefined, 'count')}\n />\n <MetricCard\n title=\"Lead Time for Changes\"\n metric={metrics.leadTime}\n description=\"Average time from PR creation to merge\"\n lowerIsBetter\n sparklineData={leadData}\n sparklineType=\"line\"\n sparklineColor=\"#818cf8\"\n weekLabels={wkLabels}\n onExpand={mkExpand('Lead Time for Changes', metrics.leadTime,\n 'Average time from PR creation to merge', true, leadData, 'line', '#818cf8', metrics.leadTime.slowestPRs, undefined, 'hours')}\n />\n {metrics.changeFailureRate !== null ? (\n <MetricCard\n title=\"Change Failure Rate\"\n metric={metrics.changeFailureRate}\n description=\"% of all merged PRs that were hotfixes\"\n lowerIsBetter\n sparklineData={cfrData}\n sparklineType=\"line\"\n sparklineColor=\"#f87171\"\n weekLabels={wkLabels}\n onExpand={mkExpand('Change Failure Rate', metrics.changeFailureRate,\n '% of all merged PRs that were hotfixes', true, cfrData, 'line', '#f87171', undefined, undefined, 'count')}\n />\n ) : (\n <NaCard title=\"Change Failure Rate\" description=\"Only tracked on production branches\" />\n )}\n {metrics.mttr !== null ? (\n <MetricCard\n title=\"Mean Time to Restore\"\n metric={metrics.mttr}\n description=\"Avg time from hotfix PR open to merge\"\n lowerIsBetter\n sparklineData={mttrData}\n sparklineType=\"line\"\n sparklineColor=\"#fbbf24\"\n weekLabels={wkLabels}\n onExpand={mkExpand('Mean Time to Restore', metrics.mttr,\n 'Avg time from hotfix PR open to merge', true, mttrData, 'line', '#fbbf24', metrics.mttr.slowestPRs, undefined, 'hours')}\n />\n ) : (\n <NaCard title=\"Mean Time to Restore\" description=\"Only tracked on production branches\" />\n )}\n {metrics.numberOfHotfixes !== null ? (\n <MetricCard\n title=\"Hotfixes to Production\"\n metric={metrics.numberOfHotfixes}\n description={`PRs labeled '${selectedEnv?.label ?? 'hotfix'}' merged to production`}\n lowerIsBetter\n expandLabel={metrics.numberOfHotfixes.value > 0 ? `View ${metrics.numberOfHotfixes.value} PR${metrics.numberOfHotfixes.value !== 1 ? 's' : ''} →` : undefined}\n onExpand={mkExpand(\n 'Hotfixes to Production',\n metrics.numberOfHotfixes,\n `PRs labeled '${selectedEnv?.label ?? 'hotfix'}' merged to production`,\n true,\n hotfixSparkData,\n 'bar',\n '#f87171',\n metrics.numberOfHotfixes.slowestPRs,\n 'All hotfixes',\n 'count',\n )}\n />\n ) : (\n <NaCard title=\"Hotfixes to Production\" description=\"Only tracked on production branches\" />\n )}\n </div>\n );\n })()}\n\n </div>\n </InfoCard>\n );\n}\n"],"names":["max"],"mappings":";;;;;;;;;;;;AAmBA,MAAM,uBAAA,GAA0B,yBAAA;AAEhC,MAAM,uBAAA,GAA0B,2BAAA;AAEhC,MAAM,kBAAA,GAAqB,sBAAA;AAG3B,SAAS,oBAAuB,GAAA,EAAwC;AACtE,EAAA,IAAI,CAAC,KAAK,OAAO,MAAA;AACjB,EAAA,IAAI;AAAE,IAAA,OAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,EAAQ,CAAA,CAAA,MAAQ;AAAE,IAAA,OAAO,MAAA;AAAA,EAAW;AACjE;AAEA,MAAM,kBAAA,GAAqB;AAAA,EACzB,EAAE,KAAA,EAAO,GAAA,EAAM,KAAA,EAAO,aAAA,EAAc;AAAA,EACpC,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,cAAA,EAAe;AAAA,EACrC,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,cAAA,EAAe;AAAA,EACrC,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,cAAA,EAAe;AAAA,EACrC,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,cAAA;AACxB,CAAA;AAIA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,EAAE,CAAA;AAC1C,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,YAAA,IAAgB,KAAK,EAAA,CAAG,CAAA;AAC7C,EAAA,MAAM,IAAI,IAAA,CAAK,KAAA,CAAO,YAAA,IAAgB,EAAA,GAAK,MAAO,EAAE,CAAA;AACpD,EAAA,MAAM,IAAI,YAAA,GAAe,EAAA;AACzB,EAAA,IAAI,CAAA,GAAI,CAAA,EAAG,OAAO,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,CAAA,CAAA,GAAM,CAAA,EAAG,CAAC,CAAA,CAAA,CAAA;AAC9C,EAAA,IAAI,CAAA,GAAI,CAAA,EAAG,OAAO,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,CAAA,CAAA,GAAM,CAAA,EAAG,CAAC,CAAA,CAAA,CAAA;AAC9C,EAAA,OAAO,GAAG,CAAC,CAAA,CAAA,CAAA;AACb;AAGA,MAAM,oBAAoB,CAAC,SAAA,EAAW,SAAA,EAAW,SAAA,EAAW,WAAW,SAAS,CAAA;AAEhF,SAAS,aAAA,CAAc,OAAe,KAAA,EAAuB;AAC3D,EAAA,IAAI,KAAA,IAAS,CAAA,EAAG,OAAO,iBAAA,CAAkB,CAAC,CAAA;AAC1C,EAAA,MAAM,IAAA,GAAA,CAAQ,iBAAA,CAAkB,MAAA,GAAS,CAAA,KAAM,KAAA,GAAQ,CAAA,CAAA;AACvD,EAAA,OAAO,iBAAA,CAAkB,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,IAAI,CAAC,CAAA;AACnD;AAKA,SAAS,eAAA,CACP,MAAA,EACA,IAAA,EACA,aAAA,EACyD;AACzD,EAAA,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,OAAO,IAAA;AAC9B,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,CAAC,CAAA;AACrC,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,CAAC,CAAA;AACrC,EAAA,MAAM,OAAO,IAAA,GAAO,IAAA;AACpB,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,GAAI,MAAO,OAAO,IAAA;AACnC,EAAA,MAAM,IAAA,GAAO,aAAA,GAAgB,IAAA,GAAO,CAAA,GAAI,IAAA,GAAO,CAAA;AAC/C,EAAA,MAAM,KAAA,GAAQ,IAAA,GAAO,CAAA,GAAI,QAAA,GAAM,QAAA;AAE/B,EAAA,MAAM,MAAM,CAAC,CAAA,KACX,IAAA,KAAS,OAAA,GAAa,eAAe,CAAC,CAAA,GACtC,IAAA,KAAS,GAAA,GAAa,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,EAAE,CAAA,CAAA,CAAA,GAChD,IAAA,KAAS,UAAA,GAAa,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,EAAE,CAAA,GAAA,CAAA,GAC1B,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,GAAI,EAAE,CAAA,GAAI,EAAE,IAAI,IAAI,CAAA,CAAA;AAE1D,EAAA,MAAM,SAAA,GAAY,OAAO,WAAA,GAAc,WAAA;AACvC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAC,CAAC,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAAA,IAClD,OAAA,EAAS,kBAAkB,GAAA,CAAI,IAAI,CAAC,CAAA,uBAAA,EAAuB,GAAA,CAAI,IAAI,CAAC;AAAA,kEAAA,CAAA;AAAA,IACpE;AAAA,GACF;AACF;AAIA,MAAM,gBAAA,GAAmB;AAAA,EACvB,2BAAA;AAAA,EACA,uBAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AAAA,EACA;AACF,CAAA;AACA,MAAM,MAAA,GAAS,CAAC,OAAA,EAAS,OAAA,EAAS,SAAS,QAAQ,CAAA;AACnD,MAAM,WAAA,GAAc,8EAAA;AACpB,MAAM,SAAA,GAAc,mDAAA;AAEpB,SAAS,kBAAA,GAAqB;AAC5B,EAAA,MAAM,QAAS,QAAA,EAAS;AACxB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AAEtC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,CAAC,CAAA;AAChD,EAAA,MAAM,CAAC,MAAA,EAAS,SAAS,CAAA,GAAa,SAAS,CAAC,CAAA;AAChD,EAAA,MAAM,CAAC,MAAA,EAAS,SAAS,CAAA,GAAa,SAAS,CAAC,CAAA;AAEhD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,EAAA,GAAK,WAAA,CAAY,MAAM,cAAA,CAAe,CAAA,CAAA,KAAA,CAAM,IAAI,CAAA,IAAK,MAAA,CAAO,MAAM,CAAA,EAAG,GAAG,CAAA;AAC9E,IAAA,OAAO,MAAM,cAAc,EAAE,CAAA;AAAA,EAC/B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,EAAA,GAAK,YAAY,MAAM;AAAE,MAAA,SAAA,CAAU,CAAA,CAAA,KAAA,CAAM,CAAA,GAAI,CAAA,IAAK,gBAAA,CAAiB,MAAM,CAAA;AAAG,MAAA,SAAA,CAAU,CAAA,CAAA,KAAK,IAAI,CAAC,CAAA;AAAA,IAAG,GAAG,IAAI,CAAA;AAChH,IAAA,OAAO,MAAM,cAAc,EAAE,CAAA;AAAA,EAC/B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,UAAA,GAAa,SACf,sHAAA,GACA,mGAAA;AAEJ,EAAA,MAAM,UAAA,GAAgB,SAAS,SAAA,GAAY,SAAA;AAC3C,EAAA,MAAM,aAAA,GAAgB,SAAS,SAAA,GAAY,SAAA;AAC3C,EAAA,MAAM,WAAA,GAAgB,SAAS,wBAAA,GAA2B,kBAAA;AAE1D,EAAA,uBACE,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBAEE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAW,MAAC,KAAA,EAAO;AAAA,IACtB,QAAA,EAAU,OAAA;AAAA,IAAS,KAAA,EAAO,CAAA;AAAA,IAAG,MAAA,EAAQ,GAAA;AAAA,IACrC,cAAA,EAAgB,YAAA;AAAA,IAChB,oBAAA,EAAsB,YAAA;AAAA,IACtB,UAAA,EAAY,SAAS,kBAAA,GAAqB;AAAA,GAC5C,EAAG,CAAA,kBAGH,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IACV,QAAA,EAAU,OAAA;AAAA,IAAS,MAAA,EAAQ,GAAA;AAAA,IAC3B,GAAA,EAAK,KAAA;AAAA,IAAO,IAAA,EAAM,KAAA;AAAA,IAClB,SAAA,EAAW,mCAAA;AAAA,IACX,OAAA,EAAS,MAAA;AAAA,IAAQ,aAAA,EAAe,QAAA;AAAA,IAAU,UAAA,EAAY,QAAA;AAAA,IACtD,OAAA,EAAS,gBAAA;AAAA,IACT,YAAA,EAAc,EAAA;AAAA,IACd,UAAA,EAAY,SAAS,mBAAA,GAAsB,wBAAA;AAAA,IAC3C,MAAA,EAAQ,aAAa,WAAW,CAAA,CAAA;AAAA,IAChC,SAAA,EAAW,SACP,8DAAA,GACA,8BAAA;AAAA,IACJ,cAAA,EAAgB,YAAA;AAAA,IAChB,oBAAA,EAAsB,YAAA;AAAA,IACtB,SAAA,EAAW,oDAAA;AAAA,IACX,QAAA,EAAU;AAAA,GACZ,EAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAW,MAAC,KAAA,EAAO;AAAA,IACtB,QAAA,EAAU,UAAA;AAAA,IAAY,KAAA,EAAO,GAAA;AAAA,IAAK,MAAA,EAAQ,GAAA;AAAA,IAC1C,IAAA,EAAM,KAAA;AAAA,IAAO,GAAA,EAAK,KAAA;AAAA,IAClB,SAAA,EAAW,mCAAA;AAAA,IACX,UAAA,EAAY,CAAA,mCAAA,EAAsC,MAAA,GAAS,sBAAA,GAAyB,sBAAsB,CAAA,qBAAA,CAAA;AAAA,IAC1G,SAAA,EAAW,qCAAA;AAAA,IACX,aAAA,EAAe,MAAA;AAAA,IAAQ,YAAA,EAAc;AAAA,GACvC,EAAG,CAAA,kBAGD,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,CAAA,EAAG,YAAA,EAAc,CAAA,EAAE,EAAA,EAC3D,CAAC,GAAA,EAAI,GAAA,EAAI,GAAA,EAAI,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,MAAA,EAAQ,CAAA,qBAC9B,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO;AAAA,IACnB,UAAA,EAAY,WAAA;AAAA,IAAa,UAAA,EAAY,GAAA;AAAA,IAAK,QAAA,EAAU,GAAA;AAAA,IACpD,KAAA,EAAO,SAAA;AAAA,IAAW,aAAA,EAAe,QAAA;AAAA,IAAU,OAAA,EAAS,cAAA;AAAA,IACpD,SAAA,EAAW,cAAA;AAAA,IAAgB,UAAA;AAAA,IAC3B,gBAAA,EAAkB,eAAA;AAAA,IAClB,SAAA,EAAW,CAAA,kDAAA,EAAqD,CAAA,GAAI,GAAG,CAAA,MAAA;AAAA,OAEtE,MACH,CACD,CACH,CAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,IAAE,UAAA,EAAY,4BAAA;AAAA,IAA8B,QAAA,EAAU,EAAA;AAAA,IAAI,UAAA,EAAY,GAAA;AAAA,IACjF,aAAA,EAAe,QAAA;AAAA,IAAU,KAAA,EAAO,aAAA;AAAA,IAAe,YAAA,EAAc,EAAA;AAAA,IAC7D,SAAA,EAAW;AAAA,GAAiC,EAAA,EAAG,SAEjD,CAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,KAAK,CAAA,EAAG,YAAA,EAAc,IAAI,SAAA,EAAW,+BAAA,MACvF,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,EAAO,CAAA,KAAM;AACxB,IAAA,MAAM,WAAa,CAAA,KAAM,WAAA;AACzB,IAAA,MAAM,aAAa,CAAA,GAAI,WAAA;AACvB,IAAA,2CACG,KAAA,EAAA,EAAI,GAAA,EAAK,OAAO,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,UAAA,EAAY,UAAU,GAAA,EAAK,CAAA,sBACpE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,OAAA,EAAS,QAAQ,aAAA,EAAe,QAAA,EAAU,UAAA,EAAY,QAAA,EAAU,KAAK,CAAA,EAAG,KAAA,EAAO,IAAG,EAAA,kBAC9F,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,UAAU,UAAA,EAAY,KAAA,EAAO,IAAI,MAAA,EAAQ,EAAA,MACpD,QAAA,oBACC,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,MAAE,QAAA,EAAU,UAAA;AAAA,MAAY,KAAA,EAAO,EAAA;AAAA,MAAI,YAAA,EAAc,KAAA;AAAA,MAC3D,MAAA,EAAQ,iCAAA;AAAA,MAAmC,SAAA,EAAW;AAAA,KAAgC,EAAG,CAAA,kBAE7F,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,MACV,KAAA,EAAO,EAAA;AAAA,MAAI,MAAA,EAAQ,EAAA;AAAA,MAAI,YAAA,EAAc,KAAA;AAAA,MACrC,UAAA,EAAa,QAAA,IAAY,UAAA,GAAc,SAAA,GAAa,SAAS,SAAA,GAAY,SAAA;AAAA,MACzE,QAAQ,CAAA,UAAA,EAAc,QAAA,IAAY,aAAc,SAAA,GAAa,MAAA,GAAS,YAAY,SAAU,CAAA,CAAA;AAAA,MAC5F,SAAA,EAAW,WAAW,8BAAA,GAAiC,MAAA;AAAA,MACvD,UAAA,EAAY;AAAA,KACd,EAAG,CACL,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,MAAE,UAAA,EAAY,4BAAA;AAAA,MAA8B,QAAA,EAAU,CAAA;AAAA,MAAG,UAAA,EAAY,GAAA;AAAA,MAChF,aAAA,EAAe,QAAA;AAAA,MAAU,aAAA,EAAe,WAAA;AAAA,MACxC,KAAA,EAAO,QAAA,GAAW,SAAA,GAAY,UAAA,GAAa,UAAA,GAAa,UAAA;AAAA,MACxD,UAAA,EAAY;AAAA,KAAa,EAAA,EACxB,KACH,CACF,CAAA,EACC,CAAA,GAAI,OAAO,MAAA,GAAS,CAAA,oBACnB,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,MAAE,KAAA,EAAO,EAAA;AAAA,MAAI,MAAA,EAAQ,CAAA;AAAA,MAAG,YAAA,EAAc,EAAA;AAAA,MAAI,QAAA,EAAU,UAAA;AAAA,MAAY,QAAA,EAAU,QAAA;AAAA,MACpF,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,MAAW,YAAA,EAAc;AAAA,KAAE,EAAA,kBAC5D,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,MACV,QAAA,EAAU,UAAA;AAAA,MAAY,KAAA,EAAO,CAAA;AAAA,MAAG,YAAA,EAAc,CAAA;AAAA,MAAG,UAAA,EAAY,SAAA;AAAA,MAC7D,eAAA,EAAiB,aAAA;AAAA,MACjB,WAAW,CAAA,OAAA,EAAU,CAAA,GAAI,cAAc,CAAA,GAAI,QAAA,GAAW,MAAM,CAAC,CAAA,CAAA,CAAA;AAAA,MAC7D,UAAA,EAAY;AAAA,KACd,EAAG,CACL,CAEJ,CAAA;AAAA,EAEJ,CAAC,CACH,CAAA,sCAGC,MAAA,EAAA,EAAK,GAAA,EAAK,QAAQ,KAAA,EAAO;AAAA,IAAE,UAAA,EAAY,SAAA;AAAA,IAAW,QAAA,EAAU,EAAA;AAAA,IAC3D,KAAA,EAAO,UAAA;AAAA,IAAY,aAAA,EAAe,QAAA;AAAA,IAAU,SAAA,EAAW;AAAA,OACtD,gBAAA,CAAiB,MAAM,CAC1B,CAEF,CAAA,sCAEC,OAAA,EAAA,IAAA,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAsBN,CACJ,CAAA;AAEJ;AAIA,SAAS,gBAAA,CAAiB;AAAA,EACxB,MAAA;AAAA,EACA,UAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA,GAAS;AACX,CAAA,EAOG;AACD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAwB,IAAI,CAAA;AAE5D,EAAA,MAAM,EAAA,GAAK,GAAA;AACX,EAAA,MAAM,WAAA,GAAc,SAAS,wBAAA,GAA2B,kBAAA;AACxD,EAAA,MAAM,SAAA,GAAc,SAAS,SAAA,GAAY,SAAA;AACzC,EAAA,MAAM,UAAA,GAAc,SAAS,SAAA,GAAY,SAAA;AACzC,EAAA,MAAM,WAAA,GAAc,SAAS,SAAA,GAAY,SAAA;AACzC,EAAA,MAAM,IAAA,GAAO,4BAAA;AAEb,EAAA,MAAM,eAAA,GAAkB,CAAC,CAAA,KAAuC;AAC9D,IAAA,MAAM,IAAA,GAAO,CAAA,CAAE,aAAA,CAAc,qBAAA,EAAsB;AACnD,IAAA,MAAM,IAAA,GAAA,CAAQ,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,QAAQ,IAAA,CAAK,KAAA;AAC5C,IAAA,WAAA,CAAY,KAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,OAAO,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,QAAQ,MAAA,CAAO,MAAA,GAAS,CAAA,CAAE,CAAC,CAAC,CAAC,CAAA;AAAA,EAC9F,CAAA;AAEA,EAAA,IAAI,SAAS,KAAA,EAAO;AAClB,IAAA,MAAMA,IAAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,QAAQ,CAAC,CAAA;AACjC,IAAA,MAAM,GAAA,GAAM,CAAA;AACZ,IAAA,MAAM,QAAQ,EAAA,GAAK,GAAA,IAAO,MAAA,CAAO,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA;AAEvD,IAAA,MAAM,SAAS,QAAA,KAAa,IAAA,GACxB,YAAY,IAAA,GAAO,GAAA,CAAA,GAAO,OAAO,CAAA,GACjC,IAAA;AACJ,IAAA,MAAM,QAAA,GAAW,WAAW,IAAA,GAAO,IAAA,CAAK,IAAI,MAAA,GAAS,EAAA,EAAI,EAAA,GAAK,EAAE,CAAA,GAAI,CAAA;AAEpE,IAAA,2CACG,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAAI,OAAA,EAAS,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,QAAI,KAAA,EAAM,MAAA;AAAA,QAAO,KAAA,EAAO,EAAE,OAAA,EAAS,OAAA,EAAS,UAAU,SAAA,EAAU;AAAA,QAC/F,WAAA,EAAa,eAAA;AAAA,QAAiB,YAAA,EAAc,MAAM,WAAA,CAAY,IAAI;AAAA,OAAA;AAAA,MACjE,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM;AACpB,QAAA,MAAM,IAAI,IAAA,CAAK,GAAA,CAAI,GAAI,CAAA,GAAIA,IAAAA,IAAQ,SAAS,CAAA,CAAE,CAAA;AAC9C,QAAA,uBACE,KAAA,CAAA,aAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YAAK,GAAA,EAAK,CAAA;AAAA,YAAG,CAAA,EAAG,KAAK,IAAA,GAAO,GAAA,CAAA;AAAA,YAAM,GAAG,MAAA,GAAS,CAAA;AAAA,YAAG,KAAA,EAAO,IAAA;AAAA,YAAM,MAAA,EAAQ,CAAA;AAAA,YAAG,EAAA,EAAI,GAAA;AAAA,YAC5E,IAAA,EAAM,KAAA;AAAA,YAAO,SAAS,CAAA,KAAM,QAAA,GAAW,IAAI,GAAA,GAAO,CAAA,GAAI,OAAO,MAAA,GAAU;AAAA;AAAA,SAAM;AAAA,MAEnF,CAAC,CAAA;AAAA,sBACD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,EAAA,EAAI,CAAA,EAAG,IAAI,MAAA,GAAS,GAAA,EAAK,EAAA,EAAI,EAAA,EAAI,IAAI,MAAA,GAAS,GAAA,EAAK,MAAA,EAAQ,WAAA,EAAa,aAAa,CAAA,EAAG,CAAA;AAAA,MAC7F,QAAA,KAAa,QAAQ,MAAA,KAAW,IAAA,8EAE7B,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,CAAA,EAAG,OAAO,EAAA,EAAI,MAAA,EAAQ,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,IAAA,EAAM,WAAW,MAAA,EAAQ,UAAA,EAAY,WAAA,EAAa,CAAA,EAAG,CAAA,kBAC5G,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,QAAA,GAAW,IAAI,CAAA,EAAG,EAAA,EAAI,YAAW,QAAA,EAAS,IAAA,EAAM,WAAA,EAAa,QAAA,EAAU,CAAA,EAAG,UAAA,EAAY,KAAK,UAAA,EAAY,IAAA,EAAA,EAC7G,UAAA,CAAW,QAAQ,CAAA,EAAE,IAAA,EAAG,YAAY,MAAA,CAAO,QAAQ,CAAC,CACvD,CACF;AAAA,KAEJ,sCACC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,gBAAgB,eAAA,EAAiB,SAAA,EAAW,GAAE,EAAA,kBAC3E,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,CAAA,EAAG,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MAAc,UAAA,CAAW,CAAC,CAAE,CAAA,kBACpF,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,CAAA,EAAG,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MAAc,UAAA,CAAW,UAAA,CAAW,SAAS,CAAC,CAAE,CAC1G,CACF,CAAA;AAAA,EAEJ;AAEA,EAAA,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,OAAO,IAAA;AAC9B,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,QAAQ,IAAI,CAAA;AACpC,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,MAAM,CAAA;AAC9B,EAAA,MAAM,KAAA,GAAQ,GAAA,GAAM,GAAA,IAAO,GAAA,IAAO,CAAA;AAClC,EAAA,MAAM,GAAA,GAAM,CAAA;AACZ,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,GAAA,CAAI,CAAC,GAAG,CAAA,KAAwB;AAAA,IAChD,KAAK,MAAA,CAAO,MAAA,GAAS,CAAA,CAAA,IAAO,EAAA,GAAK,MAAM,CAAA,CAAA,GAAK,GAAA;AAAA,IAC5C,MAAA,GAAS,MAAM,CAAA,GAAA,CAAO,CAAA,GAAI,OAAO,KAAA,IAAU,MAAA,GAAS,GAAA,GAAM,CAAA,GAAI,CAAA,CAAA,GAAK;AAAA,GACrE,CAAA;AACD,EAAA,MAAM,CAAA,GAAI,GAAA,CAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,GAAA,GAAM,GAAG,CAAA,EAAG,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,EAAI,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAClG,EAAA,MAAM,IAAA,GAAO,GAAG,CAAC,CAAA,EAAA,EAAK,IAAI,GAAA,CAAI,MAAA,GAAO,CAAC,CAAA,CAAE,CAAC,CAAC,CAAA,CAAA,EAAI,MAAM,KAAK,GAAA,CAAI,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAA,EAAA,CAAA;AAC5E,EAAA,MAAM,OAAA,GAAU,QAAA,KAAa,IAAA,GAAO,GAAA,CAAI,QAAQ,CAAA,GAAI,IAAA;AACpD,EAAA,MAAM,SAAA,GAAY,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAA,GAAI,EAAA,EAAI,EAAA,GAAK,EAAE,CAAA,GAAI,CAAA;AAEjE,EAAA,2CACG,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAAI,OAAA,EAAS,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,MAAI,KAAA,EAAM,MAAA;AAAA,MAAO,KAAA,EAAO,EAAE,OAAA,EAAS,OAAA,EAAS,UAAU,SAAA,EAAU;AAAA,MAC/F,WAAA,EAAa,eAAA;AAAA,MAAiB,YAAA,EAAc,MAAM,WAAA,CAAY,IAAI;AAAA,KAAA;AAAA,wCACjE,MAAA,EAAA,EAAK,CAAA,EAAG,MAAM,IAAA,EAAM,KAAA,EAAO,SAAS,IAAA,EAAM,CAAA;AAAA,oBAC3C,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAM,IAAA,EAAK,MAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,WAAA,EAAa,GAAA,EAAK,cAAA,EAAe,OAAA,EAAQ,aAAA,EAAc,OAAA,EAAQ,CAAA;AAAA,IACrG,GAAA,CAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA,qBAChB,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,GAAA,EAAK,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,CAAA,EAAG,CAAA,KAAM,QAAA,GAAW,GAAA,GAAM,CAAA,KAAM,GAAA,CAAI,MAAA,GAAS,CAAA,GAAI,GAAA,GAAM,CAAA,EAAG,IAAA,EAAM,KAAA,EAAO,CACtG,CAAA;AAAA,oBACD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,EAAA,EAAI,CAAA,EAAG,IAAI,MAAA,GAAS,GAAA,EAAK,EAAA,EAAI,EAAA,EAAI,IAAI,MAAA,GAAS,GAAA,EAAK,MAAA,EAAQ,WAAA,EAAa,aAAa,CAAA,EAAG,CAAA;AAAA,IAC7F,OAAA,IAAW,QAAA,KAAa,IAAA,oBACvB,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,sCACG,MAAA,EAAA,EAAK,EAAA,EAAI,OAAA,CAAQ,CAAC,GAAG,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,OAAA,CAAQ,CAAC,CAAA,EAAG,EAAA,EAAI,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,WAAA,EAAa,CAAA,EAAG,eAAA,EAAgB,OAAM,OAAA,EAAS,GAAA,EAAK,CAAA,kBAC5H,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,SAAA,EAAW,CAAA,EAAG,CAAA,EAAG,OAAO,EAAA,EAAI,MAAA,EAAQ,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,IAAA,EAAM,SAAA,EAAW,MAAA,EAAQ,YAAY,WAAA,EAAa,CAAA,EAAG,CAAA,kBAC7G,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,SAAA,GAAY,EAAA,EAAI,CAAA,EAAG,IAAI,UAAA,EAAW,QAAA,EAAS,IAAA,EAAM,WAAA,EAAa,QAAA,EAAU,CAAA,EAAG,UAAA,EAAY,GAAA,EAAK,YAAY,IAAA,EAAA,EAC9G,UAAA,CAAW,QAAQ,CAAA,EAAE,MAAG,WAAA,CAAY,MAAA,CAAO,QAAQ,CAAC,CACvD,CACF;AAAA,GAEJ,sCACC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,gBAAgB,eAAA,EAAiB,SAAA,EAAW,GAAE,EAAA,kBAC3E,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,CAAA,EAAG,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MAAc,UAAA,CAAW,CAAC,CAAE,CAAA,kBACpF,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,CAAA,EAAG,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MAAc,UAAA,CAAW,UAAA,CAAW,SAAS,CAAC,CAAE,CAC1G,CACF,CAAA;AAEJ;AAWA,SAAS,aAAA,CAAc;AAAA,EACrB,MAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,eAAA;AAAA,EACA;AACF,CAAA,EAYG;AACD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAwB,IAAI,CAAA;AAC5D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAmC,IAAI,CAAA;AACrE,EAAA,MAAM,YAAA,GAAe,OAAuB,IAAI,CAAA;AAEhD,EAAA,MAAM,CAAA,GAAI,KAAK,CAAA,GAAI,GAAA;AACnB,EAAA,MAAM,GAAA,GAAM,EAAE,GAAA,EAAK,EAAA,EAAI,OAAO,EAAA,EAAI,MAAA,EAAQ,EAAA,EAAI,IAAA,EAAM,EAAA,EAAG;AACvD,EAAA,MAAM,EAAA,GAAK,CAAA,GAAI,GAAA,CAAI,IAAA,GAAO,GAAA,CAAI,KAAA;AAC9B,EAAA,MAAM,EAAA,GAAK,CAAA,GAAI,GAAA,CAAI,GAAA,GAAM,GAAA,CAAI,MAAA;AAG7B,EAAA,MAAM,aAAA,GAAgB,GAAA,IAAO,GAAA,CAAI,MAAA,GAAS,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,GAAA,CAAI,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,aAAa,CAAC,CAAA,GAAI,CAAA;AAC3F,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,GAAG,MAAA,CAAO,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,GAAI,CAAC,CAAA,EAAG,aAAA,EAAe,IAAI,CAAA;AAC7E,EAAA,MAAM,MAAA,GAAS,aAAa,OAAA,GACxB,aAAA,CAAc,UAAU,CAAA,GACxB,QAAA,KAAa,UACb,cAAA,CAAe,UAAU,IACzB,CAAC,CAAA,EAAG,MAAM,GAAA,EAAK,IAAA,EAAM,CAAC,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,GAAI,UAAU,CAAA;AACnD,EAAA,MAAM,OAAA,GAAU,CAAA;AAChB,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,CAAC,CAAA;AACxC,EAAA,MAAM,SAAA,GAAY,UAAU,OAAA,IAAW,CAAA;AACvC,EAAA,MAAM,SAAS,CAAC,CAAA,KAAc,EAAA,GAAA,CAAO,CAAA,GAAI,WAAW,SAAA,GAAa,EAAA;AAGjE,EAAA,MAAM,WAAW,IAAA,CAAK,GAAA,KAAQ,IAAA,GAAO,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AACpD,EAAA,MAAM,OAAA,GAAW,IAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AACvC,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,KAAe,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,EAAA,EAAA,CAAM,EAAA,GAAK,QAAA,IAAY,OAAA,GAAW,EAAE,CAAC,CAAA;AAExF,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAc,WAAA,CAAY,WAAW,MAAA,CAAO,MAAA,GACtD,MAAM,WAAA,CAAY,CAAC,CAAC,CAAA,GACnB,MAAA,CAAO,SAAS,CAAA,GAAK,CAAA,IAAK,OAAO,MAAA,GAAS,CAAA,CAAA,GAAM,KAAK,EAAA,GAAK,CAAA;AAE/D,EAAA,MAAM,MAA0B,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,EAAG,MAAM,CAAC,GAAA,CAAI,IAAA,GAAO,IAAA,CAAK,CAAC,CAAA,EAAG,GAAA,CAAI,MAAM,MAAA,CAAO,CAAC,CAAC,CAAC,CAAA;AAC9F,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,GAAA,GAAM,GAAG,CAAA,EAAG,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,EAAI,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACzG,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,MAAA,GAAS,CAAA,GAC1B,CAAA,EAAG,QAAQ,CAAA,EAAA,EAAK,GAAA,CAAI,GAAA,CAAI,MAAA,GAAO,CAAC,CAAA,CAAE,CAAC,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,GAAA,GAAI,EAAE,CAAA,EAAA,EAAK,GAAA,CAAI,CAAC,CAAA,CAAE,CAAC,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,GAAA,GAAI,EAAE,CAAA,EAAA,CAAA,GAC9E,EAAA;AAEJ,EAAA,MAAM,GAAA,GAAO,CAAA;AACb,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,GAAS,CAAA,GAAA,CAAK,EAAA,GAAK,OAAO,MAAA,CAAO,MAAA,GAAS,CAAA,CAAA,IAAM,MAAA,CAAO,MAAA,GAAS,EAAA;AAEpF,EAAA,MAAM,SAAA,GAAa,SAAS,wBAAA,GAA2B,kBAAA;AACvD,EAAA,MAAM,SAAA,GAAa,SAAS,SAAA,GAAY,SAAA;AACxC,EAAA,MAAM,UAAA,GAAa,SAAS,SAAA,GAAY,SAAA;AACxC,EAAA,MAAM,IAAA,GAAa,4BAAA;AACnB,EAAA,MAAM,SAAa,CAAA,GAAA,EAAM,KAAA,CAAM,OAAA,CAAQ,GAAA,EAAI,EAAE,CAAC,CAAA,CAAA;AAG9C,EAAA,MAAM,aAAA,GAAgB,CAAC,EAAA,KAAuB;AAC5C,IAAA,IAAI,WAAA,CAAY,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AACrC,IAAA,IAAI,IAAA,GAAO,GAAG,QAAA,GAAW,QAAA;AACzB,IAAA,WAAA,CAAY,OAAA,CAAQ,CAAC,GAAA,EAAK,CAAA,KAAM;AAC9B,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAA,GAAK,GAAG,CAAA;AAC3B,MAAA,IAAI,IAAI,QAAA,EAAU;AAAE,QAAA,QAAA,GAAW,CAAA;AAAG,QAAA,IAAA,GAAO,CAAA;AAAA,MAAG;AAAA,IAC9C,CAAC,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,kBAAA,GAAqB,CAAC,CAAA,KAAuC;AACjE,IAAA,MAAM,IAAA,GAAO,CAAA,CAAE,aAAA,CAAc,qBAAA,EAAsB;AACnD,IAAA,MAAM,IAAA,GAAA,CAAQ,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,IAAA,GAAO,GAAA,CAAI,IAAA,IAAQ,IAAA,CAAK,KAAA,GAAQ,CAAA,CAAA,KAAO,IAAA,CAAK,KAAA,GAAQ,EAAA,GAAK,CAAA,CAAA;AACxF,IAAA,MAAM,MAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,MAAM,IAAA,IAAQ,MAAA,CAAO,MAAA,GAAS,CAAA,CAAE,CAAC,CAAC,CAAA;AAC5F,IAAA,WAAA,CAAY,GAAG,CAAA;AACf,IAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,OAAA,EAAS,qBAAA,EAAsB;AAC1D,IAAA,MAAM,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAM,IAAA,GAAO,CAAA;AAC3C,IAAA,MAAM,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAM,GAAA,GAAO,CAAA;AAC3C,IAAA,UAAA,CAAW,EAAE,CAAA,EAAG,CAAA,EAAG,OAAA,EAAS,WAAA,CAAY,MAAA,CAAO,GAAG,CAAC,CAAA,EAAG,SAAA,EAAW,UAAA,CAAW,GAAG,GAAG,CAAA;AAAA,EACpF,CAAA;AAEA,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,SAAI,GAAA,EAAK,YAAA,EAAc,OAAO,EAAE,QAAA,EAAU,YAAW,EAAA,kBACpD,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAAI,OAAA,EAAS,CAAA,IAAA,EAAO,CAAC,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA;AAAA,MAAI,KAAA,EAAM,MAAA;AAAA,MAAO,KAAA,EAAO,EAAE,OAAA,EAAS,OAAA,EAAQ;AAAA,MACpE,WAAA,EAAa,kBAAA;AAAA,MACb,cAAc,MAAM;AAAE,QAAA,WAAA,CAAY,IAAI,CAAA;AAAG,QAAA,UAAA,CAAW,IAAI,CAAA;AAAA,MAAG;AAAA,KAAA;AAAA,oBAC3D,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,gBAAA,EAAA,EAAe,EAAA,EAAI,QAAQ,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,IAAG,GAAA,EAAA,kBAClD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,MAAA,EAAO,IAAA,EAAK,SAAA,EAAW,KAAA,EAAO,WAAA,EAAa,IAAA,EAAM,CAAA,kBACvD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,MAAA,EAAO,MAAA,EAAO,WAAW,KAAA,EAAO,WAAA,EAAa,CAAA,EAAG,CACxD,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,cAAS,EAAA,EAAI,CAAA,OAAA,EAAU,MAAM,CAAA,CAAA,EAAA,kBAC5B,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,GAAG,GAAA,CAAI,IAAA,EAAM,CAAA,EAAG,GAAA,CAAI,GAAA,GAAM,CAAA,EAAG,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,EAAA,GAAK,EAAA,EAAI,CACjE,CACF,CAAA;AAAA,IAGC,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM;AACpB,MAAA,MAAM,CAAA,GAAI,GAAA,CAAI,GAAA,GAAM,MAAA,CAAO,CAAC,CAAA;AAC5B,MAAA,uBACE,KAAA,CAAA,aAAA,CAAC,OAAE,GAAA,EAAK,CAAA,EAAA,EACL,IAAI,CAAA,oBAAK,KAAA,CAAA,aAAA,CAAC,UAAK,EAAA,EAAI,GAAA,CAAI,MAAM,EAAA,EAAI,CAAA,EAAG,IAAI,GAAA,CAAI,IAAA,GAAK,IAAI,EAAA,EAAI,CAAA,EAAG,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAa,CAAA,EAAG,iBAAgB,KAAA,EAAM,CAAA,sCACrH,MAAA,EAAA,EAAK,CAAA,EAAG,IAAI,IAAA,GAAO,CAAA,EAAG,GAAG,CAAA,GAAI,CAAA,EAAG,YAAW,KAAA,EAAM,IAAA,EAAM,YAAY,QAAA,EAAU,EAAA,EAAI,YAAY,IAAA,EAAA,EAC3F,WAAA,CAAY,CAAC,CAChB,CACF,CAAA;AAAA,IAEJ,CAAC,CAAA;AAAA,oBACD,KAAA,CAAA,aAAA,CAAC,UAAK,EAAA,EAAI,GAAA,CAAI,MAAM,EAAA,EAAI,GAAA,CAAI,MAAI,EAAA,EAAI,EAAA,EAAI,IAAI,IAAA,GAAK,EAAA,EAAI,IAAI,GAAA,CAAI,GAAA,GAAI,IAAI,MAAA,EAAQ,SAAA,EAAW,aAAa,CAAA,EAAG,CAAA;AAAA,IAGvG,SAAS,KAAA,GACR,MAAA,CAAO,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM;AACnB,MAAA,MAAM,KAAK,IAAA,CAAK,GAAA,CAAI,IAAK,CAAA,GAAI,OAAA,IAAW,YAAa,EAAE,CAAA;AACvD,MAAA,MAAM,CAAA,GAAK,GAAA,CAAI,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,GAAA,CAAA;AAClC,MAAA,uBACE,KAAA,CAAA,aAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UAAK,GAAA,EAAK,CAAA;AAAA,UAAG,CAAA;AAAA,UAAM,CAAA,EAAG,GAAA,CAAI,GAAA,GAAI,EAAA,GAAG,EAAA;AAAA,UAAI,KAAA,EAAO,IAAA;AAAA,UAAM,MAAA,EAAQ,EAAA;AAAA,UAAI,EAAA,EAAI,CAAA;AAAA,UACjE,IAAA,EAAM,KAAA;AAAA,UAAO,OAAA,EAAS,CAAA,KAAM,QAAA,GAAW,IAAA,GAAO,GAAA,GAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,CAAC,CAAA,GAAK;AAAA;AAAA,OAAM;AAAA,IAEvG,CAAC,oBAED,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EAGG,CAAC,OAAO,QAAA,oBAAY,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,QAAA,EAAU,MAAM,CAAA,KAAA,EAAQ,MAAM,KAAK,CAAA,EAChE,CAAC,uBAAO,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,GAAG,QAAA,EAAU,IAAA,EAAK,QAAO,MAAA,EAAQ,KAAA,EAAO,aAAa,CAAA,EAAG,cAAA,EAAe,SAAQ,aAAA,EAAc,OAAA,EAAQ,GACnH,CAAC,GAAA,IAAO,IAAI,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,GAAG,CAAA,KAAM,MAAA,CAAO,CAAC,CAAA,GAAI,CAAA,oBAC1C,KAAA,CAAA,aAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QAAO,GAAA,EAAK,CAAA;AAAA,QAAG,EAAA,EAAI,CAAA;AAAA,QAAG,EAAA,EAAI,CAAA;AAAA,QACzB,CAAA,EAAG,CAAA,KAAM,QAAA,GAAW,GAAA,GAAM,CAAA;AAAA,QAC1B,IAAA,EAAM,SAAS,SAAA,GAAY,SAAA;AAAA,QAAW,MAAA,EAAQ,KAAA;AAAA,QAC9C,WAAA,EAAa,CAAA,KAAM,QAAA,GAAW,GAAA,GAAM;AAAA;AAAA,KACvC,CACH,CAAA;AAAA,IAID,GAAA,IAAO,IAAA,KAAS,MAAA,oBACf,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,QAAA,EAAU,CAAA,YAAA,EAAe,MAAM,CAAA,CAAA,CAAA,EAAA,EAC/B,GAAA,CAAI,GAAA,CAAI,CAAC,IAAI,MAAA,KAAW;AACvB,MAAA,MAAM,WAAW,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,EAAE,OAAA,EAAQ;AAC/C,MAAA,MAAM,IAAA,GAAO,cAAc,QAAQ,CAAA;AAEnC,MAAA,MAAM,WAAW,EAAA,CAAG,MAAA,GAAS,KAAK,MAAA,GAAS,EAAA,IAAM,KAAK,CAAA,IAAK,CAAA;AAC3D,MAAA,MAAM,EAAA,GAAK,GAAA,CAAI,IAAA,GAAO,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA;AAEnC,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,GAAA,GAAM,MAAA,CAAO,GAAG,aAAa,CAAA;AAC/C,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAA,GAAM,EAAA,GAAK,CAAA,EAAG,KAAK,CAAC,CAAA;AAClE,MAAA,MAAM,KAAA,GAAQ,GAAG,MAAA,KAAW,eAAA;AAG5B,MAAgB,IAAI,IAAI;AAExB,MAAA,uBACE,KAAA,CAAA,aAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UAAE,KAAK,EAAA,CAAG,MAAA;AAAA,UAAQ,KAAA,EAAO,EAAE,MAAA,EAAQ,SAAA,EAAU;AAAA,UAC5C,cAAc,CAAA,CAAA,KAAK;AACjB,YAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,YAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,OAAA,EAAS,qBAAA,EAAsB;AAC1D,YAAA,MAAM,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAM,IAAA,GAAO,CAAA;AAC3C,YAAA,MAAM,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAM,GAAA,GAAO,CAAA;AAC3C,YAAA,UAAA,CAAW;AAAA,cAAE,CAAA;AAAA,cAAG,CAAA;AAAA,cACd,SAAS,CAAA,CAAA,EAAI,EAAA,CAAG,MAAM,CAAA,EAAA,EAAK,GAAG,KAAK,CAAA,CAAA;AAAA,cACnC,SAAA,EAAW,GAAG,WAAA,CAAY,EAAA,CAAG,aAAa,CAAC,CAAA,MAAA,EAAM,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,CAAA,CAAE,kBAAA,CAAmB,SAAS,EAAE,KAAA,EAAO,SAAS,GAAA,EAAK,SAAA,EAAW,CAAC,CAAA;AAAA,aAAI,CAAA;AAC5I,YAAA,SAAA,GAAY,GAAG,MAAM,CAAA;AAAA,UACvB,CAAA;AAAA,UACA,cAAc,MAAM;AAAE,YAAA,UAAA,CAAW,IAAI,CAAA;AAAG,YAAA,SAAA,GAAY,IAAI,CAAA;AAAA,UAAG;AAAA,SAAA;AAAA,wBAE3D,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAI,EAAA,EAAI,IAAI,EAAA,EAAI,CAAA,EAAG,KAAA,GAAQ,EAAA,GAAK,GAAG,IAAA,EAAM,KAAA,EAAO,OAAA,EAAS,KAAA,GAAQ,MAAM,GAAA,EAAK,CAAA;AAAA,wBACpF,KAAA,CAAA,aAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAAO,EAAA,EAAI,EAAA;AAAA,YAAI,EAAA,EAAI,EAAA;AAAA,YAAI,CAAA,EAAG,QAAQ,GAAA,GAAM,GAAA;AAAA,YACvC,IAAA,EAAM,KAAA,GAAQ,KAAA,GAAS,MAAA,GAAS,SAAA,GAAY,MAAA;AAAA,YAC5C,MAAA,EAAQ,KAAA;AAAA,YAAO,WAAA,EAAa,QAAQ,CAAA,GAAI,GAAA;AAAA,YACxC,OAAA,EAAS,QAAQ,CAAA,GAAI;AAAA;AAAA;AAAK,OAC9B;AAAA,IAEJ,CAAC,CACH,CAAA;AAAA,IAID,aAAa,IAAA,IAAQ,IAAA,KAAS,UAAU,GAAA,CAAI,QAAQ,qBACnD,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBACE,KAAA,CAAA,aAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QAAK,EAAA,EAAI,GAAA,CAAI,QAAQ,CAAA,CAAE,CAAC,CAAA;AAAA,QAAG,IAAI,GAAA,CAAI,GAAA;AAAA,QAAK,EAAA,EAAI,GAAA,CAAI,QAAQ,CAAA,CAAE,CAAC,CAAA;AAAA,QAAG,EAAA,EAAI,IAAI,GAAA,GAAI,EAAA;AAAA,QACzE,MAAA,EAAQ,KAAA;AAAA,QAAO,WAAA,EAAa,CAAA;AAAA,QAAG,eAAA,EAAgB,KAAA;AAAA,QAAM,OAAA,EAAS;AAAA;AAAA,KAAK,kBACrE,KAAA,CAAA,aAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QAAK,IAAI,GAAA,CAAI,IAAA;AAAA,QAAM,EAAA,EAAI,GAAA,CAAI,QAAQ,CAAA,CAAE,CAAC,CAAA;AAAA,QAAG,EAAA,EAAI,IAAI,IAAA,GAAK,EAAA;AAAA,QAAI,EAAA,EAAI,GAAA,CAAI,QAAQ,CAAA,CAAE,CAAC,CAAA;AAAA,QAC5E,MAAA,EAAQ,KAAA;AAAA,QAAO,WAAA,EAAa,CAAA;AAAA,QAAG,eAAA,EAAgB,KAAA;AAAA,QAAM,OAAA,EAAS;AAAA;AAAA,KAClE,CAAA;AAAA,IAID,UAAA,CAAW,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,KAAM;AAC1B,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAA;AACzB,MAAA,MAAM,IAAA,GAAQ,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,KAAA,GAAQ,CAAC,CAAC,CAAA;AAC/C,MAAA,IAAI,CAAA,KAAM,KAAK,CAAA,KAAM,KAAA,GAAQ,KAAK,CAAA,GAAI,IAAA,KAAS,GAAG,OAAO,IAAA;AACzD,MAAA,MAAM,CAAA,GAAI,IAAA,KAAS,KAAA,GACf,GAAA,CAAI,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,GAAA,CAAA,GAAO,IAAA,GAAO,CAAA,GACrC,GAAA,CAAI,IAAA,GAAO,KAAK,CAAC,CAAA;AACrB,MAAA,2CACG,MAAA,EAAA,EAAK,GAAA,EAAK,CAAA,EAAG,CAAA,EAAM,GAAG,CAAA,GAAI,CAAA,EAAG,UAAA,EAAW,QAAA,EAAS,MAAM,UAAA,EAAY,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,QAC3F,GACH,CAAA;AAAA,IAEJ,CAAC;AAAA,GACH,EAGC,YAAY,MAAM;AACjB,IAAA,MAAM,EAAA,GAAK,GAAA;AACX,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,EAAS,WAAA,IAAe,GAAA;AACjD,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,CAAA,GAAI,EAAA,GAAK,EAAA,GAAK,GAAA;AACxC,IAAA,uBACE,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,MACV,QAAA,EAAU,UAAA;AAAA,MACV,MAAM,SAAA,GAAY,OAAA,CAAQ,IAAI,EAAA,GAAK,CAAA,GAAI,QAAQ,CAAA,GAAI,CAAA;AAAA,MACnD,GAAA,EAAK,QAAQ,CAAA,GAAI,EAAA;AAAA,MACjB,MAAA,EAAQ,IAAA;AAAA,MAAM,aAAA,EAAe,MAAA;AAAA,MAC7B,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,MACjC,MAAA,EAAQ,CAAA,UAAA,EAAa,MAAA,GAAS,SAAA,GAAY,SAAS,CAAA,CAAA;AAAA,MACnD,YAAA,EAAc,CAAA;AAAA,MAAG,OAAA,EAAS,UAAA;AAAA,MAC1B,SAAA,EAAW,SAAS,6BAAA,GAAgC,6BAAA;AAAA,MACpD,KAAA,EAAO;AAAA,KACT,EAAA,kBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,MAAE,QAAA,EAAU,EAAA;AAAA,MAAI,UAAA,EAAY,GAAA;AAAA,MAAK,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA;AAAA,MACvE,UAAA,EAAY,IAAA;AAAA,MAAM,UAAA,EAAY;AAAA,KAAI,EAAA,EACjC,QAAQ,OACX,CAAA,EACC,QAAQ,SAAA,oBACP,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,IAAI,KAAA,EAAO,MAAA,GAAS,SAAA,GAAY,SAAA,EAAW,UAAA,EAAY,IAAA,EAAM,WAAW,CAAA,EAAE,EAAA,EAC/F,OAAA,CAAQ,SACX,CAEJ,CAAA;AAAA,EAEJ,IACF,CAAA;AAEJ;AAoBA,SAAS,eAAA,CAAgB;AAAA,EACvB,IAAA;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAAA,EAIG;AACD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,iBAAiB,CAAA,CAAE,gBAAA,EAAiB;AACjE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAwB,IAAI,CAAA;AAC1E,EAAA,MAAM,SAAA,GAAY,MAAA,iBAAoC,IAAI,GAAA,EAAK,CAAA;AAE/D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAqB;AAAE,MAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,QAAA,EAAU,OAAA,EAAQ;AAAA,IAAG,CAAA;AACzE,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,KAAK,CAAA;AACxC,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,KAAK,CAAA;AAAA,EAC1D,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAGZ,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,oBAAoB,IAAA,EAAM;AAC5B,MAAA,MAAM,EAAA,GAAK,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA;AAChD,MAAA,IAAI,EAAA,KAAO,cAAA,CAAe,EAAE,UAAU,QAAA,EAAU,KAAA,EAAO,WAAW,CAAA;AAAA,IACpE;AAAA,EACF,CAAA,EAAG,CAAC,eAAe,CAAC,CAAA;AAEpB,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,WAAA,EAAa,aAAA,EAAe,eAAA,EAAiB,aAAA,EAAe,cAAA,EAAgB,QAAA,EAAU,UAAA,EAAY,WAAA,EAAa,GAAA,EAAK,aAAY,GAAI,IAAA;AAC3J,EAAA,MAAM,eAAgB,aAAA,KAAkB,KAAA;AACxC,EAAA,MAAM,YAAA,GAAgB,MAAA,CAAO,IAAA,KAAS,OAAA,GAAU,cAAA,CAAe,OAAO,KAAK,CAAA,GAAI,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AAClG,EAAA,MAAM,WAAA,GAAgB,MAAA,CAAO,IAAA,KAAS,OAAA,GAAU,KAAK,MAAA,CAAO,IAAA;AAC5D,EAAA,MAAM,SAAA,GAAgB,CAAC,CAAA,KAAc,MAAA,CAAO,SAAS,OAAA,GAAU,cAAA,CAAe,CAAC,CAAA,GAAI,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,EAAE,CAAA,CAAA;AAC7G,EAAA,MAAM,WAAA,GAAgB,SAAS,SAAA,GAAY,SAAA;AAC3C,EAAA,MAAM,UAAA,GAAgB,SAAS,SAAA,GAAY,SAAA;AAC3C,EAAA,MAAM,cAAgB,aAAA,GAClB,CAAA,UAAA,EAAa,OAAO,IAAA,KAAS,OAAA,GAAU,eAAe,MAAA,CAAO,MAAM,IAAI,MAAA,CAAO,MAAM,IAAI,WAAW,CAAA,CAAA,GACnG,kBAAa,MAAA,CAAO,MAAM,IAAI,WAAW,CAAA,CAAA;AAG7C,EAAA,MAAM,YAAY,YAAA,IAAgB,GAAA,GAAM,IAAI,MAAA,CAAO,CAAC,KAAK,EAAA,KAAO;AAC9D,IAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,CAAA,CAAE,kBAAA,CAAmB,OAAA,EAAS,EAAE,SAAS,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,GAAA,EAAK,WAAW,CAAA;AACnH,IAAA,IAAI,CAAC,GAAA,CAAI,IAAI,GAAG,GAAA,CAAI,IAAI,IAAI,EAAC;AAC7B,IAAA,GAAA,CAAI,IAAI,CAAA,CAAE,IAAA,CAAK,EAAE,CAAA;AACjB,IAAA,OAAO,GAAA;AAAA,EACT,CAAA,EAAG,EAAgC,CAAA,GAAI,EAAC;AACxC,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,CAAE,IAAA;AAAA,IACzC,CAAC,GAAG,CAAA,KAAM,IAAI,KAAK,SAAA,CAAU,CAAC,CAAA,CAAE,CAAC,CAAA,CAAE,QAAQ,EAAE,OAAA,EAAQ,GAAI,IAAI,IAAA,CAAK,SAAA,CAAU,CAAC,EAAE,CAAC,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA;AAAQ,GACtG;AAEA,EAAA,uBACE,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBAEE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAA,EAAS,SAAS,KAAA,EAAO;AAAA,IAC5B,QAAA,EAAU,OAAA;AAAA,IAAS,KAAA,EAAO,CAAA;AAAA,IAAG,MAAA,EAAQ,GAAA;AAAA,IACrC,UAAA,EAAY,SAAS,iBAAA,GAAoB,iBAAA;AAAA,IACzC,cAAA,EAAgB;AAAA,GAClB,EAAG,CAAA,kBAGH,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IACV,QAAA,EAAU,OAAA;AAAA,IACV,GAAA,EAAK,KAAA;AAAA,IAAO,IAAA,EAAM,KAAA;AAAA,IAClB,SAAA,EAAW,mCAAA;AAAA,IACX,KAAA,EAAO,kBAAA;AAAA,IACP,SAAA,EAAW,oBAAA;AAAA,IACX,MAAA,EAAQ,GAAA;AAAA,IACR,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,IACjC,MAAA,EAAQ,aAAa,WAAW,CAAA,CAAA;AAAA,IAChC,YAAA,EAAc,EAAA;AAAA,IACd,OAAA,EAAS,MAAA;AAAA,IAAQ,aAAA,EAAe,QAAA;AAAA,IAChC,QAAA,EAAU,QAAA;AAAA,IACV,SAAA,EAAW,mDAAA;AAAA,IACX,SAAA,EAAW,SAAS,6BAAA,GAAgC;AAAA,GACtD,EAAA,kBAEE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IACV,OAAA,EAAS,MAAA;AAAA,IAAQ,cAAA,EAAgB,eAAA;AAAA,IAAiB,UAAA,EAAY,YAAA;AAAA,IAC9D,OAAA,EAAS,gBAAA;AAAA,IACT,YAAA,EAAc,aAAa,WAAW,CAAA,CAAA;AAAA,IACtC,UAAA,EAAY;AAAA,GACd,EAAA,kBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,YAAY,aAAA,EAAe,WAAA,EAAa,aAAA,EAAe,QAAA,EAAU,YAAA,EAAc,CAAA,MAChI,KAAA,EAAM,QAAA,EAAI,UAAA,CAAW,CAAC,CAAA,EAAE,UAAA,EAAI,WAAW,UAAA,CAAW,MAAA,GAAS,CAAC,CAC/D,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,UAAA,EAAY,KAAK,CAAA,EAAG,QAAA,EAAU,MAAA,EAAO,EAAA,kBAC9E,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,OAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,OAAA,EAAS,UAAA,EAAY,CAAA,EAAG,aAAA,EAAe,WAAU,EAAA,EACtH,YACH,CAAA,EACC,WAAA,oBACC,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,OAAO,EAAE,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,UAAA,EAAY,UAAA,EAAY,KAAI,EAAA,EAAI,WAAY,CAAA,kBAElF,KAAA,CAAA,aAAA,CAAC,WAAA,EAAA,EAAY,MAAA,EAAQ,OAAO,MAAA,EAAQ,CACtC,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,UAAA,EAAY,SAAA,EAAW,CAAA,MACvD,WAAA,EAAY,QAAA,kBAAG,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,SAAA,EAAW,QAAA,EAAS,EAAA,EAAI,WAAY,CACrE,CACF,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MAAO,IAAA,EAAK,QAAA;AAAA,MAAS,OAAA,EAAS,OAAA;AAAA,MAAS,KAAA,EAAO;AAAA,QAC7C,GAAA,EAAK,OAAA;AAAA,QAAS,MAAA,EAAQ,SAAA;AAAA,QAAW,KAAA,EAAO,EAAA;AAAA,QAAI,MAAA,EAAQ,EAAA;AAAA,QACpD,OAAA,EAAS,MAAA;AAAA,QAAQ,UAAA,EAAY,QAAA;AAAA,QAAU,cAAA,EAAgB,QAAA;AAAA,QACvD,YAAA,EAAc,KAAA;AAAA,QAAO,UAAA,EAAY,SAAS,wBAAA,GAA2B,kBAAA;AAAA,QACrE,KAAA,EAAO,UAAA;AAAA,QAAY,QAAA,EAAU,EAAA;AAAA,QAAI,UAAA,EAAY,GAAA;AAAA,QAAK,UAAA,EAAY,WAAA;AAAA,QAAa,UAAA,EAAY;AAAA,OACzF;AAAA,MACE,cAAc,CAAA,CAAA,KAAK;AAAE,QAAC,CAAA,CAAE,aAAA,CAA8B,KAAA,CAAM,UAAA,GAAa,SAAS,wBAAA,GAA2B,iBAAA;AAAA,MAAmB,CAAA;AAAA,MAChI,cAAc,CAAA,CAAA,KAAK;AAAE,QAAC,CAAA,CAAE,aAAA,CAA8B,KAAA,CAAM,UAAA,GAAa,SAAS,wBAAA,GAA2B,kBAAA;AAAA,MAAoB;AAAA,KAAA;AAAA,IAAG;AAAA,GAGxI,mBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,IAAA,EAAM,GAAG,QAAA,EAAU,MAAA,EAAQ,SAAS,WAAA,EAAa,OAAA,EAAS,QAAQ,aAAA,EAAe,QAAA,EAAU,KAAK,EAAA,EAAG,EAAA,EAG9G,UAAA,CAAW,MAAA,IAAU,CAAA,mBACpB,KAAA,CAAA,aAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,MAAA,EAAQ,eAAA;AAAA,MAAiB,UAAA;AAAA,MACzB,WAAA;AAAA,MACA,KAAA,EAAO,cAAA;AAAA,MAAgB,IAAA,EAAM,aAAA;AAAA,MAC7B,QAAA;AAAA,MACA,WAAA,EAAa,SAAA;AAAA,MAAW,GAAA,EAAK,eAAe,MAAA,GAAY,GAAA;AAAA,MAAK,IAAA;AAAA,MAC7D,eAAA;AAAA,MAAkC,SAAA,EAAW;AAAA;AAAA,GAC/C,mBAEA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAO,UAAA,EAAY,QAAA,EAAU,EAAA,EAAI,SAAA,EAAW,QAAA,EAAU,OAAA,EAAS,UAAS,EAAA,EAAG,mDAEzF,CAAA,EAID,YAAA,IAAgB,GAAA,IAAO,GAAA,CAAI,MAAA,GAAS,CAAA,oBACnC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IAAE,QAAA,EAAU,EAAA;AAAA,IAAI,UAAA,EAAY,GAAA;AAAA,IAAK,KAAA,EAAO,UAAA;AAAA,IAAY,aAAA,EAAe,WAAA;AAAA,IAC7E,aAAA,EAAe,QAAA;AAAA,IAAU,YAAA,EAAc,EAAA;AAAA,IACvC,SAAA,EAAW,aAAa,WAAW,CAAA,CAAA;AAAA,IAAI,UAAA,EAAY;AAAA,GAAG,EAAA,EACrD,WAAA,IAAe,iBAAA,EAAkB,QAAA,EAAI,IAAI,MAAA,EAAO,KAAA,EAAI,GAAA,CAAI,MAAA,KAAW,CAAA,GAAI,GAAA,GAAM,EAChF,CAAA,sCACC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,aAAA,EAAe,QAAA,EAAU,GAAA,EAAK,IAAG,EAAA,EAC7D,WAAA,CAAY,GAAA,CAAI,CAAA,IAAA,yCACd,KAAA,EAAA,EAAI,GAAA,EAAK,IAAA,EAAA,kBAER,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,IACV,QAAA,EAAU,EAAA;AAAA,IAAI,UAAA,EAAY,GAAA;AAAA,IAAK,KAAA,EAAO,cAAA;AAAA,IACtC,YAAA,EAAc,CAAA;AAAA,IAAG,OAAA,EAAS,MAAA;AAAA,IAAQ,UAAA,EAAY,QAAA;AAAA,IAAU,GAAA,EAAK;AAAA,GAC/D,EAAA,kBACE,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAA,EAAM,IAAK,CAAA,kBACZ,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,KAAA,EAAO,UAAA,EAAY,UAAA,EAAY,KAAI,EAAA,EAAG,OAAA,EAAG,SAAA,CAAU,IAAI,CAAA,CAAE,MAAA,EAAO,KAAA,EAAI,SAAA,CAAU,IAAI,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,GAAA,GAAM,EAAG,CAC3H,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,IAAE,OAAA,EAAS,MAAA;AAAA,IAAQ,aAAA,EAAe,QAAA;AAAA,IAAU,GAAA,EAAK,CAAA;AAAA,IAAG,WAAA,EAAa,EAAA;AAAA,IAC3E,UAAA,EAAY,aAAa,cAAc,CAAA,EAAA;AAAA,GAAK,EAAA,EAC3C,SAAA,CAAU,IAAI,CAAA,CAAE,GAAA,CAAI,CAAA,EAAA,qBACnB,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,EAAA,CAAG,MAAA,EAAQ,KAAA,EAAO;AAAA,IAC1B,OAAA,EAAS,MAAA;AAAA,IAAQ,aAAA,EAAe,QAAA;AAAA,IAAU,GAAA,EAAK,CAAA;AAAA,IAC/C,OAAA,EAAS,UAAA;AAAA,IAAY,YAAA,EAAc,CAAA;AAAA,IACnC,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,IACjC,MAAA,EAAQ,aAAa,WAAW,CAAA,CAAA;AAAA,IAChC,UAAA,EAAY;AAAA,GACd,EAAA,kBAEE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,CAAA,EAAE,EAAA,kBAC1D,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,UAAA,EAAY,YAAY,CAAA,EAAG,QAAA,EAAU,EAAA,EAAG,EAAA,EAAG,KAC5F,EAAA,CAAG,MACP,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,IAAE,QAAA,EAAU,EAAA;AAAA,IAAI,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,OAAA;AAAA,IAAS,IAAA,EAAM,CAAA;AAAA,IACpE,UAAA,EAAY,QAAA;AAAA,IAAU,QAAA,EAAU,QAAA;AAAA,IAAU,YAAA,EAAc;AAAA,GAAW,EAAG,OAAO,EAAA,CAAG,KAAA,EAAA,EAC/E,GAAG,KACN,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,KAAA,EAAO,UAAA,EAAY,UAAA,EAAY,CAAA,EAAE,EAAA,EAC3D,eAAe,EAAA,CAAG,aAAa,CAAA,EAAE,OACpC,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MAAE,MAAM,EAAA,CAAG,GAAA;AAAA,MAAK,MAAA,EAAO,QAAA;AAAA,MAAS,GAAA,EAAI,qBAAA;AAAA,MACnC,KAAA,EAAO,EAAE,KAAA,EAAO,UAAA,EAAY,YAAY,CAAA;AAAE,KAAA;AAAA,oBAC1C,KAAA,CAAA,aAAA,CAAC,YAAA,EAAA,EAAa,IAAA,EAAM,EAAA,EAAI;AAAA,GAE5B,CAAA,EAEC,EAAA,CAAG,MAAA,oBACF,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,YAAY,QAAA,EAAU,GAAA,EAAK,CAAA,EAAE,EAAA,EACzD,GAAG,YAAA,mBACF,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAAI,KAAK,EAAA,CAAG,YAAA;AAAA,MAAc,KAAK,EAAA,CAAG,MAAA;AAAA,MACjC,KAAA,EAAO;AAAA,QAAE,KAAA,EAAO,EAAA;AAAA,QAAI,MAAA,EAAQ,EAAA;AAAA,QAAI,YAAA,EAAc,KAAA;AAAA,QAAO,UAAA,EAAY,CAAA;AAAA,QAC/D,MAAA,EAAQ,CAAA,UAAA,EAAa,MAAA,GAAS,SAAA,GAAY,SAAS,CAAA;AAAA;AAAG;AAAA,GAAG,mBAE7D,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO;AAAA,IAAE,KAAA,EAAO,EAAA;AAAA,IAAI,MAAA,EAAQ,EAAA;AAAA,IAAI,YAAA,EAAc,KAAA;AAAA,IAAO,UAAA,EAAY,CAAA;AAAA,IACpE,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,IACjC,OAAA,EAAS,MAAA;AAAA,IAAQ,UAAA,EAAY,QAAA;AAAA,IAAU,cAAA,EAAgB,QAAA;AAAA,IACvD,QAAA,EAAU,CAAA;AAAA,IAAG,UAAA,EAAY,GAAA;AAAA,IAAK,KAAA,EAAO;AAAA,OACpC,EAAA,CAAG,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAChB,CAAA,kBAEF,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MAAE,IAAA,EAAM,CAAA,EAAG,aAAa,CAAA,CAAA,EAAI,GAAG,MAAM,CAAA,CAAA;AAAA,MAAI,MAAA,EAAO,QAAA;AAAA,MAAS,GAAA,EAAI,qBAAA;AAAA,MAC5D,KAAA,EAAO;AAAA,QAAE,QAAA,EAAU,EAAA;AAAA,QAAI,UAAA,EAAY,GAAA;AAAA,QAAK,KAAA,EAAO,UAAA;AAAA,QAC7C,cAAA,EAAgB,MAAA;AAAA,QAAQ,aAAA,EAAe;AAAA,OAAS;AAAA,MAClD,cAAc,CAAA,CAAA,KAAK;AAAE,QAAC,CAAA,CAAE,aAAA,CAA8B,KAAA,CAAM,KAAA,GAAQ,SAAA;AAAA,MAAW,CAAA;AAAA,MAC/E,cAAc,CAAA,CAAA,KAAK;AAAE,QAAC,CAAA,CAAE,aAAA,CAA8B,KAAA,CAAM,KAAA,GAAQ,UAAA;AAAA,MAAY;AAAA,KAAA;AAAA,IAAG,GAAA;AAAA,IACjF,EAAA,CAAG;AAAA,GAET,CAEJ,CACD,CACH,CACF,CACD,CACH,CACF,CAAA,EAID,CAAC,YAAA,IAAgB,GAAA,IAAO,IAAI,MAAA,GAAS,CAAA,wCACnC,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO;AAAA,IAAE,QAAA,EAAU,EAAA;AAAA,IAAI,UAAA,EAAY,GAAA;AAAA,IAAK,KAAA,EAAO,UAAA;AAAA,IAAY,aAAA,EAAe,WAAA;AAAA,IAC7E,aAAA,EAAe,QAAA;AAAA,IAAU,YAAA,EAAc,EAAA;AAAA,IACvC,SAAA,EAAW,aAAa,WAAW,CAAA,CAAA;AAAA,IAAI,UAAA,EAAY;AAAA,GAAG,EAAA,EACrD,KAAA,CAAM,WAAA,EAAY,CAAE,QAAA,CAAS,SAAS,CAAA,GAAI,oBAAA,GAAuB,aAAA,EACjE,GAAA,EAAI,wCACP,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,aAAA,EAAe,QAAA,EAAU,GAAA,EAAK,CAAA,EAAE,EAAA,EAC5D,GAAA,CAAI,GAAA,CAAI,CAAC,EAAA,EAAI,CAAA,KAAM;AAClB,IAAA,MAAM,GAAA,GAAS,aAAA,CAAc,CAAA,EAAG,GAAA,CAAI,MAAM,CAAA;AAC1C,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,CAAC,CAAA,CAAE,aAAA;AACtB,IAAA,MAAM,SAAS,MAAA,GAAS,CAAA,GAAK,EAAA,CAAG,aAAA,GAAgB,SAAU,GAAA,GAAM,CAAA;AAChE,IAAA,MAAM,KAAA,GAAS,GAAG,MAAA,KAAW,eAAA;AAC7B,IAAA,uBACE,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAAI,KAAK,EAAA,CAAG,MAAA;AAAA,QACX,GAAA,EAAK,CAAC,EAAA,KAAO;AACX,UAAA,IAAI,IAAI,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,EAAA,CAAG,QAAQ,EAAE,CAAA;AAAA,eACtC,SAAA,CAAU,OAAA,CAAQ,MAAA,CAAO,EAAA,CAAG,MAAM,CAAA;AAAA,QACzC,CAAA;AAAA,QACA,YAAA,EAAc,MAAM,kBAAA,CAAmB,EAAA,CAAG,MAAM,CAAA;AAAA,QAChD,YAAA,EAAc,MAAM,kBAAA,CAAmB,IAAI,CAAA;AAAA,QAC3C,KAAA,EAAO;AAAA,UACL,OAAA,EAAS,UAAA;AAAA,UAAY,YAAA,EAAc,CAAA;AAAA,UACnC,YAAY,KAAA,GACP,MAAA,GAAS,SAAA,GAAY,SAAA,GACrB,SAAS,SAAA,GAAY,SAAA;AAAA,UAC1B,QAAQ,CAAA,UAAA,EAAa,KAAA,GAAQ,CAAA,EAAG,cAAc,OAAO,WAAW,CAAA,CAAA;AAAA,UAChE,OAAA,EAAS,MAAA;AAAA,UAAQ,aAAA,EAAe,QAAA;AAAA,UAAU,GAAA,EAAK,CAAA;AAAA,UAC/C,UAAA,EAAY,sCAAA;AAAA,UACZ,MAAA,EAAQ;AAAA;AACV,OAAA;AAAA,sBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,CAAA,EAAG,QAAA,EAAU,CAAA,EAAE,EAAA,sCACtE,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,KAAK,UAAA,EAAY,CAAA,EAAE,EAAA,EAAG,GAAA,EAAE,EAAA,CAAG,MAAO,CAAA,kBACvF,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,QAAE,QAAA,EAAU,EAAA;AAAA,QAAI,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,OAAA;AAAA,QAAS,IAAA,EAAM,CAAA;AAAA,QACpE,UAAA,EAAY,QAAA;AAAA,QAAU,QAAA,EAAU,QAAA;AAAA,QAAU,YAAA,EAAc;AAAA,OAAW,EAAG,KAAA,EAAO,EAAA,CAAG,KAAA,EAAA,EAC/E,GAAG,KACN,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,OAAO,UAAA,EAAY,UAAA,EAAY,CAAA,EAAE,EAAA,EAC3D,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,EAAE,kBAAA,CAAmB,OAAA,EAAS,EAAE,KAAA,EAAO,OAAA,EAAS,GAAA,EAAK,SAAA,EAAW,CACvF,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UAAE,MAAM,EAAA,CAAG,GAAA;AAAA,UAAK,MAAA,EAAO,QAAA;AAAA,UAAS,GAAA,EAAI,qBAAA;AAAA,UACnC,OAAO,EAAE,KAAA,EAAO,YAAY,UAAA,EAAY,CAAA,EAAG,YAAY,aAAA;AAAc,SAAA;AAAA,wBACrE,KAAA,CAAA,aAAA,CAAC,YAAA,EAAA,EAAa,IAAA,EAAM,EAAA,EAAI;AAAA,OAE5B,CAAA;AAAA,0CACC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,QAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,CAAA,sBACxD,KAAA,CAAA,aAAA,CAAC,WAAA,EAAA,EAAY,UAAU,MAAA,EAAQ,KAAA,EAAO,KAAK,CAAA,kBAC3C,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,YAAY,GAAA,EAAK,KAAA,EAAO,KAAK,QAAA,EAAU,EAAA,EAAI,SAAA,EAAW,OAAA,EAAS,YAAY,CAAA,EAAE,EAAA,EACvG,eAAe,EAAA,CAAG,aAAa,CAClC,CACF;AAAA,KACF;AAAA,EAEJ,CAAC,CACH,CACF,CAEJ,CACF,CAAA,sCACC,OAAA,EAAA,IAAA,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAKN,CACJ,CAAA;AAEJ;AAIA,SAAS,WAAA,CAAY,EAAE,QAAA,EAAU,KAAA,EAAM,EAAwC;AAC7E,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,CAAC,CAAA;AACpC,EAAA,MAAM,GAAA,GAAM,OAAuB,IAAI,CAAA;AACvC,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AAEtC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,QAAA,CAAS,QAAQ,GAAG,EAAE,CAAA;AACrD,IAAA,OAAO,MAAM,aAAa,KAAK,CAAA;AAAA,EACjC,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,uBACE,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACL,UAAA,EAAY,SAAS,SAAA,GAAY,SAAA;AAAA,QACjC,YAAA,EAAc,CAAA;AAAA,QACd,MAAA,EAAQ,CAAA;AAAA,QACR,QAAA,EAAU,QAAA;AAAA,QACV,IAAA,EAAM,CAAA;AAAA,QACN,QAAA,EAAU;AAAA;AACZ,KAAA;AAAA,oBAEA,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO;AAAA,UACL,MAAA,EAAQ,MAAA;AAAA,UACR,KAAA,EAAO,GAAG,KAAK,CAAA,CAAA,CAAA;AAAA,UACf,UAAA,EAAY,KAAA;AAAA,UACZ,YAAA,EAAc,CAAA;AAAA,UACd,UAAA,EAAY,0CAAA;AAAA,UACZ,SAAA,EAAW,WAAW,KAAK,CAAA,EAAA;AAAA;AAC7B;AAAA;AACF,GACF;AAEJ;AAIA,SAAS,UAAA,CAAW;AAAA,EAClB,KAAA;AAAA,EACA,MAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA,GAAgB,KAAA;AAAA,EAChB,aAAA;AAAA,EACA,aAAA,GAAgB,MAAA;AAAA,EAChB,cAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAYG;AACD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAE5C,EAAA,MAAM,YAAA,GAAe,OAAO,IAAA,KAAS,OAAA,GAAU,eAAe,MAAA,CAAO,KAAK,IAAI,MAAA,CAAO,KAAA;AACrF,EAAA,MAAM,WAAA,GAAe,MAAA,CAAO,IAAA,KAAS,OAAA,GAAU,KAAK,MAAA,CAAO,IAAA;AAC3D,EAAA,MAAM,WAAA,GAAe,OAAO,IAAA,KAAS,OAAA,GAAU,eAAe,MAAA,CAAO,MAAM,IAAI,MAAA,CAAO,MAAA;AACtF,EAAA,MAAM,WAAA,GAAe,aAAA,GACjB,CAAA,UAAA,EAAa,WAAW,IAAI,WAAW,CAAA,CAAA,CAAG,IAAA,EAAK,GAC/C,CAAA,eAAA,EAAa,WAAW,CAAA,CAAA,EAAI,WAAW,GAAG,IAAA,EAAK;AAEnD,EAAA,MAAM,eAAe,aAAA,IAAiB,aAAA,CAAc,UAAU,CAAA,IAAK,UAAA,IAAc,WAAW,MAAA,IAAU,CAAA;AACtG,EAAA,MAAM,QAAQ,YAAA,GAAe,eAAA,CAAgB,eAAgB,MAAA,CAAO,IAAA,EAAM,aAAa,CAAA,GAAI,IAAA;AAC3F,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAc,MAAA,CAAO,SAAS,OAAA,GAAU,cAAA,CAAe,CAAC,CAAA,GAAI,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,EAAE,CAAA,CAAA;AAEzG,EAAA,uBACE,KAAA,CAAA,aAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAA,GAAW,YAAY,MAAA,EAAU;AAAA,MAClD,OAAA,EAAS,QAAA;AAAA,MACT,YAAA,EAAc,MAAM,UAAA,CAAW,IAAI,CAAA;AAAA,MACnC,YAAA,EAAc,MAAM,UAAA,CAAW,KAAK;AAAA,KAAA;AAAA,oBAEpC,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,cAAA,EAAgB,iBAAiB,UAAA,EAAY,YAAA,EAAc,KAAK,CAAA,EAAE,EAAA,sCAC9F,SAAA,EAAA,IAAA,EAAW,KAAM,GACjB,QAAA,oBACC,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,MACX,QAAA,EAAU,CAAA;AAAA,MAAG,UAAA,EAAY,GAAA;AAAA,MAAK,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA;AAAA,MAC1D,OAAA,EAAS,UAAU,CAAA,GAAI,CAAA;AAAA,MAAG,UAAA,EAAY,eAAA;AAAA,MACtC,aAAA,EAAe,QAAA;AAAA,MAAU,aAAA,EAAe,WAAA;AAAA,MAAa,UAAA,EAAY;AAAA,KACnE,EAAA,EAAG,eAEH,CAEJ,CAAA;AAAA,wCAEC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,eAAe,QAAA,EAAU,UAAA,EAAY,YAAY,GAAA,EAAK,CAAA,MAClF,KAAA,oBACC,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,KAAA,CAAM,SAAS,KAAA,EAAO;AAAA,MACjC,QAAA,EAAU,EAAA;AAAA,MAAI,UAAA,EAAY,GAAA;AAAA,MAAK,MAAA,EAAQ,MAAA;AAAA,MACvC,KAAA,EAAO,KAAA,CAAM,IAAA,GAAO,SAAA,GAAY,SAAA;AAAA,MAChC,YAAY,KAAA,CAAM,IAAA,GACb,SAAS,uBAAA,GAA0B,uBAAA,GACnC,SAAS,wBAAA,GAA2B,wBAAA;AAAA,MACzC,OAAA,EAAS,SAAA;AAAA,MAAW,YAAA,EAAc,CAAA;AAAA,MAAG,UAAA,EAAY;AAAA,SAEhD,KAAA,CAAM,IACT,CAAA,kBAEF,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,MACX,QAAA,EAAU,EAAA;AAAA,MAAI,UAAA,EAAY,GAAA;AAAA,MAC1B,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA;AAAA,MAC5B,UAAA,EAAY;AAAA,KACd,EAAA,EACG,aAAA,GAAgB,uBAAA,GAAqB,wBACxC,CACF,CAAA;AAAA,wCAEC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,YAAY,UAAA,EAAY,GAAA,EAAK,GAAE,EAAA,kBAC5D,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,UAAA,EAAY,KAAK,KAAA,EAAO,MAAA,GAAS,YAAY,SAAA,EAAW,UAAA,EAAY,GAAE,EAAA,EAChG,YACH,GACC,WAAA,oBACC,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,EAAW,YAAY,GAAA,EAAI,EAAA,EACjF,WACH,CAEJ,CAAA;AAAA,oBACA,KAAA,CAAA,aAAA,CAAC,WAAA,EAAA,EAAY,MAAA,EAAQ,MAAA,CAAO,MAAA,EAAQ,CAAA;AAAA,IACnC,gCACC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,SAAA,EAAW,GAAE,EAAA,kBACzB,KAAA,CAAA,aAAA;AAAA,MAAC,gBAAA;AAAA,MAAA;AAAA,QACC,MAAA,EAAQ,aAAA;AAAA,QACR,UAAA;AAAA,QACA,OAAO,cAAA,IAAkB,SAAA;AAAA,QACzB,IAAA,EAAM,aAAA;AAAA,QACN,WAAA,EAAa;AAAA;AAAA,KAEjB,CAAA;AAAA,oBAEF,KAAA,CAAA,aAAA,CAAC,uBAAiB,WAAY,CAAA;AAAA,wCAC7B,UAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO,EAAE,YAAY,GAAA,EAAK,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA,MACzD,WACH,CAAA,EACC,eAAe,QAAA,oBACd,KAAA,CAAA,aAAA,CAAC,UAAK,KAAA,EAAO;AAAA,MACX,QAAA,EAAU,EAAA;AAAA,MAAI,UAAA,EAAY,GAAA;AAAA,MAC1B,KAAA,EAAO,SAAA;AAAA,MACP,aAAA,EAAe,QAAA;AAAA,MACf,UAAA,EAAY;AAAA,KACd,EAAA,EACG,WACH,CAEJ;AAAA,GACF;AAEJ;AAEA,SAAS,MAAA,CAAO,EAAE,KAAA,EAAO,WAAA,EAAY,EAA2C;AAC9E,EAAA,2CACG,IAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,EAAW,KAAM,mBAClB,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,UAAA,EAAY,YAAY,GAAA,EAAK,CAAA,sBAC1D,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,WAAW,UAAA,EAAY,CAAA,EAAE,EAAA,EAAG,KAAG,CACtF,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,mBAAgB,KAAA,EAAK,IAAA,EAAA,EAAE,WAAY,CACtC,CAAA;AAEJ;AAIA,SAAS,WAAA,CAAY,EAAE,QAAA,EAAS,EAAkC;AAChE,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,UAAA,EAAY,KAAK,KAAA,EAAO,KAAA,CAAM,QAAQ,IAAA,CAAK,OAAA,EAAS,cAAc,CAAA,EAAG,aAAA,EAAe,aAAa,aAAA,EAAe,QAAA,MACzI,QACH,CAAA;AAEJ;AAIO,SAAS,kBAAA,GAAqB;AACnC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,OAAA,GAAU,OAAO,iBAAiB,CAAA;AACxC,EAAA,MAAM,WAAW,QAAA,EAAS;AAC1B,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,OAAA,CAAQ,IAAA,CAAK,OAAA;AAC1C,EAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,OAAA,CAAQ,IAAA,CAAK,SAAA;AAE5C,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,GAAc,uBAAuB,CAAA,IAAK,EAAA;AAC9E,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AAGpD,EAAA,MAAM,cAAA,GAAiB,mBAAA,CAAuC,WAAA,CAAY,uBAAuB,CAAC,CAAA;AAClG,EAAA,MAAM,iBAAA,GAAoB,mBAAA,CAA0C,WAAA,CAAY,kBAAkB,CAAC,CAAA;AAEnG,EAAA,MAAM,YAAA,GAAkC,cAAA,IAAkB,OAAA,CAAQ,eAAA,EAAgB;AAClF,EAAA,MAAM,UAAA,GAAa,aAAa,CAAC,CAAA;AACjC,EAAA,MAAM,WAAA,GAAc,QAAQ,cAAA,EAAe;AAG3C,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,IAAI,QAAA,CAAiB,UAAA,EAAY,QAAQ,EAAE,CAAA;AACrF,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,IAAI,QAAA,CAAiB,MAAA,CAAO,WAAW,CAAC,CAAA;AAE5E,EAAA,MAAM,cAAc,YAAA,CAAa,IAAA,CAAK,OAAK,CAAA,CAAE,IAAA,KAAS,eAAe,CAAA,IAAK,UAAA;AAC1E,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,YAAA,EAAc,EAAE,CAAA;AAEtC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAA6B,IAAI,CAAA;AAC/D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAoC,IAAI,CAAA;AACtE,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAkC,IAAI,CAAA;AAEtE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,QAAA,CAAS,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,uBAAuB,eAAe,CAAC,CAAA;AACpF,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,OAAO,MAAM;AAAE,QAAA,SAAA,GAAY,IAAA;AAAA,MAAM,CAAA;AAAA,IACnC;AACA,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,QAAA,CAAS,IAAI,KAAA,CAAM,8CAA8C,CAAC,CAAA;AAClE,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,OAAO,MAAM;AAAE,QAAA,SAAA,GAAY,IAAA;AAAA,MAAM,CAAA;AAAA,IACnC;AAEA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,UAAA,CAAW,IAAI,CAAA;AAEf,IAAA,OAAA,CACG,UAAA,CAAW,aAAa,WAAA,EAAa,IAAA,EAAM,qBAAqB,MAAS,CAAA,CACzE,KAAK,CAAA,IAAA,KAAQ;AACZ,MAAA,IAAI,CAAC,SAAA,EAAW;AAAE,QAAA,UAAA,CAAW,IAAI,CAAA;AAAG,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAAG;AAAA,IACzD,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAe;AACrB,MAAA,IAAI,CAAC,SAAA,EAAW;AAAE,QAAA,QAAA,CAAS,GAAG,CAAA;AAAG,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAAG;AAAA,IACtD,CAAC,CAAA;AAEH,IAAA,OAAA,CACG,WAAW,WAAA,EAAa,WAAA,EAAa,IAAI,CAAA,CACzC,KAAK,CAAA,IAAA,KAAQ;AAAE,MAAA,IAAI,CAAC,SAAA,EAAW,UAAA,CAAW,IAAI,CAAA;AAAA,IAAG,CAAC,CAAA,CAClD,KAAA,CAAM,MAAM;AAAA,IAA4C,CAAC,CAAA;AAE5D,IAAA,OAAO,MAAM;AAAE,MAAA,SAAA,GAAY,IAAA;AAAA,IAAM,CAAA;AAAA,EAGnC,CAAA,EAAG,CAAC,OAAA,EAAS,WAAA,EAAa,aAAa,IAAA,EAAM,IAAA,EAAM,iBAAiB,CAAC,CAAA;AAErE,EAAA,IAAI,OAAA,EAAS,uBAAO,KAAA,CAAA,aAAA,CAAC,kBAAA,EAAA,IAAmB,CAAA;AAExC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,uBACE,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,cAAA,EAAA,kBACd,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,OAAA,EAAS,EAAA,EAAI,KAAA,EAAO,SAAA,sBAChC,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,UAAA,EAAY,GAAA,MAAO,SAAO,CAAA,EAAQ,KAAA,CAAM,OACzD,CACF,CAAA;AAAA,EAEJ;AAEA,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,YAAS,KAAA,EAAM,GAAA,EAAA,sCACb,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,OAAA,EAAS,UAAU,UAAA,EAAW,EAAA,EAElD,4BACC,KAAA,CAAA,aAAA,CAAC,eAAA,EAAA,EAAgB,MAAM,QAAA,EAAU,IAAA,EAAY,SAAS,MAAM,WAAA,CAAY,IAAI,CAAA,EAAG,CAAA,sCAIhF,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,YAAA,EAAc,EAAA,sBAC1B,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,QAAA,EAAU,IAAI,UAAA,EAAY,GAAA,EAAK,OAAO,WAAA,EAAa,aAAA,EAAe,WAAU,EAAA,EAAG,cAAY,GACxG,WAAA,oBACC,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAE,UAAU,EAAA,EAAI,KAAA,EAAO,eAAe,SAAA,EAAW,CAAA,MAAK,qBAAA,EAC5C,GAAA,sCACnB,QAAA,EAAA,EAAO,KAAA,EAAO,EAAE,KAAA,EAAO,WAAA,MAAgB,WAAA,CAAY,IAAK,GACxD,GAAA,EAAI,QAAA,EAAE,qBACP,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,OAAO,EAAE,UAAA,EAAY,aAAY,EAAA,EAAI,WAAA,CAAY,MAAO,CAChE,CAEJ,mBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,OAAA,EAAS,QAAQ,UAAA,EAAY,UAAA,EAAY,KAAK,EAAA,EAAI,QAAA,EAAU,QAAQ,YAAA,EAAc,EAAA,MAC7F,YAAA,CAAa,MAAA,GAAS,qBACrB,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,sCACE,WAAA,EAAA,IAAA,EAAY,aAAW,mBACxB,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,OAAO,eAAA,EAAiB,aAAA,EAAe,sCAC7C,KAAA,CAAA,aAAA,CAAC,aAAA,EAAA,IAAA,sCAAe,WAAA,EAAA,EAAY,WAAA,EAAY,sBAAqB,CAAE,CAAA,sCAC9D,aAAA,EAAA,IAAA,EACE,YAAA,CAAa,IAAI,CAAA,GAAA,qBAChB,KAAA,CAAA,aAAA,CAAC,cAAW,GAAA,EAAK,GAAA,CAAI,MAAM,KAAA,EAAO,GAAA,CAAI,QACnC,GAAA,CAAI,IAAA,EAAK,MAAG,GAAA,CAAI,MAAA,EAAO,GAC1B,CACD,CACH,CACF,CACF,CAAA,sCAGD,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,mBAAY,YAAU,CAAA,sCACtB,MAAA,EAAA,EAAO,KAAA,EAAO,cAAc,aAAA,EAAe,eAAA,EAAA,sCACzC,aAAA,EAAA,IAAA,kBAAc,KAAA,CAAA,aAAA,CAAC,eAAY,WAAA,EAAY,mBAAA,EAAoB,CAAE,CAAA,kBAC9D,KAAA,CAAA,aAAA,CAAC,qBACE,kBAAA,CAAmB,GAAA,CAAI,yBACtB,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,KAAK,GAAA,CAAI,KAAA,EAAO,OAAO,GAAA,CAAI,KAAA,EAAA,EAAQ,IAAI,KAAM,CAC1D,CACH,CACF,CACF,CACF,CAAA,EAAA,CAGE,MAAM;AACN,IAAA,MAAM,WAAa,OAAA,EAAS,GAAA,CAAI,OAAK,CAAA,CAAE,SAAS,KAAK,EAAC;AACtD,IAAA,MAAM,aAAa,OAAA,EAAS,GAAA,CAAI,OAAK,CAAA,CAAE,eAAe,KAAK,EAAC;AAC5D,IAAA,MAAM,WAAa,OAAA,EAAS,GAAA,CAAI,OAAK,CAAA,CAAE,aAAa,KAAK,EAAC;AAC1D,IAAA,MAAM,OAAA,GAAa,SAAS,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,iBAAA,IAAqB,CAAC,KAAK,EAAC;AACnE,IAAA,MAAM,QAAA,GAAa,SAAS,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,SAAA,IAAa,CAAC,KAAK,EAAC;AAE3D,IAAA,MAAM,OAAO,OAAA,EAAS,GAAA,CAAI,OAAK,CAAA,CAAE,WAAW,KAAK,EAAC;AAElD,IAAA,MAAM,QAAA,GAAW,CACf,KAAA,EAAe,MAAA,EAAyB,WAAA,EACxC,aAAA,EAAwB,eAAA,EACxB,aAAA,EAA+B,cAAA,EAAwB,GAAA,EACvD,WAAA,EAAsB,QAAA,KACnB,MAAM,WAAA,CAAY;AAAA,MACrB,KAAA;AAAA,MAAO,MAAA;AAAA,MAAQ,WAAA;AAAA,MAAa,aAAA;AAAA,MAC5B,eAAA;AAAA,MAAiB,aAAA;AAAA,MAAe,cAAA;AAAA,MAAgB,QAAA;AAAA,MAChD,UAAA,EAAY,QAAA;AAAA,MAAU,WAAA,EAAa,IAAA;AAAA,MAAM,GAAA;AAAA,MAAK;AAAA,KAC/C,CAAA;AAGD,IAAA,MAAM,eAAA,GAA4B,IAAA,CAAK,GAAA,CAAI,MAAM,CAAC,CAAA;AAClD,IAAA,IAAI,IAAA,CAAK,MAAA,GAAS,CAAA,IAAK,OAAA,EAAS,kBAAkB,UAAA,EAAY;AAC5D,MAAA,KAAA,MAAW,EAAA,IAAM,OAAA,CAAQ,gBAAA,CAAiB,UAAA,EAAY;AACpD,QAAA,MAAM,IAAI,IAAI,IAAA,CAAK,EAAA,CAAG,QAAQ,EAAE,OAAA,EAAQ;AACxC,QAAA,IAAI,OAAA,GAAU,GAAG,QAAA,GAAW,QAAA;AAC5B,QAAA,IAAA,CAAK,OAAA,CAAQ,CAAC,GAAA,EAAK,CAAA,KAAM;AAAE,UAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,GAAI,GAAG,CAAA;AAAG,UAAA,IAAI,IAAI,QAAA,EAAU;AAAE,YAAA,QAAA,GAAW,CAAA;AAAG,YAAA,OAAA,GAAU,CAAA;AAAA,UAAG;AAAA,QAAE,CAAC,CAAA;AAC1G,QAAA,eAAA,CAAgB,OAAO,CAAA,EAAA;AAAA,MACzB;AAAA,IACF;AAEA,IAAA,uBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,QAAQ,QAAA,EAAU,MAAA,EAAQ,GAAA,EAAK,EAAA,EAAG,EAAA,kBACvD,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,sBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,mBAAA;AAAA,QAChB,WAAA,EAAY,2CAAA;AAAA,QACZ,aAAA,EAAe,UAAA;AAAA,QACf,aAAA,EAAc,KAAA;AAAA,QACd,cAAA,EAAe,SAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,QAAA,EAAU,QAAA;AAAA,UAAS,sBAAA;AAAA,UAAwB,OAAA,CAAQ,mBAAA;AAAA,UACjD,2CAAA;AAAA,UAA6C,KAAA;AAAA,UAAO,UAAA;AAAA,UAAY,KAAA;AAAA,UAAO,SAAA;AAAA,UACvE,QAAQ,mBAAA,CAAoB,UAAA;AAAA,UAAY,MAAA;AAAA,UAAW;AAAA;AAAO;AAAA,KAC9D,kBACA,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,uBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,QAAA;AAAA,QAChB,WAAA,EAAY,wCAAA;AAAA,QACZ,aAAA,EAAa,IAAA;AAAA,QACb,aAAA,EAAe,QAAA;AAAA,QACf,aAAA,EAAc,MAAA;AAAA,QACd,cAAA,EAAe,SAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,QAAA,EAAU,QAAA;AAAA,UAAS,uBAAA;AAAA,UAAyB,OAAA,CAAQ,QAAA;AAAA,UAClD,wCAAA;AAAA,UAA0C,IAAA;AAAA,UAAM,QAAA;AAAA,UAAU,MAAA;AAAA,UAAQ,SAAA;AAAA,UAAW,QAAQ,QAAA,CAAS,UAAA;AAAA,UAAY,MAAA;AAAA,UAAW;AAAA;AAAO;AAAA,KAChI,EACC,OAAA,CAAQ,iBAAA,KAAsB,IAAA,mBAC7B,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,qBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,iBAAA;AAAA,QAChB,WAAA,EAAY,wCAAA;AAAA,QACZ,aAAA,EAAa,IAAA;AAAA,QACb,aAAA,EAAe,OAAA;AAAA,QACf,aAAA,EAAc,MAAA;AAAA,QACd,cAAA,EAAe,SAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,QAAA,EAAU,QAAA;AAAA,UAAS,qBAAA;AAAA,UAAuB,OAAA,CAAQ,iBAAA;AAAA,UAChD,wCAAA;AAAA,UAA0C,IAAA;AAAA,UAAM,OAAA;AAAA,UAAS,MAAA;AAAA,UAAQ,SAAA;AAAA,UAAW,MAAA;AAAA,UAAW,MAAA;AAAA,UAAW;AAAA;AAAO;AAAA,KAC7G,mBAEA,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,KAAA,EAAM,qBAAA,EAAsB,aAAY,qCAAA,EAAsC,CAAA,EAEvF,OAAA,CAAQ,IAAA,KAAS,IAAA,mBAChB,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,sBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,IAAA;AAAA,QAChB,WAAA,EAAY,uCAAA;AAAA,QACZ,aAAA,EAAa,IAAA;AAAA,QACb,aAAA,EAAe,QAAA;AAAA,QACf,aAAA,EAAc,MAAA;AAAA,QACd,cAAA,EAAe,SAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,QAAA,EAAU,QAAA;AAAA,UAAS,sBAAA;AAAA,UAAwB,OAAA,CAAQ,IAAA;AAAA,UACjD,uCAAA;AAAA,UAAyC,IAAA;AAAA,UAAM,QAAA;AAAA,UAAU,MAAA;AAAA,UAAQ,SAAA;AAAA,UAAW,QAAQ,IAAA,CAAK,UAAA;AAAA,UAAY,MAAA;AAAA,UAAW;AAAA;AAAO;AAAA,KAC3H,mBAEA,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,KAAA,EAAM,sBAAA,EAAuB,aAAY,qCAAA,EAAsC,CAAA,EAExF,OAAA,CAAQ,gBAAA,KAAqB,IAAA,mBAC5B,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,wBAAA;AAAA,QACN,QAAQ,OAAA,CAAQ,gBAAA;AAAA,QAChB,WAAA,EAAa,CAAA,aAAA,EAAgB,WAAA,EAAa,KAAA,IAAS,QAAQ,CAAA,sBAAA,CAAA;AAAA,QAC3D,aAAA,EAAa,IAAA;AAAA,QACb,aAAa,OAAA,CAAQ,gBAAA,CAAiB,KAAA,GAAQ,CAAA,GAAI,QAAQ,OAAA,CAAQ,gBAAA,CAAiB,KAAK,CAAA,GAAA,EAAM,QAAQ,gBAAA,CAAiB,KAAA,KAAU,CAAA,GAAI,GAAA,GAAM,EAAE,CAAA,OAAA,CAAA,GAAO,MAAA;AAAA,QACpJ,QAAA,EAAU,QAAA;AAAA,UACR,wBAAA;AAAA,UACA,OAAA,CAAQ,gBAAA;AAAA,UACR,CAAA,aAAA,EAAgB,WAAA,EAAa,KAAA,IAAS,QAAQ,CAAA,sBAAA,CAAA;AAAA,UAC9C,IAAA;AAAA,UACA,eAAA;AAAA,UACA,KAAA;AAAA,UACA,SAAA;AAAA,UACA,QAAQ,gBAAA,CAAiB,UAAA;AAAA,UACzB,cAAA;AAAA,UACA;AAAA;AACF;AAAA,wBAGF,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,OAAM,wBAAA,EAAyB,WAAA,EAAY,uCAAsC,CAE7F,CAAA;AAAA,EAEJ,CAAA,GAEF,CACF,CAAA;AAEJ;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c2l2c/backstage-plugin-dora-metrics",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "A Backstage frontend plugin that displays DORA metrics (Deployment Frequency, Lead Time, Change Failure Rate, MTTR) for catalog entities via the GitHub API.",
5
5
  "main": "./dist/index.esm.js",
6
6
  "module": "./dist/index.esm.js",
@@ -12,7 +12,7 @@
12
12
  "@c2l2c/backstage-plugin-dora-metrics"
13
13
  ]
14
14
  },
15
- "configSchema": "config.d.ts",
15
+ "configSchema": "config.schema.json",
16
16
  "exports": {
17
17
  ".": {
18
18
  "types": "./dist/index.d.ts",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "files": [
41
41
  "dist",
42
- "config.d.ts",
42
+ "config.schema.json",
43
43
  "README.md"
44
44
  ],
45
45
  "scripts": {
@@ -78,8 +78,8 @@
78
78
  "@backstage/config": "^1.3.2",
79
79
  "@backstage/core-components": "^0.18.7",
80
80
  "@backstage/core-plugin-api": "^1.12.3",
81
- "@backstage/frontend-plugin-api": "^0.16.2",
82
- "@backstage/plugin-catalog-react": "^2.0.0",
81
+ "@backstage/frontend-plugin-api": "^0.17.2",
82
+ "@backstage/plugin-catalog-react": "^3.1.0",
83
83
  "@material-ui/core": "^4.12.4",
84
84
  "@radix-ui/react-select": "^2.2.6",
85
85
  "lucide-react": "^1.7.0"
package/config.d.ts DELETED
@@ -1,75 +0,0 @@
1
- export interface Config {
2
- app?: {
3
- /**
4
- * DORA metrics configuration.
5
- * @visibility frontend
6
- */
7
- doraMetrics?: {
8
- /**
9
- * Enable debug mode — uses mock data instead of the GitHub API.
10
- * Useful for local development and screenshots.
11
- * @visibility frontend
12
- */
13
- debug?: boolean;
14
- /**
15
- * Environments to track. At most one environment may have isProduction: true.
16
- * @visibility frontend
17
- */
18
- environments?: Array<{
19
- /** Display name shown in the UI (e.g. "Production"). @visibility frontend */
20
- name: string;
21
- /**
22
- * Branch(es) to track for this environment. Accepts:
23
- * - A single branch name: "main"
24
- * - Comma-separated names: "main,master"
25
- * - A JavaScript regex string: "^(main|master)$"
26
- *
27
- * When multiple branches match, PRs are fetched from all of them and
28
- * deduplicated by PR number before metrics are computed.
29
- * @visibility frontend
30
- */
31
- branch: string;
32
- /**
33
- * Mark this environment as the production environment.
34
- * Only one environment across the entire list may set this to true.
35
- * Production environments unlock Change Failure Rate, MTTR, and Hotfix count metrics.
36
- * @visibility frontend
37
- */
38
- isProduction?: boolean;
39
- /**
40
- * Label(s) on PRs that represent production failures (hotfixes).
41
- * Defaults to "hotfix". Accepts the same three formats as `branch`:
42
- * - Single label: "hotfix"
43
- * - Comma-separated: "hotfix,fix,bug"
44
- * - JavaScript regex: "^(hotfix|fix)$"
45
- * @visibility frontend
46
- */
47
- label?: string;
48
- /** Label on PRs that represent planned feature deployments. @visibility frontend */
49
- featureLabel?: string;
50
- }>;
51
- /**
52
- * @visibility frontend
53
- */
54
- collection?: {
55
- /** Number of days of history to fetch on initial load. Defaults to 30. @visibility frontend */
56
- initialDays?: number;
57
- };
58
- /**
59
- * Default performance targets used for rating calculations.
60
- * These can be overridden per entity via the dora-metrics/targets annotation.
61
- * @visibility frontend
62
- */
63
- targets?: {
64
- /** Target deployments per week (higher is better). Defaults to 7. @visibility frontend */
65
- deploymentFrequency?: number;
66
- /** Target lead time in hours (lower is better). Defaults to 24. @visibility frontend */
67
- leadTime?: number;
68
- /** Target change failure rate as a percentage (lower is better). Defaults to 15. @visibility frontend */
69
- changeFailureRate?: number;
70
- /** Target mean time to restore in hours (lower is better). Defaults to 1. @visibility frontend */
71
- mttr?: number;
72
- };
73
- };
74
- };
75
- }