///|
/// CSS border-style values, ordered by conflict resolution priority (higher wins).
pub(all) enum BorderStyle {
  None // 0 - lowest priority
  Inset // 1
  Groove // 2
  Outset // 3
  Ridge // 4
  Dotted // 5
  Dashed // 6
  Solid // 7
  Double // 8
  Hidden // 9 - highest priority (suppresses border)
} derive(Debug, Eq)

///|
pub fn BorderStyle::priority(self : BorderStyle) -> Int {
  match self {
    None => 0
    Inset => 1
    Groove => 2
    Outset => 3
    Ridge => 4
    Dotted => 5
    Dashed => 6
    Solid => 7
    Double => 8
    Hidden => 9
  }
}

///|
/// CSS border-collapse property (for tables)
pub(all) enum BorderCollapse {
  Separate // Default - borders have spacing
  Collapse // Adjacent borders merge
} derive(Debug, Eq)

///|
/// Table layout algorithm
pub(all) enum TableLayout {
  Auto // Default - column widths based on content
  Fixed // Column widths based on first row only
} derive(Debug, Eq)

///|
/// CSS caption-side property (for table captions)
pub(all) enum CaptionSide {
  Top // Caption above the table (default)
  Bottom // Caption below the table
} derive(Debug, Eq)

///|
/// CSS white-space property (for text wrapping)
pub(all) enum WhiteSpace {
  Normal // Default - collapse whitespace, wrap text
  Nowrap // Collapse whitespace, no wrapping
  Pre // Preserve whitespace, only wrap at newlines
  PreWrap // Preserve whitespace, wrap when needed
  PreLine // Collapse whitespace, wrap at newlines
} derive(Debug, Eq)

///|
/// CSS text-overflow property.
pub(all) enum TextOverflow {
  Clip
  Ellipsis
} derive(Debug, Eq)

///|
/// CSS writing-mode property (for text direction and flow)
pub(all) enum WritingMode {
  HorizontalTb // Default - left-to-right, top-to-bottom
  VerticalRl // Top-to-bottom, right-to-left (e.g., traditional Japanese)
  VerticalLr // Top-to-bottom, left-to-right (e.g., Mongolian)
} derive(Debug, Eq)

///|
/// Check if writing mode is vertical
pub fn WritingMode::is_vertical(self : WritingMode) -> Bool {
  match self {
    HorizontalTb => false
    VerticalRl | VerticalLr => true
  }
}

///|
/// Check if block direction is right-to-left (vertical-rl)
/// In vertical-rl, blocks flow from right to left
pub fn WritingMode::is_block_rtl(self : WritingMode) -> Bool {
  match self {
    VerticalRl => true
    HorizontalTb | VerticalLr => false
  }
}

///|
/// CSS direction property (for inline text direction)
pub(all) enum Direction {
  Ltr // Left-to-right (default)
  Rtl // Right-to-left (e.g., Arabic, Hebrew)
} derive(Debug, Eq)

///|
/// Check if direction is right-to-left
pub fn Direction::is_rtl(self : Direction) -> Bool {
  match self {
    Rtl => true
    Ltr => false
  }
}

///|
/// CSS text-align property
pub(all) enum TextAlign {
  Start
  End
  Left
  Right
  Center
  Justify
} derive(Debug, Eq)

///|
/// CSS scroll-snap-type axis
pub(all) enum ScrollSnapAxis {
  None
  X
  Y
  Both
} derive(Debug, Eq)

///|
/// CSS scroll-snap-type strictness
pub(all) enum ScrollSnapStrictness {
  None
  Mandatory
  Proximity
} derive(Debug, Eq)

///|
/// CSS scroll-snap-type value
pub(all) struct ScrollSnapType {
  axis : ScrollSnapAxis
  strictness : ScrollSnapStrictness
} derive(Debug, Eq)

///|
let scroll_snap_type_none : ScrollSnapType = { axis: None, strictness: None }

///|
pub fn ScrollSnapType::none() -> ScrollSnapType {
  scroll_snap_type_none
}

///|
pub fn ScrollSnapType::is_mandatory(self : ScrollSnapType) -> Bool {
  self.strictness == Mandatory
}

///|
pub fn ScrollSnapType::snaps_x(self : ScrollSnapType) -> Bool {
  match self.axis {
    X | Both => true
    None | Y => false
  }
}

///|
pub fn ScrollSnapType::snaps_y(self : ScrollSnapType) -> Bool {
  match self.axis {
    Y | Both => true
    None | X => false
  }
}

///|
/// CSS scroll-snap-align keywords
pub(all) enum ScrollSnapAlign {
  None
  Start
  End
  Center
} derive(Debug, Eq)

///|
/// CSS visibility property
pub(all) enum Visibility {
  Visible // Element is visible (default)
  Hidden // Element is invisible but takes up space
  Collapse // Same as hidden for most elements
} derive(Debug, Eq)

///|
/// CSS pointer-events property (minimum subset for browser hit testing).
pub(all) enum PointerEvents {
  Auto
  None
} derive(Debug, Eq)

