diff --git a/Data.SeaHavenIndustries/Migrations/20260715180000_WoFlagColor.cs b/Data.SeaHavenIndustries/Migrations/20260715180000_WoFlagColor.cs
new file mode 100644
index 0000000..e48600e
--- /dev/null
+++ b/Data.SeaHavenIndustries/Migrations/20260715180000_WoFlagColor.cs
@@ -0,0 +1,33 @@
+using Data.SeaHavenIndustries;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Data.SeaHavenIndustries.Migrations
+{
+ ///
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20260715180000_WoFlagColor")]
+ public partial class WoFlagColor : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "FlagColor",
+ table: "workOrders",
+ type: "nvarchar(7)",
+ maxLength: 7,
+ nullable: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "FlagColor",
+ table: "workOrders");
+ }
+ }
+}
diff --git a/SeaHaven.Services/Helpers/WorkOrderFlagColors.cs b/SeaHaven.Services/Helpers/WorkOrderFlagColors.cs
new file mode 100644
index 0000000..6892bd8
--- /dev/null
+++ b/SeaHaven.Services/Helpers/WorkOrderFlagColors.cs
@@ -0,0 +1,50 @@
+namespace SeaHaven.Services.Helpers
+{
+ ///
+ /// Fixed board flag palette (must match FE FLAG_COLORS). Values stored as canonical #RRGGBB.
+ ///
+ public static class WorkOrderFlagColors
+ {
+ public static readonly HashSet Allowed = new(StringComparer.OrdinalIgnoreCase)
+ {
+ "#EF4444",
+ "#F97316",
+ "#F59E0B",
+ "#EAB308",
+ "#22C55E",
+ "#14B8A6",
+ "#3B82F6",
+ "#8B5CF6"
+ };
+
+ ///
+ /// Normalizes to # + 6 uppercase hex digits, or null if empty.
+ /// Returns false if the value is non-empty but not a valid hex color.
+ ///
+ public static bool TryNormalize(string? value, out string? canonical)
+ {
+ canonical = null;
+ if (string.IsNullOrWhiteSpace(value))
+ return true;
+
+ var trimmed = value.Trim();
+ if (trimmed.Length == 6 && trimmed[0] != '#')
+ trimmed = "#" + trimmed;
+
+ if (trimmed.Length != 7 || trimmed[0] != '#')
+ return false;
+
+ for (var i = 1; i < 7; i++)
+ {
+ if (!Char.IsAsciiHexDigit(trimmed[i]))
+ return false;
+ }
+
+ canonical = "#" + trimmed[1..].ToUpperInvariant();
+ return true;
+ }
+
+ public static bool IsAllowed(string? canonical)
+ => canonical != null && Allowed.Contains(canonical);
+ }
+}