diff --git a/src/JobsJobsJobs/Client/Pages/Profiles/Search.razor.cs b/src/JobsJobsJobs/Client/Pages/Profiles/Search.razor.cs
index bc725c7..e3b05b3 100644
--- a/src/JobsJobsJobs/Client/Pages/Profiles/Search.razor.cs
+++ b/src/JobsJobsJobs/Client/Pages/Profiles/Search.razor.cs
@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.WebUtilities;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Net;
 using System.Threading.Tasks;
 
 namespace JobsJobsJobs.Client.Pages.Profiles
@@ -56,10 +55,10 @@ namespace JobsJobsJobs.Client.Pages.Profiles
                 {
                     if (query.TryGetValue(part, out var partValue)) func(partValue);
                 }
-                setPart("ContinentId", x => Criteria.ContinentId = x);
-                setPart("Skill", x => Criteria.Skill = x);
-                setPart("BioExperience", x => Criteria.BioExperience = x);
-                setPart("RemoteWork", x => Criteria.RemoteWork = x);
+                setPart(nameof(Criteria.ContinentId), x => Criteria.ContinentId = x);
+                setPart(nameof(Criteria.Skill), x => Criteria.Skill = x);
+                setPart(nameof(Criteria.BioExperience), x => Criteria.BioExperience = x);
+                setPart(nameof(Criteria.RemoteWork), x => Criteria.RemoteWork = x);
 
                 await RetrieveProfiles();
             }
@@ -100,6 +99,11 @@ namespace JobsJobsJobs.Client.Pages.Profiles
             Searching = false;
         }
 
+        /// 
+        /// Return a CSS class if the user is actively seeking work
+        /// 
+        /// The result in question
+        /// A string with the appropriate CSS class, if actively seeking work
         private static string? IsSeeking(ProfileSearchResult profile) =>
             profile.SeekingEmployment ? "font-weight-bold" : null;
 
@@ -124,10 +128,10 @@ namespace JobsJobsJobs.Client.Pages.Profiles
                 if (!string.IsNullOrEmpty(func(Criteria))) dict.Add(name, func(Criteria));
             }
 
-            part("ContinentId", it => it.ContinentId);
-            part("Skill", it => it.Skill);
-            part("BioExperience", it => it.BioExperience);
-            part("RemoteWork", it => it.RemoteWork);
+            part(nameof(Criteria.ContinentId), it => it.ContinentId);
+            part(nameof(Criteria.Skill), it => it.Skill);
+            part(nameof(Criteria.BioExperience), it => it.BioExperience);
+            part(nameof(Criteria.RemoteWork), it => it.RemoteWork);
 
             return dict;
         }