///|
/// CSS margin-trim property
pub(all) enum MarginTrim {
  None
  BlockStart
  BlockEnd
  Block
  InlineStart
  InlineEnd
  Inline
} derive(Debug, Eq)

///|
/// CSS multi-column column-fill property.
pub(all) enum ColumnFill {
  Balance
  Auto
} derive(Debug, Eq)

///|
/// CSS break-before property (minimum subset for multicol tests).
pub(all) enum BreakBefore {
  Auto
  Column
  Avoid
} derive(Debug, Eq)

///|
/// CSS break-inside property (minimum subset for multicol tests).
pub(all) enum BreakInside {
  Auto
  Avoid
} derive(Debug, Eq)

///|
/// CSS column-span property.
pub(all) enum ColumnSpan {
  None
  All
} derive(Debug, Eq)

///|
pub fn MarginTrim::trim_block_start(self : MarginTrim) -> Bool {
  match self {
    BlockStart | Block => true
    None | BlockEnd | InlineStart | InlineEnd | Inline => false
  }
}

///|
pub fn MarginTrim::trim_block_end(self : MarginTrim) -> Bool {
  match self {
    BlockEnd | Block => true
    None | BlockStart | InlineStart | InlineEnd | Inline => false
  }
}

///|
/// CSS z-index property
pub(all) enum ZIndex {
  Auto // Stacking order determined by DOM order
  Value(Int) // Explicit stacking level
} derive(Debug, Eq)

///|
/// CSS transform translate value (can be length or percent)
pub(all) enum TranslateValue {
  Length(Double) // Fixed length in pixels
  Percent(Double) // Percentage of element's own dimension (0.0 - 1.0)
} derive(Debug, Eq)

///|
/// CSS transform property - list of transform functions
/// Supports a 2D subset for visual hit testing.
pub(all) struct Transform {
  translate_x : TranslateValue // translateX or first arg of translate()
  translate_y : TranslateValue // translateY or second arg of translate()
  scale_x : Double // scaleX or first arg of scale() (default 1.0)
  scale_y : Double // scaleY or second arg of scale() (default 1.0)
  rotate_degrees : Double // rotate() angle in degrees
  skew_x_degrees : Double // skewX() angle in degrees
  skew_y_degrees : Double // skewY() angle in degrees
  has_matrix : Bool // matrix() is explicitly present
  matrix_a : Double // 2D affine matrix a
  matrix_b : Double // 2D affine matrix b
  matrix_c : Double // 2D affine matrix c
  matrix_d : Double // 2D affine matrix d
  matrix_e : Double // 2D affine matrix e
  matrix_f : Double // 2D affine matrix f
  // matrix3d(): a 16-element column-major 4x4 matrix, or [] when not a 3D
  // matrix. Layout consumers that are 2D-only can ignore this.
  matrix3d : Array[Double]
} derive(Debug, Eq)

///|
/// Whether this transform carries an explicit `matrix3d()`.
pub fn Transform::has_matrix3d(self : Transform) -> Bool {
  self.matrix3d.length() == 16
}

///|
/// Shared immutable identity transform. `Transform` has no mutable fields, so a
/// single shared instance is safe and avoids re-allocating the identity on every
/// `Transform::none()` call (one per default style).
let transform_identity : Transform = {
  translate_x: Length(0.0),
  translate_y: Length(0.0),
  scale_x: 1.0,
  scale_y: 1.0,
  rotate_degrees: 0.0,
  skew_x_degrees: 0.0,
  skew_y_degrees: 0.0,
  has_matrix: false,
  matrix_a: 1.0,
  matrix_b: 0.0,
  matrix_c: 0.0,
  matrix_d: 1.0,
  matrix_e: 0.0,
  matrix_f: 0.0,
  matrix3d: [],
}

///|
/// Create a none/identity transform
pub fn Transform::none() -> Transform {
  transform_identity
}

///|
/// Check if transform is identity (no visual change)
pub fn Transform::is_none(self : Transform) -> Bool {
  let translate_none = match (self.translate_x, self.translate_y) {
    (Length(0.0), Length(0.0)) => true
    (Percent(0.0), Percent(0.0)) => true
    _ => false
  }
  let scale_none = self.scale_x == 1.0 && self.scale_y == 1.0
  let rotate_none = self.rotate_degrees == 0.0
  let skew_none = self.skew_x_degrees == 0.0 && self.skew_y_degrees == 0.0
  let matrix_none = !self.has_matrix ||
    (
      self.matrix_a == 1.0 &&
      self.matrix_b == 0.0 &&
      self.matrix_c == 0.0 &&
      self.matrix_d == 1.0 &&
      self.matrix_e == 0.0 &&
      self.matrix_f == 0.0
    )
  translate_none && scale_none && rotate_none && skew_none && matrix_none
}

///|
/// Check if transform has scale applied
pub fn Transform::has_scale(self : Transform) -> Bool {
  self.scale_x != 1.0 || self.scale_y != 1.0
}

///|
/// Check if transform has skew applied
pub fn Transform::has_skew(self : Transform) -> Bool {
  self.skew_x_degrees != 0.0 || self.skew_y_degrees != 0.0
}

