🎨 docs: shadcn-style UI redesign β€” anime.js animations (counters, reveal, bars, terminal), tabs, readable typography, clean landing

swadhinbiswas committed 2 months ago
4705f15
1 parent 9eee375
commit 4705f15
8 files changed
+791 -691
β–Ό.gitignore
+1-0
146
146
147
147
# Debugging artifacts
148
148
benchmarks/gdb_out.txt
149
+.wrangler/
149
150
β–Όdocs_site/astro.config.mjs
+3-0
26
26
{ icon: 'x.com', label: 'X (Twitter)', href: 'https://x.com/justapidev' },
27
27
],
28
28
customCss: ['./src/styles/custom.css'],
29
+ components: {
30
+ PageFrame: './src/components/PageFrame.astro',
31
+ },
29
32
editLink: {
30
33
baseUrl: 'https://github.com/swadhinbiswas/JustAPI/edit/main/docs_site',
31
34
},
32
35
β–Όdocs_site/package-lock.json
+26-0
10
10
"dependencies": {
11
11
"@astrojs/sitemap": "^3.7.3",
12
12
"@astrojs/starlight": "^0.41.4",
13
+ "animejs": "^4.5.0",
13
14
"astro": "^7.1.3"
15
+ },
16
+ "engines": {
17
+ "node": ">=22.12.0"
14
18
}
15
19
},
16
20
"node_modules/@astrojs/compiler-binding": {
2305
2309
"am-i-vibing": "dist/cli.mjs"
2306
2310
}
2307
2311
},
2312
+ "node_modules/animejs": {
2313
+ "version": "4.5.0",
2314
+ "resolved": "https://registry.npmmirror.com/animejs/-/animejs-4.5.0.tgz",
2315
+ "integrity": "sha512-NQimYX+lz8WaXonGS9zVVoviCIAjONeJayxacUditaivYLqyXgGjFKjuyl0aUUhFKm5MriX9ty1TGovNzoJQWA==",
2316
+ "license": "MIT",
2317
+ "funding": {
2318
+ "type": "github",
2319
+ "url": "https://github.com/sponsors/juliangarnier"
2320
+ },
2321
+ "peerDependencies": {
2322
+ "@types/three": ">=0.150.0",
2323
+ "three": ">=0.150.0"
2324
+ },
2325
+ "peerDependenciesMeta": {
2326
+ "@types/three": {
2327
+ "optional": true
2328
+ },
2329
+ "three": {
2330
+ "optional": true
2331
+ }
2332
+ }
2333
+ },
2308
2334
"node_modules/anymatch": {
2309
2335
"version": "3.1.3",
2310
2336
"resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
2311
2337
β–Όdocs_site/package.json
+1-0
12
12
"dependencies": {
13
13
"@astrojs/sitemap": "^3.7.3",
14
14
"@astrojs/starlight": "^0.41.4",
15
+ "animejs": "^4.5.0",
15
16
"astro": "^7.1.3"
16
17
},
17
18
"engines": {
18
19
β–Όdocs_site/src/components/PageFrame.astro
+9-0
1
+---
2
+import PageFrame from '@astrojs/starlight/components/PageFrame.astro';
3
+---
4
+<PageFrame>
5
+ <script>
6
+ import '../scripts/animations.js';
7
+ </script>
8
+ <slot />
9
+</PageFrame>
0
10
β–Όdocs_site/src/content/docs/index.mdx
+154-94
1
1
---
2
2
title: JustAPI
3
-description: Python web framework powered by Rust. 766k req/s. Built to handle real traffic.
3
+description: Python web framework powered by Rust. 766k req/s hello-world. FastAPI-compatible API, zero-config OpenAPI.
4
4
template: splash
5
-hero:
6
- tagline: Python web framework. Rust engine underneath.
7
- image:
8
- file: ../../assets/logo.svg
9
- actions:
10
- - text: Get Started
11
- link: /getting-started/overview/
12
- icon: right-arrow
13
- variant: primary
14
- - text: GitHub
15
- link: https://github.com/swadhinbiswas/JustAPI
16
- icon: external
17
- variant: minimal
18
5
---
19
6
20
-<div class="ja-hero">
21
- <img src="/og-image.png" alt="JustAPI β€” Python writes the logic. Rust does everything else." width="100%" />
7
+<div class="ja-hero" data-reveal>
8
+ <div class="ja-hero-badge">
9
+ <span class="dot"></span>
10
+ v2.0.9 Β· published on PyPI Β· 8 platforms
11
+ </div>
12
+ <h1 data-hero-title>
13
+ Python writes the logic.<br/>
14
+ <span class="grad">Rust does everything else.</span>
15
+ </h1>
16
+ <p class="ja-hero-sub">
17
+ JustAPI is a Python web framework with a Rust core. One import, zero config β€”
18
+ routing, TLS, validation, serialization and the server itself run in Rust.
19
+ </p>
20
+ <div class="ja-hero-actions">
21
+ <a class="ja-btn ja-btn--primary" href="/getting-started/overview/">Get Started β†’</a>
22
+ <a class="ja-btn ja-btn--ghost" href="https://github.com/swadhinbiswas/JustAPI" target="_blank">GitHub β†—</a>
23
+ </div>
24
+</div>
25
+
26
+<div class="ja-stats" data-reveal-group>
27
+ <div class="ja-stat" data-reveal-item>
28
+ <div class="ja-stat-value" data-count="766" data-suffix="k">0</div>
29
+ <div class="ja-stat-label">req/s hello-world</div>
30
+ </div>
31
+ <div class="ja-stat" data-reveal-item>
32
+ <div class="ja-stat-value" data-count="782" data-suffix="k">0</div>
33
+ <div class="ja-stat-label">req/s JSON echo</div>
34
+ </div>
35
+ <div class="ja-stat" data-reveal-item>
36
+ <div class="ja-stat-value" data-count="2.4" data-decimals="1" data-suffix="x">0</div>
37
+ <div class="ja-stat-label">faster than Granian</div>
38
+ </div>
39
+ <div class="ja-stat" data-reveal-item>
40
+ <div class="ja-stat-value" data-count="53" data-suffix="x">0</div>
41
+ <div class="ja-stat-label">native async DB awaits</div>
42
+ </div>
43
+ <div class="ja-stat" data-reveal-item>
44
+ <div class="ja-stat-value" data-count="181" data-suffix="k">0</div>
45
+ <div class="ja-stat-label">native CRUD SELECT</div>
46
+ </div>
47
+ <div class="ja-stat" data-reveal-item>
48
+ <div class="ja-stat-value" data-count="12" data-suffix=" MB">0</div>
49
+ <div class="ja-stat-label">resident memory</div>
50
+ </div>
22
51
</div>
23
52
24
-## Performance Matrix
53
+<div class="ja-section" data-reveal>Install in seconds</div>
25
54
26
-<div class="benchmark-card">
27
- <div class="ja-stats">
28
- <div class="ja-stat">
29
- <div class="ja-stat-value">766k</div>
30
- <div class="ja-stat-label">req/s hello-world</div>
31
- </div>
32
- <div class="ja-stat">
33
- <div class="ja-stat-value">782k</div>
34
- <div class="ja-stat-label">req/s JSON echo</div>
35
- </div>
36
- <div class="ja-stat">
37
- <div class="ja-stat-value">2.4x</div>
38
- <div class="ja-stat-label">vs Granian</div>
39
- </div>
40
- <div class="ja-stat">
41
- <div class="ja-stat-value">53x</div>
42
- <div class="ja-stat-label">native async DB</div>
43
- </div>
44
- <div class="ja-stat">
45
- <div class="ja-stat-value">12MB</div>
46
- <div class="ja-stat-label">resident memory</div>
47
- </div>
48
- <div class="ja-stat">
49
- <div class="ja-stat-value">12.4x</div>
50
- <div class="ja-stat-label">free-threaded CPU</div>
51
- </div>
55
+<div class="ja-tabs" data-tabs data-reveal>
56
+ <div class="ja-tabs-bar">
57
+ <button class="ja-tab-btn" data-tab-btn="pip" data-active="true">pip</button>
58
+ <button class="ja-tab-btn" data-tab-btn="uv">uv</button>
59
+ <button class="ja-tab-btn" data-tab-btn="scaffold">scaffold</button>
60
+ </div>
61
+ <div class="ja-tab-panel" data-tab-panel="pip">
62
+```bash
63
+pip install justapi
64
+python main.py
65
+# βœ“ JustAPI running on http://127.0.0.1:8000
66
+```
67
+ </div>
68
+ <div class="ja-tab-panel" data-tab-panel="uv" style="display:none">
69
+```bash
70
+uv pip install justapi
71
+uv run main.py
72
+```
73
+ </div>
74
+ <div class="ja-tab-panel" data-tab-panel="scaffold" style="display:none">
75
+```bash
76
+uvx justapi create my_app
77
+cd my_app && python app/main.py
78
+```
52
79
</div>
53
80
</div>
54
81
55
-## Runtime
82
+<div class="ja-section" data-reveal>Minimal app</div>
56
83
57
-<div class="ja-terminal">
84
+<div class="ja-terminal" data-terminal data-reveal>
58
85
<div class="ja-terminal-bar">
59
86
<span class="ja-terminal-dot"></span>
60
87
<span class="ja-terminal-dot"></span>
61
88
<span class="ja-terminal-dot"></span>
89
+ <span class="ja-terminal-title">main.py</span>
62
90
</div>
63
91
<div class="ja-terminal-body">
64
- <span class="cm">$</span> pip install justapi<br/>
65
- <span class="ok">βœ“ installed 2.0.9 (8 platforms)</span><br/>
66
- <span class="cm">$</span> cat main.py<br/>
67
- <span class="cm">from</span> justapi <span class="cm">import</span> JustAPIApp<br/>
68
- <br/>
69
- app = JustAPIApp()<br/>
70
- <br/>
71
- <span class="cm">@app.get</span>(<span class="ok">"/"</span>)<br/>
72
- <span class="cm">def</span> hello():<br/>
73
- &nbsp;&nbsp;&nbsp;&nbsp;<span class="cm">return</span> &#123;<span class="ok">"Hello"</span>: <span class="ok">"World"</span>&#125;<br/>
74
- <br/>
75
- app.run()<br/>
76
- <span class="ok">βœ“ server started on http://127.0.0.1:8000</span>
92
+ <div data-term-line><span class="cm">from</span> justapi <span class="cm">import</span> JustAPIApp</div>
93
+ <div data-term-line>&nbsp;</div>
94
+ <div data-term-line>app = JustAPIApp()</div>
95
+ <div data-term-line>&nbsp;</div>
96
+ <div data-term-line><span class="cm">@app.get</span>(<span class="ok">"/"</span>)</div>
97
+ <div data-term-line><span class="cm">def</span> hello():</div>
98
+ <div data-term-line>&nbsp;&nbsp;&nbsp;&nbsp;<span class="cm">return</span> &#123;<span class="ok">"Hello"</span>: <span class="ok">"World"</span>&#125;</div>
99
+ <div data-term-line>&nbsp;</div>
100
+ <div data-term-line>app.run()<span data-cursor style="display:inline-block">β–Œ</span></div>
101
+ <div data-term-line><span class="ok">βœ“</span> <span class="dim">server started Β· /docs Β· /redoc Β· OpenAPI auto</span></div>
77
102
</div>
78
103
</div>
79
104
80
-## Feature Graph
105
+<div class="ja-section" data-reveal>Benchmarks</div>
81
106
82
-<div class="ja-graph">
83
- <img src="/justapi-features.png" alt="JustAPI feature matrix" width="100%" />
107
+<div class="ja-bars" data-reveal>
108
+ <div class="ja-bar-row ja-bar-row--winner">
109
+ <span class="ja-bar-label">JustAPI</span>
110
+ <div class="ja-bar-track"><div class="ja-bar-fill" data-bar data-w="100%" data-delay="0"></div></div>
111
+ <span class="ja-bar-value">766k</span>
112
+ </div>
113
+ <div class="ja-bar-row">
114
+ <span class="ja-bar-label">Granian</span>
115
+ <div class="ja-bar-track"><div class="ja-bar-fill" data-bar data-w="41%" data-delay="150"></div></div>
116
+ <span class="ja-bar-value">314k</span>
117
+ </div>
118
+ <div class="ja-bar-row">
119
+ <span class="ja-bar-label">Robyn</span>
120
+ <div class="ja-bar-track"><div class="ja-bar-fill" data-bar data-w="5%" data-delay="300"></div></div>
121
+ <span class="ja-bar-value">39k</span>
122
+ </div>
123
+ <div class="ja-bar-row">
124
+ <span class="ja-bar-label">FastAPI</span>
125
+ <div class="ja-bar-track"><div class="ja-bar-fill" data-bar data-w="5%" data-delay="450"></div></div>
126
+ <span class="ja-bar-value">36k</span>
127
+ </div>
84
128
</div>
85
129
86
-## Pipeline
130
+<p style="font-size: 0.8rem; color: var(--ja-muted); margin-top: -0.5rem;">
131
+ hello-world, 100 concurrent connections, 30s, same hardware β€” full methodology in <a href="https://github.com/swadhinbiswas/JustAPI/blob/main/BENCHMARKS.md">BENCHMARKS.md</a>
132
+</p>
87
133
88
-<div class="ja-graph">
89
- <img src="/justapi-pipeline.svg" alt="Request pipeline β€” Rust end to end" width="100%" />
90
-</div>
91
-
92
-## Why JustAPI
134
+<div class="ja-section" data-reveal>Features</div>
93
135
94
-<div class="feature-grid">
95
- <div class="feature-card">
96
- <h3>Rust Core</h3>
97
- Networking, TLS, routing, serialization β€” all in Rust before your code runs.
136
+<div class="ja-grid" data-reveal-group>
137
+ <div class="ja-card" data-reveal-item>
138
+ <div class="ja-card-icon">⚑</div>
139
+ <h3>Rust core</h3>
140
+ <p>Networking, TLS, routing, serialization run in Rust. Python executes only your logic.</p>
141
+ </div>
142
+ <div class="ja-card" data-reveal-item>
143
+ <div class="ja-card-icon">πŸ—„οΈ</div>
144
+ <h3>Native async DB</h3>
145
+ <p><code>await app.db.query_async()</code> runs SQL on Rust's tokio runtime β€” 53Γ— faster on slow queries.</p>
98
146
</div>
99
- <div class="feature-card">
100
- <h3>Native Async DB</h3>
101
- `await app.db.query_async()` β€” SQL on Rust's tokio runtime, loop never blocked.
147
+ <div class="ja-card" data-reveal-item>
148
+ <div class="ja-card-icon">πŸ“‘</div>
149
+ <h3>Rust-native SSE</h3>
150
+ <p><code>app.sse_native()</code> streams events with zero Python per event.</p>
102
151
</div>
103
- <div class="feature-card">
104
- <h3>Rust-Native SSE</h3>
105
- `app.sse_native()` streams events with zero Python per event.
152
+ <div class="ja-card" data-reveal-item>
153
+ <div class="ja-card-icon">πŸš€</div>
154
+ <h3>Multi-worker</h3>
155
+ <p><code>justapi serve --workers N</code> β€” 1.88Γ— throughput at 4 workers, auto-scale.</p>
106
156
</div>
107
- <div class="feature-card">
108
- <h3>Multi-Worker</h3>
109
- `justapi serve --workers N --scale` β€” 1.88x throughput at 4 workers.
157
+ <div class="ja-card" data-reveal-item>
158
+ <div class="ja-card-icon">🧠</div>
159
+ <h3>Free-threaded</h3>
160
+ <p>CPython 3.14t wheels β€” CPU-bound Python scales with cores, 12.4Γ— measured.</p>
110
161
</div>
111
- <div class="feature-card">
112
- <h3>Free-Threaded</h3>
113
- CPython 3.14t wheels β€” CPU-bound Python scales with cores.
162
+ <div class="ja-card" data-reveal-item>
163
+ <div class="ja-card-icon">πŸ”</div>
164
+ <h3>Security built-in</h3>
165
+ <p>JWT, rate limiting, CORS, security headers β€” all in Rust middleware.</p>
114
166
</div>
115
- <div class="feature-card">
116
- <h3>Type-Checked</h3>
117
- `py.typed` + complete stubs β€” mypy-clean on user code.
167
+ <div class="ja-card" data-reveal-item>
168
+ <div class="ja-card-icon">πŸ›‘οΈ</div>
169
+ <h3>Production-ready</h3>
170
+ <p>Circuit breakers, OTel tracing, Prometheus metrics, health checks.</p>
171
+ </div>
172
+ <div class="ja-card" data-reveal-item>
173
+ <div class="ja-card-icon">πŸ“‹</div>
174
+ <h3>Type-checked</h3>
175
+ <p><code>py.typed</code> + complete stubs β€” mypy-clean on user code.</p>
176
+ </div>
177
+ <div class="ja-card" data-reveal-item>
178
+ <div class="ja-card-icon">πŸ”—</div>
179
+ <h3>FastAPI-compatible</h3>
180
+ <p>Same decorators, Pydantic models, Depends. Migrate with one import.</p>
118
181
</div>
119
182
</div>
120
183
121
-## Framework vs FastAPI
184
+<div class="ja-section" data-reveal>Framework comparison</div>
122
185
123
186
| | FastAPI | JustAPI |
124
187
|---|---|---|
127
190
| **JSON echo** | ~33k req/s | **~782k req/s** |
128
191
| **Native DB SELECT** | Python driver | **181k req/s (Γ—125)** |
129
192
| **Memory** | 29 MB | **12 MB** |
130
-| **OpenAPI docs** | /docs | /docs |
131
193
132
-<div class="ja-cta">
133
- <div class="ja-cta-title">Ready to build?</div>
134
- <p style={{ color: "var(--ja-muted)" }}>One import. Rust underneath. Zero config.</p>
135
- <p>
136
- <a href="/getting-started/overview/" class="ja-cta-btn" style={{ display: "inline-block", padding: "0.75rem 1.5rem", border: "1px solid var(--ja-accent)", borderRadius: "9999px", color: "var(--ja-accent)", fontWeight: 700 }}>Get Started β†’</a>
137
- </p>
194
+<div class="ja-cta" data-reveal>
195
+ <div class="ja-cta-title">Ready to build something fast?</div>
196
+ <p class="ja-cta-sub">One import. Rust underneath. Zero config.</p>
197
+ <a class="ja-btn ja-btn--primary" href="/getting-started/overview/">Get Started β†’</a>
138
198
</div>
139
199
β–Όdocs_site/src/scripts/animations.js
+182-0
1
+// JustAPI docs β€” animations (anime.js v4)
2
+// Count-up stats, scroll-reveal, hero entrance, benchmark bars, terminal.
3
+import { animate, createTimeline, utils } from 'animejs';
4
+
5
+export function initAnimations() {
6
+ const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
7
+ if (prefersReduced) return;
8
+
9
+ // ─── Animated stat counters (data-count attributes) ───
10
+ document.querySelectorAll('[data-count]').forEach((el) => {
11
+ const target = parseFloat(el.dataset.count);
12
+ const suffix = el.dataset.suffix || '';
13
+ const decimals = el.dataset.decimals ? parseInt(el.dataset.decimals, 10) : 0;
14
+ const obj = { v: 0 };
15
+ const io = new IntersectionObserver(
16
+ (entries) => {
17
+ if (!entries[0].isIntersecting) return;
18
+ io.disconnect();
19
+ animate({
20
+ targets: obj,
21
+ v: target,
22
+ duration: 1400,
23
+ ease: 'outExpo',
24
+ update: () => {
25
+ el.textContent = obj.v.toFixed(decimals) + suffix;
26
+ },
27
+ });
28
+ },
29
+ { threshold: 0.4 },
30
+ );
31
+ io.observe(el);
32
+ });
33
+
34
+ // ─── Scroll-reveal sections ───
35
+ document.querySelectorAll('[data-reveal]').forEach((el) => {
36
+ el.style.opacity = '0';
37
+ el.style.transform = 'translateY(24px)';
38
+ const io = new IntersectionObserver(
39
+ (entries) => {
40
+ if (!entries[0].isIntersecting) return;
41
+ io.disconnect();
42
+ animate({
43
+ targets: el,
44
+ opacity: [0, 1],
45
+ translateY: [24, 0],
46
+ duration: 700,
47
+ ease: 'outCubic',
48
+ delay: parseInt(el.dataset.revealDelay || '0', 10),
49
+ });
50
+ },
51
+ { threshold: 0.15 },
52
+ );
53
+ io.observe(el);
54
+ });
55
+
56
+ // ─── Staggered children reveal ───
57
+ document.querySelectorAll('[data-reveal-group]').forEach((group) => {
58
+ const items = Array.from(group.querySelectorAll('[data-reveal-item]'));
59
+ items.forEach((it) => {
60
+ it.style.opacity = '0';
61
+ it.style.transform = 'translateY(20px)';
62
+ });
63
+ const io = new IntersectionObserver(
64
+ (entries) => {
65
+ if (!entries[0].isIntersecting) return;
66
+ io.disconnect();
67
+ animate({
68
+ targets: items,
69
+ opacity: [0, 1],
70
+ translateY: [20, 0],
71
+ duration: 650,
72
+ delay: (el, i) => i * 90,
73
+ ease: 'outCubic',
74
+ });
75
+ },
76
+ { threshold: 0.12 },
77
+ );
78
+ io.observe(group);
79
+ });
80
+
81
+ // ─── Hero entrance ───
82
+ const heroTitle = document.querySelector('[data-hero-title]');
83
+ if (heroTitle) {
84
+ createTimeline({ ease: 'outExpo' })
85
+ .add({
86
+ targets: heroTitle,
87
+ opacity: [0, 1],
88
+ translateY: [30, 0],
89
+ duration: 800,
90
+ })
91
+ .add({
92
+ targets: heroTitle,
93
+ textShadow: ['0 0 0px rgba(34,197,94,0)', '0 0 40px rgba(34,197,94,0.35)'],
94
+ duration: 900,
95
+ }, '-=400');
96
+ }
97
+
98
+ // ─── Terminal typing reveal ───
99
+ const term = document.querySelector('[data-terminal]');
100
+ if (term) {
101
+ const lines = Array.from(term.querySelectorAll('[data-term-line]'));
102
+ lines.forEach((l) => { l.style.opacity = '0'; });
103
+ const io = new IntersectionObserver(
104
+ (entries) => {
105
+ if (!entries[0].isIntersecting) return;
106
+ io.disconnect();
107
+ createTimeline({ ease: 'outCubic' })
108
+ .add({
109
+ targets: lines,
110
+ opacity: [0, 1],
111
+ translateX: [-8, 0],
112
+ duration: 220,
113
+ delay: (el, i) => i * 140,
114
+ });
115
+ },
116
+ { threshold: 0.3 },
117
+ );
118
+ io.observe(term);
119
+ }
120
+
121
+ // ─── Cursor blink ───
122
+ const cursor = document.querySelector('[data-cursor]');
123
+ if (cursor) {
124
+ animate({
125
+ targets: cursor,
126
+ opacity: [1, 0],
127
+ duration: 500,
128
+ loop: true,
129
+ ease: 'steps(1)',
130
+ });
131
+ }
132
+
133
+ // ─── Benchmark bars ───
134
+ document.querySelectorAll('[data-bar]').forEach((bar) => {
135
+ const w = bar.dataset.w || '0%';
136
+ bar.style.width = '0%';
137
+ const io = new IntersectionObserver(
138
+ (entries) => {
139
+ if (!entries[0].isIntersecting) return;
140
+ io.disconnect();
141
+ animate({
142
+ targets: bar,
143
+ width: w,
144
+ duration: 1200,
145
+ ease: 'outExpo',
146
+ delay: parseInt(bar.dataset.delay || '0', 10),
147
+ });
148
+ },
149
+ { threshold: 0.4 },
150
+ );
151
+ io.observe(bar);
152
+ });
153
+
154
+ // ─── Tabs (shadcn-style) ───
155
+ document.querySelectorAll('[data-tabs]').forEach((tabs) => {
156
+ const btns = tabs.querySelectorAll('[data-tab-btn]');
157
+ const panels = tabs.querySelectorAll('[data-tab-panel]');
158
+ btns.forEach((btn) => {
159
+ btn.addEventListener('click', () => {
160
+ const target = btn.dataset.tabBtn;
161
+ btns.forEach((b) => b.setAttribute('data-active', b === btn ? 'true' : 'false'));
162
+ panels.forEach((p) => {
163
+ const show = p.dataset.tabPanel === target;
164
+ p.style.display = show ? 'block' : 'none';
165
+ if (show) {
166
+ animate({ targets: p, opacity: [0, 1], translateY: [6, 0], duration: 300, ease: 'outCubic' });
167
+ }
168
+ });
169
+ });
170
+ });
171
+ const first = tabs.querySelector('[data-tab-btn]');
172
+ if (first) first.click();
173
+ });
174
+}
175
+
176
+if (typeof window !== 'undefined') {
177
+ if (document.readyState === 'loading') {
178
+ document.addEventListener('DOMContentLoaded', initAnimations);
179
+ } else {
180
+ initAnimations();
181
+ }
182
+}
0
183
β–Όdocs_site/src/styles/custom.css
+415-597
1
1
/* ═══════════════════════════════════════════════════════════════════
2
- JustAPI Documentation β€” Sci-Fi Design System
3
- Themes: Sci-Fi Dark (default) Β· Light Β· Dracula Β· RosΓ© Pine Β· Warm
4
- Palette: green #00ff9d Β· black #05080f Β· white #e6f1ff Β· red #ff2e4d
2
+ JustAPI Docs β€” Clean, readable design system
3
+ Inspired by shadcn/ui + Stripe docs patterns.
4
+ Themes: dark (default) Β· light Β· dracula Β· rose-pine Β· warm
5
5
═══════════════════════════════════════════════════════════════════ */
6
6
7
-/* ─── Base palette (Sci-Fi Dark) ─── */
8
7
:root,
9
8
[data-theme='dark'] {
10
- --ja-bg: #05080f;
11
- --ja-bg-2: #0a0f1c;
12
- --ja-bg-3: #101830;
13
- --ja-panel: rgba(10, 15, 28, 0.8);
14
- --ja-border: #1a2440;
15
- --ja-green: #00ff9d;
16
- --ja-green-dim: #00c97d;
17
- --ja-red: #ff2e4d;
18
- --ja-white: #e6f1ff;
19
- --ja-muted: #7d8db1;
20
- --ja-accent: #00ff9d;
21
- --ja-accent-2: #00d4ff;
22
-
23
- --sl-color-accent-low: #003d26;
24
- --sl-color-accent: #00ff9d;
25
- --sl-color-accent-high: #66ffc4;
26
- --sl-color-white: #e6f1ff;
27
- --sl-color-gray-1: #e6f1ff;
28
- --sl-color-gray-2: #b8c6e0;
29
- --sl-color-gray-3: #7d8db1;
30
- --sl-color-gray-4: #55648c;
31
- --sl-color-gray-5: #2a3550;
32
- --sl-color-gray-6: #141c30;
33
- --sl-color-black: #05080f;
34
- --sl-color-bg: #05080f;
35
- --sl-color-bg-nav: #070b15;
36
- --sl-color-bg-sidebar: #070b15;
37
- --sl-color-bg-hover: #0d1526;
38
- --sl-color-text: #e6f1ff;
39
- --sl-color-text-muted: #7d8db1;
40
- --sl-color-border: #1a2440;
41
-}
42
-
43
-/* ─── Light theme ─── */
9
+ --ja-bg: #09090b;
10
+ --ja-bg-2: #101012;
11
+ --ja-bg-3: #18181b;
12
+ --ja-panel: #101012;
13
+ --ja-border: #27272a;
14
+ --ja-green: #22c55e;
15
+ --ja-green-dim: #16a34a;
16
+ --ja-red: #ef4444;
17
+ --ja-white: #fafafa;
18
+ --ja-muted: #a1a1aa;
19
+ --ja-accent: #22c55e;
20
+ --ja-accent-2: #3b82f6;
21
+
22
+ --sl-color-accent-low: #052e16;
23
+ --sl-color-accent: #22c55e;
24
+ --sl-color-accent-high: #86efac;
25
+ --sl-color-white: #fafafa;
26
+ --sl-color-gray-1: #fafafa;
27
+ --sl-color-gray-2: #d4d4d8;
28
+ --sl-color-gray-3: #a1a1aa;
29
+ --sl-color-gray-4: #71717a;
30
+ --sl-color-gray-5: #3f3f46;
31
+ --sl-color-gray-6: #18181b;
32
+ --sl-color-black: #09090b;
33
+ --sl-color-bg: #09090b;
34
+ --sl-color-bg-nav: #0c0c0e;
35
+ --sl-color-bg-sidebar: #0c0c0e;
36
+ --sl-color-bg-hover: #18181b;
37
+ --sl-color-text: #fafafa;
38
+ --sl-color-text-muted: #a1a1aa;
39
+ --sl-color-border: #27272a;
40
+}
41
+
42
+/* ─── Light ─── */
44
43
[data-theme='light'] {
45
- --ja-bg: #f4f7fb;
46
- --ja-bg-2: #ffffff;
47
- --ja-bg-3: #eef2f8;
48
- --ja-panel: rgba(255, 255, 255, 0.92);
49
- --ja-border: #d5dde8;
50
- --ja-green: #00925c;
51
- --ja-green-dim: #007a4c;
52
- --ja-red: #d91f3c;
53
- --ja-white: #0d1526;
54
- --ja-muted: #5a6a8c;
55
- --ja-accent: #00925c;
56
- --ja-accent-2: #0077a8;
57
-
58
- --sl-color-accent-low: #e0f7ee;
59
- --sl-color-accent: #00925c;
60
- --sl-color-accent-high: #00593a;
61
- --sl-color-white: #0d1526;
62
- --sl-color-gray-1: #0d1526;
63
- --sl-color-gray-2: #2a3550;
64
- --sl-color-gray-3: #5a6a8c;
65
- --sl-color-gray-4: #7d8db1;
66
- --sl-color-gray-5: #d5dde8;
67
- --sl-color-gray-6: #eef2f8;
44
+ --ja-bg: #ffffff;
45
+ --ja-bg-2: #fafafa;
46
+ --ja-bg-3: #f4f4f5;
47
+ --ja-panel: #ffffff;
48
+ --ja-border: #e4e4e7;
49
+ --ja-green: #16a34a;
50
+ --ja-green-dim: #15803d;
51
+ --ja-red: #dc2626;
52
+ --ja-white: #18181b;
53
+ --ja-muted: #71717a;
54
+ --ja-accent: #16a34a;
55
+ --ja-accent-2: #2563eb;
56
+
57
+ --sl-color-accent-low: #dcfce7;
58
+ --sl-color-accent: #16a34a;
59
+ --sl-color-accent-high: #14532d;
60
+ --sl-color-white: #18181b;
61
+ --sl-color-gray-1: #18181b;
62
+ --sl-color-gray-2: #3f3f46;
63
+ --sl-color-gray-3: #71717a;
64
+ --sl-color-gray-4: #a1a1aa;
65
+ --sl-color-gray-5: #e4e4e7;
66
+ --sl-color-gray-6: #f4f4f5;
68
67
--sl-color-black: #ffffff;
69
- --sl-color-bg: #f4f7fb;
68
+ --sl-color-bg: #ffffff;
70
69
--sl-color-bg-nav: #ffffff;
71
70
--sl-color-bg-sidebar: #ffffff;
72
- --sl-color-bg-hover: #eef2f8;
73
- --sl-color-text: #0d1526;
74
- --sl-color-text-muted: #5a6a8c;
75
- --sl-color-border: #d5dde8;
71
+ --sl-color-bg-hover: #f4f4f5;
72
+ --sl-color-text: #18181b;
73
+ --sl-color-text-muted: #71717a;
74
+ --sl-color-border: #e4e4e7;
76
75
}
77
76
78
-/* ─── Dracula theme ─── */
77
+/* ─── Dracula ─── */
79
78
[data-theme='dracula'] {
80
- --ja-bg: #282a36;
81
- --ja-bg-2: #21222c;
82
- --ja-bg-3: #343746;
83
- --ja-panel: rgba(33, 34, 44, 0.9);
84
- --ja-border: #44475a;
85
- --ja-green: #50fa7b;
86
- --ja-green-dim: #3dd962;
87
- --ja-red: #ff5555;
88
- --ja-white: #f8f8f2;
89
- --ja-muted: #6272a4;
90
- --ja-accent: #bd93f9;
91
- --ja-accent-2: #8be9fd;
92
-
93
- --sl-color-accent-low: #3d3552;
94
- --sl-color-accent: #bd93f9;
95
- --sl-color-accent-high: #e3d5ff;
96
- --sl-color-white: #f8f8f2;
97
- --sl-color-gray-1: #f8f8f2;
98
- --sl-color-gray-2: #bfc2d8;
99
- --sl-color-gray-3: #6272a4;
100
- --sl-color-gray-4: #4d5a80;
101
- --sl-color-gray-5: #343746;
102
- --sl-color-gray-6: #21222c;
103
- --sl-color-black: #282a36;
104
- --sl-color-bg: #282a36;
105
- --sl-color-bg-nav: #21222c;
106
- --sl-color-bg-sidebar: #21222c;
107
- --sl-color-bg-hover: #343746;
108
- --sl-color-text: #f8f8f2;
109
- --sl-color-text-muted: #6272a4;
110
- --sl-color-border: #44475a;
111
-}
112
-
113
-/* ─── RosΓ© Pine theme ─── */
79
+ --ja-bg: #282a36; --ja-bg-2: #21222c; --ja-bg-3: #343746; --ja-panel: #21222c;
80
+ --ja-border: #44475a; --ja-green: #50fa7b; --ja-green-dim: #3dd962; --ja-red: #ff5555;
81
+ --ja-white: #f8f8f2; --ja-muted: #6272a4; --ja-accent: #bd93f9; --ja-accent-2: #8be9fd;
82
+ --sl-color-accent-low: #3d3552; --sl-color-accent: #bd93f9; --sl-color-accent-high: #e3d5ff;
83
+ --sl-color-white: #f8f8f2; --sl-color-gray-1: #f8f8f2; --sl-color-gray-2: #bfc2d8;
84
+ --sl-color-gray-3: #6272a4; --sl-color-gray-4: #4d5a80; --sl-color-gray-5: #343746;
85
+ --sl-color-gray-6: #21222c; --sl-color-black: #282a36; --sl-color-bg: #282a36;
86
+ --sl-color-bg-nav: #21222c; --sl-color-bg-sidebar: #21222c; --sl-color-bg-hover: #343746;
87
+ --sl-color-text: #f8f8f2; --sl-color-text-muted: #6272a4; --sl-color-border: #44475a;
88
+}
89
+
90
+/* ─── RosΓ© Pine ─── */
114
91
[data-theme='rose-pine'] {
115
- --ja-bg: #191724;
116
- --ja-bg-2: #1f1d2e;
117
- --ja-bg-3: #26233a;
118
- --ja-panel: rgba(31, 29, 46, 0.9);
119
- --ja-border: #403d52;
120
- --ja-green: #9ccfd8;
121
- --ja-green-dim: #7fb4bd;
122
- --ja-red: #eb6f92;
123
- --ja-white: #e0def4;
124
- --ja-muted: #908caa;
125
- --ja-accent: #c4a7e7;
126
- --ja-accent-2: #ebbcba;
127
-
128
- --sl-color-accent-low: #3d3552;
129
- --sl-color-accent: #c4a7e7;
130
- --sl-color-accent-high: #e0def4;
131
- --sl-color-white: #e0def4;
132
- --sl-color-gray-1: #e0def4;
133
- --sl-color-gray-2: #b5b1d6;
134
- --sl-color-gray-3: #908caa;
135
- --sl-color-gray-4: #6e6a86;
136
- --sl-color-gray-5: #403d52;
137
- --sl-color-gray-6: #26233a;
138
- --sl-color-black: #191724;
139
- --sl-color-bg: #191724;
140
- --sl-color-bg-nav: #1f1d2e;
141
- --sl-color-bg-sidebar: #1f1d2e;
142
- --sl-color-bg-hover: #26233a;
143
- --sl-color-text: #e0def4;
144
- --sl-color-text-muted: #908caa;
145
- --sl-color-border: #403d52;
146
-}
147
-
148
-/* ─── Warm theme ─── */
92
+ --ja-bg: #191724; --ja-bg-2: #1f1d2e; --ja-bg-3: #26233a; --ja-panel: #1f1d2e;
93
+ --ja-border: #403d52; --ja-green: #9ccfd8; --ja-green-dim: #7fb4bd; --ja-red: #eb6f92;
94
+ --ja-white: #e0def4; --ja-muted: #908caa; --ja-accent: #c4a7e7; --ja-accent-2: #ebbcba;
95
+ --sl-color-accent-low: #3d3552; --sl-color-accent: #c4a7e7; --sl-color-accent-high: #e0def4;
96
+ --sl-color-white: #e0def4; --sl-color-gray-1: #e0def4; --sl-color-gray-2: #b5b1d6;
97
+ --sl-color-gray-3: #908caa; --sl-color-gray-4: #6e6a86; --sl-color-gray-5: #403d52;
98
+ --sl-color-gray-6: #26233a; --sl-color-black: #191724; --sl-color-bg: #191724;
99
+ --sl-color-bg-nav: #1f1d2e; --sl-color-bg-sidebar: #1f1d2e; --sl-color-bg-hover: #26233a;
100
+ --sl-color-text: #e0def4; --sl-color-text-muted: #908caa; --sl-color-border: #403d52;
101
+}
102
+
103
+/* ─── Warm ─── */
149
104
[data-theme='warm'] {
150
- --ja-bg: #1c1412;
151
- --ja-bg-2: #241a17;
152
- --ja-bg-3: #33241e;
153
- --ja-panel: rgba(36, 26, 23, 0.9);
154
- --ja-border: #4a362e;
155
- --ja-green: #ffb454;
156
- --ja-green-dim: #e89b3a;
157
- --ja-red: #ff6b6b;
158
- --ja-white: #f8ead9;
159
- --ja-muted: #a88b7a;
160
- --ja-accent: #ff9d5c;
161
- --ja-accent-2: #ffd166;
162
-
163
- --sl-color-accent-low: #4a2c1a;
164
- --sl-color-accent: #ff9d5c;
165
- --sl-color-accent-high: #ffd0a8;
166
- --sl-color-white: #f8ead9;
167
- --sl-color-gray-1: #f8ead9;
168
- --sl-color-gray-2: #d4bca8;
169
- --sl-color-gray-3: #a88b7a;
170
- --sl-color-gray-4: #7d6353;
171
- --sl-color-gray-5: #4a362e;
172
- --sl-color-gray-6: #33241e;
173
- --sl-color-black: #1c1412;
174
- --sl-color-bg: #1c1412;
175
- --sl-color-bg-nav: #241a17;
176
- --sl-color-bg-sidebar: #241a17;
177
- --sl-color-bg-hover: #33241e;
178
- --sl-color-text: #f8ead9;
179
- --sl-color-text-muted: #a88b7a;
180
- --sl-color-border: #4a362e;
181
-}
182
-
183
-/* ─── Typography ─── */
105
+ --ja-bg: #1c1412; --ja-bg-2: #241a17; --ja-bg-3: #33241e; --ja-panel: #241a17;
106
+ --ja-border: #4a362e; --ja-green: #ffb454; --ja-green-dim: #e89b3a; --ja-red: #ff6b6b;
107
+ --ja-white: #f8ead9; --ja-muted: #a88b7a; --ja-accent: #ff9d5c; --ja-accent-2: #ffd166;
108
+ --sl-color-accent-low: #4a2c1a; --sl-color-accent: #ff9d5c; --sl-color-accent-high: #ffd0a8;
109
+ --sl-color-white: #f8ead9; --sl-color-gray-1: #f8ead9; --sl-color-gray-2: #d4bca8;
110
+ --sl-color-gray-3: #a88b7a; --sl-color-gray-4: #7d6353; --sl-color-gray-5: #4a362e;
111
+ --sl-color-gray-6: #33241e; --sl-color-black: #1c1412; --sl-color-bg: #1c1412;
112
+ --sl-color-bg-nav: #241a17; --sl-color-bg-sidebar: #241a17; --sl-color-bg-hover: #33241e;
113
+ --sl-color-text: #f8ead9; --sl-color-text-muted: #a88b7a; --sl-color-border: #4a362e;
114
+}
115
+
116
+/* ─── Typography (readable, not flashy) ─── */
184
117
html {
185
118
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
186
119
-webkit-font-smoothing: antialiased;
187
120
text-rendering: optimizeLegibility;
188
- scroll-behavior: smooth;
189
121
}
190
122
191
-body {
192
- line-height: 1.7;
193
- letter-spacing: -0.011em;
194
-}
123
+body { line-height: 1.7; }
195
124
196
125
.content h1 {
197
- font-size: 2.25rem;
126
+ font-size: 2rem;
198
127
font-weight: 800;
199
- letter-spacing: -0.025em;
128
+ letter-spacing: -0.02em;
200
129
line-height: 1.2;
201
130
margin-bottom: 1rem;
202
- background: linear-gradient(90deg, var(--ja-white), var(--ja-accent));
203
- -webkit-background-clip: text;
204
- -webkit-text-fill-color: transparent;
205
- background-clip: text;
206
-}
207
-
208
-[data-theme='light'] .content h1 {
209
- background: linear-gradient(90deg, var(--ja-white), var(--ja-green));
210
- -webkit-background-clip: text;
211
- background-clip: text;
131
+ color: var(--ja-white);
212
132
}
213
133
214
134
.content h2 {
215
- font-size: 1.5rem;
135
+ font-size: 1.4rem;
216
136
font-weight: 700;
217
- margin-top: 3rem;
218
- margin-bottom: 1rem;
219
- padding-bottom: 0.75rem;
137
+ margin-top: 2.5rem;
138
+ margin-bottom: 0.75rem;
139
+ padding-bottom: 0.5rem;
220
140
border-bottom: 1px solid var(--sl-color-border);
221
141
}
222
142
223
-.content h3 {
224
- font-size: 1.2rem;
225
- font-weight: 600;
226
- margin-top: 2rem;
227
- margin-bottom: 0.75rem;
228
-}
143
+.content h3 { font-size: 1.15rem; font-weight: 600; margin-top: 2rem; margin-bottom: 0.5rem; }
144
+.content h4 { font-size: 1rem; font-weight: 600; margin-top: 1.5rem; }
229
145
230
-/* ─── Links ─── */
231
146
.content a {
232
147
color: var(--ja-accent);
233
148
text-decoration: none;
234
- transition: color 0.15s ease, text-shadow 0.15s ease;
235
-}
236
-
237
-.content a:hover {
238
- color: var(--ja-accent-high);
239
- text-decoration: underline;
240
- text-decoration-thickness: 2px;
241
- text-underline-offset: 3px;
242
- text-shadow: 0 0 12px rgba(0, 255, 157, 0.4);
149
+ transition: color 0.15s ease;
243
150
}
151
+.content a:hover { text-decoration: underline; text-underline-offset: 3px; }
244
152
245
-/* ─── Code Blocks ─── */
153
+/* ─── Code blocks (shiki themes from astro.config) ─── */
246
154
code {
247
155
font-family: 'JetBrains Mono', 'Fira Code', monospace;
248
156
font-size: 0.85em;
252
160
color: var(--ja-accent);
253
161
}
254
162
255
-[data-theme='light'] code {
256
- background: #e8f5ee;
257
- color: #00925c;
258
-}
259
-
260
163
pre {
261
164
font-family: 'JetBrains Mono', 'Fira Code', monospace;
262
165
font-size: 0.875rem;
263
- line-height: 1.6;
264
- border-radius: 12px;
265
- border: 1px solid var(--ja-border);
266
- padding: 1.25rem;
166
+ line-height: 1.65;
167
+ border-radius: 10px;
168
+ border: 1px solid var(--sl-color-border);
169
+ padding: 1.1rem 1.25rem;
267
170
overflow-x: auto;
268
171
background: var(--ja-bg-2);
269
- box-shadow: inset 0 0 30px rgba(0, 255, 157, 0.03);
270
-}
271
-
272
-[data-theme='light'] pre {
273
- background: #0d1526;
274
- color: #e6f1ff;
275
-}
276
-
277
-[data-theme='dracula'] pre { background: #21222c; }
278
-[data-theme='rose-pine'] pre { background: #1f1d2e; }
279
-[data-theme='warm'] pre { background: #241a17; }
280
-
281
-pre code {
282
- background: none;
283
- padding: 0;
284
- border-radius: 0;
285
- color: inherit;
286
172
}
287
173
288
-[data-theme='light'] pre code { color: #e6f1ff; }
174
+pre code { background: none; padding: 0; color: inherit; }
289
175
290
176
/* ─── Tables ─── */
291
177
.content table {
292
178
width: 100%;
293
179
font-size: 0.875rem;
294
- border-collapse: separate;
295
- border-spacing: 0;
296
- border-radius: 12px;
180
+ border-collapse: collapse;
181
+ border: 1px solid var(--sl-color-border);
182
+ margin: 1.25rem 0;
183
+ border-radius: 10px;
297
184
overflow: hidden;
298
- border: 1px solid var(--ja-border);
299
- margin: 1.5rem 0;
300
-}
301
-
302
-.content thead {
303
- background: var(--ja-bg-3);
304
185
}
305
186
306
187
.content th {
307
188
font-weight: 600;
308
189
text-align: left;
309
- padding: 0.75rem 1rem;
310
- border-bottom: 2px solid var(--ja-border);
311
- color: var(--ja-accent);
190
+ padding: 0.65rem 0.9rem;
191
+ background: var(--ja-bg-3);
192
+ border-bottom: 1px solid var(--sl-color-border);
312
193
}
313
194
314
195
.content td {
315
- padding: 0.75rem 1rem;
316
- border-bottom: 1px solid var(--ja-border);
196
+ padding: 0.65rem 0.9rem;
197
+ border-bottom: 1px solid var(--sl-color-border);
317
198
}
318
199
319
-.content tbody tr {
320
- transition: background 0.15s ease;
200
+.content tbody tr:last-child td { border-bottom: none; }
201
+.content tbody tr:hover { background: var(--ja-bg-3); }
202
+
203
+/* ─── Callouts ─── */
204
+.starlight-aside { border-radius: 10px; border-left-width: 3px; padding: 0.9rem 1.1rem; margin: 1.25rem 0; }
205
+.starlight-aside--note { border-left-color: var(--ja-accent-2); background: color-mix(in srgb, var(--ja-accent-2) 6%, transparent); }
206
+.starlight-aside--tip { border-left-color: var(--ja-green); background: color-mix(in srgb, var(--ja-green) 6%, transparent); }
207
+.starlight-aside--caution { border-left-color: #f59e0b; background: color-mix(in srgb, #f59e0b 6%, transparent); }
208
+.starlight-aside--danger { border-left-color: var(--ja-red); background: color-mix(in srgb, var(--ja-red) 6%, transparent); }
209
+
210
+/* ═══════════════════════════════════════════════════════════════════
211
+ Landing page β€” shadcn-style components
212
+ ═══════════════════════════════════════════════════════════════════ */
213
+
214
+/* Hero */
215
+.ja-hero {
216
+ position: relative;
217
+ text-align: center;
218
+ padding: 4rem 1rem 3rem;
219
+ margin: 0 0 2rem;
321
220
}
322
221
323
-.content tbody tr:hover {
324
- background: var(--ja-bg-3);
222
+.ja-hero-badge {
223
+ display: inline-flex;
224
+ align-items: center;
225
+ gap: 0.4rem;
226
+ padding: 0.25rem 0.75rem;
227
+ border-radius: 9999px;
228
+ border: 1px solid var(--sl-color-border);
229
+ background: var(--ja-bg-2);
230
+ font-size: 0.8rem;
231
+ color: var(--ja-muted);
232
+ margin-bottom: 1.25rem;
325
233
}
326
234
327
-/* ─── Callouts ─── */
328
-.starlight-aside {
329
- border-radius: 12px;
330
- border-left-width: 4px;
331
- padding: 1rem 1.25rem;
332
- margin: 1.5rem 0;
235
+.ja-hero-badge .dot {
236
+ width: 6px; height: 6px;
237
+ border-radius: 9999px;
238
+ background: var(--ja-green);
239
+ animation: pulse 2s ease-in-out infinite;
333
240
}
334
241
335
-.starlight-aside--note { border-left-color: var(--ja-accent-2); background: rgba(0, 212, 255, 0.06); }
336
-.starlight-aside--tip { border-left-color: var(--ja-green); background: rgba(0, 255, 157, 0.06); }
337
-.starlight-aside--caution { border-left-color: #ffb454; background: rgba(255, 180, 84, 0.06); }
338
-.starlight-aside--danger { border-left-color: var(--ja-red); background: rgba(255, 46, 77, 0.06); }
242
+@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
339
243
340
-/* ─── Sci-Fi Background Grid ─── */
341
-.site-frame::before,
342
-body::before {
343
- content: '';
344
- position: fixed;
345
- inset: 0;
346
- z-index: -1;
347
- pointer-events: none;
348
- background-image:
349
- linear-gradient(rgba(0, 255, 157, 0.03) 1px, transparent 1px),
350
- linear-gradient(90deg, rgba(0, 255, 157, 0.03) 1px, transparent 1px);
351
- background-size: 48px 48px;
352
- animation: gridScroll 24s linear infinite;
244
+.ja-hero h1 {
245
+ font-size: clamp(2.4rem, 6vw, 3.75rem);
246
+ font-weight: 800;
247
+ letter-spacing: -0.03em;
248
+ line-height: 1.1;
249
+ margin: 0 0 1rem;
250
+ background: linear-gradient(180deg, var(--ja-white), var(--ja-muted));
251
+ -webkit-background-clip: text;
252
+ -webkit-text-fill-color: transparent;
253
+ background-clip: text;
353
254
}
354
255
355
-[data-theme='light'] body::before {
356
- background-image:
357
- linear-gradient(rgba(0, 146, 92, 0.04) 1px, transparent 1px),
358
- linear-gradient(90deg, rgba(0, 146, 92, 0.04) 1px, transparent 1px);
256
+.ja-hero h1 .grad {
257
+ background: linear-gradient(90deg, var(--ja-accent), var(--ja-accent-2));
258
+ -webkit-background-clip: text;
259
+ -webkit-text-fill-color: transparent;
260
+ background-clip: text;
359
261
}
360
262
361
-@keyframes gridScroll {
362
- from { background-position: 0 0; }
363
- to { background-position: 0 48px; }
263
+.ja-hero-sub {
264
+ font-size: 1.1rem;
265
+ color: var(--ja-muted);
266
+ max-width: 34rem;
267
+ margin: 0 auto 2rem;
364
268
}
365
269
366
-/* ─── Scanline sweep ─── */
367
-.main-frame::after,
368
-body::after {
369
- content: '';
370
- position: fixed;
371
- left: 0;
372
- right: 0;
373
- height: 120px;
374
- z-index: 9999;
375
- pointer-events: none;
376
- background: linear-gradient(180deg, transparent, rgba(0, 255, 157, 0.04), transparent);
377
- animation: scanSweep 7s linear infinite;
270
+.ja-hero-actions {
271
+ display: flex;
272
+ gap: 0.75rem;
273
+ justify-content: center;
274
+ flex-wrap: wrap;
378
275
}
379
276
380
-[data-theme='light'] body::after {
381
- background: linear-gradient(180deg, transparent, rgba(0, 146, 92, 0.05), transparent);
277
+.ja-btn {
278
+ display: inline-flex;
279
+ align-items: center;
280
+ gap: 0.4rem;
281
+ padding: 0.55rem 1.25rem;
282
+ border-radius: 8px;
283
+ font-size: 0.9rem;
284
+ font-weight: 600;
285
+ transition: all 0.15s ease;
286
+ border: 1px solid transparent;
382
287
}
383
288
384
-@keyframes scanSweep {
385
- from { top: -120px; }
386
- to { top: 100vh; }
289
+.ja-btn--primary {
290
+ background: var(--ja-accent);
291
+ color: #052e16;
387
292
}
388
-
389
-/* ─── Glow Effects ─── */
390
-.content h2::after {
391
- content: '';
392
- display: block;
393
- width: 3rem;
394
- height: 3px;
395
- margin-top: 0.5rem;
396
- background: linear-gradient(90deg, var(--ja-accent), transparent);
397
- animation: glowPulse 3s ease-in-out infinite;
293
+.ja-btn--primary:hover {
294
+ filter: brightness(1.1);
295
+ text-decoration: none;
398
296
}
297
+[data-theme='light'] .ja-btn--primary { color: #fff; }
399
298
400
-@keyframes glowPulse {
401
- 0%, 100% { opacity: 0.6; }
402
- 50% { opacity: 1; box-shadow: 0 0 12px rgba(0, 255, 157, 0.6); }
299
+.ja-btn--ghost {
300
+ border-color: var(--sl-color-border);
301
+ color: var(--ja-white);
302
+ background: transparent;
403
303
}
304
+.ja-btn--ghost:hover { background: var(--ja-bg-3); text-decoration: none; }
404
305
405
-/* ─── Feature Cards ─── */
406
-.feature-grid {
306
+/* Stats */
307
+.ja-stats {
407
308
display: grid;
408
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
409
- gap: 1.5rem;
309
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
310
+ gap: 0.75rem;
410
311
margin: 2rem 0;
411
312
}
412
313
413
-.feature-card {
414
- border: 1px solid var(--ja-border);
415
- border-radius: 16px;
416
- padding: 1.75rem;
314
+.ja-stat {
315
+ border: 1px solid var(--sl-color-border);
316
+ border-radius: 12px;
317
+ padding: 1.1rem 0.75rem;
318
+ text-align: center;
417
319
background: var(--ja-panel);
418
- backdrop-filter: blur(12px);
419
- transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
420
- position: relative;
421
- overflow: hidden;
422
-}
423
-
424
-.feature-card::before {
425
- content: '';
426
- position: absolute;
427
- top: 0;
428
- left: 0;
429
- right: 0;
430
- height: 3px;
431
- background: linear-gradient(90deg, var(--ja-accent), var(--ja-accent-2));
432
- opacity: 0;
433
- transition: opacity 0.25s ease;
320
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
434
321
}
435
322
436
-.feature-card:hover {
323
+.ja-stat:hover {
437
324
border-color: var(--ja-accent);
438
- transform: translateY(-4px);
439
- box-shadow: 0 0 30px rgba(0, 255, 157, 0.15);
325
+ box-shadow: 0 4px 20px -4px color-mix(in srgb, var(--ja-accent) 25%, transparent);
440
326
}
441
327
442
-.feature-card:hover::before { opacity: 1; }
443
-
444
-.feature-card h3 {
445
- margin-top: 0;
446
- font-size: 1.1rem;
447
- font-weight: 600;
328
+.ja-stat-value {
329
+ font-size: 1.6rem;
330
+ font-weight: 800;
448
331
color: var(--ja-white);
332
+ font-family: 'JetBrains Mono', monospace;
333
+ font-variant-numeric: tabular-nums;
449
334
}
450
335
451
-/* ─── Benchmark Card ─── */
452
-.benchmark-card {
453
- border: 1px solid rgba(0, 255, 157, 0.3);
454
- border-radius: 16px;
455
- padding: 1.75rem;
456
- background: var(--ja-panel);
457
- backdrop-filter: blur(16px);
458
- margin-bottom: 1.5rem;
459
- position: relative;
460
- overflow: hidden;
336
+.ja-stat-label {
337
+ font-size: 0.8rem;
338
+ color: var(--ja-muted);
339
+ margin-top: 0.25rem;
461
340
}
462
341
463
-.benchmark-card::before {
464
- content: '';
465
- position: absolute;
466
- top: 0;
467
- left: 0;
468
- right: 0;
469
- height: 2px;
470
- background: linear-gradient(90deg, var(--ja-green), var(--ja-accent), var(--ja-accent-2));
471
- background-size: 200% 100%;
472
- animation: gradientShift 4s linear infinite;
342
+/* Cards (shadcn-style) */
343
+.ja-grid {
344
+ display: grid;
345
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
346
+ gap: 0.9rem;
347
+ margin: 1.5rem 0;
473
348
}
474
349
475
-@keyframes gradientShift {
476
- from { background-position: 0 0; }
477
- to { background-position: 200% 0; }
350
+.ja-card {
351
+ border: 1px solid var(--sl-color-border);
352
+ border-radius: 12px;
353
+ padding: 1.25rem;
354
+ background: var(--ja-panel);
355
+ transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
478
356
}
479
357
480
-/* ─── Performance Table ─── */
481
-.performance-table {
482
- width: 100%;
483
- border-collapse: separate;
484
- border-spacing: 0;
485
- border-radius: 12px;
486
- overflow: hidden;
487
- border: 1px solid var(--ja-border);
358
+.ja-card:hover {
359
+ border-color: color-mix(in srgb, var(--ja-accent) 60%, var(--sl-color-border));
360
+ box-shadow: 0 8px 30px -8px color-mix(in srgb, var(--ja-accent) 20%, transparent);
488
361
}
489
362
490
-.performance-table th {
491
- text-align: left;
492
- padding: 1rem 1.25rem;
493
- font-weight: 600;
494
- background: var(--ja-bg-3);
363
+.ja-card-icon {
364
+ width: 2rem;
365
+ height: 2rem;
366
+ border-radius: 8px;
367
+ display: flex;
368
+ align-items: center;
369
+ justify-content: center;
370
+ font-size: 1rem;
371
+ margin-bottom: 0.75rem;
372
+ background: color-mix(in srgb, var(--ja-accent) 12%, transparent);
495
373
color: var(--ja-accent);
496
374
}
497
375
498
-.performance-table td {
499
- padding: 1rem 1.25rem;
500
- text-align: right;
376
+.ja-card h3 {
377
+ font-size: 0.95rem;
378
+ font-weight: 600;
379
+ margin: 0 0 0.4rem;
380
+ color: var(--ja-white);
501
381
}
502
382
503
-.performance-table td:first-child {
504
- text-align: left;
505
- font-weight: 600;
383
+.ja-card p {
384
+ font-size: 0.85rem;
385
+ color: var(--ja-muted);
386
+ margin: 0;
387
+ line-height: 1.6;
506
388
}
507
389
508
-.performance-table .winner {
509
- color: var(--ja-green);
510
- font-weight: 700;
511
- font-size: 1.1em;
512
- text-shadow: 0 0 10px rgba(0, 255, 157, 0.5);
390
+/* Tabs (shadcn) */
391
+.ja-tabs {
392
+ border: 1px solid var(--sl-color-border);
393
+ border-radius: 12px;
394
+ overflow: hidden;
395
+ background: var(--ja-panel);
396
+ margin: 1.5rem 0;
513
397
}
514
398
515
-/* ─── Sidebar ─── */
516
-.sidebar-title {
517
- font-weight: 700;
518
- font-size: 0.8rem;
519
- text-transform: uppercase;
520
- letter-spacing: 0.05em;
399
+.ja-tabs-bar {
400
+ display: flex;
401
+ gap: 0.25rem;
402
+ padding: 0.4rem;
403
+ border-bottom: 1px solid var(--sl-color-border);
404
+ background: var(--ja-bg-2);
405
+ overflow-x: auto;
406
+}
407
+
408
+.ja-tab-btn {
409
+ padding: 0.4rem 0.9rem;
410
+ border-radius: 8px;
411
+ font-size: 0.82rem;
412
+ font-weight: 500;
521
413
color: var(--ja-muted);
414
+ border: none;
415
+ background: transparent;
416
+ cursor: pointer;
417
+ white-space: nowrap;
418
+ transition: all 0.15s ease;
419
+ font-family: inherit;
522
420
}
523
421
524
-/* ─── Badges ─── */
525
-.badge--success {
526
- background: rgba(0, 255, 157, 0.15);
527
- color: var(--ja-green);
528
- border: 1px solid rgba(0, 255, 157, 0.3);
529
- border-radius: 9999px;
530
- padding: 0.15em 0.6em;
531
- font-size: 0.75rem;
532
- font-weight: 600;
422
+.ja-tab-btn:hover { color: var(--ja-white); background: var(--ja-bg-3); }
423
+.ja-tab-btn[data-active='true'] {
424
+ color: var(--ja-white);
425
+ background: var(--ja-bg-3);
533
426
}
534
427
535
-/* ─── Cards ─── */
536
-.sl-card {
428
+.ja-tab-panel { padding: 1rem; }
429
+.ja-tab-panel pre { margin: 0; border: none; background: transparent; }
430
+
431
+/* Terminal */
432
+.ja-terminal {
433
+ border: 1px solid var(--sl-color-border);
537
434
border-radius: 12px;
538
- border: 1px solid var(--ja-border);
539
- transition: all 0.2s ease;
540
- background: var(--ja-panel);
435
+ overflow: hidden;
436
+ margin: 1.5rem 0;
437
+ background: var(--ja-bg-2);
541
438
}
542
439
543
-.sl-card:hover {
544
- border-color: var(--ja-accent);
545
- box-shadow: 0 0 24px rgba(0, 255, 157, 0.15);
440
+.ja-terminal-bar {
441
+ display: flex;
442
+ align-items: center;
443
+ gap: 0.4rem;
444
+ padding: 0.55rem 0.9rem;
445
+ background: var(--ja-bg-3);
446
+ border-bottom: 1px solid var(--sl-color-border);
546
447
}
547
448
548
-/* ─── Search ─── */
549
-.search-input {
550
- border-radius: 10px !important;
551
- font-size: 0.9rem !important;
552
-}
449
+.ja-terminal-dot { width: 10px; height: 10px; border-radius: 9999px; }
450
+.ja-terminal-dot:nth-child(1) { background: #ef4444; }
451
+.ja-terminal-dot:nth-child(2) { background: #f59e0b; }
452
+.ja-terminal-dot:nth-child(3) { background: var(--ja-green); }
553
453
554
-/* ─── Mobile Responsive ─── */
555
-@media (max-width: 768px) {
556
- .content h1 { font-size: 1.75rem; }
557
- .content h2 { font-size: 1.3rem; }
558
- .feature-grid { grid-template-columns: 1fr; }
559
- .performance-table { font-size: 0.8rem; }
454
+.ja-terminal-title {
455
+ margin-left: 0.5rem;
456
+ font-size: 0.75rem;
457
+ color: var(--ja-muted);
458
+ font-family: 'JetBrains Mono', monospace;
560
459
}
561
460
562
-/* ─── Print ─── */
563
-@media print {
564
- .sidebar, .toc-panel, .edit-link, .prev-next-nav { display: none !important; }
565
- .content { max-width: 100%; }
461
+.ja-terminal-body {
462
+ padding: 1rem 1.1rem;
463
+ font-family: 'JetBrains Mono', monospace;
464
+ font-size: 0.8rem;
465
+ line-height: 1.7;
466
+ color: var(--ja-green);
467
+ overflow-x: auto;
566
468
}
567
469
568
-/* ─── Accessibility ─── */
569
-:focus-visible {
570
- outline: 2px solid var(--ja-accent);
571
- outline-offset: 2px;
572
-}
470
+.ja-terminal-body .cm { color: var(--ja-muted); }
471
+.ja-terminal-body .ok { color: var(--ja-green); }
472
+.ja-terminal-body .err { color: var(--ja-red); }
473
+.ja-terminal-body .dim { color: var(--ja-muted); }
573
474
574
-::selection {
575
- background: rgba(0, 255, 157, 0.3);
576
- color: var(--ja-white);
577
-}
475
+/* Benchmark bars */
476
+.ja-bars { margin: 1.5rem 0; display: flex; flex-direction: column; gap: 0.6rem; }
578
477
579
-[data-theme='light'] ::selection {
580
- background: rgba(0, 146, 92, 0.2);
478
+.ja-bar-row {
479
+ display: grid;
480
+ grid-template-columns: 7rem 1fr 4rem;
481
+ align-items: center;
482
+ gap: 0.75rem;
483
+ font-size: 0.85rem;
581
484
}
582
485
583
-/* ─── Scrollbar ─── */
584
-::-webkit-scrollbar { width: 8px; height: 8px; }
585
-::-webkit-scrollbar-track { background: transparent; }
586
-::-webkit-scrollbar-thumb {
587
- background: var(--ja-border);
588
- border-radius: 4px;
486
+.ja-bar-label { color: var(--ja-muted); text-align: right; font-weight: 500; }
487
+.ja-bar-track {
488
+ height: 1.6rem;
489
+ border-radius: 6px;
490
+ background: var(--ja-bg-3);
491
+ overflow: hidden;
589
492
}
590
-::-webkit-scrollbar-thumb:hover { background: var(--ja-accent); }
591
493
592
-/* ─── Animations ─── */
593
-@keyframes fadeInUp {
594
- from { opacity: 0; transform: translateY(12px); }
595
- to { opacity: 1; transform: translateY(0); }
494
+.ja-bar-fill {
495
+ height: 100%;
496
+ border-radius: 6px;
497
+ background: linear-gradient(90deg, var(--ja-accent), var(--ja-accent-2));
498
+ width: 0%;
596
499
}
597
500
598
-.content > * {
599
- animation: fadeInUp 0.3s ease-out;
501
+.ja-bar-row--winner .ja-bar-fill { background: linear-gradient(90deg, var(--ja-green), var(--ja-accent-2)); }
502
+.ja-bar-row--winner .ja-bar-label { color: var(--ja-green); font-weight: 700; }
503
+.ja-bar-value { font-family: 'JetBrains Mono', monospace; font-size: 0.8rem; color: var(--ja-white); }
504
+
505
+/* CTA */
506
+.ja-cta {
507
+ border: 1px solid var(--sl-color-border);
508
+ border-radius: 16px;
509
+ padding: 2.5rem 1.5rem;
510
+ text-align: center;
511
+ margin: 2.5rem 0 1rem;
512
+ background:
513
+ radial-gradient(ellipse at top, color-mix(in srgb, var(--ja-accent) 8%, transparent), transparent 60%),
514
+ var(--ja-panel);
600
515
}
601
516
602
-/* ─── Code Group Tabs ─── */
603
-.tab-label {
604
- font-family: 'JetBrains Mono', monospace;
605
- font-size: 0.8rem;
517
+.ja-cta-title {
518
+ font-size: 1.6rem;
519
+ font-weight: 800;
520
+ margin: 0 0 0.5rem;
521
+ color: var(--ja-white);
606
522
}
607
523
608
-/* ─── Theme Switcher Widget ─── */
524
+.ja-cta-sub { color: var(--ja-muted); margin: 0 0 1.5rem; font-size: 0.95rem; }
525
+
526
+/* Theme switcher */
609
527
.ja-theme-switcher {
610
528
position: fixed;
611
- bottom: 1.25rem;
612
- right: 1.25rem;
529
+ bottom: 1rem;
530
+ right: 1rem;
613
531
z-index: 1000;
614
532
display: flex;
615
- gap: 0.35rem;
616
- padding: 0.35rem;
533
+ gap: 0.3rem;
534
+ padding: 0.3rem;
617
535
border-radius: 9999px;
618
- border: 1px solid var(--ja-border);
536
+ border: 1px solid var(--sl-color-border);
619
537
background: var(--ja-panel);
620
- backdrop-filter: blur(12px);
538
+ box-shadow: 0 4px 20px -4px rgba(0,0,0,0.4);
621
539
}
622
540
623
541
.ja-theme-btn {
624
- width: 2rem;
625
- height: 2rem;
542
+ width: 1.75rem;
543
+ height: 1.75rem;
626
544
border-radius: 9999px;
627
545
border: 1px solid transparent;
628
546
cursor: pointer;
629
547
transition: all 0.2s ease;
630
- position: relative;
631
548
}
632
-
633
549
.ja-theme-btn:hover { transform: scale(1.15); }
634
-.ja-theme-btn[data-active='true'] {
635
- border-color: var(--ja-white);
636
- box-shadow: 0 0 12px currentColor;
637
-}
550
+.ja-theme-btn[data-active='true'] { border-color: var(--ja-white); box-shadow: 0 0 8px currentColor; }
638
551
639
-.ja-theme-btn--dark { background: #05080f; color: #00ff9d; }
640
-.ja-theme-btn--light { background: #f4f7fb; color: #00925c; }
552
+.ja-theme-btn--dark { background: #09090b; color: #22c55e; }
553
+.ja-theme-btn--light { background: #ffffff; color: #16a34a; }
641
554
.ja-theme-btn--dracula { background: #282a36; color: #bd93f9; }
642
555
.ja-theme-btn--rosepine { background: #191724; color: #c4a7e7; }
643
556
.ja-theme-btn--warm { background: #1c1412; color: #ff9d5c; }
644
557
645
-/* ─── Landing Page ─── */
646
-.ja-hero {
647
- position: relative;
648
- border: 1px solid var(--ja-border);
649
- border-radius: 24px;
650
- overflow: hidden;
651
- margin: 1.5rem 0;
652
- background: var(--ja-bg-2);
653
-}
654
-
655
-.ja-hero img {
656
- display: block;
657
- width: 100%;
658
- height: auto;
659
-}
660
-
661
-.ja-stats {
662
- display: grid;
663
- grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
664
- gap: 1rem;
665
- margin: 2rem 0;
666
-}
667
-
668
-.ja-stat {
669
- border: 1px solid var(--ja-border);
670
- border-radius: 16px;
671
- padding: 1.25rem;
672
- text-align: center;
673
- background: var(--ja-panel);
674
- transition: all 0.25s ease;
675
-}
676
-
677
-.ja-stat:hover {
678
- border-color: var(--ja-accent);
679
- transform: translateY(-3px);
680
- box-shadow: 0 0 24px rgba(0, 255, 157, 0.12);
681
-}
682
-
683
-.ja-stat-value {
684
- font-size: 2rem;
685
- font-weight: 800;
686
- color: var(--ja-accent);
687
- font-family: 'JetBrains Mono', monospace;
688
- text-shadow: 0 0 16px rgba(0, 255, 157, 0.4);
689
-}
690
-
691
-.ja-stat-label {
692
- font-size: 0.85rem;
693
- color: var(--ja-muted);
694
- margin-top: 0.25rem;
695
-}
696
-
697
-/* ─── Landing: Code Terminal ─── */
698
-.ja-terminal {
699
- border: 1px solid var(--ja-border);
700
- border-radius: 16px;
701
- overflow: hidden;
702
- margin: 2rem 0;
703
- background: var(--ja-bg-2);
704
-}
705
-
706
-.ja-terminal-bar {
558
+/* Section headings on landing */
559
+.ja-section {
560
+ margin: 2.5rem 0 1.25rem;
561
+ font-size: 1.1rem;
562
+ font-weight: 700;
563
+ color: var(--ja-white);
707
564
display: flex;
708
565
align-items: center;
709
566
gap: 0.5rem;
710
- padding: 0.6rem 1rem;
711
- background: var(--ja-bg-3);
712
- border-bottom: 1px solid var(--ja-border);
713
567
}
714
568
715
-.ja-terminal-dot {
716
- width: 12px;
717
- height: 12px;
718
- border-radius: 9999px;
719
-}
720
-
721
-.ja-terminal-dot:nth-child(1) { background: #ff2e4d; }
722
-.ja-terminal-dot:nth-child(2) { background: #ffb454; }
723
-.ja-terminal-dot:nth-child(3) { background: #00ff9d; }
724
-
725
-.ja-terminal-body {
726
- padding: 1.25rem;
727
- font-family: 'JetBrains Mono', monospace;
728
- font-size: 0.85rem;
729
- color: var(--ja-green);
730
- line-height: 1.7;
731
-}
732
-
733
-.ja-terminal-body .cm { color: var(--ja-muted); }
734
-.ja-terminal-body .ok { color: var(--ja-green); }
735
-.ja-terminal-body .err { color: var(--ja-red); }
736
-
737
-/* ─── Landing: Feature Matrix Graph ─── */
738
-.ja-graph {
739
- border: 1px solid var(--ja-border);
740
- border-radius: 16px;
741
- padding: 1rem;
742
- background: var(--ja-bg-2);
743
- margin: 2rem 0;
569
+.ja-section::after {
570
+ content: '';
571
+ flex: 1;
572
+ height: 1px;
573
+ background: var(--sl-color-border);
744
574
}
745
575
746
-.ja-graph img {
747
- display: block;
748
- width: 100%;
749
- height: auto;
750
-}
576
+/* Accessibility */
577
+:focus-visible { outline: 2px solid var(--ja-accent); outline-offset: 2px; }
578
+::selection { background: color-mix(in srgb, var(--ja-accent) 25%, transparent); }
751
579
752
-/* ─── Landing: Call-to-Action ─── */
753
-.ja-cta {
754
- border: 1px solid var(--ja-accent);
755
- border-radius: 16px;
756
- padding: 2rem;
757
- text-align: center;
758
- margin: 2rem 0;
759
- background:
760
- radial-gradient(ellipse at center, rgba(0, 255, 157, 0.08), transparent 70%),
761
- var(--ja-panel);
762
- animation: ctaGlow 4s ease-in-out infinite;
763
-}
580
+/* Scrollbar */
581
+::-webkit-scrollbar { width: 8px; height: 8px; }
582
+::-webkit-scrollbar-track { background: transparent; }
583
+::-webkit-scrollbar-thumb { background: var(--sl-color-gray-5); border-radius: 4px; }
584
+::-webkit-scrollbar-thumb:hover { background: var(--sl-color-gray-4); }
764
585
765
-@keyframes ctaGlow {
766
- 0%, 100% { box-shadow: 0 0 20px rgba(0, 255, 157, 0.1); }
767
- 50% { box-shadow: 0 0 40px rgba(0, 255, 157, 0.25); }
586
+/* Mobile */
587
+@media (max-width: 768px) {
588
+ .content h1 { font-size: 1.6rem; }
589
+ .ja-hero { padding: 2.5rem 0.5rem 2rem; }
590
+ .ja-bar-row { grid-template-columns: 5.5rem 1fr 3.5rem; }
591
+ .ja-stats { grid-template-columns: repeat(2, 1fr); }
768
592
}
769
593
770
-.ja-cta-title {
771
- font-size: 1.75rem;
772
- font-weight: 800;
773
- background: linear-gradient(90deg, var(--ja-white), var(--ja-accent));
774
- -webkit-background-clip: text;
775
- -webkit-text-fill-color: transparent;
776
- background-clip: text;
777
-}
594
+/* Print */
595
+@media print { .sidebar, .toc-panel, .edit-link, .prev-next-nav, .ja-theme-switcher { display: none !important; } }
778
596