Clarkoer commited on
Commit
3f20b0b
Β·
1 Parent(s): fb2b586

FIX terminal

Browse files
Files changed (4) hide show
  1. GAL_SYSTEM_DOCUMENTATION.md +1398 -0
  2. UI/index.html +6 -5
  3. UI/main.js +20 -16
  4. UI/style.pixel.css +270 -188
GAL_SYSTEM_DOCUMENTATION.md ADDED
@@ -0,0 +1,1398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GAL Compiler β€” Full System Documentation
2
+
3
+ > **GAL (Grow A Language)** is a custom programming language with a garden/nature theme. This document covers the complete compilation pipeline: architecture, lexical analysis, context-free grammar, parsing, semantic analysis, intermediate code generation, interpretation, and the web-based UI.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [System Architecture & Flow](#1-system-architecture--flow)
10
+ 2. [Lexical Analysis (Lexer)](#2-lexical-analysis-lexer)
11
+ 3. [Context-Free Grammar (CFG)](#3-context-free-grammar-cfg)
12
+ 4. [Syntax Analysis (Parser)](#4-syntax-analysis-parser)
13
+ 5. [Semantic Analysis](#5-semantic-analysis)
14
+ 6. [Intermediate Code Generation (ICG)](#6-intermediate-code-generation-icg)
15
+ 7. [Interpreter](#7-interpreter)
16
+ 8. [NLP Fallback / AI Chat Assistant](#8-nlp-fallback--ai-chat-assistant)
17
+ 9. [Web Interface & Server](#9-web-interface--server)
18
+ 10. [General Rules & Constraints](#10-general-rules--constraints)
19
+ 11. [Keyword Reference: GAL ↔ Conventional](#11-keyword-reference-gal--conventional)
20
+ 12. [AST Node Types Reference](#12-ast-node-types-reference)
21
+ 13. [Complete Error Catalogue](#13-complete-error-catalogue)
22
+
23
+ ---
24
+
25
+ ## 1. System Architecture & Flow
26
+
27
+ ### 1.1 High-Level Pipeline
28
+
29
+ The GAL compiler follows a **classic multi-phase compilation pipeline**. Each phase validates its input and passes structured output to the next phase. If any phase encounters an error, the pipeline halts and reports the error to the user.
30
+
31
+ ```
32
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
33
+ β”‚ GAL Source Code (.gal) β”‚
34
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
35
+ β”‚
36
+ β–Ό
37
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
38
+ β”‚ PHASE 1: LEXICAL ANALYSIS (lexer.py) β”‚
39
+ β”‚ β€’ Reads source code character-by-character β”‚
40
+ β”‚ β€’ Groups characters into tokens (keywords, identifiers, literals, etc.) β”‚
41
+ β”‚ β€’ Validates delimiters after each token β”‚
42
+ β”‚ β€’ Detects lexical errors (illegal chars, unclosed strings, etc.) β”‚
43
+ β”‚ Output: Token Stream [Token(type, value, line, col), ...] β”‚
44
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
45
+ β”‚
46
+ β–Ό
47
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
48
+ β”‚ PHASE 2: SYNTAX ANALYSIS (Gal_Parser.py + cfg.py) β”‚
49
+ β”‚ β€’ LL(1) table-driven predictive parser β”‚
50
+ β”‚ β€’ Uses FIRST, FOLLOW, PREDICT sets computed from CFG β”‚
51
+ β”‚ β€’ Validates token sequence against grammar productions β”‚
52
+ β”‚ β€’ Detects syntax errors with helpful, context-aware messages β”‚
53
+ β”‚ β€’ Detects conventional keyword misuse (e.g., "if" β†’ "spring") β”‚
54
+ β”‚ Output: Validated token stream (syntax is correct) β”‚
55
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
56
+ β”‚
57
+ β–Ό
58
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
59
+ β”‚ PHASE 3: SEMANTIC ANALYSIS (GALsemantic.py) β”‚
60
+ β”‚ β€’ Builds Abstract Syntax Tree (AST) from token stream β”‚
61
+ β”‚ β€’ Tree-walking validation of the AST β”‚
62
+ β”‚ β€’ Type checking, scope validation, function signature checking β”‚
63
+ β”‚ β€’ Detects semantic errors (type mismatch, undeclared vars, etc.) β”‚
64
+ β”‚ Output: Validated AST + Symbol Table β”‚
65
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
66
+ β”‚ β”‚
67
+ β–Ό β–Ό
68
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
69
+ β”‚ PHASE 4A: ICG (icg.py) β”‚ β”‚ PHASE 4B: INTERPRETER β”‚
70
+ β”‚ β€’ Three-Address Code β”‚ β”‚ (GALinterpreter.py) β”‚
71
+ β”‚ β€’ Quad format β”‚ β”‚ β€’ Tree-walk interpreter β”‚
72
+ β”‚ β€’ (op, arg1, arg2, res) β”‚ β”‚ β€’ Executes AST nodes β”‚
73
+ β”‚ Output: TAC instructions β”‚ β”‚ β€’ Async I/O via WebSocket β”‚
74
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Output: Program output β”‚
75
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
76
+ ```
77
+
78
+ ### 1.2 Module Inventory
79
+
80
+ | Module | File | Approx. Lines | Responsibility |
81
+ |--------|------|---------------|----------------|
82
+ | Lexer | `Backend/lexer.py` | ~2000 | Hand-written character-by-character tokenizer |
83
+ | CFG | `Backend/cfg.py` | ~850 | Context-free grammar definition + FIRST/FOLLOW/PREDICT computation |
84
+ | Parser | `Backend/Gal_Parser.py` | ~1600 | LL(1) table-driven parser with rich error messages |
85
+ | Semantic Analyzer | `Backend/GALsemantic.py` | ~4000 | AST construction + tree-walking semantic validation |
86
+ | Interpreter | `Backend/GALinterpreter.py` | ~1600 | Tree-walk interpreter with async I/O (Socket.IO) |
87
+ | ICG | `Backend/icg.py` | ~1200 | Three-address code generator from token stream |
88
+ | Server | `Backend/server.py` | ~600 | Flask + Socket.IO web server with REST API endpoints |
89
+ | NLP Fallback | `Backend/gal_fallback.py` | ~800 | Regex + semantic-search AI chat assistant for error help |
90
+ | Frontend | `UI/index.html`, `UI/style.pixel.css`, `UI/main.js` | β€” | Web-based code editor UI |
91
+
92
+ ### 1.3 How the Phases Connect (Server Flow)
93
+
94
+ When the user clicks **"Run"** in the web UI:
95
+
96
+ 1. The frontend sends the source code to the server via **Socket.IO** (`run_code` event) or **REST API** (`/api/run`).
97
+ 2. **Lexer** tokenizes the code β†’ if errors, stop and return lexical errors.
98
+ 3. **Parser** validates syntax using LL(1) table β†’ if errors, stop and return syntax errors.
99
+ 4. **Semantic Analyzer** builds AST and validates types/scopes β†’ if errors, stop and return semantic errors.
100
+ 5. **Interpreter** executes the AST β†’ streams output back via Socket.IO events.
101
+ 6. If `water()` (input) is called, the server emits `input_required` and waits for user response.
102
+
103
+ For **ICG** (intermediate code generation), the pipeline is:
104
+ Lexer β†’ Parser β†’ Semantic β†’ ICG (generates three-address code from tokens in parallel).
105
+
106
+ ---
107
+
108
+ ## 2. Lexical Analysis (Lexer)
109
+
110
+ **File:** `Backend/lexer.py` (~2000 lines)
111
+
112
+ ### 2.1 How It Works
113
+
114
+ The lexer is a **hand-written, character-by-character scanner** (not using regex or generator tools like Lex/Flex). It works as follows:
115
+
116
+ 1. The source code string is scanned one character at a time using a `Position` tracker (index, line, column).
117
+ 2. For each character, the lexer determines what token it could start:
118
+ - **Letters** β†’ could be a keyword or identifier
119
+ - **Digits** β†’ integer or double literal
120
+ - **Quote characters** β†’ string or character literal
121
+ - **Operator characters** β†’ single or multi-character operator
122
+ - **Whitespace** β†’ skipped (except newlines, which are emitted as `\n` tokens)
123
+ 3. Keywords are recognized by **character-by-character matching** (e.g., `s` β†’ `se` β†’ `see` β†’ `seed` β†’ check delimiter). If the full keyword is matched and followed by a valid delimiter, it's emitted as a keyword token. Otherwise, it falls through to identifier parsing.
124
+ 4. After each token is recognized, the lexer checks the **delimiter** β€” the character immediately following the token β€” to ensure it's valid for that token type. Invalid delimiters produce a lexical error.
125
+
126
+ ### 2.2 Character Sets
127
+
128
+ | Constant | Characters | Purpose |
129
+ |----------|-----------|---------|
130
+ | `ZERO` | `0` | Special case for leading zeros |
131
+ | `DIGIT` | `123456789` | Non-zero digits |
132
+ | `ZERODIGIT` | `0123456789` | All digits |
133
+ | `LOW_ALPHA` | `a-z` | Lowercase letters |
134
+ | `UPPER_ALPHA` | `A-Z` | Uppercase letters |
135
+ | `ALPHA` | `a-zA-Z` | All letters |
136
+ | `ALPHANUM` | `a-zA-Z0-9_` | Valid identifier characters |
137
+
138
+ ### 2.3 Reserved Keywords (24 keywords)
139
+
140
+ GAL uses a garden/nature theme for all its keywords:
141
+
142
+ | GAL Keyword | Conventional Equivalent | Category | Description |
143
+ |-------------|------------------------|----------|-------------|
144
+ | `seed` | `int` | Data Type | Integer type |
145
+ | `tree` | `float` / `double` | Data Type | Floating-point type |
146
+ | `leaf` | `char` | Data Type | Character type |
147
+ | `branch` | `bool` / `boolean` | Data Type | Boolean type |
148
+ | `vine` | `string` | Data Type | String type |
149
+ | `empty` | `void` | Return Type | Void return type |
150
+ | `fertile` | `const` | Modifier | Constant declaration |
151
+ | `bundle` | `struct` | Aggregate | Struct/record type |
152
+ | `root` | `main` | Entry Point | Main function |
153
+ | `pollinate` | `function` | Function | Function declaration keyword |
154
+ | `spring` | `if` | Conditional | If statement |
155
+ | `bud` | `else if` / `elif` | Conditional | Else-if statement |
156
+ | `wither` | `else` | Conditional | Else statement |
157
+ | `grow` | `while` | Loop | While loop |
158
+ | `cultivate` | `for` | Loop | For loop |
159
+ | `tend` | `do` | Loop | Do-while loop start |
160
+ | `harvest` | `switch` | Switch | Switch statement |
161
+ | `variety` | `case` | Switch | Case label |
162
+ | `soil` | `default` | Switch | Default case |
163
+ | `plant` | `print` / `printf` | I/O | Output function |
164
+ | `water` | `input` / `scanf` | I/O | Input function |
165
+ | `prune` | `break` | Control Flow | Break out of loop/switch |
166
+ | `skip` | `continue` | Control Flow | Skip to next iteration |
167
+ | `reclaim` | `return` | Control Flow | Return from function |
168
+
169
+ ### 2.4 Boolean Literals
170
+
171
+ | Token | Meaning |
172
+ |-------|---------|
173
+ | `sunshine` | Boolean `true` |
174
+ | `frost` | Boolean `false` |
175
+
176
+ ### 2.5 Token Types
177
+
178
+ The lexer produces the following token categories:
179
+
180
+ #### 2.5.1 Literal Tokens
181
+
182
+ | Token Type | Description | Example |
183
+ |------------|-------------|---------|
184
+ | `intlit` | Integer literal (max 8 digits) | `42`, `100` |
185
+ | `dbllit` | Double/float literal (max 8.8 digits) | `3.14`, `2.5` |
186
+ | `stringlit` | String literal (double-quoted) | `"hello world"` |
187
+ | `chrlit` | Character literal (single-quoted, exactly 1 char) | `'a'`, `'Z'` |
188
+ | `~intlit` | Negated integer literal | `~5` β†’ `-5` |
189
+ | `~dbllit` | Negated double literal | `~3.14` β†’ `-3.14` |
190
+ | `sunshine` | Boolean true literal | `sunshine` |
191
+ | `frost` | Boolean false literal | `frost` |
192
+
193
+ #### 2.5.2 Operator Tokens
194
+
195
+ | Token | Type | Description |
196
+ |-------|------|-------------|
197
+ | `+` | Arithmetic | Addition |
198
+ | `-` | Arithmetic | Subtraction |
199
+ | `*` | Arithmetic | Multiplication |
200
+ | `/` | Arithmetic | Division |
201
+ | `%` | Arithmetic | Modulo (remainder) |
202
+ | `**` | Arithmetic | Exponentiation |
203
+ | `~` | Unary | Negation (replaces unary minus) |
204
+ | `++` | Unary | Increment |
205
+ | `--` | Unary | Decrement |
206
+ | `=` | Assignment | Simple assignment |
207
+ | `+=` | Assignment | Add and assign |
208
+ | `-=` | Assignment | Subtract and assign |
209
+ | `*=` | Assignment | Multiply and assign |
210
+ | `/=` | Assignment | Divide and assign |
211
+ | `%=` | Assignment | Modulo and assign |
212
+ | `==` | Comparison | Equality |
213
+ | `!=` | Comparison | Not equal |
214
+ | `<` | Comparison | Less than |
215
+ | `>` | Comparison | Greater than |
216
+ | `<=` | Comparison | Less than or equal |
217
+ | `>=` | Comparison | Greater than or equal |
218
+ | `&&` | Logical | Logical AND |
219
+ | `\|\|` | Logical | Logical OR |
220
+ | `!` | Logical | Logical NOT |
221
+ | `` ` `` | String | Concatenation operator |
222
+ | `.` | Access | Struct member access |
223
+
224
+ #### 2.5.3 Delimiter Tokens
225
+
226
+ | Token | Description |
227
+ |-------|-------------|
228
+ | `(` | Left parenthesis |
229
+ | `)` | Right parenthesis |
230
+ | `{` | Left brace (block start) |
231
+ | `}` | Right brace (block end) |
232
+ | `[` | Left bracket (array indexing) |
233
+ | `]` | Right bracket |
234
+ | `;` | Semicolon (statement terminator) |
235
+ | `,` | Comma (separator) |
236
+ | `:` | Colon (switch cases) |
237
+
238
+ #### 2.5.4 Special Tokens
239
+
240
+ | Token | Description |
241
+ |-------|-------------|
242
+ | `\n` | Newline (skipped by parser) |
243
+ | `EOF` | End of file marker |
244
+ | `id` | Identifier (variable/function name) |
245
+
246
+ ### 2.6 Delimiter Validation
247
+
248
+ A key feature of the GAL lexer is **post-token delimiter checking**. After recognizing each token, the lexer verifies that the character immediately following the token is valid for that token type. For example:
249
+
250
+ - After `seed` (data type keyword): must be followed by a space, tab, or newline
251
+ - After `bud` (else-if): must be followed by whitespace, `:`, or `(`
252
+ - After an identifier: must be followed by a space, comma, semicolon, operator, bracket, etc.
253
+ - After an integer literal: must be followed by a semicolon, operator, closing bracket, etc.
254
+
255
+ This catches errors like `seedx` (missing space) or `42abc` (identifier starting with a digit).
256
+
257
+ ### 2.7 Escape Sequences in Strings
258
+
259
+ | Sequence | Meaning |
260
+ |----------|---------|
261
+ | `\n` | Newline |
262
+ | `\t` | Tab |
263
+ | `\\` | Backslash |
264
+ | `\"` | Double quote |
265
+ | `\{` | Literal left brace |
266
+ | `\}` | Literal right brace |
267
+
268
+ ### 2.8 Comments
269
+
270
+ | Syntax | Type |
271
+ |--------|------|
272
+ | `// ...` | Single-line comment (until end of line) |
273
+ | `/* ... */` | Multi-line comment (must be closed) |
274
+
275
+ ### 2.9 Identifier Rules
276
+
277
+ - Must start with a **letter** (a-z, A-Z)
278
+ - Can contain letters, digits (0-9), and underscore (`_`)
279
+ - **Maximum length: 15 characters**
280
+ - Cannot be a reserved keyword
281
+ - Cannot start with a digit
282
+
283
+ ### 2.10 Number Literal Rules
284
+
285
+ - **Integers**: Maximum 8 digits. No leading zeros (except `0` itself).
286
+ - **Doubles/Floats**: Integer part max 8 digits + decimal point + fractional part max 8 digits.
287
+ - **Negative numbers**: Use the `~` prefix (e.g., `~5` for -5, `~3.14` for -3.14).
288
+
289
+ ### 2.11 Lexer Error Messages
290
+
291
+ | Error | Trigger |
292
+ |-------|---------|
293
+ | `"Invalid delimiter 'X' after 'Y'"` | Unexpected character following a token |
294
+ | `"Identifier exceeds maximum length of 15 characters"` | Identifier > 15 chars |
295
+ | `"Integer exceeds maximum of 8 digits"` | Integer portion > 8 digits |
296
+ | `"Fractional part exceeds maximum of 8 digits"` | Decimal portion > 8 digits |
297
+ | `"Missing closing '"' for string literal"` | Unclosed string |
298
+ | `"Missing closing quote for character literal"` | Unclosed char literal |
299
+ | `"Character literal must contain exactly one character"` | Multi-char in `'...'` |
300
+ | `"Illegal Character 'X'"` | Unknown/disallowed character |
301
+ | `"Identifiers cannot start with a number"` | e.g., `1abc` |
302
+ | `"Invalid escape sequence"` | `\x` where `x` is not recognized |
303
+ | `"Missing closing '*/' for multi-line comment"` | Unclosed `/* */` |
304
+
305
+ ---
306
+
307
+ ## 3. Context-Free Grammar (CFG)
308
+
309
+ **File:** `Backend/cfg.py` (~850 lines)
310
+
311
+ ### 3.1 What Is the CFG?
312
+
313
+ The **Context-Free Grammar (CFG)** formally defines the syntax of the GAL language. It specifies which arrangements of tokens are valid programs. The CFG is used to build the **LL(1) parsing table** that drives the parser.
314
+
315
+ ### 3.2 Notation
316
+
317
+ - **Non-terminals**: Enclosed in angle brackets, e.g., `<program>`, `<expression>`
318
+ - **Terminals**: Actual token types from the lexer, e.g., `seed`, `id`, `{`, `;`
319
+ - **Ξ» (lambda/epsilon)**: Represents an empty production (the rule can produce nothing)
320
+ - **|**: Separates alternatives (multiple productions for the same non-terminal)
321
+
322
+ ### 3.3 FIRST, FOLLOW, and PREDICT Sets
323
+
324
+ The CFG module computes three critical sets used for LL(1) parsing:
325
+
326
+ **FIRST(X)** β€” the set of terminals that can appear at the **beginning** of any string derived from non-terminal X.
327
+ - Example: `FIRST(<data_type>) = {seed, tree, leaf, branch, vine}`
328
+
329
+ **FOLLOW(X)** β€” the set of terminals that can appear **immediately after** non-terminal X in any derivation.
330
+ - Example: `FOLLOW(<statement>) includes }, EOF`
331
+
332
+ **PREDICT(A β†’ Ξ±)** β€” the set of terminals that indicate which production to use:
333
+ - If Ξ± does NOT derive Ξ»: `PREDICT = FIRST(Ξ±)`
334
+ - If Ξ± CAN derive Ξ»: `PREDICT = FIRST(Ξ±) βˆͺ FOLLOW(A)`
335
+
336
+ These sets are computed automatically by `compute_first()`, `compute_follow()`, and `compute_predict()` in `cfg.py`.
337
+
338
+ ### 3.4 Complete Grammar Productions
339
+
340
+ #### 3.4.1 Program Structure
341
+
342
+ ```
343
+ <program> β†’ <global_declaration> <function_definition> root ( ) { <statement> }
344
+ ```
345
+
346
+ Every GAL program must have a `root()` function as its entry point (equivalent to `main()` in C).
347
+
348
+ #### 3.4.2 Global Declarations
349
+
350
+ ```
351
+ <global_declaration> β†’ bundle id <bundle_or_var> <global_declaration>
352
+ | <data_type> id <array_dec> <var_value> ; <global_declaration>
353
+ | fertile <data_type> id = <init_val> <const_next> ; <global_declaration>
354
+ | Ξ»
355
+ ```
356
+
357
+ #### 3.4.3 Data Types
358
+
359
+ ```
360
+ <data_type> β†’ seed | tree | leaf | branch | vine
361
+ ```
362
+
363
+ #### 3.4.4 Variable Declarations
364
+
365
+ ```
366
+ <var_dec> β†’ <data_type> id <array_dec> <var_value>
367
+ | bundle id <bundle_mem_dec>
368
+
369
+ <var_value> β†’ = <init_val> <var_value_next>
370
+ | <var_value_next>
371
+
372
+ <var_value_next> β†’ , id <array_dec> <var_value>
373
+ | Ξ»
374
+
375
+ <init_val> β†’ <array_init_opt>
376
+ | water ( <water_arg> )
377
+ | <expression>
378
+ ```
379
+
380
+ Multiple variables can be declared in one statement: `seed x = 1, y = 2, z;`
381
+
382
+ #### 3.4.5 Constant Declarations
383
+
384
+ ```
385
+ <const_dec> β†’ fertile <data_type> id = <init_val> <const_next>
386
+
387
+ <const_next> β†’ , id = <init_val> <const_next>
388
+ | Ξ»
389
+ ```
390
+
391
+ Example: `fertile seed MAX = 100;`
392
+
393
+ #### 3.4.6 Array Declarations
394
+
395
+ ```
396
+ <array_dec> β†’ [ <array_dim_opt> ] <array_dec>
397
+ | Ξ»
398
+
399
+ <array_dim_opt> β†’ intlit | Ξ»
400
+ ```
401
+
402
+ Supports multi-dimensional arrays: `seed matrix[2][3];`
403
+
404
+ #### 3.4.7 Array Initialization
405
+
406
+ ```
407
+ <array_init_opt> β†’ { <init_vals> }
408
+ | Ξ»
409
+
410
+ <init_vals> β†’ <init_val_item> <init_vals_next>
411
+ | Ξ»
412
+
413
+ <init_val_item> β†’ { <init_vals> }
414
+ | <expression>
415
+ ```
416
+
417
+ Example: `seed arr[] = {1, 2, 3};` or nested: `seed m[][] = {{1, 2}, {3, 4}};`
418
+
419
+ #### 3.4.8 Bundle (Struct) Definition
420
+
421
+ ```
422
+ <bundle_declaration> β†’ bundle id { <bundle_members> }
423
+
424
+ <bundle_members> β†’ <data_type> id ; <bundle_members>
425
+ | id id ; <bundle_members>
426
+ | Ξ»
427
+ ```
428
+
429
+ Example:
430
+ ```
431
+ bundle Person {
432
+ seed age;
433
+ vine name;
434
+ };
435
+ ```
436
+
437
+ #### 3.4.9 Function Definition
438
+
439
+ ```
440
+ <function_definition> β†’ pollinate <return_type> id ( <parameters> ) { <statement> } <function_definition>
441
+ | Ξ»
442
+
443
+ <return_type> β†’ <data_type> | empty | id
444
+
445
+ <parameters> β†’ <param> <param_next> | Ξ»
446
+
447
+ <param> β†’ <data_type> id | id id
448
+
449
+ <param_next> β†’ , <param> <param_next> | Ξ»
450
+ ```
451
+
452
+ Example:
453
+ ```
454
+ pollinate seed add(seed a, seed b) {
455
+ reclaim a + b;
456
+ }
457
+ ```
458
+
459
+ #### 3.4.10 Statements
460
+
461
+ ```
462
+ <statement> β†’ <simple_stmt> <statement>
463
+ | Ξ»
464
+
465
+ <simple_stmt> β†’ id <id_stmt>
466
+ | <inc_dec_op> id ;
467
+ | <io_stmt>
468
+ | <conditional_stmt>
469
+ | <loop_stmt>
470
+ | <switch_stmt>
471
+ | <control_stmt>
472
+ | reclaim <reclaim_value>
473
+ | <var_dec> ;
474
+ | <const_dec> ;
475
+ ```
476
+
477
+ Declarations and statements can be **interleaved** (like C99), not just at the beginning of a block.
478
+
479
+ #### 3.4.11 Assignment Statements
480
+
481
+ ```
482
+ <id_stmt> β†’ <id_next> <assign_op> <assign_rhs> ;
483
+ | <inc_dec_op> ;
484
+ | ( <arguments> ) ;
485
+
486
+ <assign_op> β†’ = | += | -= | *= | /= | %=
487
+
488
+ <assign_rhs> β†’ water ( <water_arg> )
489
+ | <expression>
490
+ ```
491
+
492
+ #### 3.4.12 I/O Statements
493
+
494
+ ```
495
+ <io_stmt> β†’ plant ( <arguments> ) ;
496
+ | water ( <water_arg> ) ;
497
+
498
+ <water_arg> β†’ <data_type> | id <water_id_tail> | Ξ»
499
+ ```
500
+
501
+ - `plant("Hello {}!", name)` β€” output with placeholder substitution
502
+ - `water(seed)` β€” read integer input
503
+ - `water(name)` β€” read input into variable
504
+
505
+ #### 3.4.13 Conditional Statements (if / else-if / else)
506
+
507
+ ```
508
+ <conditional_stmt> β†’ spring ( <expression> ) { <statement> } <elseif_chain> <else_opt>
509
+
510
+ <elseif_chain> β†’ bud ( <expression> ) { <statement> } <elseif_chain>
511
+ | Ξ»
512
+
513
+ <else_opt> β†’ wither { <statement> }
514
+ | Ξ»
515
+ ```
516
+
517
+ Example:
518
+ ```
519
+ spring (x > 0) {
520
+ plant("positive");
521
+ }
522
+ bud (x < 0) {
523
+ plant("negative");
524
+ }
525
+ wither {
526
+ plant("zero");
527
+ }
528
+ ```
529
+
530
+ #### 3.4.14 Loop Statements
531
+
532
+ **While loop:**
533
+ ```
534
+ grow ( <expression> ) { <statement> }
535
+ ```
536
+
537
+ **For loop:**
538
+ ```
539
+ cultivate ( <for_init> ; <expression> ; <for_update> ) { <statement> }
540
+
541
+ <for_init> β†’ <data_type> id <array_dec> <var_value>
542
+ | id <id_next> <assign_op> <expression>
543
+ | Ξ»
544
+
545
+ <for_update> β†’ id <for_update_type> | Ξ»
546
+
547
+ <for_update_type> β†’ <inc_dec_op>
548
+ | <id_next> <assign_op> <expression>
549
+ ```
550
+
551
+ **Do-while loop:**
552
+ ```
553
+ tend { <statement> } grow ( <expression> ) ;
554
+ ```
555
+
556
+ Examples:
557
+ ```
558
+ grow (i < 10) { plant("{}",i); i++; }
559
+
560
+ cultivate (seed i = 0; i < 10; i++) { plant("{}",i); }
561
+
562
+ tend { plant("at least once"); } grow (frost);
563
+ ```
564
+
565
+ #### 3.4.15 Switch Statement
566
+
567
+ ```
568
+ <switch_stmt> β†’ harvest ( <expression> ) { <case_list> <default_opt> }
569
+
570
+ <case_list> β†’ variety <case_literal> : <case_statements> <case_list>
571
+ | Ξ»
572
+
573
+ <case_literal> β†’ intlit | chrlit | stringlit | sunshine | frost
574
+
575
+ <default_opt> β†’ soil : <case_statements> | Ξ»
576
+ ```
577
+
578
+ Example:
579
+ ```
580
+ harvest (choice) {
581
+ variety 1: plant("One"); prune;
582
+ variety 2: plant("Two"); prune;
583
+ soil: plant("Default");
584
+ }
585
+ ```
586
+
587
+ #### 3.4.16 Expressions (Operator Precedence β€” lowest to highest)
588
+
589
+ ```
590
+ Level 1 (lowest): <expression> β†’ <logic_or>
591
+ Level 2: <logic_or> β†’ <logic_and> ( || <logic_and> )*
592
+ Level 3: <logic_and> β†’ <relational> ( && <relational> )*
593
+ Level 4: <relational> β†’ <arithmetic> ( <rel_op> <arithmetic> )?
594
+ Level 5: <arithmetic> β†’ <term> ( (+|-|`) <term> )*
595
+ Level 6: <term> β†’ <factor> ( (*|/|%) <factor> )*
596
+ Level 7 (highest): <factor> β†’ ( <paren_expr> ) | <unary_op> <factor>
597
+ | id <factor_id_next> | <literal>
598
+ ```
599
+
600
+ **Operator Precedence Table:**
601
+
602
+ | Precedence | Operators | Associativity | Description |
603
+ |------------|-----------|---------------|-------------|
604
+ | 1 (lowest) | `\|\|` | Left-to-right | Logical OR |
605
+ | 2 | `&&` | Left-to-right | Logical AND |
606
+ | 3 | `==` `!=` `<` `>` `<=` `>=` | Non-associative | Relational |
607
+ | 4 | `+` `-` `` ` `` | Left-to-right | Addition, subtraction, concatenation |
608
+ | 5 | `*` `/` `%` | Left-to-right | Multiplication, division, modulo |
609
+ | 6 (highest) | `~` `!` `++` `--` | Right-to-left (unary) | Negation, NOT, increment, decrement |
610
+
611
+ #### 3.4.17 Type Casting
612
+
613
+ ```
614
+ <paren_expr> β†’ <data_type> ) <factor> // Cast: (seed)x, (tree)3.14
615
+ | <expression> ) // Grouped: (x + 1)
616
+ ```
617
+
618
+ #### 3.4.18 Control Flow
619
+
620
+ ```
621
+ <control_stmt> β†’ prune ; // break
622
+ | skip ; // continue
623
+ ```
624
+
625
+ #### 3.4.19 Return Statement
626
+
627
+ ```
628
+ <reclaim_value> β†’ <expression> ; // Return with value: reclaim x + y;
629
+ | ; // Return without value: reclaim;
630
+ ```
631
+
632
+ ---
633
+
634
+ ## 4. Syntax Analysis (Parser)
635
+
636
+ **File:** `Backend/Gal_Parser.py` (~1600 lines)
637
+
638
+ ### 4.1 Parser Type
639
+
640
+ The GAL parser is an **LL(1) table-driven predictive parser**. This means:
641
+
642
+ - **LL**: reads input **L**eft-to-right, produces **L**eftmost derivation
643
+ - **(1)**: uses **1** token of lookahead to decide which production to apply
644
+ - **Table-driven**: uses a pre-computed parsing table (not recursive descent)
645
+
646
+ ### 4.2 How It Works
647
+
648
+ 1. **Parsing Table Construction**: At startup, the `LL1Parser` class builds a parsing table from the PREDICT sets computed in `cfg.py`. For each non-terminal and lookahead terminal, the table stores which production to use.
649
+
650
+ 2. **Stack-Based Parsing**: The parser maintains a stack initialized with `[<program>, EOF]`. At each step:
651
+ - If the top of stack is a **terminal**: match it against the current input token, then pop and advance.
652
+ - If the top of stack is a **non-terminal**: look up the parsing table using the non-terminal and the current input token to find the production, then replace the non-terminal on the stack with the production's right-hand side.
653
+ - If the top is **Ξ» (epsilon)**: simply pop it (empty production).
654
+
655
+ 3. **Token Normalization**: The parser normalizes token types to match CFG terminal names (e.g., `dbllit` β†’ `dblit`). Newline tokens (`\n`) are automatically skipped.
656
+
657
+ 4. **Error Detection**: If no matching production exists in the parsing table for a given (non-terminal, lookahead) pair, a syntax error is reported. The parser **does not perform error recovery** β€” it stops at the first error.
658
+
659
+ ### 4.3 After Parsing: AST Construction
660
+
661
+ After the LL(1) syntax validation succeeds, the parser calls `GALsemantic.build_ast()` to construct the Abstract Syntax Tree. The method `parse_and_build()` combines:
662
+ 1. LL(1) syntax validation
663
+ 2. AST construction
664
+ 3. Returns `{success, ast, symbol_table, errors}`
665
+
666
+ ### 4.4 Context-Aware Error Messages
667
+
668
+ The parser provides **smart, context-aware error messages** rather than generic "unexpected token" errors:
669
+
670
+ #### 4.4.1 Conventional Keyword Detection
671
+
672
+ If a user accidentally uses a keyword from another language, the parser recognizes it and suggests the GAL equivalent:
673
+
674
+ | Mistaken Keyword | Suggestion |
675
+ |------------------|-----------|
676
+ | `if` | `'if' is not a GAL keyword. Use 'spring' instead.` |
677
+ | `else` | `'else' is not a GAL keyword. Use 'wither' instead.` |
678
+ | `elif` | `'elif' is not a GAL keyword. Use 'bud' instead.` |
679
+ | `while` | `'while' is not a GAL keyword. Use 'grow' instead.` |
680
+ | `for` | `'for' is not a GAL keyword. Use 'cultivate' instead.` |
681
+ | `switch` | `'switch' is not a GAL keyword. Use 'harvest' instead.` |
682
+ | `case` | `'case' is not a GAL keyword. Use 'variety' instead.` |
683
+ | `default` | `'default' is not a GAL keyword. Use 'soil' instead.` |
684
+ | `break` | `'break' is not a GAL keyword. Use 'prune' instead.` |
685
+ | `continue` | `'continue' is not a GAL keyword. Use 'skip' instead.` |
686
+ | `return` | `'return' is not a GAL keyword. Use 'reclaim' instead.` |
687
+ | `int` | `'int' is not a GAL keyword. Use 'seed' instead.` |
688
+ | `float` / `double` | Use `'tree'` instead |
689
+ | `char` | Use `'leaf'` instead |
690
+ | `bool` / `boolean` | Use `'branch'` instead |
691
+ | `string` | Use `'vine'` instead |
692
+ | `void` | Use `'empty'` instead |
693
+ | `const` | Use `'fertile'` instead |
694
+ | `struct` | Use `'bundle'` instead |
695
+ | `function` | Use `'pollinate'` instead |
696
+ | `printf` / `print` | Use `'plant'` instead |
697
+ | `scanf` / `input` | Use `'water'` instead |
698
+
699
+ #### 4.4.2 Invalid Operator Detection
700
+
701
+ | Pattern | Error Message |
702
+ |---------|---------------|
703
+ | `===` used | `'===' is not valid in GAL. Use '==' for comparison.` |
704
+ | Single `&` | `'&' is not valid in GAL. Use '&&' for logical AND.` |
705
+ | Single `\|` | `'\|' is not valid in GAL. Use '\|\|' for logical OR.` |
706
+
707
+ #### 4.4.3 Structural Error Detection
708
+
709
+ | Situation | Error Message |
710
+ |-----------|---------------|
711
+ | Empty block `{}` | `Empty block. Expected at least one statement inside braces.` |
712
+ | Code after `reclaim` | `Unreachable code after 'reclaim'. Statements after a return will never execute.` |
713
+ | Chained `++`/`--` | `Increment/decrement operators cannot be chained.` |
714
+ | Missing return type after `pollinate` | `Missing return type after 'pollinate'.` |
715
+ | Missing parameter type | `Missing type for parameter '{name}'.` |
716
+ | Code after program end | `Unexpected token after program end. All code must be inside functions or global declarations.` |
717
+
718
+ ### 4.5 Syntax Error Format
719
+
720
+ ```
721
+ SYNTAX error line {line} col {col} {message}. Expected: {expected_set}
722
+ ```
723
+
724
+ ---
725
+
726
+ ## 5. Semantic Analysis
727
+
728
+ **File:** `Backend/GALsemantic.py` (~4000 lines)
729
+
730
+ ### 5.1 Overview
731
+
732
+ Semantic analysis is the **third phase** of compilation. It ensures the program is **meaningful** β€” not just syntactically correct. The semantic analyzer:
733
+
734
+ 1. **Builds the Abstract Syntax Tree (AST)** from the validated token stream
735
+ 2. **Tree-walks the AST** to check types, scopes, function signatures, and other rules
736
+ 3. Maintains a **Symbol Table** to track variables, functions, and scopes
737
+
738
+ ### 5.2 Symbol Table
739
+
740
+ The `SymbolTable` class manages all declared symbols:
741
+
742
+ ```
743
+ SymbolTable:
744
+ β”œβ”€β”€ variables: {} # Global variable registry
745
+ β”œβ”€β”€ global_variables: {} # Global scope variables
746
+ β”œβ”€β”€ functions: {} # Function declarations {name: {return_type, params, ...}}
747
+ β”œβ”€β”€ scopes: [{}] # Stack of scopes (push on block entry, pop on exit)
748
+ β”œβ”€β”€ current_func_name: None # Currently being analyzed function
749
+ β”œβ”€β”€ function_variables: {} # Variables per function (for duplicate detection)
750
+ └── bundle_types: {} # Struct type definitions
751
+ ```
752
+
753
+ **Scope Rules:**
754
+ - A new scope is pushed when entering a function body, loop body, or conditional block.
755
+ - When a scope is popped, its local variables are no longer accessible.
756
+ - Variables in outer scopes are visible in inner scopes (lexical scoping).
757
+ - Global variables are accessible from all functions.
758
+
759
+ ### 5.3 AST Construction
760
+
761
+ The `build_ast()` function walks the token stream and constructs a tree of `ASTNode` objects. Each node type represents a language construct:
762
+
763
+ - `ProgramNode` β€” root of the AST
764
+ - `VariableDeclarationNode` β€” `seed x = 5;`
765
+ - `AssignmentNode` β€” `x = 10;`
766
+ - `BinaryOpNode` β€” `a + b`, `x == y`
767
+ - `UnaryOpNode` β€” `~x`, `!flag`, `x++`
768
+ - `FunctionDeclarationNode` β€” `pollinate seed add(...) { ... }`
769
+ - `FunctionCallNode` β€” `add(2, 3)`
770
+ - `IfStatementNode` β€” `spring (...) { ... } bud (...) { ... } wither { ... }`
771
+ - `ForLoopNode` β€” `cultivate (...) { ... }`
772
+ - `WhileLoopNode` β€” `grow (...) { ... }`
773
+ - `DoWhileLoopNode` β€” `tend { ... } grow (...);`
774
+ - `SwitchNode` β€” `harvest (...) { variety ...: ... }`
775
+ - `PrintNode` β€” `plant(...);`
776
+ - `ReturnNode` β€” `reclaim x;`
777
+ - `ListNode` β€” `{1, 2, 3}` (array literal)
778
+ - `ListAccessNode` β€” `arr[i]`
779
+ - `CastNode` β€” `seed(x)`, `tree(3)`
780
+ - `MemberAccessNode` β€” `person.age`
781
+ - `BundleDefinitionNode` β€” `bundle Person { ... }`
782
+
783
+ ### 5.4 Semantic Rules β€” General Rules
784
+
785
+ These rules apply across the entire program:
786
+
787
+ #### 5.4.1 Variable Declaration Rules
788
+
789
+ | Rule | Error If Violated |
790
+ |------|-------------------|
791
+ | Variables must be declared before use | `Variable '{name}' used before declaration.` |
792
+ | No duplicate variable names in the same scope | `Variable '{name}' already declared.` |
793
+ | Variable name cannot conflict with function name | `'{name}' is already declared as a function.` |
794
+ | Constants (`fertile`) must be initialized at declaration | `Fertile variables must be initialized.` |
795
+ | Constants cannot be reassigned | `Variable '{name}' is declared as fertile and cannot be re-assigned.` |
796
+ | Multiple `fertile` variables in one line are not allowed | `Multiple fertile declaration is not allowed.` |
797
+
798
+ #### 5.4.2 Type System Rules
799
+
800
+ GAL has a **static type system** with 5 primitive types:
801
+
802
+ | Type | GAL Keyword | Values |
803
+ |------|-------------|--------|
804
+ | Integer | `seed` | Whole numbers: `0`, `42`, `~5` |
805
+ | Float | `tree` | Decimal numbers: `3.14`, `~2.5` |
806
+ | Character | `leaf` | Single characters: `'a'`, `'Z'` |
807
+ | Boolean | `branch` | `sunshine` (true), `frost` (false) |
808
+ | String | `vine` | Text: `"hello"` |
809
+
810
+ **Type Compatibility Matrix:**
811
+
812
+ | Operation | Allowed Types | Notes |
813
+ |-----------|--------------|-------|
814
+ | Assignment `=` | Must match or be `seed`↔`tree` | Implicit numeric conversion |
815
+ | Arithmetic `+ - * /` | `seed`, `tree` | `seed`↔`tree` mixing allowed |
816
+ | Modulo `%` | `seed` only | Both operands must be `seed` |
817
+ | Comparison `== != < > <= >=` | Same type or `seed`↔`tree` | Cannot compare incompatible types |
818
+ | Logical `&& \|\|` | `branch` | Must be boolean |
819
+ | NOT `!` | `branch` | Operand must be boolean |
820
+ | Concatenation `` ` `` | `vine` | At least one operand must be `vine` |
821
+ | Increment/Decrement `++ --` | `seed`, `tree` | Numeric types only |
822
+
823
+ **Implicit Conversions:**
824
+ - `seed` ↔ `tree` are compatible (implicit numeric conversion)
825
+ - All other type mismatches produce errors
826
+
827
+ #### 5.4.3 Function Rules
828
+
829
+ | Rule | Error If Violated |
830
+ |------|-------------------|
831
+ | Functions must be declared before use | `Function '{name}' is not defined.` |
832
+ | No duplicate function declarations | `Function '{name}' already declared.` |
833
+ | Argument count must match parameter count | `Function '{name}' expects {n} argument(s), got {m}.` |
834
+ | Argument types must match parameter types | `Argument {i} of function '{name}': expected '{expected}', got '{actual}'.` |
835
+ | `empty` functions must not return a value | `empty function must not return any value.` |
836
+ | Non-`empty` functions must return a value | `Function expects to return a '{type}' value.` |
837
+ | Return type must match function declaration | `Function '{name}' returns '{actual}', but expected '{expected}'.` |
838
+ | All code paths must return a value | `Function '{name}' must return a value on all code paths.` |
839
+ | Functions must end with `reclaim` | `Function '{name}' must end with 'reclaim'.` |
840
+ | `water()` cannot be used as an expression value | `'water()' is an I/O statement, not a value expression.` |
841
+
842
+ #### 5.4.4 Control Flow Rules
843
+
844
+ | Rule | Error If Violated |
845
+ |------|-------------------|
846
+ | `spring`/`bud` condition must be `branch` | `spring condition must be branch, got {type}.` |
847
+ | `grow` condition must be `branch` | `grow condition must be branch, got {type}.` |
848
+ | `tend` condition must be `branch` | `tend condition must be branch, got {type}.` |
849
+ | `cultivate` condition must be `branch` | `cultivate condition must be branch, got {type}.` |
850
+ | `prune` only inside loop or switch | `'prune' used outside a loop or switch.` |
851
+ | `skip` only inside loop | `'skip' used outside a loop.` |
852
+
853
+ ### 5.5 Semantic Rules β€” Specific Rules
854
+
855
+ These rules apply to specific language constructs:
856
+
857
+ #### 5.5.1 Array/List Rules
858
+
859
+ | Rule | Error If Violated |
860
+ |------|-------------------|
861
+ | Array size must be `seed` (integer) | `Array size must be of type 'seed' (integer).` |
862
+ | List index must be `seed` | `List index must be of type 'seed', got '{type}'.` |
863
+ | Only lists can be indexed | `'{name}' is not a list.` |
864
+ | Lists must be indexed in expressions | `List '{name}' must be indexed with '[]' in expressions.` |
865
+
866
+ #### 5.5.2 Switch (Harvest) Rules
867
+
868
+ | Rule | Error If Violated |
869
+ |------|-------------------|
870
+ | `harvest` expression must be `seed`, `leaf`, or `branch` | `'harvest' expression must be 'seed'/'leaf'/'branch', not '{type}'.` |
871
+ | `variety` value must match switch expression type | `'variety' value type mismatch.` |
872
+ | No duplicate `variety` values | `Duplicate 'variety' value '{value}' in 'harvest'.` |
873
+
874
+ #### 5.5.3 Bundle (Struct) Rules
875
+
876
+ | Rule | Error If Violated |
877
+ |------|-------------------|
878
+ | Bundle type must be defined before use | `Bundle type '{name}' is not defined.` |
879
+ | No duplicate members in a bundle | `Duplicate member '{name}' in bundle.` |
880
+
881
+ #### 5.5.4 Built-in Function Rules
882
+
883
+ | Rule | Error If Violated |
884
+ |------|-------------------|
885
+ | `ts()` only on lists or strings | `'ts()' can only be used on lists or vines.` |
886
+ | `taper()` only on `leaf` type | `'taper()' can only be used on 'leaf' type.` |
887
+ | `plant()` max 15 arguments | `Exceeded maximum amount of 15 arguments in plant statement.` |
888
+ | `plant()` placeholder count must match args | `Found {n} argument(s). Expected {m} argument(s).` |
889
+ | `plant()` string concat only with `vine` | `Only values of type vine can be concatenated in plant().` |
890
+
891
+ #### 5.5.5 Constant (`fertile`) Rules
892
+
893
+ | Rule | Error If Violated |
894
+ |------|-------------------|
895
+ | Must be initialized with matching literal type | `'{name}' must be initialized with a {type} literal.` |
896
+ | `seed` constants: `intlit` or `dbllit` accepted | β€” |
897
+ | `tree` constants: `intlit` or `dbllit` accepted | β€” |
898
+ | `leaf` constants: only `chrlit` | β€” |
899
+ | `branch` constants: only `sunshine` or `frost` | β€” |
900
+ | `vine` constants: only `stringlit` | β€” |
901
+
902
+ ### 5.6 Error Format
903
+
904
+ ```
905
+ Ln {line} Semantic Error: {message}
906
+ ```
907
+
908
+ ---
909
+
910
+ ## 6. Intermediate Code Generation (ICG)
911
+
912
+ **File:** `Backend/icg.py` (~1200 lines)
913
+
914
+ ### 6.1 Overview
915
+
916
+ The ICG module generates **Three-Address Code (TAC)** from the GAL token stream. TAC is an intermediate representation where each instruction has at most three operands:
917
+
918
+ ```
919
+ result = arg1 op arg2
920
+ ```
921
+
922
+ This is represented as a quad: `(op, arg1, arg2, result)`
923
+
924
+ ### 6.2 How It Works
925
+
926
+ 1. The ICG receives the **token stream** (after lexing β€” it runs in parallel with semantic analysis).
927
+ 2. It walks through the tokens, generating TAC instructions for each construct.
928
+ 3. Temporary variables (`t0`, `t1`, ...) are created for intermediate values.
929
+ 4. Labels (`L0`, `L1`, ...) are created for control flow.
930
+
931
+ ### 6.3 GAL β†’ TAC Type Mapping
932
+
933
+ | GAL Type | TAC Type |
934
+ |----------|----------|
935
+ | `seed` | `int` |
936
+ | `tree` | `float` |
937
+ | `leaf` | `char` |
938
+ | `branch` | `bool` |
939
+ | `vine` | `string` |
940
+ | `empty` | `void` |
941
+
942
+ ### 6.4 TAC Instruction Types
943
+
944
+ | Instruction | Format | Example |
945
+ |-------------|--------|---------|
946
+ | `DECLARE` | `(DECLARE, type, _, name)` | `declare x : int` |
947
+ | `ASSIGN` | `(=, value, _, name)` | `x = 5` |
948
+ | `CONST` | `(CONST, type, value, name)` | `const MAX : int = 100` |
949
+ | `ARRAY_DECLARE` | `(ARRAY_DECLARE, type, size, name)` | `declare arr[10] : int` |
950
+ | `ARRAY_STORE` | `(ARRAY_STORE, value, index, name)` | `arr[0] = 5` |
951
+ | `ARRAY_LOAD` | `(ARRAY_LOAD, name, index, temp)` | `t0 = arr[0]` |
952
+ | `STRUCT_STORE` | `(STRUCT_STORE, value, member, name)` | `person.age = 25` |
953
+ | `STRUCT_LOAD` | `(STRUCT_LOAD, name, member, temp)` | `t0 = person.age` |
954
+ | Binary ops | `(op, left, right, temp)` | `t0 = a + b` |
955
+ | `UNARY_MINUS` | `(UNARY_MINUS, arg, _, temp)` | `t0 = -a` |
956
+ | `NOT` | `(NOT, arg, _, temp)` | `t0 = !flag` |
957
+ | `INC` / `DEC` | `(INC, name, _, name)` | `x = x + 1` |
958
+ | `LABEL` | `(LABEL, _, _, label)` | `L0:` |
959
+ | `GOTO` | `(GOTO, _, _, label)` | `goto L1` |
960
+ | `IF` | `(IF, cond, _, label)` | `if t0 goto L1` |
961
+ | `IFFALSE` | `(IFFALSE, cond, _, label)` | `ifFalse t0 goto L2` |
962
+ | `PARAM` | `(PARAM, _, _, value)` | `param x` |
963
+ | `CALL` | `(CALL, func, argc, temp)` | `t1 = call add, 2` |
964
+ | `RETURN` | `(RETURN, _, _, value)` | `return t0` |
965
+ | `FUNC` | `(FUNC, _, _, name)` | `func add:` |
966
+ | `ENDFUNC` | `(ENDFUNC, _, _, name)` | `endfunc` |
967
+ | `PRINT` | `(PRINT, _, _, value)` | `print t0` |
968
+ | `READ` | `(READ, type, _, name)` | `read x` |
969
+
970
+ ### 6.5 ICG Example
971
+
972
+ **GAL Source:**
973
+ ```
974
+ root() {
975
+ seed x = 5;
976
+ seed y = 10;
977
+ seed z = x + y;
978
+ plant("{}", z);
979
+ }
980
+ ```
981
+
982
+ **Generated TAC:**
983
+ ```
984
+ func main:
985
+ declare x : int
986
+ x = 5
987
+ declare y : int
988
+ y = 10
989
+ t0 = x + y
990
+ declare z : int
991
+ z = t0
992
+ print z
993
+ endfunc
994
+ ```
995
+
996
+ ### 6.6 Public API
997
+
998
+ ```python
999
+ generate_icg(tokens) β†’ {
1000
+ "success": bool,
1001
+ "tac": List[TACInstruction],
1002
+ "tac_text": str, # Human-readable TAC output
1003
+ "errors": List[str]
1004
+ }
1005
+ ```
1006
+
1007
+ ---
1008
+
1009
+ ## 7. Interpreter
1010
+
1011
+ **File:** `Backend/GALinterpreter.py` (~1600 lines)
1012
+
1013
+ ### 7.1 Overview
1014
+
1015
+ The interpreter is a **tree-walk interpreter** that directly executes the AST produced by the semantic analyzer. It does NOT compile to machine code β€” it traverses the AST nodes and evaluates them on the fly.
1016
+
1017
+ ### 7.2 How It Works
1018
+
1019
+ 1. The `Interpreter` class receives the validated AST (rooted at a `ProgramNode`).
1020
+ 2. It walks the tree, evaluating each node:
1021
+ - `VariableDeclarationNode` β†’ stores variable in the current scope
1022
+ - `AssignmentNode` β†’ updates variable value
1023
+ - `BinaryOpNode` β†’ evaluates left and right children, applies operator
1024
+ - `IfStatementNode` β†’ evaluates condition, enters appropriate branch
1025
+ - `ForLoopNode` β†’ evaluates init, then loops: check condition β†’ body β†’ update
1026
+ - `PrintNode` β†’ evaluates arguments, sends output via Socket.IO
1027
+ - `FunctionCallNode` β†’ pushes new scope, evaluates body, pops scope
1028
+ 3. For `water()` (input): the interpreter emits an `input_required` event via Socket.IO and **blocks** until the client sends the input value back.
1029
+
1030
+ ### 7.3 Scope Management
1031
+
1032
+ The interpreter uses a **stack-based scope system**:
1033
+ - `self.scopes = [{}]` β€” starts with one global scope
1034
+ - `enter_scope()` β€” pushes a new empty scope dict onto the stack
1035
+ - `exit_scope()` β€” pops the top scope
1036
+ - Variable lookup searches from innermost scope outward (lexical scoping)
1037
+ - `self.global_variables` β€” separate dict for globally declared variables
1038
+
1039
+ ### 7.4 Type Casting
1040
+
1041
+ The interpreter supports explicit type casting:
1042
+
1043
+ | Cast | Behaviour |
1044
+ |------|-----------|
1045
+ | `seed(x)` | Converts to integer (`int(x)`) |
1046
+ | `tree(x)` | Converts to float (`float(x)`) |
1047
+ | `leaf(x)` | Converts to character (first char of string or `chr()`) |
1048
+ | `branch(x)` | Converts to boolean |
1049
+ | `vine(x)` | Converts to string (`str(x)`) |
1050
+
1051
+ ### 7.5 Built-in Functions
1052
+
1053
+ | Function | Description |
1054
+ |----------|-------------|
1055
+ | `ts(x)` | Returns length of list or string (like `len()`) |
1056
+ | `taper(x)` | Splits a `leaf`/string into a list of individual characters (like `split('')`) |
1057
+ | `.append(x)` | Appends element to a list |
1058
+ | `.insert(i, x)` | Inserts element at index in a list |
1059
+ | `.remove(i)` | Removes element at index from a list |
1060
+
1061
+ ### 7.6 Runtime Behaviour
1062
+
1063
+ | Feature | Behaviour |
1064
+ |---------|-----------|
1065
+ | Max loop iterations | 10,000 (prevents infinite loops) |
1066
+ | Max number digits | 16 digits for any evaluated number |
1067
+ | Float display | Truncated to 5 decimal places, trailing zeros stripped |
1068
+ | `seed` ↔ `tree` coercion | Implicit β€” `seed` vars can hold `tree` values and vice versa |
1069
+ | String interpolation | `plant("Hello {}!", name)` β€” `{}` replaced positionally |
1070
+ | Async I/O | Uses Socket.IO + eventlet for non-blocking `water()` input |
1071
+ | `++`/`--` | Pre and post: pre returns new value, post returns old |
1072
+
1073
+ ### 7.7 Runtime Error Catalogue
1074
+
1075
+ | Category | Errors |
1076
+ |----------|--------|
1077
+ | **Arithmetic** | Division by zero; Number exceeds 16 digits |
1078
+ | **Loop** | Infinite loop detected (>10,000 iterations) |
1079
+ | **Type** | Condition must be boolean; List index must be integer; Expected int/float/branch/leaf value |
1080
+ | **Array** | Index out of bounds; Not a list; Cannot index non-list |
1081
+ | **Bundle** | Value is not a bundle; No member '{name}'; Member not a bundle |
1082
+ | **Function** | Argument count mismatch; Break/continue outside loop |
1083
+ | **Variable** | Already declared; Not declared; Used before declaration |
1084
+
1085
+ ---
1086
+
1087
+ ## 8. NLP Fallback / AI Chat Assistant
1088
+
1089
+ **File:** `Backend/gal_fallback.py` (~800 lines)
1090
+
1091
+ ### 8.1 Three-Layer Architecture
1092
+
1093
+ The AI chat assistant uses a hybrid approach to help users understand errors:
1094
+
1095
+ **Layer 1 β€” Rule Engine** (regex-based):
1096
+ - Matches compiler error messages against 50+ regex patterns
1097
+ - Returns structured explanations with: Stage, Cause, Rule, Fix, Explanation
1098
+
1099
+ **Layer 2 β€” Semantic Retrieval** (sentence-transformers):
1100
+ - Uses a fine-tuned `gal-mpnet-finetuned` model for semantic search
1101
+ - Matches user questions against 50+ knowledge base topics
1102
+ - Returns the most relevant topic if similarity > 0.35 threshold
1103
+
1104
+ **Layer 3 β€” Default**:
1105
+ - Falls back to a help menu if no match is found
1106
+ - Returns a general help message listing available topics
1107
+
1108
+ ### 8.2 Error Explanation Format
1109
+
1110
+ ```
1111
+ **Stage:** Lexical Analysis (Lexer)
1112
+ **Cause:** An identifier exceeded 15 characters.
1113
+ **Rule:** Identifiers must be at most 15 characters long.
1114
+ **Fix:**
1115
+ // BAD: seed thisIsWayTooLong = 5;
1116
+ // GOOD: seed shortName = 5;
1117
+ ```
1118
+
1119
+ ---
1120
+
1121
+ ## 9. Web Interface & Server
1122
+
1123
+ ### 9.1 Server (`Backend/server.py`)
1124
+
1125
+ - **Framework**: Flask + Flask-SocketIO (with eventlet for async)
1126
+ - **CORS**: Enabled for cross-origin requests
1127
+
1128
+ **REST API Endpoints:**
1129
+
1130
+ | Endpoint | Method | Description |
1131
+ |----------|--------|-------------|
1132
+ | `/api/lex` | POST | Lexical analysis only β†’ returns tokens + errors |
1133
+ | `/api/parse` | POST | Lexer + Parser β†’ returns syntax analysis results |
1134
+ | `/api/semantic` | POST | Full pipeline through semantic analysis β†’ returns AST + symbol table |
1135
+ | `/api/icg` | POST | Full pipeline + ICG β†’ returns three-address code |
1136
+ | `/api/run` | POST | Full pipeline + execution (REST, no input support) |
1137
+ | `/api/health` | GET | Health check |
1138
+
1139
+ **Socket.IO Events:**
1140
+
1141
+ | Event | Direction | Description |
1142
+ |-------|-----------|-------------|
1143
+ | `run_code` | Client β†’ Server | Send source code for execution |
1144
+ | `output` | Server β†’ Client | Program output line |
1145
+ | `input_required` | Server β†’ Client | Program needs user input (`water()`) |
1146
+ | `input_response` | Client β†’ Server | User provides input value |
1147
+ | `stage_complete` | Server β†’ Client | Compilation stage completed |
1148
+ | `execution_complete` | Server β†’ Client | Program finished |
1149
+
1150
+ ### 9.2 Frontend (`UI/`)
1151
+
1152
+ - `index.html` β€” Main page with code editor and output panels
1153
+ - `style.pixel.css` β€” Pixel/retro-themed CSS styling
1154
+ - `main.js` β€” Client-side logic: editor, Socket.IO communication, UI updates
1155
+
1156
+ ---
1157
+
1158
+ ## 10. General Rules & Constraints
1159
+
1160
+ ### 10.1 Program Structure Rules
1161
+
1162
+ 1. Every program **must** have a `root()` function (entry point).
1163
+ 2. Global declarations (variables, constants, bundles) come **before** function definitions.
1164
+ 3. Function definitions come **before** `root()`.
1165
+ 4. All code must be inside functions or global declarations.
1166
+ 5. Statements end with semicolons (`;`).
1167
+ 6. Blocks are delimited by `{ }`.
1168
+ 7. Declarations and statements can be interleaved within blocks (C99-style).
1169
+
1170
+ ### 10.2 Limits & Constraints
1171
+
1172
+ | Constraint | Value |
1173
+ |------------|-------|
1174
+ | Max identifier length | 15 characters |
1175
+ | Max integer literal digits | 8 digits |
1176
+ | Max fractional digits | 8 digits |
1177
+ | Max evaluated number digits (runtime) | 16 digits |
1178
+ | Max loop iterations | 10,000 |
1179
+ | Max `plant()` arguments | 15 |
1180
+ | Float display precision | 5 decimal places |
1181
+ | String escape sequences | `\n`, `\t`, `\\`, `\"`, `\{`, `\}` |
1182
+
1183
+ ### 10.3 Naming Conventions
1184
+
1185
+ - **Identifiers**: Start with a letter, followed by letters/digits/underscores, max 15 chars.
1186
+ - **Keywords**: All lowercase, garden-themed (see Section 2.3).
1187
+ - **Boolean literals**: `sunshine` (true), `frost` (false).
1188
+ - **Negation**: Uses `~` prefix instead of unary minus `-`.
1189
+
1190
+ ---
1191
+
1192
+ ## 11. Keyword Reference: GAL ↔ Conventional
1193
+
1194
+ | Category | Conventional | GAL Keyword |
1195
+ |----------|-------------|-------------|
1196
+ | **Data Types** | `int` | `seed` |
1197
+ | | `float` / `double` | `tree` |
1198
+ | | `char` | `leaf` |
1199
+ | | `bool` / `boolean` | `branch` |
1200
+ | | `string` | `vine` |
1201
+ | | `void` | `empty` |
1202
+ | **Modifiers** | `const` | `fertile` |
1203
+ | **Aggregates** | `struct` | `bundle` |
1204
+ | **Booleans** | `true` | `sunshine` |
1205
+ | | `false` | `frost` |
1206
+ | **Entry Point** | `main` | `root` |
1207
+ | **Functions** | `function` | `pollinate` |
1208
+ | | `return` | `reclaim` |
1209
+ | **Conditionals** | `if` | `spring` |
1210
+ | | `else if` | `bud` |
1211
+ | | `else` | `wither` |
1212
+ | **Loops** | `while` | `grow` |
1213
+ | | `for` | `cultivate` |
1214
+ | | `do` (do-while) | `tend` |
1215
+ | **Switch** | `switch` | `harvest` |
1216
+ | | `case` | `variety` |
1217
+ | | `default` | `soil` |
1218
+ | **Control Flow** | `break` | `prune` |
1219
+ | | `continue` | `skip` |
1220
+ | **I/O** | `print` / `printf` | `plant` |
1221
+ | | `input` / `scanf` | `water` |
1222
+ | **Built-ins** | `len()` | `ts()` |
1223
+ | | `split('')` | `taper()` |
1224
+ | | `.append()` | `.append()` |
1225
+ | | `.insert()` | `.insert()` |
1226
+ | | `.remove()` | `.remove()` |
1227
+ | **Operators** | unary `-` | `~` (tilde) |
1228
+ | | string `+` | `` ` `` (backtick) |
1229
+
1230
+ ---
1231
+
1232
+ ## 12. AST Node Types Reference
1233
+
1234
+ | Node Class | Purpose | Example |
1235
+ |-----------|---------|---------|
1236
+ | `ProgramNode` | Root of AST | Entire program |
1237
+ | `VariableDeclarationNode` | Variable declaration | `seed x = 5;` |
1238
+ | `FertileDeclarationNode` | Constant declaration | `fertile seed MAX = 100;` |
1239
+ | `AssignmentNode` | Assignment statement | `x = 10;` |
1240
+ | `BinaryOpNode` | Binary operation | `a + b`, `x == y` |
1241
+ | `UnaryOpNode` | Unary operation | `~x`, `!flag`, `x++` |
1242
+ | `FunctionDeclarationNode` | Function definition | `pollinate seed add(...)` |
1243
+ | `FunctionCallNode` | Function call | `add(2, 3)` |
1244
+ | `IfStatementNode` | Conditional chain | `spring/bud/wither` |
1245
+ | `ForLoopNode` | For loop | `cultivate (...)` |
1246
+ | `WhileLoopNode` | While loop | `grow (...)` |
1247
+ | `DoWhileLoopNode` | Do-while loop | `tend {...} grow (...)` |
1248
+ | `SwitchNode` | Switch statement | `harvest (...)` |
1249
+ | `PrintNode` | Output statement | `plant(...)` |
1250
+ | `ReturnNode` | Return statement | `reclaim x;` |
1251
+ | `UpdateNode` | For-loop update | `i++` in for-loop |
1252
+ | `ContinueNode` | Continue statement | `skip;` |
1253
+ | `BreakNode` | Break statement | `prune;` |
1254
+ | `ListNode` | Array literal | `{1, 2, 3}` |
1255
+ | `ListAccessNode` | Array indexing | `arr[i]` |
1256
+ | `AppendNode` | List append | `.append(x)` |
1257
+ | `InsertNode` | List insert | `.insert(0, x)` |
1258
+ | `RemoveNode` | List remove | `.remove(0)` |
1259
+ | `CastNode` | Type cast | `seed(x)` |
1260
+ | `TaperNode` | Character split | `taper(str)` |
1261
+ | `TSNode` | Length function | `ts(arr)` |
1262
+ | `MemberAccessNode` | Struct member access | `person.age` |
1263
+ | `ArrayMemberAccessNode` | Array element member | `people[0].name` |
1264
+ | `BundleDefinitionNode` | Struct definition | `bundle Person {...}` |
1265
+
1266
+ ---
1267
+
1268
+ ## 13. Complete Error Catalogue
1269
+
1270
+ ### 13.1 Lexical Errors
1271
+
1272
+ | # | Error Message | Cause |
1273
+ |---|---------------|-------|
1274
+ | L1 | `Invalid delimiter 'X' after 'Y'` | Wrong character after a token |
1275
+ | L2 | `Identifier exceeds maximum length of 15 characters` | Identifier too long |
1276
+ | L3 | `Integer exceeds maximum of 8 digits` | Integer literal too large |
1277
+ | L4 | `Fractional part exceeds maximum of 8 digits` | Too many decimal digits |
1278
+ | L5 | `Missing closing '"' for string literal` | Unclosed string |
1279
+ | L6 | `Missing closing quote for character literal` | Unclosed char |
1280
+ | L7 | `Character literal must contain exactly one character` | Multi-char in `'...'` |
1281
+ | L8 | `Illegal Character 'X'` | Unknown character |
1282
+ | L9 | `Identifiers cannot start with a number` | e.g., `1abc` |
1283
+ | L10 | `Invalid escape sequence` | Bad escape like `\x` |
1284
+ | L11 | `Missing closing '*/' for multi-line comment` | Unclosed comment |
1285
+
1286
+ ### 13.2 Syntax Errors
1287
+
1288
+ | # | Error Message | Cause |
1289
+ |---|---------------|-------|
1290
+ | S1 | `'X' is not a GAL keyword. Use 'Y' instead.` | Conventional keyword used |
1291
+ | S2 | `'===' is not valid in GAL. Use '==' for comparison.` | Triple equals |
1292
+ | S3 | `'&' is not valid. Use '&&' for logical AND.` | Single ampersand |
1293
+ | S4 | `'\|' is not valid. Use '\|\|' for logical OR.` | Single pipe |
1294
+ | S5 | `Empty block. Expected at least one statement.` | Empty `{}` |
1295
+ | S6 | `Unreachable code after 'reclaim'.` | Code after return |
1296
+ | S7 | `Increment/decrement operators cannot be chained.` | `++++x` |
1297
+ | S8 | `Missing return type after 'pollinate'.` | No type before function name |
1298
+ | S9 | `Missing type for parameter '{name}'.` | Parameter without type |
1299
+ | S10 | `Unexpected token after program end.` | Code after `root(){}` closing |
1300
+ | S11 | `Type mismatch in declaration` | Wrong literal type |
1301
+ | S12 | `Empty character literal.` | `''` |
1302
+ | S13 | `Invalid character literal: '{value}'.` | `'abc'` |
1303
+
1304
+ ### 13.3 Semantic Errors
1305
+
1306
+ | # | Error Message | Cause |
1307
+ |---|---------------|-------|
1308
+ | SE1 | `Variable '{name}' already declared.` | Duplicate declaration |
1309
+ | SE2 | `Variable '{name}' used before declaration.` | Undeclared variable use |
1310
+ | SE3 | `'{name}' is already declared as a function.` | Name conflict |
1311
+ | SE4 | `Fertile variables must be initialized.` | Uninitialized constant |
1312
+ | SE5 | `Variable '{name}' is declared as fertile and cannot be re-assigned.` | Constant mutation |
1313
+ | SE6 | `Type Mismatch! Cannot assign {rhs} to variable '{name}' of type {lhs}.` | Type mismatch |
1314
+ | SE7 | `Modulo operator '%' requires 'seed' operands.` | Non-integer modulo |
1315
+ | SE8 | `'!' operator can only be used with 'branch' values.` | NOT on non-boolean |
1316
+ | SE9 | `Cannot compare '{type1}' and '{type2}'.` | Incompatible comparison |
1317
+ | SE10 | `Cannot concatenate non-vine with backtick.` | Non-string concat |
1318
+ | SE11 | `Array size must be of type 'seed'.` | Non-integer array size |
1319
+ | SE12 | `List index must be of type 'seed'.` | Non-integer index |
1320
+ | SE13 | `'{name}' is not a list.` | Indexing non-array |
1321
+ | SE14 | `Function '{name}' already declared.` | Duplicate function |
1322
+ | SE15 | `Function '{name}' is not defined.` | Undefined function |
1323
+ | SE16 | `Function '{name}' expects {n} argument(s), got {m}.` | Wrong arg count |
1324
+ | SE17 | `Argument {i}: expected '{expected}', got '{actual}'.` | Wrong arg type |
1325
+ | SE18 | `empty function must not return any value.` | Void returning value |
1326
+ | SE19 | `spring condition must be branch, got {type}.` | Non-boolean condition |
1327
+ | SE20 | `'prune' used outside a loop or switch.` | Break outside loop |
1328
+ | SE21 | `'skip' used outside a loop.` | Continue outside loop |
1329
+ | SE22 | `'harvest' expression must be 'seed'/'leaf'/'branch'.` | Invalid switch type |
1330
+ | SE23 | `Duplicate 'variety' value '{value}'.` | Duplicate case |
1331
+ | SE24 | `Bundle type '{name}' is not defined.` | Unknown struct |
1332
+ | SE25 | `'ts()' can only be used on lists or vines.` | Wrong ts() target |
1333
+ | SE26 | `'taper()' can only be used on 'leaf' type.` | Wrong taper() target |
1334
+ | SE27 | `Exceeded maximum of 15 arguments in plant.` | Too many print args |
1335
+
1336
+ ### 13.4 Runtime Errors
1337
+
1338
+ | # | Error Message | Cause |
1339
+ |---|---------------|-------|
1340
+ | R1 | `Division by zero is undefined` | `x / 0` |
1341
+ | R2 | `Evaluated number exceeds maximum of 16 digits` | Number overflow |
1342
+ | R3 | `Infinite loop detected!` | > 10,000 iterations |
1343
+ | R4 | `Condition must be a boolean. Got '{value}'` | Non-boolean in condition |
1344
+ | R5 | `List index must be an integer. Got '{index}'` | Non-integer index |
1345
+ | R6 | `Index '{idx}' out of bounds for list '{name}'` | Array out of bounds |
1346
+ | R7 | `Variable '{name}' is not a list` | Indexing non-array |
1347
+ | R8 | `Value is not a bundle` | Struct access on non-struct |
1348
+ | R9 | `Bundle has no member '{name}'` | Invalid member access |
1349
+ | R10 | `Function '{name}' expects N argument(s), got M` | Wrong arg count at runtime |
1350
+ | R11 | `Input value exceeds maximum of 16 digits` | Input too large |
1351
+
1352
+ ---
1353
+
1354
+ ## Sample GAL Program
1355
+
1356
+ ```gal
1357
+ // Global constant
1358
+ fertile seed MAX = 100;
1359
+
1360
+ // Function definition
1361
+ pollinate seed add(seed a, seed b) {
1362
+ reclaim a + b;
1363
+ }
1364
+
1365
+ // Main entry point
1366
+ root() {
1367
+ seed x = 10;
1368
+ seed y = 20;
1369
+ seed result = add(x, y);
1370
+
1371
+ spring (result > MAX) {
1372
+ plant("Result {} exceeds max!", result);
1373
+ }
1374
+ bud (result == MAX) {
1375
+ plant("Result equals max.");
1376
+ }
1377
+ wither {
1378
+ plant("Result is {}", result);
1379
+ }
1380
+
1381
+ // Loop example
1382
+ cultivate (seed i = 0; i < 5; i++) {
1383
+ plant("i = {}", i);
1384
+ }
1385
+
1386
+ // Array example
1387
+ seed arr[] = {1, 2, 3, 4, 5};
1388
+ plant("Array length: {}", ts(arr));
1389
+
1390
+ // Input example
1391
+ seed userInput = water(seed);
1392
+ plant("You entered: {}", userInput);
1393
+ }
1394
+ ```
1395
+
1396
+ ---
1397
+
1398
+ *End of Documentation*
UI/index.html CHANGED
@@ -202,7 +202,7 @@
202
  <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.43.0/min/vs/loader.js"></script> <!-- Monaco editor -->
203
 
204
 
205
- <script src="main.js?v=20260222a"></script>
206
 
207
 
208
  <script>
@@ -281,7 +281,7 @@
281
 
282
  // Lexer / Syntax / Semantic modes β€” call global functions directly
283
  if (modeText.indexOf('Lexer') !== -1) {
284
- if (term) { term.reset(); term.clear(); }
285
  resetChips();
286
  if (window._clearErrorHighlights) window._clearErrorHighlights();
287
  if (window.runLexer) return window.runLexer({ silent: false });
@@ -289,7 +289,7 @@
289
  return;
290
  }
291
  if (modeText.indexOf('Syntax') !== -1) {
292
- if (term) { term.reset(); term.clear(); }
293
  resetChips();
294
  if (window._clearErrorHighlights) window._clearErrorHighlights();
295
  if (window.runSyntax) return window.runSyntax({ silent: false });
@@ -297,7 +297,7 @@
297
  return;
298
  }
299
  if (modeText.indexOf('Semantic') !== -1) {
300
- if (term) { term.reset(); term.clear(); }
301
  resetChips();
302
  if (window._clearErrorHighlights) window._clearErrorHighlights();
303
  if (window.runSemantic) return window.runSemantic({ silent: false });
@@ -309,7 +309,8 @@
309
  try { code = monaco.editor.getModels()[0].getValue(); }
310
  catch(e) { tw('Error: Editor not ready.\r\n'); return; }
311
 
312
- if (term) { term.reset(); term.clear(); }
 
313
  resetChips();
314
  // Clear previous error highlights immediately
315
  if (window._clearErrorHighlights) window._clearErrorHighlights();
 
202
  <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.43.0/min/vs/loader.js"></script> <!-- Monaco editor -->
203
 
204
 
205
+ <script src="main.js?v=20260329h"></script>
206
 
207
 
208
  <script>
 
281
 
282
  // Lexer / Syntax / Semantic modes β€” call global functions directly
283
  if (modeText.indexOf('Lexer') !== -1) {
284
+ if (term) { term.write('\x1b[2J\x1b[3J\x1b[H'); }
285
  resetChips();
286
  if (window._clearErrorHighlights) window._clearErrorHighlights();
287
  if (window.runLexer) return window.runLexer({ silent: false });
 
289
  return;
290
  }
291
  if (modeText.indexOf('Syntax') !== -1) {
292
+ if (term) { term.write('\x1b[2J\x1b[3J\x1b[H'); }
293
  resetChips();
294
  if (window._clearErrorHighlights) window._clearErrorHighlights();
295
  if (window.runSyntax) return window.runSyntax({ silent: false });
 
297
  return;
298
  }
299
  if (modeText.indexOf('Semantic') !== -1) {
300
+ if (term) { term.write('\x1b[2J\x1b[3J\x1b[H'); }
301
  resetChips();
302
  if (window._clearErrorHighlights) window._clearErrorHighlights();
303
  if (window.runSemantic) return window.runSemantic({ silent: false });
 
309
  try { code = monaco.editor.getModels()[0].getValue(); }
310
  catch(e) { tw('Error: Editor not ready.\r\n'); return; }
311
 
312
+ if (term) { term.write('\x1b[2J\x1b[3J\x1b[H'); }
313
+ window._needsNewline = false;
314
  resetChips();
315
  // Clear previous error highlights immediately
316
  if (window._clearErrorHighlights) window._clearErrorHighlights();
UI/main.js CHANGED
@@ -966,9 +966,17 @@
966
  updateStatusChips(data.stage, data.success);
967
  if (data.stage === 'execution') {
968
  if (data.success) {
969
- if (!silent) term.write('Code execution successful.\r\n');
 
 
 
 
970
  } else {
971
- if (!silent) term.write('Code execution failed.\r\n');
 
 
 
 
972
  }
973
  }
974
  // Highlight errors from collected socket output
@@ -1000,11 +1008,6 @@
1000
  const silent = options.silent || false;
1001
  const sourceCode = editor.getValue();
1002
 
1003
- // Clear terminal FIRST, before anything else
1004
- if (!silent) {
1005
- term.reset();
1006
- term.clear();
1007
- }
1008
  // Clear previous error highlights
1009
  if (window._clearErrorHighlights) window._clearErrorHighlights();
1010
  // Remove stale socket listeners
@@ -1037,12 +1040,17 @@
1037
  // Run generation counter β€” ignore output from stale/previous runs
1038
  window._runGeneration = 0;
1039
  window._socketOutputLog = [];
 
1040
  socket.on('output', function (data) {
1041
  // Ignore output from a previous run
1042
  if (data._gen !== undefined && data._gen !== window._runGeneration) return;
1043
  const text = data.output;
1044
  window._socketOutputLog.push(text);
1045
  const isError = /error|Error/.test(text);
 
 
 
 
1046
  const lines = text.split('\n');
1047
  lines.forEach((line, index) => {
1048
  if (isError) {
@@ -1054,8 +1062,7 @@
1054
  term.write('\r\n');
1055
  }
1056
  });
1057
- // Ensure each message ends on its own line
1058
- term.write('\r\n');
1059
  if (autoScroll) term.scrollToBottom();
1060
  term.focus();
1061
  });
@@ -1088,6 +1095,7 @@
1088
 
1089
  if (e === '\r') {
1090
  term.write('\r\n');
 
1091
  waitingForInput = false;
1092
  socket.emit('capture_input', { var_name: variable, input: userInput });
1093
  userInput = '';
@@ -1107,11 +1115,7 @@
1107
  const prompt = data.prompt;
1108
  variable = data.variable;
1109
 
1110
- // Display the prompt so the user knows to type
1111
- if (prompt) {
1112
- term.write(prompt);
1113
- }
1114
-
1115
  waitingForInput = true;
1116
  userInput = '';
1117
  });
@@ -1121,8 +1125,8 @@
1121
 
1122
  window.runCode = async function () {
1123
  // Clear terminal and error highlights at the start of each run
1124
- term.reset();
1125
- term.clear();
1126
  if (window._clearErrorHighlights) window._clearErrorHighlights();
1127
  // Remove any stale socket listeners from previous runs
1128
  socket.removeAllListeners('execution_complete');
 
966
  updateStatusChips(data.stage, data.success);
967
  if (data.stage === 'execution') {
968
  if (data.success) {
969
+ if (!silent) {
970
+ if (window._needsNewline) term.write('\r\n');
971
+ term.write('\x1b[1;32mCode execution successful.\x1b[0m\r\n');
972
+ window._needsNewline = false;
973
+ }
974
  } else {
975
+ if (!silent) {
976
+ if (window._needsNewline) term.write('\r\n');
977
+ term.write('\x1b[1;31mCode execution failed.\x1b[0m\r\n');
978
+ window._needsNewline = false;
979
+ }
980
  }
981
  }
982
  // Highlight errors from collected socket output
 
1008
  const silent = options.silent || false;
1009
  const sourceCode = editor.getValue();
1010
 
 
 
 
 
 
1011
  // Clear previous error highlights
1012
  if (window._clearErrorHighlights) window._clearErrorHighlights();
1013
  // Remove stale socket listeners
 
1040
  // Run generation counter β€” ignore output from stale/previous runs
1041
  window._runGeneration = 0;
1042
  window._socketOutputLog = [];
1043
+ window._needsNewline = false;
1044
  socket.on('output', function (data) {
1045
  // Ignore output from a previous run
1046
  if (data._gen !== undefined && data._gen !== window._runGeneration) return;
1047
  const text = data.output;
1048
  window._socketOutputLog.push(text);
1049
  const isError = /error|Error/.test(text);
1050
+ // Add newline before this output if a previous output already wrote to the terminal
1051
+ if (window._needsNewline) {
1052
+ term.write('\r\n');
1053
+ }
1054
  const lines = text.split('\n');
1055
  lines.forEach((line, index) => {
1056
  if (isError) {
 
1062
  term.write('\r\n');
1063
  }
1064
  });
1065
+ window._needsNewline = true;
 
1066
  if (autoScroll) term.scrollToBottom();
1067
  term.focus();
1068
  });
 
1095
 
1096
  if (e === '\r') {
1097
  term.write('\r\n');
1098
+ window._needsNewline = false;
1099
  waitingForInput = false;
1100
  socket.emit('capture_input', { var_name: variable, input: userInput });
1101
  userInput = '';
 
1115
  const prompt = data.prompt;
1116
  variable = data.variable;
1117
 
1118
+ window._needsNewline = false;
 
 
 
 
1119
  waitingForInput = true;
1120
  userInput = '';
1121
  });
 
1125
 
1126
  window.runCode = async function () {
1127
  // Clear terminal and error highlights at the start of each run
1128
+ term.write('\x1b[2J\x1b[3J\x1b[H');
1129
+ window._needsNewline = false;
1130
  if (window._clearErrorHighlights) window._clearErrorHighlights();
1131
  // Remove any stale socket listeners from previous runs
1132
  socket.removeAllListeners('execution_complete');
UI/style.pixel.css CHANGED
@@ -12,11 +12,31 @@
12
  --shadow: rgba(0,0,0,.25);
13
  --radius: 12px;
14
  --pixel-font: 'Press Start 2P', system-ui, sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
16
 
17
  /* Monaco Editor Line Highlighting */
18
  .current-line-highlight {
19
- background: rgba(124, 210, 111, 0.03) !important;
20
  border-left: none !important;
21
  }
22
 
@@ -60,10 +80,10 @@ body{
60
  font-family: 'Baloo 2', Arial, sans-serif;
61
  margin: 0;
62
  padding: 0;
63
- background: radial-gradient(1400px 700px at 80% -20%, rgba(124,210,111,.18), transparent),
64
- radial-gradient(1000px 600px at 0% 100%, rgba(255,211,110,.15), transparent),
65
- radial-gradient(800px 800px at 50% 50%, rgba(124,210,111,.08), transparent),
66
- linear-gradient(135deg, #0a150c 0%, #0d1b0f 50%, #0f2014 100%);
67
  color: var(--ink);
68
  display: flex;
69
  flex-direction: column;
@@ -83,8 +103,8 @@ body::before {
83
  width: 100%;
84
  height: 100%;
85
  background:
86
- radial-gradient(circle at 20% 30%, rgba(124,210,111,.05) 0%, transparent 25%),
87
- radial-gradient(circle at 80% 70%, rgba(255,211,110,.05) 0%, transparent 25%);
88
  pointer-events: none;
89
  animation: breathe 8s ease-in-out infinite alternate;
90
  }
@@ -107,7 +127,7 @@ img, canvas{ image-rendering: pixelated; }
107
  ::-webkit-scrollbar-thumb{
108
  background:linear-gradient(135deg, #35543b 0%, #2a4530 100%);
109
  border-radius: 6px;
110
- border: 2px solid rgba(42,90,53,.6);
111
  box-shadow: 0 2px 8px rgba(0,0,0,.3), inset 0 1px 0 rgba(255,255,255,.1);
112
  }
113
  ::-webkit-scrollbar-thumb:hover{
@@ -138,9 +158,9 @@ img, canvas{ image-rendering: pixelated; }
138
  height: 2px;
139
  background: linear-gradient(90deg,
140
  transparent 0%,
141
- rgba(124,210,111,.4) 20%,
142
- rgba(124,210,111,.6) 50%,
143
- rgba(124,210,111,.4) 80%,
144
  transparent 100%);
145
  animation: shimmer 3s ease-in-out infinite;
146
  }
@@ -156,10 +176,10 @@ img, canvas{ image-rendering: pixelated; }
156
  align-items:center;
157
  justify-content:space-between;
158
  padding:10px 20px;
159
- background:linear-gradient(135deg, #2a5a35 0%, #1a3a21 100%);
160
- border-bottom:3px solid #2a5a35;
161
  box-shadow: 0 4px 16px rgba(0,0,0,.4),
162
- inset 0 1px 0 rgba(124,210,111,.15);
163
  position: relative;
164
  z-index: 90;
165
  }
@@ -171,7 +191,7 @@ img, canvas{ image-rendering: pixelated; }
171
  font-weight: 700;
172
  color: #b6ffb6;
173
  background: linear-gradient(135deg, rgba(15,32,20,.7) 0%, rgba(10,24,14,.85) 100%);
174
- border: 1px solid rgba(124,210,111,.3);
175
  border-radius: 6px;
176
  padding: 7px 14px 7px 30px;
177
  max-width: 280px;
@@ -179,7 +199,7 @@ img, canvas{ image-rendering: pixelated; }
179
  text-overflow: ellipsis;
180
  white-space: nowrap;
181
  letter-spacing: .4px;
182
- text-shadow: 0 0 8px rgba(124,210,111,.4), 0 1px 3px rgba(0,0,0,.5);
183
  position: relative;
184
  box-shadow: 0 2px 8px rgba(0,0,0,.25), inset 0 1px 0 rgba(255,255,255,.05);
185
  transition: all .3s ease;
@@ -191,16 +211,16 @@ img, canvas{ image-rendering: pixelated; }
191
  top: 50%;
192
  transform: translateY(-50%);
193
  font-size: 12px;
194
- filter: drop-shadow(0 0 3px rgba(124,210,111,.4));
195
  }
196
  .current-file-name:hover {
197
- border-color: rgba(124,210,111,.5);
198
- text-shadow: 0 0 12px rgba(124,210,111,.6), 0 1px 3px rgba(0,0,0,.5);
199
- box-shadow: 0 3px 12px rgba(0,0,0,.3), inset 0 1px 0 rgba(255,255,255,.08), 0 0 15px rgba(124,210,111,.08);
200
  }
201
  .app-title{ font-family: var(--pixel-font); letter-spacing:.5px; color: var(--ink); opacity:.9; text-shadow: 0 2px 4px rgba(0,0,0,.3); }
202
  .tb-btn{
203
- border:2px solid rgba(42,90,53,.8);
204
  background:linear-gradient(135deg, #15301e 0%, #102417 100%);
205
  color:var(--ink);
206
  padding:10px 16px;
@@ -223,7 +243,7 @@ img, canvas{ image-rendering: pixelated; }
223
  left: -100%;
224
  width: 100%;
225
  height: 100%;
226
- background: linear-gradient(90deg, transparent, rgba(124,210,111,.2), transparent);
227
  transition: left .5s ease;
228
  }
229
 
@@ -232,11 +252,11 @@ img, canvas{ image-rendering: pixelated; }
232
  }
233
 
234
  .tb-btn:hover{
235
- background:linear-gradient(135deg, #1a3a21 0%, #15301e 100%);
236
  transform: translateY(-2px);
237
  box-shadow: 0 6px 16px rgba(0,0,0,.4),
238
  inset 0 1px 0 rgba(255,255,255,.12);
239
- border-color: rgba(124,210,111,.6);
240
  }
241
 
242
  .tb-btn:active{
@@ -255,7 +275,7 @@ img, canvas{ image-rendering: pixelated; }
255
  left: 0;
256
  min-width: 180px;
257
  background: linear-gradient(135deg, #152a18 0%, #0f1d12 100%);
258
- border: 2px solid rgba(124,210,111,.35);
259
  border-radius: 8px;
260
  padding: 6px 0;
261
  box-shadow: 0 10px 32px rgba(0,0,0,.55), 0 0 0 1px rgba(0,0,0,.3);
@@ -282,7 +302,7 @@ img, canvas{ image-rendering: pixelated; }
282
  transition: background .15s, color .15s;
283
  }
284
  .file-popup-item:hover {
285
- background: rgba(124,210,111,.15);
286
  color: #fff;
287
  }
288
  .file-popup-item.danger {
@@ -295,21 +315,21 @@ img, canvas{ image-rendering: pixelated; }
295
  .file-popup-divider {
296
  height: 1px;
297
  margin: 4px 12px;
298
- background: rgba(124,210,111,.2);
299
  }
300
 
301
  .tb-run{
302
- background:linear-gradient(135deg, #7cd26f 0%, #49a25c 100%);
303
  color:#0f1d12;
304
  border-color:#2e6f3e;
305
  text-shadow: 0 1px 2px rgba(255,255,255,.4);
306
- box-shadow: 0 3px 12px rgba(124,210,111,.4),
307
  inset 0 1px 0 rgba(255,255,255,.2);
308
  }
309
 
310
  .tb-run:hover{
311
  background:linear-gradient(135deg, #8ee280 0%, #55b86a 100%);
312
- box-shadow: 0 6px 20px rgba(124,210,111,.6),
313
  inset 0 1px 0 rgba(255,255,255,.3);
314
  }
315
 
@@ -326,7 +346,7 @@ img, canvas{ image-rendering: pixelated; }
326
  min-height: 48px;
327
  background: linear-gradient(180deg, #1a2e1d 0%, #111f14 100%);
328
  padding: 0 14px;
329
- border-bottom: 1px solid rgba(42,90,53,.5);
330
  z-index: 100;
331
  flex-shrink: 0;
332
  position: relative;
@@ -379,8 +399,8 @@ img, canvas{ image-rendering: pixelated; }
379
  }
380
 
381
  .ide-menu-btn {
382
- background: linear-gradient(135deg, #2a5a35 0%, #1a3a21 100%);
383
- border: 1px solid rgba(124,210,111,.35);
384
  color: var(--ink);
385
  font-family: var(--pixel-font);
386
  font-size: 10px;
@@ -393,8 +413,8 @@ img, canvas{ image-rendering: pixelated; }
393
  }
394
 
395
  .ide-menu-btn:hover {
396
- background: linear-gradient(135deg, #35744a 0%, #224b2b 100%);
397
- border-color: rgba(124,210,111,.6);
398
  color: #8ee280;
399
  }
400
 
@@ -405,7 +425,7 @@ img, canvas{ image-rendering: pixelated; }
405
  height: 20px;
406
  min-height: 20px;
407
  background: #0b160d;
408
- border-bottom: 1px solid rgba(42,90,53,.35);
409
  flex-shrink: 0;
410
  overflow-x: auto;
411
  overflow-y: hidden;
@@ -417,7 +437,7 @@ img, canvas{ image-rendering: pixelated; }
417
  gap: 4px;
418
  padding: 0 10px;
419
  background: transparent;
420
- border-right: 1px solid rgba(42,90,53,.2);
421
  cursor: default;
422
  white-space: nowrap;
423
  min-width: 0;
@@ -492,7 +512,7 @@ img, canvas{ image-rendering: pixelated; }
492
  height: 32px;
493
  min-height: 32px;
494
  background: linear-gradient(180deg, #162e1b 0%, #0e1f12 100%);
495
- border-top: 1px solid rgba(42,90,53,.4);
496
  padding: 0 14px;
497
  flex-shrink: 0;
498
  z-index: 100;
@@ -549,18 +569,18 @@ img, canvas{ image-rendering: pixelated; }
549
 
550
  /* Sidebar drag-resize handle */
551
  .sidebarResizer{ width:6px; background:linear-gradient(180deg, #35543b, #2a4530); cursor:ew-resize; border-radius:2px; flex-shrink:0; transition: background .2s ease; position:relative; z-index:5; margin:0 3px; }
552
- .sidebarResizer:hover, .sidebarResizer.active{ background:linear-gradient(180deg, #7cd26f, #49a25c); }
553
- .sidebarResizer::after{ content:''; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); width:2px; height:30px; background:rgba(124,210,111,.5); border-radius:1px; }
554
  .sidebar{
555
  width: 520px;
556
  min-width: 200px;
557
  max-width: 70%;
558
  flex-shrink: 0;
559
  background:linear-gradient(135deg, rgba(24,54,29,.95) 0%, rgba(18,40,22,.95) 100%);
560
- outline:2px solid rgba(42,90,53,.9);
561
  padding:16px;
562
  box-shadow:0 6px 24px rgba(0,0,0,.4),
563
- inset 0 1px 0 rgba(124,210,111,.1);
564
  border-radius:8px;
565
  backdrop-filter: blur(6px);
566
  overflow-y:auto;
@@ -574,7 +594,7 @@ img, canvas{ image-rendering: pixelated; }
574
  left: 0;
575
  width: 100%;
576
  height: 2px;
577
- background: linear-gradient(90deg, transparent, rgba(124,210,111,.4), transparent);
578
  animation: slideGlow 3s ease-in-out infinite;
579
  }
580
 
@@ -586,8 +606,8 @@ img, canvas{ image-rendering: pixelated; }
586
  .sd-section{ font-family:var(--pixel-font); font-size:11px; color:var(--accent-2); margin:12px 0 8px; text-transform:uppercase; letter-spacing:.8px; text-shadow: 0 1px 2px rgba(0,0,0,.3); }
587
  .explorer-list, .themes-list{ list-style:none; padding:0; margin:0; }
588
  .explorer-list li, .themes-list li{ padding:8px 10px; border-left:3px solid transparent; cursor:pointer; transition: all .2s ease; border-radius:3px; margin-bottom:2px; }
589
- .explorer-list li:hover, .themes-list li:hover{ background:rgba(26,58,33,.8); border-left-color:#7cd26f; transform: translateX(2px); }
590
- .themes-list .active{ background:linear-gradient(90deg, rgba(34,75,43,.9) 0%, rgba(26,58,33,.7) 100%); border-left-color:#7cd26f; box-shadow: inset 0 1px 3px rgba(0,0,0,.2); }
591
  .navbar img{ margin-right: 10px; }
592
  .logoContainer{ display:flex; justify-content:flex-end; align-items:center; }
593
  .logo-img{ height: 96px; width: auto; max-width: 260px; object-fit: contain; }
@@ -602,9 +622,9 @@ img, canvas{ image-rendering: pixelated; }
602
  background: linear-gradient(135deg, rgba(13,27,15,.95) 0%, rgba(18,36,20,.9) 100%);
603
  padding: 6px;
604
  border-radius: 10px;
605
- border: 2px solid rgba(124,210,111,.15);
606
  box-shadow: 0 4px 16px rgba(0,0,0,.6),
607
- inset 0 1px 0 rgba(124,210,111,.1);
608
  position: relative;
609
  overflow: hidden;
610
  backdrop-filter: blur(8px);
@@ -631,8 +651,8 @@ img, canvas{ image-rendering: pixelated; }
631
 
632
  .mode-radio:hover {
633
  color: rgba(166,195,169,.95);
634
- background: rgba(124,210,111,.08);
635
- border-color: rgba(124,210,111,.2);
636
  transform: translateY(-1px);
637
  }
638
 
@@ -645,26 +665,26 @@ img, canvas{ image-rendering: pixelated; }
645
  }
646
 
647
  .mode-radio input[type="radio"]:checked ~ span {
648
- color: #7cd26f;
649
  font-weight: bold;
650
  letter-spacing: 0.5px;
651
  animation: modeGlow 0.5s ease-out;
652
  }
653
 
654
  .mode-radio input[type="radio"]:checked {
655
- accent-color: #7cd26f;
656
  }
657
 
658
  .mode-radio:has(input:checked) {
659
- color: #7cd26f;
660
- background: rgba(124,210,111,.12);
661
- border-color: rgba(124,210,111,.4);
662
  transform: scale(1.03);
663
  }
664
 
665
  @keyframes modeGlow {
666
  0% {
667
- text-shadow: 0 0 4px rgba(124,210,111,.4);
668
  }
669
  50% {
670
  text-shadow: 0 2px 6px rgba(0,0,0,.6);
@@ -677,16 +697,16 @@ img, canvas{ image-rendering: pixelated; }
677
 
678
 
679
  /* Sidebar tokens table */
680
- .side-tokens{ max-height: 90%; overflow:auto; margin-top:8px; border-radius:6px; outline:2px solid rgba(42,90,53,.6); box-shadow:0 2px 10px rgba(0,0,0,.25), inset 0 1px 0 rgba(255,255,255,.05); background:linear-gradient(135deg, rgba(24,54,29,.9), rgba(18,40,22,.9)); }
681
  .side-token-table{ width:100%; border-collapse:separate; border-spacing:0; color: #e8ffe4; font-size:12px; }
682
- .side-token-table thead th{ position:sticky; top:0; z-index:2; background:linear-gradient(135deg, #1a3a21, #153018); padding:8px; font-family:var(--pixel-font); font-size:10px; letter-spacing:.5px; text-align:left; color: #ffd36e; font-weight: bold; border: 1px solid rgba(42,90,53,.6); box-shadow: 0 2px 4px rgba(0,0,0,.3); }
683
- .side-token-table td{ padding:6px 8px; border: 1px solid rgba(42,90,53,.4); word-break:break-word; font-weight: bold; color: #e8ffe4; }
684
  .side-token-table td:first-child{ color: #f2ffef; }
685
  .side-token-table td:nth-child(2){ color: #89cff0; }
686
  .side-token-table td:nth-child(3){ color: #ffa86a; }
687
  .side-token-table tr:nth-child(even){ background:rgba(16,36,20,.7); }
688
  .side-token-table tr:nth-child(odd){ background:rgba(20,44,26,.7); }
689
- .side-token-table tr:hover{ background:rgba(42,90,53,.55) !important; transition: background .15s ease; }
690
  /* Make Token Type (3rd column) a bit smaller to fit */
691
  .side-token-table th:nth-child(3), .side-token-table td:nth-child(3){
692
  font-size: 11px;
@@ -723,7 +743,7 @@ img, canvas{ image-rendering: pixelated; }
723
  left: 0;
724
  width: 100%;
725
  height: 100%;
726
- background: linear-gradient(135deg, rgba(124,210,111,.1), transparent);
727
  opacity: 0;
728
  transition: opacity .3s ease;
729
  }
@@ -736,7 +756,7 @@ img, canvas{ image-rendering: pixelated; }
736
  background:linear-gradient(135deg, rgba(32,75,43,.95) 0%, rgba(26,58,33,.95) 100%);
737
  }
738
 
739
- .button-group .dropdown-toggle{ border-left:2px solid rgba(124,210,111,0.2); }
740
 
741
  .nav-links{
742
  list-style:none;
@@ -748,7 +768,7 @@ img, canvas{ image-rendering: pixelated; }
748
  z-index:10;
749
  box-shadow:0 10px 30px rgba(0,0,0,.5);
750
  transition:all .3s ease;
751
- outline:2px solid rgba(124,210,111,.6);
752
  border-radius:6px;
753
  overflow:hidden;
754
  backdrop-filter:blur(10px);
@@ -772,7 +792,7 @@ img, canvas{ image-rendering: pixelated; }
772
  top: 0;
773
  width: 3px;
774
  height: 100%;
775
- background: linear-gradient(180deg, #7cd26f, #49a25c);
776
  transform: scaleY(0);
777
  transition: transform .3s ease;
778
  }
@@ -782,7 +802,7 @@ img, canvas{ image-rendering: pixelated; }
782
  }
783
 
784
  .nav-links li a:hover{
785
- background:rgba(42,90,53,.6);
786
  padding-left: 22px;
787
  }
788
  .hidden{ display:none; }
@@ -795,10 +815,10 @@ img, canvas{ image-rendering: pixelated; }
795
  min-width:50%;
796
  background:linear-gradient(135deg, rgba(24,54,29,.95) 0%, rgba(18,40,22,.95) 100%);
797
  border-radius:8px;
798
- outline:2px solid rgba(42,90,53,.9);
799
  padding:10px;
800
  box-shadow:0 6px 24px rgba(0,0,0,.4),
801
- inset 0 1px 0 rgba(124,210,111,.1);
802
  position:relative;
803
  height:auto;
804
  backdrop-filter: blur(6px);
@@ -807,21 +827,21 @@ img, canvas{ image-rendering: pixelated; }
807
 
808
  .textFieldCont:hover{
809
  box-shadow:0 8px 32px rgba(0,0,0,.5),
810
- inset 0 1px 0 rgba(124,210,111,.15);
811
- outline-color: rgba(124,210,111,.7);
812
  }
813
- .editor-header{ display:flex; align-items:center; justify-content:space-between; background:linear-gradient(135deg, rgba(26,58,33,.7), rgba(16,36,23,.6)); border-radius:4px; padding:6px 8px; margin-bottom:8px; border:1px solid rgba(42,90,53,.5); }
814
  .editor-tabs{ display:flex; gap:6px; }
815
  .tab{ padding:6px 10px; border-radius:4px; border:1px solid transparent; font-family:var(--pixel-font); font-size:10px; color:var(--ink); background:transparent; cursor:pointer; transition:all .2s ease; }
816
  .tab:hover{ background:rgba(26,58,33,.55); }
817
- .tab.active{ background:linear-gradient(135deg, #224b2b, #1a3a21); border-color:#2a5a35; box-shadow: inset 0 1px 2px rgba(0,0,0,.25); }
818
- .editor-actions .tab-btn{ padding:6px 8px; border:1px solid #2a5a35; border-radius:4px; background:linear-gradient(135deg, #15301e, #102417); color:var(--ink); font-family:var(--pixel-font); font-size:10px; cursor:pointer; }
819
  .editor-body{ height: calc(100% - 36px); }
820
  .widthResizer{ width:4px; background:linear-gradient(135deg, #35543b, #2a4530); cursor:ew-resize; position:relative; height:10%; top:45%; border-radius:2px; transition: background .2s ease; }
821
  .widthResizer:hover{ background:linear-gradient(135deg, #4a6d4f, #35543b); }
822
  .heightResizer{ width:100%; height:6px; background:linear-gradient(180deg, #35543b, #2a4530); cursor:ns-resize; border-radius:2px; flex-shrink:0; transition: background .2s ease; position:relative; z-index:5; }
823
- .heightResizer:hover, .heightResizer.active{ background:linear-gradient(180deg, #7cd26f, #49a25c); }
824
- .heightResizer::after{ content:''; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); width:30px; height:2px; background:rgba(124,210,111,.5); border-radius:1px; }
825
  .outputCont{ flex:1; display:flex; flex-direction:column; overflow-y:auto; border-radius:6px; overflow-x:hidden; }
826
 
827
  /* Mobile tokens section - visible by default, hidden on desktop */
@@ -838,8 +858,8 @@ img, canvas{ image-rendering: pixelated; }
838
  overflow: hidden;
839
  border-radius: 12px;
840
  background: rgba(24,54,29,.98);
841
- outline: 3px solid rgba(124,210,111,.6);
842
- box-shadow: 0 10px 50px rgba(0,0,0,.7), 0 0 0 1px rgba(124,210,111,.3);
843
  z-index: 1000;
844
  flex-direction: column;
845
  will-change: transform;
@@ -855,7 +875,7 @@ img, canvas{ image-rendering: pixelated; }
855
  align-items: center;
856
  padding: 12px 16px;
857
  background: linear-gradient(135deg, #2a4c30 0%, #234026 100%);
858
- border-bottom: 2px solid rgba(42,90,53,.8);
859
  }
860
 
861
  .mobile-tokens-title {
@@ -964,13 +984,13 @@ img, canvas{ image-rendering: pixelated; }
964
  width: 48px;
965
  height: 48px;
966
  border-radius: 50%;
967
- background: linear-gradient(135deg, #2a5a35, #1a3a21);
968
- border: 2px solid rgba(124,210,111,.5);
969
- color: #7cd26f;
970
  font-size: 20px;
971
  cursor: pointer;
972
  z-index: 998;
973
- box-shadow: 0 4px 16px rgba(0,0,0,.5), 0 0 0 1px rgba(124,210,111,.2);
974
  align-items: center;
975
  justify-content: center;
976
  transition: all .2s ease;
@@ -1012,8 +1032,8 @@ img, canvas{ image-rendering: pixelated; }
1012
  max-width: 80vw;
1013
  border-radius: 12px;
1014
  background: rgba(24,54,29,.98);
1015
- outline: 3px solid rgba(124,210,111,.6);
1016
- box-shadow: 0 10px 50px rgba(0,0,0,.7), 0 0 0 1px rgba(124,210,111,.3);
1017
  z-index: 1000;
1018
  flex-direction: column;
1019
  overflow: hidden;
@@ -1029,7 +1049,7 @@ img, canvas{ image-rendering: pixelated; }
1029
  align-items: center;
1030
  padding: 10px 14px;
1031
  background: linear-gradient(135deg, #2a4c30 0%, #234026 100%);
1032
- border-bottom: 2px solid rgba(42,90,53,.8);
1033
  }
1034
 
1035
  .mobile-status-title {
@@ -1074,18 +1094,18 @@ img, canvas{ image-rendering: pixelated; }
1074
  font-size: 11px;
1075
  padding: 8px 12px;
1076
  border-radius: 6px;
1077
- background: rgba(42,90,53,.3);
1078
- border: 1px solid rgba(42,90,53,.5);
1079
  color: var(--muted);
1080
  letter-spacing: .3px;
1081
  text-align: center;
1082
  }
1083
 
1084
  .mobile-status-chip.ok {
1085
- background: linear-gradient(135deg, #7cd26f 0%, #49a25c 100%);
1086
  color: #0f1d12;
1087
  border-color: #2e6f3e;
1088
- box-shadow: 0 2px 8px rgba(124,210,111,.3);
1089
  }
1090
 
1091
  .mobile-status-chip.err {
@@ -1101,7 +1121,7 @@ img, canvas{ image-rendering: pixelated; }
1101
  }
1102
 
1103
  .btn-toggle-lexemes {
1104
- background: linear-gradient(135deg, #7cd26f 0%, #49a25c 100%);
1105
  color: #0f1d12;
1106
  border: 2px solid #2e6f3e;
1107
  border-radius: 6px;
@@ -1157,7 +1177,7 @@ img, canvas{ image-rendering: pixelated; }
1157
 
1158
  /* Mobile performance optimizations */
1159
  * {
1160
- -webkit-tap-highlight-color: rgba(124,210,111,0.2);
1161
  box-sizing: border-box;
1162
  }
1163
 
@@ -1204,14 +1224,14 @@ img, canvas{ image-rendering: pixelated; }
1204
  background:linear-gradient(135deg, rgba(24,54,29,.95) 0%, rgba(18,40,22,.95) 100%);
1205
  color: white;
1206
  border-radius:8px;
1207
- outline:2px solid rgba(42,90,53,.9);
1208
  box-shadow:0 6px 24px rgba(0,0,0,.4),
1209
- inset 0 1px 0 rgba(124,210,111,.1);
1210
  overflow:hidden;
1211
  }
1212
 
1213
  .tokenTable th{
1214
- background:linear-gradient(135deg, #1a3a21 0%, #153018 100%);
1215
  color: #ffd36e;
1216
  text-align:center;
1217
  padding:12px 14px;
@@ -1220,7 +1240,7 @@ img, canvas{ image-rendering: pixelated; }
1220
  letter-spacing:.6px;
1221
  text-shadow: 0 1px 3px rgba(0,0,0,.4);
1222
  font-weight: bold;
1223
- border: 1px solid rgba(42,90,53,.7);
1224
  position: sticky;
1225
  top: 0;
1226
  z-index: 2;
@@ -1233,7 +1253,7 @@ img, canvas{ image-rendering: pixelated; }
1233
  left: 0;
1234
  width: 100%;
1235
  height: 1px;
1236
- background: linear-gradient(90deg, transparent, rgba(124,210,111,.5), transparent);
1237
  }
1238
 
1239
  .tokenTable td{
@@ -1241,7 +1261,7 @@ img, canvas{ image-rendering: pixelated; }
1241
  padding:10px 14px;
1242
  font-size:14px;
1243
  word-break:break-word;
1244
- border: 1px solid rgba(42,90,53,.4);
1245
  font-weight: bold;
1246
  transition: background .15s ease;
1247
  color: #e8ffe4;
@@ -1254,7 +1274,7 @@ img, canvas{ image-rendering: pixelated; }
1254
  .tokenTable tr:nth-child(odd){ background:rgba(20,44,26,.75); }
1255
 
1256
  .tokenTable tr:hover{
1257
- background:rgba(42,90,53,.6) !important;
1258
  }
1259
  /* Make Token Type (3rd column) smaller to aid fit */
1260
  .tokenTable th:nth-child(3), .tokenTable td:nth-child(3){
@@ -1272,14 +1292,14 @@ img, canvas{ image-rendering: pixelated; }
1272
  flex-shrink:0;
1273
  background:linear-gradient(135deg, rgba(24,54,29,.95) 0%, rgba(18,40,22,.95) 100%);
1274
  border-radius:8px;
1275
- border:2px solid rgba(42,90,53,.9);
1276
  color:var(--ink);
1277
  margin:0;
1278
  display:flex;
1279
  flex-direction:column;
1280
  overflow:hidden;
1281
  box-shadow:0 6px 24px rgba(0,0,0,.4),
1282
- inset 0 1px 0 rgba(124,210,111,.1);
1283
  backdrop-filter: blur(6px);
1284
  position:relative;
1285
  box-sizing:border-box;
@@ -1291,7 +1311,7 @@ img, canvas{ image-rendering: pixelated; }
1291
  justify-content:space-between;
1292
  gap:10px;
1293
  padding:8px 12px;
1294
- border-bottom:2px solid rgba(42,90,53,.7);
1295
  background:linear-gradient(135deg, rgba(26,58,33,.8), rgba(16,36,23,.7));
1296
  box-shadow: 0 2px 10px rgba(0,0,0,.2);
1297
  flex-shrink: 0;
@@ -1299,7 +1319,7 @@ img, canvas{ image-rendering: pixelated; }
1299
  .panel-tabs{ display:flex; gap:6px; }
1300
  .panel-tabs .tab{ padding:6px 10px; border-radius:4px; border:1px solid transparent; font-family:var(--pixel-font); font-size:10px; color:var(--ink); background:transparent; cursor:pointer; transition:all .2s ease; }
1301
  .panel-tabs .tab:hover{ background:rgba(26,58,33,.55); }
1302
- .panel-tabs .tab.active{ background:linear-gradient(135deg, #224b2b, #1a3a21); border-color:#2a5a35; box-shadow: inset 0 1px 2px rgba(0,0,0,.25); }
1303
  .term-title{
1304
  font-family:var(--pixel-font);
1305
  font-size:11px;
@@ -1325,14 +1345,14 @@ img, canvas{ image-rendering: pixelated; }
1325
  }
1326
 
1327
  .dot-green{
1328
- background:#7cd26f;
1329
- box-shadow:0 0 8px rgba(124,210,111,.7),
1330
  inset 0 0 4px rgba(255,255,255,.3);
1331
  }
1332
  .term-actions{ display:flex; gap:8px; }
1333
  .term-btn{
1334
  padding:7px 12px;
1335
- border:2px solid rgba(42,90,53,.8);
1336
  border-radius:5px;
1337
  cursor:pointer;
1338
  font-family:var(--pixel-font);
@@ -1346,17 +1366,17 @@ img, canvas{ image-rendering: pixelated; }
1346
 
1347
  .term-btn:hover{
1348
  transform: translateY(-2px);
1349
- background:linear-gradient(135deg, #1a3a21, #15301e);
1350
  box-shadow:0 5px 15px rgba(0,0,0,.4),
1351
  inset 0 1px 0 rgba(255,255,255,.12);
1352
- border-color: rgba(124,210,111,.6);
1353
  }
1354
 
1355
  .term-btn[aria-pressed="true"]{
1356
- background:linear-gradient(135deg, #7cd26f, #49a25c);
1357
  color:#0f1d12;
1358
  border-color:#2e6f3e;
1359
- box-shadow:0 3px 12px rgba(124,210,111,.5),
1360
  inset 0 1px 0 rgba(255,255,255,.2);
1361
  }
1362
 
@@ -1372,23 +1392,23 @@ img, canvas{ image-rendering: pixelated; }
1372
  }
1373
 
1374
  /* Status chips inside terminal */
1375
- .term-status{ display:flex; flex-wrap:nowrap; gap:6px; align-items:center; padding:6px 8px; border-bottom:2px solid rgba(42,90,53,.4); background:linear-gradient(135deg, rgba(18,40,22,.7), rgba(12,28,17,.7)); }
1376
  .term-status .status-chip{ padding:4px 8px; font-size:10px; white-space:nowrap; }
1377
 
1378
- .terminal-body{ flex:1; padding:12px; position:relative; overflow:auto; border:2px solid rgba(42,90,53,.6); border-radius:6px; box-shadow: inset 0 2px 6px rgba(0,0,0,.5); background:linear-gradient(135deg,#0f2014,#0a1a0e 60%); }
1379
  #terminal{
1380
  height:100%; width:100%; background:#0a1a0e; color:var(--ink);
1381
  border:none; border-radius:4px;
1382
  padding:0; overflow:hidden; box-shadow: inset 0 2px 8px rgba(0,0,0,.4);
1383
  font-family: 'Courier New', monospace; font-size:14px; line-height:1.4;
1384
  }
1385
- .panel-placeholder{ position:absolute; inset:12px; background:linear-gradient(135deg, rgba(24,54,29,.6), rgba(18,40,22,.6)); color:var(--muted); border:1px dashed rgba(42,90,53,.6); border-radius:6px; display:flex; align-items:center; justify-content:center; font-family:var(--pixel-font); font-size:10px; letter-spacing:.6px; }
1386
  .token-value{ white-space:pre; }
1387
 
1388
  /* Analysis status row */
1389
  .analysis-status{ display:flex; gap:12px; align-items:center; padding:10px 16px; height:auto; flex-shrink:0; }
1390
- .status-chip{ padding:6px 12px; border:2px solid rgba(42,90,53,.8); background:linear-gradient(135deg, rgba(16,36,23,.95) 0%, rgba(12,28,17,.95) 100%); border-radius:4px; font-family:var(--pixel-font); font-size:10px; color:var(--ink); box-shadow: 0 2px 6px rgba(0,0,0,.2), inset 0 1px 0 rgba(255,255,255,.05); text-align: center; white-space: nowrap; box-sizing: border-box; }
1391
- .status-chip.ok{ background:linear-gradient(135deg, #7cd26f 0%, #49a25c 100%); color:#0f1d12; border-color:#2e6f3e; box-shadow: 0 2px 8px rgba(124,210,111,.3), inset 0 1px 0 rgba(255,255,255,.2); }
1392
  .status-chip.err{ background:linear-gradient(135deg, #ff8a5b 0%, #c95f36 100%); color:#2b0c06; border-color:#8c3b21; box-shadow: 0 2px 8px rgba(255,138,91,.3), inset 0 1px 0 rgba(255,255,255,.2); }
1393
 
1394
  /* Accessibility */
@@ -1396,16 +1416,16 @@ img, canvas{ image-rendering: pixelated; }
1396
 
1397
  /* HUD */
1398
  .hud{ display:flex; align-items:center; gap:14px; }
1399
- .coin-badge{ background:linear-gradient(135deg, #ffd36e 0%, #ffb800 100%); color:#3a271c; font-weight:800; padding:6px 12px; border-radius:6px; border:2px solid #f3a300; box-shadow: 0 3px 10px rgba(255,211,110,.4), inset 0 1px 0 rgba(255,231,169,.6); min-width:50px; text-align:center; font-family:var(--pixel-font); transition: transform .2s ease; }
1400
  .coin-badge:hover{ transform: scale(1.05); }
1401
  .energy-wrap{ display:flex; align-items:center; gap:10px; }
1402
  .energy-bar{ width:170px; height:16px; background:#0a1a0e; border-radius:8px; padding:2px; border:2px solid #2d5d38; box-shadow: inset 0 2px 6px rgba(0,0,0,.4), 0 2px 8px rgba(0,0,0,.2); }
1403
- .energy-fill{ height:100%; background:linear-gradient(90deg, #7cd26f 0%, #b6ff85 100%); border-radius:6px; box-shadow:0 0 12px rgba(124,210,111,.6), inset 0 1px 0 rgba(255,255,255,.3); transition: width .3s ease; }
1404
  .energy-label{ color:var(--ink); font-weight:700; font-family:var(--pixel-font); font-size:12px; text-shadow: 0 1px 2px rgba(0,0,0,.3); }
1405
 
1406
 
1407
  /* Status Bar */
1408
- .statusbar{ position:fixed; bottom:0; left:0; right:0; height:28px; background:linear-gradient(180deg, #15301e, #0f2014); border-top:3px solid #2a5a35; color:var(--ink); display:flex; align-items:center; justify-content:space-between; padding:0 12px; font-size:12px; }
1409
  .sb-left{ opacity:.85; }
1410
  .sb-right{ opacity:.85; }
1411
 
@@ -1415,8 +1435,8 @@ img, canvas{ image-rendering: pixelated; }
1415
  .btn{
1416
  border-radius:6px;
1417
  padding:12px 20px;
1418
- border:2px solid rgba(42,90,53,.8);
1419
- background:linear-gradient(135deg, #1a3a21 0%, #15301e 100%);
1420
  color:var(--ink);
1421
  font-weight:800;
1422
  cursor:pointer;
@@ -1435,7 +1455,7 @@ img, canvas{ image-rendering: pixelated; }
1435
  left: -100%;
1436
  width: 100%;
1437
  height: 100%;
1438
- background: linear-gradient(90deg, transparent, rgba(124,210,111,.25), transparent);
1439
  transition: left .5s ease;
1440
  }
1441
 
@@ -1444,11 +1464,11 @@ img, canvas{ image-rendering: pixelated; }
1444
  }
1445
 
1446
  .btn:hover{
1447
- background:linear-gradient(135deg, #224b2b 0%, #1a3a21 100%);
1448
  transform: translateY(-2px);
1449
  box-shadow: 0 6px 20px rgba(0,0,0,.4),
1450
  inset 0 1px 0 rgba(255,255,255,.12);
1451
- border-color: rgba(124,210,111,.7);
1452
  }
1453
 
1454
  .btn:active{
@@ -1457,20 +1477,20 @@ img, canvas{ image-rendering: pixelated; }
1457
  }
1458
 
1459
  .btn-primary{
1460
- background:linear-gradient(135deg, #2a5a35 0%, #1a3a21 100%);
1461
- border-color:#1a3a21;
1462
- color:#7cd26f;
1463
  text-shadow:0 1px 2px rgba(0,0,0,.5);
1464
  box-shadow: 0 4px 16px rgba(0,0,0,.4),
1465
- inset 0 1px 0 rgba(124,210,111,.15);
1466
  min-width: 140px;
1467
  padding: 10px 32px;
1468
  }
1469
 
1470
  .btn-primary:hover{
1471
- background:linear-gradient(135deg, #35744a 0%, #224b2b 100%);
1472
  box-shadow: 0 6px 24px rgba(0,0,0,.5),
1473
- inset 0 1px 0 rgba(124,210,111,.25);
1474
  color:#8ee280;
1475
  }
1476
 
@@ -1487,24 +1507,24 @@ img, canvas{ image-rendering: pixelated; }
1487
  }
1488
 
1489
  .btn-dropdown-toggle {
1490
- background: linear-gradient(135deg, #2a5a35 0%, #1a3a21 100%);
1491
- border: 2px solid #1a3a21;
1492
- border-left: 1px solid rgba(124,210,111,0.2);
1493
  border-radius: 0 8px 8px 0;
1494
- color: #7cd26f;
1495
  font-family: 'Press Start 2P', monospace;
1496
  font-size: 10px;
1497
  padding: 10px 12px;
1498
  cursor: pointer;
1499
  text-shadow: 0 1px 2px rgba(0,0,0,.5);
1500
- box-shadow: 0 4px 16px rgba(0,0,0,.4), inset 0 1px 0 rgba(124,210,111,.15);
1501
  transition: all 0.2s ease;
1502
  }
1503
 
1504
  .btn-dropdown-toggle:hover {
1505
- background: linear-gradient(135deg, #35744a 0%, #224b2b 100%);
1506
  color: #8ee280;
1507
- box-shadow: 0 6px 24px rgba(0,0,0,.5), inset 0 1px 0 rgba(124,210,111,.25);
1508
  }
1509
 
1510
  .run-dropdown-menu {
@@ -1512,8 +1532,8 @@ img, canvas{ image-rendering: pixelated; }
1512
  top: 100%;
1513
  right: 0;
1514
  margin-top: 8px;
1515
- background: linear-gradient(135deg, #2a5a35 0%, #1a3a21 100%);
1516
- border: 2px solid #1a3a21;
1517
  border-radius: 8px;
1518
  min-width: 160px;
1519
  box-shadow: 0 8px 32px rgba(0,0,0,.6);
@@ -1527,12 +1547,12 @@ img, canvas{ image-rendering: pixelated; }
1527
 
1528
  .run-dropdown-item {
1529
  padding: 12px 20px;
1530
- color: #7cd26f;
1531
  font-family: 'Press Start 2P', monospace;
1532
  font-size: 10px;
1533
  cursor: pointer;
1534
  transition: all 0.2s ease;
1535
- border-bottom: 1px solid rgba(124,210,111,0.1);
1536
  }
1537
 
1538
  .run-dropdown-item:last-child {
@@ -1540,7 +1560,7 @@ img, canvas{ image-rendering: pixelated; }
1540
  }
1541
 
1542
  .run-dropdown-item:hover {
1543
- background: rgba(124,210,111,0.15);
1544
  color: #8ee280;
1545
  }
1546
 
@@ -1788,7 +1808,7 @@ img, canvas{ image-rendering: pixelated; }
1788
  .run-dropdown-item {
1789
  padding: 14px 16px;
1790
  font-size: 10px;
1791
- -webkit-tap-highlight-color: rgba(124,210,111,0.3);
1792
  cursor: pointer;
1793
  }
1794
 
@@ -2091,27 +2111,27 @@ img, canvas{ image-rendering: pixelated; }
2091
  gap: 4px;
2092
  font-family: var(--pixel-font);
2093
  font-size: 11px;
2094
- background: linear-gradient(135deg, #2a5a35 0%, #1a3a21 100%);
2095
- border: 2px solid rgba(42,90,53,.8);
2096
  border-radius: 5px;
2097
  padding: 4px 18px;
2098
  cursor: pointer;
2099
  transition: all .3s ease;
2100
- box-shadow: 0 3px 8px rgba(0,0,0,.3), inset 0 1px 0 rgba(124,210,111,.15);
2101
  white-space: nowrap;
2102
  }
2103
  .btn-chat-toggle:hover {
2104
- background: linear-gradient(135deg, #35744a 0%, #224b2b 100%);
2105
- border-color: rgba(124,210,111,.7);
2106
  transform: translateY(-2px);
2107
- box-shadow: 0 6px 20px rgba(0,0,0,.4), inset 0 1px 0 rgba(124,210,111,.25);
2108
  }
2109
  .btn-chat-toggle:active {
2110
  transform: translateY(0);
2111
  box-shadow: 0 2px 8px rgba(0,0,0,.3);
2112
  }
2113
  .chat-btn-text {
2114
- color: #7cd26f;
2115
  font-size: 8px;
2116
  font-weight: 800;
2117
  text-shadow: 0 1px 2px rgba(0,0,0,.5);
@@ -2138,7 +2158,7 @@ img, canvas{ image-rendering: pixelated; }
2138
  width: 420px;
2139
  max-width: 100vw;
2140
  background: var(--bg);
2141
- border-left: 1px solid rgba(124,210,111,.15);
2142
  box-shadow: -2px 0 16px rgba(0,0,0,.4);
2143
  z-index: 999;
2144
  display: flex;
@@ -2157,7 +2177,7 @@ img, canvas{ image-rendering: pixelated; }
2157
  justify-content: space-between;
2158
  padding: 12px 16px;
2159
  background: linear-gradient(180deg, var(--panel) 0%, rgba(24,54,29,.95) 100%);
2160
- border-bottom: 1px solid rgba(124,210,111,.18);
2161
  flex-shrink: 0;
2162
  min-height: 52px;
2163
  box-sizing: border-box;
@@ -2199,9 +2219,9 @@ img, canvas{ image-rendering: pixelated; }
2199
  transition: all .15s ease;
2200
  }
2201
  .chat-header-btn:hover {
2202
- background: rgba(124,210,111,.08);
2203
  color: var(--ink);
2204
- border-color: rgba(124,210,111,.2);
2205
  }
2206
 
2207
  /* Messages container β€” KEY: min-height:0 enables flex scroll */
@@ -2218,7 +2238,7 @@ img, canvas{ image-rendering: pixelated; }
2218
  /* Individual message row */
2219
  .chat-msg {
2220
  padding: 12px 20px;
2221
- border-bottom: 1px solid rgba(124,210,111,.06);
2222
  display: flex;
2223
  gap: 12px;
2224
  align-items: flex-start;
@@ -2231,12 +2251,12 @@ img, canvas{ image-rendering: pixelated; }
2231
  to { opacity: 1; transform: translateY(0); }
2232
  }
2233
  .chat-msg.user {
2234
- background: rgba(124,210,111,.08);
2235
- border-left: 3px solid rgba(124,210,111,.25);
2236
  }
2237
  .chat-msg.assistant {
2238
  background: rgba(13,27,15,.3);
2239
- border-left: 3px solid rgba(124,210,111,.08);
2240
  }
2241
 
2242
  /* Avatar icon */
@@ -2253,11 +2273,11 @@ img, canvas{ image-rendering: pixelated; }
2253
  margin-top: 1px;
2254
  }
2255
  .chat-msg.user .chat-avatar {
2256
- background: rgba(255,211,110,.12);
2257
  color: var(--accent-2);
2258
  }
2259
  .chat-msg.assistant .chat-avatar {
2260
- background: rgba(124,210,111,.18);
2261
  color: var(--accent);
2262
  }
2263
 
@@ -2307,7 +2327,7 @@ img, canvas{ image-rendering: pixelated; }
2307
  margin: 6px 0;
2308
  border-radius: 8px;
2309
  overflow: hidden;
2310
- border: 1px solid rgba(124,210,111,.12);
2311
  background: #0a140c;
2312
  }
2313
  .chat-bubble .code-block-header {
@@ -2315,8 +2335,8 @@ img, canvas{ image-rendering: pixelated; }
2315
  align-items: center;
2316
  justify-content: space-between;
2317
  padding: 4px 10px;
2318
- background: rgba(124,210,111,.06);
2319
- border-bottom: 1px solid rgba(124,210,111,.08);
2320
  min-height: 26px;
2321
  }
2322
  .chat-bubble .code-block-lang {
@@ -2328,7 +2348,7 @@ img, canvas{ image-rendering: pixelated; }
2328
  }
2329
  .chat-copy-btn {
2330
  background: transparent;
2331
- border: 1px solid rgba(124,210,111,.15);
2332
  border-radius: 4px;
2333
  color: var(--muted);
2334
  font-size: 10.5px;
@@ -2338,9 +2358,9 @@ img, canvas{ image-rendering: pixelated; }
2338
  font-family: 'Baloo 2', sans-serif;
2339
  }
2340
  .chat-copy-btn:hover {
2341
- background: rgba(124,210,111,.12);
2342
  color: var(--ink);
2343
- border-color: rgba(124,210,111,.3);
2344
  }
2345
  .chat-copy-btn.copied {
2346
  color: var(--accent);
@@ -2355,7 +2375,7 @@ img, canvas{ image-rendering: pixelated; }
2355
  }
2356
  .chat-bubble pre {
2357
  background: #0a140c;
2358
- border: 1px solid rgba(124,210,111,.12);
2359
  border-radius: 8px;
2360
  padding: 10px 12px;
2361
  margin: 6px 0;
@@ -2377,16 +2397,16 @@ img, canvas{ image-rendering: pixelated; }
2377
  background: transparent;
2378
  }
2379
  .chat-bubble pre::-webkit-scrollbar-thumb {
2380
- background: rgba(124,210,111,.12);
2381
  border-radius: 2px;
2382
  }
2383
  .chat-bubble pre::-webkit-scrollbar-thumb:hover {
2384
- background: rgba(124,210,111,.22);
2385
  }
2386
  .chat-bubble code {
2387
  font-family: 'Consolas', 'Courier New', monospace;
2388
  font-size: 12px;
2389
- background: rgba(124,210,111,.08);
2390
  padding: 2px 5px;
2391
  border-radius: 3px;
2392
  color: var(--accent);
@@ -2437,8 +2457,8 @@ img, canvas{ image-rendering: pixelated; }
2437
  font-weight: 400;
2438
  line-height: 1.4;
2439
  }
2440
- .chat-tag.tag-syntax { background: rgba(124,210,111,.12); color: var(--accent); border: 1px solid rgba(124,210,111,.2); }
2441
- .chat-tag.tag-semantic { background: rgba(255,211,110,.1); color: var(--accent-2); border: 1px solid rgba(255,211,110,.2); }
2442
  .chat-tag.tag-code { background: rgba(138,180,255,.1); color: #8ab4ff; border: 1px solid rgba(138,180,255,.2); }
2443
  .chat-tag.tag-fix { background: rgba(255,138,91,.1); color: var(--accent-3); border: 1px solid rgba(255,138,91,.2); }
2444
  .chat-tag.tag-info { background: rgba(166,195,169,.08); color: var(--muted); border: 1px solid rgba(166,195,169,.15); }
@@ -2450,14 +2470,14 @@ img, canvas{ image-rendering: pixelated; }
2450
  flex-wrap: wrap;
2451
  margin-top: 8px;
2452
  padding-top: 8px;
2453
- border-top: 1px solid rgba(124,210,111,.06);
2454
  }
2455
  .chat-action-btn {
2456
  font-family: 'Baloo 2', sans-serif;
2457
  font-size: 11px;
2458
  color: var(--muted);
2459
- background: rgba(124,210,111,.04);
2460
- border: 1px solid rgba(124,210,111,.12);
2461
  border-radius: 6px;
2462
  padding: 4px 10px;
2463
  cursor: pointer;
@@ -2467,9 +2487,9 @@ img, canvas{ image-rendering: pixelated; }
2467
  gap: 4px;
2468
  }
2469
  .chat-action-btn:hover {
2470
- background: rgba(124,210,111,.1);
2471
  color: var(--ink);
2472
- border-color: rgba(124,210,111,.3);
2473
  }
2474
  .chat-action-btn .action-icon {
2475
  font-size: 12px;
@@ -2480,6 +2500,7 @@ img, canvas{ image-rendering: pixelated; }
2480
  display: flex;
2481
  gap: 4px;
2482
  padding: 4px 0;
 
2483
  }
2484
  .chat-typing span {
2485
  width: 6px;
@@ -2495,10 +2516,71 @@ img, canvas{ image-rendering: pixelated; }
2495
  to { opacity: 1; transform: translateY(-3px); }
2496
  }
2497
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2498
  /* Input area */
2499
  .chat-input-area {
2500
  padding: 12px 16px 14px;
2501
- border-top: 1px solid rgba(124,210,111,.15);
2502
  flex-shrink: 0;
2503
  background: linear-gradient(0deg, var(--panel) 0%, rgba(24,54,29,.95) 100%);
2504
  }
@@ -2527,14 +2609,14 @@ img, canvas{ image-rendering: pixelated; }
2527
  display: flex;
2528
  align-items: center;
2529
  background: var(--bg);
2530
- border: 1px solid rgba(124,210,111,.2);
2531
  border-radius: 8px;
2532
  transition: border-color .2s ease, box-shadow .2s ease;
2533
  min-height: 38px;
2534
  }
2535
  .chat-input-wrap:focus-within {
2536
  border-color: var(--accent);
2537
- box-shadow: 0 0 0 2px rgba(124,210,111,.12), 0 0 12px rgba(124,210,111,.08);
2538
  }
2539
  .chat-input-icon {
2540
  padding-left: 10px;
@@ -2566,7 +2648,7 @@ img, canvas{ image-rendering: pixelated; }
2566
  width: 38px;
2567
  height: 38px;
2568
  background: linear-gradient(135deg, #3a8f45, #2a6a32);
2569
- border: 1px solid rgba(124,210,111,.3);
2570
  border-radius: 8px;
2571
  color: #fff;
2572
  font-size: 13px;
@@ -2579,7 +2661,7 @@ img, canvas{ image-rendering: pixelated; }
2579
  }
2580
  .chat-send-btn:hover {
2581
  background: linear-gradient(135deg, #4aaf55, #3a8f45);
2582
- box-shadow: 0 0 12px rgba(124,210,111,.25);
2583
  transform: translateY(-1px);
2584
  }
2585
  .chat-send-btn:disabled {
@@ -2597,11 +2679,11 @@ img, canvas{ image-rendering: pixelated; }
2597
  background: transparent;
2598
  }
2599
  .chat-messages::-webkit-scrollbar-thumb {
2600
- background: rgba(124,210,111,.15);
2601
  border-radius: 3px;
2602
  }
2603
  .chat-messages::-webkit-scrollbar-thumb:hover {
2604
- background: rgba(124,210,111,.3);
2605
  }
2606
 
2607
  /* ── Empty state / Welcome ──────────────────────────── */
@@ -2629,7 +2711,7 @@ img, canvas{ image-rendering: pixelated; }
2629
  height: 180px;
2630
  object-fit: contain;
2631
  image-rendering: pixelated;
2632
- filter: drop-shadow(0 0 8px rgba(124,210,111,.08));
2633
  }
2634
  .chat-welcome-title {
2635
  font-family: 'Baloo 2', sans-serif;
@@ -2655,8 +2737,8 @@ img, canvas{ image-rendering: pixelated; }
2655
  font-family: 'Baloo 2', sans-serif;
2656
  font-size: 11px;
2657
  color: rgba(166,195,169,.7);
2658
- background: rgba(124,210,111,.04);
2659
- border: 1px solid rgba(124,210,111,.08);
2660
  border-radius: 20px;
2661
  padding: 3px 10px;
2662
  letter-spacing: .2px;
@@ -2674,8 +2756,8 @@ img, canvas{ image-rendering: pixelated; }
2674
  font-family: 'Baloo 2', sans-serif;
2675
  font-size: 12px;
2676
  color: var(--accent);
2677
- background: rgba(124,210,111,.08);
2678
- border: 1px solid rgba(124,210,111,.25);
2679
  border-radius: 8px;
2680
  padding: 7px 14px;
2681
  cursor: pointer;
@@ -2685,9 +2767,9 @@ img, canvas{ image-rendering: pixelated; }
2685
  letter-spacing: .1px;
2686
  }
2687
  .chat-chip:hover {
2688
- background: rgba(124,210,111,.18);
2689
- border-color: rgba(124,210,111,.5);
2690
- box-shadow: 0 2px 12px rgba(124,210,111,.15);
2691
  transform: translateY(-1px);
2692
  color: #fff;
2693
  }
 
12
  --shadow: rgba(0,0,0,.25);
13
  --radius: 12px;
14
  --pixel-font: 'Press Start 2P', system-ui, sans-serif;
15
+
16
+ /* RGB components for theme-aware rgba() */
17
+ --accent-rgb: 124,210,111;
18
+ --accent-2-rgb: 255,211,110;
19
+ --accent-3-rgb: 255,138,91;
20
+ --btn-start-rgb: 42,90,53;
21
+
22
+ /* Button / gradient */
23
+ --btn-start: #2a5a35;
24
+ --btn-end: #1a3a21;
25
+ --btn-hover-start: #35744a;
26
+ --btn-hover-end: #224b2b;
27
+
28
+ /* Editor (Monaco) */
29
+ --editor-bg: #18361d;
30
+ --editor-line: #204b27;
31
+
32
+ /* AI chat accent (separate from compiler) */
33
+ --chat-accent: #7cd26f;
34
+ --chat-accent-rgb: 124,210,111;
35
  }
36
 
37
  /* Monaco Editor Line Highlighting */
38
  .current-line-highlight {
39
+ background: rgba(var(--accent-rgb), 0.03) !important;
40
  border-left: none !important;
41
  }
42
 
 
80
  font-family: 'Baloo 2', Arial, sans-serif;
81
  margin: 0;
82
  padding: 0;
83
+ background: radial-gradient(1400px 700px at 80% -20%, rgba(var(--accent-rgb),.18), transparent),
84
+ radial-gradient(1000px 600px at 0% 100%, rgba(var(--accent-2-rgb),.15), transparent),
85
+ radial-gradient(800px 800px at 50% 50%, rgba(var(--accent-rgb),.08), transparent),
86
+ linear-gradient(135deg, #0a150c 0%, var(--bg) 50%, var(--panel-2) 100%);
87
  color: var(--ink);
88
  display: flex;
89
  flex-direction: column;
 
103
  width: 100%;
104
  height: 100%;
105
  background:
106
+ radial-gradient(circle at 20% 30%, rgba(var(--accent-rgb),.05) 0%, transparent 25%),
107
+ radial-gradient(circle at 80% 70%, rgba(var(--accent-2-rgb),.05) 0%, transparent 25%);
108
  pointer-events: none;
109
  animation: breathe 8s ease-in-out infinite alternate;
110
  }
 
127
  ::-webkit-scrollbar-thumb{
128
  background:linear-gradient(135deg, #35543b 0%, #2a4530 100%);
129
  border-radius: 6px;
130
+ border: 2px solid rgba(var(--btn-start-rgb),.6);
131
  box-shadow: 0 2px 8px rgba(0,0,0,.3), inset 0 1px 0 rgba(255,255,255,.1);
132
  }
133
  ::-webkit-scrollbar-thumb:hover{
 
158
  height: 2px;
159
  background: linear-gradient(90deg,
160
  transparent 0%,
161
+ rgba(var(--accent-rgb),.4) 20%,
162
+ rgba(var(--accent-rgb),.6) 50%,
163
+ rgba(var(--accent-rgb),.4) 80%,
164
  transparent 100%);
165
  animation: shimmer 3s ease-in-out infinite;
166
  }
 
176
  align-items:center;
177
  justify-content:space-between;
178
  padding:10px 20px;
179
+ background:linear-gradient(135deg, var(--btn-start) 0%, var(--btn-end) 100%);
180
+ border-bottom:3px solid var(--btn-start);
181
  box-shadow: 0 4px 16px rgba(0,0,0,.4),
182
+ inset 0 1px 0 rgba(var(--accent-rgb),.15);
183
  position: relative;
184
  z-index: 90;
185
  }
 
191
  font-weight: 700;
192
  color: #b6ffb6;
193
  background: linear-gradient(135deg, rgba(15,32,20,.7) 0%, rgba(10,24,14,.85) 100%);
194
+ border: 1px solid rgba(var(--accent-rgb),.3);
195
  border-radius: 6px;
196
  padding: 7px 14px 7px 30px;
197
  max-width: 280px;
 
199
  text-overflow: ellipsis;
200
  white-space: nowrap;
201
  letter-spacing: .4px;
202
+ text-shadow: 0 0 8px rgba(var(--accent-rgb),.4), 0 1px 3px rgba(0,0,0,.5);
203
  position: relative;
204
  box-shadow: 0 2px 8px rgba(0,0,0,.25), inset 0 1px 0 rgba(255,255,255,.05);
205
  transition: all .3s ease;
 
211
  top: 50%;
212
  transform: translateY(-50%);
213
  font-size: 12px;
214
+ filter: drop-shadow(0 0 3px rgba(var(--accent-rgb),.4));
215
  }
216
  .current-file-name:hover {
217
+ border-color: rgba(var(--accent-rgb),.5);
218
+ text-shadow: 0 0 12px rgba(var(--accent-rgb),.6), 0 1px 3px rgba(0,0,0,.5);
219
+ box-shadow: 0 3px 12px rgba(0,0,0,.3), inset 0 1px 0 rgba(255,255,255,.08), 0 0 15px rgba(var(--accent-rgb),.08);
220
  }
221
  .app-title{ font-family: var(--pixel-font); letter-spacing:.5px; color: var(--ink); opacity:.9; text-shadow: 0 2px 4px rgba(0,0,0,.3); }
222
  .tb-btn{
223
+ border:2px solid rgba(var(--btn-start-rgb),.8);
224
  background:linear-gradient(135deg, #15301e 0%, #102417 100%);
225
  color:var(--ink);
226
  padding:10px 16px;
 
243
  left: -100%;
244
  width: 100%;
245
  height: 100%;
246
+ background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb),.2), transparent);
247
  transition: left .5s ease;
248
  }
249
 
 
252
  }
253
 
254
  .tb-btn:hover{
255
+ background:linear-gradient(135deg, var(--btn-end) 0%, #15301e 100%);
256
  transform: translateY(-2px);
257
  box-shadow: 0 6px 16px rgba(0,0,0,.4),
258
  inset 0 1px 0 rgba(255,255,255,.12);
259
+ border-color: rgba(var(--accent-rgb),.6);
260
  }
261
 
262
  .tb-btn:active{
 
275
  left: 0;
276
  min-width: 180px;
277
  background: linear-gradient(135deg, #152a18 0%, #0f1d12 100%);
278
+ border: 2px solid rgba(var(--accent-rgb),.35);
279
  border-radius: 8px;
280
  padding: 6px 0;
281
  box-shadow: 0 10px 32px rgba(0,0,0,.55), 0 0 0 1px rgba(0,0,0,.3);
 
302
  transition: background .15s, color .15s;
303
  }
304
  .file-popup-item:hover {
305
+ background: rgba(var(--accent-rgb),.15);
306
  color: #fff;
307
  }
308
  .file-popup-item.danger {
 
315
  .file-popup-divider {
316
  height: 1px;
317
  margin: 4px 12px;
318
+ background: rgba(var(--accent-rgb),.2);
319
  }
320
 
321
  .tb-run{
322
+ background:linear-gradient(135deg, var(--accent) 0%, #49a25c 100%);
323
  color:#0f1d12;
324
  border-color:#2e6f3e;
325
  text-shadow: 0 1px 2px rgba(255,255,255,.4);
326
+ box-shadow: 0 3px 12px rgba(var(--accent-rgb),.4),
327
  inset 0 1px 0 rgba(255,255,255,.2);
328
  }
329
 
330
  .tb-run:hover{
331
  background:linear-gradient(135deg, #8ee280 0%, #55b86a 100%);
332
+ box-shadow: 0 6px 20px rgba(var(--accent-rgb),.6),
333
  inset 0 1px 0 rgba(255,255,255,.3);
334
  }
335
 
 
346
  min-height: 48px;
347
  background: linear-gradient(180deg, #1a2e1d 0%, #111f14 100%);
348
  padding: 0 14px;
349
+ border-bottom: 1px solid rgba(var(--btn-start-rgb),.5);
350
  z-index: 100;
351
  flex-shrink: 0;
352
  position: relative;
 
399
  }
400
 
401
  .ide-menu-btn {
402
+ background: linear-gradient(135deg, var(--btn-start) 0%, var(--btn-end) 100%);
403
+ border: 1px solid rgba(var(--accent-rgb),.35);
404
  color: var(--ink);
405
  font-family: var(--pixel-font);
406
  font-size: 10px;
 
413
  }
414
 
415
  .ide-menu-btn:hover {
416
+ background: linear-gradient(135deg, var(--btn-hover-start) 0%, var(--btn-hover-end) 100%);
417
+ border-color: rgba(var(--accent-rgb),.6);
418
  color: #8ee280;
419
  }
420
 
 
425
  height: 20px;
426
  min-height: 20px;
427
  background: #0b160d;
428
+ border-bottom: 1px solid rgba(var(--btn-start-rgb),.35);
429
  flex-shrink: 0;
430
  overflow-x: auto;
431
  overflow-y: hidden;
 
437
  gap: 4px;
438
  padding: 0 10px;
439
  background: transparent;
440
+ border-right: 1px solid rgba(var(--btn-start-rgb),.2);
441
  cursor: default;
442
  white-space: nowrap;
443
  min-width: 0;
 
512
  height: 32px;
513
  min-height: 32px;
514
  background: linear-gradient(180deg, #162e1b 0%, #0e1f12 100%);
515
+ border-top: 1px solid rgba(var(--btn-start-rgb),.4);
516
  padding: 0 14px;
517
  flex-shrink: 0;
518
  z-index: 100;
 
569
 
570
  /* Sidebar drag-resize handle */
571
  .sidebarResizer{ width:6px; background:linear-gradient(180deg, #35543b, #2a4530); cursor:ew-resize; border-radius:2px; flex-shrink:0; transition: background .2s ease; position:relative; z-index:5; margin:0 3px; }
572
+ .sidebarResizer:hover, .sidebarResizer.active{ background:linear-gradient(180deg, var(--accent), #49a25c); }
573
+ .sidebarResizer::after{ content:''; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); width:2px; height:30px; background:rgba(var(--accent-rgb),.5); border-radius:1px; }
574
  .sidebar{
575
  width: 520px;
576
  min-width: 200px;
577
  max-width: 70%;
578
  flex-shrink: 0;
579
  background:linear-gradient(135deg, rgba(24,54,29,.95) 0%, rgba(18,40,22,.95) 100%);
580
+ outline:2px solid rgba(var(--btn-start-rgb),.9);
581
  padding:16px;
582
  box-shadow:0 6px 24px rgba(0,0,0,.4),
583
+ inset 0 1px 0 rgba(var(--accent-rgb),.1);
584
  border-radius:8px;
585
  backdrop-filter: blur(6px);
586
  overflow-y:auto;
 
594
  left: 0;
595
  width: 100%;
596
  height: 2px;
597
+ background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb),.4), transparent);
598
  animation: slideGlow 3s ease-in-out infinite;
599
  }
600
 
 
606
  .sd-section{ font-family:var(--pixel-font); font-size:11px; color:var(--accent-2); margin:12px 0 8px; text-transform:uppercase; letter-spacing:.8px; text-shadow: 0 1px 2px rgba(0,0,0,.3); }
607
  .explorer-list, .themes-list{ list-style:none; padding:0; margin:0; }
608
  .explorer-list li, .themes-list li{ padding:8px 10px; border-left:3px solid transparent; cursor:pointer; transition: all .2s ease; border-radius:3px; margin-bottom:2px; }
609
+ .explorer-list li:hover, .themes-list li:hover{ background:rgba(26,58,33,.8); border-left-color:var(--accent); transform: translateX(2px); }
610
+ .themes-list .active{ background:linear-gradient(90deg, rgba(34,75,43,.9) 0%, rgba(26,58,33,.7) 100%); border-left-color:var(--accent); box-shadow: inset 0 1px 3px rgba(0,0,0,.2); }
611
  .navbar img{ margin-right: 10px; }
612
  .logoContainer{ display:flex; justify-content:flex-end; align-items:center; }
613
  .logo-img{ height: 96px; width: auto; max-width: 260px; object-fit: contain; }
 
622
  background: linear-gradient(135deg, rgba(13,27,15,.95) 0%, rgba(18,36,20,.9) 100%);
623
  padding: 6px;
624
  border-radius: 10px;
625
+ border: 2px solid rgba(var(--accent-rgb),.15);
626
  box-shadow: 0 4px 16px rgba(0,0,0,.6),
627
+ inset 0 1px 0 rgba(var(--accent-rgb),.1);
628
  position: relative;
629
  overflow: hidden;
630
  backdrop-filter: blur(8px);
 
651
 
652
  .mode-radio:hover {
653
  color: rgba(166,195,169,.95);
654
+ background: rgba(var(--accent-rgb),.08);
655
+ border-color: rgba(var(--accent-rgb),.2);
656
  transform: translateY(-1px);
657
  }
658
 
 
665
  }
666
 
667
  .mode-radio input[type="radio"]:checked ~ span {
668
+ color: var(--accent);
669
  font-weight: bold;
670
  letter-spacing: 0.5px;
671
  animation: modeGlow 0.5s ease-out;
672
  }
673
 
674
  .mode-radio input[type="radio"]:checked {
675
+ accent-color: var(--accent);
676
  }
677
 
678
  .mode-radio:has(input:checked) {
679
+ color: var(--accent);
680
+ background: rgba(var(--accent-rgb),.12);
681
+ border-color: rgba(var(--accent-rgb),.4);
682
  transform: scale(1.03);
683
  }
684
 
685
  @keyframes modeGlow {
686
  0% {
687
+ text-shadow: 0 0 4px rgba(var(--accent-rgb),.4);
688
  }
689
  50% {
690
  text-shadow: 0 2px 6px rgba(0,0,0,.6);
 
697
 
698
 
699
  /* Sidebar tokens table */
700
+ .side-tokens{ max-height: 90%; overflow:auto; margin-top:8px; border-radius:6px; outline:2px solid rgba(var(--btn-start-rgb),.6); box-shadow:0 2px 10px rgba(0,0,0,.25), inset 0 1px 0 rgba(255,255,255,.05); background:linear-gradient(135deg, rgba(24,54,29,.9), rgba(18,40,22,.9)); }
701
  .side-token-table{ width:100%; border-collapse:separate; border-spacing:0; color: #e8ffe4; font-size:12px; }
702
+ .side-token-table thead th{ position:sticky; top:0; z-index:2; background:linear-gradient(135deg, var(--btn-end), #153018); padding:8px; font-family:var(--pixel-font); font-size:10px; letter-spacing:.5px; text-align:left; color: #ffd36e; font-weight: bold; border: 1px solid rgba(var(--btn-start-rgb),.6); box-shadow: 0 2px 4px rgba(0,0,0,.3); }
703
+ .side-token-table td{ padding:6px 8px; border: 1px solid rgba(var(--btn-start-rgb),.4); word-break:break-word; font-weight: bold; color: #e8ffe4; }
704
  .side-token-table td:first-child{ color: #f2ffef; }
705
  .side-token-table td:nth-child(2){ color: #89cff0; }
706
  .side-token-table td:nth-child(3){ color: #ffa86a; }
707
  .side-token-table tr:nth-child(even){ background:rgba(16,36,20,.7); }
708
  .side-token-table tr:nth-child(odd){ background:rgba(20,44,26,.7); }
709
+ .side-token-table tr:hover{ background:rgba(var(--btn-start-rgb),.55) !important; transition: background .15s ease; }
710
  /* Make Token Type (3rd column) a bit smaller to fit */
711
  .side-token-table th:nth-child(3), .side-token-table td:nth-child(3){
712
  font-size: 11px;
 
743
  left: 0;
744
  width: 100%;
745
  height: 100%;
746
+ background: linear-gradient(135deg, rgba(var(--accent-rgb),.1), transparent);
747
  opacity: 0;
748
  transition: opacity .3s ease;
749
  }
 
756
  background:linear-gradient(135deg, rgba(32,75,43,.95) 0%, rgba(26,58,33,.95) 100%);
757
  }
758
 
759
+ .button-group .dropdown-toggle{ border-left:2px solid rgba(var(--accent-rgb),0.2); }
760
 
761
  .nav-links{
762
  list-style:none;
 
768
  z-index:10;
769
  box-shadow:0 10px 30px rgba(0,0,0,.5);
770
  transition:all .3s ease;
771
+ outline:2px solid rgba(var(--accent-rgb),.6);
772
  border-radius:6px;
773
  overflow:hidden;
774
  backdrop-filter:blur(10px);
 
792
  top: 0;
793
  width: 3px;
794
  height: 100%;
795
+ background: linear-gradient(180deg, var(--accent), #49a25c);
796
  transform: scaleY(0);
797
  transition: transform .3s ease;
798
  }
 
802
  }
803
 
804
  .nav-links li a:hover{
805
+ background:rgba(var(--btn-start-rgb),.6);
806
  padding-left: 22px;
807
  }
808
  .hidden{ display:none; }
 
815
  min-width:50%;
816
  background:linear-gradient(135deg, rgba(24,54,29,.95) 0%, rgba(18,40,22,.95) 100%);
817
  border-radius:8px;
818
+ outline:2px solid rgba(var(--btn-start-rgb),.9);
819
  padding:10px;
820
  box-shadow:0 6px 24px rgba(0,0,0,.4),
821
+ inset 0 1px 0 rgba(var(--accent-rgb),.1);
822
  position:relative;
823
  height:auto;
824
  backdrop-filter: blur(6px);
 
827
 
828
  .textFieldCont:hover{
829
  box-shadow:0 8px 32px rgba(0,0,0,.5),
830
+ inset 0 1px 0 rgba(var(--accent-rgb),.15);
831
+ outline-color: rgba(var(--accent-rgb),.7);
832
  }
833
+ .editor-header{ display:flex; align-items:center; justify-content:space-between; background:linear-gradient(135deg, rgba(26,58,33,.7), rgba(16,36,23,.6)); border-radius:4px; padding:6px 8px; margin-bottom:8px; border:1px solid rgba(var(--btn-start-rgb),.5); }
834
  .editor-tabs{ display:flex; gap:6px; }
835
  .tab{ padding:6px 10px; border-radius:4px; border:1px solid transparent; font-family:var(--pixel-font); font-size:10px; color:var(--ink); background:transparent; cursor:pointer; transition:all .2s ease; }
836
  .tab:hover{ background:rgba(26,58,33,.55); }
837
+ .tab.active{ background:linear-gradient(135deg, var(--btn-hover-end), var(--btn-end)); border-color:var(--btn-start); box-shadow: inset 0 1px 2px rgba(0,0,0,.25); }
838
+ .editor-actions .tab-btn{ padding:6px 8px; border:1px solid var(--btn-start); border-radius:4px; background:linear-gradient(135deg, #15301e, #102417); color:var(--ink); font-family:var(--pixel-font); font-size:10px; cursor:pointer; }
839
  .editor-body{ height: calc(100% - 36px); }
840
  .widthResizer{ width:4px; background:linear-gradient(135deg, #35543b, #2a4530); cursor:ew-resize; position:relative; height:10%; top:45%; border-radius:2px; transition: background .2s ease; }
841
  .widthResizer:hover{ background:linear-gradient(135deg, #4a6d4f, #35543b); }
842
  .heightResizer{ width:100%; height:6px; background:linear-gradient(180deg, #35543b, #2a4530); cursor:ns-resize; border-radius:2px; flex-shrink:0; transition: background .2s ease; position:relative; z-index:5; }
843
+ .heightResizer:hover, .heightResizer.active{ background:linear-gradient(180deg, var(--accent), #49a25c); }
844
+ .heightResizer::after{ content:''; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); width:30px; height:2px; background:rgba(var(--accent-rgb),.5); border-radius:1px; }
845
  .outputCont{ flex:1; display:flex; flex-direction:column; overflow-y:auto; border-radius:6px; overflow-x:hidden; }
846
 
847
  /* Mobile tokens section - visible by default, hidden on desktop */
 
858
  overflow: hidden;
859
  border-radius: 12px;
860
  background: rgba(24,54,29,.98);
861
+ outline: 3px solid rgba(var(--accent-rgb),.6);
862
+ box-shadow: 0 10px 50px rgba(0,0,0,.7), 0 0 0 1px rgba(var(--accent-rgb),.3);
863
  z-index: 1000;
864
  flex-direction: column;
865
  will-change: transform;
 
875
  align-items: center;
876
  padding: 12px 16px;
877
  background: linear-gradient(135deg, #2a4c30 0%, #234026 100%);
878
+ border-bottom: 2px solid rgba(var(--btn-start-rgb),.8);
879
  }
880
 
881
  .mobile-tokens-title {
 
984
  width: 48px;
985
  height: 48px;
986
  border-radius: 50%;
987
+ background: linear-gradient(135deg, var(--btn-start), var(--btn-end));
988
+ border: 2px solid rgba(var(--accent-rgb),.5);
989
+ color: var(--accent);
990
  font-size: 20px;
991
  cursor: pointer;
992
  z-index: 998;
993
+ box-shadow: 0 4px 16px rgba(0,0,0,.5), 0 0 0 1px rgba(var(--accent-rgb),.2);
994
  align-items: center;
995
  justify-content: center;
996
  transition: all .2s ease;
 
1032
  max-width: 80vw;
1033
  border-radius: 12px;
1034
  background: rgba(24,54,29,.98);
1035
+ outline: 3px solid rgba(var(--accent-rgb),.6);
1036
+ box-shadow: 0 10px 50px rgba(0,0,0,.7), 0 0 0 1px rgba(var(--accent-rgb),.3);
1037
  z-index: 1000;
1038
  flex-direction: column;
1039
  overflow: hidden;
 
1049
  align-items: center;
1050
  padding: 10px 14px;
1051
  background: linear-gradient(135deg, #2a4c30 0%, #234026 100%);
1052
+ border-bottom: 2px solid rgba(var(--btn-start-rgb),.8);
1053
  }
1054
 
1055
  .mobile-status-title {
 
1094
  font-size: 11px;
1095
  padding: 8px 12px;
1096
  border-radius: 6px;
1097
+ background: rgba(var(--btn-start-rgb),.3);
1098
+ border: 1px solid rgba(var(--btn-start-rgb),.5);
1099
  color: var(--muted);
1100
  letter-spacing: .3px;
1101
  text-align: center;
1102
  }
1103
 
1104
  .mobile-status-chip.ok {
1105
+ background: linear-gradient(135deg, var(--accent) 0%, #49a25c 100%);
1106
  color: #0f1d12;
1107
  border-color: #2e6f3e;
1108
+ box-shadow: 0 2px 8px rgba(var(--accent-rgb),.3);
1109
  }
1110
 
1111
  .mobile-status-chip.err {
 
1121
  }
1122
 
1123
  .btn-toggle-lexemes {
1124
+ background: linear-gradient(135deg, var(--accent) 0%, #49a25c 100%);
1125
  color: #0f1d12;
1126
  border: 2px solid #2e6f3e;
1127
  border-radius: 6px;
 
1177
 
1178
  /* Mobile performance optimizations */
1179
  * {
1180
+ -webkit-tap-highlight-color: rgba(var(--accent-rgb),0.2);
1181
  box-sizing: border-box;
1182
  }
1183
 
 
1224
  background:linear-gradient(135deg, rgba(24,54,29,.95) 0%, rgba(18,40,22,.95) 100%);
1225
  color: white;
1226
  border-radius:8px;
1227
+ outline:2px solid rgba(var(--btn-start-rgb),.9);
1228
  box-shadow:0 6px 24px rgba(0,0,0,.4),
1229
+ inset 0 1px 0 rgba(var(--accent-rgb),.1);
1230
  overflow:hidden;
1231
  }
1232
 
1233
  .tokenTable th{
1234
+ background:linear-gradient(135deg, var(--btn-end) 0%, #153018 100%);
1235
  color: #ffd36e;
1236
  text-align:center;
1237
  padding:12px 14px;
 
1240
  letter-spacing:.6px;
1241
  text-shadow: 0 1px 3px rgba(0,0,0,.4);
1242
  font-weight: bold;
1243
+ border: 1px solid rgba(var(--btn-start-rgb),.7);
1244
  position: sticky;
1245
  top: 0;
1246
  z-index: 2;
 
1253
  left: 0;
1254
  width: 100%;
1255
  height: 1px;
1256
+ background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb),.5), transparent);
1257
  }
1258
 
1259
  .tokenTable td{
 
1261
  padding:10px 14px;
1262
  font-size:14px;
1263
  word-break:break-word;
1264
+ border: 1px solid rgba(var(--btn-start-rgb),.4);
1265
  font-weight: bold;
1266
  transition: background .15s ease;
1267
  color: #e8ffe4;
 
1274
  .tokenTable tr:nth-child(odd){ background:rgba(20,44,26,.75); }
1275
 
1276
  .tokenTable tr:hover{
1277
+ background:rgba(var(--btn-start-rgb),.6) !important;
1278
  }
1279
  /* Make Token Type (3rd column) smaller to aid fit */
1280
  .tokenTable th:nth-child(3), .tokenTable td:nth-child(3){
 
1292
  flex-shrink:0;
1293
  background:linear-gradient(135deg, rgba(24,54,29,.95) 0%, rgba(18,40,22,.95) 100%);
1294
  border-radius:8px;
1295
+ border:2px solid rgba(var(--btn-start-rgb),.9);
1296
  color:var(--ink);
1297
  margin:0;
1298
  display:flex;
1299
  flex-direction:column;
1300
  overflow:hidden;
1301
  box-shadow:0 6px 24px rgba(0,0,0,.4),
1302
+ inset 0 1px 0 rgba(var(--accent-rgb),.1);
1303
  backdrop-filter: blur(6px);
1304
  position:relative;
1305
  box-sizing:border-box;
 
1311
  justify-content:space-between;
1312
  gap:10px;
1313
  padding:8px 12px;
1314
+ border-bottom:2px solid rgba(var(--btn-start-rgb),.7);
1315
  background:linear-gradient(135deg, rgba(26,58,33,.8), rgba(16,36,23,.7));
1316
  box-shadow: 0 2px 10px rgba(0,0,0,.2);
1317
  flex-shrink: 0;
 
1319
  .panel-tabs{ display:flex; gap:6px; }
1320
  .panel-tabs .tab{ padding:6px 10px; border-radius:4px; border:1px solid transparent; font-family:var(--pixel-font); font-size:10px; color:var(--ink); background:transparent; cursor:pointer; transition:all .2s ease; }
1321
  .panel-tabs .tab:hover{ background:rgba(26,58,33,.55); }
1322
+ .panel-tabs .tab.active{ background:linear-gradient(135deg, var(--btn-hover-end), var(--btn-end)); border-color:var(--btn-start); box-shadow: inset 0 1px 2px rgba(0,0,0,.25); }
1323
  .term-title{
1324
  font-family:var(--pixel-font);
1325
  font-size:11px;
 
1345
  }
1346
 
1347
  .dot-green{
1348
+ background:var(--accent);
1349
+ box-shadow:0 0 8px rgba(var(--accent-rgb),.7),
1350
  inset 0 0 4px rgba(255,255,255,.3);
1351
  }
1352
  .term-actions{ display:flex; gap:8px; }
1353
  .term-btn{
1354
  padding:7px 12px;
1355
+ border:2px solid rgba(var(--btn-start-rgb),.8);
1356
  border-radius:5px;
1357
  cursor:pointer;
1358
  font-family:var(--pixel-font);
 
1366
 
1367
  .term-btn:hover{
1368
  transform: translateY(-2px);
1369
+ background:linear-gradient(135deg, var(--btn-end), #15301e);
1370
  box-shadow:0 5px 15px rgba(0,0,0,.4),
1371
  inset 0 1px 0 rgba(255,255,255,.12);
1372
+ border-color: rgba(var(--accent-rgb),.6);
1373
  }
1374
 
1375
  .term-btn[aria-pressed="true"]{
1376
+ background:linear-gradient(135deg, var(--accent), #49a25c);
1377
  color:#0f1d12;
1378
  border-color:#2e6f3e;
1379
+ box-shadow:0 3px 12px rgba(var(--accent-rgb),.5),
1380
  inset 0 1px 0 rgba(255,255,255,.2);
1381
  }
1382
 
 
1392
  }
1393
 
1394
  /* Status chips inside terminal */
1395
+ .term-status{ display:flex; flex-wrap:nowrap; gap:6px; align-items:center; padding:6px 8px; border-bottom:2px solid rgba(var(--btn-start-rgb),.4); background:linear-gradient(135deg, rgba(18,40,22,.7), rgba(12,28,17,.7)); }
1396
  .term-status .status-chip{ padding:4px 8px; font-size:10px; white-space:nowrap; }
1397
 
1398
+ .terminal-body{ flex:1; padding:12px; position:relative; overflow:auto; border:2px solid rgba(var(--btn-start-rgb),.6); border-radius:6px; box-shadow: inset 0 2px 6px rgba(0,0,0,.5); background:linear-gradient(135deg,var(--panel-2),#0a1a0e 60%); }
1399
  #terminal{
1400
  height:100%; width:100%; background:#0a1a0e; color:var(--ink);
1401
  border:none; border-radius:4px;
1402
  padding:0; overflow:hidden; box-shadow: inset 0 2px 8px rgba(0,0,0,.4);
1403
  font-family: 'Courier New', monospace; font-size:14px; line-height:1.4;
1404
  }
1405
+ .panel-placeholder{ position:absolute; inset:12px; background:linear-gradient(135deg, rgba(24,54,29,.6), rgba(18,40,22,.6)); color:var(--muted); border:1px dashed rgba(var(--btn-start-rgb),.6); border-radius:6px; display:flex; align-items:center; justify-content:center; font-family:var(--pixel-font); font-size:10px; letter-spacing:.6px; }
1406
  .token-value{ white-space:pre; }
1407
 
1408
  /* Analysis status row */
1409
  .analysis-status{ display:flex; gap:12px; align-items:center; padding:10px 16px; height:auto; flex-shrink:0; }
1410
+ .status-chip{ padding:6px 12px; border:2px solid rgba(var(--btn-start-rgb),.8); background:linear-gradient(135deg, rgba(16,36,23,.95) 0%, rgba(12,28,17,.95) 100%); border-radius:4px; font-family:var(--pixel-font); font-size:10px; color:var(--ink); box-shadow: 0 2px 6px rgba(0,0,0,.2), inset 0 1px 0 rgba(255,255,255,.05); text-align: center; white-space: nowrap; box-sizing: border-box; }
1411
+ .status-chip.ok{ background:linear-gradient(135deg, var(--accent) 0%, #49a25c 100%); color:#0f1d12; border-color:#2e6f3e; box-shadow: 0 2px 8px rgba(var(--accent-rgb),.3), inset 0 1px 0 rgba(255,255,255,.2); }
1412
  .status-chip.err{ background:linear-gradient(135deg, #ff8a5b 0%, #c95f36 100%); color:#2b0c06; border-color:#8c3b21; box-shadow: 0 2px 8px rgba(255,138,91,.3), inset 0 1px 0 rgba(255,255,255,.2); }
1413
 
1414
  /* Accessibility */
 
1416
 
1417
  /* HUD */
1418
  .hud{ display:flex; align-items:center; gap:14px; }
1419
+ .coin-badge{ background:linear-gradient(135deg, #ffd36e 0%, #ffb800 100%); color:#3a271c; font-weight:800; padding:6px 12px; border-radius:6px; border:2px solid #f3a300; box-shadow: 0 3px 10px rgba(var(--accent-2-rgb),.4), inset 0 1px 0 rgba(255,231,169,.6); min-width:50px; text-align:center; font-family:var(--pixel-font); transition: transform .2s ease; }
1420
  .coin-badge:hover{ transform: scale(1.05); }
1421
  .energy-wrap{ display:flex; align-items:center; gap:10px; }
1422
  .energy-bar{ width:170px; height:16px; background:#0a1a0e; border-radius:8px; padding:2px; border:2px solid #2d5d38; box-shadow: inset 0 2px 6px rgba(0,0,0,.4), 0 2px 8px rgba(0,0,0,.2); }
1423
+ .energy-fill{ height:100%; background:linear-gradient(90deg, var(--accent) 0%, #b6ff85 100%); border-radius:6px; box-shadow:0 0 12px rgba(var(--accent-rgb),.6), inset 0 1px 0 rgba(255,255,255,.3); transition: width .3s ease; }
1424
  .energy-label{ color:var(--ink); font-weight:700; font-family:var(--pixel-font); font-size:12px; text-shadow: 0 1px 2px rgba(0,0,0,.3); }
1425
 
1426
 
1427
  /* Status Bar */
1428
+ .statusbar{ position:fixed; bottom:0; left:0; right:0; height:28px; background:linear-gradient(180deg, #15301e, var(--panel-2)); border-top:3px solid var(--btn-start); color:var(--ink); display:flex; align-items:center; justify-content:space-between; padding:0 12px; font-size:12px; }
1429
  .sb-left{ opacity:.85; }
1430
  .sb-right{ opacity:.85; }
1431
 
 
1435
  .btn{
1436
  border-radius:6px;
1437
  padding:12px 20px;
1438
+ border:2px solid rgba(var(--btn-start-rgb),.8);
1439
+ background:linear-gradient(135deg, var(--btn-end) 0%, #15301e 100%);
1440
  color:var(--ink);
1441
  font-weight:800;
1442
  cursor:pointer;
 
1455
  left: -100%;
1456
  width: 100%;
1457
  height: 100%;
1458
+ background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb),.25), transparent);
1459
  transition: left .5s ease;
1460
  }
1461
 
 
1464
  }
1465
 
1466
  .btn:hover{
1467
+ background:linear-gradient(135deg, var(--btn-hover-end) 0%, var(--btn-end) 100%);
1468
  transform: translateY(-2px);
1469
  box-shadow: 0 6px 20px rgba(0,0,0,.4),
1470
  inset 0 1px 0 rgba(255,255,255,.12);
1471
+ border-color: rgba(var(--accent-rgb),.7);
1472
  }
1473
 
1474
  .btn:active{
 
1477
  }
1478
 
1479
  .btn-primary{
1480
+ background:linear-gradient(135deg, var(--btn-start) 0%, var(--btn-end) 100%);
1481
+ border-color:var(--btn-end);
1482
+ color:var(--accent);
1483
  text-shadow:0 1px 2px rgba(0,0,0,.5);
1484
  box-shadow: 0 4px 16px rgba(0,0,0,.4),
1485
+ inset 0 1px 0 rgba(var(--accent-rgb),.15);
1486
  min-width: 140px;
1487
  padding: 10px 32px;
1488
  }
1489
 
1490
  .btn-primary:hover{
1491
+ background:linear-gradient(135deg, var(--btn-hover-start) 0%, var(--btn-hover-end) 100%);
1492
  box-shadow: 0 6px 24px rgba(0,0,0,.5),
1493
+ inset 0 1px 0 rgba(var(--accent-rgb),.25);
1494
  color:#8ee280;
1495
  }
1496
 
 
1507
  }
1508
 
1509
  .btn-dropdown-toggle {
1510
+ background: linear-gradient(135deg, var(--btn-start) 0%, var(--btn-end) 100%);
1511
+ border: 2px solid var(--btn-end);
1512
+ border-left: 1px solid rgba(var(--accent-rgb),0.2);
1513
  border-radius: 0 8px 8px 0;
1514
+ color: var(--accent);
1515
  font-family: 'Press Start 2P', monospace;
1516
  font-size: 10px;
1517
  padding: 10px 12px;
1518
  cursor: pointer;
1519
  text-shadow: 0 1px 2px rgba(0,0,0,.5);
1520
+ box-shadow: 0 4px 16px rgba(0,0,0,.4), inset 0 1px 0 rgba(var(--accent-rgb),.15);
1521
  transition: all 0.2s ease;
1522
  }
1523
 
1524
  .btn-dropdown-toggle:hover {
1525
+ background: linear-gradient(135deg, var(--btn-hover-start) 0%, var(--btn-hover-end) 100%);
1526
  color: #8ee280;
1527
+ box-shadow: 0 6px 24px rgba(0,0,0,.5), inset 0 1px 0 rgba(var(--accent-rgb),.25);
1528
  }
1529
 
1530
  .run-dropdown-menu {
 
1532
  top: 100%;
1533
  right: 0;
1534
  margin-top: 8px;
1535
+ background: linear-gradient(135deg, var(--btn-start) 0%, var(--btn-end) 100%);
1536
+ border: 2px solid var(--btn-end);
1537
  border-radius: 8px;
1538
  min-width: 160px;
1539
  box-shadow: 0 8px 32px rgba(0,0,0,.6);
 
1547
 
1548
  .run-dropdown-item {
1549
  padding: 12px 20px;
1550
+ color: var(--accent);
1551
  font-family: 'Press Start 2P', monospace;
1552
  font-size: 10px;
1553
  cursor: pointer;
1554
  transition: all 0.2s ease;
1555
+ border-bottom: 1px solid rgba(var(--accent-rgb),0.1);
1556
  }
1557
 
1558
  .run-dropdown-item:last-child {
 
1560
  }
1561
 
1562
  .run-dropdown-item:hover {
1563
+ background: rgba(var(--accent-rgb),0.15);
1564
  color: #8ee280;
1565
  }
1566
 
 
1808
  .run-dropdown-item {
1809
  padding: 14px 16px;
1810
  font-size: 10px;
1811
+ -webkit-tap-highlight-color: rgba(var(--accent-rgb),0.3);
1812
  cursor: pointer;
1813
  }
1814
 
 
2111
  gap: 4px;
2112
  font-family: var(--pixel-font);
2113
  font-size: 11px;
2114
+ background: linear-gradient(135deg, var(--btn-start) 0%, var(--btn-end) 100%);
2115
+ border: 2px solid rgba(var(--btn-start-rgb),.8);
2116
  border-radius: 5px;
2117
  padding: 4px 18px;
2118
  cursor: pointer;
2119
  transition: all .3s ease;
2120
+ box-shadow: 0 3px 8px rgba(0,0,0,.3), inset 0 1px 0 rgba(var(--accent-rgb),.15);
2121
  white-space: nowrap;
2122
  }
2123
  .btn-chat-toggle:hover {
2124
+ background: linear-gradient(135deg, var(--btn-hover-start) 0%, var(--btn-hover-end) 100%);
2125
+ border-color: rgba(var(--accent-rgb),.7);
2126
  transform: translateY(-2px);
2127
+ box-shadow: 0 6px 20px rgba(0,0,0,.4), inset 0 1px 0 rgba(var(--accent-rgb),.25);
2128
  }
2129
  .btn-chat-toggle:active {
2130
  transform: translateY(0);
2131
  box-shadow: 0 2px 8px rgba(0,0,0,.3);
2132
  }
2133
  .chat-btn-text {
2134
+ color: var(--accent);
2135
  font-size: 8px;
2136
  font-weight: 800;
2137
  text-shadow: 0 1px 2px rgba(0,0,0,.5);
 
2158
  width: 420px;
2159
  max-width: 100vw;
2160
  background: var(--bg);
2161
+ border-left: 1px solid rgba(var(--accent-rgb),.15);
2162
  box-shadow: -2px 0 16px rgba(0,0,0,.4);
2163
  z-index: 999;
2164
  display: flex;
 
2177
  justify-content: space-between;
2178
  padding: 12px 16px;
2179
  background: linear-gradient(180deg, var(--panel) 0%, rgba(24,54,29,.95) 100%);
2180
+ border-bottom: 1px solid rgba(var(--accent-rgb),.18);
2181
  flex-shrink: 0;
2182
  min-height: 52px;
2183
  box-sizing: border-box;
 
2219
  transition: all .15s ease;
2220
  }
2221
  .chat-header-btn:hover {
2222
+ background: rgba(var(--accent-rgb),.08);
2223
  color: var(--ink);
2224
+ border-color: rgba(var(--accent-rgb),.2);
2225
  }
2226
 
2227
  /* Messages container β€” KEY: min-height:0 enables flex scroll */
 
2238
  /* Individual message row */
2239
  .chat-msg {
2240
  padding: 12px 20px;
2241
+ border-bottom: 1px solid rgba(var(--accent-rgb),.06);
2242
  display: flex;
2243
  gap: 12px;
2244
  align-items: flex-start;
 
2251
  to { opacity: 1; transform: translateY(0); }
2252
  }
2253
  .chat-msg.user {
2254
+ background: rgba(var(--accent-rgb),.08);
2255
+ border-left: 3px solid rgba(var(--accent-rgb),.25);
2256
  }
2257
  .chat-msg.assistant {
2258
  background: rgba(13,27,15,.3);
2259
+ border-left: 3px solid rgba(var(--accent-rgb),.08);
2260
  }
2261
 
2262
  /* Avatar icon */
 
2273
  margin-top: 1px;
2274
  }
2275
  .chat-msg.user .chat-avatar {
2276
+ background: rgba(var(--accent-2-rgb),.12);
2277
  color: var(--accent-2);
2278
  }
2279
  .chat-msg.assistant .chat-avatar {
2280
+ background: rgba(var(--accent-rgb),.18);
2281
  color: var(--accent);
2282
  }
2283
 
 
2327
  margin: 6px 0;
2328
  border-radius: 8px;
2329
  overflow: hidden;
2330
+ border: 1px solid rgba(var(--accent-rgb),.12);
2331
  background: #0a140c;
2332
  }
2333
  .chat-bubble .code-block-header {
 
2335
  align-items: center;
2336
  justify-content: space-between;
2337
  padding: 4px 10px;
2338
+ background: rgba(var(--accent-rgb),.06);
2339
+ border-bottom: 1px solid rgba(var(--accent-rgb),.08);
2340
  min-height: 26px;
2341
  }
2342
  .chat-bubble .code-block-lang {
 
2348
  }
2349
  .chat-copy-btn {
2350
  background: transparent;
2351
+ border: 1px solid rgba(var(--accent-rgb),.15);
2352
  border-radius: 4px;
2353
  color: var(--muted);
2354
  font-size: 10.5px;
 
2358
  font-family: 'Baloo 2', sans-serif;
2359
  }
2360
  .chat-copy-btn:hover {
2361
+ background: rgba(var(--accent-rgb),.12);
2362
  color: var(--ink);
2363
+ border-color: rgba(var(--accent-rgb),.3);
2364
  }
2365
  .chat-copy-btn.copied {
2366
  color: var(--accent);
 
2375
  }
2376
  .chat-bubble pre {
2377
  background: #0a140c;
2378
+ border: 1px solid rgba(var(--accent-rgb),.12);
2379
  border-radius: 8px;
2380
  padding: 10px 12px;
2381
  margin: 6px 0;
 
2397
  background: transparent;
2398
  }
2399
  .chat-bubble pre::-webkit-scrollbar-thumb {
2400
+ background: rgba(var(--accent-rgb),.12);
2401
  border-radius: 2px;
2402
  }
2403
  .chat-bubble pre::-webkit-scrollbar-thumb:hover {
2404
+ background: rgba(var(--accent-rgb),.22);
2405
  }
2406
  .chat-bubble code {
2407
  font-family: 'Consolas', 'Courier New', monospace;
2408
  font-size: 12px;
2409
+ background: rgba(var(--accent-rgb),.08);
2410
  padding: 2px 5px;
2411
  border-radius: 3px;
2412
  color: var(--accent);
 
2457
  font-weight: 400;
2458
  line-height: 1.4;
2459
  }
2460
+ .chat-tag.tag-syntax { background: rgba(var(--accent-rgb),.12); color: var(--accent); border: 1px solid rgba(var(--accent-rgb),.2); }
2461
+ .chat-tag.tag-semantic { background: rgba(var(--accent-2-rgb),.1); color: var(--accent-2); border: 1px solid rgba(var(--accent-2-rgb),.2); }
2462
  .chat-tag.tag-code { background: rgba(138,180,255,.1); color: #8ab4ff; border: 1px solid rgba(138,180,255,.2); }
2463
  .chat-tag.tag-fix { background: rgba(255,138,91,.1); color: var(--accent-3); border: 1px solid rgba(255,138,91,.2); }
2464
  .chat-tag.tag-info { background: rgba(166,195,169,.08); color: var(--muted); border: 1px solid rgba(166,195,169,.15); }
 
2470
  flex-wrap: wrap;
2471
  margin-top: 8px;
2472
  padding-top: 8px;
2473
+ border-top: 1px solid rgba(var(--accent-rgb),.06);
2474
  }
2475
  .chat-action-btn {
2476
  font-family: 'Baloo 2', sans-serif;
2477
  font-size: 11px;
2478
  color: var(--muted);
2479
+ background: rgba(var(--accent-rgb),.04);
2480
+ border: 1px solid rgba(var(--accent-rgb),.12);
2481
  border-radius: 6px;
2482
  padding: 4px 10px;
2483
  cursor: pointer;
 
2487
  gap: 4px;
2488
  }
2489
  .chat-action-btn:hover {
2490
+ background: rgba(var(--accent-rgb),.1);
2491
  color: var(--ink);
2492
+ border-color: rgba(var(--accent-rgb),.3);
2493
  }
2494
  .chat-action-btn .action-icon {
2495
  font-size: 12px;
 
2500
  display: flex;
2501
  gap: 4px;
2502
  padding: 4px 0;
2503
+ align-items: center;
2504
  }
2505
  .chat-typing span {
2506
  width: 6px;
 
2516
  to { opacity: 1; transform: translateY(-3px); }
2517
  }
2518
 
2519
+ /* Thinking indicator β€” multi-phase */
2520
+ .chat-thinking {
2521
+ display: flex;
2522
+ flex-direction: column;
2523
+ gap: 6px;
2524
+ padding: 8px 0;
2525
+ max-width: 240px;
2526
+ }
2527
+ .chat-thinking-status {
2528
+ display: flex;
2529
+ align-items: center;
2530
+ gap: 8px;
2531
+ font-family: var(--pixel-font);
2532
+ font-size: 8px;
2533
+ color: rgba(var(--accent-rgb), .8);
2534
+ animation: thinkFadeIn .3s ease;
2535
+ }
2536
+ .chat-thinking-status .think-icon {
2537
+ width: 16px;
2538
+ height: 16px;
2539
+ border: 2px solid rgba(var(--accent-rgb), .3);
2540
+ border-top-color: var(--accent);
2541
+ border-radius: 50%;
2542
+ animation: thinkSpin .8s linear infinite;
2543
+ flex-shrink: 0;
2544
+ }
2545
+ .chat-thinking-status .think-text {
2546
+ opacity: 1;
2547
+ transition: opacity .3s ease;
2548
+ }
2549
+ .chat-thinking-status.fading .think-text {
2550
+ opacity: 0;
2551
+ }
2552
+ .chat-thinking-progress {
2553
+ height: 2px;
2554
+ background: rgba(var(--accent-rgb), .1);
2555
+ border-radius: 1px;
2556
+ overflow: hidden;
2557
+ }
2558
+ .chat-thinking-progress-bar {
2559
+ height: 100%;
2560
+ background: linear-gradient(90deg, var(--accent), rgba(var(--accent-rgb), .4));
2561
+ border-radius: 1px;
2562
+ width: 0%;
2563
+ animation: thinkProgress 2s ease-out forwards;
2564
+ }
2565
+ @keyframes thinkSpin {
2566
+ to { transform: rotate(360deg); }
2567
+ }
2568
+ @keyframes thinkFadeIn {
2569
+ from { opacity: 0; transform: translateY(4px); }
2570
+ to { opacity: 1; transform: translateY(0); }
2571
+ }
2572
+ @keyframes thinkProgress {
2573
+ 0% { width: 0%; }
2574
+ 30% { width: 45%; }
2575
+ 60% { width: 70%; }
2576
+ 90% { width: 85%; }
2577
+ 100% { width: 95%; }
2578
+ }
2579
+
2580
  /* Input area */
2581
  .chat-input-area {
2582
  padding: 12px 16px 14px;
2583
+ border-top: 1px solid rgba(var(--accent-rgb),.15);
2584
  flex-shrink: 0;
2585
  background: linear-gradient(0deg, var(--panel) 0%, rgba(24,54,29,.95) 100%);
2586
  }
 
2609
  display: flex;
2610
  align-items: center;
2611
  background: var(--bg);
2612
+ border: 1px solid rgba(var(--accent-rgb),.2);
2613
  border-radius: 8px;
2614
  transition: border-color .2s ease, box-shadow .2s ease;
2615
  min-height: 38px;
2616
  }
2617
  .chat-input-wrap:focus-within {
2618
  border-color: var(--accent);
2619
+ box-shadow: 0 0 0 2px rgba(var(--accent-rgb),.12), 0 0 12px rgba(var(--accent-rgb),.08);
2620
  }
2621
  .chat-input-icon {
2622
  padding-left: 10px;
 
2648
  width: 38px;
2649
  height: 38px;
2650
  background: linear-gradient(135deg, #3a8f45, #2a6a32);
2651
+ border: 1px solid rgba(var(--accent-rgb),.3);
2652
  border-radius: 8px;
2653
  color: #fff;
2654
  font-size: 13px;
 
2661
  }
2662
  .chat-send-btn:hover {
2663
  background: linear-gradient(135deg, #4aaf55, #3a8f45);
2664
+ box-shadow: 0 0 12px rgba(var(--accent-rgb),.25);
2665
  transform: translateY(-1px);
2666
  }
2667
  .chat-send-btn:disabled {
 
2679
  background: transparent;
2680
  }
2681
  .chat-messages::-webkit-scrollbar-thumb {
2682
+ background: rgba(var(--accent-rgb),.15);
2683
  border-radius: 3px;
2684
  }
2685
  .chat-messages::-webkit-scrollbar-thumb:hover {
2686
+ background: rgba(var(--accent-rgb),.3);
2687
  }
2688
 
2689
  /* ── Empty state / Welcome ──────────────────────────── */
 
2711
  height: 180px;
2712
  object-fit: contain;
2713
  image-rendering: pixelated;
2714
+ filter: drop-shadow(0 0 8px rgba(var(--accent-rgb),.08));
2715
  }
2716
  .chat-welcome-title {
2717
  font-family: 'Baloo 2', sans-serif;
 
2737
  font-family: 'Baloo 2', sans-serif;
2738
  font-size: 11px;
2739
  color: rgba(166,195,169,.7);
2740
+ background: rgba(var(--accent-rgb),.04);
2741
+ border: 1px solid rgba(var(--accent-rgb),.08);
2742
  border-radius: 20px;
2743
  padding: 3px 10px;
2744
  letter-spacing: .2px;
 
2756
  font-family: 'Baloo 2', sans-serif;
2757
  font-size: 12px;
2758
  color: var(--accent);
2759
+ background: rgba(var(--accent-rgb),.08);
2760
+ border: 1px solid rgba(var(--accent-rgb),.25);
2761
  border-radius: 8px;
2762
  padding: 7px 14px;
2763
  cursor: pointer;
 
2767
  letter-spacing: .1px;
2768
  }
2769
  .chat-chip:hover {
2770
+ background: rgba(var(--accent-rgb),.18);
2771
+ border-color: rgba(var(--accent-rgb),.5);
2772
+ box-shadow: 0 2px 12px rgba(var(--accent-rgb),.15);
2773
  transform: translateY(-1px);
2774
  color: #fff;
2775
  }