///|
/// Check if transform has an explicit non-identity matrix applied
pub fn Transform::has_affine_matrix(self : Transform) -> Bool {
  self.has_matrix &&
  !(self.matrix_a == 1.0 &&
  self.matrix_b == 0.0 &&
  self.matrix_c == 0.0 &&
  self.matrix_d == 1.0 &&
  self.matrix_e == 0.0 &&
  self.matrix_f == 0.0)
}

///|
/// Return a quarter-turn rotation count (0..3), or -1 for other angles.
pub fn Transform::quarter_turns(self : Transform) -> Int {
  let raw_turns = (self.rotate_degrees / 90.0).round().to_int()
  let snapped_degrees = raw_turns.to_double() * 90.0
  if (self.rotate_degrees - snapped_degrees).abs() > 0.0001 {
    return -1
  }
  let mut turns = raw_turns % 4
  if turns < 0 {
    turns = turns + 4
  }
  turns
}

///|
/// Compute translate X offset given element width
pub fn Transform::compute_translate_x(
  self : Transform,
  element_width : Double,
) -> Double {
  match self.translate_x {
    Length(v) => v
    Percent(p) => element_width * p
  }
}

///|
/// Compute translate Y offset given element height
pub fn Transform::compute_translate_y(
  self : Transform,
  element_height : Double,
) -> Double {
  match self.translate_y {
    Length(v) => v
    Percent(p) => element_height * p
  }
}

///|
/// CSS clip-path property subset used by paint and hit testing.
/// Shape values are pixels; negative values encode percentages as fractions.
pub(all) enum ClipPath {
  None
  Inset(Double, Double, Double, Double) // top, right, bottom, left
  Rect(Double, Double, Double, Double) // top, right, bottom, left coordinates
  Xywh(Double, Double, Double, Double) // x, y, width, height
  Circle(Double, Double, Double) // radius, center-x, center-y
  Ellipse(Double, Double, Double, Double) // radius-x, radius-y, center-x, center-y
  Polygon(Array[(Double, Double)])
} derive(Debug, Eq)

///|
pub fn ClipPath::none() -> ClipPath {
  None
}

///|
/// CSS contain property - uses flags since multiple values can be combined
/// e.g., "contain: layout paint" or "contain: strict"
pub(all) struct Contain {
  size : Bool // Size containment - element size computed independently
  inline_size : Bool // Inline-size containment only (mutually exclusive with size)
  layout : Bool // Layout containment - isolates internal layout
  paint : Bool // Paint containment - clips overflow, creates stacking context
  style : Bool // Style containment - scopes counters/quotes
} derive(Debug, Eq)

///|
/// Shared immutable `Contain` sentinels. `Contain` has no mutable fields, so a
/// single shared instance per keyword is safe and avoids re-allocating on every
/// access (`Contain::none()` runs once per default style).
let contain_none : Contain = {
  size: false,
  inline_size: false,
  layout: false,
  paint: false,
  style: false,
}

///|
let contain_strict : Contain = {
  size: true,
  inline_size: false,
  layout: true,
  paint: true,
  style: true,
}

///|
let contain_content : Contain = {
  size: false,
  inline_size: false,
  layout: true,
  paint: true,
  style: true,
}

///|
pub fn Contain::none() -> Contain {
  contain_none
}

///|
/// strict = size + layout + paint + style
pub fn Contain::strict() -> Contain {
  contain_strict
}

///|
/// content = layout + paint + style (no size)
pub fn Contain::content() -> Contain {
  contain_content
}

///|
pub fn Contain::has_containment(self : Contain) -> Bool {
  self.size || self.inline_size || self.layout || self.paint || self.style
}

///|
/// Whether containment suppresses intrinsic contribution on physical width.
pub fn Style::suppresses_intrinsic_width(self : Style) -> Bool {
  self.contain.size ||
  (self.contain.inline_size && !self.writing_mode.is_vertical())
}

///|
/// Whether containment suppresses intrinsic contribution on physical height.
pub fn Style::suppresses_intrinsic_height(self : Style) -> Bool {
  self.contain.size ||
  (self.contain.inline_size && self.writing_mode.is_vertical())
}

///|
/// Fallback intrinsic contribution on physical width from contain-intrinsic-size.
pub fn Style::contained_intrinsic_width_fallback(self : Style) -> Double {
  if self.writing_mode.is_vertical() {
    match self.contain_intrinsic_block_size {
      Some(v) => if v > 0.0 { v } else { 0.0 }
      None => 0.0
    }
  } else {
    match self.contain_intrinsic_inline_size {
      Some(v) => if v > 0.0 { v } else { 0.0 }
      None => 0.0
    }
  }
}

///|
/// Fallback intrinsic contribution on physical height from contain-intrinsic-size.
pub fn Style::contained_intrinsic_height_fallback(self : Style) -> Double {
  if self.writing_mode.is_vertical() {
    match self.contain_intrinsic_inline_size {
      Some(v) => if v > 0.0 { v } else { 0.0 }
      None => 0.0
    }
  } else {
    match self.contain_intrinsic_block_size {
      Some(v) => if v > 0.0 { v } else { 0.0 }
      None => 0.0
    }
  }
}

