1
//! CSS property cache for efficient style resolution and animation.
2
//!
3
//! This module implements a cache layer between the raw CSS stylesheet and the rendered DOM.
4
//! It resolves CSS properties for each node, handling:
5
//!
6
//! - **Cascade resolution**: Computes final values from CSS rules, inline styles, and inheritance
7
//! - **Pseudo-class states**: Caches styles for `:hover`, `:active`, `:focus`, etc.
8
//! - **Animation support**: Tracks animating properties for smooth interpolation
9
//! - **Performance**: Avoids re-parsing and re-resolving unchanged properties
10
//!
11
//! # Architecture
12
//!
13
//! The cache is organized per-node and per-property-type. Each property has a dedicated
14
//! getter method that:
15
//!
16
//! 1. Checks if the property is cached
17
//! 2. If not, resolves it from CSS rules + inline styles
18
//! 3. Caches the result for subsequent frames
19
//!
20
//! # Thread Safety
21
//!
22
//! Not thread-safe. Each window has its own cache instance.
23

            
24
extern crate alloc;
25

            
26
use alloc::{boxed::Box, string::String, vec::Vec};
27

            
28
use crate::dom::NodeType;
29

            
30
/// Tracks the origin of a CSS property value.
31
/// Used to correctly implement the CSS cascade and inheritance rules.
32
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
33
pub enum CssPropertyOrigin {
34
    /// Property was inherited from parent node (only for inheritable properties)
35
    Inherited,
36
    /// Property is the node's own value (from UA CSS, CSS file, inline style, or user override)
37
    Own,
38
}
39

            
40
/// A CSS property with its origin tracking.
41
#[derive(Debug, Clone, PartialEq)]
42
pub struct CssPropertyWithOrigin {
43
    pub property: CssProperty,
44
    pub origin: CssPropertyOrigin,
45
}
46

            
47
use azul_css::{
48
    css::{Css, CssPath},
49
    props::{
50
        basic::{StyleFontFamily, StyleFontFamilyVec, StyleFontSize},
51
        layout::{LayoutDisplay, LayoutHeight, LayoutWidth},
52
        property::{
53
            BoxDecorationBreakValue, BreakInsideValue, CaretAnimationDurationValue,
54
            CaretColorValue, CaretWidthValue, ClipPathValue, ColumnCountValue, ColumnFillValue,
55
            ColumnRuleColorValue, ColumnRuleStyleValue, ColumnRuleWidthValue, ColumnSpanValue,
56
            ColumnWidthValue, ContentValue, CounterIncrementValue, CounterResetValue, CssProperty,
57
            CssPropertyType, FlowFromValue, FlowIntoValue, LayoutAlignContentValue,
58
            LayoutAlignItemsValue, LayoutAlignSelfValue, LayoutBorderBottomWidthValue,
59
            LayoutBorderLeftWidthValue, LayoutBorderRightWidthValue, LayoutBorderSpacingValue,
60
            LayoutBorderTopWidthValue, LayoutBoxSizingValue, LayoutClearValue,
61
            LayoutColumnGapValue, LayoutDisplayValue, LayoutFlexBasisValue,
62
            LayoutFlexDirectionValue, LayoutFlexGrowValue, LayoutFlexShrinkValue,
63
            LayoutFlexWrapValue, LayoutFloatValue, LayoutGapValue, LayoutGridAutoColumnsValue,
64
            LayoutGridAutoFlowValue, LayoutGridAutoRowsValue, LayoutGridColumnValue,
65
            LayoutGridRowValue, LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
66
            LayoutHeightValue, LayoutInsetBottomValue, LayoutJustifyContentValue,
67
            LayoutJustifyItemsValue, LayoutJustifySelfValue, LayoutLeftValue,
68
            LayoutMarginBottomValue, LayoutMarginLeftValue, LayoutMarginRightValue,
69
            LayoutMarginTopValue, LayoutMaxHeightValue, LayoutMaxWidthValue, LayoutMinHeightValue,
70
            LayoutMinWidthValue, LayoutOverflowValue, LayoutPaddingBottomValue,
71
            LayoutPaddingLeftValue, LayoutPaddingRightValue, LayoutPaddingTopValue,
72
            LayoutPositionValue, LayoutRightValue, LayoutRowGapValue, LayoutScrollbarWidthValue,
73
            LayoutTableLayoutValue, LayoutTextJustifyValue, LayoutTopValue, LayoutWidthValue,
74
            LayoutWritingModeValue, LayoutZIndexValue, OrphansValue, PageBreakValue,
75
            StyleBackgroundContentValue, ScrollbarFadeDelayValue, ScrollbarFadeDurationValue,
76
            ScrollbarVisibilityModeValue, SelectionBackgroundColorValue, SelectionColorValue,
77
            SelectionRadiusValue, ShapeImageThresholdValue, ShapeInsideValue, ShapeMarginValue,
78
            ShapeOutsideValue, StringSetValue, StyleBackfaceVisibilityValue,
79
            StyleBackgroundContentVecValue, StyleBackgroundPositionVecValue,
80
            StyleBackgroundRepeatVecValue, StyleBackgroundSizeVecValue,
81
            StyleBorderBottomColorValue, StyleBorderBottomLeftRadiusValue,
82
            StyleBorderBottomRightRadiusValue, StyleBorderBottomStyleValue,
83
            StyleBorderCollapseValue, StyleBorderLeftColorValue, StyleBorderLeftStyleValue,
84
            StyleBorderRightColorValue, StyleBorderRightStyleValue, StyleBorderTopColorValue,
85
            StyleBorderTopLeftRadiusValue, StyleBorderTopRightRadiusValue,
86
            StyleBorderTopStyleValue, StyleBoxShadowValue, StyleCaptionSideValue, StyleCursorValue,
87
            StyleDirectionValue, StyleEmptyCellsValue, StyleExclusionMarginValue,
88
            StyleFilterVecValue, StyleFontFamilyVecValue, StyleFontSizeValue, StyleFontStyleValue,
89
            StyleFontValue, StyleFontWeightValue, StyleHangingPunctuationValue,
90
            StyleHyphenationLanguageValue, StyleHyphensValue, StyleInitialLetterValue,
91
            StyleLetterSpacingValue, StyleLineBreakValue, StyleLineClampValue, StyleLineHeightValue,
92
            StyleListStylePositionValue, StyleListStyleTypeValue, StyleMixBlendModeValue,
93
            StyleAspectRatioValue, StyleObjectFitValue, StyleObjectPositionValue,
94
            StyleOpacityValue, StylePerspectiveOriginValue,
95
            StyleScrollbarColorValue, StyleOverflowWrapValue, StyleTabSizeValue,
96
            StyleTextAlignLastValue, StyleTextOrientationValue,
97
            StyleTextAlignValue, StyleTextColorValue,
98
            StyleTextCombineUprightValue, StyleUnicodeBidiValue,
99
            StyleTextBoxTrimValue, StyleTextBoxEdgeValue,
100
            StyleDominantBaselineValue, StyleAlignmentBaselineValue,
101
            StyleInitialLetterAlignValue, StyleInitialLetterWrapValue,
102
            StyleScrollbarGutterValue, StyleOverflowClipMarginValue, StyleClipRectValue,
103
            StyleTextDecorationValue, StyleTextIndentValue,
104
            StyleTransformOriginValue, StyleTransformVecValue, StyleUserSelectValue,
105
            StyleVerticalAlignValue, StyleVisibilityValue, StyleWhiteSpaceValue,
106
            StyleWordBreakValue, StyleWordSpacingValue, WidowsValue,
107
        },
108
        style::{StyleCursor, StyleTextColor, StyleTransformOrigin},
109
    },
110
    AzString,
111
};
112

            
113
use crate::{
114
    dom::{NodeData, NodeId, TabIndex, TagId},
115
    id::{NodeDataContainer, NodeDataContainerRef},
116
    style::CascadeInfo,
117
    styled_dom::{
118
        NodeHierarchyItem, NodeHierarchyItemId, NodeHierarchyItemVec, ParentWithNodeDepth,
119
        ParentWithNodeDepthVec, StyledNodeState, TagIdToNodeIdMapping,
120
    },
121
};
122

            
123
use azul_css::dynamic_selector::{
124
    CssPropertyWithConditions, CssPropertyWithConditionsVec, DynamicSelectorContext,
125
};
126

            
127
#[cfg(feature = "std")]
128
std::thread_local! {
129
    static PROP_COUNTS: core::cell::RefCell<
130
        std::collections::HashMap<&'static str, usize>
131
    > = core::cell::RefCell::new(std::collections::HashMap::new());
132
}
133

            
134
/// Drain the per-thread CSS cascade-walk counter populated by
135
/// [`CssPropertyCache::get_property`] when `AZ_PROP_COUNT=1` is set
136
/// in the environment. Returns `(property_label, count)` pairs
137
/// sorted by count descending. Layout-side instrumentation calls
138
/// this after each `layout_document` to print which properties
139
/// drove the most cascade walks.
140
#[cfg(feature = "std")]
141
pub fn drain_css_prop_counts() -> Vec<(&'static str, usize)> {
142
    // try_with: no real TLS in the lifted-to-wasm web backend (see the
143
    // get_property recording site) — return empty rather than panic.
144
    PROP_COUNTS
145
        .try_with(|c| {
146
            let map = core::mem::take(&mut *c.borrow_mut());
147
            let mut v: Vec<_> = map.into_iter().collect();
148
            v.sort_by(|a, b| b.1.cmp(&a.1));
149
            v
150
        })
151
        .unwrap_or_default()
152
}
153

            
154
// Unit conversion constants (CSS absolute units → pixels)
155
const PT_TO_PX: f32 = 1.333333;
156
const IN_TO_PX: f32 = 96.0;
157
const CM_TO_PX: f32 = 37.7952755906;
158
const MM_TO_PX: f32 = 3.7795275591;
159

            
160
/// Match on any CssProperty variant and access the inner CssPropertyValue<T>.
161
#[allow(unused_macros)]
162
macro_rules! match_property_value {
163
    ($property:expr, $value:ident, $expr:expr) => {
164
        match $property {
165
            CssProperty::CaretColor($value) => $expr,
166
            CssProperty::CaretAnimationDuration($value) => $expr,
167
            CssProperty::SelectionBackgroundColor($value) => $expr,
168
            CssProperty::SelectionColor($value) => $expr,
169
            CssProperty::SelectionRadius($value) => $expr,
170
            CssProperty::TextColor($value) => $expr,
171
            CssProperty::FontSize($value) => $expr,
172
            CssProperty::FontFamily($value) => $expr,
173
            CssProperty::FontWeight($value) => $expr,
174
            CssProperty::FontStyle($value) => $expr,
175
            CssProperty::TextAlign($value) => $expr,
176
            CssProperty::TextJustify($value) => $expr,
177
            CssProperty::VerticalAlign($value) => $expr,
178
            CssProperty::LetterSpacing($value) => $expr,
179
            CssProperty::TextIndent($value) => $expr,
180
            CssProperty::InitialLetter($value) => $expr,
181
            CssProperty::LineClamp($value) => $expr,
182
            CssProperty::HangingPunctuation($value) => $expr,
183
            CssProperty::TextCombineUpright($value) => $expr,
184
            CssProperty::UnicodeBidi($value) => $expr,
185
            CssProperty::TextBoxTrim($value) => $expr,
186
            CssProperty::TextBoxEdge($value) => $expr,
187
            CssProperty::DominantBaseline($value) => $expr,
188
            CssProperty::AlignmentBaseline($value) => $expr,
189
            CssProperty::InitialLetterAlign($value) => $expr,
190
            CssProperty::InitialLetterWrap($value) => $expr,
191
            CssProperty::ScrollbarGutter($value) => $expr,
192
            CssProperty::OverflowClipMargin($value) => $expr,
193
            CssProperty::Clip($value) => $expr,
194
            CssProperty::ExclusionMargin($value) => $expr,
195
            CssProperty::HyphenationLanguage($value) => $expr,
196
            CssProperty::LineHeight($value) => $expr,
197
            CssProperty::WordSpacing($value) => $expr,
198
            CssProperty::TabSize($value) => $expr,
199
            CssProperty::WhiteSpace($value) => $expr,
200
            CssProperty::Hyphens($value) => $expr,
201
            CssProperty::Direction($value) => $expr,
202
            CssProperty::UserSelect($value) => $expr,
203
            CssProperty::TextDecoration($value) => $expr,
204
            CssProperty::Cursor($value) => $expr,
205
            CssProperty::Display($value) => $expr,
206
            CssProperty::Float($value) => $expr,
207
            CssProperty::BoxSizing($value) => $expr,
208
            CssProperty::Width($value) => $expr,
209
            CssProperty::Height($value) => $expr,
210
            CssProperty::MinWidth($value) => $expr,
211
            CssProperty::MinHeight($value) => $expr,
212
            CssProperty::MaxWidth($value) => $expr,
213
            CssProperty::MaxHeight($value) => $expr,
214
            CssProperty::Position($value) => $expr,
215
            CssProperty::Top($value) => $expr,
216
            CssProperty::Right($value) => $expr,
217
            CssProperty::Left($value) => $expr,
218
            CssProperty::Bottom($value) => $expr,
219
            CssProperty::ZIndex($value) => $expr,
220
            CssProperty::FlexWrap($value) => $expr,
221
            CssProperty::FlexDirection($value) => $expr,
222
            CssProperty::FlexGrow($value) => $expr,
223
            CssProperty::FlexShrink($value) => $expr,
224
            CssProperty::FlexBasis($value) => $expr,
225
            CssProperty::JustifyContent($value) => $expr,
226
            CssProperty::AlignItems($value) => $expr,
227
            CssProperty::AlignContent($value) => $expr,
228
            CssProperty::AlignSelf($value) => $expr,
229
            CssProperty::JustifyItems($value) => $expr,
230
            CssProperty::JustifySelf($value) => $expr,
231
            CssProperty::BackgroundContent($value) => $expr,
232
            CssProperty::BackgroundPosition($value) => $expr,
233
            CssProperty::BackgroundSize($value) => $expr,
234
            CssProperty::BackgroundRepeat($value) => $expr,
235
            CssProperty::OverflowX($value) => $expr,
236
            CssProperty::OverflowY($value) => $expr,
237
            CssProperty::OverflowBlock($value) => $expr,
238
            CssProperty::OverflowInline($value) => $expr,
239
            CssProperty::PaddingTop($value) => $expr,
240
            CssProperty::PaddingLeft($value) => $expr,
241
            CssProperty::PaddingRight($value) => $expr,
242
            CssProperty::PaddingBottom($value) => $expr,
243
            CssProperty::MarginTop($value) => $expr,
244
            CssProperty::MarginLeft($value) => $expr,
245
            CssProperty::MarginRight($value) => $expr,
246
            CssProperty::MarginBottom($value) => $expr,
247
            CssProperty::BorderTopLeftRadius($value) => $expr,
248
            CssProperty::BorderTopRightRadius($value) => $expr,
249
            CssProperty::BorderBottomLeftRadius($value) => $expr,
250
            CssProperty::BorderBottomRightRadius($value) => $expr,
251
            CssProperty::BorderTopColor($value) => $expr,
252
            CssProperty::BorderRightColor($value) => $expr,
253
            CssProperty::BorderLeftColor($value) => $expr,
254
            CssProperty::BorderBottomColor($value) => $expr,
255
            CssProperty::BorderTopStyle($value) => $expr,
256
            CssProperty::BorderRightStyle($value) => $expr,
257
            CssProperty::BorderLeftStyle($value) => $expr,
258
            CssProperty::BorderBottomStyle($value) => $expr,
259
            CssProperty::BorderTopWidth($value) => $expr,
260
            CssProperty::BorderRightWidth($value) => $expr,
261
            CssProperty::BorderLeftWidth($value) => $expr,
262
            CssProperty::BorderBottomWidth($value) => $expr,
263
            CssProperty::BoxShadow($value) => $expr,
264
            CssProperty::Opacity($value) => $expr,
265
            CssProperty::Transform($value) => $expr,
266
            CssProperty::TransformOrigin($value) => $expr,
267
            CssProperty::PerspectiveOrigin($value) => $expr,
268
            CssProperty::BackfaceVisibility($value) => $expr,
269
            CssProperty::MixBlendMode($value) => $expr,
270
            CssProperty::Filter($value) => $expr,
271
            CssProperty::Visibility($value) => $expr,
272
            CssProperty::WritingMode($value) => $expr,
273
            CssProperty::GridTemplateColumns($value) => $expr,
274
            CssProperty::GridTemplateRows($value) => $expr,
275
            CssProperty::GridAutoColumns($value) => $expr,
276
            CssProperty::GridAutoRows($value) => $expr,
277
            CssProperty::GridAutoFlow($value) => $expr,
278
            CssProperty::GridColumn($value) => $expr,
279
            CssProperty::GridRow($value) => $expr,
280
            CssProperty::GridTemplateAreas($value) => $expr,
281
            CssProperty::Gap($value) => $expr,
282
            CssProperty::ColumnGap($value) => $expr,
283
            CssProperty::RowGap($value) => $expr,
284
            CssProperty::Clear($value) => $expr,
285
            CssProperty::ScrollbarTrack($value) => $expr,
286
            CssProperty::ScrollbarThumb($value) => $expr,
287
            CssProperty::ScrollbarButton($value) => $expr,
288
            CssProperty::ScrollbarCorner($value) => $expr,
289
            CssProperty::ScrollbarResizer($value) => $expr,
290
            CssProperty::ScrollbarWidth($value) => $expr,
291
            CssProperty::ScrollbarColor($value) => $expr,
292
            CssProperty::ListStyleType($value) => $expr,
293
            CssProperty::ListStylePosition($value) => $expr,
294
            CssProperty::Font($value) => $expr,
295
            CssProperty::ColumnCount($value) => $expr,
296
            CssProperty::ColumnWidth($value) => $expr,
297
            CssProperty::ColumnSpan($value) => $expr,
298
            CssProperty::ColumnFill($value) => $expr,
299
            CssProperty::ColumnRuleStyle($value) => $expr,
300
            CssProperty::ColumnRuleWidth($value) => $expr,
301
            CssProperty::ColumnRuleColor($value) => $expr,
302
            CssProperty::FlowInto($value) => $expr,
303
            CssProperty::FlowFrom($value) => $expr,
304
            CssProperty::ShapeOutside($value) => $expr,
305
            CssProperty::ShapeInside($value) => $expr,
306
            CssProperty::ShapeImageThreshold($value) => $expr,
307
            CssProperty::ShapeMargin($value) => $expr,
308
            CssProperty::ClipPath($value) => $expr,
309
            CssProperty::Content($value) => $expr,
310
            CssProperty::CounterIncrement($value) => $expr,
311
            CssProperty::CounterReset($value) => $expr,
312
            CssProperty::StringSet($value) => $expr,
313
            CssProperty::Orphans($value) => $expr,
314
            CssProperty::Widows($value) => $expr,
315
            CssProperty::PageBreakBefore($value) => $expr,
316
            CssProperty::PageBreakAfter($value) => $expr,
317
            CssProperty::PageBreakInside($value) => $expr,
318
            CssProperty::BreakInside($value) => $expr,
319
            CssProperty::BoxDecorationBreak($value) => $expr,
320
            CssProperty::TableLayout($value) => $expr,
321
            CssProperty::BorderCollapse($value) => $expr,
322
            CssProperty::BorderSpacing($value) => $expr,
323
            CssProperty::CaptionSide($value) => $expr,
324
            CssProperty::EmptyCells($value) => $expr,
325
        }
326
    };
327
}
328

            
329
/// A CSS property tagged with its pseudo-state and property type.
330
/// Replaces the per-pseudo-state BTreeMap approach: instead of 6 BTreeMaps
331
/// per node (Normal/Hover/Active/Focus/Dragging/DragOver), we store one Vec
332
/// per node and tag each property with its state. Lookups use `.iter().find()`.
333
#[derive(Debug, Clone, PartialEq)]
334
pub struct StatefulCssProperty {
335
    pub state: azul_css::dynamic_selector::PseudoStateType,
336
    pub prop_type: CssPropertyType,
337
    pub property: CssProperty,
338
}
339

            
340
// =============================================================================
341
// FlatVecVec: Cache-friendly replacement for Vec<Vec<T>>
342
// =============================================================================
343

            
344
/// A flat, cache-friendly replacement for `Vec<Vec<T>>`.
345
///
346
/// During the **build phase**, items are pushed into per-node inner Vecs
347
/// (same as before). After building is complete, `flatten()` compacts all
348
/// inner Vecs into a single contiguous `Vec<T>` with a `(start, len)` offset
349
/// table per node. All subsequent reads use the flat layout, eliminating
350
/// N heap allocations and pointer chasing.
351
///
352
/// ## Lifecycle
353
///
354
/// ```text
355
/// new(n) → push_to(idx, item)* → sort_each_and_flatten(key_fn) → get_slice(idx)*
356
///          ── build phase ──       ── transition ──                ── read phase ──
357
/// ```
358
#[derive(Debug, Clone)]
359
pub struct FlatVecVec<T> {
360
    /// Per-node inner Vecs (used during build phase, empty after flatten).
361
    build: Vec<Vec<T>>,
362
    /// Flat contiguous storage (populated after flatten).
363
    data: Vec<T>,
364
    /// `(start, len)` offsets into `data` for each node (populated after flatten).
365
    offsets: Vec<(u32, u32)>,
366
}
367

            
368
impl<T: PartialEq> PartialEq for FlatVecVec<T> {
369
    fn eq(&self, other: &Self) -> bool {
370
        let self_in_build = !self.build.is_empty() && self.offsets.is_empty();
371
        let other_in_build = !other.build.is_empty() && other.offsets.is_empty();
372
        debug_assert!(
373
            self_in_build == other_in_build,
374
            "FlatVecVec::eq called across phases (one build, one flattened)"
375
        );
376
        if self_in_build || other_in_build {
377
            self.build == other.build
378
        } else {
379
            self.data == other.data && self.offsets == other.offsets
380
        }
381
    }
382
}
383

            
384
impl<T> Default for FlatVecVec<T> {
385
    fn default() -> Self {
386
        Self {
387
            build: Vec::new(),
388
            data: Vec::new(),
389
            offsets: Vec::new(),
390
        }
391
    }
392
}
393

            
394
impl<T> FlatVecVec<T> {
395
    /// Approximate heap bytes retained. Sums capacity of the
396
    /// flattened `data` + `offsets` tables and the per-node build
397
    /// Vecs (in case `sort_each_and_flatten` hasn't been called
398
    /// yet). `per_element_size` should be `size_of::<T>()`.
399
4
    pub fn heap_bytes(&self, per_element_size: usize) -> usize {
400
4
        let data_bytes = self.data.capacity() * per_element_size;
401
4
        let offsets_bytes =
402
4
            self.offsets.capacity() * core::mem::size_of::<(u32, u32)>();
403
4
        let mut build_bytes = self.build.capacity() * core::mem::size_of::<Vec<T>>();
404
104
        for v in &self.build {
405
100
            build_bytes += v.capacity() * per_element_size;
406
100
        }
407
4
        data_bytes + offsets_bytes + build_bytes
408
4
    }
409

            
410
    /// Create a new `FlatVecVec` with `node_count` empty slots (build phase).
411
17550
    pub fn new(node_count: usize) -> Self {
412
17550
        let mut build = Vec::with_capacity(node_count);
413
91898
        for _ in 0..node_count {
414
91898
            build.push(Vec::new());
415
91898
        }
416
17550
        Self {
417
17550
            build,
418
17550
            data: Vec::new(),
419
17550
            offsets: Vec::new(),
420
17550
        }
421
17550
    }
422

            
423
    /// Push an item to the inner Vec at `node_index` (build phase).
424
    ///
425
    /// # Panics
426
    /// Panics if already flattened or if `node_index >= len()`.
427
    #[inline]
428
152542
    pub fn push_to(&mut self, node_index: usize, item: T) {
429
152542
        self.build[node_index].push(item);
430
152542
    }
431

            
432
    /// Get a mutable reference to the inner Vec at `node_index` (build phase).
433
    #[inline]
434
4565
    pub fn build_mut(&mut self, node_index: usize) -> &mut Vec<T> {
435
4565
        &mut self.build[node_index]
436
4565
    }
437

            
438
    /// Iterate mutably over all inner Vecs (build phase, e.g. for clearing).
439
    #[inline]
440
5476
    pub fn build_iter_mut(&mut self) -> core::slice::IterMut<'_, Vec<T>> {
441
5476
        self.build.iter_mut()
442
5476
    }