diff --git a/src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor b/src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor
new file mode 100644
index 0000000..06166b1
--- /dev/null
+++ b/src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor
@@ -0,0 +1,56 @@
+@page "/profile/seeking"
+@inject HttpClient http
+@inject NavigationManager nav
+@inject AppState state
+
+
+
People Seeking Work
+
+
+  @if (Searching)
+  {
+    Searching profiles...
+  }
+  else
+  {
+    if (!Searched)
+    {
+      Enter one or more criteria to filter results, or just click “Search” to list all profiles.
+    }
+    
+      
+    
+    
+    @if (SearchResults.Any())
+    {
+      
+        
+          
+            | Profile | 
+            Continent | 
+            Region | 
+            Remote? | 
+            Skills | 
+          
+        
+        
+          @foreach (var profile in SearchResults)
+          {
+            
+              | View | 
+              @profile.DisplayName | 
+              @YesOrNo(profile.SeekingEmployment) | 
+              @YesOrNo(profile.RemoteWork) | 
+              @YesOrNo(profile.FullTime) | 
+               | 
+            
+          }
+        
+      
+    }
+    else if (Searched)
+    {
+      No results found for the specified criteria
+    }
+  }
+
diff --git a/src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor.cs b/src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor.cs
new file mode 100644
index 0000000..bea3afb
--- /dev/null
+++ b/src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor.cs
@@ -0,0 +1,134 @@
+using JobsJobsJobs.Shared;
+using JobsJobsJobs.Shared.Api;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.WebUtilities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace JobsJobsJobs.Client.Pages.Profiles
+{
+    public partial class Seeking : ComponentBase
+    {
+        /// 
+        /// Whether a search has been performed
+        /// 
+        private bool Searched { get; set; } = false;
+
+        /// 
+        /// Indicates whether a request for matching profiles is in progress
+        /// 
+        private bool Searching { get; set; } = false;
+
+        /// 
+        /// The search criteria
+        /// 
+        private PublicSearch Criteria { get; set; } = new PublicSearch();
+
+        /// 
+        /// Error messages encountered while searching for profiles
+        /// 
+        private IList ErrorMessages { get; } = new List();
+
+        /// 
+        /// All continents
+        /// 
+        private IEnumerable Continents { get; set; } = Enumerable.Empty();
+
+        /// 
+        /// The search results
+        /// 
+        private IEnumerable SearchResults { get; set; } = Enumerable.Empty();
+
+        protected override async Task OnInitializedAsync()
+        {
+            Continents = await state.GetContinents(http);
+
+            // Determine if we have searched before
+            var query = QueryHelpers.ParseQuery(nav.ToAbsoluteUri(nav.Uri).Query);
+
+            if (query.TryGetValue("Searched", out var searched))
+            {
+                Searched = Convert.ToBoolean(searched);
+                void setPart(string part, Action func)
+                {
+                    if (query.TryGetValue(part, out var partValue)) func(partValue);
+                }
+                setPart(nameof(Criteria.ContinentId), x => Criteria.ContinentId = x);
+                setPart(nameof(Criteria.Region), x => Criteria.Region = x);
+                setPart(nameof(Criteria.Skill), x => Criteria.Skill = x);
+                setPart(nameof(Criteria.RemoteWork), x => Criteria.RemoteWork = x);
+
+                await RetrieveProfiles();
+            }
+        }
+
+        /// 
+        /// Do a search
+        /// 
+        /// This navigates with the parameters in the URL; this should trigger a search
+        private async Task DoSearch()
+        {
+            var query = SearchQuery();
+            query.Add("Searched", "True");
+            nav.NavigateTo(QueryHelpers.AddQueryString("/profile/search", query));
+            await RetrieveProfiles();
+        }
+
+        /// 
+        /// Retreive profiles matching the current search criteria
+        /// 
+        private async Task RetrieveProfiles()
+        {
+            Searching = true;
+
+            var searchResult = await ServerApi.RetrieveMany(http,
+                QueryHelpers.AddQueryString("profile/search", SearchQuery()));
+
+            if (searchResult.IsOk)
+            {
+                SearchResults = searchResult.Ok;
+            }
+            else
+            {
+                ErrorMessages.Add(searchResult.Error);
+            }
+
+            Searched = true;
+            Searching = false;
+        }
+
+        private static string? IsSeeking(ProfileSearchResult profile) =>
+            profile.SeekingEmployment ? "font-weight-bold" : null;
+
+        /// 
+        /// Return "Yes" for true and "No" for false
+        /// 
+        /// The condition in question
+        /// "Yes" for true, "No" for false
+        private static string YesOrNo(bool condition) => condition ? "Yes" : "No";
+
+        /// 
+        /// Create a search query string from the currently-entered criteria
+        /// 
+        /// The query string for the currently-entered criteria
+        private IDictionary SearchQuery()
+        {
+            var dict = new Dictionary();
+            if (Criteria.IsEmptySearch) return dict;
+
+            void part(string name, Func func)
+            {
+                if (!string.IsNullOrEmpty(func(Criteria))) dict.Add(name, func(Criteria));
+            }
+
+            part(nameof(Criteria.ContinentId), it => it.ContinentId);
+            part(nameof(Criteria.Region), it => it.Region);
+            part(nameof(Criteria.Skill), it => it.Skill);
+            part(nameof(Criteria.RemoteWork), it => it.RemoteWork);
+
+            return dict;
+        }
+    }
+}
diff --git a/src/JobsJobsJobs/Client/Shared/NavMenu.razor b/src/JobsJobsJobs/Client/Shared/NavMenu.razor
index 0aa76bc..7bb8323 100644
--- a/src/JobsJobsJobs/Client/Shared/NavMenu.razor
+++ b/src/JobsJobsJobs/Client/Shared/NavMenu.razor
@@ -18,6 +18,11 @@
            Home
         
       
+      
+        
+           Job Seekers
+        
+      
       
         
            Log On
diff --git a/src/JobsJobsJobs/Client/Shared/PublicSearchForm.razor b/src/JobsJobsJobs/Client/Shared/PublicSearchForm.razor
new file mode 100644
index 0000000..550e6a0
--- /dev/null
+++ b/src/JobsJobsJobs/Client/Shared/PublicSearchForm.razor
@@ -0,0 +1,60 @@
+@using JobsJobsJobs.Shared.Api
+
+
+  
+  
+
+
+@code {
+  [Parameter]
+  public PublicSearch Criteria { get; set; } = default!;
+
+  [Parameter]
+  public EventCallback OnSearch { get; set; } = default!;
+
+  [Parameter]
+  public IEnumerable Continents { get; set; } = default!;
+}
diff --git a/src/JobsJobsJobs/Server/Areas/Api/Controllers/ContinentController.cs b/src/JobsJobsJobs/Server/Areas/Api/Controllers/ContinentController.cs
index 42808e9..e7925af 100644
--- a/src/JobsJobsJobs/Server/Areas/Api/Controllers/ContinentController.cs
+++ b/src/JobsJobsJobs/Server/Areas/Api/Controllers/ContinentController.cs
@@ -1,5 +1,4 @@
 using JobsJobsJobs.Server.Data;