///|
/// Whether this style enables CSS multi-column layout.
pub fn Style::has_multicol(self : Style) -> Bool {
  match self.column_count {
    // `column-count: 1` still establishes a multicol container; overflowing
    // content may fragment into additional columns.
    Some(v) => v > 0
    None =>
      match self.column_width {
        Length(v) => v > 0.0
        Percent(v) => v > 0.0
        Calc(px, pct) => px > 0.0 || pct > 0.0
        MathFn(_, _) => true
        Auto | MinContent | MaxContent | FitContent(_) => false
      }
  }
}

///|
/// Vertical alignment for inline elements
pub(all) enum VerticalAlign {
  Baseline // Align baseline (default, simplified to bottom for now)
  Top // Align to top of line box
  Middle // Align middle
  Bottom // Align to bottom of line box
  TextTop // Align to top of parent's font (simplified to Top)
  TextBottom // Align to bottom of parent's font (simplified to Bottom)
  Sub // Subscript position
  Super // Superscript position
} derive(Debug, Eq)

///|
pub impl Show for BorderStyle with fn output(self, logger) {
  let name = match self {
    None => "None"
    Inset => "Inset"
    Groove => "Groove"
    Outset => "Outset"
    Ridge => "Ridge"
    Dotted => "Dotted"
    Dashed => "Dashed"
    Solid => "Solid"
    Double => "Double"
    Hidden => "Hidden"
  }
  logger.write_string(name)
}

///|
pub impl Show for BorderCollapse with fn output(self, logger) {
  let name = match self {
    Separate => "Separate"
    Collapse => "Collapse"
  }
  logger.write_string(name)
}

///|
pub impl Show for TableLayout with fn output(self, logger) {
  let name = match self {
    Auto => "Auto"
    Fixed => "Fixed"
  }
  logger.write_string(name)
}

///|
pub impl Show for CaptionSide with fn output(self, logger) {
  let name = match self {
    Top => "Top"
    Bottom => "Bottom"
  }
  logger.write_string(name)
}

///|
pub impl Show for WhiteSpace with fn output(self, logger) {
  let name = match self {
    Normal => "Normal"
    Nowrap => "Nowrap"
    Pre => "Pre"
    PreWrap => "PreWrap"
    PreLine => "PreLine"
  }
  logger.write_string(name)
}

///|
pub impl Show for TextOverflow with fn output(self, logger) {
  let name = match self {
    Clip => "Clip"
    Ellipsis => "Ellipsis"
  }
  logger.write_string(name)
}

///|
pub impl Show for WritingMode with fn output(self, logger) {
  let name = match self {
    HorizontalTb => "HorizontalTb"
    VerticalRl => "VerticalRl"
    VerticalLr => "VerticalLr"
  }
  logger.write_string(name)
}

///|
pub impl Show for Direction with fn output(self, logger) {
  let name = match self {
    Ltr => "Ltr"
    Rtl => "Rtl"
  }
  logger.write_string(name)
}

///|
pub impl Show for TextAlign with fn output(self, logger) {
  let name = match self {
    Start => "Start"
    End => "End"
    Left => "Left"
    Right => "Right"
    Center => "Center"
    Justify => "Justify"
  }
  logger.write_string(name)
}

///|
pub impl Show for ScrollSnapAxis with fn output(self, logger) {
  let name = match self {
    None => "None"
    X => "X"
    Y => "Y"
    Both => "Both"
  }
  logger.write_string(name)
}

///|
pub impl Show for ScrollSnapStrictness with fn output(self, logger) {
  let name = match self {
    None => "None"
    Mandatory => "Mandatory"
    Proximity => "Proximity"
  }
  logger.write_string(name)
}

///|
pub impl Show for ScrollSnapType with fn output(self, logger) {
  logger.write_string("{axis: ")
  logger.write_string(self.axis.to_string())
  logger.write_string(", strictness: ")
  logger.write_string(self.strictness.to_string())
  logger.write_string("}")
}

///|
pub impl Show for ScrollSnapAlign with fn output(self, logger) {
  let name = match self {
    None => "None"
    Start => "Start"
    End => "End"
    Center => "Center"
  }
  logger.write_string(name)
}

///|
pub impl Show for Visibility with fn output(self, logger) {
  let name = match self {
    Visible => "Visible"
    Hidden => "Hidden"
    Collapse => "Collapse"
  }
  logger.write_string(name)
}

///|
pub impl Show for PointerEvents with fn output(self, logger) {
  let name = match self {
    Auto => "Auto"
    None => "None"
  }
  logger.write_string(name)
}

///|
pub impl Show for MarginTrim with fn output(self, logger) {
  let name = match self {
    None => "None"
    BlockStart => "BlockStart"
    BlockEnd => "BlockEnd"
    Block => "Block"
    InlineStart => "InlineStart"
    InlineEnd => "InlineEnd"
    Inline => "Inline"
  }
  logger.write_string(name)
}

///|
pub impl Show for ColumnFill with fn output(self, logger) {
  let name = match self {
    Balance => "Balance"
    Auto => "Auto"
  }
  logger.write_string(name)
}

