/* Media — library grid, title detail, player. Requires light.css or dark.css. */

.media-page {
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background-color: var(--p-bg);
}

.media-library-tab,
.media-unmatched-tab,
.media-agents-tab {
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.media-content {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-height: 0;
    overflow-y: auto;
    padding: 12px;
}

.media-toolbar {
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
}

.media-search {
    margin-left: auto;
    min-width: 220px;
}

.media-filter.is-active {
    background-color: var(--p-accent);
}

.media-section-header {
    font-size: 15px;
    font-weight: 600;
    color: var(--p-detail-title);
    margin-top: 4px;
}

.media-empty {
    color: var(--p-detail-label);
    padding: 24px 4px;
}

.media-warning {
    color: var(--p-danger);
}

.media-hint {
    font-size: 12px;
    color: var(--p-detail-label);
}

.media-notice {
    color: var(--p-accent);
}

/* --- grid --- */

.media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 14px;
    align-content: start;
}

/* The continue-watching rail scrolls sideways rather than wrapping, so it stays
   one row however many titles are in progress. */
.media-row {
    gap: 14px;
    overflow-x: auto;
    padding-bottom: 4px;
    flex: 0 0 auto;
}

.media-row .media-tile {
    flex: 0 0 150px;
}

.media-tile {
    cursor: pointer;
    gap: 4px;
    border-radius: 6px;
    padding: 4px;
}

.media-tile:hover {
    background-color: var(--p-hover-bg);
}

.media-poster {
    width: 100%;
    aspect-ratio: 2 / 3;
    object-fit: cover;
    border-radius: 4px;
    background-color: var(--p-album-thumb-bg);
    display: block;
}

/* Wrapper for a poster carrying the watched tick. Only watched tiles have it,
   so it must not change the poster's own box. */
.media-poster-wrap {
    position: relative;
    display: block;
}

/* Corner badge on a poster: a tick once the title has been watched through —
   every episode the library holds, for a show — or the number of episodes
   still to go. White on accent, as .p-btn-primary pairs them. A pill rather
   than a circle so a three-digit count does not spill out of it. */