443

            
444
    /// Get a reference to the inner Vec at `node_index` during build phase.
445
    /// During read phase, returns None (use `get_slice` instead).
446
    #[inline]
447
    pub fn build_get(&self, node_index: usize) -> Option<&Vec<T>> {
448
        self.build.get(node_index)
449
    }
450

            
451
    /// Number of node slots.
452
    #[inline]
453
17362
    pub fn len(&self) -> usize {
454
17362
        if !self.offsets.is_empty() {
455
8681
            self.offsets.len()
456
        } else {
457
8681
            self.build.len()
458
        }
459
17362
    }
460

            
461
    /// Returns true if this is in read (flattened) mode.
462
    #[inline]
463
8700
    pub fn is_flattened(&self) -> bool {
464
8700
        !self.offsets.is_empty() || self.build.is_empty()
465
8700
    }
466

            
467
    /// Get a slice for the node at `node_index` (read phase).
468
    /// Returns empty slice if index is out of bounds or not yet flattened
469
    /// (falls back to build-phase data if not yet flattened).
470
    #[inline]
471
2682565
    pub fn get_slice(&self, node_index: usize) -> &[T] {
472
2682565
        if !self.offsets.is_empty() {
473
            // Read phase: use flat data
474
2346887
            if let Some(&(start, len)) = self.offsets.get(node_index) {
475
2346887
                let s = start as usize;
476
2346887
                let l = len as usize;
477
2346887
                &self.data[s..s + l]
478
            } else {
479
                &[]
480
            }
481
        } else {
482
            // Build phase fallback: use inner Vecs
483
335678
            self.build.get(node_index).map(|v| v.as_slice()).unwrap_or(&[])
484
        }
485
2682565
    }
486

            
487
    /// Flatten: sort each inner Vec by key, deduplicate by keeping the last
488
    /// occurrence of each key (CSS cascade: later source order wins among
489
    /// equal specificity), then compact into flat storage.
490
    /// Drains all build-phase Vecs. After this call, only `get_slice()` works.
491
17381
    pub fn sort_each_and_flatten<K: Ord + Eq>(&mut self, key_fn: impl Fn(&T) -> K) {
492
17381
        let node_count = self.build.len();
493
91661
        let total: usize = self.build.iter().map(|v| v.len()).sum();
494

            
495
17381
        let mut flat_data = Vec::with_capacity(total);
496
17381
        let mut offsets = Vec::with_capacity(node_count);
497

            
498
91661
        for inner in self.build.iter_mut() {
499
190383
            inner.sort_by(|a, b| key_fn(a).cmp(&key_fn(b)));
500

            
501
            // Deduplicate: keep last of each consecutive-key group (CSS cascade).
502
91661
            let n = inner.len();
503
91661
            let mut keep = vec![false; n];
504
159699
            for i in 0..n {
505
159699
                if i + 1 >= n || key_fn(&inner[i]) != key_fn(&inner[i + 1]) {
506
159599
                    keep[i] = true;
507
159599
                }
508
            }
509

            
510
91661
            let start = flat_data.len() as u32;
511
            // Drain inner and push only kept items
512
159699
            for (i, item) in inner.drain(..).enumerate() {
513
159699
                if keep[i] {
514
159599
                    flat_data.push(item);
515
159599
                }
516
            }
517

            
518
91661
            let len = (flat_data.len() as u32) - start;
519
91661
            offsets.push((start, len));
520
        }
521

            
522
17381
        flat_data.shrink_to_fit();
523
17381
        self.data = flat_data;
524
17381
        self.offsets = offsets;
525
17381
        self.build = Vec::new();
526
17381
    }
527

            
528
    /// Flatten without sorting (for data that's already sorted).
529
    pub fn flatten(&mut self) {
530
        let node_count = self.build.len();
531
        let total: usize = self.build.iter().map(|v| v.len()).sum();
532

            
533
        let mut flat_data = Vec::with_capacity(total);
534
        let mut offsets = Vec::with_capacity(node_count);
535

            
536
        for inner in self.build.iter_mut() {
537
            let start = flat_data.len() as u32;
538
            let len = inner.len() as u32;
539
            offsets.push((start, len));
540
            flat_data.append(inner);
541
        }
542

            
543
        self.data = flat_data;
544
        self.offsets = offsets;
545
        self.build = Vec::new();
546
    }
547

            
548
    /// Rebuild flat storage, keeping only items matching `predicate`.
549
    /// Must be called after flatten. Preserves per-node ordering.
550
17397
    pub fn retain(&mut self, predicate: impl Fn(&T) -> bool) where T: Clone {
551
17397
        if self.offsets.is_empty() { return; }
552
17380
        let node_count = self.offsets.len();
553
17380
        let mut new_data = Vec::new();
554
17380
        let mut new_offsets = Vec::with_capacity(node_count);
555
108941
        for &(start, len) in &self.offsets {
556
91561
            let s = start as usize;
557
91561
            let l = len as usize;
558
91561
            let new_start = new_data.len() as u32;
559
91561
            let slice = &self.data[s..s + l];
560
91561
            let mut kept = 0u32;
561
250960
            for item in slice {
562
159399
                if predicate(item) {
563
28342
                    new_data.push((*item).clone());
564
28342
                    kept += 1;
565
131057
                }
566
            }
567
91561
            new_offsets.push((new_start, kept));
568
        }
569
17380
        new_data.shrink_to_fit();
570
17380
        self.data = new_data;
571
17380
        self.offsets = new_offsets;
572
17397
    }
573

            
574
    /// Like `retain`, but passes each item's owning node index to the predicate.
575
    /// Must be called after flatten. Preserves per-node ordering.
576
    pub fn retain_with_node_index(
577
        &mut self,
578
        predicate: impl Fn(usize, &T) -> bool,
579
    ) where T: Clone {
580
        if self.offsets.is_empty() { return; }
581
        let node_count = self.offsets.len();
582
        let mut new_data = Vec::new();
583
        let mut new_offsets = Vec::with_capacity(node_count);
584
        for (node_idx, &(start, len)) in self.offsets.iter().enumerate() {
585
            let s = start as usize;
586
            let l = len as usize;
587
            let new_start = new_data.len() as u32;
588
            let slice = &self.data[s..s + l];
589
            let mut kept = 0u32;
590
            for item in slice {
591
                if predicate(node_idx, item) {
592
                    new_data.push((*item).clone());
593
                    kept += 1;
594
                }
595
            }
596
            new_offsets.push((new_start, kept));
597
        }
598
        new_data.shrink_to_fit();
599
        self.data = new_data;
600
        self.offsets = new_offsets;
601
    }
602

            
603
    /// Iterate over all nodes, yielding (node_index, &[T]) for each.
604
    /// Works in both build and flattened phases.
605
17362
    pub(crate) fn iter_node_slices(&self) -> FlatVecVecIter<'_, T> {
606
17362
        FlatVecVecIter {
607
17362
            fvv: self,
608
17362
            idx: 0,
609
17362
            count: self.len(),
610
17362
        }
611
17362
    }
612

            
613
    /// Extend this FlatVecVec with all nodes from `other` (append for DOM merge).
614
    /// Both must be in build phase, or both must be flattened.
615
408
    pub fn extend_from(&mut self, other: &mut Self) {
616
408
        if !self.offsets.is_empty() && !other.offsets.is_empty() {
617
            // Both flattened: extend flat data with offset adjustment
618
408
            let base = self.data.len() as u32;
619
408
            self.data.extend(other.data.drain(..));
620
646
            self.offsets.extend(other.offsets.drain(..).map(|(s, l)| (s + base, l)));
621
        } else {
622
            // At least one in build phase: extend build vecs
623
            self.build.extend(other.build.drain(..));
624
            // Invalidate flat data if it existed
625
            self.data.clear();
626
            self.offsets.clear();
627
        }
628
408
    }
629
}
630

            
631
/// Iterator over (node_index, &[T]) pairs from a `FlatVecVec`.
632
pub(crate) struct FlatVecVecIter<'a, T> {
633
    fvv: &'a FlatVecVec<T>,
634
    idx: usize,
635
    count: usize,