///|
pub impl Show for BreakBefore with fn output(self, logger) {
  let name = match self {
    Auto => "Auto"
    Column => "Column"
    Avoid => "Avoid"
  }
  logger.write_string(name)
}

///|
pub impl Show for BreakInside with fn output(self, logger) {
  let name = match self {
    Auto => "Auto"
    Avoid => "Avoid"
  }
  logger.write_string(name)
}

///|
pub impl Show for ColumnSpan with fn output(self, logger) {
  let name = match self {
    None => "None"
    All => "All"
  }
  logger.write_string(name)
}

///|
pub impl Show for ZIndex with fn output(self, logger) {
  let text = match self {
    Auto => "Auto"
    Value(v) => "Value(\{v})"
  }
  logger.write_string(text)
}

///|
pub impl Show for TranslateValue with fn output(self, logger) {
  let text = match self {
    Length(v) => "Length(\{v})"
    Percent(v) => "Percent(\{v})"
  }
  logger.write_string(text)
}

///|
pub impl Show for Transform with fn output(self, logger) {
  logger.write_string("{translate_x: ")
  logger.write_string(self.translate_x.to_string())
  logger.write_string(", translate_y: ")
  logger.write_string(self.translate_y.to_string())
  logger.write_string(", scale_x: ")
  logger.write_string(self.scale_x.to_string())
  logger.write_string(", scale_y: ")
  logger.write_string(self.scale_y.to_string())
  logger.write_string(", rotate_degrees: ")
  logger.write_string(self.rotate_degrees.to_string())
  logger.write_string(", skew_x_degrees: ")
  logger.write_string(self.skew_x_degrees.to_string())
  logger.write_string(", skew_y_degrees: ")
  logger.write_string(self.skew_y_degrees.to_string())
  logger.write_string(", has_matrix: ")
  logger.write_string(self.has_matrix.to_string())
  logger.write_string(", matrix: [")
  logger.write_string(self.matrix_a.to_string())
  logger.write_string(", ")
  logger.write_string(self.matrix_b.to_string())
  logger.write_string(", ")
  logger.write_string(self.matrix_c.to_string())
  logger.write_string(", ")
  logger.write_string(self.matrix_d.to_string())
  logger.write_string(", ")
  logger.write_string(self.matrix_e.to_string())
  logger.write_string(", ")
  logger.write_string(self.matrix_f.to_string())
  logger.write_string("]")
  if self.matrix3d.length() == 16 {
    logger.write_string(", matrix3d: [")
    for i = 0; i < 16; i = i + 1 {
      if i > 0 {
        logger.write_string(", ")
      }
      logger.write_string(self.matrix3d[i].to_string())
    }
    logger.write_string("]")
  }
  logger.write_string("}")
}

///|
pub impl Show for Contain with fn output(self, logger) {
  logger.write_string("{size: ")
  logger.write_string(self.size.to_string())
  logger.write_string(", inline_size: ")
  logger.write_string(self.inline_size.to_string())
  logger.write_string(", layout: ")
  logger.write_string(self.layout.to_string())
  logger.write_string(", paint: ")
  logger.write_string(self.paint.to_string())
  logger.write_string(", style: ")
  logger.write_string(self.style.to_string())
  logger.write_string("}")
}

///|
pub impl Show for VerticalAlign with fn output(self, logger) {
  let name = match self {
    Baseline => "Baseline"
    Top => "Top"
    Middle => "Middle"
    Bottom => "Bottom"
    TextTop => "TextTop"
    TextBottom => "TextBottom"
    Sub => "Sub"
    Super => "Super"
  }
  logger.write_string(name)
}

///|
pub struct BoxShadow {
  inset : Bool
  offset_x : Double
  offset_y : Double
  blur_radius : Double
  spread_radius : Double
  color : @types.Color
} derive(Debug, Eq)

///|
pub fn BoxShadow::new(
  inset : Bool,
  offset_x : Double,
  offset_y : Double,
  blur_radius : Double,
  spread_radius : Double,
  color : @types.Color,
) -> BoxShadow {
  { inset, offset_x, offset_y, blur_radius, spread_radius, color }
}

