repo-dive 0.15.2 → 0.16.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
@@ -73,12 +73,13 @@ Collectors so far:
73
73
  - **todo-comments** — TODO/FIXME/HACK/XXX counts
74
74
  - **languages** — lines and file count per language across a commit's source files (lockfiles, minified bundles and generated data excluded)
75
75
  - **survival** — `git blame` line survival by extension, author and age cohort (sampled monthly)
76
+ - **file-survival** — file survival by extension, creator and creation cohort, renames followed (sampled monthly)
76
77
 
77
78
  The catalog hides itself from git, but other tools that walk the repository (prettier, markdownlint, cspell, docker builds) each read one ignore file at its root.
78
79
  `scan` warns when the catalog is missing from those; `repo-dive ignore` adds it to every one that needs it, writing the entry in the shape the file is already written in and skipping the files whose tool learns about the catalog elsewhere.
79
80
 
80
81
  `index` normalizes raw snapshots into `.repo-dive/index/metrics.sqlite` — a facts-by-categories cube, rebuildable at any time — plus `dashboard.json`.
81
- `dashboard` then serves a local React app with interactive charts: languages over time, a GitHub-style commit calendar, monthly commits with AI-assisted share, churn, lint-suppression trends, dependency counts over time, code survival by cohort and author, and more.
82
+ `dashboard` then serves a local React app with interactive charts: languages over time, file counts over time, a GitHub-style commit calendar, monthly commits with AI-assisted share, churn, lint-suppression trends, dependency counts over time, code survival by cohort and author, and more.
82
83
 
83
84
  ## Configuration
84
85
 
package/dist/cli.js CHANGED
@@ -43882,7 +43882,7 @@ var runWith = (command, config) => {
43882
43882
  };