636
}
637

            
638
impl<'a, T> Iterator for FlatVecVecIter<'a, T> {
639
    type Item = (usize, &'a [T]);
640

            
641
    #[inline]
642
108828
    fn next(&mut self) -> Option<Self::Item> {
643
108828
        if self.idx >= self.count {
644
17362
            return None;
645
91466
        }
646
91466
        let i = self.idx;
647
91466
        self.idx += 1;
648
91466
        Some((i, self.fvv.get_slice(i)))
649
108828
    }
650

            
651
    fn size_hint(&self) -> (usize, Option<usize>) {
652
        let rem = self.count - self.idx;
653
        (rem, Some(rem))
654
    }
655
}
656

            
657
impl<'a, T> ExactSizeIterator for FlatVecVecIter<'a, T> {}
658

            
659
// NOTE: To avoid large memory allocations, this is a "cache" that stores all the CSS properties
660
// found in the DOM. This cache exists on a per-DOM basis, so it scales independent of how many
661
// nodes are in the DOM.
662
//
663
// If each node would carry its own CSS properties, that would unnecessarily consume memory
664
// because most nodes use the default properties or override only one or two properties.
665
//
666
// The cache can compute the property of any node at any given time, given the current node
667
// state (hover, active, focused, normal). This way we don't have to duplicate the CSS properties
668
// onto every single node and exchange them when the style changes. Two caches can be appended
669
// to each other by simply merging their NodeIds.
670
#[derive(Debug, Default, Clone, PartialEq)]
671
pub struct CssPropertyCache {
672
    // number of nodes in the current DOM
673
    pub node_count: usize,
674

            
675
    // properties that were overridden in callbacks (not specific to any node state)
676
    pub user_overridden_properties: Vec<Vec<(CssPropertyType, CssProperty)>>,
677

            
678
    // non-default CSS properties that were cascaded from the parent,
679
    // unified across all pseudo-states (Normal, Hover, Active, Focus, Dragging, DragOver).
680
    // Stored in a flat cache-friendly layout after sort_and_flatten().
681
    pub cascaded_props: FlatVecVec<StatefulCssProperty>,
682

            
683
    // non-default CSS properties that were set via a CSS file,
684
    // unified across all pseudo-states.
685
    pub css_props: FlatVecVec<StatefulCssProperty>,
686

            
687
    // Pre-resolved inherited properties (sorted Vec per node, keyed by CssPropertyType)
688
    pub computed_values: Vec<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
689

            
690
    // Compact layout cache: three-tier numeric encoding for O(1) layout lookups.
691
    // Built once after restyle + apply_ua_css + compute_inherited_values.
692
    // Non-compact properties (background, shadow, transform) use get_property_slow().
693
    pub compact_cache: Option<azul_css::compact_cache::CompactLayoutCache>,
694

            
695
    // Global CSS properties from `*` rules — shared across all nodes.
696
    // Applied during build_compact_cache_with_inheritance instead of being
697
    // cloned into each node's css_props (saves 50K×N clones).
698
    pub global_css_props: Vec<CssProperty>,
699

            
700
    /// Per-node resolved font-size, in pixels, for the `Normal`
701
    /// pseudo-state. Populated lazily on first call to
702
    /// [`crate::styled_dom::StyledDom::resolved_font_size_px`] via a
703
    /// single bottom-up DOM walk; subsequent reads are O(1) Vec
704
    /// index by `NodeId::index()`.
705
    ///
706
    /// Motivation: `get_font_size` is called ~730× per node per
707
    /// layout pass (see `AZ_PROP_COUNT=1` report — 329 629
708
    /// cascade walks on excel.html alone). Each resolution
709
    /// recursively reads the parent's font-size (for `em`) plus
710
    /// the root's font-size (for `rem`), multiplying the walk
711
    /// count. Caching the pre-resolved pixel value collapses that
712
    /// to a single `Vec<f32>` indexed lookup.
713
    pub resolved_font_sizes_px: crate::sync::OnceLock<Vec<f32>>,
714
}
715

            
716
/// Heap-size breakdown of a `CssPropertyCache`, produced by
717
/// [`CssPropertyCache::memory_breakdown`]. All values in bytes.
718
///
719
/// Primarily a diagnostic — the numbers are capacity-based and
720
/// don't chase into property-variant payloads (e.g. the `Vec`
721
/// inside a `FontFamily(...)`). Intended for "which subfield is
722
/// eating RSS" triage, not for precise accounting.
723
#[derive(Debug, Clone, Copy, Default)]
724
pub struct CssPropertyCacheBreakdown {
725
    pub node_count: usize,
726
    pub cascaded_props_bytes: usize,
727
    pub css_props_bytes: usize,
728
    pub computed_values_bytes: usize,
729
    pub user_overridden_bytes: usize,
730
    pub global_css_props_bytes: usize,
731
    pub compact_cache_bytes: usize,
732
    pub resolved_font_sizes_bytes: usize,
733
}
734

            
735
impl CssPropertyCacheBreakdown {
736
    /// Sum of all subfields.
737
    pub fn total_bytes(&self) -> usize {
738
        self.cascaded_props_bytes
739
            + self.css_props_bytes
740
            + self.computed_values_bytes
741
            + self.user_overridden_bytes
742
            + self.global_css_props_bytes
743
            + self.compact_cache_bytes
744
            + self.resolved_font_sizes_bytes
745
    }
746
}
747

            
748
impl CssPropertyCache {
749
    /// Approximate heap bytes retained by this cache, broken out by
750
    /// subfield. Used by `StyledDom::memory_breakdown` + the
751
    /// `AZ_MEM_BREAKDOWN=1` reporter. Sums capacity × element size
752
    /// for each Vec and adds a coarse allowance for the inner Vec
753
    /// headers inside `computed_values`.
754
    ///
755
    /// This is a measurement helper, not a tight bound — it doesn't
756
    /// chase into the `CssProperty` enum variants that carry their
757
    /// own `Vec`/`String` allocations (notably `FontFamily` →
758
    /// `StyleFontFamilyVec` → `Vec<StyleFontFamily>`), so the real
759
    /// heap footprint for a property-rich DOM can be 2-3× these
760
    /// numbers. Still useful for spotting gross duplication between
761
    /// the pre-compact and compact caches.
762
    pub fn memory_breakdown(&self) -> CssPropertyCacheBreakdown {
763
        let stateful_sz = core::mem::size_of::<StatefulCssProperty>();
764
        let computed_entry_sz =
765
            core::mem::size_of::<(CssPropertyType, CssPropertyWithOrigin)>();
766
        let outer_vec_sz = core::mem::size_of::<Vec<(CssPropertyType, CssPropertyWithOrigin)>>();
767

            
768
        let cascaded_bytes = self.cascaded_props.heap_bytes(stateful_sz);
769
        let css_bytes = self.css_props.heap_bytes(stateful_sz);
770

            
771
        let mut computed_bytes = self.computed_values.capacity() * outer_vec_sz;
772
        for v in &self.computed_values {
773
            computed_bytes += v.capacity() * computed_entry_sz;
774
        }
775

            
776
        let user_overridden_bytes = {
777
            let mut b = self.user_overridden_properties.capacity() * outer_vec_sz;
778
            for v in &self.user_overridden_properties {
779
                b += v.capacity()
780
                    * core::mem::size_of::<(CssPropertyType, CssProperty)>();
781
            }
782
            b
783
        };
784

            
785
        let global_bytes = self.global_css_props.capacity()
786
            * core::mem::size_of::<CssProperty>();
787

            
788
        let compact_bytes = self
789
            .compact_cache
790
            .as_ref()
791
            .map(|c| {
792
                c.tier1_enums.capacity() * 8
793
                    + c.tier2_dims.capacity() * 68
794
                    + c.tier2_cold.capacity() * 28
795
                    + c.tier2b_text.capacity() * 24
796
                    + c.prev_font_hashes.capacity() * 8
797
                    + c.font_dirty_nodes.capacity() * 8
798
            })
799
            .unwrap_or(0);
800

            
801
        let resolved_font_sizes_bytes = self
802
            .resolved_font_sizes_px
803
            .get()
804
            .map(|v| v.capacity() * core::mem::size_of::<f32>())
805
            .unwrap_or(0);
806

            
807
        CssPropertyCacheBreakdown {
808
            node_count: self.node_count,
809
            cascaded_props_bytes: cascaded_bytes,
810
            css_props_bytes: css_bytes,
811
            computed_values_bytes: computed_bytes,
812
            user_overridden_bytes,
813
            global_css_props_bytes: global_bytes,
814
            compact_cache_bytes: compact_bytes,
815
            resolved_font_sizes_bytes,
816
        }
817
    }
818

            
819
    /// Drop Normal-state properties that have compact encodings from
820
    /// `css_props` and `cascaded_props`. After `build_compact_cache_with_inheritance`,
821
    /// these are redundant — the compact cache is the source of truth for layout.
822
    /// Non-Normal entries (hover/active/focus) and non-compact properties
823
    /// (background, box-shadow, transform, etc.) are kept for `get_property_slow`.
824
8698
    pub fn prune_compact_normal_props(&mut self) {
825
        use azul_css::dynamic_selector::PseudoStateType;
826

            
827
        #[cfg(feature = "std")]
828
        {
829
        static PRUNE_DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
830
8698
        let dbg = *PRUNE_DBG.get_or_init(crate::profile::memory_enabled);
831
8698
        if dbg {
832
            let mut normal_compact = 0usize;
833
            let mut normal_noncompact = 0usize;
834
            let mut nonnormal = 0usize;
835
            for i in 0..self.css_props.len() {
836
                for p in self.css_props.get_slice(i) {
837
                    if p.state != PseudoStateType::Normal {
838
                        nonnormal += 1;
839
                    } else if p.prop_type.has_compact_encoding() {
840
                        normal_compact += 1;
841
                    } else {
842
                        normal_noncompact += 1;
843
                    }
844
                }
845
            }
846
            let ssp_sz = core::mem::size_of::<StatefulCssProperty>();
847
            let mut casc_normal_compact = 0usize;
848
            let mut casc_total = 0usize;
849
            for i in 0..self.cascaded_props.len() {
850
                for p in self.cascaded_props.get_slice(i) {
851
                    casc_total += 1;
852
                    if p.state == PseudoStateType::Normal && p.prop_type.has_compact_encoding() {
853
                        casc_normal_compact += 1;
854
                    }
855
                }
856
            }
857
            eprintln!("[PRUNE] css_props: norm+compact={} norm+other={} nonnorm={} SSP={}B | cascaded: total={} norm+compact={}",
858
                normal_compact, normal_noncompact, nonnormal, ssp_sz, casc_total, casc_normal_compact);
859
8698
        }
860
        }
861

            
862
        // The compact cache stores SENTINEL for pixel-valued properties whose inner
863
        // value is Exact with a non-px metric (vh, vw, %, em, rem, calc(), ...).
864
        // Those need the slow `css_props` walk at layout time because the compact
865
        // cache has nothing usable. We must keep them here or the slow path falls
866
        // back to UA CSS and silently clobbers the author's rule.
867
158399
        let keep = |p: &StatefulCssProperty| -> bool {
868
158399
            if p.state != PseudoStateType::Normal {
869
                return true;
870
158399
            }
871
158399
            if !p.prop_type.has_compact_encoding() {
872
21882
                return true;
873
136517
            }
874
            // Compact-encoded AND Normal: drop only if the compact cache fully
875
            // captured the value (px metric, or Auto/Initial/Inherit/None).
876
136517
            if property_needs_slow_path_after_compact(&p.property) {
877
5960
                return true;
878
130557
            }
879
130557
            false
880
158399
        };
881
8698
        self.css_props.retain(keep);
882
8698
        if !self.cascaded_props.is_flattened() {
883
332625
            self.cascaded_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
884
        }
885
8698
        self.cascaded_props.retain(keep);
886
8698
    }
887

            
888
    /// Look up a CSS property for a specific pseudo-state in a stateful property vec.
889
    /// Requires the vec to be sorted by (state, prop_type).
890
    #[inline]
891
1576985
    fn find_in_stateful<'a>(
892
1576985
        props: &'a [StatefulCssProperty],
893
1576985
        state: azul_css::dynamic_selector::PseudoStateType,
894
1576985
        prop_type: &CssPropertyType,
895
1576985
    ) -> Option<&'a CssProperty> {
896
1576985
        let key = (state, *prop_type);
897
1576985
        props.binary_search_by_key(&key, |p| (p.state, p.prop_type))
898
1576985
            .ok()
899
1576985
            .map(|idx| &props[idx].property)
900
1576985
    }
901

            
902
    /// Check if any properties exist for a specific pseudo-state in a stateful property vec.
903
    /// Requires the vec to be sorted by (state, prop_type).
904
    #[inline]
905
446926
    fn has_state_props(
906
446926
        props: &[StatefulCssProperty],
907
446926
        state: azul_css::dynamic_selector::PseudoStateType,
908
446926
    ) -> bool {
909
        // All entries with the same state are contiguous. Use partition_point
910
        // to find the first entry >= state, then check if it matches.
911
446946
        let i = props.partition_point(|p| p.state < state);
912
446926
        i < props.len() && props[i].state == state
913
446926
    }
914

            
915
    /// Collect all property types for a specific pseudo-state.
916
850
    pub(crate) fn prop_types_for_state<'a>(
917
850
        props: &'a [StatefulCssProperty],
918
850
        state: azul_css::dynamic_selector::PseudoStateType,
919
850
    ) -> impl Iterator<Item = &'a CssPropertyType> + 'a {
920
850
        props.iter().filter(move |p| p.state == state).map(|p| &p.prop_type)
921
850
    }
922
}
923

            
924
/// Returns true if `prop`'s value cannot be fully represented in the compact
925
/// cache and therefore needs to survive `prune_compact_normal_props` so the
926
/// slow `css_props` walk can still find it at layout time.
927
///
928
/// Pixel-valued properties (margin, padding, width, height, ...) are the only
929
/// case: `Exact(pv)` with `pv.metric != Px` (vh, vw, %, em, rem, ...) encodes
930
/// to the compact cache's SENTINEL slot, which loses the value. All other
931
/// compact-encoded types (tier1 enums, colors, hashes, etc.) always round-trip
932
/// through the compact encoding.
933
136517
fn property_needs_slow_path_after_compact(prop: &CssProperty) -> bool {
934
    use azul_css::css::CssPropertyValue;
935
    use azul_css::props::{
936
        basic::length::SizeMetric,
937
        layout::{
938
            dimensions::{LayoutHeight, LayoutWidth},
939
            flex::LayoutFlexBasis,
940
        },
941
    };
942

            
943
    // `inner: PixelValue` wrapper types — check metric directly.
944
    macro_rules! check_plain {
945
        ($v:expr) => {{
946
            if let CssPropertyValue::Exact(ref inner) = $v {
947
                return inner.inner.metric != SizeMetric::Px;
948
            }
949
            false
950
        }};
951
    }
952

            
953
136517
    match prop {
954
        // LayoutWidth / LayoutHeight: enum with `Px(PixelValue)` variant.
955
        // Non-pixel variants (Auto / MinContent / MaxContent / FitContent / Calc)
956
        // are already handled by the tier1 fast path or don't exist as i16 dims.
957
5370
        CssProperty::Width(v) => {
958
5286
            if let CssPropertyValue::Exact(LayoutWidth::Px(pv)) = v {
959
5269
                return pv.metric != SizeMetric::Px;
960
101
            }
961
101
            false
962
        }
963
9702
        CssProperty::Height(v) => {
964
9702
            if let CssPropertyValue::Exact(LayoutHeight::Px(pv)) = v {
965
9702
                return pv.metric != SizeMetric::Px;
966
            }
967
            false
968
        }
969

            
970
        // LayoutFlexBasis: enum with `Exact(PixelValue)` variant.
971
        CssProperty::FlexBasis(v) => {
972
            if let CssPropertyValue::Exact(LayoutFlexBasis::Exact(pv)) = v {
973
                return pv.metric != SizeMetric::Px;
974
            }
975
            false
976
        }
977

            
978
        // `inner: PixelValue` wrappers
979
        CssProperty::MinWidth(v) => check_plain!(v),
980
42
        CssProperty::MaxWidth(v) => check_plain!(v),
981
336
        CssProperty::MinHeight(v) => check_plain!(v),
982
        CssProperty::MaxHeight(v) => check_plain!(v),
983
4494
        CssProperty::FontSize(v) => check_plain!(v),
984
5193
        CssProperty::PaddingTop(v) => check_plain!(v),
985
5109
        CssProperty::PaddingRight(v) => check_plain!(v),
986
5109
        CssProperty::PaddingBottom(v) => check_plain!(v),
987
5109
        CssProperty::PaddingLeft(v) => check_plain!(v),
988
8750
        CssProperty::MarginTop(v) => check_plain!(v),
989
6947
        CssProperty::MarginRight(v) => check_plain!(v),
990
8330
        CssProperty::MarginBottom(v) => check_plain!(v),
991
6947
        CssProperty::MarginLeft(v) => check_plain!(v),
992
496
        CssProperty::BorderTopWidth(v) => check_plain!(v),
993
454
        CssProperty::BorderRightWidth(v) => check_plain!(v),
994
454
        CssProperty::BorderBottomWidth(v) => check_plain!(v),
995
454
        CssProperty::BorderLeftWidth(v) => check_plain!(v),
996
59
        CssProperty::Top(v) => check_plain!(v),
997
59
        CssProperty::Right(v) => check_plain!(v),
998
17
        CssProperty::Bottom(v) => check_plain!(v),
999
17
        CssProperty::Left(v) => check_plain!(v),
        CssProperty::ColumnGap(v) => check_plain!(v),
        CssProperty::RowGap(v) => check_plain!(v),
        CssProperty::LetterSpacing(v) => check_plain!(v),
        CssProperty::WordSpacing(v) => check_plain!(v),
        CssProperty::TextIndent(v) => check_plain!(v),
        CssProperty::TabSize(v) => check_plain!(v),
        // All other compact-encoded types round-trip through the compact cache.
63069
        _ => false,
    }
136517
}
impl CssPropertyCache {
    /// Match CSS selectors to nodes and populate css_props.
    /// Returns tag IDs for hit-testing. If compact_cache is available,
    /// uses it for fast display/overflow checks; otherwise falls back to slow path.
    #[must_use]
8681
    pub fn restyle(
8681
        &mut self,
8681
        css: &mut Css,
8681
        node_data: &NodeDataContainerRef<NodeData>,
8681
        node_hierarchy: &NodeHierarchyItemVec,
8681
        non_leaf_nodes: &ParentWithNodeDepthVec,
8681
        html_tree: &NodeDataContainerRef<CascadeInfo>,
8681
    ) -> Vec<TagIdToNodeIdMapping> {
        use azul_css::{
            css::{CssDeclaration, CssPathPseudoSelector::*},
            props::layout::LayoutDisplay,
        };
8681
        let css_is_empty = css.is_empty();
8681
        if !css_is_empty {
5476
            css.sort_by_specificity();
            // Separate CSS rules into "global only" (just `*`) vs "has specific selector".
            // Global-only rules apply to ALL nodes — push directly into css_props
            // without per-node selector matching (avoids m×n for these rules).
            // Specific rules still go through matches_html_element per-node.
            use azul_css::css::{CssPathSelector, CssRuleBlock};
5476
            let mut global_only_rules: Vec<&CssRuleBlock> = Vec::new();
5476
            let mut specific_rules: Vec<&CssRuleBlock> = Vec::new();
13399
            for rule in css.rules() {
13399
                let selectors = rule.path.selectors.as_ref();
13399
                let is_global_only = selectors.len() == 1
13357
                    && matches!(selectors.first(), Some(CssPathSelector::Global));
13399
                if is_global_only {
2655
                    global_only_rules.push(rule);
10744
                } else {
10744
                    specific_rules.push(rule);
10744
                }
            }
            // Clear all css_props before assigning
25863
            for entry in self.css_props.build_iter_mut() { entry.clear(); }
            use azul_css::dynamic_selector::PseudoStateType;
            // Collect global-only rule declarations ONCE (not per-node).
            // These are stored in self.global_css_props and applied during
            // build_compact_cache_with_inheritance for each node, avoiding
            // 50K × N clones into per-node css_props Vecs.
5476
            self.global_css_props.clear();
8131
            for rule in &global_only_rules {
2655
                if crate::style::rule_ends_with(&rule.path, None) {
18122
                    for d in rule.declarations.iter() {
18122
                        if let CssDeclaration::Static(s) = d {
18122
                            self.global_css_props.push(s.clone());
18122
                        }
                    }
                }
            }
            // Phase 2: Match specific rules per-node (only non-global rules)
5476
            if !specific_rules.is_empty() {
            // Per-node "which declarations match" lists are built as
            // `(rule_idx, decl_idx)` pairs — 4 bytes per entry instead of
            // cloning a 140-byte `CssProperty`. The clone only happens at
            // the final push_to step, so the transient peak is ~35× smaller.
            //
            // rule_idx indexes into `specific_rules` (Vec<&CssRuleBlock>),
            // decl_idx indexes into `rule.declarations.as_slice()`. Both
            // fit in u16 since real stylesheets have far fewer than 65k
            // rules and declarations per rule.
            macro_rules! filter_rules {($expected_pseudo_selector:expr, $node_id:expr) => {{
                let mut out: Vec<(u16, u16)> = Vec::new();
                for (rule_idx, rule_block) in specific_rules.iter().enumerate() {
                    if !crate::style::rule_ends_with(&rule_block.path, $expected_pseudo_selector) {
                        continue;
                    }
                    if !crate::style::matches_html_element(
                        &rule_block.path,
                        $node_id,
                        &node_hierarchy.as_container(),
                        &node_data,
                        &html_tree,
                        $expected_pseudo_selector,
                    ) {
                        continue;
                    }
                    for (decl_idx, decl) in rule_block.declarations.as_slice().iter().enumerate() {
                        if matches!(decl, CssDeclaration::Static(_)) {
                            out.push((rule_idx as u16, decl_idx as u16));
                        }
                    }
                }
                out
            }};}
            // Pre-check which pseudo-states have any matching rules at all.
            // This avoids iterating 50K nodes for pseudo-states with zero rules
            // (common: most stylesheets have no :hover/:focus/:active rules).
5400
            let has_normal = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, None));
10744
            let has_hover = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Hover)));
10744
            let has_active = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Active)));
10744
            let has_focus = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Focus)));
10744
            let has_dragging = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Dragging)));
10744
            let has_drag_over = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(DragOver)));
            macro_rules! collect_and_assign {
                ($pseudo:expr, $state:expr, $has_any:expr) => {
                    if $has_any {
                        let indices: NodeDataContainer<(NodeId, Vec<(u16, u16)>)> = node_data
25635
                            .transform_nodeid_optional(|node_id| {
62752
                                let r = filter_rules!($pseudo, node_id);
25635
                                if r.is_empty() { None } else { Some((node_id, r)) }
25635
                            });
                        for (n, pairs) in indices.internal.into_iter() {
                            for (rule_idx, decl_idx) in pairs {
                                let decl = &specific_rules[rule_idx as usize]
                                    .declarations
                                    .as_slice()[decl_idx as usize];
                                if let CssDeclaration::Static(prop) = decl {
                                    self.css_props.push_to(n.index(), StatefulCssProperty {
                                        state: $state,
                                        prop_type: prop.get_type(),
                                        property: prop.clone(),
                                    });
                                }
                            }
                        }
                    }
                };
            }
47388
            collect_and_assign!(None, PseudoStateType::Normal, has_normal);
5400
            collect_and_assign!(Some(Hover), PseudoStateType::Hover, has_hover);
5400
            collect_and_assign!(Some(Active), PseudoStateType::Active, has_active);
5400
            collect_and_assign!(Some(Focus), PseudoStateType::Focus, has_focus);
5400
            collect_and_assign!(Some(Dragging), PseudoStateType::Dragging, has_dragging);
5400
            collect_and_assign!(Some(DragOver), PseudoStateType::DragOver, has_drag_over);
76
            } // end if !specific_rules.is_empty()
3205
        }
        // Inheritance: Inherit all values of the parent to the children, but
        // only if the property is inheritable and isn't yet set