///|
/// CSS Style definition for layout computation
/// All fields are mutable to support Yoga-like setter API
pub(all) struct Style {
  mut display : @types.Display
  mut position : @types.Position
  mut float : @types.Float
  mut clear : @types.Clear
  // Box sizing
  mut box_sizing : @types.BoxSizing
  // Overflow
  mut overflow_x : @types.Overflow
  mut overflow_y : @types.Overflow
  // Scroll snap
  mut scroll_snap_type : ScrollSnapType
  mut scroll_snap_align_x : ScrollSnapAlign
  mut scroll_snap_align_y : ScrollSnapAlign
  // Sizing
  mut width : @types.Dimension
  mut height : @types.Dimension
  mut min_width : @types.Dimension
  mut min_height : @types.Dimension
  mut max_width : @types.Dimension
  mut max_height : @types.Dimension
  // Box model
  mut margin : @types.Rect[@types.Dimension]
  mut padding : @types.Rect[@types.Dimension]
  mut border : @types.Rect[@types.Dimension]
  mut border_style : @types.Rect[BorderStyle]
  mut border_color : @types.Rect[@types.Color]
  mut margin_trim : MarginTrim
  // Flexbox
  mut flex_direction : @types.FlexDirection
  mut flex_wrap : @types.FlexWrap
  mut justify_content : @types.Alignment
  mut align_items : @types.Alignment
  mut align_content : @types.Alignment
  // Whether overflow alignment requested `unsafe` position.
  // Unqualified values default to safe behavior for initial scroll positioning.
  mut justify_content_unsafe : Bool
  mut align_content_unsafe : Bool
  mut align_self : @types.AlignSelf
  mut flex_grow : Double
  mut flex_shrink : Double
  mut flex_basis : @types.Dimension
  mut order : Int // Flex item order (default 0)
  // Gap
  mut row_gap : @types.Dimension
  mut column_gap : @types.Dimension
  mut column_gap_is_normal : Bool
  // Multi-column layout
  mut column_count : Int? // None = auto
  mut column_width : @types.Dimension // auto or fixed width
  mut column_fill : ColumnFill
  // Aspect ratio (width / height)
  mut aspect_ratio : Double?
  // Inset for position: absolute (left, right, top, bottom)
  mut inset : @types.Rect[@types.Dimension]
  // Grid container properties
  mut grid_template_rows : Array[@types.TrackSizingFunction]
  mut grid_template_columns : Array[@types.TrackSizingFunction]
  mut grid_auto_rows : Array[@types.TrackSizingFunction]
  mut grid_auto_columns : Array[@types.TrackSizingFunction]
  mut grid_auto_flow : @types.GridAutoFlow
  // Named lines declared in grid-template-columns/rows. Index i holds the names
  // declared at grid line i (0-based), so the array length is tracks + 1.
  // e.g. `[a] 1fr [b c] 2fr` => [["a"], ["b", "c"], []]
  mut grid_template_column_line_names : Array[Array[String]]
  mut grid_template_row_line_names : Array[Array[String]]
  // Axis kind: Explicit track list (default), subgrid, or masonry.
  mut grid_template_columns_kind : @types.GridTemplateKind
  mut grid_template_rows_kind : @types.GridTemplateKind
  // Grid template areas (each string is a row, e.g. ["header header", "sidebar main"])
  mut grid_template_areas : Array[String]
  // Grid item properties
  mut grid_row : @types.GridLine
  mut grid_column : @types.GridLine
  mut grid_area : String? // Named area for item placement
  // justify-items for grid (different from justify-content)
  mut justify_items : @types.Alignment
  // justify-self for grid items (horizontal self-alignment)
  mut justify_self : @types.AlignSelf
  // Font properties (for baseline calculation)
  mut font_size : Double // Computed font size in pixels (default 16.0)
  mut font_weight : Double // Computed font weight (100-900, default 400)
  mut font_family : String // Computed font family (default "")
  mut line_height : Double // Computed line height in pixels (default = font_size)
  // Text wrapping and direction
  mut text_align : TextAlign
  mut white_space : WhiteSpace // white-space: normal | nowrap | pre | pre-wrap | pre-line
  mut text_overflow : TextOverflow // text-overflow: clip | ellipsis
  mut writing_mode : WritingMode // writing-mode: horizontal-tb | vertical-rl | vertical-lr
  mut direction : Direction // direction: ltr | rtl (inline text direction)
  // Vertical alignment for inline elements
  mut vertical_align : VerticalAlign
  // Clip (legacy, deprecated but widely used for a11y)
  mut clip : @types.ClipRect
  // Clip path (minimum paint/hit-test subset)
  mut clip_path : ClipPath
  // Paint properties (visual, not layout)
  mut visibility : Visibility
  mut pointer_events : PointerEvents
  mut z_index : ZIndex
  mut opacity : Double // 0.0 = transparent, 1.0 = opaque
  // Color properties (resolved RGBA values)
  mut color : @types.Color // Text color (inherited)
  mut background_color : @types.Color // Background color (not inherited)
  mut background_image : @types.BackgroundImage // Background image (gradient etc.)
  mut box_shadows : Array[BoxShadow]
  // Containment (for layout optimization)
  mut contain : Contain
  // Fragmentation
  mut break_before : BreakBefore
  mut break_after : BreakBefore
  mut break_inside : BreakInside
  mut column_span : ColumnSpan
  // contain-intrinsic-* fallback sizes (content-box lengths in px)
  mut contain_intrinsic_inline_size : Double?
  mut contain_intrinsic_block_size : Double?
  // CSS transform (visual offset, applied in paint phase)
  mut transform : Transform
  // Individual transform properties (`translate` / `scale` / `rotate`),
  // composed into one Transform. Distinct from the `transform` shorthand.
  individual_transform : Transform
  // Animation properties (parallel longhand lists; index = animation in the
  // comma-separated list). Set by the `animation` shorthand or the longhands.
  animation_name : Array[String]
  animation_duration : Array[Double] // seconds
  animation_timing_function : Array[@types.Easing]
  animation_delay : Array[Double] // seconds
  animation_iteration_count : Array[@types.AnimationIterationCount]
  animation_direction : Array[@types.AnimationDirection]
  animation_fill_mode : Array[@types.AnimationFillMode]
  animation_play_state : Array[@types.AnimationPlayState]
  // Transition properties (parallel longhand lists).
  transition_property : Array[String]
  transition_duration : Array[Double] // seconds
  transition_timing_function : Array[@types.Easing]
  transition_delay : Array[Double] // seconds
  // CSS filter / backdrop-filter presence flag. Layout only cares whether
  // such a property establishes a containing block for abspos descendants,
  // not the actual filter functions.
  mut has_filter : Bool
  // CSS zoom (affects layout scaling, inherited)
  mut zoom : Double // zoom factor (1.0 = 100%, 2.0 = 200%)
  // Table properties
  mut border_spacing : Double // border-spacing horizontal in pixels
  mut border_spacing_vertical : Double // border-spacing vertical in pixels
  mut border_collapse : BorderCollapse // border-collapse: separate | collapse
  mut table_layout : TableLayout // table-layout: auto | fixed
  mut caption_side : CaptionSide // caption-side: top | bottom
  // Table cell properties (from HTML attributes)
  mut rowspan : Int // rowspan attribute (default 1)
  mut colspan : Int // colspan attribute (default 1)
  // Border radius (computed px values for rounded corners)
  mut border_top_left_radius : Double
  mut border_top_right_radius : Double
  mut border_bottom_right_radius : Double
  mut border_bottom_left_radius : Double
  // Text decoration
  mut text_decoration_underline : Bool
  mut text_decoration_line_through : Bool
  mut text_decoration_overline : Bool
  // Letter/word spacing
  mut letter_spacing : Double // in pixels, 0 = normal
  mut word_spacing : Double // in pixels, 0 = normal
} derive(Debug, Eq)

