///|
/// Inline style parser - parses CSS property declarations
/// e.g., "width: 100px; height: 50px; display: flex"

// Default viewport size for inline parser vh/vw resolution
let default_viewport_width : Double = 1000.0

///|
let default_viewport_height : Double = 1000.0

///|
/// Parse a calc() expression and try to simplify it to a Dimension
/// Returns Some(dimension) if simplification is possible, None otherwise
fn parse_calc_expression(
  tokens : ArrayView[@token.Token],
) -> (@types.Dimension, ArrayView[@token.Token])? {
  // Skip the Function("calc") token - we're already past it
  // Parse tokens until RightParen, collecting values and operators
  let mut remaining = tokens
  let mut result_px : Double = 0.0
  let mut result_pct : Double = 0.0
  let mut current_op : Char = '+'
  while remaining.length() > 0 {
    remaining = skip_whitespace(remaining)
    if remaining.length() == 0 {
      break
    }
    match remaining[0] {
      @token.Token::RightParen => {
        // End of calc expression
        let next_tokens = remaining[1:]
        // Determine final dimension type
        if result_pct.abs() < 0.0001 {
          // Pure length
          return Some((@types.Length(result_px), next_tokens))
        } else if result_px.abs() < 0.0001 {
          // Pure percentage
          return Some((@types.Percent(result_pct / 100.0), next_tokens))
        } else {
          // Mixed - cannot simplify (return None to fall back to Auto)
          return None
        }
      }
      @token.Token::Dimension(value, unit) => {
        if unit == "px" {
          match current_op {
            '+' => result_px = result_px + value
            '-' => result_px = result_px - value
            '*' => result_px = result_px * value
            '/' => result_px = result_px / value
            _ => ()
          }
        } else if unit == "%" {
          match current_op {
            '+' => result_pct = result_pct + value
            '-' => result_pct = result_pct - value
            '*' => result_pct = result_pct * value
            '/' => result_pct = result_pct / value
            _ => ()
          }
        }
        remaining = remaining[1:]
      }
      @token.Token::Percentage(value) => {
        match current_op {
          '+' => result_pct = result_pct + value
          '-' => result_pct = result_pct - value
          '*' => result_pct = result_pct * value
          '/' => result_pct = result_pct / value
          _ => ()
        }
        remaining = remaining[1:]
      }
      @token.Token::Number(value, _) =>
        // Unitless number - treat as px if 0, otherwise ignore
        if value == 0.0 {
          // 0 has no effect
          remaining = remaining[1:]
        } else {
          // Non-zero unitless number in calc - skip for now
          remaining = remaining[1:]
        }
      @token.Token::Delim(c) =>
        if c == '+' || c == '-' || c == '*' || c == '/' {
          current_op = c
          remaining = remaining[1:]
        } else {
          remaining = remaining[1:]
        }
      _ => remaining = remaining[1:]
    }
  }
  None
}

///|
/// Parse a dimension value (length, percent, auto)
/// vh/vw are converted to Length using default viewport (1000x1000)
fn parse_dimension(tokens : ArrayView[@token.Token]) -> @types.Dimension? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Function(name) =>
      if name == "calc" {
        // Try to parse and simplify calc expression
        match parse_calc_expression(tokens[1:]) {
          Some((dim, _)) => return Some(dim)
          None => return Some(@types.Auto) // Fallback for complex calc
        }
      } else {
        None
      }
    @token.Token::Dimension(value, unit) =>
      if unit == "px" {
        Some(@types.Length(value))
      } else if unit == "%" {
        Some(@types.Percent(value / 100.0))
      } else if unit == "vw" {
        // Convert vw to pixels using default viewport width
        Some(@types.Length(value * default_viewport_width / 100.0))
      } else if unit == "vh" {
        // Convert vh to pixels using default viewport height
        Some(@types.Length(value * default_viewport_height / 100.0))
      } else if unit == "em" || unit == "rem" {
        // em/rem treated as 16px multiplier for now
        Some(@types.Length(value * 16.0))
      } else {
        // Other units treated as px
        Some(@types.Length(value))
      }
    @token.Token::Percentage(value) => Some(@types.Percent(value / 100.0))
    @token.Token::Number(value, _) =>
      // Unitless 0 is valid
      if value == 0.0 {
        Some(@types.Length(0.0))
      } else {
        None
      }
    @token.Token::Ident(s) =>
      if s == "auto" {
        Some(@types.Auto)
      } else if s == "min-content" {
        Some(@types.MinContent)
      } else if s == "max-content" {
        Some(@types.MaxContent)
      } else if s == "fit-content" {
        // fit-content without argument is equivalent to fit-content(max-content)
        // Using infinity to represent unbounded
        Some(@types.FitContent(1.0e10))
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse multiple dimension values (for shorthand properties like margin, padding)
fn parse_multi_dimensions(
  tokens : ArrayView[@token.Token],
  max_count : Int,
) -> Array[@types.Dimension] {
  let result : Array[@types.Dimension] = []
  let mut rest = tokens
  while result.length() < max_count && rest.length() > 0 {
    // Skip whitespace
    rest = skip_whitespace(rest)
    if rest.length() == 0 {
      break
    }
    // Try to parse a dimension
    match parse_dimension(rest) {
      Some(dim) => {
        result.push(dim)
        rest = rest[1:]
      }
      None => break
    }
  }
  result
}

///|
fn parse_display_keyword(keyword : String) -> @types.Display? {
  if keyword == "block" {
    Some(@types.Block)
  } else if keyword == "inline" {
    Some(@types.Inline)
  } else if keyword == "inline-block" {
    Some(@types.InlineBlock)
  } else if keyword == "flex" {
    Some(@types.Flex)
  } else if keyword == "inline-flex" {
    Some(@types.InlineFlex)
  } else if keyword == "grid" {
    Some(@types.Grid)
  } else if keyword == "inline-grid" {
    Some(@types.InlineGrid)
  } else if keyword == "table" {
    Some(@types.Table)
  } else if keyword == "inline-table" {
    Some(@types.InlineTable)
  } else if keyword == "table-row" {
    Some(@types.TableRow)
  } else if keyword == "table-cell" {
    Some(@types.TableCell)
  } else if keyword == "table-caption" {
    Some(@types.TableCaption)
  } else if keyword == "table-row-group" {
    Some(@types.TableRowGroup)
  } else if keyword == "table-header-group" {
    Some(@types.TableHeaderGroup)
  } else if keyword == "table-footer-group" {
    Some(@types.TableFooterGroup)
  } else if keyword == "table-column" {
    Some(@types.TableColumn)
  } else if keyword == "table-column-group" {
    Some(@types.TableColumnGroup)
  } else if keyword == "none" {
    Some(@types.Display::None)
  } else if keyword == "contents" {
    Some(@types.Contents)
  } else if keyword == "flow-root" {
    Some(@types.FlowRoot)
  } else if keyword == "math" {
    Some(@types.Inline)
  } else {
    None
  }
}

///|
/// Parse display property
fn parse_display(tokens : ArrayView[@token.Token]) -> @types.Display? {
  if tokens.length() == 0 {
    return None
  }
  let keywords : Array[String] = []
  for token in tokens {
    match token {
      @token.Token::Ident(s) => keywords.push(s)
      @token.Token::Whitespace => ()
      _ => return None
    }
  }
  if keywords.length() == 0 {
    return None
  }
  if keywords.length() == 2 {
    let first = keywords[0]
    let second = keywords[1]
    if (first == "math" && second == "inline") ||
      (first == "inline" && second == "math") {
      return Some(@types.Inline)
    }
    if (first == "math" && second == "block") ||
      (first == "block" && second == "math") {
      return Some(@types.Block)
    }
    if (first == "flow-root" && second == "list-item") ||
      (first == "list-item" && second == "flow-root") {
      return Some(@types.FlowRoot)
    }
  }
  if keywords.length() != 1 {
    return None
  }
  parse_display_keyword(keywords[0])
}

///|
/// Parse position property
fn parse_position(tokens : ArrayView[@token.Token]) -> @types.Position? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "relative" {
        Some(@types.Relative)
      } else if s == "absolute" {
        Some(@types.Absolute)
      } else if s == "fixed" {
        Some(@types.Fixed)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse float property
fn parse_float(
  tokens : ArrayView[@token.Token],
  direction : @style.Direction,
) -> @types.Float? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "left" {
        Some(@types.Float::Left)
      } else if s == "right" {
        Some(@types.Float::Right)
      } else if s == "inline-start" {
        Some(
          if direction.is_rtl() {
            @types.Float::Right
          } else {
            @types.Float::Left
          },
        )
      } else if s == "inline-end" {
        Some(
          if direction.is_rtl() {
            @types.Float::Left
          } else {
            @types.Float::Right
          },
        )
      } else if s == "none" {
        Some(@types.Float::None)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse clear property
fn parse_clear(
  tokens : ArrayView[@token.Token],
  direction : @style.Direction,
) -> @types.Clear? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "left" {
        Some(@types.Clear::Left)
      } else if s == "right" {
        Some(@types.Clear::Right)
      } else if s == "inline-start" {
        Some(
          if direction.is_rtl() {
            @types.Clear::Right
          } else {
            @types.Clear::Left
          },
        )
      } else if s == "inline-end" {
        Some(
          if direction.is_rtl() {
            @types.Clear::Left
          } else {
            @types.Clear::Right
          },
        )
      } else if s == "both" {
        Some(@types.Clear::Both)
      } else if s == "none" {
        Some(@types.Clear::None)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse flex-direction property
fn parse_flex_direction(
  tokens : ArrayView[@token.Token],
) -> @types.FlexDirection? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "row" {
        Some(@types.Row)
      } else if s == "row-reverse" {
        Some(@types.RowReverse)
      } else if s == "column" {
        Some(@types.Column)
      } else if s == "column-reverse" {
        Some(@types.ColumnReverse)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse flex-wrap property
fn parse_flex_wrap(tokens : ArrayView[@token.Token]) -> @types.FlexWrap? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "nowrap" {
        Some(@types.NoWrap)
      } else if s == "wrap" {
        Some(@types.FlexWrap::Wrap)
      } else if s == "wrap-reverse" {
        Some(@types.WrapReverse)
      } else {
        None
      }
    _ => None
  }
}

///|
fn parse_alignment_keyword(value : String) -> @types.Alignment? {
  if value == "flex-start" {
    Some(@types.Alignment::FlexStart)
  } else if value == "start" {
    Some(@types.Alignment::Start)
  } else if value == "flex-end" {
    Some(@types.Alignment::FlexEnd)
  } else if value == "end" {
    Some(@types.Alignment::End)
  } else if value == "left" {
    Some(@types.Alignment::Left)
  } else if value == "right" {
    Some(@types.Alignment::Right)
  } else if value == "center" {
    Some(@types.Alignment::Center)
  } else if value == "space-between" {
    Some(@types.SpaceBetween)
  } else if value == "space-around" {
    Some(@types.SpaceAround)
  } else if value == "space-evenly" {
    Some(@types.SpaceEvenly)
  } else if value == "stretch" {
    Some(@types.Alignment::Stretch)
  } else if value == "baseline" || value == "first baseline" {
    Some(@types.Alignment::Baseline)
  } else if value == "last baseline" {
    Some(@types.Alignment::End)
  } else {
    None
  }
}

///|
fn collect_alignment_parts(tokens : ArrayView[@token.Token]) -> Array[String] {
  let parts : Array[String] = []
  for i = 0; i < tokens.length(); i = i + 1 {
    match tokens[i] {
      @token.Token::Ident(s) => parts.push(s)
      _ => ()
    }
  }
  parts
}

///|
fn parse_alignment_segment_with_overflow(
  parts : Array[String],
  start : Int,
) -> ((@types.Alignment, Bool), Int)? {
  if start >= parts.length() {
    return None
  }

  let mut idx = start
  let mut is_safe = false
  let mut is_unsafe = false
  if parts[idx] == "safe" {
    is_safe = true
    idx = idx + 1
  } else if parts[idx] == "unsafe" {
    is_unsafe = true
    idx = idx + 1
  }
  if idx >= parts.length() {
    return None
  }

  let keyword = if idx + 1 < parts.length() &&
    parts[idx + 1] == "baseline" &&
    (parts[idx] == "first" || parts[idx] == "last") {
    parts[idx] + " baseline"
  } else {
    parts[idx]
  }
  let next_index = if keyword == parts[idx] { idx + 1 } else { idx + 2 }

  match parse_alignment_keyword(keyword) {
    Some(parsed) => {
      let alignment = if is_safe {
        match parsed {
          @types.Alignment::Center
          | @types.Alignment::End
          | @types.Alignment::FlexEnd => @types.Alignment::Start
          _ => parsed
        }
      } else {
        parsed
      }
      Some(((alignment, is_unsafe), next_index))
    }
    None => None
  }
}

///|
fn parse_alignment_segment(
  parts : Array[String],
  start : Int,
) -> (@types.Alignment, Int)? {
  match parse_alignment_segment_with_overflow(parts, start) {
    Some(((alignment, _), next_index)) => Some((alignment, next_index))
    None => None
  }
}

///|
fn parse_alignment_with_overflow(
  tokens : ArrayView[@token.Token],
) -> (@types.Alignment, Bool)? {
  let parts = collect_alignment_parts(tokens)
  match parse_alignment_segment_with_overflow(parts, 0) {
    Some(((alignment, is_unsafe), _)) => Some((alignment, is_unsafe))
    None => None
  }
}

///|
/// Parse alignment property (justify-content, align-items, etc.)
fn parse_alignment(tokens : ArrayView[@token.Token]) -> @types.Alignment? {
  let parts = collect_alignment_parts(tokens)
  match parse_alignment_segment(parts, 0) {
    Some((alignment, _)) => Some(alignment)
    None => None
  }
}

///|
fn parse_place_content_with_overflow(
  tokens : ArrayView[@token.Token],
) -> ((@types.Alignment, Bool), (@types.Alignment, Bool))? {
  let parts = collect_alignment_parts(tokens)
  match parse_alignment_segment_with_overflow(parts, 0) {
    Some((align_content, next_index)) =>
      if next_index >= parts.length() {
        Some((align_content, align_content))
      } else {
        match parse_alignment_segment_with_overflow(parts, next_index) {
          Some((justify_content, _)) => Some((align_content, justify_content))
          None => Some((align_content, align_content))
        }
      }
    None => None
  }
}

///|
/// Parse self-alignment property (align-self, justify-self)
fn parse_self_alignment(tokens : ArrayView[@token.Token]) -> @types.AlignSelf? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "auto" {
        Some(@types.AlignSelf::Auto)
      } else if s == "flex-start" || s == "start" {
        Some(@types.AlignSelf::Start)
      } else if s == "flex-end" || s == "end" {
        Some(@types.AlignSelf::End)
      } else if s == "center" {
        Some(@types.AlignSelf::Center)
      } else if s == "stretch" {
        Some(@types.AlignSelf::Stretch)
      } else if s == "baseline" {
        Some(@types.AlignSelf::Baseline)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse a numeric value
fn parse_number(tokens : ArrayView[@token.Token]) -> Double? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Number(value, _) => Some(value)
    _ => None
  }
}

///|
/// Parse an integer value
fn parse_integer(tokens : ArrayView[@token.Token]) -> Int? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Number(value, _) => Some(value.to_int())
    _ => None
  }
}

