package domain import ( "errors" "regexp" "time" "github.com/google/uuid" ) // Branding is one event's visual customisation. All fields are optional — // a brand-new event renders with the GuestGuard defaults until the host // fills any of these in. Tier 2 Block D. type Branding struct { EventID uuid.UUID `json:"event_id"` PrimaryColor *string `json:"primary_color,omitempty"` AccentColor *string `json:"accent_color,omitempty"` LogoURL *string `json:"logo_url,omitempty"` CoverImageURL *string `json:"cover_image_url,omitempty"` FontFamily *string `json:"font_family,omitempty"` GreetingMessage *string `json:"greeting_message,omitempty"` UpdatedAt time.Time `json:"updated_at"` } // AllowedFonts is the curated picker list the host can choose from. Locking // it down keeps render times sane (no hot-loading arbitrary @font-face // files) and avoids smuggling untrusted CSS into our emails. var AllowedFonts = []string{ "Inter", "Playfair Display", "Cormorant Garamond", "Lora", "DM Sans", "Manrope", } // IsAllowedFont reports whether `f` is in the curated set. Empty string is // fine — the RSVP page falls back to its default. func IsAllowedFont(f string) bool { if f == "" { return true } for _, a := range AllowedFonts { if f == a { return true } } return false } // hexColorRe matches #abc, #abcd, #aabbcc, #aabbccdd (alpha optional). // Lowercase + uppercase OK. Length checked because Go regexp + Postgres // don't agree on Unicode digits and we want plain ASCII here. var hexColorRe = regexp.MustCompile(`^#([0-9A-Fa-f]{3,8})$`) // IsValidHexColor accepts the three common hex shorthand forms. Empty is OK // (clears the column). func IsValidHexColor(c string) bool { if c == "" { return true } if !hexColorRe.MatchString(c) { return false } n := len(c) - 1 return n == 3 || n == 4 || n == 6 || n == 8 } var ( ErrBrandingNotFound = errors.New("branding not found") ErrInvalidColor = errors.New("color must be a hex value (e.g. #22c55e)") ErrUnknownFont = errors.New("font is not in the allowlist") )