Skip to main content

euv_ui/style/class/page/
fn.rs

1use super::*;
2
3#[cfg(not(feature = "theme-custom"))]
4class! {
5    // ═══════════════════════════════════════════════════════════════════════════
6    // Camera Page
7    // ═══════════════════════════════════════════════════════════════════════════
8
9    pub c_camera_video_container {
10        margin: format!("{} 0", var!(space-lg));
11    }
12
13    pub c_camera_video_active {
14        width: "100%";
15        background: var!(surface-media);
16        aspect-ratio: "16 / 9";
17        object-fit: "cover";
18        border: format!("2px solid {}", var!(primary));
19        box-sizing: "border-box";
20    }
21
22    pub c_camera_video_hidden {
23        width: "100%";
24        aspect-ratio: "16 / 9";
25        display: "none";
26    }
27
28    pub c_camera_video_placeholder {
29        width: "100%";
30        aspect-ratio: "16 / 9";
31        border: format!("2px dashed {}", var!(border));
32        display: "flex";
33        align-items: "center";
34        justify-content: "center";
35        box-sizing: "border-box";
36    }
37
38    pub c_camera_placeholder_content {
39        display: "flex";
40        flex-direction: "column";
41        align-items: "center";
42        gap: var!(space-md);
43    }
44
45    pub c_camera_placeholder_icon {
46        font-size: var!(font-4xl);
47        opacity: "1";
48    }
49
50    pub c_camera_placeholder_text {
51        font-size: var!(font-base);
52        color: "inherit";
53        opacity: "1";
54    }
55
56    pub c_camera_error_box {
57        margin: format!("{} 0px", var!(gap-component));
58        color: var!(destructive);
59        font-size: var!(font-base);
60        word-break: "break-all";
61        overflow-wrap: "break-word";
62        box-sizing: "border-box";
63    }
64
65    pub c_camera_scan_result_box {
66        margin-top: var!(gap-component);
67        box-sizing: "border-box";
68    }
69
70    pub c_camera_scan_result_label {
71        font-size: var!(font-sm);
72        font-weight: var!(font-weight-semibold);
73        color: var!(foreground);
74        margin-bottom: var!(space-xs);
75    }
76
77    pub c_camera_scan_result_value {
78        font-size: var!(font-base);
79        color: "inherit";
80        word-break: "break-all";
81        overflow-wrap: "break-word";
82    }
83
84    // ═══════════════════════════════════════════════════════════════════════════
85    // Canvas Drawing Board
86    // ═══════════════════════════════════════════════════════════════════════════
87
88    pub c_canvas_preview_container {
89        margin: format!("{} 0", var!(space-lg));
90        overflow: "hidden";
91        border: format!("2px solid {}", var!(border));
92        background: var!(surface-drawing);
93        aspect-ratio: "9 / 16";
94        display: "flex";
95        align-items: "center";
96        justify-content: "center";
97        box-sizing: "border-box";
98    }
99
100    pub c_canvas_placeholder {
101        color: var!(muted-foreground);
102        font-size: var!(font-sm);
103        text-align: "center";
104        padding: var!(space-lg);
105    }
106
107    pub c_canvas_preview_image {
108        width: "100%";
109        height: "100%";
110        object-fit: "cover";
111        display: "block";
112    }
113
114    pub c_canvas_container_fullscreen {
115        width: "100%";
116        height: "100%";
117        background: var!(background);
118        display: "flex";
119        flex-direction: "column";
120        position: "fixed";
121        top: "0";
122        left: "0";
123        z-index: "10002";
124        padding: format!("{} {} {} {}", var!(padding-shell-top), var!(space-lg), var!(padding-shell-bottom), var!(space-lg));
125        box-sizing: "border-box";
126    }
127
128    pub c_canvas_drawing_fullscreen_wrapper {
129        flex: "1";
130        display: "flex";
131        align-items: "center";
132        justify-content: "center";
133        overflow: "hidden";
134        padding: var!(space-xs);
135    }
136
137    pub c_canvas_drawing_fullscreen {
138        border: format!("2px solid {}", var!(border));
139        display: "block";
140        cursor: "crosshair";
141        touch-action: "none";
142    }
143
144    pub c_canvas_fullscreen_toolbar {
145        display: "flex";
146        flex-direction: "column";
147        align-items: "stretch";
148        width: "100%";
149        padding: format!("{} {}", var!(space-xs), "0px");
150        flex-shrink: "0";
151        gap: var!(space-xs);
152    }
153
154    pub c_canvas_fullscreen_toolbar_row_top {
155        display: "flex";
156        align-items: "center";
157        height: var!(mobile-header-height);
158        width: "100%";
159        gap: var!(space-sm);
160    }
161
162    pub c_canvas_fullscreen_toolbar_color_wrapper {
163        display: "flex";
164        align-items: "center";
165        justify-content: "center";
166        flex: "1";
167        min-width: "0";
168    }
169
170    pub c_canvas_color_input_fullscreen {
171        width: "100%";
172        height: "42px";
173        border: format!("{} solid {}", var!(border-width), var!(border));
174        cursor: "pointer";
175        padding: format!("{}", var!(space-xs));
176    }
177
178    pub c_canvas_fullscreen_toolbar_row_bottom {
179        display: "flex";
180        align-items: "center";
181        width: "100%";
182    }
183
184    pub c_canvas_fullscreen_range_input {
185        flex: "1 1 0%";
186        min-width: "120px";
187        cursor: "pointer";
188        -webkit-appearance: "none";
189        appearance: "none";
190        border: "none";
191        outline: "none";
192        height: "24px";
193        ::-webkit-slider-runnable-track {
194            height: "6px";
195            background: format!("linear-gradient(to right, {} 0%, {} var(--value,50%), {} var(--value,50%), {} 100%)", var!(foreground), var!(foreground), var!(border), var!(border));
196            border-radius: var!(radius-xs);
197            border: "none";
198        }
199        ::-webkit-slider-thumb {
200            width: "20px";
201            height: "20px";
202            background: var!(background);
203            border: format!("2px solid {}", var!(primary));
204            border-radius: var!(radius-full);
205            cursor: "pointer";
206            -webkit-appearance: "none";
207            // The thumb is taller than the track it rides on, so the margin
208            // pulls it up until it sits centred on the track.
209            margin-top: "-7px";
210        }
211        ::-moz-range-track {
212            height: "6px";
213            background: format!("linear-gradient(to right, {} 0%, {} var(--value,50%), {} var(--value,50%), {} 100%)", var!(foreground), var!(foreground), var!(border), var!(border));
214            border-radius: var!(radius-xs);
215            border: "none";
216        }
217        ::-moz-range-thumb {
218            width: "20px";
219            height: "20px";
220            background: var!(background);
221            border: format!("2px solid {}", var!(primary));
222            border-radius: var!(radius-full);
223            cursor: "pointer";
224        }
225    }
226
227    pub c_canvas_fullscreen_toolbar_button {
228        flex: "0 0 auto";
229        overflow: "hidden";
230    }
231
232    // ═══════════════════════════════════════════════════════════════════════════
233    // Game Fullscreen (2D / 3D)
234    // ═══════════════════════════════════════════════════════════════════════════
235
236    // Fullscreen overlay container for the 2D and 3D game pages.
237    //
238    // Mirrors `c_canvas_container_fullscreen` so the safe-area caching
239    // pass in `UseEuvLayout::apply_cached_insets` can pick this selector
240    // up alongside the canvas one. Differs in that the game overlay holds
241    // the *same* `<canvas>` element that the inline page mounts (no
242    // canvas re-creation), so the running game loop, balls, cubes, FPS
243    // counter and other reactive state survive the fullscreen toggle
244    // without reset.
245    pub c_game_container_fullscreen {
246        width: "100%";
247        height: "100%";
248        background: var!(background);
249        display: "flex";
250        flex-direction: "column";
251        position: "fixed";
252        top: "0";
253        left: "0";
254        z-index: "10002";
255        padding: format!(
256        "{} {} {} {}",
257        var!(padding-shell-top),
258        var!(space-lg),
259        var!(padding-shell-bottom),
260        var!(space-lg)
261        );
262        box-sizing: "border-box";
263    }
264
265    // Toolbar wrapping the fullscreen exit button on game pages.
266    //
267    // Game pages only need an `Exit` button here (no color picker / line
268    // width control like the canvas drawing board), so the layout is a
269    // single horizontal row at the bottom of the overlay.
270    pub c_game_fullscreen_toolbar {
271        display: "flex";
272        align-items: "center";
273        justify-content: "flex-end";
274        width: "100%";
275        padding: format!("{} {} {} {}", var!(space-xs), var!(space-lg), var!(space-xs), var!(space-lg));
276        flex-shrink: "0";
277        gap: var!(space-sm);
278        box-sizing: "border-box";
279    }
280
281    // Wrapper that fills the fullscreen game container with the canvas.
282    //
283    // The game's `<canvas>` backing buffer is 600x400 (3:2). The canvas
284    // itself has `width: 100%; height: 100%` so it fills this wrapper
285    // exactly, and `object-fit: contain` on the canvas class makes the
286    // browser uniformly scale the backing buffer to fit while preserving
287    // its 3:2 aspect ratio. Without `object-fit: contain`, the canvas
288    // would be stretched to whatever shape the wrapper has, making balls
289    // render as horizontally-ovoid ellipses (e.g. on 1280x800 viewports
290    // → 1.6:1, slightly squashed vertically). The letterbox bars appear
291    // inside the canvas element rather than around an outer wrapper, so
292    // the canvas always fills the fullscreen container with the backing
293    // buffer uniformly scaled to its native aspect ratio.
294    pub c_game_fullscreen_canvas_letterbox {
295        position: "relative";
296        width: "100%";
297        max-width: "100%";
298        max-height: "100%";
299        height: "100%";
300        display: "flex";
301        align-items: "center";
302        justify-content: "center";
303    }
304
305    // ═══════════════════════════════════════════════════════════════════════════
306    // Custom Attrs Demo
307    // ═══════════════════════════════════════════════════════════════════════════
308
309    pub c_custom_attrs_demo {
310        // Use the theme foreground token (not `inherit`) so the text inside
311        // the dynamic demo block stays readable when the block itself
312        // applies a user-supplied background (e.g. `background-color: #000000`)
313        // that otherwise paints the inherited text the same colour.
314        color: var!(foreground);
315        box-sizing: "border-box";
316    }
317
318    pub c_demo_text {
319        color: var!(foreground);
320        margin-bottom: var!(gap-component);
321    }
322
323    pub c_demo_text_muted {
324        color: var!(foreground);
325        opacity: "1";
326        font-size: var!(font-base);
327        margin-bottom: "0px";
328    }
329
330    // ═══════════════════════════════════════════════════════════════════════════
331    // Conditional Rendering Demo
332    // ═══════════════════════════════════════════════════════════════════════════
333
334    pub c_toggle_content {
335        margin-top: var!(gap-component);
336        color: "inherit";
337    }
338
339    pub c_toggle_title {
340        margin-top: "0px";
341        color: var!(primary);
342        font-size: var!(font-md);
343    }
344
345    pub c_role_button_row {
346        display: "flex";
347        flex-wrap: "nowrap";
348        gap: var!(gap-element);
349        margin-bottom: var!(gap-component);
350    }
351
352    pub c_role_guest_text {
353        color: var!(foreground);
354        font-size: var!(font-base);
355    }
356
357    pub c_role_user_text {
358        color: var!(foreground);
359        font-size: var!(font-base);
360    }
361
362    pub c_role_admin_text {
363        color: var!(foreground);
364        font-size: var!(font-base);
365    }
366
367    // ═══════════════════════════════════════════════════════════════════════════
368    // 404 Not Found
369    // ═══════════════════════════════════════════════════════════════════════════
370
371    pub c_render_count_text {
372        font-size: var!(font-base);
373        color: "inherit";
374        margin-bottom: var!(gap-component);
375    }
376
377    // ═══════════════════════════════════════════════════════════════════════════
378    // Event Demo
379    // ═══════════════════════════════════════════════════════════════════════════
380
381    pub c_event_result {
382        font-size: var!(font-base);
383        color: "inherit";
384        margin-top: var!(gap-element);
385    }
386
387    pub c_event_highlight {
388        font-weight: var!(font-weight-semibold);
389        color: var!(primary);
390    }
391
392    pub c_event_info_grid {
393        display: "grid";
394        grid-template-columns: "minmax(0px, 1fr) minmax(0px, 1fr)";
395        gap: var!(gap-element);
396        margin-top: var!(gap-component);
397        @media ((max-width: 767px)) {
398            grid-template-columns: "1fr";
399            gap: var!(gap-element);
400        }
401    }
402
403    pub c_event_info_row {
404        display: "flex";
405        align-items: "center";
406        gap: var!(space-sm);
407        overflow: "hidden";
408    }
409
410    pub c_event_info_label {
411        font-size: var!(font-base);
412        font-weight: var!(font-weight-semibold);
413        color: var!(foreground);
414        flex-shrink: "0";
415    }
416
417    pub c_event_info_value {
418        c_text_ellipsis();
419        font-size: var!(font-base);
420        font-weight: var!(font-weight-semibold);
421        color: var!(primary);
422        font-family: var!(font-family-mono);
423        flex: "1";
424    }
425
426    pub c_event_mouse_area {
427        min-height: "120px";
428        padding: format!("{} 0px", var!(space-xl));
429        border: format!("2px dashed {}", var!(border));
430        cursor: "crosshair";
431        text-align: "center";
432        user-select: "none";
433        color: "inherit";
434    }
435
436    pub c_event_drag_zone {
437        min-height: "100px";
438        padding: format!("{} 0px", var!(space-xl));
439        border: format!("2px dashed {}", var!(border));
440        text-align: "center";
441        user-select: "none";
442        color: var!(foreground);
443    }
444
445    pub c_event_drag_zone_active {
446        min-height: "100px";
447        padding: format!("{} 0px", var!(space-xl));
448        border: format!("2px dashed {}", var!(border));
449        text-align: "center";
450        user-select: "none";
451        color: var!(foreground);
452    }
453
454    pub c_event_drag_item {
455        display: "inline-block";
456        padding: format!("{} {}", var!(space-sm), var!(space-xl));
457        background: var!(primary);
458        color: var!(primary-foreground);
459        font-size: var!(font-base);
460        font-weight: var!(font-weight-medium);
461        cursor: "grab";
462        margin: var!(space-sm);
463    }
464
465    pub c_event_drop_zone {
466        border: format!("2px dashed {}", var!(border));
467        padding: format!("{} {}", var!(space-4xl), var!(space-xl));
468        text-align: "center";
469        cursor: "pointer";
470    }
471
472    pub c_event_drop_zone_active {
473        border: format!("2px dashed {}", var!(primary));
474        padding: format!("{} {}", var!(space-4xl), var!(space-xl));
475        text-align: "center";
476        cursor: "pointer";
477        background: var!(muted);
478    }
479
480    pub c_event_drop_icon {
481        font-size: var!(font-5xl);
482        display: "block";
483        padding-bottom: var!(space-md);
484    }
485
486    pub c_event_drop_text {
487        font-size: var!(font-lg);
488        font-weight: var!(font-weight-medium);
489        color: "inherit";
490        padding-bottom: var!(space-md);
491    }
492
493    pub c_event_drop_hint {
494        font-size: var!(font-base);
495        color: "inherit";
496        opacity: "1";
497        margin: "0px";
498    }
499
500    pub c_event_wheel_zone {
501        min-height: "120px";
502        padding: format!("{} 0px", var!(space-xl));
503        border: format!("2px dashed {}", var!(border));
504        text-align: "center";
505        overflow: "auto";
506        color: var!(foreground);
507    }
508
509    pub c_event_clipboard_area {
510        color: var!(foreground);
511    }
512
513    pub c_event_touch_zone {
514        min-height: "120px";
515        padding: format!("{} 0px", var!(space-xl));
516        border: format!("2px dashed {}", var!(primary));
517        text-align: "center";
518        touch-action: "none";
519        user-select: "none";
520        color: var!(primary);
521    }
522
523    pub c_event_form_area {
524        padding: format!("{} 0px", var!(space-lg));
525        color: var!(foreground);
526    }
527
528    pub c_event_media_area {
529        padding: format!("{} 0px", var!(space-lg));
530        color: var!(foreground);
531        max-width: "100%";
532        overflow: "hidden";
533    }
534
535    pub c_event_audio {
536        width: "100%";
537        max-width: "100%";
538    }
539
540    pub c_event_video_area {
541        color: var!(foreground);
542        width: "100%";
543        overflow: "hidden";
544    }
545
546    pub c_event_video {
547        width: "100%";
548        max-width: "100%";
549    }
550
551    pub c_event_image_area {
552        padding: format!("{} 0px", var!(space-lg));
553        color: "inherit";
554        width: "100%";
555        overflow: "hidden";
556        display: "flex";
557        flex-direction: "column";
558        align-items: "center";
559        gap: var!(space-md);
560    }
561
562    pub c_event_image {
563        width: "200px";
564        max-width: "100%";
565        object-fit: "contain";
566        display: "block";
567    }
568
569    pub c_event_url_text {
570        text-align: "center";
571        font-size: var!(font-sm);
572        color: "inherit";
573        opacity: "1";
574        font-family: var!(font-family-mono);
575        word-break: "break-all";
576        width: "100%";
577    }
578
579    // ═══════════════════════════════════════════════════════════════════════════
580    // Timer Demo
581    // ═══════════════════════════════════════════════════════════════════════════
582
583    pub c_timer_display {
584        text-align: "center";
585        padding: var!(space-xl);
586        margin: format!("{} 0px", var!(gap-component));
587        color: "inherit";
588    }
589
590    pub c_timer_value {
591        font-size: var!(font-base);
592        font-weight: var!(font-weight-bold);
593        color: var!(primary);
594        letter-spacing: var!(letter-spacing-wide);
595        font-family: var!(font-family-mono);
596    }
597
598    pub c_timer_controls {
599        display: "flex";
600        flex-wrap: "wrap";
601        gap: var!(gap-element);
602    }
603
604    pub c_timer_done {
605        text-align: "center";
606        margin-top: var!(gap-component);
607        font-size: var!(font-lg);
608        font-weight: var!(font-weight-semibold);
609        color: var!(foreground);
610    }
611
612    // ═══════════════════════════════════════════════════════════════════════════
613    // Browser API Demo
614    // ═══════════════════════════════════════════════════════════════════════════
615
616    pub c_browser_api_row {
617        display: "grid";
618        grid-template-columns: "minmax(0px, 1fr) minmax(0px, 1fr)";
619        gap: var!(gap-component);
620        margin-bottom: var!(gap-component);
621        @media ((max-width: 767px)) {
622            grid-template-columns: "1fr";
623        }
624    }
625
626    pub c_browser_api_actions {
627        display: "flex";
628        flex-wrap: "wrap";
629        gap: var!(gap-element);
630        margin: format!("{} 0px", var!(gap-component));
631    }
632
633    pub c_browser_result_box {
634        margin-top: var!(gap-component);
635        font-size: var!(font-base);
636        word-break: "break-all";
637        color: "inherit";
638    }
639
640    pub c_browser_result_label {
641        font-weight: var!(font-weight-semibold);
642        color: var!(primary);
643    }
644
645    pub c_browser_result_value {
646        color: "inherit";
647    }
648
649    pub c_browser_info_grid {
650        display: "grid";
651        gap: var!(gap-component);
652        margin-top: var!(gap-component);
653        @media ((max-width: 767px)) {
654            grid-template-columns: "1fr";
655        }
656    }
657
658    pub c_browser_info_item {
659        display: "flex";
660        flex-direction: "column";
661        gap: var!(space-xs);
662        color: "inherit";
663    }
664
665    pub c_browser_info_label {
666        font-size: var!(font-sm);
667        font-weight: var!(font-weight-semibold);
668        color: "inherit";
669        opacity: "1";
670        text-transform: var!(text-transform-uppercase);
671        letter-spacing: var!(letter-spacing-wide);
672    }
673
674    pub c_browser_info_value {
675        font-size: var!(font-base);
676        color: "inherit";
677        word-break: "break-all";
678        font-family: var!(font-family-mono);
679    }
680
681    // ═══════════════════════════════════════════════════════════════════════════
682    // Keep-Alive Demo
683    // ═══════════════════════════════════════════════════════════════════════════
684
685    pub c_keep_alive_tab_bar {
686        display: "flex";
687        border-bottom: format!("{} solid {}", var!(border-width), var!(border));
688        margin-bottom: var!(gap-component);
689        gap: var!(gap-element);
690    }
691
692    pub c_keep_alive_tab_panel {
693        padding: format!("{} 0px", var!(gap-element));
694    }
695
696    pub c_keep_alive_panel_title {
697        margin-top: "0px";
698        color: var!(primary);
699        font-size: var!(font-md);
700        margin-bottom: var!(gap-element);
701    }
702
703    pub c_keep_alive_demo_text {
704        color: "inherit";
705        font-size: var!(font-base);
706        margin-bottom: var!(gap-component);
707    }
708
709    pub c_keep_alive_counter_display {
710        display: "flex";
711        justify-content: "center";
712        align-items: "center";
713        margin: format!("{} 0px", var!(space-xl));
714    }
715
716    pub c_keep_alive_counter_value {
717        font-size: var!(font-base);
718        font-weight: var!(font-weight-bold);
719        font-variant-numeric: "tabular-nums";
720        color: var!(primary);
721        text-align: "center";
722    }
723
724    pub c_keep_alive_counter_controls {
725        display: "flex";
726        flex-wrap: "wrap";
727        gap: format!("{}", var!(space-md));
728    }
729
730    pub c_keep_alive_form_group {
731        margin-bottom: var!(gap-component);
732    }
733
734    pub c_keep_alive_form_preview {
735        margin-top: var!(gap-component);
736        background: var!(muted);
737    }
738
739    pub c_keep_alive_preview_label {
740        font-size: var!(font-base);
741        font-weight: var!(font-weight-semibold);
742        color: var!(primary);
743        margin: format!("0px 0px {} 0px", var!(space-xs));
744    }
745
746    // ═══════════════════════════════════════════════════════════════════════════
747    // Binding Demo
748    // ═══════════════════════════════════════════════════════════════════════════
749
750    pub c_binding_child_box {
751        display: "flex";
752        flex-direction: "column";
753        gap: var!(gap-component);
754        margin-top: var!(gap-component);
755    }
756
757    pub c_binding_child_label {
758        font-size: var!(font-base);
759        font-weight: var!(font-weight-semibold);
760        color: var!(primary);
761        text-transform: var!(text-transform-uppercase);
762        letter-spacing: var!(letter-spacing-wide);
763    }
764
765    pub c_binding_parent_box {
766        display: "flex";
767        flex-direction: "column";
768        gap: var!(gap-component);
769        margin-top: var!(gap-component);
770    }
771
772    pub c_binding_section_title {
773        margin-top: var!(gap-component);
774        margin-bottom: var!(gap-element);
775        color: var!(primary);
776        font-size: var!(font-md);
777        font-weight: var!(font-weight-semibold);
778    }
779
780    pub c_binding_temp_converter {
781        display: "flex";
782        align-items: "flex-end";
783        gap: var!(gap-component);
784        flex-wrap: "wrap";
785        margin-top: var!(gap-component);
786    }
787
788    pub c_binding_temp_field {
789        flex: "1";
790        min-width: "120px";
791    }
792
793    pub c_binding_temp_arrow {
794        font-size: var!(font-xl);
795        font-weight: var!(font-weight-bold);
796        color: var!(primary);
797        // The row is `align-items: flex-end`, so the glyph would sit on the
798        // bottom edge of the input. The padding lifts it by half the
799        // difference between the input box and the line box of the glyph —
800        // a number no spacing token holds, because it is derived from the
801        // two heights rather than chosen.
802        padding-bottom: "10px";
803    }
804
805    pub c_binding_color_mixer {
806        margin-top: var!(gap-component);
807    }
808
809    pub c_binding_color_preview {
810        width: "100%";
811        height: "80px";
812        display: "flex";
813        align-items: "center";
814        justify-content: "center";
815        margin-bottom: var!(gap-component);
816    }
817
818    pub c_binding_color_hex {
819        font-family: var!(font-family-mono);
820        font-size: var!(font-xl);
821        font-weight: var!(font-weight-bold);
822        color: var!(primary-foreground);
823        letter-spacing: var!(letter-spacing-normal);
824    }
825
826    pub c_binding_slider_row {
827        display: "flex";
828        align-items: "center";
829        gap: format!("{}", var!(space-md));
830        margin-bottom: var!(space-sm);
831    }
832
833    pub c_binding_slider_label {
834        font-size: var!(font-base);
835        font-weight: var!(font-weight-bold);
836        min-width: var!(font-sm);
837    }
838
839    pub c_binding_slider {
840        flex: "1";
841        height: "24px";
842        padding: "0px";
843        cursor: "pointer";
844        -webkit-appearance: "none";
845        appearance: "none";
846        border: "none";
847        outline: "none";
848        ::-webkit-slider-runnable-track {
849            height: "6px";
850            background: format!("linear-gradient(to right, {} 0%, {} var(--value,50%), {} var(--value,50%), {} 100%)", var!(foreground), var!(foreground), var!(border), var!(border));
851            border-radius: var!(radius-xs);
852            border: "none";
853        }
854        ::-webkit-slider-thumb {
855            width: "20px";
856            height: "20px";
857            background: var!(background);
858            border: format!("2px solid {}", var!(primary));
859            border-radius: var!(radius-full);
860            cursor: "pointer";
861            -webkit-appearance: "none";
862            // The thumb is taller than the track it rides on, so the margin
863            // pulls it up until it sits centred on the track.
864            margin-top: "-7px";
865        }
866        : active::-webkit-slider-thumb {
867            transform: "scale(0.92)";
868        }
869        ::-moz-range-track {
870            height: "6px";
871            background: format!("linear-gradient(to right, {} 0%, {} var(--value,50%), {} var(--value,50%), {} 100%)", var!(foreground), var!(foreground), var!(border), var!(border));
872            border-radius: var!(radius-xs);
873            border: "none";
874        }
875        ::-moz-range-thumb {
876            width: "20px";
877            height: "20px";
878            background: var!(background);
879            border: format!("2px solid {}", var!(primary));
880            border-radius: var!(radius-full);
881            cursor: "pointer";
882        }
883        : active::-moz-range-thumb {
884            transform: "scale(0.92)";
885        }
886    }
887
888    pub c_binding_slider_value {
889        font-size: var!(font-base);
890        font-weight: var!(font-weight-medium);
891        color: "inherit";
892        min-width: "32px";
893        text-align: "right";
894        font-family: var!(font-family-mono);
895    }
896
897    pub c_binding_typed_prop_value {
898        font-family: var!(font-family-mono);
899        font-size: var!(font-base);
900        font-weight: var!(font-weight-semibold);
901        color: var!(primary);
902        padding: format!("1px {}", var!(space-sm));
903    }
904
905    pub c_binding_typed_prop_group {
906        display: "flex";
907        align-items: "center";
908        gap: var!(space-sm);
909    }
910
911    pub c_binding_typed_warning {
912        font-size: var!(font-base);
913        font-weight: var!(font-weight-medium);
914        color: var!(warning-foreground);
915        background: var!(warning);
916        margin-bottom: "0px";
917        border-radius: var!(radius-sm);
918    }
919
920    pub c_binding_form_label {
921        display: "block";
922        color: "inherit";
923        font-weight: var!(font-weight-medium);
924        font-size: var!(font-base);
925    }
926
927    pub c_binding_demo_text {
928        color: "inherit";
929        margin: "0px"
930    }
931
932    pub c_binding_compact_button {
933        display: "inline-flex";
934        justify-content: "center";
935        align-items: "center";
936        height: "42px";
937        padding: format!("0px {}", var!(space-md));
938        background: var!(primary);
939        color: var!(primary-foreground);
940        border: format!("{} solid {}", var!(border-width), var!(primary));
941        cursor: "pointer";
942        font-size: var!(font-sm);
943        font-weight: var!(font-weight-semibold);
944        outline: "none";
945        white-space: "nowrap";
946        user-select: "none";
947        -webkit-user-select: "none";
948        box-sizing: "border-box";
949        :hover {
950            background: var!(primary);
951        }
952        :focus-visible {
953            outline: "none";
954        }
955        :active {
956            background: var!(primary);
957            color: var!(primary-foreground);
958            border-color: var!(primary);
959        }
960        border-radius: var!(radius-md);
961    }
962
963    // ═══════════════════════════════════════════════════════════════════════════
964    // Custom Attrs - Dynamic Style
965    // ═══════════════════════════════════════════════════════════════════════════
966
967    pub c_attrs_dynamic_demo(prop_key: &str, prop_value: &str) {
968        {
969            prop_key
970        }
971        : prop_value;
972        // No layout defaults: this class lives on a `<p>` whose own margin /
973        // spacing is controlled by its parent's typography rules, and pinning
974        // any colour here would shadow the user-supplied dynamic property.
975        // Users are expected to set `color` / `background` / `font-*` /
976        // etc. via the two CSS Property Key / Value inputs above; whatever
977        // they type becomes the literal CSS rule applied to the paragraph.
978    }
979
980    // ═══════════════════════════════════════════════════════════════════════════
981    // Dynamic Component Demo
982    // ═══════════════════════════════════════════════════════════════════════════
983
984    pub c_dynamic_component_tab_bar {
985        display: "flex";
986        flex-wrap: "wrap";
987        gap: var!(gap-element);
988        margin-bottom: var!(gap-component);
989    }
990
991    pub c_dynamic_component_panel {
992        display: "block";
993        min-height: var!(control-height-sm);
994        margin-top: var!(gap-component);
995    }
996
997    // ═══════════════════════════════════════════════════════════════════════════
998    // Sticky & CSS Effects Demo
999    // ═══════════════════════════════════════════════════════════════════════════
1000
1001    // ═══════════════════════════════════════════════════════════════════════════
1002    // Home Page — Section
1003    // ═══════════════════════════════════════════════════════════════════════════
1004
1005    pub c_home {
1006        position: "relative";
1007        text-align: "center";
1008        margin-bottom: var!(space-xl);
1009        flex: "1";
1010        display: "flex";
1011        flex-direction: "column";
1012        justify-content: "center";
1013        @media ((max-width: 767px)) {
1014            margin-bottom: var!(space-lg);
1015            flex: "0 0 auto";
1016            justify-content: "flex-start";
1017        }
1018    }
1019
1020    pub c_home_content {
1021        position: "relative";
1022        z-index: "1";
1023    }
1024
1025    pub c_home_badge_row {
1026        display: "inline-flex";
1027        align-items: "center";
1028        // A base rule resets this to `flex`, so the row spans the full column
1029        // and `text-align: center` on the parent no longer centres it — a flex
1030        // child is placed by its own alignment. Without this the badges sit at
1031        // the left edge while the centred title below them does not.
1032        justify-content: "center";
1033        gap: var!(space-sm);
1034        margin-bottom: var!(space-md);
1035    }
1036
1037    pub c_home_badge {
1038        // The wrapping-atom recipe (§7), like the tag and the badge.
1039        display: "inline-block";
1040        vertical-align: "middle";
1041        text-align: "center";
1042        line-height: "1";
1043        box-sizing: "border-box";
1044        padding: format!("{} {}", var!(space-xs), var!(space-sm));
1045        // Text on a coloured surface takes that surface's own foreground role:
1046        // `primary` is a brand colour, not a readable one, and several schemes
1047        // (linear, stripe) sit below 4.5:1 against `accent`.
1048        color: var!(accent-foreground);
1049        background: var!(accent);
1050        font-size: var!(font-xs);
1051        font-weight: var!(font-weight-semibold);
1052        letter-spacing: var!(letter-spacing-wide);
1053        // The border keeps the brand colour — it is a 1px line, so 3:1 applies.
1054        border: format!("{} solid {}", var!(border-width), var!(primary));
1055    }
1056
1057    pub c_home_title {
1058        font-size: var!(font-5xl);
1059        font-weight: var!(font-weight-bold);
1060        letter-spacing: var!(letter-spacing-tight);
1061        margin: "0px";
1062        color: var!(foreground);
1063        margin-bottom: var!(space-xs);
1064        @media ((max-width: 767px)) {
1065            font-size: var!(font-4xl);
1066        }
1067    }
1068
1069    pub c_home_subtitle {
1070        font-size: var!(font-lg);
1071        color: var!(foreground);
1072        margin: "0px auto";
1073        max-width: "520px";
1074        margin-bottom: var!(space-lg);
1075        @media ((max-width: 767px)) {
1076            font-size: var!(font-base);
1077        }
1078    }
1079
1080    pub c_home_actions {
1081        display: "flex";
1082        gap: var!(space-md);
1083        justify-content: "center";
1084        flex-wrap: "wrap";
1085        // Below the breakpoint the actions become equal-width rows: the two
1086        // labels differ in length, so a content-sized wrap leaves the shorter
1087        // button visibly narrower and the row reads as misaligned.
1088        @media ((max-width: 767px)) {
1089            flex-wrap: "nowrap";
1090            align-items: "stretch";
1091        }
1092    }
1093
1094    pub c_home_btn_primary {
1095        display: "inline-flex";
1096        justify-content: "center";
1097        align-items: "center";
1098        gap: var!(space-sm);
1099        // A button is a nowrap element (§7).
1100        white-space: "nowrap";
1101        padding: format!("{} {}", var!(space-sm), var!(space-2xl));
1102        background: var!(primary);
1103        color: var!(primary-foreground);
1104        font-size: var!(font-base);
1105        font-weight: var!(font-weight-semibold);
1106        text-decoration: "none";
1107        border: "1.5px solid transparent";
1108        cursor: "pointer";
1109        letter-spacing: var!(letter-spacing-normal);
1110        vertical-align: "middle";
1111        min-height: var!(control-height-sm);
1112        // The home page's own buttons answer exactly the way the library
1113        // buttons do: a lift on hover, a settle on press, both driven by
1114        // the shared motion tokens rather than their own numbers.
1115        transition: format!(
1116            "background-color {} {}, border-color {} {}, transform {} {}",
1117            var!(duration-fast),
1118            var!(ease-out),
1119            var!(duration-fast),
1120            var!(ease-out),
1121            var!(duration-fast),
1122            var!(ease-out)
1123        );
1124        @media ((max-width: 767px)) {
1125            flex: "1 1 0px";
1126            min-width: "0px";
1127        }
1128        :hover {
1129            background: var!(primary-hover);
1130            color: var!(primary-foreground);
1131            border-color: "1.5px solid transparent";
1132            transform: format!("translateY({})", var!(lift-hover));
1133        }
1134        :focus-visible {
1135            outline: "none";
1136        }
1137        :active {
1138            background: var!(primary);
1139            color: var!(primary-foreground);
1140            border-color: "1.5px solid transparent";
1141            // The press drops the lift back down and settles into the
1142            // surface in one movement.
1143            transform: format!("translateY(0px) scale({})", var!(press-scale));
1144        }
1145        border-radius: var!(radius-sm);
1146    }
1147
1148    pub c_home_btn_secondary {
1149        display: "inline-flex";
1150        justify-content: "center";
1151        align-items: "center";
1152        gap: var!(space-sm);
1153        // A button is a nowrap element (§7).
1154        white-space: "nowrap";
1155        padding: format!("{} {}", var!(space-sm), var!(space-2xl));
1156        color: var!(primary);
1157        font-size: var!(font-base);
1158        font-weight: var!(font-weight-semibold);
1159        text-decoration: "none";
1160        border: format!("1.5px solid {}", var!(primary));
1161        cursor: "pointer";
1162        letter-spacing: var!(letter-spacing-normal);
1163        vertical-align: "middle";
1164        min-height: var!(control-height-sm);
1165        // Same hover/press vocabulary as the primary home button and the
1166        // library buttons, so the three of them feel like one system.
1167        transition: format!(
1168            "background-color {} {}, border-color {} {}, transform {} {}",
1169            var!(duration-fast),
1170            var!(ease-out),
1171            var!(duration-fast),
1172            var!(ease-out),
1173            var!(duration-fast),
1174            var!(ease-out)
1175        );
1176        @media ((max-width: 767px)) {
1177            flex: "1 1 0px";
1178            min-width: "0px";
1179        }
1180        :hover {
1181            background: var!(secondary);
1182            color: var!(secondary-foreground);
1183            border-color: var!(input);
1184            transform: format!("translateY({})", var!(lift-hover));
1185        }
1186        :focus-visible {
1187            outline: "none";
1188        }
1189        :active {
1190            background: "transparent";
1191            color: var!(primary);
1192            border-color: var!(primary);
1193            transform: format!("translateY(0px) scale({})", var!(press-scale));
1194        }
1195        border-radius: var!(radius-sm);
1196    }
1197
1198    // ═══════════════════════════════════════════════════════════════════════════
1199    // Home Page — Stats Row
1200    // ═══════════════════════════════════════════════════════════════════════════
1201
1202    pub c_home_stats {
1203        display: "grid";
1204        grid-template-columns: "repeat(4, minmax(0px, 1fr))";
1205        gap: var!(space-md);
1206        margin-bottom: var!(space-xl);
1207        @media ((max-width: 767px)) {
1208            grid-template-columns: "repeat(2, 1fr)";
1209            gap: var!(space-sm);
1210        }
1211    }
1212
1213    pub c_home_stat_card {
1214        display: "flex";
1215        flex-direction: "column";
1216        align-items: "center";
1217        justify-content: "center";
1218        gap: var!(space-xs);
1219        border-radius: var!(radius-xl);
1220    }
1221
1222    pub c_home_stat_icon {
1223        font-size: var!(font-2xl);
1224    }
1225
1226    pub c_home_stat_value {
1227        font-size: var!(font-xl);
1228        font-weight: var!(font-weight-bold);
1229        color: var!(foreground);
1230        letter-spacing: var!(letter-spacing-normal);
1231    }
1232
1233    pub c_home_stat_label {
1234        font-size: var!(font-sm);
1235        color: var!(muted-foreground);
1236        font-weight: var!(font-weight-medium);
1237    }
1238
1239    // ═══════════════════════════════════════════════════════════════════════════
1240    // Home Page — Section & Feature Cards
1241    // ═══════════════════════════════════════════════════════════════════════════
1242
1243    pub c_home_section_title {
1244        font-size: var!(font-2xl);
1245        font-weight: var!(font-weight-bold);
1246        color: var!(foreground);
1247        margin: "0px";
1248        margin: format!("{} 0px", var!(gap-component));
1249        letter-spacing: var!(letter-spacing-tight);
1250    }
1251
1252    pub c_home_section_desc {
1253        font-size: var!(font-base);
1254        color: var!(foreground);
1255        margin: "0px";
1256        margin-bottom: var!(space-2xl);
1257    }
1258
1259    pub c_home_feature_grid {
1260        display: "grid";
1261        grid-template-columns: "repeat(2, minmax(0px, 1fr))";
1262        // The cards carry the block gap between them, so the grid only has
1263        // to separate the columns: a row gap on top of that would count the
1264        // same space twice and the rows would drift apart.
1265        column-gap: var!(space-md);
1266        row-gap: "0px";
1267        @media ((max-width: 767px)) {
1268            grid-template-columns: "1fr";
1269        }
1270    }
1271
1272    pub c_feature_card {
1273        display: "flex";
1274        flex-direction: "column";
1275        gap: var!(space-sm);
1276        padding: "0px";
1277        overflow: "hidden";
1278        border-radius: var!(radius-xl);
1279    }
1280
1281    pub c_feature_header {
1282        display: "flex";
1283        align-items: "center";
1284        gap: var!(space-sm);
1285    }
1286
1287    pub c_feature_icon {
1288        font-size: var!(font-2xl);
1289        display: "flex";
1290        align-items: "center";
1291        justify-content: "center";
1292        line-height: "1";
1293        flex-shrink: "0";
1294    }
1295
1296    pub c_feature_name {
1297        font-size: var!(font-lg);
1298        font-weight: var!(font-weight-semibold);
1299        color: var!(foreground);
1300        margin: "0px";
1301        overflow: "hidden";
1302        text-overflow: "ellipsis";
1303        white-space: "nowrap";
1304    }
1305
1306    pub c_feature_desc {
1307        font-size: var!(font-sm);
1308        color: var!(foreground);
1309        margin: "0px";
1310    }
1311
1312    pub c_info_row {
1313        display: "flex";
1314        align-items: "center";
1315        gap: var!(space-md);
1316        padding: format!("{} 0", var!(space-sm));
1317        overflow: "hidden";
1318    }
1319
1320    pub c_info_label {
1321        font-size: var!(font-sm);
1322        font-weight: var!(font-weight-medium);
1323        color: var!(muted-foreground);
1324        min-width: "72px";
1325        flex-shrink: "0";
1326        letter-spacing: var!(letter-spacing-normal);
1327    }
1328
1329    pub c_info_value {
1330        c_text_ellipsis();
1331        font-size: var!(font-base);
1332        font-weight: var!(font-weight-semibold);
1333        color: var!(foreground);
1334        font-family: var!(font-family-mono);
1335        flex: "1";
1336    }
1337
1338    pub c_info_link {
1339        c_text_ellipsis();
1340        font-size: var!(font-base);
1341        font-weight: var!(font-weight-semibold);
1342        color: var!(primary);
1343        font-family: var!(font-family-mono);
1344        flex: "1";
1345        text-decoration: "none";
1346        transition: format!("opacity {} {}", var!(duration-fast), var!(ease-out));
1347        :hover {
1348            opacity: "1";
1349        }
1350    }
1351
1352    pub c_spinner {
1353        width: "40px";
1354        height: "40px";
1355        border: format!("4px solid {}", var!(muted));
1356        border-top: format!("4px solid {}", var!(primary));
1357        border-right: format!("4px solid {}", var!(primary));
1358        border-bottom: format!("4px solid {}", var!(primary));
1359        border-radius: var!(radius-full);
1360        flex-shrink: "0";
1361        animation: "euv-spin 0.8s linear infinite";
1362    }
1363
1364    pub c_loading_overlay(background: &str) {
1365        position: "absolute";
1366        top: "0";
1367        left: "0";
1368        width: "100%";
1369        height: "100%";
1370        display: "flex";
1371        flex-direction: "column";
1372        align-items: "center";
1373        justify-content: "center";
1374        gap: var!(gap-component);
1375        background: background;
1376        z-index: "5";
1377        box-sizing: "border-box";
1378    }
1379
1380    pub c_error_container {
1381        display: "flex";
1382        align-items: "center";
1383        gap: var!(gap-element);
1384        margin-top: var!(gap-component);
1385        box-sizing: "border-box";
1386        border-radius: var!(radius-lg);
1387    }
1388
1389    pub c_error_icon {
1390        width: "20px";
1391        height: "20px";
1392        display: "flex";
1393        align-items: "center";
1394        justify-content: "center";
1395        flex-shrink: "0";
1396        color: var!(destructive);
1397        font-size: var!(font-sm);
1398        font-weight: var!(font-weight-bold);
1399    }
1400
1401    pub c_error_text {
1402        color: var!(destructive);
1403        font-size: var!(font-base);
1404    }
1405
1406    pub c_data_box {
1407        margin: format!("{} 0px", var!(gap-component));
1408        box-sizing: "border-box";
1409        border-radius: var!(radius-sm);
1410    }
1411
1412    pub c_data_pre {
1413        color: var!(foreground);
1414        font-size: var!(font-base);
1415        margin: "0px";
1416        white-space: "pre-wrap";
1417        word-break: "break-all";
1418        font-family: var!(font-family-mono);
1419    }
1420
1421    pub c_fetch_hint {
1422        color: "inherit";
1423        opacity: "1";
1424        font-size: var!(font-base);
1425        margin-bottom: var!(gap-component);
1426    }
1427
1428    // ═══════════════════════════════════════════════════════════════════════════
1429    // Log / Console Display
1430    // ═══════════════════════════════════════════════════════════════════════════
1431
1432    pub c_log_container {
1433        font-family: var!(font-family-mono);
1434        font-size: var!(font-base);
1435        margin-top: "0px";
1436        color: var!(foreground);
1437    }
1438
1439    pub c_log_item {
1440        padding: format!("{} 0px", var!(space-sm));
1441        font-size: var!(font-base);
1442        color: var!(foreground);
1443        border-radius: var!(radius-sm);
1444    }
1445
1446    // ═══════════════════════════════════════════════════════════════════════════
1447    // Page Banner (unified header with emoji icon)
1448    // ═══════════════════════════════════════════════════════════════════════════
1449
1450    pub(crate) c_page {
1451        position: "relative";
1452        text-align: "center";
1453        box-sizing: "border-box";
1454    }
1455
1456    pub(crate) c_page_glow {
1457        position: "absolute";
1458        top: "-50%";
1459        left: "50%";
1460        transform: "translateX(-50%)";
1461        width: "400px";
1462        max-width: "100%";
1463        height: "400px";
1464        pointer-events: "none";
1465    }
1466
1467    pub(crate) c_page_content {
1468        position: "relative";
1469        z-index: "1";
1470    }
1471
1472    pub(crate) c_page_icon {
1473        font-size: "36px";
1474        padding-bottom: var!(space-md);
1475        @media ((max-width: 767px)) {
1476            font-size: "40px";
1477        }
1478    }
1479
1480    pub(crate) c_page_title {
1481        font-size: var!(font-4xl);
1482        font-weight: var!(font-weight-bold);
1483        letter-spacing: var!(letter-spacing-tight);
1484        margin: "0px";
1485        color: var!(foreground);
1486        margin-bottom: var!(space-sm);
1487        @media ((max-width: 767px)) {
1488            font-size: var!(font-3xl);
1489        }
1490    }
1491
1492    pub(crate) c_page_subtitle {
1493        font-size: var!(font-lg);
1494        color: var!(muted-foreground);
1495        margin: "0px auto";
1496        margin-bottom: var!(space-sm);
1497        max-width: "560px";
1498        opacity: "1";
1499        @media ((max-width: 767px)) {
1500            font-size: var!(font-base);
1501        }
1502    }
1503
1504    // ═══════════════════════════════════════════════════════════════════════════
1505    // Home Page
1506    // ═══════════════════════════════════════════════════════════════════════════
1507
1508    pub(crate) c_text_ellipsis {
1509        overflow: "hidden";
1510        text-overflow: "ellipsis";
1511        white-space: "nowrap";
1512    }
1513
1514    // ═══════════════════════════════════════════════════════════════════════════
1515    // Async / Loading States
1516    // ═══════════════════════════════════════════════════════════════════════════
1517
1518    pub(crate) c_loading_container {
1519        display: "flex";
1520        align-items: "center";
1521        justify-content: "center";
1522        gap: var!(gap-component);
1523        margin: format!("{} 0px", var!(gap-component));
1524        box-sizing: "border-box";
1525    }
1526
1527    pub(crate) c_loading_text_col {
1528        display: "flex";
1529        flex-direction: "column";
1530        gap: var!(space-xs);
1531    }
1532
1533    pub(crate) c_loading_title {
1534        color: var!(foreground);
1535        font-size: var!(font-md);
1536        font-weight: var!(font-weight-medium);
1537    }
1538
1539    pub(crate) c_loading_subtitle {
1540        color: var!(muted-foreground);
1541        font-size: var!(font-base);
1542    }
1543}
1544
1545#[cfg(feature = "theme-custom")]
1546class! {
1547    // ═══════════════════════════════════════════════════════════════════════════
1548    // Camera Page
1549    // ═══════════════════════════════════════════════════════════════════════════
1550
1551    pub c_camera_video_container {}
1552
1553    pub c_camera_video_active {}
1554
1555    pub c_camera_video_hidden {}
1556
1557    pub c_camera_video_placeholder {}
1558
1559    pub c_camera_placeholder_content {}
1560
1561    pub c_camera_placeholder_icon {}
1562
1563    pub c_camera_placeholder_text {}
1564
1565    pub c_camera_error_box {}
1566
1567    pub c_camera_scan_result_box {}
1568
1569    pub c_camera_scan_result_label {}
1570
1571    pub c_camera_scan_result_value {}
1572
1573    // ═══════════════════════════════════════════════════════════════════════════
1574    // Canvas Drawing Board
1575    // ═══════════════════════════════════════════════════════════════════════════
1576
1577    pub c_canvas_preview_container {}
1578
1579    pub c_canvas_placeholder {}
1580
1581    pub c_canvas_preview_image {}
1582
1583    pub c_canvas_container_fullscreen {}
1584
1585    pub c_canvas_drawing_fullscreen_wrapper {}
1586
1587    pub c_canvas_drawing_fullscreen {}
1588
1589    pub c_canvas_fullscreen_toolbar {}
1590
1591    pub c_canvas_fullscreen_toolbar_row_top {}
1592
1593    pub c_canvas_fullscreen_toolbar_color_wrapper {}
1594
1595    pub c_canvas_color_input_fullscreen {}
1596
1597    pub c_canvas_fullscreen_toolbar_row_bottom {}
1598
1599    pub c_canvas_fullscreen_range_input {}
1600
1601    pub c_canvas_fullscreen_toolbar_button {}
1602
1603    // ═══════════════════════════════════════════════════════════════════════════
1604    // Game Fullscreen (2D / 3D)
1605    // ═══════════════════════════════════════════════════════════════════════════
1606
1607    // Fullscreen overlay container for the 2D and 3D game pages.
1608    //
1609    // Mirrors `c_canvas_container_fullscreen` so the safe-area caching
1610    // pass in `UseEuvLayout::apply_cached_insets` can pick this selector
1611    // up alongside the canvas one. Differs in that the game overlay holds
1612    // the *same* `<canvas>` element that the inline page mounts (no
1613    // canvas re-creation), so the running game loop, balls, cubes, FPS
1614    // counter and other reactive state survive the fullscreen toggle
1615    // without reset.
1616    pub c_game_container_fullscreen {}
1617
1618    // Toolbar wrapping the fullscreen exit button on game pages.
1619    //
1620    // Game pages only need an `Exit` button here (no color picker / line
1621    // width control like the canvas drawing board), so the layout is a
1622    // single horizontal row at the bottom of the overlay.
1623    pub c_game_fullscreen_toolbar {}
1624
1625    // Wrapper that fills the fullscreen game container with the canvas.
1626    //
1627    // The game's `<canvas>` backing buffer is 600x400 (3:2). The canvas
1628    // itself has `width: 100%; height: 100%` so it fills this wrapper
1629    // exactly, and `object-fit: contain` on the canvas class makes the
1630    // browser uniformly scale the backing buffer to fit while preserving
1631    // its 3:2 aspect ratio. Without `object-fit: contain`, the canvas
1632    // would be stretched to whatever shape the wrapper has, making balls
1633    // render as horizontally-ovoid ellipses (e.g. on 1280x800 viewports
1634    // → 1.6:1, slightly squashed vertically). The letterbox bars appear
1635    // inside the canvas element rather than around an outer wrapper, so
1636    // the canvas always fills the fullscreen container with the backing
1637    // buffer uniformly scaled to its native aspect ratio.
1638    pub c_game_fullscreen_canvas_letterbox {}
1639
1640    // ═══════════════════════════════════════════════════════════════════════════
1641    // Custom Attrs Demo
1642    // ═══════════════════════════════════════════════════════════════════════════
1643
1644    pub c_custom_attrs_demo {}
1645
1646    pub c_demo_text {}
1647
1648    pub c_demo_text_muted {}
1649
1650    // ═══════════════════════════════════════════════════════════════════════════
1651    // Conditional Rendering Demo
1652    // ═══════════════════════════════════════════════════════════════════════════
1653
1654    pub c_toggle_content {}
1655
1656    pub c_toggle_title {}
1657
1658    pub c_role_button_row {}
1659
1660    pub c_role_guest_text {}
1661
1662    pub c_role_user_text {}
1663
1664    pub c_role_admin_text {}
1665
1666    // ═══════════════════════════════════════════════════════════════════════════
1667    // 404 Not Found
1668    // ═══════════════════════════════════════════════════════════════════════════
1669
1670    pub c_render_count_text {}
1671
1672    // ═══════════════════════════════════════════════════════════════════════════
1673    // Event Demo
1674    // ═══════════════════════════════════════════════════════════════════════════
1675
1676    pub c_event_result {}
1677
1678    pub c_event_highlight {}
1679
1680    pub c_event_info_grid {}
1681
1682    pub c_event_info_row {}
1683
1684    pub c_event_info_label {}
1685
1686    pub c_event_info_value {}
1687
1688    pub c_event_mouse_area {}
1689
1690    pub c_event_drag_zone {}
1691
1692    pub c_event_drag_zone_active {}
1693
1694    pub c_event_drag_item {}
1695
1696    pub c_event_drop_zone {}
1697
1698    pub c_event_drop_zone_active {}
1699
1700    pub c_event_drop_icon {}
1701
1702    pub c_event_drop_text {}
1703
1704    pub c_event_drop_hint {}
1705
1706    pub c_event_wheel_zone {}
1707
1708    pub c_event_clipboard_area {}
1709
1710    pub c_event_touch_zone {}
1711
1712    pub c_event_form_area {}
1713
1714    pub c_event_media_area {}
1715
1716    pub c_event_audio {}
1717
1718    pub c_event_video_area {}
1719
1720    pub c_event_video {}
1721
1722    pub c_event_image_area {}
1723
1724    pub c_event_image {}
1725
1726    pub c_event_url_text {}
1727
1728    // ═══════════════════════════════════════════════════════════════════════════
1729    // Timer Demo
1730    // ═══════════════════════════════════════════════════════════════════════════
1731
1732    pub c_timer_display {}
1733
1734    pub c_timer_value {}
1735
1736    pub c_timer_controls {}
1737
1738    pub c_timer_done {}
1739
1740    // ═══════════════════════════════════════════════════════════════════════════
1741    // Browser API Demo
1742    // ═══════════════════════════════════════════════════════════════════════════
1743
1744    pub c_browser_api_row {}
1745
1746    pub c_browser_api_actions {}
1747
1748    pub c_browser_result_box {}
1749
1750    pub c_browser_result_label {}
1751
1752    pub c_browser_result_value {}
1753
1754    pub c_browser_info_grid {}
1755
1756    pub c_browser_info_item {}
1757
1758    pub c_browser_info_label {}
1759
1760    pub c_browser_info_value {}
1761
1762    // ═══════════════════════════════════════════════════════════════════════════
1763    // Keep-Alive Demo
1764    // ═══════════════════════════════════════════════════════════════════════════
1765
1766    pub c_keep_alive_tab_bar {}
1767
1768    pub c_keep_alive_tab_panel {}
1769
1770    pub c_keep_alive_panel_title {}
1771
1772    pub c_keep_alive_demo_text {}
1773
1774    pub c_keep_alive_counter_display {}
1775
1776    pub c_keep_alive_counter_value {}
1777
1778    pub c_keep_alive_counter_controls {}
1779
1780    pub c_keep_alive_form_group {}
1781
1782    pub c_keep_alive_form_preview {}
1783
1784    pub c_keep_alive_preview_label {}
1785
1786    // ═══════════════════════════════════════════════════════════════════════════
1787    // Binding Demo
1788    // ═══════════════════════════════════════════════════════════════════════════
1789
1790    pub c_binding_child_box {}
1791
1792    pub c_binding_child_label {}
1793
1794    pub c_binding_parent_box {}
1795
1796    pub c_binding_section_title {}
1797
1798    pub c_binding_temp_converter {}
1799
1800    pub c_binding_temp_field {}
1801
1802    pub c_binding_temp_arrow {}
1803
1804    pub c_binding_color_mixer {}
1805
1806    pub c_binding_color_preview {}
1807
1808    pub c_binding_color_hex {}
1809
1810    pub c_binding_slider_row {}
1811
1812    pub c_binding_slider_label {}
1813
1814    pub c_binding_slider {}
1815
1816    pub c_binding_slider_value {}
1817
1818    pub c_binding_typed_prop_value {}
1819
1820    pub c_binding_typed_prop_group {}
1821
1822    pub c_binding_typed_warning {}
1823
1824    pub c_binding_form_label {}
1825
1826    pub c_binding_demo_text {}
1827
1828    pub c_binding_compact_button {}
1829
1830    // ═══════════════════════════════════════════════════════════════════════════
1831    // Custom Attrs - Dynamic Style
1832    // ═══════════════════════════════════════════════════════════════════════════
1833
1834    pub c_attrs_dynamic_demo(prop_key: &str, prop_value: &str) {}
1835
1836    // ═══════════════════════════════════════════════════════════════════════════
1837    // Dynamic Component Demo
1838    // ═══════════════════════════════════════════════════════════════════════════
1839
1840    pub c_dynamic_component_tab_bar {}
1841
1842    pub c_dynamic_component_panel {}
1843
1844    // ═══════════════════════════════════════════════════════════════════════════
1845    // Sticky & CSS Effects Demo
1846    // ═══════════════════════════════════════════════════════════════════════════
1847
1848    // ═══════════════════════════════════════════════════════════════════════════
1849    // Home Page — Section
1850    // ═══════════════════════════════════════════════════════════════════════════
1851
1852    pub c_home {}
1853
1854    pub c_home_content {}
1855
1856    pub c_home_badge_row {}
1857
1858    pub c_home_badge {}
1859
1860    pub c_home_title {}
1861
1862    pub c_home_subtitle {}
1863
1864    pub c_home_actions {}
1865
1866    pub c_home_btn_primary {}
1867
1868    pub c_home_btn_secondary {}
1869
1870    // ═══════════════════════════════════════════════════════════════════════════
1871    // Home Page — Stats Row
1872    // ═══════════════════════════════════════════════════════════════════════════
1873
1874    pub c_home_stats {}
1875
1876    pub c_home_stat_card {}
1877
1878    pub c_home_stat_icon {}
1879
1880    pub c_home_stat_value {}
1881
1882    pub c_home_stat_label {}
1883
1884    // ═══════════════════════════════════════════════════════════════════════════
1885    // Home Page — Section & Feature Cards
1886    // ═══════════════════════════════════════════════════════════════════════════
1887
1888    pub c_home_section_title {}
1889
1890    pub c_home_section_desc {}
1891
1892    pub c_home_feature_grid {}
1893
1894    pub c_feature_card {}
1895
1896    pub c_feature_header {}
1897
1898    pub c_feature_icon {}
1899
1900    pub c_feature_name {}
1901
1902    pub c_feature_desc {}
1903
1904    pub c_info_row {}
1905
1906    pub c_info_label {}
1907
1908    pub c_info_value {}
1909
1910    pub c_info_link {}
1911
1912    pub c_spinner {}
1913
1914    pub c_loading_overlay(background: &str) {}
1915
1916    pub c_error_container {}
1917
1918    pub c_error_icon {}
1919
1920    pub c_error_text {}
1921
1922    pub c_data_box {}
1923
1924    pub c_data_pre {}
1925
1926    pub c_fetch_hint {}
1927
1928    // ═══════════════════════════════════════════════════════════════════════════
1929    // Log / Console Display
1930    // ═══════════════════════════════════════════════════════════════════════════
1931
1932    pub c_log_container {}
1933
1934    pub c_log_item {}
1935
1936    // ═══════════════════════════════════════════════════════════════════════════
1937    // Page Banner (unified header with emoji icon)
1938    // ═══════════════════════════════════════════════════════════════════════════
1939
1940    pub(crate) c_page {}
1941
1942    pub(crate) c_page_glow {}
1943
1944    pub(crate) c_page_content {}
1945
1946    pub(crate) c_page_icon {}
1947
1948    pub(crate) c_page_title {}
1949
1950    pub(crate) c_page_subtitle {}
1951
1952    // ═══════════════════════════════════════════════════════════════════════════
1953    // Home Page
1954    // ═══════════════════════════════════════════════════════════════════════════
1955
1956    pub(crate) c_text_ellipsis {}
1957
1958    // ═══════════════════════════════════════════════════════════════════════════
1959    // Async / Loading States
1960    // ═══════════════════════════════════════════════════════════════════════════
1961
1962    pub(crate) c_loading_container {}
1963
1964    pub(crate) c_loading_text_col {}
1965
1966    pub(crate) c_loading_title {}
1967
1968    pub(crate) c_loading_subtitle {}
1969}