///|
/// Parse flex shorthand property
/// flex: none | auto |  [] []
/// The values can appear in any order, with these rules:
/// - Numbers are flex-grow (first) and flex-shrink (second)
/// - Dimensions/percentages/keywords are flex-basis
fn parse_flex_shorthand(
  tokens : ArrayView[@token.Token],
) -> (Double, Double, @types.Dimension)? {
  let tokens = skip_whitespace(tokens)
  if tokens.length() == 0 {
    return None
  }

  // Check for keyword values
  match tokens[0] {
    @token.Token::Ident(s) => {
      if s == "none" {
        // flex: none = 0 0 auto
        return Some((0.0, 0.0, @types.Auto))
      }
      if s == "auto" {
        // flex: auto = 1 1 auto
        return Some((1.0, 1.0, @types.Auto))
      }
      if s == "initial" {
        // flex: initial = 0 1 auto
        return Some((0.0, 1.0, @types.Auto))
      }
    }
    _ => ()
  }

  // CSS flex shorthand order:  [] []
  // Numbers must come first, then optionally a basis value.
  // Invalid orderings like "1 0% 1" (basis in middle) must return None.
  let numbers : Array[Double] = []
  let mut basis : @types.Dimension? = None
  let mut tokens_view = tokens
  let mut seen_basis = false

  // First, parse all leading numbers (grow and optionally shrink)
  while tokens_view.length() > 0 {
    tokens_view = skip_whitespace(tokens_view)
    if tokens_view.length() == 0 {
      break
    }
    match tokens_view[0] {
      @token.Token::Number(v, _) => {
        if seen_basis {
          // Number after basis is invalid (e.g., "1 0% 1")
          return None
        }
        numbers.push(v)
        tokens_view = tokens_view[1:]
      }
      @token.Token::Dimension(_, _) | @token.Token::Percentage(_) => {
        // This is flex-basis
        if seen_basis {
          // Second basis value is invalid
          return None
        }
        match parse_dimension(tokens_view) {
          Some(dim) => {
            basis = Some(dim)
            seen_basis = true
            tokens_view = tokens_view[1:]
          }
          None => break
        }
      }
      @token.Token::Ident(s) =>
        // Could be flex-basis keyword (auto, content, etc.)
        if s == "auto" || s == "content" {
          if seen_basis {
            return None
          }
          basis = Some(@types.Auto)
          seen_basis = true
          tokens_view = tokens_view[1:]
        } else {
          break
        }
      _ => break
    }
  }

  // Check for trailing tokens (anything after basis is invalid)
  tokens_view = skip_whitespace(tokens_view)
  if tokens_view.length() > 0 {
    // Still have tokens left - could be invalid like "1 0% 1"
    match tokens_view[0] {
      @token.Token::Number(_, _) =>
        // Number after basis is invalid
        if seen_basis {
          return None
        }
      _ => ()
    }
  }

  // Determine values based on what we found
  let grow = if numbers.length() >= 1 { numbers[0] } else { 1.0 }
  let shrink = if numbers.length() >= 2 { numbers[1] } else { 1.0 }
  let final_basis = match basis {
    Some(b) => b
    None =>
      // CSS spec: when only numbers are specified, basis defaults to 0%.
      if numbers.length() >= 1 {
        @types.Percent(0.0)
      } else {
        @types.Auto
      }
  }
  if numbers.length() == 0 && basis is None {
    return None
  }
  Some((grow, shrink, final_basis))
}

///|
/// Parse flex-flow shorthand property
/// flex-flow:  || 
fn parse_flex_flow_shorthand(
  tokens : ArrayView[@token.Token],
) -> (@types.FlexDirection, @types.FlexWrap)? {
  let tokens = skip_whitespace(tokens)
  if tokens.length() == 0 {
    return None
  }
  let mut direction : @types.FlexDirection? = None
  let mut wrap : @types.FlexWrap? = None

  // Parse up to 2 tokens
  let mut i = 0
  while i < tokens.length() {
    match tokens[i] {
      @token.Token::Ident(s) =>
        // Check if it's a flex-direction value
        if s == "row" {
          direction = Some(@types.FlexDirection::Row)
        } else if s == "row-reverse" {
          direction = Some(@types.FlexDirection::RowReverse)
        } else if s == "column" {
          direction = Some(@types.FlexDirection::Column)
        } else if s == "column-reverse" {
          direction = Some(@types.FlexDirection::ColumnReverse)
          // Check if it's a flex-wrap value
        } else if s == "nowrap" {
          wrap = Some(@types.FlexWrap::NoWrap)
        } else if s == "wrap" {
          wrap = Some(@types.FlexWrap::Wrap)
        } else if s == "wrap-reverse" {
          wrap = Some(@types.FlexWrap::WrapReverse)
        }
      @token.Token::Whitespace => ()
      _ => ()
    }
    i += 1
  }

  // If we found at least one value, return with defaults for the other
  if direction is Some(_) || wrap is Some(_) {
    Some(
      (
        direction.unwrap_or(@types.FlexDirection::Row),
        wrap.unwrap_or(@types.FlexWrap::NoWrap),
      ),
    )
  } else {
    None
  }
}

///|
/// Parse aspect-ratio property (e.g., "16 / 9" or "1.5")
fn parse_aspect_ratio(tokens : ArrayView[@token.Token]) -> Double? {
  if tokens.length() == 0 {
    return None
  }
  let tokens = skip_whitespace(tokens)
  match tokens[0] {
    @token.Token::Number(value, _) => {
      // Check for "x / y" format
      let rest = skip_whitespace(tokens[1:])
      if rest.length() >= 2 {
        match (rest[0], skip_whitespace(rest[1:])) {
          (@token.Token::Delim('/'), rest2) =>
            if rest2.length() > 0 {
              match rest2[0] {
                @token.Token::Number(divisor, _) =>
                  if divisor != 0.0 {
                    return Some(value / divisor)
                  } else {
                    return Some(value)
                  }
                _ => return Some(value)
              }
            } else {
              return Some(value)
            }
          _ => return Some(value)
        }
      }
      Some(value)
    }
    _ => None
  }
}

