///|
/// CSS Computed Values
/// Resolves cascaded values to computed values with inheritance

///|
/// Computed values context for resolving relative units
pub(all) struct ComputeContext {
  /// Parent's computed style (for inheritance)
  parent_style : @style.Style?
  /// Root font size (for rem units)
  root_font_size : Double
  /// Current font size (for em units)
  font_size : Double
  /// Viewport width (for vw units)
  viewport_width : Double
  /// Viewport height (for vh units)
  viewport_height : Double
  /// CSS Custom Properties (variables) - inherited from parent and local
  custom_properties : Map[String, String]
}

///|
/// Reserved implementation sentinel, not a supported author custom property.
let internal_effective_color_scheme_key = "--milky-css-effective-color-scheme"

///|
fn view_to_string(v : StringView) -> String {
  let sb = StringBuilder::new()
  sb.write_stringview(v)
  sb.to_string()
}

///|
/// Allocation-free prefix test for the hot cascade/compute path.
/// MoonBit's `String::has_prefix` runs a Boyer-Moore-Horspool search that
/// allocates a skip table (~850 bytes) per call; comparing UTF-16 code units
/// directly avoids that. This runs per declaration (the custom-property `--`
/// test) for every element, so the per-call allocation showed up in profiling.
fn starts_with(s : String, prefix : String) -> Bool {
  let pl = prefix.length()
  if pl > s.length() {
    return false
  }
  for i in 0.. Bool {
  let pl = prefix.length()
  if pl > s.length() {
    return false
  }
  for i in 0.. ComputeContext {
  {
    parent_style: None,
    root_font_size: 16.0,
    font_size: 16.0,
    viewport_width: 1920.0,
    viewport_height: 1080.0,
    custom_properties: Map([]),
  }
}

///|
fn ch_unit_ratio(ctx : ComputeContext) -> Double {
  match ctx.parent_style {
    Some(style) =>
      if style.font_family.to_lower().contains("ahem") {
        1.0
      } else {
        0.5
      }
    None => 0.5
  }
}

///|
pub fn ComputeContext::with_parent(parent : @style.Style) -> ComputeContext {
  {
    parent_style: Some(parent),
    root_font_size: 16.0,
    font_size: parent.font_size,
    viewport_width: 1920.0,
    viewport_height: 1080.0,
    custom_properties: Map([]),
  }
}

///|
/// Create context with parent style and inherited custom properties
pub fn ComputeContext::with_parent_and_vars(
  parent : @style.Style,
  parent_vars : Map[String, String],
) -> ComputeContext {
  // Copy parent vars for inheritance (custom properties inherit by default)
  let vars : Map[String, String] = Map([])
  for k, v in parent_vars {
    vars[k] = v
  }
  {
    parent_style: Some(parent),
    root_font_size: 16.0,
    font_size: parent.font_size,
    viewport_width: 1920.0,
    viewport_height: 1080.0,
    custom_properties: vars,
  }
}

///|
/// Create context with custom viewport dimensions
pub fn ComputeContext::with_viewport(
  viewport_width : Double,
  viewport_height : Double,
) -> ComputeContext {
  {
    parent_style: None,
    root_font_size: 16.0,
    font_size: 16.0,
    viewport_width,
    viewport_height,
    custom_properties: Map([]),
  }
}

///|
/// Mutable style builder for collecting computed values
priv struct StyleBuilder {
  mut display : @types.Display
  mut position : @types.Position
  mut float : @types.Float
  mut clear : @types.Clear
  mut box_sizing : @types.BoxSizing
  mut overflow_x : @types.Overflow
  mut overflow_y : @types.Overflow
  mut scroll_snap_type : @style.ScrollSnapType
  mut scroll_snap_align_x : @style.ScrollSnapAlign
  mut scroll_snap_align_y : @style.ScrollSnapAlign
  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
  mut margin_top : @types.Dimension
  mut margin_right : @types.Dimension
  mut margin_bottom : @types.Dimension
  mut margin_left : @types.Dimension
  mut margin_trim : @style.MarginTrim
  mut padding_top : @types.Dimension
  mut padding_right : @types.Dimension
  mut padding_bottom : @types.Dimension
  mut padding_left : @types.Dimension
  mut border_top : @types.Dimension
  mut border_right : @types.Dimension
  mut border_bottom : @types.Dimension
  mut border_left : @types.Dimension
  mut border_style_top : @style.BorderStyle
  mut border_style_right : @style.BorderStyle
  mut border_style_bottom : @style.BorderStyle
  mut border_style_left : @style.BorderStyle
  mut border_color_top : @types.Color
  mut border_color_right : @types.Color
  mut border_color_bottom : @types.Color
  mut border_color_left : @types.Color
  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
  mut justify_content_unsafe : Bool
  mut align_content_unsafe : Bool
  mut align_self : @types.AlignSelf
  mut justify_self : @types.AlignSelf
  mut justify_items : @types.Alignment
  mut flex_grow : Double
  mut flex_shrink : Double
  mut flex_basis : @types.Dimension
  mut order : Int
  mut row_gap : @types.Dimension
  mut column_gap : @types.Dimension
  mut column_gap_is_normal : Bool
  mut column_count : Int?
  mut column_width : @types.Dimension
  mut column_fill : @style.ColumnFill
  mut aspect_ratio : Double?
  mut inset_top : @types.Dimension
  mut inset_right : @types.Dimension
  mut inset_bottom : @types.Dimension
  mut inset_left : @types.Dimension
  mut grid_auto_flow : @types.GridAutoFlow
  mut grid_template_columns : Array[@types.TrackSizingFunction]
  mut grid_template_rows : Array[@types.TrackSizingFunction]
  mut grid_auto_rows : Array[@types.TrackSizingFunction]
  mut grid_auto_columns : Array[@types.TrackSizingFunction]
  // Named lines declared in grid-template-columns/rows (index = grid line)
  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 (container)
  mut grid_template_areas : Array[String]
  // Grid item placement
  mut grid_column_start : @types.GridPlacement
  mut grid_column_end : @types.GridPlacement
  mut grid_row_start : @types.GridPlacement
  mut grid_row_end : @types.GridPlacement
  // Grid area name (item)
  mut grid_area : String?
  // Font properties
  mut font_size : Double
  mut font_weight : Double
  mut font_family : String
  mut line_height : Double
  // Text wrapping and direction
  mut text_align : @style.TextAlign
  mut white_space : @style.WhiteSpace
  mut text_overflow : @style.TextOverflow
  mut writing_mode : @style.WritingMode
  mut direction : @style.Direction
  // Vertical alignment for inline elements
  mut vertical_align : @style.VerticalAlign
  // Clip (legacy)
  mut clip : @types.ClipRect
  mut clip_path : @style.ClipPath
  // Paint properties
  mut visibility : @style.Visibility
  mut pointer_events : @style.PointerEvents
  mut z_index : @style.ZIndex
  mut opacity : Double
  // Color properties
  mut color : @types.Color
  mut background_color : @types.Color
  mut background_image : @types.BackgroundImage
  mut box_shadows : Array[@style.BoxShadow]
  // Containment
  mut contain : @style.Contain
  // Fragmentation
  mut break_before : @style.BreakBefore
  mut break_after : @style.BreakBefore
  mut break_inside : @style.BreakInside
  mut column_span : @style.ColumnSpan
  mut contain_intrinsic_inline_size : Double?
  mut contain_intrinsic_block_size : Double?
  // Transform
  mut transform : @style.Transform
  mut individual_transform : @style.Transform
  // Animation / transition properties (parallel longhand lists).
  mut animation_name : Array[String]
  mut animation_duration : Array[Double]
  mut animation_timing_function : Array[@types.Easing]
  mut animation_delay : Array[Double]
  mut animation_iteration_count : Array[@types.AnimationIterationCount]
  mut animation_direction : Array[@types.AnimationDirection]
  mut animation_fill_mode : Array[@types.AnimationFillMode]
  mut animation_play_state : Array[@types.AnimationPlayState]
  mut transition_property : Array[String]
  mut transition_duration : Array[Double]
  mut transition_timing_function : Array[@types.Easing]
  mut transition_delay : Array[Double]
  // CSS filter / backdrop-filter presence (not none). Layout uses this only
  // to know that the element establishes a containing block for abspos.
  mut has_filter : Bool
  // Zoom (affects layout scaling)
  mut zoom : Double
  // Table properties
  mut border_spacing : Double
  mut border_spacing_vertical : Double
  mut border_collapse : @style.BorderCollapse
  mut table_layout : @style.TableLayout
  mut caption_side : @style.CaptionSide
  // Table cell properties (not mutable via CSS, set from HTML attributes)
  rowspan : Int
  colspan : Int
  // Border radius
  mut border_top_left_radius : Double
  mut border_top_right_radius : Double
  mut border_bottom_right_radius : Double
  mut border_bottom_left_radius : Double
  mut text_decoration_underline : Bool
  mut text_decoration_line_through : Bool
  mut text_decoration_overline : Bool
  mut letter_spacing : Double
  mut word_spacing : Double
}

///|
fn StyleBuilder::new() -> StyleBuilder {
  {
    display: Block,
    position: Static,
    float: None,
    clear: None,
    box_sizing: ContentBox,
    overflow_x: Visible,
    overflow_y: Visible,
    scroll_snap_type: @style.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_top: Length(0.0),
    margin_right: Length(0.0),
    margin_bottom: Length(0.0),
    margin_left: Length(0.0),
    margin_trim: None,
    padding_top: Length(0.0),
    padding_right: Length(0.0),
    padding_bottom: Length(0.0),
    padding_left: Length(0.0),
    border_top: Length(0.0),
    border_right: Length(0.0),
    border_bottom: Length(0.0),
    border_left: Length(0.0),
    border_style_top: None,
    border_style_right: None,
    border_style_bottom: None,
    border_style_left: None,
    border_color_top: @types.Color::transparent(),
    border_color_right: @types.Color::transparent(),
    border_color_bottom: @types.Color::transparent(),
    border_color_left: @types.Color::transparent(),
    flex_direction: Row,
    flex_wrap: NoWrap,
    justify_content: FlexStart,
    align_items: Stretch,
    align_content: Stretch,
    justify_content_unsafe: false,
    align_content_unsafe: false,
    align_self: Auto,
    justify_self: Auto,
    justify_items: Stretch,
    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_top: Auto,
    inset_right: Auto,
    inset_bottom: Auto,
    inset_left: Auto,
    grid_auto_flow: Row,
    grid_template_columns: [],
    grid_template_rows: [],
    grid_auto_rows: [],
    grid_auto_columns: [],
    grid_template_column_line_names: [],
    grid_template_row_line_names: [],
    grid_template_columns_kind: Explicit,
    grid_template_rows_kind: Explicit,
    grid_template_areas: [],
    grid_column_start: Auto,
    grid_column_end: Auto,
    grid_row_start: Auto,
    grid_row_end: Auto,
    grid_area: None,
    font_size: 16.0,
    font_weight: 400.0,
    font_family: "",
    line_height: 16.0,
    text_align: Start,
    white_space: Normal,
    text_overflow: Clip,
    writing_mode: HorizontalTb,
    direction: Ltr,
    vertical_align: Baseline,
    clip: Auto,
    clip_path: @style.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: @style.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: @style.Transform::none(),
    individual_transform: @style.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,
  }
}

///|
/// Create a StyleBuilder from an existing Style
fn StyleBuilder::from_style(style : @style.Style) -> StyleBuilder {
  {
    display: style.display,
    position: style.position,
    float: style.float,
    clear: style.clear,
    box_sizing: style.box_sizing,
    overflow_x: style.overflow_x,
    overflow_y: style.overflow_y,
    scroll_snap_type: style.scroll_snap_type,
    scroll_snap_align_x: style.scroll_snap_align_x,
    scroll_snap_align_y: style.scroll_snap_align_y,
    width: style.width,
    height: style.height,
    min_width: style.min_width,
    min_height: style.min_height,
    max_width: style.max_width,
    max_height: style.max_height,
    margin_top: style.margin.top,
    margin_right: style.margin.right,
    margin_bottom: style.margin.bottom,
    margin_left: style.margin.left,
    margin_trim: style.margin_trim,
    padding_top: style.padding.top,
    padding_right: style.padding.right,
    padding_bottom: style.padding.bottom,
    padding_left: style.padding.left,
    border_top: style.border.top,
    border_right: style.border.right,
    border_bottom: style.border.bottom,
    border_left: style.border.left,
    border_style_top: style.border_style.top,
    border_style_right: style.border_style.right,
    border_style_bottom: style.border_style.bottom,
    border_style_left: style.border_style.left,
    border_color_top: style.border_color.top,
    border_color_right: style.border_color.right,
    border_color_bottom: style.border_color.bottom,
    border_color_left: style.border_color.left,
    flex_direction: style.flex_direction,
    flex_wrap: style.flex_wrap,
    justify_content: style.justify_content,
    align_items: style.align_items,
    align_content: style.align_content,
    justify_content_unsafe: style.justify_content_unsafe,
    align_content_unsafe: style.align_content_unsafe,
    align_self: style.align_self,
    justify_self: style.justify_self,
    justify_items: style.justify_items,
    flex_grow: style.flex_grow,
    flex_shrink: style.flex_shrink,
    flex_basis: style.flex_basis,
    order: style.order,
    row_gap: style.row_gap,
    column_gap: style.column_gap,
    column_gap_is_normal: style.column_gap_is_normal,
    column_count: style.column_count,
    column_width: style.column_width,
    column_fill: style.column_fill,
    aspect_ratio: style.aspect_ratio,
    inset_top: style.inset.top,
    inset_right: style.inset.right,
    inset_bottom: style.inset.bottom,
    inset_left: style.inset.left,
    grid_auto_flow: style.grid_auto_flow,
    grid_template_columns: style.grid_template_columns,
    grid_template_rows: style.grid_template_rows,
    grid_auto_rows: style.grid_auto_rows,
    grid_auto_columns: style.grid_auto_columns,
    grid_template_column_line_names: style.grid_template_column_line_names,
    grid_template_row_line_names: style.grid_template_row_line_names,
    grid_template_columns_kind: style.grid_template_columns_kind,
    grid_template_rows_kind: style.grid_template_rows_kind,
    grid_template_areas: style.grid_template_areas,
    grid_column_start: style.grid_column.start,
    grid_column_end: style.grid_column.end,
    grid_row_start: style.grid_row.start,
    grid_row_end: style.grid_row.end,
    grid_area: style.grid_area,
    font_size: style.font_size,
    font_weight: style.font_weight,
    font_family: style.font_family,
    line_height: style.line_height,
    text_align: style.text_align,
    white_space: style.white_space,
    text_overflow: style.text_overflow,
    writing_mode: style.writing_mode,
    direction: style.direction,
    vertical_align: style.vertical_align,
    clip: style.clip,
    clip_path: style.clip_path,
    visibility: style.visibility,
    pointer_events: style.pointer_events,
    z_index: style.z_index,
    opacity: style.opacity,
    color: style.color,
    background_color: style.background_color,
    background_image: style.background_image,
    box_shadows: style.box_shadows,
    contain: style.contain,
    break_before: style.break_before,
    break_after: style.break_after,
    break_inside: style.break_inside,
    column_span: style.column_span,
    contain_intrinsic_inline_size: style.contain_intrinsic_inline_size,
    contain_intrinsic_block_size: style.contain_intrinsic_block_size,
    transform: style.transform,
    individual_transform: style.individual_transform,
    animation_name: style.animation_name,
    animation_duration: style.animation_duration,
    animation_timing_function: style.animation_timing_function,
    animation_delay: style.animation_delay,
    animation_iteration_count: style.animation_iteration_count,
    animation_direction: style.animation_direction,
    animation_fill_mode: style.animation_fill_mode,
    animation_play_state: style.animation_play_state,
    transition_property: style.transition_property,
    transition_duration: style.transition_duration,
    transition_timing_function: style.transition_timing_function,
    transition_delay: style.transition_delay,
    has_filter: style.has_filter,
    zoom: style.zoom,
    border_spacing: style.border_spacing,
    border_spacing_vertical: style.border_spacing_vertical,
    border_collapse: style.border_collapse,
    table_layout: style.table_layout,
    caption_side: style.caption_side,
    rowspan: style.rowspan,
    colspan: style.colspan,
    border_top_left_radius: style.border_top_left_radius,
    border_top_right_radius: style.border_top_right_radius,
    border_bottom_right_radius: style.border_bottom_right_radius,
    border_bottom_left_radius: style.border_bottom_left_radius,
    text_decoration_underline: style.text_decoration_underline,
    text_decoration_line_through: style.text_decoration_line_through,
    text_decoration_overline: style.text_decoration_overline,
    letter_spacing: style.letter_spacing,
    word_spacing: style.word_spacing,
  }
}

///|
fn StyleBuilder::build(self : StyleBuilder) -> @style.Style {
  {
    display: self.display,
    position: self.position,
    float: self.float,
    clear: self.clear,
    box_sizing: self.box_sizing,
    overflow_x: self.overflow_x,
    overflow_y: self.overflow_y,
    scroll_snap_type: self.scroll_snap_type,
    scroll_snap_align_x: self.scroll_snap_align_x,
    scroll_snap_align_y: self.scroll_snap_align_y,
    width: self.width,
    height: self.height,
    min_width: self.min_width,
    min_height: self.min_height,
    max_width: self.max_width,
    max_height: self.max_height,
    margin: {
      top: self.margin_top,
      right: self.margin_right,
      bottom: self.margin_bottom,
      left: self.margin_left,
    },
    margin_trim: self.margin_trim,
    padding: {
      top: self.padding_top,
      right: self.padding_right,
      bottom: self.padding_bottom,
      left: self.padding_left,
    },
    border: {
      top: self.border_top,
      right: self.border_right,
      bottom: self.border_bottom,
      left: self.border_left,
    },
    border_style: {
      top: self.border_style_top,
      right: self.border_style_right,
      bottom: self.border_style_bottom,
      left: self.border_style_left,
    },
    border_color: {
      top: self.border_color_top,
      right: self.border_color_right,
      bottom: self.border_color_bottom,
      left: self.border_color_left,
    },
    flex_direction: self.flex_direction,
    flex_wrap: self.flex_wrap,
    justify_content: self.justify_content,
    align_items: self.align_items,
    align_content: self.align_content,
    justify_content_unsafe: self.justify_content_unsafe,
    align_content_unsafe: self.align_content_unsafe,
    align_self: self.align_self,
    flex_grow: self.flex_grow,
    flex_shrink: self.flex_shrink,
    flex_basis: self.flex_basis,
    order: self.order,
    row_gap: self.row_gap,
    column_gap: self.column_gap,
    column_gap_is_normal: self.column_gap_is_normal,
    column_count: self.column_count,
    column_width: self.column_width,
    column_fill: self.column_fill,
    aspect_ratio: self.aspect_ratio,
    inset: {
      top: self.inset_top,
      right: self.inset_right,
      bottom: self.inset_bottom,
      left: self.inset_left,
    },
    grid_template_rows: self.grid_template_rows,
    grid_template_columns: self.grid_template_columns,
    grid_auto_rows: self.grid_auto_rows,
    grid_auto_columns: self.grid_auto_columns,
    grid_auto_flow: self.grid_auto_flow,
    grid_template_column_line_names: self.grid_template_column_line_names,
    grid_template_row_line_names: self.grid_template_row_line_names,
    grid_template_columns_kind: self.grid_template_columns_kind,
    grid_template_rows_kind: self.grid_template_rows_kind,
    grid_template_areas: self.grid_template_areas,
    grid_row: { start: self.grid_row_start, end: self.grid_row_end },
    grid_column: { start: self.grid_column_start, end: self.grid_column_end },
    grid_area: self.grid_area,
    justify_items: self.justify_items,
    justify_self: self.justify_self,
    font_size: self.font_size,
    font_weight: self.font_weight,
    font_family: self.font_family,
    line_height: self.line_height,
    text_align: self.text_align,
    white_space: self.white_space,
    text_overflow: self.text_overflow,
    writing_mode: self.writing_mode,
    direction: self.direction,
    vertical_align: self.vertical_align,
    clip: self.clip,
    clip_path: self.clip_path,
    visibility: self.visibility,
    pointer_events: self.pointer_events,
    z_index: self.z_index,
    opacity: self.opacity,
    color: self.color,
    background_color: self.background_color,
    background_image: self.background_image,
    box_shadows: self.box_shadows,
    contain: self.contain,
    break_before: self.break_before,
    break_after: self.break_after,
    break_inside: self.break_inside,
    column_span: self.column_span,
    contain_intrinsic_inline_size: self.contain_intrinsic_inline_size,
    contain_intrinsic_block_size: self.contain_intrinsic_block_size,
    transform: self.transform,
    individual_transform: self.individual_transform,
    animation_name: self.animation_name,
    animation_duration: self.animation_duration,
    animation_timing_function: self.animation_timing_function,
    animation_delay: self.animation_delay,
    animation_iteration_count: self.animation_iteration_count,
    animation_direction: self.animation_direction,
    animation_fill_mode: self.animation_fill_mode,
    animation_play_state: self.animation_play_state,
    transition_property: self.transition_property,
    transition_duration: self.transition_duration,
    transition_timing_function: self.transition_timing_function,
    transition_delay: self.transition_delay,
    has_filter: self.has_filter,
    zoom: self.zoom,
    border_spacing: self.border_spacing,
    border_spacing_vertical: self.border_spacing_vertical,
    border_collapse: self.border_collapse,
    table_layout: self.table_layout,
    caption_side: self.caption_side,
    rowspan: self.rowspan,
    colspan: self.colspan,
    border_top_left_radius: self.border_top_left_radius,
    border_top_right_radius: self.border_top_right_radius,
    border_bottom_right_radius: self.border_bottom_right_radius,
    border_bottom_left_radius: self.border_bottom_left_radius,
    text_decoration_underline: self.text_decoration_underline,
    text_decoration_line_through: self.text_decoration_line_through,
    text_decoration_overline: self.text_decoration_overline,
    letter_spacing: self.letter_spacing,
    word_spacing: self.word_spacing,
  }
}

///|
/// Resolve a CSS keyword value to actual value
fn resolve_keyword(
  property : String,
  value : @cascade.PropertyValue,
  ctx : ComputeContext,
) -> String {
  match value {
    Value(v) => v
    Inherit =>
      // Get value from parent, or use initial if no parent
      get_inherited_value(property, ctx)
    Initial => initial_value(property)
    Unset =>
      // For inherited properties, acts like inherit
      // For non-inherited properties, acts like initial
      if is_inherited(property) {
        get_inherited_value(property, ctx)
      } else {
        initial_value(property)
      }
    Revert =>
      // This engine has author styles only. With no lower origin available,
      // use the same inherited/initial boundary as `unset`.
      if is_inherited(property) {
        get_inherited_value(property, ctx)
      } else {
        initial_value(property)
      }
    RevertLayer =>
      // Cascade layers are not represented, so fall back to the author-origin
      // boundary documented for `revert` above.
      if is_inherited(property) {
        get_inherited_value(property, ctx)
      } else {
        initial_value(property)
      }
  }
}

///|
/// Get inherited value from parent or initial value
fn get_inherited_value(property : String, ctx : ComputeContext) -> String {
  match ctx.parent_style {
    Some(parent) => get_style_value_as_string(property, parent)
    None => initial_value(property)
  }
}

///|
/// Get a style property value as string representation
fn get_style_value_as_string(property : String, style : @style.Style) -> String {
  match property {
    "display" =>
      match style.display {
        Block => "block"
        Inline => "inline"
        InlineBlock => "inline-block"
        Flex => "flex"
        InlineFlex => "inline-flex"
        Grid => "grid"
        InlineGrid => "inline-grid"
        Table => "table"
        InlineTable => "inline-table"
        TableRow => "table-row"
        TableCell => "table-cell"
        TableCaption => "table-caption"
        TableRowGroup => "table-row-group"
        TableHeaderGroup => "table-header-group"
        TableFooterGroup => "table-footer-group"
        TableColumn => "table-column"
        TableColumnGroup => "table-column-group"
        None => "none"
        Contents => "contents"
        FlowRoot => "flow-root"
      }
    "position" =>
      match style.position {
        Static => "static"
        Relative => "relative"
        Absolute => "absolute"
        Fixed => "fixed"
      }
    "flex-direction" =>
      match style.flex_direction {
        Row => "row"
        RowReverse => "row-reverse"
        Column => "column"
        ColumnReverse => "column-reverse"
      }
    "flex-wrap" =>
      match style.flex_wrap {
        NoWrap => "nowrap"
        Wrap => "wrap"
        WrapReverse => "wrap-reverse"
      }
    "width" => dimension_to_string(style.width)
    "height" => dimension_to_string(style.height)
    "min-width" => dimension_to_string(style.min_width)
    "min-height" => dimension_to_string(style.min_height)
    "max-width" => dimension_to_string(style.max_width)
    "max-height" => dimension_to_string(style.max_height)
    "margin-top" => dimension_to_string(style.margin.top)
    "margin-right" => dimension_to_string(style.margin.right)
    "margin-bottom" => dimension_to_string(style.margin.bottom)
    "margin-left" => dimension_to_string(style.margin.left)
    "margin-trim" =>
      match style.margin_trim {
        None => "none"
        BlockStart => "block-start"
        BlockEnd => "block-end"
        Block => "block"
        InlineStart => "inline-start"
        InlineEnd => "inline-end"
        Inline => "inline"
      }
    "padding-top" => dimension_to_string(style.padding.top)
    "padding-right" => dimension_to_string(style.padding.right)
    "padding-bottom" => dimension_to_string(style.padding.bottom)
    "padding-left" => dimension_to_string(style.padding.left)
    "column-count" =>
      match style.column_count {
        Some(v) => v.to_string()
        None => "auto"
      }
    "column-width" => dimension_to_string(style.column_width)
    "column-fill" =>
      match style.column_fill {
        Balance => "balance"
        Auto => "auto"
      }
    "break-before" =>
      match style.break_before {
        Auto => "auto"
        Column => "column"
        Avoid => "avoid"
      }
    "break-after" =>
      match style.break_after {
        Auto => "auto"
        Column => "column"
        Avoid => "avoid"
      }
    "break-inside" | "page-break-inside" =>
      match style.break_inside {
        Auto => "auto"
        Avoid => "avoid"
      }
    "column-span" =>
      match style.column_span {
        None => "none"
        All => "all"
      }
    "flex-grow" => style.flex_grow.to_string()
    "flex-shrink" => style.flex_shrink.to_string()
    "flex-basis" => dimension_to_string(style.flex_basis)
    "color" => style.color.to_hex()
    "background-color" => style.background_color.to_hex()
    "caption-side" =>
      match style.caption_side {
        Top => "top"
        Bottom => "bottom"
      }
    "border-collapse" =>
      match style.border_collapse {
        Separate => "separate"
        Collapse => "collapse"
      }
    "border-spacing" =>
      if style.border_spacing == style.border_spacing_vertical {
        style.border_spacing.to_string() + "px"
      } else {
        style.border_spacing.to_string() +
        "px " +
        style.border_spacing_vertical.to_string() +
        "px"
      }
    "writing-mode" =>
      match style.writing_mode {
        HorizontalTb => "horizontal-tb"
        VerticalRl => "vertical-rl"
        VerticalLr => "vertical-lr"
      }
    "direction" =>
      match style.direction {
        Ltr => "ltr"
        Rtl => "rtl"
      }
    "font-size" => style.font_size.to_string() + "px"
    "font-weight" => style.font_weight.to_string()
    "line-height" => style.line_height.to_string() + "px"
    "text-decoration" | "text-decoration-line" =>
      text_decoration_to_string(
        style.text_decoration_underline,
        style.text_decoration_line_through,
        style.text_decoration_overline,
      )
    "text-overflow" =>
      match style.text_overflow {
        Clip => "clip"
        Ellipsis => "ellipsis"
      }
    "text-align" =>
      match style.text_align {
        Start => "start"
        End => "end"
        Left => "left"
        Right => "right"
        Center => "center"
        Justify => "justify"
      }
    "white-space" =>
      match style.white_space {
        Normal => "normal"
        Nowrap => "nowrap"
        Pre => "pre"
        PreWrap => "pre-wrap"
        PreLine => "pre-line"
      }
    "pointer-events" =>
      match style.pointer_events {
        Auto => "auto"
        None => "none"
      }
    "opacity" => style.opacity.to_string()
    "contain-intrinsic-inline-size" =>
      match style.contain_intrinsic_inline_size {
        Some(v) => v.to_string() + "px"
        None => "none"
      }
    "contain-intrinsic-block-size" =>
      match style.contain_intrinsic_block_size {
        Some(v) => v.to_string() + "px"
        None => "none"
      }
    _ => initial_value(property)
  }
}

///|
/// Convert dimension to string
fn dimension_to_string(dim : @types.Dimension) -> String {
  match dim {
    Auto => "auto"
    Length(n) => if n == 0.0 { "0" } else { n.to_string() + "px" }
    Percent(n) => (n * 100.0).to_string() + "%"
    MinContent => "min-content"
    MaxContent => "max-content"
    FitContent(n) => "fit-content(" + n.to_string() + "px)"
    Calc(px, pct) => {
      let pct_part = (pct * 100.0).to_string() + "%"
      if px < 0.0 {
        "calc(" + pct_part + " - " + (-px).to_string() + "px)"
      } else {
        "calc(" + pct_part + " + " + px.to_string() + "px)"
      }
    }
    MathFn(op, args) => {
      let name = match op {
        Min => "min("
        Max => "max("
        Clamp => "clamp("
      }
      let parts = args.map(fn(a) { math_arg_to_string(a.0, a.1) })
      name + parts.join(", ") + ")"
    }
  }
}

///|
/// Serialize a single min()/max()/clamp() argument from its linear (px, pct)
/// form back to CSS, mirroring dimension_to_string's length/percent/calc tiers.
fn math_arg_to_string(px : Double, pct : Double) -> String {
  if pct.abs() < 0.0001 {
    if px == 0.0 {
      "0"
    } else {
      px.to_string() + "px"
    }
  } else if px.abs() < 0.0001 {
    (pct * 100.0).to_string() + "%"
  } else {
    let pct_part = (pct * 100.0).to_string() + "%"
    if px < 0.0 {
      "calc(" + pct_part + " - " + (-px).to_string() + "px)"
    } else {
      "calc(" + pct_part + " + " + px.to_string() + "px)"
    }
  }
}

///|
fn text_decoration_to_string(
  underline : Bool,
  line_through : Bool,
  overline : Bool,
) -> String {
  let values : Array[String] = []
  if underline {
    values.push("underline")
  }
  if line_through {
    values.push("line-through")
  }
  if overline {
    values.push("overline")
  }
  if values.is_empty() {
    "none"
  } else {
    let buf = StringBuilder::new()
    for i, value in values {
      if i > 0 {
        buf.write_string(" ")
      }
      buf.write_string(value)
    }
    buf.to_string()
  }
}

///|
/// Compute a Style from cascaded values
pub fn compute(
  cascaded : @cascade.CascadedValues,
  ctx : ComputeContext,
) -> @style.Style {
  let builder = StyleBuilder::new()

  // First pass: collect custom properties (--*)
  // Custom properties are stored in the context for var() resolution
  cascaded.each(fn(prop, decl) {
    if starts_with(prop, "--") {
      let value = resolve_keyword(prop, decl.value, ctx)
      // Store custom property in context
      ctx.custom_properties[prop] = value
    }
  })
  seed_effective_color_scheme(cascaded, ctx)

  seed_direction(builder, cascaded, ctx)
  // Seed writing-mode before applying logical properties.
  // Logical sizing must follow the element's effective writing mode.
  seed_writing_mode(builder, cascaded, ctx)
  // Seed font metrics before resolving em/ch/ex units in other properties.
  let font_ctx = seed_font_metrics(builder, cascaded, ctx)

  // Second pass: apply regular properties with var() resolution
  for item in regular_declarations_in_source_order(cascaded) {
    let (prop, decl) = item
    let value = resolve_keyword(prop, decl.value, font_ctx)
    apply_property(builder, prop, value, font_ctx)
  }

  // Handle inheritance for inherited properties
  apply_inheritance(builder, cascaded, ctx)
  builder.build()
}

///|
/// Compute a Style and return updated custom properties
/// Used when building a style tree to pass custom properties to children
pub fn compute_with_vars(
  cascaded : @cascade.CascadedValues,
  ctx : ComputeContext,
) -> (@style.Style, Map[String, String]) {
  let builder = StyleBuilder::new()

  // First pass: collect custom properties (--*)
  cascaded.each(fn(prop, decl) {
    if starts_with(prop, "--") {
      let value = resolve_keyword(prop, decl.value, ctx)
      ctx.custom_properties[prop] = value
    }
  })
  seed_effective_color_scheme(cascaded, ctx)

  // Seed writing-mode before applying logical properties.
  seed_direction(builder, cascaded, ctx)
  seed_writing_mode(builder, cascaded, ctx)
  let font_ctx = seed_font_metrics(builder, cascaded, ctx)

  // Second pass: apply regular properties
  for item in regular_declarations_in_source_order(cascaded) {
    let (prop, decl) = item
    let value = resolve_keyword(prop, decl.value, font_ctx)
    apply_property(builder, prop, value, font_ctx)
  }

  // Handle inheritance
  apply_inheritance(builder, cascaded, ctx)

  // Return both the style and the custom properties for children
  (builder.build(), ctx.custom_properties)
}

///|
fn seed_effective_color_scheme(
  cascaded : @cascade.CascadedValues,
  ctx : ComputeContext,
) -> Unit {
  let inherited = match
    ctx.custom_properties.get(internal_effective_color_scheme_key) {
    Some(value) => value
    None => "light"
  }
  let mut scheme = inherited
  match cascaded.get_value("color-scheme") {
    Some(raw_value) => {
      let resolved = if raw_value.contains("var(") {
        resolve_all_vars(raw_value, ctx)
      } else {
        raw_value
      }
      match first_color_scheme_keyword(resolved) {
        Some(value) => scheme = value
        None => ()
      }
    }
    None => ()
  }
  ctx.custom_properties[internal_effective_color_scheme_key] = scheme
}

///|
fn first_color_scheme_keyword(value : String) -> String? {
  let normalized = value.to_lower()
  let tokens = normalized.split(" ")
  for token in tokens {
    let t = token.to_owned().trim()
    if t == "light" || t == "dark" {
      return Some(t.to_owned())
    }
  }
  None
}

///|
fn seed_direction(
  builder : StyleBuilder,
  cascaded : @cascade.CascadedValues,
  ctx : ComputeContext,
) -> Unit {
  match ctx.parent_style {
    Some(parent_style) => builder.direction = parent_style.direction
    None => ()
  }
  match cascaded.get("direction") {
    Some(decl) => {
      let value = resolve_keyword("direction", decl.value, ctx)
      builder.direction = parse_direction(value)
    }
    None => ()
  }
}

///|
/// Seed writing-mode on the builder before logical properties are applied.
/// Uses inherited writing-mode as default, then overrides with explicit declaration.
fn seed_writing_mode(
  builder : StyleBuilder,
  cascaded : @cascade.CascadedValues,
  ctx : ComputeContext,
) -> Unit {
  match ctx.parent_style {
    Some(parent_style) => builder.writing_mode = parent_style.writing_mode
    None => ()
  }
  match cascaded.get("writing-mode") {
    Some(decl) => {
      let value = resolve_keyword("writing-mode", decl.value, ctx)
      builder.writing_mode = parse_writing_mode(value)
    }
    None => ()
  }
}

///|
fn is_preseeded_font_property(property : String) -> Bool {
  property == "font" ||
  property == "font-size" ||
  property == "line-height" ||
  property == "font-weight" ||
  property == "font-family"
}

///|
/// Return regular declarations in CSS source order. CascadedValues is a map,
/// so applying it directly loses relative shorthand/longhand order.
fn regular_declarations_in_source_order(
  cascaded : @cascade.CascadedValues,
) -> Array[(String, @cascade.Declaration)] {
  let declarations : Array[(String, @cascade.Declaration)] = []
  cascaded.each(fn(prop, decl) {
    if !starts_with(prop, "--") && !is_preseeded_font_property(prop) {
      declarations.push((prop, decl))
    }
  })
  declarations.sort_by(fn(a, b) { a.1.source_order - b.1.source_order })
  declarations
}

///|
/// Seed font-size/line-height ahead of general property application so
/// em/ch/ex units in width/height and gaps resolve against the element's font.
fn seed_font_metrics(
  builder : StyleBuilder,
  cascaded : @cascade.CascadedValues,
  ctx : ComputeContext,
) -> ComputeContext {
  let mut seeded_font_size = ctx.font_size
  let mut seeded_line_height = ctx.font_size * 1.2 // CSS initial: normal ≈ 1.2
  let mut seeded_font_weight = match ctx.parent_style {
    Some(parent_style) => parent_style.font_weight
    None => 400.0
  }
  let mut seeded_font_family = match ctx.parent_style {
    Some(parent_style) => parent_style.font_family
    None => ""
  }
  let mut font_decl : @cascade.Declaration? = None
  let mut font_size_decl : @cascade.Declaration? = None
  let mut line_height_decl : @cascade.Declaration? = None
  let mut font_weight_decl : @cascade.Declaration? = None
  let mut font_family_decl : @cascade.Declaration? = None
  match cascaded.get("font") {
    Some(decl) => font_decl = Some(decl)
    None => ()
  }
  match cascaded.get("font-size") {
    Some(decl) => font_size_decl = Some(decl)
    None => ()
  }
  match cascaded.get("line-height") {
    Some(decl) => line_height_decl = Some(decl)
    None => ()
  }
  match cascaded.get("font-weight") {
    Some(decl) => font_weight_decl = Some(decl)
    None => ()
  }
  match cascaded.get("font-family") {
    Some(decl) => font_family_decl = Some(decl)
    None => ()
  }
  for idx = 0; idx < 5; idx = idx + 1 {
    let mut next_prop = ""
    let mut next_decl : @cascade.Declaration? = None
    let mut next_order = 2147483647
    match font_decl {
      Some(decl) =>
        if decl.source_order < next_order {
          next_prop = "font"
          next_decl = Some(decl)
          next_order = decl.source_order
        }
      None => ()
    }
    match font_size_decl {
      Some(decl) =>
        if decl.source_order < next_order {
          next_prop = "font-size"
          next_decl = Some(decl)
          next_order = decl.source_order
        }
      None => ()
    }
    match line_height_decl {
      Some(decl) =>
        if decl.source_order < next_order {
          next_prop = "line-height"
          next_decl = Some(decl)
          next_order = decl.source_order
        }
      None => ()
    }
    match font_weight_decl {
      Some(decl) =>
        if decl.source_order < next_order {
          next_prop = "font-weight"
          next_decl = Some(decl)
          next_order = decl.source_order
        }
      None => ()
    }
    match font_family_decl {
      Some(decl) =>
        if decl.source_order < next_order {
          next_prop = "font-family"
          next_decl = Some(decl)
        }
      None => ()
    }
    if next_prop == "" {
      break
    }
    let decl = match next_decl {
      Some(decl) => decl
      None => break
    }
    let unresolved = resolve_keyword(next_prop, decl.value, ctx)
    match resolve_custom_property_value(unresolved, ctx.custom_properties) {
      Some(value) =>
        match next_prop {
          "font" => {
            let parsed = parse_font_shorthand(value, ctx)
            seeded_font_size = parsed.font_size
            seeded_line_height = parsed.line_height
            seeded_font_weight = parsed.font_weight
            seeded_font_family = parsed.font_family
            font_decl = None
          }
          "font-size" => {
            seeded_font_size = parse_font_size(value, ctx)
            font_size_decl = None
          }
          "line-height" => {
            seeded_line_height = parse_line_height(
              value,
              seeded_font_size,
              root_font_size=ctx.root_font_size,
            )
            line_height_decl = None
          }
          "font-weight" => {
            seeded_font_weight = parse_font_weight(value)
            font_weight_decl = None
          }
          "font-family" => {
            seeded_font_family = parse_font_family(value)
            font_family_decl = None
          }
          _ => ()
        }
      None => ()
    }
  }

  builder.font_size = seeded_font_size
  builder.line_height = seeded_line_height
  builder.font_weight = seeded_font_weight
  builder.font_family = seeded_font_family
  { ..ctx, font_size: seeded_font_size }
}

///|
/// Resolve a 1-or-2 value logical shorthand (margin-inline, padding-block, …)
/// into a (start, end) dimension pair. A single value applies to both ends.
fn resolve_logical_pair(
  value : String,
  ctx : ComputeContext,
) -> (@types.Dimension, @types.Dimension) {
  let dims : Array[@types.Dimension] = []
  for part in value.trim().to_owned().split(" ") {
    let p = part.to_owned().trim().to_owned()
    if !p.is_empty() {
      dims.push(resolve_dimension(p, ctx))
    }
  }
  match dims.length() {
    0 => {
      let d = resolve_dimension(value, ctx)
      (d, d)
    }
    1 => (dims[0], dims[0])
    _ => (dims[0], dims[1])
  }
}