diff --git a/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.html b/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.html
new file mode 100644
index 000000000..0037a9e76
--- /dev/null
+++ b/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.html
@@ -0,0 +1,123 @@
+
diff --git a/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.scss b/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.scss
new file mode 100644
index 000000000..119f9028b
--- /dev/null
+++ b/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.scss
@@ -0,0 +1,90 @@
+// Combined Filter & Sort menu styles
+.filter-section,
+.sort-section {
+ padding: 4px 0;
+}
+
+.menu-section-header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 16px 4px;
+ font-size: 11px;
+ font-weight: 600;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ pointer-events: none;
+
+ .section-icon {
+ font-size: 16px;
+ width: 16px;
+ height: 16px;
+ color: rgba(139, 92, 246, 0.6);
+ }
+}
+
+// Menu item layout with right-aligned checkmark
+::ng-deep .filter-sort-menu {
+ .mat-mdc-menu-item {
+ display: flex !important;
+ align-items: center !important;
+ gap: 12px !important;
+ min-height: 40px !important;
+ padding: 0 16px !important;
+
+ .mat-icon:first-child {
+ margin-right: 0 !important;
+ flex-shrink: 0;
+ }
+
+ .menu-item-text {
+ flex: 1;
+ text-align: left;
+ }
+
+ .check-icon {
+ margin-left: auto !important;
+ margin-right: 0 !important;
+ flex-shrink: 0;
+ opacity: 0;
+ transform: scale(0.8);
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+
+ &.check-visible {
+ opacity: 1;
+ transform: scale(1);
+ }
+ }
+ }
+}
+
+.active-option {
+ background-color: rgba(139, 92, 246, 0.12) !important;
+ position: relative;
+
+ &::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 3px;
+ height: 60%;
+ background: rgba(139, 92, 246, 0.8);
+ border-radius: 0 2px 2px 0;
+ }
+}
+
+.check-icon {
+ font-size: 18px !important;
+ width: 18px !important;
+ height: 18px !important;
+ color: rgba(139, 92, 246, 1) !important;
+}
+
+// Menu footer
+.menu-footer {
+ padding: 4px 8px 4px;
+ display: flex;
+ justify-content: center;
+}
diff --git a/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.ts b/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.ts
new file mode 100644
index 000000000..6bf906626
--- /dev/null
+++ b/apps/web/src/app/shared/components/filter-sort-menu/filter-sort-menu.component.ts
@@ -0,0 +1,119 @@
+import {
+ Component,
+ effect,
+ inject,
+ input,
+ output,
+ viewChild,
+} from '@angular/core';
+import { toSignal } from '@angular/core/rxjs-interop';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { MatMenu, MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
+import { Store } from '@ngrx/store';
+import { TranslateModule } from '@ngx-translate/core';
+import { FilterActions, selectActiveTypeFilters } from 'm3u-state';
+import { SortBy, SortOrder, SortService } from 'services';
+
+@Component({
+ selector: 'app-filter-sort-menu',
+ templateUrl: './filter-sort-menu.component.html',
+ styleUrls: ['./filter-sort-menu.component.scss'],
+ imports: [MatButtonModule, MatIconModule, MatMenuModule, TranslateModule],
+})
+export class FilterSortMenuComponent {
+ private store = inject(Store);
+ private sortService = inject(SortService);
+
+ readonly menu = viewChild.required('filterSortMenu');
+ readonly menuTrigger = input();
+ readonly filterChanged = output();
+
+ playlistTypes = [
+ {
+ id: 'm3u',
+ icon: 'playlist_play',
+ translationKey: 'HOME.PLAYLIST_TYPES.M3U',
+ checked: true,
+ },
+ {
+ id: 'xtream',
+ icon: 'cloud',
+ translationKey: 'HOME.PLAYLIST_TYPES.XTREAM',
+ checked: true,
+ },
+ {
+ id: 'stalker',
+ icon: 'router',
+ translationKey: 'HOME.PLAYLIST_TYPES.STALKER',
+ checked: true,
+ },
+ ];
+
+ private readonly selectedTypeFilters = this.store.selectSignal(
+ selectActiveTypeFilters
+ );
+
+ readonly SortBy = SortBy;
+ readonly SortOrder = SortOrder;
+ private readonly currentSortOptions = toSignal(
+ this.sortService.getSortOptions(),
+ {
+ requireSync: true,
+ }
+ );
+
+ constructor() {
+ effect(() => {
+ if (this.selectedTypeFilters) {
+ this.playlistTypes = this.playlistTypes.map((type) => {
+ type.checked = this.selectedTypeFilters().includes(type.id);
+ return type;
+ });
+ }
+ });
+ }
+
+ togglePlaylistType(type: {
+ id: string;
+ icon: string;
+ translationKey: string;
+ checked: boolean;
+ }) {
+ const currentlySelectedCount = this.playlistTypes.filter(
+ (f) => f.checked
+ ).length;
+
+ // Prevent deselecting if it's the last selected option
+ if (type.checked && currentlySelectedCount === 1) {
+ return;
+ }
+
+ type.checked = !type.checked;
+ this.store.dispatch(
+ FilterActions.setSelectedFilters({
+ selectedFilters: this.playlistTypes
+ .filter((f) => f.checked)
+ .map((f) => f.id),
+ })
+ );
+ this.filterChanged.emit();
+ }
+
+ setSortOptions(by: SortBy, order: SortOrder): void {
+ this.sortService.setSortOptions({ by, order });
+ }
+
+ isSortActive(by: SortBy, order: SortOrder): boolean {
+ const options = this.currentSortOptions();
+ return options?.by === by && options?.order === order;
+ }
+
+ get activeFiltersCount(): number {
+ return this.playlistTypes.filter((type) => !type.checked).length;
+ }
+
+ closeMenu(): void {
+ this.menuTrigger()?.closeMenu();
+ }
+}
diff --git a/apps/web/src/app/shared/components/header/header.component.html b/apps/web/src/app/shared/components/header/header.component.html
index dc81325e0..f45a226e3 100644
--- a/apps/web/src/app/shared/components/header/header.component.html
+++ b/apps/web/src/app/shared/components/header/header.component.html
@@ -34,23 +34,23 @@
-
+
-
+
@if (isDesktop) {