///|
/// Parse grid-auto-flow property
fn parse_grid_auto_flow(
  tokens : ArrayView[@token.Token],
) -> @types.GridAutoFlow? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "row" {
        Some(@types.GridAutoFlow::Row)
      } else if s == "column" {
        Some(@types.GridAutoFlow::Column)
      } else if s == "row dense" {
        Some(@types.RowDense)
      } else if s == "column dense" {
        Some(@types.ColumnDense)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse a single track value for MinTrackSizing
fn parse_min_track_sizing(
  tokens : ArrayView[@token.Token],
) -> (@types.MinTrackSizing, ArrayView[@token.Token])? {
  let tokens = skip_whitespace(tokens)
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Dimension(value, unit) =>
      if unit == "px" {
        Some((@types.MinTrackSizing::Length(value), tokens[1:]))
      } else if unit == "%" {
        Some((@types.MinTrackSizing::Percent(value / 100.0), tokens[1:]))
      } else {
        Some((@types.MinTrackSizing::Length(value), tokens[1:]))
      }
    @token.Token::Percentage(value) =>
      Some((@types.MinTrackSizing::Percent(value / 100.0), tokens[1:]))
    @token.Token::Number(value, _) =>
      if value == 0.0 {
        Some((@types.MinTrackSizing::Length(0.0), tokens[1:]))
      } else {
        None
      }
    @token.Token::Ident(s) =>
      if s == "auto" {
        Some((@types.MinTrackSizing::Auto, tokens[1:]))
      } else if s == "min-content" {
        Some((@types.MinTrackSizing::MinContent, tokens[1:]))
      } else if s == "max-content" {
        Some((@types.MinTrackSizing::MaxContent, tokens[1:]))
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse a single track value for MaxTrackSizing
fn parse_max_track_sizing(
  tokens : ArrayView[@token.Token],
) -> (@types.MaxTrackSizing, ArrayView[@token.Token])? {
  let tokens = skip_whitespace(tokens)
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Dimension(value, unit) =>
      if unit == "px" {
        Some((@types.MaxTrackSizing::Length(value), tokens[1:]))
      } else if unit == "%" {
        Some((@types.MaxTrackSizing::Percent(value / 100.0), tokens[1:]))
      } else if unit == "fr" {
        Some((@types.MaxTrackSizing::Fr(value), tokens[1:]))
      } else {
        Some((@types.MaxTrackSizing::Length(value), tokens[1:]))
      }
    @token.Token::Percentage(value) =>
      Some((@types.MaxTrackSizing::Percent(value / 100.0), tokens[1:]))
    @token.Token::Number(value, _) =>
      if value == 0.0 {
        Some((@types.MaxTrackSizing::Length(0.0), tokens[1:]))
      } else {
        None
      }
    @token.Token::Ident(s) =>
      if s == "auto" {
        Some((@types.MaxTrackSizing::Auto, tokens[1:]))
      } else if s == "min-content" {
        Some((@types.MaxTrackSizing::MinContent, tokens[1:]))
      } else if s == "max-content" {
        Some((@types.MaxTrackSizing::MaxContent, tokens[1:]))
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse a single track sizing value (without repeat)
fn parse_single_track_sizing(
  tokens : ArrayView[@token.Token],
) -> (@types.SingleTrackSizing, ArrayView[@token.Token])? {
  let tokens = skip_whitespace(tokens)
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Dimension(value, unit) =>
      if unit == "px" {
        Some((@types.SingleTrackSizing::Length(value), tokens[1:]))
      } else if unit == "%" {
        Some((@types.SingleTrackSizing::Percent(value / 100.0), tokens[1:]))
      } else if unit == "fr" {
        Some((@types.SingleTrackSizing::Fr(value), tokens[1:]))
      } else {
        Some((@types.SingleTrackSizing::Length(value), tokens[1:]))
      }
    @token.Token::Percentage(value) =>
      Some((@types.SingleTrackSizing::Percent(value / 100.0), tokens[1:]))
    @token.Token::Number(value, _) =>
      if value == 0.0 {
        Some((@types.SingleTrackSizing::Length(0.0), tokens[1:]))
      } else {
        None
      }
    @token.Token::Ident(s) =>
      if s == "auto" {
        Some((@types.SingleTrackSizing::Auto, tokens[1:]))
      } else if s == "min-content" {
        Some((@types.SingleTrackSizing::MinContent, tokens[1:]))
      } else if s == "max-content" {
        Some((@types.SingleTrackSizing::MaxContent, tokens[1:]))
      } else {
        None
      }
    @token.Token::Function(name) =>
      if name == "minmax" {
        // Parse minmax(min, max)
        let rest = tokens[1:] // skip Function token (already consumed '(')
        match parse_min_track_sizing(rest) {
          Some((min_val, rest2)) => {
            // Skip whitespace and comma
            let rest3 = skip_whitespace(rest2)
            if rest3.length() == 0 {
              return None
            }
            let rest4 = match rest3[0] {
              @token.Token::Comma => rest3[1:]
              _ => rest3
            }
            match parse_max_track_sizing(rest4) {
              Some((max_val, rest5)) => {
                // Skip to closing paren
                let rest6 = skip_whitespace(rest5)
                if rest6.length() > 0 {
                  match rest6[0] {
                    @token.Token::RightParen => {
                      let next_tokens = rest6[1:]
                      Some(
                        (
                          @types.SingleTrackSizing::MinMax(min_val, max_val),
                          next_tokens,
                        ),
                      )
                    }
                    _ => None
                  }
                } else {
                  None
                }
              }
              None => None
            }
          }
          None => None
        }
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse a track sizing function (including repeat)
fn parse_track_sizing_function(
  tokens : ArrayView[@token.Token],
) -> (@types.TrackSizingFunction, ArrayView[@token.Token])? {
  let tokens = skip_whitespace(tokens)
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Function(name) =>
      if name == "repeat" {
        // Parse repeat(count, tracks...)
        let rest = tokens[1:]
        let rest = skip_whitespace(rest)
        if rest.length() == 0 {
          return None
        }
        // Parse repeat count
        let (count, rest2) : (@types.RepeatCount, ArrayView[@token.Token]) = match
          rest[0] {
          @token.Token::Number(value, _) =>
            (@types.RepeatCount::Count(value.to_int()), rest[1:])
          @token.Token::Ident(s) =>
            if s == "auto-fill" {
              (@types.RepeatCount::AutoFill, rest[1:])
            } else if s == "auto-fit" {
              (@types.RepeatCount::AutoFit, rest[1:])
            } else {
              return None
            }
          _ => return None
        }
        // Skip comma
        let rest3 = skip_whitespace(rest2)
        if rest3.length() == 0 {
          return None
        }
        let rest4 = match rest3[0] {
          @token.Token::Comma => rest3[1:]
          _ => rest3
        }
        // Parse track list until closing paren
        let tracks : Array[@types.SingleTrackSizing] = []
        let mut remaining = rest4
        while remaining.length() > 0 {
          remaining = skip_whitespace(remaining)
          if remaining.length() == 0 {
            break
          }
          match remaining[0] {
            @token.Token::RightParen => {
              remaining = remaining[1:]
              break
            }
            _ =>
              match parse_single_track_sizing(remaining) {
                Some((track, rest)) => {
                  tracks.push(track)
                  remaining = rest
                }
                None => break
              }
          }
        }
        if tracks.length() > 0 {
          Some((@types.TrackSizingFunction::Repeat(count, tracks), remaining))
        } else {
          None
        }
      } else if name == "minmax" {
        // minmax is also valid as TrackSizingFunction
        match parse_single_track_sizing(tokens) {
          Some((@types.SingleTrackSizing::MinMax(min_val, max_val), rest)) =>
            Some((@types.TrackSizingFunction::MinMax(min_val, max_val), rest))
          _ => None
        }
      } else {
        None
      }
    @token.Token::Dimension(value, unit) =>
      if unit == "px" {
        Some((@types.TrackSizingFunction::Length(value), tokens[1:]))
      } else if unit == "%" {
        Some((@types.TrackSizingFunction::Percent(value / 100.0), tokens[1:]))
      } else if unit == "fr" {
        Some((@types.TrackSizingFunction::Fr(value), tokens[1:]))
      } else {
        Some((@types.TrackSizingFunction::Length(value), tokens[1:]))
      }
    @token.Token::Percentage(value) =>
      Some((@types.TrackSizingFunction::Percent(value / 100.0), tokens[1:]))
    @token.Token::Number(value, _) =>
      if value == 0.0 {
        Some((@types.TrackSizingFunction::Length(0.0), tokens[1:]))
      } else {
        None
      }
    @token.Token::Ident(s) =>
      if s == "auto" {
        Some((@types.TrackSizingFunction::Auto, tokens[1:]))
      } else if s == "min-content" {
        Some((@types.TrackSizingFunction::MinContent, tokens[1:]))
      } else if s == "max-content" {
        Some((@types.TrackSizingFunction::MaxContent, tokens[1:]))
      } else if s == "none" {
        // none means empty template
        None
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse grid-template-columns or grid-template-rows value
fn parse_grid_template_tracks(
  tokens : ArrayView[@token.Token],
) -> Array[@types.TrackSizingFunction] {
  let result : Array[@types.TrackSizingFunction] = []
  let mut remaining = tokens
  while remaining.length() > 0 {
    remaining = skip_whitespace(remaining)
    if remaining.length() == 0 {
      break
    }
    match remaining[0] {
      @token.Token::Semicolon | @token.Token::EOF => break
      @token.Token::Ident(s) =>
        if s == "none" {
          // none means empty template
          return []
        } else {
          match parse_track_sizing_function(remaining) {
            Some((track, rest)) => {
              result.push(track)
              remaining = rest
            }
            None => break
          }
        }
      _ =>
        match parse_track_sizing_function(remaining) {
          Some((track, rest)) => {
            result.push(track)
            remaining = rest
          }
          None => break
        }
    }
  }
  result
}

///|
/// Parse grid-template shorthand: ` / `
fn parse_grid_template_shorthand(
  tokens : ArrayView[@token.Token],
) -> (Array[@types.TrackSizingFunction], Array[@types.TrackSizingFunction])? {
  let mut slash_index = -1
  for i = 0; i < tokens.length(); i = i + 1 {
    match tokens[i] {
      @token.Token::Delim('/') => {
        slash_index = i
        break
      }
      _ => ()
    }
  }
  if slash_index < 0 {
    return None
  }
  let rows = parse_grid_template_tracks(tokens[:slash_index])
  let cols = parse_grid_template_tracks(tokens[slash_index + 1:])
  Some((rows, cols))
}

///|
/// Parse grid-template-columns or grid-template-rows from a raw string
pub fn parse_grid_template_tracks_from_string(
  value : String,
) -> Array[@types.TrackSizingFunction] {
  let tokens = @token.tokenize(value)
  parse_grid_template_tracks(tokens[:])
}

///|
/// Parse overflow property
fn parse_overflow(tokens : ArrayView[@token.Token]) -> @types.Overflow? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "visible" {
        Some(@types.Visible)
      } else if s == "hidden" {
        Some(@types.Hidden)
      } else if s == "clip" {
        Some(@types.Clip)
      } else if s == "scroll" {
        Some(@types.Scroll)
      } else if s == "auto" {
        Some(@types.Overflow::Auto)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse clip property (legacy, for accessibility patterns)
fn parse_clip(tokens : ArrayView[@token.Token]) -> @types.ClipRect {
  if tokens.length() == 0 {
    return @types.ClipRect::Auto
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "auto" || s == "initial" || s == "unset" {
        @types.ClipRect::Auto
      } else {
        @types.ClipRect::Auto
      }
    @token.Token::Function(name) =>
      if name == "rect" {
        // Parse rect(top, right, bottom, left)
        // tokens[1:] contains the arguments
        let args = tokens[1:]
        let values : Array[Double] = []
        for token in args {
          match token {
            @token.Token::Dimension(v, _) => values.push(v)
            @token.Token::Number(v, _) => values.push(v)
            @token.Token::Ident(s) =>
              if s == "auto" {
                values.push(0.0) // auto in rect is treated as 0
              }
            _ => ()
          }
          if values.length() >= 4 {
            break
          }
        }
        if values.length() >= 4 {
          @types.ClipRect::Rect(
            top=values[0],
            right=values[1],
            bottom=values[2],
            left=values[3],
          )
        } else {
          @types.ClipRect::Auto
        }
      } else {
        @types.ClipRect::Auto
      }
    _ => @types.ClipRect::Auto
  }
}

///|
/// Parse clip-path property subset for paint and hit testing.
fn parse_clip_path(tokens : ArrayView[@token.Token]) -> @style.ClipPath {
  let remaining = skip_whitespace(tokens)
  if remaining.length() == 0 {
    return @style.ClipPath::None
  }
  match remaining[0] {
    @token.Token::Ident(s) =>
      if s == "none" || s == "initial" || s == "unset" {
        @style.ClipPath::None
      } else {
        @style.ClipPath::None
      }
    @token.Token::Function(name) =>
      if name == "inset" {
        parse_clip_path_inset(remaining[1:])
      } else if name == "rect" {
        parse_clip_path_rect(remaining[1:])
      } else if name == "xywh" {
        parse_clip_path_xywh(remaining[1:])
      } else if name == "circle" {
        parse_clip_path_circle(remaining[1:])
      } else if name == "ellipse" {
        parse_clip_path_ellipse(remaining[1:])
      } else if name == "polygon" {
        parse_clip_path_polygon(remaining[1:])
      } else if name == "path" {
        parse_clip_path_path(remaining[1:])
      } else {
        @style.ClipPath::None
      }
    _ => @style.ClipPath::None
  }
}

///|
fn parse_clip_path_inset(tokens : ArrayView[@token.Token]) -> @style.ClipPath {
  let values : Array[Double] = []
  for token in tokens {
    match token {
      @token.Token::Dimension(v, unit) =>
        if unit == "%" {
          values.push(-(v / 100.0))
        } else {
          values.push(v)
        }
      @token.Token::Percentage(v) => values.push(-(v / 100.0))
      @token.Token::Number(v, _) => values.push(v)
      @token.Token::Ident(s) => if s == "round" { break }
      @token.Token::RightParen | @token.Token::Semicolon | @token.Token::EOF =>
        break
      _ => ()
    }
    if values.length() >= 4 {
      break
    }
  }
  match values.length() {
    0 => @style.ClipPath::None
    1 => {
      let v = values[0]
      @style.ClipPath::Inset(v, v, v, v)
    }
    2 => {
      let vertical = values[0]
      let horizontal = values[1]
      @style.ClipPath::Inset(vertical, horizontal, vertical, horizontal)
    }
    3 => @style.ClipPath::Inset(values[0], values[1], values[2], values[1])
    _ => @style.ClipPath::Inset(values[0], values[1], values[2], values[3])
  }
}

///|
fn parse_clip_path_rect(tokens : ArrayView[@token.Token]) -> @style.ClipPath {
  let values : Array[Double] = []
  for token in tokens {
    match token {
      @token.Token::RightParen | @token.Token::Semicolon | @token.Token::EOF =>
        break
      @token.Token::Whitespace => ()
      _ =>
        match parse_clip_path_shape_token(token) {
          Some(v) => values.push(v)
          None => ()
        }
    }
    if values.length() >= 4 {
      break
    }
  }
  if values.length() < 4 {
    @style.ClipPath::None
  } else {
    @style.ClipPath::Rect(values[0], values[1], values[2], values[3])
  }
}

///|
fn parse_clip_path_xywh(tokens : ArrayView[@token.Token]) -> @style.ClipPath {
  let values : Array[Double] = []
  for token in tokens {
    match token {
      @token.Token::Ident(s) => if s == "round" { break }
      @token.Token::RightParen | @token.Token::Semicolon | @token.Token::EOF =>
        break
      @token.Token::Whitespace => ()
      _ =>
        match parse_clip_path_shape_token(token) {
          Some(v) => values.push(v)
          None => ()
        }
    }
    if values.length() >= 4 {
      break
    }
  }
  if values.length() < 4 {
    @style.ClipPath::None
  } else {
    @style.ClipPath::Xywh(values[0], values[1], values[2], values[3])
  }
}

///|
fn parse_clip_path_circle(tokens : ArrayView[@token.Token]) -> @style.ClipPath {
  let mut radius = -0.5
  let mut center_x = -0.5
  let mut center_y = -0.5
  let center_values : Array[Double] = []
  let mut seen_at = false
  let mut radius_set = false
  for token in tokens {
    match token {
      @token.Token::Ident(s) =>
        if s == "at" {
          seen_at = true
        } else {
          return @style.ClipPath::None
        }
      @token.Token::RightParen | @token.Token::Semicolon | @token.Token::EOF =>
        break
      @token.Token::Whitespace => ()
      _ =>
        match parse_clip_path_shape_token(token) {
          Some(v) =>
            if seen_at {
              center_values.push(v)
            } else if !radius_set {
              radius = v
              radius_set = true
            }
          None => ()
        }
    }
  }
  if seen_at {
    if center_values.length() < 2 {
      return @style.ClipPath::None
    }
    center_x = center_values[0]
    center_y = center_values[1]
  }
  @style.ClipPath::Circle(radius, center_x, center_y)
}

///|
fn parse_clip_path_ellipse(tokens : ArrayView[@token.Token]) -> @style.ClipPath {
  let mut radius_x = -0.5
  let mut radius_y = -0.5
  let mut center_x = -0.5
  let mut center_y = -0.5
  let radii : Array[Double] = []
  let center_values : Array[Double] = []
  let mut seen_at = false
  for token in tokens {
    match token {
      @token.Token::Ident(s) =>
        if s == "at" {
          seen_at = true
        } else {
          return @style.ClipPath::None
        }
      @token.Token::RightParen | @token.Token::Semicolon | @token.Token::EOF =>
        break
      @token.Token::Whitespace => ()
      _ =>
        match parse_clip_path_shape_token(token) {
          Some(v) => if seen_at { center_values.push(v) } else { radii.push(v) }
          None => ()
        }
    }
  }
  if radii.length() == 0 {
    ()
  } else if radii.length() >= 2 {
    radius_x = radii[0]
    radius_y = radii[1]
  } else {
    return @style.ClipPath::None
  }
  if seen_at {
    if center_values.length() < 2 {
      return @style.ClipPath::None
    }
    center_x = center_values[0]
    center_y = center_values[1]
  }
  @style.ClipPath::Ellipse(radius_x, radius_y, center_x, center_y)
}

///|
fn parse_clip_path_polygon(tokens : ArrayView[@token.Token]) -> @style.ClipPath {
  let values : Array[Double] = []
  for token in tokens {
    match token {
      @token.Token::Ident(s) =>
        if s == "evenodd" || s == "nonzero" {
          ()
        } else {
          return @style.ClipPath::None
        }
      @token.Token::RightParen | @token.Token::Semicolon | @token.Token::EOF =>
        break
      @token.Token::Whitespace => ()
      _ =>
        match parse_clip_path_shape_token(token) {
          Some(v) => values.push(v)
          None => ()
        }
    }
  }
  if values.length() < 6 || values.length() % 2 != 0 {
    return @style.ClipPath::None
  }
  let points : Array[(Double, Double)] = []
  let mut i = 0
  while i + 1 < values.length() {
    points.push((values[i], values[i + 1]))
    i = i + 2
  }
  @style.ClipPath::Polygon(points)
}

///|
fn parse_clip_path_path(tokens : ArrayView[@token.Token]) -> @style.ClipPath {
  for token in tokens {
    match token {
      @token.Token::String(path_data) =>
        return parse_clip_path_path_data(path_data)
      @token.Token::RightParen | @token.Token::Semicolon | @token.Token::EOF =>
        break
      _ => ()
    }
  }
  @style.ClipPath::None
}

///|
fn parse_clip_path_path_data(path_data : String) -> @style.ClipPath {
  let tokens = tokenize_clip_path_path_data(path_data)
  let points : Array[(Double, Double)] = []
  let mut i = 0
  let mut command = ""
  let mut current_x = 0.0
  let mut current_y = 0.0
  let mut start_x = 0.0
  let mut start_y = 0.0
  let mut has_quadratic_control = false
  let mut quadratic_control_x = 0.0
  let mut quadratic_control_y = 0.0
  let mut has_cubic_control = false
  let mut cubic_control_x = 0.0
  let mut cubic_control_y = 0.0
  while i < tokens.length() {
    let token = tokens[i]
    if is_clip_path_path_command(token) {
      if token == "Z" || token == "z" {
        current_x = start_x
        current_y = start_y
        has_quadratic_control = false
        has_cubic_control = false
        command = ""
        i += 1
        continue
      }
      command = token
      i += 1
      continue
    }
    match command {
      "M" | "m" => {
        if i + 1 >= tokens.length() {
          return @style.ClipPath::None
        }
        let x = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let y = match parse_clip_path_path_number(tokens[i + 1]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        if command == "m" {
          current_x += x
          current_y += y
        } else {
          current_x = x
          current_y = y
        }
        start_x = current_x
        start_y = current_y
        points.push((current_x, current_y))
        command = if command == "m" { "l" } else { "L" }
        has_quadratic_control = false
        has_cubic_control = false
        i += 2
      }
      "L" | "l" => {
        if i + 1 >= tokens.length() {
          return @style.ClipPath::None
        }
        let x = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let y = match parse_clip_path_path_number(tokens[i + 1]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        if command == "l" {
          current_x += x
          current_y += y
        } else {
          current_x = x
          current_y = y
        }
        points.push((current_x, current_y))
        has_quadratic_control = false
        has_cubic_control = false
        i += 2
      }
      "H" | "h" => {
        let x = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        if command == "h" {
          current_x += x
        } else {
          current_x = x
        }
        points.push((current_x, current_y))
        has_quadratic_control = false
        has_cubic_control = false
        i += 1
      }
      "V" | "v" => {
        let y = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        if command == "v" {
          current_y += y
        } else {
          current_y = y
        }
        points.push((current_x, current_y))
        has_quadratic_control = false
        has_cubic_control = false
        i += 1
      }
      "Q" | "q" => {
        if i + 3 >= tokens.length() {
          return @style.ClipPath::None
        }
        let control_x = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let control_y = match parse_clip_path_path_number(tokens[i + 1]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_x = match parse_clip_path_path_number(tokens[i + 2]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_y = match parse_clip_path_path_number(tokens[i + 3]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let absolute_control_x = if command == "q" {
          current_x + control_x
        } else {
          control_x
        }
        let absolute_control_y = if command == "q" {
          current_y + control_y
        } else {
          control_y
        }
        let absolute_end_x = if command == "q" {
          current_x + end_x
        } else {
          end_x
        }
        let absolute_end_y = if command == "q" {
          current_y + end_y
        } else {
          end_y
        }
        append_quadratic_path_points(
          points, current_x, current_y, absolute_control_x, absolute_control_y, absolute_end_x,
          absolute_end_y,
        )
        current_x = absolute_end_x
        current_y = absolute_end_y
        has_quadratic_control = true
        quadratic_control_x = absolute_control_x
        quadratic_control_y = absolute_control_y
        has_cubic_control = false
        i += 4
      }
      "T" | "t" => {
        if i + 1 >= tokens.length() {
          return @style.ClipPath::None
        }
        let end_x = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_y = match parse_clip_path_path_number(tokens[i + 1]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let absolute_control_x = if has_quadratic_control {
          2.0 * current_x - quadratic_control_x
        } else {
          current_x
        }
        let absolute_control_y = if has_quadratic_control {
          2.0 * current_y - quadratic_control_y
        } else {
          current_y
        }
        let absolute_end_x = if command == "t" {
          current_x + end_x
        } else {
          end_x
        }
        let absolute_end_y = if command == "t" {
          current_y + end_y
        } else {
          end_y
        }
        append_quadratic_path_points(
          points, current_x, current_y, absolute_control_x, absolute_control_y, absolute_end_x,
          absolute_end_y,
        )
        current_x = absolute_end_x
        current_y = absolute_end_y
        has_quadratic_control = true
        quadratic_control_x = absolute_control_x
        quadratic_control_y = absolute_control_y
        has_cubic_control = false
        i += 2
      }
      "C" | "c" => {
        if i + 5 >= tokens.length() {
          return @style.ClipPath::None
        }
        let control1_x = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let control1_y = match parse_clip_path_path_number(tokens[i + 1]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let control2_x = match parse_clip_path_path_number(tokens[i + 2]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let control2_y = match parse_clip_path_path_number(tokens[i + 3]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_x = match parse_clip_path_path_number(tokens[i + 4]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_y = match parse_clip_path_path_number(tokens[i + 5]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let absolute_control1_x = if command == "c" {
          current_x + control1_x
        } else {
          control1_x
        }
        let absolute_control1_y = if command == "c" {
          current_y + control1_y
        } else {
          control1_y
        }
        let absolute_control2_x = if command == "c" {
          current_x + control2_x
        } else {
          control2_x
        }
        let absolute_control2_y = if command == "c" {
          current_y + control2_y
        } else {
          control2_y
        }
        let absolute_end_x = if command == "c" {
          current_x + end_x
        } else {
          end_x
        }
        let absolute_end_y = if command == "c" {
          current_y + end_y
        } else {
          end_y
        }
        append_cubic_path_points(
          points, current_x, current_y, absolute_control1_x, absolute_control1_y,
          absolute_control2_x, absolute_control2_y, absolute_end_x, absolute_end_y,
        )
        current_x = absolute_end_x
        current_y = absolute_end_y
        has_cubic_control = true
        cubic_control_x = absolute_control2_x
        cubic_control_y = absolute_control2_y
        has_quadratic_control = false
        i += 6
      }
      "S" | "s" => {
        if i + 3 >= tokens.length() {
          return @style.ClipPath::None
        }
        let control2_x = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let control2_y = match parse_clip_path_path_number(tokens[i + 1]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_x = match parse_clip_path_path_number(tokens[i + 2]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_y = match parse_clip_path_path_number(tokens[i + 3]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let absolute_control1_x = if has_cubic_control {
          2.0 * current_x - cubic_control_x
        } else {
          current_x
        }
        let absolute_control1_y = if has_cubic_control {
          2.0 * current_y - cubic_control_y
        } else {
          current_y
        }
        let absolute_control2_x = if command == "s" {
          current_x + control2_x
        } else {
          control2_x
        }
        let absolute_control2_y = if command == "s" {
          current_y + control2_y
        } else {
          control2_y
        }
        let absolute_end_x = if command == "s" {
          current_x + end_x
        } else {
          end_x
        }
        let absolute_end_y = if command == "s" {
          current_y + end_y
        } else {
          end_y
        }
        append_cubic_path_points(
          points, current_x, current_y, absolute_control1_x, absolute_control1_y,
          absolute_control2_x, absolute_control2_y, absolute_end_x, absolute_end_y,
        )
        current_x = absolute_end_x
        current_y = absolute_end_y
        has_cubic_control = true
        cubic_control_x = absolute_control2_x
        cubic_control_y = absolute_control2_y
        has_quadratic_control = false
        i += 4
      }
      "A" | "a" => {
        if i + 6 >= tokens.length() {
          return @style.ClipPath::None
        }
        let radius_x = match parse_clip_path_path_number(tokens[i]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let radius_y = match parse_clip_path_path_number(tokens[i + 1]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let rotation_degrees = match
          parse_clip_path_path_number(tokens[i + 2]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let large_arc_flag = match parse_clip_path_path_number(tokens[i + 3]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let sweep_flag = match parse_clip_path_path_number(tokens[i + 4]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_x = match parse_clip_path_path_number(tokens[i + 5]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let end_y = match parse_clip_path_path_number(tokens[i + 6]) {
          Some(v) => v
          None => return @style.ClipPath::None
        }
        let absolute_end_x = if command == "a" {
          current_x + end_x
        } else {
          end_x
        }
        let absolute_end_y = if command == "a" {
          current_y + end_y
        } else {
          end_y
        }
        append_arc_path_points(
          points,
          current_x,
          current_y,
          radius_x,
          radius_y,
          rotation_degrees,
          large_arc_flag != 0.0,
          sweep_flag != 0.0,
          absolute_end_x,
          absolute_end_y,
        )
        current_x = absolute_end_x
        current_y = absolute_end_y
        has_quadratic_control = false
        has_cubic_control = false
        i += 7
      }
      _ => return @style.ClipPath::None
    }
  }
  if points.length() < 3 {
    return @style.ClipPath::None
  }
  @style.ClipPath::Polygon(points)
}

///|
fn tokenize_clip_path_path_data(path_data : String) -> Array[String] {
  let tokens : Array[String] = []
  let current = StringBuilder::new()
  for c in path_data.iter() {
    if is_clip_path_path_command_char(c) {
      flush_clip_path_path_token(tokens, current)
      tokens.push(clip_path_path_char_to_string(c))
    } else if c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r' {
      flush_clip_path_path_token(tokens, current)
    } else if c == '-' || c == '+' {
      let current_text = current.to_string()
      if !current_text.is_empty() &&
        !current_text.has_suffix("e") &&
        !current_text.has_suffix("E") {
        flush_clip_path_path_token(tokens, current)
      }
      current.write_char(c)
    } else {
      current.write_char(c)
    }
  }
  flush_clip_path_path_token(tokens, current)
  tokens
}

///|
fn flush_clip_path_path_token(
  tokens : Array[String],
  current : StringBuilder,
) -> Unit {
  let token = current.to_string().trim().to_string()
  if !token.is_empty() {
    tokens.push(token)
    current.reset()
  }
}

///|
fn append_quadratic_path_points(
  points : Array[(Double, Double)],
  start_x : Double,
  start_y : Double,
  control_x : Double,
  control_y : Double,
  end_x : Double,
  end_y : Double,
) -> Unit {
  let segments = 8
  for step in 1..<(segments + 1) {
    let t = step.to_double() / segments.to_double()
    let one_minus_t = 1.0 - t
    let x = one_minus_t * one_minus_t * start_x +
      2.0 * one_minus_t * t * control_x +
      t * t * end_x
    let y = one_minus_t * one_minus_t * start_y +
      2.0 * one_minus_t * t * control_y +
      t * t * end_y
    points.push((x, y))
  }
}

///|
fn append_cubic_path_points(
  points : Array[(Double, Double)],
  start_x : Double,
  start_y : Double,
  control1_x : Double,
  control1_y : Double,
  control2_x : Double,
  control2_y : Double,
  end_x : Double,
  end_y : Double,
) -> Unit {
  let segments = 8
  for step in 1..<(segments + 1) {
    let t = step.to_double() / segments.to_double()
    let one_minus_t = 1.0 - t
    let x = one_minus_t * one_minus_t * one_minus_t * start_x +
      3.0 * one_minus_t * one_minus_t * t * control1_x +
      3.0 * one_minus_t * t * t * control2_x +
      t * t * t * end_x
    let y = one_minus_t * one_minus_t * one_minus_t * start_y +
      3.0 * one_minus_t * one_minus_t * t * control1_y +
      3.0 * one_minus_t * t * t * control2_y +
      t * t * t * end_y
    points.push((x, y))
  }
}

///|
fn append_arc_path_points(
  points : Array[(Double, Double)],
  start_x : Double,
  start_y : Double,
  radius_x : Double,
  radius_y : Double,
  rotation_degrees : Double,
  large_arc : Bool,
  sweep : Bool,
  end_x : Double,
  end_y : Double,
) -> Unit {
  if (start_x == end_x && start_y == end_y) ||
    radius_x == 0.0 ||
    radius_y == 0.0 {
    points.push((end_x, end_y))
    return
  }
  let mut rx = if radius_x < 0.0 { -radius_x } else { radius_x }
  let mut ry = if radius_y < 0.0 { -radius_y } else { radius_y }
  let pi = 3.14159265358979323846
  let phi = rotation_degrees * pi / 180.0
  let cos_phi = @math.cos(phi)
  let sin_phi = @math.sin(phi)
  let dx = (start_x - end_x) / 2.0
  let dy = (start_y - end_y) / 2.0
  let x1p = cos_phi * dx + sin_phi * dy
  let y1p = -sin_phi * dx + cos_phi * dy
  let lambda = x1p * x1p / (rx * rx) + y1p * y1p / (ry * ry)
  if lambda > 1.0 {
    let scale = lambda.sqrt()
    rx = rx * scale
    ry = ry * scale
  }
  let rx2 = rx * rx
  let ry2 = ry * ry
  let x1p2 = x1p * x1p
  let y1p2 = y1p * y1p
  let denominator = rx2 * y1p2 + ry2 * x1p2
  if denominator == 0.0 {
    points.push((end_x, end_y))
    return
  }
  let sq = (rx2 * ry2 - rx2 * y1p2 - ry2 * x1p2) / denominator
  let sq_abs = if sq < 0.0 { 0.0 } else { sq }
  let coef = sq_abs.sqrt() * (if large_arc == sweep { -1.0 } else { 1.0 })
  let cxp = coef * rx * y1p / ry
  let cyp = -coef * ry * x1p / rx
  let center_x = cos_phi * cxp - sin_phi * cyp + (start_x + end_x) / 2.0
  let center_y = sin_phi * cxp + cos_phi * cyp + (start_y + end_y) / 2.0
  let theta1 = clip_path_angle_between(
    1.0,
    0.0,
    (x1p - cxp) / rx,
    (y1p - cyp) / ry,
  )
  let mut dtheta = clip_path_angle_between(
    (x1p - cxp) / rx,
    (y1p - cyp) / ry,
    (-x1p - cxp) / rx,
    (-y1p - cyp) / ry,
  )
  if !sweep && dtheta > 0.0 {
    dtheta = dtheta - 2.0 * pi
  } else if sweep && dtheta < 0.0 {
    dtheta = dtheta + 2.0 * pi
  }
  let segments = clip_path_max_int((dtheta.abs() / pi * 8.0).to_int(), 2)
  for step in 1..<(segments + 1) {
    let t = step.to_double() / segments.to_double()
    let theta = theta1 + t * dtheta
    let cos_t = @math.cos(theta)
    let sin_t = @math.sin(theta)
    let x = cos_phi * rx * cos_t - sin_phi * ry * sin_t + center_x
    let y = sin_phi * rx * cos_t + cos_phi * ry * sin_t + center_y
    points.push((x, y))
  }
}

///|
fn clip_path_angle_between(
  ux : Double,
  uy : Double,
  vx : Double,
  vy : Double,
) -> Double {
  let dot = ux * vx + uy * vy
  let len_product = (ux * ux + uy * uy).sqrt() * (vx * vx + vy * vy).sqrt()
  if len_product == 0.0 {
    return 0.0
  }
  let mut cos_angle = dot / len_product
  if cos_angle > 1.0 {
    cos_angle = 1.0
  }
  if cos_angle < -1.0 {
    cos_angle = -1.0
  }
  let angle = @math.acos(cos_angle)
  let cross = ux * vy - uy * vx
  if cross < 0.0 {
    -angle
  } else {
    angle
  }
}

///|
fn clip_path_max_int(a : Int, b : Int) -> Int {
  if a > b {
    a
  } else {
    b
  }
}

///|
fn is_clip_path_path_command(token : String) -> Bool {
  token == "M" ||
  token == "m" ||
  token == "L" ||
  token == "l" ||
  token == "H" ||
  token == "h" ||
  token == "V" ||
  token == "v" ||
  token == "Q" ||
  token == "q" ||
  token == "T" ||
  token == "t" ||
  token == "C" ||
  token == "c" ||
  token == "S" ||
  token == "s" ||
  token == "A" ||
  token == "a" ||
  token == "Z" ||
  token == "z"
}

///|
fn is_clip_path_path_command_char(c : Char) -> Bool {
  c == 'M' ||
  c == 'm' ||
  c == 'L' ||
  c == 'l' ||
  c == 'H' ||
  c == 'h' ||
  c == 'V' ||
  c == 'v' ||
  c == 'Z' ||
  c == 'z' ||
  c == 'C' ||
  c == 'c' ||
  c == 'S' ||
  c == 's' ||
  c == 'Q' ||
  c == 'q' ||
  c == 'T' ||
  c == 't' ||
  c == 'A' ||
  c == 'a'
}

///|
fn clip_path_path_char_to_string(c : Char) -> String {
  let sb = StringBuilder::new()
  sb.write_char(c)
  sb.to_string()
}

///|
fn parse_clip_path_path_number(token : String) -> Double? {
  let value = @string.parse_double(token) catch { _ => return None }
  Some(value)
}

///|
fn parse_clip_path_shape_token(token : @token.Token) -> Double? {
  match token {
    @token.Token::Dimension(v, unit) =>
      if unit == "%" {
        Some(-(v / 100.0))
      } else {
        Some(v)
      }
    @token.Token::Percentage(v) => Some(-(v / 100.0))
    @token.Token::Number(v, _) => Some(v)
    _ => None
  }
}

///|
/// Parse box-sizing property
fn parse_box_sizing(tokens : ArrayView[@token.Token]) -> @types.BoxSizing? {
  if tokens.length() == 0 {
    return None
  }
  match tokens[0] {
    @token.Token::Ident(s) =>
      if s == "content-box" {
        Some(@types.ContentBox)
      } else if s == "border-box" {
        Some(@types.BorderBox)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Skip whitespace tokens and return remaining view
fn skip_whitespace(tokens : ArrayView[@token.Token]) -> ArrayView[@token.Token] {
  let mut i = 0
  while i < tokens.length() {
    match tokens[i] {
      @token.Token::Whitespace => i += 1
      _ => break
    }
  }
  tokens[i:]
}

///|
/// Parse CSS contain property
/// Supports: none, strict, content, size, inline-size, layout, paint, style
/// Multiple keywords can be combined (e.g., "layout paint")
fn parse_contain(tokens : ArrayView[@token.Token]) -> @style.Contain {
  let mut result = @style.Contain::none()
  let mut remaining = skip_whitespace(tokens)
  while remaining.length() > 0 {
    match remaining[0] {
      @token.Token::Ident(s) => {
        if s == "none" {
          return @style.Contain::none()
        } else if s == "strict" {
          return @style.Contain::strict()
        } else if s == "content" {
          return @style.Contain::content()
        } else if s == "size" {
          result = { ..result, size: true }
        } else if s == "inline-size" {
          result = { ..result, inline_size: true }
        } else if s == "layout" {
          result = { ..result, layout: true }
        } else if s == "paint" {
          result = { ..result, paint: true }
        } else if s == "style" {
          result = { ..result, style: true }
        }
        remaining = skip_whitespace(remaining[1:])
      }
      @token.Token::Whitespace => remaining = skip_whitespace(remaining[1:])
      _ => break
    }
  }
  result
}

///|
fn merge_contain(
  base : @style.Contain,
  extra : @style.Contain,
) -> @style.Contain {
  let merged : @style.Contain = {
    size: base.size || extra.size,
    inline_size: base.inline_size || extra.inline_size,
    layout: base.layout || extra.layout,
    paint: base.paint || extra.paint,
    style: base.style || extra.style,
  }
  if merged.size {
    { ..merged, inline_size: false }
  } else {
    merged
  }
}

///|
/// Parse CSS container-type property into implied containment bits.
/// Supported: normal | size | inline-size
fn parse_container_type(tokens : ArrayView[@token.Token]) -> @style.Contain? {
  let remaining = skip_whitespace(tokens)
  if remaining.length() == 0 {
    return None
  }
  match remaining[0] {
    @token.Token::Ident(s) =>
      if s == "normal" {
        Some(@style.Contain::none())
      } else if s == "size" {
        Some({ ..@style.Contain::none(), size: true, style: true })
      } else if s == "inline-size" {
        Some({ ..@style.Contain::none(), inline_size: true, style: true })
      } else {
        None
      }
    _ => None
  }
}

///|
/// Parse contain-intrinsic-inline-size / contain-intrinsic-block-size.
/// Returns a content-box fallback length in px, or None for none/auto.
fn parse_contain_intrinsic_axis(tokens : ArrayView[@token.Token]) -> Double? {
  let remaining = skip_whitespace(tokens)
  if remaining.length() == 0 {
    return None
  }
  match remaining[0] {
    @token.Token::Ident(s) =>
      if s == "none" || s == "auto" {
        None
      } else {
        match parse_dimension(remaining) {
          Some(@types.Length(v)) => if v > 0.0 { Some(v) } else { Some(0.0) }
          _ => None
        }
      }
    _ =>
      match parse_dimension(remaining) {
        Some(@types.Length(v)) => if v > 0.0 { Some(v) } else { Some(0.0) }
        _ => None
      }
  }
}

///|
/// Parse contain-intrinsic-size shorthand.
/// One value applies to both inline/block axes; two values map to inline then block.
fn parse_contain_intrinsic_size(
  tokens : ArrayView[@token.Token],
) -> (Double?, Double?)? {
  let mut remaining = skip_whitespace(tokens)
  let axis_values : Array[Double?] = []
  while remaining.length() > 0 {
    match remaining[0] {
      @token.Token::Whitespace => remaining = skip_whitespace(remaining[1:])
      @token.Token::Ident(s) =>
        if s == "auto" {
          remaining = skip_whitespace(remaining[1:])
        } else if s == "none" {
          axis_values.push(None)
          remaining = skip_whitespace(remaining[1:])
        } else {
          match parse_dimension(remaining) {
            Some(@types.Length(v)) =>
              axis_values.push(Some(if v > 0.0 { v } else { 0.0 }))
            _ => ()
          }
          remaining = skip_whitespace(remaining[1:])
        }
      _ => {
        match parse_dimension(remaining) {
          Some(@types.Length(v)) =>
            axis_values.push(Some(if v > 0.0 { v } else { 0.0 }))
          _ => ()
        }
        remaining = skip_whitespace(remaining[1:])
      }
    }
  }
  if axis_values.length() == 0 {
    None
  } else if axis_values.length() == 1 {
    Some((axis_values[0], axis_values[0]))
  } else {
    Some((axis_values[0], axis_values[1]))
  }
}

///|
/// Parse CSS transform property
/// Supports: translate(x, y), translateX(x), translateY(y), translate3d(x, y, z), none
fn parse_transform(tokens : ArrayView[@token.Token]) -> @style.Transform {
  let remaining = skip_whitespace(tokens)
  if remaining.length() == 0 {
    return @style.Transform::none()
  }
  // Check for "none"
  match remaining[0] {
    @token.Token::Ident(s) => if s == "none" { return @style.Transform::none() }
    _ => ()
  }
  // Reconstruct the value as string for parsing with the full transform parser
  // Since transform values like "translate(10px, 20px)" are complex,
  // we build the string and reuse the computed transform parser
  let sb = StringBuilder::new()
  for i = 0; i < remaining.length(); i = i + 1 {
    match remaining[i] {
      @token.Token::Ident(s) => sb.write_string(s)
      @token.Token::Function(name) => {
        sb.write_string(name)
        sb.write_char('(')
      }
      @token.Token::Dimension(value, unit) => {
        sb.write_string(value.to_string())
        sb.write_string(unit)
      }
      @token.Token::Percentage(value) => {
        sb.write_string(value.to_string())
        sb.write_char('%')
      }
      @token.Token::Number(value, _) => sb.write_string(value.to_string())
      @token.Token::Comma => sb.write_char(',')
      @token.Token::RightParen => sb.write_char(')')
      @token.Token::LeftParen => sb.write_char('(')
      @token.Token::Whitespace => sb.write_char(' ')
      _ => ()
    }
  }
  let value_str = sb.to_string()
  // Parse the reconstructed string
  parse_transform_string(value_str)
}

///|
fn parse_pointer_events(
  tokens : ArrayView[@token.Token],
) -> @style.PointerEvents {
  let remaining = skip_whitespace(tokens)
  if remaining.length() == 0 {
    return @style.PointerEvents::Auto
  }
  match remaining[0] {
    @token.Token::Ident(s) =>
      if s == "none" {
        @style.PointerEvents::None
      } else {
        @style.PointerEvents::Auto
      }
    _ => @style.PointerEvents::Auto
  }
}

///|
/// Parse CSS transform from a string value
fn parse_transform_string(value : String) -> @style.Transform {
  let v = value.trim().to_lower()
  // Handle none
  if v == "none" || v.is_empty() {
    return @style.Transform::none()
  }
  let mut translate_x = @style.TranslateValue::Length(0.0)
  let mut translate_y = @style.TranslateValue::Length(0.0)
  let mut scale_x = 1.0
  let mut scale_y = 1.0
  let mut rotate_degrees = 0.0
  let mut skew_x_degrees = 0.0
  let mut skew_y_degrees = 0.0
  let mut has_matrix = false
  let mut matrix_a = 1.0
  let mut matrix_b = 0.0
  let mut matrix_c = 0.0
  let mut matrix_d = 1.0
  let mut matrix_e = 0.0
  let mut matrix_f = 0.0
  // Parse transform functions
  let chars = v.to_array()
  let len = chars.length()
  let mut i = 0
  while i < len {
    // Skip whitespace
    while i < len && (chars[i] == ' ' || chars[i] == '\t') {
      i = i + 1
    }
    if i >= len {
      break
    }
    // Find function name
    let func_start = i
    while i < len && chars[i] != '(' {
      i = i + 1
    }
    if i >= len {
      break
    }
    let func_name = transform_chars_to_string(chars[func_start:i])
    i = i + 1 // Skip '('
    // Find function arguments (handle nested parens)
    let args_start = i
    let mut paren_depth = 1
    while i < len && paren_depth > 0 {
      if chars[i] == '(' {
        paren_depth = paren_depth + 1
      } else if chars[i] == ')' {
        paren_depth = paren_depth - 1
      }
      i = i + 1
    }
    let args_str = transform_chars_to_string(chars[args_start:i - 1])
    // Parse transform function
    match func_name.trim() {
      "translate" => {
        // translate(x) or translate(x, y)
        let args = split_transform_args(args_str)
        if args.length() >= 1 {
          translate_x = parse_translate_value(args[0])
        }
        if args.length() >= 2 {
          translate_y = parse_translate_value(args[1])
        }
      }
      "translatex" => translate_x = parse_translate_value(args_str)
      "translatey" => translate_y = parse_translate_value(args_str)
      "translate3d" => {
        // translate3d(x, y, z) - we only use x and y
        let args = split_transform_args(args_str)
        if args.length() >= 1 {
          translate_x = parse_translate_value(args[0])
        }
        if args.length() >= 2 {
          translate_y = parse_translate_value(args[1])
        }
      }
      "scale" => {
        // scale(x) or scale(x, y)
        let args = split_transform_args(args_str)
        if args.length() >= 1 {
          let sx = parse_scale_value(args[0])
          scale_x = scale_x * sx
          scale_y = scale_y * sx
        }
        if args.length() >= 2 {
          scale_y = parse_scale_value(args[1])
        }
      }
      "scalex" => scale_x = scale_x * parse_scale_value(args_str)
      "scaley" => scale_y = scale_y * parse_scale_value(args_str)
      "scale3d" => {
        // scale3d(x, y, z) - we only use x and y
        let args = split_transform_args(args_str)
        if args.length() >= 1 {
          scale_x = scale_x * parse_scale_value(args[0])
        }
        if args.length() >= 2 {
          scale_y = scale_y * parse_scale_value(args[1])
        }
      }
      "rotate" =>
        rotate_degrees = rotate_degrees + parse_rotate_degrees(args_str)
      "skewx" => skew_x_degrees += parse_rotate_degrees(args_str)
      "skewy" => skew_y_degrees += parse_rotate_degrees(args_str)
      "skew" => {
        let args = split_transform_args(args_str)
        if args.length() >= 1 {
          skew_x_degrees += parse_rotate_degrees(args[0])
        }
        if args.length() >= 2 {
          skew_y_degrees += parse_rotate_degrees(args[1])
        }
      }
      "matrix" => {
        let args = split_transform_args(args_str)
        if args.length() >= 6 {
          has_matrix = true
          matrix_a = parse_scale_value(args[0])
          matrix_b = parse_scale_value(args[1])
          matrix_c = parse_scale_value(args[2])
          matrix_d = parse_scale_value(args[3])
          matrix_e = parse_scale_value(args[4])
          matrix_f = parse_scale_value(args[5])
        }
      }
      "matrix3d" => {
        let args = split_transform_args(args_str)
        if args.length() >= 16 {
          has_matrix = true
          matrix_a = parse_scale_value(args[0])
          matrix_b = parse_scale_value(args[1])
          matrix_c = parse_scale_value(args[4])
          matrix_d = parse_scale_value(args[5])
          matrix_e = parse_scale_value(args[12])
          matrix_f = parse_scale_value(args[13])
        }
      }
      _ => () // Ignore unsupported functions like matrix/perspective.
    }
  }
  {
    translate_x,
    translate_y,
    scale_x,
    scale_y,
    rotate_degrees,
    skew_x_degrees,
    skew_y_degrees,
    has_matrix,
    matrix_a,
    matrix_b,
    matrix_c,
    matrix_d,
    matrix_e,
    matrix_f,
  }
}

///|
fn parse_rotate_degrees(value : String) -> Double {
  let v = value.trim().to_string()
  if v.is_empty() {
    return 0.0
  }
  if v.has_suffix("deg") {
    let num_str = v.unsafe_substring(start=0, end=v.length() - 3)
    return @string.parse_double(num_str.trim()) catch { _ => 0.0 }
  }
  if v.has_suffix("turn") {
    let num_str = v.unsafe_substring(start=0, end=v.length() - 4)
    let turns = @string.parse_double(num_str.trim()) catch { _ => return 0.0 }
    return turns * 360.0
  }
  if v.has_suffix("rad") {
    let num_str = v.unsafe_substring(start=0, end=v.length() - 3)
    let radians = @string.parse_double(num_str.trim()) catch { _ => return 0.0 }
    return radians * 57.29577951308232
  }
  @string.parse_double(v) catch {
    _ => 0.0
  }
}

///|
/// Parse a scale value (number, no unit)
fn parse_scale_value(value : String) -> Double {
  let v = value.trim()
  if v.is_empty() {
    return 1.0
  }
  @string.parse_double(v) catch {
    _ => 1.0
  }
}

///|
/// Parse a single translate value (can be length or percentage)
fn parse_translate_value(value : String) -> @style.TranslateValue {
  let v = value.trim()
  if v.is_empty() {
    return @style.TranslateValue::Length(0.0)
  }
  // Check for percentage
  if v.has_suffix("%") {
    let num_str = v[:v.length() - 1].to_string()
    let n = @string.parse_double(num_str.trim()) catch {
      _ => return @style.TranslateValue::Length(0.0)
    }
    @style.TranslateValue::Percent(n / 100.0)
  } else if v.has_suffix("px") {
    let num_str = v[:v.length() - 2].to_string()
    let n = @string.parse_double(num_str.trim()) catch {
      _ => return @style.TranslateValue::Length(0.0)
    }
    @style.TranslateValue::Length(n)
  } else if v.has_suffix("em") || v.has_suffix("rem") {
    // em/rem - convert to pixels assuming 16px base
    let suffix_len = if v.has_suffix("rem") { 3 } else { 2 }
    let num_str = v[:v.length() - suffix_len].to_string()
    let n = @string.parse_double(num_str.trim()) catch {
      _ => return @style.TranslateValue::Length(0.0)
    }
    @style.TranslateValue::Length(n * 16.0)
  } else {
    // Try parsing as plain number (assumed px)
    let n = @string.parse_double(v) catch {
      _ => return @style.TranslateValue::Length(0.0)
    }
    @style.TranslateValue::Length(n)
  }
}

///|
/// Split string by comma (for transform arguments) - local version
fn split_transform_args(s : String) -> Array[String] {
  let result : Array[String] = []
  let current = StringBuilder::new()
  for c in s {
    if c == ',' {
      result.push(current.to_string())
      current.reset()
    } else {
      current.write_char(c)
    }
  }
  let last = current.to_string()
  if !last.is_empty() {
    result.push(last)
  }
  result
}

///|
/// Convert char array slice to string
fn transform_chars_to_string(chars : ArrayView[Char]) -> String {
  let sb = StringBuilder::new()
  for c in chars {
    sb.write_char(c)
  }
  sb.to_string()
}

///|
/// Extract property name and value tokens from a declaration
fn parse_declaration(
  tokens : ArrayView[@token.Token],
) -> (String, ArrayView[@token.Token])? {
  let tokens = skip_whitespace(tokens)
  if tokens.length() == 0 {
    return None
  }

  // Get property name
  let property_name = match tokens[0] {
    @token.Token::Ident(name) => name
    _ => return None
  }

  // Skip to colon
  let mut pos = 1
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::Whitespace => pos += 1
      @token.Token::Colon => {
        pos += 1
        break
      }
      _ => return None
    }
  }

  // Get value tokens (until semicolon or end)
  let value_start = pos
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::Semicolon | @token.Token::EOF => break
      _ => pos += 1
    }
  }
  let value_tokens = skip_whitespace(tokens[value_start:pos])
  Some((property_name, value_tokens))
}

///|
/// Apply a single property to a style
fn apply_property(
  style : @style.Style,
  property : String,
  value_tokens : ArrayView[@token.Token],
) -> @style.Style {
  // display
  if property == "display" {
    match parse_display(value_tokens) {
      Some(v) => return { ..style, display: v }
      None => ()
    }
  }
  // position
  if property == "position" {
    match parse_position(value_tokens) {
      Some(v) => return { ..style, position: v }
      None => ()
    }
  }
  // float
  if property == "float" {
    match parse_float(value_tokens, style.direction) {
      Some(v) => return { ..style, float: v }
      None => ()
    }
  }
  // clear
  if property == "clear" {
    match parse_clear(value_tokens, style.direction) {
      Some(v) => return { ..style, clear: v }
      None => ()
    }
  }
  // box-sizing
  if property == "box-sizing" {
    match parse_box_sizing(value_tokens) {
      Some(v) => return { ..style, box_sizing: v }
      None => ()
    }
  }
  // width
  if property == "width" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, width: v }
      None => ()
    }
  }
  // height
  if property == "height" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, height: v }
      None => ()
    }
  }
  // min-width
  if property == "min-width" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, min_width: v }
      None => ()
    }
  }
  // min-height
  if property == "min-height" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, min_height: v }
      None => ()
    }
  }
  // max-width
  if property == "max-width" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, max_width: v }
      None => ()
    }
  }
  // max-height
  if property == "max-height" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, max_height: v }
      None => ()
    }
  }
  // flex-direction
  if property == "flex-direction" {
    match parse_flex_direction(value_tokens) {
      Some(v) => return { ..style, flex_direction: v }
      None => ()
    }
  }
  // flex-wrap
  if property == "flex-wrap" {
    match parse_flex_wrap(value_tokens) {
      Some(v) => return { ..style, flex_wrap: v }
      None => ()
    }
  }
  // justify-content
  if property == "justify-content" {
    match parse_alignment_with_overflow(value_tokens) {
      Some((v, is_unsafe)) =>
        return {
          ..style,
          justify_content: v,
          justify_content_unsafe: is_unsafe,
        }
      None => ()
    }
  }
  // align-items
  if property == "align-items" {
    match parse_alignment(value_tokens) {
      Some(v) => return { ..style, align_items: v }
      None => ()
    }
  }
  // align-content
  if property == "align-content" {
    match parse_alignment_with_overflow(value_tokens) {
      Some((v, is_unsafe)) =>
        return { ..style, align_content: v, align_content_unsafe: is_unsafe }
      None => ()
    }
  }
  // place-content
  if property == "place-content" {
    match parse_place_content_with_overflow(value_tokens) {
      Some(
        (
          (align_content, align_content_unsafe),
          (justify_content, justify_content_unsafe),
        )
      ) =>
        return {
          ..style,
          align_content,
          align_content_unsafe,
          justify_content,
          justify_content_unsafe,
        }
      None => ()
    }
  }
  // align-self
  if property == "align-self" {
    match parse_self_alignment(value_tokens) {
      Some(v) => return { ..style, align_self: v }
      None => ()
    }
  }
  // justify-self
  if property == "justify-self" {
    match parse_self_alignment(value_tokens) {
      Some(v) => return { ..style, justify_self: v }
      None => ()
    }
  }
  // justify-items
  if property == "justify-items" {
    match parse_alignment(value_tokens) {
      Some(v) => return { ..style, justify_items: v }
      None => ()
    }
  }
  // flex-grow (must be non-negative)
  if property == "flex-grow" {
    match parse_number(value_tokens) {
      Some(v) if v >= 0.0 => return { ..style, flex_grow: v }
      _ => ()
    }
  }
  // flex-shrink (must be non-negative)
  if property == "flex-shrink" {
    match parse_number(value_tokens) {
      Some(v) if v >= 0.0 => return { ..style, flex_shrink: v }
      _ => ()
    }
  }
  // flex-basis
  if property == "flex-basis" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, flex_basis: v }
      None => ()
    }
  }
  // flex shorthand
  if property == "flex" {
    match parse_flex_shorthand(value_tokens) {
      Some((grow, shrink, basis)) =>
        return {
          ..style,
          flex_grow: grow,
          flex_shrink: shrink,
          flex_basis: basis,
        }
      None => ()
    }
  }
  // flex-flow shorthand
  if property == "flex-flow" {
    match parse_flex_flow_shorthand(value_tokens) {
      Some((direction, wrap)) =>
        return { ..style, flex_direction: direction, flex_wrap: wrap }
      None => ()
    }
  }
  // order
  if property == "order" {
    match parse_integer(value_tokens) {
      Some(v) => return { ..style, order: v }
      None => ()
    }
  }
  // margin (supports 1, 2, 3, or 4 values)
  if property == "margin" {
    let dims = parse_multi_dimensions(value_tokens, 4)
    match dims.length() {
      1 => {
        // All sides
        let v = dims[0]
        return { ..style, margin: { top: v, right: v, bottom: v, left: v } }
      }
      2 => {
        // top/bottom, left/right
        let tb = dims[0]
        let lr = dims[1]
        return { ..style, margin: { top: tb, right: lr, bottom: tb, left: lr } }
      }
      3 => {
        // top, left/right, bottom
        let t = dims[0]
        let lr = dims[1]
        let b = dims[2]
        return { ..style, margin: { top: t, right: lr, bottom: b, left: lr } }
      }
      4 =>
        // top, right, bottom, left
        return {
          ..style,
          margin: {
            top: dims[0],
            right: dims[1],
            bottom: dims[2],
            left: dims[3],
          },
        }
      _ => ()
    }
  }
  // margin-left
  if property == "margin-left" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, margin: { ..style.margin, left: v } }
      None => ()
    }
  }
  // margin-right
  if property == "margin-right" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, margin: { ..style.margin, right: v } }
      None => ()
    }
  }
  // margin-top
  if property == "margin-top" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, margin: { ..style.margin, top: v } }
      None => ()
    }
  }
  // margin-bottom
  if property == "margin-bottom" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, margin: { ..style.margin, bottom: v } }
      None => ()
    }
  }
  // padding (supports 1, 2, 3, or 4 values)
  if property == "padding" {
    let dims = parse_multi_dimensions(value_tokens, 4)
    match dims.length() {
      1 => {
        let v = dims[0]
        return { ..style, padding: { top: v, right: v, bottom: v, left: v } }
      }
      2 => {
        let tb = dims[0]
        let lr = dims[1]
        return {
          ..style,
          padding: { top: tb, right: lr, bottom: tb, left: lr },
        }
      }
      3 => {
        let t = dims[0]
        let lr = dims[1]
        let b = dims[2]
        return { ..style, padding: { top: t, right: lr, bottom: b, left: lr } }
      }
      4 =>
        return {
          ..style,
          padding: {
            top: dims[0],
            right: dims[1],
            bottom: dims[2],
            left: dims[3],
          },
        }
      _ => ()
    }
  }
  // padding-left
  if property == "padding-left" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, padding: { ..style.padding, left: v } }
      None => ()
    }
  }
  // padding-right
  if property == "padding-right" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, padding: { ..style.padding, right: v } }
      None => ()
    }
  }
  // padding-top
  if property == "padding-top" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, padding: { ..style.padding, top: v } }
      None => ()
    }
  }
  // padding-bottom
  if property == "padding-bottom" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, padding: { ..style.padding, bottom: v } }
      None => ()
    }
  }
  // padding-block (logical property: sets top and bottom for horizontal writing mode)
  if property == "padding-block" {
    match parse_dimension(value_tokens) {
      Some(v) =>
        return { ..style, padding: { ..style.padding, top: v, bottom: v } }
      None => ()
    }
  }
  // padding-inline (logical property: sets left and right for horizontal writing mode)
  if property == "padding-inline" {
    match parse_dimension(value_tokens) {
      Some(v) =>
        return { ..style, padding: { ..style.padding, left: v, right: v } }
      None => ()
    }
  }
  // gap
  if property == "gap" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, row_gap: v, column_gap: v }
      None => ()
    }
  }
  // row-gap
  if property == "row-gap" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, row_gap: v }
      None => ()
    }
  }
  // column-gap
  if property == "column-gap" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, column_gap: v }
      None => ()
    }
  }
  // overflow
  if property == "overflow" {
    match parse_overflow(value_tokens) {
      Some(v) => return { ..style, overflow_x: v, overflow_y: v }
      None => ()
    }
  }
  // overflow-x
  if property == "overflow-x" {
    match parse_overflow(value_tokens) {
      Some(v) => return { ..style, overflow_x: v }
      None => ()
    }
  }
  // overflow-y
  if property == "overflow-y" {
    match parse_overflow(value_tokens) {
      Some(v) => return { ..style, overflow_y: v }
      None => ()
    }
  }
  // clip (legacy, for accessibility)
  if property == "clip" {
    return { ..style, clip: parse_clip(value_tokens) }
  }
  // clip-path (paint/hit-test subset)
  if property == "clip-path" {
    return { ..style, clip_path: parse_clip_path(value_tokens) }
  }
  // border (single value)
  if property == "border-width" {
    match parse_dimension(value_tokens) {
      Some(v) =>
        return { ..style, border: { left: v, right: v, top: v, bottom: v } }
      None => ()
    }
  }
  // border-left-width
  if property == "border-left-width" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, border: { ..style.border, left: v } }
      None => ()
    }
  }
  // border-right-width
  if property == "border-right-width" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, border: { ..style.border, right: v } }
      None => ()
    }
  }
  // border-top-width
  if property == "border-top-width" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, border: { ..style.border, top: v } }
      None => ()
    }
  }
  // border-bottom-width
  if property == "border-bottom-width" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, border: { ..style.border, bottom: v } }
      None => ()
    }
  }
  // border-block (logical property: sets top and bottom border width)
  // Shorthand can be: border-block: 10px | border-block: 10px solid | border-block: 10px solid green
  if property == "border-block" {
    // Parse first token as dimension (width)
    match parse_dimension(value_tokens) {
      Some(v) =>
        return { ..style, border: { ..style.border, top: v, bottom: v } }
      None => ()
    }
  }
  // border-block-width
  if property == "border-block-width" {
    match parse_dimension(value_tokens) {
      Some(v) =>
        return { ..style, border: { ..style.border, top: v, bottom: v } }
      None => ()
    }
  }
  // border-inline (logical property: sets left and right border width)
  if property == "border-inline" {
    match parse_dimension(value_tokens) {
      Some(v) =>
        return { ..style, border: { ..style.border, left: v, right: v } }
      None => ()
    }
  }
  // border-inline-width
  if property == "border-inline-width" {
    match parse_dimension(value_tokens) {
      Some(v) =>
        return { ..style, border: { ..style.border, left: v, right: v } }
      None => ()
    }
  }
  // inset (supports 1, 2, 3, or 4 values like margin/padding)
  if property == "inset" {
    let dims = parse_multi_dimensions(value_tokens, 4)
    match dims.length() {
      1 => {
        let v = dims[0]
        return { ..style, inset: { top: v, right: v, bottom: v, left: v } }
      }
      2 => {
        let tb = dims[0]
        let lr = dims[1]
        return { ..style, inset: { top: tb, right: lr, bottom: tb, left: lr } }
      }
      3 => {
        let t = dims[0]
        let lr = dims[1]
        let b = dims[2]
        return { ..style, inset: { top: t, right: lr, bottom: b, left: lr } }
      }
      4 =>
        return {
          ..style,
          inset: {
            top: dims[0],
            right: dims[1],
            bottom: dims[2],
            left: dims[3],
          },
        }
      _ => ()
    }
  }
  // top
  if property == "top" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, inset: { ..style.inset, top: v } }
      None => ()
    }
  }
  // right
  if property == "right" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, inset: { ..style.inset, right: v } }
      None => ()
    }
  }
  // bottom
  if property == "bottom" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, inset: { ..style.inset, bottom: v } }
      None => ()
    }
  }
  // left
  if property == "left" {
    match parse_dimension(value_tokens) {
      Some(v) => return { ..style, inset: { ..style.inset, left: v } }
      None => ()
    }
  }
  // aspect-ratio
  if property == "aspect-ratio" {
    match parse_aspect_ratio(value_tokens) {
      Some(v) => return { ..style, aspect_ratio: Some(v) }
      None => ()
    }
  }
  // grid-auto-flow
  if property == "grid-auto-flow" {
    match parse_grid_auto_flow(value_tokens) {
      Some(v) => return { ..style, grid_auto_flow: v }
      None => ()
    }
  }
  // grid-template-columns
  if property == "grid-template-columns" {
    let tracks = parse_grid_template_tracks(value_tokens)
    return { ..style, grid_template_columns: tracks }
  }
  // grid-template-rows
  if property == "grid-template-rows" {
    let tracks = parse_grid_template_tracks(value_tokens)
    return { ..style, grid_template_rows: tracks }
  }
  // grid-template shorthand:  / 
  if property == "grid-template" {
    match parse_grid_template_shorthand(value_tokens) {
      Some((rows, cols)) =>
        return {
          ..style,
          grid_template_rows: rows,
          grid_template_columns: cols,
        }
      None => ()
    }
  }
  // grid shorthand (partial): support template syntax ` / `
  if property == "grid" {
    match parse_grid_template_shorthand(value_tokens) {
      Some((rows, cols)) =>
        return {
          ..style,
          grid_template_rows: rows,
          grid_template_columns: cols,
        }
      None => ()
    }
  }
  // contain
  if property == "contain" {
    let contain = parse_contain(value_tokens)
    return { ..style, contain, }
  }
  // container-type
  if property == "container-type" {
    match parse_container_type(value_tokens) {
      Some(implied) =>
        if implied.has_containment() {
          return { ..style, contain: merge_contain(style.contain, implied) }
        } else {
          return style
        }
      None => ()
    }
  }
  // contain-intrinsic-inline-size
  if property == "contain-intrinsic-inline-size" {
    let contain_intrinsic_inline_size = parse_contain_intrinsic_axis(
      value_tokens,
    )
    return { ..style, contain_intrinsic_inline_size, }
  }
  // contain-intrinsic-block-size
  if property == "contain-intrinsic-block-size" {
    let contain_intrinsic_block_size = parse_contain_intrinsic_axis(
      value_tokens,
    )
    return { ..style, contain_intrinsic_block_size, }
  }
  // contain-intrinsic-size
  if property == "contain-intrinsic-size" {
    match parse_contain_intrinsic_size(value_tokens) {
      Some((contain_intrinsic_inline_size, contain_intrinsic_block_size)) =>
        return {
          ..style,
          contain_intrinsic_inline_size,
          contain_intrinsic_block_size,
        }
      None => ()
    }
  }
  // transform
  if property == "transform" {
    let transform = parse_transform(value_tokens)
    return { ..style, transform, }
  }
  // pointer-events
  if property == "pointer-events" {
    let pointer_events = parse_pointer_events(value_tokens)
    return { ..style, pointer_events, }
  }
  // Unknown property - ignore
  style
}