27363
        for ParentWithNodeDepth { depth: _, node_id } in non_leaf_nodes.iter() {
27363
            let parent_id = match node_id.into_crate_internal() {
27363
                Some(s) => s,
                None => continue,
            };
            use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
27363
            let all_states = [
27363
                PseudoStateType::Normal,
27363
                PseudoStateType::Hover,
27363
                PseudoStateType::Active,
27363
                PseudoStateType::Focus,
27363
                PseudoStateType::Dragging,
27363
                PseudoStateType::DragOver,
27363
            ];
191541
            for &state in &all_states {
                // 1. Inherit inline CSS properties from parent for this pseudo-state
164178
                let parent_inheritable_inline: Vec<(CssPropertyType, CssProperty)> = node_data[parent_id]
164178
                    .style
164178
                    .iter_inline_properties()
164178
                    .filter(|(_prop, conds)| {
3468
                        let conditions = conds.as_slice();
3468
                        if conditions.is_empty() {
3468
                            state == PseudoStateType::Normal
                        } else {
                            conditions.iter().all(|c| {
                                matches!(c, DynamicSelector::PseudoState(s) if *s == state)
                            })
                        }
3468
                    })
164178
                    .map(|(prop, _)| prop)
164178
                    .filter(|prop| prop.get_type().is_inheritable())
164178
                    .map(|p| (p.get_type(), p.clone()))
164178
                    .collect();
                // 2. Inherit CSS stylesheet properties from parent for this pseudo-state
164178
                let parent_inheritable_css: Vec<(CssPropertyType, CssProperty)> = if !css_is_empty {
79830
                    self.css_props.get_slice(parent_id.index())
79830
                        .iter()
201336
                        .filter(|p| p.state == state && p.prop_type.is_inheritable())
79830
                        .map(|p| (p.prop_type, p.property.clone()))
79830
                        .collect()
                } else {
84348
                    Vec::new()
                };
                // 3. Inherit cascaded properties from parent for this pseudo-state
164178
                let parent_inheritable_cascaded: Vec<(CssPropertyType, CssProperty)> =
164178
                    self.cascaded_props.get_slice(parent_id.index())
164178
                        .iter()
164178
                        .filter(|p| p.state == state && p.prop_type.is_inheritable())
164178
                        .map(|p| (p.prop_type, p.property.clone()))
164178
                        .collect();
                // Combine all inheritable props (inline first = strongest, cascaded last)
                // Only insert if child doesn't already have that (state, prop_type) combo
164178
                if parent_inheritable_inline.is_empty()
163668
                    && parent_inheritable_css.is_empty()
161129
                    && parent_inheritable_cascaded.is_empty()
                {
159806
                    continue;
4372
                }
4582
                for child_id in parent_id.az_children(&node_hierarchy.as_container()) {
4565
                    let child_vec = self.cascaded_props.build_mut(child_id.index());
7667
                    for (prop_type, prop_value) in parent_inheritable_inline
4565
                        .iter()
4565
                        .chain(parent_inheritable_css.iter())
4565
                        .chain(parent_inheritable_cascaded.iter())
                    {
                        // or_insert: only insert if child doesn't already have this (state, prop_type)
7667
                        if !child_vec.iter().any(|p| p.state == state && p.prop_type == *prop_type) {
7077
                            child_vec.push(StatefulCssProperty {
7077
                                state,
7077
                                prop_type: *prop_type,
7077
                                property: prop_value.clone(),
7077
                            });
7077
                        }
                    }
                }
            }
        }
        // Sort css_props by (state, prop_type) for binary search lookups,
        // then flatten into contiguous memory for cache-friendly reads.
249188
        self.css_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
8681
        self.generate_tag_ids(node_data, node_hierarchy)
8681
    }
    /// Generate hit-test tag IDs for nodes that need event handling.
    /// Uses compact cache (if available) for fast display/overflow reads.
    /// Can be called separately after build_compact_cache_with_inheritance.