///|
/// Compare only fields that can affect layout results.
///
/// Paint-only fields such as colors, backgrounds, shadows, opacity,
/// pointer-events, z-index, and clipping are intentionally ignored so callers
/// can distinguish layout invalidation from repaint-only changes.
pub fn Style::layout_eq(self : Style, other : Style) -> Bool {
  self.display == other.display &&
  self.position == other.position &&
  self.float == other.float &&
  self.clear == other.clear &&
  self.box_sizing == other.box_sizing &&
  self.overflow_x == other.overflow_x &&
  self.overflow_y == other.overflow_y &&
  self.width == other.width &&
  self.height == other.height &&
  self.min_width == other.min_width &&
  self.min_height == other.min_height &&
  self.max_width == other.max_width &&
  self.max_height == other.max_height &&
  self.margin == other.margin &&
  self.padding == other.padding &&
  self.border == other.border &&
  self.border_style == other.border_style &&
  self.margin_trim == other.margin_trim &&
  self.flex_direction == other.flex_direction &&
  self.flex_wrap == other.flex_wrap &&
  self.justify_content == other.justify_content &&
  self.align_items == other.align_items &&
  self.align_content == other.align_content &&
  self.justify_content_unsafe == other.justify_content_unsafe &&
  self.align_content_unsafe == other.align_content_unsafe &&
  self.align_self == other.align_self &&
  self.flex_grow == other.flex_grow &&
  self.flex_shrink == other.flex_shrink &&
  self.flex_basis == other.flex_basis &&
  self.order == other.order &&
  self.row_gap == other.row_gap &&
  self.column_gap == other.column_gap &&
  self.column_gap_is_normal == other.column_gap_is_normal &&
  self.column_count == other.column_count &&
  self.column_width == other.column_width &&
  self.column_fill == other.column_fill &&
  self.aspect_ratio == other.aspect_ratio &&
  self.inset == other.inset &&
  self.grid_template_rows == other.grid_template_rows &&
  self.grid_template_columns == other.grid_template_columns &&
  self.grid_auto_rows == other.grid_auto_rows &&
  self.grid_auto_columns == other.grid_auto_columns &&
  self.grid_auto_flow == other.grid_auto_flow &&
  self.grid_template_column_line_names == other.grid_template_column_line_names &&
  self.grid_template_row_line_names == other.grid_template_row_line_names &&
  self.grid_template_columns_kind == other.grid_template_columns_kind &&
  self.grid_template_rows_kind == other.grid_template_rows_kind &&
  self.grid_template_areas == other.grid_template_areas &&
  self.grid_row == other.grid_row &&
  self.grid_column == other.grid_column &&
  self.grid_area == other.grid_area &&
  self.justify_items == other.justify_items &&
  self.justify_self == other.justify_self &&
  self.font_size == other.font_size &&
  self.font_weight == other.font_weight &&
  self.font_family == other.font_family &&
  self.line_height == other.line_height &&
  self.text_align == other.text_align &&
  self.white_space == other.white_space &&
  self.text_overflow == other.text_overflow &&
  self.writing_mode == other.writing_mode &&
  self.direction == other.direction &&
  self.vertical_align == other.vertical_align &&
  self.visibility == other.visibility &&
  self.contain == other.contain &&
  self.break_before == other.break_before &&
  self.break_after == other.break_after &&
  self.break_inside == other.break_inside &&
  self.column_span == other.column_span &&
  self.contain_intrinsic_inline_size == other.contain_intrinsic_inline_size &&
  self.contain_intrinsic_block_size == other.contain_intrinsic_block_size &&
  self.transform == other.transform &&
  self.individual_transform == other.individual_transform &&
  self.has_filter == other.has_filter &&
  self.zoom == other.zoom &&
  self.border_spacing == other.border_spacing &&
  self.border_spacing_vertical == other.border_spacing_vertical &&
  self.border_collapse == other.border_collapse &&
  self.table_layout == other.table_layout &&
  self.caption_side == other.caption_side &&
  self.rowspan == other.rowspan &&
  self.colspan == other.colspan &&
  self.letter_spacing == other.letter_spacing &&
  self.word_spacing == other.word_spacing
}

