Skip to main content

euv_ui/style/class/display/
fn.rs

1use super::*;
2
3class! {
4    pub(crate) c_euv_tabs {
5        display: "flex";
6        flex-direction: "column";
7        width: "100%";
8    }
9
10    pub(crate) c_euv_tabs_bar {
11        display: "flex";
12        border-bottom: format!("1px dashed {}", var!(border));
13        margin-bottom: var!(gap-component);
14        gap: var!(gap-element);
15        @media ((max-width: 767px)) {
16            flex-wrap: "wrap";
17        }
18    }
19
20    pub(crate) c_euv_tab_item {
21        padding: format!("{} {}", var!(space-md), var!(space-xl));
22        cursor: "pointer";
23        font-size: var!(font-base);
24        font-weight: "500";
25        border-bottom: "2px solid transparent";
26        color: "inherit";
27        background: "transparent";
28        touch-action: "manipulation";
29        user-select: "none";
30        -webkit-user-select: "none";
31        white-space: "nowrap";
32        :hover {
33            background: var!(accent-muted);
34            color: var!(accent);
35        }
36    }
37
38    pub(crate) c_euv_tab_item_active {
39        padding: format!("{} {}", var!(space-md), var!(space-xl));
40        cursor: "pointer";
41        font-size: var!(font-base);
42        font-weight: "500";
43        border-bottom: format!("2px solid {}", var!(accent));
44        color: var!(text-on-accent);
45        background: var!(accent);
46        touch-action: "manipulation";
47        user-select: "none";
48        -webkit-user-select: "none";
49        white-space: "nowrap";
50    }
51
52    pub(crate) c_euv_tabs_panel {
53        padding: format!("{} 0px", var!(gap-element));
54    }
55
56    pub(crate) c_euv_collapse_item {
57        border-bottom: format!("1px dashed {}", var!(border));
58    }
59
60    pub(crate) c_euv_collapse_header {
61        display: "flex";
62        align-items: "center";
63        gap: var!(space-sm);
64        width: "100%";
65        min-height: var!(min-height-base);
66        padding: format!("{} 0px", var!(space-sm));
67        cursor: "pointer";
68        text-align: "left";
69        font-size: var!(font-base);
70        font-weight: "500";
71        color: var!(foreground);
72        background: "transparent";
73        border: "none";
74        outline: "none";
75        touch-action: "manipulation";
76        user-select: "none";
77        -webkit-user-select: "none";
78        :hover {
79            background: var!(accent-muted);
80            color: var!(accent);
81        }
82    }
83
84    pub(crate) c_euv_collapse_header_active {
85        display: "flex";
86        align-items: "center";
87        gap: var!(space-sm);
88        width: "100%";
89        min-height: var!(min-height-base);
90        padding: format!("{} 0px", var!(space-sm));
91        cursor: "pointer";
92        text-align: "left";
93        font-size: var!(font-base);
94        font-weight: "600";
95        color: var!(accent);
96        background: var!(accent-muted);
97        border: "none";
98        outline: "none";
99        touch-action: "manipulation";
100        user-select: "none";
101        -webkit-user-select: "none";
102    }
103
104    pub(crate) c_euv_collapse_body {
105        overflow: "hidden";
106        font-size: var!(font-base);
107        color: var!(foreground);
108        transition: format!("max-height {} {}, opacity {} {}", var!(duration-normal), var!(ease-out), var!(duration-normal), var!(ease-out));
109    }
110
111    pub(crate) c_euv_collapse_body_open {
112        overflow: "hidden";
113        max-height: "none";
114        opacity: "1";
115        padding-bottom: var!(space-md);
116        font-size: var!(font-base);
117        color: var!(foreground);
118        transition: format!("max-height {} {}, opacity {} {}", var!(duration-normal), var!(ease-out), var!(duration-normal), var!(ease-out));
119    }
120
121    pub(crate) c_euv_collapse_body_closed {
122        overflow: "hidden";
123        max-height: "0px";
124        opacity: "0";
125        font-size: var!(font-base);
126        color: var!(foreground);
127        transition: format!("max-height {} {}, opacity {} {}", var!(duration-normal), var!(ease-out), var!(duration-normal), var!(ease-out));
128    }
129
130    pub(crate) c_euv_breadcrumb {
131        display: "flex";
132        align-items: "center";
133        flex-wrap: "wrap";
134        gap: var!(space-xs);
135        font-size: var!(font-sm);
136    }
137
138    pub(crate) c_euv_breadcrumb_item {
139        font-size: var!(font-sm);
140        color: var!(muted-foreground);
141        cursor: "pointer";
142        text-decoration: "none";
143        touch-action: "manipulation";
144        user-select: "none";
145        -webkit-user-select: "none";
146        :hover {
147            background: var!(accent-muted);
148            color: var!(accent);
149        }
150    }
151
152    pub(crate) c_euv_breadcrumb_sep {
153        font-size: var!(font-sm);
154        color: var!(muted-foreground);
155        user-select: "none";
156        -webkit-user-select: "none";
157    }
158
159    pub(crate) c_euv_breadcrumb_current {
160        font-size: var!(font-sm);
161        font-weight: "600";
162        color: var!(foreground);
163        cursor: "default";
164    }
165
166    pub(crate) c_euv_divider {
167        display: "block";
168        width: "100%";
169    }
170
171    pub(crate) c_euv_divider_horizontal {
172        display: "block";
173        width: "100%";
174        height: "0px";
175        border: "none";
176        border-top: format!("1px solid {}", var!(border));
177        margin: format!("{} 0px", var!(space-lg));
178    }
179
180    pub(crate) c_euv_divider_vertical {
181        display: "block";
182        width: "0px";
183        height: "auto";
184        align-self: "center";
185        border: "none";
186        border-left: format!("1px solid {}", var!(border));
187        margin: format!("0px {}", var!(space-sm));
188    }
189
190    pub(crate) c_euv_divider_labeled {
191        display: "flex";
192        align-items: "center";
193        gap: var!(space-sm);
194        width: "100%";
195        margin: format!("{} 0px", var!(space-lg));
196    }
197
198    pub(crate) c_euv_divider_label_text {
199        font-size: var!(font-xs);
200        font-weight: "500";
201        color: var!(muted-foreground);
202        white-space: "nowrap";
203        user-select: "none";
204        -webkit-user-select: "none";
205    }
206
207    // ═══════════════════════════════════════════════════════════════════════════
208    // Card Component
209    // ═══════════════════════════════════════════════════════════════════════════
210
211    pub(crate) c_card {
212        color: var!(foreground);
213        box-sizing: "border-box";
214        // The card title carries a `gap-component` margin, and that margin
215        // collapses out of the card — the card has no padding and no border
216        // to stop it. The gap between two cards was therefore whatever the
217        // title's margin happened to be, not a value anybody chose. The
218        // block gap is the value the page template asks for, so it is named
219        // here instead of being left to a side effect.
220        margin-top: var!(page-block-gap);
221        @media ((max-width: 767px)) {
222            margin-top: var!(page-block-gap-mobile);
223        }
224    }
225
226    pub c_card_title {
227        margin: format!("{} 0px", var!(gap-component));
228        color: var!(foreground);
229        font-size: var!(font-lg);
230        font-weight: "600";
231        padding-bottom: var!(gap-component);
232        border-bottom: format!("1px dashed {}", var!(border));
233        letter-spacing: "-0.01em";
234        @media ((max-width: 767px)) {
235            font-size: var!(font-md);
236        }
237    }
238
239    // ═══════════════════════════════════════════════════════════════════════════
240    // Buttons
241    // ═══════════════════════════════════════════════════════════════════════════
242
243    pub c_primary_button {
244        display: "flex";
245        justify-content: "center";
246        align-items: "center";
247        gap: var!(space-sm);
248        width: "100%";
249        height: "42px";
250        background: var!(accent);
251        color: var!(text-on-accent);
252        border: "1px solid transparent";
253        padding: format!("0px {}", var!(space-2xl));
254        cursor: "pointer";
255        font-size: var!(font-base);
256        font-weight: "500";
257        letter-spacing: "0.02em";
258        text-align: "center";
259        outline: "none";
260        box-sizing: "border-box";
261        white-space: "nowrap";
262        overflow: "hidden";
263        text-overflow: "ellipsis";
264        user-select: "none";
265        -webkit-user-select: "none";
266        :focus-visible {
267            outline: "none";
268        }
269        :disabled {
270            background: var!(muted-foreground);
271            cursor: "not-allowed";
272            opacity: "1";
273        }
274        :hover {
275            background: var!(accent);
276        }
277        :active {
278            background: var!(accent);
279            color: var!(text-on-accent);
280            border-color: "transparent";
281        }
282        @media ((max-width: 767px)) {
283            max-width: "100%";
284            padding: format!("{} {}", var!(space-lg), var!(space-xl));
285            font-size: var!(font-md);
286        }
287    }
288
289    pub(crate) c_modal_close_button {
290        display: "flex";
291        justify-content: "center";
292        align-items: "center";
293        color: "inherit";
294        border: "none";
295        padding: format!("{} {}", var!(space-sm), var!(space-md));
296        cursor: "pointer";
297        font-size: "20px";
298        font-weight: "400";
299        vertical-align: "middle";
300        outline: "none";
301        opacity: "1";
302        flex-shrink: "0";
303        transition: format!("opacity {} {}", var!(duration-fast), var!(ease-out));
304        :hover {
305            opacity: "1";
306        }
307        :active {
308            opacity: "1";
309        }
310        :focus {
311            outline: "none";
312        }
313    }
314
315    // ═══════════════════════════════════════════════════════════════════════════
316    // Button Controls Container
317    // ═══════════════════════════════════════════════════════════════════════════
318
319    pub c_button_controls {
320        display: "flex";
321        flex-wrap: "wrap";
322        gap: var!(gap-element);
323        margin-top: var!(gap-component);
324        margin-bottom: var!(gap-component);
325    }
326
327    // ═══════════════════════════════════════════════════════════════════════════
328    // Euv Button Variants
329    // ═══════════════════════════════════════════════════════════════════════════
330
331    pub c_euv_button_primary_md {
332        display: "flex";
333        justify-content: "center";
334        align-items: "center";
335        gap: var!(space-sm);
336        flex: "1 1 120px";
337        height: "42px";
338        padding: format!("0px {}", var!(space-xl));
339        background: var!(accent);
340        color: var!(text-on-accent);
341        cursor: "pointer";
342        font-size: var!(font-base);
343        font-weight: "500";
344        outline: "none";
345        white-space: "nowrap";
346        user-select: "none";
347        -webkit-user-select: "none";
348        box-sizing: "border-box";
349        :disabled {
350            background: var!(muted-foreground);
351            cursor: "not-allowed";
352            opacity: "1";
353        }
354    }
355
356    pub(crate) c_euv_button_outline_md {
357        display: "flex";
358        justify-content: "center";
359        align-items: "center";
360        gap: var!(space-sm);
361        flex: "1 1 120px";
362        height: "42px";
363        padding: format!("0px {}", var!(space-xl));
364        color: var!(foreground);
365        border: format!("1px solid {}", var!(border));
366        cursor: "pointer";
367        font-size: var!(font-base);
368        font-weight: "500";
369        outline: "none";
370        white-space: "nowrap";
371        user-select: "none";
372        -webkit-user-select: "none";
373        box-sizing: "border-box";
374        :focus-visible {
375            outline: "none";
376        }
377        :disabled {
378            background: var!(muted-foreground);
379            cursor: "not-allowed";
380            opacity: "1";
381        }
382    }
383
384    // ═══════════════════════════════════════════════════════════════════════════
385    // Badges
386    // ═══════════════════════════════════════════════════════════════════════════
387
388    pub(crate) c_badge {
389        // The wrapping-atom recipe (§7): an inline flex box is atomic, so a
390        // wrapped label left-aligns while the border stays at the parent
391        // width — the right border reads as missing. The four `c_euv_tag_*`
392        // variants already use this recipe; a badge is the same kind of
393        // element and must not mix the two.
394        display: "inline-block";
395        vertical-align: "middle";
396        text-align: "center";
397        line-height: "1";
398        box-sizing: "border-box";
399        color: var!(text-on-accent);
400        padding: format!("{} {}", var!(space-2xs), var!(space-sm));
401        border: format!("1px solid {}", var!(accent));
402        font-size: var!(font-xs);
403        font-weight: "600";
404        cursor: "pointer";
405        background: var!(accent);
406        :focus-visible {
407            outline: "none";
408        }
409        @media ((max-width: 767px)) {
410            padding: format!("{} {}", var!(space-2xs), var!(space-xs));
411            font-size: var!(font-xs);
412        }
413    }
414
415    pub(crate) c_badge_outline {
416        display: "inline-block";
417        vertical-align: "middle";
418        text-align: "center";
419        line-height: "1";
420        box-sizing: "border-box";
421        color: var!(foreground);
422        padding: format!("{} {}", var!(space-2xs), var!(space-sm));
423        font-size: var!(font-xs);
424        font-weight: "600";
425        cursor: "pointer";
426        border: format!("1.5px solid {}", var!(border));
427        @media ((max-width: 767px)) {
428            padding: format!("{} {}", var!(space-2xs), var!(space-xs));
429            font-size: var!(font-xs);
430        }
431    }
432
433    pub(crate) c_euv_tag_solid_black {
434        display: "inline-block";
435        vertical-align: "middle";
436        text-align: "center";
437        line-height: "1";
438        color: var!(text-on-accent);
439        padding: format!("{} {}", var!(space-xs), var!(space-md));
440        font-size: var!(font-sm);
441        font-weight: "600";
442        cursor: "pointer";
443        background: var!(accent);
444        border: format!("1px solid {}", var!(accent));
445        box-sizing: "border-box";
446    }
447
448    pub(crate) c_euv_tag_solid_white {
449        display: "inline-block";
450        vertical-align: "middle";
451        text-align: "center";
452        line-height: "1";
453        color: var!(foreground);
454        padding: format!("{} {}", var!(space-2xs), var!(space-sm));
455        font-size: var!(font-xs);
456        font-weight: "600";
457        cursor: "pointer";
458        border: format!("1.5px solid {}", var!(accent));
459        box-sizing: "border-box";
460    }
461
462    // ═══════════════════════════════════════════════════════════════════════════
463    // Euv Tag Variants (semantic colour tags)
464    // ═══════════════════════════════════════════════════════════════════════════
465
466    pub(crate) c_euv_tag_outline_black {
467        display: "inline-block";
468        vertical-align: "middle";
469        text-align: "center";
470        line-height: "1";
471        color: var!(accent);
472        padding: format!("{} {}", var!(space-2xs), var!(space-sm));
473        font-size: var!(font-xs);
474        font-weight: "600";
475        cursor: "pointer";
476        border: format!("1.5px solid {}", var!(accent));
477        box-sizing: "border-box";
478    }
479
480    pub(crate) c_euv_tag_outline_white {
481        display: "inline-block";
482        vertical-align: "middle";
483        text-align: "center";
484        line-height: "1";
485        color: var!(foreground);
486        padding: format!("{} {}", var!(space-xs), var!(space-md));
487        font-size: var!(font-sm);
488        font-weight: "600";
489        cursor: "pointer";
490        border: format!("1.5px solid {}", var!(border));
491        box-sizing: "border-box";
492    }
493
494    // ═══════════════════════════════════════════════════════════════════════════
495    // Status Boxes
496    // ═══════════════════════════════════════════════════════════════════════════
497
498    pub c_error_box {
499        margin: format!("{} 0px", var!(gap-component));
500        background: var!(accent-muted);
501        color: var!(foreground);
502        font-size: var!(font-base);
503        box-sizing: "border-box";
504    }
505
506    pub c_success_box {
507        margin: format!("{} 0px", var!(gap-component));
508        background: var!(accent-muted);
509        color: var!(foreground);
510        font-size: var!(font-base);
511        box-sizing: "border-box";
512    }
513
514    // ═══════════════════════════════════════════════════════════════════════════
515    // Counter / Signals Demo
516    // ═══════════════════════════════════════════════════════════════════════════
517
518    pub c_counter_row {
519        display: "grid";
520        grid-template-columns: "1fr 1fr";
521        align-items: "center";
522        gap: var!(gap-component);
523        @media ((max-width: 767px)) {
524            grid-template-columns: "1fr";
525        }
526    }
527
528    pub c_counter_text {
529        font-size: var!(font-base);
530        color: "inherit";
531        margin-bottom: var!(gap-component);
532    }
533
534    pub c_counter_value {
535        font-weight: "700";
536        color: var!(accent);
537        font-size: var!(font-base);
538    }
539
540    pub c_counter_value_row {
541        margin-top: var!(space-sm);
542        word-break: "break-all";
543        overflow-wrap: "anywhere";
544        font-size: var!(font-base);
545        color: "inherit";
546        font-weight: "400";
547    }
548
549    pub c_badge_row {
550        display: "flex";
551        gap: var!(gap-inline);
552        flex-wrap: "wrap";
553        align-items: "center";
554    }
555
556    pub c_badge_hint {
557        margin-bottom: var!(gap-component);
558        color: "inherit";
559        opacity: "1";
560        font-size: var!(font-base);
561    }
562
563    // ═══════════════════════════════════════════════════════════════════════════
564    // Tabs
565    // ═══════════════════════════════════════════════════════════════════════════
566
567    pub c_tab_bar {
568        display: "flex";
569        border-bottom: format!("1px dashed {}", var!(border));
570        margin-bottom: var!(gap-component);
571        gap: var!(gap-element);
572        @media ((max-width: 767px)) {
573            flex-wrap: "wrap";
574        }
575    }
576
577    pub c_tab_item_active {
578        padding: format!("{} {}", var!(space-md), var!(space-xl));
579        cursor: "pointer";
580        border-bottom: format!("2px solid {}", var!(accent));
581        color: var!(text-on-accent);
582        background: var!(accent);
583        font-size: var!(font-base);
584        font-weight: "500";
585        // iOS WebKit: tap on a non-button element inside a scrollable
586        // container can be silently dropped because iOS interprets the
587        // touch as the start of a scroll gesture and never dispatches the
588        // synthetic `click`. `touch-action: manipulation` tells iOS this
589        // element only responds to taps and panning — no double-tap-zoom
590        // wait, no scroll-gesture disambiguation. `user-select: none`
591        // additionally suppresses the iOS text-selection bubble that
592        // would otherwise intercept the tap. See ui/src/style/css/fn.rs
593        // for the global reset and PR that documents the iOS repro.
594        touch-action: "manipulation";
595        user-select: "none";
596        -webkit-user-select: "none";
597    }
598
599    pub c_tab_item_inactive {
600        padding: format!("{} {}", var!(space-md), var!(space-xl));
601        cursor: "pointer";
602        border-bottom: "2px solid transparent";
603        color: "inherit";
604        font-size: var!(font-base);
605        font-weight: "500";
606        // iOS WebKit fix — see c_tab_item_active above for the rationale.
607        touch-action: "manipulation";
608        user-select: "none";
609        -webkit-user-select: "none";
610        :hover {
611            background: var!(accent-muted);
612            color: var!(accent);
613        }
614    }
615
616    pub c_tab_content {
617        padding: format!("{} 0px", var!(gap-element));
618    }
619
620    pub c_tab_text {
621        color: "inherit";
622        font-size: var!(font-base);
623        margin-bottom: var!(gap-element);
624    }
625
626    pub c_tab_text_muted {
627        color: "inherit";
628        opacity: "1";
629        font-size: var!(font-base);
630    }
631
632    pub c_tab_text_input {
633        color: "inherit";
634        font-size: var!(font-base);
635        margin-bottom: var!(gap-component);
636    }
637
638    // ═══════════════════════════════════════════════════════════════════════════
639    // List
640    // ═══════════════════════════════════════════════════════════════════════════
641
642    pub c_list_input {
643        width: "100%";
644        flex: "1";
645        height: "42px";
646        padding: format!("0px {}", var!(space-lg));
647        border: format!("1px solid {}", var!(border));
648        font-size: var!(font-base);
649        line-height: "normal";
650        box-sizing: "border-box";
651        outline: "none";
652        color: var!(foreground);
653        appearance: "none";
654        -webkit-appearance: "none";
655        -moz-appearance: "none";
656        vertical-align: "middle";
657        :hover {
658            border-color: var!(accent);
659            background: var!(accent-muted);
660        }
661        :focus {
662            outline: "none";
663            border-color: var!(accent);
664            background: var!(accent-muted);
665        }
666    }
667
668    pub c_list_input_error {
669        width: "100%";
670        flex: "1";
671        height: "42px";
672        padding: format!("0px {}", var!(space-lg));
673        border: format!("1px solid {}", var!(foreground));
674        font-size: var!(font-base);
675        line-height: "normal";
676        box-sizing: "border-box";
677        outline: "none";
678        color: var!(foreground);
679        appearance: "none";
680        -webkit-appearance: "none";
681        -moz-appearance: "none";
682        vertical-align: "middle";
683        :focus {
684            outline: "none";
685            border-color: var!(foreground);
686        }
687    }
688
689    pub c_list_error_text {
690        color: var!(foreground);
691        font-size: var!(font-base);
692        margin: format!("{} 0px", var!(gap-element));
693    }
694
695    pub c_inline_input_button_wrap {
696        flex-shrink: "0";
697    }
698
699    pub c_list_ul {
700        list-style: "none";
701        padding: "0px";
702        margin: "0px";
703        margin-top: var!(gap-component);
704    }
705
706    pub c_list_item {
707        display: "flex";
708        justify-content: "space-between";
709        align-items: "center";
710        gap: var!(gap-component);
711        min-height: var!(min-height-base);
712        margin: format!("{} 0px", var!(gap-element));
713    }
714
715    pub c_list_item_text {
716        flex: "1";
717        overflow: "hidden";
718        text-overflow: "ellipsis";
719        white-space: "nowrap";
720        font-size: var!(font-base);
721        color: "inherit";
722        min-width: "0";
723    }
724
725    pub c_list_item_button {
726        max-width: "120px";
727        flex-shrink: "0";
728    }
729
730    // ═══════════════════════════════════════════════════════════════════════════
731    // Animation Demo
732    // ═══════════════════════════════════════════════════════════════════════════
733
734    pub c_anim_fade_in {
735        margin-top: var!(gap-component);
736        animation: format!("euv-fade-in 0.5s {}", var!(ease-out));
737        font-size: var!(font-base);
738        color: "inherit";
739    }
740
741    pub c_anim_spin_container {
742        display: "flex";
743        justify-content: "center";
744        align-items: "center";
745        margin: format!("{} 0px", var!(space-lg));
746        min-height: "80px";
747    }
748
749    pub c_anim_spin {
750        font-size: var!(font-5xl);
751        animation: "euv-spin 1.5s linear infinite";
752        display: "inline-block";
753        @media ((max-width: 767px)) {
754            font-size: var!(font-4xl);
755        }
756    }
757
758    pub c_anim_spin_stopped {
759        font-size: var!(font-5xl);
760        display: "inline-block";
761        opacity: "1";
762        @media ((max-width: 767px)) {
763            font-size: var!(font-4xl);
764        }
765    }
766
767    pub c_anim_pulse_container {
768        display: "flex";
769        justify-content: "center";
770        align-items: "center";
771        margin: format!("{} 0px", var!(space-lg));
772        min-height: "80px";
773    }
774
775    pub c_anim_pulse {
776        font-size: var!(font-5xl);
777        animation: "euv-pulse 1.5s ease-in-out infinite";
778        display: "inline-block";
779        color: var!(foreground);
780        @media ((max-width: 767px)) {
781            font-size: var!(font-4xl);
782        }
783    }
784
785    pub c_anim_pulse_stopped {
786        font-size: var!(font-5xl);
787        display: "inline-block";
788        opacity: "1";
789        color: var!(foreground);
790        @media ((max-width: 767px)) {
791            font-size: var!(font-4xl);
792        }
793    }
794
795    pub c_progress_container {
796        width: "100%";
797        height: "12px";
798        margin: format!("{} 0px", var!(space-lg));
799        overflow: "hidden";
800    }
801
802    pub c_progress_bar_running {
803        height: "100%";
804        background: var!(accent);
805        animation: format!("euv-progress 1.6s {} forwards", var!(ease-in-out));
806    }
807
808    pub c_progress_bar_stopped {
809        height: "100%";
810        background: var!(accent);
811        width: "0%";
812    }
813
814    pub c_anim_scale_box {
815        margin: format!("{} 0px", var!(gap-component));
816        padding: var!(space-xl);
817        background: var!(accent);
818        cursor: "pointer";
819        transition: format!("transform {} {}", var!(duration-normal), var!(ease-out));
820    }
821
822    // ═══════════════════════════════════════════════════════════════════════════
823    // Unified Layout Primitives
824    // ═══════════════════════════════════════════════════════════════════════════
825
826    pub c_element_stack {
827        display: "flex";
828        flex-direction: "column";
829        gap: var!(gap-element);
830    }
831
832    pub c_switcher {
833        display: "flex";
834        gap: var!(gap-component);
835        flex-wrap: "wrap";
836        @media ((max-width: 767px)) {
837            flex-direction: "column";
838            gap: var!(gap-component-mobile);
839        }
840    }
841
842    pub c_switcher_col {
843        flex: "1";
844        min-width: "200px";
845        @media ((max-width: 767px)) {
846            min-width: "100%";
847        }
848    }
849
850    // ═══════════════════════════════════════════════════════════════════════════
851    // Unified Description / Hint Text
852    // ═══════════════════════════════════════════════════════════════════════════
853
854    pub c_hint {
855        color: "inherit";
856        opacity: "1";
857        font-size: var!(font-base);
858        margin-bottom: var!(gap-component);
859    }
860
861    // ═══════════════════════════════════════════════════════════════════════════
862    // euv_pagination
863    // ═══════════════════════════════════════════════════════════════════════════
864
865    pub c_euv_pagination {
866        // CSS grid with two equal columns gives us perfect 50/50 widths
867        // regardless of the link content length, which flex with `1 1
868        // auto` cannot (each link starts at its own content size). The
869        // mobile breakpoint stacks the grid to a single column so each
870        // link sizes to its content.
871        display: "grid";
872        grid-template-columns: "1fr 1fr";
873        gap: var!(gap-component);
874        // ~`space-2xl` (1.5rem) above the pagination — comfortable
875        // breathing room without a huge gap before prev/next. The original
876        // `space-4xl` (2.5rem) margin was doubled by a `padding-top` in the
877        // docs app override, leaving ~100px of empty space above the
878        // pagination boxes which made the page feel bottom-heavy.
879        margin-top: var!(space-2xl);
880        // `min-width: 0` lets prev/next text actually use ellipsis when the
881        // container is narrow — without it the grid track blows out the row.
882        min-width: "0px";
883        @media ((max-width: 767px)) {
884            grid-template-columns: "minmax(0, 1fr)";
885        }
886    }
887
888    pub c_euv_pagination_link {
889        // `min-width: 0` lets long labels ellipsize on the desktop row
890        // layout. `overflow: hidden` is required to actually apply ellipsis
891        // when content is wider than the link.
892        min-width: "0px";
893        border: format!("1px solid {}", var!(border));
894        padding: var!(space-lg);
895        cursor: "pointer";
896        display: "flex";
897        flex-direction: "column";
898        gap: var!(space-2xs);
899        overflow: "hidden";
900        :hover {
901            border-color: var!(accent);
902        }
903    }
904
905    pub(crate) c_euv_pagination_label {
906        font-size: var!(font-xs);
907        color: var!(muted-foreground);
908        text-transform: "uppercase";
909        letter-spacing: "0.08em";
910    }
911
912    pub(crate) c_euv_pagination_text {
913        font-size: var!(font-base);
914        font-weight: "600";
915        color: var!(accent);
916        // Ellipsize when the link is too narrow for the page title. The
917        // outer `.c_euv_pagination_link` already sets `overflow: hidden`
918        // and `min-width: 0`, so a single `text-overflow: ellipsis` here
919        // is enough — without `white-space: nowrap` the text wraps to
920        // multiple lines and the ellipsis never kicks in.
921        white-space: "nowrap";
922        overflow: "hidden";
923        text-overflow: "ellipsis";
924        min-width: "0px";
925    }
926
927    pub(crate) c_euv_pagination_next {
928        text-align: "right";
929    }
930
931    pub(crate) c_euv_pagination_spacer {
932        flex: "1";
933    }
934
935    // ═══════════════════════════════════════════════════════════════════════════
936    // euv_markdown / euv_footer
937    // ═══════════════════════════════════════════════════════════════════════════
938
939    pub(crate) c_euv_markdown {
940        width: "100%";
941        min-width: "0px";
942    }
943
944    // Scroll container for a markdown table.
945    //
946    // The table keeps `display: table` (so it fills the column and its
947    // columns are sized by real table layout), which means the *wrapper*
948    // is what can scroll: `overflow-x` on the table box itself is ignored.
949    // `min-width: 0` is what lets the wrapper shrink inside the flex/grid
950    // prose column instead of being floored by its table's intrinsic width,
951    // so a wide table scrolls rather than pushing the whole article wide.
952    pub(crate) c_euv_markdown_table_scroll {
953        overflow-x: "auto";
954        max-width: "100%";
955        min-width: "0px";
956    }
957
958    pub c_euv_footer {
959        // Footer sits at the natural end of the article column. The user
960        // reaches it by scrolling to the bottom of the article — no sticky
961        // or fixed positioning. This matches the desktop behavior and
962        // Avoid any layout interference on mobile (where a sticky footer
963        // would overlap the last lines of content while scrolling).
964        // No top margin: the preceding tail block already carries the gap
965        // between the article and its pagination, so a margin here would
966        // stack with it and leave the rule stranded below an empty band.
967        // The dashed rule is this element's own `border-top`, so the
968        // padding below is the gap from the rule to the text and the
969        // padding above is the gap from the preceding content to the rule.
970        // Only one of them belongs to the band inside the rule, and it is
971        // the top one — that is the space between the divider and the text.
972        // `space-md` keeps the text close to the rule; the block is
973        // `display: flex` with `align-items: center` so the line is
974        // centred in the row the padding defines rather than riding its
975        // top edge.
976        padding: format!("{} 0px", var!(space-md));
977        border-top: format!("1px dashed {}", var!(border));
978        display: "flex";
979        align-items: "center";
980        justify-content: "center";
981        text-align: "center";
982        font-size: var!(font-sm);
983        color: var!(muted-foreground);
984    }
985
986    // ═══════════════════════════════════════════════════════════════════════════
987    // euv_result / euv_doc_layout
988    // ═══════════════════════════════════════════════════════════════════════════
989
990    pub(crate) c_euv_result {
991        display: "flex";
992        flex-direction: "column";
993        align-items: "center";
994        justify-content: "center";
995        text-align: "center";
996        gap: var!(space-md);
997        padding: format!("{} {}", var!(space-4xl), var!(space-md));
998        // Half the available height, not half the viewport: the result block
999        // lives inside a scroll container, so a viewport unit would be taller
1000        // than the visible area on short pages and leave the buttons below
1001        // the fold.
1002        min-height: "50%";
1003    }
1004
1005    pub(crate) c_euv_result_code {
1006        font-size: var!(font-6xl);
1007        font-weight: "700";
1008        line-height: "1";
1009        letter-spacing: "-0.02em";
1010    }
1011
1012    pub(crate) c_euv_result_title {
1013        font-size: var!(font-2xl);
1014        font-weight: "600";
1015        margin: "0px";
1016    }
1017
1018    pub(crate) c_euv_result_description {
1019        color: var!(muted-foreground);
1020        margin: "0px";
1021    }
1022
1023    pub(crate) c_euv_result_actions {
1024        display: "flex";
1025        gap: var!(space-md);
1026        margin-top: var!(space-lg);
1027    }
1028
1029    pub c_euv_doc_layout {
1030        display: "flex";
1031        gap: var!(space-4xl);
1032        width: "100%";
1033        min-width: "0px";
1034        max-width: "1080px";
1035        margin: "0px auto";
1036        // Fill the visible area of the scroll container (`c_app_main`,
1037        // `overflow: auto` with a definite `height: 100%`) so the content
1038        // column has room to push its tail to the bottom. A viewport unit
1039        // would be wrong here: `100vh` measures the whole window including
1040        // the app header, overshooting the visible area by the header
1041        // height and pushing pagination/footer one header past the fold.
1042        // Being a direct child of the scroll container, a percentage does
1043        // resolve against its content box — put it one level deeper and
1044        // the intermediate auto-height flex row makes `100%` degenerate to
1045        // `auto`.
1046        min-height: "100%";
1047        // As a flex item of the scroll container's column, the default
1048        // `flex-shrink: 1` lets this row be compressed to the `min-height`
1049        // floor — 824px here — even though its content is 12780px tall. A
1050        // sticky descendant can only travel inside its containing block, so
1051        // the clamped row made `c_euv_doc_toc` unpin after roughly one
1052        // viewport of scroll. Never shrink: the row must carry the article's
1053        // full height so the sticky range spans the whole page.
1054        flex-shrink: "0";
1055    }
1056
1057    pub c_euv_doc_content {
1058        // Two parallel children — the article body and the
1059        // `c_euv_doc_tail` wrapper (pagination + footer). The flex column
1060        // distributes them at the two ends:
1061        //   - when the article is shorter than the column's intrinsic
1062        //     height, `justify-content: space-between` pushes the tail to
1063        //     the bottom edge;
1064        //   - when the article is longer, the tail sits right after the
1065        //     article in normal flow (because content height exceeds
1066        //     min-height, there's no remaining space to distribute).
1067        // The scroll container is `c_app_main` (`overflow: auto`), not the
1068        // window, so a viewport unit measures the whole window including the
1069        // app header. `100vh` therefore overshoots by the header height and
1070        // pushes the tail one header past the fold on a short page. Height
1071        // comes from `c_euv_doc_layout` (a direct child of the scroll
1072        // container, where a percentage resolves); here `align-items:
1073        // stretch` — the flex default — makes this column take that height
1074        // so `space-between` has the full column to distribute across.
1075        //   - article shorter than the column: `space-between` drops the
1076        //     tail to the bottom edge of the visible area;
1077        //   - article longer: the column grows with it and the tail follows
1078        //     the body in normal flow, scrolling with the page.
1079        flex: "1";
1080        min-width: "0px";
1081        max-width: var!(content-max-width);
1082        display: "flex";
1083        flex-direction: "column";
1084        justify-content: "space-between";
1085    }
1086
1087    pub c_euv_doc_body {
1088        // Groups the page title and the rendered markdown into a single
1089        // flex item. Without this the column would hold the `<h1>` and
1090        // the `<article>` as two separate items, and
1091        // `justify-content: space-between` would push them apart — a wide
1092        // blank band between the title and the first paragraph on any
1093        // page whose body is shorter than the column.
1094        display: "flex";
1095        flex-direction: "column";
1096        // The body takes its natural height; only the tail is pushed to
1097        // the far end. `flex-shrink: 0` keeps a long body from being
1098        // compressed when the column is height-constrained.
1099        flex: "0 0 auto";
1100        min-width: "0px";
1101    }
1102
1103    pub c_euv_doc_tail {
1104        // Plain block wrapper that groups pagination + footer into one
1105        // flex child. With `display: contents` the wrapper would be
1106        // skipped and the two siblings would behave as direct children
1107        // of `c_euv_doc_content` — breaking the two-parallel-containers
1108        // contract that `justify-content: space-between` relies on.
1109        display: "block";
1110    }
1111
1112    pub c_euv_doc_toc {
1113        width: "200px";
1114        flex-shrink: "0";
1115        // Pin the TOC column to the viewport while the main content scrolls.
1116        // The sticky scroll container is `c_app_main` (the overflow:auto
1117        // wrapper), so this offset is measured from that container's top edge.
1118        // It tracks `padding-main-top` rather than a bare `0` so the pinned
1119        // column keeps the same breathing room below the container edge as the
1120        // article has below the same edge — the two columns stay on one
1121        // vertical rhythm instead of the TOC riding flush against the top.
1122        position: "sticky";
1123        top: var!(padding-main-top);
1124        @media ((max-width: 1100px)) {
1125            display: "none";
1126        }
1127    }
1128
1129    // Makes the element's own box disappear while its children stay in the
1130    // parent's layout flow, so a wrapper can carry a key or a conditional
1131    // without contributing a box.
1132    //
1133    // The docs shell and page wrappers spelled this as an inline
1134    // `style: "display: contents"`; the rule is the same either way, but a
1135    // named class keeps the markup declarative and lets the browser cache it.
1136    pub c_euv_display_contents {
1137        display: "contents";
1138    }
1139
1140    // ═══════════════════════════════════════════════════════════════════════
1141    // Home Page Layout
1142    // ═══════════════════════════════════════════════════════════════════════
1143
1144    pub c_home_layout {
1145        // Mirrors `c_euv_doc_layout`: fill the visible area of the scroll
1146        // container (`c_app_main`, `overflow: auto` with a definite
1147        // `height: 100%`) so the column below has room to push its footer to
1148        // the bottom. A viewport unit would overshoot by the app header.
1149        //
1150        // As a flex item of the scroll container's column it must never be
1151        // shrunk below its content, or a tall home page collapses and the
1152        // sticky-free column loses its height floor.
1153        display: "flex";
1154        flex-direction: "column";
1155        width: "100%";
1156        min-width: "0px";
1157        min-height: "100%";
1158        flex-shrink: "0";
1159        margin: "0px auto";
1160        max-width: var!(content-max-width);
1161    }
1162
1163    pub c_home_content {
1164        // Two parallel children — the content region (hero, stats, feature
1165        // grid, blocks) and `c_home_tail` (the footer). `space-between`
1166        // puts the footer at the far end of the column: on a short home page
1167        // it drops to the bottom of the visible area, and on a tall one it
1168        // follows the content in normal flow.
1169        flex: "1";
1170        min-width: "0px";
1171        display: "flex";
1172        flex-direction: "column";
1173        justify-content: "space-between";
1174    }
1175
1176    pub c_home_body {
1177        // Groups hero + stats + grid + blocks into one flex item. Without
1178        // this wrapper `space-between` would distribute the free space
1179        // between the hero and the grid, tearing the title away from the
1180        // content it introduces.
1181        display: "flex";
1182        flex-direction: "column";
1183        flex-shrink: "0";
1184    }
1185
1186    pub c_home_tail {
1187        // Plain block wrapper around the footer so the column keeps exactly
1188        // two children. A `display: contents` wrapper would be skipped and
1189        // the footer would become a direct flex item, which is the case this
1190        // structure exists to avoid.
1191        display: "block";
1192        flex-shrink: "0";
1193    }
1194
1195    // ═══════════════════════════════════════════════════════════════════════════
1196    // Empty
1197    // ═══════════════════════════════════════════════════════════════════════════
1198
1199    pub(crate) c_euv_empty {
1200        display: "flex";
1201        flex-direction: "column";
1202        align-items: "center";
1203        justify-content: "center";
1204        gap: var!(space-sm);
1205        width: "100%";
1206        padding: var!(space-3xl);
1207        box-sizing: "border-box";
1208        text-align: "center";
1209    }
1210
1211    pub(crate) c_euv_empty_icon {
1212        font-size: var!(font-3xl);
1213        line-height: "1";
1214        color: var!(muted-foreground);
1215    }
1216
1217    pub(crate) c_euv_empty_text {
1218        font-size: var!(font-lg);
1219        font-weight: "600";
1220        color: var!(foreground);
1221    }
1222
1223    pub(crate) c_euv_empty_desc {
1224        font-size: var!(font-sm);
1225        color: var!(muted-foreground);
1226        max-width: var!(text-measure);
1227    }
1228
1229    pub(crate) c_euv_empty_action {
1230        margin-top: var!(space-sm);
1231    }
1232
1233    // ═══════════════════════════════════════════════════════════════════════════
1234    // Typography
1235    // ═══════════════════════════════════════════════════════════════════════════
1236
1237    pub(crate) c_euv_text {
1238        margin: "0px";
1239        color: var!(foreground);
1240        font-size: var!(font-base);
1241        line-height: "1.6";
1242    }
1243
1244    pub(crate) c_euv_text_title {
1245        margin: "0px";
1246        color: var!(foreground);
1247        font-size: var!(font-xl);
1248        font-weight: "600";
1249        line-height: "1.4";
1250    }
1251
1252    pub(crate) c_euv_text_title_lg {
1253        margin: "0px";
1254        color: var!(foreground);
1255        font-size: var!(font-2xl);
1256        font-weight: "600";
1257        line-height: "1.35";
1258    }
1259
1260    pub(crate) c_euv_text_title_sm {
1261        margin: "0px";
1262        color: var!(foreground);
1263        font-size: var!(font-lg);
1264        font-weight: "600";
1265        line-height: "1.4";
1266    }
1267
1268    pub(crate) c_euv_text_paragraph {
1269        margin: "0px";
1270        color: var!(foreground);
1271        font-size: var!(font-base);
1272        line-height: "1.6";
1273    }
1274
1275    pub(crate) c_euv_text_secondary {
1276        margin: "0px";
1277        color: var!(muted-foreground);
1278        font-size: var!(font-sm);
1279        line-height: "1.6";
1280    }
1281
1282    pub(crate) c_euv_text_mono {
1283        margin: "0px";
1284        color: var!(foreground);
1285        font-size: var!(font-sm);
1286        font-family: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
1287        line-height: "1.5";
1288    }
1289
1290    pub(crate) c_euv_text_deletion {
1291        margin: "0px";
1292        color: var!(muted-foreground);
1293        font-size: var!(font-base);
1294        text-decoration: "line-through";
1295        line-height: "1.6";
1296    }
1297
1298    pub(crate) c_euv_text_underline {
1299        margin: "0px";
1300        color: var!(foreground);
1301        font-size: var!(font-base);
1302        text-decoration: "underline";
1303        line-height: "1.6";
1304    }
1305
1306    // ═══════════════════════════════════════════════════════════════════════════
1307    // Link
1308    // ═══════════════════════════════════════════════════════════════════════════
1309
1310    pub(crate) c_euv_link {
1311        display: "inline";
1312        color: var!(foreground);
1313        font-size: var!(font-base);
1314        text-decoration: "underline";
1315        text-underline-offset: "2px";
1316        cursor: "pointer";
1317        touch-action: "manipulation";
1318        :hover {
1319            color: var!(muted-foreground);
1320        }
1321    }
1322
1323    pub(crate) c_euv_link_disabled {
1324        display: "inline";
1325        color: var!(muted-foreground);
1326        font-size: var!(font-base);
1327        text-decoration: "none";
1328        cursor: "not-allowed";
1329        opacity: "0.5";
1330    }
1331
1332    // ═══════════════════════════════════════════════════════════════════════════
1333    // Image
1334    // ═══════════════════════════════════════════════════════════════════════════
1335
1336    pub(crate) c_euv_image {
1337        display: "block";
1338        max-width: "100%";
1339        height: "auto";
1340        border: "none";
1341        background: "transparent";
1342    }
1343
1344    pub(crate) c_euv_image_placeholder {
1345        display: "flex";
1346        align-items: "center";
1347        justify-content: "center";
1348        width: "100%";
1349        min-height: var!(min-height-base);
1350        padding: var!(space-md);
1351        border: format!("1px dashed {}", var!(border));
1352        background: var!(background);
1353        color: var!(muted-foreground);
1354        font-size: var!(font-sm);
1355        text-align: "center";
1356        box-sizing: "border-box";
1357    }
1358
1359    pub(crate) c_euv_image_failed {
1360        display: "flex";
1361        align-items: "center";
1362        justify-content: "center";
1363        width: "100%";
1364        min-height: var!(min-height-base);
1365        padding: var!(space-md);
1366        border: format!("1px dashed {}", var!(border));
1367        background: var!(background);
1368        color: var!(muted-foreground);
1369        font-size: var!(font-sm);
1370        text-align: "center";
1371        box-sizing: "border-box";
1372    }
1373
1374    // ═══════════════════════════════════════════════════════════════════════════
1375    // Carousel
1376    // ═══════════════════════════════════════════════════════════════════════════
1377
1378    pub(crate) c_euv_carousel {
1379        position: "relative";
1380        display: "flex";
1381        flex-direction: "column";
1382        width: "100%";
1383        overflow: "hidden";
1384    }
1385
1386    pub(crate) c_euv_carousel_track {
1387        display: "flex";
1388        flex-direction: "row";
1389        width: "100%";
1390        transition: format!("transform {} {}", var!(duration-normal), var!(ease-out));
1391    }
1392
1393    pub(crate) c_euv_carousel_slide {
1394        flex: "0 0 100%";
1395        width: "100%";
1396        min-width: "100%";
1397        box-sizing: "border-box";
1398    }
1399
1400    pub(crate) c_euv_carousel_dots {
1401        display: "flex";
1402        align-items: "center";
1403        justify-content: "center";
1404        gap: var!(space-sm);
1405        padding-top: var!(space-sm);
1406    }
1407
1408    pub(crate) c_euv_carousel_dot {
1409        width: var!(space-sm);
1410        height: var!(space-sm);
1411        padding: "0px";
1412        border: format!("1px solid {}", var!(border));
1413        border-radius: "0px";
1414        background: var!(background);
1415        cursor: "pointer";
1416        touch-action: "manipulation";
1417        :hover {
1418            background: var!(accent-muted);
1419        }
1420    }
1421
1422    pub(crate) c_euv_carousel_dot_active {
1423        width: var!(space-sm);
1424        height: var!(space-sm);
1425        padding: "0px";
1426        border: format!("1px solid {}", var!(border));
1427        border-radius: "0px";
1428        background: var!(accent);
1429        cursor: "pointer";
1430        touch-action: "manipulation";
1431        :hover {
1432            background: var!(accent);
1433        }
1434    }
1435
1436    // ═══════════════════════════════════════════════════════════════════════════
1437    // Anchor
1438    // ═══════════════════════════════════════════════════════════════════════════
1439
1440    pub(crate) c_euv_anchor {
1441        display: "flex";
1442        flex-direction: "column";
1443        gap: var!(space-xs);
1444        width: "100%";
1445    }
1446
1447    pub(crate) c_euv_anchor_title {
1448        font-size: var!(font-sm);
1449        font-weight: "600";
1450        color: var!(foreground);
1451        margin-bottom: var!(space-xs);
1452    }
1453
1454    pub(crate) c_euv_anchor_link {
1455        display: "block";
1456        padding: format!("{} 0px", var!(space-2xs));
1457        color: var!(muted-foreground);
1458        font-size: var!(font-sm);
1459        text-decoration: "none";
1460        cursor: "pointer";
1461        touch-action: "manipulation";
1462        :hover {
1463            color: var!(foreground);
1464        }
1465    }
1466
1467    pub(crate) c_euv_anchor_link_active {
1468        display: "block";
1469        padding: format!("{} 0px", var!(space-2xs));
1470        color: var!(foreground);
1471        font-size: var!(font-sm);
1472        font-weight: "600";
1473        text-decoration: "none";
1474        cursor: "pointer";
1475        touch-action: "manipulation";
1476        :hover {
1477            color: var!(foreground);
1478        }
1479    }
1480
1481    // ═══════════════════════════════════════════════════════════════════════════
1482    // Watermark
1483    // ═══════════════════════════════════════════════════════════════════════════
1484
1485    pub(crate) c_euv_watermark {
1486        position: "relative";
1487        display: "block";
1488        width: "100%";
1489    }
1490
1491    pub(crate) c_euv_watermark_layer {
1492        position: "absolute";
1493        top: "0px";
1494        left: "0px";
1495        right: "0px";
1496        bottom: "0px";
1497        z-index: "1";
1498        display: "flex";
1499        align-items: "center";
1500        justify-content: "center";
1501        overflow: "hidden";
1502        pointer-events: "none";
1503    }
1504
1505    pub(crate) c_euv_watermark_text {
1506        color: var!(muted-foreground);
1507        font-size: var!(font-2xl);
1508        font-weight: "700";
1509        letter-spacing: "0.1em";
1510        opacity: "0.12";
1511        transform: "rotate(-22deg)";
1512        user-select: "none";
1513        -webkit-user-select: "none";
1514        white-space: "nowrap";
1515    }
1516
1517    // ═══════════════════════════════════════════════════════════════════════════
1518    // Back To Top
1519    // ═══════════════════════════════════════════════════════════════════════════
1520
1521
1522    pub(crate) c_euv_back_top_visible {
1523        position: "fixed";
1524        right: var!(space-xl);
1525        bottom: var!(space-xl);
1526        z-index: "900";
1527        display: "flex";
1528        align-items: "center";
1529        justify-content: "center";
1530        width: "40px";
1531        height: "40px";
1532        padding: "0px";
1533        border: format!("1px solid {}", var!(border));
1534        background: var!(background);
1535        color: var!(foreground);
1536        font-size: var!(font-sm);
1537        line-height: "1";
1538        cursor: "pointer";
1539        box-shadow: var!(shadow-sm);
1540        touch-action: "manipulation";
1541        :hover {
1542            background: var!(accent-muted);
1543            color: var!(accent);
1544        }
1545        @media ((max-width: 767px)) {
1546            right: var!(space-lg);
1547            bottom: var!(space-lg);
1548        }
1549    }
1550
1551    pub(crate) c_euv_back_top_hidden {
1552        position: "fixed";
1553        right: var!(space-xl);
1554        bottom: var!(space-xl);
1555        z-index: "900";
1556        display: "none";
1557        align-items: "center";
1558        justify-content: "center";
1559        width: "40px";
1560        height: "40px";
1561        padding: "0px";
1562        border: format!("1px solid {}", var!(border));
1563        background: var!(background);
1564        color: var!(foreground);
1565        font-size: var!(font-sm);
1566        line-height: "1";
1567        cursor: "pointer";
1568        box-shadow: var!(shadow-sm);
1569        touch-action: "manipulation";
1570        :hover {
1571            background: var!(accent-muted);
1572            color: var!(accent);
1573        }
1574        @media ((max-width: 767px)) {
1575            right: var!(space-lg);
1576            bottom: var!(space-lg);
1577        }
1578    }
1579
1580    // ═══════════════════════════════════════════════════════════════════════════
1581    // Splitter
1582    // ═══════════════════════════════════════════════════════════════════════════
1583
1584    pub(crate) c_euv_splitter {
1585        display: "flex";
1586        align-items: "stretch";
1587        width: "100%";
1588        height: "100%";
1589        min-height: "0px";
1590    }
1591
1592    pub(crate) c_euv_splitter_pane {
1593        flex: "1";
1594        min-width: "0px";
1595        min-height: "0px";
1596        overflow: "auto";
1597    }
1598
1599    pub(crate) c_euv_splitter_bar {
1600        flex: "0 0 6px";
1601        align-self: "stretch";
1602        background: var!(border);
1603        cursor: "col-resize";
1604        touch-action: "none";
1605        user-select: "none";
1606        -webkit-user-select: "none";
1607        :hover {
1608            background: var!(accent);
1609        }
1610    }
1611
1612    // ═══════════════════════════════════════════════════════════════════════════
1613    // Grid
1614    // ═══════════════════════════════════════════════════════════════════════════
1615
1616    pub(crate) c_euv_grid {
1617        display: "flex";
1618        flex-wrap: "wrap";
1619        width: "100%";
1620        box-sizing: "border-box";
1621    }
1622
1623    // A grid row that spaces its columns.
1624    //
1625    // The spacing is padding on the columns, not a `gap` on the row: a
1626    // column's width is a percentage of the row, so a gap would be
1627    // added on top of a row that already sums to a hundred percent and
1628    // push the last column onto a second line. The row pulls itself
1629    // out by half the gutter and the columns pad themselves back in, so
1630    // the columns still add up to the row's full width.
1631    pub(crate) c_euv_grid_gutter {
1632        display: "flex";
1633        flex-wrap: "wrap";
1634        width: "100%";
1635        box-sizing: "border-box";
1636        margin-left: {
1637            format!("calc({} / -2)", var!(gap-component))
1638        };
1639        margin-right: {
1640            format!("calc({} / -2)", var!(gap-component))
1641        };
1642    }
1643
1644    // A grid column. The width is a share of the row, and the caller
1645    // sets it inline, so a row's columns add up exactly whatever their
1646    // span.
1647    pub(crate) c_euv_grid_col {
1648        display: "block";
1649        flex: "0 0 auto";
1650        box-sizing: "border-box";
1651        min-width: "0px";
1652    }
1653
1654    // A grid column inside a spaced row. The column keeps its share of
1655    // the row and takes half the gutter as padding on each side, which
1656    // is what opens the space between two columns.
1657    pub(crate) c_euv_grid_gutter_col {
1658        display: "block";
1659        flex: "0 0 auto";
1660        box-sizing: "border-box";
1661        min-width: "0px";
1662        padding-left: {
1663            format!("calc({} / 2)", var!(gap-component))
1664        };
1665        padding-right: {
1666            format!("calc({} / 2)", var!(gap-component))
1667        };
1668    }
1669
1670    // A grid column that is pushed to the right by an offset. The
1671    // offset's size is set inline, like the column's own width.
1672    pub(crate) c_euv_grid_col_offset {
1673        display: "block";
1674        flex: "0 0 auto";
1675        box-sizing: "border-box";
1676        min-width: "0px";
1677    }
1678
1679    // A grid column inside a spaced row that is also pushed to the
1680    // right by an offset. It is the spaced column and the offset
1681    // column at once: the padding that opens the gutter and the offset
1682    // that pushes the column along the row.
1683    pub(crate) c_euv_grid_gutter_col_offset {
1684        display: "block";
1685        flex: "0 0 auto";
1686        box-sizing: "border-box";
1687        min-width: "0px";
1688        padding-left: {
1689            format!("calc({} / 2)", var!(gap-component))
1690        };
1691        padding-right: {
1692            format!("calc({} / 2)", var!(gap-component))
1693        };
1694    }
1695
1696    // ═══════════════════════════════════════════════════════════════════════════
1697    // Affix
1698    // ═══════════════════════════════════════════════════════════════════════════
1699
1700    pub(crate) c_euv_affix {
1701        position: "sticky";
1702        z-index: "10";
1703        width: "100%";
1704        box-sizing: "border-box";
1705    }
1706}