17362
    pub fn generate_tag_ids(
17362
        &self,
17362
        node_data: &NodeDataContainerRef<NodeData>,
17362
        node_hierarchy: &NodeHierarchyItemVec,
17362
    ) -> Vec<TagIdToNodeIdMapping> {
        // Tag ID generation: determine which nodes need hit-test tags for
        // hover/click/scroll events. Uses compact cache for display/overflow
        // checks instead of get_property_slow (which searches 6 data structures).
        use azul_css::compact_cache::{
            DISPLAY_SHIFT, DISPLAY_MASK,
            OVERFLOW_X_SHIFT, OVERFLOW_Y_SHIFT, OVERFLOW_MASK,
        };
17362
        let compact_cache = self.compact_cache.as_ref();
17362
        let node_data_container = &node_data.internal;
17362
        let tag_ids = node_data
17362
            .internal
17362
            .iter()
17362
            .enumerate()
91466
            .filter_map(|(node_idx, node_data)| {
91466
                let node_id = NodeId::new(node_idx);
91466
                let should_auto_insert_tabindex = node_data
91466
                    .get_callbacks()
91466
                    .iter()
91466
                    .any(|cb| cb.event.is_focus_callback());
91466
                let tab_index = match node_data.get_tab_index() {
672
                    Some(s) => Some(s),
                    None => {
90794
                        if should_auto_insert_tabindex {
                            Some(TabIndex::Auto)
                        } else {
90794
                            None
                        }
                    }
                };
91466
                let mut need_tag = false;
                loop {
                    // display:none check — read directly from compact tier1 (fast u64 read)
91466
                    if let Some(cc) = compact_cache.as_ref() {
45733
                        let t1 = cc.tier1_enums[node_idx];
45733
                        let display_val = ((t1 >> DISPLAY_SHIFT) & DISPLAY_MASK) as u8;
45733
                        if display_val == 4 { break; } // 4 = LayoutDisplay::None (new encoding)
45733
                    }
91424
                    if node_data.has_context_menu() || node_data.get_context_menu().is_some() {
                        need_tag = true; break;
91424
                    }
91424
                    if tab_index.is_some() { need_tag = true; break; }
                    // Pseudo-state property checks (hover/active/focus/dragging/drag-over)
                    {
                        use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
448320
                        let has_pseudo = |state: PseudoStateType| -> bool {
448320
                            node_data.style.iter_inline_properties().any(|(_p, conds)| {
10166
                                conds.as_slice().iter().any(|c|
2856
                                    matches!(c, DynamicSelector::PseudoState(s) if *s == state)
                                )
446926
                            }) || Self::has_state_props(self.css_props.get_slice(node_idx), state)
448320
                        };
90752
                        if has_pseudo(PseudoStateType::Hover)
89426
                            || has_pseudo(PseudoStateType::Active)
89426
                            || has_pseudo(PseudoStateType::Focus)
89358
                            || has_pseudo(PseudoStateType::Dragging)
89358
                            || has_pseudo(PseudoStateType::DragOver)
                        {
1394
                            need_tag = true; break;
89358
                        }
                    }
                    // Non-window callbacks
89358
                    let has_non_window_cb = !node_data.get_callbacks().is_empty()
                        && !node_data.get_callbacks().iter().all(|cb| cb.event.is_window_callback());
89358
                    if has_non_window_cb { need_tag = true; break; }
                    // Cursor check — read from cached css_props or inline style.
89358
                    if self.css_props.get_slice(node_idx).iter().any(|p|
44887
                        p.state == azul_css::dynamic_selector::PseudoStateType::Normal
44887
                        && p.prop_type == azul_css::props::property::CssPropertyType::Cursor
89358
                    ) || node_data.style.iter_inline_properties().any(|(p, _)|
1156
                        p.get_type() == azul_css::props::property::CssPropertyType::Cursor
                    ) {
                        need_tag = true; break;
89358
                    }
                    // Overflow scroll check — read from compact tier1
89358
                    if let Some(cc) = compact_cache.as_ref() {
44658
                        let t1 = cc.tier1_enums[node_idx];
44658
                        let ox = ((t1 >> OVERFLOW_X_SHIFT) & OVERFLOW_MASK) as u8;
44658
                        let oy = ((t1 >> OVERFLOW_Y_SHIFT) & OVERFLOW_MASK) as u8;
                        // 2 = Scroll, 3 = Auto in layout_overflow_to_u8 (new encoding)
44658
                        if ox == 2 || ox == 3 || oy == 2 || oy == 3 {
                            need_tag = true; break;
44658
                        }
44700
                    }
                    // Selectable text check
                    {
                        use crate::dom::NodeType;
89358
                        let hier = node_hierarchy.as_container()[node_id];
89358
                        let mut has_text = false;
89358
                        if let Some(first_child) = hier.first_child_id(node_id) {
51730
                            let mut child_id = Some(first_child);
92748
                            while let Some(cid) = child_id {
58092
                                if matches!(node_data_container[cid.index()].get_node_type(), NodeType::Text(_)) {
17074
                                    has_text = true; break;
41018
                                }
41018
                                child_id = node_hierarchy.as_container()[cid].next_sibling_id();
                            }
37628
                        }
89358
                        if has_text { need_tag = true; break; }
                    }
72284
                    break;
                }
91466
                if !need_tag {
72326
                    None
                } else {
19140
                    Some(TagIdToNodeIdMapping {
19140
                        tag_id: TagId::from_crate_internal(TagId::unique()),
19140
                        node_id: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
19140
                        tab_index: tab_index.into(),
19140
                    })
                }
91466
            })
17362
            .collect::<Vec<_>>();
17362
        tag_ids
17362
    }
    pub fn get_computed_css_style_string(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> String {
        let mut s = String::new();
        if let Some(p) = self.get_background_content(&node_data, node_id, node_state) {
            s.push_str(&format!("background: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_background_position(&node_data, node_id, node_state) {
            s.push_str(&format!("background-position: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_background_size(&node_data, node_id, node_state) {
            s.push_str(&format!("background-size: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_background_repeat(&node_data, node_id, node_state) {
            s.push_str(&format!("background-repeat: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_font_size(&node_data, node_id, node_state) {
            s.push_str(&format!("font-size: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_font_family(&node_data, node_id, node_state) {
            s.push_str(&format!("font-family: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_text_color(&node_data, node_id, node_state) {
            s.push_str(&format!("color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_text_align(&node_data, node_id, node_state) {
            s.push_str(&format!("text-align: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_line_height(&node_data, node_id, node_state) {
            s.push_str(&format!("line-height: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_letter_spacing(&node_data, node_id, node_state) {
            s.push_str(&format!("letter-spacing: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_word_spacing(&node_data, node_id, node_state) {
            s.push_str(&format!("word-spacing: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_tab_size(&node_data, node_id, node_state) {
            s.push_str(&format!("tab-size: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_cursor(&node_data, node_id, node_state) {
            s.push_str(&format!("cursor: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_box_shadow_left(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "-azul-box-shadow-left: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_box_shadow_right(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "-azul-box-shadow-right: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_box_shadow_top(&node_data, node_id, node_state) {
            s.push_str(&format!("-azul-box-shadow-top: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_box_shadow_bottom(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "-azul-box-shadow-bottom: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_border_top_color(&node_data, node_id, node_state) {
            s.push_str(&format!("border-top-color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_left_color(&node_data, node_id, node_state) {
            s.push_str(&format!("border-left-color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_right_color(&node_data, node_id, node_state) {
            s.push_str(&format!("border-right-color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_bottom_color(&node_data, node_id, node_state) {
            s.push_str(&format!("border-bottom-color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_top_style(&node_data, node_id, node_state) {
            s.push_str(&format!("border-top-style: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_left_style(&node_data, node_id, node_state) {
            s.push_str(&format!("border-left-style: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_right_style(&node_data, node_id, node_state) {
            s.push_str(&format!("border-right-style: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_bottom_style(&node_data, node_id, node_state) {
            s.push_str(&format!("border-bottom-style: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_top_left_radius(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "border-top-left-radius: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_border_top_right_radius(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "border-top-right-radius: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_border_bottom_left_radius(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "border-bottom-left-radius: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_border_bottom_right_radius(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "border-bottom-right-radius: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_opacity(&node_data, node_id, node_state) {
            s.push_str(&format!("opacity: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_transform(&node_data, node_id, node_state) {
            s.push_str(&format!("transform: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_transform_origin(&node_data, node_id, node_state) {
            s.push_str(&format!("transform-origin: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_perspective_origin(&node_data, node_id, node_state) {
            s.push_str(&format!("perspective-origin: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_backface_visibility(&node_data, node_id, node_state) {
            s.push_str(&format!("backface-visibility: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_hyphens(&node_data, node_id, node_state) {
            s.push_str(&format!("hyphens: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_direction(&node_data, node_id, node_state) {
            s.push_str(&format!("direction: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_unicode_bidi(&node_data, node_id, node_state) {
            s.push_str(&format!("unicode-bidi: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_text_box_trim(&node_data, node_id, node_state) {
            s.push_str(&format!("text-box-trim: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_text_box_edge(&node_data, node_id, node_state) {
            s.push_str(&format!("text-box-edge: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_dominant_baseline(&node_data, node_id, node_state) {
            s.push_str(&format!("dominant-baseline: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_alignment_baseline(&node_data, node_id, node_state) {
            s.push_str(&format!("alignment-baseline: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_initial_letter_align(&node_data, node_id, node_state) {
            s.push_str(&format!("initial-letter-align: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_initial_letter_wrap(&node_data, node_id, node_state) {
            s.push_str(&format!("initial-letter-wrap: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_scrollbar_gutter(&node_data, node_id, node_state) {
            s.push_str(&format!("scrollbar-gutter: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_overflow_clip_margin(&node_data, node_id, node_state) {
            s.push_str(&format!("overflow-clip-margin: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_clip(&node_data, node_id, node_state) {
            s.push_str(&format!("clip: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_white_space(&node_data, node_id, node_state) {
            s.push_str(&format!("white-space: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_display(&node_data, node_id, node_state) {
            s.push_str(&format!("display: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_float(&node_data, node_id, node_state) {
            s.push_str(&format!("float: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_box_sizing(&node_data, node_id, node_state) {
            s.push_str(&format!("box-sizing: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_width(&node_data, node_id, node_state) {
            s.push_str(&format!("width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_height(&node_data, node_id, node_state) {
            s.push_str(&format!("height: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_min_width(&node_data, node_id, node_state) {
            s.push_str(&format!("min-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_min_height(&node_data, node_id, node_state) {
            s.push_str(&format!("min-height: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_max_width(&node_data, node_id, node_state) {
            s.push_str(&format!("max-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_max_height(&node_data, node_id, node_state) {
            s.push_str(&format!("max-height: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_position(&node_data, node_id, node_state) {
            s.push_str(&format!("position: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_top(&node_data, node_id, node_state) {
            s.push_str(&format!("top: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_bottom(&node_data, node_id, node_state) {
            s.push_str(&format!("bottom: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_right(&node_data, node_id, node_state) {
            s.push_str(&format!("right: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_left(&node_data, node_id, node_state) {
            s.push_str(&format!("left: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_padding_top(&node_data, node_id, node_state) {
            s.push_str(&format!("padding-top: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_padding_bottom(&node_data, node_id, node_state) {
            s.push_str(&format!("padding-bottom: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_padding_left(&node_data, node_id, node_state) {
            s.push_str(&format!("padding-left: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_padding_right(&node_data, node_id, node_state) {
            s.push_str(&format!("padding-right: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_margin_top(&node_data, node_id, node_state) {
            s.push_str(&format!("margin-top: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_margin_bottom(&node_data, node_id, node_state) {
            s.push_str(&format!("margin-bottom: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_margin_left(&node_data, node_id, node_state) {
            s.push_str(&format!("margin-left: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_margin_right(&node_data, node_id, node_state) {
            s.push_str(&format!("margin-right: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_top_width(&node_data, node_id, node_state) {
            s.push_str(&format!("border-top-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_left_width(&node_data, node_id, node_state) {
            s.push_str(&format!("border-left-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_right_width(&node_data, node_id, node_state) {
            s.push_str(&format!("border-right-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_bottom_width(&node_data, node_id, node_state) {
            s.push_str(&format!("border-bottom-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_overflow_x(&node_data, node_id, node_state) {
            s.push_str(&format!("overflow-x: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_overflow_y(&node_data, node_id, node_state) {
            s.push_str(&format!("overflow-y: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_flex_direction(&node_data, node_id, node_state) {
            s.push_str(&format!("flex-direction: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_flex_wrap(&node_data, node_id, node_state) {
            s.push_str(&format!("flex-wrap: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_flex_grow(&node_data, node_id, node_state) {
            s.push_str(&format!("flex-grow: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_flex_shrink(&node_data, node_id, node_state) {
            s.push_str(&format!("flex-shrink: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_justify_content(&node_data, node_id, node_state) {
            s.push_str(&format!("justify-content: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_align_items(&node_data, node_id, node_state) {
            s.push_str(&format!("align-items: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_align_content(&node_data, node_id, node_state) {
            s.push_str(&format!("align-content: {};", p.get_css_value_fmt()));
        }
        s
    }
}
#[repr(C)]
#[derive(Debug, PartialEq, Clone)]
pub struct CssPropertyCachePtr {
    pub ptr: Box<CssPropertyCache>,
    pub run_destructor: bool,
}
impl CssPropertyCachePtr {
8723
    pub fn new(cache: CssPropertyCache) -> Self {
8723
        Self {
8723
            ptr: Box::new(cache),
8723
            run_destructor: true,
8723
        }
8723
    }
3
    pub fn downcast_mut<'a>(&'a mut self) -> &'a mut CssPropertyCache {
3
        &mut *self.ptr
3
    }
}
impl Drop for CssPropertyCachePtr {
8723
    fn drop(&mut self) {
8723
        self.run_destructor = false;
8723
    }
}
impl CssPropertyCache {
8774
    pub fn empty(node_count: usize) -> Self {
8774
        Self {
8774
            node_count,
8774
            user_overridden_properties: Vec::new(),
8774

            
8774
            cascaded_props: FlatVecVec::new(node_count),
8774
            css_props: FlatVecVec::new(node_count),
8774

            
8774
            computed_values: Vec::new(),
8774
            compact_cache: None,
8774
            global_css_props: Vec::new(),
8774
            resolved_font_sizes_px: crate::sync::OnceLock::new(),
8774
        }
8774
    }
    /// Clear the lazily-populated font-size cache. Call after any
    /// mutation that could change resolved font-sizes (restyle,
    /// DOM mutation, `append`, etc.). The next
    /// [`crate::styled_dom::StyledDom::resolved_font_size_px`] call
    /// repopulates via a single bottom-up tree walk.
    pub fn invalidate_resolved_font_sizes(&mut self) {
        self.resolved_font_sizes_px = crate::sync::OnceLock::new();
    }
204
    pub fn append(&mut self, other: &mut Self) {
204
        self.user_overridden_properties.extend(other.user_overridden_properties.drain(..));
204
        self.cascaded_props.extend_from(&mut other.cascaded_props);
204
        self.css_props.extend_from(&mut other.css_props);
204
        self.computed_values.extend(other.computed_values.drain(..));
204
        self.node_count += other.node_count;
        // Indices shifted — invalidate the font-size cache too.
204
        self.resolved_font_sizes_px = crate::sync::OnceLock::new();
        // Invalidate compact cache since node IDs shifted
204
        self.compact_cache = None;
204
    }
    pub fn is_horizontal_overflow_visible(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_overflow_x(node_data, node_id, node_state)
            .and_then(|p| p.get_property_or_default())
            .unwrap_or_default()
            .is_overflow_visible()
    }
    pub fn is_vertical_overflow_visible(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_overflow_y(node_data, node_id, node_state)
            .and_then(|p| p.get_property_or_default())
            .unwrap_or_default()
            .is_overflow_visible()
    }
    pub fn is_horizontal_overflow_hidden(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_overflow_x(node_data, node_id, node_state)
            .and_then(|p| p.get_property_or_default())
            .unwrap_or_default()
            .is_overflow_hidden()
    }
    pub fn is_vertical_overflow_hidden(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_overflow_y(node_data, node_id, node_state)
            .and_then(|p| p.get_property_or_default())
            .unwrap_or_default()
            .is_overflow_hidden()
    }
    pub fn get_text_color_or_default(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> StyleTextColor {
        use azul_css::defaults::DEFAULT_TEXT_COLOR;
        self.get_text_color(node_data, node_id, node_state)
            .and_then(|fs| fs.get_property().cloned())
            .unwrap_or(DEFAULT_TEXT_COLOR)
    }
    /// Returns the font family of the node, or the default font family if none is set.
    pub fn get_font_id_or_default(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> StyleFontFamilyVec {
        use azul_css::defaults::DEFAULT_FONT_ID;
        let default_font_id = vec![StyleFontFamily::System(AzString::from_const_str(
            DEFAULT_FONT_ID,
        ))]
        .into();
        let font_family_opt = self.get_font_family(node_data, node_id, node_state);
        font_family_opt
            .as_ref()
            .and_then(|family| Some(family.get_property()?.clone()))
            .unwrap_or(default_font_id)
    }
    pub fn get_font_size_or_default(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> StyleFontSize {
        use azul_css::defaults::DEFAULT_FONT_SIZE;
        self.get_font_size(node_data, node_id, node_state)
            .and_then(|fs| fs.get_property().cloned())
            .unwrap_or(DEFAULT_FONT_SIZE)
    }
    pub fn has_border(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_border_left_width(node_data, node_id, node_state)
            .is_some()
            || self
                .get_border_right_width(node_data, node_id, node_state)
                .is_some()
            || self
                .get_border_top_width(node_data, node_id, node_state)
                .is_some()
            || self
                .get_border_bottom_width(node_data, node_id, node_state)
                .is_some()
    }
    pub fn has_box_shadow(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_box_shadow_left(node_data, node_id, node_state)
            .is_some()
            || self
                .get_box_shadow_right(node_data, node_id, node_state)
                .is_some()
            || self
                .get_box_shadow_top(node_data, node_id, node_state)
                .is_some()
            || self
                .get_box_shadow_bottom(node_data, node_id, node_state)
                .is_some()
    }
792491
    pub fn get_property<'a>(
792491
        &'a self,
792491
        node_data: &'a NodeData,
792491
        node_id: &NodeId,
792491
        node_state: &StyledNodeState,
792491
        css_property_type: &CssPropertyType,
792491
    ) -> Option<&CssProperty> {
        // Thread-local counter of cascade walks, broken down by
        // property type. Drain with `drain_css_prop_counts` (free
        // fn below) when `AZ_PROP_COUNT=1` is set to see which
        // properties dominate the cold layout path.
        //
        // Env check is read ONCE at process start and cached in a
        // `OnceLock<bool>`. Before this, the env check ran per
        // `get_property` call — and the function fires 710k+ times
        // per cold layout on excel.html. `std::env::var_os` takes
        // ~100 ns per call on macOS (env lock + hashmap lookup), so
        // the naive check added ~70 ms of pure noise to every
        // single layout, regardless of whether the env var was set.
        // Using a one-time cached bool removes that overhead.
        //
        // `no_std` builds have no thread-locals / env, so the profiling
        // counter is compiled out entirely.
        #[cfg(feature = "std")]
        {
            static PROP_COUNT_ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
792491
            let enabled = *PROP_COUNT_ENABLED.get_or_init(crate::profile::cascade_enabled);
792491
            if enabled {
                // `try_with` (not `with`): the lifted-to-wasm web backend has no
                // real TLS, so `with` would hit `panic_access_error` (the layout
                // path reads CSS props via these getters → would trap). `try_with`
                // returns Err and we skip the profiling-only increment (and its
                // inner Mutex-guarded label table). Desktop behaviour unchanged —
                // when the env var is unset the whole block is gated off anyway.
                let _ = PROP_COUNTS.try_with(|c| {
                    *c.borrow_mut()
                        .entry(Self::css_prop_type_label(css_property_type))
                        .or_insert(0) += 1;
                });
792491
            }
        }
        // Always use full cascade resolution.
        // Tier 1/2/2b handle layout-hot properties via direct typed getters.
        // This path is only used for paint-time reads (background, shadow, etc.)
792491
        self.get_property_slow(node_data, node_id, node_state, css_property_type)
792491
    }
    #[cfg(feature = "std")]
    fn css_prop_type_label(t: &CssPropertyType) -> &'static str {
        // Intern Debug-format labels under a mutex-guarded map so
        // we leak at most one `&'static str` per distinct
        // `CssPropertyType` variant (bounded at ≤ 178 total). Only
        // triggered when `AZ_PROP_COUNT=1`, so zero cost normally.
        use std::sync::{Mutex, OnceLock};
        static TABLE: OnceLock<Mutex<std::collections::HashMap<CssPropertyType, &'static str>>> =
            OnceLock::new();
        let m = TABLE.get_or_init(|| Mutex::new(std::collections::HashMap::new()));
        let mut g = m.lock().expect("AZ_PROP_COUNT label table poisoned");
        if let Some(s) = g.get(t) {
            return *s;
        }
        let s: String = std::format!("{:?}", t);
        let leaked: &'static str = std::boxed::Box::leak(s.into_boxed_str());
        g.insert(*t, leaked);
        leaked
    }
    /// Full cascade resolution for any CSS property type.
    /// Walks all cascade layers: user overrides → inline → stylesheet → cascaded → computed → UA.
    /// Also used by restyle functions that need state-aware lookups.
793341
    pub(crate) fn get_property_slow<'a>(
793341
        &'a self,
793341
        node_data: &'a NodeData,
793341
        node_id: &NodeId,
793341
        node_state: &StyledNodeState,
793341
        css_property_type: &CssPropertyType,
793341
    ) -> Option<&CssProperty> {
        use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
        // First test if there is some user-defined override for the property
793341
        if let Some(v) = self.user_overridden_properties.get(node_id.index()) {
            if let Ok(idx) = v.binary_search_by_key(css_property_type, |(k, _)| *k) {
                return Some(&v[idx].1);
            }
793341
        }
        // Helper: do these conditions identify a rule that applies in `state`?
        // Empty conditions = Normal-only. Otherwise all conditions must be
        // PseudoState(state).
1649
        fn matches_pseudo_state(
1649
            conds: &azul_css::dynamic_selector::DynamicSelectorVec,
1649
            state: PseudoStateType,
1649
        ) -> bool {
1649
            let conditions = conds.as_slice();
1649
            if conditions.is_empty() {
850
                state == PseudoStateType::Normal
            } else {
799
                conditions
799
                    .iter()
799
                    .all(|c| matches!(c, DynamicSelector::PseudoState(s) if *s == state))
            }
1649
        }
        // If that fails, see if there is an inline CSS property that matches
        // :focus > :active > :hover > normal (fallback)
793341
        if node_state.focused {
            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
612
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
612
                if matches_pseudo_state(conds,PseudoStateType::Focus)
306
                    && prop.get_type() == *css_property_type
                {
306
                    Some(prop)
                } else {
306
                    None
                }
612
            }) {
306
                return Some(p);
            }
            // PRIORITY 2: CSS stylesheet properties
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::Focus,
                css_property_type,
            ) {
                return Some(p);
            }
            // PRIORITY 3: Cascaded/inherited properties
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::Focus,
                css_property_type,
            ) {
                return Some(p);
            }
793035
        }
793035
        if node_state.active {
            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
340
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
340
                if matches_pseudo_state(conds,PseudoStateType::Active)
85
                    && prop.get_type() == *css_property_type
                {
85
                    Some(prop)
                } else {
255
                    None
                }
340
            }) {
85
                return Some(p);
            }
            // PRIORITY 2: CSS stylesheet properties
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::Active,
                css_property_type,
            ) {
                return Some(p);
            }
            // PRIORITY 3: Cascaded/inherited properties
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::Active,
                css_property_type,
            ) {
                return Some(p);
            }
792950
        }
        // :dragging pseudo-state (higher priority than :hover)
792950
        if node_state.dragging {
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
                if matches_pseudo_state(conds,PseudoStateType::Dragging)
                    && prop.get_type() == *css_property_type
                {
                    Some(prop)
                } else {
                    None
                }
            }) {
                return Some(p);
            }
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::Dragging,
                css_property_type,
            ) {
                return Some(p);
            }
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::Dragging,
                css_property_type,
            ) {
                return Some(p);
            }
792950
        }
        // :drag-over pseudo-state (higher priority than :hover)
792950
        if node_state.drag_over {
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
                if matches_pseudo_state(conds,PseudoStateType::DragOver)
                    && prop.get_type() == *css_property_type
                {
                    Some(prop)
                } else {
                    None
                }
            }) {
                return Some(p);
            }
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::DragOver,
                css_property_type,
            ) {
                return Some(p);
            }
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::DragOver,
                css_property_type,
            ) {
                return Some(p);
            }
792950
        }
792950
        if node_state.hover {
            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
357
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
357
                if matches_pseudo_state(conds,PseudoStateType::Hover)
119
                    && prop.get_type() == *css_property_type
                {
119
                    Some(prop)
                } else {
238
                    None
                }
357
            }) {
119
                return Some(p);
            }
            // PRIORITY 2: CSS stylesheet properties
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::Hover,
                css_property_type,
            ) {
                return Some(p);
            }
            // PRIORITY 3: Cascaded/inherited properties
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::Hover,
                css_property_type,
            ) {
                return Some(p);
            }
792831
        }
        // Normal/fallback properties - always apply as base layer
        // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
792831
        if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
340
            if matches_pseudo_state(conds, PseudoStateType::Normal)
340
                && prop.get_type() == *css_property_type
            {
340
                Some(prop)
            } else {
                None
            }
340
        }) {
340
            return Some(p);
792491
        }
        // PRIORITY 2: CSS stylesheet properties
792491
        if let Some(p) = Self::find_in_stateful(
792491
            self.css_props.get_slice(node_id.index()),
792491
            PseudoStateType::Normal,
792491
            css_property_type,
792491
        ) {
7997
            return Some(p);
784494
        }
        // PRIORITY 2b: Global `*` selector properties (specificity 0,0,0)
        // These are collected once during restyle and apply to all nodes.
        // Lower priority than per-node rules but higher than inheritance/UA.
784494
        if let Some(p) = self.global_css_props.iter().find(|p| p.get_type() == *css_property_type) {
            return Some(p);
784494
        }
        // PRIORITY 3: Cascaded/inherited properties
784494
        if let Some(p) = Self::find_in_stateful(
784494
            self.cascaded_props.get_slice(node_id.index()),
784494
            PseudoStateType::Normal,
784494
            css_property_type,
784494
        ) {
16548
            return Some(p);
767946
        }
        // Check computed values cache for inherited properties
        // Sorted Vec with binary search
767946
        if css_property_type.is_inheritable() {
730002
            if let Some(vec) = self.computed_values.get(node_id.index()) {
730002
                if let Ok(idx) = vec.binary_search_by_key(css_property_type, |(k, _)| *k) {
                    return Some(&vec[idx].1.property);
730002
                }
            }
37944
        }
        // User-agent stylesheet fallback (lowest precedence)
        // Check if the node type has a default value for this property
767946
        crate::ua_css::get_ua_property(&node_data.node_type, *css_property_type)
793341
    }
    /// Get a CSS property using DynamicSelectorContext for evaluation.
    ///
    /// This is the new API that supports @media queries, @container queries,
    /// OS-specific styles, and all pseudo-states via `CssPropertyWithConditions`.
    ///
    /// The evaluation follows "last wins" semantics - properties are evaluated
    /// in reverse order and the first matching property wins.
    pub(crate) fn get_property_with_context<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        context: &DynamicSelectorContext,
        css_property_type: &CssPropertyType,
    ) -> Option<&CssProperty> {
        // First test if there is some user-defined override for the property
        if let Some(v) = self.user_overridden_properties.get(node_id.index()) {
            if let Ok(idx) = v.binary_search_by_key(css_property_type, |(k, _)| *k) {
                return Some(&v[idx].1);
            }
        }
        // Check inline CSS properties with DynamicSelectorContext evaluation.
        // Iterate in REVERSE order across the flat (prop, conds) view —
        // "last found wins" semantics, replacing the old Focus > Active >
        // Hover > Normal priority chain.
        let inline_props_rev: Vec<_> = node_data
            .style
            .iter_inline_properties()
            .collect::<Vec<_>>();
        if let Some(prop) = inline_props_rev.into_iter().rev().find_map(|(prop, conds)| {
            let conditions_match = conds.as_slice().iter().all(|c| c.matches(context));
            if prop.get_type() == *css_property_type && conditions_match {
                Some(prop)
            } else {
                None
            }
        }) {
            return Some(prop);
        }
        // Fall back to CSS file and cascaded properties
        let legacy_state = StyledNodeState::from_pseudo_state_flags(&context.pseudo_state);
        if let Some(p) = self.get_property(node_data, node_id, &legacy_state, css_property_type) {
            return Some(p);
        }
        None
    }
    /// Check if any properties with conditions would change between two contexts.
    /// This is used for re-layout detection on viewport/container resize.
    pub(crate) fn check_properties_changed(
        node_data: &NodeData,
        old_context: &DynamicSelectorContext,
        new_context: &DynamicSelectorContext,
    ) -> bool {
        for (_prop, conds) in node_data.style.iter_inline_properties() {
            let was_active = conds.as_slice().iter().all(|c| c.matches(old_context));
            let is_active = conds.as_slice().iter().all(|c| c.matches(new_context));
            if was_active != is_active {
                return true;
            }
        }
        false
    }
    /// Check if any layout-affecting properties would change between two contexts.
    /// This is a more targeted check for re-layout detection.
    pub(crate) fn check_layout_properties_changed(
        node_data: &NodeData,
        old_context: &DynamicSelectorContext,
        new_context: &DynamicSelectorContext,
    ) -> bool {
        for (prop, conds) in node_data.style.iter_inline_properties() {
            // Skip non-layout-affecting properties
            if !prop.get_type().can_trigger_relayout() {
                continue;
            }
            let was_active = conds.as_slice().iter().all(|c| c.matches(old_context));
            let is_active = conds.as_slice().iter().all(|c| c.matches(new_context));
            if was_active != is_active {
                return true;
            }
        }
        false
    }
7577
    pub fn get_background_content<'a>(
7577
        &'a self,
7577
        node_data: &'a NodeData,
7577
        node_id: &NodeId,
7577
        node_state: &StyledNodeState,
7577
    ) -> Option<&'a StyleBackgroundContentVecValue> {
7577
        self.get_property(
7577
            node_data,
7577
            node_id,
7577
            node_state,
7577
            &CssPropertyType::BackgroundContent,
        )
7577
        .and_then(|p| p.as_background_content())
7577
    }
    /// Method for getting hyphens property
    pub fn get_hyphens<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleHyphensValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Hyphens)
            .and_then(|p| p.as_hyphens())
    }
    /// Method for getting word-break property
    pub fn get_word_break<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleWordBreakValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::WordBreak)
            .and_then(|p| p.as_word_break())
    }
    /// Method for getting overflow-wrap property
42
    pub fn get_overflow_wrap<'a>(
42
        &'a self,
42
        node_data: &'a NodeData,
42
        node_id: &NodeId,
42
        node_state: &StyledNodeState,
42
    ) -> Option<&'a StyleOverflowWrapValue> {
42
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowWrap)
42
            .and_then(|p| p.as_overflow_wrap())
42
    }
    /// Method for getting line-break property
    pub fn get_line_break<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleLineBreakValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::LineBreak)
            .and_then(|p| p.as_line_break())
    }
    /// Method for getting text-align-last property
    pub fn get_text_align_last<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextAlignLastValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextAlignLast)
            .and_then(|p| p.as_text_align_last())
    }
    /// Method for getting object-fit property
    pub fn get_object_fit<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleObjectFitValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ObjectFit)
            .and_then(|p| p.as_object_fit())
    }
    /// Method for getting text-orientation property
    pub fn get_text_orientation<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextOrientationValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextOrientation)
            .and_then(|p| p.as_text_orientation())
    }
    /// Method for getting object-position property
    pub fn get_object_position<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleObjectPositionValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ObjectPosition)
            .and_then(|p| p.as_object_position())
    }
    /// Method for getting aspect-ratio property
    pub fn get_aspect_ratio<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleAspectRatioValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::AspectRatio)
            .and_then(|p| p.as_aspect_ratio())
    }
    /// Method for getting direction property
    pub fn get_direction<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleDirectionValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Direction)
            .and_then(|p| p.as_direction())
    }
    pub fn get_unicode_bidi<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleUnicodeBidiValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::UnicodeBidi)
            .and_then(|p| p.as_unicode_bidi())
    }
    pub fn get_text_box_trim<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextBoxTrimValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextBoxTrim)
            .and_then(|p| p.as_text_box_trim())
    }
    pub fn get_text_box_edge<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextBoxEdgeValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextBoxEdge)
            .and_then(|p| p.as_text_box_edge())
    }
    pub fn get_dominant_baseline<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleDominantBaselineValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::DominantBaseline)
            .and_then(|p| p.as_dominant_baseline())
    }
    pub fn get_alignment_baseline<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleAlignmentBaselineValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignmentBaseline)
            .and_then(|p| p.as_alignment_baseline())
    }
    pub fn get_initial_letter_align<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleInitialLetterAlignValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::InitialLetterAlign)
            .and_then(|p| p.as_initial_letter_align())
    }
    pub fn get_initial_letter_wrap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleInitialLetterWrapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::InitialLetterWrap)
            .and_then(|p| p.as_initial_letter_wrap())
    }
    pub fn get_scrollbar_gutter<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleScrollbarGutterValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarGutter)
            .and_then(|p| p.as_scrollbar_gutter())
    }
    pub fn get_overflow_clip_margin<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleOverflowClipMarginValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowClipMargin)
            .and_then(|p| p.as_overflow_clip_margin())
    }
    pub fn get_clip<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleClipRectValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Clip)
            .and_then(|p| p.as_clip())
    }
    /// Method for getting white-space property
    pub fn get_white_space<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleWhiteSpaceValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::WhiteSpace)
            .and_then(|p| p.as_white_space())
    }
    pub fn get_background_position<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundPositionVecValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BackgroundPosition,
        )
        .and_then(|p| p.as_background_position())
    }
    pub fn get_background_size<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundSizeVecValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BackgroundSize,
        )
        .and_then(|p| p.as_background_size())
    }
    pub fn get_background_repeat<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundRepeatVecValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BackgroundRepeat,
        )
        .and_then(|p| p.as_background_repeat())
    }
675696
    pub fn get_font_size<'a>(
675696
        &'a self,
675696
        node_data: &'a NodeData,
675696
        node_id: &NodeId,
675696
        node_state: &StyledNodeState,
675696
    ) -> Option<&'a StyleFontSizeValue> {
675696
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontSize)
675696
            .and_then(|p| p.as_font_size())
675696
    }
    pub fn get_font_family<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleFontFamilyVecValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontFamily)
            .and_then(|p| p.as_font_family())
    }
    pub fn get_font_weight<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleFontWeightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontWeight)
            .and_then(|p| p.as_font_weight())
    }
    pub fn get_font_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleFontStyleValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontStyle)
            .and_then(|p| p.as_font_style())
    }
39438
    pub fn get_text_color<'a>(
39438
        &'a self,
39438
        node_data: &'a NodeData,
39438
        node_id: &NodeId,
39438
        node_state: &StyledNodeState,
39438
    ) -> Option<&'a StyleTextColorValue> {
39438
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextColor)
39438
            .and_then(|p| p.as_text_color())
39438
    }
    /// Method for getting text-indent property
    pub fn get_text_indent<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextIndentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextIndent)
            .and_then(|p| p.as_text_indent())
    }
    /// Method for getting initial-letter property
    pub fn get_initial_letter<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleInitialLetterValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::InitialLetter,
        )
        .and_then(|p| p.as_initial_letter())
    }
    /// Method for getting line-clamp property
    pub fn get_line_clamp<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleLineClampValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::LineClamp)
            .and_then(|p| p.as_line_clamp())
    }
    /// Method for getting hanging-punctuation property
    pub fn get_hanging_punctuation<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleHangingPunctuationValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::HangingPunctuation,
        )
        .and_then(|p| p.as_hanging_punctuation())
    }
    /// Method for getting text-combine-upright property
    pub fn get_text_combine_upright<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextCombineUprightValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::TextCombineUpright,
        )
        .and_then(|p| p.as_text_combine_upright())
    }
    /// Method for getting -azul-exclusion-margin property
    pub fn get_exclusion_margin<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleExclusionMarginValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ExclusionMargin,
        )
        .and_then(|p| p.as_exclusion_margin())
    }
    /// Method for getting -azul-hyphenation-language property
    pub fn get_hyphenation_language<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleHyphenationLanguageValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::HyphenationLanguage,
        )
        .and_then(|p| p.as_hyphenation_language())
    }
    /// Method for getting caret-color property
1260
    pub fn get_caret_color<'a>(
1260
        &'a self,
1260
        node_data: &'a NodeData,
1260
        node_id: &NodeId,
1260
        node_state: &StyledNodeState,
1260
    ) -> Option<&'a CaretColorValue> {
1260
        self.get_property(node_data, node_id, node_state, &CssPropertyType::CaretColor)
1260
            .and_then(|p| p.as_caret_color())
1260
    }
    /// Method for getting -azul-caret-width property
1260
    pub fn get_caret_width<'a>(
1260
        &'a self,
1260
        node_data: &'a NodeData,
1260
        node_id: &NodeId,
1260
        node_state: &StyledNodeState,
1260
    ) -> Option<&'a CaretWidthValue> {
1260
        self.get_property(node_data, node_id, node_state, &CssPropertyType::CaretWidth)
1260
            .and_then(|p| p.as_caret_width())
1260
    }
    /// Method for getting caret-animation-duration property
1260
    pub fn get_caret_animation_duration<'a>(
1260
        &'a self,
1260
        node_data: &'a NodeData,
1260
        node_id: &NodeId,
1260
        node_state: &StyledNodeState,
1260
    ) -> Option<&'a CaretAnimationDurationValue> {
1260
        self.get_property(
1260
            node_data,
1260
            node_id,
1260
            node_state,
1260
            &CssPropertyType::CaretAnimationDuration,
        )
1260
        .and_then(|p| p.as_caret_animation_duration())
1260
    }
    /// Method for getting selection-background-color property
    pub fn get_selection_background_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a SelectionBackgroundColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::SelectionBackgroundColor,
        )
        .and_then(|p| p.as_selection_background_color())
    }
    /// Method for getting selection-color property
    pub fn get_selection_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a SelectionColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::SelectionColor,
        )
        .and_then(|p| p.as_selection_color())
    }
    /// Method for getting -azul-selection-radius property
    pub fn get_selection_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a SelectionRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::SelectionRadius,
        )
        .and_then(|p| p.as_selection_radius())
    }
    /// Method for getting text-justify property
    pub fn get_text_justify<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutTextJustifyValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::TextJustify,
        )
        .and_then(|p| p.as_text_justify())
    }
    /// Method for getting z-index property
    pub fn get_z_index<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutZIndexValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ZIndex)
            .and_then(|p| p.as_z_index())
    }
    /// Method for getting flex-basis property
    pub fn get_flex_basis<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFlexBasisValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexBasis)
            .and_then(|p| p.as_flex_basis())
    }
    /// Method for getting column-gap property
    pub fn get_column_gap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutColumnGapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnGap)
            .and_then(|p| p.as_column_gap())
    }
    /// Method for getting row-gap property
    pub fn get_row_gap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutRowGapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::RowGap)
            .and_then(|p| p.as_row_gap())
    }
    /// Method for getting grid-template-columns property
    pub fn get_grid_template_columns<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridTemplateColumnsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridTemplateColumns,
        )
        .and_then(|p| p.as_grid_template_columns())
    }
    /// Method for getting grid-template-rows property
    pub fn get_grid_template_rows<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridTemplateRowsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridTemplateRows,
        )
        .and_then(|p| p.as_grid_template_rows())
    }
    /// Method for getting grid-auto-columns property
    pub fn get_grid_auto_columns<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridAutoColumnsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridAutoColumns,
        )
        .and_then(|p| p.as_grid_auto_columns())
    }
    /// Method for getting grid-auto-rows property
    pub fn get_grid_auto_rows<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridAutoRowsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridAutoRows,
        )
        .and_then(|p| p.as_grid_auto_rows())
    }
    /// Method for getting grid-column property
    pub fn get_grid_column<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridColumnValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridColumn)
            .and_then(|p| p.as_grid_column())
    }
    /// Method for getting grid-row property
    pub fn get_grid_row<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridRowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridRow)
            .and_then(|p| p.as_grid_row())
    }
    /// Method for getting grid-auto-flow property
    pub fn get_grid_auto_flow<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridAutoFlowValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridAutoFlow,
        )
        .and_then(|p| p.as_grid_auto_flow())
    }
    /// Method for getting justify-self property
    pub fn get_justify_self<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutJustifySelfValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::JustifySelf,
        )
        .and_then(|p| p.as_justify_self())
    }
    /// Method for getting justify-items property
    pub fn get_justify_items<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutJustifyItemsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::JustifyItems,
        )
        .and_then(|p| p.as_justify_items())
    }
    /// Method for getting gap property
    pub fn get_gap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Gap)
            .and_then(|p| p.as_gap())
    }
    /// Method for getting grid-gap property
    pub(crate) fn get_grid_gap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridGap)
            .and_then(|p| p.as_grid_gap())
    }
    /// Method for getting align-self property
    pub fn get_align_self<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutAlignSelfValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignSelf)
            .and_then(|p| p.as_align_self())
    }
    /// Method for getting font property
    pub fn get_font<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleFontValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Font)
            .and_then(|p| p.as_font())
    }
    /// Method for getting writing-mode property
    pub fn get_writing_mode<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutWritingModeValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::WritingMode,
        )
        .and_then(|p| p.as_writing_mode())
    }
    /// Method for getting clear property
    pub fn get_clear<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutClearValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Clear)
            .and_then(|p| p.as_clear())
    }
    /// Method for getting shape-outside property
    pub fn get_shape_outside<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ShapeOutsideValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ShapeOutside,
        )
        .and_then(|p| p.as_shape_outside())
    }
    /// Method for getting shape-inside property
    pub fn get_shape_inside<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ShapeInsideValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ShapeInside,
        )
        .and_then(|p| p.as_shape_inside())
    }
    /// Method for getting clip-path property
    pub fn get_clip_path<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ClipPathValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ClipPath)
            .and_then(|p| p.as_clip_path())
    }
    /// Method for getting scrollbar track background
    pub fn get_scrollbar_track<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarTrack)
            .and_then(|p| p.as_scrollbar_track())
    }
    /// Method for getting scrollbar thumb background
    pub fn get_scrollbar_thumb<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarThumb)
            .and_then(|p| p.as_scrollbar_thumb())
    }
    /// Method for getting scrollbar button background
    pub fn get_scrollbar_button<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarButton)
            .and_then(|p| p.as_scrollbar_button())
    }
    /// Method for getting scrollbar corner background
    pub fn get_scrollbar_corner<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarCorner)
            .and_then(|p| p.as_scrollbar_corner())
    }
    /// Method for getting scrollbar resizer background
    pub fn get_scrollbar_resizer<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarResizer)
            .and_then(|p| p.as_scrollbar_resizer())
    }
    /// Method for getting scrollbar-width property
    pub fn get_scrollbar_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutScrollbarWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarWidth,
        )
        .and_then(|p| p.as_scrollbar_width())
    }
    /// Method for getting scrollbar-color property
    pub fn get_scrollbar_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleScrollbarColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarColor,
        )
        .and_then(|p| p.as_scrollbar_color())
    }
    /// Method for getting -azul-scrollbar-visibility property
    pub fn get_scrollbar_visibility<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ScrollbarVisibilityModeValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarVisibility,
        )
        .and_then(|p| p.as_scrollbar_visibility())
    }
    /// Method for getting -azul-scrollbar-fade-delay property
    pub fn get_scrollbar_fade_delay<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ScrollbarFadeDelayValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarFadeDelay,
        )
        .and_then(|p| p.as_scrollbar_fade_delay())
    }
    /// Method for getting -azul-scrollbar-fade-duration property
    pub fn get_scrollbar_fade_duration<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ScrollbarFadeDurationValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarFadeDuration,
        )
        .and_then(|p| p.as_scrollbar_fade_duration())
    }
    /// Method for getting visibility property
    pub fn get_visibility<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleVisibilityValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Visibility)
            .and_then(|p| p.as_visibility())
    }
    /// Method for getting break-before property
    pub fn get_break_before<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a PageBreakValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BreakBefore,
        )
        .and_then(|p| p.as_break_before())
    }
    /// Method for getting break-after property
    pub fn get_break_after<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a PageBreakValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::BreakAfter)
            .and_then(|p| p.as_break_after())
    }
    /// Method for getting break-inside property
    pub fn get_break_inside<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a BreakInsideValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BreakInside,
        )
        .and_then(|p| p.as_break_inside())
    }
    /// Method for getting orphans property
    pub fn get_orphans<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a OrphansValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Orphans)
            .and_then(|p| p.as_orphans())
    }
    /// Method for getting widows property
    pub fn get_widows<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a WidowsValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Widows)
            .and_then(|p| p.as_widows())
    }
    /// Method for getting box-decoration-break property
    pub fn get_box_decoration_break<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a BoxDecorationBreakValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BoxDecorationBreak,
        )
        .and_then(|p| p.as_box_decoration_break())
    }
    /// Method for getting column-count property
    pub fn get_column_count<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnCountValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnCount,
        )
        .and_then(|p| p.as_column_count())
    }
    /// Method for getting column-width property
    pub fn get_column_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnWidth,
        )
        .and_then(|p| p.as_column_width())
    }
    /// Method for getting column-span property
    pub fn get_column_span<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnSpanValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnSpan)
            .and_then(|p| p.as_column_span())
    }
    /// Method for getting column-fill property
    pub fn get_column_fill<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnFillValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnFill)
            .and_then(|p| p.as_column_fill())
    }
    /// Method for getting column-rule-width property
    pub fn get_column_rule_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnRuleWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnRuleWidth,
        )
        .and_then(|p| p.as_column_rule_width())
    }
    /// Method for getting column-rule-style property
    pub fn get_column_rule_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnRuleStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnRuleStyle,
        )
        .and_then(|p| p.as_column_rule_style())
    }
    /// Method for getting column-rule-color property
    pub fn get_column_rule_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnRuleColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnRuleColor,
        )
        .and_then(|p| p.as_column_rule_color())
    }
    /// Method for getting flow-into property
    pub fn get_flow_into<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a FlowIntoValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlowInto)
            .and_then(|p| p.as_flow_into())
    }
    /// Method for getting flow-from property
    pub fn get_flow_from<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a FlowFromValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlowFrom)
            .and_then(|p| p.as_flow_from())
    }
    /// Method for getting shape-margin property
    pub fn get_shape_margin<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ShapeMarginValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ShapeMargin,
        )
        .and_then(|p| p.as_shape_margin())
    }
    /// Method for getting shape-image-threshold property
    pub fn get_shape_image_threshold<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ShapeImageThresholdValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ShapeImageThreshold,
        )
        .and_then(|p| p.as_shape_image_threshold())
    }
    /// Method for getting content property
    pub fn get_content<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Content)
            .and_then(|p| p.as_content())
    }
    /// Method for getting counter-reset property
    pub fn get_counter_reset<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a CounterResetValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::CounterReset,
        )
        .and_then(|p| p.as_counter_reset())
    }
    /// Method for getting counter-increment property
    pub fn get_counter_increment<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a CounterIncrementValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::CounterIncrement,
        )
        .and_then(|p| p.as_counter_increment())
    }
    /// Method for getting string-set property
    pub fn get_string_set<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StringSetValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::StringSet)
            .and_then(|p| p.as_string_set())
    }
    pub fn get_text_align<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextAlignValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextAlign)
            .and_then(|p| p.as_text_align())
    }
