modscape 1.0.1 → 1.0.3

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.ja.md CHANGED
@@ -21,20 +21,19 @@
21
21
  ## 主な機能
22
22
 
23
23
  - **YAML-as-Code**: データアーキテクチャ全体を単一のYAMLファイルで定義。Gitによる変更管理が可能。
24
- - **統合プロフェッショナル・エディタ**: **CodeMirror 6** を内蔵。シンタックスハイライト対応のエディタをサイドバーで直接利用可能。
25
- - **統合 Undo/Redo & オートセーブ**:
26
- - ビジュアル操作(ドラッグ、リサイズ、編集)がエディタの履歴と同期。 `Ctrl+Z` で直前の操作を元に戻せます。
27
- - **オートセーブ**をONにすれば、キャンバス上の変更が即座にローカルファイルに反映されます。
28
- - **ダーク/ライトモード対応**: 利用環境やドキュメント作成の用途に合わせて、ワンクリックでテーマを切り替え可能。
29
- - **データ分析特化のモデリング**: `fact`, `dimension`, `mart`, `hub`, `link`, `satellite` などのエンティティタイプを標準サポート。
24
+ - **3階層ネーミングシステム**: エンティティを **概念名**(ビジュアル)、**論理名**(ビジネス定義)、**物理名**(実際のテーブル名)の3段階でドキュメント化。
25
+ - **自動レイアウト調整**: インテリジェントな階層型レイアウトエンジンにより、リレーションに基づいてテーブルとドメインを自動的に整列(※モデルの複雑さによっては手動での微調整が必要な場合があります)。
26
+ - **刷新されたモデリング・ノード**: 左上に突き出した「インデックス・タブ」で種類(FACT, DIM, HUB等)を明示。長い物理名は自動省略され、プロフェッショナルな外観を維持。
30
27
  - **インタラクティブなビジュアルキャンバス**:
31
- - **ドラッグで接続**: カラム間のリレーションを直感的に作成。吸着(Snapping)機能で快適な操作感。
28
+ - **ドラッグで接続**: カラム間のリレーションを直感的に作成。吸着機能で快適な操作感。
29
+ - **意味的なエッジバッジ**: 接続点に `( 1 )` や `[ M ]` バッジを表示し、カーディナリティ(多重度)を視覚化。
32
30
  - **データリネージ・モード**: データの流れをアニメーション付きの点線矢印で可視化。
33
31
  - **ドメイン階層ナビゲーション**: テーブルをビジネスドメインごとに整理し、構造化されたサイドバーから素早くアクセス。
34
- - **分析メタデータ**:
35
- - **ファクトテーブル・タイプ**: `transaction`, `periodic`, `accumulating`, `factless` といったデータの性質を定義。
36
- - **SCD管理**: `SCD Type 2` などの履歴管理方式を可視化。
37
- - **加算規則(Additivity)**: カラムが合計可能か(`fully`, `semi`, `non`)を明示 (Σ アイコン)。
32
+ - **統合 Undo/Redo & オートセーブ**:
33
+ - ドラッグや自動整列、編集などの操作が内蔵エディタの履歴と同期。
34
+ - オートセーブにより、ローカルのYAMLを常に最新の状態に維持。
35
+ - **ダーク/ライトモード対応**: 利用環境やドキュメント作成の用途に合わせて、ワンクリックでテーマを切り替え可能。
36
+ - **データ分析特化のモデリング**: `fact`, `dimension`, `mart`, `hub`, `link`, `satellite` に加え、汎用的な `table` タイプを標準サポート。
38
37
  - **AIエージェント対応**: **Gemini, Claude, Codex** 用の雛形を内蔵。LLMを活用してモデリング作業を劇的に加速。
39
38
 
40
39
  ## インストール
@@ -74,25 +73,34 @@ npm install -g modscape
74
73
  domains:
75
74
  - id: core_sales
76
75
  name: 主要売上
77
- color: "#3b82f6"
78
- tables: [orders, products]
76
+ color: "rgba(59, 130, 246, 0.1)"
77
+ tables: [orders]
79
78
 
80
79
  # 2. Tables: エンティティ定義
81
80
  tables:
82
81
  - id: orders
83
- name: ORDERS
82
+ name: 注文 # 概念名(大)
83
+ logical_name: "顧客注文履歴" # 論理名(中)
84
+ physical_name: "fct_retail_sales" # 物理名(小)
84
85
  appearance:
85
- type: fact # fact | dimension | mart | hub | link | satellite
86
+ type: fact # fact | dimension | mart | hub | link | satellite | table
86
87
  sub_type: transaction
87
- scd: type2
88
- icon: 📦
88
+ icon: 💰
89
89
  columns:
90
90
  - id: order_id
91
91
  logical:
92
92
  name: ORDER_ID
93
93
  isPrimaryKey: true
94
- additivity: fully # fully | semi | non (Σ アイコン)
95
- isMetadata: false # 監査用カラム等 (🕒 アイコン)
94
+ additivity: fully
95
+ sampleData:
96
+ - [order_id, amount, status]
97
+ - [1001, 50.0, "COMPLETED"]
98
+
99
+ # 3. Relationships: カーディナリティの定義
100
+ relationships:
101
+ - from: { table: customers, column: customer_id }
102
+ to: { table: orders, column: customer_id }
103
+ type: one-to-many
96
104
  ```
97
105
 
98
106
  ---
@@ -121,11 +129,10 @@ Modscape は以下の素晴らしいオープンソースプロジェクトに
121
129
 
122
130
  - [React Flow](https://reactflow.dev/) - インタラクティブなグラフ UI フレームワーク。
123
131
  - [CodeMirror 6](https://codemirror.net/) - 次世代のウェブベース・コードエディタ。
132
+ - [Dagre](https://github.com/dagrejs/dagre) - 階層型グラフ・レイアウトエンジン。
124
133
  - [Lucide React](https://lucide.dev/) - シンプルで美しいアイコンセット。
125
134
  - [Zustand](https://github.com/pmndrs/zustand) - React 用の状態管理ライブラリ。
126
- - [Express](https://expressjs.com/) - Node.js 用のウェブフレームワーク。
127
135
  - [js-yaml](https://github.com/nodeca/js-yaml) - JavaScript 用 YAML パーサー。
128
- - [Commander.js](https://github.com/tj/commander.js) - CLI フレームワーク。
129
136
 
130
137
  ## ライセンス
131
138
  MIT
package/README.md CHANGED
@@ -21,21 +21,19 @@ In modern data analysis platforms, data modeling is no longer just about drawing
21
21
  ## Key Features
22
22
 
23
23
  - **YAML-as-Code**: Define your entire data architecture in a single, human-readable YAML file. Track changes via Git.
24
- - **Instant Local Visualization**: Visualize your YAML models instantly on your machine. No database connections or cloud infrastructure required—just point to your file and start exploring.
25
- - **Integrated Professional Editor**: Powered by **CodeMirror 6**, providing syntax highlighting and a rich YAML editing experience directly in the sidebar.
26
- - **Unified Undo/Redo & Auto-save**:
27
- - Visual actions (dragging, resizing, metadata edits) are synchronized with the editor's history. Undo your last action with `Ctrl+Z`.
28
- - Optional **Auto-save** ensures your local YAML is always up-to-date with your visual changes.
29
- - **Dark/Light Mode Support**: Switch between themes seamlessly for better eye comfort or documentation exports.
30
- - **Specialized Modeling Types**: Native support for entity types like `fact`, `dimension`, `mart`, `hub`, `link`, and `satellite`.
24
+ - **Tri-Layer Naming System**: Document entities across three levels of abstraction: **Conceptual** (Visual name), **Logical** (Formal business name), and **Physical** (Actual database table name).
25
+ - **Auto-Format Layout**: Automatically arrange tables and domains based on their relationships using an intelligent hierarchical layout engine (Note: manual adjustment may be required for complex models).
26
+ - **Redesigned Modeling Nodes**: Protruding "Index Tabs" for entity types (FACT, DIM, HUB, LINK, etc.) and auto-truncating physical names for a professional look.
31
27
  - **Interactive Visual Canvas**:
32
28
  - **Drag-to-Connect**: Create relationships between columns intuitively with "Magnetic Snapping".
29
+ - **Semantic Edge Badges**: Visually identify cardinality with `( 1 )` and `[ M ]` badges at the connection points.
33
30
  - **Data Lineage Mode**: Visualize data flow with animated dashed arrows.
34
31
  - **Domain-Grouped Navigation**: Organize tables into visual business domains and navigate them via a structured sidebar.
35
- - **Analytics Metadata**:
36
- - **Fact Table Types**: Define `transaction`, `periodic`, `accumulating`, or `factless` grains.
37
- - **SCD Management**: Visualize `SCD Type 2` and other history-tracking dimensions.
38
- - **Additivity Rules**: Mark columns as `fully`, `semi`, or `non-additive` (Σ icon).
32
+ - **Unified Undo/Redo & Auto-save**:
33
+ - Visual actions (dragging, formatting, editing) are synchronized with the built-in CodeMirror editor's history.
34
+ - Optional **Auto-save** ensures your local YAML is always up-to-date.
35
+ - **Dark/Light Mode Support**: Switch between themes seamlessly for better eye comfort or documentation exports.
36
+ - **Specialized Modeling Types**: Native support for entity types like `fact`, `dimension`, `mart`, `hub`, `link`, `satellite`, and generic `table`.
39
37
  - **AI-Agent Ready**: Built-in scaffolding for **Gemini, Claude, and Codex** to accelerate your modeling workflow using LLMs.
40
38
 
41
39
  ## Installation
@@ -90,26 +88,35 @@ Modscape uses a schema designed for data analysis contexts.
90
88
  domains:
91
89
  - id: core_sales
92
90
  name: Core Sales
93
- color: "#3b82f6"
94
- tables: [orders, products]
91
+ color: "rgba(59, 130, 246, 0.1)"
92
+ tables: [orders]
95
93
 
96
- # 2. Tables: Entity definitions with multi-layer metadata
94
+ # 2. Tables: Entity definitions with tri-layer metadata
97
95
  tables:
98
96
  - id: orders
99
- name: ORDERS
97
+ name: Orders # Conceptual (Big)
98
+ logical_name: "Customer Purchase Record" # Logical (Medium)
99
+ physical_name: "fct_retail_sales" # Physical (Small)
100
100
  appearance:
101
- type: fact # fact | dimension | mart | hub | link | satellite
101
+ type: fact # fact | dimension | mart | hub | link | satellite | table
102
102
  sub_type: transaction
103
- scd: type2
104
- icon: 📦
103
+ icon: 💰
105
104
  columns:
106
105
  - id: order_id
107
106
  logical:
108
107
  name: ORDER_ID
109
108
  type: Int
110
109
  isPrimaryKey: true
111
- additivity: fully # fully | semi | non (Σ icon)
112
- isMetadata: false # audit column (🕒 icon)
110
+ additivity: fully
111
+ sampleData:
112
+ - [order_id, amount, status]
113
+ - [1001, 50.0, "COMPLETED"]
114
+
115
+ # 3. Relationships: Define ER cardinality
116
+ relationships:
117
+ - from: { table: customers, column: customer_id }
118
+ to: { table: orders, column: customer_id }
119
+ type: one-to-many
113
120
  ```