-using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using System.Threading.Tasks;
 
@@ -9,7 +8,6 @@ namespace JobsJobsJobs.Server.Areas.Api.Controllers
     /// API endpoint for continent information
     /// 
     [Route("api/[controller]")]
-    [Authorize]
     [ApiController]
     public class ContinentController : ControllerBase
     {
diff --git a/src/JobsJobsJobs/Server/Areas/Api/Controllers/ProfileController.cs b/src/JobsJobsJobs/Server/Areas/Api/Controllers/ProfileController.cs
index 534a18a..da4e376 100644
--- a/src/JobsJobsJobs/Server/Areas/Api/Controllers/ProfileController.cs
+++ b/src/JobsJobsJobs/Server/Areas/Api/Controllers/ProfileController.cs
@@ -120,6 +120,11 @@ namespace JobsJobsJobs.Server.Areas.Api.Controllers
         public async Task Search([FromQuery] ProfileSearch search) =>
             Ok(await _db.SearchProfiles(search));
 
+        [HttpGet("public-search")]
+        [AllowAnonymous]
+        public async Task SearchPublic([FromQuery] PublicSearch search) =>
+            Ok(await _db.SearchPublicProfiles(search));
+
         [HttpPatch("employment-found")]
         public async Task EmploymentFound()
         {
diff --git a/src/JobsJobsJobs/Server/Data/Converters.cs b/src/JobsJobsJobs/Server/Data/Converters.cs
index 7f8d07f..66230a2 100644
--- a/src/JobsJobsJobs/Server/Data/Converters.cs
+++ b/src/JobsJobsJobs/Server/Data/Converters.cs
@@ -12,38 +12,36 @@ namespace JobsJobsJobs.Server.Data
         /// Citizen ID converter
         /// 
         public static readonly ValueConverter CitizenIdConverter =
-            new ValueConverter(v => v.ToString(), v => CitizenId.Parse(v));
+            new(v => v.ToString(), v => CitizenId.Parse(v));
 
         /// 
         /// Continent ID converter
         /// 
         public static readonly ValueConverter ContinentIdConverter =
-            new ValueConverter(v => v.ToString(), v => ContinentId.Parse(v));
+            new(v => v.ToString(), v => ContinentId.Parse(v));
 
         /// 
         /// Markdown converter
         /// 
         public static readonly ValueConverter MarkdownStringConverter =
-            new ValueConverter(v => v.Text, v => new MarkdownString(v));
+            new(v => v.Text, v => new MarkdownString(v));
 
         /// 
         /// Markdown converter for possibly-null values
         /// 
         public static readonly ValueConverter OptionalMarkdownStringConverter =
-            new ValueConverter(
-                v => v == null ? null : v.Text,
-                v => v == null ? null : new MarkdownString(v));
+            new(v => v == null ? null : v.Text, v => v == null ? null : new MarkdownString(v));
 
         /// 
         /// Skill ID converter
         /// 
         public static readonly ValueConverter SkillIdConverter =
-            new ValueConverter(v => v.ToString(), v => SkillId.Parse(v));
+            new(v => v.ToString(), v => SkillId.Parse(v));
 
         /// 
         /// Success ID converter
         /// 
         public static readonly ValueConverter SuccessIdConverter =
-            new ValueConverter(v => v.ToString(), v => SuccessId.Parse(v));
+            new(v => v.ToString(), v => SuccessId.Parse(v));
     }
 }
diff --git a/src/JobsJobsJobs/Server/Data/ProfileExtensions.cs b/src/JobsJobsJobs/Server/Data/ProfileExtensions.cs
index fdd9780..7b1d0e7 100644
--- a/src/JobsJobsJobs/Server/Data/ProfileExtensions.cs
+++ b/src/JobsJobsJobs/Server/Data/ProfileExtensions.cs
@@ -111,7 +111,7 @@ namespace JobsJobsJobs.Server.Data
         /// 
         /// Search profiles by the given criteria
         /// 
