///|
/// Whether `css[i..]` begins with the at-rule `@layer` followed by a boundary.
fn at_layer_keyword_at(css : String, i : Int) -> Bool {
  let kw = "@layer"
  if i + kw.length() > css.length() {
    return false
  }
  let mut k = 0
  while k < kw.length() {
    let c = css[i + k].to_int().unsafe_to_char()
    let lc = if c >= 'A' && c <= 'Z' {
      (c.to_int() + 32).unsafe_to_char()
    } else {
      c
    }
    if lc != kw[k].to_int().unsafe_to_char() {
      return false
    }
    k += 1
  }
  if i + kw.length() >= css.length() {
    return true
  }
  let nc = css[i + kw.length()].to_int().unsafe_to_char()
  nc == ' ' || nc == '\t' || nc == '\n' || nc == '\r' || nc == '{' || nc == ';'
}

///|
/// Flatten CSS cascade layers so their rules are not dropped before parsing.
/// The current parser does not understand `@layer`, so `@layer name { rules }`
/// is rewritten to just `rules` (layer precedence is approximated by source
/// order), and bare `@layer a, b;` declarations are removed. Modern CSS
/// frameworks (Primer, Tailwind, Bootstrap) place their base/reset rules — body
/// margin, `* { box-sizing: border-box }`, normalize — inside `@layer`, so
/// dropping those badly distorts layout.
pub fn flatten_css_cascade_layers(css : String) -> String {
  // Fast path: avoid the rewrite when there is no @layer at all.
  if !css.contains("@layer") {
    return css
  }
  let out = StringBuilder::new()
  let len = css.length()
  let mut i = 0
  let mut depth = 0
  let layer_depths : Array[Int] = []
  while i < len {
    let c = css[i].to_int().unsafe_to_char()
    // Pass comments and strings through untouched so braces / "@layer" inside
    // them cannot corrupt brace counting.
    if c == '/' && i + 1 < len && css[i + 1].to_int().unsafe_to_char() == '*' {
      out.write_char(c)
      out.write_char('*')
      i += 2
      while i < len {
        let d = css[i].to_int().unsafe_to_char()
        out.write_char(d)
        i += 1
        if d == '*' && i < len && css[i].to_int().unsafe_to_char() == '/' {
          out.write_char('/')
          i += 1
          break
        }
      }
      continue
    }
    if c == '"' || c == '\'' {
      let quote = c
      out.write_char(c)
      i += 1
      while i < len {
        let d = css[i].to_int().unsafe_to_char()
        out.write_char(d)
        i += 1
        if d == '\\' && i < len {
          out.write_char(css[i].to_int().unsafe_to_char())
          i += 1
        } else if d == quote {
          break
        }
      }
      continue
    }
    if c == '@' && at_layer_keyword_at(css, i) {
      // Find the next '{' or ';'.
      let mut j = i + 6
      while j < len {
        let nc = css[j].to_int().unsafe_to_char()
        if nc == '{' || nc == ';' {
          break
        }
        j += 1
      }
      if j < len && css[j].to_int().unsafe_to_char() == '{' {
        // `@layer name { ... }`: drop the prefix; the matching close brace at
        // this depth is dropped when reached.
        depth += 1
        layer_depths.push(depth)
        i = j + 1
      } else {
        // `@layer name;` or unterminated: drop the whole statement.
        i = if j < len { j + 1 } else { len }
      }
      continue
    }
    if c == '{' {
      depth += 1
      out.write_char(c)
    } else if c == '}' {
      if layer_depths.length() > 0 &&
        layer_depths[layer_depths.length() - 1] == depth {
        layer_depths.pop() |> ignore
        depth -= 1
        // drop this layer's closing brace
      } else {
        depth -= 1
        out.write_char(c)
      }
    } else {
      out.write_char(c)
    }
    i += 1
  }
  out.to_string()
}