func cssEscapeString(s string) string {
	var b bytes.Buffer
	b.Grow(len(s))
	for _, c := range s {
		switch {
		case c == '\u0000':
			b.WriteString("\uFFFD")
		case c == '<',  
			c == '"', c == '\\',  
			c <= '\u001F', c == '\u007F',  
			c >= '\u0080' && c <= '\u009F',  
			c == '\u2028', c == '\u2029':    
			fmt.Fprintf(&b, "\\%06X", c)
		default:
			b.WriteRune(c)
		}
	}
	return b.String()
}
cssEscapeString escapes s so that it is safe to put between "" to form a CSS <string-token>.
See syntax at https://www.w3.org/TR/css-syntax-3/#string-token-diagram.

On top of the escape sequences required in <string-token>, this function also escapes
control runes to minimize the risk of these runes triggering browser-specific bugs.
package safehtml
import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
)
type Style struct {
	str string
}
func StyleFromConstant(style stringConstant) Style {
	if strings.ContainsAny(string(style), "<>") {
		panic(fmt.Sprintf("style string %q contains angle brackets", style))
	}
	if !strings.HasSuffix(string(style), ";") {
		panic(fmt.Sprintf("style string %q must end with ';'", style))
	}
	if !strings.Contains(string(style), ":") {
		panic(fmt.Sprintf("style string %q must contain at least one ':' to specify a property-value pair", style))
	}
	return Style{string(style)}
}
func (s Style) String() string {
	return s.str
}
type StyleProperties struct {
	BackgroundImageURLs []string
	FontFamily []string
	Display string
	BackgroundColor    string
	BackgroundPosition string
	BackgroundRepeat   string
	BackgroundSize     string
	Color              string
	Height             string
	Width              string
	Left               string
	Right              string
	Top                string
	Bottom             string
	FontWeight         string
	Padding            string
	ZIndex string
}
var identifierPattern = regexp.MustCompile(`^[a-zA-Z][-a-zA-Z]+$`)
func StyleFromProperties(properties StyleProperties) Style {
	var buf bytes.Buffer
	if len(properties.BackgroundImageURLs) > 0 {
		buf.WriteString("background-image:")
		for i, url := range properties.BackgroundImageURLs {
			if i > 0 {
				buf.WriteString(", ")
			}
			fmt.Fprintf(&buf, "url(\"%s\")", cssEscapeString(URLSanitized(url).String()))
		}
		buf.WriteString(";")
	}
	if len(properties.FontFamily) > 0 {
		buf.WriteString("font-family:")
		for i, name := range properties.FontFamily {
			if i > 0 {
				buf.WriteString(", ")
			}
			if identifierPattern.MatchString(name) {
				buf.WriteString(name)
				continue
			}
			unescaped := name
			if len(name) >= 3 && strings.HasPrefix(name, `"`) && strings.HasSuffix(name, `"`) {
				unescaped = name[1 : len(name)-1]
			}
			fmt.Fprintf(&buf, `"%s"`, cssEscapeString(unescaped))
		}
		buf.WriteByte(';')
	}
	if properties.Display != "" {
		fmt.Fprintf(&buf, "display:%s;", filter(properties.Display, safeEnumPropertyValuePattern))
	}
	if properties.BackgroundColor != "" {
		fmt.Fprintf(&buf, "background-color:%s;", filter(properties.BackgroundColor, safeRegularPropertyValuePattern))
	}
	if properties.BackgroundPosition != "" {
		fmt.Fprintf(&buf, "background-position:%s;", filter(properties.BackgroundPosition, safeRegularPropertyValuePattern))
	}
	if properties.BackgroundRepeat != "" {
		fmt.Fprintf(&buf, "background-repeat:%s;", filter(properties.BackgroundRepeat, safeRegularPropertyValuePattern))
	}
	if properties.BackgroundSize != "" {
		fmt.Fprintf(&buf, "background-size:%s;", filter(properties.BackgroundSize, safeRegularPropertyValuePattern))
	}
	if properties.Color != "" {
		fmt.Fprintf(&buf, "color:%s;", filter(properties.Color, safeRegularPropertyValuePattern))
	}
	if properties.Height != "" {
		fmt.Fprintf(&buf, "height:%s;", filter(properties.Height, safeRegularPropertyValuePattern))
	}
	if properties.Width != "" {
		fmt.Fprintf(&buf, "width:%s;", filter(properties.Width, safeRegularPropertyValuePattern))
	}
	if properties.Left != "" {
		fmt.Fprintf(&buf, "left:%s;", filter(properties.Left, safeRegularPropertyValuePattern))
	}
	if properties.Right != "" {
		fmt.Fprintf(&buf, "right:%s;", filter(properties.Right, safeRegularPropertyValuePattern))
	}
	if properties.Top != "" {
		fmt.Fprintf(&buf, "top:%s;", filter(properties.Top, safeRegularPropertyValuePattern))
	}
	if properties.Bottom != "" {
		fmt.Fprintf(&buf, "bottom:%s;", filter(properties.Bottom, safeRegularPropertyValuePattern))
	}
	if properties.FontWeight != "" {
		fmt.Fprintf(&buf, "font-weight:%s;", filter(properties.FontWeight, safeRegularPropertyValuePattern))
	}
	if properties.Padding != "" {
		fmt.Fprintf(&buf, "padding:%s;", filter(properties.Padding, safeRegularPropertyValuePattern))
	}
	if properties.ZIndex != "" {
		fmt.Fprintf(&buf, "z-index:%s;", filter(properties.ZIndex, safeRegularPropertyValuePattern))
	}
	return Style{buf.String()}
}
const InnocuousPropertyValue = "zGoSafezInvalidPropertyValue"
var safeRegularPropertyValuePattern = regexp.MustCompile(`^(?:[*/]?(?:[0-9a-zA-Z+-.!#%_ \t]|$))*$`)
var safeEnumPropertyValuePattern = regexp.MustCompile(`^[a-zA-Z-]*$`)
func filter(value string, pattern *regexp.Regexp) string {
	if !pattern.MatchString(value) {
		return InnocuousPropertyValue
	}
	return value
}