///|
pub fn Style::default() -> Style {
  {
    display: Block,
    position: Static,
    float: None,
    clear: None,
    box_sizing: ContentBox,
    overflow_x: Visible,
    overflow_y: Visible,
    scroll_snap_type: ScrollSnapType::none(),
    scroll_snap_align_x: None,
    scroll_snap_align_y: None,
    width: Auto,
    height: Auto,
    min_width: Auto,
    min_height: Auto,
    max_width: Auto,
    max_height: Auto,
    margin: {
      left: Length(0.0),
      right: Length(0.0),
      top: Length(0.0),
      bottom: Length(0.0),
    },
    border_style: { left: None, right: None, top: None, bottom: None },
    border_color: {
      left: @types.Color::transparent(),
      right: @types.Color::transparent(),
      top: @types.Color::transparent(),
      bottom: @types.Color::transparent(),
    },
    padding: {
      left: Length(0.0),
      right: Length(0.0),
      top: Length(0.0),
      bottom: Length(0.0),
    },
    border: {
      left: Length(0.0),
      right: Length(0.0),
      top: Length(0.0),
      bottom: Length(0.0),
    },
    margin_trim: None,
    flex_direction: Row,
    flex_wrap: NoWrap,
    justify_content: Start,
    align_items: Stretch,
    align_content: Stretch,
    justify_content_unsafe: false,
    align_content_unsafe: false,
    align_self: Auto,
    flex_grow: 0.0,
    flex_shrink: 1.0,
    flex_basis: Auto,
    order: 0,
    row_gap: Length(0.0),
    column_gap: Length(0.0),
    column_gap_is_normal: true,
    column_count: None,
    column_width: Auto,
    column_fill: Balance,
    aspect_ratio: None,
    inset: { left: Auto, right: Auto, top: Auto, bottom: Auto },
    grid_template_rows: [],
    grid_template_columns: [],
    grid_auto_rows: [],
    grid_auto_columns: [],
    grid_auto_flow: Row,
    grid_template_column_line_names: [],
    grid_template_row_line_names: [],
    grid_template_columns_kind: Explicit,
    grid_template_rows_kind: Explicit,
    grid_template_areas: [],
    grid_row: @types.GridLine::auto(),
    grid_column: @types.GridLine::auto(),
    grid_area: None,
    justify_items: Stretch,
    justify_self: Auto,
    font_size: 16.0,
    font_weight: 400.0,
    font_family: "",
    line_height: 19.2, // 16.0 * 1.2 (CSS initial: normal)
    text_align: Start,
    white_space: Normal,
    text_overflow: Clip,
    writing_mode: HorizontalTb,
    direction: Ltr,
    vertical_align: Baseline,
    clip: Auto,
    clip_path: ClipPath::none(),
    visibility: Visible,
    pointer_events: Auto,
    z_index: Auto,
    opacity: 1.0,
    color: @types.Color::black(),
    background_color: @types.Color::transparent(),
    background_image: None,
    box_shadows: [],
    contain: Contain::none(),
    break_before: Auto,
    break_after: Auto,
    break_inside: Auto,
    column_span: None,
    contain_intrinsic_inline_size: None,
    contain_intrinsic_block_size: None,
    transform: Transform::none(),
    individual_transform: Transform::none(),
    animation_name: [],
    animation_duration: [],
    animation_timing_function: [],
    animation_delay: [],
    animation_iteration_count: [],
    animation_direction: [],
    animation_fill_mode: [],
    animation_play_state: [],
    transition_property: [],
    transition_duration: [],
    transition_timing_function: [],
    transition_delay: [],
    has_filter: false,
    zoom: 1.0,
    border_spacing: 0.0,
    border_spacing_vertical: 0.0,
    border_collapse: Separate,
    table_layout: Auto,
    caption_side: Top,
    rowspan: 1,
    colspan: 1,
    border_top_left_radius: 0.0,
    border_top_right_radius: 0.0,
    border_bottom_right_radius: 0.0,
    border_bottom_left_radius: 0.0,
    text_decoration_underline: false,
    text_decoration_line_through: false,
    text_decoration_overline: false,
    letter_spacing: 0.0,
    word_spacing: 0.0,
  }
}