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