12054
    pub fn get_user_select<'a>(
12054
        &'a self,
12054
        node_data: &'a NodeData,
12054
        node_id: &NodeId,
12054
        node_state: &StyledNodeState,
12054
    ) -> Option<&'a StyleUserSelectValue> {
12054
        self.get_property(node_data, node_id, node_state, &CssPropertyType::UserSelect)
12054
            .and_then(|p| p.as_user_select())
12054
    }
10374
    pub fn get_text_decoration<'a>(
10374
        &'a self,
10374
        node_data: &'a NodeData,
10374
        node_id: &NodeId,
10374
        node_state: &StyledNodeState,
10374
    ) -> Option<&'a StyleTextDecorationValue> {
10374
        self.get_property(
10374
            node_data,
10374
            node_id,
10374
            node_state,
10374
            &CssPropertyType::TextDecoration,
        )
10374
        .and_then(|p| p.as_text_decoration())
10374
    }
8568
    pub fn get_vertical_align<'a>(
8568
        &'a self,
8568
        node_data: &'a NodeData,
8568
        node_id: &NodeId,
8568
        node_state: &StyledNodeState,
8568
    ) -> Option<&'a StyleVerticalAlignValue> {
8568
        self.get_property(
8568
            node_data,
8568
            node_id,
8568
            node_state,
8568
            &CssPropertyType::VerticalAlign,
        )
8568
        .and_then(|p| p.as_vertical_align())
8568
    }
    pub fn get_line_height<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleLineHeightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::LineHeight)
            .and_then(|p| p.as_line_height())
    }
    pub fn get_letter_spacing<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleLetterSpacingValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::LetterSpacing,
        )
        .and_then(|p| p.as_letter_spacing())
    }
    pub fn get_word_spacing<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleWordSpacingValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::WordSpacing,
        )
        .and_then(|p| p.as_word_spacing())
    }
    pub fn get_tab_size<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTabSizeValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TabSize)
            .and_then(|p| p.as_tab_size())
    }
6048
    pub fn get_cursor<'a>(
6048
        &'a self,
6048
        node_data: &'a NodeData,
6048
        node_id: &NodeId,
6048
        node_state: &StyledNodeState,
6048
    ) -> Option<&'a StyleCursorValue> {
6048
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Cursor)
6048
            .and_then(|p| p.as_cursor())
6048
    }
42
    pub fn get_box_shadow_left<'a>(
42
        &'a self,
42
        node_data: &'a NodeData,
42
        node_id: &NodeId,
42
        node_state: &StyledNodeState,
42
    ) -> Option<&'a StyleBoxShadowValue> {
42
        self.get_property(
42
            node_data,
42
            node_id,
42
            node_state,
42
            &CssPropertyType::BoxShadowLeft,
        )
42
        .and_then(|p| p.as_box_shadow_left())
42
    }
42
    pub fn get_box_shadow_right<'a>(
42
        &'a self,
42
        node_data: &'a NodeData,
42
        node_id: &NodeId,
42
        node_state: &StyledNodeState,
42
    ) -> Option<&'a StyleBoxShadowValue> {
42
        self.get_property(
42
            node_data,
42
            node_id,
42
            node_state,
42
            &CssPropertyType::BoxShadowRight,
        )
42
        .and_then(|p| p.as_box_shadow_right())
42
    }
42
    pub fn get_box_shadow_top<'a>(
42
        &'a self,
42
        node_data: &'a NodeData,
42
        node_id: &NodeId,
42
        node_state: &StyledNodeState,
42
    ) -> Option<&'a StyleBoxShadowValue> {
42
        self.get_property(
42
            node_data,
42
            node_id,
42
            node_state,
42
            &CssPropertyType::BoxShadowTop,
        )
42
        .and_then(|p| p.as_box_shadow_top())
42
    }
42
    pub fn get_box_shadow_bottom<'a>(
42
        &'a self,
42
        node_data: &'a NodeData,
42
        node_id: &NodeId,
42
        node_state: &StyledNodeState,
42
    ) -> Option<&'a StyleBoxShadowValue> {
42
        self.get_property(
42
            node_data,
42
            node_id,
42
            node_state,
42
            &CssPropertyType::BoxShadowBottom,
        )
42
        .and_then(|p| p.as_box_shadow_bottom())