-        //  TODO: A criteria parameter!
+        /// The search parameters
         /// The information for profiles matching the criteria
         public static async Task> SearchProfiles(this JobsDbContext db,
             ProfileSearch search)
@@ -160,7 +160,56 @@ namespace JobsJobsJobs.Server.Data
                 x.Profile.SeekingEmployment, x.Profile.RemoteWork, x.Profile.FullTime, x.Profile.LastUpdatedOn))
                 .ToListAsync().ConfigureAwait(false);
         }
-        
+
+        /// 
+        /// Search public profiles by the given criteria
+        /// 
+        /// The search parameters
+        /// The information for profiles matching the criteria
+        public static async Task> SearchPublicProfiles(this JobsDbContext db,
+            PublicSearch search)
+        {
+            var query = db.Profiles
+                .Join(db.Citizens, p => p.Id, c => c.Id, (p, c) => new { Profile = p, Citizen = c })
+                .Where(it => it.Profile.IsPublic);
+
+            var useIds = false;
+            var citizenIds = new List();
+
+            if (!string.IsNullOrEmpty(search.ContinentId))
+            {
+                query = query.Where(it => it.Profile.ContinentId == ContinentId.Parse(search.ContinentId));
+            }
+
+            if (!string.IsNullOrEmpty(search.Region))
+            {
+                query = query.Where(it => it.Profile.Region.ToLower().Contains(search.Region.ToLower()));
+            }
+
+            if (!string.IsNullOrEmpty(search.RemoteWork))
+            {
+                query = query.Where(it => it.Profile.RemoteWork == (search.RemoteWork == "yes"));
+            }
+
+            if (!string.IsNullOrEmpty(search.Skill))
+            {
+                useIds = true;
+                citizenIds.AddRange(await db.Skills
+                    .Where(s => s.Description.ToLower().Contains(search.Skill.ToLower()))
+                    .Select(s => s.CitizenId)
+                    .ToListAsync().ConfigureAwait(false));
+            }
+
+            if (useIds)
+            {
+                query = query.Where(it => citizenIds.Contains(it.Citizen.Id));
+            }
+
+            return await query.Select(x => new ProfileSearchResult(x.Citizen.Id, x.Citizen.CitizenName,
+                x.Profile.SeekingEmployment, x.Profile.RemoteWork, x.Profile.FullTime, x.Profile.LastUpdatedOn))
+                .ToListAsync().ConfigureAwait(false);
+        }
+
         /// 
         /// Delete skills and profile for the given citizen
         /// 
diff --git a/src/JobsJobsJobs/Server/JobsJobsJobs.Server.csproj.user b/src/JobsJobsJobs/Server/JobsJobsJobs.Server.csproj.user
index 4aff699..476125d 100644
--- a/src/JobsJobsJobs/Server/JobsJobsJobs.Server.csproj.user
+++ b/src/JobsJobsJobs/Server/JobsJobsJobs.Server.csproj.user
@@ -6,7 +6,7 @@
     JobsJobsJobs.Server
     MvcControllerEmptyScaffolder
     root/Common/MVC/Controller
-    FolderProfile
+    C:\Users\danie\Documents\sandbox\jobs-jobs-jobs\src\JobsJobsJobs\Server\Properties\PublishProfiles\FolderProfile.pubxml
   
   
     ProjectDebugger
diff --git a/src/JobsJobsJobs/Shared/Api/PublicSearch.cs b/src/JobsJobsJobs/Shared/Api/PublicSearch.cs
new file mode 100644
index 0000000..91e4e01
--- /dev/null
+++ b/src/JobsJobsJobs/Shared/Api/PublicSearch.cs
@@ -0,0 +1,37 @@
+namespace JobsJobsJobs.Shared.Api
+{
+    /// 
+    /// The parameters for a public job search
+    /// 
+    public class PublicSearch
+    {
+        /// 
+        /// Retrieve citizens from this continent
+        /// 
+        public string? ContinentId { get; set; }
+
+        /// 
+        /// Retrieve citizens from this region
+        /// 
+        public string? Region { get; set; }
+
+        /// 
+        /// Text for a search within a citizen's skills
+        /// 
+        public string? Skill { get; set; }
+
+        /// 
+        /// Whether to retrieve citizens who do or do not want remote work
+        /// 
+        public string RemoteWork { get; set; } = "";
+
+        /// 
+        /// Is the search empty?
+        /// 
+        public bool IsEmptySearch =>
+            string.IsNullOrEmpty(ContinentId)
+            && string.IsNullOrEmpty(Region)
+            && string.IsNullOrEmpty(Skill)
+            && string.IsNullOrEmpty(RemoteWork);
+    }
+}