///|
fn seed_direction(tokens : Array[@token.Token]) -> @style.Direction {
  let mut direction = @style.Direction::Ltr
  let mut pos = 0
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::Whitespace | @token.Token::Semicolon => {
        pos += 1
        continue
      }
      @token.Token::EOF => break
      _ => ()
    }

    match parse_declaration(tokens[pos:]) {
      Some((property, value_tokens)) => {
        if property == "direction" {
          match value_tokens.length() {
            0 => ()
            _ =>
              match value_tokens[0] {
                @token.Token::Ident(s) =>
                  if s == "rtl" {
                    direction = @style.Direction::Rtl
                  } else if s == "ltr" {
                    direction = @style.Direction::Ltr
                  }
                _ => ()
              }
          }
        }
        while pos < tokens.length() {
          match tokens[pos] {
            @token.Token::Semicolon => {
              pos += 1
              break
            }
            @token.Token::EOF => break
            _ => pos += 1
          }
        }
      }
      None => pos += 1
    }
  }
  direction
}

///|
/// Parse inline CSS style string and return a Style object
/// e.g., "width: 100px; height: 50px; display: flex"
pub fn parse_inline_style(css : String) -> @style.Style {
  let tokens = @token.tokenize(css)
  let mut style = {
    ..@style.Style::default(),
    direction: seed_direction(tokens),
  }
  let mut pos = 0
  while pos < tokens.length() {
    // Skip leading whitespace and semicolons
    match tokens[pos] {
      @token.Token::Whitespace | @token.Token::Semicolon => {
        pos += 1
        continue
      }
      @token.Token::EOF => break
      _ => ()
    }

    // Try to parse a declaration
    match parse_declaration(tokens[pos:]) {
      Some((property, value_tokens)) => {
        style = apply_property(style, property, value_tokens)
        // Skip to next semicolon or end
        while pos < tokens.length() {
          match tokens[pos] {
            @token.Token::Semicolon => {
              pos += 1
              break
            }
            @token.Token::EOF => break
            _ => pos += 1
          }
        }
      }
      None => pos += 1
    }
  }
  // Convert content-box dimensions to border-box dimensions
  // The layout engine always works with outer (border-box) dimensions
  // For content-box (CSS default), width/height specify content area only,
  // so we need to add padding and border to get the outer dimensions
  adjust_for_box_sizing(style)
}

