flowdoc-gen 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -13,11 +13,11 @@ program.command("init").description("Scaffold a flowdoc.config.ts in the current
13
13
  init();
14
14
  });
15
15
  program.command("generate").description("Parse your routes and write docs to the output folder").option("-c, --config <path>", "Path to flowdoc config file").option("-o, --output <path>", "Override output directory").option("-q, --quiet", "Suppress output").action(async (opts) => {
16
- const { generate } = await import("./generate-4NMSVNJF.js");
16
+ const { generate } = await import("./generate-TGEAHUE2.js");
17
17
  await generate(opts);
18
18
  });
19
19
  program.command("serve").description("Generate docs and serve them locally").option("-c, --config <path>", "Path to flowdoc config file").option("-o, --output <path>", "Override output directory").option("-p, --port <number>", "Port to serve on (default: 4000)", "4000").option("-w, --watch", "Re-generate docs on source file changes").option("--no-open", "Don't open browser automatically").action(async (opts) => {
20
- const { serve } = await import("./serve-AD6IWZXI.js");
20
+ const { serve } = await import("./serve-M5UPSOY5.js");
21
21
  const serveOpts = {
22
22
  port: opts.port ? parseInt(opts.port, 10) : 4e3,
23
23
  noOpen: !opts.open,
@@ -885,14 +885,23 @@ var tryExtractRoute = (call, ctx) => {
885
885
  const { requestBody, parameters } = extractFromMiddleware(middlewareArgs, ctx);
886
886
  const pathParams = extractPathParameters(path);
887
887
  const handlerInfo = extractHandlerInfo(handlerArg);
888
+ const responseSchemas = extractResponseSchemas(handlerArg);
888
889
  const allParameters = mergeParameters(parameters, pathParams);
889
890
  const tags = inferTags(path);
891
+ const responses = buildDefaultResponses(method);
892
+ for (const [code, schema] of Object.entries(responseSchemas)) {
893
+ const existing = responses[code] ?? { description: `HTTP ${code}` };
894
+ responses[code] = {
895
+ ...existing,
896
+ content: { "application/json": { schema } }
897
+ };
898
+ }
890
899
  const route = {
891
900
  method,
892
901
  path,
893
902
  tags,
894
903
  parameters: allParameters,
895
- responses: buildDefaultResponses(method),
904
+ responses,
896
905
  middleware: middlewareArgs.map((m) => m.getText())
897
906
  };
898
907
  if (handlerInfo.summary !== void 0) route.summary = handlerInfo.summary;
@@ -951,6 +960,64 @@ var extractPathParameters = (path) => {
951
960
  }
952
961
  return params;
953
962
  };
963
+ var extractResponseSchemas = (handler) => {
964
+ const result = {};
965
+ const calls = handler.getDescendantsOfKind(SyntaxKind3.CallExpression);
966
+ for (const call of calls) {
967
+ const expr = call.getExpression();
968
+ if (!Node5.isPropertyAccessExpression(expr)) continue;
969
+ const methodName = expr.getName();
970
+ if (methodName !== "json" && methodName !== "send") continue;
971
+ let statusCode = "200";
972
+ const callee = expr.getExpression();
973
+ if (Node5.isCallExpression(callee)) {
974
+ const statusExpr = callee.getExpression();
975
+ if (Node5.isPropertyAccessExpression(statusExpr) && statusExpr.getName() === "status") {
976
+ const statusArg = callee.getArguments()[0];
977
+ if (statusArg && Node5.isNumericLiteral(statusArg)) {
978
+ statusCode = statusArg.getText();
979
+ }
980
+ }
981
+ }
982
+ const args = call.getArguments();
983
+ if (args.length === 0) continue;
984
+ const arg = args[0];
985
+ if (!arg || !Node5.isObjectLiteralExpression(arg)) continue;
986
+ const schema = schemaFromObjectLiteral(arg);
987
+ if (schema && Object.keys(schema.properties ?? {}).length > 0) {
988
+ if (!result[statusCode]) result[statusCode] = schema;
989
+ }
990
+ }
991
+ return result;
992
+ };
993
+ var schemaFromObjectLiteral = (obj) => {
994
+ const properties = {};
995
+ for (const prop of obj.getProperties()) {
996
+ if (!Node5.isPropertyAssignment(prop)) continue;
997
+ const name = prop.getNameNode().getText().replace(/['"]/g, "");
998
+ const init = prop.getInitializer();
999
+ if (!init) continue;
1000
+ properties[name] = schemaFromValue(init);
1001
+ }
1002
+ return { type: "object", properties };
1003
+ };
1004
+ var schemaFromValue = (expr) => {
1005
+ if (Node5.isStringLiteral(expr) || Node5.isTemplateExpression(expr) || Node5.isNoSubstitutionTemplateLiteral(expr))
1006
+ return { type: "string", example: Node5.isStringLiteral(expr) ? expr.getLiteralValue() : void 0 };
1007
+ if (Node5.isNumericLiteral(expr))
1008
+ return { type: "number", example: Number(expr.getLiteralValue()) };
1009
+ if (expr.getKind() === SyntaxKind3.TrueKeyword || expr.getKind() === SyntaxKind3.FalseKeyword)
1010
+ return { type: "boolean", example: expr.getKind() === SyntaxKind3.TrueKeyword };
1011
+ if (Node5.isNullLiteral(expr))
1012
+ return { type: "null" };
1013
+ if (Node5.isArrayLiteralExpression(expr)) {
1014
+ const items = expr.getElements()[0];
1015
+ return { type: "array", items: items ? schemaFromValue(items) : {} };
1016
+ }
1017
+ if (Node5.isObjectLiteralExpression(expr))
1018
+ return schemaFromObjectLiteral(expr);
1019
+ return {};
1020
+ };
954
1021
  var extractHandlerInfo = (handler) => {
955
1022
  const leadingComments = handler.getLeadingCommentRanges();
956
1023
  if (leadingComments.length === 0) return {};
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  generate
3
- } from "./chunk-ZJCRRUX7.js";
3
+ } from "./chunk-KSXE5LV4.js";
4
4
  export {
5
5
  generate
6
6
  };
package/dist/index.cjs CHANGED
@@ -925,14 +925,23 @@ var tryExtractRoute = (call, ctx) => {
925
925
  const { requestBody, parameters } = extractFromMiddleware(middlewareArgs, ctx);
926
926
  const pathParams = extractPathParameters(path);
927
927
  const handlerInfo = extractHandlerInfo(handlerArg);
928
+ const responseSchemas = extractResponseSchemas(handlerArg);
928
929
  const allParameters = mergeParameters(parameters, pathParams);
929
930
  const tags = inferTags(path);
931
+ const responses = buildDefaultResponses(method);
932
+ for (const [code, schema] of Object.entries(responseSchemas)) {
933
+ const existing = responses[code] ?? { description: `HTTP ${code}` };
934
+ responses[code] = {
935
+ ...existing,
936
+ content: { "application/json": { schema } }
937
+ };
938
+ }
930
939
  const route = {
931
940
  method,
932
941
  path,
933
942
  tags,
934
943
  parameters: allParameters,
935
- responses: buildDefaultResponses(method),
944
+ responses,
936
945
  middleware: middlewareArgs.map((m) => m.getText())
937
946
  };
938
947
  if (handlerInfo.summary !== void 0) route.summary = handlerInfo.summary;
@@ -991,6 +1000,64 @@ var extractPathParameters = (path) => {
991
1000
  }
992
1001
  return params;
993
1002
  };
1003
+ var extractResponseSchemas = (handler) => {
1004
+ const result = {};
1005
+ const calls = handler.getDescendantsOfKind(import_ts_morph.SyntaxKind.CallExpression);
1006
+ for (const call of calls) {
1007
+ const expr = call.getExpression();
1008
+ if (!import_ts_morph.Node.isPropertyAccessExpression(expr)) continue;
1009
+ const methodName = expr.getName();
1010
+ if (methodName !== "json" && methodName !== "send") continue;
1011
+ let statusCode = "200";
1012
+ const callee = expr.getExpression();
1013
+ if (import_ts_morph.Node.isCallExpression(callee)) {
1014
+ const statusExpr = callee.getExpression();
1015
+ if (import_ts_morph.Node.isPropertyAccessExpression(statusExpr) && statusExpr.getName() === "status") {
1016
+ const statusArg = callee.getArguments()[0];
1017
+ if (statusArg && import_ts_morph.Node.isNumericLiteral(statusArg)) {
1018
+ statusCode = statusArg.getText();
1019
+ }
1020
+ }
1021
+ }
1022
+ const args = call.getArguments();
1023
+ if (args.length === 0) continue;
1024
+ const arg = args[0];
1025
+ if (!arg || !import_ts_morph.Node.isObjectLiteralExpression(arg)) continue;
1026
+ const schema = schemaFromObjectLiteral(arg);
1027
+ if (schema && Object.keys(schema.properties ?? {}).length > 0) {
1028
+ if (!result[statusCode]) result[statusCode] = schema;
1029
+ }
1030
+ }
1031
+ return result;
1032
+ };
1033
+ var schemaFromObjectLiteral = (obj) => {
1034
+ const properties = {};
1035
+ for (const prop of obj.getProperties()) {
1036
+ if (!import_ts_morph.Node.isPropertyAssignment(prop)) continue;
1037
+ const name = prop.getNameNode().getText().replace(/['"]/g, "");
1038
+ const init2 = prop.getInitializer();
1039
+ if (!init2) continue;
1040
+ properties[name] = schemaFromValue(init2);
1041
+ }
1042
+ return { type: "object", properties };
1043
+ };
1044
+ var schemaFromValue = (expr) => {
1045
+ if (import_ts_morph.Node.isStringLiteral(expr) || import_ts_morph.Node.isTemplateExpression(expr) || import_ts_morph.Node.isNoSubstitutionTemplateLiteral(expr))
1046
+ return { type: "string", example: import_ts_morph.Node.isStringLiteral(expr) ? expr.getLiteralValue() : void 0 };
1047
+ if (import_ts_morph.Node.isNumericLiteral(expr))
1048
+ return { type: "number", example: Number(expr.getLiteralValue()) };
1049
+ if (expr.getKind() === import_ts_morph.SyntaxKind.TrueKeyword || expr.getKind() === import_ts_morph.SyntaxKind.FalseKeyword)
1050
+ return { type: "boolean", example: expr.getKind() === import_ts_morph.SyntaxKind.TrueKeyword };
1051
+ if (import_ts_morph.Node.isNullLiteral(expr))
1052
+ return { type: "null" };
1053
+ if (import_ts_morph.Node.isArrayLiteralExpression(expr)) {
1054
+ const items = expr.getElements()[0];
1055
+ return { type: "array", items: items ? schemaFromValue(items) : {} };
1056
+ }
1057
+ if (import_ts_morph.Node.isObjectLiteralExpression(expr))
1058
+ return schemaFromObjectLiteral(expr);
1059
+ return {};
1060
+ };
994
1061
  var extractHandlerInfo = (handler) => {
995
1062
  const leadingComments = handler.getLeadingCommentRanges();
996
1063
  if (leadingComments.length === 0) return {};
@@ -1333,6 +1400,7 @@ var MIME = {
1333
1400
  ".json": "application/json"
1334
1401
  };
1335
1402
  var flowdoc = (opts = {}) => {
1403
+ const disabled = opts.disabled ?? process.env.NODE_ENV === "production";
1336
1404
  const cwd = process.cwd();
1337
1405
  let outputDir = null;
1338
1406
  let ready = false;
@@ -1361,6 +1429,10 @@ var flowdoc = (opts = {}) => {
1361
1429
  }
1362
1430
  })();
1363
1431
  return async (req, res, next) => {
1432
+ if (disabled) {
1433
+ res.status(403).send("API docs are not available in this environment.");
1434
+ return;
1435
+ }
1364
1436
  await init2;
1365
1437
  if (initError || !outputDir) {
1366
1438
  res.status(500).send(`flowdoc init failed: ${initError}`);
@@ -1370,8 +1442,9 @@ var flowdoc = (opts = {}) => {
1370
1442
  if (urlPath === "/index.html") {
1371
1443
  const brand = "#6366f1";
1372
1444
  const baseUrl = `${req.protocol}://${req.get("host")}`;
1445
+ const docsBase = req.baseUrl || "";
1373
1446
  res.setHeader("Content-Type", "text/html");
1374
- res.send(buildHtml({ baseUrl, brand }));
1447
+ res.send(buildHtml({ baseUrl, brand, docsBase }));
1375
1448
  return;
1376
1449
  }
1377
1450
  const filePath = (0, import_path6.join)(outputDir, urlPath);
@@ -1384,7 +1457,7 @@ var flowdoc = (opts = {}) => {
1384
1457
  (0, import_fs6.createReadStream)(filePath).pipe(res);
1385
1458
  };
1386
1459
  };
1387
- var buildHtml = ({ baseUrl, brand }) => `<!DOCTYPE html>
1460
+ var buildHtml = ({ baseUrl, brand, docsBase }) => `<!DOCTYPE html>
1388
1461
  <html lang="en" class="dark">
1389
1462
  <head>
1390
1463
  <meta charset="UTF-8" />
@@ -1393,9 +1466,10 @@ var buildHtml = ({ baseUrl, brand }) => `<!DOCTYPE html>
1393
1466
  <script>
1394
1467
  window.__FLOWDOC_BRAND__ = "${brand}";
1395
1468
  window.__FLOWDOC_BASE_URL__ = "${baseUrl}";
1469
+ window.__FLOWDOC_DOCS_BASE__ = "${docsBase}";
1396
1470
  </script>
1397
- <script type="module" crossorigin src="./assets/ui.js"></script>
1398
- <link rel="stylesheet" href="./assets/index.css" />
1471
+ <script type="module" crossorigin src="${docsBase}/assets/ui.js"></script>
1472
+ <link rel="stylesheet" href="${docsBase}/assets/index.css" />
1399
1473
  </head>
1400
1474
  <body><div id="root"></div></body>
1401
1475
  </html>`;
package/dist/index.d.cts CHANGED
@@ -131,6 +131,13 @@ interface FlowDocMiddlewareOptions {
131
131
  config?: string;
132
132
  /** Route prefix the middleware is mounted at — used only for the HTML shell */
133
133
  path?: string;
134
+ /**
135
+ * Disable the docs endpoint. Useful for production environments.
136
+ * Defaults to `process.env.NODE_ENV === "production"` when not set,
137
+ * meaning docs are served in development and blocked in production.
138
+ * Pass `false` to explicitly enable in production; `true` to always block.
139
+ */
140
+ disabled?: boolean;
134
141
  }
135
142
  /**
136
143
  * Express middleware that serves flowdoc docs at whatever route you mount it on.
package/dist/index.d.ts CHANGED
@@ -131,6 +131,13 @@ interface FlowDocMiddlewareOptions {
131
131
  config?: string;
132
132
  /** Route prefix the middleware is mounted at — used only for the HTML shell */
133
133
  path?: string;
134
+ /**
135
+ * Disable the docs endpoint. Useful for production environments.
136
+ * Defaults to `process.env.NODE_ENV === "production"` when not set,
137
+ * meaning docs are served in development and blocked in production.
138
+ * Pass `false` to explicitly enable in production; `true` to always block.
139
+ */
140
+ disabled?: boolean;
134
141
  }
135
142
  /**
136
143
  * Express middleware that serves flowdoc docs at whatever route you mount it on.
package/dist/index.js CHANGED
@@ -885,14 +885,23 @@ var tryExtractRoute = (call, ctx) => {
885
885
  const { requestBody, parameters } = extractFromMiddleware(middlewareArgs, ctx);
886
886
  const pathParams = extractPathParameters(path);
887
887
  const handlerInfo = extractHandlerInfo(handlerArg);
888
+ const responseSchemas = extractResponseSchemas(handlerArg);
888
889
  const allParameters = mergeParameters(parameters, pathParams);
889
890
  const tags = inferTags(path);
891
+ const responses = buildDefaultResponses(method);
892
+ for (const [code, schema] of Object.entries(responseSchemas)) {
893
+ const existing = responses[code] ?? { description: `HTTP ${code}` };
894
+ responses[code] = {
895
+ ...existing,
896
+ content: { "application/json": { schema } }
897
+ };
898
+ }
890
899
  const route = {
891
900
  method,
892
901
  path,
893
902
  tags,
894
903
  parameters: allParameters,
895
- responses: buildDefaultResponses(method),
904
+ responses,
896
905
  middleware: middlewareArgs.map((m) => m.getText())
897
906
  };
898
907
  if (handlerInfo.summary !== void 0) route.summary = handlerInfo.summary;
@@ -951,6 +960,64 @@ var extractPathParameters = (path) => {
951
960
  }
952
961
  return params;
953
962
  };
963
+ var extractResponseSchemas = (handler) => {
964
+ const result = {};
965
+ const calls = handler.getDescendantsOfKind(SyntaxKind3.CallExpression);
966
+ for (const call of calls) {
967
+ const expr = call.getExpression();
968
+ if (!Node5.isPropertyAccessExpression(expr)) continue;
969
+ const methodName = expr.getName();
970
+ if (methodName !== "json" && methodName !== "send") continue;
971
+ let statusCode = "200";
972
+ const callee = expr.getExpression();
973
+ if (Node5.isCallExpression(callee)) {
974
+ const statusExpr = callee.getExpression();
975
+ if (Node5.isPropertyAccessExpression(statusExpr) && statusExpr.getName() === "status") {
976
+ const statusArg = callee.getArguments()[0];
977
+ if (statusArg && Node5.isNumericLiteral(statusArg)) {
978
+ statusCode = statusArg.getText();
979
+ }
980
+ }
981
+ }
982
+ const args = call.getArguments();
983
+ if (args.length === 0) continue;
984
+ const arg = args[0];
985
+ if (!arg || !Node5.isObjectLiteralExpression(arg)) continue;
986
+ const schema = schemaFromObjectLiteral(arg);
987
+ if (schema && Object.keys(schema.properties ?? {}).length > 0) {
988
+ if (!result[statusCode]) result[statusCode] = schema;
989
+ }
990
+ }
991
+ return result;
992
+ };
993
+ var schemaFromObjectLiteral = (obj) => {
994
+ const properties = {};
995
+ for (const prop of obj.getProperties()) {
996
+ if (!Node5.isPropertyAssignment(prop)) continue;
997
+ const name = prop.getNameNode().getText().replace(/['"]/g, "");
998
+ const init2 = prop.getInitializer();
999
+ if (!init2) continue;
1000
+ properties[name] = schemaFromValue(init2);
1001
+ }
1002
+ return { type: "object", properties };
1003
+ };
1004
+ var schemaFromValue = (expr) => {
1005
+ if (Node5.isStringLiteral(expr) || Node5.isTemplateExpression(expr) || Node5.isNoSubstitutionTemplateLiteral(expr))
1006
+ return { type: "string", example: Node5.isStringLiteral(expr) ? expr.getLiteralValue() : void 0 };
1007
+ if (Node5.isNumericLiteral(expr))
1008
+ return { type: "number", example: Number(expr.getLiteralValue()) };
1009
+ if (expr.getKind() === SyntaxKind3.TrueKeyword || expr.getKind() === SyntaxKind3.FalseKeyword)
1010
+ return { type: "boolean", example: expr.getKind() === SyntaxKind3.TrueKeyword };
1011
+ if (Node5.isNullLiteral(expr))
1012
+ return { type: "null" };
1013
+ if (Node5.isArrayLiteralExpression(expr)) {
1014
+ const items = expr.getElements()[0];
1015
+ return { type: "array", items: items ? schemaFromValue(items) : {} };
1016
+ }
1017
+ if (Node5.isObjectLiteralExpression(expr))
1018
+ return schemaFromObjectLiteral(expr);
1019
+ return {};
1020
+ };
954
1021
  var extractHandlerInfo = (handler) => {
955
1022
  const leadingComments = handler.getLeadingCommentRanges();
956
1023
  if (leadingComments.length === 0) return {};
@@ -1293,6 +1360,7 @@ var MIME = {
1293
1360
  ".json": "application/json"
1294
1361
  };
1295
1362
  var flowdoc = (opts = {}) => {
1363
+ const disabled = opts.disabled ?? process.env.NODE_ENV === "production";
1296
1364
  const cwd = process.cwd();
1297
1365
  let outputDir = null;
1298
1366
  let ready = false;
@@ -1321,6 +1389,10 @@ var flowdoc = (opts = {}) => {
1321
1389
  }
1322
1390
  })();
1323
1391
  return async (req, res, next) => {
1392
+ if (disabled) {
1393
+ res.status(403).send("API docs are not available in this environment.");
1394
+ return;
1395
+ }
1324
1396
  await init2;
1325
1397
  if (initError || !outputDir) {
1326
1398
  res.status(500).send(`flowdoc init failed: ${initError}`);
@@ -1330,8 +1402,9 @@ var flowdoc = (opts = {}) => {
1330
1402
  if (urlPath === "/index.html") {
1331
1403
  const brand = "#6366f1";
1332
1404
  const baseUrl = `${req.protocol}://${req.get("host")}`;
1405
+ const docsBase = req.baseUrl || "";
1333
1406
  res.setHeader("Content-Type", "text/html");
1334
- res.send(buildHtml({ baseUrl, brand }));
1407
+ res.send(buildHtml({ baseUrl, brand, docsBase }));
1335
1408
  return;
1336
1409
  }
1337
1410
  const filePath = join5(outputDir, urlPath);
@@ -1344,7 +1417,7 @@ var flowdoc = (opts = {}) => {
1344
1417
  createReadStream2(filePath).pipe(res);
1345
1418
  };
1346
1419
  };
1347
- var buildHtml = ({ baseUrl, brand }) => `<!DOCTYPE html>
1420
+ var buildHtml = ({ baseUrl, brand, docsBase }) => `<!DOCTYPE html>
1348
1421
  <html lang="en" class="dark">
1349
1422
  <head>
1350
1423
  <meta charset="UTF-8" />
@@ -1353,9 +1426,10 @@ var buildHtml = ({ baseUrl, brand }) => `<!DOCTYPE html>
1353
1426
  <script>
1354
1427
  window.__FLOWDOC_BRAND__ = "${brand}";
1355
1428
  window.__FLOWDOC_BASE_URL__ = "${baseUrl}";
1429
+ window.__FLOWDOC_DOCS_BASE__ = "${docsBase}";
1356
1430
  </script>
1357
- <script type="module" crossorigin src="./assets/ui.js"></script>
1358
- <link rel="stylesheet" href="./assets/index.css" />
1431
+ <script type="module" crossorigin src="${docsBase}/assets/ui.js"></script>
1432
+ <link rel="stylesheet" href="${docsBase}/assets/index.css" />
1359
1433
  </head>
1360
1434
  <body><div id="root"></div></body>
1361
1435
  </html>`;
@@ -3,7 +3,7 @@ import {
3
3
  generate,
4
4
  loadConfig,
5
5
  resolveConfig
6
- } from "./chunk-ZJCRRUX7.js";
6
+ } from "./chunk-KSXE5LV4.js";
7
7
 
8
8
  // src/serve.ts
9
9
  import { createServer } from "http";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowdoc-gen",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Auto-generate beautiful API documentation from your Express codebase — no annotations required",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +1 @@
1
- *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.sticky{position:sticky}.top-0{top:0}.mx-auto{margin-left:auto;margin-right:auto}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:.25rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-12{height:3rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-96{max-height:24rem}.w-10{width:2.5rem}.w-12{width:3rem}.w-24{width:6rem}.w-64{width:16rem}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[42px\]{min-width:42px}.min-w-\[52px\]{min-width:52px}.max-w-3xl{max-width:48rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.resize-y{resize:vertical}.grid-cols-\[1fr_auto_1fr\]{grid-template-columns:1fr auto 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-\[var\(--brand\)\]{border-color:var(--brand)}.border-red-900\/50{border-color:#7f1d1d80}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity, 1))}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity, 1))}.bg-\[var\(--brand\)\]{background-color:var(--brand)}.bg-amber-500\/15{background-color:#f59e0b26}.bg-blue-500\/15{background-color:#3b82f626}.bg-emerald-500\/15{background-color:#10b98126}.bg-orange-500\/15{background-color:#f9731626}.bg-purple-500\/15{background-color:#a855f726}.bg-red-500\/15{background-color:#ef444426}.bg-red-950\/40{background-color:#450a0a66}.bg-slate-500\/15{background-color:#64748b26}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity, 1))}.p-3{padding:.75rem}.p-4{padding:1rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.pl-0{padding-left:0}.pl-3{padding-left:.75rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-\[var\(--brand\)\]{color:var(--brand)}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-inset{--tw-ring-inset: inset}.ring-amber-500\/20{--tw-ring-color: rgb(245 158 11 / .2)}.ring-blue-500\/20{--tw-ring-color: rgb(59 130 246 / .2)}.ring-emerald-500\/20{--tw-ring-color: rgb(16 185 129 / .2)}.ring-orange-500\/20{--tw-ring-color: rgb(249 115 22 / .2)}.ring-purple-500\/20{--tw-ring-color: rgb(168 85 247 / .2)}.ring-red-500\/20{--tw-ring-color: rgb(239 68 68 / .2)}.ring-slate-500\/20{--tw-ring-color: rgb(100 116 139 / .2)}.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-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}:root{--brand: #6366f1}*{box-sizing:border-box}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Inter,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#334155;border-radius:2px}.placeholder\:text-slate-600::-moz-placeholder{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.placeholder\:text-slate-600::placeholder{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.hover\:bg-slate-800\/50:hover{background-color:#1e293b80}.hover\:text-slate-200:hover{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.hover\:text-slate-300:hover{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.hover\:opacity-90:hover{opacity:.9}.focus\:border-\[var\(--brand\)\]:focus{border-color:var(--brand)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:opacity-50:disabled{opacity:.5}
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.sticky{position:sticky}.top-0{top:0}.mx-auto{margin-left:auto;margin-right:auto}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:.25rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-12{height:3rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-96{max-height:24rem}.w-10{width:2.5rem}.w-12{width:3rem}.w-24{width:6rem}.w-64{width:16rem}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[42px\]{min-width:42px}.min-w-\[52px\]{min-width:52px}.max-w-3xl{max-width:48rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.resize-y{resize:vertical}.grid-cols-\[1fr_auto_1fr\]{grid-template-columns:1fr auto 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-\[var\(--brand\)\]{border-color:var(--brand)}.border-red-900\/50{border-color:#7f1d1d80}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity, 1))}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity, 1))}.bg-\[var\(--brand\)\]{background-color:var(--brand)}.bg-amber-500\/15{background-color:#f59e0b26}.bg-blue-500\/15{background-color:#3b82f626}.bg-emerald-500\/15{background-color:#10b98126}.bg-orange-500\/15{background-color:#f9731626}.bg-purple-500\/15{background-color:#a855f726}.bg-red-500\/15{background-color:#ef444426}.bg-red-950\/40{background-color:#450a0a66}.bg-slate-500\/15{background-color:#64748b26}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity, 1))}.p-3{padding:.75rem}.p-4{padding:1rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.pl-0{padding-left:0}.pl-1{padding-left:.25rem}.pl-3{padding-left:.75rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-\[var\(--brand\)\]{color:var(--brand)}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-inset{--tw-ring-inset: inset}.ring-amber-500\/20{--tw-ring-color: rgb(245 158 11 / .2)}.ring-blue-500\/20{--tw-ring-color: rgb(59 130 246 / .2)}.ring-emerald-500\/20{--tw-ring-color: rgb(16 185 129 / .2)}.ring-orange-500\/20{--tw-ring-color: rgb(249 115 22 / .2)}.ring-purple-500\/20{--tw-ring-color: rgb(168 85 247 / .2)}.ring-red-500\/20{--tw-ring-color: rgb(239 68 68 / .2)}.ring-slate-500\/20{--tw-ring-color: rgb(100 116 139 / .2)}.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-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}:root{--brand: #6366f1}*{box-sizing:border-box}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Inter,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#334155;border-radius:2px}.placeholder\:text-slate-600::-moz-placeholder{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.placeholder\:text-slate-600::placeholder{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.hover\:bg-slate-800\/50:hover{background-color:#1e293b80}.hover\:text-slate-200:hover{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.hover\:text-slate-300:hover{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.hover\:opacity-90:hover{opacity:.9}.focus\:border-\[var\(--brand\)\]:focus{border-color:var(--brand)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:opacity-50:disabled{opacity:.5}