.tv-mode .layout-wrapper {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: none;
}

/* Cancel the global grow-on-focus effect: in a grid it shifts the neighbouring cards, which makes
   the spatial navigation targets move under the user.

   This used to say `width: initial`, which was correct while .poster-grid was CSS Grid - a grid item
   with width:auto still fills its 1fr column. The grid is now flexbox (Grid is Chrome 57), where
   width:auto on a `flex: 0 0 auto` item means "shrink to content" instead - and the content is a
   wrapper whose height comes from percentage padding, so it has no intrinsic width. That collapsed
   the focused card to 6px (just its border). The width is therefore restated explicitly, and the
   selector is scoped to the grid so it clearly outranks the global :focus rule. */
.poster-grid .poster-card:focus {
    outline: 0px none var(--brand-color);
    outline-offset: -3px;
    transform: none;
    width: 180px;
}

/* Split from the rule above: an unsupported pseudo-class in a comma-separated list invalidates the
   whole list, which on Chromium 53 would drop the :focus styling with it. */
.poster-grid .poster-card:focus-visible {
    outline: 0px none var(--brand-color);
    outline-offset: -3px;
    transform: none;
    width: 180px;
}

/* The global rule also adds a border to the wrapper on focus. Left in place it would eat 6px of the
   card's content width, changing the padding-derived height and nudging rows - the very shifting
   these rules exist to prevent. */
.poster-grid .poster-card:focus .poster-card-image-wrapper {
    border: 0;
    width: auto;
}

.poster-grid .poster-card:focus-visible .poster-card-image-wrapper {
    border: 0;
    width: auto;
}

/* ---- Genre filter ---- */

/* The positioning context for the dropdown. Rendered inside .layout-wrapper - the scroll container
   in TV mode - rather than pinned above it: position:sticky is unsupported on Chromium 53 (and
   blocked by scripts/check-css.mjs), and pinning it any other way would mean re-plumbing the flex
   chain that makes .layout-wrapper scroll. Scrolling away with the content is acceptable because Up
   from the first poster row focuses the button, which scrolls it back into view. */
.library-filter {
    position: relative;
    margin-bottom: 1rem;
}

/* Same shape as .player-text-btn: this is a decision, not a transport control. Restated rather than
   reusing that class, which is scoped to the player's own panels for its focus styling. */
.library-filter-btn {
    padding: 0.5rem 1rem;
    border: 1px solid var(--surface-border);
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.08);
    color: var(--bright-color);
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.18s ease;
}

/* An active filter is a state the user has to be able to see from across a room - otherwise an empty
   grid looks like an empty library. */
.library-filter-btn-on {
    background: var(--brand-color);
}

.library-filter-btn:hover,
.library-filter-btn:focus {
    outline: 1px solid var(--bright-color);
    outline-offset: -1px;
    background: var(--brand-color);
}

/* Split from the rule above: an unsupported pseudo-class in a comma list invalidates the whole list,
   which would drop the :focus styling with it - and on a D-pad app that means no focus indicator. */
.library-filter-btn:focus-visible {
    outline: 1px solid var(--bright-color);
    outline-offset: -1px;
    background: var(--brand-color);
}

/* Overlays the grid rather than displacing it: the cards behind must not reflow while the menu is
   open, or every spatial-navigation target moves the moment it closes. */
.library-filter-menu {
    position: absolute;
    top: 100%;
    left: 0;
    /* Above the TV info bar (15), below the app header (20).
       Not the 3 copied from .player-menu: 3 works there only because the whole player sits on a
       z-index 30 overlay of its own, and there is no such overlay here. The side menu no longer
       carries a z-index at all - it is back in flow in both modes - but the info bar still does, so
       this stays above the app's other stacked chrome rather than relying on document order. */
    z-index: 16;
    display: flex;
    flex-direction: column;
    min-width: 14rem;
    max-width: 24rem;
    /* A fixed height rather than the player menu's 60vh: this menu hangs off a button part-way down
       the page - below the TV info bar - so a viewport-relative height runs off the bottom of a 720p
       screen. 22rem keeps it on screen from where the bar actually sits, and the list scrolls inside
       it. Overshooting is not fatal (the wrapper scrolls the menu up as focus moves down) but a menu
       disappearing off the edge of a TV reads as broken. */
    max-height: 22rem;
    overflow-y: auto;
    padding: 0.5rem;
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.94);
    border: 1px solid var(--surface-border);
}

.library-filter-heading {
    padding: 0.4rem 0.75rem 0.6rem;
    font-size: 0.8rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.library-filter-item {
    display: flex;
    align-items: center;
    padding: 0.6rem 0.75rem;
    border-radius: 4px;
    color: var(--bright-color);
    font-size: 0.95rem;
    text-decoration: none;
    transition: background-color 0.18s ease;
}

/* No flexbox gap (Chrome 84), so the gutter-to-label spacing is a margin on the second child. */
.library-filter-item > * + * {
    margin-left: 0.6rem;
}

.library-filter-menu .library-filter-item:hover,
.library-filter-menu .library-filter-item:focus {
    outline: 0px none var(--brand-color);
    background: var(--brand-color);
    box-shadow: inset 0 0 0 1px var(--bright-color);
}

/* Split from the rule above; see the note on .library-filter-btn:focus-visible. */
.library-filter-menu .library-filter-item:focus-visible {
    outline: 0px none var(--brand-color);
    background: var(--brand-color);
    box-shadow: inset 0 0 0 1px var(--bright-color);
}

/* Fixed-width gutter so the labels line up whether or not a row is ticked. */
.library-filter-check {
    flex: 0 0 auto;
    width: 1.1rem;
    display: flex;
    align-items: center;
    color: var(--brand-color);
}

.library-filter-item:hover .library-filter-check,
.library-filter-item:focus .library-filter-check {
    color: var(--bright-color);
}

.library-filter-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.library-empty {
    color: var(--text-muted);
}
