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` so the row is centred in the band the padding
974        // defines rather than riding its top edge.
975        padding: format!("{} 0px", var!(space-md));
976        border-top: format!("1px dashed {}", var!(border));
977        display: "flex";
978        // One row: the text and the notices sit side by side and never
979        // wrap. `align-items: center` keeps the badge on the text's
980        // midline, and `overflow: hidden` keeps a long row inside the
981        // column instead of pushing the page wider.
982        flex-wrap: "nowrap";
983        align-items: "center";
984        justify-content: "center";
985        gap: var!(space-md);
986        text-align: "center";
987        font-size: var!(font-sm);
988        color: var!(muted-foreground);
989        min-width: "0px";
990        overflow: "hidden";
991    }
992
993    // One item of the footer row.
994    //
995    // `inline-block` rather than a flex box: the text is inline content
996    // inside this box, so `text-overflow: ellipsis` can cut it with a
997    // dot when the row is too narrow. A flex box would put the text in
998    // an anonymous item that no selector can reach, and the text would
999    // be clipped without a hint that it was cut.
1000    pub c_euv_footer_item {
1001        display: "inline-block";
1002        min-width: "0px";
1003        max-width: "100%";
1004        overflow: "hidden";
1005        text-overflow: "ellipsis";
1006        white-space: "nowrap";
1007        vertical-align: "middle";
1008    }
1009
1010    // A linked item of the footer row.
1011    pub c_euv_footer_item_link {
1012        c_euv_footer_item();
1013        color: "inherit";
1014        text-decoration: "none";
1015        :hover {
1016            color: var!(accent);
1017        }
1018    }
1019
1020    // The official badge that precedes a filing number.
1021    //
1022    // The height is the badge's own size (20px), so the picture is
1023    // served at its native resolution instead of being scaled by the
1024    // browser. The margin is the gap to the number: the link is an
1025    // inline box, so the gap cannot come from a flex `gap`.
1026    pub c_euv_footer_item_icon {
1027        display: "inline-block";
1028        height: var!(space-xl);
1029        width: "auto";
1030        vertical-align: "middle";
1031        margin-right: var!(space-2xs);
1032    }
1033
1034    // ═══════════════════════════════════════════════════════════════════════════
1035    // euv_result / euv_doc_layout
1036    // ═══════════════════════════════════════════════════════════════════════════
1037
1038    pub(crate) c_euv_result {
1039        display: "flex";
1040        flex-direction: "column";
1041        align-items: "center";
1042        justify-content: "center";
1043        text-align: "center";
1044        gap: var!(space-md);
1045        padding: format!("{} {}", var!(space-4xl), var!(space-md));
1046        // Half the available height, not half the viewport: the result block
1047        // lives inside a scroll container, so a viewport unit would be taller
1048        // than the visible area on short pages and leave the buttons below
1049        // the fold.
1050        min-height: "50%";
1051    }
1052
1053    pub(crate) c_euv_result_code {
1054        font-size: var!(font-6xl);
1055        font-weight: "700";
1056        line-height: "1";
1057        letter-spacing: "-0.02em";
1058    }
1059
1060    pub(crate) c_euv_result_title {
1061        font-size: var!(font-2xl);
1062        font-weight: "600";
1063        margin: "0px";
1064    }
1065
1066    pub(crate) c_euv_result_description {
1067        color: var!(muted-foreground);
1068        margin: "0px";
1069    }
1070
1071    pub(crate) c_euv_result_actions {
1072        display: "flex";
1073        gap: var!(space-md);
1074        margin-top: var!(space-lg);
1075    }
1076
1077    pub c_euv_doc_layout {
1078        display: "flex";
1079        gap: var!(space-4xl);
1080        width: "100%";
1081        min-width: "0px";
1082        max-width: "1080px";
1083        margin: "0px auto";
1084        // Fill the visible area of the scroll container (`c_app_main`,
1085        // `overflow: auto` with a definite `height: 100%`) so the content
1086        // column has room to push its tail to the bottom. A viewport unit
1087        // would be wrong here: `100vh` measures the whole window including
1088        // the app header, overshooting the visible area by the header
1089        // height and pushing pagination/footer one header past the fold.
1090        // Being a direct child of the scroll container, a percentage does
1091        // resolve against its content box — put it one level deeper and
1092        // the intermediate auto-height flex row makes `100%` degenerate to
1093        // `auto`.
1094        min-height: "100%";
1095        // As a flex item of the scroll container's column, the default
1096        // `flex-shrink: 1` lets this row be compressed to the `min-height`
1097        // floor — 824px here — even though its content is 12780px tall. A
1098        // sticky descendant can only travel inside its containing block, so
1099        // the clamped row made `c_euv_doc_toc` unpin after roughly one
1100        // viewport of scroll. Never shrink: the row must carry the article's
1101        // full height so the sticky range spans the whole page.
1102        flex-shrink: "0";
1103    }
1104
1105    pub c_euv_doc_content {
1106        // Two parallel children — the article body and the
1107        // `c_euv_doc_tail` wrapper (pagination + footer). The flex column
1108        // distributes them at the two ends:
1109        //   - when the article is shorter than the column's intrinsic
1110        //     height, `justify-content: space-between` pushes the tail to
1111        //     the bottom edge;
1112        //   - when the article is longer, the tail sits right after the
1113        //     article in normal flow (because content height exceeds
1114        //     min-height, there's no remaining space to distribute).
1115        // The scroll container is `c_app_main` (`overflow: auto`), not the
1116        // window, so a viewport unit measures the whole window including the
1117        // app header. `100vh` therefore overshoots by the header height and
1118        // pushes the tail one header past the fold on a short page. Height
1119        // comes from `c_euv_doc_layout` (a direct child of the scroll
1120        // container, where a percentage resolves); here `align-items:
1121        // stretch` — the flex default — makes this column take that height
1122        // so `space-between` has the full column to distribute across.
1123        //   - article shorter than the column: `space-between` drops the
1124        //     tail to the bottom edge of the visible area;
1125        //   - article longer: the column grows with it and the tail follows
1126        //     the body in normal flow, scrolling with the page.
1127        flex: "1";
1128        min-width: "0px";
1129        max-width: var!(content-max-width);
1130        display: "flex";
1131        flex-direction: "column";
1132        justify-content: "space-between";
1133    }
1134
1135    pub c_euv_doc_body {
1136        // Groups the page title and the rendered markdown into a single
1137        // flex item. Without this the column would hold the `<h1>` and
1138        // the `<article>` as two separate items, and
1139        // `justify-content: space-between` would push them apart — a wide
1140        // blank band between the title and the first paragraph on any
1141        // page whose body is shorter than the column.
1142        display: "flex";
1143        flex-direction: "column";
1144        // The body takes its natural height; only the tail is pushed to
1145        // the far end. `flex-shrink: 0` keeps a long body from being
1146        // compressed when the column is height-constrained.
1147        flex: "0 0 auto";
1148        min-width: "0px";
1149    }
1150
1151    pub c_euv_doc_tail {
1152        // Plain block wrapper that groups pagination + footer into one
1153        // flex child. With `display: contents` the wrapper would be
1154        // skipped and the two siblings would behave as direct children
1155        // of `c_euv_doc_content` — breaking the two-parallel-containers
1156        // contract that `justify-content: space-between` relies on.
1157        display: "block";
1158    }
1159
1160    pub c_euv_doc_toc {
1161        width: "200px";
1162        flex-shrink: "0";
1163        // Pin the TOC column to the viewport while the main content scrolls.
1164        // The sticky scroll container is `c_app_main` (the overflow:auto
1165        // wrapper), so this offset is measured from that container's top edge.
1166        // It tracks `padding-main-top` rather than a bare `0` so the pinned
1167        // column keeps the same breathing room below the container edge as the
1168        // article has below the same edge — the two columns stay on one
1169        // vertical rhythm instead of the TOC riding flush against the top.
1170        position: "sticky";
1171        top: var!(padding-main-top);
1172        @media ((max-width: 1100px)) {
1173            display: "none";
1174        }
1175    }
1176
1177    // Makes the element's own box disappear while its children stay in the
1178    // parent's layout flow, so a wrapper can carry a key or a conditional
1179    // without contributing a box.
1180    //
1181    // The docs shell and page wrappers spelled this as an inline
1182    // `style: "display: contents"`; the rule is the same either way, but a
1183    // named class keeps the markup declarative and lets the browser cache it.
1184    pub c_euv_display_contents {
1185        display: "contents";
1186    }
1187
1188    // ═══════════════════════════════════════════════════════════════════════
1189    // Home Page Layout
1190    // ═══════════════════════════════════════════════════════════════════════
1191
1192    pub c_home_layout {
1193        // Mirrors `c_euv_doc_layout`: fill the visible area of the scroll
1194        // container (`c_app_main`, `overflow: auto` with a definite
1195        // `height: 100%`) so the column below has room to push its footer to
1196        // the bottom. A viewport unit would overshoot by the app header.
1197        //
1198        // As a flex item of the scroll container's column it must never be
1199        // shrunk below its content, or a tall home page collapses and the
1200        // sticky-free column loses its height floor.
1201        display: "flex";
1202        flex-direction: "column";
1203        width: "100%";
1204        min-width: "0px";
1205        min-height: "100%";
1206        flex-shrink: "0";
1207        margin: "0px auto";
1208        max-width: var!(content-max-width);
1209    }
1210
1211    pub c_home_content {
1212        // Two parallel children — the content region (hero, stats, feature
1213        // grid, blocks) and `c_home_tail` (the footer). `space-between`
1214        // puts the footer at the far end of the column: on a short home page
1215        // it drops to the bottom of the visible area, and on a tall one it
1216        // follows the content in normal flow.
1217        flex: "1";
1218        min-width: "0px";
1219        display: "flex";
1220        flex-direction: "column";
1221        justify-content: "space-between";
1222    }
1223
1224    pub c_home_body {
1225        // Groups hero + stats + grid + blocks into one flex item. Without
1226        // this wrapper `space-between` would distribute the free space
1227        // between the hero and the grid, tearing the title away from the
1228        // content it introduces.
1229        display: "flex";
1230        flex-direction: "column";
1231        flex-shrink: "0";
1232    }
1233
1234    pub c_home_tail {
1235        // Plain block wrapper around the footer so the column keeps exactly
1236        // two children. A `display: contents` wrapper would be skipped and
1237        // the footer would become a direct flex item, which is the case this
1238        // structure exists to avoid.
1239        display: "block";
1240        flex-shrink: "0";
1241    }
1242
1243    // ═══════════════════════════════════════════════════════════════════════════
1244    // Empty
1245    // ═══════════════════════════════════════════════════════════════════════════
1246
1247    pub(crate) c_euv_empty {
1248        display: "flex";
1249        flex-direction: "column";
1250        align-items: "center";
1251        justify-content: "center";
1252        gap: var!(space-sm);
1253        width: "100%";
1254        padding: var!(space-3xl);
1255        box-sizing: "border-box";
1256        text-align: "center";
1257    }
1258
1259    pub(crate) c_euv_empty_icon {
1260        font-size: var!(font-3xl);
1261        line-height: "1";
1262        color: var!(muted-foreground);
1263    }
1264
1265    pub(crate) c_euv_empty_text {
1266        font-size: var!(font-lg);
1267        font-weight: "600";
1268        color: var!(foreground);
1269    }
1270
1271    pub(crate) c_euv_empty_desc {
1272        font-size: var!(font-sm);
1273        color: var!(muted-foreground);
1274        max-width: var!(text-measure);
1275    }
1276
1277    pub(crate) c_euv_empty_action {
1278        margin-top: var!(space-sm);
1279    }
1280
1281    // ═══════════════════════════════════════════════════════════════════════════
1282    // Typography
1283    // ═══════════════════════════════════════════════════════════════════════════
1284
1285    pub(crate) c_euv_text {
1286        margin: "0px";
1287        color: var!(foreground);
1288        font-size: var!(font-base);
1289        line-height: "1.6";
1290    }
1291
1292    pub(crate) c_euv_text_title {
1293        margin: "0px";
1294        color: var!(foreground);
1295        font-size: var!(font-xl);
1296        font-weight: "600";
1297        line-height: "1.4";
1298    }
1299
1300    pub(crate) c_euv_text_title_lg {
1301        margin: "0px";
1302        color: var!(foreground);
1303        font-size: var!(font-2xl);
1304        font-weight: "600";
1305        line-height: "1.35";
1306    }
1307
1308    pub(crate) c_euv_text_title_sm {
1309        margin: "0px";
1310        color: var!(foreground);
1311        font-size: var!(font-lg);
1312        font-weight: "600";
1313        line-height: "1.4";
1314    }
1315
1316    pub(crate) c_euv_text_paragraph {
1317        margin: "0px";
1318        color: var!(foreground);
1319        font-size: var!(font-base);
1320        line-height: "1.6";
1321    }
1322
1323    pub(crate) c_euv_text_secondary {
1324        margin: "0px";
1325        color: var!(muted-foreground);
1326        font-size: var!(font-sm);
1327        line-height: "1.6";
1328    }
1329
1330    pub(crate) c_euv_text_mono {
1331        margin: "0px";
1332        color: var!(foreground);
1333        font-size: var!(font-sm);
1334        font-family: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
1335        line-height: "1.5";
1336    }
1337
1338    pub(crate) c_euv_text_deletion {
1339        margin: "0px";
1340        color: var!(muted-foreground);
1341        font-size: var!(font-base);
1342        text-decoration: "line-through";
1343        line-height: "1.6";
1344    }
1345
1346    pub(crate) c_euv_text_underline {
1347        margin: "0px";
1348        color: var!(foreground);
1349        font-size: var!(font-base);
1350        text-decoration: "underline";
1351        line-height: "1.6";
1352    }
1353
1354    // ═══════════════════════════════════════════════════════════════════════════
1355    // Link
1356    // ═══════════════════════════════════════════════════════════════════════════
1357
1358    pub(crate) c_euv_link {
1359        display: "inline";
1360        color: var!(foreground);
1361        font-size: var!(font-base);
1362        text-decoration: "underline";
1363        text-underline-offset: "2px";
1364        cursor: "pointer";
1365        touch-action: "manipulation";
1366        :hover {
1367            color: var!(muted-foreground);
1368        }
1369    }
1370
1371    pub(crate) c_euv_link_disabled {
1372        display: "inline";
1373        color: var!(muted-foreground);
1374        font-size: var!(font-base);
1375        text-decoration: "none";
1376        cursor: "not-allowed";
1377        opacity: "0.5";
1378    }
1379
1380    // ═══════════════════════════════════════════════════════════════════════════
1381    // Image
1382    // ═══════════════════════════════════════════════════════════════════════════
1383
1384    pub(crate) c_euv_image {
1385        display: "block";
1386        max-width: "100%";
1387        height: "auto";
1388        border: "none";
1389        background: "transparent";
1390    }
1391
1392    pub(crate) c_euv_image_placeholder {
1393        display: "flex";
1394        align-items: "center";
1395        justify-content: "center";
1396        width: "100%";
1397        min-height: var!(min-height-base);
1398        padding: var!(space-md);
1399        border: format!("1px dashed {}", var!(border));
1400        background: var!(background);
1401        color: var!(muted-foreground);
1402        font-size: var!(font-sm);
1403        text-align: "center";
1404        box-sizing: "border-box";
1405    }
1406
1407    pub(crate) c_euv_image_failed {
1408        display: "flex";
1409        align-items: "center";
1410        justify-content: "center";
1411        width: "100%";
1412        min-height: var!(min-height-base);
1413        padding: var!(space-md);
1414        border: format!("1px dashed {}", var!(border));
1415        background: var!(background);
1416        color: var!(muted-foreground);
1417        font-size: var!(font-sm);
1418        text-align: "center";
1419        box-sizing: "border-box";
1420    }
1421
1422    // ═══════════════════════════════════════════════════════════════════════════
1423    // Carousel
1424    // ═══════════════════════════════════════════════════════════════════════════
1425
1426    pub(crate) c_euv_carousel {
1427        position: "relative";
1428        display: "flex";
1429        flex-direction: "column";
1430        width: "100%";
1431        overflow: "hidden";
1432    }
1433
1434    pub(crate) c_euv_carousel_track {
1435        display: "flex";
1436        flex-direction: "row";
1437        width: "100%";
1438        transition: format!("transform {} {}", var!(duration-normal), var!(ease-out));
1439    }
1440
1441    pub(crate) c_euv_carousel_slide {
1442        flex: "0 0 100%";
1443        width: "100%";
1444        min-width: "100%";
1445        box-sizing: "border-box";
1446    }
1447
1448    pub(crate) c_euv_carousel_dots {
1449        display: "flex";
1450        align-items: "center";
1451        justify-content: "center";
1452        gap: var!(space-sm);
1453        padding-top: var!(space-sm);
1454    }
1455
1456    pub(crate) c_euv_carousel_dot {
1457        width: var!(space-sm);
1458        height: var!(space-sm);
1459        padding: "0px";
1460        border: format!("1px solid {}", var!(border));
1461        border-radius: "0px";
1462        background: var!(background);
1463        cursor: "pointer";
1464        touch-action: "manipulation";
1465        :hover {
1466            background: var!(accent-muted);
1467        }
1468    }
1469
1470    pub(crate) c_euv_carousel_dot_active {
1471        width: var!(space-sm);
1472        height: var!(space-sm);
1473        padding: "0px";
1474        border: format!("1px solid {}", var!(border));
1475        border-radius: "0px";
1476        background: var!(accent);
1477        cursor: "pointer";
1478        touch-action: "manipulation";
1479        :hover {
1480            background: var!(accent);
1481        }
1482    }
1483
1484    // ═══════════════════════════════════════════════════════════════════════════
1485    // Anchor
1486    // ═══════════════════════════════════════════════════════════════════════════
1487
1488    pub(crate) c_euv_anchor {
1489        display: "flex";
1490        flex-direction: "column";
1491        gap: var!(space-xs);
1492        width: "100%";
1493    }
1494
1495    pub(crate) c_euv_anchor_title {
1496        font-size: var!(font-sm);
1497        font-weight: "600";
1498        color: var!(foreground);
1499        margin-bottom: var!(space-xs);
1500    }
1501
1502    pub(crate) c_euv_anchor_link {
1503        display: "block";
1504        padding: format!("{} 0px", var!(space-2xs));
1505        color: var!(muted-foreground);
1506        font-size: var!(font-sm);
1507        text-decoration: "none";
1508        cursor: "pointer";
1509        touch-action: "manipulation";
1510        :hover {
1511            color: var!(foreground);
1512        }
1513    }
1514
1515    pub(crate) c_euv_anchor_link_active {
1516        display: "block";
1517        padding: format!("{} 0px", var!(space-2xs));
1518        color: var!(foreground);
1519        font-size: var!(font-sm);
1520        font-weight: "600";
1521        text-decoration: "none";
1522        cursor: "pointer";
1523        touch-action: "manipulation";
1524        :hover {
1525            color: var!(foreground);
1526        }
1527    }
1528
1529    // ═══════════════════════════════════════════════════════════════════════════
1530    // Watermark
1531    // ═══════════════════════════════════════════════════════════════════════════
1532
1533    pub(crate) c_euv_watermark {
1534        position: "relative";
1535        display: "block";
1536        width: "100%";
1537    }
1538
1539    pub(crate) c_euv_watermark_layer {
1540        position: "absolute";
1541        top: "0px";
1542        left: "0px";
1543        right: "0px";
1544        bottom: "0px";
1545        z-index: "1";
1546        display: "flex";
1547        align-items: "center";
1548        justify-content: "center";
1549        overflow: "hidden";
1550        pointer-events: "none";
1551    }
1552
1553    pub(crate) c_euv_watermark_text {
1554        color: var!(muted-foreground);
1555        font-size: var!(font-2xl);
1556        font-weight: "700";
1557        letter-spacing: "0.1em";
1558        opacity: "0.12";
1559        transform: "rotate(-22deg)";
1560        user-select: "none";
1561        -webkit-user-select: "none";
1562        white-space: "nowrap";
1563    }
1564
1565    // ═══════════════════════════════════════════════════════════════════════════
1566    // Back To Top
1567    // ═══════════════════════════════════════════════════════════════════════════
1568
1569
1570    pub(crate) c_euv_back_top_visible {
1571        position: "fixed";
1572        right: var!(space-xl);
1573        bottom: var!(space-xl);
1574        z-index: "900";
1575        display: "flex";
1576        align-items: "center";
1577        justify-content: "center";
1578        width: "40px";
1579        height: "40px";
1580        padding: "0px";
1581        border: format!("1px solid {}", var!(border));
1582        background: var!(background);
1583        color: var!(foreground);
1584        font-size: var!(font-sm);
1585        line-height: "1";
1586        cursor: "pointer";
1587        box-shadow: var!(shadow-sm);
1588        touch-action: "manipulation";
1589        :hover {
1590            background: var!(accent-muted);
1591            color: var!(accent);
1592        }
1593        @media ((max-width: 767px)) {
1594            right: var!(space-lg);
1595            bottom: var!(space-lg);
1596        }
1597    }
1598
1599    pub(crate) c_euv_back_top_hidden {
1600        position: "fixed";
1601        right: var!(space-xl);
1602        bottom: var!(space-xl);
1603        z-index: "900";
1604        display: "none";
1605        align-items: "center";
1606        justify-content: "center";
1607        width: "40px";
1608        height: "40px";
1609        padding: "0px";
1610        border: format!("1px solid {}", var!(border));
1611        background: var!(background);
1612        color: var!(foreground);
1613        font-size: var!(font-sm);
1614        line-height: "1";
1615        cursor: "pointer";
1616        box-shadow: var!(shadow-sm);
1617        touch-action: "manipulation";
1618        :hover {
1619            background: var!(accent-muted);
1620            color: var!(accent);
1621        }
1622        @media ((max-width: 767px)) {
1623            right: var!(space-lg);
1624            bottom: var!(space-lg);
1625        }
1626    }
1627
1628    // ═══════════════════════════════════════════════════════════════════════════
1629    // Splitter
1630    // ═══════════════════════════════════════════════════════════════════════════
1631
1632    pub(crate) c_euv_splitter {
1633        display: "flex";
1634        align-items: "stretch";
1635        width: "100%";
1636        height: "100%";
1637        min-height: "0px";
1638    }
1639
1640    pub(crate) c_euv_splitter_pane {
1641        flex: "1";
1642        min-width: "0px";
1643        min-height: "0px";
1644        overflow: "auto";
1645    }
1646
1647    pub(crate) c_euv_splitter_bar {
1648        flex: "0 0 6px";
1649        align-self: "stretch";
1650        background: var!(border);
1651        cursor: "col-resize";
1652        touch-action: "none";
1653        user-select: "none";
1654        -webkit-user-select: "none";
1655        :hover {
1656            background: var!(accent);
1657        }
1658    }
1659
1660    // ═══════════════════════════════════════════════════════════════════════════
1661    // Grid
1662    // ═══════════════════════════════════════════════════════════════════════════
1663
1664    pub(crate) c_euv_grid {
1665        display: "flex";
1666        flex-wrap: "wrap";
1667        width: "100%";
1668        box-sizing: "border-box";
1669    }
1670
1671    // A grid row that spaces its columns.
1672    //
1673    // The spacing is padding on the columns, not a `gap` on the row: a
1674    // column's width is a percentage of the row, so a gap would be
1675    // added on top of a row that already sums to a hundred percent and
1676    // push the last column onto a second line. The row pulls itself
1677    // out by half the gutter and the columns pad themselves back in, so
1678    // the columns still add up to the row's full width.
1679    pub(crate) c_euv_grid_gutter {
1680        display: "flex";
1681        flex-wrap: "wrap";
1682        width: "100%";
1683        box-sizing: "border-box";
1684        margin-left: {
1685            format!("calc({} / -2)", var!(gap-component))
1686        };
1687        margin-right: {
1688            format!("calc({} / -2)", var!(gap-component))
1689        };
1690    }
1691
1692    // A grid column. The width is a share of the row, and the caller
1693    // sets it inline, so a row's columns add up exactly whatever their
1694    // span.
1695    pub(crate) c_euv_grid_col {
1696        display: "block";
1697        flex: "0 0 auto";
1698        box-sizing: "border-box";
1699        min-width: "0px";
1700    }
1701
1702    // A grid column inside a spaced row. The column keeps its share of
1703    // the row and takes half the gutter as padding on each side, which
1704    // is what opens the space between two columns.
1705    pub(crate) c_euv_grid_gutter_col {
1706        display: "block";
1707        flex: "0 0 auto";
1708        box-sizing: "border-box";
1709        min-width: "0px";
1710        padding-left: {
1711            format!("calc({} / 2)", var!(gap-component))
1712        };
1713        padding-right: {
1714            format!("calc({} / 2)", var!(gap-component))
1715        };
1716    }
1717
1718    // A grid column that is pushed to the right by an offset. The
1719    // offset's size is set inline, like the column's own width.
1720    pub(crate) c_euv_grid_col_offset {
1721        display: "block";
1722        flex: "0 0 auto";
1723        box-sizing: "border-box";
1724        min-width: "0px";
1725    }
1726
1727    // A grid column inside a spaced row that is also pushed to the
1728    // right by an offset. It is the spaced column and the offset
1729    // column at once: the padding that opens the gutter and the offset
1730    // that pushes the column along the row.
1731    pub(crate) c_euv_grid_gutter_col_offset {
1732        display: "block";
1733        flex: "0 0 auto";
1734        box-sizing: "border-box";
1735        min-width: "0px";
1736        padding-left: {
1737            format!("calc({} / 2)", var!(gap-component))
1738        };
1739        padding-right: {
1740            format!("calc({} / 2)", var!(gap-component))
1741        };
1742    }
1743
1744    // ═══════════════════════════════════════════════════════════════════════════
1745    // Affix
1746    // ═══════════════════════════════════════════════════════════════════════════
1747
1748    pub(crate) c_euv_affix {
1749        position: "sticky";
1750        z-index: "10";
1751        width: "100%";
1752        box-sizing: "border-box";
1753    }
1754}