1use dioxus::prelude::*;
14#[cfg(all(feature = "split", target_arch = "wasm32"))]
15use dioxus::wasm_split;
16use crate::ui::router::Route;
17use crate::ui::components::main_nav::{MainNav, ActivePage};
18use crate::ui::components::footer::Footer;
19use crate::ui::components::icon::{Icon, IconVariant, IconSize};
20use crate::ui::components::code_editor::{CodeEditor, Language};
21use crate::ui::seo::{JsonLdMultiple, PageHead, organization_schema, website_schema, faq_schema, pages as seo_pages};
22use crate::ui::state::StudioMode;
23use logicaffeine_compile::{compile_for_ui, compile_theorem_for_ui, interpret_for_ui, generate_rust_code};
24use logicaffeine_kernel::interface::Repl;
25use crate::ui::examples::{
26 CODE_HELLO, CODE_FIBONACCI, CODE_CRDT_COUNTERS, CODE_CRDT_TALLY,
27 LOGIC_LEIBNIZ, LOGIC_BARBER, LOGIC_SIMPLE, LOGIC_QUANTIFIERS,
28 MATH_GODEL_LITERATE, MATH_INCOMPLETENESS_LITERATE, MATH_AUTO, MATH_NAT, MATH_BOOL, MATH_PROP_LOGIC,
29};
30
31struct DemoExample {
32 filename: &'static str,
33 icon: &'static str,
34 content: &'static str,
35 output: &'static str,
36 compiled: &'static str,
37 studio_path: &'static str,
38}
39
40const CODE_DEMO_EXAMPLES: [DemoExample; 4] = [
41 DemoExample {
42 filename: "hello-world.logos",
43 icon: "λ",
44 content: CODE_HELLO,
45 output: "",
46 compiled: "",
47 studio_path: "examples/code/hello-world.logos",
48 },
49 DemoExample {
50 filename: "fibonacci.logos",
51 icon: "λ",
52 content: CODE_FIBONACCI,
53 output: "",
54 compiled: "",
55 studio_path: "examples/code/fibonacci.logos",
56 },
57 DemoExample {
58 filename: "counters.logos",
59 icon: "λ",
60 content: CODE_CRDT_COUNTERS,
61 output: "",
62 compiled: "",
63 studio_path: "examples/code/distributed/counters.logos",
64 },
65 DemoExample {
66 filename: "tally.logos",
67 icon: "λ",
68 content: CODE_CRDT_TALLY,
69 output: "",
70 compiled: "",
71 studio_path: "examples/code/distributed/tally.logos",
72 },
73];
74
75const LOGIC_DEMO_EXAMPLES: [DemoExample; 4] = [
76 DemoExample {
77 filename: "leibniz-identity.logic",
78 icon: "∀",
79 content: LOGIC_LEIBNIZ,
80 output: "",
81 compiled: "",
82 studio_path: "examples/logic/leibniz-identity.logic",
83 },
84 DemoExample {
85 filename: "barber-paradox.logic",
86 icon: "∀",
87 content: LOGIC_BARBER,
88 output: "",
89 compiled: "",
90 studio_path: "examples/logic/barber-paradox.logic",
91 },
92 DemoExample {
93 filename: "simple-sentences.logic",
94 icon: "∀",
95 content: LOGIC_SIMPLE,
96 output: "",
97 compiled: "",
98 studio_path: "examples/logic/simple-sentences.logic",
99 },
100 DemoExample {
101 filename: "quantifiers.logic",
102 icon: "∀",
103 content: LOGIC_QUANTIFIERS,
104 output: "",
105 compiled: "",
106 studio_path: "examples/logic/quantifiers.logic",
107 },
108];
109
110const MATH_DEMO_EXAMPLES: [DemoExample; 6] = [
111 DemoExample {
112 filename: "godel-literate.logos",
113 icon: "π",
114 content: MATH_GODEL_LITERATE,
115 output: "",
116 compiled: "",
117 studio_path: "examples/math/godel-literate.logos",
118 },
119 DemoExample {
120 filename: "incompleteness-literate.logos",
121 icon: "π",
122 content: MATH_INCOMPLETENESS_LITERATE,
123 output: "",
124 compiled: "",
125 studio_path: "examples/math/incompleteness-literate.logos",
126 },
127 DemoExample {
128 filename: "auto-tactic.logos",
129 icon: "π",
130 content: MATH_AUTO,
131 output: "",
132 compiled: "",
133 studio_path: "examples/math/auto-tactic.logos",
134 },
135 DemoExample {
136 filename: "natural-numbers.logos",
137 icon: "π",
138 content: MATH_NAT,
139 output: "",
140 compiled: "",
141 studio_path: "examples/math/natural-numbers.logos",
142 },
143 DemoExample {
144 filename: "boolean-logic.logos",
145 icon: "π",
146 content: MATH_BOOL,
147 output: "",
148 compiled: "",
149 studio_path: "examples/math/boolean-logic.logos",
150 },
151 DemoExample {
152 filename: "prop-logic.logos",
153 icon: "π",
154 content: MATH_PROP_LOGIC,
155 output: "",
156 compiled: "",
157 studio_path: "examples/math/prop-logic.logos",
158 },
159];
160
161fn execute_math_code(content: &str) -> (Vec<String>, Option<String>) {
162 let mut repl = Repl::new();
163 let mut lines = Vec::new();
164 let mut error = None;
165 let statements = parse_math_statements(content);
166 for stmt in statements {
167 match repl.execute(&stmt) {
168 Ok(output) => {
169 if !output.is_empty() {
170 lines.push(output);
171 }
172 }
173 Err(e) => {
174 error = Some(e.to_string());
175 break;
176 }
177 }
178 }
179 (lines, error)
180}
181
182fn parse_math_statements(code: &str) -> Vec<String> {
183 let mut statements = Vec::new();
184 let lines: Vec<&str> = code.lines().collect();
185 let mut i = 0;
186
187 while i < lines.len() {
188 let line = lines[i];
189 let trimmed = line.trim();
190
191 if trimmed.is_empty() || trimmed.starts_with("--") {
192 i += 1;
193 continue;
194 }
195
196 if trimmed.starts_with("## To ") {
197 let mut block = String::new();
198 block.push_str(trimmed);
199 i += 1;
200 while i < lines.len() {
201 let next_line = lines[i];
202 let next_trimmed = next_line.trim();
203 if next_trimmed.is_empty() || next_trimmed.starts_with("--") {
204 i += 1;
205 continue;
206 }
207 let is_indented = next_line.starts_with(' ') || next_line.starts_with('\t');
208 let is_continuation = next_trimmed.starts_with("Consider ")
209 || next_trimmed.starts_with("When ")
210 || next_trimmed.starts_with("Yield ");
211 if is_indented || is_continuation {
212 block.push(' ');
213 block.push_str(next_trimmed);
214 i += 1;
215 } else {
216 break;
217 }
218 }
219 statements.push(block);
220 continue;
221 }
222
223 if trimmed.starts_with("## Theorem:") {
224 let mut block = String::new();
225 block.push_str(trimmed);
226 i += 1;
227 while i < lines.len() {
228 let next_line = lines[i];
229 let next_trimmed = next_line.trim();
230 if next_trimmed.is_empty() || next_trimmed.starts_with("--") {
231 i += 1;
232 continue;
233 }
234 let is_indented = next_line.starts_with(' ') || next_line.starts_with('\t');
235 let is_theorem_part = next_trimmed.starts_with("Statement:")
236 || next_trimmed.starts_with("Proof:");
237 if is_indented || is_theorem_part {
238 block.push('\n');
239 block.push_str(next_line);
240 i += 1;
241 if next_trimmed.starts_with("Proof:") && next_trimmed.ends_with('.') {
242 break;
243 }
244 } else {
245 break;
246 }
247 }
248 statements.push(block);
249 continue;
250 }
251
252 if (trimmed.starts_with("A ") || trimmed.starts_with("An ")) && trimmed.contains(" is either") {
253 if trimmed.ends_with('.') && !trimmed.trim_end_matches('.').ends_with(':') {
254 statements.push(trimmed.to_string());
255 i += 1;
256 continue;
257 }
258 let mut block = String::new();
259 block.push_str(trimmed);
260 i += 1;
261 while i < lines.len() {
262 let next_line = lines[i];
263 let next_trimmed = next_line.trim();
264 if next_trimmed.is_empty() || next_trimmed.starts_with("--") {
265 i += 1;
266 continue;
267 }
268 let is_indented = next_line.starts_with(' ') || next_line.starts_with('\t');
269 let looks_like_variant = next_trimmed.starts_with("a ")
270 || next_trimmed.chars().next().map(|c| c.is_uppercase()).unwrap_or(false);
271 if is_indented || (looks_like_variant && !next_trimmed.starts_with("A ") && !next_trimmed.starts_with("An ")) {
272 if !block.ends_with(':') {
273 block.push_str(" or ");
274 } else {
275 block.push(' ');
276 }
277 block.push_str(next_trimmed.trim_end_matches('.'));
278 i += 1;
279 } else {
280 break;
281 }
282 }
283 if !block.ends_with('.') {
284 block.push('.');
285 }
286 statements.push(block);
287 continue;
288 }
289
290 let mut current_stmt = String::new();
291 while i < lines.len() {
292 let line = lines[i];
293 let trimmed = line.trim();
294 if trimmed.is_empty() || trimmed.starts_with("--") {
295 i += 1;
296 continue;
297 }
298 if !current_stmt.is_empty() {
299 current_stmt.push(' ');
300 }
301 current_stmt.push_str(trimmed);
302 i += 1;
303 if trimmed.ends_with('.') {
304 break;
305 }
306 }
307 if !current_stmt.is_empty() {
308 statements.push(current_stmt);
309 }
310 }
311
312 statements
313}
314
315fn examples_for_mode(mode: StudioMode) -> &'static [DemoExample] {
316 match mode {
317 StudioMode::Code => &CODE_DEMO_EXAMPLES,
318 StudioMode::Logic | StudioMode::Hardware => &LOGIC_DEMO_EXAMPLES,
320 StudioMode::Math => &MATH_DEMO_EXAMPLES,
321 }
322}
323
324const LANDING_STYLE: &str = r#"
325body:has(.landing) {
326 overflow: hidden;
327}
328
329.landing {
330 height: 100vh;
331 color: var(--text-primary);
332 background:
333 radial-gradient(1200px 600px at 50% -120px, rgba(167,139,250,0.18), transparent 60%),
334 radial-gradient(900px 500px at 15% 30%, rgba(96,165,250,0.18), transparent 60%),
335 radial-gradient(800px 450px at 90% 45%, rgba(34,197,94,0.10), transparent 62%),
336 linear-gradient(180deg, #070a12, #0b1022 55%, #070a12);
337 overflow-x: hidden;
338 overflow-y: auto;
339 font-family: var(--font-sans);
340 position: relative;
341}
342
343.bg-orb {
344 position: absolute;
345 inset: auto;
346 width: 520px;
347 height: 520px;
348 border-radius: var(--radius-full);
349 filter: blur(42px);
350 opacity: 0.22;
351 pointer-events: none;
352 animation: float 14s ease-in-out infinite, pulse-glow 10s ease-in-out infinite;
353}
354.orb1 { top: -220px; left: -160px; background: radial-gradient(circle at 30% 30%, var(--color-accent-blue), transparent 60%); animation-delay: 0s; }
355.orb2 { top: 120px; right: -200px; background: radial-gradient(circle at 40% 35%, var(--color-accent-purple), transparent 60%); animation-delay: -5s; }
356.orb3 { bottom: -260px; left: 20%; background: radial-gradient(circle at 40% 35%, rgba(34,197,94,0.9), transparent 60%); animation-delay: -10s; }
357
358@media (max-width: 640px) {
359 /* Purely decorative: shrink and stop animating the 520px blurred orbs on
360 phones — a continuous blur(42px) repaint is the most expensive thing on a
361 mobile GPU here, for zero content value. */
362 .bg-orb { width: 300px; height: 300px; filter: blur(32px); animation: none; }
363}
364
365.container {
366 width: 100%;
367 max-width: 1120px;
368 margin: 0 auto;
369 padding: 0 var(--spacing-xl);
370}
371
372/* Navigation now handled by MainNav component */
373
374.btn {
375 display: inline-flex;
376 align-items: center;
377 justify-content: center;
378 gap: 10px;
379 padding: var(--spacing-md) var(--spacing-lg);
380 border-radius: var(--radius-lg);
381 border: 1px solid rgba(255,255,255,0.10);
382 background: rgba(255,255,255,0.05);
383 text-decoration: none;
384 font-weight: 650;
385 font-size: var(--font-body-md);
386 transition: transform 0.18s ease, background 0.18s ease, border-color 0.18s ease;
387 will-change: transform;
388}
389.btn:hover { transform: translateY(-1px); background: rgba(255,255,255,0.07); border-color: rgba(255,255,255,0.18); }
390.btn:active { transform: translateY(0px); }
391
392.btn-primary {
393 background: linear-gradient(135deg, rgba(96,165,250,0.95), rgba(167,139,250,0.95));
394 border-color: rgba(255,255,255,0.20);
395 color: #060814;
396 box-shadow: 0 18px 40px rgba(96,165,250,0.18);
397}
398.btn-primary:hover {
399 background: linear-gradient(135deg, var(--color-accent-blue), var(--color-accent-purple));
400}
401
402.btn-ghost {
403 background: rgba(255,255,255,0.03);
404}
405
406.btn-icon {
407 padding: 10px;
408 background: rgba(255,255,255,0.03);
409}
410.btn-icon svg {
411 width: 20px;
412 height: 20px;
413 fill: currentColor;
414}
415
416.github-link {
417 display: inline-flex;
418 align-items: center;
419 gap: 6px;
420 color: inherit;
421 text-decoration: none;
422 transition: color 0.2s ease;
423}
424.github-link:hover {
425 color: var(--text-primary);
426}
427
428.hero {
429 padding: 64px 0 30px;
430}
431
432.hero-grid {
433 display: grid;
434 grid-template-columns: 1.05fr 0.95fr;
435 gap: 36px;
436 align-items: center;
437}
438
439.badge {
440 display: inline-flex;
441 align-items: center;
442 gap: 10px;
443 padding: 10px 14px;
444 border-radius: var(--radius-full);
445 background: rgba(255,255,255,0.06);
446 border: 1px solid rgba(255,255,255,0.10);
447 backdrop-filter: blur(18px);
448 box-shadow: 0 18px 40px rgba(0,0,0,0.25);
449 color: var(--text-primary);
450 font-size: var(--font-caption-md);
451 font-weight: 650;
452}
453.badge .dot {
454 width: 8px;
455 height: 8px;
456 border-radius: var(--radius-full);
457 background: var(--color-success);
458 box-shadow: 0 0 0 6px rgba(34,197,94,0.12);
459 animation: pulse-glow 2s ease-in-out infinite;
460}
461
462.hero .badge { animation: fadeInUp 0.6s ease both; }
463.hero .h-title { animation: fadeInUp 0.6s ease 0.08s both; }
464.hero .h-sub { animation: fadeInUp 0.6s ease 0.16s both; }
465.hero .hero-ctas { animation: fadeInUp 0.6s ease 0.24s both; }
466.hero .microcopy { animation: fadeInUp 0.6s ease 0.30s both; }
467.hero .demo { animation: fadeInUp 0.8s ease 0.44s both; }
468
469.h-title {
470 margin: 24px 0 var(--spacing-lg);
471 font-size: var(--font-display-xl);
472 line-height: 1.15;
473 letter-spacing: -2px;
474 font-weight: 900;
475 background: linear-gradient(180deg, #ffffff 0%, rgba(229,231,235,0.78) 65%, rgba(229,231,235,0.62) 100%);
476 -webkit-background-clip: text;
477 -webkit-text-fill-color: transparent;
478}
479
480.h-sub {
481 margin: 0 0 var(--spacing-xl);
482 max-width: 580px;
483 color: var(--text-secondary);
484 font-size: var(--font-body-lg);
485 line-height: 1.65;
486}
487
488.hero-ctas {
489 display: flex;
490 gap: var(--spacing-md);
491 flex-wrap: wrap;
492 margin: 18px 0 14px;
493}
494
495.microcopy {
496 font-size: var(--font-caption-md);
497 color: var(--text-tertiary);
498}
499
500.demo {
501 border-radius: var(--radius-xl);
502 border: 1px solid rgba(255,255,255,0.10);
503 background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.03));
504 backdrop-filter: blur(18px);
505 box-shadow: 0 30px 80px rgba(0,0,0,0.55);
506 overflow: hidden;
507 position: relative;
508}
509
510.demo::before {
511 content: "";
512 position: absolute;
513 inset: -2px;
514 background: radial-gradient(600px 280px at 10% 10%, rgba(96,165,250,0.22), transparent 55%),
515 radial-gradient(520px 240px at 90% 20%, rgba(167,139,250,0.22), transparent 55%);
516 opacity: 0.9;
517 pointer-events: none;
518}
519
520.demo-head {
521 position: relative;
522 display: flex;
523 align-items: center;
524 justify-content: space-between;
525 padding: 14px var(--spacing-lg);
526 border-bottom: 1px solid rgba(255,255,255,0.06);
527 background: rgba(0,0,0,0.10);
528}
529
530.win-dots { display: flex; gap: var(--spacing-sm); align-items: center; }
531.wdot { width: 11px; height: 11px; border-radius: var(--radius-full); opacity: 0.9; }
532.wr { background: #ef4444; } .wy { background: #fbbf24; } .wg { background: #22c55e; }
533
534.demo-label {
535 font-size: var(--font-caption-sm);
536 color: var(--text-secondary);
537 border: 1px solid rgba(255,255,255,0.10);
538 padding: 7px 10px;
539 border-radius: var(--radius-full);
540 background: rgba(255,255,255,0.04);
541}
542
543.demo-body {
544 position: relative;
545 display: grid;
546 grid-template-columns: 1fr 1fr;
547}
548
549.demo-col {
550 padding: 18px 18px 22px;
551 min-height: 240px;
552}
553
554.demo-col + .demo-col {
555 border-left: 1px solid rgba(255,255,255,0.06);
556 background: rgba(0,0,0,0.18);
557}
558
559.demo-kicker {
560 display: flex;
561 align-items: center;
562 justify-content: space-between;
563 margin-bottom: var(--spacing-md);
564 font-size: var(--font-caption-sm);
565 color: var(--text-secondary);
566}
567
568.pill {
569 border: 1px solid rgba(255,255,255,0.10);
570 background: rgba(255,255,255,0.04);
571 padding: 6px 10px;
572 border-radius: var(--radius-full);
573}
574
575.code {
576 font-family: var(--font-mono);
577 font-size: var(--font-caption-md);
578 line-height: 1.6;
579 color: var(--text-primary);
580 white-space: pre-wrap;
581}
582
583.code.logic { color: var(--color-accent-purple); }
584
585.demo-foot {
586 display: flex;
587 gap: 10px;
588 flex-wrap: wrap;
589 padding: 14px var(--spacing-lg);
590 border-top: 1px solid rgba(255,255,255,0.06);
591 background: rgba(0,0,0,0.12);
592 color: var(--text-secondary);
593 font-size: var(--font-caption-md);
594}
595
596.section {
597 padding: 74px 0;
598}
599
600.section-title {
601 font-size: var(--font-display-md);
602 letter-spacing: -1.2px;
603 margin: 0 0 14px;
604 font-weight: 800;
605}
606.section-sub {
607 margin: 0 0 var(--spacing-xl);
608 color: var(--text-secondary);
609 line-height: 1.65;
610 max-width: 760px;
611}
612.section-right .section-title,
613.section-right .section-sub {
614 text-align: right;
615}
616.section-right .section-sub {
617 margin-left: auto;
618}
619.section-center .section-title,
620.section-center .section-sub {
621 text-align: center;
622}
623.section-center .section-sub {
624 margin-left: auto;
625 margin-right: auto;
626}
627
628.grid3 {
629 display: grid;
630 grid-template-columns: repeat(3, 1fr);
631 gap: 18px;
632}
633.grid2 {
634 display: grid;
635 grid-template-columns: 1fr 1fr;
636 gap: 18px;
637}
638
639.card {
640 position: relative;
641 border-radius: var(--radius-xl);
642 border: 1px solid rgba(255,255,255,0.10);
643 background: rgba(255,255,255,0.04);
644 backdrop-filter: blur(18px);
645 padding: 18px;
646 transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
647 overflow: hidden;
648}
649.card::before {
650 content: "";
651 position: absolute;
652 inset: 0;
653 border-radius: var(--radius-xl);
654 background: linear-gradient(135deg, rgba(96,165,250,0.12), rgba(167,139,250,0.12));
655 opacity: 0;
656 transition: opacity 0.3s ease;
657 pointer-events: none;
658}
659.card:hover {
660 transform: translateY(-3px);
661 border-color: rgba(167,139,250,0.28);
662 background: rgba(255,255,255,0.06);
663}
664.card:hover::before {
665 opacity: 1;
666}
667
668.icon-box {
669 width: 48px; height: 48px;
670 border-radius: var(--radius-lg);
671 display: flex;
672 align-items: center;
673 justify-content: center;
674 background: rgba(255,255,255,0.06);
675 border: 1px solid rgba(255,255,255,0.10);
676 margin-bottom: var(--spacing-md);
677}
678
679.icon-box .icon {
680 width: 24px;
681 height: 24px;
682}
683
684.card h3 {
685 margin: 0 0 var(--spacing-sm);
686 font-size: var(--font-body-md);
687 letter-spacing: -0.2px;
688}
689.card p {
690 margin: 0;
691 color: var(--text-secondary);
692 line-height: 1.6;
693 font-size: var(--font-body-md);
694}
695
696.quote {
697 font-size: var(--font-body-md);
698 line-height: 1.65;
699 color: var(--text-primary);
700}
701.quoter {
702 margin-top: 10px;
703 color: var(--text-tertiary);
704 font-size: var(--font-caption-md);
705}
706
707
708.tech-stack {
709 display: flex;
710 gap: 10px;
711 flex-wrap: wrap;
712 margin-top: 14px;
713}
714
715.tech-badge {
716 font-size: var(--font-caption-sm);
717 padding: 6px var(--spacing-md);
718 border-radius: 6px;
719 background: rgba(255,255,255,0.03);
720 border: 1px solid rgba(255,255,255,0.08);
721 color: var(--text-secondary);
722}
723
724.tech-badge.rust {
725 background: linear-gradient(135deg, rgba(183,65,14,0.15), rgba(222,165,132,0.10));
726 border-color: rgba(222,165,132,0.3);
727 color: #dea584;
728}
729
730.hello-world-layout {
731 display: flex;
732 flex-direction: column;
733 align-items: center;
734 gap: 40px;
735}
736
737.hello-pill-wrap,
738.hello-cta-wrap {
739 text-align: center;
740}
741
742.hello-editor {
743 width: 100%;
744 max-width: 820px;
745 border-radius: var(--radius-lg);
746 border: 1px solid rgba(255,255,255,0.10);
747 background: rgba(0,0,0,0.3);
748 overflow: hidden;
749 backdrop-filter: blur(8px);
750}
751
752.hello-loading {
753 width: 100%;
754 max-width: 820px;
755 min-height: 305px;
756}
757
758.hello-editor-head {
759 display: flex;
760 align-items: center;
761 justify-content: space-between;
762 padding: 10px 14px;
763 background: rgba(255,255,255,0.03);
764 border-bottom: 1px solid rgba(255,255,255,0.06);
765}
766
767.hello-editor-head .hello-filename {
768 font-size: var(--font-caption-sm);
769 color: var(--text-secondary);
770 font-family: var(--font-mono);
771}
772
773.hello-run-btn {
774 display: inline-flex;
775 align-items: center;
776 gap: 6px;
777 padding: 5px 14px;
778 border-radius: var(--radius-sm);
779 border: 1px solid rgba(52,211,153,0.4);
780 background: rgba(52,211,153,0.10);
781 color: #34d399;
782 font-size: var(--font-caption-sm);
783 font-weight: 600;
784 cursor: pointer;
785 transition: background 0.2s, border-color 0.2s;
786}
787
788.hello-run-btn:hover {
789 background: rgba(52,211,153,0.20);
790 border-color: rgba(52,211,153,0.6);
791}
792
793.hello-run-btn:disabled {
794 opacity: 0.5;
795 cursor: not-allowed;
796}
797
798.hello-editor-body {
799 display: flex;
800 min-height: 260px;
801}
802
803.hello-editor-left {
804 flex: 1;
805 min-width: 0;
806}
807
808.hello-editor-right {
809 flex: 1;
810 min-width: 0;
811 border-left: 1px solid rgba(255,255,255,0.06);
812 display: flex;
813 flex-direction: column;
814}
815
816.hello-output-head {
817 padding: 8px 14px;
818 font-size: var(--font-caption-sm);
819 color: var(--text-secondary);
820 font-family: var(--font-mono);
821 background: rgba(255,255,255,0.02);
822 border-bottom: 1px solid rgba(255,255,255,0.04);
823 text-transform: uppercase;
824 letter-spacing: 0.05em;
825}
826
827.hello-output-body {
828 padding: var(--spacing-md);
829 flex: 1;
830 overflow-y: auto;
831 font-family: var(--font-mono);
832 font-size: var(--font-body-md);
833 line-height: 1.6;
834}
835
836.hello-output-line {
837 margin: 0;
838 white-space: pre-wrap;
839 color: var(--text-primary);
840}
841
842.hello-output-error {
843 margin: 0;
844 white-space: pre-wrap;
845 color: var(--color-error, #f87171);
846}
847
848.hello-output-loading {
849 color: var(--text-secondary);
850 font-style: italic;
851}
852
853.hello-output-empty {
854 color: var(--text-secondary);
855 opacity: 0.5;
856}
857
858@media (max-width: 700px) {
859 .hello-editor-body {
860 flex-direction: column;
861 }
862 .hello-editor-right {
863 border-left: none;
864 border-top: 1px solid rgba(255,255,255,0.06);
865 }
866}
867
868.hello-note {
869 text-align: center;
870 font-size: var(--font-body-md);
871 color: var(--text-secondary);
872 display: inline-block;
873 padding: 8px 20px;
874 border: 1px solid rgba(167,139,250,0.3);
875 border-radius: var(--radius-full);
876 background: rgba(167,139,250,0.06);
877 box-shadow: 0 0 20px rgba(167,139,250,0.12), 0 0 40px rgba(96,165,250,0.08);
878}
879
880.compare-table {
881 display: flex;
882 flex-direction: column;
883 border-radius: var(--radius-lg);
884 border: 1px solid rgba(255,255,255,0.10);
885 overflow: hidden;
886 max-width: 800px;
887 margin: 0 auto;
888}
889
890.compare-row {
891 display: grid;
892 grid-template-columns: 1.2fr repeat(5, 1fr);
893}
894
895.compare-row.header {
896 background: rgba(255,255,255,0.05);
897 font-weight: 600;
898 font-size: var(--font-caption-md);
899}
900
901.compare-row:not(.header) {
902 border-top: 1px solid rgba(255,255,255,0.06);
903}
904
905.compare-cell {
906 padding: var(--spacing-md) 14px;
907 font-size: var(--font-caption-md);
908 color: var(--text-secondary);
909 text-align: center;
910}
911
912.compare-cell.label {
913 text-align: left;
914 color: var(--text-primary);
915 font-weight: 500;
916}
917
918.compare-cell.highlight {
919 background: rgba(167,139,250,0.08);
920 color: var(--color-accent-purple);
921 font-weight: 500;
922}
923
924.compare-row.header .compare-cell.highlight {
925 background: rgba(167,139,250,0.15);
926}
927
928@media (max-width: 700px) {
929 .compare-row {
930 grid-template-columns: 1fr 1fr 1fr;
931 }
932 .compare-cell:nth-child(4),
933 .compare-cell:nth-child(5),
934 .compare-cell:nth-child(6) {
935 display: none;
936 }
937}
938
939.faq-item {
940 padding: var(--spacing-lg) var(--spacing-lg) 14px;
941 border-radius: var(--radius-xl);
942 border: 1px solid rgba(255,255,255,0.10);
943 background: rgba(255,255,255,0.03);
944}
945.faq-q { font-weight: 750; margin-bottom: var(--spacing-sm); }
946.faq-a { color: var(--text-secondary); line-height: 1.6; font-size: var(--font-body-md); }
947
948.footer {
949 padding: 34px 0 44px;
950 border-top: 1px solid rgba(255,255,255,0.06);
951 color: var(--text-tertiary);
952 font-size: var(--font-caption-md);
953}
954
955.footer-row {
956 display: flex;
957 align-items: center;
958 justify-content: space-between;
959 gap: var(--spacing-lg);
960 flex-wrap: wrap;
961}
962
963@media (max-width: 980px) {
964 .hero-grid { grid-template-columns: 1fr; }
965 .demo-body { grid-template-columns: 1fr; }
966 .demo-col + .demo-col { border-left: none; border-top: 1px solid rgba(255,255,255,0.06); }
967 .grid3 { grid-template-columns: 1fr; }
968 .grid2 { grid-template-columns: 1fr; }
969 .h-title { font-size: var(--font-display-lg); }
970}
971
972@keyframes fadeInUp {
973 from { opacity: 0; transform: translateY(24px); }
974 to { opacity: 1; transform: translateY(0); }
975}
976
977@keyframes float {
978 0%, 100% { transform: translate3d(0, 0, 0); }
979 50% { transform: translate3d(0, -20px, 0); }
980}
981
982@keyframes pulse-glow {
983 0%, 100% { opacity: 0.22; }
984 50% { opacity: 0.32; }
985}
986
987@keyframes blink {
988 50% { opacity: 0; }
989}
990
991html { scroll-behavior: smooth; }
992
993.section + .section {
994 border-top: 1px solid rgba(255,255,255,0.04);
995}
996
997.steps {
998 display: flex;
999 align-items: center;
1000 justify-content: center;
1001 gap: var(--spacing-xl);
1002 flex-wrap: wrap;
1003}
1004
1005.step {
1006 flex: 1;
1007 min-width: 200px;
1008 max-width: 280px;
1009 text-align: center;
1010 padding: var(--spacing-xl);
1011 border-radius: var(--radius-xl);
1012 background: rgba(255,255,255,0.04);
1013 border: 1px solid rgba(255,255,255,0.10);
1014 animation: fadeInUp 0.6s ease both;
1015}
1016
1017.step:nth-child(1) { animation-delay: 0s; }
1018.step:nth-child(3) { animation-delay: 0.1s; }
1019.step:nth-child(5) { animation-delay: 0.2s; }
1020
1021.step-num {
1022 width: 48px;
1023 height: 48px;
1024 margin: 0 auto var(--spacing-lg);
1025 border-radius: 50%;
1026 background: linear-gradient(135deg, var(--color-accent-blue), var(--color-accent-purple));
1027 color: #060814;
1028 font-weight: 800;
1029 font-size: var(--font-heading-sm);
1030 display: grid;
1031 place-items: center;
1032}
1033
1034.step h3 {
1035 margin: 0 0 var(--spacing-sm);
1036 font-size: var(--font-body-lg);
1037}
1038
1039.step p {
1040 margin: 0;
1041 color: var(--text-secondary);
1042 font-size: var(--font-body-md);
1043 line-height: 1.5;
1044}
1045
1046.step-arrow {
1047 font-size: 24px;
1048 color: var(--text-tertiary);
1049}
1050
1051.grid3 .card:nth-child(1) .icon-box { background: rgba(0,212,255,0.15); }
1052.grid3 .card:nth-child(2) .icon-box { background: rgba(129,140,248,0.15); }
1053.grid3 .card:nth-child(3) .icon-box { background: rgba(34,197,94,0.15); }
1054.grid3 .card:nth-child(4) .icon-box { background: rgba(251,191,36,0.15); }
1055.grid3 .card:nth-child(5) .icon-box { background: rgba(236,72,153,0.15); }
1056.grid3 .card:nth-child(6) .icon-box { background: rgba(129,140,248,0.15); }
1057
1058/* Mini-Studio */
1059.mini-studio {
1060 border-radius: var(--radius-xl);
1061 border: 1px solid rgba(255,255,255,0.10);
1062 background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.03));
1063 backdrop-filter: blur(18px);
1064 box-shadow: 0 30px 80px rgba(0,0,0,0.55);
1065 overflow: hidden;
1066 position: relative;
1067 display: flex;
1068 flex-direction: column;
1069 height: 635px;
1070}
1071.showcase-loading {
1072 min-height: 635px;
1073}
1074.hello-loading {
1075 min-height: 220px;
1076}
1077/* Loading skeleton shown while the wasm boots / a lazy body streams in — reads
1078 as "loading," never as a blank/broken panel. */
1079.mini-studio-skeleton { pointer-events: none; }
1080.skeleton-body {
1081 flex: 1;
1082 padding: 24px;
1083 display: flex;
1084 flex-direction: column;
1085 gap: 16px;
1086}
1087.skeleton-line {
1088 height: 14px;
1089 border-radius: 6px;
1090 background: linear-gradient(90deg, rgba(255,255,255,0.05) 25%, rgba(255,255,255,0.12) 37%, rgba(255,255,255,0.05) 63%);
1091 background-size: 400% 100%;
1092 animation: skeleton-shimmer 1.4s ease infinite;
1093}
1094.skeleton-line.w40 { width: 40%; }
1095.skeleton-line.w50 { width: 50%; }
1096.skeleton-line.w60 { width: 60%; }
1097.skeleton-line.w70 { width: 70%; }
1098.skeleton-line.w80 { width: 80%; }
1099.skeleton-line.w90 { width: 90%; }
1100@keyframes skeleton-shimmer {
1101 0% { background-position: 100% 50%; }
1102 100% { background-position: 0% 50%; }
1103}
1104.mini-studio::before {
1105 content: "";
1106 position: absolute;
1107 inset: -2px;
1108 background: radial-gradient(600px 280px at 10% 10%, rgba(96,165,250,0.22), transparent 55%),
1109 radial-gradient(520px 240px at 90% 20%, rgba(167,139,250,0.22), transparent 55%);
1110 opacity: 0.9;
1111 pointer-events: none;
1112}
1113.mini-studio-head {
1114 position: relative;
1115 display: flex;
1116 align-items: center;
1117 justify-content: center;
1118 padding: 14px var(--spacing-lg);
1119 border-bottom: 1px solid rgba(255,255,255,0.06);
1120 background: rgba(0,0,0,0.10);
1121}
1122.mini-studio-head .win-dots {
1123 position: absolute;
1124 left: var(--spacing-lg);
1125}
1126.mini-mode-toggle {
1127 display: flex;
1128 gap: 4px;
1129 background: rgba(255,255,255,0.04);
1130 border: 1px solid rgba(255,255,255,0.10);
1131 border-radius: var(--radius-full);
1132 padding: 3px;
1133}
1134.mini-toggle-btn {
1135 padding: 6px 12px;
1136 border-radius: var(--radius-full);
1137 border: none;
1138 background: transparent;
1139 color: var(--text-secondary);
1140 font-size: var(--font-caption-sm);
1141 font-weight: 600;
1142 cursor: pointer;
1143 transition: background 0.18s, color 0.18s;
1144 display: flex;
1145 align-items: center;
1146 gap: 6px;
1147 font-family: inherit;
1148}
1149.mini-toggle-btn.active {
1150 background: rgba(96,165,250,0.2);
1151 color: var(--text-primary);
1152}
1153.mini-toggle-btn:hover:not(.active) {
1154 background: rgba(255,255,255,0.06);
1155}
1156.mini-studio-body {
1157 position: relative;
1158 display: grid;
1159 grid-template-columns: 180px 1fr;
1160 flex: 1;
1161 min-height: 0;
1162 overflow: hidden;
1163}
1164.mini-file-tabs {
1165 display: none;
1166 gap: 4px;
1167 padding: 8px 12px;
1168 overflow-x: auto;
1169 border-bottom: 1px solid rgba(255,255,255,0.06);
1170 background: rgba(0,0,0,0.08);
1171 flex-shrink: 0;
1172 -webkit-overflow-scrolling: touch;
1173}
1174.mini-file-tab {
1175 flex-shrink: 0;
1176 padding: 5px 10px;
1177 border-radius: var(--radius-full);
1178 border: 1px solid rgba(255,255,255,0.10);
1179 background: rgba(255,255,255,0.04);
1180 color: var(--text-secondary);
1181 font-size: var(--font-caption-sm);
1182 font-weight: 500;
1183 cursor: pointer;
1184 white-space: nowrap;
1185 font-family: var(--font-mono);
1186 transition: background 0.15s, color 0.15s, border-color 0.15s;
1187}
1188.mini-file-tab:hover {
1189 background: rgba(255,255,255,0.08);
1190}
1191.mini-file-tab.active {
1192 background: rgba(96,165,250,0.2);
1193 border-color: rgba(96,165,250,0.35);
1194 color: var(--text-primary);
1195}
1196.mini-explorer {
1197 border-right: 1px solid rgba(255,255,255,0.06);
1198 padding: 8px 0;
1199 background: rgba(0,0,0,0.08);
1200}
1201.mini-explorer-label {
1202 padding: 7px 14px;
1203 font-size: var(--font-caption-sm);
1204 color: var(--text-tertiary);
1205 text-transform: uppercase;
1206 letter-spacing: 0.5px;
1207 font-weight: 600;
1208}
1209.mini-file-item {
1210 padding: 7px 14px;
1211 font-size: var(--font-caption-sm);
1212 color: var(--text-secondary);
1213 cursor: pointer;
1214 display: flex;
1215 align-items: center;
1216 gap: 8px;
1217 transition: background 0.15s, color 0.15s;
1218 white-space: nowrap;
1219 overflow: hidden;
1220 text-overflow: ellipsis;
1221}
1222.mini-file-item:hover {
1223 background: rgba(255,255,255,0.04);
1224}
1225.mini-file-item.active {
1226 background: rgba(96,165,250,0.15);
1227 color: var(--text-primary);
1228}
1229.mini-file-item.view-more {
1230 color: var(--accent-secondary);
1231 font-style: italic;
1232 opacity: 0.8;
1233 margin-top: 4px;
1234 text-decoration: none;
1235}
1236.mini-file-item.view-more:hover {
1237 opacity: 1;
1238 background: rgba(96,165,250,0.10);
1239}
1240.mini-file-tab.view-more {
1241 color: var(--accent-secondary);
1242 font-style: italic;
1243 border-color: rgba(96,165,250,0.2);
1244 text-decoration: none;
1245}
1246.mini-file-tab.view-more:hover {
1247 background: rgba(96,165,250,0.10);
1248}
1249.mini-file-icon {
1250 opacity: 0.5;
1251}
1252.mini-code-panel {
1253 padding: 16px;
1254 overflow-y: auto;
1255 position: relative;
1256}
1257.mini-code-filename {
1258 display: flex;
1259 align-items: center;
1260 gap: 8px;
1261 margin-bottom: 12px;
1262 font-size: var(--font-caption-sm);
1263 color: var(--text-secondary);
1264}
1265.mini-code-panel .code-editor {
1266 height: 100%;
1267 background: transparent;
1268}
1269.mini-code-panel .code-editor-input {
1270 min-height: 0;
1271}
1272.mini-code-panel .code-editor-textarea,
1273.mini-code-panel .code-editor-highlight {
1274 padding: 0;
1275 padding-bottom: 40px;
1276 font-size: var(--font-caption-md);
1277 line-height: 1.6;
1278}
1279.mini-action-bar {
1280 display: flex;
1281 align-items: center;
1282 justify-content: flex-end;
1283 gap: 8px;
1284 padding: 8px 16px;
1285 border-bottom: 1px solid rgba(255,255,255,0.06);
1286 background: rgba(0,0,0,0.06);
1287 position: relative;
1288 flex-shrink: 0;
1289}
1290.mini-exec-btn {
1291 padding: 6px 14px;
1292 border: none;
1293 border-radius: 6px;
1294 color: white;
1295 font-size: 13px;
1296 font-weight: 500;
1297 cursor: pointer;
1298 transition: all 0.15s ease;
1299 display: flex;
1300 align-items: center;
1301 gap: 6px;
1302 font-family: inherit;
1303}
1304.mini-exec-btn:hover {
1305 transform: translateY(-1px);
1306 box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
1307}
1308.mini-exec-btn:active { transform: translateY(0); }
1309.mini-exec-btn.compile {
1310 background: linear-gradient(135deg, #56b6c2 0%, #61afef 100%);
1311}
1312.mini-exec-btn.run {
1313 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1314}
1315.mini-terminal {
1316 border-top: 1px solid rgba(255,255,255,0.06);
1317 background: rgba(0,0,0,0.20);
1318 display: flex;
1319 flex-direction: column;
1320 overflow: hidden;
1321 position: relative;
1322}
1323.mini-terminal-head {
1324 display: flex;
1325 align-items: center;
1326 padding: 6px 16px;
1327 font-size: 11px;
1328 text-transform: uppercase;
1329 letter-spacing: 0.5px;
1330 color: var(--text-tertiary);
1331 font-weight: 600;
1332 border-bottom: 1px solid rgba(255,255,255,0.04);
1333 flex-shrink: 0;
1334}
1335.mini-terminal-body {
1336 flex: 1;
1337 min-height: 0;
1338 overflow-y: auto;
1339 padding: 8px 16px 13px;
1340 font-family: var(--font-mono);
1341 font-size: 12px;
1342 line-height: 1.5;
1343}
1344.mini-output-line {
1345 margin: 0;
1346 color: #4ade80;
1347 white-space: pre-wrap;
1348 font: inherit;
1349 line-height: inherit;
1350}
1351.mini-output-error {
1352 margin: 4px 0 0;
1353 color: #e06c75;
1354 white-space: pre-wrap;
1355 font: inherit;
1356 line-height: inherit;
1357 padding: 8px;
1358 background: rgba(224, 108, 117, 0.1);
1359 border-radius: 4px;
1360}
1361.mini-output-empty {
1362 color: var(--text-tertiary);
1363 font-style: italic;
1364}
1365.mini-output-loading {
1366 color: #667eea;
1367 animation: blink 1s step-end infinite;
1368}
1369.mini-term-output {
1370 margin: 0;
1371 color: #4ade80;
1372 white-space: pre-wrap;
1373 font: inherit;
1374 line-height: inherit;
1375}
1376.mini-compiled {
1377 border-top: 1px solid rgba(255,255,255,0.06);
1378 background: rgba(0,0,0,0.25);
1379 display: flex;
1380 flex-direction: column;
1381 overflow: hidden;
1382 position: relative;
1383}
1384.mini-compiled-head {
1385 display: flex;
1386 align-items: center;
1387 padding: 6px 16px;
1388 font-size: 11px;
1389 text-transform: uppercase;
1390 letter-spacing: 0.5px;
1391 color: var(--text-tertiary);
1392 font-weight: 600;
1393 border-bottom: 1px solid rgba(255,255,255,0.04);
1394 flex-shrink: 0;
1395}
1396.mini-compiled-body {
1397 flex: 1;
1398 min-height: 0;
1399 overflow-y: auto;
1400 padding: 8px 16px;
1401 font-family: var(--font-mono);
1402 font-size: 12px;
1403 line-height: 1.5;
1404 color: #e5c07b;
1405 white-space: pre-wrap;
1406 margin: 0;
1407}
1408.mini-terminal-resizer {
1409 height: 16px;
1410 background: transparent;
1411 cursor: row-resize;
1412 position: relative;
1413 flex-shrink: 0;
1414 z-index: 2;
1415}
1416.mini-terminal-resizer::after {
1417 content: "";
1418 position: absolute;
1419 left: 50%;
1420 top: 50%;
1421 transform: translate(-50%, -50%);
1422 width: 40px;
1423 height: 5px;
1424 border-radius: 3px;
1425 background: rgba(255,255,255,0.15);
1426 transition: background 0.15s ease;
1427}
1428.mini-terminal-resizer:hover::after,
1429.mini-terminal-resizer.active::after {
1430 background: rgba(96,165,250,0.6);
1431}
1432.mini-studio-cta {
1433 position: relative;
1434 padding: 12px 16px;
1435 border-top: 1px solid rgba(255,255,255,0.06);
1436 background: rgba(0,0,0,0.12);
1437 text-align: center;
1438 flex-shrink: 0;
1439}
1440.mini-cta-btn {
1441 display: inline-flex;
1442 align-items: center;
1443 gap: 8px;
1444 padding: 10px 24px;
1445 background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
1446 border: none;
1447 border-radius: 8px;
1448 color: white;
1449 font-size: 14px;
1450 font-weight: 600;
1451 cursor: pointer;
1452 transition: all 0.18s ease;
1453 text-decoration: none;
1454 font-family: inherit;
1455}
1456.mini-cta-btn:hover {
1457 transform: translateY(-1px);
1458 box-shadow: 0 6px 20px rgba(59, 130, 246, 0.35);
1459}
1460
1461/* Mode Stories */
1462.mode-stories {
1463 display: grid;
1464 grid-template-columns: repeat(3, 1fr);
1465 gap: 18px;
1466}
1467.mode-story {
1468 border-radius: var(--radius-xl);
1469 border: 1px solid rgba(255,255,255,0.10);
1470 background: rgba(255,255,255,0.04);
1471 backdrop-filter: blur(18px);
1472 padding: 24px;
1473 transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
1474 overflow: hidden;
1475}
1476.mode-story:hover {
1477 transform: translateY(-3px);
1478 border-color: rgba(167,139,250,0.28);
1479 background: rgba(255,255,255,0.06);
1480}
1481.mode-story-icon {
1482 width: 48px;
1483 height: 48px;
1484 border-radius: var(--radius-lg);
1485 display: flex;
1486 align-items: center;
1487 justify-content: center;
1488 font-size: 22px;
1489 font-weight: 700;
1490 margin-bottom: var(--spacing-md);
1491 border: 1px solid rgba(255,255,255,0.10);
1492}
1493.mode-story:nth-child(1) .mode-story-icon { background: rgba(96,165,250,0.15); color: #60a5fa; }
1494.mode-story:nth-child(2) .mode-story-icon { background: rgba(167,139,250,0.15); color: #a78bfa; }
1495.mode-story:nth-child(3) .mode-story-icon { background: rgba(34,197,94,0.15); color: #22c55e; }
1496.mode-story h3 {
1497 margin: 0 0 var(--spacing-md);
1498 font-size: var(--font-body-lg);
1499}
1500.mode-story-demo {
1501 border-radius: var(--radius-lg);
1502 background: rgba(0,0,0,0.3);
1503 padding: 12px;
1504 margin-bottom: var(--spacing-md);
1505 font-family: var(--font-mono);
1506 font-size: var(--font-caption-md);
1507 line-height: 1.6;
1508}
1509.mode-story-row {
1510 display: flex;
1511 gap: 8px;
1512 align-items: baseline;
1513}
1514.mode-story-label {
1515 color: var(--text-tertiary);
1516 font-size: var(--font-caption-sm);
1517 min-width: 54px;
1518 flex-shrink: 0;
1519}
1520.mode-story-value {
1521 color: var(--text-primary);
1522}
1523.mode-story-value.logic {
1524 color: var(--color-accent-purple);
1525}
1526.mode-story-value.success {
1527 color: var(--color-success);
1528}
1529.mode-story-arrow {
1530 text-align: center;
1531 color: var(--text-tertiary);
1532 padding: 4px 0;
1533 font-size: var(--font-caption-sm);
1534}
1535.mode-story > p {
1536 margin: 0;
1537 color: var(--text-secondary);
1538 font-size: var(--font-body-md);
1539 line-height: 1.6;
1540}
1541
1542/* Security Demo */
1543.security-demo {
1544 margin-top: var(--spacing-xl);
1545 border-radius: var(--radius-xl);
1546 border: 1px solid rgba(167,139,250,0.20);
1547 background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02));
1548 overflow: hidden;
1549 box-shadow:
1550 0 0 40px rgba(167,139,250,0.08),
1551 0 0 80px rgba(96,165,250,0.06),
1552 0 20px 60px rgba(0,0,0,0.4);
1553 position: relative;
1554}
1555.security-demo::before {
1556 content: "";
1557 position: absolute;
1558 inset: -1px;
1559 border-radius: var(--radius-xl);
1560 background: radial-gradient(400px 200px at 20% 0%, rgba(167,139,250,0.15), transparent 60%),
1561 radial-gradient(400px 200px at 80% 100%, rgba(96,165,250,0.12), transparent 60%);
1562 pointer-events: none;
1563}
1564.security-demo-head {
1565 position: relative;
1566 display: flex;
1567 align-items: center;
1568 justify-content: center;
1569 padding: 16px var(--spacing-lg);
1570 border-bottom: 1px solid rgba(255,255,255,0.08);
1571 background: rgba(0,0,0,0.12);
1572 font-size: var(--font-body-md);
1573 font-weight: 700;
1574 color: var(--text-primary);
1575 letter-spacing: -0.2px;
1576}
1577.security-demo-body {
1578 position: relative;
1579 display: grid;
1580 grid-template-columns: 1fr auto 1fr;
1581 align-items: stretch;
1582}
1583.security-demo-col {
1584 padding: 20px 24px;
1585}
1586.security-demo-col + .security-demo-col {
1587 border-left: 1px solid rgba(255,255,255,0.06);
1588}
1589.security-demo-arrow {
1590 display: flex;
1591 align-items: center;
1592 justify-content: center;
1593 padding: 0 16px;
1594 font-size: 28px;
1595 color: var(--color-accent-purple);
1596 border-left: 1px solid rgba(255,255,255,0.06);
1597 border-right: 1px solid rgba(255,255,255,0.06);
1598 background: rgba(0,0,0,0.06);
1599 text-shadow: 0 0 12px rgba(167,139,250,0.5);
1600}
1601.security-demo-label {
1602 display: inline-block;
1603 font-size: 11px;
1604 text-transform: uppercase;
1605 letter-spacing: 0.5px;
1606 font-weight: 600;
1607 color: #4ade80;
1608 margin-bottom: var(--spacing-sm);
1609 padding: 4px 10px;
1610 border: 1px solid rgba(74,222,128,0.3);
1611 border-radius: var(--radius-full);
1612 background: rgba(74,222,128,0.08);
1613}
1614
1615@media (max-width: 980px) {
1616 .hero-grid { grid-template-columns: 1fr; }
1617 .demo-body { grid-template-columns: 1fr; }
1618 .demo-col + .demo-col { border-left: none; border-top: 1px solid rgba(255,255,255,0.06); }
1619 .grid3 { grid-template-columns: 1fr; }
1620 .grid2 { grid-template-columns: 1fr; }
1621 .h-title { font-size: var(--font-display-lg); }
1622 .step-arrow { display: none; }
1623 .steps { flex-direction: column; }
1624 .mini-studio { height: 78vh !important; }
1625 .showcase-loading { min-height: 78vh; }
1626 .mini-studio-body { grid-template-columns: 1fr; }
1627 .mini-explorer { display: none; }
1628 .mini-file-tabs { display: flex; }
1629 .mini-terminal { max-height: 35vh !important; }
1630 .mini-compiled { max-height: 30vh !important; }
1631 .mode-stories { grid-template-columns: 1fr; }
1632 .security-demo-body { grid-template-columns: 1fr; }
1633 .security-demo-arrow { display: none; }
1634 .security-demo-col + .security-demo-col { border-left: none; border-top: 1px solid rgba(255,255,255,0.06); }
1635}
1636
1637@media (max-width: 768px) {
1638 .mini-studio { height: 78vh !important; }
1639 .mini-studio-head {
1640 justify-content: flex-end;
1641 }
1642 .mini-studio-head .win-dots {
1643 display: none;
1644 }
1645 .mini-exec-btn {
1646 padding: 10px 14px;
1647 min-height: 44px;
1648 font-size: 12px;
1649 }
1650 .mini-cta-btn {
1651 min-height: 44px;
1652 width: 100%;
1653 justify-content: center;
1654 }
1655 .mini-action-bar { padding: 8px 12px; }
1656}
1657
1658@media (max-width: 480px) {
1659 .mini-studio { height: 78vh !important; }
1660 .mini-exec-btn .btn-label { display: none; }
1661}
1662
1663@media (prefers-reduced-motion: reduce) {
1664 * { transition: none !important; animation: none !important; }
1665}
1666"#;
1667
1668#[component]
1669pub fn Landing() -> Element {
1670 let schemas = vec![
1671 organization_schema(),
1672 website_schema(),
1673 faq_schema(&[
1674 ("Is it really free?", "Yes — free for individuals, universities, and teams under 25 people. For commercial licensing, contact us."),
1675 ("Do I need to know logic already?", "No. Start in Learn. The system introduces concepts progressively and uses examples to teach scope, quantifiers, and structure."),
1676 ("Is this an AI that guesses?", "The goal is the opposite: to force explicit structure. When language is ambiguous, the tutor prompts clarifying questions."),
1677 ("Where do I begin?", "If you want speed, open Studio. If you want mastery, Start Learning and follow the lessons."),
1678 ("What is LOGOS written in?", "Rust. The entire transpiler, parser, and runtime are written in Rust for maximum performance and safety."),
1679 ("How fast is it?", "Native speed. LOGOS compiles to Rust, which then compiles via LLVM to optimized machine code. Zero interpreter overhead."),
1680 ]),
1681 ];
1682
1683 rsx! {
1684 PageHead {
1685 title: seo_pages::LANDING.title,
1686 description: seo_pages::LANDING.description,
1687 canonical_path: seo_pages::LANDING.canonical_path,
1688 }
1689 style { "{LANDING_STYLE}" }
1690 JsonLdMultiple { schemas }
1691
1692 div { class: "landing",
1693 div { class: "bg-orb orb1" }
1694 div { class: "bg-orb orb2" }
1695 div { class: "bg-orb orb3" }
1696
1697 MainNav { active: ActivePage::Other }
1698
1699 main { class: "container",
1700 section { class: "hero",
1701 div { class: "hero-grid",
1702 div {
1703 div { class: "badge",
1704 div { class: "dot" }
1705 span { "Free for individuals • Commercial licenses available" }
1706 }
1707
1708 h1 { class: "h-title", "Debug Your Thoughts." }
1709
1710 p { class: "h-sub",
1711 "Write Code, Logic, and Math in plain English. LOGOS compiles your words into programs, proofs, and formal systems — no symbols required."
1712 }
1713
1714 div { class: "hero-ctas",
1715 Link { to: Route::Learn {}, class: "btn btn-primary", "Start Learning" }
1716 Link { to: Route::Studio { file: None }, class: "btn", "Open Studio" }
1717 Link { to: Route::Pricing {}, class: "btn btn-ghost", "Contact Us" }
1718 }
1719
1720 p { class: "microcopy",
1721 "Students, engineers, researchers, and attorneys — anyone who thinks for a living."
1722 }
1723
1724 div { class: "tech-stack",
1725 span { class: "tech-badge rust",
1726 "Rust-Powered 🦀"
1727 }
1728 span { class: "tech-badge", "WASM Ready" }
1729 span { class: "tech-badge", "Markdown Source" }
1730 span { class: "tech-badge", "Proof-Checked" }
1731 }
1732 }
1733
1734 SuspenseBoundary {
1735 fallback: |_| rsx! {
1736 div { class: "showcase-loading",
1737 div { class: "mini-studio mini-studio-skeleton",
1738 div { class: "mini-studio-head",
1739 div { class: "win-dots",
1740 div { class: "wdot wr" }
1741 div { class: "wdot wy" }
1742 div { class: "wdot wg" }
1743 }
1744 }
1745 div { class: "skeleton-body",
1746 div { class: "skeleton-line w70" }
1747 div { class: "skeleton-line w90" }
1748 div { class: "skeleton-line w50" }
1749 div { class: "skeleton-line w80" }
1750 div { class: "skeleton-line w40" }
1751 }
1752 }
1753 }
1754 },
1755 LandingShowcase {}
1756 }
1757 }
1758 }
1759
1760 section { class: "section how-it-works section-center",
1761 h2 { class: "section-title", "How it works" }
1762 p { class: "section-sub",
1763 "Three modes. One language: English."
1764 }
1765
1766 div { class: "mode-stories",
1767 div { class: "mode-story",
1768 div { class: "mode-story-icon", "λ" }
1769 h3 { "Write a program" }
1770 div { class: "mode-story-demo",
1771 div { class: "mode-story-row",
1772 span { class: "mode-story-label", "Input" }
1773 span { class: "mode-story-value", "Let x be 10. Show x + 5." }
1774 }
1775 div { class: "mode-story-arrow", "↓" }
1776 div { class: "mode-story-row",
1777 span { class: "mode-story-label", "Output" }
1778 span { class: "mode-story-value success", "15" }
1779 }
1780 }
1781 p { "Type readable definitions. Get compiled programs — Rust under the hood, English on the surface." }
1782 }
1783
1784 div { class: "mode-story",
1785 div { class: "mode-story-icon", "∀" }
1786 h3 { "Formalize an argument" }
1787 div { class: "mode-story-demo",
1788 div { class: "mode-story-row",
1789 span { class: "mode-story-label", "Input" }
1790 span { class: "mode-story-value", "Every cat sleeps." }
1791 }
1792 div { class: "mode-story-arrow", "↓" }
1793 div { class: "mode-story-row",
1794 span { class: "mode-story-label", "Output" }
1795 span { class: "mode-story-value logic", "∀x(Cat(x) → Sleep(x))" }
1796 }
1797 }
1798 p { "Turn plain language into First-Order Logic. Every reading surfaced — no guessing." }
1799 }
1800
1801 div { class: "mode-story",
1802 div { class: "mode-story-icon", "π" }
1803 h3 { "Prove a theorem" }
1804 div { class: "mode-story-demo",
1805 div { class: "mode-story-row",
1806 span { class: "mode-story-label", "Input" }
1807 span { class: "mode-story-value", "Theorem: ∀n, n + 0 = n." }
1808 }
1809 div { class: "mode-story-arrow", "↓" }
1810 div { class: "mode-story-row",
1811 span { class: "mode-story-label", "Output" }
1812 span { class: "mode-story-value success", "Proof: by induction. ✓" }
1813 }
1814 }
1815 p { "Define types, state theorems, and prove them with automated tactics." }
1816 }
1817 }
1818 }
1819
1820 section { class: "section hello-world section-center hello-world-layout",
1821 h2 { class: "section-title", "Hello World in LOGOS" }
1822
1823 SuspenseBoundary {
1824 fallback: |_| rsx! {
1825 div { class: "hello-loading",
1826 div { class: "skeleton-body",
1827 div { class: "skeleton-line w80" }
1828 div { class: "skeleton-line w60" }
1829 div { class: "skeleton-line w70" }
1830 }
1831 }
1832 },
1833 LandingHelloWorld {}
1834 }
1835 div { class: "hello-pill-wrap",
1836 p { class: "hello-note", "Compiles to a native binary via Rust. Zero runtime overhead." }
1837 }
1838 div { class: "hello-cta-wrap",
1839 a { href: crate::ui::router::studio_file_url("examples/code/hello-world.logos"), class: "btn btn-primary",
1840 "Open in Studio →"
1841 }
1842 }
1843 }
1844
1845 section { class: "section",
1846 h2 { class: "section-title", "What you get" }
1847 p { class: "section-sub",
1848 "LOGICAFFEINE translates intuition into structure — so you can test it, teach it, or ship it."
1849 }
1850
1851 div { class: "grid3",
1852 div { class: "card",
1853 div { class: "icon-box",
1854 Icon { variant: IconVariant::Lightning, size: IconSize::Large, color: "#00d4ff" }
1855 }
1856 h3 { "Instant Transpilation" }
1857 p { "Type normal English. Get programs, logic, and math output in seconds — readable enough to learn from, strict enough to verify." }
1858 }
1859 div { class: "card",
1860 div { class: "icon-box",
1861 Icon { variant: IconVariant::Brain, size: IconSize::Large, color: "#818cf8" }
1862 }
1863 h3 { "Socratic Tutor" }
1864 p { "When your statement is ambiguous, the tutor asks questions that force clarity instead of guessing." }
1865 }
1866 div { class: "card",
1867 div { class: "icon-box",
1868 Icon { variant: IconVariant::Document, size: IconSize::Large, color: "#22c55e" }
1869 }
1870 h3 { "Assumption Surfacing" }
1871 p { "Reveal missing premises, hidden quantifiers, and scope mistakes — the usual sources of bad arguments." }
1872 }
1873 div { class: "card",
1874 div { class: "icon-box",
1875 Icon { variant: IconVariant::Beaker, size: IconSize::Large, color: "#fbbf24" }
1876 }
1877 h3 { "Consistency & Validity Checks" }
1878 p { "Spot contradictions, invalid inferences, and rule collisions across Code, Logic, and Math modes — before they hit production or policy." }
1879 }
1880 div { class: "card",
1881 div { class: "icon-box",
1882 Icon { variant: IconVariant::Tools, size: IconSize::Large, color: "#ec4899" }
1883 }
1884 h3 { "Studio + Curriculum" }
1885 p { "Explore freely in Studio, then build mastery in Learn with structured lessons and practice." }
1886 }
1887 div { class: "card",
1888 div { class: "icon-box",
1889 Icon { variant: IconVariant::Lock, size: IconSize::Large, color: "#8b5cf6" }
1890 }
1891 h3 { "Commercial-Ready" }
1892 p { "Licensing options for teams and enterprises — with a path toward governance and controlled deployments." }
1893 }
1894 }
1895 }
1896
1897 section { class: "section section-center",
1898 h2 { class: "section-title", "Security & Policies" }
1899 p { class: "section-sub",
1900 "Capability-based security with policy blocks. Define who can do what in plain English."
1901 }
1902
1903 div { class: "grid2",
1904 div { class: "card",
1905 div { class: "icon-box",
1906 Icon { variant: IconVariant::Shield, size: IconSize::Large, color: "#60a5fa" }
1907 }
1908 h3 { "Policy Blocks" }
1909 p { "Define security rules as readable policy sections. Who can access what — stated plainly." }
1910 }
1911 div { class: "card",
1912 div { class: "icon-box",
1913 Icon { variant: IconVariant::Lock, size: IconSize::Large, color: "#a78bfa" }
1914 }
1915 h3 { "Capabilities" }
1916 p { "Role-based access control expressed in English. No annotation soup." }
1917 }
1918 div { class: "card",
1919 div { class: "icon-box",
1920 Icon { variant: IconVariant::Beaker, size: IconSize::Large, color: "#22c55e" }
1921 }
1922 h3 { "Check Guards" }
1923 p { "Runtime guard checks that enforce your policies. \"Check that the user is admin.\"" }
1924 }
1925 div { class: "card",
1926 div { class: "icon-box",
1927 Icon { variant: IconVariant::Brain, size: IconSize::Large, color: "#fbbf24" }
1928 }
1929 h3 { "Predicates" }
1930 p { "Define custom predicates: \"A User is admin if the user's role equals 'admin'.\"" }
1931 }
1932 }
1933
1934 div { class: "security-demo",
1935 div { class: "security-demo-head",
1936 span { "Policy → Compiled Output" }
1937 }
1938 div { class: "security-demo-body",
1939 div { class: "security-demo-col",
1940 div { class: "security-demo-label", "LOGOS Policy" }
1941 pre { class: "code",
1942"## Definition\nA User has:\n a role: Text.\n\n## Policy\nA User is admin\n if the user's role equals \"admin\".\n\n## Main\nLet u be a new User with role \"admin\".\nCheck that u is admin.\nShow \"Access granted\"." }
1943 }
1944 div { class: "security-demo-arrow", "→" }
1945 div { class: "security-demo-col",
1946 div { class: "security-demo-label", "Compiled Output" }
1947 pre { class: "code",
1948"struct User {{\n role: String,\n}}\n\nimpl User {{\n fn is_admin(&self) -> bool {{\n self.role == \"admin\"\n }}\n}}\n\nfn main() {{\n let u = User {{ role: \"admin\".into() }};\n assert!(u.is_admin());\n println!(\"Access granted\");\n}}" }
1949 }
1950 }
1951 }
1952 }
1953
1954 section { class: "section", id: "for",
1955 h2 { class: "section-title", style: "padding: 50px 0; font-size: var(--font-display-lg);",
1956 "For people who want their reasoning to survive contact with reality."
1957 }
1958
1959 div { class: "grid3",
1960 div { class: "card",
1961 div { class: "icon-box",
1962 Icon { variant: IconVariant::GraduationCap, size: IconSize::Large, color: "#00d4ff" }
1963 }
1964 h3 { "Students & Educators" }
1965 p { "Teach formal reasoning with feedback that's immediate, concrete, and harder to game than multiple choice." }
1966 }
1967 div { class: "card",
1968 div { class: "icon-box",
1969 Icon { variant: IconVariant::Shield, size: IconSize::Large, color: "#818cf8" }
1970 }
1971 h3 { "Law, Policy, Compliance" }
1972 p { "Translate policy language into verifiable rules. Reduce ambiguity. Make reviews faster and safer." }
1973 }
1974 div { class: "card",
1975 div { class: "icon-box",
1976 Icon { variant: IconVariant::Tools, size: IconSize::Large, color: "#22c55e" }
1977 }
1978 h3 { "Engineering & Research" }
1979 p { "Specify systems, constraints, and invariants in a form you can test — without forcing everyone into formal syntax." }
1980 }
1981 }
1982 }
1983
1984 section { class: "section compare-section section-center",
1985 h2 { class: "section-title", "How LOGOS Compares" }
1986 p { class: "section-sub",
1987 "A new approach to formal reasoning."
1988 }
1989
1990 div { class: "compare-table",
1991 div { class: "compare-row header",
1992 div { class: "compare-cell", "Feature" }
1993 div { class: "compare-cell highlight", "LOGOS" }
1994 div { class: "compare-cell", "Python" }
1995 div { class: "compare-cell", "Lean 4" }
1996 div { class: "compare-cell", "Rust" }
1997 div { class: "compare-cell", "Elixir" }
1998 }
1999 div { class: "compare-row",
2000 div { class: "compare-cell label", "Syntax" }
2001 div { class: "compare-cell highlight", "English prose" }
2002 div { class: "compare-cell", "Symbols" }
2003 div { class: "compare-cell", "Lean DSL" }
2004 div { class: "compare-cell", "Symbols" }
2005 div { class: "compare-cell", "Symbols" }
2006 }
2007 div { class: "compare-row",
2008 div { class: "compare-cell label", "File Format" }
2009 div { class: "compare-cell highlight", "Markdown (.md)" }
2010 div { class: "compare-cell", ".py" }
2011 div { class: "compare-cell", ".lean" }
2012 div { class: "compare-cell", ".rs" }
2013 div { class: "compare-cell", ".ex" }
2014 }
2015 div { class: "compare-row",
2016 div { class: "compare-cell label", "Performance" }
2017 div { class: "compare-cell highlight", "Native (via Rust)" }
2018 div { class: "compare-cell", "Interpreted" }
2019 div { class: "compare-cell", "Native" }
2020 div { class: "compare-cell", "Native" }
2021 div { class: "compare-cell", "BEAM VM" }
2022 }
2023 div { class: "compare-row",
2024 div { class: "compare-cell label", "Proofs" }
2025 div { class: "compare-cell highlight", "Built-in" }
2026 div { class: "compare-cell", "None" }
2027 div { class: "compare-cell", "Required" }
2028 div { class: "compare-cell", "Optional" }
2029 div { class: "compare-cell", "None" }
2030 }
2031 div { class: "compare-row",
2032 div { class: "compare-cell label", "Memory" }
2033 div { class: "compare-cell highlight", "Ownership (English)" }
2034 div { class: "compare-cell", "GC" }
2035 div { class: "compare-cell", "GC" }
2036 div { class: "compare-cell", "Ownership" }
2037 div { class: "compare-cell", "GC" }
2038 }
2039 }
2040 }
2041
2042 section { class: "section", id: "faq",
2043 h2 { class: "section-title", "FAQ" }
2044 p { class: "section-sub",
2045 "Common questions about LOGICAFFEINE."
2046 }
2047
2048 div { class: "grid2",
2049 div { class: "faq-item",
2050 div { class: "faq-q", "Is it really free?" }
2051 div { class: "faq-a", "Yes — free for individuals, universities, and teams under 25 people. For commercial licensing, contact us." }
2052 }
2053 div { class: "faq-item",
2054 div { class: "faq-q", "Do I need to know logic already?" }
2055 div { class: "faq-a", "No. Start in Learn. The system introduces concepts progressively and uses examples to teach scope, quantifiers, and structure." }
2056 }
2057 div { class: "faq-item",
2058 div { class: "faq-q", "Is this an AI that \"guesses\"?" }
2059 div { class: "faq-a", "The goal is the opposite: to force explicit structure. When language is ambiguous, the tutor prompts clarifying questions." }
2060 }
2061 div { class: "faq-item",
2062 div { class: "faq-q", "Where do I begin?" }
2063 div { class: "faq-a", "If you want speed, open Studio. If you want mastery, Start Learning and follow the lessons." }
2064 }
2065 div { class: "faq-item",
2066 div { class: "faq-q", "What is LOGOS written in?" }
2067 div { class: "faq-a", "Rust. The entire transpiler, parser, and runtime are written in Rust for maximum performance and safety." }
2068 }
2069 div { class: "faq-item",
2070 div { class: "faq-q", "How fast is it?" }
2071 div { class: "faq-a", "Native speed. LOGOS compiles to Rust, which then compiles via LLVM to optimized machine code. Zero interpreter overhead." }
2072 }
2073 }
2074 }
2075
2076 section {
2077 class: "section",
2078 style: "padding-bottom: 100px;",
2079 div {
2080 class: "card",
2081 style: "padding: 32px; overflow: visible;",
2082 h2 { class: "section-title", "Make your reasoning impossible to ignore." }
2083 p {
2084 class: "section-sub",
2085 style: "margin-bottom: 20px;",
2086 "Start with the Curriculum, or explore any mode in the Studio. Code, Logic, Math — your call."
2087 }
2088 div { class: "hero-ctas",
2089 Link { to: Route::Learn {}, class: "btn btn-primary", "Start Learning" }
2090 Link { to: Route::Pricing {}, class: "btn btn-ghost", "Contact Us" }
2091 }
2092 }
2093 }
2094
2095 Footer {}
2096 }
2097 }
2098 }
2099}
2100
2101#[component(lazy)]
2105fn LandingShowcase() -> Element {
2106 let mut demo_mode = use_signal(|| StudioMode::Code);
2107 let mut active_index = use_signal(|| 0usize);
2108 let mut cycling_paused = use_signal(|| false);
2109 let mut timer_started = use_signal(|| false);
2110 let mut terminal_height = use_signal(|| 135.0f64);
2111 let mut resizing_terminal = use_signal(|| false);
2112 let mut compiled_height = use_signal(|| 140.0f64);
2113 let mut show_compiled = use_signal(|| false);
2114 let mut resizing_compiled = use_signal(|| false);
2115 let mut code_content = use_signal(|| String::new());
2116 let mut output_lines = use_signal(|| Vec::<String>::new());
2117 let mut output_error = use_signal(|| Option::<String>::None);
2118 let mut compiled_output = use_signal(|| String::new());
2119 let mut is_running = use_signal(|| false);
2120
2121 use_effect(move || {
2122 let mode = *demo_mode.read();
2123 let idx = *active_index.read();
2124 let examples = examples_for_mode(mode);
2125 let clamped = idx.min(examples.len().saturating_sub(1));
2126 let ex = &examples[clamped];
2127 let content = ex.content.to_string();
2128 code_content.set(content.clone());
2129 output_error.set(None);
2130 compiled_output.set(ex.compiled.to_string());
2131
2132 match mode {
2133 StudioMode::Code => {
2134 output_lines.set(Vec::new());
2135 spawn(async move {
2136 let result = interpret_for_ui(&content).await;
2137 output_lines.set(result.lines);
2138 output_error.set(result.error);
2139 });
2140 }
2141 StudioMode::Logic | StudioMode::Hardware => {
2142 let mut lines: Vec<String> = Vec::new();
2143
2144 if content.contains("## Theorem:") {
2145 for line in content.lines() {
2146 let trimmed = line.trim();
2147 if let Some(sentence) = trimmed.strip_prefix("Given:") {
2148 let fol = compile_for_ui(sentence.trim());
2149 if let Some(logic) = fol.logic {
2150 lines.push(logic);
2151 }
2152 } else if let Some(sentence) = trimmed.strip_prefix("Prove:") {
2153 let fol = compile_for_ui(sentence.trim());
2154 if let Some(logic) = fol.logic {
2155 lines.push(format!("Goal: {}", logic));
2156 }
2157 }
2158 }
2159
2160 let theorem_result = compile_theorem_for_ui(&content);
2161 if let Some(ref err) = theorem_result.error {
2162 output_error.set(Some(err.clone()));
2163 } else {
2164 lines.push(String::new());
2165 if theorem_result.derivation.is_some() {
2166 lines.push(format!("Theorem: {} ✓", theorem_result.name));
2167 } else {
2168 lines.push(format!("Theorem: {} — not proved", theorem_result.name));
2169 }
2170 }
2171 } else {
2172 let result = compile_for_ui(&content);
2173 if let Some(logic) = result.logic {
2174 for line in logic.lines() {
2175 lines.push(line.to_string());
2176 }
2177 }
2178 if let Some(ref err) = result.error {
2179 output_error.set(Some(err.clone()));
2180 }
2181 }
2182
2183 output_lines.set(lines);
2184 }
2185 StudioMode::Math => {
2186 let (lines, err) = execute_math_code(&content);
2187 output_lines.set(lines);
2188 output_error.set(err);
2189 }
2190 }
2191 });
2192
2193 use_effect(move || {
2194 if *timer_started.read() { return; }
2195 timer_started.set(true);
2196 #[cfg(target_arch = "wasm32")]
2197 spawn(async move {
2198 loop {
2199 gloo_timers::future::TimeoutFuture::new(7_000).await;
2200 if !*cycling_paused.read() {
2201 let mode = *demo_mode.read();
2202 let count = examples_for_mode(mode).len();
2203 let next = (*active_index.read() + 1) % count;
2204 active_index.set(next);
2205 }
2206 }
2207 });
2208 });
2209
2210 rsx! {
2211 div {
2212 class: "mini-studio",
2213 id: "product",
2214 style: if *resizing_terminal.read() || *resizing_compiled.read() { "user-select: none;" } else { "" },
2215 onmouseenter: move |_| { cycling_paused.set(true); },
2216 onmouseleave: move |_| {
2217 cycling_paused.set(false);
2218 resizing_terminal.set(false);
2219 resizing_compiled.set(false);
2220 },
2221 onmousemove: move |evt| {
2222 let window = web_sys::window().unwrap();
2223 let document = window.document().unwrap();
2224 let cta_height: f64 = 45.0;
2225 if *resizing_terminal.read() {
2226 if let Some(el) = document.get_element_by_id("product") {
2227 let rect = el.get_bounding_client_rect();
2228 let coords = evt.data().client_coordinates();
2229 let client_y: f64 = coords.y;
2230 let below = cta_height
2231 + if *show_compiled.read() { *compiled_height.read() + 6.0 } else { 0.0 };
2232 let new_height = rect.bottom() - client_y - below;
2233 terminal_height.set(new_height.clamp(60.0, 300.0));
2234 }
2235 } else if *resizing_compiled.read() {
2236 if let Some(el) = document.get_element_by_id("product") {
2237 let rect = el.get_bounding_client_rect();
2238 let coords = evt.data().client_coordinates();
2239 let client_y: f64 = coords.y;
2240 let new_height = rect.bottom() - client_y - cta_height;
2241 let max_compiled = rect.height() - *terminal_height.read() - cta_height - 6.0 - 120.0;
2242 compiled_height.set(new_height.clamp(60.0, max_compiled.max(60.0)));
2243 }
2244 }
2245 },
2246 onmouseup: move |_| {
2247 resizing_terminal.set(false);
2248 resizing_compiled.set(false);
2249 },
2250 ontouchmove: move |evt| {
2251 let window = web_sys::window().unwrap();
2252 let document = window.document().unwrap();
2253 let cta_height: f64 = 45.0;
2254 if *resizing_terminal.read() || *resizing_compiled.read() {
2255 evt.prevent_default();
2256 let touches = evt.data().touches();
2257 if let Some(touch) = touches.first() {
2258 if let Some(el) = document.get_element_by_id("product") {
2259 let rect = el.get_bounding_client_rect();
2260 let coords = touch.client_coordinates();
2261 let client_y: f64 = coords.y;
2262 if *resizing_terminal.read() {
2263 let below = cta_height
2264 + if *show_compiled.read() { *compiled_height.read() + 6.0 } else { 0.0 };
2265 let new_height = rect.bottom() - client_y - below;
2266 terminal_height.set(new_height.clamp(60.0, 300.0));
2267 } else {
2268 let new_height = rect.bottom() - client_y - cta_height;
2269 let max_compiled = rect.height() - *terminal_height.read() - cta_height - 6.0 - 120.0;
2270 compiled_height.set(new_height.clamp(60.0, max_compiled.max(60.0)));
2271 }
2272 }
2273 }
2274 }
2275 },
2276 ontouchend: move |_| {
2277 resizing_terminal.set(false);
2278 resizing_compiled.set(false);
2279 },
2280
2281 div { class: "mini-studio-head",
2282 div { class: "win-dots",
2283 div { class: "wdot wr" }
2284 div { class: "wdot wy" }
2285 div { class: "wdot wg" }
2286 }
2287 div { class: "mini-mode-toggle",
2288 button {
2289 class: if *demo_mode.read() == StudioMode::Code { "mini-toggle-btn active" } else { "mini-toggle-btn" },
2290 onclick: move |_| {
2291 demo_mode.set(StudioMode::Code);
2292 active_index.set(0);
2293 },
2294 span { "λ" }
2295 span { class: "mini-toggle-label", "Code" }
2296 }
2297 button {
2298 class: if *demo_mode.read() == StudioMode::Logic { "mini-toggle-btn active" } else { "mini-toggle-btn" },
2299 onclick: move |_| {
2300 demo_mode.set(StudioMode::Logic);
2301 active_index.set(0);
2302 show_compiled.set(false);
2303 },
2304 span { "∀" }
2305 span { class: "mini-toggle-label", "Logic" }
2306 }
2307 button {
2308 class: if *demo_mode.read() == StudioMode::Math { "mini-toggle-btn active" } else { "mini-toggle-btn" },
2309 onclick: move |_| {
2310 demo_mode.set(StudioMode::Math);
2311 active_index.set(0);
2312 show_compiled.set(false);
2313 },
2314 span { "π" }
2315 span { class: "mini-toggle-label", "Math" }
2316 }
2317 }
2318 }
2319
2320 div { class: "mini-action-bar",
2321 if *demo_mode.read() == StudioMode::Code {
2322 button {
2323 class: "mini-exec-btn compile",
2324 onclick: move |_| {
2325 let code = code_content.read().clone();
2326
2327 match generate_rust_code(&code) {
2328 Ok(rust) => compiled_output.set(rust),
2329 Err(e) => compiled_output.set(format!("// Compile error: {:?}", e)),
2330 }
2331
2332 let current = *show_compiled.read();
2333 if !current {
2334 show_compiled.set(true);
2335 }
2336 },
2337 "🦀"
2338 span { class: "btn-label", "Compile to Rust" }
2339 }
2340 }
2341 button {
2342 class: "mini-exec-btn run",
2343 onclick: move |_| {
2344 let code = code_content.read().clone();
2345 let mode = *demo_mode.read();
2346 is_running.set(true);
2347 output_lines.set(Vec::new());
2348 output_error.set(None);
2349
2350 match mode {
2351 StudioMode::Code => {
2352 spawn(async move {
2353 let result = interpret_for_ui(&code).await;
2354 output_lines.set(result.lines);
2355 output_error.set(result.error);
2356 is_running.set(false);
2357 });
2358 }
2359 StudioMode::Logic | StudioMode::Hardware => {
2360 let mut lines: Vec<String> = Vec::new();
2361
2362 if code.contains("## Theorem:") {
2363 for line in code.lines() {
2365 let trimmed = line.trim();
2366 if let Some(sentence) = trimmed.strip_prefix("Given:") {
2367 let sentence = sentence.trim();
2368 let fol = compile_for_ui(sentence);
2369 if let Some(logic) = fol.logic {
2370 lines.push(logic);
2371 }
2372 } else if let Some(sentence) = trimmed.strip_prefix("Prove:") {
2373 let sentence = sentence.trim();
2374 let fol = compile_for_ui(sentence);
2375 if let Some(logic) = fol.logic {
2376 lines.push(format!("Goal: {}", logic));
2377 }
2378 }
2379 }
2380
2381 let theorem_result = compile_theorem_for_ui(&code);
2383 if let Some(ref err) = theorem_result.error {
2384 output_error.set(Some(err.clone()));
2385 } else {
2386 lines.push(String::new());
2387 let proved = theorem_result.derivation.is_some();
2388 if proved {
2389 lines.push(format!("Theorem: {} ✓", theorem_result.name));
2390 } else {
2391 lines.push(format!("Theorem: {} — not proved", theorem_result.name));
2392 }
2393 }
2394 } else {
2395 let result = compile_for_ui(&code);
2396 if let Some(logic) = result.logic {
2397 for line in logic.lines() {
2398 lines.push(line.to_string());
2399 }
2400 }
2401 if let Some(ref err) = result.error {
2402 output_error.set(Some(err.clone()));
2403 }
2404 }
2405
2406 output_lines.set(lines);
2407 is_running.set(false);
2408 }
2409 StudioMode::Math => {
2410 let (lines, err) = execute_math_code(&code);
2411 output_lines.set(lines);
2412 output_error.set(err);
2413 is_running.set(false);
2414 }
2415 }
2416 },
2417 "▶"
2418 span { class: "btn-label",
2419 if *demo_mode.read() == StudioMode::Code { "Run" } else { "Execute" }
2420 }
2421 }
2422 }
2423
2424 div { class: "mini-file-tabs",
2425 for i in 0..examples_for_mode(*demo_mode.read()).len() {
2426 button {
2427 key: "{i}",
2428 class: if *active_index.read() == i { "mini-file-tab active" } else { "mini-file-tab" },
2429 onclick: move |_| {
2430 active_index.set(i);
2431 cycling_paused.set(true);
2432 },
2433 "{examples_for_mode(*demo_mode.read())[i].filename}"
2434 }
2435 }
2436 a {
2437 class: "mini-file-tab view-more",
2438 href: "/studio",
2439 "View more..."
2440 }
2441 }
2442
2443 div { class: "mini-studio-body",
2444 div { class: "mini-explorer",
2445 div { class: "mini-explorer-label", "FILES" }
2446 for i in 0..examples_for_mode(*demo_mode.read()).len() {
2447 div {
2448 key: "{i}",
2449 class: if *active_index.read() == i { "mini-file-item active" } else { "mini-file-item" },
2450 onclick: move |_| {
2451 active_index.set(i);
2452 cycling_paused.set(true);
2453 },
2454 span { class: "mini-file-icon", "●" }
2455 span { "{examples_for_mode(*demo_mode.read())[i].filename}" }
2456 }
2457 }
2458 a {
2459 class: "mini-file-item view-more",
2460 href: "/studio",
2461 "View more..."
2462 }
2463 }
2464 div { class: "mini-code-panel",
2465 {
2466 let mode = *demo_mode.read();
2467 let examples = examples_for_mode(mode);
2468 let idx = (*active_index.read()).min(examples.len().saturating_sub(1));
2469 let ex = &examples[idx];
2470 rsx! {
2471 div { class: "mini-code-filename",
2472 span { "{ex.filename}" }
2473 span { " {ex.icon}" }
2474 }
2475 CodeEditor {
2476 value: code_content.read().clone(),
2477 on_change: move |v: String| code_content.set(v),
2478 language: match mode {
2479 StudioMode::Code => Language::Logos,
2480 StudioMode::Logic | StudioMode::Hardware => Language::Logos,
2481 StudioMode::Math => Language::Vernacular,
2482 },
2483 placeholder: "Enter code...".to_string(),
2484 }
2485 }
2486 }
2487 }
2488 }
2489
2490 div {
2491 class: if *resizing_terminal.read() { "mini-terminal-resizer active" } else { "mini-terminal-resizer" },
2492 onmousedown: move |e| {
2493 e.prevent_default();
2494 resizing_terminal.set(true);
2495 },
2496 ontouchstart: move |e| {
2497 e.prevent_default();
2498 resizing_terminal.set(true);
2499 },
2500 }
2501
2502 div { class: "mini-terminal", style: "height: {terminal_height}px;",
2503 div { class: "mini-terminal-head", "OUTPUT" }
2504 div { class: "mini-terminal-body",
2505 if *is_running.read() {
2506 div { class: "mini-output-loading", "Running..." }
2507 }
2508 {
2509 let lines = output_lines.read().clone();
2510 let error = output_error.read().clone();
2511 rsx! {
2512 for (i, line) in lines.iter().enumerate() {
2513 pre { key: "{i}", class: "mini-output-line", "{line}" }
2514 }
2515 if let Some(ref err) = error {
2516 pre { class: "mini-output-error", "{err}" }
2517 }
2518 if lines.is_empty() && error.is_none() && !*is_running.read() {
2519 div { class: "mini-output-empty", "Click Run to see output" }
2520 }
2521 }
2522 }
2523 }
2524 }
2525
2526 if *show_compiled.read() {
2527 div {
2528 class: if *resizing_compiled.read() { "mini-terminal-resizer active" } else { "mini-terminal-resizer" },
2529 onmousedown: move |e| {
2530 e.prevent_default();
2531 resizing_compiled.set(true);
2532 },
2533 ontouchstart: move |e| {
2534 e.prevent_default();
2535 resizing_compiled.set(true);
2536 },
2537 }
2538 div { class: "mini-compiled", style: "height: {compiled_height}px;",
2539 div { class: "mini-compiled-head", "COMPILED RUST" }
2540 pre { class: "mini-compiled-body", "{compiled_output}" }
2541 }
2542 }
2543
2544 div { class: "mini-studio-cta",
2545 {
2546 let mode = *demo_mode.read();
2547 let examples = examples_for_mode(mode);
2548 let idx = (*active_index.read()).min(examples.len().saturating_sub(1));
2549 let studio_url = crate::ui::router::studio_file_url(examples[idx].studio_path);
2550 rsx! {
2551 a { href: "{studio_url}", class: "mini-cta-btn",
2552 "Try it in the Studio →"
2553 }
2554 }
2555 }
2556 }
2557 }
2558 }
2559}
2560
2561#[component(lazy)]
2563fn LandingHelloWorld() -> Element {
2564 let mut hello_code = use_signal(|| CODE_DEMO_EXAMPLES[0].content.to_string());
2565 let mut hello_output = use_signal(|| CODE_DEMO_EXAMPLES[0].output.lines().map(|l| l.to_string()).collect::<Vec<_>>());
2566 let mut hello_error = use_signal(|| Option::<String>::None);
2567 let mut hello_running = use_signal(|| false);
2568
2569 rsx! {
2570 div { class: "hello-editor",
2571 div { class: "hello-editor-head",
2572 span { class: "hello-filename", "hello-world.logos" }
2573 button {
2574 class: "hello-run-btn",
2575 disabled: *hello_running.read(),
2576 onclick: move |_| {
2577 let code = hello_code.read().clone();
2578 hello_running.set(true);
2579 hello_output.set(Vec::new());
2580 hello_error.set(None);
2581 spawn(async move {
2582 let result = interpret_for_ui(&code).await;
2583 hello_output.set(result.lines);
2584 hello_error.set(result.error);
2585 hello_running.set(false);
2586 });
2587 },
2588 if *hello_running.read() { "Running..." } else { "▶ Run" }
2589 }
2590 }
2591 div { class: "hello-editor-body",
2592 div { class: "hello-editor-left",
2593 CodeEditor {
2594 value: hello_code.read().clone(),
2595 on_change: move |v: String| hello_code.set(v),
2596 language: Language::Logos,
2597 placeholder: "Enter code...".to_string(),
2598 }
2599 }
2600 div { class: "hello-editor-right",
2601 div { class: "hello-output-head", "Output" }
2602 div { class: "hello-output-body",
2603 if *hello_running.read() {
2604 div { class: "hello-output-loading", "Running..." }
2605 }
2606 {
2607 let lines = hello_output.read().clone();
2608 let error = hello_error.read().clone();
2609 rsx! {
2610 for (i, line) in lines.iter().enumerate() {
2611 pre { key: "{i}", class: "hello-output-line", "{line}" }
2612 }
2613 if let Some(ref err) = error {
2614 pre { class: "hello-output-error", "{err}" }
2615 }
2616 if lines.is_empty() && error.is_none() && !*hello_running.read() {
2617 div { class: "hello-output-empty", "Click Run to see output" }
2618 }
2619 }
2620 }
2621 }
2622 }
2623 }
2624 }
2625 }
2626}