drenayaz commited on
Commit
49c6bc2
Β·
1 Parent(s): 2cfce74

improve frontpage

Browse files
Files changed (3) hide show
  1. hand_tracker_v2/static/reachy-mini-waving.png +3 -0
  2. index.html +93 -228
  3. style.css +172 -336
hand_tracker_v2/static/reachy-mini-waving.png ADDED

Git LFS Details

  • SHA256: cd5f98954b9ba719edfb92a844327bb5fa36a06af7a0f314d5430a0e5a37b8a2
  • Pointer size: 131 Bytes
  • Size of remote file: 186 kB
index.html CHANGED
@@ -1,235 +1,100 @@
1
- <!doctype html>
2
- <html>
3
-
4
  <head>
5
- <meta charset="utf-8" />
6
- <meta name="viewport" content="width=device-width" />
7
- <title> Hand Tracker V2 </title>
8
- <link rel="stylesheet" href="style.css" />
 
9
  </head>
10
-
11
  <body>
12
- <div class="hero">
13
- <div class="hero-content">
14
- <div class="app-icon">πŸ€–πŸ‘‹</div>
15
- <h1> Hand Tracker V2 </h1>
16
- <p class="tagline">Reachy Mini follows your hand!</p>
17
- </div>
18
- </div>
19
-
20
- <div class="container">
21
- <div class="main-card">
22
- <div class="app-preview">
23
- <div class="preview-image">
24
- <div class="camera-feed">πŸ› οΈ</div>
25
- </div>
26
- </div>
27
- </div>
28
- </div>
29
-
30
- <div class="download-section">
31
- <div class="download-card">
32
- <h2>Install This App</h2>
33
-
34
- <div class="dashboard-config">
35
- <label for="dashboardUrl">Your Reachy Dashboard URL:</label>
36
- <input type="url" id="dashboardUrl" value="http://localhost:8000"
37
- placeholder="http://your-reachy-ip:8000" />
38
- </div>
39
-
40
- <button id="installBtn" class="install-btn primary">
41
- <span class="btn-icon">πŸ“₯</span>
42
- Install Hand Tracker V2 to Reachy Mini
43
- </button>
44
-
45
- <div id="installStatus" class="install-status"></div>
46
-
47
- </div>
48
- </div>
49
-
50
- <div class="footer">
51
- <p>
52
- πŸ€– Hand Tracker V2 β€’
53
- <a href="https://github.com/pollen-robotics" target="_blank">Pollen Robotics</a> β€’
54
- <a href="https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Apps" target="_blank">Browse More
55
- Apps</a>
56
- </p>
57
- </div>
58
- </div>
59
-
60
- <script>
61
- // Get the current Hugging Face Space URL as the repository URL
62
- function getCurrentSpaceUrl() {
63
- // Get current page URL and convert to repository format
64
- const currentUrl = window.location.href;
65
-
66
- // Remove any trailing slashes and query parameters
67
- const cleanUrl = currentUrl.split('?')[0].replace(/\/$/, '');
68
-
69
- return cleanUrl;
70
- }
71
-
72
- // Parse TOML content to extract project name
73
- function parseTomlProjectName(tomlContent) {
74
- try {
75
- const lines = tomlContent.split('\n');
76
- let inProjectSection = false;
77
-
78
- for (const line of lines) {
79
- const trimmedLine = line.trim();
80
-
81
- // Check if we're entering the [project] section
82
- if (trimmedLine === '[project]') {
83
- inProjectSection = true;
84
- continue;
85
- }
86
-
87
- // Check if we're entering a different section
88
- if (trimmedLine.startsWith('[') && trimmedLine !== '[project]') {
89
- inProjectSection = false;
90
- continue;
91
- }
92
-
93
- // If we're in the project section, look for the name field
94
- if (inProjectSection && trimmedLine.startsWith('name')) {
95
- const match = trimmedLine.match(/name\s*=\s*["']([^"']+)["']/);
96
- if (match) {
97
- // Convert to lowercase and replace invalid characters for app naming
98
- return match[1].toLowerCase().replace(/[^a-z0-9-_]/g, '-');
99
- }
100
- }
101
- }
102
-
103
- throw new Error('Project name not found in pyproject.toml');
104
- } catch (error) {
105
- console.error('Error parsing pyproject.toml:', error);
106
- return 'unknown-app';
107
- }
108
- }
109
-
110
- // Fetch and parse pyproject.toml from the current space
111
- async function getAppNameFromCurrentSpace() {
112
- try {
113
- // Fetch pyproject.toml from the current space
114
- const response = await fetch('./pyproject.toml');
115
- if (!response.ok) {
116
- throw new Error(`Failed to fetch pyproject.toml: ${response.status}`);
117
- }
118
 
119
- const tomlContent = await response.text();
120
- return parseTomlProjectName(tomlContent);
121
- } catch (error) {
122
- console.error('Error fetching app name from current space:', error);
123
- // Fallback to extracting from URL if pyproject.toml is not accessible
124
- const url = getCurrentSpaceUrl();
125
- const parts = url.split('/');
126
- const spaceName = parts[parts.length - 1];
127
- return spaceName.toLowerCase().replace(/[^a-z0-9-_]/g, '-');
128
- }
129
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
- async function installToReachy() {
132
- const dashboardUrl = document.getElementById('dashboardUrl').value.trim();
133
- const statusDiv = document.getElementById('installStatus');
134
- const installBtn = document.getElementById('installBtn');
135
-
136
- if (!dashboardUrl) {
137
- showStatus('error', 'Please enter your Reachy dashboard URL');
138
- return;
139
- }
140
-
141
- try {
142
- installBtn.disabled = true;
143
- installBtn.innerHTML = '<span class="btn-icon">⏳</span>Installing...';
144
- showStatus('loading', 'Connecting to your Reachy dashboard...');
145
-
146
- // Test connection
147
- const testResponse = await fetch(`${dashboardUrl}/api/status`, {
148
- method: 'GET',
149
- mode: 'cors',
150
- });
151
-
152
- if (!testResponse.ok) {
153
- throw new Error('Cannot connect to dashboard. Make sure the URL is correct and the dashboard is running.');
154
- }
155
-
156
- showStatus('loading', 'Reading app configuration...');
157
-
158
- // Get app name from pyproject.toml in current space
159
- const appName = await getAppNameFromCurrentSpace();
160
-
161
- // Get current space URL as repository URL
162
- const repoUrl = getCurrentSpaceUrl();
163
-
164
- showStatus('loading', `Starting installation of "${appName}"...`);
165
-
166
- // Start installation
167
- const installResponse = await fetch(`${dashboardUrl}/api/install`, {
168
- method: 'POST',
169
- mode: 'cors',
170
- headers: {
171
- 'Content-Type': 'application/json',
172
- },
173
- body: JSON.stringify({
174
- url: repoUrl,
175
- name: appName
176
- })
177
- });
178
-
179
- const result = await installResponse.json();
180
-
181
- if (installResponse.ok) {
182
- showStatus('success', `βœ… Installation started for "${appName}"! Check your dashboard for progress.`);
183
- setTimeout(() => {
184
- showStatus('info', `Open your dashboard at ${dashboardUrl} to see the installed app.`);
185
- }, 3000);
186
- } else {
187
- throw new Error(result.detail || 'Installation failed');
188
- }
189
-
190
- } catch (error) {
191
- console.error('Installation error:', error);
192
- showStatus('error', `❌ ${error.message}`);
193
- } finally {
194
- installBtn.disabled = false;
195
- installBtn.innerHTML = '<span class="btn-icon">πŸ“₯</span>Install App to Reachy';
196
- }
197
- }
198
-
199
- function showStatus(type, message) {
200
- const statusDiv = document.getElementById('installStatus');
201
- statusDiv.className = `install-status ${type}`;
202
- statusDiv.textContent = message;
203
- statusDiv.style.display = 'block';
204
- }
205
-
206
- function copyToClipboard() {
207
- const repoUrl = document.getElementById('repoUrl').textContent;
208
- navigator.clipboard.writeText(repoUrl).then(() => {
209
- showStatus('success', 'πŸ“‹ Repository URL copied to clipboard!');
210
- }).catch(() => {
211
- showStatus('error', 'Failed to copy URL. Please copy manually.');
212
- });
213
- }
214
-
215
- // Update the displayed repository URL on page load
216
- document.addEventListener('DOMContentLoaded', () => {
217
- // Auto-detect local dashboard
218
- const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
219
- if (isLocalhost) {
220
- document.getElementById('dashboardUrl').value = 'http://localhost:8000';
221
- }
222
-
223
- // Update the repository URL display if element exists
224
- const repoUrlElement = document.getElementById('repoUrl');
225
- if (repoUrlElement) {
226
- repoUrlElement.textContent = getCurrentSpaceUrl();
227
- }
228
- });
229
-
230
- // Event listeners
231
- document.getElementById('installBtn').addEventListener('click', installToReachy);
232
- </script>
233
  </body>
234
-
235
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
 
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Hand Tracking App - Reachy Mini</title>
7
+ <meta name="description" content="Control Reachy Mini's antennas with your hands using MediaPipe hand tracking technology.">
8
+ <link rel="stylesheet" href="style.css">
9
  </head>
 
10
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ <!-- Hero Section -->
13
+ <section class="hero">
14
+ <div class="container">
15
+ <div class="hero-grid">
16
+ <div>
17
+ <img src="assets/reachy-mini-hand-tracking.gif" alt="Hand Tracking Demo" class="hero-gif">
18
+ </div>
19
+
20
+ <div class="hero-content">
21
+ <div class="hero-header">
22
+ <img src="assets/reachy-mini-waving.png" alt="Reachy Mini" class="hero-icon">
23
+ <h1 class="hero-title">Hand Tracking</h1>
24
+ </div>
25
+
26
+ <div class="tags">
27
+ <span class="tag">vision</span>
28
+ <span class="tag">lite</span>
29
+ <span class="tag">wireless</span>
30
+ <span class="tag">hands-tracking</span>
31
+ <span class="tag">mediapipe</span>
32
+ </div>
33
+
34
+ <p class="hero-description">
35
+ Control Reachy Mini's antennas with your hands using real-time hand tracking powered by MediaPipe.
36
+ </p>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </section>
41
+
42
+ <!-- Technical Section -->
43
+ <section class="technical">
44
+ <div class="container">
45
+ <div class="technical-grid">
46
+ <div>
47
+ <h2 class="section-title">How it works</h2>
48
+ <p class="section-text">
49
+ This application uses <strong>MediaPipe Hands</strong> to detect and track your hands in real-time through Reachy Mini's camera.
50
+ </p>
51
+ <p class="section-text">
52
+ The 2D position of your hands is mapped to control Reachy Mini's antennas. Move your hands up, down, left, or right, and watch the antennas follow your movements wirelessly.
53
+ </p>
54
+
55
+ <h2 class="section-title" style="margin-top: 2rem;">How to install</h2>
56
+ <ol class="install-steps">
57
+ <li>
58
+ <span>Make sure you already have the <a href="https://github.com/pollen-robotics/reachy-mini-desktop-app/releases/latest" class="link-tertiary">dashboard</a> installed.</span>
59
+ </li>
60
+ <li>
61
+ <span>Connect to your Reachy Mini using the dashboard.</span>
62
+ </li>
63
+ <li>
64
+ <span>Navigate to the "Applications" tab in the dashboard.</span>
65
+ </li>
66
+ <li>
67
+ <span>Find "Hand Tracking" in the list of available applications.</span>
68
+ </li>
69
+ <li>
70
+ <span>Click "Install" and allow camera access when prompted.</span>
71
+ </li>
72
+ </ol>
73
+ </div>
74
+
75
+ <div>
76
+ <img src="assets/reachy-mini-demo-dashboard.gif" alt="Dashboard Demo" class="demo-gif">
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </section>
81
+
82
+ <!-- Footer -->
83
+ <footer class="footer">
84
+ <div class="container">
85
+ <div class="footer-content">
86
+ <div class="footer-links">
87
+ <a href="https://github.com/pollen-robotics/reachy_mini" target="_blank" rel="noopener">> Documentation</a>
88
+ <a href="https://huggingface.co/spaces/pollen-robotics/Reachy-Mini_Best_Spaces" target="_blank" rel="noopener">> Browse other apps</a>
89
+ <a href="https://huggingface.co/blog/pollen-robotics/make-and-publish-your-reachy-mini-apps" target="_blank" rel="noopener">> Make and publish your Reachy Mini app</a>
90
+ </div>
91
+ <p class="footer-help">
92
+ Need help? Contact us on <a href="https://discord.gg/u3QtUBhy" target="_blank" rel="noopener">Discord</a> and join the community.
93
+ Proudly brought by <a href="https://www.pollen-robotics.com/" target="_blank">Pollen Robotics</a> x πŸ€— <a href="https://huggingface.co" target="_blank">Hugging Face</a>
94
+ </p>
95
+ </div>
96
+ </div>
97
+ </footer>
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  </body>
 
100
  </html>
style.css CHANGED
@@ -1,411 +1,247 @@
1
- * {
2
- margin: 0;
3
- padding: 0;
4
- box-sizing: border-box;
5
- }
6
-
7
- body {
8
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
9
- line-height: 1.6;
10
- color: #333;
11
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
12
- min-height: 100vh;
13
- }
14
-
15
- .hero {
16
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
- color: white;
18
- padding: 4rem 2rem;
19
- text-align: center;
20
- }
21
-
22
- .hero-content {
23
- max-width: 800px;
24
- margin: 0 auto;
25
- }
26
 
27
- .app-icon {
28
- font-size: 4rem;
29
- margin-bottom: 1rem;
30
- display: inline-block;
31
  }
32
 
33
- .hero h1 {
34
- font-size: 3rem;
35
- font-weight: 700;
36
- margin-bottom: 1rem;
37
- background: linear-gradient(45deg, #fff, #f0f9ff);
38
- background-clip: text;
39
- -webkit-background-clip: text;
40
- -webkit-text-fill-color: transparent;
 
 
41
  }
42
 
43
- .tagline {
44
- font-size: 1.25rem;
45
- opacity: 0.9;
46
- max-width: 600px;
47
- margin: 0 auto;
 
48
  }
49
 
50
  .container {
51
- max-width: 1200px;
52
- margin: 0 auto;
53
- padding: 0 2rem;
54
- position: relative;
55
- z-index: 2;
56
- }
57
-
58
- .main-card {
59
- background: white;
60
- border-radius: 20px;
61
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
62
- margin-top: -2rem;
63
- overflow: hidden;
64
- margin-bottom: 3rem;
65
- }
66
-
67
- .app-preview {
68
- background: linear-gradient(135deg, #1e3a8a, #3b82f6);
69
- padding: 3rem;
70
- color: white;
71
- text-align: center;
72
- position: relative;
73
- }
74
-
75
- .preview-image {
76
- background: #000;
77
- border-radius: 15px;
78
- padding: 2rem;
79
- max-width: 500px;
80
- margin: 0 auto;
81
- position: relative;
82
- overflow: hidden;
83
- }
84
-
85
- .camera-feed {
86
- font-size: 4rem;
87
- margin-bottom: 1rem;
88
- opacity: 0.7;
89
- }
90
-
91
- .detection-overlay {
92
- position: absolute;
93
- top: 50%;
94
- left: 50%;
95
- transform: translate(-50%, -50%);
96
- width: 100%;
97
- }
98
-
99
- .bbox {
100
- background: rgba(34, 197, 94, 0.9);
101
- color: white;
102
- padding: 0.5rem 1rem;
103
- border-radius: 8px;
104
- font-size: 0.9rem;
105
- font-weight: 600;
106
- margin: 0.5rem;
107
- display: inline-block;
108
- border: 2px solid #22c55e;
109
- }
110
-
111
- .app-details {
112
- padding: 3rem;
113
- }
114
-
115
- .app-details h2 {
116
- font-size: 2rem;
117
- color: #1e293b;
118
- margin-bottom: 2rem;
119
- text-align: center;
120
- }
121
-
122
- .template-info {
123
- display: grid;
124
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
125
- gap: 2rem;
126
- margin-bottom: 3rem;
127
- }
128
-
129
- .info-box {
130
- background: #f0f9ff;
131
- border: 2px solid #e0f2fe;
132
- border-radius: 12px;
133
- padding: 2rem;
134
- }
135
-
136
- .info-box h3 {
137
- color: #0c4a6e;
138
- margin-bottom: 1rem;
139
- font-size: 1.2rem;
140
- }
141
-
142
- .info-box p {
143
- color: #0369a1;
144
- line-height: 1.6;
145
- }
146
-
147
- .how-to-use {
148
- background: #fefce8;
149
- border: 2px solid #fde047;
150
- border-radius: 12px;
151
- padding: 2rem;
152
- margin-top: 3rem;
153
  }
154
 
155
- .how-to-use h3 {
156
- color: #a16207;
157
- margin-bottom: 1.5rem;
158
- font-size: 1.3rem;
159
- text-align: center;
160
- }
161
-
162
- .steps {
163
- display: flex;
164
- flex-direction: column;
165
- gap: 1.5rem;
166
  }
167
 
168
- .step {
169
- display: flex;
170
- align-items: flex-start;
171
- gap: 1rem;
 
172
  }
173
 
174
- .step-number {
175
- background: #eab308;
176
- color: white;
177
- width: 2rem;
178
- height: 2rem;
179
- border-radius: 50%;
180
- display: flex;
181
- align-items: center;
182
- justify-content: center;
183
- font-weight: bold;
184
- flex-shrink: 0;
185
  }
186
 
187
- .step h4 {
188
- color: #a16207;
189
- margin-bottom: 0.5rem;
190
- font-size: 1.1rem;
191
  }
192
 
193
- .step p {
194
- color: #ca8a04;
 
 
 
 
195
  }
196
 
197
- .download-card {
198
- background: white;
199
- border-radius: 20px;
200
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
201
- padding: 3rem;
202
- text-align: center;
203
  }
204
 
205
- .download-card h2 {
206
- font-size: 2rem;
207
- color: #1e293b;
208
- margin-bottom: 1rem;
209
  }
210
 
211
- .download-card>p {
212
- color: #64748b;
213
- font-size: 1.1rem;
214
- margin-bottom: 2rem;
 
 
215
  }
216
 
217
- .dashboard-config {
218
- margin-bottom: 2rem;
219
- text-align: left;
220
- max-width: 400px;
221
- margin-left: auto;
222
- margin-right: auto;
 
 
 
223
  }
224
 
225
- .dashboard-config label {
226
- display: block;
227
- color: #374151;
228
- font-weight: 600;
229
- margin-bottom: 0.5rem;
230
  }
231
 
232
- .dashboard-config input {
233
- width: 100%;
234
- padding: 0.75rem 1rem;
235
- border: 2px solid #e5e7eb;
236
- border-radius: 8px;
237
- font-size: 0.95rem;
238
- transition: border-color 0.2s;
239
  }
240
 
241
- .dashboard-config input:focus {
242
- outline: none;
243
- border-color: #667eea;
 
 
244
  }
245
 
246
- .install-btn {
247
- background: linear-gradient(135deg, #667eea, #764ba2);
248
- color: white;
249
- border: none;
250
- padding: 1.25rem 3rem;
251
- border-radius: 16px;
252
- font-size: 1.2rem;
253
- font-weight: 700;
254
- cursor: pointer;
255
- transition: all 0.3s ease;
256
- display: inline-flex;
257
- align-items: center;
258
- gap: 0.75rem;
259
- margin-bottom: 2rem;
260
- box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
261
  }
262
 
263
- .install-btn:hover:not(:disabled) {
264
- transform: translateY(-3px);
265
- box-shadow: 0 15px 35px rgba(102, 126, 234, 0.4);
 
 
266
  }
267
 
268
- .install-btn:disabled {
269
- opacity: 0.7;
270
- cursor: not-allowed;
271
- transform: none;
 
272
  }
273
 
274
- .manual-option {
275
- background: #f8fafc;
276
- border-radius: 12px;
277
- padding: 2rem;
278
- margin-top: 2rem;
279
  }
280
 
281
- .manual-option h3 {
282
- color: #1e293b;
283
- margin-bottom: 1rem;
284
- font-size: 1.2rem;
285
  }
286
 
287
- .manual-option>p {
288
- color: #64748b;
289
- margin-bottom: 1rem;
290
  }
291
 
292
- .btn-icon {
293
- font-size: 1.1rem;
 
 
 
 
 
294
  }
295
 
296
- .install-status {
297
- padding: 1rem;
298
- border-radius: 8px;
299
- font-size: 0.9rem;
300
- text-align: center;
301
- display: none;
302
- margin-top: 1rem;
 
 
 
 
 
303
  }
304
 
305
- .install-status.success {
306
- background: #dcfce7;
307
- color: #166534;
308
- border: 1px solid #bbf7d0;
309
  }
310
 
311
- .install-status.error {
312
- background: #fef2f2;
313
- color: #dc2626;
314
- border: 1px solid #fecaca;
315
  }
316
 
317
- .install-status.loading {
318
- background: #dbeafe;
319
- color: #1d4ed8;
320
- border: 1px solid #bfdbfe;
321
  }
322
 
323
- .install-status.info {
324
- background: #e0f2fe;
325
- color: #0369a1;
326
- border: 1px solid #7dd3fc;
 
327
  }
328
 
329
- .manual-install {
330
- background: #1f2937;
331
- border-radius: 8px;
332
- padding: 1rem;
333
- margin-bottom: 1rem;
334
- display: flex;
335
- align-items: center;
336
- gap: 1rem;
337
  }
338
 
339
- .manual-install code {
340
- color: #10b981;
341
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
342
- font-size: 0.85rem;
343
- flex: 1;
344
- overflow-x: auto;
345
  }
346
 
347
- .copy-btn {
348
- background: #374151;
349
- color: white;
350
- border: none;
351
- padding: 0.5rem 1rem;
352
- border-radius: 6px;
353
- font-size: 0.8rem;
354
- cursor: pointer;
355
- transition: background-color 0.2s;
356
  }
357
 
358
- .copy-btn:hover {
359
- background: #4b5563;
360
  }
361
 
362
- .manual-steps {
363
- color: #6b7280;
364
- font-size: 0.9rem;
365
- line-height: 1.8;
366
  }
367
 
368
- .footer {
369
- text-align: center;
370
- padding: 2rem;
371
- color: white;
372
- opacity: 0.8;
373
  }
374
 
375
- .footer a {
376
- color: white;
377
- text-decoration: none;
378
- font-weight: 600;
379
  }
380
 
381
- .footer a:hover {
382
- text-decoration: underline;
 
 
383
  }
384
 
385
- /* Responsive Design */
386
- @media (max-width: 768px) {
387
- .hero {
388
- padding: 2rem 1rem;
389
- }
390
-
391
- .hero h1 {
392
- font-size: 2rem;
393
- }
394
-
395
- .container {
396
- padding: 0 1rem;
397
- }
398
-
399
- .app-details,
400
- .download-card {
401
- padding: 2rem;
402
- }
403
-
404
- .features-grid {
405
- grid-template-columns: 1fr;
406
- }
407
-
408
- .download-options {
409
- grid-template-columns: 1fr;
410
- }
411
  }
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ * {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
  }
8
 
9
+ :root {
10
+ --background: #F6F6F8;
11
+ --foreground: #333333;
12
+ --primary: #FF9900;
13
+ --primary-hover: #FFB333;
14
+ --muted: #E8E8EB;
15
+ --muted-foreground: #878789;
16
+ --border: #E0E0E0;
17
+ --card: #FFFFFF;
18
+ --radius: 0.5rem;
19
  }
20
 
21
+ body {
22
+ font-family: 'Inter', sans-serif;
23
+ background-color: var(--background);
24
+ color: var(--foreground);
25
+ line-height: 1.6;
26
+ -webkit-font-smoothing: antialiased;
27
  }
28
 
29
  .container {
30
+ max-width: 1200px;
31
+ margin: 0 auto;
32
+ padding: 0 1.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
 
35
+ /* Hero Section */
36
+ .hero {
37
+ padding: 4rem 0;
 
 
 
 
 
 
 
 
38
  }
39
 
40
+ .hero-grid {
41
+ display: grid;
42
+ grid-template-columns: 1fr 1fr;
43
+ gap: 3rem;
44
+ align-items: center;
45
  }
46
 
47
+ @media (max-width: 768px) {
48
+ .hero-grid {
49
+ grid-template-columns: 1fr;
50
+ }
 
 
 
 
 
 
 
51
  }
52
 
53
+ .hero-content {
54
+ text-align: left;
 
 
55
  }
56
 
57
+ .hero-header {
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ gap: 1rem;
62
+ margin-bottom: 1.5rem;
63
  }
64
 
65
+ .hero-icon {
66
+ width: 64px;
67
+ height: 64px;
68
+ object-fit: contain;
 
 
69
  }
70
 
71
+ .hero-title {
72
+ font-size: 3rem;
73
+ font-weight: 700;
74
+ color: var(--foreground);
75
  }
76
 
77
+ .tags {
78
+ display: flex;
79
+ flex-wrap: wrap;
80
+ justify-content: center;
81
+ gap: 0.5rem;
82
+ margin-bottom: 1.5rem;
83
  }
84
 
85
+ .tag {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ padding: 0.25rem 0.75rem;
89
+ font-size: 0.75rem;
90
+ font-weight: 500;
91
+ border-radius: 9999px;
92
+ background-color: var(--muted);
93
+ color: var(--muted-foreground);
94
  }
95
 
96
+ .tag.primary {
97
+ background-color: rgba(255, 153, 0, 0.15);
98
+ color: var(--primary);
 
 
99
  }
100
 
101
+ .hero-description {
102
+ font-size: 1.125rem;
103
+ color: var(--muted-foreground);
104
+ max-width: 600px;
105
+ margin: 0 auto 2rem;
 
 
106
  }
107
 
108
+ .hero-gif {
109
+ width: 100%;
110
+ max-width: 700px;
111
+ border-radius: var(--radius);
112
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15);
113
  }
114
 
115
+ /* Technical Section */
116
+ .technical {
117
+ padding: 4rem 0;
118
+ background-color: var(--card);
 
 
 
 
 
 
 
 
 
 
 
119
  }
120
 
121
+ .technical-grid {
122
+ display: grid;
123
+ grid-template-columns: 1fr 1fr;
124
+ gap: 4rem;
125
+ align-items: start;
126
  }
127
 
128
+ @media (max-width: 768px) {
129
+ .technical-grid {
130
+ grid-template-columns: 1fr;
131
+ gap: 2rem;
132
+ }
133
  }
134
 
135
+ .section-title {
136
+ font-size: 1.5rem;
137
+ font-weight: 700;
138
+ color: var(--foreground);
139
+ margin-bottom: 1rem;
140
  }
141
 
142
+ .section-text {
143
+ color: var(--muted-foreground);
144
+ margin-bottom: 1.5rem;
 
145
  }
146
 
147
+ .install-steps {
148
+ list-style: none;
149
+ counter-reset: step;
150
  }
151
 
152
+ .install-steps li {
153
+ counter-increment: step;
154
+ display: flex;
155
+ align-items: flex-start;
156
+ gap: 1rem;
157
+ margin-bottom: 1rem;
158
+ color: var(--muted-foreground);
159
  }
160
 
161
+ .install-steps li::before {
162
+ content: counter(step);
163
+ display: flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ min-width: 1.75rem;
167
+ height: 1.75rem;
168
+ border-radius: 50%;
169
+ background-color: var(--primary);
170
+ color: white;
171
+ font-size: 0.875rem;
172
+ font-weight: 600;
173
  }
174
 
175
+ .link-tertiary {
176
+ color: var(--muted-foreground);
177
+ text-decoration: underline;
178
+ transition: color 0.2s;
179
  }
180
 
181
+ .link-tertiary:hover {
182
+ color: var(--primary);
 
 
183
  }
184
 
185
+ .demo-gif {
186
+ width: 100%;
187
+ border-radius: var(--radius);
188
+ box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.1);
189
  }
190
 
191
+ /* Footer */
192
+ .footer {
193
+ background-color: #F6F6F8;
194
+ border-top: 1px solid var(--border);
195
+ padding: 2rem 0;
196
  }
197
 
198
+ .footer-content {
199
+ display: flex;
200
+ flex-direction: column;
201
+ align-items: center;
202
+ gap: 1rem;
203
+ text-align: center;
 
 
204
  }
205
 
206
+ .footer-links {
207
+ display: flex;
208
+ flex-wrap: wrap;
209
+ justify-content: center;
210
+ gap: 1.5rem;
211
+ font-size: 0.875rem;
212
  }
213
 
214
+ .footer-links a {
215
+ color: var(--muted-foreground);
216
+ text-decoration: none;
217
+ transition: color 0.2s;
 
 
 
 
 
218
  }
219
 
220
+ .footer-links a:hover {
221
+ color: var(--primary);
222
  }
223
 
224
+ .footer-help {
225
+ font-size: 0.875rem;
226
+ color: var(--muted-foreground);
 
227
  }
228
 
229
+ .footer-help a {
230
+ color: var(--primary);
231
+ text-decoration: underline;
 
 
232
  }
233
 
234
+ .footer-help a:hover {
235
+ color: var(--primary-hover);
 
 
236
  }
237
 
238
+ .footer-credit {
239
+ font-size: 0.875rem;
240
+ color: var(--muted-foreground);
241
+ margin-top: 0.5rem;
242
  }
243
 
244
+ .footer-credit strong {
245
+ color: var(--foreground);
246
+ font-weight: 600;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }