Skip to main content

euv_ui/style/class/display/
fn.rs

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