42
    }
    pub fn get_border_top_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderTopColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopColor,
        )
        .and_then(|p| p.as_border_top_color())
    }
    pub fn get_border_left_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderLeftColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderLeftColor,
        )
        .and_then(|p| p.as_border_left_color())
    }
    pub fn get_border_right_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderRightColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderRightColor,
        )
        .and_then(|p| p.as_border_right_color())
    }
    pub fn get_border_bottom_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderBottomColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomColor,
        )
        .and_then(|p| p.as_border_bottom_color())
    }
    pub fn get_border_top_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderTopStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopStyle,
        )
        .and_then(|p| p.as_border_top_style())
    }
    pub fn get_border_left_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderLeftStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderLeftStyle,
        )
        .and_then(|p| p.as_border_left_style())
    }
    pub fn get_border_right_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderRightStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderRightStyle,
        )
        .and_then(|p| p.as_border_right_style())
    }
    pub fn get_border_bottom_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderBottomStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomStyle,
        )
        .and_then(|p| p.as_border_bottom_style())
    }
    pub fn get_border_top_left_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderTopLeftRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopLeftRadius,
        )
        .and_then(|p| p.as_border_top_left_radius())
    }
    pub fn get_border_top_right_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderTopRightRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopRightRadius,
        )
        .and_then(|p| p.as_border_top_right_radius())
    }
    pub fn get_border_bottom_left_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderBottomLeftRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomLeftRadius,
        )
        .and_then(|p| p.as_border_bottom_left_radius())
    }
    pub fn get_border_bottom_right_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderBottomRightRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomRightRadius,
        )
        .and_then(|p| p.as_border_bottom_right_radius())
    }
    pub fn get_opacity<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleOpacityValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Opacity)
            .and_then(|p| p.as_opacity())
    }
126
    pub fn get_transform<'a>(
126
        &'a self,
126
        node_data: &'a NodeData,
126
        node_id: &NodeId,
126
        node_state: &StyledNodeState,
126
    ) -> Option<&'a StyleTransformVecValue> {
126
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Transform)
126
            .and_then(|p| p.as_transform())
126
    }
126
    pub fn get_transform_origin<'a>(
126
        &'a self,
126
        node_data: &'a NodeData,
126
        node_id: &NodeId,
126
        node_state: &StyledNodeState,
126
    ) -> Option<&'a StyleTransformOriginValue> {
126
        self.get_property(
126
            node_data,
126
            node_id,
126
            node_state,
126
            &CssPropertyType::TransformOrigin,
        )
126
        .and_then(|p| p.as_transform_origin())
126
    }
    pub fn get_perspective_origin<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StylePerspectiveOriginValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::PerspectiveOrigin,
        )
        .and_then(|p| p.as_perspective_origin())
    }
    pub fn get_backface_visibility<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackfaceVisibilityValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BackfaceVisibility,
        )
        .and_then(|p| p.as_backface_visibility())
    }
17
    pub fn get_display<'a>(
17
        &'a self,
17
        node_data: &'a NodeData,
17
        node_id: &NodeId,
17
        node_state: &StyledNodeState,
17
    ) -> Option<&'a LayoutDisplayValue> {
17
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Display)
17
            .and_then(|p| p.as_display())
17
    }
    pub fn get_float<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFloatValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Float)
            .and_then(|p| p.as_float())
    }
    pub fn get_box_sizing<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBoxSizingValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::BoxSizing)
            .and_then(|p| p.as_box_sizing())
    }
34
    pub fn get_width<'a>(
34
        &'a self,
34
        node_data: &'a NodeData,
34
        node_id: &NodeId,
34
        node_state: &StyledNodeState,
34
    ) -> Option<&'a LayoutWidthValue> {
34
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Width)
34
            .and_then(|p| p.as_width())
34
    }
4780
    pub fn get_height<'a>(
4780
        &'a self,
4780
        node_data: &'a NodeData,
4780
        node_id: &NodeId,
4780
        node_state: &StyledNodeState,
4780
    ) -> Option<&'a LayoutHeightValue> {
4780
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Height)
4780
            .and_then(|p| p.as_height())
4780
    }
    pub fn get_min_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMinWidthValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MinWidth)
            .and_then(|p| p.as_min_width())
    }
    pub fn get_min_height<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMinHeightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MinHeight)
            .and_then(|p| p.as_min_height())
    }
    pub fn get_max_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMaxWidthValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MaxWidth)
            .and_then(|p| p.as_max_width())
    }
    pub fn get_max_height<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMaxHeightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MaxHeight)
            .and_then(|p| p.as_max_height())
    }
    pub fn get_position<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPositionValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Position)
            .and_then(|p| p.as_position())
    }
    pub fn get_top<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutTopValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Top)
            .and_then(|p| p.as_top())
    }
    pub fn get_bottom<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutInsetBottomValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Bottom)
            .and_then(|p| p.as_bottom())
    }
    pub fn get_right<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutRightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Right)
            .and_then(|p| p.as_right())
    }
    pub fn get_left<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutLeftValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Left)
            .and_then(|p| p.as_left())
    }
    pub fn get_padding_top<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPaddingTopValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::PaddingTop)
            .and_then(|p| p.as_padding_top())
    }
    pub fn get_padding_bottom<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPaddingBottomValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::PaddingBottom,
        )
        .and_then(|p| p.as_padding_bottom())
    }
    pub fn get_padding_left<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPaddingLeftValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::PaddingLeft,
        )
        .and_then(|p| p.as_padding_left())
    }
    pub fn get_padding_right<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPaddingRightValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::PaddingRight,
        )
        .and_then(|p| p.as_padding_right())
    }
101
    pub fn get_margin_top<'a>(
101
        &'a self,
101
        node_data: &'a NodeData,
101
        node_id: &NodeId,
101
        node_state: &StyledNodeState,
101
    ) -> Option<&'a LayoutMarginTopValue> {
101
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MarginTop)
101
            .and_then(|p| p.as_margin_top())
101
    }
126
    pub fn get_margin_bottom<'a>(
126
        &'a self,
126
        node_data: &'a NodeData,
126
        node_id: &NodeId,
126
        node_state: &StyledNodeState,
126
    ) -> Option<&'a LayoutMarginBottomValue> {
126
        self.get_property(
126
            node_data,
126
            node_id,
126
            node_state,
126
            &CssPropertyType::MarginBottom,
        )
126
        .and_then(|p| p.as_margin_bottom())
126
    }
    pub fn get_margin_left<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMarginLeftValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MarginLeft)
            .and_then(|p| p.as_margin_left())
    }
    pub fn get_margin_right<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMarginRightValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::MarginRight,
        )
        .and_then(|p| p.as_margin_right())
    }
    pub fn get_border_top_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderTopWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopWidth,
        )
        .and_then(|p| p.as_border_top_width())
    }
    pub fn get_border_left_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderLeftWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderLeftWidth,
        )
        .and_then(|p| p.as_border_left_width())
    }
    pub fn get_border_right_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderRightWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderRightWidth,
        )
        .and_then(|p| p.as_border_right_width())
    }
    pub fn get_border_bottom_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderBottomWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomWidth,
        )
        .and_then(|p| p.as_border_bottom_width())
    }
    pub fn get_overflow_x<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutOverflowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowX)
            .and_then(|p| p.as_overflow_x())
    }
    pub fn get_overflow_y<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutOverflowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowY)
            .and_then(|p| p.as_overflow_y())
    }
    pub fn get_overflow_block<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutOverflowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowBlock)
            .and_then(|p| p.as_overflow_block())
    }
    pub fn get_overflow_inline<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutOverflowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowInline)
            .and_then(|p| p.as_overflow_inline())
    }
    pub fn get_flex_direction<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFlexDirectionValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::FlexDirection,
        )
        .and_then(|p| p.as_flex_direction())
    }
378
    pub fn get_flex_wrap<'a>(
378
        &'a self,
378
        node_data: &'a NodeData,
378
        node_id: &NodeId,
378
        node_state: &StyledNodeState,
378
    ) -> Option<&'a LayoutFlexWrapValue> {
378
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexWrap)
378
            .and_then(|p| p.as_flex_wrap())
378
    }
    pub fn get_flex_grow<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFlexGrowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexGrow)
            .and_then(|p| p.as_flex_grow())
    }
    pub fn get_flex_shrink<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFlexShrinkValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexShrink)
            .and_then(|p| p.as_flex_shrink())
    }
    pub fn get_justify_content<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutJustifyContentValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::JustifyContent,
        )
        .and_then(|p| p.as_justify_content())
    }
    pub fn get_align_items<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutAlignItemsValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignItems)
            .and_then(|p| p.as_align_items())
    }
    pub fn get_align_content<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutAlignContentValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::AlignContent,
        )
        .and_then(|p| p.as_align_content())
    }
    pub fn get_mix_blend_mode<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleMixBlendModeValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::MixBlendMode,
        )
        .and_then(|p| p.as_mix_blend_mode())
    }
6216
    pub fn get_filter<'a>(
6216
        &'a self,
6216
        node_data: &'a NodeData,
6216
        node_id: &NodeId,
6216
        node_state: &StyledNodeState,
6216
    ) -> Option<&'a StyleFilterVecValue> {
6216
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Filter)
6216
            .and_then(|p| p.as_filter())
6216
    }
6216
    pub fn get_backdrop_filter<'a>(
6216
        &'a self,
6216
        node_data: &'a NodeData,
6216
        node_id: &NodeId,
6216
        node_state: &StyledNodeState,
6216
    ) -> Option<&'a StyleFilterVecValue> {
6216
        self.get_property(node_data, node_id, node_state, &CssPropertyType::BackdropFilter)
6216
            .and_then(|p| p.as_backdrop_filter())
6216
    }
6426
    pub fn get_text_shadow<'a>(
6426
        &'a self,
6426
        node_data: &'a NodeData,
6426
        node_id: &NodeId,
6426
        node_state: &StyledNodeState,
6426
    ) -> Option<&'a StyleBoxShadowValue> {
6426
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextShadow)
6426
            .and_then(|p| p.as_text_shadow())
6426
    }
    pub fn get_list_style_type<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleListStyleTypeValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ListStyleType,
        )
        .and_then(|p| p.as_list_style_type())
    }
    pub fn get_list_style_position<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleListStylePositionValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ListStylePosition,
        )
        .and_then(|p| p.as_list_style_position())
    }
1386
    pub fn get_table_layout<'a>(
1386
        &'a self,
1386
        node_data: &'a NodeData,
1386
        node_id: &NodeId,
1386
        node_state: &StyledNodeState,
1386
    ) -> Option<&'a LayoutTableLayoutValue> {
1386
        self.get_property(
1386
            node_data,
1386
            node_id,
1386
            node_state,
1386
            &CssPropertyType::TableLayout,
        )
1386
        .and_then(|p| p.as_table_layout())
1386
    }
    pub fn get_border_collapse<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderCollapseValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderCollapse,
        )
        .and_then(|p| p.as_border_collapse())
    }
    pub fn get_border_spacing<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderSpacingValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderSpacing,
        )
        .and_then(|p| p.as_border_spacing())
    }
1386
    pub fn get_caption_side<'a>(
1386
        &'a self,
1386
        node_data: &'a NodeData,
1386
        node_id: &NodeId,
1386
        node_state: &StyledNodeState,
1386
    ) -> Option<&'a StyleCaptionSideValue> {
1386
        self.get_property(
1386
            node_data,
1386
            node_id,
1386
            node_state,
1386
            &CssPropertyType::CaptionSide,
        )
1386
        .and_then(|p| p.as_caption_side())
1386
    }
1428
    pub fn get_empty_cells<'a>(
1428
        &'a self,
1428
        node_data: &'a NodeData,
1428
        node_id: &NodeId,
1428
        node_state: &StyledNodeState,
1428
    ) -> Option<&'a StyleEmptyCellsValue> {
1428
        self.get_property(node_data, node_id, node_state, &CssPropertyType::EmptyCells)
1428
            .and_then(|p| p.as_empty_cells())