///|
/// Adjust dimensions for box-sizing: content-box
/// When box-sizing is content-box (default), specified width/height are content dimensions.
/// The layout engine expects outer dimensions (border-box), so we adjust here.
fn adjust_for_box_sizing(style : @style.Style) -> @style.Style {
  match style.box_sizing {
    @types.BorderBox => style // No adjustment needed
    @types.ContentBox => {
      // Calculate padding and border sums
      let padding_h = resolve_dimension_to_px(style.padding.left) +
        resolve_dimension_to_px(style.padding.right)
      let padding_v = resolve_dimension_to_px(style.padding.top) +
        resolve_dimension_to_px(style.padding.bottom)
      let border_h = resolve_dimension_to_px(style.border.left) +
        resolve_dimension_to_px(style.border.right)
      let border_v = resolve_dimension_to_px(style.border.top) +
        resolve_dimension_to_px(style.border.bottom)

      // Adjust width and height to include padding+border
      let adjusted_width = match style.width {
        @types.Dimension::Length(w) =>
          @types.Dimension::Length(w + padding_h + border_h)
        other => other
      }
      let adjusted_height = match style.height {
        @types.Dimension::Length(h) =>
          @types.Dimension::Length(h + padding_v + border_v)
        other => other
      }
      // Adjust min/max constraints too
      let adjusted_min_width = match style.min_width {
        @types.Dimension::Length(w) =>
          @types.Dimension::Length(w + padding_h + border_h)
        other => other
      }
      let adjusted_min_height = match style.min_height {
        @types.Dimension::Length(h) =>
          @types.Dimension::Length(h + padding_v + border_v)
        other => other
      }
      let adjusted_max_width = match style.max_width {
        @types.Dimension::Length(w) =>
          @types.Dimension::Length(w + padding_h + border_h)
        other => other
      }
      let adjusted_max_height = match style.max_height {
        @types.Dimension::Length(h) =>
          @types.Dimension::Length(h + padding_v + border_v)
        other => other
      }
      {
        ..style,
        width: adjusted_width,
        height: adjusted_height,
        min_width: adjusted_min_width,
        min_height: adjusted_min_height,
        max_width: adjusted_max_width,
        max_height: adjusted_max_height,
        // Mark as adjusted (now using border-box semantics internally)
        box_sizing: @types.BorderBox,
      }
    }
  }
}

///|
/// Helper to resolve a dimension to pixels (for padding/border calculation)
fn resolve_dimension_to_px(dim : @types.Dimension) -> Double {
  match dim {
    @types.Length(v) => v
    @types.Percent(_) => 0.0 // Percentages are resolved later
    @types.Auto => 0.0
    @types.MinContent => 0.0 // Intrinsic sizing resolved during layout
    @types.MaxContent => 0.0
    @types.FitContent(_) => 0.0
  }
}