From 1dbcc39bc77d81baea087419d80cf2cc1501eff4 Mon Sep 17 00:00:00 2001 From: Mike McGhen Date: Mon, 25 Aug 2025 20:26:37 -0400 Subject: [PATCH] Redesign movie and show search pages with new UI Major UI/UX overhaul for SearchMovies.razor and SearchShows.razor, including new navigation toggles, animated search forms, improved results display, and enhanced styling. Moved layout and header styles from MainLayout.razor to CSS, unified button styles on Home.razor, and added interactive elements and animations for a more engaging user experience. --- .../Components/Layout/MainLayout.razor | 38 - .../Components/Layout/MainLayout.razor.css | 28 - MovieActorLookup/Components/Pages/Home.razor | 15 +- .../Components/Pages/SearchMovies.razor | 980 ++++++++++++++++-- .../Components/Pages/SearchShows.razor | 479 +++++++-- 5 files changed, 1250 insertions(+), 290 deletions(-) diff --git a/MovieActorLookup/Components/Layout/MainLayout.razor b/MovieActorLookup/Components/Layout/MainLayout.razor index 1cb0578..7b09b53 100644 --- a/MovieActorLookup/Components/Layout/MainLayout.razor +++ b/MovieActorLookup/Components/Layout/MainLayout.razor @@ -28,42 +28,4 @@ height: 100vh; overflow: hidden; } - - .sidebar { - width: 250px; - background: #ffffff; - border-right: 1px solid rgba(0, 0, 0, 0.06); - position: relative; - z-index: 100; - } - - .main-content { - flex: 1; - display: flex; - flex-direction: column; - min-height: 0; - width: 100%; - } - - .top-row { - background: rgba(255, 255, 255, 0.95); - border-bottom: 1px solid rgba(0, 0, 0, 0.06); - display: flex; - justify-content: flex-end; - align-items: center; - height: 3.5rem; - padding: 0 24px; - } - - .top-row a { - color: #6c757d; - text-decoration: none; - font-weight: 500; - font-size: 14px; - transition: color 0.2s ease; - } - - .top-row a:hover { - color: #667eea; - } diff --git a/MovieActorLookup/Components/Layout/MainLayout.razor.css b/MovieActorLookup/Components/Layout/MainLayout.razor.css index 45d97f6..2865b4e 100644 --- a/MovieActorLookup/Components/Layout/MainLayout.razor.css +++ b/MovieActorLookup/Components/Layout/MainLayout.razor.css @@ -8,34 +8,6 @@ main { flex: 1; } -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; - display: flex; - align-items: center; -} - - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } - - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } - - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - @media (max-width: 640.98px) { .top-row { justify-content: space-between; diff --git a/MovieActorLookup/Components/Pages/Home.razor b/MovieActorLookup/Components/Pages/Home.razor index 85d70ea..e99db84 100644 --- a/MovieActorLookup/Components/Pages/Home.razor +++ b/MovieActorLookup/Components/Pages/Home.razor @@ -8,7 +8,7 @@

This app helps you quickly identify actors, find out what else they've been in, and explore cast details for your favorite movies and TV shows.

🎬 Search Movies - 📺 Search TV Shows + 📺 Search TV Shows
@@ -180,24 +180,13 @@ } .cta-button.primary { - background: linear-gradient(45deg, #667eea, #764ba2); - color: white; - box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4); - } - - .cta-button.primary:hover { - transform: translateY(-3px) scale(1.05); - box-shadow: 0 20px 40px rgba(102, 126, 234, 0.6); - } - - .cta-button.secondary { background: rgba(255, 255, 255, 0.15); color: white; border: 2px solid rgba(255, 255, 255, 0.3); backdrop-filter: blur(10px); } - .cta-button.secondary:hover { + .cta-button.primary:hover { transform: translateY(-3px) scale(1.05); background: rgba(255, 255, 255, 0.25); border-color: rgba(255, 255, 255, 0.5); diff --git a/MovieActorLookup/Components/Pages/SearchMovies.razor b/MovieActorLookup/Components/Pages/SearchMovies.razor index ec3cddb..6e58e5c 100644 --- a/MovieActorLookup/Components/Pages/SearchMovies.razor +++ b/MovieActorLookup/Components/Pages/SearchMovies.razor @@ -3,154 +3,920 @@ @using MovieActorLookup.Models.Movies @using System.ComponentModel.DataAnnotations @inject MovieService MovieService +@inject IJSRuntime JSRuntime @rendermode InteractiveServer -

Search for Actor by Movie and Character Name

+
+
+ +

Discover Actors & Their Movies

+

Search by movie and character to explore an actor's complete filmography

+
-
- - - -
- -@if (_isSearching) -{ -

Searching...

-} - -else if (_actor != null) -{ -

Actor: @_actor.Name

- - @if (_matchedMovie != null) - { -
- Movie Poster -
-
@_matchedMovie.MovieTitle
- @_matchedMovie.CharacterName -
- @_matchedMovie.Franchise - Release Date: @_matchedMovie.ReleaseDate +
+
+
+ +
🎬
+
+
+ +
🎭
- } - - @foreach (var franchiseGroup in _actor.MovieCharacters - .GroupBy(mc => mc.Franchise) - .OrderBy(g => g.Key == "Standalone") - .ThenBy(g => g.Key)) - { -
@franchiseGroup.Key
-
- @foreach (var movieCharacter in (franchiseGroup.Key == "Standalone" - ? franchiseGroup.OrderByDescending(mc => - DateTime.TryParse(mc.ReleaseDate, out var d) ? d : DateTime.MinValue) - : franchiseGroup.OrderBy(mc => - DateTime.TryParse(mc.ReleaseDate, out var d) ? d : DateTime.MaxValue))) + +
+ + @if (_isSearching) + { +
+
+
+
+
+
+

Finding the perfect match...

+
+ } + else if (_actor != null) + { +
+
+

@_actor.Name

+
@_actor.MovieCharacters.Count movie@(_actor.MovieCharacters.Count != 1 ? "s" : "") found
+
+ + @if (_matchedMovie != null) + { +
+

Your Search Match

+
} + +
+

Complete Movie Filmography

+ + @foreach (var group in _actor.MovieCharacters + .GroupBy(mc => mc.Franchise) + .OrderBy(g => g.Key == "Standalone") + .ThenBy(g => g.Key)) + { + @if (group.Key != "Standalone") + { +
+
@group.Key Collection
+
+ } + +
+ @foreach (var movie in (group.Key == "Standalone" + ? group.OrderByDescending(mc => DateTime.TryParse(mc.ReleaseDate, out var d) ? d : DateTime.MinValue) + : group.OrderBy(mc => DateTime.TryParse(mc.ReleaseDate, out var d) ? d : DateTime.MaxValue))) + { +
+
+ @movie.MovieTitle Poster +
+
+ View Details +
+
+
+
+
@movie.MovieTitle
+
as @movie.CharacterName
+ @if (!string.IsNullOrEmpty(movie.Overview)) + { +
+

+ @movie.Overview +

+ @if (movie.Overview.Length > 150) + { + + } +
+ } +
@movie.ReleaseDate
+
+
+ } +
+ } +
} -} -else -{ -

Enter a movie title and character name to begin your search.

-} + else if (!string.IsNullOrEmpty(_movieTitle) || !string.IsNullOrEmpty(_characterName)) + { +
+
🔍
+

No matches found

+

Try adjusting your search terms or check the spelling

+
+ } + else + { +
+
🎬
+

Ready to explore?

+

Enter a movie title and character name to discover an actor's complete film journey

+
+ } +
+ + + + @code { - private string _movieTitle; - private string _characterName; + private string _movieTitle = ""; + private string _characterName = ""; private Actor _actor; private MovieCharacter _matchedMovie; private bool _isSearching = false; + private HashSet _expandedOverviews = new HashSet(); + + private bool IsOverviewExpanded(string movieTitle) + { + return _expandedOverviews.Contains(movieTitle); + } + + private void ToggleOverview(string movieTitle) + { + if (_expandedOverviews.Contains(movieTitle)) + { + _expandedOverviews.Remove(movieTitle); + } + else + { + _expandedOverviews.Add(movieTitle); + } + } private async Task SearchForActorAndCharacters() { + if (string.IsNullOrWhiteSpace(_movieTitle) || string.IsNullOrWhiteSpace(_characterName)) + return; + _isSearching = true; _actor = null; - _matchedMovie = null; // reset matched movie + _matchedMovie = null; StateHasChanged(); - if (!string.IsNullOrWhiteSpace(_movieTitle) && !string.IsNullOrWhiteSpace(_characterName)) + await Task.Delay(100); + await JSRuntime.InvokeVoidAsync("startAnimations"); + + try { var movies = await MovieService.GetMoviesByTitle(_movieTitle); + foreach (var movie in movies) { var credits = await MovieService.GetMovieCreditsByMovieId(movie.Id); + foreach (var castMember in credits.Cast) { - if (castMember.Character.ToLower().Contains(_characterName.ToLower())) + if (castMember.Character.Contains(_characterName, StringComparison.OrdinalIgnoreCase)) { if (_actor == null) { _actor = new Actor(castMember.Name); - int personId = castMember.Id; - - var filmography = await MovieService.GetActorMovieCreditsById(personId); - if (filmography?.Cast != null) - { - foreach (var role in filmography.Cast) - { - var fullMovie = await MovieService.GetMovieDetailsById(role.Id); - - var movieCharacter = new MovieCharacter - { - MovieTitle = role.Title, - CharacterName = role.Character, - MoviePosterPath = role.PosterPath, - ReleaseDate = DateTime.TryParse(fullMovie?.ReleaseDate, out var parsedDate) - ? parsedDate.ToString("MM/dd/yyyy") - : "N/A", - Overview = fullMovie?.Overview ?? "", - Franchise = fullMovie?.BelongsToCollection?.Name ?? "Standalone" - }; - - _actor.MovieCharacters.Add(movieCharacter); - - if (_matchedMovie == null) - { - if (role.Title == movie.Title && role.Character == castMember.Character) - { - _matchedMovie = movieCharacter; - } - } - } - } + await LoadActorFilmography(castMember.Id, movie, castMember); } + break; } } + + if (_actor != null) break; + } + } + catch (Exception ex) + { + Console.WriteLine($"Search error: {ex.Message}"); + } + finally + { + _isSearching = false; + StateHasChanged(); + } + } + + private async Task LoadActorFilmography(int personId, object originalMovie, object originalCast) + { + var filmography = await MovieService.GetActorMovieCreditsById(personId); + + if (filmography?.Cast != null) + { + foreach (var role in filmography.Cast) + { + var fullMovie = await MovieService.GetMovieDetailsById(role.Id); + + var movieCharacter = new MovieCharacter + { + MovieTitle = role.Title ?? "Unknown Title", + CharacterName = role.Character ?? "Unknown Character", + MoviePosterPath = role.PosterPath ?? "", + ReleaseDate = DateTime.TryParse(fullMovie?.ReleaseDate, out var parsedDate) + ? parsedDate.ToString("MMM dd, yyyy") + : "Unknown Date", + Overview = fullMovie?.Overview ?? "", + Franchise = fullMovie?.BelongsToCollection?.Name ?? "Standalone" + }; + + _actor.MovieCharacters.Add(movieCharacter); + + if (_matchedMovie == null && + string.Equals(role.Title, GetPropertyValue(originalMovie, "Title")?.ToString(), StringComparison.OrdinalIgnoreCase) && + string.Equals(role.Character, GetPropertyValue(originalCast, "Character")?.ToString(), StringComparison.OrdinalIgnoreCase)) + { + _matchedMovie = movieCharacter; + } } } - - _isSearching = false; - StateHasChanged(); } - - private async Task GetProperMovieBasedOffOfActorInvolvement(string movieActorTitle, CastMember actor) + private object GetPropertyValue(object obj, string propertyName) { - List overallCredits = new(); - - List actorMovies = await MovieService.GetMoviesByTitle(movieActorTitle); - - foreach (Movie movie in actorMovies) - { - overallCredits.Add(await MovieService.GetMovieCreditsByMovieId(movie.Id)); - } - - Credits actorMovieCredits = overallCredits.FirstOrDefault(x => x.Cast.Any(c => c.Id == actor.Id)); - Movie movie1 = actorMovies.Where(x => x.Id == actorMovieCredits.Id).FirstOrDefault(); - - return movie1; + if (obj == null) return null; + var property = obj.GetType().GetProperty(propertyName); + return property?.GetValue(obj); } } \ No newline at end of file diff --git a/MovieActorLookup/Components/Pages/SearchShows.razor b/MovieActorLookup/Components/Pages/SearchShows.razor index 839ae33..08d7ea5 100644 --- a/MovieActorLookup/Components/Pages/SearchShows.razor +++ b/MovieActorLookup/Components/Pages/SearchShows.razor @@ -6,6 +6,12 @@
+

Discover Actors & Their Shows

Search by TV show and character to explore an actor's complete filmography

@@ -74,6 +80,11 @@ alt="@_matchedShow.ShowTitle Poster" loading="lazy" />
✨ Match
+
+
+ View Details +
+
@_matchedShow.ShowTitle
@@ -116,6 +127,11 @@ src="@($"https://image.tmdb.org/t/p/w500{show.ShowPosterPath}")" alt="@show.ShowTitle Poster" loading="lazy" /> +
+
+ View Details +
+
@show.ShowTitle
@@ -154,7 +170,7 @@ else {
-
🎬
+
📺

Ready to explore?

Enter a TV show title and character name to discover an actor's complete television journey

@@ -167,37 +183,167 @@ margin: 0 auto; padding: 2rem 1rem; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); + min-height: 100vh; + position: relative; + } + + .search-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/svg+xml,') repeat; + opacity: 0.4; + pointer-events: none; + animation: float 25s ease-in-out infinite; + } + + @@keyframes float { + 0%, 100% + + { + transform: translateY(0px) rotate(0deg); + } + + 50% { + transform: translateY(-30px) rotate(180deg); + } + } .search-header { text-align: center; margin-bottom: 2.5rem; + position: relative; + z-index: 1; } + .navigation-toggle { + display: flex; + justify-content: center; + margin-bottom: 1.5rem; + animation: slideInDown 0.8s ease-out 0.2s both; + } + + .nav-btn { + background: rgba(255,255,255,0.15); + color: white; + text-decoration: none; + padding: 0.75rem 1.5rem; + border-radius: 2rem; + font-weight: 600; + font-size: 0.9rem; + display: flex; + align-items: center; + gap: 0.5rem; + transition: all 0.3s ease; + backdrop-filter: blur(10px); + border: 1px solid rgba(255,255,255,0.2); + box-shadow: 0 4px 15px rgba(0,0,0,0.1); + } + + .nav-btn:hover { + background: rgba(255,255,255,0.25); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0,0,0,0.15); + color: white; + text-decoration: none; + } + + .nav-btn:active { + transform: translateY(0px); + } + + .nav-icon { + font-size: 1.1rem; + transition: transform 0.3s ease; + } + + .nav-btn:hover .nav-icon { + transform: scale(1.2); + } + + .movies-btn { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3)); + } + + .movies-btn:hover { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.5), rgba(118, 75, 162, 0.5)); + } + .search-title { font-size: 2.5rem; font-weight: 700; - color: #1a1a1a; + color: white; margin-bottom: 0.5rem; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; + text-shadow: 2px 2px 4px rgba(0,0,0,0.3); + animation: slideInDown 1s ease-out; + } + + @@keyframes slideInDown { + from + + { + opacity: 0; + transform: translateY(-50px); + } + + to { + opacity: 1; + transform: translateY(0); + } + } .search-subtitle { font-size: 1.1rem; - color: #666; + color: rgba(255,255,255,0.9); margin: 0; + animation: fadeIn 1.5s ease-out 0.3s both; + } + + @@keyframes fadeIn { + from + + { + opacity: 0; + } + + to { + opacity: 1; + } + } .search-form { - background: white; - border-radius: 1rem; + background: rgba(255,255,255,0.95); + border-radius: 1.5rem; padding: 2rem; - box-shadow: 0 8px 25px rgba(0,0,0,0.08); + box-shadow: 0 20px 40px rgba(0,0,0,0.1); margin-bottom: 2rem; - border: 1px solid #f0f0f0; + backdrop-filter: blur(10px); + border: 1px solid rgba(255,255,255,0.2); + animation: slideInUp 1s ease-out 0.6s both; + position: relative; + z-index: 1; + } + + @@keyframes slideInUp { + from + + { + opacity: 0; + transform: translateY(30px); + } + + to { + opacity: 1; + transform: translateY(0); + } + } .input-group { @@ -215,9 +361,9 @@ .search-input { width: 100%; - padding: 0.875rem 1rem 0.875rem 3rem; + padding: 1rem 1rem 1rem 3.5rem; border: 2px solid #e1e5e9; - border-radius: 0.75rem; + border-radius: 1rem; font-size: 1rem; transition: all 0.3s ease; background: #fafbfc; @@ -225,25 +371,31 @@ .search-input:focus { outline: none; - border-color: #667eea; + border-color: #4f46e5; background: white; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); + box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1), 0 8px 25px rgba(79, 70, 229, 0.15); + transform: translateY(-2px); } .input-icon { position: absolute; - left: 1rem; + left: 1.2rem; top: 50%; transform: translateY(-50%); - font-size: 1.2rem; + font-size: 1.3rem; + transition: transform 0.3s ease; + } + + .input-wrapper:focus-within .input-icon { + transform: translateY(-50%) scale(1.1); } .search-btn { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); border: none; color: white; - padding: 0.875rem 2rem; - border-radius: 0.75rem; + padding: 1rem 2.5rem; + border-radius: 1rem; font-size: 1rem; font-weight: 600; cursor: pointer; @@ -251,13 +403,18 @@ display: flex; align-items: center; gap: 0.5rem; - min-width: 140px; + min-width: 160px; justify-content: center; + box-shadow: 0 4px 15px rgba(79, 70, 229, 0.3); } .search-btn:hover:not(:disabled) { - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3); + transform: translateY(-3px); + box-shadow: 0 12px 35px rgba(79, 70, 229, 0.4); + } + + .search-btn:active:not(:disabled) { + transform: translateY(-1px); } .search-btn:disabled { @@ -266,17 +423,34 @@ } .spinner { - width: 16px; - height: 16px; + width: 18px; + height: 18px; border: 2px solid rgba(255,255,255,0.3); border-top: 2px solid white; border-radius: 50%; + animation: spin 1s linear infinite; + } + + @@keyframes spin { + 0% + + { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } .loading-state, .welcome-state, .no-results { text-align: center; padding: 3rem 2rem; - color: #666; + color: rgba(255,255,255,0.9); + animation: fadeIn 0.5s ease-out; + position: relative; + z-index: 1; } .loading-animation { @@ -287,19 +461,58 @@ } .loading-dot { - width: 8px; - height: 8px; - background: #667eea; + width: 12px; + height: 12px; + background: white; border-radius: 50%; + animation: bounce 1.4s ease-in-out infinite both; + } + + .loading-dot:nth-child(1) { + animation-delay: -0.32s; + } + + .loading-dot:nth-child(2) { + animation-delay: -0.16s; + } + + @@keyframes bounce { + 0%, 80%, 100% + + { + transform: scale(0); + } + + 40% { + transform: scale(1); + } + } .welcome-icon, .no-results-icon { - font-size: 3rem; + font-size: 4rem; margin-bottom: 1rem; + animation: pulse 2s ease-in-out infinite; + } + + @@keyframes pulse { + 0%, 100% + + { + transform: scale(1); + } + + 50% { + transform: scale(1.1); + } + } .results-section { margin-top: 2rem; + animation: slideInUp 0.8s ease-out; + position: relative; + z-index: 1; } .actor-header { @@ -309,22 +522,29 @@ margin-bottom: 2rem; flex-wrap: wrap; gap: 1rem; + background: rgba(255,255,255,0.1); + padding: 1.5rem 2rem; + border-radius: 1rem; + backdrop-filter: blur(10px); } .actor-name { font-size: 2rem; font-weight: 700; - color: #1a1a1a; + color: white; margin: 0; + text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .results-count { - background: #f0f4ff; - color: #4c63d2; - padding: 0.5rem 1rem; + background: rgba(255,255,255,0.2); + color: white; + padding: 0.5rem 1.5rem; border-radius: 2rem; font-weight: 500; font-size: 0.9rem; + backdrop-filter: blur(10px); + border: 1px solid rgba(255,255,255,0.1); } .matched-show-section { @@ -334,11 +554,12 @@ .section-title { font-size: 1.5rem; font-weight: 600; - color: #1a1a1a; + color: white; margin-bottom: 1.5rem; display: flex; align-items: center; gap: 0.5rem; + text-shadow: 1px 1px 2px rgba(0,0,0,0.3); } .franchise-group { @@ -348,10 +569,13 @@ .franchise-title { font-size: 1.3rem; font-weight: 600; - color: #4c63d2; + color: white; margin: 0; - padding-left: 1rem; - border-left: 4px solid #4c63d2; + padding: 1rem 1.5rem; + background: rgba(255,255,255,0.1); + border-radius: 0.5rem; + border-left: 4px solid rgba(255,255,255,0.5); + backdrop-filter: blur(10px); } .shows-grid { @@ -363,22 +587,23 @@ .show-card { background: white; - border-radius: 1rem; + border-radius: 1.2rem; overflow: hidden; - box-shadow: 0 4px 15px rgba(0,0,0,0.08); - transition: all 0.3s ease; - border: 1px solid #f0f0f0; + box-shadow: 0 8px 25px rgba(0,0,0,0.15); + transition: all 0.4s ease; + border: 1px solid rgba(255,255,255,0.2); position: relative; + transform-style: preserve-3d; } .show-card:hover { - transform: translateY(-4px); - box-shadow: 0 8px 30px rgba(0,0,0,0.12); + transform: translateY(-8px) scale(1.02); + box-shadow: 0 20px 40px rgba(0,0,0,0.25); } .show-card.featured { - border: 2px solid #667eea; - box-shadow: 0 8px 30px rgba(102, 126, 234, 0.15); + border: 2px solid rgba(255,255,255,0.5); + box-shadow: 0 12px 35px rgba(79, 70, 229, 0.25); } .show-card.matched { @@ -390,6 +615,7 @@ width: 100%; aspect-ratio: 2/3; overflow: hidden; + background: linear-gradient(45deg, #f0f0f0, #e0e0e0); } .poster-image { @@ -397,27 +623,53 @@ height: 100%; object-fit: contain; background-color: #f8f9fa; - transition: transform 0.3s ease; + transition: all 0.4s ease; + } + + .poster-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.8) 100%); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: flex-end; + padding: 1rem; + } + + .show-card:hover .poster-overlay { + opacity: 1; } .show-card:hover .poster-image { transform: scale(1.05); } + .overlay-content { + color: white; + font-weight: 600; + font-size: 0.9rem; + } + .match-badge { position: absolute; - top: 0.5rem; - right: 0.5rem; + top: 0.8rem; + right: 0.8rem; background: linear-gradient(135deg, #10b981, #059669); color: white; - padding: 0.3rem 0.8rem; - border-radius: 1rem; + padding: 0.4rem 1rem; + border-radius: 1.5rem; font-size: 0.8rem; font-weight: 600; + box-shadow: 0 4px 15px rgba(16, 185, 129, 0.4); + z-index: 2; } .card-content { - padding: 1.25rem; + padding: 1.5rem; } .show-title { @@ -429,7 +681,7 @@ } .character-name { - color: #667eea; + color: #4f46e5; font-weight: 500; margin-bottom: 0.75rem; font-size: 0.95rem; @@ -456,18 +708,19 @@ .read-more-btn { background: none; border: none; - color: #667eea; + color: #4f46e5; font-size: 0.85rem; font-weight: 600; cursor: pointer; padding: 0.25rem 0; margin-top: 0.5rem; text-decoration: underline; - transition: color 0.3s ease; + transition: all 0.3s ease; } .read-more-btn:hover { - color: #4c63d2; + color: #3730a3; + transform: translateX(5px); } .air-date { @@ -477,42 +730,43 @@ } @@media (max-width: 768px) { - .search-container { - padding: 1rem; - } + .search-container - .search-title { - font-size: 2rem; - } + { + padding: 1rem; + } - .input-group { - flex-direction: column; - } + .search-title { + font-size: 2rem; + } - .input-wrapper { - min-width: auto; - } + .input-group { + flex-direction: column; + } - .actor-header { - flex-direction: column; - align-items: stretch; - } + .input-wrapper { + min-width: auto; + } + + .actor-header { + flex-direction: column; + align-items: stretch; + } + + .shows-grid { + grid-template-columns: 1fr; + gap: 1rem; + } - .shows-grid { - grid-template-columns: 1fr; - gap: 1rem; - } } @code { @@ -592,7 +864,6 @@ _matchedShow = null; StateHasChanged(); - // Start animations after a small delay to ensure DOM is ready await Task.Delay(100); await JSRuntime.InvokeVoidAsync("startAnimations");