1428
    }
    // Width calculation methods
    pub fn calc_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_width(node_data, node_id, styled_node_state)
            .and_then(|w| match w.get_property()? {
                LayoutWidth::Px(px) => Some(px.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                )),
                _ => Some(0.0), // min-content/max-content not resolved here
            })
            .unwrap_or(0.0)
    }
    pub fn calc_min_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_min_width(node_data, node_id, styled_node_state)
            .and_then(|w| {
                Some(w.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_max_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> Option<f32> {
        self.get_max_width(node_data, node_id, styled_node_state)
            .and_then(|w| {
                Some(w.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    // Height calculation methods
    pub fn calc_height(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_height(node_data, node_id, styled_node_state)
            .and_then(|h| match h.get_property()? {
                LayoutHeight::Px(px) => Some(px.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                )),
                _ => Some(0.0), // min-content/max-content not resolved here
            })
            .unwrap_or(0.0)
    }
    pub fn calc_min_height(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_min_height(node_data, node_id, styled_node_state)
            .and_then(|h| {
                Some(h.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_max_height(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> Option<f32> {
        self.get_max_height(node_data, node_id, styled_node_state)
            .and_then(|h| {
                Some(h.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    // Position calculation methods
    pub fn calc_left(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> Option<f32> {
        self.get_left(node_data, node_id, styled_node_state)
            .and_then(|l| {
                Some(l.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    pub fn calc_right(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> Option<f32> {
        self.get_right(node_data, node_id, styled_node_state)
            .and_then(|r| {
                Some(r.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    pub fn calc_top(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> Option<f32> {
        self.get_top(node_data, node_id, styled_node_state)
            .and_then(|t| {
                Some(t.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    pub fn calc_bottom(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> Option<f32> {
        self.get_bottom(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    // Border calculation methods
    pub fn calc_border_left_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_border_left_width(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_border_right_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_border_right_width(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_border_top_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_border_top_width(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_border_bottom_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_border_bottom_width(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    // Padding calculation methods
    pub fn calc_padding_left(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_padding_left(node_data, node_id, styled_node_state)
            .and_then(|p| {
                Some(p.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_padding_right(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_padding_right(node_data, node_id, styled_node_state)
            .and_then(|p| {
                Some(p.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_padding_top(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_padding_top(node_data, node_id, styled_node_state)
            .and_then(|p| {
                Some(p.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_padding_bottom(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_padding_bottom(node_data, node_id, styled_node_state)
            .and_then(|p| {
                Some(p.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    // Margin calculation methods
    pub fn calc_margin_left(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_margin_left(node_data, node_id, styled_node_state)
            .and_then(|m| {
                Some(m.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_margin_right(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_margin_right(node_data, node_id, styled_node_state)
            .and_then(|m| {
                Some(m.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_margin_top(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_margin_top(node_data, node_id, styled_node_state)
            .and_then(|m| {
                Some(m.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_margin_bottom(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_margin_bottom(node_data, node_id, styled_node_state)
            .and_then(|m| {
                Some(m.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
26451
    fn resolve_property_dependency(
26451
        target_property: &CssProperty,
26451
        reference_property: &CssProperty,
26451
    ) -> Option<CssProperty> {
        use azul_css::{
            css::CssPropertyValue,
            props::{
                basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
                layout::*,
                style::{SelectionRadius, StyleLetterSpacing, StyleWordSpacing},
            },
        };
        // Extract PixelValue from various property types (returns owned value)
36063
        let get_pixel_value = |prop: &CssProperty| -> Option<PixelValue> {
36063
            match prop {
13310
                CssProperty::FontSize(val) => val.get_property().map(|v| v.inner),
                CssProperty::LetterSpacing(val) => val.get_property().map(|v| v.inner),
                CssProperty::WordSpacing(val) => val.get_property().map(|v| v.inner),
706
                CssProperty::PaddingLeft(val) => val.get_property().map(|v| v.inner),
672
                CssProperty::PaddingRight(val) => val.get_property().map(|v| v.inner),
672
                CssProperty::PaddingTop(val) => val.get_property().map(|v| v.inner),
672
                CssProperty::PaddingBottom(val) => val.get_property().map(|v| v.inner),
336
                CssProperty::MarginLeft(val) => val.get_property().map(|v| v.inner),
336
                CssProperty::MarginRight(val) => val.get_property().map(|v| v.inner),
1092
                CssProperty::MarginTop(val) => val.get_property().map(|v| v.inner),
1092
                CssProperty::MarginBottom(val) => val.get_property().map(|v| v.inner),
                CssProperty::MinWidth(val) => val.get_property().map(|v| v.inner),
336
                CssProperty::MinHeight(val) => val.get_property().map(|v| v.inner),
                CssProperty::MaxWidth(val) => val.get_property().map(|v| v.inner),
                CssProperty::MaxHeight(val) => val.get_property().map(|v| v.inner),
                CssProperty::SelectionRadius(val) => val.get_property().map(|v| v.inner),
16839
                _ => None,
            }
36063
        };
26451
        let target_pixel_value = get_pixel_value(target_property)?;
9612
        let reference_pixel_value = get_pixel_value(reference_property)?;
        // Convert reference to absolute pixels first
9612
        let reference_px = match reference_pixel_value.metric {
9612
            SizeMetric::Px => reference_pixel_value.number.get(),
            SizeMetric::Pt => reference_pixel_value.number.get() * PT_TO_PX,
            SizeMetric::In => reference_pixel_value.number.get() * IN_TO_PX,
            SizeMetric::Cm => reference_pixel_value.number.get() * CM_TO_PX,
            SizeMetric::Mm => reference_pixel_value.number.get() * MM_TO_PX,
            SizeMetric::Em => return None, // Reference can't be relative
            SizeMetric::Rem => return None, // Reference can't be relative
            SizeMetric::Percent => return None, // Reference can't be relative
            // Reference can't be viewport-relative
            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
        };
        // Resolve target based on reference
9612
        let resolved_px = match target_pixel_value.metric {
7785
            SizeMetric::Px => target_pixel_value.number.get(),
            SizeMetric::Pt => target_pixel_value.number.get() * PT_TO_PX,
            SizeMetric::In => target_pixel_value.number.get() * IN_TO_PX,
            SizeMetric::Cm => target_pixel_value.number.get() * CM_TO_PX,
            SizeMetric::Mm => target_pixel_value.number.get() * MM_TO_PX,
1725
            SizeMetric::Em => target_pixel_value.number.get() * reference_px,
            // Use reference as root font-size
            SizeMetric::Rem => target_pixel_value.number.get() * reference_px,
102
            SizeMetric::Percent => target_pixel_value.number.get() / 100.0 * reference_px,
            // Need viewport context
            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
        };
        // Create a new property with the resolved value
9612
        let resolved_pixel_value = PixelValue::px(resolved_px);
9612
        match target_property {
3698
            CssProperty::FontSize(_) => Some(CssProperty::FontSize(CssPropertyValue::Exact(
3698
                StyleFontSize {
3698
                    inner: resolved_pixel_value,
3698
                },
3698
            ))),
            CssProperty::LetterSpacing(_) => Some(CssProperty::LetterSpacing(
                CssPropertyValue::Exact(StyleLetterSpacing {
                    inner: resolved_pixel_value,
                }),
            )),
            CssProperty::WordSpacing(_) => Some(CssProperty::WordSpacing(CssPropertyValue::Exact(
                StyleWordSpacing {
                    inner: resolved_pixel_value,
                },
            ))),
706
            CssProperty::PaddingLeft(_) => Some(CssProperty::PaddingLeft(CssPropertyValue::Exact(
706
                LayoutPaddingLeft {
706
                    inner: resolved_pixel_value,
706
                },
706
            ))),
672
            CssProperty::PaddingRight(_) => Some(CssProperty::PaddingRight(
672
                CssPropertyValue::Exact(LayoutPaddingRight {
672
                    inner: resolved_pixel_value,
672
                }),
672
            )),
672
            CssProperty::PaddingTop(_) => Some(CssProperty::PaddingTop(CssPropertyValue::Exact(
672
                LayoutPaddingTop {
672
                    inner: resolved_pixel_value,
672
                },
672
            ))),
672
            CssProperty::PaddingBottom(_) => Some(CssProperty::PaddingBottom(
672
                CssPropertyValue::Exact(LayoutPaddingBottom {
672
                    inner: resolved_pixel_value,
672
                }),
672
            )),
336
            CssProperty::MarginLeft(_) => Some(CssProperty::MarginLeft(CssPropertyValue::Exact(
336
                LayoutMarginLeft {
336
                    inner: resolved_pixel_value,
336
                },
336
            ))),
336
            CssProperty::MarginRight(_) => Some(CssProperty::MarginRight(CssPropertyValue::Exact(
336
                LayoutMarginRight {
336
                    inner: resolved_pixel_value,
336
                },
336
            ))),
1092
            CssProperty::MarginTop(_) => Some(CssProperty::MarginTop(CssPropertyValue::Exact(
1092
                LayoutMarginTop {
1092
                    inner: resolved_pixel_value,
1092
                },
1092
            ))),
1092
            CssProperty::MarginBottom(_) => Some(CssProperty::MarginBottom(
1092
                CssPropertyValue::Exact(LayoutMarginBottom {
1092
                    inner: resolved_pixel_value,
1092
                }),
1092
            )),
            CssProperty::MinWidth(_) => Some(CssProperty::MinWidth(CssPropertyValue::Exact(
                LayoutMinWidth {
                    inner: resolved_pixel_value,
                },
            ))),
336
            CssProperty::MinHeight(_) => Some(CssProperty::MinHeight(CssPropertyValue::Exact(
336
                LayoutMinHeight {
336
                    inner: resolved_pixel_value,
336
                },
336
            ))),
            CssProperty::MaxWidth(_) => Some(CssProperty::MaxWidth(CssPropertyValue::Exact(
                LayoutMaxWidth {
                    inner: resolved_pixel_value,
                },
            ))),
            CssProperty::MaxHeight(_) => Some(CssProperty::MaxHeight(CssPropertyValue::Exact(
                LayoutMaxHeight {
                    inner: resolved_pixel_value,
                },
            ))),
            CssProperty::SelectionRadius(_) => Some(CssProperty::SelectionRadius(
                CssPropertyValue::Exact(SelectionRadius {
                    inner: resolved_pixel_value,
                }),
            )),
            _ => None,
        }
26451
    }
    /// Applies user-agent (UA) CSS properties to the cascade before inheritance.
    ///
    /// UA CSS has the lowest priority in the cascade, so it should only be applied
    /// if the node doesn't already have the property from inline styles or author CSS.
    ///
    /// This is critical for text nodes: UA CSS properties (like font-weight: bold for H1)
    /// must be in the cascade maps so they can be inherited by child text nodes.
    ///
    /// Uses a bitset per node to avoid O(n²) scanning of property vecs.
8681
    pub fn apply_ua_css(&mut self, node_data: &[NodeData]) {
        use azul_css::props::property::CssPropertyType;
        use azul_css::dynamic_selector::PseudoStateType;
8681
        let node_count = node_data.len();
8681
        if node_count == 0 {
            return;
8681
        }
        // Build a bitset per node: which CssPropertyType values are already set (Normal state).
        // CssPropertyType has ~178 variants, so we need [u128; 2] per node (256 bits).
8681
        let mut prop_set: Vec<[u128; 2]> = vec![[0u128; 2]; node_count];
        // Mark properties from css_props (author CSS, Normal state)
45733
        for (node_idx, props) in self.css_props.iter_node_slices() {
50992
            for p in props.iter() {
47388
                if p.state == PseudoStateType::Normal {
47388
                    let d = p.prop_type as u16 as usize;
47388
                    if d < 128 {
47119
                        prop_set[node_idx][0] |= 1u128 << d;
47119
                    } else {
269
                        prop_set[node_idx][1] |= 1u128 << (d - 128);
269
                    }
                }
            }
        }
        // Mark properties from cascaded_props (Normal state)
45733
        for (node_idx, props) in self.cascaded_props.iter_node_slices() {
45733
            for p in props.iter() {
7077
                if p.state == PseudoStateType::Normal {
7077
                    let d = p.prop_type as u16 as usize;
7077
                    if d < 128 {
7077
                        prop_set[node_idx][0] |= 1u128 << d;
7077
                    } else {
                        prop_set[node_idx][1] |= 1u128 << (d - 128);
                    }
                }
            }
        }
        // Mark properties from inline CSS (NodeData.style, unconditional = Normal)
45733
        for (node_idx, node) in node_data.iter().enumerate() {
45733
            for (prop, conds) in node.style.iter_inline_properties() {
3298
                let is_normal = conds.as_slice().is_empty();
3298
                if is_normal {
1275
                    let d = prop.get_type() as u16 as usize;
1275
                    if d < 128 {
1258
                        prop_set[node_idx][0] |= 1u128 << d;
1258
                    } else {
17
                        prop_set[node_idx][1] |= 1u128 << (d - 128);
17
                    }
2023
                }
            }
        }
        // All UA property types that get_ua_property() may return Some for
8681
        let property_types = [
8681
            CssPropertyType::Display,
8681
            CssPropertyType::Width,
8681
            CssPropertyType::Height,
8681
            CssPropertyType::FontSize,
8681
            CssPropertyType::FontWeight,
8681
            CssPropertyType::FontFamily,
8681
            CssPropertyType::MarginTop,
8681
            CssPropertyType::MarginBottom,
8681
            CssPropertyType::MarginLeft,
8681
            CssPropertyType::MarginRight,
8681
            CssPropertyType::PaddingTop,
8681
            CssPropertyType::PaddingBottom,
8681
            CssPropertyType::PaddingLeft,
8681
            CssPropertyType::PaddingRight,
8681
            CssPropertyType::BorderTopStyle,
8681
            CssPropertyType::BorderTopWidth,
8681
            CssPropertyType::BorderTopColor,
8681
            CssPropertyType::BreakInside,
8681
            CssPropertyType::BreakAfter,
8681
            CssPropertyType::ListStyleType,
8681
            CssPropertyType::CounterReset,
8681
            CssPropertyType::TextDecoration,
8681
            CssPropertyType::TextAlign,
8681
            CssPropertyType::VerticalAlign,
8681
            CssPropertyType::Cursor,
8681
        ];
        // Apply UA CSS: only insert for property types not yet set (bitset check = O(1))
45733
        for (node_index, node) in node_data.iter().enumerate() {
45733
            let node_type = &node.node_type;
1189058
            for prop_type in &property_types {
                // Check bitset: if already set, skip entirely
1143325
                let d = *prop_type as u16 as usize;
1143325
                let has_prop = if d < 128 {
960393
                    (prop_set[node_index][0] & (1u128 << d)) != 0
                } else {
182932
                    (prop_set[node_index][1] & (1u128 << (d - 128))) != 0
                };
1143325
                if has_prop {
39783
                    continue;
1103542
                }
                // Check if UA CSS defines this property for this node type
1103542
                if let Some(ua_prop) = crate::ua_css::get_ua_property(node_type, *prop_type) {
103849
                    self.cascaded_props.push_to(node_index, StatefulCssProperty {
103849
                        state: PseudoStateType::Normal,
103849
                        prop_type: *prop_type,
103849
                        property: ua_prop.clone(),
103849
                    });
                    // Mark as set in the bitset (prevent duplicate insertion for same node)
103849
                    if d < 128 {
102209
                        prop_set[node_index][0] |= 1u128 << d;
102209
                    } else {
1640
                        prop_set[node_index][1] |= 1u128 << (d - 128);
1640
                    }
999693
                }
            }
        }
8681
    }
    /// Sort cascaded_props by (state, prop_type) and flatten into contiguous memory.
    /// Must be called after apply_ua_css() which adds entries to cascaded_props.
    pub fn sort_cascaded_props(&mut self) {
        self.cascaded_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
    }
    /// Compute inherited values for all nodes in the DOM tree.
    ///
    /// Implements CSS inheritance: walk tree depth-first, apply cascade priority
    /// (inherited → cascaded → css → inline → user), create dependency chains for
    /// relative values. Call `apply_ua_css()` before this function.
9327
    pub fn compute_inherited_values(
9327
        &mut self,
9327
        node_hierarchy: &[NodeHierarchyItem],
9327
        node_data: &[NodeData],
9327
    ) -> Vec<NodeId> {
9327
        if self.computed_values.len() < node_hierarchy.len() {
8681
            self.computed_values.resize(node_hierarchy.len(), Vec::new());
8681
        }
9327
        node_hierarchy
9327
            .iter()
9327
            .enumerate()
47875
            .filter_map(|(node_index, hierarchy_item)| {
47875
                let node_id = NodeId::new(node_index);
47875
                let parent_id = hierarchy_item.parent_id();
47875
                let parent_computed: Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>> =
47875
                    parent_id.and_then(|pid| self.computed_values.get(pid.index()).cloned());
47875
                let mut ctx = InheritanceContext {
47875
                    node_id,
47875
                    parent_id,
47875
                    computed_values: Vec::new(),
47875
                };
                // Step 1: Inherit from parent
47875
                if let Some(ref parent_values) = parent_computed {
38548
                    self.inherit_from_parent(&mut ctx, parent_values);
38548
                }
                // Steps 2-5: Apply cascade in priority order
47875
                self.apply_cascade_properties(
47875
                    &mut ctx,
47875
                    node_id,
47875
                    &parent_computed,
47875
                    node_data,
47875
                    node_index,
                );
                // Check for changes and store
47875
                let changed = self.store_if_changed(&ctx);
47875
                changed.then_some(node_id)
47875
            })
9327
            .collect()
9327
    }
    /// Inherit inheritable properties from parent node
38548
    fn inherit_from_parent(
38548
        &self,
38548
        ctx: &mut InheritanceContext,
38548
        parent_values: &[(CssPropertyType, CssPropertyWithOrigin)],
38548
    ) {
10766
        for (prop_type, prop_with_origin) in
146227
            parent_values.iter().filter(|(pt, _)| pt.is_inheritable())
        {
10766
            let entry = (*prop_type, CssPropertyWithOrigin {
10766
                property: prop_with_origin.property.clone(),
10766
                origin: CssPropertyOrigin::Inherited,
10766
            });
            // Insert into sorted vec
10766
            match ctx.computed_values.binary_search_by_key(prop_type, |(k, _)| *k) {
                Ok(idx) => ctx.computed_values[idx] = entry,
10766
                Err(idx) => ctx.computed_values.insert(idx, entry),
            }
        }
38548
    }
    /// Apply all cascade properties in priority order
47875
    fn apply_cascade_properties(
47875
        &self,
47875
        ctx: &mut InheritanceContext,
47875
        node_id: NodeId,
47875
        parent_computed: &Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
47875
        node_data: &[NodeData],
47875
        node_index: usize,
47875
    ) {
        // Step 2: Cascaded properties (UA CSS)
        {
47875
            let cascaded_slice = self.cascaded_props.get_slice(node_id.index());
113102
            for p in cascaded_slice.iter() {
113102
                if p.state == azul_css::dynamic_selector::PseudoStateType::Normal {
113102
                    if self.should_apply_cascaded(&ctx.computed_values, p.prop_type, &p.property) {
112635
                        self.process_property(ctx, &p.property, parent_computed);
112635
                    }
                }
            }
        }
        // Step 3: CSS properties (stylesheets)
        {
47875
            let css_slice = self.css_props.get_slice(node_id.index());
53134
            for p in css_slice.iter() {
47388
                if p.state == azul_css::dynamic_selector::PseudoStateType::Normal {
47388
                    self.process_property(ctx, &p.property, parent_computed);
47388
                }
            }
        }
        // Step 4: Inline CSS properties
47875
        for (prop, conds) in node_data[node_index].style.iter_inline_properties() {
            // Only apply unconditional (normal) properties
4165
            if conds.as_slice().is_empty() {
2142
                self.process_property(ctx, prop, parent_computed);
2142
            }
        }
        // Step 5: User-overridden properties
47875
        if let Some(user_props) = self.user_overridden_properties.get(node_id.index()) {
            for (_, prop) in user_props.iter() {
                self.process_property(ctx, &prop, parent_computed);
            }
47875
        }
47875
    }
    /// Check if a cascaded property should be applied
113102
    fn should_apply_cascaded(
113102
        &self,
113102
        computed: &[(CssPropertyType, CssPropertyWithOrigin)],
113102
        prop_type: CssPropertyType,
113102
        prop: &CssProperty,
113102
    ) -> bool {
        // Skip relative font-size if we already have inherited resolved value
113102
        if prop_type == CssPropertyType::FontSize {
3430
            if let Ok(idx) = computed.binary_search_by_key(&prop_type, |(k, _)| *k) {
3286
                if computed[idx].1.origin == CssPropertyOrigin::Inherited
3286
                    && Self::has_relative_font_size_unit(prop)
                {
467
                    return false;
2819
                }
144
            }
109672
        }
112635
        match computed.binary_search_by_key(&prop_type, |(k, _)| *k) {
105770
            Err(_) => true,
6865
            Ok(idx) => computed[idx].1.origin == CssPropertyOrigin::Inherited,
        }
113102
    }
    /// Process a single property: resolve and store
162165
    fn process_property(
162165
        &self,
162165
        ctx: &mut InheritanceContext,
162165
        prop: &CssProperty,
162165
        parent_computed: &Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
162165
    ) {
162165
        let prop_type = prop.get_type();
162165
        let resolved = if prop_type == CssPropertyType::FontSize {
5404
            self.resolve_font_size_property(prop, parent_computed)
        } else {
156761
            self.resolve_other_property(prop, &ctx.computed_values)
        };
162165
        let entry = (prop_type, CssPropertyWithOrigin {
162165
            property: resolved,
162165
            origin: CssPropertyOrigin::Own,
162165
        });
162165
        match ctx.computed_values.binary_search_by_key(&prop_type, |(k, _)| *k) {
7803
            Ok(idx) => ctx.computed_values[idx] = entry,
154362
            Err(idx) => ctx.computed_values.insert(idx, entry),
        }
162165
    }
    /// Resolve font-size property (uses parent's font-size as reference)
5404
    fn resolve_font_size_property(
5404
        &self,
5404
        prop: &CssProperty,
5404
        parent_computed: &Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
5404
    ) -> CssProperty {
5404
        let parent_font_size = parent_computed
5404
            .as_ref()
5404
            .and_then(|p| {
3909
                p.binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k)
3909
                    .ok()
3909
                    .map(|idx| &p[idx].1)
3909
            });
5404
        match parent_font_size {
3698
            Some(pfs) => Self::resolve_property_dependency(prop, &pfs.property).unwrap_or_else(
                || {
                    Self::resolve_font_size_to_pixels(
                        prop,
                        azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    )
                },
            ),
1706
            None => Self::resolve_font_size_to_pixels(
1706
                prop,
                azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
            ),
        }
5404
    }
    /// Resolve other properties (uses current node's font-size as reference)
156761
    fn resolve_other_property(
156761
        &self,
156761
        prop: &CssProperty,
156761
        computed: &[(CssPropertyType, CssPropertyWithOrigin)],
156761
    ) -> CssProperty {
156761
        computed
156761
            .binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k)
156761
            .ok()
156761
            .and_then(|idx| Self::resolve_property_dependency(prop, &computed[idx].1.property))
156761
            .unwrap_or_else(|| prop.clone())
156761
    }
    /// Convert font-size to absolute pixels
1706
    fn resolve_font_size_to_pixels(prop: &CssProperty, reference_px: f32) -> CssProperty {
        use azul_css::{
            css::CssPropertyValue,
            props::basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
        };
1706
        let CssProperty::FontSize(css_val) = prop else {
            return prop.clone();
        };
1706
        let Some(font_size) = css_val.get_property() else {
            return prop.clone();
        };
1706
        let resolved_px = match font_size.inner.metric {
1528
            SizeMetric::Px => font_size.inner.number.get(),
            SizeMetric::Pt => font_size.inner.number.get() * PT_TO_PX,
            SizeMetric::In => font_size.inner.number.get() * IN_TO_PX,
            SizeMetric::Cm => font_size.inner.number.get() * CM_TO_PX,
            SizeMetric::Mm => font_size.inner.number.get() * MM_TO_PX,
178
            SizeMetric::Em => font_size.inner.number.get() * reference_px,
            SizeMetric::Rem => {
                font_size.inner.number.get() * azul_css::props::basic::pixel::DEFAULT_FONT_SIZE
            }
            SizeMetric::Percent => font_size.inner.number.get() / 100.0 * reference_px,
            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
                return prop.clone();
            }
        };
1706
        CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
1706
            inner: PixelValue::px(resolved_px),
1706
        }))
1706
    }
    /// Check if font-size has relative unit (em, rem, %)
3286
    fn has_relative_font_size_unit(prop: &CssProperty) -> bool {
        use azul_css::props::basic::length::SizeMetric;
3286
        let CssProperty::FontSize(css_val) = prop else {
            return false;
        };
3286
        css_val
3286
            .get_property()
3286
            .map(|fs| {
2819
                matches!(
3286
                    fs.inner.metric,
                    SizeMetric::Em | SizeMetric::Rem | SizeMetric::Percent
                )
3286
            })
3286
            .unwrap_or(false)
3286
    }
    /// Store computed values if changed, returns true if values were updated
47875
    fn store_if_changed(&mut self, ctx: &InheritanceContext) -> bool {
47875
        let values_changed = self
47875
            .computed_values
47875
            .get(ctx.node_id.index())
47875
            .map(|old| old != &ctx.computed_values)
47875
            .unwrap_or(true);
47875
        self.computed_values[ctx.node_id.index()] = ctx.computed_values.clone();
47875
        values_changed
47875
    }
}
/// Context for computing inherited values for a single node
struct InheritanceContext {
    node_id: NodeId,
    parent_id: Option<NodeId>,
    computed_values: Vec<(CssPropertyType, CssPropertyWithOrigin)>,
}
impl CssPropertyCache {
    /// Clear the entire compact cache. Call after major DOM changes.
    pub(crate) fn invalidate_resolved_cache(&mut self) {
        self.compact_cache = None;
    }
}