@ngxsplayer/ngx-smart-player-ui-video 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngxsplayer-ngx-smart-player-ui-video.mjs","sources":["../../../projects/ngx-smart-player-ui-video/src/lib/video-player-ui.component.ts","../../../projects/ngx-smart-player-ui-video/src/lib/video-ui.module.ts","../../../projects/ngx-smart-player-ui-video/src/ngxsplayer-ngx-smart-player-ui-video.ts"],"sourcesContent":["import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, EventEmitter, Input, Output, ViewChild, inject } from '@angular/core';\nimport { CommonModule, DOCUMENT } from '@angular/common';\nimport { SmartPlayerComponent, SmartPlayerIconComponent, SmartSettingsComponent } from '@ngxsplayer/ngx-smart-player';\nimport type { PlayerMenuLabels, SmartCompactMode, SmartPlayerProgress, SmartSettingAction, SmartSettingChange, SmartSettingsSection } from '@ngxsplayer/ngx-smart-player';\nimport { NgxspVolumeControl, type NgxspTimelineMarker } from '@ngxsplayer/ngx-smart-player-ui-core';\nimport type {\n EngineFactory,\n PlayerConfig,\n PlayerPlugin,\n PlayerPorts,\n PlayerState,\n SmartPlayerIconName,\n} from '@ngxsplayer/ngx-smart-player';\n\nexport type VideoUiMediaKind = 'movie' | 'series' | 'episode' | 'clip' | 'live';\n\n/** Something that can be done to one video, offered on its card rather than somewhere else. */\nexport interface VideoUiCardAction {\n id: string;\n label: string;\n icon: SmartPlayerIconName;\n /** Whether it takes something away for good, which the menu says in its colours. */\n danger?: boolean;\n}\n\nexport interface VideoUiMediaCard {\n id: string;\n title: string;\n subtitle?: string;\n meta?: string;\n duration?: string;\n sourceUrl?: string;\n artworkUrl?: string;\n active?: boolean;\n kind?: VideoUiMediaKind;\n kindLabel?: string;\n parentTitle?: string;\n seasonLabel?: string;\n episodeLabel?: string;\n releaseYear?: string;\n progressPercent?: number;\n tags?: string[];\n /** What can be done to this one video. Cards with none keep their single gesture: open it. */\n actions?: VideoUiCardAction[];\n}\n\nexport interface VideoUiQueueActionLabels {\n play?: string;\n moveUp?: string;\n moveDown?: string;\n remove?: string;\n}\n\nexport interface VideoUiSearchFilter {\n id: string;\n label: string;\n count?: number;\n active?: boolean;\n disabled?: boolean;\n}\n\nexport interface VideoUiSearchActionLabels {\n play?: string;\n queue?: string;\n watchLater?: string;\n favorite?: string;\n download?: string;\n}\n\n/**\n * One strip along the bottom of the shell, for the two things a view has to say from outside\n * the flow of cards: a question that wants an answer before something happens, and how far a\n * long job has got. The viewer is told once and then left alone.\n */\nexport interface VideoUiPromptBar {\n /** What is happening, or what is being asked. */\n message: string;\n /** Both present when an answer is wanted, and the strip then waits for one. */\n confirmLabel?: string;\n cancelLabel?: string;\n /** Present while something is running: 0 to 100. */\n percent?: number;\n /** A second line: the file in hand, or what the answer decides. */\n hint?: string;\n /** Whether the answer takes something away for good, which the strip says in its colours. */\n danger?: boolean;\n /** Which question this is, so an answer cannot be taken for the answer to another. */\n id?: string;\n}\n\nexport interface VideoUiSection {\n id: string;\n title: string;\n items: VideoUiMediaCard[];\n}\n\n/** Where the menu goes, which is the viewer's choice and not a guess from the window's width. */\nexport type VideoUiMenuLayout = 'vertical' | 'horizontal' | 'hidden';\n\n/** One step of the way down: where the view is, and what it took to get there. */\nexport interface VideoUiTrailStep {\n id: string;\n label: string;\n}\n\nexport interface VideoUiMediaDetailEpisode {\n id: string;\n title: string;\n subtitle?: string;\n duration?: string;\n artworkUrl?: string;\n progressPercent?: number;\n active?: boolean;\n}\n\nexport interface VideoUiMediaDetailAction {\n id: string;\n label: string;\n icon: SmartPlayerIconName;\n primary?: boolean;\n disabled?: boolean;\n}\n\nexport type VideoUiMediaWorkspaceTone = 'teal' | 'red' | 'blue' | 'gold' | 'neutral';\n\nexport interface VideoUiMediaChapter {\n id: string;\n title: string;\n label?: string;\n time?: string;\n timeSec?: number;\n summary?: string;\n actionId?: string;\n active?: boolean;\n}\n\nexport interface VideoUiMediaChecklistItem {\n id: string;\n label: string;\n value?: string;\n description?: string;\n tone?: VideoUiMediaWorkspaceTone;\n}\n\nexport interface VideoUiMediaDetail {\n id: string;\n title: string;\n kicker?: string;\n subtitle?: string;\n summary?: string;\n posterUrl?: string;\n kindLabel?: string;\n metaItems?: string[];\n tags?: string[];\n progressPercent?: number;\n progressLabel?: string;\n actions?: VideoUiMediaDetailAction[];\n workspaceTitle?: string;\n workspaceSummary?: string;\n chaptersTitle?: string;\n chapters?: VideoUiMediaChapter[];\n checklistTitle?: string;\n checklist?: VideoUiMediaChecklistItem[];\n episodesTitle?: string;\n episodes?: VideoUiMediaDetailEpisode[];\n}\n\nexport interface VideoUiNavItem {\n id: string;\n label: string;\n icon: SmartPlayerIconName;\n active?: boolean;\n disabled?: boolean;\n}\n\nexport type VideoUiToolTone = 'teal' | 'red' | 'blue' | 'gold' | 'neutral';\nexport type VideoUiRuntimeMode = 'online' | 'offline';\nexport type VideoUiPlaybackIntent = 'play' | 'pause';\n\nexport interface VideoUiToolCard {\n id: string;\n label: string;\n value: string;\n icon: SmartPlayerIconName;\n active?: boolean;\n tone?: VideoUiToolTone;\n}\n\nexport type VideoUiInspectorTone = 'neutral' | 'success' | 'warning';\n\nexport interface VideoUiInspectorMetric {\n label: string;\n value: string;\n tone?: VideoUiInspectorTone;\n}\n\nexport interface VideoUiInspectorItem {\n label: string;\n value?: string;\n description?: string;\n tone?: VideoUiInspectorTone;\n}\n\nexport interface VideoUiInspectorAction {\n id: string;\n label: string;\n icon: SmartPlayerIconName;\n primary?: boolean;\n disabled?: boolean;\n}\n\n/** A server type of the connection form. */\nexport interface VideoUiServerKind {\n id: string;\n label: string;\n /** False for servers without accounts (DLNA): the user name and password fields are hidden. */\n credentials?: boolean;\n /** Placeholder of the address field for this type. */\n addressPlaceholder?: string;\n /** The app can look for servers of this type on the local network. */\n discoverable?: boolean;\n}\n\nexport interface VideoUiServerDiscovery {\n searchLabel: string;\n searchingLabel: string;\n connectLabel: string;\n /** Shown after a search that found nothing. */\n emptyLabel?: string;\n busy?: boolean;\n servers: Array<{ id: string; name: string; subtitle?: string }>;\n}\n\n/** Media servers the viewer connected, and the form to add one: shown in the inspector panel, after its settings. */\nexport interface VideoUiServerForm {\n /** Title of the servers part of the panel. */\n heading?: string;\n title: string;\n /** Server types to choose from; the first one is selected by default. */\n kinds?: VideoUiServerKind[];\n kindLabel?: string;\n addressPlaceholder: string;\n usernamePlaceholder: string;\n passwordPlaceholder: string;\n submitLabel: string;\n disconnectLabel: string;\n openLabel: string;\n hint?: string;\n busy?: boolean;\n servers: Array<{ id: string; name: string; subtitle?: string; active?: boolean }>;\n /** Servers found on the local network, for the kinds marked `discoverable`. */\n discovery?: VideoUiServerDiscovery;\n}\n\n/** A list the viewer created, shown in the side panel. */\nexport interface VideoUiPlaylistOption {\n id: string;\n name: string;\n itemCount?: number;\n active?: boolean;\n /** The panel's target is already in this list: the button takes it out instead of adding it. */\n containsTarget?: boolean;\n}\n\n/** Everything the side panel needs to manage lists; hidden entirely when null. */\nexport interface VideoUiPlaylistPanel {\n title: string;\n namePlaceholder: string;\n renamePlaceholder: string;\n createLabel: string;\n addLabel: string;\n openLabel: string;\n renameLabel: string;\n deleteLabel: string;\n saveLabel: string;\n cancelLabel: string;\n itemsCountLabel: string;\n emptyLabel: string;\n removeLabel: string;\n /** Item the \"add\" buttons file; without one the buttons are hidden. */\n target?: { id: string; title: string } | null;\n targetLabel?: string;\n playlists: VideoUiPlaylistOption[];\n}\n\n/** A tab of the info panel over the video: its id is the id of the panel it opens. */\n/**\n * A video loaded where it was left: the player says so, and offers to start it over. The point itself is not shown\n * (resume stays private); a new key shows the offer again.\n */\nexport interface VideoUiResumeOffer {\n key: string;\n message: string;\n resumeLabel: string;\n restartLabel: string;\n}\n\nexport interface VideoUiInfoTab {\n id: string;\n label: string;\n icon: SmartPlayerIconName;\n}\n\nexport interface VideoUiInspectorPanel {\n id: string;\n title: string;\n subtitle?: string;\n status?: string;\n icon?: SmartPlayerIconName;\n metrics?: VideoUiInspectorMetric[];\n items?: VideoUiInspectorItem[];\n /** Settings shown in the panel; changes and actions come back through `settingChange` and `settingAction`. */\n settings?: SmartSettingsSection[];\n /** Settings shown after the servers (`serverForm`), which follow `settings`. */\n trailingSettings?: SmartSettingsSection[];\n actions?: VideoUiInspectorAction[];\n}\n\nexport type VideoUiViewMode = 'grid' | 'list';\n\nexport interface VideoUiLanguageOption {\n id: string;\n label: string;\n}\n\nexport type VideoUiStatusKind = 'info' | 'success' | 'error';\n\ninterface VideoUiCardRef {\n sectionId: string;\n item: VideoUiMediaCard;\n index: number;\n}\n\nconst CONTINUE_SECTION_ID = '__continue';\nconst FALLBACK_SECTION_ID = '__fallback';\n/**\n * A shelf holds all of its section: it is a row one scrolls, so cutting it at a row's worth only\n * hides cards behind an edge that looks like the end. The cap is there to keep a catalogue of\n * thousands from being drawn at once, not to make a preview.\n */\nconst SHELF_RAIL_MAX = 60;\n/** Below this, seeing the row and seeing it all are the same thing, and Show all says nothing. */\nconst SHELF_WORTH_EXPANDING = 4;\nconst QUEUE_SECTION_ID = '__queue';\n/** How long the resume offer stays once the video plays. */\nconst RESUME_PROMPT_MS = 10_000;\n\nconst DEFAULT_VIDEO_NAV_ITEMS: VideoUiNavItem[] = [\n { id: 'home', label: 'Home', icon: 'home', active: true },\n { id: 'library', label: 'Library', icon: 'layers' },\n { id: 'video', label: 'Video', icon: 'video' },\n { id: 'live', label: 'Live', icon: 'broadcast' },\n { id: 'favorites', label: 'Favorites', icon: 'heart' },\n { id: 'downloads', label: 'Downloads', icon: 'download' },\n { id: 'settings', label: 'Settings', icon: 'settings' },\n];\n\nconst OFFLINE_VIDEO_TOOL_CARDS: VideoUiToolCard[] = [\n { id: 'offline-library', label: 'Local library', value: 'Open folder', icon: 'folder', tone: 'teal', active: true },\n { id: 'engine', label: 'Local engine', value: 'Adaptive', icon: 'network', tone: 'blue' },\n { id: 'tracks', label: 'Local tracks', value: 'Selectable', icon: 'sliders', tone: 'blue' },\n { id: 'subtitles', label: 'Sidecars', value: 'VTT / SRT', icon: 'captions', tone: 'neutral' },\n { id: 'offline', label: 'Resume', value: 'Progress', icon: 'cloud', tone: 'gold' },\n { id: 'diagnostics', label: 'Local telemetry', value: 'Inspect', icon: 'waveform', tone: 'red' },\n];\n\nconst ONLINE_VIDEO_TOOL_CARDS: VideoUiToolCard[] = [\n { id: 'online-catalog', label: 'Remote catalog', value: 'Configure', icon: 'broadcast', tone: 'teal', active: true },\n { id: 'engine', label: 'Adaptive engine', value: 'HLS / DASH', icon: 'network', tone: 'blue' },\n { id: 'tracks', label: 'Manifests', value: 'Audio / video', icon: 'sliders', tone: 'blue' },\n { id: 'subtitles', label: 'Timed text', value: 'Ready', icon: 'captions', tone: 'neutral' },\n { id: 'cast', label: 'Devices', value: 'Cast ready', icon: 'devices', tone: 'gold' },\n { id: 'diagnostics', label: 'Telemetry', value: 'Live signals', icon: 'waveform', tone: 'red' },\n];\n\nconst DEFAULT_VIDEO_TOOL_CARDS: VideoUiToolCard[] = OFFLINE_VIDEO_TOOL_CARDS;\n\nconst OFFLINE_VIDEO_CARDS: VideoUiMediaCard[] = [\n {\n id: 'offline-library',\n title: 'Local video library',\n subtitle: 'Folders, files, resume',\n meta: 'Offline workspace',\n },\n {\n id: 'tracks',\n title: 'Track inspection',\n subtitle: 'Audio, video, subtitles',\n meta: 'Local media',\n },\n {\n id: 'offline',\n title: 'Resume sessions',\n subtitle: 'Progress and continuity',\n meta: 'Persisted locally',\n },\n {\n id: 'devices',\n title: 'Device playback',\n subtitle: 'DLNA / UPnP',\n meta: 'Local network',\n },\n];\n\nconst ONLINE_VIDEO_CARDS: VideoUiMediaCard[] = [\n {\n id: 'vod',\n title: 'Video on demand',\n subtitle: 'Catalog VOD and featured rails',\n meta: 'Online catalog',\n },\n {\n id: 'live',\n title: 'Live playback',\n subtitle: 'Latency-aware streams',\n meta: 'Live source',\n },\n {\n id: 'online-catalog',\n title: 'Remote catalog',\n subtitle: 'JSON configuration and feeds',\n meta: 'Configure',\n },\n {\n id: 'devices',\n title: 'Multi-device playback',\n subtitle: 'Cast, tracks, subtitles',\n meta: 'Delivery tools',\n },\n];\n\nconst WAVEFORM_BARS = [\n 22, 42, 28, 64, 35, 78, 48, 30, 58, 74, 44, 88,\n 52, 36, 66, 46, 76, 54, 32, 62, 84, 48, 28, 56,\n 70, 38, 80, 50, 30, 64, 42, 72, 58, 34, 86, 46,\n];\n\n@Component({\n selector: 'ngxsp-video-player-ui',\n standalone: true,\n imports: [CommonModule, SmartPlayerComponent, SmartPlayerIconComponent, SmartSettingsComponent, NgxspVolumeControl],\n template: `\n <section\n class=\"ngxsp-yt-shell\"\n [class.ngxsp-yt-online]=\"runtimeMode === 'online'\"\n [class.ngxsp-yt-offline]=\"runtimeMode === 'offline'\"\n [class.ngxsp-yt-playing]=\"isPlaying\"\n [class.ngxsp-yt-playerView]=\"playerView\"\n [class.ngxsp-yt-cinema]=\"cinemaMode\"\n [class.ngxsp-has-source]=\"!!config\"\n [class.ngxsp-queue-hidden]=\"!queueVisible\"\n [class.ngxsp-mini-hidden]=\"!miniPlayerVisible\"\n [class.ngxsp-yt-dragActive]=\"fileDragActive\"\n [class.ngxsp-yt-library-focus]=\"isLibraryFocused\"\n [class.ngxsp-yt-menuRow]=\"menuLayout === 'horizontal'\"\n [class.ngxsp-yt-menuOff]=\"menuLayout === 'hidden'\"\n [class.ngxsp-yt-railExpanded]=\"railExpanded\"\n [class.ngxsp-yt-compact]=\"compactMode === 'mini'\"\n (dragenter)=\"onFileDragEnter($event)\"\n (dragover)=\"onFileDragOver($event)\"\n (dragleave)=\"onFileDragLeave($event)\"\n (drop)=\"onFileDrop($event)\"\n >\n @if (fileDragActive) {\n <div class=\"ngxsp-yt-dropOverlay\" aria-hidden=\"true\">\n <span class=\"ngxsp-yt-dropIcon\"><ngxsp-smart-icon name=\"folder\" /></span>\n <strong>{{ sourceDropTitle || sourceChangeTitle || sourceChangeLabel }}</strong>\n @if (sourceDropHint) {\n <span>{{ sourceDropHint }}</span>\n }\n </div>\n }\n\n @if (promptBar) {\n <div class=\"ngxsp-yt-convert\" [class.asking]=\"promptAsking\" [class.danger]=\"!!promptBar.danger\" [attr.role]=\"promptAsking ? 'alertdialog' : 'status'\" aria-live=\"polite\">\n <span class=\"ngxsp-yt-convertIcon\" aria-hidden=\"true\"><ngxsp-smart-icon [name]=\"promptBar.danger ? 'trash' : 'download'\" /></span>\n <span class=\"ngxsp-yt-convertText\">\n <strong>{{ promptBar.message }}</strong>\n @if (promptBar.hint) { <small>{{ promptBar.hint }}</small> }\n @if (promptBar.percent !== undefined) {\n <em role=\"progressbar\" [attr.aria-valuenow]=\"safeProgressPercent(promptBar.percent)\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <i [style.width.%]=\"safeProgressPercent(promptBar.percent)\"></i>\n </em>\n }\n </span>\n @if (promptAsking) {\n <span class=\"ngxsp-yt-convertActions\">\n <button type=\"button\" class=\"keep\" (click)=\"promptAnswer.emit(true)\">{{ promptBar.confirmLabel }}</button>\n <button type=\"button\" (click)=\"promptAnswer.emit(false)\">{{ promptBar.cancelLabel }}</button>\n </span>\n }\n </div>\n }\n\n @if (cardMenu) {\n <button\n class=\"ngxsp-yt-cardMenuScrim\"\n type=\"button\"\n [attr.aria-label]=\"cardMenuCloseLabel\"\n (click)=\"closeCardMenu()\"\n (contextmenu)=\"$event.preventDefault(); closeCardMenu()\"\n ></button>\n <div\n #cardMenuBox\n class=\"ngxsp-yt-cardMenu\"\n role=\"menu\"\n tabindex=\"-1\"\n [attr.aria-label]=\"cardMenu.card.title\"\n [style.left.px]=\"cardMenu.x\"\n [style.top.px]=\"cardMenu.y\"\n (keydown.escape)=\"closeCardMenu()\"\n >\n <strong class=\"ngxsp-yt-cardMenuTitle\">{{ cardMenu.card.title }}</strong>\n @for (action of cardMenu.card.actions; track action.id) {\n <button type=\"button\" role=\"menuitem\" [class.danger]=\"!!action.danger\" (click)=\"chooseCardAction(action)\">\n <ngxsp-smart-icon [name]=\"action.icon\" />\n <span>{{ action.label }}</span>\n </button>\n }\n </div>\n }\n\n <header class=\"ngxsp-yt-topbar\" data-tauri-drag-region>\n <div class=\"ngxsp-yt-brand\">\n <button class=\"ngxsp-yt-railMenu\" type=\"button\" [attr.aria-expanded]=\"railExpanded\" [attr.aria-label]=\"menuLabel\" [title]=\"menuLabel\" (click)=\"toggleRail()\">\n <ngxsp-smart-icon name=\"menu\" />\n </button>\n @if (brandLogoSrc) {\n <img class=\"ngxsp-yt-brandLogo\" [src]=\"brandLogoSrc\" alt=\"\" width=\"26\" height=\"26\" />\n } @else {\n <span class=\"ngxsp-yt-brandIcon\" aria-hidden=\"true\"><ngxsp-smart-icon name=\"play\" /></span>\n }\n <strong>NGXSP Video</strong>\n </div>\n\n <label class=\"ngxsp-yt-search\">\n <ngxsp-smart-icon name=\"search\" />\n <input type=\"search\" [placeholder]=\"searchPlaceholder\" [value]=\"searchTerm\" (input)=\"onSearchInput($event)\" (focus)=\"searchOpen = true\" [attr.aria-label]=\"searchLabel\" />\n </label>\n\n <div class=\"ngxsp-yt-actions\">\n @if (statusMessage) {\n <span class=\"ngxsp-yt-status\" [class.success]=\"statusKind === 'success'\" [class.error]=\"statusKind === 'error'\" [attr.role]=\"statusKind === 'error' ? 'alert' : 'status'\" [attr.aria-live]=\"statusKind === 'error' ? 'assertive' : 'polite'\" aria-atomic=\"true\" [title]=\"statusMessage\">\n {{ statusMessage }}\n </span>\n }\n @if (runtimeModeInHeader) {\n <div class=\"ngxsp-yt-mode\" role=\"group\" [attr.aria-label]=\"runtimeModeLabel\">\n <button type=\"button\" [class.active]=\"runtimeMode === 'online'\" [attr.aria-pressed]=\"runtimeMode === 'online'\" (click)=\"selectRuntimeMode('online')\"><i aria-hidden=\"true\"></i>{{ onlineLabel }}</button>\n <button type=\"button\" [class.active]=\"runtimeMode === 'offline'\" [attr.aria-pressed]=\"runtimeMode === 'offline'\" (click)=\"selectRuntimeMode('offline')\"><i aria-hidden=\"true\"></i>{{ offlineLabel }}</button>\n </div>\n }\n @if (folderImportLabel) {\n <!-- A folder is what builds the library; picking a single file next to it led nowhere. -->\n <button class=\"ngxsp-yt-sourceButton\" type=\"button\" [disabled]=\"libraryBusy\" [attr.aria-label]=\"folderImportLabel\" [title]=\"folderImportLabel\" (click)=\"addFolder.emit()\">\n <ngxsp-smart-icon name=\"folder\" />\n <span>{{ folderImportLabel }}</span>\n </button>\n } @else {\n <button class=\"ngxsp-yt-sourceButton\" type=\"button\" [attr.aria-label]=\"sourceChangeTitle || sourceChangeLabel\" [title]=\"sourceChangeTitle || sourceChangeLabel\" (click)=\"videoFileInput.click()\">\n <ngxsp-smart-icon name=\"folder\" />\n <span>{{ sourceChangeLabel }}</span>\n </button>\n }\n <input #videoFileInput class=\"ngxsp-yt-fileInput\" type=\"file\" [accept]=\"sourceChangeAccept\" aria-hidden=\"true\" tabindex=\"-1\" (change)=\"onSourceFileInput($event)\" />\n @if (languageOptions.length) {\n <div class=\"ngxsp-yt-languageSwitch\" role=\"group\" [attr.aria-label]=\"languageLabel\">\n @for (language of languageOptions; track language.id) {\n <button type=\"button\" [class.active]=\"language.id === activeLanguage\" [attr.aria-pressed]=\"language.id === activeLanguage\" (click)=\"languageChange.emit(language.id)\">\n {{ language.label }}\n </button>\n }\n </div>\n }\n @if (compactModes && compactMode === 'full') {\n <button class=\"ngxsp-yt-round ngxsp-yt-windowButton\" type=\"button\" [attr.aria-label]=\"compactMiniLabel\" [title]=\"compactMiniLabel\" (click)=\"compactModeChange.emit('mini')\">\n <ngxsp-smart-icon name=\"window-mini\" />\n </button>\n }\n <button class=\"ngxsp-yt-round ngxsp-yt-queueButton\" type=\"button\" [class.active]=\"queuePanelActive\" [attr.aria-pressed]=\"queuePanelActive\" [attr.aria-label]=\"queueActionLabel\" [title]=\"queueActionLabel\" (click)=\"onQueueAction()\">\n <ngxsp-smart-icon name=\"panel-right\" />\n </button>\n <button class=\"ngxsp-yt-round ngxsp-yt-notificationsButton\" type=\"button\" [attr.aria-label]=\"notificationsLabel\" [title]=\"notificationsLabel\" (click)=\"toolSelect.emit('notifications')\">\n <ngxsp-smart-icon name=\"bell\" />\n </button>\n <button class=\"ngxsp-yt-round\" type=\"button\" [attr.aria-label]=\"settingsLabel\" [title]=\"settingsLabel\" (click)=\"toolSelect.emit('settings')\">\n <ngxsp-smart-icon name=\"settings\" />\n </button>\n </div>\n </header>\n\n <aside class=\"ngxsp-yt-rail\" [attr.aria-label]=\"navigationLabel\">\n @for (item of navItems; track item.id) {\n <button class=\"ngxsp-yt-railButton\" type=\"button\" [class.active]=\"item.active\" [disabled]=\"item.disabled\" [attr.aria-current]=\"item.active ? 'page' : null\" [attr.aria-label]=\"item.label\" [title]=\"item.label\" (click)=\"selectNav(item)\">\n <ngxsp-smart-icon [name]=\"item.icon\" />\n <span>{{ item.label }}</span>\n </button>\n }\n </aside>\n\n <div class=\"ngxsp-yt-body\" [class.ngxsp-yt-playing]=\"playerView\" [class.ngxsp-yt-sources]=\"!showActiveMedia\" [class.ngxsp-yt-hasInfoTabs]=\"infoTabs.length > 0\" [class.ngxsp-yt-sectionsBeside]=\"sidePanelContent === 'sections' && queueVisible\">\n <section #contentScroll class=\"ngxsp-yt-watch\" [class.player]=\"playerView\" [attr.aria-label]=\"displayHeroTitle\">\n <!-- Watching: the player has the whole area. Browsing: the area belongs to the view being read. -->\n @if (playerView) {\n <section class=\"ngxsp-yt-playerShell\">\n @if (playerView) {\n <button class=\"ngxsp-yt-playerBack\" type=\"button\" [attr.aria-label]=\"playerBackLabel\" [title]=\"playerBackLabel\" (click)=\"playerBack.emit()\">\n <ngxsp-smart-icon name=\"chevron-left\" />\n <span>{{ playerBackLabel }}</span>\n </button>\n }\n <div\n class=\"ngxsp-yt-playerFrame\"\n [class.empty]=\"!config\"\n [class.ngxsp-yt-infoOpen]=\"!!infoPanel\"\n (pointerdown)=\"onFramePointerDown($event)\"\n (pointermove)=\"onFramePointerMove($event)\"\n (pointerup)=\"onFramePointerUp($event)\"\n (pointercancel)=\"onFramePointerUp($event)\"\n >\n @if (brightness < 100) {\n <span class=\"ngxsp-yt-veil\" aria-hidden=\"true\" [style.opacity]=\"(100 - brightness) / 100\"></span>\n }\n @if (touchAdjusting; as adjusting) {\n <span class=\"ngxsp-yt-touchBadge\" role=\"status\" aria-live=\"polite\">\n <ngxsp-smart-icon [name]=\"adjusting.kind === 'brightness' ? 'spark' : adjusting.value === 0 ? 'volume-mute' : adjusting.value < 50 ? 'volume-low' : 'volume-high'\" />\n <strong>{{ adjusting.value }}%</strong>\n <em><i [style.width.%]=\"adjusting.value\"></i></em>\n </span>\n }\n @if (compactMode === 'mini') {\n <!-- A mini window has no title bar: this strip moves it. -->\n <div class=\"ngxsp-yt-compactDrag\" aria-hidden=\"true\"></div>\n }\n @if (showPosterPreview) {\n <button class=\"ngxsp-yt-previewPlayer\" type=\"button\" [attr.aria-label]=\"playLabel + ' - ' + displayHeroTitle\" (click)=\"togglePrimaryPlayback()\">\n <img [src]=\"displayHeroArtwork\" alt=\"\" />\n <span class=\"ngxsp-yt-previewOverlay\" aria-hidden=\"true\">\n <span class=\"ngxsp-yt-previewPlay\"><ngxsp-smart-icon name=\"play\" /></span>\n </span>\n </button>\n } @else if (config) {\n <ngx-smart-player\n [config]=\"config\"\n [plugins]=\"plugins\"\n [ports]=\"ports\"\n [engines]=\"engines\"\n [debug]=\"debug\"\n [startTimeSec]=\"startTimeSec\"\n [playRequest]=\"playRequest\"\n [canPrev]=\"canPrev\"\n [canNext]=\"canNext\"\n [previousLabel]=\"previousLabel\"\n [nextLabel]=\"nextLabel\"\n [playLabel]=\"playLabel\"\n [pauseLabel]=\"pauseLabel\"\n [shuffleActive]=\"shuffleActive\"\n [shuffleLabel]=\"shuffleLabel\"\n [repeatMode]=\"repeatMode\"\n [repeatLabel]=\"repeatLabel\"\n [hasPlaylistControls]=\"hasPlaylistControls\"\n [mediaMode]=\"'video'\"\n [castEnabled]=\"castEnabled\"\n [castConnected]=\"castConnected\"\n [menuLabels]=\"playerMenuLabels\"\n [documentShortcuts]=\"false\"\n [nowPlayingLabel]=\"nowPlayingLabel\"\n [fullscreenHintLabel]=\"fullscreenHintLabel\"\n [fullscreenLabel]=\"fullscreenLabel\"\n [readyStatusLabel]=\"readyStatusLabel\"\n [playingStatusLabel]=\"playingStatusLabel\"\n [pausedStatusLabel]=\"pausedStatusLabel\"\n [bufferingStatusLabel]=\"bufferingStatusLabel\"\n [loadingStatusLabel]=\"loadingStatusLabel\"\n [endedStatusLabel]=\"endedStatusLabel\"\n [errorStatusLabel]=\"errorStatusLabel\"\n [outputVolume]=\"outputVolume / 100\"\n [outputVolumeLabel]=\"outputVolumeLabel\"\n [outputMuteLabel]=\"outputMuteLabel\"\n [outputUnmuteLabel]=\"outputUnmuteLabel\"\n [timelineDurationSec]=\"timelineDurationSec\"\n [timelineMarkers]=\"timelineMarkers\"\n (ended)=\"ended.emit()\"\n (videoSize)=\"videoSize.emit($event)\"\n (progress)=\"progress.emit($event)\"\n (prev)=\"prev.emit()\"\n (next)=\"next.emit()\"\n (toggleShuffle)=\"toggleShuffle.emit()\"\n (cycleRepeat)=\"cycleRepeat.emit()\"\n (cast)=\"cast.emit()\"\n (subtitlesLoaded)=\"subtitlesLoaded.emit($event)\"\n (stateChange)=\"onPlayerStateChange($event)\"\n (playbackToggle)=\"primaryPlaybackToggle.emit($event)\"\n (outputVolumeChange)=\"onOutputVolumeRatioChange($event)\"\n >\n <div ngxsp-player-inline-controls class=\"ngxsp-yt-playerInlineControls\">\n <!-- What a viewer does about the video they are watching, without leaving it. -->\n @if (watchActionsInPlayer && compactMode !== 'mini') {\n <button class=\"ngxsp-yt-playerAction\" type=\"button\" [class.active]=\"likeActive\" [attr.aria-pressed]=\"likeActive\" [attr.aria-label]=\"likeLabel\" [title]=\"likeLabel\" (click)=\"toolSelect.emit('like')\">\n <ngxsp-smart-icon name=\"thumb-up\" />\n </button>\n <button class=\"ngxsp-yt-playerAction\" type=\"button\" [class.active]=\"watchLaterActive\" [attr.aria-pressed]=\"watchLaterActive\" [attr.aria-label]=\"watchLaterLabel\" [title]=\"watchLaterLabel\" (click)=\"watchLaterToggle.emit()\">\n <ngxsp-smart-icon name=\"clock\" />\n </button>\n <button class=\"ngxsp-yt-playerAction\" type=\"button\" [attr.aria-label]=\"queueAddActiveLabel\" [title]=\"queueAddActiveLabel\" (click)=\"queueAddActive.emit()\">\n <ngxsp-smart-icon name=\"playlist-plus\" />\n </button>\n <button class=\"ngxsp-yt-playerAction\" type=\"button\" [attr.aria-label]=\"shareLabel\" [title]=\"shareLabel\" (click)=\"toolSelect.emit('share')\">\n <ngxsp-smart-icon name=\"share\" />\n </button>\n }\n @if (compactMode === 'mini') {\n <button class=\"ngxsp-yt-playerAction\" type=\"button\" [attr.aria-label]=\"compactRestoreLabel\" [title]=\"compactRestoreLabel\" (click)=\"compactModeChange.emit('full')\">\n <ngxsp-smart-icon name=\"window-restore\" />\n </button>\n } @else if (compactModes) {\n <button class=\"ngxsp-yt-playerAction\" type=\"button\" [attr.aria-label]=\"compactMiniLabel\" [title]=\"compactMiniLabel\" (click)=\"compactModeChange.emit('mini')\">\n <ngxsp-smart-icon name=\"window-mini\" />\n </button>\n }\n <button class=\"ngxsp-yt-playerAction\" type=\"button\" [class.active]=\"cinemaMode\" [attr.aria-pressed]=\"cinemaMode\" [attr.aria-label]=\"cinemaButtonLabel\" [title]=\"cinemaButtonLabel\" (click)=\"toggleCinema.emit()\">\n <ngxsp-smart-icon [name]=\"cinemaMode ? 'cinema-active' : 'cinema'\" />\n </button>\n <button class=\"ngxsp-yt-playerAction ngxsp-yt-queueButton\" type=\"button\" [class.active]=\"queuePanelActive\" [attr.aria-expanded]=\"queuePanelActive\" [attr.aria-label]=\"queueActionLabel\" [title]=\"queueActionLabel\" (click)=\"onQueueAction()\">\n <ngxsp-smart-icon name=\"panel-right\" />\n </button>\n @if (infoTabs.length) {\n <button class=\"ngxsp-yt-playerAction ngxsp-yt-infoButton\" type=\"button\" [class.active]=\"!!infoPanel\" [attr.aria-pressed]=\"!!infoPanel\" [attr.aria-label]=\"infoLabel\" [title]=\"infoLabel\" (click)=\"infoToggle.emit()\">\n <ngxsp-smart-icon name=\"info\" />\n </button>\n }\n </div>\n </ngx-smart-player>\n } @else {\n <div class=\"ngxsp-yt-emptyPlayer\">\n <button class=\"ngxsp-yt-bigPlay\" type=\"button\" [attr.aria-label]=\"primarySourceLabel\" (click)=\"isOfflineMode ? videoFileInput.click() : onPrimarySourceAction()\">\n <ngxsp-smart-icon [name]=\"primarySourceIcon\" />\n </button>\n <span>{{ emptyKicker }}</span>\n <strong>{{ emptyTitle }}</strong>\n <p>{{ emptyBody }}</p>\n </div>\n }\n @if (resumePromptShown && resumeOffer && config && !showPosterPreview) {\n <div class=\"ngxsp-yt-resumePrompt\" role=\"group\" [attr.aria-label]=\"resumeOffer.message\">\n <span>{{ resumeOffer.message }}</span>\n <button class=\"primary\" type=\"button\" (click)=\"chooseResume('resume')\">{{ resumeOffer.resumeLabel }}</button>\n <button type=\"button\" (click)=\"chooseResume('restart')\">{{ resumeOffer.restartLabel }}</button>\n </div>\n }\n @if (infoPanel; as panel) {\n <!-- Info panels float over the right of the picture; on phones they open in the sheet instead. -->\n <section class=\"ngxsp-yt-infoOverlay\" [attr.aria-label]=\"panel.title\">\n <div class=\"ngxsp-yt-infoTabs\" role=\"tablist\" [attr.aria-label]=\"infoLabel\">\n @for (tab of infoTabs; track tab.id) {\n <button type=\"button\" role=\"tab\" [class.active]=\"tab.id === panel.id\" [attr.aria-selected]=\"tab.id === panel.id\" [title]=\"tab.label\" (click)=\"infoTabSelect.emit(tab.id)\">\n <ngxsp-smart-icon [name]=\"tab.icon\" />\n <span>{{ tab.label }}</span>\n </button>\n }\n <button class=\"ngxsp-yt-infoClose\" type=\"button\" [attr.aria-label]=\"inspectorCloseLabel\" [title]=\"inspectorCloseLabel\" (click)=\"closeInspector.emit()\">\n <ngxsp-smart-icon name=\"close\" />\n </button>\n </div>\n <div class=\"ngxsp-yt-infoHead\" role=\"tabpanel\">\n <strong>{{ panel.title }}</strong>\n @if (panel.subtitle) { <small>{{ panel.subtitle }}</small> }\n </div>\n <ng-container *ngTemplateOutlet=\"panelBody; context: { $implicit: panel }\" />\n </section>\n }\n @if (showPosterPreview || !config) {\n <section class=\"ngxsp-yt-transportBar\" [attr.aria-label]=\"transportLabel\">\n <div class=\"ngxsp-yt-transportCluster primary\">\n <button class=\"ngxsp-yt-transportButton\" type=\"button\" [disabled]=\"!canPrev\" [attr.aria-label]=\"previousLabel\" [title]=\"previousLabel\" (click)=\"prev.emit()\">\n <ngxsp-smart-icon name=\"prev\" />\n </button>\n <button class=\"ngxsp-yt-transportPlay\" type=\"button\" [attr.aria-label]=\"isPlaying ? pauseLabel : playLabel\" [title]=\"isPlaying ? pauseLabel : playLabel\" (click)=\"togglePrimaryPlayback()\">\n @if (isPlaying) { <ngxsp-smart-icon name=\"pause\" /> } @else { <ngxsp-smart-icon name=\"play\" /> }\n </button>\n <button class=\"ngxsp-yt-transportButton\" type=\"button\" [disabled]=\"!canNext\" [attr.aria-label]=\"nextLabel\" [title]=\"nextLabel\" (click)=\"next.emit()\">\n <ngxsp-smart-icon name=\"next\" />\n </button>\n </div>\n <div class=\"ngxsp-yt-transportInfo\" aria-hidden=\"true\">\n <span>{{ heroEyebrow }}</span>\n <strong>{{ displayHeroTitle }}</strong>\n </div>\n <div class=\"ngxsp-yt-transportCluster secondary\">\n <button class=\"ngxsp-yt-transportButton\" type=\"button\" [class.active]=\"repeatMode !== 'off'\" [attr.aria-pressed]=\"repeatMode !== 'off'\" [attr.aria-label]=\"repeatLabel\" [title]=\"repeatLabel\" (click)=\"cycleRepeat.emit()\">\n <ngxsp-smart-icon [name]=\"repeatMode === 'one' ? 'repeat-one' : 'repeat'\" />\n </button>\n <ngxsp-volume-control\n class=\"ngxsp-yt-volumeControl\"\n [volume]=\"outputVolume / 100\"\n [step]=\"0.05\"\n [volumeLabel]=\"outputVolumeLabel\"\n [muteLabel]=\"outputMuteLabel\"\n [unmuteLabel]=\"outputUnmuteLabel\"\n (muteToggle)=\"outputMuteToggle.emit()\"\n (volumeChange)=\"onOutputVolumeRatioChange($event)\"\n />\n <button class=\"ngxsp-yt-transportButton ngxsp-yt-transportCinema\" type=\"button\" [class.active]=\"cinemaMode\" [attr.aria-pressed]=\"cinemaMode\" [attr.aria-label]=\"cinemaButtonLabel\" [title]=\"cinemaButtonLabel\" (click)=\"toggleCinema.emit()\">\n <ngxsp-smart-icon [name]=\"cinemaMode ? 'cinema-active' : 'cinema'\" />\n </button>\n <button class=\"ngxsp-yt-transportButton ngxsp-yt-queueButton\" type=\"button\" [class.active]=\"queuePanelActive\" [attr.aria-expanded]=\"queuePanelActive\" [attr.aria-label]=\"queueActionLabel\" [title]=\"queueActionLabel\" (click)=\"onQueueAction()\">\n <ngxsp-smart-icon name=\"panel-right\" />\n </button>\n @if (compactMode === 'mini') {\n <button class=\"ngxsp-yt-transportButton\" type=\"button\" [attr.aria-label]=\"compactRestoreLabel\" [title]=\"compactRestoreLabel\" (click)=\"compactModeChange.emit('full')\">\n <ngxsp-smart-icon name=\"window-restore\" />\n </button>\n } @else if (compactModes) {\n <button class=\"ngxsp-yt-transportButton\" type=\"button\" [attr.aria-label]=\"compactMiniLabel\" [title]=\"compactMiniLabel\" (click)=\"compactModeChange.emit('mini')\">\n <ngxsp-smart-icon name=\"window-mini\" />\n </button>\n }\n </div>\n </section>\n }\n </div>\n </section>\n } @else {\n\n <!-- What is being watched. A home view opens on where videos come from instead. -->\n @if (showActiveMedia) {\n <section class=\"ngxsp-yt-metaBar\">\n <div class=\"ngxsp-yt-titleGroup\" [attr.aria-label]=\"displayHeroTitle + ' - ' + displayHeroSubtitle\">\n <h1>{{ displayHeroTitle }}</h1>\n @if (displayHeroSubtitle) { <p>{{ displayHeroSubtitle }}</p> }\n @if (displayHeroMeta) { <small>{{ displayHeroMeta }}</small> }\n </div>\n <div class=\"ngxsp-yt-watchActions\">\n <span class=\"ngxsp-yt-rating\">\n <button type=\"button\" [class.active]=\"likeActive\" [attr.aria-pressed]=\"likeActive\" [attr.aria-label]=\"likeLabel\" [title]=\"likeLabel\" (click)=\"toolSelect.emit('like')\"><ngxsp-smart-icon name=\"thumb-up\" /><span>{{ likeLabel }}</span></button>\n <button type=\"button\" [attr.aria-label]=\"dislikeLabel\" [title]=\"dislikeLabel\" (click)=\"toolSelect.emit('dislike')\"><ngxsp-smart-icon name=\"thumb-down\" /><span>{{ dislikeLabel }}</span></button>\n </span>\n <button type=\"button\" [attr.aria-label]=\"shareLabel\" [title]=\"shareLabel\" (click)=\"toolSelect.emit('share')\"><ngxsp-smart-icon name=\"share\" /><span>{{ shareLabel }}</span></button>\n <button type=\"button\" [class.active]=\"watchLaterActive\" [attr.aria-pressed]=\"watchLaterActive\" [attr.aria-label]=\"watchLaterLabel\" [title]=\"watchLaterLabel\" (click)=\"watchLaterToggle.emit()\"><ngxsp-smart-icon name=\"clock\" /><span>{{ watchLaterLabel }}</span></button>\n <button type=\"button\" [attr.aria-label]=\"queueAddActiveLabel\" [title]=\"queueAddActiveLabel\" (click)=\"queueAddActive.emit()\"><ngxsp-smart-icon name=\"playlist-plus\" /><span>{{ queueAddActiveShortLabel }}</span></button>\n @if (revealFileLabel) {\n <button type=\"button\" [attr.aria-label]=\"revealFileLabel\" [title]=\"revealFileLabel\" (click)=\"revealFile.emit()\"><ngxsp-smart-icon name=\"folder\" /><span>{{ revealFileLabel }}</span></button>\n }\n <button type=\"button\" class=\"ngxsp-yt-moreAction\" [attr.aria-label]=\"moreLabel\" [title]=\"moreLabel\" (click)=\"toolSelect.emit('more')\"><ngxsp-smart-icon name=\"more\" /><span>{{ moreShortLabel }}</span></button>\n </div>\n </section>\n }\n\n <!-- Phones: the queue is a sheet, and this row is the one place that opens it. -->\n <button class=\"ngxsp-yt-queueButton ngxsp-yt-queueOpen\" type=\"button\" [attr.aria-expanded]=\"queueSheetOpen\" [title]=\"queueActionLabel\" (click)=\"onQueueAction()\">\n <ngxsp-smart-icon name=\"panel-right\" />\n <span>{{ sidePanelTitle }}</span>\n <small>{{ sidePanelCount }}</small>\n </button>\n\n @if (toolCards.length) {\n <section class=\"ngxsp-yt-toolRow\">\n @for (tool of toolCards; track tool.id) {\n <button class=\"ngxsp-yt-tool\" type=\"button\" [class.active]=\"tool.active\" [attr.data-tool]=\"tool.id\" [attr.data-tone]=\"tool.tone || 'neutral'\" (click)=\"toolSelect.emit(tool.id)\">\n <span class=\"ngxsp-yt-toolIcon\" aria-hidden=\"true\"><ngxsp-smart-icon [name]=\"tool.icon\" /></span>\n <span class=\"ngxsp-yt-toolCopy\"><small>{{ tool.label }}</small><strong>{{ tool.value }}</strong></span>\n </button>\n }\n </section>\n }\n\n @if (hasSearchQuery) {\n <section class=\"ngxsp-yt-searchWorkspace\" [attr.aria-label]=\"searchResultsTitle\">\n <div class=\"ngxsp-yt-searchHead\">\n <div>\n <strong>{{ searchResultsTitle }}</strong>\n @if (searchResultsSummary) { <small>{{ searchResultsSummary }}</small> }\n </div>\n <button type=\"button\" [attr.aria-label]=\"searchClearLabel\" [title]=\"searchClearLabel\" (click)=\"clearSearch()\">\n <ngxsp-smart-icon name=\"close\" />\n </button>\n </div>\n @if (searchFilters.length) {\n <div class=\"ngxsp-yt-searchFilters\" role=\"group\" [attr.aria-label]=\"searchFilterLabel\">\n @for (filter of searchFilters; track filter.id) {\n <button type=\"button\" [class.active]=\"filter.active\" [disabled]=\"filter.disabled\" [attr.aria-pressed]=\"!!filter.active\" (click)=\"selectSearchFilter(filter)\">\n <span>{{ filter.label }}</span>\n @if (filter.count !== undefined) { <small>{{ filter.count }}</small> }\n </button>\n }\n </div>\n }\n <div class=\"ngxsp-yt-searchResults\" [class.list]=\"activeViewMode === 'list'\">\n @for (card of searchResultItems; track card.sectionId + ':' + card.item.id) {\n <article class=\"ngxsp-yt-searchResult\" [class.active]=\"card.item.active\" [attr.data-art]=\"artVariant(card)\">\n <button class=\"ngxsp-yt-searchResultMain\" type=\"button\" [attr.aria-current]=\"card.item.active ? 'true' : null\" [attr.aria-label]=\"searchResultActionLabel('play', card)\" [title]=\"searchResultActionLabel('play', card)\" (click)=\"selectCard(card)\">\n <span class=\"ngxsp-yt-searchThumb\" [class.has-image]=\"!!card.item.artworkUrl\">\n @if (card.item.artworkUrl) {\n <img [src]=\"card.item.artworkUrl\" alt=\"\" />\n } @else {\n <span class=\"ngxsp-yt-art\" aria-hidden=\"true\"><ngxsp-smart-icon name=\"video\" /></span>\n }\n @if (card.item.duration && !card.item.artworkUrl) { <small>{{ card.item.duration }}</small> }\n </span>\n <span class=\"ngxsp-yt-searchCopy\">\n @if (card.item.kindLabel || card.item.episodeLabel) { <small>{{ card.item.episodeLabel || card.item.kindLabel }}</small> }\n <strong>{{ card.item.title }}</strong>\n @if (card.item.subtitle) { <em>{{ card.item.subtitle }}</em> }\n @if (mediaCardMeta(card.item)) { <span>{{ mediaCardMeta(card.item) }}</span> }\n </span>\n </button>\n <span class=\"ngxsp-yt-searchActions\">\n <button type=\"button\" [attr.aria-label]=\"searchResultActionLabel('queue', card)\" [title]=\"searchResultActionLabel('queue', card)\" (click)=\"searchResultQueueAdd.emit(card.item.id)\">\n <ngxsp-smart-icon name=\"playlist-plus\" />\n </button>\n <button type=\"button\" [attr.aria-label]=\"searchResultActionLabel('watchLater', card)\" [title]=\"searchResultActionLabel('watchLater', card)\" (click)=\"searchResultWatchLater.emit(card.item.id)\">\n <ngxsp-smart-icon name=\"clock\" />\n </button>\n <button type=\"button\" [attr.aria-label]=\"searchResultActionLabel('favorite', card)\" [title]=\"searchResultActionLabel('favorite', card)\" (click)=\"searchResultFavorite.emit(card.item.id)\">\n <ngxsp-smart-icon name=\"heart\" />\n </button>\n <button type=\"button\" [attr.aria-label]=\"searchResultActionLabel('download', card)\" [title]=\"searchResultActionLabel('download', card)\" (click)=\"searchResultDownload.emit(card.item.id)\">\n <ngxsp-smart-icon name=\"download\" />\n </button>\n </span>\n </article>\n } @empty {\n <div class=\"ngxsp-yt-searchEmpty\">\n <ngxsp-smart-icon name=\"search\" />\n <strong>{{ searchResultsEmptyTitle }}</strong>\n <span>{{ searchResultsEmptyBody }}</span>\n </div>\n }\n </div>\n </section>\n }\n\n @if (showActiveMedia) {\n @if (mediaDetail; as detail) {\n <section class=\"ngxsp-yt-mediaDetail\" [attr.aria-label]=\"detail.title\">\n <div class=\"ngxsp-yt-mediaHero\" [class.echoesHeader]=\"detailEchoesHeader\">\n <div class=\"ngxsp-yt-mediaPoster\" [class.has-image]=\"!!detail.posterUrl\">\n @if (detail.posterUrl) {\n <img [src]=\"detail.posterUrl\" alt=\"\" />\n } @else {\n <span>{{ posterInitial(detail.title) }}</span>\n }\n </div>\n <div class=\"ngxsp-yt-mediaDetailBody\">\n <span class=\"ngxsp-yt-mediaKicker\">{{ detail.kicker || detail.kindLabel || heroEyebrow }}</span>\n <h2>{{ detail.title }}</h2>\n @if (detail.subtitle) { <p class=\"ngxsp-yt-mediaSubtitle\">{{ detail.subtitle }}</p> }\n @if (detail.metaItems?.length) {\n <div class=\"ngxsp-yt-mediaMeta\">\n @for (meta of detail.metaItems; track meta) {\n <span>{{ meta }}</span>\n }\n </div>\n }\n @if (detail.summary) { <p class=\"ngxsp-yt-mediaSummary\">{{ detail.summary }}</p> }\n @if (showResumeProgress && detail.progressPercent !== undefined) {\n <div class=\"ngxsp-yt-mediaProgress\">\n <span><i [style.width.%]=\"safeProgressPercent(detail.progressPercent)\"></i></span>\n @if (detail.progressLabel) { <small>{{ detail.progressLabel }}</small> }\n </div>\n }\n @if (detail.tags?.length) {\n <div class=\"ngxsp-yt-mediaTags\">\n @for (tag of detail.tags; track tag) {\n <span>{{ tag }}</span>\n }\n </div>\n }\n @if (detail.actions?.length) {\n <div class=\"ngxsp-yt-mediaActions\">\n @for (action of detail.actions; track action.id) {\n <button type=\"button\" [class.primary]=\"action.primary\" [disabled]=\"action.disabled\" (click)=\"detailAction.emit(action.id)\">\n <ngxsp-smart-icon [name]=\"action.icon\" />\n <span>{{ action.label }}</span>\n </button>\n }\n </div>\n }\n </div>\n </div>\n @if (detail.chapters?.length || detail.checklist?.length) {\n <div class=\"ngxsp-yt-mediaWorkspace\">\n @if (detail.workspaceTitle || detail.workspaceSummary) {\n <div class=\"ngxsp-yt-mediaWorkspaceHead\">\n @if (detail.workspaceTitle) { <strong>{{ detail.workspaceTitle }}</strong> }\n @if (detail.workspaceSummary) { <small>{{ detail.workspaceSummary }}</small> }\n </div>\n }\n <div class=\"ngxsp-yt-mediaWorkspaceGrid\">\n @if (detail.chapters?.length) {\n <div class=\"ngxsp-yt-chapterPanel\">\n @if (detail.chaptersTitle) { <strong class=\"ngxsp-yt-panelTitle\">{{ detail.chaptersTitle }}</strong> }\n <div class=\"ngxsp-yt-chapterList\">\n @for (chapter of detail.chapters; track chapter.id) {\n <button type=\"button\" class=\"ngxsp-yt-mediaChapter\" [class.active]=\"chapter.active\" (click)=\"selectMediaChapter(chapter)\">\n <span class=\"ngxsp-yt-chapterTime\">\n @if (chapter.time) { <em>{{ chapter.time }}</em> }\n @if (chapter.label) { <b>{{ chapter.label }}</b> }\n </span>\n <span class=\"ngxsp-yt-chapterCopy\">\n <strong>{{ chapter.title }}</strong>\n @if (chapter.summary) { <small>{{ chapter.summary }}</small> }\n </span>\n </button>\n }\n </div>\n </div>\n }\n @if (detail.checklist?.length) {\n <div class=\"ngxsp-yt-checkPanel\">\n @if (detail.checklistTitle) { <strong class=\"ngxsp-yt-panelTitle\">{{ detail.checklistTitle }}</strong> }\n <div class=\"ngxsp-yt-checkList\">\n @for (item of detail.checklist; track item.id) {\n <div class=\"ngxsp-yt-checkItem\" [attr.data-tone]=\"item.tone || 'neutral'\">\n <span aria-hidden=\"true\"></span>\n <div>\n <strong>{{ item.label }}</strong>\n @if (item.description) { <small>{{ item.description }}</small> }\n </div>\n @if (item.value) { <em>{{ item.value }}</em> }\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n }\n @if (detail.episodes?.length) {\n <div class=\"ngxsp-yt-episodeRail\">\n @if (detail.episodesTitle) { <strong class=\"ngxsp-yt-panelTitle\">{{ detail.episodesTitle }}</strong> }\n <div>\n @for (episode of detail.episodes; track episode.id) {\n <button type=\"button\" [class.active]=\"episode.active\" (click)=\"selectDetailEpisode.emit(episode.id)\">\n <span class=\"ngxsp-yt-episodeThumb\" [class.has-image]=\"!!episode.artworkUrl\">\n @if (episode.artworkUrl) {\n <img [src]=\"episode.artworkUrl\" alt=\"\" />\n } @else {\n <i>{{ posterInitial(episode.title) }}</i>\n }\n @if (showResumeProgress && episode.progressPercent !== undefined) {\n <em><i [style.width.%]=\"safeProgressPercent(episode.progressPercent)\"></i></em>\n }\n </span>\n <span>\n <strong>{{ episode.title }}</strong>\n @if (episode.subtitle) { <small>{{ episode.subtitle }}</small> }\n @if (episode.duration) { <em>{{ episode.duration }}</em> }\n </span>\n </button>\n }\n </div>\n </div>\n }\n </section>\n }\n }\n\n @if (libraryVisible) {\n <section class=\"ngxsp-yt-library\">\n <div class=\"ngxsp-yt-sectionHead\">\n <div>\n <strong>{{ libraryTitle }}</strong>\n @if (libraryStatus) {\n <small class=\"ngxsp-yt-libraryStatus\" [class.busy]=\"libraryBusy\" role=\"status\" aria-live=\"polite\">{{ libraryStatus }}</small>\n }\n </div>\n <button class=\"ngxsp-yt-sectionLink\" type=\"button\" [attr.aria-label]=\"browseLibraryLabel\" [title]=\"browseLibraryLabel\" (click)=\"toggleLibrary.emit()\">\n <ngxsp-smart-icon name=\"chevron-up\" />\n </button>\n </div>\n @if (trail.length > 1) {\n <nav class=\"ngxsp-yt-trail\" [attr.aria-label]=\"trailLabel\">\n @for (step of trail; track step.id; let last = $last) {\n @if (last) {\n <b aria-current=\"page\">{{ step.label }}</b>\n } @else {\n <button class=\"ngxsp-yt-trailStep\" type=\"button\" (click)=\"trailStep.emit(step.id)\">{{ step.label }}</button>\n <span aria-hidden=\"true\">/</span>\n }\n }\n </nav>\n }\n @for (shelf of librarySections; track shelf.id) {\n <div class=\"ngxsp-yt-shelf\">\n @if (shelf.titled) {\n <div class=\"ngxsp-yt-shelfHead\">\n <strong>{{ shelf.title }}</strong>\n <small>{{ shelf.count }}</small>\n @if (shelf.hidden) {\n <button class=\"ngxsp-yt-shelfAll\" type=\"button\" [attr.aria-expanded]=\"isSectionExpanded(shelf.id)\" (click)=\"toggleSection(shelf.id)\">{{ isSectionExpanded(shelf.id) ? showLessLabel : showAllLabel }}</button>\n }\n </div>\n }\n <div class=\"ngxsp-yt-grid\" [class.list]=\"activeViewMode === 'list'\" [class.rail]=\"activeViewMode !== 'list' && !isSectionExpanded(shelf.id)\">\n @for (card of shelf.cards; track card.sectionId + ':' + card.item.id) {\n <ng-container *ngTemplateOutlet=\"mediaCard; context: { $implicit: card }\" />\n }\n </div>\n </div>\n }\n </section>\n }\n }\n </section>\n\n <ng-template #mediaCard let-card>\n <span class=\"ngxsp-yt-cardWrap\">\n <button class=\"ngxsp-yt-card\" type=\"button\" [class.active]=\"card.item.active\" [class.synthetic]=\"isSyntheticSection(card.sectionId)\" [attr.aria-current]=\"card.item.active ? 'true' : null\" [attr.data-art]=\"artVariant(card)\" (click)=\"selectCard(card)\" (contextmenu)=\"openCardMenu($event, card.item)\">\n <span class=\"ngxsp-yt-thumbnail\" [class.has-image]=\"!!card.item.artworkUrl\">\n @if (card.item.artworkUrl) {\n <img [src]=\"card.item.artworkUrl\" alt=\"\" />\n } @else {\n <span class=\"ngxsp-yt-art\" aria-hidden=\"true\"><ngxsp-smart-icon name=\"video\" /></span>\n }\n @if (card.item.episodeLabel || card.item.kindLabel) {\n <small class=\"ngxsp-yt-thumbKind\">{{ card.item.episodeLabel || card.item.kindLabel }}</small>\n }\n @if (!card.item.artworkUrl) {\n <small class=\"ngxsp-yt-thumbBadge\">{{ card.item.duration || card.item.meta || sourceTypeLabel }}</small>\n }\n @if (card.item.active) {\n <span class=\"ngxsp-yt-nowBadge\" aria-hidden=\"true\"><ngxsp-smart-icon [name]=\"isPlaying ? 'waveform' : 'play'\" /></span>\n }\n @if (showResumeProgress && card.item.progressPercent !== undefined) {\n <span class=\"ngxsp-yt-cardProgress\" aria-hidden=\"true\"><i [style.width.%]=\"safeProgressPercent(card.item.progressPercent)\"></i></span>\n }\n </span>\n <span class=\"ngxsp-yt-cardCopy\">\n <strong>{{ card.item.title }}</strong>\n @if (card.item.subtitle) { <span>{{ card.item.subtitle }}</span> }\n @if (mediaCardMeta(card.item)) { <small>{{ mediaCardMeta(card.item) }}</small> }\n </span>\n </button>\n @if (card.item.actions?.length) {\n <button\n class=\"ngxsp-yt-cardMore\"\n type=\"button\"\n [attr.aria-label]=\"cardActionsLabel\"\n [attr.aria-expanded]=\"cardMenu?.card?.id === card.item.id\"\n [title]=\"cardActionsLabel\"\n (click)=\"openCardMenu($event, card.item)\"\n >\n <ngxsp-smart-icon name=\"more\" />\n </button>\n }\n </span>\n </ng-template>\n\n <!-- What a panel shows: in the side column, or over the video for the info panels. -->\n <ng-template #panelBody let-panel>\n <div class=\"ngxsp-yt-inspectorScroll\">\n @if (panel.status) {\n <p class=\"ngxsp-yt-inspectorStatus\">{{ panel.status }}</p>\n }\n\n @if (panel.metrics?.length) {\n <div class=\"ngxsp-yt-inspectorMetrics\">\n @for (metric of panel.metrics; track metric.label) {\n <span class=\"ngxsp-yt-inspectorMetric\" [class.success]=\"metric.tone === 'success'\" [class.warning]=\"metric.tone === 'warning'\">\n <small>{{ metric.label }}</small>\n <strong>{{ metric.value }}</strong>\n </span>\n }\n </div>\n }\n\n @if (panel.settings?.length) {\n <ngxsp-settings [sections]=\"panel.settings!\" (settingChange)=\"settingChange.emit($event)\" (settingAction)=\"settingAction.emit($event)\" />\n }\n\n @if (serverForm; as form) {\n <section class=\"ngxsp-yt-servers\" [attr.aria-label]=\"form.heading || form.title\">\n @if (form.heading) { <h2 class=\"ngxsp-yt-serversHeading\">{{ form.heading }}</h2> }\n @for (server of form.servers; track server.id) {\n <div class=\"ngxsp-yt-serverRow\" [class.active]=\"server.active\">\n <button class=\"ngxsp-yt-serverMain\" type=\"button\" [title]=\"form.openLabel + ' - ' + server.name\" (click)=\"selectServer.emit(server.id)\">\n <ngxsp-smart-icon name=\"network\" />\n <span>\n <strong>{{ server.name }}</strong>\n @if (server.subtitle) { <small>{{ server.subtitle }}</small> }\n </span>\n </button>\n <button class=\"ngxsp-yt-serverDisconnect\" type=\"button\" [attr.aria-label]=\"form.disconnectLabel + ' - ' + server.name\" [title]=\"form.disconnectLabel\" (click)=\"disconnectServer.emit(server.id)\">\n <ngxsp-smart-icon name=\"close\" />\n </button>\n </div>\n }\n <form class=\"ngxsp-yt-serverForm\" (submit)=\"submitServer($event)\">\n <span class=\"ngxsp-yt-serverTitle\">{{ form.title }}</span>\n @if (form.kinds && form.kinds.length > 1) {\n <select class=\"ngxsp-yt-serverKind\" [value]=\"serverKindDraft || form.kinds[0].id\" (change)=\"onServerKindChange($event)\" [attr.aria-label]=\"form.kindLabel || form.title\">\n @for (kind of form.kinds; track kind.id) {\n <option [value]=\"kind.id\">{{ kind.label }}</option>\n }\n </select>\n }\n @let serverKind = selectedServerKind(form);\n <input type=\"text\" inputmode=\"url\" [value]=\"serverAddressDraft\" (input)=\"onServerAddressInput($event)\" [placeholder]=\"serverKind?.addressPlaceholder || form.addressPlaceholder\" [attr.aria-label]=\"serverKind?.addressPlaceholder || form.addressPlaceholder\" />\n @if (serverKind?.credentials !== false) {\n <input type=\"text\" autocomplete=\"username\" [value]=\"serverUsernameDraft\" (input)=\"onServerUsernameInput($event)\" [placeholder]=\"form.usernamePlaceholder\" [attr.aria-label]=\"form.usernamePlaceholder\" />\n <input type=\"password\" autocomplete=\"current-password\" [value]=\"serverPasswordDraft\" (input)=\"onServerPasswordInput($event)\" [placeholder]=\"form.passwordPlaceholder\" [attr.aria-label]=\"form.passwordPlaceholder\" />\n }\n <button type=\"submit\" [disabled]=\"form.busy || !serverAddressDraft.trim()\">{{ form.submitLabel }}</button>\n @if (form.discovery && serverKind?.discoverable) {\n <button class=\"secondary\" type=\"button\" [disabled]=\"form.discovery.busy\" (click)=\"discoverServers.emit(serverKind!.id)\">{{ form.discovery.busy ? form.discovery.searchingLabel : form.discovery.searchLabel }}</button>\n }\n @if (form.hint) { <small class=\"ngxsp-yt-serverHint\">{{ form.hint }}</small> }\n @if (form.discovery && serverKind?.discoverable && (form.discovery.servers.length || form.discovery.emptyLabel)) {\n <ul class=\"ngxsp-yt-discovered\" [attr.aria-label]=\"form.discovery.searchLabel\">\n @for (server of form.discovery.servers; track server.id) {\n <li>\n <span><strong>{{ server.name }}</strong>@if (server.subtitle) { <small>{{ server.subtitle }}</small> }</span>\n <button class=\"secondary\" type=\"button\" [disabled]=\"form.busy\" [attr.aria-label]=\"form.discovery.connectLabel + ' - ' + server.name\" (click)=\"connectDiscoveredServer.emit(server.id)\">{{ form.discovery.connectLabel }}</button>\n </li>\n } @empty {\n <li class=\"empty\">{{ form.discovery.emptyLabel }}</li>\n }\n </ul>\n }\n </form>\n </section>\n }\n\n @if (panel.trailingSettings?.length) {\n <ngxsp-settings [sections]=\"panel.trailingSettings!\" (settingChange)=\"settingChange.emit($event)\" (settingAction)=\"settingAction.emit($event)\" />\n }\n\n @if (panel.items?.length) {\n <div class=\"ngxsp-yt-inspectorList\">\n @for (item of panel.items; track item.label) {\n <div class=\"ngxsp-yt-inspectorItem\" [class.success]=\"item.tone === 'success'\" [class.warning]=\"item.tone === 'warning'\">\n <span>\n <strong>{{ item.label }}</strong>\n @if (item.description) { <small>{{ item.description }}</small> }\n </span>\n @if (item.value) { <em>{{ item.value }}</em> }\n </div>\n }\n </div>\n }\n </div>\n\n @if (panel.actions?.length) {\n <div class=\"ngxsp-yt-inspectorActions\">\n @for (action of panel.actions; track action.id) {\n <button type=\"button\" [class.primary]=\"action.primary\" [disabled]=\"action.disabled\" (click)=\"inspectorAction.emit(action.id)\">\n <ngxsp-smart-icon [name]=\"action.icon\" />\n <span>{{ action.label }}</span>\n </button>\n }\n </div>\n }\n </ng-template>\n\n <!-- A row of the side panel: a video of the queue or of the section on show. -->\n <ng-template #sideRow let-card let-label=\"label\">\n <button class=\"ngxsp-yt-suggestionMain\" type=\"button\" [attr.aria-current]=\"card.item.active ? 'true' : null\" [attr.aria-label]=\"label\" [title]=\"label\" (click)=\"selectCard(card)\">\n <span class=\"ngxsp-yt-suggestionThumb\" [class.has-image]=\"!!card.item.artworkUrl\">\n @if (card.item.artworkUrl) {\n <img [src]=\"card.item.artworkUrl\" alt=\"\" />\n } @else {\n <span class=\"ngxsp-yt-art\" aria-hidden=\"true\"><ngxsp-smart-icon name=\"video\" /></span>\n }\n @if (card.item.duration && !card.item.artworkUrl) { <small>{{ card.item.duration }}</small> }\n @if (card.item.active) {\n <span class=\"ngxsp-yt-nowBadge\" aria-hidden=\"true\"><ngxsp-smart-icon [name]=\"isPlaying ? 'waveform' : 'play'\" /></span>\n }\n @if (showResumeProgress && card.item.progressPercent !== undefined) {\n <span class=\"ngxsp-yt-cardProgress\" aria-hidden=\"true\"><i [style.width.%]=\"safeProgressPercent(card.item.progressPercent)\"></i></span>\n }\n </span>\n <span class=\"ngxsp-yt-suggestionCopy\">\n <strong>{{ card.item.title }}</strong>\n @if (card.item.subtitle) { <span>{{ card.item.subtitle }}</span> }\n @if (mediaCardMeta(card.item)) { <small>{{ mediaCardMeta(card.item) }}</small> }\n </span>\n </button>\n </ng-template>\n\n @if (queueVisible || queueSheetOpen) {\n <aside\n class=\"ngxsp-yt-suggestions\"\n #queuePanel\n tabindex=\"-1\"\n [class.sheet]=\"queueSheetOpen\"\n [attr.role]=\"queueSheetOpen ? 'dialog' : null\"\n [attr.aria-modal]=\"queueSheetOpen ? 'true' : null\"\n [attr.aria-label]=\"sidePanelTitle\"\n (keydown.escape)=\"closeQueueSheet()\"\n >\n <!-- Phones only: the panel covers the page instead of pushing it down, and closes from here. -->\n @if (queueSheetOpen) {\n <div class=\"ngxsp-yt-sheetHeader\">\n <strong>{{ sidePanel?.title || sidePanelTitle }}</strong>\n <button class=\"ngxsp-yt-textButton\" type=\"button\" (click)=\"closeQueueSheet()\">{{ closeLabel }}</button>\n </div>\n }\n <div class=\"ngxsp-yt-panelTabs\" role=\"group\" [attr.aria-label]=\"sidePanelTitle\">\n <button type=\"button\" class=\"ngxsp-yt-panelPill\" [class.active]=\"isPanelOpen('queue')\" [attr.aria-pressed]=\"isPanelOpen('queue')\" (click)=\"togglePanel('queue')\">{{ sidePanelTitle }}</button>\n @for (panel of knownPanels; track panel.id) {\n <button type=\"button\" class=\"ngxsp-yt-panelPill\" [class.active]=\"isPanelOpen(panel.id)\" [attr.aria-pressed]=\"isPanelOpen(panel.id)\" (click)=\"togglePanel(panel.id)\">{{ panel.title }}</button>\n }\n </div>\n\n <div class=\"ngxsp-yt-panelStack\">\n @if (isPanelOpen('queue')) {\n <section class=\"ngxsp-yt-stackItem\" [class.collapsed]=\"isPanelCollapsed('queue')\">\n <header class=\"ngxsp-yt-stackHead\">\n <strong>{{ sidePanelTitle }}</strong>\n @if (sidePanelCount) { <small>{{ sidePanelCount }}</small> }\n <button class=\"ngxsp-yt-stackIcon\" type=\"button\" [attr.aria-expanded]=\"!isPanelCollapsed('queue')\" [attr.aria-label]=\"sidePanelTitle\" (click)=\"togglePanelCollapsed('queue')\">\n <ngxsp-smart-icon [name]=\"isPanelCollapsed('queue') ? 'chevron-down' : 'chevron-up'\" />\n </button>\n <button class=\"ngxsp-yt-stackIcon\" type=\"button\" [attr.aria-label]=\"closeLabel\" [title]=\"closeLabel\" (click)=\"togglePanel('queue')\">\n <ngxsp-smart-icon name=\"close\" />\n </button>\n </header>\n @if (!isPanelCollapsed('queue')) {\n <!-- A page with nothing to list and nothing to say about it (the lists block above speaks for it) shows no list. -->\n @let sideListShown = sidePanelContent !== 'sections' || !!sidePanelCount || !!emptyCatalogLabel;\n @if (sideListShown) {\n <div class=\"ngxsp-yt-sectionHead compact\">\n <div>\n @if (sideBackLabel) {\n <button class=\"ngxsp-yt-backButton\" type=\"button\" [attr.aria-label]=\"sideBackLabel\" [title]=\"sideBackLabel\" (click)=\"sideBack.emit()\">\n <ngxsp-smart-icon name=\"chevron-left\" />\n <span>{{ sideBackLabel }}</span>\n </button>\n }\n <strong>{{ sidePanelTitle }}</strong>\n @if (sidePanelMeta) { <small>{{ sidePanelMeta }}</small> }\n </div>\n <span class=\"ngxsp-yt-countBadge\">{{ sidePanelCount }}</span>\n </div>\n }\n @if (sidePanelContent === 'sections') {\n @if (sideListShown) {\n <div class=\"ngxsp-yt-suggestionList\">\n @for (group of sideSectionGroups; track group.id) {\n @if (sideSectionGroups.length > 1 && group.cards.length) { <strong class=\"ngxsp-yt-sideGroupTitle\">{{ group.title }}</strong> }\n @for (card of group.cards; track card.sectionId + ':' + card.item.id) {\n <article class=\"ngxsp-yt-suggestion\" [class.active]=\"card.item.active\" [attr.data-art]=\"artVariant(card)\">\n <ng-container *ngTemplateOutlet=\"sideRow; context: { $implicit: card, label: playLabel + ' - ' + card.item.title }\" />\n </article>\n }\n }\n @if (!sidePanelCount) {\n <div class=\"ngxsp-yt-emptyList\">{{ emptyCatalogLabel }}</div>\n }\n </div>\n }\n } @else {\n <div class=\"ngxsp-yt-suggestionList\" #queueList>\n @for (card of displayQueueItems; track card.sectionId + ':' + card.item.id) {\n <article class=\"ngxsp-yt-suggestion\" [class.active]=\"card.item.active\" [class.synthetic]=\"isSyntheticSection(card.sectionId)\" [class.managed]=\"hasManagedQueue\" [attr.data-art]=\"artVariant(card)\">\n <ng-container *ngTemplateOutlet=\"sideRow; context: { $implicit: card, label: queueItemActionLabel('play', card) }\" />\n @if (hasManagedQueue) {\n <span class=\"ngxsp-yt-queueControls\">\n <button type=\"button\" [disabled]=\"!canMoveQueueItem(card, 'up')\" [attr.aria-label]=\"queueItemActionLabel('moveUp', card)\" [title]=\"queueItemActionLabel('moveUp', card)\" (click)=\"queueMoveUp.emit(card.item.id)\">\n <ngxsp-smart-icon class=\"ngxsp-yt-queueUpIcon\" name=\"chevron-up\" />\n </button>\n <button type=\"button\" [disabled]=\"!canMoveQueueItem(card, 'down')\" [attr.aria-label]=\"queueItemActionLabel('moveDown', card)\" [title]=\"queueItemActionLabel('moveDown', card)\" (click)=\"queueMoveDown.emit(card.item.id)\">\n <ngxsp-smart-icon class=\"ngxsp-yt-queueDownIcon\" name=\"chevron-down\" />\n </button>\n <button type=\"button\" [attr.aria-label]=\"queueItemActionLabel('remove', card)\" [title]=\"queueItemActionLabel('remove', card)\" (click)=\"queueRemove.emit(card.item.id)\">\n <ngxsp-smart-icon name=\"close\" />\n </button>\n </span>\n }\n </article>\n } @empty {\n <div class=\"ngxsp-yt-emptyList\">{{ emptyCatalogLabel }}</div>\n }\n </div>\n }\n }\n </section>\n }\n @for (panel of openPanels; track panel.id) {\n <section class=\"ngxsp-yt-stackItem\" [class.collapsed]=\"isPanelCollapsed(panel.id)\">\n <section class=\"ngxsp-yt-inspector\" [attr.aria-label]=\"panel.title\">\n <div class=\"ngxsp-yt-inspectorHead\">\n <span class=\"ngxsp-yt-inspectorIcon\" aria-hidden=\"true\">\n <ngxsp-smart-icon [name]=\"panel.icon || 'settings'\" />\n </span>\n <span class=\"ngxsp-yt-inspectorTitle\">\n <strong>{{ panel.title }}</strong>\n @if (panel.subtitle) { <small>{{ panel.subtitle }}</small> }\n </span>\n <button class=\"ngxsp-yt-inspectorClose\" type=\"button\" [attr.aria-expanded]=\"!isPanelCollapsed(panel.id)\" [attr.aria-label]=\"panel.title\" [title]=\"panel.title\" (click)=\"togglePanelCollapsed(panel.id)\">\n <ngxsp-smart-icon [name]=\"isPanelCollapsed(panel.id) ? 'chevron-down' : 'chevron-up'\" />\n </button>\n <button class=\"ngxsp-yt-inspectorClose\" type=\"button\" [attr.aria-label]=\"inspectorCloseLabel\" [title]=\"inspectorCloseLabel\" (click)=\"closePanel(panel.id)\">\n <ngxsp-smart-icon name=\"close\" />\n </button>\n </div>\n\n @if (!isPanelCollapsed(panel.id)) {\n <ng-container *ngTemplateOutlet=\"panelBody; context: { $implicit: panel }\" />\n }\n </section>\n </section>\n }\n </div>\n @if (playlistPanel; as lists) {\n <section class=\"ngxsp-yt-lists\" [attr.aria-label]=\"lists.title\">\n <div class=\"ngxsp-yt-sectionHead compact\">\n <div><strong>{{ lists.title }}</strong></div>\n <span class=\"ngxsp-yt-countBadge\">{{ lists.playlists.length }}</span>\n </div>\n <form class=\"ngxsp-yt-listForm\" (submit)=\"submitCreatePlaylist($event)\">\n <input type=\"text\" [value]=\"playlistDraftName\" (input)=\"onPlaylistDraftInput($event)\" [placeholder]=\"lists.namePlaceholder\" [attr.aria-label]=\"lists.namePlaceholder\" />\n <button type=\"submit\" [disabled]=\"!playlistDraftName.trim()\">{{ lists.createLabel }}</button>\n </form>\n @if (lists.target; as target) {\n <p class=\"ngxsp-yt-listTarget\">{{ lists.targetLabel }}<strong>{{ target.title }}</strong></p>\n }\n <div class=\"ngxsp-yt-listRows\">\n @for (list of lists.playlists; track list.id) {\n <div class=\"ngxsp-yt-listRow\" [class.active]=\"list.active\">\n <button class=\"ngxsp-yt-listMain\" type=\"button\" [attr.aria-current]=\"list.active ? 'true' : null\" [title]=\"lists.openLabel + ' - ' + list.name\" (click)=\"selectPlaylist.emit(list.id)\">\n <ngxsp-smart-icon name=\"list\" />\n <span>\n <strong>{{ list.name }}</strong>\n @if (list.itemCount !== undefined) { <small>{{ list.itemCount }} {{ lists.itemsCountLabel }}</small> }\n </span>\n </button>\n <span class=\"ngxsp-yt-listActions\">\n @if (lists.target) {\n @if (list.containsTarget) {\n <button type=\"button\" [attr.aria-label]=\"lists.removeLabel + ' - ' + list.name\" [title]=\"lists.removeLabel\" (click)=\"removeFromPlaylist.emit(list.id)\">\n <ngxsp-smart-icon name=\"check\" />\n </button>\n } @else {\n <button type=\"button\" [attr.aria-label]=\"lists.addLabel + ' - ' + list.name\" [title]=\"lists.addLabel\" (click)=\"addToPlaylist.emit(list.id)\">\n <ngxsp-smart-icon name=\"plus\" />\n </button>\n }\n }\n <button type=\"button\" [attr.aria-label]=\"lists.renameLabel + ' - ' + list.name\" [title]=\"lists.renameLabel\" (click)=\"startRenamePlaylist(list)\">\n <ngxsp-smart-icon name=\"tag\" />\n </button>\n <button type=\"button\" class=\"danger\" [attr.aria-label]=\"lists.deleteLabel + ' - ' + list.name\" [title]=\"lists.deleteLabel\" (click)=\"deletePlaylist.emit(list.id)\">\n <ngxsp-smart-icon name=\"close\" />\n </button>\n </span>\n @if (renamingPlaylistId === list.id) {\n <form class=\"ngxsp-yt-listForm\" (submit)=\"submitRenamePlaylist($event)\">\n <input type=\"text\" [value]=\"playlistRenameDraft\" (input)=\"onPlaylistRenameInput($event)\" [placeholder]=\"lists.renamePlaceholder\" [attr.aria-label]=\"lists.renamePlaceholder\" />\n <button type=\"submit\" [disabled]=\"!playlistRenameDraft.trim()\">{{ lists.saveLabel }}</button>\n <button type=\"button\" (click)=\"cancelRenamePlaylist()\">{{ lists.cancelLabel }}</button>\n </form>\n }\n </div>\n } @empty {\n <p class=\"ngxsp-yt-listEmpty\">{{ lists.emptyLabel }}</p>\n }\n </div>\n </section>\n }\n @if (miniPlayerVisible) {\n <article class=\"ngxsp-yt-miniPlayer\" [class.playing]=\"isPlaying\" [attr.aria-label]=\"nowPlayingLabel\">\n <button class=\"ngxsp-yt-miniArtwork\" type=\"button\" [attr.aria-label]=\"miniPlayerFocusLabel + ' - ' + displayHeroTitle\" [title]=\"miniPlayerFocusLabel\" (click)=\"miniPlayerFocus.emit()\">\n @if (displayHeroArtwork) {\n <img [src]=\"displayHeroArtwork\" alt=\"\" />\n } @else {\n <span class=\"ngxsp-yt-art\" aria-hidden=\"true\"><ngxsp-smart-icon name=\"video\" /></span>\n }\n <span class=\"ngxsp-yt-miniPlayBadge\" aria-hidden=\"true\">\n @if (isPlaying) { <ngxsp-smart-icon name=\"waveform\" /> } @else { <ngxsp-smart-icon name=\"play\" /> }\n </span>\n </button>\n <span class=\"ngxsp-yt-miniDetails\">\n <small>{{ miniPlayerMeta || heroEyebrow }}</small>\n <strong>{{ displayHeroTitle }}</strong>\n @if (displayHeroSubtitle) { <span>{{ displayHeroSubtitle }}</span> }\n </span>\n <button class=\"ngxsp-yt-miniControl\" type=\"button\" [attr.aria-label]=\"miniPlayerPlaybackLabel\" [title]=\"miniPlayerPlaybackLabel\" (click)=\"toggleMiniPlayerPlayback()\">\n @if (isPlaying) { <ngxsp-smart-icon name=\"pause\" /> } @else { <ngxsp-smart-icon name=\"play\" /> }\n </button>\n <button class=\"ngxsp-yt-miniClose\" type=\"button\" [attr.aria-label]=\"miniPlayerCloseLabel\" [title]=\"miniPlayerCloseLabel\" (click)=\"miniPlayerClose.emit()\">\n <ngxsp-smart-icon name=\"close\" />\n </button>\n </article>\n } @else {\n <button class=\"ngxsp-yt-miniRestore\" type=\"button\" [attr.aria-label]=\"miniPlayerRestoreLabel\" [title]=\"miniPlayerRestoreLabel\" (click)=\"miniPlayerRestore.emit()\">\n <ngxsp-smart-icon name=\"window-mini\" />\n <span>{{ miniPlayerRestoreLabel }}</span>\n </button>\n }\n </aside>\n }\n </div>\n </section>\n`,\n styles: [`\n :host{\n display:block;\n height:100%;\n min-height:0;\n color:var(--nsp-text, #ededf0);\n font-family:var(--nsp-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Ubuntu, sans-serif);\n font-size:13px;\n line-height:1.4;\n }\n .ngxsp-yt-shell{\n --yt-bg:var(--nsp-bg, #0d0e11);\n --yt-s0:var(--nsp-surface-0, #121317);\n --yt-s1:var(--nsp-surface-1, #17181d);\n --yt-s2:var(--nsp-surface-2, #1e1f25);\n --yt-s3:var(--nsp-surface-3, #26272e);\n --yt-line:var(--nsp-line, rgb(255 255 255 / .07));\n --yt-line-strong:var(--nsp-line-strong, rgb(255 255 255 / .13));\n --yt-text:var(--nsp-text, #ededf0);\n --yt-text-2:var(--nsp-text-2, #a3a5ae);\n --yt-text-3:var(--nsp-text-3, #80828c);\n --yt-accent:var(--nsp-accent, #8b90ff);\n --yt-accent-strong:var(--nsp-accent-strong, #5d63ee);\n --yt-accent-soft:var(--nsp-accent-soft, rgb(139 144 255 / .14));\n --yt-on-accent:var(--nsp-on-accent, #fff);\n --yt-success:var(--nsp-success, #3ecf8e);\n --yt-warning:var(--nsp-warning, #f2a93b);\n --yt-danger:var(--nsp-danger, #f25f5c);\n --yt-danger-text:var(--nsp-danger-text, #ffb4b2);\n --yt-live:var(--nsp-live, #f25f5c);\n --yt-r-sm:var(--nsp-radius-sm, 6px);\n --yt-r-md:var(--nsp-radius-md, 10px);\n --yt-r-lg:var(--nsp-radius-lg, 14px);\n --yt-ease:var(--nsp-ease, cubic-bezier(.2,0,0,1));\n --yt-dur:var(--nsp-dur, 140ms);\n --yt-title-h:var(--nsp-titlebar-height, 48px);\n --yt-rail-w:84px;\n --yt-side-w:360px;\n --yt-mode:var(--yt-text-2);\n position:relative;\n height:100%;\n min-height:600px;\n display:grid;\n grid-template-columns:var(--yt-rail-w) minmax(0,1fr);\n grid-template-rows:var(--yt-title-h) minmax(0,1fr);\n grid-template-areas:\n \"top top\"\n \"rail body\";\n overflow:hidden;\n background:var(--yt-bg);\n color:var(--yt-text);\n box-sizing:border-box;\n }\n .ngxsp-yt-shell.ngxsp-yt-railExpanded{--yt-rail-w:220px}\n /* Across the top: the same shape a narrow window takes on its own, chosen rather than\n deduced. The rail keeps its buttons and the row scrolls sideways to the ones that overflow. */\n .ngxsp-yt-shell.ngxsp-yt-menuRow{\n grid-template-columns:minmax(0,1fr);\n grid-template-rows:var(--yt-title-h) auto minmax(0,1fr);\n grid-template-areas:\n \"top\"\n \"rail\"\n \"body\";\n }\n .ngxsp-yt-menuRow .ngxsp-yt-rail{flex-direction:row;padding:6px 12px;border-right:0;border-bottom:1px solid var(--yt-line);overflow-x:auto;overflow-y:hidden}\n .ngxsp-yt-menuRow .ngxsp-yt-railButton{width:auto;min-height:36px;flex:0 0 auto;flex-direction:row;gap:6px;padding:0 10px}\n .ngxsp-yt-menuRow .ngxsp-yt-railButton span{font-size:12px}\n .ngxsp-yt-menuRow .ngxsp-yt-railMenu{display:none}\n /* Away: the views are still reached from the search, the trail and Back. The rule outranks\n the narrow window's, which would otherwise bring the row back on a phone. */\n .ngxsp-yt-shell.ngxsp-yt-menuOff{\n grid-template-columns:minmax(0,1fr);\n grid-template-rows:var(--yt-title-h) minmax(0,1fr);\n grid-template-areas:\n \"top\"\n \"body\";\n }\n .ngxsp-yt-menuOff .ngxsp-yt-rail{display:none}\n .ngxsp-yt-shell.ngxsp-yt-online{--yt-mode:var(--yt-live)}\n .ngxsp-yt-shell.ngxsp-yt-offline{--yt-mode:var(--yt-success)}\n .ngxsp-yt-shell *{box-sizing:border-box;letter-spacing:0}\n .ngxsp-yt-shell *::-webkit-scrollbar{width:10px;height:10px}\n .ngxsp-yt-shell *::-webkit-scrollbar-track{background:transparent}\n .ngxsp-yt-shell *::-webkit-scrollbar-thumb{background:var(--yt-s3);border-radius:999px;border:3px solid transparent;background-clip:padding-box}\n button,input{font:inherit;color:inherit}\n button{cursor:pointer}\n img{display:block;width:100%;height:100%;object-fit:cover}\n h1,h2,p{margin:0}\n ngxsp-smart-icon{width:18px;height:18px}\n .ngxsp-yt-fileInput{display:none}\n .ngxsp-yt-shell button:focus-visible,\n .ngxsp-yt-shell input:focus-visible{outline:2px solid var(--yt-accent);outline-offset:2px}\n\n /* Title bar */\n .ngxsp-yt-topbar{\n grid-area:top;\n min-width:0;\n display:grid;\n grid-template-columns:auto minmax(200px, 1fr) auto;\n align-items:center;\n gap:16px;\n padding:0 calc(12px + var(--nsp-titlebar-inset-end, 0px)) 0 calc(12px + var(--nsp-titlebar-inset-start, 0px));\n border-bottom:1px solid var(--yt-line);\n background:var(--yt-s0);\n -webkit-app-region:drag;\n user-select:none;\n }\n .ngxsp-yt-topbar button,\n .ngxsp-yt-topbar input,\n .ngxsp-yt-topbar label,\n .ngxsp-yt-topbar .ngxsp-yt-status{-webkit-app-region:no-drag}\n .ngxsp-yt-brand{min-width:0;display:flex;align-items:center;gap:10px}\n .ngxsp-yt-railMenu{\n width:32px;\n height:32px;\n display:grid;\n place-items:center;\n padding:0;\n margin-right:6px;\n border:0;\n border-radius:var(--yt-r-sm);\n background:transparent;\n color:var(--yt-text-2);\n }\n .ngxsp-yt-railMenu:hover{background:var(--yt-s2);color:var(--yt-text)}\n .ngxsp-yt-brandIcon{\n width:26px;\n height:26px;\n flex:0 0 auto;\n display:grid;\n place-items:center;\n border-radius:7px;\n color:#fff;\n background:linear-gradient(145deg, #8b90ff 0%, #5d63ee 55%, #4146c9 100%);\n box-shadow:inset 0 1px 0 rgb(255 255 255 / .22);\n }\n .ngxsp-yt-brandIcon ngxsp-smart-icon{width:14px;height:14px;margin-left:1px}\n .ngxsp-yt-brandLogo{width:26px;height:26px;flex:0 0 auto;display:block}\n .ngxsp-yt-brand strong{font-size:13px;font-weight:600;white-space:nowrap}\n .ngxsp-yt-search{\n position:relative;\n justify-self:center;\n width:100%;\n max-width:520px;\n height:34px;\n display:block;\n color:var(--yt-text-3);\n }\n .ngxsp-yt-search ngxsp-smart-icon{position:absolute;left:11px;top:50%;width:16px;height:16px;transform:translateY(-50%);pointer-events:none}\n .ngxsp-yt-search input{\n width:100%;\n height:100%;\n padding:0 10px 0 35px;\n border:1px solid transparent;\n border-radius:8px;\n outline:0;\n background:var(--yt-s2);\n color:var(--yt-text);\n font-size:13px;\n transition:border-color var(--yt-dur) var(--yt-ease), background-color var(--yt-dur) var(--yt-ease), box-shadow var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-search input:hover{border-color:var(--yt-line-strong)}\n .ngxsp-yt-shell .ngxsp-yt-search input:focus,\n .ngxsp-yt-shell .ngxsp-yt-search input:focus-visible{outline:0;border-color:var(--yt-accent);background:var(--yt-s1);box-shadow:0 0 0 3px var(--yt-accent-soft)}\n .ngxsp-yt-search input::placeholder{color:var(--yt-text-3)}\n .ngxsp-yt-actions{min-width:0;display:flex;align-items:center;justify-content:flex-end;gap:8px}\n .ngxsp-yt-status{\n min-width:0;\n max-width:260px;\n height:26px;\n display:inline-flex;\n align-items:center;\n gap:7px;\n padding:0 10px;\n border-radius:999px;\n background:var(--yt-s2);\n color:var(--yt-text-2);\n font-size:12px;\n white-space:nowrap;\n overflow:hidden;\n text-overflow:ellipsis;\n }\n .ngxsp-yt-status::before{content:'';width:6px;height:6px;flex:0 0 auto;border-radius:50%;background:var(--yt-accent)}\n .ngxsp-yt-status.success::before{background:var(--yt-success)}\n .ngxsp-yt-status.error{color:var(--yt-danger-text);background:rgb(242 95 92 / .16)}\n .ngxsp-yt-status.error::before{background:var(--yt-danger)}\n .ngxsp-yt-mode,\n .ngxsp-yt-languageSwitch{\n flex:0 0 auto;\n display:inline-flex;\n align-items:center;\n gap:2px;\n padding:2px;\n border-radius:8px;\n background:var(--yt-s2);\n }\n .ngxsp-yt-mode button,\n .ngxsp-yt-languageSwitch button{\n height:32px;\n display:inline-flex;\n align-items:center;\n gap:6px;\n padding:0 10px;\n border:0;\n border-radius:6px;\n background:transparent;\n color:var(--yt-text-2);\n font-size:12px;\n font-weight:500;\n white-space:nowrap;\n }\n .ngxsp-yt-mode button i{width:6px;height:6px;border-radius:50%;background:var(--yt-text-3)}\n .ngxsp-yt-mode button:hover,\n .ngxsp-yt-languageSwitch button:hover{color:var(--yt-text)}\n .ngxsp-yt-mode button.active,\n .ngxsp-yt-languageSwitch button.active{background:var(--yt-s3);color:var(--yt-text);box-shadow:0 1px 2px rgb(0 0 0 / .3)}\n .ngxsp-yt-mode button.active i{background:var(--yt-mode);box-shadow:0 0 0 3px color-mix(in srgb, var(--yt-mode) 22%, transparent)}\n .ngxsp-yt-sourceButton{\n flex:0 0 auto;\n height:32px;\n display:inline-flex;\n align-items:center;\n gap:7px;\n padding:0 12px 0 10px;\n border:0;\n border-radius:var(--yt-r-sm);\n background:var(--yt-accent-strong);\n color:var(--yt-on-accent);\n font-size:12px;\n font-weight:600;\n white-space:nowrap;\n }\n .ngxsp-yt-sourceButton ngxsp-smart-icon{width:16px;height:16px}\n .ngxsp-yt-sourceButton:hover{filter:brightness(1.1)}\n .ngxsp-yt-sourceButton:disabled{opacity:.7;cursor:progress}\n .ngxsp-yt-libraryStatus{display:inline-flex;align-items:center;gap:6px}\n .ngxsp-yt-libraryStatus::before{content:'';width:6px;height:6px;flex:0 0 auto;border-radius:50%;background:var(--yt-success)}\n .ngxsp-yt-libraryStatus.busy::before{background:var(--yt-accent);animation:ngxsp-yt-pulse 1.2s ease-in-out infinite}\n @keyframes ngxsp-yt-pulse{0%,100%{opacity:.35}50%{opacity:1}}\n .ngxsp-yt-round{\n width:32px;\n height:32px;\n flex:0 0 auto;\n display:grid;\n place-items:center;\n padding:0;\n border:0;\n border-radius:var(--yt-r-sm);\n background:transparent;\n color:var(--yt-text-2);\n }\n .ngxsp-yt-round:hover{background:var(--yt-s2);color:var(--yt-text)}\n\n /* Rail */\n .ngxsp-yt-rail{\n grid-area:rail;\n min-width:0;\n min-height:0;\n display:flex;\n flex-direction:column;\n gap:2px;\n padding:10px 8px;\n overflow-y:auto;\n overflow-x:hidden;\n border-right:1px solid var(--yt-line);\n background:var(--yt-s0);\n }\n .ngxsp-yt-railButton{\n width:100%;\n min-height:56px;\n flex:0 0 auto;\n display:flex;\n flex-direction:column;\n align-items:center;\n justify-content:center;\n gap:4px;\n padding:6px 2px;\n border:0;\n border-radius:var(--yt-r-sm);\n background:transparent;\n color:var(--yt-text-2);\n transition:background-color var(--yt-dur) var(--yt-ease), color var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-railButton ngxsp-smart-icon{width:20px;height:20px}\n .ngxsp-yt-railButton span{max-width:100%;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;font-size:10px;font-weight:500;line-height:1.15;text-align:center;hyphens:auto;overflow-wrap:anywhere}\n .ngxsp-yt-railButton:hover:not(:disabled){background:var(--yt-s2);color:var(--yt-text)}\n .ngxsp-yt-railButton.active{background:var(--yt-s3);color:var(--yt-text)}\n .ngxsp-yt-railButton.active ngxsp-smart-icon{color:var(--yt-accent)}\n .ngxsp-yt-railButton:disabled{opacity:.4;cursor:default}\n .ngxsp-yt-railExpanded .ngxsp-yt-railButton{min-height:36px;flex-direction:row;justify-content:flex-start;gap:12px;padding:0 12px}\n .ngxsp-yt-railExpanded .ngxsp-yt-railButton span{font-size:13px;text-align:left;-webkit-line-clamp:1;overflow-wrap:normal}\n .ngxsp-yt-railExpanded .ngxsp-yt-railButton ngxsp-smart-icon{width:18px;height:18px}\n\n /* Body */\n .ngxsp-yt-body{\n grid-area:body;\n min-width:0;\n min-height:0;\n display:grid;\n grid-template-columns:minmax(0,1fr) var(--yt-side-w);\n overflow:hidden;\n }\n .ngxsp-queue-hidden .ngxsp-yt-body{grid-template-columns:minmax(0,1fr)}\n .ngxsp-yt-watch{\n /* The gutter is named so a rail can run into it rather than stop short of it. */\n --yt-gutter:24px;\n min-width:0;\n min-height:0;\n display:flex;\n flex-direction:column;\n gap:20px;\n padding:20px var(--yt-gutter) 40px;\n overflow:auto;\n }\n .ngxsp-yt-cinema .ngxsp-yt-body{display:block;overflow:auto}\n .ngxsp-yt-cinema .ngxsp-yt-watch{overflow:visible;padding-top:16px}\n /* The player view is the player and nothing else: it takes the area whole, with no padding to sit in. Cinema\n mode lets the page scroll for the details below, which the player view does not have, so it keeps its height. */\n .ngxsp-yt-watch.player{padding:0;gap:0;overflow:hidden;height:100%}\n .ngxsp-yt-cinema .ngxsp-yt-watch.player{padding:0;overflow:hidden;height:100%}\n .ngxsp-yt-playerBack{\n position:absolute;top:12px;left:12px;z-index:4;display:none;align-items:center;gap:2px;\n height:36px;padding:0 14px 0 8px;border:0;border-radius:999px;\n background:rgb(0 0 0 / .55);color:#fff;font:inherit;font-size:13px;font-weight:600;cursor:pointer;\n backdrop-filter:blur(8px);\n }\n .ngxsp-yt-playerBack ngxsp-smart-icon{width:18px;height:18px}\n .ngxsp-yt-playerShell{position:relative}\n .ngxsp-yt-watch.player .ngxsp-yt-playerShell{flex:1 1 auto;min-height:0}\n .ngxsp-yt-watch.player .ngxsp-yt-playerFrame{width:100%;min-width:0;height:100%;aspect-ratio:auto;border-radius:0;box-shadow:none}\n\n /* Player */\n .ngxsp-yt-playerShell{flex:0 0 auto;display:flex;justify-content:center}\n .ngxsp-yt-playerFrame{\n position:relative;\n width:min(100%, calc((100vh - var(--yt-title-h) - 190px) * 16 / 9));\n min-width:min(100%, 620px);\n aspect-ratio:16 / 9;\n overflow:hidden;\n border-radius:var(--yt-r-md);\n background:#000;\n box-shadow:0 0 0 1px var(--yt-line), var(--nsp-shadow-media, 0 24px 60px rgb(0 0 0 / .45));\n }\n .ngxsp-yt-cinema .ngxsp-yt-playerFrame{width:min(100%, calc((100vh - var(--yt-title-h) - 48px) * 16 / 9))}\n .ngxsp-yt-playerFrame ngx-smart-player{position:absolute;inset:0;display:block}\n .ngxsp-yt-previewPlayer{position:absolute;inset:0;width:100%;height:100%;padding:0;border:0;background:#000;overflow:hidden}\n .ngxsp-yt-previewPlayer img{transition:transform 600ms var(--yt-ease), filter var(--yt-dur) var(--yt-ease)}\n .ngxsp-yt-previewPlayer:hover img{transform:scale(1.015);filter:brightness(.92)}\n .ngxsp-yt-previewOverlay{position:absolute;inset:0;display:grid;place-items:center;background:linear-gradient(180deg, rgb(0 0 0 / .1) 40%, rgb(0 0 0 / .55))}\n .ngxsp-yt-previewPlay{\n width:72px;\n height:72px;\n display:grid;\n place-items:center;\n border-radius:999px;\n background:rgb(255 255 255 / .94);\n color:#0d0e11;\n box-shadow:0 12px 40px rgb(0 0 0 / .45);\n transition:transform var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-previewPlay ngxsp-smart-icon{width:30px;height:30px;margin-left:3px}\n .ngxsp-yt-previewPlayer:hover .ngxsp-yt-previewPlay{transform:scale(1.06)}\n .ngxsp-yt-emptyPlayer{\n position:absolute;\n inset:0;\n display:grid;\n place-content:center;\n justify-items:center;\n gap:8px;\n padding:24px 24px 88px;\n background:radial-gradient(80% 90% at 50% 20%, rgb(93 99 238 / .16), transparent 70%), #08090b;\n text-align:center;\n }\n .ngxsp-yt-bigPlay{\n width:64px;\n height:64px;\n display:grid;\n place-items:center;\n margin-bottom:8px;\n border:0;\n border-radius:18px;\n background:var(--yt-accent-soft);\n color:var(--yt-accent);\n transition:background-color var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-bigPlay:hover{background:rgb(139 144 255 / .24)}\n .ngxsp-yt-bigPlay ngxsp-smart-icon{width:28px;height:28px}\n .ngxsp-yt-emptyPlayer span{color:var(--yt-text-2);font-size:12px;font-weight:600;}\n .ngxsp-yt-emptyPlayer strong{font-size:20px;font-weight:600}\n .ngxsp-yt-emptyPlayer p{max-width:56ch;color:var(--yt-text-2)}\n .ngxsp-yt-transportBar{\n position:absolute;\n left:0;\n right:0;\n bottom:0;\n z-index:2;\n display:grid;\n grid-template-columns:auto minmax(0,1fr) auto;\n align-items:center;\n gap:12px;\n padding:40px 12px 10px;\n background:linear-gradient(180deg, rgb(0 0 0 / 0), rgb(0 0 0 / .72));\n pointer-events:none;\n }\n .ngxsp-yt-transportBar > *{pointer-events:auto}\n /* Mini player window (desktop): the player alone fills the window, moved by a strip along its top. The window is\n the player: its height is the viewport's, whatever the host page does with its own. Outside macOS the window\n controls stay in an overlay along the top: that strip is left to them. */\n .ngxsp-yt-shell.ngxsp-yt-compact{display:block;height:100vh;min-height:0;padding:env(titlebar-area-height, 0px) 0 0;box-sizing:border-box}\n .ngxsp-yt-shell.ngxsp-yt-compact > :not(.ngxsp-yt-body){display:none!important}\n .ngxsp-yt-compact .ngxsp-yt-body{display:block;height:100%}\n .ngxsp-yt-compact .ngxsp-yt-body > :not(.ngxsp-yt-watch){display:none!important}\n .ngxsp-yt-compact .ngxsp-yt-watch{display:block;height:100%;padding:0;overflow:hidden}\n .ngxsp-yt-compact .ngxsp-yt-watch > :not(.ngxsp-yt-playerShell){display:none!important}\n .ngxsp-yt-compact .ngxsp-yt-playerShell{height:100%}\n .ngxsp-yt-compact .ngxsp-yt-playerFrame{position:relative;width:100%;min-width:0;height:100%;aspect-ratio:auto;border-radius:0;box-shadow:none}\n .ngxsp-yt-compactDrag{position:absolute;top:0;left:0;right:0;z-index:6;height:28px;-webkit-app-region:drag}\n .ngxsp-yt-compact .ngxsp-yt-playerInlineControls > .ngxsp-yt-playerAction:not(:first-child){display:none}\n /* The player drops these buttons on phone widths, which a mini window has: they are the way back here. */\n .ngxsp-yt-compact ngx-smart-player ::ng-deep .ngxsp-inline-projection{display:inline-flex}\n .ngxsp-yt-compact :is(.ngxsp-yt-transportCinema, .ngxsp-yt-transportBar .ngxsp-yt-queueButton){display:none}\n .ngxsp-yt-transportCluster{display:flex;align-items:center;gap:4px}\n .ngxsp-yt-transportButton{\n width:36px;\n height:36px;\n display:grid;\n place-items:center;\n padding:0;\n border:0;\n border-radius:var(--yt-r-sm);\n background:transparent;\n color:rgb(255 255 255 / .92);\n transition:background-color var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-transportButton ngxsp-smart-icon{width:20px;height:20px}\n .ngxsp-yt-transportButton:hover:not(:disabled){background:rgb(255 255 255 / .12)}\n .ngxsp-yt-transportButton.active{color:var(--yt-accent)}\n .ngxsp-yt-transportButton:disabled{opacity:.4;cursor:default}\n .ngxsp-yt-transportPlay{\n width:44px;\n height:44px;\n display:grid;\n place-items:center;\n margin:0 4px;\n padding:0;\n border:0;\n border-radius:999px;\n background:#fff;\n color:#0d0e11;\n transition:transform var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-transportPlay:hover{transform:scale(1.05)}\n .ngxsp-yt-transportPlay ngxsp-smart-icon{width:20px;height:20px}\n .ngxsp-yt-transportInfo{min-width:0;display:grid;gap:1px;color:#fff}\n .ngxsp-yt-transportInfo span{color:rgb(255 255 255 / .72);font-size:12px;font-weight:600;}\n .ngxsp-yt-transportInfo strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600}\n .ngxsp-yt-volumeControl{\n --ngxsp-control-size:36px;\n --ngxsp-volume-range-min:64px;\n --ngxsp-volume-range-max:92px;\n --ngxsp-volume-gap:2px;\n --ngxsp-volume-button-border:0;\n --ngxsp-volume-button-radius:6px;\n --ngxsp-volume-accent:#fff;\n --ngxsp-volume-track-bg:rgb(255 255 255 / .28);\n --ngxsp-volume-thumb-bg:#fff;\n --ngxsp-volume-thumb-size:12px;\n --ngxsp-volume-thumb-shadow:none;\n --ngxsp-focus-ring:0 0 0 2px var(--yt-accent);\n color:#fff;\n }\n .ngxsp-yt-playerInlineControls{display:inline-flex;align-items:center;gap:0}\n .ngxsp-yt-playerAction{\n width:36px;\n height:36px;\n display:grid;\n place-items:center;\n padding:0;\n border:0;\n border-radius:6px;\n background:transparent;\n color:rgb(255 255 255 / .92);\n }\n .ngxsp-yt-playerAction ngxsp-smart-icon{width:20px;height:20px}\n .ngxsp-yt-playerAction:hover{background:rgb(255 255 255 / .12)}\n .ngxsp-yt-playerAction.active{color:var(--yt-accent)}\n\n /* Metadata */\n .ngxsp-yt-metaBar{\n flex:0 0 auto;\n min-width:0;\n display:flex;\n align-items:flex-start;\n justify-content:space-between;\n flex-wrap:wrap;\n gap:12px 24px;\n }\n .ngxsp-yt-titleGroup{flex:1 1 320px;min-width:0;display:grid;gap:4px}\n .ngxsp-yt-titleGroup h1{font-size:22px;line-height:1.25;font-weight:700}\n .ngxsp-yt-shell .ngxsp-yt-titleGroup h1{letter-spacing:-.01em}\n .ngxsp-yt-titleGroup p{color:var(--yt-text-2);font-size:13px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n .ngxsp-yt-titleGroup small{color:var(--yt-text-3);font-size:12px}\n .ngxsp-yt-watchActions{display:flex;align-items:center;flex-wrap:wrap;gap:8px}\n .ngxsp-yt-rating{display:inline-flex;border-radius:999px;background:var(--yt-s2);overflow:hidden}\n .ngxsp-yt-watchActions button{\n height:34px;\n display:inline-flex;\n align-items:center;\n gap:7px;\n padding:0 14px 0 12px;\n border:0;\n border-radius:999px;\n background:var(--yt-s2);\n color:var(--yt-text);\n font-size:12px;\n font-weight:500;\n white-space:nowrap;\n transition:background-color var(--yt-dur) var(--yt-ease), color var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-watchActions button:hover{background:var(--yt-s3)}\n .ngxsp-yt-watchActions button.active{color:var(--yt-accent)}\n .ngxsp-yt-watchActions button.active ngxsp-smart-icon ::ng-deep path{fill:var(--yt-accent-soft)}\n .ngxsp-yt-rating button{border-radius:0}\n .ngxsp-yt-rating button + button{position:relative;padding-left:12px}\n .ngxsp-yt-rating button + button::before{content:'';position:absolute;left:0;top:9px;bottom:9px;width:1px;background:var(--yt-line-strong)}\n .ngxsp-yt-rating button + button span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}\n .ngxsp-yt-moreAction{width:34px;padding:0!important;justify-content:center}\n .ngxsp-yt-moreAction span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}\n .ngxsp-yt-watchActions ngxsp-smart-icon{width:17px;height:17px}\n\n /* Capability strip */\n .ngxsp-yt-toolRow{flex:0 0 auto;display:grid;grid-template-columns:repeat(auto-fit, minmax(146px, 1fr));gap:8px}\n .ngxsp-yt-tool{\n min-width:0;\n height:52px;\n display:grid;\n grid-template-columns:30px minmax(0,1fr);\n align-items:center;\n gap:9px;\n padding:0 10px 0 9px;\n border:1px solid var(--yt-line);\n border-radius:var(--yt-r-md);\n background:var(--yt-s1);\n text-align:left;\n transition:border-color var(--yt-dur) var(--yt-ease), background-color var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-tool:hover{border-color:var(--yt-line-strong);background:var(--yt-s2)}\n .ngxsp-yt-tool.active{border-color:rgb(139 144 255 / .45);background:var(--yt-accent-soft)}\n .ngxsp-yt-toolIcon{--tone:var(--yt-text-2);width:30px;height:30px;display:grid;place-items:center;border-radius:8px;background:color-mix(in srgb, var(--tone) 16%, transparent);color:var(--tone)}\n .ngxsp-yt-tool[data-tone=\"teal\"] .ngxsp-yt-toolIcon{--tone:var(--nsp-tone-teal, #3fcfbf)}\n .ngxsp-yt-tool[data-tone=\"red\"] .ngxsp-yt-toolIcon{--tone:var(--nsp-tone-red, #f47c7a)}\n .ngxsp-yt-tool[data-tone=\"blue\"] .ngxsp-yt-toolIcon{--tone:var(--nsp-tone-blue, #7cb1ff)}\n .ngxsp-yt-tool[data-tone=\"gold\"] .ngxsp-yt-toolIcon{--tone:var(--nsp-tone-gold, #f2bb5b)}\n .ngxsp-yt-toolIcon ngxsp-smart-icon{width:17px;height:17px}\n .ngxsp-yt-toolCopy{min-width:0;display:grid;gap:1px}\n .ngxsp-yt-toolCopy small,\n .ngxsp-yt-toolCopy strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-toolCopy small{color:var(--yt-text-2);font-size:11px}\n .ngxsp-yt-toolCopy strong{font-size:13px;font-weight:500}\n\n /* Cards shared */\n .ngxsp-yt-art{position:absolute;inset:0;display:grid;place-items:center;color:rgb(255 255 255 / .55)}\n .ngxsp-yt-art ngxsp-smart-icon{width:26px;height:26px}\n [data-art] .ngxsp-yt-thumbnail:not(.has-image),\n [data-art] .ngxsp-yt-suggestionThumb:not(.has-image),\n [data-art] .ngxsp-yt-searchThumb:not(.has-image){background:radial-gradient(90% 80% at 15% 10%, rgb(255 255 255 / .14), transparent 60%), linear-gradient(140deg, var(--art-a, #3a3c55), var(--art-b, #15161c))}\n [data-art=\"aurora\"]{--art-a:#3a7f78;--art-b:#10252a}\n [data-art=\"orbit\"]{--art-a:#5157c9;--art-b:#1a1b3d}\n [data-art=\"ember\"]{--art-a:#b0603a;--art-b:#301810}\n [data-art=\"ridge\"]{--art-a:#4f6f94;--art-b:#131d2a}\n [data-art=\"signal\"]{--art-a:#a8416a;--art-b:#2c1120}\n [data-art=\"fog\"]{--art-a:#5d6270;--art-b:#1a1b20}\n .ngxsp-yt-nowBadge{position:absolute;right:8px;top:8px;width:26px;height:26px;display:grid;place-items:center;border-radius:999px;background:var(--yt-accent-strong);color:#fff;box-shadow:0 4px 12px rgb(0 0 0 / .4)}\n .ngxsp-yt-nowBadge ngxsp-smart-icon{width:14px;height:14px}\n .ngxsp-yt-thumbKind{position:absolute;left:8px;top:8px;max-width:calc(100% - 50px);padding:2px 7px;border-radius:4px;background:rgb(0 0 0 / .72);color:#fff;font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;backdrop-filter:blur(6px)}\n .ngxsp-yt-cardProgress{position:absolute;left:0;right:0;bottom:0;height:3px;background:rgb(255 255 255 / .2)}\n .ngxsp-yt-cardProgress i{position:absolute;inset:0 auto 0 0;background:var(--yt-accent)}\n .ngxsp-yt-thumbBadge,\n .ngxsp-yt-suggestionThumb > small,\n .ngxsp-yt-searchThumb > small{\n position:absolute;\n right:6px;\n bottom:7px;\n max-width:calc(100% - 12px);\n padding:2px 6px;\n border-radius:4px;\n background:rgb(0 0 0 / .78);\n color:#fff;\n font-size:11px;\n font-weight:600;\n font-variant-numeric:tabular-nums;\n white-space:nowrap;\n overflow:hidden;\n text-overflow:ellipsis;\n }\n\n /* Section heads */\n .ngxsp-yt-sectionHead{display:flex;align-items:center;justify-content:space-between;gap:12px}\n .ngxsp-yt-sectionHead > div{min-width:0;display:grid;gap:2px}\n .ngxsp-yt-sectionHead strong{font-size:16px;font-weight:600}\n .ngxsp-yt-sectionHead small{color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-sectionHead.compact{padding:14px 16px 10px}\n .ngxsp-yt-sectionHead.compact strong{font-size:14px}\n .ngxsp-yt-sectionLink{\n width:32px;\n height:32px;\n display:grid;\n place-items:center;\n padding:0;\n border:0;\n border-radius:var(--yt-r-sm);\n background:transparent;\n color:var(--yt-text-2);\n }\n .ngxsp-yt-sectionLink:hover{background:var(--yt-s2);color:var(--yt-text)}\n .ngxsp-yt-servers{display:grid;gap:8px}\n .ngxsp-yt-serversHeading{margin:6px 0 2px;font-size:13px;font-weight:600;letter-spacing:.02em;color:var(--yt-text-2);text-transform:uppercase}\n .ngxsp-yt-serverForm{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--yt-line);border-radius:var(--yt-r-md);background:var(--yt-s1)}\n .ngxsp-yt-serverTitle{font-size:12px;font-weight:600;color:var(--yt-text-2)}\n .ngxsp-yt-discovered{flex:1 1 100%;display:grid;gap:4px;margin:0;padding:0;list-style:none}\n .ngxsp-yt-discovered li{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:36px;padding:2px 0 2px 8px;border-top:1px solid var(--yt-line)}\n .ngxsp-yt-discovered li span{display:grid;min-width:0}\n .ngxsp-yt-discovered strong{overflow:hidden;font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-discovered small,.ngxsp-yt-discovered li.empty{overflow:hidden;color:var(--yt-text-2);font-size:12px;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-serverKind{height:32px;padding:0 8px;border:1px solid var(--yt-line);border-radius:var(--yt-r-sm);background:var(--yt-s2);color:var(--yt-text);font-size:12px}\n .ngxsp-yt-serverForm input{\n flex:1 1 180px;\n min-width:0;\n height:32px;\n padding:0 10px;\n border:1px solid var(--yt-line);\n border-radius:var(--yt-r-sm);\n background:var(--yt-s0, var(--yt-s2));\n color:var(--yt-text);\n font-size:12px;\n }\n .ngxsp-yt-serverForm input::placeholder{color:var(--yt-text-3)}\n .ngxsp-yt-serverForm button{\n min-height:32px;\n padding:0 14px;\n border:1px solid var(--yt-line);\n border-radius:var(--yt-r-sm);\n background:var(--yt-accent, var(--yt-s3));\n color:var(--yt-text);\n font-size:12px;\n font-weight:600;\n cursor:pointer;\n }\n .ngxsp-yt-serverForm button.secondary{border-color:var(--yt-line-strong, var(--yt-line));background:transparent}\n .ngxsp-yt-serverForm button[disabled]{opacity:.5;cursor:default}\n .ngxsp-yt-serverHint{flex:1 0 100%;color:var(--yt-text-3);font-size:11px}\n .ngxsp-yt-serverRow{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px;border-radius:var(--yt-r-sm)}\n .ngxsp-yt-serverRow.active{background:var(--yt-s1)}\n .ngxsp-yt-serverMain{display:flex;align-items:center;gap:8px;min-width:0;padding:6px 8px;border:0;border-radius:var(--yt-r-sm);background:transparent;color:var(--yt-text);text-align:left;cursor:pointer}\n .ngxsp-yt-serverMain span{display:grid;min-width:0}\n .ngxsp-yt-serverMain strong{font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-serverMain small{color:var(--yt-text-3);font-size:11px}\n .ngxsp-yt-serverMain:hover{background:var(--yt-s2)}\n .ngxsp-yt-serverDisconnect{\n flex:0 0 32px;\n width:32px;\n height:32px;\n display:grid;\n place-items:center;\n border:0;\n border-radius:999px;\n background:transparent;\n color:var(--yt-text-2);\n cursor:pointer;\n }\n .ngxsp-yt-serverDisconnect:hover{background:var(--yt-s2);color:var(--yt-danger, #e5484d)}\n .ngxsp-yt-lists{display:grid;gap:8px;margin-bottom:16px}\n .ngxsp-yt-listForm{display:flex;gap:6px}\n .ngxsp-yt-listForm input{\n flex:1;\n min-width:0;\n height:32px;\n padding:0 10px;\n border:1px solid var(--yt-line);\n border-radius:var(--yt-r-sm);\n background:var(--yt-s1);\n color:var(--yt-text);\n font-size:12px;\n }\n .ngxsp-yt-listForm input::placeholder{color:var(--yt-text-3)}\n .ngxsp-yt-listForm button{\n min-height:32px;\n height:32px;\n padding:0 12px;\n border:1px solid var(--yt-line);\n border-radius:var(--yt-r-sm);\n background:var(--yt-s2);\n color:var(--yt-text);\n font-size:12px;\n font-weight:600;\n cursor:pointer;\n }\n .ngxsp-yt-listForm button[disabled]{opacity:.5;cursor:default}\n .ngxsp-yt-listTarget{margin:0;color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-listTarget strong{margin-left:4px;color:var(--yt-text)}\n .ngxsp-yt-listRows{display:grid;gap:4px}\n .ngxsp-yt-listRow{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px;border-radius:var(--yt-r-sm)}\n .ngxsp-yt-listRow.active{background:var(--yt-s1)}\n .ngxsp-yt-listMain{\n display:flex;\n align-items:center;\n gap:8px;\n min-width:0;\n padding:6px 8px;\n border:0;\n border-radius:var(--yt-r-sm);\n background:transparent;\n color:var(--yt-text);\n text-align:left;\n cursor:pointer;\n }\n .ngxsp-yt-listMain span{display:grid;min-width:0}\n .ngxsp-yt-listMain strong{font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-listMain small{color:var(--yt-text-3);font-size:11px}\n .ngxsp-yt-listMain:hover{background:var(--yt-s2)}\n .ngxsp-yt-listActions{display:flex;gap:2px}\n .ngxsp-yt-listActions button{\n flex:0 0 32px;\n width:32px;\n height:32px;\n display:grid;\n place-items:center;\n border:0;\n border-radius:999px;\n background:transparent;\n color:var(--yt-text-2);\n cursor:pointer;\n }\n .ngxsp-yt-listActions button:hover{background:var(--yt-s2);color:var(--yt-text)}\n .ngxsp-yt-listActions button.danger:hover{color:var(--yt-danger, #e5484d)}\n .ngxsp-yt-listRow .ngxsp-yt-listForm{grid-column:1 / -1;padding:0 8px 6px}\n .ngxsp-yt-listEmpty{margin:0;color:var(--yt-text-3);font-size:12px}\n .ngxsp-yt-countBadge{min-width:24px;height:22px;display:grid;place-items:center;padding:0 7px;border-radius:999px;background:var(--yt-s3);color:var(--yt-text-2);font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}\n .ngxsp-yt-panelTitle{display:block;color:var(--yt-text-2);font-size:12px;font-weight:600;}\n\n /* Search */\n .ngxsp-yt-searchWorkspace{flex:0 0 auto;display:grid;gap:12px;padding:16px;border:1px solid var(--yt-line);border-radius:var(--yt-r-lg);background:var(--yt-s1)}\n .ngxsp-yt-searchHead{display:flex;align-items:center;justify-content:space-between;gap:12px}\n .ngxsp-yt-searchHead > div{min-width:0;display:grid;gap:2px}\n .ngxsp-yt-searchHead strong{font-size:16px;font-weight:600}\n .ngxsp-yt-searchHead small{color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-searchHead button{width:32px;height:32px;display:grid;place-items:center;padding:0;border:0;border-radius:var(--yt-r-sm);background:transparent;color:var(--yt-text-2)}\n .ngxsp-yt-searchHead button:hover{background:var(--yt-s3);color:var(--yt-text)}\n .ngxsp-yt-searchFilters{display:flex;flex-wrap:wrap;gap:6px}\n .ngxsp-yt-searchFilters button{height:32px;display:inline-flex;align-items:center;gap:6px;padding:0 12px;border:1px solid var(--yt-line-strong);border-radius:999px;background:transparent;color:var(--yt-text-2);font-size:12px;font-weight:500}\n .ngxsp-yt-searchFilters button small{color:var(--yt-text-3);font-variant-numeric:tabular-nums}\n .ngxsp-yt-searchFilters button:hover:not(:disabled){color:var(--yt-text);border-color:var(--yt-text-3)}\n .ngxsp-yt-searchFilters button.active{border-color:transparent;background:var(--yt-text);color:var(--yt-bg)}\n .ngxsp-yt-searchFilters button.active small{color:var(--yt-bg)}\n .ngxsp-yt-searchFilters button:disabled{opacity:.4;cursor:default}\n .ngxsp-yt-searchResults{display:grid;grid-template-columns:repeat(auto-fill, minmax(260px, 1fr));gap:8px}\n .ngxsp-yt-searchResults.list{grid-template-columns:1fr}\n .ngxsp-yt-searchResult{position:relative;min-width:0;border-radius:var(--yt-r-md);transition:background-color var(--yt-dur) var(--yt-ease)}\n .ngxsp-yt-searchResult:hover,\n .ngxsp-yt-searchResult:focus-within{background:var(--yt-s2)}\n .ngxsp-yt-searchResult.active{background:var(--yt-accent-soft)}\n .ngxsp-yt-searchResultMain{width:100%;min-width:0;display:grid;grid-template-columns:128px minmax(0,1fr);align-items:center;gap:12px;padding:8px;border:0;border-radius:inherit;background:transparent;text-align:left}\n .ngxsp-yt-searchThumb{position:relative;aspect-ratio:16 / 9;overflow:hidden;border-radius:6px;background:var(--yt-s3)}\n .ngxsp-yt-searchCopy{min-width:0;display:grid;gap:2px}\n .ngxsp-yt-searchCopy small{color:var(--yt-accent);font-size:11px;font-weight:600}\n .ngxsp-yt-searchCopy strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500}\n .ngxsp-yt-searchCopy em,\n .ngxsp-yt-searchCopy span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--yt-text-2);font-size:12px;font-style:normal}\n .ngxsp-yt-searchActions{position:absolute;right:8px;bottom:8px;display:flex;gap:2px;padding:2px;border-radius:8px;background:var(--yt-s3);opacity:0;transition:opacity var(--yt-dur) var(--yt-ease)}\n .ngxsp-yt-searchResult:hover .ngxsp-yt-searchActions,\n .ngxsp-yt-searchResult:focus-within .ngxsp-yt-searchActions{opacity:1}\n .ngxsp-yt-searchActions button{width:32px;height:32px;display:grid;place-items:center;padding:0;border:0;border-radius:6px;background:transparent;color:var(--yt-text-2)}\n .ngxsp-yt-searchActions button:hover{background:var(--yt-s2);color:var(--yt-text)}\n .ngxsp-yt-searchActions ngxsp-smart-icon{width:15px;height:15px}\n .ngxsp-yt-searchEmpty{grid-column:1 / -1;display:grid;justify-items:center;gap:6px;padding:28px 16px;color:var(--yt-text-2);text-align:center}\n .ngxsp-yt-searchEmpty ngxsp-smart-icon{width:24px;height:24px;color:var(--yt-text-3)}\n .ngxsp-yt-searchEmpty strong{color:var(--yt-text);font-weight:600}\n\n /* Media detail */\n .ngxsp-yt-mediaDetail{flex:0 0 auto;min-width:0;display:grid;gap:0;border:1px solid var(--yt-line);border-radius:var(--yt-r-lg);background:var(--yt-s1);overflow:hidden}\n .ngxsp-yt-mediaHero{display:grid;grid-template-columns:minmax(200px, 280px) minmax(0,1fr);gap:20px;padding:20px}\n .ngxsp-yt-mediaPoster{position:relative;align-self:start;aspect-ratio:16 / 9;overflow:hidden;display:grid;place-items:center;border-radius:var(--yt-r-md);background:linear-gradient(140deg, #4f6f94, #131d2a);box-shadow:var(--nsp-shadow-card, 0 12px 30px rgb(0 0 0 / .35))}\n .ngxsp-yt-mediaPoster span{color:rgb(255 255 255 / .8);font-size:40px;font-weight:700}\n .ngxsp-yt-mediaDetailBody{min-width:0;display:grid;align-content:start;gap:8px}\n .ngxsp-yt-mediaKicker{color:var(--yt-accent);font-size:12px;font-weight:600;}\n .ngxsp-yt-mediaDetailBody h2{font-size:18px;line-height:1.3;font-weight:600}\n .ngxsp-yt-mediaSubtitle{color:var(--yt-text);font-size:13px}\n .ngxsp-yt-mediaMeta{display:flex;flex-wrap:wrap;align-items:center;gap:4px 0;color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-mediaMeta span + span::before{content:'';display:inline-block;width:3px;height:3px;margin:0 8px 3px;border-radius:50%;background:var(--yt-text-3)}\n .ngxsp-yt-mediaSummary{max-width:72ch;color:var(--yt-text-2);font-size:13px;line-height:1.55}\n .ngxsp-yt-mediaProgress{display:flex;align-items:center;gap:10px;max-width:420px}\n .ngxsp-yt-mediaProgress > span{position:relative;flex:1;height:4px;border-radius:999px;background:var(--yt-s3);overflow:hidden}\n .ngxsp-yt-mediaProgress i{position:absolute;inset:0 auto 0 0;background:var(--yt-accent);border-radius:inherit}\n .ngxsp-yt-mediaProgress small{color:var(--yt-text-2);font-size:12px;white-space:nowrap}\n .ngxsp-yt-mediaTags{display:flex;flex-wrap:wrap;gap:6px}\n .ngxsp-yt-mediaTags span{height:22px;display:inline-flex;align-items:center;padding:0 8px;border-radius:6px;background:var(--yt-s3);color:var(--yt-text-2);font-size:11px;font-weight:500}\n .ngxsp-yt-mediaActions{display:flex;flex-wrap:wrap;gap:8px;margin-top:4px}\n .ngxsp-yt-mediaActions button,\n .ngxsp-yt-inspectorActions button,\n .ngxsp-yt-miniRestore{\n height:32px;\n display:inline-flex;\n align-items:center;\n justify-content:center;\n gap:7px;\n padding:0 12px 0 10px;\n border:1px solid var(--yt-line-strong);\n border-radius:var(--yt-r-sm);\n background:transparent;\n color:var(--yt-text);\n font-size:12px;\n font-weight:500;\n white-space:nowrap;\n }\n .ngxsp-yt-mediaActions button ngxsp-smart-icon,\n .ngxsp-yt-inspectorActions button ngxsp-smart-icon,\n .ngxsp-yt-miniRestore ngxsp-smart-icon{width:16px;height:16px}\n .ngxsp-yt-mediaActions button:hover:not(:disabled),\n .ngxsp-yt-inspectorActions button:hover:not(:disabled),\n .ngxsp-yt-miniRestore:hover{background:var(--yt-s3)}\n .ngxsp-yt-mediaActions button.primary,\n .ngxsp-yt-inspectorActions button.primary{border-color:transparent;background:var(--yt-accent-strong);color:var(--yt-on-accent)}\n .ngxsp-yt-mediaActions button.primary:hover:not(:disabled),\n .ngxsp-yt-inspectorActions button.primary:hover:not(:disabled){background:var(--yt-accent-strong);filter:brightness(1.1)}\n .ngxsp-yt-mediaActions button:disabled,\n .ngxsp-yt-inspectorActions button:disabled{opacity:.45;cursor:default}\n .ngxsp-yt-mediaWorkspace{display:grid;gap:14px;padding:16px 20px 20px;border-top:1px solid var(--yt-line)}\n .ngxsp-yt-mediaWorkspaceHead{display:grid;gap:2px}\n .ngxsp-yt-mediaWorkspaceHead strong{font-size:14px;font-weight:600}\n .ngxsp-yt-mediaWorkspaceHead small{color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-mediaWorkspaceGrid{display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:16px}\n .ngxsp-yt-chapterPanel,\n .ngxsp-yt-checkPanel{min-width:0;display:grid;align-content:start;gap:8px}\n .ngxsp-yt-chapterList,\n .ngxsp-yt-checkList{display:grid;gap:2px}\n .ngxsp-yt-mediaChapter{\n width:100%;\n min-width:0;\n display:grid;\n grid-template-columns:72px minmax(0,1fr);\n align-items:start;\n gap:12px;\n padding:9px 10px;\n border:0;\n border-radius:var(--yt-r-sm);\n background:transparent;\n text-align:left;\n transition:background-color var(--yt-dur) var(--yt-ease);\n }\n .ngxsp-yt-mediaChapter:hover{background:var(--yt-s2)}\n .ngxsp-yt-mediaChapter.active{background:var(--yt-accent-soft)}\n .ngxsp-yt-chapterTime{display:grid;gap:2px}\n .ngxsp-yt-chapterTime em{justify-self:start;padding:1px 6px;border-radius:4px;background:var(--yt-s3);color:var(--yt-text);font-family:var(--nsp-font-mono, ui-monospace, Menlo, monospace);font-size:11px;font-style:normal;font-variant-numeric:tabular-nums}\n .ngxsp-yt-mediaChapter.active .ngxsp-yt-chapterTime em{background:var(--yt-accent-strong);color:#fff}\n .ngxsp-yt-chapterTime b{color:var(--yt-text-2);font-size:11px;font-weight:500}\n .ngxsp-yt-chapterCopy{min-width:0;display:grid;gap:2px}\n .ngxsp-yt-chapterCopy strong{font-size:13px;font-weight:500}\n .ngxsp-yt-chapterCopy small{color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-checkItem{display:grid;grid-template-columns:8px minmax(0,1fr) auto;align-items:start;gap:12px;padding:9px 10px;border-radius:var(--yt-r-sm)}\n .ngxsp-yt-checkItem:hover{background:var(--yt-s2)}\n .ngxsp-yt-checkItem > span{--tone:var(--yt-text-3);width:8px;height:8px;margin-top:5px;border-radius:50%;background:var(--tone);box-shadow:0 0 0 3px color-mix(in srgb, var(--tone) 20%, transparent)}\n .ngxsp-yt-checkItem[data-tone=\"teal\"] > span{--tone:var(--nsp-tone-teal, #3fcfbf)}\n .ngxsp-yt-checkItem[data-tone=\"red\"] > span{--tone:var(--nsp-tone-red, #f47c7a)}\n .ngxsp-yt-checkItem[data-tone=\"blue\"] > span{--tone:var(--nsp-tone-blue, #7cb1ff)}\n .ngxsp-yt-checkItem[data-tone=\"gold\"] > span{--tone:var(--nsp-tone-gold, #f2bb5b)}\n .ngxsp-yt-checkItem > div{min-width:0;display:grid;gap:2px}\n .ngxsp-yt-checkItem strong{font-size:13px;font-weight:500}\n .ngxsp-yt-checkItem small{color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-checkItem em{color:var(--yt-text-2);font-size:12px;font-style:normal;white-space:nowrap}\n .ngxsp-yt-episodeRail{display:grid;gap:10px;padding:16px 20px 20px;border-top:1px solid var(--yt-line)}\n .ngxsp-yt-episodeRail > div{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(200px, 220px);gap:12px;overflow-x:auto;padding-bottom:4px}\n .ngxsp-yt-episodeRail button{min-width:0;display:grid;gap:8px;padding:6px;border:0;border-radius:var(--yt-r-md);background:transparent;text-align:left}\n .ngxsp-yt-episodeRail button:hover{background:var(--yt-s2)}\n .ngxsp-yt-episodeRail button.active{background:var(--yt-accent-soft)}\n .ngxsp-yt-episodeThumb{position:relative;aspect-ratio:16 / 9;overflow:hidden;display:grid;place-items:center;border-radius:6px;background:linear-gradient(140deg, #4f6f94, #131d2a)}\n .ngxsp-yt-episodeThumb > i{color:rgb(255 255 255 / .8);font-size:22px;font-weight:700;font-style:normal}\n .ngxsp-yt-episodeThumb > em{position:absolute;left:0;right:0;bottom:0;height:3px;background:rgb(255 255 255 / .2)}\n .ngxsp-yt-episodeThumb > em i{position:absolute;inset:0 auto 0 0;background:var(--yt-accent)}\n .ngxsp-yt-episodeRail button > span:last-child{min-width:0;display:grid;gap:1px}\n .ngxsp-yt-episodeRail strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500}\n .ngxsp-yt-episodeRail small,\n .ngxsp-yt-episodeRail button em{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--yt-text-2);font-size:12px;font-style:normal}\n\n /* Library */\n .ngxsp-yt-library{flex:0 0 auto;display:grid;gap:12px}\n /* One shelf per section, the same shape in every view: a name, how many, and the way to the rest. */\n .ngxsp-yt-shelf{display:grid;gap:8px}\n .ngxsp-yt-shelfHead{display:flex;align-items:baseline;gap:8px;min-width:0}\n /* A shelf can be called \"Classic cartoons - Internet Archive\" on a phone: the name gives way\n before the count and the way to the rest, which are what one reaches for. */\n .ngxsp-yt-shelfHead strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:700}\n .ngxsp-yt-shelfHead small,.ngxsp-yt-shelfAll{flex:0 0 auto}\n .ngxsp-yt-shelfHead small{color:var(--yt-text-2);font-size:11.5px;font-variant-numeric:tabular-nums}\n .ngxsp-yt-shelfAll{margin-left:auto;padding:2px 8px;border:0;border-radius:999px;background:transparent;color:var(--yt-accent-strong);font:inherit;font-size:11.5px;font-weight:600;cursor:pointer}\n .ngxsp-yt-shelfAll:hover,.ngxsp-yt-shelfAll:focus-visible{background:var(--yt-s2)}\n .ngxsp-yt-trail{display:flex;flex-wrap:wrap;align-items:center;gap:6px;font-size:12px;color:var(--yt-text-2)}\n .ngxsp-yt-trail b{color:var(--yt-text);font-weight:600}\n .ngxsp-yt-trailStep{padding:2px 6px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\n .ngxsp-yt-trailStep:hover,.ngxsp-yt-trailStep:focus-visible{background:var(--yt-s2);color:var(--yt-text)}\n .ngxsp-yt-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));gap:20px 16px}\n .ngxsp-yt-grid.list{grid-template-columns:1fr;gap:4px}\n /* A shelf is a row one runs along, not a wall: it scrolls sideways until Show all is asked,\n which is the moment the whole of it is wanted and a grid reads better. */\n .ngxsp-yt-grid.rail{\n /* One card, every screen: a phone shows two, a laptop six, a television four large ones\n from across a room. A fixed width would give the television forty small ones. */\n --yt-rail-card:clamp(168px, 17vw, 320px);\n display:flex;\n gap:16px;\n overflow-x:auto;\n overscroll-behavior-x:contain;\n scroll-snap-type:x proximity;\n /* The row runs out through the view's own gutter and is cut at its edge, not a hand's\n width inside it: a card half-hidden against the margin reads as broken, a card leaving\n the screen reads as a row that goes on. Scrolling brings them back past the gutter. */\n margin-inline:calc(var(--yt-gutter, 24px) * -1);\n padding-inline:var(--yt-gutter, 24px);\n scroll-padding-inline:var(--yt-gutter, 24px);\n padding-bottom:4px;\n scrollbar-width:thin;\n }\n .ngxsp-yt-grid.rail > *{flex:0 0 var(--yt-rail-card);scroll-snap-align:start}\n .ngxsp-yt-card{min-width:0;display:grid;align-content:start;gap:10px;padding:0;border:0;background:transparent;text-align:left}\n .ngxsp-yt-thumbnail{position:relative;aspect-ratio:16 / 9;overflow:hidden;border-radius:var(--yt-r-md);background:var(--yt-s2);box-shadow:0 0 0 1px var(--yt-line)}\n .ngxsp-yt-thumbnail img{transition:transform 400ms var(--yt-ease)}\n .ngxsp-yt-card:hover .ngxsp-yt-thumbnail img{transform:scale(1.04)}\n .ngxsp-yt-card.active .ngxsp-yt-thumbnail{box-shadow:0 0 0 2px var(--yt-accent)}\n .ngxsp-yt-cardCopy{min-width:0;display:grid;gap:2px;padding:0 2px}\n .ngxsp-yt-cardCopy strong{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;font-size:13px;font-weight:500;line-height:1.35}\n .ngxsp-yt-card:hover .ngxsp-yt-cardCopy strong{color:var(--yt-text)}\n .ngxsp-yt-card.active .ngxsp-yt-cardCopy strong{color:var(--yt-accent)}\n .ngxsp-yt-cardCopy span,\n .ngxsp-yt-cardCopy small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-cardCopy span{color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-cardCopy small{color:var(--yt-text-3);font-size:11px}\n .ngxsp-yt-grid.list .ngxsp-yt-card{grid-template-columns:160px minmax(0,1fr);align-items:center;gap:14px;padding:6px;border-radius:var(--yt-r-md)}\n .ngxsp-yt-grid.list .ngxsp-yt-card:hover{background:var(--yt-s1)}\n\n /* Side panel */\n .ngxsp-yt-sheetHeader{display:none}\n .ngxsp-yt-queueOpen{display:none}\n .ngxsp-yt-textButton{\n border:1px solid var(--yt-line);\n border-radius:var(--yt-r-sm);\n background:var(--yt-s2);\n color:var(--yt-text);\n font:inherit;\n font-size:13px;\n padding:6px 12px;\n cursor:pointer;\n }\n .ngxsp-yt-panelTabs{display:flex;flex-wrap:wrap;gap:6px;padding:12px 8px 8px}\n .ngxsp-yt-panelPill{padding:6px 11px;border:1px solid var(--yt-line);border-radius:999px;background:transparent;color:inherit;font:inherit;font-size:12px;line-height:1.2;cursor:pointer}\n .ngxsp-yt-panelPill.active{background:var(--yt-accent-strong);border-color:transparent;color:var(--yt-on-accent);font-weight:600}\n .ngxsp-yt-panelStack{display:flex;flex-direction:column;gap:10px;min-height:0}\n .ngxsp-yt-stackItem{border:1px solid var(--yt-line);border-radius:10px;overflow:hidden;display:flex;flex-direction:column;min-height:0}\n .ngxsp-yt-stackItem.collapsed{flex:0 0 auto}\n .ngxsp-yt-stackHead{display:flex;align-items:center;gap:8px;padding:7px 8px 7px 11px;background:var(--yt-surface-2,rgba(127,127,127,.08))}\n .ngxsp-yt-stackHead strong{font-size:12.5px;font-weight:600}\n .ngxsp-yt-stackHead small{color:var(--yt-text-2);font-size:11px}\n .ngxsp-yt-stackIcon{display:grid;place-items:center;width:24px;height:24px;padding:0;border:0;border-radius:6px;background:transparent;color:inherit;cursor:pointer;opacity:.65}\n .ngxsp-yt-stackIcon:hover,.ngxsp-yt-stackIcon:focus-visible{opacity:1}\n .ngxsp-yt-stackHead .ngxsp-yt-stackIcon:first-of-type{margin-left:auto}\n .ngxsp-yt-stackItem svg{width:14px;height:14px}\n .ngxsp-yt-suggestions{\n min-width:0;\n min-height:0;\n display:flex;\n flex-direction:column;\n border-left:1px solid var(--yt-line);\n background:var(--yt-s0);\n overflow:hidden;\n }\n .ngxsp-yt-cinema .ngxsp-yt-suggestions{border-left:0;border-top:1px solid var(--yt-line);background:transparent;overflow:visible;padding:0 8px 24px}\n .ngxsp-yt-suggestionList{flex:1;min-height:0;display:grid;align-content:start;gap:2px;padding:0 8px 12px;overflow:auto}\n .ngxsp-yt-cinema .ngxsp-yt-suggestionList{grid-template-columns:repeat(auto-fill, minmax(320px, 1fr));overflow:visible}\n .ngxsp-yt-suggestion{position:relative;min-width:0;border-radius:var(--yt-r-md);transition:background-color var(--yt-dur) var(--yt-ease)}\n .ngxsp-yt-suggestion:hover,\n .ngxsp-yt-suggestion:focus-within{background:var(--yt-s2)}\n .ngxsp-yt-suggestion.active{background:var(--yt-accent-soft)}\n .ngxsp-yt-suggestionMain{width:100%;min-width:0;display:grid;grid-template-columns:140px minmax(0,1fr);align-items:start;gap:12px;padding:8px;border:0;border-radius:inherit;background:transparent;text-align:left}\n .ngxsp-yt-suggestion.managed .ngxsp-yt-suggestionMain{padding-right:44px}\n .ngxsp-yt-suggestionThumb{position:relative;aspect-ratio:16 / 9;overflow:hidden;border-radius:6px;background:var(--yt-s3)}\n .ngxsp-yt-suggestionThumb .ngxsp-yt-nowBadge{left:5px;right:auto;top:5px;width:22px;height:22px}\n .ngxsp-yt-suggestionCopy{min-width:0;display:grid;gap:3px;padding-top:1px}\n .ngxsp-yt-suggestionCopy strong{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;font-size:13px;font-weight:500;line-height:1.3}\n .ngxsp-yt-suggestion.active .ngxsp-yt-suggestionCopy strong{color:var(--yt-accent)}\n .ngxsp-yt-suggestionCopy span,\n .ngxsp-yt-suggestionCopy small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-suggestionCopy span{color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-suggestionCopy small{color:var(--yt-text-3);font-size:11px}\n .ngxsp-yt-queueControls{position:absolute;top:4px;right:4px;display:grid;gap:0;opacity:0;transition:opacity var(--yt-dur) var(--yt-ease)}\n .ngxsp-yt-suggestion:hover .ngxsp-yt-queueControls,\n .ngxsp-yt-suggestion:focus-within .ngxsp-yt-queueControls,\n .ngxsp-yt-suggestion.active .ngxsp-yt-queueControls{opacity:1}\n .ngxsp-yt-queueControls button{width:32px;height:32px;display:grid;place-items:center;padding:0;border:0;border-radius:5px;background:transparent;color:var(--yt-text-2)}\n .ngxsp-yt-queueControls button:hover:not(:disabled){background:var(--yt-s3);color:var(--yt-text)}\n .ngxsp-yt-queueControls button:disabled{opacity:.3;cursor:default}\n .ngxsp-yt-queueControls ngxsp-smart-icon{width:15px;height:15px}\n .ngxsp-yt-backButton{display:inline-flex;align-items:center;gap:2px;max-width:100%;min-height:32px;margin:0 0 2px -6px;padding:4px 10px 4px 4px;border:0;border-radius:999px;background:transparent;color:var(--yt-text-2);font:inherit;font-size:12px;font-weight:600;cursor:pointer}\n .ngxsp-yt-backButton ngxsp-smart-icon{flex:none;width:16px;height:16px}\n .ngxsp-yt-backButton span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-backButton:hover{background:var(--yt-s2);color:var(--yt-text)}\n .ngxsp-yt-sideGroupTitle{grid-column:1 / -1;padding:12px 8px 4px;color:var(--yt-text-2);font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase}\n .ngxsp-yt-sideGroupTitle:first-child{padding-top:0}\n /* The side panel lists the section: the grid under the player would only repeat it. */\n @media (min-width:861px){\n .ngxsp-yt-sectionsBeside .ngxsp-yt-library{display:none}\n }\n .ngxsp-yt-emptyList{margin:8px;padding:28px 16px;border:1px dashed var(--yt-line-strong);border-radius:var(--yt-r-md);color:var(--yt-text-2);text-align:center}\n .ngxsp-yt-miniPlayer{\n flex:0 0 auto;\n display:grid;\n grid-template-columns:88px minmax(0,1fr) auto auto;\n align-items:center;\n gap:10px;\n margin:0 8px 8px;\n padding:8px;\n border:1px solid var(--yt-line);\n border-radius:var(--yt-r-md);\n background:var(--yt-s1);\n }\n .ngxsp-yt-cinema .ngxsp-yt-miniPlayer{max-width:420px}\n .ngxsp-yt-miniArtwork{position:relative;aspect-ratio:16 / 9;overflow:hidden;padding:0;border:0;border-radius:5px;background:var(--yt-s3)}\n .ngxsp-yt-miniPlayBadge{position:absolute;inset:0;display:grid;place-items:center;background:rgb(0 0 0 / .35);color:#fff;opacity:0;transition:opacity var(--yt-dur) var(--yt-ease)}\n .ngxsp-yt-miniArtwork:hover .ngxsp-yt-miniPlayBadge,\n .ngxsp-yt-miniPlayer.playing .ngxsp-yt-miniPlayBadge{opacity:1}\n .ngxsp-yt-miniDetails{min-width:0;display:grid;gap:1px}\n .ngxsp-yt-miniDetails small{color:var(--yt-text-2);font-size:11px;font-weight:600;}\n .ngxsp-yt-miniDetails strong,\n .ngxsp-yt-miniDetails span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-miniDetails strong{font-size:12px;font-weight:600}\n .ngxsp-yt-miniDetails span{color:var(--yt-text-2);font-size:11px}\n .ngxsp-yt-miniControl,\n .ngxsp-yt-miniClose{width:32px;height:32px;display:grid;place-items:center;padding:0;border:0;border-radius:999px;background:transparent;color:var(--yt-text-2)}\n .ngxsp-yt-miniControl{background:var(--yt-text);color:var(--yt-bg)}\n .ngxsp-yt-miniClose:hover{background:var(--yt-s3);color:var(--yt-text)}\n .ngxsp-yt-miniControl ngxsp-smart-icon,\n .ngxsp-yt-miniClose ngxsp-smart-icon{width:15px;height:15px}\n .ngxsp-yt-miniRestore{flex:0 0 auto;margin:0 16px 12px;color:var(--yt-text-2)}\n\n /* Inspector */\n .ngxsp-yt-inspector{flex:1;min-height:0;display:flex;flex-direction:column}\n .ngxsp-yt-inspectorHead{display:grid;grid-template-columns:36px minmax(0,1fr) auto;align-items:center;gap:10px;padding:14px 12px 12px 16px;border-bottom:1px solid var(--yt-line)}\n .ngxsp-yt-inspectorIcon{width:36px;height:36px;display:grid;place-items:center;border-radius:9px;background:var(--yt-accent-soft);color:var(--yt-accent)}\n .ngxsp-yt-inspectorTitle{min-width:0;display:grid;gap:1px}\n .ngxsp-yt-inspectorTitle strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600}\n .ngxsp-yt-inspectorTitle small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-inspectorClose{width:32px;height:32px;display:grid;place-items:center;padding:0;border:0;border-radius:var(--yt-r-sm);background:transparent;color:var(--yt-text-2)}\n .ngxsp-yt-inspectorClose:hover{background:var(--yt-s2);color:var(--yt-text)}\n .ngxsp-yt-inspectorScroll{flex:1;min-height:0;display:grid;align-content:start;gap:14px;padding:14px 16px;overflow:auto}\n .ngxsp-yt-inspectorScroll ngxsp-settings{\n --ngxsp-settings-surface:var(--yt-s2);\n --ngxsp-settings-text:var(--yt-text);\n --ngxsp-settings-muted:var(--yt-text-2);\n --ngxsp-settings-line:var(--yt-line);\n --ngxsp-settings-control:var(--yt-s3);\n --ngxsp-settings-accent:var(--yt-accent);\n --ngxsp-settings-radius:var(--yt-r-md);\n gap:20px;\n }\n .ngxsp-yt-inspectorStatus{color:var(--yt-text-2);font-size:13px;line-height:1.5}\n .ngxsp-yt-inspectorMetrics{display:grid;grid-template-columns:repeat(2, minmax(0,1fr));gap:8px}\n .ngxsp-yt-inspectorMetric{min-width:0;display:grid;gap:3px;padding:10px 12px;border:1px solid var(--yt-line);border-radius:var(--yt-r-md);background:var(--yt-s1)}\n .ngxsp-yt-inspectorMetric small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--yt-text-2);font-size:11px}\n .ngxsp-yt-inspectorMetric strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:15px;font-weight:600;font-variant-numeric:tabular-nums}\n .ngxsp-yt-inspectorMetric.success strong{color:var(--yt-success)}\n .ngxsp-yt-inspectorMetric.warning strong{color:var(--yt-warning)}\n .ngxsp-yt-inspectorList{display:grid;border:1px solid var(--yt-line);border-radius:var(--yt-r-md);overflow:hidden}\n .ngxsp-yt-inspectorItem{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:10px 12px;background:var(--yt-s1)}\n .ngxsp-yt-inspectorItem + .ngxsp-yt-inspectorItem{border-top:1px solid var(--yt-line)}\n .ngxsp-yt-inspectorItem > span{min-width:0;display:grid;gap:2px}\n .ngxsp-yt-inspectorItem strong{font-size:13px;font-weight:500}\n .ngxsp-yt-inspectorItem small{color:var(--yt-text-2);font-size:12px}\n .ngxsp-yt-inspectorItem em{flex:0 0 auto;max-width:45%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:1px 7px;border-radius:999px;background:var(--yt-s3);color:var(--yt-text-2);font-size:11px;font-style:normal;font-weight:500}\n .ngxsp-yt-inspectorItem.success em{background:rgb(62 207 142 / .14);color:var(--yt-success)}\n .ngxsp-yt-inspectorItem.warning em{background:rgb(242 169 59 / .14);color:var(--yt-warning)}\n /* Resume offer: a small card at the bottom left of the picture, above the controls. */\n .ngxsp-yt-resumePrompt{position:absolute;left:14px;bottom:84px;z-index:6;display:flex;align-items:center;flex-wrap:wrap;gap:8px;max-width:calc(100% - 28px);padding:8px 8px 8px 14px;border-radius:var(--yt-r-md);background:rgb(12 13 17 / .8);box-shadow:0 10px 30px rgb(0 0 0 / .35), 0 0 0 1px rgb(255 255 255 / .08);backdrop-filter:blur(12px);color:#f1f1f4;font-size:13px}\n .ngxsp-yt-resumePrompt span{margin-right:4px}\n .ngxsp-yt-resumePrompt button{height:32px;padding:0 12px;border:1px solid rgb(255 255 255 / .22);border-radius:999px;background:transparent;color:inherit;font:inherit;font-weight:600;cursor:pointer}\n .ngxsp-yt-resumePrompt button.primary{border-color:transparent;background:#fff;color:#111}\n .ngxsp-yt-resumePrompt button:hover{background:rgb(255 255 255 / .14)}\n .ngxsp-yt-resumePrompt button.primary:hover{background:rgb(255 255 255 / .86)}\n .ngxsp-yt-compact .ngxsp-yt-resumePrompt{left:8px;bottom:56px;font-size:12px}\n /* Info panel: a tinted glass column over the right of the picture. */\n .ngxsp-yt-playerFrame{container-type:inline-size;--yt-info-w:min(360px, 42cqw)}\n /* The play, seek and previous/next buttons move into the part of the picture left free. */\n .ngxsp-yt-infoOpen ngx-smart-player ::ng-deep .ngxsp-vod-centerDock{transform:translateX(calc(var(--yt-info-w) / -2 - 5px));transition:transform .2s ease}\n .ngxsp-yt-infoOverlay{position:absolute;top:10px;right:10px;bottom:76px;z-index:6;width:var(--yt-info-w);display:flex;flex-direction:column;border-radius:var(--yt-r-md);background:rgb(12 13 17 / .72);box-shadow:0 12px 40px rgb(0 0 0 / .4), 0 0 0 1px rgb(255 255 255 / .08);backdrop-filter:blur(14px) saturate(1.2);color:#f1f1f4;overflow:hidden}\n .ngxsp-yt-infoTabs{display:flex;align-items:center;gap:2px;padding:6px;border-bottom:1px solid rgb(255 255 255 / .1);overflow-x:auto;scrollbar-width:none}\n .ngxsp-yt-infoTabs button{flex:none;display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 10px;border:0;border-radius:var(--yt-r-sm);background:transparent;color:rgb(255 255 255 / .72);font:inherit;font-size:12px;white-space:nowrap;cursor:pointer}\n .ngxsp-yt-infoTabs button ngxsp-smart-icon{width:16px;height:16px}\n .ngxsp-yt-infoTabs button:hover{background:rgb(255 255 255 / .1);color:#fff}\n .ngxsp-yt-infoTabs button.active{background:rgb(255 255 255 / .14);color:#fff}\n .ngxsp-yt-infoTabs button:not(.active) span{display:none}\n .ngxsp-yt-infoTabs .ngxsp-yt-infoClose{margin-left:auto}\n .ngxsp-yt-infoHead{display:grid;gap:2px;padding:12px 16px 0}\n .ngxsp-yt-infoHead strong{font-size:15px;font-weight:600}\n .ngxsp-yt-infoHead small{color:rgb(255 255 255 / .65);font-size:12px}\n .ngxsp-yt-infoOverlay .ngxsp-yt-inspectorScroll{--yt-text:#f1f1f4;--yt-text-2:rgb(255 255 255 / .68);--yt-line:rgb(255 255 255 / .12);--yt-s1:rgb(255 255 255 / .06);--yt-s2:rgb(255 255 255 / .1)}\n .ngxsp-yt-infoOverlay .ngxsp-yt-inspectorActions{--yt-line:rgb(255 255 255 / .12);--yt-s1:rgb(255 255 255 / .06);--yt-text:#f1f1f4}\n /* The Infos button replaces the tool cards while the player has room; phones keep the cards, which open the sheet. */\n @media (min-width:861px){\n .ngxsp-yt-hasInfoTabs:not(.ngxsp-yt-sources) .ngxsp-yt-toolRow{display:none}\n }\n @media (max-width:860px){\n .ngxsp-yt-infoOverlay{display:none}\n }\n .ngxsp-yt-inspectorActions{display:flex;flex-wrap:wrap;gap:8px;padding:12px 16px 16px;border-top:1px solid var(--yt-line)}\n\n /* Drop overlay */\n /* Dimming is drawn over the picture: nothing on a screen can be made brighter than it is. */\n .ngxsp-yt-veil{position:absolute;inset:0;z-index:3;pointer-events:none;background:#000}\n .ngxsp-yt-touchBadge{\n position:absolute;\n left:50%;\n top:50%;\n z-index:6;\n transform:translate(-50%,-50%);\n display:grid;\n justify-items:center;\n gap:6px;\n min-width:112px;\n padding:14px 18px;\n pointer-events:none;\n border-radius:var(--yt-r-lg);\n background:rgb(0 0 0 / .72);\n color:#fff;\n }\n .ngxsp-yt-touchBadge strong{font-size:15px;font-weight:600;font-variant-numeric:tabular-nums}\n .ngxsp-yt-touchBadge em{display:block;width:88px;height:4px;border-radius:999px;background:rgb(255 255 255 / .28);overflow:hidden}\n .ngxsp-yt-touchBadge em i{display:block;height:100%;border-radius:999px;background:#fff}\n\n /* A card and the way in to what can be done to it. */\n .ngxsp-yt-cardWrap{position:relative;display:block;min-width:0}\n .ngxsp-yt-cardMore{\n position:absolute;\n top:8px;\n right:8px;\n z-index:2;\n display:grid;\n place-items:center;\n width:30px;\n height:30px;\n border:0;\n border-radius:50%;\n background:rgb(0 0 0 / .62);\n color:#fff;\n opacity:0;\n cursor:pointer;\n transition:opacity var(--yt-dur) var(--yt-ease);\n }\n /* Shown on approach, and always shown to a keyboard or a touch screen, which never hover. */\n .ngxsp-yt-cardWrap:hover .ngxsp-yt-cardMore,\n .ngxsp-yt-cardMore:focus-visible,\n .ngxsp-yt-cardWrap:focus-within .ngxsp-yt-cardMore{opacity:1}\n @media (hover:none){ .ngxsp-yt-cardMore{opacity:1} }\n .ngxsp-yt-cardMenuScrim{position:fixed;inset:0;z-index:60;padding:0;border:0;background:transparent;cursor:default}\n .ngxsp-yt-cardMenu{\n position:fixed;\n z-index:61;\n min-width:208px;\n max-width:280px;\n display:grid;\n gap:2px;\n padding:6px;\n border:1px solid var(--yt-line-strong);\n border-radius:var(--yt-r-md);\n background:var(--yt-s2);\n box-shadow:0 16px 40px rgb(0 0 0 / .48);\n }\n .ngxsp-yt-cardMenuTitle{\n padding:6px 10px 8px;\n font-size:11px;\n font-weight:600;\n color:var(--yt-text-3);\n overflow:hidden;\n text-overflow:ellipsis;\n white-space:nowrap;\n }\n .ngxsp-yt-cardMenu button{\n display:flex;\n align-items:center;\n gap:10px;\n min-height:34px;\n padding:0 10px;\n border:0;\n border-radius:var(--yt-r-sm);\n background:transparent;\n color:var(--yt-text);\n font-size:13px;\n text-align:left;\n cursor:pointer;\n }\n .ngxsp-yt-cardMenu button:hover{background:var(--yt-s3)}\n .ngxsp-yt-cardMenu button.danger{color:var(--yt-danger-text)}\n .ngxsp-yt-cardMenu button.danger:hover{background:rgb(242 95 92 / .16)}\n\n /* Above everything, out of the grid: the three menu layouts stay as they are. */\n .ngxsp-yt-convert{\n position:absolute;\n left:12px;\n right:12px;\n bottom:12px;\n z-index:45;\n display:flex;\n align-items:center;\n gap:12px;\n padding:10px 14px;\n border:1px solid var(--yt-line-strong);\n border-radius:var(--yt-r-lg);\n background:var(--yt-s2);\n box-shadow:0 12px 32px rgb(0 0 0 / .42);\n }\n .ngxsp-yt-convert.asking{border-color:var(--yt-accent)}\n /* Taking something away for good does not look like anything else in the shell. */\n .ngxsp-yt-convert.danger{border-color:var(--yt-danger)}\n .ngxsp-yt-convert.danger .ngxsp-yt-convertIcon{background:rgb(242 95 92 / .16);color:var(--yt-danger-text)}\n .ngxsp-yt-convert.danger .ngxsp-yt-convertActions button.keep{background:var(--yt-danger);color:var(--yt-on-accent)}\n .ngxsp-yt-convertIcon{display:grid;place-items:center;width:32px;height:32px;flex:0 0 auto;border-radius:50%;background:var(--yt-accent-soft);color:var(--yt-accent)}\n .ngxsp-yt-convertText{display:flex;flex-direction:column;gap:4px;min-width:0;flex:1 1 auto}\n .ngxsp-yt-convertText strong{font-size:13px;font-weight:600}\n .ngxsp-yt-convertText small{font-size:11px;color:var(--yt-text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-convertText em{display:block;height:4px;border-radius:999px;background:var(--yt-s3);overflow:hidden}\n .ngxsp-yt-convertText em i{display:block;height:100%;border-radius:999px;background:var(--yt-accent);transition:width var(--yt-dur) var(--yt-ease)}\n .ngxsp-yt-convertActions{display:flex;gap:8px;flex:0 0 auto}\n .ngxsp-yt-convertActions button{\n min-height:32px;\n padding:0 14px;\n border:1px solid var(--yt-line-strong);\n border-radius:999px;\n background:var(--yt-s1);\n color:var(--yt-text);\n font-size:12px;\n font-weight:600;\n cursor:pointer;\n }\n .ngxsp-yt-convertActions button.keep{border-color:transparent;background:var(--yt-accent-strong);color:var(--yt-on-accent)}\n /* Narrow: the question stacks rather than pushing its buttons off the edge. */\n @media (max-width:560px){\n .ngxsp-yt-convert{flex-wrap:wrap}\n .ngxsp-yt-convertActions{width:100%;justify-content:flex-end}\n }\n .ngxsp-yt-dropOverlay{\n position:absolute;\n inset:12px;\n z-index:40;\n display:grid;\n place-content:center;\n justify-items:center;\n gap:10px;\n border:2px dashed var(--yt-accent);\n border-radius:14px;\n background:var(--nsp-scrim, rgb(13 14 17 / .88));\n backdrop-filter:blur(6px);\n color:var(--yt-text);\n text-align:center;\n pointer-events:none;\n }\n .ngxsp-yt-dropIcon{width:56px;height:56px;display:grid;place-items:center;border-radius:14px;background:var(--yt-accent-soft);color:var(--yt-accent)}\n .ngxsp-yt-dropIcon ngxsp-smart-icon{width:28px;height:28px}\n .ngxsp-yt-dropOverlay strong{font-size:16px;font-weight:600}\n .ngxsp-yt-dropOverlay span:last-child{color:var(--yt-text-2)}\n\n @media (hover:none), (pointer:coarse){\n .ngxsp-yt-queueControls,\n .ngxsp-yt-searchActions{opacity:1}\n }\n\n @media (max-width:1380px){\n .ngxsp-yt-shell{--yt-side-w:320px}\n .ngxsp-yt-suggestionMain{grid-template-columns:120px minmax(0,1fr)}\n .ngxsp-yt-watchActions button > span{display:none}\n .ngxsp-yt-watchActions button{width:34px;padding:0;justify-content:center}\n .ngxsp-yt-rating button + button{padding-left:0}\n }\n\n @media (max-width:1180px){\n .ngxsp-yt-topbar{grid-template-columns:auto minmax(200px, 1fr) auto}\n .ngxsp-yt-status{display:none}\n .ngxsp-yt-sourceButton span{display:none}\n .ngxsp-yt-sourceButton{width:32px;padding:0;justify-content:center}\n .ngxsp-yt-mediaHero{grid-template-columns:200px minmax(0,1fr)}\n }\n\n @media (max-width:1040px) and (min-width:861px){\n .ngxsp-yt-body,\n .ngxsp-queue-hidden .ngxsp-yt-body{display:block;overflow:auto}\n .ngxsp-yt-watch{overflow:visible}\n .ngxsp-yt-suggestions{border-left:0;border-top:1px solid var(--yt-line);background:transparent;overflow:visible;padding:0 16px 24px}\n .ngxsp-yt-suggestionList{grid-template-columns:repeat(auto-fill, minmax(300px, 1fr));overflow:visible}\n .ngxsp-yt-playerFrame{width:100%}\n }\n\n /* Mobile */\n @media (max-width:860px){\n .ngxsp-yt-shell{\n height:auto;\n min-height:100%;\n grid-template-columns:minmax(0,1fr);\n grid-template-rows:auto auto auto;\n grid-template-areas:\n \"top\"\n \"rail\"\n \"body\";\n overflow:visible;\n }\n .ngxsp-yt-topbar{grid-template-columns:minmax(0,1fr) auto;grid-template-rows:48px auto;gap:0 8px;padding:0 12px 10px}\n .ngxsp-yt-search{grid-column:1 / -1;grid-row:2}\n .ngxsp-yt-railMenu{display:none}\n .ngxsp-yt-round,\n .ngxsp-yt-languageSwitch,\n .ngxsp-yt-status{display:none}\n /* The settings are in the rail and the menu has nothing to fold here, but what the\n application has to say is reached from this one button and no other, and so is the mini\n window: they stay. */\n .ngxsp-yt-notificationsButton,\n .ngxsp-yt-windowButton{display:grid}\n .ngxsp-yt-rail{flex-direction:row;padding:6px 12px;border-right:0;border-bottom:1px solid var(--yt-line);overflow-x:auto}\n /* Every view stays in the rail: it keeps its size and the row scrolls sideways to them. */\n .ngxsp-yt-railButton{width:auto;min-height:36px;flex:0 0 auto;flex-direction:row;gap:6px;padding:0 10px}\n .ngxsp-yt-railButton span{font-size:12px}\n .ngxsp-yt-body,\n .ngxsp-queue-hidden .ngxsp-yt-body,\n .ngxsp-yt-cinema .ngxsp-yt-body{display:block;overflow:visible}\n .sheet > .ngxsp-yt-sheetHeader{\n display:flex;\n align-items:center;\n justify-content:space-between;\n gap:12px;\n padding:12px 0;\n position:sticky;\n top:0;\n background:var(--yt-s0);\n z-index:1;\n }\n .ngxsp-yt-watch{--yt-gutter:12px;gap:14px;padding:12px;overflow:visible}\n .ngxsp-yt-playerFrame,\n .ngxsp-yt-cinema .ngxsp-yt-playerFrame{width:100%;min-width:0;border-radius:var(--yt-r-sm)}\n .ngxsp-yt-transportBar{padding:28px 6px 6px;gap:4px}\n .ngxsp-yt-transportInfo,\n .ngxsp-yt-transportCinema{display:none}\n .ngxsp-yt-transportBar{grid-template-columns:auto auto;justify-content:space-between}\n .ngxsp-yt-titleGroup h1{font-size:18px}\n .ngxsp-yt-watchActions > button:nth-of-type(n+3){display:none}\n .ngxsp-yt-rating button + button{display:none}\n /* A strip of every tool rather than a wall of four cards: the value line is what the\n panel behind the tool says anyway. */\n .ngxsp-yt-toolRow{display:flex;gap:8px;overflow-x:auto;scrollbar-width:none;padding-bottom:2px}\n .ngxsp-yt-toolRow::-webkit-scrollbar{display:none}\n .ngxsp-yt-toolRow .ngxsp-yt-tool{flex:0 0 auto;height:44px;grid-template-columns:24px auto;gap:8px}\n .ngxsp-yt-toolRow .ngxsp-yt-toolIcon{width:24px;height:24px}\n .ngxsp-yt-toolRow .ngxsp-yt-toolCopy strong{display:none}\n /* The strip scrolls, so whatever sits fifth is off the screen. Playing on a television\n or a speaker leads it here: it is the one tool a phone is most likely to want. */\n .ngxsp-yt-toolRow .ngxsp-yt-tool[data-tool=\"devices\"],\n .ngxsp-yt-toolRow .ngxsp-yt-tool[data-tool=\"cast\"]{order:-1}\n .ngxsp-yt-toolRow .ngxsp-yt-toolCopy small{color:var(--yt-text);font-size:13px}\n .ngxsp-yt-mediaHero{grid-template-columns:1fr;padding:14px}\n /* The player and the title bar already carry the poster, the title and the meta line. */\n .ngxsp-yt-mediaHero.echoesHeader .ngxsp-yt-mediaPoster,\n .ngxsp-yt-mediaHero.echoesHeader .ngxsp-yt-mediaKicker,\n .ngxsp-yt-mediaHero.echoesHeader .ngxsp-yt-mediaDetailBody > h2,\n .ngxsp-yt-mediaHero.echoesHeader .ngxsp-yt-mediaSubtitle,\n .ngxsp-yt-mediaHero.echoesHeader .ngxsp-yt-mediaMeta{display:none}\n .ngxsp-yt-mediaWorkspace{display:none}\n .ngxsp-yt-episodeRail{padding:14px}\n .ngxsp-yt-grid{grid-template-columns:repeat(2, minmax(0,1fr));gap:14px 10px}\n .ngxsp-yt-grid .ngxsp-yt-card:nth-child(n+5){display:none}\n .ngxsp-yt-searchResults{grid-template-columns:1fr}\n .ngxsp-yt-queueOpen{\n display:flex;\n align-items:center;\n gap:10px;\n width:100%;\n min-height:44px;\n padding:0 12px;\n border:1px solid var(--yt-line);\n border-radius:var(--yt-r-md);\n background:var(--yt-s1);\n color:var(--yt-text);\n font:inherit;\n font-size:14px;\n text-align:left;\n cursor:pointer;\n }\n .ngxsp-yt-queueOpen span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .ngxsp-yt-queueOpen small{color:var(--yt-text-2);font-size:12px}\n /* The queue is not part of the page here: it opens from the row above as a sheet\n over it, so the watch column keeps the screen to itself. */\n .ngxsp-yt-suggestions,\n .ngxsp-yt-cinema .ngxsp-yt-suggestions{display:none}\n .ngxsp-yt-suggestions.sheet,\n .ngxsp-yt-cinema .ngxsp-yt-suggestions.sheet{\n display:flex;\n position:fixed;\n inset:0;\n z-index:40;\n border:0;\n overflow-y:auto;\n background:var(--yt-s0);\n padding:env(safe-area-inset-top, 0px) 12px calc(24px + env(safe-area-inset-bottom, 0px));\n }\n .ngxsp-yt-suggestionList,\n .ngxsp-yt-cinema .ngxsp-yt-suggestionList{grid-template-columns:1fr;overflow:visible}\n /* The sheet shows the whole queue: there is room for it now. */\n .ngxsp-yt-suggestionList .ngxsp-yt-suggestion:nth-child(n+3){display:grid}\n .ngxsp-yt-suggestionMain{grid-template-columns:128px minmax(0,1fr)}\n .ngxsp-yt-miniPlayer{display:none}\n .ngxsp-yt-library-focus .ngxsp-yt-library{order:3}\n }\n\n @media (max-width:540px){\n .ngxsp-yt-brand strong{font-size:12px}\n .ngxsp-yt-mode button{padding:0 8px}\n .ngxsp-yt-railButton span{display:none}\n .ngxsp-yt-grid{grid-template-columns:1fr 1fr}\n }\n\n @media (prefers-reduced-motion:reduce){\n .ngxsp-yt-shell *,\n .ngxsp-yt-shell *::before,\n .ngxsp-yt-shell *::after{animation:none!important;transition:none!important}\n }\n\n /* Watching, in every layout. The compact ones let the page scroll for what sits under the player, which the\n player view does not have: left as a block of auto height, the area measures nothing and the picture with\n it. Last in the sheet so it wins over those overrides. */\n .ngxsp-yt-body.ngxsp-yt-playing{display:grid;grid-template-columns:minmax(0,1fr) var(--yt-side-w);overflow:hidden;min-height:0}\n .ngxsp-queue-hidden .ngxsp-yt-body.ngxsp-yt-playing{grid-template-columns:minmax(0,1fr)}\n /* A phone lets the page scroll, so the area has no height of its own to fill and a player asked for all of it\n measures nothing. There the picture keeps its shape across the width, as every phone player does. */\n /* A phone or a tablet playing something is a player and nothing else: the top bar and the\n rail leave, the picture takes the screen, and the way out is drawn on the picture. There\n is no room on those screens for a shelf beside a film, and none for a page under it. */\n @media (max-width: 1024px) {\n .ngxsp-yt-shell.ngxsp-yt-playerView .ngxsp-yt-topbar,\n .ngxsp-yt-shell.ngxsp-yt-playerView .ngxsp-yt-rail{display:none}\n /* Nothing above gives this a height on these screens — the page grew with its content —\n so the player view takes the window's, which is what a player is on a phone. dvh\n follows the browser's own bars as they come and go. */\n .ngxsp-yt-shell.ngxsp-yt-playerView{\n height:100vh;height:100dvh;min-height:0;\n grid-template-areas:none;grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr);\n }\n .ngxsp-yt-shell.ngxsp-yt-playerView .ngxsp-yt-body{grid-area:auto}\n .ngxsp-yt-shell.ngxsp-yt-playerView .ngxsp-yt-body{grid-template-columns:minmax(0,1fr);height:100%}\n .ngxsp-yt-shell.ngxsp-yt-playerView .ngxsp-yt-watch.player{height:100%;padding:0}\n .ngxsp-yt-shell.ngxsp-yt-playerView .ngxsp-yt-playerShell{flex:1 1 auto;min-height:0}\n .ngxsp-yt-shell.ngxsp-yt-playerView .ngxsp-yt-playerFrame{height:100%;aspect-ratio:auto;border-radius:0}\n .ngxsp-yt-shell.ngxsp-yt-playerView .ngxsp-yt-playerBack{display:inline-flex}\n }\n\n`],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NgxSmartPlayerVideoUiComponent {\n /**\n * Keeps what is playing in sight in the queue. One scrolls down a queue to see what is\n * coming, and the next video then starts out of view; it only scrolls when what plays\n * changes, and inside the list rather than moving the page under everything else.\n */\n ngAfterViewChecked(): void {\n const queue = this.queueListEl?.nativeElement;\n const playing = queue?.querySelector<HTMLElement>('.ngxsp-yt-suggestion.active');\n const key = playing && queue\n ? `${Array.prototype.indexOf.call(queue.children, playing)}:${queue.clientHeight}`\n : '';\n if (!queue || !playing || key === this.queueFollowed) return;\n this.queueFollowed = key;\n const reduced = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n // Measured against the list itself: offsetTop answers for whichever ancestor happens to be\n // positioned, which is not this one, and the answer is then a scroll to nowhere.\n const box = queue.getBoundingClientRect();\n const row = playing.getBoundingClientRect();\n queue.scrollTo({\n top: queue.scrollTop + (row.top - box.top) - (box.height - row.height) / 2,\n behavior: reduced ? 'auto' : 'smooth',\n });\n }\n\n constructor() {\n inject(DestroyRef).onDestroy(() => clearTimeout(this.resumePromptTimer));\n }\n\n @Input() config: PlayerConfig | null = null;\n /** The host app's logo in the masthead (an image URL); without it, a generic play tile. */\n @Input() brandLogoSrc = '';\n @Input() plugins: PlayerPlugin[] = [];\n @Input() ports?: Partial<PlayerPorts>;\n @Input() engines?: EngineFactory[];\n @Input() debug = false;\n @Input() startTimeSec: number | null = null;\n /** Incremented by the host when the viewer picks something to watch; the poster preview then gives way to the player. */\n @Input()\n set playRequest(value: number) {\n if (value > this.playRequestValue) this.posterPreviewDismissed = true;\n this.playRequestValue = value;\n }\n get playRequest(): number {\n return this.playRequestValue;\n }\n private playRequestValue = 0;\n @Input() canPrev = false;\n @Input() canNext = false;\n @Input() previousLabel = 'Previous video';\n @Input() playerBackLabel = 'Back';\n @Input() nextLabel = 'Next video';\n @Input() playLabel = 'Play';\n @Input() pauseLabel = 'Pause';\n @Input() transportLabel = 'Video playback controls';\n @Input() cinemaMode = false;\n @Input() cinemaLabel = 'Enter cinema mode';\n @Input() cinemaActiveLabel = 'Exit cinema mode';\n @Input() shuffleActive = false;\n @Input() shuffleLabel = 'Shuffle: off';\n @Input() repeatMode: 'off' | 'one' | 'all' = 'off';\n @Input() repeatLabel = 'Repeat: off';\n @Input() hasPlaylistControls = false;\n @Input() castEnabled = false;\n @Input() castConnected = false;\n /** Set when the video loads where it was left (see `VideoUiResumeOffer`). */\n @Input() set resumeOffer(offer: VideoUiResumeOffer | null) {\n if (offer?.key !== this.offer?.key) {\n this.clearResumePromptTimer();\n this.resumePromptShown = !!offer;\n }\n this.offer = offer;\n }\n get resumeOffer(): VideoUiResumeOffer | null {\n return this.offer;\n }\n private offer: VideoUiResumeOffer | null = null;\n resumePromptShown = false;\n private resumePromptTimer?: ReturnType<typeof setTimeout>;\n\n chooseResume(choice: 'resume' | 'restart'): void {\n if (choice === 'restart') this.smartPlayer?.seekTo(0);\n this.closeResumePrompt();\n this.resumeChoice.emit(choice);\n }\n\n private closeResumePrompt(): void {\n this.clearResumePromptTimer();\n this.resumePromptShown = false;\n this.cdr.markForCheck();\n }\n\n private clearResumePromptTimer(): void {\n clearTimeout(this.resumePromptTimer);\n this.resumePromptTimer = undefined;\n }\n\n /** Tabs of the info panel (library, engine, tracks, subtitles, progress, telemetry): their ids are panel ids. */\n @Input() infoTabs: VideoUiInfoTab[] = [];\n @Input() infoLabel = 'Info';\n /** The info panel open, shown over the video (in the sheet on phones); null when closed. */\n @Input() set infoPanel(panel: VideoUiInspectorPanel | null) {\n this.info = panel;\n if (panel && this.isSheetLayout()) this.queueSheetOpen = true;\n }\n get infoPanel(): VideoUiInspectorPanel | null {\n return this.info && !this.isSheetLayout() ? this.info : null;\n }\n private info: VideoUiInspectorPanel | null = null;\n /** The words of the player's menus (tracks, speed, subtitles). */\n @Input() playerMenuLabels: Partial<PlayerMenuLabels> = {};\n @Input() heroEyebrow = 'Featured';\n @Input() heroTitle = '';\n @Input() heroSubtitle = '';\n @Input() heroMeta = '';\n @Input() heroArtworkUrl = '';\n @Input() preferPosterPreview = true;\n @Input() runtimeModeLabel = 'Video runtime mode';\n @Input() onlineLabel = 'Online';\n @Input() offlineLabel = 'Offline';\n @Input() navigationLabel = 'Video navigation';\n @Input() menuLabel = 'Menu';\n @Input() searchPlaceholder = 'Search';\n @Input() searchLabel = 'Search videos';\n @Input() searchResultsTitle = 'Search results';\n @Input() searchResultsSummary = '';\n @Input() searchResultsEmptyTitle = 'No result';\n @Input() searchResultsEmptyBody = 'Try another title, format, or tag.';\n @Input() searchClearLabel = 'Clear search';\n @Input() searchFilterLabel = 'Filter search results';\n @Input() searchFilters: VideoUiSearchFilter[] = [];\n @Input() searchActionLabels: VideoUiSearchActionLabels = {};\n @Input() notificationsLabel = 'Notifications';\n @Input() settingsLabel = 'Player settings';\n @Input() sourceChangeLabel = 'Open file';\n @Input() sourceChangeTitle = '';\n @Input() sourceDropTitle = '';\n @Input() sourceDropHint = '';\n @Input() sourceChangeAccept = 'video/*';\n /** When set (desktop shell), the title bar offers folder import and keeps single-file import as a secondary action. */\n @Input() folderImportLabel = '';\n @Input() libraryStatus = '';\n /**\n * Sliding a finger up or down over the picture: brightness on the left half, sound on the\n * right. Off where it would get in the way — a pointer that only ever clicks, say.\n */\n @Input() touchAdjust = true;\n /** How bright the picture is, 10 to 100. Below 100 a veil is drawn over it; nothing is brightened past its own. */\n @Input() brightness = 100;\n @Output() brightnessChange = new EventEmitter<number>();\n /** \"More\": what opens a card's own menu, for a screen reader and for a tooltip. */\n @Input() cardActionsLabel = 'More';\n /** The way out of a card's menu, for whoever cannot see where the menu stops. */\n @Input() cardMenuCloseLabel = 'Close the menu';\n /** The action chosen on a card's menu: which action, on which video. */\n @Output() cardAction = new EventEmitter<{ actionId: string; cardId: string }>();\n /** The strip along the bottom: a question, or how far a long job has got. Null when there is nothing to say. */\n @Input() promptBar: VideoUiPromptBar | null = null;\n /** True when the viewer took the first way out, false for the second. */\n @Output() promptAnswer = new EventEmitter<boolean>();\n @Input() libraryBusy = false;\n @Input() sourceTypeLocalLabel = 'LOCAL';\n @Input() sourceTypeStreamLabel = 'STREAM';\n @Input() languageLabel = 'Language';\n @Input() activeLanguage = '';\n @Input() languageOptions: VideoUiLanguageOption[] = [];\n @Input() statusMessage = '';\n @Input() statusKind: VideoUiStatusKind = 'info';\n @Input() likeLabel = 'Like';\n @Input() likeActive = false;\n @Input() dislikeLabel = 'Dislike';\n @Input() shareLabel = 'Share';\n @Input() watchLaterLabel = 'Watch later';\n @Input() watchLaterActive = false;\n @Input() queueActionLabel = 'Show or hide queue';\n @Input() queueShortLabel = 'Queue';\n @Input() queueAddActiveLabel = 'Add to queue';\n @Input() queueAddActiveShortLabel = 'Add';\n @Input() queueActionLabels: VideoUiQueueActionLabels = {};\n @Input() moreLabel = 'More options';\n @Input() moreShortLabel = 'More';\n @Input() browseLibraryLabel = 'Browse library';\n /**\n * Where the view stands, from its own name down to what is open. Entering a series or a folder\n * is not another page: it is another step here, and everything else keeps its shape.\n */\n @Input() trail: VideoUiTrailStep[] = [];\n @Input() trailLabel = 'Where you are';\n @Input() showAllLabel = 'Show all';\n @Input() showLessLabel = 'Show less';\n @Input() emptyCatalogLabel = 'No catalog section available.';\n @Input() continueWatchingMeta = '';\n @Input() continueWatching: VideoUiMediaCard[] = [];\n @Input() managedQueue = false;\n @Input() upNextItems: VideoUiMediaCard[] = [];\n /** Lists the viewer created; null hides the block entirely. */\n @Input() playlistPanel: VideoUiPlaylistPanel | null = null;\n /** Media servers block above the library grid; null hides it entirely. */\n @Input() serverForm: VideoUiServerForm | null = null;\n @Input() showResumeProgress = false;\n @Input() mediaDetail: VideoUiMediaDetail | null = null;\n @Input() sidePanelTitle = 'Catalog';\n @Input() sidePanelMeta = '';\n @Input() sections: VideoUiSection[] = [];\n @Input() navItems: VideoUiNavItem[] = DEFAULT_VIDEO_NAV_ITEMS;\n @Input() toolCards: VideoUiToolCard[] = DEFAULT_VIDEO_TOOL_CARDS;\n /** The tools open their panel inside the queue column, which is a sheet on a phone. */\n @Input() set inspectorPanel(panel: VideoUiInspectorPanel | null) {\n this.inspector = panel;\n if (panel && this.isSheetLayout()) this.queueSheetOpen = true;\n }\n\n get inspectorPanel(): VideoUiInspectorPanel | null {\n return this.inspector;\n }\n\n /** What the side column shows: its own panel, or on phones the info panel. */\n get sidePanel(): VideoUiInspectorPanel | null {\n return this.inspector ?? (this.info && this.isSheetLayout() ? this.info : null);\n }\n\n private inspector: VideoUiInspectorPanel | null = null;\n\n /**\n * Every panel the host offers the side column. It used to hand one at a time, so a single\n * panel still works: it is the list of one.\n */\n @Input() inspectorPanels: VideoUiInspectorPanel[] = [];\n /** The queue's own switch, which used to be \"whatever showed when no panel did\". */\n queueOpen = true;\n private readonly collapsedPanels = new Set<string>();\n private readonly closedPanels = new Set<string>();\n\n get knownPanels(): VideoUiInspectorPanel[] {\n if (this.inspectorPanels.length) return this.inspectorPanels;\n return this.inspector ? [this.inspector] : [];\n }\n\n /** The ones actually stacked: what the host offers, minus what the viewer put away. */\n get openPanels(): VideoUiInspectorPanel[] {\n return this.knownPanels.filter((panel) => !this.closedPanels.has(panel.id));\n }\n\n isPanelOpen(id: string): boolean {\n if (id === 'queue') return this.queueOpen;\n return this.knownPanels.some((panel) => panel.id === id) && !this.closedPanels.has(id);\n }\n\n isPanelCollapsed(id: string): boolean {\n return this.collapsedPanels.has(id);\n }\n\n /** Folds a panel to its header, so several open ones stay usable without hiding any. */\n togglePanelCollapsed(id: string): void {\n if (!this.collapsedPanels.delete(id)) this.collapsedPanels.add(id);\n }\n\n togglePanel(id: string): void {\n if (id === 'queue') {\n this.queueOpen = !this.queueOpen;\n return;\n }\n if (this.closedPanels.delete(id)) this.collapsedPanels.delete(id);\n else this.closePanel(id);\n }\n\n /**\n * Puts a panel away. The host is told, since it is the one that decides what it offers; a host\n * that keeps offering it gets it back from the pill, with no round trip.\n */\n closePanel(id: string): void {\n this.closedPanels.add(id);\n this.panelClosed.emit(id);\n if (this.knownPanels.length <= 1) this.closeInspector.emit();\n }\n @Input() inspectorCloseLabel = 'Close panel';\n @Input() libraryVisible = true;\n /**\n * Whether the area shows the player. A host with views of its own shows it only while watching, and gives the whole\n * area to the view being browsed the rest of the time; the player then fills that area rather than sitting in it.\n */\n @Input() playerView = true;\n /**\n * Whether liking, sharing and keeping a video for later sit in the player's own controls. A host whose player has\n * the screen to itself needs them there: there is no page around it to put them on.\n */\n @Input() watchActionsInPlayer = false;\n /**\n * Whether the area leads with the video being watched — its title, its buttons, its details. A home view turns it\n * off so it opens on where videos come from, rather than on the last one that was played.\n */\n @Input() showActiveMedia = true;\n @Input() closeLabel = 'Close';\n @Input() queueVisible = false;\n /**\n * What the side panel lists: the queue, or the videos of `sections` (a library, a collection, a list) in full. With\n * `sections`, the library grid under the player gives way to the panel while the panel is beside it; on phones,\n * where the panel is a sheet, the grid stays and shows them all.\n */\n @Input() sidePanelContent: 'queue' | 'sections' = 'queue';\n /** \"Back to Lists\": shown above the side panel's title while something is open inside it; null at the top. */\n @Input() sideBackLabel: string | null = null;\n @Input() searchTerm = '';\n @Input() runtimeMode: VideoUiRuntimeMode = 'offline';\n /** False when the host offers the online/offline choice elsewhere (its settings). */\n @Input() runtimeModeInHeader = true;\n @Input()\n set activeViewMode(value: VideoUiViewMode) {\n this.viewMode = value || 'grid';\n }\n get activeViewMode(): VideoUiViewMode {\n return this.viewMode;\n }\n @Input() libraryTitle = 'Media library';\n @Input() nowPlayingLabel = 'Now playing';\n @Input() fullscreenHintLabel = 'Double-click for fullscreen';\n @Input() fullscreenLabel = 'Fullscreen';\n @Input() readyStatusLabel = 'Ready';\n @Input() playingStatusLabel = 'Playing';\n @Input() pausedStatusLabel = 'Paused';\n @Input() bufferingStatusLabel = 'Buffering';\n @Input() loadingStatusLabel = 'Loading';\n @Input() endedStatusLabel = 'Ended';\n @Input() errorStatusLabel = 'Error';\n @Input() miniPlayerVisible = true;\n @Input() miniPlayerMeta = '';\n @Input() miniPlayerPlayLabel = 'Play from mini player';\n @Input() miniPlayerPauseLabel = 'Pause from mini player';\n @Input() miniPlayerFocusLabel = 'Open current video';\n @Input() miniPlayerCloseLabel = 'Hide mini player';\n @Input() miniPlayerRestoreLabel = 'Show mini player';\n @Input() outputVolume = 100;\n @Input() outputVolumeLabel = 'Output volume';\n @Input() outputMuteLabel = 'Mute output';\n @Input() outputUnmuteLabel = 'Restore sound';\n\n // Public media output aliases intentionally mirror native media event names.\n // eslint-disable-next-line @angular-eslint/no-output-native\n @Output() ended = new EventEmitter<void>();\n @Output() videoSize = new EventEmitter<{ width: number; height: number }>();\n // eslint-disable-next-line @angular-eslint/no-output-native\n @Output() progress = new EventEmitter<SmartPlayerProgress>();\n /** The window's size (desktop): the whole app or a floating mini player ('bar' is an audio layout, shown in full). */\n @Input() compactMode: SmartCompactMode = 'full';\n /** True where the host can resize its window: shows the button that makes it a mini player. */\n @Input() compactModes = false;\n /**\n * Down the side, across the top, or away. A narrow window still takes the row whatever is\n * chosen — there is no side to put it down — but away stays away.\n */\n @Input() menuLayout: VideoUiMenuLayout = 'vertical';\n @Input() compactMiniLabel = 'Mini player';\n @Input() compactRestoreLabel = 'Full window';\n @Output() compactModeChange = new EventEmitter<SmartCompactMode>();\n /** \"Show in Finder\" for the video playing, where the host can show its file (desktop); empty hides the button. */\n @Input() revealFileLabel = '';\n @Output() revealFile = new EventEmitter<void>();\n @Output() prev = new EventEmitter<void>();\n @Output() next = new EventEmitter<void>();\n @Output() toggleShuffle = new EventEmitter<void>();\n @Output() cycleRepeat = new EventEmitter<void>();\n @Output() cast = new EventEmitter<void>();\n /** A subtitle file the viewer chose was read (ok), or could not be. */\n /** The Infos button: open the info panel on its last tab, or close it. */\n @Output() infoToggle = new EventEmitter<void>();\n /** Back out of what the side panel shows: a server folder, a list, a search (see `sideBackLabel`). */\n @Output() sideBack = new EventEmitter<void>();\n /** Leaving the player. Reaching it from the mini player left nothing behind to go back to. */\n @Output() playerBack = new EventEmitter<void>();\n /** The viewer's answer to `resumeOffer`: keep going from where it was, or start over (the player went back to 0). */\n @Output() resumeChoice = new EventEmitter<'resume' | 'restart'>();\n @Output() infoTabSelect = new EventEmitter<string>();\n @Output() subtitlesLoaded = new EventEmitter<{ label: string; ok: boolean; error?: string }>();\n @Output() selectContinueWatching = new EventEmitter<string>();\n @Output() selectSectionItem = new EventEmitter<{ sectionId: string; itemId: string }>();\n @Output() selectDetailEpisode = new EventEmitter<string>();\n @Output() chapterSeek = new EventEmitter<{ id: string; timeSec: number }>();\n @Output() navSelect = new EventEmitter<string>();\n @Output() toolSelect = new EventEmitter<string>();\n @Output() detailAction = new EventEmitter<string>();\n @Output() inspectorAction = new EventEmitter<string>();\n @Output() settingChange = new EventEmitter<SmartSettingChange>();\n @Output() settingAction = new EventEmitter<SmartSettingAction>();\n @Output() closeInspector = new EventEmitter<void>();\n /** Which panel the viewer put away, when the host offers several. */\n @Output() panelClosed = new EventEmitter<string>();\n @Output() openSource = new EventEmitter<void>();\n @Output() toggleLibrary = new EventEmitter<void>();\n /** A step of the trail the viewer asked to go back to. */\n @Output() trailStep = new EventEmitter<string>();\n @Output() toggleQueue = new EventEmitter<void>();\n @Output() toggleCinema = new EventEmitter<void>();\n @Output() miniPlayerFocus = new EventEmitter<void>();\n @Output() miniPlayerClose = new EventEmitter<void>();\n @Output() miniPlayerRestore = new EventEmitter<void>();\n @Output() queueAddActive = new EventEmitter<void>();\n @Output() connectServer = new EventEmitter<{ kind?: string; address: string; username: string; password: string }>();\n /** Looks for servers of this kind on the local network. */\n @Output() discoverServers = new EventEmitter<string>();\n @Output() connectDiscoveredServer = new EventEmitter<string>();\n @Output() selectServer = new EventEmitter<string>();\n @Output() disconnectServer = new EventEmitter<string>();\n @Output() createPlaylist = new EventEmitter<string>();\n @Output() selectPlaylist = new EventEmitter<string>();\n /** Files the panel's target item in the given list. */\n @Output() addToPlaylist = new EventEmitter<string>();\n /** Takes the panel's target out of the given list. */\n @Output() removeFromPlaylist = new EventEmitter<string>();\n @Output() renamePlaylist = new EventEmitter<{ id: string; name: string }>();\n @Output() deletePlaylist = new EventEmitter<string>();\n @Output() watchLaterToggle = new EventEmitter<void>();\n @Output() queueMoveUp = new EventEmitter<string>();\n @Output() queueMoveDown = new EventEmitter<string>();\n @Output() queueRemove = new EventEmitter<string>();\n @Output() searchFilterChange = new EventEmitter<string>();\n @Output() searchResultQueueAdd = new EventEmitter<string>();\n @Output() searchResultWatchLater = new EventEmitter<string>();\n @Output() searchResultFavorite = new EventEmitter<string>();\n @Output() searchResultDownload = new EventEmitter<string>();\n // eslint-disable-next-line @angular-eslint/no-output-native\n @Output() search = new EventEmitter<void>();\n @Output() searchTermChange = new EventEmitter<string>();\n @Output() viewModeChange = new EventEmitter<VideoUiViewMode>();\n @Output() runtimeModeChange = new EventEmitter<VideoUiRuntimeMode>();\n @Output() primaryPlay = new EventEmitter<void>();\n @Output() primaryPlaybackToggle = new EventEmitter<VideoUiPlaybackIntent>();\n @Output() playerStateChange = new EventEmitter<PlayerState>();\n @Output() sourceFileChange = new EventEmitter<File>();\n @Output() addFolder = new EventEmitter<void>();\n @Output() languageChange = new EventEmitter<string>();\n @Output() outputVolumeChange = new EventEmitter<number>();\n @Output() outputMuteToggle = new EventEmitter<void>();\n\n readonly waveformBars = WAVEFORM_BARS;\n searchOpen = false;\n railExpanded = false;\n fileDragActive = false;\n playerState: PlayerState | null = null;\n private posterPreviewDismissed = false;\n private viewMode: VideoUiViewMode = 'grid';\n private readonly cdr = inject(ChangeDetectorRef);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private fileDragDepth = 0;\n\n @ViewChild('cardMenuBox') private cardMenuBox?: ElementRef<HTMLElement>;\n @ViewChild(SmartPlayerComponent) private smartPlayer?: SmartPlayerComponent;\n private readonly document = inject(DOCUMENT);\n @ViewChild('contentScroll') private contentScroll?: ElementRef<HTMLElement>;\n @ViewChild('queueList') private queueListEl?: ElementRef<HTMLElement>;\n private queueFollowed = '';\n @ViewChild('videoFileInput') private videoFileInput?: ElementRef<HTMLInputElement>;\n\n // On phones the queue is a sheet over the page, opened from the queue button and closed from\n // its own header: move focus into it so Escape and the close button are reachable right away.\n @ViewChild('queuePanel') set queuePanel(panel: ElementRef<HTMLElement> | undefined) {\n if (!panel || !this.queueSheetOpen) return;\n setTimeout(() => panel.nativeElement.focus());\n }\n\n // The same viewport the mobile sheet styles use.\n private isSheetLayout(): boolean {\n return typeof matchMedia === 'function' && matchMedia('(max-width: 860px)').matches;\n }\n\n queueSheetOpen = false;\n\n get queuePanelActive(): boolean {\n return this.queueSheetOpen || (this.queueVisible && !this.isSheetLayout());\n }\n\n onQueueAction(): void {\n if (!this.isSheetLayout()) {\n this.toggleQueue.emit();\n return;\n }\n if (this.queueSheetOpen) {\n this.closeQueueSheet();\n return;\n }\n this.queueSheetOpen = true;\n }\n\n closeQueueSheet(): void {\n if (!this.queueSheetOpen) return;\n this.queueSheetOpen = false;\n // A tool panel opened this sheet: closing the sheet is how it is dismissed here.\n if (this.sidePanel) this.closeInspector.emit();\n setTimeout(() => {\n const buttons = Array.from(this.host.nativeElement.querySelectorAll<HTMLElement>('.ngxsp-yt-queueButton'));\n buttons.find((button) => button.offsetParent !== null)?.focus();\n });\n }\n\n /** True when the detail block only repeats the title bar above it, as it does for the playing media. */\n get detailEchoesHeader(): boolean {\n return this.mediaDetail?.title === this.displayHeroTitle;\n }\n\n get isPlaying(): boolean {\n return this.playerState?.kind === 'playing';\n }\n\n get isLiveSource(): boolean {\n return !!this.config?.source?.live;\n }\n\n get cinemaButtonLabel(): string {\n return this.cinemaMode ? this.cinemaActiveLabel : this.cinemaLabel;\n }\n\n get miniPlayerPlaybackLabel(): string {\n return this.isPlaying ? this.miniPlayerPauseLabel : this.miniPlayerPlayLabel;\n }\n\n get isOfflineMode(): boolean {\n return this.runtimeMode === 'offline';\n }\n\n get showPosterPreview(): boolean {\n return !!this.config && !!this.displayHeroArtwork && this.preferPosterPreview && !this.posterPreviewDismissed;\n }\n\n get sourceTypeLabel(): string {\n if (this.config?.source?.type) return this.config.source.type.toUpperCase();\n return this.isOfflineMode ? this.sourceTypeLocalLabel : this.sourceTypeStreamLabel;\n }\n\n get playbackStateLabel(): string {\n if (!this.config) return this.isOfflineMode ? 'Offline ready' : 'Online ready';\n return this.isLiveSource ? 'Live source' : 'On demand';\n }\n\n get primarySourceLabel(): string {\n return this.isOfflineMode ? 'Open folder' : 'Configure catalog';\n }\n\n get primarySourceShortLabel(): string {\n return this.isOfflineMode ? 'Open' : 'Catalog';\n }\n\n get primarySourceIcon(): SmartPlayerIconName {\n return this.isOfflineMode ? 'folder' : 'broadcast';\n }\n\n get emptyKicker(): string {\n return this.isOfflineMode ? 'Video Offline' : 'Video Online';\n }\n\n get emptyTitle(): string {\n return this.isOfflineMode ? 'Open a folder to start playback' : 'Connect a catalog to start streaming';\n }\n\n get emptyBody(): string {\n return this.isOfflineMode\n ? 'Build a local queue, resume progress, inspect tracks, subtitles, nearby devices, and offline eligibility from one focused player.'\n : 'Connect a remote catalog, validate VOD/live streams, adaptive manifests, subtitles, casting, and telemetry in the online player.';\n }\n\n get libraryMeta(): string {\n const count = this.itemCount;\n if (count) return `${count} item${count > 1 ? 's' : ''}`;\n return this.config ? this.sourceTypeLabel : 'No source selected';\n }\n\n get railMeta(): string {\n const count = this.railItems.length;\n return `${count} ready`;\n }\n\n get displayHeroTitle(): string {\n return this.heroTitle || this.activeCard?.item.title || 'NGXSP video cockpit';\n }\n\n get displayHeroSubtitle(): string {\n return this.heroSubtitle || this.activeCard?.item.subtitle || (this.isOfflineMode\n ? 'A production-grade offline shell for local folders, queues, resume state, subtitles, and device playback.'\n : 'A production-grade online shell for VOD, live streams, adaptive manifests, subtitles, casting, and telemetry.');\n }\n\n get displayHeroMeta(): string {\n return this.heroMeta || `${this.playbackStateLabel} | ${this.displayQueueItems.length} queued`;\n }\n\n get displayHeroArtwork(): string {\n return this.heroArtworkUrl || this.activeCard?.item.artworkUrl || this.featuredItems.find((card) => !!card.item.artworkUrl)?.item.artworkUrl || '';\n }\n\n get timelineMarkers(): NgxspTimelineMarker[] {\n const chapters = this.mediaDetail?.chapters ?? [];\n return chapters\n .flatMap((chapter) => {\n const timeSec = this.chapterTimeSec(chapter);\n if (!Number.isFinite(timeSec) || timeSec < 0) return [];\n const marker: NgxspTimelineMarker = {\n id: chapter.id,\n timeSec,\n title: chapter.title,\n };\n const label = chapter.time || chapter.label;\n if (label) marker.label = label;\n if (chapter.active !== undefined) marker.active = chapter.active;\n return [marker];\n });\n }\n\n get timelineDurationSec(): number | null {\n const durationSec = this.timeLabelToSeconds(this.activeCard?.item.duration);\n return Number.isFinite(durationSec) && durationSec > 0 ? durationSec : null;\n }\n\n get hasSearchQuery(): boolean {\n return this.searchTerm.trim().length > 0;\n }\n\n get searchResultItems(): VideoUiCardRef[] {\n if (!this.hasSearchQuery) return [];\n return this.sections\n .flatMap((section) => this.toRefs(section.id, section.items))\n .slice(0, 18)\n .map((card, index) => ({ ...card, index }));\n }\n\n private readonly expandedSections = new Set<string>();\n\n isSectionExpanded(id: string): boolean {\n return this.expandedSections.has(id);\n }\n\n /** Shows the rest of a shelf, or puts it back to a row. */\n toggleSection(id: string): void {\n if (!this.expandedSections.delete(id)) this.expandedSections.add(id);\n }\n\n /**\n * The view as a stack of titled shelves — one per section, empty ones left out, each holding a\n * row until it is asked for the rest. A view with nothing to shelve keeps what it showed before,\n * under no heading of its own.\n */\n get librarySections(): Array<{ id: string; title: string; count: number; titled: boolean; hidden: boolean; cards: VideoUiCardRef[] }> {\n const filled = this.sections.filter((section) => section.items.length);\n if (filled.length) {\n return filled.map((section) => {\n const cards = this.toRefs(section.id, section.items);\n const expanded = this.isSectionExpanded(section.id);\n return {\n id: section.id,\n title: section.title,\n count: section.items.length,\n // A lone section named after the view would say the same thing twice.\n titled: filled.length > 1 || section.title !== this.libraryTitle,\n hidden: cards.length > SHELF_WORTH_EXPANDING,\n cards: expanded ? cards : cards.slice(0, SHELF_RAIL_MAX),\n };\n });\n }\n const cards = this.continueWatching.length\n ? this.toRefs(CONTINUE_SECTION_ID, this.continueWatching.slice(0, SHELF_RAIL_MAX))\n : this.toRefs(FALLBACK_SECTION_ID, this.fallbackCards);\n if (!cards.length) return [];\n return [{ id: FALLBACK_SECTION_ID, title: '', count: cards.length, titled: false, hidden: false, cards }];\n }\n\n get featuredItems(): VideoUiCardRef[] {\n const sectionItems = this.sections.flatMap((section) => this.toRefs(section.id, section.items));\n if (sectionItems.length) return this.sidePanelContent === 'sections' ? sectionItems : sectionItems.slice(0, 8);\n if (this.continueWatching.length) return this.toRefs(CONTINUE_SECTION_ID, this.continueWatching.slice(0, 8));\n return this.toRefs(FALLBACK_SECTION_ID, this.fallbackCards);\n }\n\n get railItems(): VideoUiCardRef[] {\n if (this.continueWatching.length) return this.toRefs(CONTINUE_SECTION_ID, this.continueWatching.slice(0, 8));\n return this.featuredItems.slice(0, 8);\n }\n\n /** The sections listed by the side panel when it shows them (`sidePanelContent`). */\n get sideSectionGroups(): Array<{ id: string; title: string; cards: VideoUiCardRef[] }> {\n return this.sections.map((section) => ({ id: section.id, title: section.title, cards: this.toRefs(section.id, section.items) }));\n }\n\n /** How many videos the side panel lists. */\n get sidePanelCount(): number {\n return this.sidePanelContent === 'sections'\n ? this.sections.reduce((count, section) => count + section.items.length, 0)\n : this.displayQueueItems.length;\n }\n\n get displayQueueItems(): VideoUiCardRef[] {\n const managedItems = this.managedQueue ? this.toRefs(QUEUE_SECTION_ID, this.upNextItems) : [];\n const sectionItems = this.sections.flatMap((section) => this.toRefs(section.id, section.items));\n return (this.managedQueue ? managedItems : sectionItems.length ? sectionItems : this.featuredItems)\n .slice(0, 12)\n .map((card, index) => ({ ...card, index }));\n }\n\n get hasManagedQueue(): boolean {\n return this.managedQueue;\n }\n\n get isLibraryFocused(): boolean {\n const activeNavId = this.navItems.find((item) => item.active)?.id;\n return !!activeNavId && ['home', 'library', 'history', 'watch-later', 'favorites', 'downloads'].includes(activeNavId);\n }\n\n queueItemActionLabel(action: keyof VideoUiQueueActionLabels, card: VideoUiCardRef): string {\n const fallback: Required<VideoUiQueueActionLabels> = {\n play: 'Play from queue',\n moveUp: 'Move up in queue',\n moveDown: 'Move down in queue',\n remove: 'Remove from queue',\n };\n return `${this.queueActionLabels[action] ?? fallback[action]} - ${card.item.title}`;\n }\n\n searchResultActionLabel(action: keyof VideoUiSearchActionLabels, card: VideoUiCardRef): string {\n const fallback: Required<VideoUiSearchActionLabels> = {\n play: this.playLabel,\n queue: this.queueAddActiveLabel,\n watchLater: this.watchLaterLabel,\n favorite: this.likeLabel,\n download: 'Download',\n };\n return `${this.searchActionLabels[action] ?? fallback[action]} - ${card.item.title}`;\n }\n\n canMoveQueueItem(card: VideoUiCardRef, direction: 'up' | 'down'): boolean {\n if (!this.hasManagedQueue) return false;\n return direction === 'up' ? card.index > 0 : card.index < this.displayQueueItems.length - 1;\n }\n\n toggleRail(): void {\n this.railExpanded = !this.railExpanded;\n }\n\n selectNav(item: VideoUiNavItem): void {\n if (item.disabled) return;\n this.navSelect.emit(item.id);\n }\n\n selectViewMode(mode: VideoUiViewMode): void {\n if (this.activeViewMode === mode) return;\n this.viewMode = mode;\n this.viewModeChange.emit(mode);\n }\n\n selectSearchFilter(filter: VideoUiSearchFilter): void {\n if (filter.disabled || filter.active) return;\n this.searchFilterChange.emit(filter.id);\n }\n\n selectRuntimeMode(mode: VideoUiRuntimeMode): void {\n if (this.runtimeMode === mode) return;\n this.runtimeMode = mode;\n this.runtimeModeChange.emit(mode);\n }\n\n onPrimarySourceAction(): void {\n if (this.isOfflineMode) {\n this.openSource.emit();\n return;\n }\n this.toolSelect.emit('online-catalog');\n }\n\n playlistDraftName = '';\n playlistRenameDraft = '';\n renamingPlaylistId: string | null = null;\n serverKindDraft = '';\n serverAddressDraft = '';\n serverUsernameDraft = '';\n serverPasswordDraft = '';\n\n selectedServerKind(form: VideoUiServerForm): VideoUiServerKind | undefined {\n const id = this.serverKindDraft || form.kinds?.[0]?.id;\n return form.kinds?.find((kind) => kind.id === id);\n }\n\n onServerKindChange(event: Event): void {\n this.serverKindDraft = (event.target as HTMLSelectElement | null)?.value ?? '';\n }\n\n onServerAddressInput(event: Event): void {\n this.serverAddressDraft = (event.target as HTMLInputElement | null)?.value ?? '';\n }\n\n onServerUsernameInput(event: Event): void {\n this.serverUsernameDraft = (event.target as HTMLInputElement | null)?.value ?? '';\n }\n\n onServerPasswordInput(event: Event): void {\n this.serverPasswordDraft = (event.target as HTMLInputElement | null)?.value ?? '';\n }\n\n submitServer(event: Event): void {\n event.preventDefault();\n const address = this.serverAddressDraft.trim();\n if (!address) return;\n const kind = this.serverForm ? this.selectedServerKind(this.serverForm) : undefined;\n const credentials = kind?.credentials !== false;\n this.connectServer.emit({\n ...(kind ? { kind: kind.id } : {}),\n address,\n username: credentials ? this.serverUsernameDraft.trim() : '',\n password: credentials ? this.serverPasswordDraft : '',\n });\n // The password is handed over once and never kept in the form.\n this.serverPasswordDraft = '';\n }\n\n onPlaylistDraftInput(event: Event): void {\n this.playlistDraftName = (event.target as HTMLInputElement | null)?.value ?? '';\n }\n\n submitCreatePlaylist(event: Event): void {\n event.preventDefault();\n const name = this.playlistDraftName.trim();\n if (!name) return;\n this.createPlaylist.emit(name);\n this.playlistDraftName = '';\n }\n\n startRenamePlaylist(list: VideoUiPlaylistOption): void {\n this.renamingPlaylistId = list.id;\n this.playlistRenameDraft = list.name;\n }\n\n onPlaylistRenameInput(event: Event): void {\n this.playlistRenameDraft = (event.target as HTMLInputElement | null)?.value ?? '';\n }\n\n submitRenamePlaylist(event: Event): void {\n event.preventDefault();\n const name = this.playlistRenameDraft.trim();\n if (!name || !this.renamingPlaylistId) return;\n this.renamePlaylist.emit({ id: this.renamingPlaylistId, name });\n this.cancelRenamePlaylist();\n }\n\n cancelRenamePlaylist(): void {\n this.renamingPlaylistId = null;\n this.playlistRenameDraft = '';\n }\n\n selectCard(card: VideoUiCardRef): void {\n if (this.isSyntheticSection(card.sectionId)) {\n this.toolSelect.emit(card.item.id);\n return;\n }\n if (card.sectionId === CONTINUE_SECTION_ID) {\n this.selectContinueWatching.emit(card.item.id);\n return;\n }\n this.selectSectionItem.emit({ sectionId: card.sectionId, itemId: card.item.id });\n }\n\n selectMediaChapter(chapter: VideoUiMediaChapter): void {\n const timeSec = this.chapterTimeSec(chapter);\n if (this.smartPlayer && Number.isFinite(timeSec) && timeSec >= 0) {\n this.smartPlayer.seekTo(timeSec);\n this.chapterSeek.emit({ id: chapter.id, timeSec });\n }\n this.detailAction.emit(chapter.actionId || chapter.id);\n }\n\n private chapterTimeSec(chapter: VideoUiMediaChapter): number {\n const explicit = Number(chapter.timeSec);\n if (Number.isFinite(explicit) && explicit >= 0) return explicit;\n return this.timeLabelToSeconds(chapter.time);\n }\n\n private timeLabelToSeconds(value: string | undefined): number {\n if (!value) return Number.NaN;\n const parts = value.trim().split(':').map((part) => Number(part));\n if (parts.some((part) => !Number.isFinite(part) || part < 0)) return Number.NaN;\n if (parts.length === 2) return (parts[0] * 60) + parts[1];\n if (parts.length === 3) return (parts[0] * 3600) + (parts[1] * 60) + parts[2];\n return Number.NaN;\n }\n\n isSyntheticSection(sectionId: string): boolean {\n return sectionId === FALLBACK_SECTION_ID;\n }\n\n private get itemCount(): number {\n return this.sections.reduce((total, section) => total + section.items.length, 0);\n }\n\n private get fallbackCards(): VideoUiMediaCard[] {\n return this.isOfflineMode ? OFFLINE_VIDEO_CARDS : ONLINE_VIDEO_CARDS;\n }\n\n private get activeCard(): VideoUiCardRef | undefined {\n return [...this.displayQueueItems, ...this.railItems, ...this.featuredItems].find((card) => card.item.active);\n }\n\n private toRefs(sectionId: string, items: VideoUiMediaCard[]): VideoUiCardRef[] {\n return items.map((item, index) => ({ sectionId, item, index }));\n }\n\n toggleSearch(): void {\n this.searchOpen = !this.searchOpen;\n this.search.emit();\n }\n\n clearSearch(): void {\n this.searchOpen = false;\n this.searchFilterChange.emit('all');\n if (this.searchTerm) this.searchTermChange.emit('');\n }\n\n onSearchInput(event: Event): void {\n this.searchTermChange.emit((event.target as HTMLInputElement).value);\n }\n\n togglePrimaryPlayback(): void {\n if (this.config && this.showPosterPreview) {\n this.posterPreviewDismissed = true;\n this.cdr.detectChanges();\n this.setOutputVolumePercent(this.outputVolume);\n this.primaryPlaybackToggle.emit('play');\n this.cdr.markForCheck();\n return;\n }\n\n if (this.config && this.smartPlayer) {\n const intent: VideoUiPlaybackIntent = this.isPlaying ? 'pause' : 'play';\n this.smartPlayer.togglePlay();\n this.primaryPlaybackToggle.emit(intent);\n return;\n }\n this.primaryPlay.emit();\n }\n\n toggleMiniPlayerPlayback(): void {\n this.togglePrimaryPlayback();\n }\n\n /**\n * How far the area is scrolled. A host that moves between views reads it before leaving one, so coming back\n * can put the viewer where they were rather than at the top of a list they had already walked down. On a\n * narrow layout the page scrolls instead of the panel, so whichever one moved is the one reported.\n */\n contentScrollTop(): number {\n const panel = this.contentScroll?.nativeElement;\n if (panel && panel.scrollTop > 0) return panel.scrollTop;\n return this.document.defaultView?.scrollY ?? 0;\n }\n\n /** Puts the area back where it was, or at the top with 0 on entering a view for the first time. */\n setContentScrollTop(value: number): void {\n const top = Number.isFinite(value) && value > 0 ? value : 0;\n const panel = this.contentScroll?.nativeElement;\n // After the new view's rows exist, otherwise there is nothing tall enough to scroll through yet.\n requestAnimationFrame(() => {\n if (panel) panel.scrollTop = top;\n this.document.defaultView?.scrollTo({ top, behavior: 'auto' });\n });\n }\n\n /** Moves the playhead, for a host driven from outside the page: a lock screen scrubber, a remote, another device. */\n seekTo(timeSec: number): void {\n if (Number.isFinite(timeSec) && timeSec >= 0) this.smartPlayer?.seekTo(timeSec);\n }\n\n setOutputVolumePercent(value: number): void {\n this.smartPlayer?.setOutputVolume(this.normalizeOutputVolume(value) / 100);\n }\n\n onOutputVolumeRatioChange(value: number): void {\n const nextVolume = this.normalizeOutputVolume(value * 100);\n this.setOutputVolumePercent(nextVolume);\n this.outputVolumeChange.emit(nextVolume);\n }\n\n openSourcePicker(): void {\n this.videoFileInput?.nativeElement.click();\n }\n\n onSourceFileInput(event: Event): void {\n const input = event.target as HTMLInputElement | null;\n const file = input?.files?.[0];\n if (file) this.sourceFileChange.emit(file);\n if (input) {\n input.value = '';\n input.blur();\n }\n }\n\n onFileDragEnter(event: DragEvent): void {\n if (!this.dragHasFiles(event)) return;\n event.preventDefault();\n this.fileDragDepth += 1;\n this.fileDragActive = true;\n this.cdr.markForCheck();\n }\n\n onFileDragOver(event: DragEvent): void {\n if (!this.dragHasFiles(event)) return;\n event.preventDefault();\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy';\n }\n\n onFileDragLeave(event: DragEvent): void {\n if (!this.dragHasFiles(event)) return;\n event.preventDefault();\n this.fileDragDepth = Math.max(0, this.fileDragDepth - 1);\n if (this.fileDragDepth === 0) this.fileDragActive = false;\n this.cdr.markForCheck();\n }\n\n onFileDrop(event: DragEvent): void {\n if (!this.dragHasFiles(event)) return;\n event.preventDefault();\n this.fileDragDepth = 0;\n this.fileDragActive = false;\n const file = event.dataTransfer?.files?.[0];\n if (file) this.sourceFileChange.emit(file);\n this.cdr.markForCheck();\n }\n\n onPlayerStateChange(state: PlayerState): void {\n this.playerState = state;\n // The offer stays a few seconds of playback, then goes: resuming is what happens when nothing is chosen.\n if (this.resumePromptShown && state.kind === 'playing' && !this.resumePromptTimer) {\n this.resumePromptTimer = setTimeout(() => this.closeResumePrompt(), RESUME_PROMPT_MS);\n }\n this.playerStateChange.emit(state);\n this.cdr.markForCheck();\n }\n\n posterInitial(title: string): string {\n return (title.trim().charAt(0) || 'V').toUpperCase();\n }\n\n mediaCardMeta(card: VideoUiMediaCard): string {\n const values = [\n card.kindLabel ?? '',\n card.parentTitle ?? '',\n card.seasonLabel ?? '',\n card.releaseYear ?? '',\n card.meta ?? '',\n card.duration ?? '',\n ].filter(Boolean);\n return [...new Set(values)].join(' | ');\n }\n\n /** What a finger is changing right now, and where it stands. Null when nothing is being changed. */\n protected touchAdjusting: { kind: 'brightness' | 'volume'; value: number } | null = null;\n private touchStart: { x: number; y: number; kind: 'brightness' | 'volume'; from: number; pointerId: number } | null = null;\n /** A drag is not a click: the one that follows it is swallowed rather than reaching a control. */\n private swallowNextClick = false;\n /** How far the picture has to be crossed to go from nothing to everything. */\n private static readonly TOUCH_TRAVEL = 0.6;\n private static readonly TOUCH_THRESHOLD = 12;\n private static readonly MIN_BRIGHTNESS = 10;\n\n /**\n * A gesture starts on the picture, never on something built to be pressed: a button, a slider,\n * a menu. Those keep the events that belong to them.\n */\n protected onFramePointerDown(event: PointerEvent): void {\n this.touchStart = null;\n if (!this.touchAdjust || !this.config) return;\n const target = event.target as HTMLElement | null;\n if (target?.closest('button, input, select, a, [role=\"slider\"], [role=\"menu\"], [role=\"menuitem\"]')) return;\n const frame = event.currentTarget as HTMLElement;\n const box = frame.getBoundingClientRect();\n if (box.width <= 0) return;\n this.touchStart = {\n x: event.clientX,\n y: event.clientY,\n kind: event.clientX - box.left < box.width / 2 ? 'brightness' : 'volume',\n from: 0,\n pointerId: event.pointerId,\n };\n }\n\n /**\n * Nothing happens until the finger has travelled further down than across: a tap stays a tap,\n * and a sideways drag is somebody else's gesture.\n */\n protected onFramePointerMove(event: PointerEvent): void {\n const start = this.touchStart;\n if (!start || event.pointerId !== start.pointerId) return;\n const dx = event.clientX - start.x;\n const dy = event.clientY - start.y;\n if (!this.touchAdjusting) {\n if (Math.abs(dy) < NgxSmartPlayerVideoUiComponent.TOUCH_THRESHOLD || Math.abs(dy) <= Math.abs(dx)) return;\n start.from = start.kind === 'brightness' ? this.brightness : this.outputVolume;\n (event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);\n this.swallowNextClick = true;\n }\n event.preventDefault();\n const box = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const travel = Math.max(1, box.height * NgxSmartPlayerVideoUiComponent.TOUCH_TRAVEL);\n // Up is more, which is the way every other player and every phone has taught it.\n const floor = start.kind === 'brightness' ? NgxSmartPlayerVideoUiComponent.MIN_BRIGHTNESS : 0;\n const raw = start.from - (dy / travel) * 100;\n const value = Math.round(Math.max(floor, Math.min(100, raw)));\n this.touchAdjusting = { kind: start.kind, value };\n if (start.kind === 'brightness') {\n this.brightness = value;\n this.brightnessChange.emit(value);\n } else {\n this.setOutputVolumePercent(value);\n this.outputVolume = value;\n this.outputVolumeChange.emit(value);\n }\n }\n\n protected onFramePointerUp(event: PointerEvent): void {\n if (this.touchStart && event.pointerId === this.touchStart.pointerId) {\n (event.currentTarget as HTMLElement).releasePointerCapture?.(event.pointerId);\n }\n if (this.swallowNextClick) {\n this.swallowNextClick = false;\n this.swallowTheClickThatEndsADrag();\n }\n this.touchStart = null;\n this.touchAdjusting = null;\n }\n\n /**\n * Le clic qui clôt un glissement mettrait sinon en pause la vidéo dont le glissement ne\n * parlait pas. Posé ici plutôt que dans le gabarit : avaler un clic n'est pas une interaction,\n * et en capture il est arrêté avant d'atteindre la moindre commande.\n */\n private swallowTheClickThatEndsADrag(): void {\n const view = this.document.defaultView;\n if (!view) return;\n const swallow = (event: Event) => {\n event.preventDefault();\n event.stopPropagation();\n };\n view.addEventListener('click', swallow, { capture: true, once: true });\n // Rendu au bout d'un tour : un glissement qui ne produit aucun clic ne doit pas manger\n // le clic suivant, qui serait celui de quelqu'un d'autre.\n queueMicrotask(() => view.removeEventListener('click', swallow, { capture: true }));\n }\n\n /** The card menu while it is open: which card, and where the gesture put it. */\n protected cardMenu: { card: VideoUiMediaCard; x: number; y: number } | null = null;\n\n /**\n * Opens a card's own menu where the gesture happened — a right click, or the button that\n * appears on the card — and keeps it inside the window rather than off its edge.\n */\n protected openCardMenu(event: MouseEvent, card: VideoUiMediaCard): void {\n if (!card.actions?.length) return;\n event.preventDefault();\n event.stopPropagation();\n const width = 280;\n const height = 60 + card.actions.length * 36;\n const view = this.document.defaultView;\n const maxX = (view?.innerWidth ?? width) - width - 8;\n const maxY = (view?.innerHeight ?? height) - height - 8;\n this.cardMenu = {\n card,\n x: Math.max(8, Math.min(event.clientX, Math.max(8, maxX))),\n y: Math.max(8, Math.min(event.clientY, Math.max(8, maxY))),\n };\n // Le menu prend le focus : sans cela, Échap ne lui parvenait jamais après un clic droit,\n // et il n'y avait aucun moyen d'en sortir au clavier.\n queueMicrotask(() => this.cardMenuBox?.nativeElement.focus());\n }\n\n protected closeCardMenu(): void {\n this.cardMenu = null;\n }\n\n protected chooseCardAction(action: VideoUiCardAction): void {\n const card = this.cardMenu?.card;\n this.closeCardMenu();\n if (card) this.cardAction.emit({ actionId: action.id, cardId: card.id });\n }\n\n /** Whether the strip is waiting for an answer rather than reporting: both ways out have a name. */\n get promptAsking(): boolean {\n const prompt = this.promptBar;\n return !!prompt?.confirmLabel && !!prompt?.cancelLabel;\n }\n\n safeProgressPercent(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.max(0, Math.min(100, value));\n }\n\n artVariant(card: VideoUiCardRef): string {\n const variants = ['aurora', 'orbit', 'ember', 'ridge', 'signal', 'fog'];\n const seed = `${card.sectionId}:${card.item.id}:${card.item.title}`;\n let hash = 0;\n for (let i = 0; i < seed.length; i += 1) {\n hash = (hash * 31 + seed.charCodeAt(i)) >>> 0;\n }\n return variants[hash % variants.length];\n }\n\n private normalizeOutputVolume(value: number): number {\n return Math.max(0, Math.min(100, Math.round(Number.isFinite(value) ? value : this.outputVolume)));\n }\n\n private dragHasFiles(event: DragEvent): boolean {\n return Array.from(event.dataTransfer?.types ?? []).includes('Files');\n }\n}\n","import { NgModule } from '@angular/core';\nimport { NgxSmartPlayerVideoUiComponent } from './video-player-ui.component';\n\n@NgModule({\n imports: [NgxSmartPlayerVideoUiComponent],\n exports: [NgxSmartPlayerVideoUiComponent],\n})\nexport class NgxSmartPlayerVideoUiModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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;;sBAEA;;sBACA;;sBAKA;;sBAEA;;sBACA;;sBAEA;;sBAEA;;sBAEA;;sBAEA;;sBAEA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAKA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBAEA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBAoBA;;sBAgDA;;sBACA;;sBAKA;;sBAKA;;sBAKA;;sBACA;;sBACA;;sBAMA;;sBAEA;;sBACA;;sBACA;;sBAEA;;sBACA;;sBAOA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAIA;;sBACA;;sBAEA;;sBAEA;;sBAEA;;sBAKA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAGA;;sBAEA;;sBAEA;;sBAEA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBACA;;sBACA;;sBAEA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBAEA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAaA,SAAS;uBAAC,aAAa;;sBACvB,SAAS;uBAAC,oBAAoB;;sBAE9B,SAAS;uBAAC,eAAe;;sBACzB,SAAS;uBAAC,WAAW;;sBAErB,SAAS;uBAAC,gBAAgB;;sBAI1B,SAAS;uBAAC,YAAY;;;MCx0GZ,2BAA2B,CAAA;wGAA3B,2BAA2B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,QAAA,EAAA,CAAA;yGAA3B,2BAA2B,EAAA,OAAA,EAAA,CAH5B,8BAA8B,CAAA,EAAA,OAAA,EAAA,CAC9B,8BAA8B,CAAA,EAAA,CAAA;AAE7B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,2BAA2B,YAH5B,8BAA8B,CAAA,EAAA,CAAA;;4FAG7B,2BAA2B,EAAA,UAAA,EAAA,CAAA;kBAJvC,QAAQ;AAAC,YAAA,IAAA,EAAA,CAAA;oBACR,OAAO,EAAE,CAAC,8BAA8B,CAAC;oBACzC,OAAO,EAAE,CAAC,8BAA8B,CAAC;AAC1C,iBAAA;;;ACND;;AAEG;;"}