114
121
 
115
122
  ---
@@ -138,11 +145,10 @@ Modscape is made possible by these incredible open-source projects:
138
145
 
139
146
  - [React Flow](https://reactflow.dev/) - Interactive node-based UI framework.
140
147
  - [CodeMirror 6](https://codemirror.net/) - Next-generation code editor for the web.
148
+ - [Dagre](https://github.com/dagrejs/dagre) - Directed graph layout engine.
141
149
  - [Lucide React](https://lucide.dev/) - Beautifully simple pixel-perfect icons.
142
150
  - [Zustand](https://github.com/pmndrs/zustand) - Bear necessities for state management.
143
- - [Express](https://expressjs.com/) - Fast, unopinionated web framework for Node.js.
144
151
  - [js-yaml](https://github.com/nodeca/js-yaml) - JavaScript YAML parser and dumper.
145
- - [Commander.js](https://github.com/tj/commander.js) - CLI framework.
146
152
 
147
153
  ## License
148
154
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modscape",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Modscape: A YAML-driven data modeling visualizer CLI",
5
5
  "repository": {
6
6
  "type": "git",
package/src/index.js CHANGED
@@ -15,7 +15,7 @@ const program = new Command();
15
15
  program
16
16
  .name('modscape')
17
17
  .description('Modscape: A YAML-driven data modeling visualizer CLI')
18
- .version('1.0.1');
18
+ .version('1.0.3');
19
19
 
20
20
  program
21
21
  .command('init')
@@ -1,92 +1,120 @@
1
1
  # Data Modeling Rules for this Project
2
2
 
3
3
  ## 1. Modeling Strategy
4
- <!-- Define your modeling methodology here. Example: Data Vault 2.0, Star Schema, 3NF -->
5
4
  - **Dimensional Modeling (Star Schema)**: Recommended for most analytical use cases.
6
5
  - Use `appearance.type: fact` for central measurement tables.
7
6
  - Use `appearance.type: dimension` for descriptive attribute tables.
8
7
  - Use `appearance.type: mart` for downstream reporting-ready tables (Data Marts).
9
8
  - **Data Vault 2.0**: For highly scalable enterprise data warehouses.
10
9
  - Use `appearance.type: hub`, `link`, or `satellite`.
10
+ - **Generic Modeling**: For all other physical tables.
11
+ - Use `appearance.type: table` for raw mirror tables, simple RDB exports, or utility tables.
11
12
 
12
- ## 2. Analytics Metadata
13
- Modscape supports attributes to communicate the "Story" and "Grain" of your data to humans and AI agents.
13
+ ## 2. Table Naming Hierarchy
14
+ Modscape supports three levels of naming to bridge the gap between business and technology.
14
15
 
15
- ### Fact Table Types / Dimension History (`appearance.sub_type` and `appearance.scd`)
16
- Modscape separates a table's core nature from its history tracking method.
16
+ 1. **Conceptual Name (`name`)**: The primary title on the canvas (e.g., "Customers"). Largest font.
17
+ 2. **Logical Name (`logical_name`)**: Formal business name (e.g., "Customer Master"). Hidden on canvas if identical to Conceptual Name.
18
+ 3. **Physical Name (`physical_name`)**: Actual database table name (e.g., `dim_customers`). Defaults to the technical `id` if empty.
17
19
 
20
+ ## 3. Analytics & Visual Metadata
21
+ Modscape supports attributes to communicate the "Story" and "Grain" of your data.
22
+
23
+ ### Grain & History (`appearance.sub_type` and `appearance.scd`)
18
24
  - **`sub_type` (The "What")**:
19
25
  - **For Fact Tables**: `transaction` (atomic event), `periodic` (state interval), `accumulating` (milestones), `factless` (occurrence only).
20
26
  - **For Dimension Tables**: `conformed` (shared), `junk` (flags), `degenerate` (in-fact).
21
27
  - **`scd` (The "How it changes")**:
22
28
  `type0` (fixed), `type1` (overwrite), `type2` (history row), `type3` (history col), `type4` (history table), `type5` (1+4), `type6` (1+2+3), `type7` (1+2).
23
29
 
30
+ ### Visual Semantics (`appearance.icon` and `appearance.color`)
31
+ Categorize entities visually to make the canvas intuitive:
32
+ - **`icon`**: Use a single Emoji (e.g., 🛒, 👥, 📋) to represent the business concept.
33
+ - **`color`**: Use Hex or RGBA codes for specific table highlighting (optional).
34
+
24
35
  ### Data Lineage (`lineage.upstream`)
25
- Explicitly define dependencies to communicate the data flow:
36
+ Define dependencies to communicate data flow:
26
37
  - Use `lineage.upstream: [table_id1, table_id2]` to list source tables.
27
38
 
28
- ### Column Additivity (`logical.additivity`)
29
- - `fully`: Can be summed across all dimensions (e.g., sales amount). Displays as `Σ`.
30
- - `semi`: Summing is valid only across some dimensions (e.g., bank balance - can't sum across time). Displays as `Σ~`.
31
- - `non`: Summing is never valid (e.g., unit price, ratios). Displays as `⊘`.
39
+ ## 4. Sample Data Stories
40
+ **Every table must include realistic sample data** to explain the context without requiring SQL queries.
41
+ - **Format**: A 2D array where the first row is headers.
42
+ - **Rule**: Provide at least 3 rows of high-quality, representative data.
43
+ ```yaml
44
+ sampleData:
45
+ - [header1, header2]
46
+ - [value1, value2]
47
+ ```
32
48
 
33
- ### Metadata Columns (`logical.isMetadata`)
34
- Mark technical or audit columns (like `created_at`, `dbt_updated_at`) with `isMetadata: true` to display a `🕒` icon.
49
+ ## 5. Physical Modeling Rules
50
+ Provide physical mapping metadata to ensure the model is implementation-ready.
35
51
 
36
- ## 3. Naming Conventions
37
- - **Casing**: [Select one: snake_case / UPPER_SNAKE_CASE / camelCase]
38
- - **Prefixes**: (e.g., `f_` for facts, `d_` for dimensions, `m_` for marts)
39
- - **Suffixes**: (e.g., `_id` for primary keys, `_h` for history tables)
52
+ - **Physical Naming**: All names (tables, schemas, columns) must use `snake_case`.
53
+ - **Schema Standards**: `raw`, `staging`, `analytics`, or `mart`.
54
+ - **Data Types**: Use standard SQL types (e.g., `VARCHAR`, `BIGINT`, `NUMBER(38,2)`, `DATE`, `TIMESTAMP_NTZ`).
40
55
 
41
- ## 4. Standard Data Types
42
- - String: Varchar, Text
43
- - Numeric: Integer, Decimal, Float
44
- - Date/Time: Timestamp, Date
56
+ ## 6. Logical Column Rules
57
+ - **Key Flags**: Mark `isPrimaryKey: true`, `isForeignKey: true`, or `isPartitionKey: true` (for performance).
58
+ - **Metadata Columns**: Mark technical columns (e.g., `updated_at`) with `isMetadata: true` (🕒 icon).
59
+ - **Additivity**:
60
+ - `fully`: Can be summed across all dimensions (Σ icon).
61
+ - `semi`: Summing is restricted (e.g., bank balance) (Σ~ icon).
62
+ - `non`: Summing is never valid (e.g., unit price) (⊘ icon).
45
63
 
46
- ## 5. YAML Schema Reference
64
+ ## 7. Relationship Cardinality
65
+ Explicitly define the nature of ER connections using `type`:
66
+ - Options: `one-to-one`, `one-to-many`, `many-to-one`, `many-to-many`.
67
+
68
+ ## 8. Domain Organization
69
+ Group tables into business domains to manage complexity.
70
+ - Assign a unique `color` to each domain container.
71
+ - Use `description` to explain the domain's business purpose.
72
+
73
+ ## 9. YAML Schema Reference (Hero Example)
47
74
  ```yaml
75
+ domains:
76
+ - id: sales
77
+ name: Sales Intelligence
78
+ color: "rgba(59, 130, 246, 0.1)"
79
+ tables: [fct_orders]
80
+
48
81
  tables:
49
82
  - id: fct_orders
50
83
  name: Orders Fact
84
+ logical_name: "Sales Transaction Record"
85
+ physical_name: "fct_orders"
51
86
  appearance:
52
87
  type: fact
53
88
  sub_type: transaction
54
- scd: type2
55
- conceptual:
56
- description: "Records of customer purchases"
89
+ icon: "🛒"
90
+ physical:
91
+ name: fct_orders
92
+ schema: analytics
57
93
  columns:
58
94
  - id: order_id
59
95
  logical: { name: ID, type: Int, isPrimaryKey: true }
96
+ physical: { name: order_id, type: BIGINT }
60
97
  - id: amount
61
- logical: { name: Amount, type: Decimal, additivity: fully }
98
+ logical: { name: Amount, type: Decimal, additivity: fully }
99
+ physical: { name: total_amount, type: NUMBER(18,2) }
100
+ sampleData:
101
+ - [order_id, amount, status]
102
+ - [1001, 50.0, "COMPLETED"]
103
+ - [1002, 120.5, "PENDING"]
62
104
 
63
- - id: dim_customers
64
- name: Customers Dim
65
- appearance:
66
- type: dimension
67
- sub_type: conformed
68
- scd: type2
69
- columns:
70
- - id: customer_id
71
- logical: { name: ID, type: Int, isPrimaryKey: true }
72
-
73
- - id: mart_daily_revenue
74
- name: Daily Revenue Mart
75
- appearance:
76
- type: mart
77
- sub_type: periodic
78
- columns:
79
- - id: report_date
80
- logical: { name: Date, type: Date, isPrimaryKey: true }
81
- - id: daily_amount
82
- logical: { name: Revenue, type: Decimal, additivity: fully }
105
+ relationships:
106
+ - from: { table: dim_customers, column: customer_id }
107
+ to: { table: fct_orders, column: customer_id }
108
+ type: one-to-many
83
109
  ```
84
110
 
85
- ## 6. Layout Management
111
+ ## 10. Layout Management
86
112
  - **Visualizer Priority**: The GUI handles layout through drag-and-drop.
87
113
  - **AI Agent Responsibility**: When creating new entities, assign initial (x, y) coordinates to place them near related tables.
88
114
 
89
- ## 7. The Golden Rules
115
+ ## 11. The Golden Rules
90
116
  - When updating `model.yaml`, always self-audit against these rules.
91
- - Set appropriate `appearance.type`, `sub_type`, and `scd` for new tables.
92
- - Use `additivity` for numeric measures to inform BI tools and analysts.
117
+ - **Utilize the 3-layer naming (Conceptual, Logical, Physical) for clarity.**
118
+ - **Generate realistic `sampleData` for every new entity.**
119
+ - **Provide `physical` mappings to make the model implementation-ready.**
120
+ - Use `isPartitionKey` for large tables to inform performance design.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "visualizer",
3
3
  "private": true,
4
- "version": "1.0.1",
4
+ "version": "1.0.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -19,6 +19,7 @@
19
19
  "class-variance-authority": "^0.7.1",
20
20
  "clsx": "^2.1.1",
21
21
  "codemirror": "^6.0.2",
22
+ "dagre": "^0.8.5",
22
23
  "js-yaml": "^4.1.1",
23
24
  "lucide-react": "^0.575.0",
24
25
  "react": "^19.2.0",
@@ -29,6 +30,7 @@
29
30
  },
30
31
  "devDependencies": {
31
32
  "@eslint/js": "^9.39.1",
33
+ "@types/dagre": "^0.7.54",
32
34
  "@types/js-yaml": "^4.0.9",
33
35
  "@types/node": "^24.10.1",
34
36
  "@types/react": "^19.2.7",
@@ -0,0 +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}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.collapse{visibility:collapse}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-right-3\.5{right:-.875rem}.bottom-1{bottom:.25rem}.bottom-4{bottom:1rem}.left-0\.5{left:.125rem}.left-1\/2{left:50%}.left-3{left:.75rem}.left-4{left:1rem}.right-1{right:.25rem}.right-3{right:.75rem}.right-4{right:1rem}.top-0{top:0}.top-0\.5{top:.125rem}.top-1\/2{top:50%}.top-4{top:1rem}.z-10{z-index:10}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[60\]{z-index:60}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-4{margin-left:1rem}.mr-1{margin-right:.25rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.flex{display:flex}.inline-flex{display:inline-flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:0px}.w-10{width:2.5rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[400px\]{width:400px}.w-\[50px\]{width:50px}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.min-w-\[140px\]{min-width:140px}.min-w-\[60px\]{min-width:60px}.max-w-\[180px\]{max-width:180px}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.origin-top-left{transform-origin:top left}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-3{--tw-translate-x: .75rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-help{cursor:help}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.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-8{gap:2rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-\[4px\]{border-radius:4px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-amber-500\/40{border-color:#f59e0b66}.border-amber-500\/50{border-color:#f59e0b80}.border-blue-100{--tw-border-opacity: 1;border-color:rgb(219 234 254 / var(--tw-border-opacity, 1))}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-blue-500\/20{border-color:#3b82f633}.border-blue-500\/30{border-color:#3b82f64d}.border-blue-500\/40{border-color:#3b82f666}.border-blue-500\/50{border-color:#3b82f680}.border-emerald-200{--tw-border-opacity: 1;border-color:rgb(167 243 208 / var(--tw-border-opacity, 1))}.border-emerald-500\/30{border-color:#10b9814d}.border-emerald-500\/50{border-color:#10b98180}.border-red-500\/50{border-color:#ef444480}.border-slate-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity, 1))}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-slate-300\/50{border-color:#cbd5e180}.border-slate-600\/50{border-color:#47556980}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity, 1))}.border-slate-700\/50{border-color:#33415580}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-\[\#282c34\]{--tw-bg-opacity: 1;background-color:rgb(40 44 52 / var(--tw-bg-opacity, 1))}.bg-amber-500\/20{background-color:#f59e0b33}.bg-amber-500\/30{background-color:#f59e0b4d}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500\/10{background-color:#3b82f61a}.bg-blue-500\/20{background-color:#3b82f633}.bg-blue-500\/30{background-color:#3b82f64d}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-emerald-500\/10{background-color:#10b9811a}.bg-emerald-500\/20{background-color:#10b98133}.bg-emerald-600{--tw-bg-opacity: 1;background-color:rgb(5 150 105 / var(--tw-bg-opacity, 1))}.bg-red-900\/90{background-color:#7f1d1de6}.bg-slate-100{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.bg-slate-200{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.bg-slate-300{--tw-bg-opacity: 1;background-color:rgb(203 213 225 / var(--tw-bg-opacity, 1))}.bg-slate-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.bg-slate-50\/50{background-color:#f8fafc80}.bg-slate-700\/50{background-color:#33415580}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.bg-slate-800\/50{background-color:#1e293b80}.bg-slate-800\/60{background-color:#1e293b99}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-slate-900\/20{background-color:#0f172a33}.bg-slate-900\/50{background-color:#0f172a80}.bg-slate-900\/85{background-color:#0f172ad9}.bg-slate-900\/90{background-color:#0f172ae6}.bg-slate-900\/95{background-color:#0f172af2}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity, 1))}.bg-slate-950\/20{background-color:#02061733}.bg-slate-950\/60{background-color:#02061799}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/60{background-color:#fff9}.bg-white\/80{background-color:#fffc}.bg-white\/90{background-color:#ffffffe6}.bg-white\/95{background-color:#fffffff2}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.p-\[1px\]{padding:1px}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.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-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-4{padding-top:1rem;padding-bottom:1rem}.pb-3{padding-bottom:.75rem}.pl-2{padding-left:.5rem}.pl-9{padding-left:2.25rem}.pr-1{padding-right:.25rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-tighter{letter-spacing:-.05em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-amber-400\/70{color:#fbbf24b3}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-amber-600\/70{color:#d97706b3}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-400\/70{color:#60a5fab3}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-600\/70{color:#2563ebb3}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-emerald-500{--tw-text-opacity: 1;color:rgb(16 185 129 / var(--tw-text-opacity, 1))}.text-red-100{--tw-text-opacity: 1;color:rgb(254 226 226 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-400\/80{color:#f87171cc}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-500\/50{color:#ef444480}.text-red-500\/80{color:#ef4444cc}.text-red-600\/80{color:#dc2626cc}.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-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-slate-800{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity, 1))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-20{opacity:.2}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-blue-500\/20{--tw-shadow-color: rgb(59 130 246 / .2);--tw-shadow: var(--tw-shadow-colored)}.shadow-blue-500\/5{--tw-shadow-color: rgb(59 130 246 / .05);--tw-shadow: var(--tw-shadow-colored)}.shadow-emerald-500\/20{--tw-shadow-color: rgb(16 185 129 / .2);--tw-shadow: var(--tw-shadow-colored)}.shadow-emerald-500\/30{--tw-shadow-color: rgb(16 185 129 / .3);--tw-shadow: var(--tw-shadow-colored)}.shadow-slate-200\/50{--tw-shadow-color: rgb(226 232 240 / .5);--tw-shadow: var(--tw-shadow-colored)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.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)}.backdrop-blur-md{--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur: blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.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}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}:root{--canvas-bg: #f1f5f9;--node-bg: #ffffff;--text-primary: #0f172a;--text-secondary: #64748b;--border-main: #e2e8f0;--header-bg: #f8fafc;--glass-bg: rgba(255, 255, 255, .7);--glass-border: rgba(203, 213, 225, .5)}.dark{--canvas-bg: #020617;--node-bg: #1e293b;--text-primary: #f1f5f9;--text-secondary: #94a3b8;--border-main: #334155;--header-bg: #0f172a;--glass-bg: rgba(15, 23, 42, .85);--glass-border: rgba(51, 65, 85, .5)}*{border-color:var(--border-main)}body{margin:0;padding:0;background-color:var(--canvas-bg);color:var(--text-primary);transition:background-color .3s ease,color .3s ease}#root{height:100vh;width:100vw}.react-flow{background-color:var(--canvas-bg)}.react-flow__pane{cursor:grab}.react-flow__pane:active{cursor:grabbing}.react-flow__background{background-color:var(--canvas-bg)}.react-flow__controls{background:var(--header-bg)!important;border:1px solid var(--border-main)!important;border-radius:8px;box-shadow:0 4px 12px #0000001a}.react-flow__controls-button{background:var(--header-bg)!important;border-bottom:1px solid var(--border-main)!important;fill:var(--text-primary)!important}.react-flow__controls-button:hover{background:var(--border-main)!important}.react-flow__controls-button svg{fill:var(--text-primary)!important}.column-row:hover .column-handle{opacity:1!important;background-color:#3b82f6!important;width:8px!important;height:8px!important;border-radius:50%!important;border:2px solid #ffffff!important;transition:opacity .15s ease-in-out}.column-handle{transition:opacity .15s ease-in-out;pointer-events:auto}@keyframes pulse-handle{0%{transform:scale(1);box-shadow:0 0 #3b82f6b3}70%{transform:scale(1.4);box-shadow:0 0 0 10px #3b82f600}to{transform:scale(1);box-shadow:0 0 #3b82f600}}.handle-pulse{animation:pulse-handle 1.5s infinite;background-color:#3b82f6!important;border:2px solid #ffffff!important;z-index:100!important;opacity:1!important}.handle-dim{opacity:.2!important;transition:opacity .3s ease}.react-flow__handle{transition:transform .2s ease,background-color .2s ease,opacity .3s ease}.react-flow__handle:hover{transform:scale(1.5)}@keyframes node-appear{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.animate-creation{animation:node-appear .4s ease-out forwards}.hover\:bg-blue-50:hover{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-emerald-700:hover{--tw-bg-opacity: 1;background-color:rgb(4 120 87 / var(--tw-bg-opacity, 1))}.hover\:bg-red-500\/20:hover{background-color:#ef444433}.hover\:bg-slate-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-200:hover{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-700:hover{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-800:hover{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-800\/30:hover{background-color:#1e293b4d}.hover\:bg-slate-800\/50:hover{background-color:#1e293b80}.hover\:bg-white:hover{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.hover\:text-blue-400:hover{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.hover\:text-blue-500:hover{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.hover\:text-blue-600:hover{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.hover\:text-emerald-400:hover{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.hover\:text-emerald-600:hover{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity, 1))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\:text-slate-100:hover{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.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\:text-slate-600:hover{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:text-yellow-400:hover{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-50:hover{opacity:.5}.hover\:shadow-sm:hover{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:border-blue-400:focus{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.focus\:bg-blue-50:focus{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.focus\:bg-slate-50:focus{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.focus\:bg-slate-800:focus{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--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)}.focus\:ring-blue-400:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(96 165 250 / var(--tw-ring-opacity, 1))}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-blue-500\/50:focus{--tw-ring-color: rgb(59 130 246 / .5)}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--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(2px + 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)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.active\:scale-95:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:opacity-100{opacity:1}.data-\[state\=inactive\]\:hidden[data-state=inactive]{display:none}.data-\[state\=active\]\:bg-blue-600[data-state=active]{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.data-\[state\=active\]\:bg-white[data-state=active]{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.data-\[state\=active\]\:text-blue-600[data-state=active]{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.data-\[state\=active\]\:text-white[data-state=active]{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}@media(prefers-color-scheme:dark){.dark\:bg-slate-700{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}.dark\:text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.dark\:text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:0}.\[\&\>tr\]\:last\:border-b-0:last-child>tr{border-bottom-width:0px}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-width:0px}.\[\&_tr\]\:border-b tr{border-bottom-width:1px}.react-flow{direction:ltr}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;cursor:grab}.react-flow__pane.selection{cursor:pointer}.react-flow__pane.dragging{cursor:grabbing}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow .react-flow__edges{pointer-events:none;overflow:visible}.react-flow__edge-path,.react-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.react-flow__edge{pointer-events:visibleStroke;cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge:focus .react-flow__edge-path,.react-flow__edge:focus-visible .react-flow__edge-path{stroke:#555}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge-textbg{fill:#fff}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__connectionline{z-index:1001}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:grab}.react-flow__node.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background:#1a192b;border:1px solid white;border-radius:100%}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:-4px;transform:translate(-50%)}.react-flow__handle-top{left:50%;top:-4px;transform:translate(-50%)}.react-flow__handle-left{top:50%;left:-4px;transform:translateY(-50%)}.react-flow__handle-right{right:-4px;top:50%;transform:translateY(-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:#ffffff80;padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-default,.react-flow__node-input,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:3px;width:150px;font-size:12px;color:#222;text-align:center;border-width:1px;border-style:solid;border-color:#1a192b;background-color:#fff}.react-flow__node-default.selectable:hover,.react-flow__node-input.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:0 0 0 .5px #1a192b}.react-flow__node-group{background-color:#f0f0f040}.react-flow__nodesselection-rect,.react-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls{box-shadow:0 0 2px 1px #00000014}.react-flow__controls-button{border:none;background:#fefefe;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:5px}.react-flow__controls-button:hover{background:#f4f4f4}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__minimap{background-color:#fff}.react-flow__minimap svg{display:block}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:#3367d9;transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:#3367d9;border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}