43883
43883
  var package_default = {
43884
43884
  name: "repo-dive",
43885
- version: "0.15.2",
43885
+ version: "0.16.0",
43886
43886
  description: "Dive into a git repo's history: per-commit snapshots, an indexed metrics catalog and an interactive dashboard",
43887
43887
  keywords: [
43888
43888
  "git",
@@ -43968,9 +43968,9 @@ var package_default = {
43968
43968
  "@tsconfig/node-ts": "23.6.4",
43969
43969
  "@tsconfig/strictest": "2.0.8",
43970
43970
  "@types/babel__core": "7.20.5",
43971
- "@types/node": "26.4.0",
43971
+ "@types/node": "26.4.1",
43972
43972
  "@types/react": "19.2.18",
43973
- "@types/react-dom": "19.2.5",
43973
+ "@types/react-dom": "19.2.7",
43974
43974
  "@typescript/native": "npm:typescript@7.0.2",
43975
43975
  "@visx/axis": "4.0.0",
43976
43976
  "@visx/curve": "4.0.0",
@@ -43981,14 +43981,14 @@ var package_default = {
43981
43981
  "@vitejs/plugin-react": "6.1.1",
43982
43982
  "babel-plugin-react-compiler": "1.0.0",
43983
43983
  "class-variance-authority": "0.7.1",
43984
- "cspell": "10.1.1",
43984
+ "cspell": "10.2.1",
43985
43985
  "effect": "4.0.0-rc.112",
43986
43986
  "eslint": "9.39.5",
43987
43987
  "eslint-plugin-react-hooks": "7.1.1",
43988
43988
  "husky": "9.1.7",
43989
- "knip": "6.33.0",
43989
+ "knip": "6.34.0",
43990
43990
  "lint-staged": "17.4.1",
43991
- "lucide-react": "1.35.0",
43991
+ "lucide-react": "1.40.0",
43992
43992
  "markdownlint-cli": "0.49.1",
43993
43993
  "npm-run-all2": "9.0.3",
43994
43994
  "prettier": "3.9.6",
@@ -43998,7 +43998,7 @@ var package_default = {
43998
43998
  "tailwindcss": "4.3.3",
43999
43999
  "typescript": "npm:@typescript/typescript6@6.0.2",
44000
44000
  "vite": "8.2.2",
44001
- "vitest": "4.1.11",
44001
+ "vitest": "5.0.0",
44002
44002
  "yaml": "2.9.0"
44003
44003
  },
44004
44004
  packageManager: "pnpm@12.3.3",
@@ -44156,6 +44156,14 @@ var languageOfExtension = (extension) => languageByExtension[extension] ?? exten
44156
44156
  * state, so charting it puts a cliff into every timeline.
44157
44157
  */
44158
44158
  var describesTreeState = (collector) => collector.strategy !== "log";
44159
+ /**
44160
+ * YYYY-MM of a unix timestamp in UTC — the age-cohort key the survival
44161
+ * collectors bin by, shared so lines and files land in identical cohorts.
44162
+ */
44163
+ var cohortMonthOf = (unixSeconds) => {
44164
+ const date = /* @__PURE__ */ new Date(unixSeconds * 1e3);
44165
+ return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}`;
44166
+ };
44159
44167
  /** File extension used as a category key, e.g. ".ts"; files without one map to "(none)". */
44160
44168
  var extensionOf = (filePath) => {
44161
44169
  const basename = filePath.split("/").at(-1) ?? "";
@@ -44189,7 +44197,7 @@ var isScannableSourceFile = (filePath) => {
44189
44197
  };
44190
44198
  //#endregion
44191
44199
  //#region src/cli/shared/collectors/churn.ts
44192
- var recordSeparator$1 = "";
44200
+ var recordSeparator$2 = "";
44193
44201
  /** Parses `git show --numstat --format=` output: "<added>\t<deleted>\t<path>" per file. */
44194
44202
  var parseNumstat = (stdout) => {
44195
44203
  let filesChanged = 0;
@@ -44249,11 +44257,11 @@ var churnCollector = {
44249
44257
  "-C",
44250
44258
  repoRoot,
44251
44259
  "log",
44252
- `--format=${recordSeparator$1}%H`,
44260
+ `--format=${recordSeparator$2}%H`,
44253
44261
  "--numstat"
44254
44262
  ]).pipe(map$7((stdout) => {
44255
44263
  const outputs = /* @__PURE__ */ new Map();
44256
- for (const record of stdout.split(recordSeparator$1)) {
44264
+ for (const record of stdout.split(recordSeparator$2)) {
44257
44265
  const newlineIndex = record.indexOf("\n");
44258
44266
  if (newlineIndex === -1) continue;
44259
44267
  const sha = record.slice(0, newlineIndex).trim();
@@ -44277,8 +44285,8 @@ var churnCollector = {
44277
44285
  };
44278
44286
  //#endregion
44279
44287
  //#region src/cli/shared/collectors/commit-meta.ts
44280
- var fieldSeparator$1 = "";
44281
- var recordSeparator = "";
44288
+ var fieldSeparator$2 = "";
44289
+ var recordSeparator$1 = "";
44282
44290
  var format = [
44283
44291
  "%H",
44284
44292
  "%an",
@@ -44306,7 +44314,7 @@ var parseTrailers = (raw) => {
44306
44314
  return trailers;
44307
44315
  };
44308
44316
  var parseCommitMeta = (stdout) => {
44309
- const [sha = "", authorName = "", authorEmail = "", authoredAt = "", committerName = "", committerEmail = "", committedAt = "", parentsRaw = "", subject = "", trailersRaw = ""] = stdout.trim().split(fieldSeparator$1);
44317
+ const [sha = "", authorName = "", authorEmail = "", authoredAt = "", committerName = "", committerEmail = "", committedAt = "", parentsRaw = "", subject = "", trailersRaw = ""] = stdout.trim().split(fieldSeparator$2);
44310
44318
  const trailers = parseTrailers(trailersRaw);
44311
44319
  return {
44312
44320
  sha,
@@ -44340,10 +44348,10 @@ var commitMetaCollector = {
44340
44348
  "-C",
44341
44349
  repoRoot,
44342
44350
  "log",
44343
- `--format=${recordSeparator}${format}`
44351
+ `--format=${recordSeparator$1}${format}`
44344
44352
  ]).pipe(map$7((stdout) => {
44345
44353
  const outputs = /* @__PURE__ */ new Map();
44346
- for (const record of stdout.split(recordSeparator)) {
44354
+ for (const record of stdout.split(recordSeparator$1)) {
44347
44355
  if (!record.trim()) continue;
44348
44356
  const meta = parseCommitMeta(record);
44349
44357
  if (shas.has(meta.sha)) outputs.set(meta.sha, meta);
@@ -50893,6 +50901,154 @@ var directivesCollector = {
50893
50901
  }
50894
50902
  };
50895
50903
  //#endregion
50904
+ //#region src/cli/shared/collectors/file-survival.ts
50905
+ /** Between the fields of one commit header in the log stream. */
50906
+ var fieldSeparator$1 = "";
50907
+ /** Before each commit record in the log stream. */
50908
+ var recordSeparator = "";
50909
+ /**
50910
+ * Resolves each present file to the commit that created it, from one
50911
+ * `git log --name-status` stream over the history below the snapshot
50912
+ * (newest first, `--format=%x1E%ae%x1F%at`).
50913
+ *
50914
+ * The walk keeps a map from a path *as history spelled it at that point* to
50915
+ * the present-day paths it became. An `A` entry is the creation event for
50916
+ * whatever that path currently traces; an `R old new` entry re-keys the trace
50917
+ * from `new` to `old`, so a rename chain resolves to the original creation
50918
+ * rather than restarting the file's life — later edits never re-bin a file
50919
+ * either, because only `A`/`R` entries are read at all. Renames of one commit
50920
+ * are applied as a batch: `a→b` and `c→a` in the same commit must both re-key
50921
+ * against the pre-commit names.
50922
+ *
50923
+ * A file whose creation the walk never meets (a history boundary, or a rename
50924
+ * past git's detection limit inside a merge) stays unresolved and is simply
50925
+ * absent from the returned map.
50926
+ */
50927
+ var resolveFileOrigins = (stdout, presentFiles) => {
50928
+ const origins = /* @__PURE__ */ new Map();
50929
+ /** Path as spelled at this point of the walk → present-day paths it became. */
50930
+ const pending = /* @__PURE__ */ new Map();
50931
+ for (const filePath of presentFiles) pending.set(filePath, [filePath]);
50932
+ for (const record of stdout.split(recordSeparator)) {
50933
+ if (pending.size === 0) break;
50934
+ const lines = record.split("\n");
50935
+ const [authorEmail = "", authorTimeRaw = ""] = (lines[0] ?? "").split(fieldSeparator$1);
50936
+ const authorTime = Number(authorTimeRaw);
50937
+ if (!Number.isFinite(authorTime)) continue;
50938
+ const origin = {
50939
+ authorEmail,
50940
+ cohortMonth: cohortMonthOf(authorTime)
50941
+ };
50942
+ /** This commit's renames, applied after its entries so they re-key atomically. */
50943
+ const renamedTo = /* @__PURE__ */ new Map();
50944
+ for (const line of lines.slice(1)) {
50945
+ const [status = "", ...paths] = line.split(" ");
50946
+ const createdPath = status === "A" ? paths[0] : void 0;
50947
+ const copiedPath = status.startsWith("C") ? paths[1] : void 0;
50948
+ const resolvedPath = createdPath ?? copiedPath;
50949
+ if (resolvedPath !== void 0) {
50950
+ const targets = pending.get(resolvedPath);
50951
+ if (targets) {
50952
+ for (const target of targets) origins.set(target, origin);
50953
+ pending.delete(resolvedPath);
50954
+ }
50955
+ } else if (status.startsWith("R")) {
50956
+ const [oldPath, newPath] = paths;
50957
+ if (oldPath === void 0 || newPath === void 0) continue;
50958
+ const targets = pending.get(newPath);
50959
+ if (targets) {
50960
+ pending.delete(newPath);
50961
+ renamedTo.set(oldPath, [...renamedTo.get(oldPath) ?? [], ...targets]);
50962
+ }
50963
+ }
50964
+ }
50965
+ for (const [oldPath, targets] of renamedTo) pending.set(oldPath, [...pending.get(oldPath) ?? [], ...targets]);
50966
+ }
50967
+ return origins;
50968
+ };
50969
+ /**
50970
+ * When a file was created and by whom, for every source file alive at a
50971
+ * commit. The granular sibling of `survival`: where that collector asks which
50972
+ * *lines* still live, this one only asks whether the *file* still exists —
50973
+ * edits after creation neither re-attribute a file nor move it to a newer
50974
+ * cohort, and only a deletion ends it.
50975
+ *
50976
+ * Emits into the `survival.*` metric namespace on purpose: `survival.lines`
50977
+ * and `survival.files` are two grains of the same question, and the dashboard
50978
+ * draws them with the same machinery. Collected separately so bumping either
50979
+ * collector's version does not invalidate the other's outputs (blame is far
50980
+ * more expensive than this log walk).
50981
+ */
50982
+ var fileSurvivalCollector = {
50983
+ name: "file-survival",
50984
+ description: "File survival via git log: living files by extension, creator and creation cohort, renames followed (sampled monthly by default)",
50985
+ version: "1",
50986
+ strategy: "tree",
50987
+ defaultSampling: "monthly",
50988
+ collect: ({ repoRoot, sha }) => gen(function* () {
50989
+ const fileList = yield* runGit([
50990
+ "-C",
50991
+ repoRoot,
50992
+ "ls-tree",
50993
+ "-r",
50994
+ sha
50995
+ ]);
50996
+ const files = [];
50997
+ for (const line of fileList.split("\n")) {
50998
+ const tabIndex = line.indexOf(" ");
50999
+ if (tabIndex === -1) continue;
51000
+ const [, type] = line.slice(0, tabIndex).split(/\s+/);
51001
+ const filePath = line.slice(tabIndex + 1);
51002
+ if (type === "blob" && isScannableSourceFile(filePath)) files.push(filePath);
51003
+ }
51004
+ const origins = resolveFileOrigins(yield* runGit([
51005
+ "-C",
51006
+ repoRoot,
51007
+ "log",
51008
+ "--topo-order",
51009
+ "-M",
51010
+ "--name-status",
51011
+ `--format=${recordSeparator}%ae${fieldSeparator$1}%at`,
51012
+ sha
51013
+ ]), files);
51014
+ const counts = /* @__PURE__ */ new Map();
51015
+ for (const filePath of files) {
51016
+ const origin = origins.get(filePath);
51017
+ const key = [
51018
+ extensionOf(filePath),
51019
+ origin?.authorEmail ?? "",
51020
+ origin?.cohortMonth ?? ""
51021
+ ].join(fieldSeparator$1);
51022
+ counts.set(key, (counts.get(key) ?? 0) + 1);
51023
+ }
51024
+ return {
51025
+ rows: [...counts.entries()].map(([key, fileCount]) => {
51026
+ const [extension = "", authorEmail = "", cohortMonth = ""] = key.split(fieldSeparator$1);
51027
+ return {
51028
+ extension,
51029
+ authorEmail,
51030
+ cohortMonth,
51031
+ files: fileCount
51032
+ };
51033
+ }),
51034
+ totalFiles: files.length
51035
+ };
51036
+ }),
51037
+ normalize: (raw) => {
51038
+ const facts = [];
51039
+ for (const row of arrayAt(raw, "rows")) facts.push({
51040
+ metric: "survival.files",
51041
+ value: numberAt(row, "files"),
51042
+ categories: {
51043
+ extension: stringAt(row, "extension"),
51044
+ author: stringAt(row, "authorEmail"),
51045
+ cohort: stringAt(row, "cohortMonth")
51046
+ }
51047
+ });
51048
+ return facts;
51049
+ }
51050
+ };
51051
+ //#endregion
50896
51052
  //#region src/cli/shared/collectors/file-types.ts
50897
51053
  /** Parses `git ls-tree -r -l <sha>` output: "<mode> <type> <sha> <size>\t<path>" per entry. */
50898
51054
  var parseLsTree = (stdout) => {
@@ -51028,10 +51184,6 @@ var languagesCollector = {
51028
51184
  };
51029
51185
  //#endregion
51030
51186
  //#region src/cli/shared/collectors/survival.ts
51031
- var monthOf = (unixSeconds) => {
51032
- const date = /* @__PURE__ */ new Date(unixSeconds * 1e3);
51033
- return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}`;
51034
- };
51035
51187
  /**
51036
51188
  * Parses `git blame --line-porcelain` output into one attribution per line.
51037
51189
  * Porcelain repeats full headers per line, so author-mail/author-time always
@@ -51045,7 +51197,7 @@ var parseBlamePorcelain = (stdout) => {
51045
51197
  else if (line.startsWith("author-time ")) authorTime = Number(line.slice(12));
51046
51198
  else if (line.startsWith(" ")) attributions.push({
51047
51199
  authorEmail,
51048
- cohortMonth: monthOf(authorTime)
51200
+ cohortMonth: cohortMonthOf(authorTime)
51049
51201
  });
51050
51202
  return attributions;
51051
51203
  };
@@ -51178,7 +51330,8 @@ var builtInCollectors = [
51178
51330
  }
51179
51331
  },
51180
51332
  languagesCollector,
51181
- survivalCollector
51333
+ survivalCollector,
51334
+ fileSurvivalCollector
51182
51335
  ];
51183
51336
  var UnknownCollectorError = class extends TaggedError$1("UnknownCollectorError") {
51184
51337
  get message() {
@@ -51302,6 +51455,7 @@ var collectorsCommand = make$6("collectors").pipe(withDescription("List availabl
51302
51455
  */
51303
51456
  var chartIds = [
51304
51457
  "lines-of-code",
51458
+ "number-of-files",
51305
51459
  "commit-calendar",
51306
51460
  "commits-per-month",
51307
51461
  "churn-per-month",
@@ -53020,7 +53174,8 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
53020
53174
  const languages = composeSeries((commit) => hasMetric(commit, "languages.lines"), (commit) => ({
53021
53175
  sha: commit.sha.slice(0, 10),
53022
53176
  date: commit.committedAt,
53023
- byLanguage: groupMetric(commit, "languages.lines", "language")
53177
+ byLanguage: groupMetric(commit, "languages.lines", "language"),
53178
+ byLanguageFiles: groupMetric(commit, "languages.files", "language")
53024
53179
  }));
53025
53180
  const fileTypes = composeSeries((commit) => hasMetric(commit, "files.count"), (commit) => ({
53026
53181
  sha: commit.sha.slice(0, 10),
@@ -53090,11 +53245,17 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
53090
53245
  if (resolved.kind !== "human") return kindGroupLabels[resolved.kind];
53091
53246
  return nameByCanonicalEmail.get(resolved.canonicalEmail.toLowerCase()) ?? resolved.label;
53092
53247
  };
53093
- const survival = composeSeries((commit) => hasMetric(commit, "survival.lines"), (commit) => {
53248
+ /**
53249
+ * One survival series — living lines or living files, the same shape at two
53250
+ * grains: the units cross-tabulated by contributor (and by extension) and
53251
+ * the year each was authored, so the dashboard can split a contributor's or
53252
+ * language's area into year bands.
53253
+ */
53254
+ const survivalSeriesOf = (metric) => composeSeries((commit) => hasMetric(commit, metric), (commit) => {
53094
53255
  const byContributorYear = {};
53095
53256
  const byLanguageYear = {};
53096
53257
  for (const facts of commit.factsByCollector.values()) for (const fact of facts) {
53097
- if (fact.metric !== "survival.lines") continue;
53258
+ if (fact.metric !== metric) continue;
53098
53259
  const label = survivalLabelOf(fact.categories?.["author"] ?? "");
53099
53260
  const year = (fact.categories?.["cohort"] ?? "").slice(0, 4) || "?";
53100
53261
  const byYear = byContributorYear[label] ??= {};
@@ -53106,13 +53267,15 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
53106
53267
  return {
53107
53268
  sha: commit.sha.slice(0, 10),
53108
53269
  date: commit.committedAt,
53109
- byCohort: groupMetric(commit, "survival.lines", "cohort"),
53110
- byContributor: sumByKey(groupMetric(commit, "survival.lines", "author"), survivalLabelOf),
53270
+ byCohort: groupMetric(commit, metric, "cohort"),
53271
+ byContributor: sumByKey(groupMetric(commit, metric, "author"), survivalLabelOf),
53111
53272
  byContributorYear,
53112
- byLanguage: sumByKey(groupMetric(commit, "survival.lines", "extension"), languageOfExtension),
53273
+ byLanguage: sumByKey(groupMetric(commit, metric, "extension"), languageOfExtension),
53113
53274
  byLanguageYear
53114
53275
  };
53115
53276
  });
53277
+ const survival = survivalSeriesOf("survival.lines");
53278
+ const fileSurvival = survivalSeriesOf("survival.files");
53116
53279
  const contributorMap = /* @__PURE__ */ new Map();
53117
53280
  /**
53118
53281
  * Fetches or creates a contributor's bucket.
@@ -53209,6 +53372,7 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
53209
53372
  dependencies,
53210
53373
  topRules,
53211
53374
  survival,
53375
+ fileSurvival,
53212
53376
  contributors
53213
53377
  };
53214
53378
  };
package/dist/config.d.ts CHANGED
@@ -75,7 +75,7 @@ export type WeekStart = "monday" | "sunday";
75
75
  * Stable identifiers of the dashboard's chart sections, in page order — the
76
76
  * keys {@link ChartsConfig.annotations} accepts.
77
77
  */
78
- export declare const chartIds: readonly ["lines-of-code", "commit-calendar", "commits-per-month", "churn-per-month", "direct-dependencies", "dependencies", "loose-ends", "most-suppressed-eslint-rules", "contributors"];
78
+ export declare const chartIds: readonly ["lines-of-code", "number-of-files", "commit-calendar", "commits-per-month", "churn-per-month", "direct-dependencies", "dependencies", "loose-ends", "most-suppressed-eslint-rules", "contributors"];
79
79
  export type ChartId = (typeof chartIds)[number];
80
80
  export type ChartsConfig = {
81
81
  /**
package/dist/config.js CHANGED
@@ -37,6 +37,7 @@
37
37
  */
38
38
  export const chartIds = [
39
39
  "lines-of-code",
40
+ "number-of-files",
40
41
  "commit-calendar",
41
42
  "commits-per-month",
42
43
  "churn-per-month",
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-xs:.125rem;--radius-sm:.25rem;--radius-md:.375rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--animate-late-fade-in:late-fade-in .15s ease-out .5s both}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-2{top:calc(var(--spacing) * 2)}.right-0{right:0}.right-2{right:calc(var(--spacing) * 2)}.bottom-0{bottom:0}.left-0{left:0}.isolate{isolation:isolate}.z-10{z-index:10}.z-50{z-index:50}.col-start-1{grid-column-start:1}.row-start-1{grid-row-start:1}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mx-auto{margin-inline:auto}.my-0\.5{margin-block:calc(var(--spacing) * .5)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mr-1{margin-right:var(--spacing)}.mb-1{margin-bottom:var(--spacing)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.table{display:table}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-full{width:100%;height:100%}.h-\(--scroll-area-thumb-height\){height:var(--scroll-area-thumb-height)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-full{height:100%}.max-h-\(--available-height\){max-height:var(--available-height)}.max-h-64{max-height:calc(var(--spacing) * 64)}.w-\(--scroll-area-thumb-width\){width:var(--scroll-area-thumb-width)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-12{width:calc(var(--spacing) * 12)}.w-20{width:calc(var(--spacing) * 20)}.w-36{width:calc(var(--spacing) * 36)}.w-56{width:calc(var(--spacing) * 56)}.w-80{width:calc(var(--spacing) * 80)}.w-fit{width:fit-content}.w-full{width:100%}.w-max{width:max-content}.max-w-5xl{max-width:var(--container-5xl)}.max-w-full{max-width:100%}.max-w-prose{max-width:65ch}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.min-w-\(--anchor-width\){min-width:var(--anchor-width)}.min-w-8{min-width:calc(var(--spacing) * 8)}.min-w-9{min-width:calc(var(--spacing) * 9)}.min-w-10{min-width:calc(var(--spacing) * 10)}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.origin-\(--transform-origin\){transform-origin:var(--transform-origin)}.animate-late-fade-in{animation:var(--animate-late-fade-in)}.cursor-default{cursor:default}.cursor-help{cursor:help}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-1{row-gap:var(--spacing)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:2147483647px}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xs{border-radius:var(--radius-xs)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-\(--grid-line\),.border-border,.border-input{border-color:var(--grid-line)}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-\(--surface-1\){background-color:var(--surface-1)}.bg-\(--surface-2\){background-color:var(--surface-2)}.bg-background\/85{background-color:var(--surface-1)}@supports (color:color-mix(in lab, red, red)){.bg-background\/85{background-color:color-mix(in oklab, var(--surface-1) 85%, transparent)}}.bg-border{background-color:var(--grid-line)}.bg-muted{background-color:var(--surface-2)}.bg-popover{background-color:var(--surface-1)}.bg-transparent{background-color:#0000}.fill-muted{fill:var(--surface-2)}.stroke-border{stroke:var(--grid-line)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-px{padding:1px}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-16{padding-block:calc(var(--spacing) * 16)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-1{padding-bottom:var(--spacing)}.pb-2\.5{padding-bottom:calc(var(--spacing) * 2.5)}.pl-1{padding-left:var(--spacing)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.65rem\]{font-size:.65rem}.text-\[0\.85em\]{font-size:.85em}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.text-balance{text-wrap:balance}.whitespace-nowrap{white-space:nowrap}.text-\(--text-muted\){color:var(--text-muted)}.text-\(--text-secondary\){color:var(--text-secondary)}.text-foreground{color:var(--text-primary)}.text-muted-foreground{color:var(--text-muted)}.text-popover-foreground{color:var(--text-primary)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.decoration-\(--text-muted\){-webkit-text-decoration-color:var(--text-muted);-webkit-text-decoration-color:var(--text-muted);-webkit-text-decoration-color:var(--text-muted);text-decoration-color:var(--text-muted)}.decoration-dotted{text-decoration-style:dotted}.underline-offset-2{text-underline-offset:2px}.underline-offset-4{text-underline-offset:4px}.opacity-35{opacity:.35}.opacity-60{opacity:.6}.opacity-90{opacity:.9}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[opacity\,transform\]{transition-property:opacity,transform;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.not-data-pressed\:text-\(--text-muted\):not([data-pressed]){color:var(--text-muted)}@media (hover:hover){.group-hover\:text-\(--series-1\):is(:where(.group):hover *){color:var(--series-1)}.group-hover\:line-through:is(:where(.group):hover *){text-decoration-line:line-through}.group-hover\:decoration-muted-foreground\/40:is(:where(.group):hover *){-webkit-text-decoration-color:var(--text-muted);-webkit-text-decoration-color:var(--text-muted);-webkit-text-decoration-color:var(--text-muted);text-decoration-color:var(--text-muted)}@supports (color:color-mix(in lab, red, red)){.group-hover\:decoration-muted-foreground\/40:is(:where(.group):hover *){-webkit-text-decoration-color:color-mix(in oklab, var(--text-muted) 40%, transparent);-webkit-text-decoration-color:color-mix(in oklab, var(--text-muted) 40%, transparent);-webkit-text-decoration-color:color-mix(in oklab, var(--text-muted) 40%, transparent);text-decoration-color:color-mix(in oklab, var(--text-muted) 40%, transparent)}}}.first\:mt-0:first-child{margin-top:0}@media (hover:hover){.hover\:text-\(--series-1\):hover{color:var(--series-1)}.hover\:text-\(--text-primary\):hover,.hover\:text-foreground:hover{color:var(--text-primary)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:z-10:focus,.focus-visible\:z-10:focus-visible{z-index:10}.focus-visible\:border-ring:focus-visible{border-color:var(--series-1)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--series-1)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--series-1) 50%, transparent)}}.focus-visible\:outline-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}@media (hover:hover){.enabled\:hover\:bg-accent:enabled:hover,.enabled\:hover\:bg-muted:enabled:hover{background-color:var(--surface-2)}.enabled\:hover\:text-accent-foreground:enabled:hover{color:var(--text-primary)}.enabled\:hover\:text-muted-foreground:enabled:hover{color:var(--text-muted)}.not-data-pressed\:enabled\:hover\:bg-transparent:not([data-pressed]):enabled:hover{background-color:#0000}.not-data-pressed\:enabled\:hover\:text-\(--text-secondary\):not([data-pressed]):enabled:hover{color:var(--text-secondary)}}.disabled\:pointer-events-auto:disabled{pointer-events:auto}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.data-disabled\:pointer-events-none[data-disabled]{pointer-events:none}.data-disabled\:opacity-50[data-disabled]{opacity:.5}.data-highlighted\:bg-accent[data-highlighted]{background-color:var(--surface-2)}.data-highlighted\:text-accent-foreground[data-highlighted]{color:var(--text-primary)}.data-pressed\:bg-accent[data-pressed]{background-color:var(--surface-2)}.data-pressed\:text-accent-foreground[data-pressed]{color:var(--text-primary)}.data-\[ending-style\]\:scale-95[data-ending-style]{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.data-\[ending-style\]\:opacity-0[data-ending-style]{opacity:0}.data-\[side\=bottom\]\:-top-\[4px\][data-side=bottom]{top:-4px}.data-\[side\=bottom\]\:rotate-180[data-side=bottom]{rotate:180deg}.data-\[side\=left\]\:-right-\[6\.5px\][data-side=left]{right:-6.5px}.data-\[side\=left\]\:-rotate-90[data-side=left]{rotate:-90deg}.data-\[side\=right\]\:-left-\[6\.5px\][data-side=right]{left:-6.5px}.data-\[side\=right\]\:rotate-90[data-side=right]{rotate:90deg}.data-\[side\=top\]\:-bottom-\[4px\][data-side=top]{bottom:-4px}.data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--spacing) * 9)}.data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--spacing) * 8)}.data-\[size\=xs\]\:h-7[data-size=xs]{height:calc(var(--spacing) * 7)}.data-\[size\=xs\]\:px-2[data-size=xs]{padding-inline:calc(var(--spacing) * 2)}.data-\[size\=xs\]\:text-xs[data-size=xs]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-2>*)[data-slot=select-value]{gap:calc(var(--spacing) * 2)}.data-\[starting-style\]\:scale-95[data-starting-style]{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.data-\[starting-style\]\:opacity-0[data-starting-style]{opacity:0}.data-\[variant\=outline\]\:gap-0[data-variant=outline]{gap:0}.data-\[variant\=outline\]\:rounded-md[data-variant=outline]{border-radius:var(--radius-md)}.data-\[variant\=outline\]\:rounded-none[data-variant=outline]{border-radius:0}.data-\[variant\=outline\]\:border-l-0[data-variant=outline]{border-left-style:var(--tw-border-style);border-left-width:0}.data-\[variant\=outline\]\:first\:rounded-l-md[data-variant=outline]:first-child{border-top-left-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.data-\[variant\=outline\]\:first\:border-l[data-variant=outline]:first-child{border-left-style:var(--tw-border-style);border-left-width:1px}.data-\[variant\=outline\]\:last\:rounded-r-md[data-variant=outline]:last-child{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md)}@media (prefers-reduced-motion:no-preference){.motion-safe\:animate-pulse{animation:var(--animate-pulse)}.motion-safe\:animate-spin{animation:var(--animate-spin)}}@media (width>=40rem){.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width>=64rem){.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--text-muted)}}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--surface-1:#fcfcfb;--surface-2:#f4f4f2;--text-primary:#0b0b0b;--text-secondary:#52514e;--text-muted:#86847e;--grid-line:#e7e6e2;--series-1:#2a78d6;--series-2:#1baf7a;--series-3:#eda100;--series-4:#008300;--series-5:#4a3aa7;--series-6:#e34948;--series-7:#e87ba4;--series-8:#eb6834;--series-9:#8569e8;--series-10:#958400;--series-11:#a92172;--series-12:#925001;--series-13:#00b8bf;--series-14:#59bc42;--series-15:#df71d7;--series-16:#8339b0;--series-17:#5b6c00;--series-18:#006a9e;--series-19:#b51b34;--series-20:#3d54cb;--diverge-pos:#2a78d6;--diverge-neg:#e34948;--kind-human:var(--series-1);--kind-bot:var(--series-3);--kind-ai:#93278f;--seq-100:#cde2fb;--seq-200:#9ec5f4;--seq-300:#6da7ec;--seq-400:#3987e5;--seq-500:#256abf;--seq-600:#184f95;--seq-700:#0d366b}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--surface-1:#1a1a19;--surface-2:#242423;--text-primary:#fff;--text-secondary:#c3c2b7;--text-muted:#8b897f;--grid-line:#33332f;--series-1:#3987e5;--series-2:#199e70;--series-3:#c98500;--series-4:#008300;--series-5:#9085e9;--series-6:#e66767;--series-7:#d55181;--series-8:#d95926;--series-9:#8164e3;--series-10:#907f03;--series-11:#b02978;--series-12:#9b5400;--series-13:#03a5ab;--series-14:#47aa2d;--series-15:#cd5fc5;--series-16:#893fb6;--series-17:#607201;--series-18:#0070a6;--series-19:#bc2539;--series-20:#425ad1;--diverge-pos:#3987e5;--diverge-neg:#e66767;--kind-ai:#b02fa8}}html{overflow-y:scroll}body{background:var(--surface-1);color:var(--text-primary)}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes late-fade-in{0%{opacity:0}to{opacity:1}}