.media-poster-badge {
    position: absolute;
    top: 6px;
    right: 6px;
    box-sizing: border-box;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border-radius: 11px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    line-height: 1;
    color: #ffffff;
    background-color: var(--p-accent);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

/* A card whose media is no longer on the media server: the last scan of the
   library did not find the file. Across the middle of the poster rather than in
   a corner, because unlike the badge above it this is the reason the card is not
   worth clicking — and the corner is already saying something entirely
   different. The veil is what makes the words legible over artwork of any
   brightness, and dimming the poster is the point: a title that cannot play
   should not look like one that can.

   pointer-events stay off, so the ⋮ underneath and the tile's own click still
   work — a card marked this way is exactly the one somebody wants to open and
   delete. */
.media-poster-missing {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    box-sizing: border-box;
    padding: 0 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-size: 15px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #ffffff;
    background-color: rgba(0, 0, 0, 0.55);
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
    pointer-events: none;
}

/* A poster that is missing or still loading keeps its slot, so the grid does
   not reflow as artwork arrives. */
.media-poster-empty {
    background-color: var(--p-album-thumb-bg);
    border: 1px dashed var(--p-border);
}

.media-tile-title {
    font-size: 13px;
    color: var(--p-detail-value);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Clipped like the title above it. A caption is two or three facts joined with
   dots — "вчера · 2019 · 1 ч 52 мин" on the recently-added row — and a card is
   150px wide, so left to wrap it makes the tiles in one row different heights. */
.media-tile-caption {
    font-size: 12px;
    color: var(--p-detail-label);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.media-progress {
    height: 3px;
    background-color: var(--p-border);
    border-radius: 2px;
    overflow: hidden;
}

.media-progress-fill {
    height: 100%;
    background-color: var(--p-accent);
}

/* --- detail --- */

.media-back {
    align-self: flex-start;
}

.media-detail-head {
    gap: 16px;
    align-items: flex-start;
    flex: 0 0 auto;
}

.media-detail-head .media-poster {
    width: 200px;
    flex: 0 0 200px;
}

.media-detail-info {
    gap: 8px;
    flex: 1 1 auto;
    min-width: 0;
}

.media-detail-title {
    font-size: 22px;
    font-weight: 600;
    color: var(--p-detail-title);
}

.media-detail-meta,
.media-detail-original {
    color: var(--p-detail-label);
    font-size: 13px;
}

.media-detail-overview {
    color: var(--p-detail-value);
    line-height: 1.5;
    max-width: 70ch;
}

.media-detail-crew {
    color: var(--p-detail-label);
    font-size: 13px;
}

/* --- what is on the disk --- */

/* Dimmer and smaller than the metadata above it, because it answers a different
   kind of question: not what the film is, but what this copy of it is. */
.media-detail-tech {
    gap: 2px;
    margin: 2px 0;
    max-width: 70ch;
    min-width: 0;
}

.media-tech-row {
    gap: 8px;
    align-items: baseline;
}

/* A fixed label column, so four rows read as a table rather than as four
   sentences that happen to begin with a word. */
.media-tech-label {
    flex: 0 0 auto;
    width: 76px;
    color: var(--p-detail-label);
    font-size: 12px;
    opacity: 0.75;
}

/* The value wraps instead of scrolling the page sideways: six dubbed audio
   tracks, and a path under a deep library root, are both longer than the pane.
   min-width:0 is what lets it — a flex item will not shrink below its content
   without it, whatever the wrapping says. */
.media-tech-value {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--p-detail-label);
    font-size: 12px;
    word-break: break-word;
}

/* --- cast --- */

/* Circular headshots that scroll sideways, so a forty-name cast cannot push
   the season list off the page. */
.media-cast-row {
    gap: 14px;
    overflow-x: auto;
    padding-bottom: 4px;
    flex: 0 0 auto;
}

.media-cast {
    flex: 0 0 96px;
    align-items: center;
    gap: 4px;
    text-align: center;
}

.media-cast-avatar {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    object-fit: cover;
    background-color: var(--p-album-thumb-bg);
    display: block;
}

/* TMDb has no photo for plenty of credited people; initials keep the circle
   rather than leaving a hole in the row. */
.media-cast-initials {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    color: var(--p-detail-label);
    border: 1px solid var(--p-border);
}

.media-cast-name,
.media-cast-role {
    max-width: 96px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.media-cast-name {
    font-size: 12px;
    color: var(--p-detail-value);
}

.media-cast-role {
    font-size: 11px;
    color: var(--p-detail-label);
}

.media-play-btn {
    align-self: flex-start;
    margin-top: 8px;
}

.media-detail-actions {
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
}

.media-detail-actions .media-play-btn {
    margin-top: 0;
}

/* The mark-watched control. `is-watched` describes the state, not the action:
   the button reads as filled once the thing has been seen, and clicking it is
   what clears the mark. */
.media-watch-btn {
    flex: 0 0 auto;
}

.media-watch-btn.is-watched {
    background-color: var(--p-accent);
}

/* --- episodes --- */

/* Season title on the left, its mark-watched button pushed to the far edge so
   it never sits under the pointer on the way to the first episode. */
.media-season-head {
    align-items: center;
    gap: 12px;
    margin-top: 4px;
}

.media-watch-season {
    margin-left: auto;
    font-size: 12px;
    padding: 2px 8px;
}

.media-episodes {
    gap: 2px;
}

.media-episode {
    padding: 8px 10px;
    border-bottom: 1px solid var(--p-detail-row-border);
    cursor: pointer;
    gap: 12px;
    align-items: center;
}

.media-episode:hover {
    background-color: var(--p-hover-bg);
}

/* An episode the scraper knows about but this library has no file for. Shown
   rather than hidden, so gaps in the collection are visible. */
.media-episode-missing {
    opacity: 0.45;
    cursor: default;
}

.media-episode-missing:hover {
    background-color: transparent;
}

/* An episode whose file HAS gone from the media server, as opposed to one the
   library never had. Not dimmed like the row above: this one keeps its
   controls, and they are the ones worth reaching for — the row is there to be
   tidied up. The caption says so in words, in the same colour every other
   warning on the page uses. */
.media-episode-gone {
    cursor: default;
}

.media-episode-gone .media-episode-caption {
    color: var(--p-danger);
}

.media-episode-watched .media-episode-title {
    color: var(--p-detail-label);
}

.media-episode-info {
    min-width: 0;
    gap: 2px;
}

.media-episode-title {
    color: var(--p-detail-value);
}

/* Both sit at the end of an episode row, whose own click starts playback.
   margin-left goes on the first of them so the pair travels together. */
.media-fix-episode {
    margin-left: auto;
}

.media-fix-episode,
.media-watch-episode {
    min-width: 30px;
    padding: 2px 6px;
    flex: 0 0 auto;
}

/* Off to the tracker with this title. Secondary and in the left-hand group:
   it is a step out of the library rather than an action on what is on screen,
   and it must not compete with «Смотреть». Before .media-fix-btn, which takes
   the row's margin-left:auto and everything after it with it. */
.media-find-btn {
    flex: 0 0 auto;
}

/* Quieter than the play and watched buttons beside it: correcting a match is a
   rare repair, not something to draw the eye on every title. */
.media-fix-btn {
    flex: 0 0 auto;
    margin-left: auto;
}

/* The two delete controls. Both are danger-coloured rather than positioned
   apart, because .media-fix-btn already takes the row's margin-left:auto and a
   second right-aligned button would make the spacing depend on which of them
   happened to be rendered. */
.media-delete-btn {
    flex: 0 0 auto;
    color: var(--p-danger);
}

/* Last in an episode row, after the fix and watched buttons. */
.media-delete-episode {
    min-width: 30px;
    padding: 2px 6px;
    flex: 0 0 auto;
    color: var(--p-danger);
}

.media-episode-caption {
    font-size: 12px;
    color: var(--p-detail-label);
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

/* --- player --- */

.media-player-title {
    font-size: 16px;
    color: var(--p-detail-title);
}

.media-video {
    width: 100%;
    max-height: 75vh;
    background-color: #000;
    border-radius: 6px;
}

/* The next-episode control, under the picture. A row, so the button keeps its
   own width and the episode's name sits beside it: a long title as part of the
   label would stretch the button across the pane. */
.media-player-next {
    align-items: center;
    gap: 10px;
    margin-top: 8px;
}

.media-player-next .media-play-btn {
    margin-top: 0;
}

.media-player-next-label {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    color: var(--p-detail-label);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- unmatched queue --- */

.media-unmatched-row {
    padding: 8px 10px;
    border-bottom: 1px solid var(--p-detail-row-border);
    cursor: pointer;
    gap: 2px;
}

.media-unmatched-row:hover {
    background-color: var(--p-hover-bg);
}

.media-unmatched-path {
    color: var(--p-detail-value);
    font-family: monospace;
    font-size: 12px;
    word-break: break-all;
}

.media-unmatched-guess {
    font-size: 12px;
    color: var(--p-detail-label);
}

.media-fix-row {
    gap: 8px;
    align-items: center;
    flex: 0 0 auto;
}

/* The movie/series switch in the match picker. Two buttons rather than a
   dropdown: there are only ever two answers, and which one is active decides
   what the whole form below it means. */
.media-kind-button {
    padding: 5px 14px;
    border: 1px solid var(--p-border);
    border-radius: 4px;
    background-color: transparent;
    color: var(--p-text-secondary);
    font-size: 13px;
}

.media-kind-selected {
    background-color: var(--p-accent);
    border-color: var(--p-accent);
    color: #ffffff;
}

.media-candidates {
    gap: 2px;
}

.media-candidate {
    padding: 8px 10px;
    border-bottom: 1px solid var(--p-detail-row-border);
    cursor: pointer;
    gap: 12px;
    align-items: center;
}

.media-candidate:hover {
    background-color: var(--p-hover-bg);
}

.media-candidate-title {
    flex: 1 1 auto;
    color: var(--p-detail-value);
}

/* The numbering a pick would apply. Monospaced and fixed-width so the codes
   line up down the list — reading them as a column is how a wrong episode
   number gets spotted. */
.media-candidate-code {
    flex: 0 0 auto;
    font-family: monospace;
    font-size: 12px;
    color: var(--p-detail-value);
    background-color: var(--p-hover-bg);
    border-radius: 4px;
    padding: 2px 6px;
}

.media-candidate-score {
    color: var(--p-detail-label);
    font-size: 12px;
}

/* Fixed box, so a row keeps its height whether or not the image has loaded and
   the list does not reflow under the pointer while the operator is picking. */
.media-candidate-poster {
    flex: 0 0 46px;
    width: 46px;
    height: 69px;
    object-fit: cover;
    border-radius: 4px;
    background-color: var(--p-album-thumb-bg, var(--p-hover-bg));
    display: block;
}

/* --- agents --- */

.media-agent-add {
    gap: 8px;
    align-items: center;
    flex: 0 0 auto;
}

.media-agent-row {
    padding: 10px;
    border: 1px solid var(--p-card-border);
    border-radius: 6px;
    background-color: var(--p-card-bg);
    gap: 6px;
}

.media-agent-online {
    border-left: 3px solid var(--p-accent);
}

.media-agent-offline {
    border-left: 3px solid var(--p-border-strong);
}

.media-agent-name {
    font-weight: 600;
    color: var(--p-detail-title);
}

.media-agent-details {
    font-size: 12px;
    color: var(--p-detail-label);
}

.media-agent-actions {
    gap: 8px;
    margin-top: 4px;
}

/* Shown once, never again — make it selectable and obvious. */
.media-credential {
    font-family: monospace;
    font-size: 13px;
    color: var(--p-detail-value);
    background-color: var(--p-logs-bg);
    padding: 6px 8px;
    border-radius: 4px;
    user-select: all;
    word-break: break-all;
}

/* --- devices --- */

/* A paired television. Deliberately the same card as an agent row: they are
   both "a machine that has a credential", and the only difference worth showing
   is that anyone can own one of these. */
.media-device-add {
    gap: 8px;
    align-items: center;
    flex: 0 0 auto;
}

.media-device-row {
    padding: 10px;
    border: 1px solid var(--p-card-border);
    border-radius: 6px;
    background-color: var(--p-card-bg);
    gap: 6px;
}

.media-device-name {
    font-weight: 600;
    color: var(--p-detail-title);
}

.media-device-details {
    font-size: 12px;
    color: var(--p-detail-label);
}

.media-device-actions {
    gap: 8px;
    margin-top: 4px;
}

/* --- collections --- */

/* A collection tile must read as a stack before it is clicked, or it looks like
   a film that will play. Two card edges peeking out behind the poster say so.
   Drawn as box-shadows rather than pseudo-elements: shadows always paint behind
   their own box, so this needs no z-index and cannot end up over the watched
   badge or under the tile's own hover background. The 8px it spills eats into
   the grid's 14px gap and never into the next poster. */
.media-poster-stack .media-poster {
    box-shadow:
        4px 4px 0 0 var(--p-card-bg),
        4px 4px 0 1px var(--p-border),
        8px 8px 0 0 var(--p-card-bg),
        8px 8px 0 1px var(--p-border);
}

/* A card's ⋮. Top left, opposite the watched badge. Dimmed rather than
   hidden-until-hover: right-click opens the same menu, but a control that only
   exists under a pointer cannot be reached on a touch screen at all. */
.media-tile-menu {
    position: absolute;
    top: 6px;
    left: 6px;
    box-sizing: border-box;
    min-width: 22px;
    height: 22px;
    padding: 0;
    border-radius: 11px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    line-height: 1;
    opacity: 0.55;
}

.media-tile:hover .media-tile-menu,
.media-tile-menu:hover {
    opacity: 1;
}

/* The add-to-collection dialog. Bounded so a long list of collections scrolls
   inside the modal instead of growing it past the window. */
.media-popup-body {
    flex-direction: column;
    gap: 12px;
    min-width: 320px;
    max-width: 420px;
}

.media-popup-subject {
    color: var(--p-detail-label);
    font-size: 13px;
}

.media-popup-body .media-candidates {
    flex-direction: column;
    gap: 2px;
    max-height: 40vh;
    overflow-y: auto;
}

/* The episode-timings table: one row per episode in a shared file, each with
   the point in the file where it starts. */
.media-offsets-list {
    flex-direction: column;
    gap: 6px;
    max-height: 40vh;
    overflow-y: auto;
}

.media-offset-row {
    align-items: center;
    gap: 10px;
}

.media-offset-label {
    flex: 1 1 auto;
    font-size: 13px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.media-offset-row .p-input-wrapper {
    flex: 0 0 120px;
}

.media-collection-head {
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    flex: 0 0 auto;
}

/* Pushes the rename/delete pair to the far edge, away from the name it is
   about — and away from the first poster in the grid below. */
.media-collection-head .media-collection-action:first-of-type {
    margin-left: auto;
}

.media-collection-action {
    flex: 0 0 auto;
}

.media-collection-head .p-input-wrapper {
    flex: 1 1 auto;
    max-width: 420px;
}

/* Where a film is filed, shown on the film. Each chip opens its collection;
   the × unfiles it, which is where anyone looks for that first. */
.media-collection-chips {
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.media-collection-chip {
    align-items: center;
    gap: 6px;
    padding: 2px 4px 2px 10px;
    border-radius: 13px;
    border: 1px solid var(--p-border);
    background-color: var(--p-card-bg);
    color: var(--p-detail-value);
    font-size: 12px;
    cursor: pointer;
    flex: 0 0 auto;
}

.media-collection-chip:hover {
    background-color: var(--p-hover-bg);
}

.media-chip-remove {
    min-width: 18px;
    height: 18px;
    padding: 0;
    border-radius: 9px;
    font-size: 12px;
    line-height: 1;
    flex: 0 0 auto;
}

.media-collection-btn {
    flex: 0 0 auto;
}

/* --- reordering a collection --- */

/* The grab cursor is the only thing that says a poster can be dragged before
   someone tries it; the hint line above the grid says it in words once. */
.media-member-tile {
    cursor: grab;
    user-select: none;
}

.media-member-tile.is-dragging {
    cursor: grabbing;
    opacity: 0.4;
}

/* Where the dragged poster will land — it takes this tile's place, and this one
   slides over to make room. */
.media-member-tile.is-drop-target {
    outline: 2px solid var(--p-accent);
    outline-offset: 2px;
    border-radius: 6px;
}

.media-drag-hint {
    font-size: 12px;
    color: var(--p-detail-label);
}

/* --- torrents (root only) --- */

.media-torrents-head {
    gap: 12px;
    align-items: center;
    flex: 0 0 auto;
}

/* The seedbox's download disk, beside the heading. It is the number that
   decides whether adding the next film is a good idea, and the heading is where
   somebody is standing when they decide. Quiet by default: it is a fact, not a
   warning, until it nearly is one. */
.media-torrents-space {
    flex: 0 1 auto;
    font-size: 12px;
    color: var(--p-detail-label);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.media-torrents-space-low {
    color: var(--p-danger);
}

/* Pushed to the end, so the button does not drift as the figure beside the
   heading changes width. */
.media-torrents-head-action {
    margin-left: auto;
    flex: 0 0 auto;
}

/* Only the FIRST one gets the auto margin. Two of them each take half the free
   space, which does not read as "both on the right" — it strands the first in
   the middle of the header with empty space on either side, looking like a
   centred button nobody asked for. */
.media-torrents-head-action ~ .media-torrents-head-action {
    margin-left: 0;
}

/* A row is the same card as an agent's: both are "one thing with a state and
   some buttons", and inventing a second look for it would say there is a
   difference where there is none. */
.media-torrent-row {
    padding: 10px;
    border: 1px solid var(--p-card-border);
    border-radius: 6px;
    background-color: var(--p-card-bg);
    gap: 6px;
    cursor: pointer;
}

/* Anything wrong — a tracker refusing, a transfer that failed — is marked on the
   edge rather than by recolouring the row: the text under it says what, and a
   red card would drown that out. */
.media-torrent-failed {
    border-left: 3px solid var(--p-danger);
}

.media-torrent-title {
    gap: 8px;
    align-items: baseline;
}

/* The type chip. Small and quiet: it answers a question you only ask of a row
   you are already looking at. */
.media-torrent-kind {
    flex: 0 0 auto;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    padding: 2px 6px;
    border-radius: 4px;
    background-color: var(--p-logs-bg);
    color: var(--p-detail-label);
}

.media-torrent-name {
    font-weight: 600;
    color: var(--p-detail-title);
    word-break: break-word;
}

.media-torrent-details {
    font-size: 12px;
    color: var(--p-detail-label);
}

/* Remote-controlled text — a tracker's message, a seedbox path. It can be long
   and it can be one unbroken word, so it wraps rather than pushing the row wide. */
.media-torrent-error {
    font-size: 12px;
    color: var(--p-danger);
    word-break: break-word;
}

.media-torrent-actions {
    gap: 8px;
    margin-top: 4px;
    align-items: center;
}

/* The heading of the file list, carrying the selection count and the two bulk
   edits. The count is why the screen exists, so it sits on the same line as the
   heading rather than under it. */
.media-torrent-files-head {
    gap: 12px;
    align-items: baseline;
    margin-top: 8px;
}

/* «Добавлен остановленным». A call-out and not another .media-torrent-details
   line: this row is waiting for a person, and the whole failure mode of the
   feature is a torrent nobody realises is waiting for them. */
.media-torrent-hold {
    gap: 4px;
    padding: 10px 12px;
    margin: 8px 0;
    font-size: 13px;
    line-height: 1.45;
    color: var(--p-detail-value);
    border: 1px solid var(--p-accent);
    border-radius: 8px;
    background-color: var(--p-hover-bg);
}

.media-torrent-file,
.media-torrent-peer {
    gap: 10px;
    align-items: center;
    padding: 4px 6px;
    font-size: 13px;
    color: var(--p-detail-value);
}

/* The name takes the slack so the size and priority stay put down the column
   and can be read as one. */
.media-torrent-file-name {
    flex: 1 1 auto;
    word-break: break-all;
}

.media-torrent-file-size,
.media-torrent-file-priority,
.media-torrent-peer-addr {
    flex: 0 0 auto;
    font-size: 12px;
    color: var(--p-detail-label);
}

.media-torrent-tracker {
    gap: 2px;
    padding: 4px 6px;
}

.media-torrent-tracker-host {
    font-size: 13px;
    color: var(--p-detail-value);
    word-break: break-all;
}

/* --- the add dialog --- */

/* Narrower than it is wide is wrong here: the hint under the type switch is a
   sentence, and a dialog that reflows it to four lines reads as fine print. */
.media-torrent-add-body {
    min-width: 400px;
    max-width: 460px;
}

/* One labelled control. */
.media-torrent-add-field {
    gap: 6px;
}

.media-torrent-add-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--p-detail-label);
}

/* «или» with a rule either side. The link and the file answer the same
   question, so what separates them has to say "one of these" and not "and". */
.media-torrent-add-or {
    align-items: center;
    gap: 10px;
    font-size: 12px;
    color: var(--p-detail-label);
}

.media-torrent-add-or::before,
.media-torrent-add-or::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background-color: var(--p-border);
}

/* A chosen file wins over anything typed, so the link steps visibly back rather
   than being dropped in silence. */
.media-torrent-source-muted {
    opacity: .45;
}

.media-torrent-add-file {
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

/* The chosen .torrent, shaped like the collection chips: a name and the × that
   takes it back off. Not clickable — there is nowhere for it to go. */
.media-torrent-file-chip {
    align-items: center;
    gap: 6px;
    padding: 2px 4px 2px 10px;
    border-radius: 13px;
    border: 1px solid var(--p-border);
    background-color: var(--p-card-bg);
    color: var(--p-detail-value);
    font-size: 12px;
    /* A release name runs to 120 characters. This one shrinks and clips instead
       of inheriting the row default, whose "never shrink, scroll instead" is
       right for a list row and wrong here: it scrolled the × out of the chip,
       and that is the only control that takes the chosen file back off. */
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
}

.media-torrent-file-chip-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.media-torrent-add-kinds {
    gap: 8px;
}

.media-torrent-add-hint {
    font-size: 12px;
    color: var(--p-detail-label);
    max-width: 460px;
}

.media-torrent-add-status {
    font-size: 12px;
    color: var(--p-detail-value);
    min-height: 16px;
}

.media-torrent-add-status-error {
    color: var(--p-danger);
}

/* Off screen, and clicked through a button of ours: the browser's own file
   control is the one widget in the dialog that cannot be made to match it. */
.media-torrent-file-input {
    display: none;
}

/* The second journey: off the seedbox and onto the media server. A different
   colour from the download bar because they measure different things — one is
   Transmission's, one is ours — and a row in transfer shows both. */
.media-torrent-transfer-bar .p-progress-bar-fill {
    background-color: var(--p-accent);
    opacity: .65;
}

/* ---- tracker search --------------------------------------------------------

   A section of the Торренты tab, not a tab of its own: the result of a search is
   an add, and the add dialog is already there. It borrows the torrent row's card
   look for the same reason that row borrowed the agent's — both are "one thing
   with some facts and a button", and a second look would claim a difference that
   is not there. */

/* NOT `.media-search` — that name is already the LIBRARY tab's search box, and
   it carries `margin-left: auto` to push it into its corner. Inherited here it
   shoved this whole section to the right, and because an auto cross-axis margin
   also cancels the stretch, squeezed it to a quarter of the width. Nothing
   about the result looked like a name collision. */
.media-search-pane {
    gap: 12px;
}

/* The query, the year and the button on one line, because they are one
   question. The year is the only optional part and is sized to say so. */
.media-search-form {
    gap: 8px;
    align-items: center;
    flex: 0 0 auto;
    /* Capped rather than full-bleed: the content pane is most of a wide monitor,
       and a single-line text field a thousand pixels across is harder to read
       back than a short one, not easier. */
    max-width: 820px;
}

/* The query fills the row. Without this the input wrapper sits at its intrinsic
   width and the whole form reads as three small controls huddled on the left,
   with the rest of the row empty. */
.media-search-form > .p-input-wrapper {
    flex: 1 1 auto;
    min-width: 0;
}

.media-search-year {
    flex: 0 0 110px;
    max-width: 110px;
}

/* The section switch, captioned. Three bare buttons under a search box read as
   an unexplained toolbar; one word in front of them makes them a filter. */
.media-search-filters {
    gap: 10px;
    align-items: center;
    flex: 0 0 auto;
}

.media-search-results {
    gap: 8px;
}

/* Horizontal: the text in a column, the button pinned to the end. Stacking the
   button under the facts made every result three blocks tall, which over fifty
   of them is a page you scroll rather than a list you scan — and the titles are
   what the eye runs down. */
.media-search-row {
    padding: 10px 12px;
    border: 1px solid var(--p-card-border);
    border-radius: 6px;
    background-color: var(--p-card-bg);
    gap: 12px;
    align-items: center;
    cursor: pointer;
}

/* The whole row opens the release, so it answers the pointer. */
.media-search-row:hover {
    border-color: var(--p-accent);
    background-color: var(--p-hover-bg);
}

/* min-width:0 is load-bearing: without it a long release name refuses to
   shrink below its content and pushes the button off the end of the row. */
.media-search-body {
    flex: 1 1 auto;
    min-width: 0;
    gap: 4px;
}

.media-search-download {
    flex: 0 0 auto;
}

/* Nobody is seeding it. Dimmed rather than hidden or struck through: it may be
   the only copy of something, and whether to wait for a seeder is the user's
   call, not the panel's. */
.media-search-row-dead {
    opacity: .55;
}

/* The original title and year, under the Russian one. Quieter than the name but
   not as quiet as the facts line: it is what somebody reads to check they have
   found the right film. */
.media-search-sub {
    font-size: 12px;
    color: var(--p-detail-label);
    word-break: break-word;
}

.media-search-nav {
    gap: 8px;
    align-items: center;
    justify-content: center;
    padding: 8px 0;
}

/* ---- one release ---------------------------------------------------------- */

.media-search-info {
    gap: 16px;
    align-items: flex-start;
    flex-wrap: wrap;
}

.media-search-poster {
    flex: 0 0 auto;
    width: 200px;
    border-radius: 6px;
}

.media-search-fields {
    gap: 4px;
    flex: 1 1 320px;
    min-width: 0;
}

/* Label and value on one line, the label a fixed column so the values line up.
   Wraps rather than truncating: these are cast lists and genre lists, and half
   a cast list is worse than a tall row. */
.media-search-field {
    gap: 8px;
    align-items: baseline;
    flex-wrap: wrap;
}

.media-search-field-key {
    flex: 0 0 160px;
    font-size: 12px;
    color: var(--p-detail-label);
}

.media-search-field-value {
    flex: 1 1 200px;
    min-width: 0;
    font-size: 13px;
    color: var(--p-detail-title);
    word-break: break-word;
}

/* «Техданные» — the release's own measurements, directly under the facts line.
   Boxed and tinted so it reads as a block about the FILE, distinct from the
   film's labelled fields further down, which look identical row for row. */
.media-search-tech {
    gap: 4px;
    align-self: stretch;
    min-width: 0;
    padding: 8px 10px;
    border-radius: 8px;
    background-color: var(--p-hover-bg);
}

.media-search-description {
    font-size: 13px;
    line-height: 1.5;
    color: var(--p-detail-title);
    white-space: pre-wrap;
    word-break: break-word;
}

/* The technical block keeps one line per track, which is why it is a stack of
   spans and not one wrapped paragraph. Monospaced so the bitrates line up. */
.media-search-specs {
    gap: 2px;
    padding: 10px;
    border-radius: 6px;
    background-color: var(--p-logs-bg);
}

.media-search-spec {
    font-family: var(--p-mono-font, monospace);
    font-size: 12px;
    color: var(--p-detail-label);
    word-break: break-word;
}

.media-search-files {
    gap: 4px;
}

.media-search-file {
    gap: 10px;
    align-items: baseline;
    padding: 4px 0;
    border-bottom: 1px solid var(--p-card-border);
}

.media-search-file-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    color: var(--p-detail-title);
    word-break: break-all;
}

/* A wrapping strip rather than a grid: the stills are not all one shape, and a
   grid would either crop them or leave gaps. */
.media-search-shots {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.media-search-shot {
    height: 120px;
    border-radius: 4px;
}

/* The release the add dialog was opened on, in place of the link and file
   fields. Read-only on purpose — it is what was chosen, not something to edit. */
.media-torrent-add-release {
    font-size: 13px;
    font-weight: 600;
    color: var(--p-detail-title);
    word-break: break-word;
}

/* --- «Топ раздач» --------------------------------------------------------- */

.media-top-pane {
    gap: 12px;
}

/* The four selects and the button on one line, wrapping on a narrow pane.
   Bottom-aligned: .p-select-wrapper reserves 16px above the field for its
   floating label, so a top- or centre-aligned button sits visibly high. */
.media-top-controls {
    gap: 8px;
    align-items: flex-end;
    flex-wrap: wrap;
    flex: 0 0 auto;
}

/* Категория runs to «Классика, театр, опера, балет», which left unbounded makes
   one control as wide as the other three together. */
.media-top-controls .p-select-wrapper {
    max-width: 230px;
}
