/*
 * File Share - the files screen
 *
 * The listing of a storage the way the share has always drawn it: the
 * storages as pills along the top, a panel with the path of the folder and
 * what can be done in it, and a table of what the folder holds - the name
 * with the symbol of its kind, the size, the day it changed, and at the end
 * of the row the download and the menu. The same listing serves whoever is
 * signed in, a visitor of what was made public and the holder of a link; only
 * the controls differ.
 *
 * This sheet holds what the listing shares with no other page. Everything
 * around it - the panels, the table, the buttons, the dialogs, the bar of
 * how full a storage is - is main.css, and every colour here is one of its.
 *
 * Organisation
 *   1. Menu of a row               8. Frame of a drop
 *   2. Toolbar and selection       9. Viewer
 *   3. Rows                       10. Move dialog
 *   4. States of the listing      11. Share dialog
 *   5. How full the storage is    12. Address in a toast
 *   6. The page of a listing      13. Public and shared pages
 *   7. Upload queue               14. Narrow screens
 *
 * Under 640px the table is a stack of cards (`data--stack` of main.css), the
 * buttons of the toolbar are their symbols, and the viewer takes the whole
 * window.
 */

/* ---------------------------------------------------------- Menu of a row */

/*
 * The menu of a right click and of the three dots at the end of a row. Drawn
 * into the body, so it floats over everything; it looks like the menus of
 * the header (see components/contextMenu.ts).
 *
 * Over the messages of the corner as well: the dots of a row are at the right
 * edge, so the menu of a row low on the screen opens right where a message
 * is shown - and a message about the last upload must not lie over the entry
 * somebody is about to click.
 */
.context-menu {
    position: fixed;
    z-index: 320;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 232px;
    max-width: min(320px, calc(100vw - 16px));
    max-height: calc(100vh - 16px);
    padding: 0.4rem;
    overflow-y: auto;
    background: var(--panel);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: 0 14px 38px rgba(0, 0, 0, 0.55);
    animation: menu-pop 0.1s ease-out;
    outline: none;
    scrollbar-width: thin;
}

@keyframes menu-pop {
    from {
        opacity: 0;
        transform: scale(0.98);
    }
}

/*
 * What the menu is about: the name of the file or folder which was clicked.
 * Written the way it is called and never in capitals - `Foto.JPG` and
 * `foto.jpg` are two files, and the menu of one must not read like the other.
 */
.context-menu__title {
    padding: 0.35rem 0.55rem 0.45rem;
    margin-bottom: 0.15rem;
    border-bottom: 1px solid var(--border);
    color: var(--text-dim);
    font-size: 0.78rem;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.context-menu__item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    min-height: 32px;
    padding: 0.35rem 0.55rem 0.35rem calc(0.55rem + var(--depth, 0) * 0.9rem);
    background: transparent;
    border: 0;
    border-radius: var(--radius);
    color: var(--text);
    font-family: inherit;
    font-size: 0.83rem;
    font-weight: 500;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.context-menu__item .icon {
    flex: none;
    color: var(--text-dim);
}

.context-menu__label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*
 * The key of an item, in a small box of its own. Written in the face of the
 * share rather than a monospace one: the monospace fallbacks carry a deep
 * descent which pushes a lone capital to the top of its box. Its line is as
 * tall as the box, so the capital sits in the middle whatever the face.
 */
.context-menu__shortcut {
    display: inline-block;
    flex: none;
    box-sizing: border-box;
    min-width: 1.45rem;
    height: 1.45rem;
    padding: 0 0.4rem;
    background: var(--panel-alt);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--text-dim);
    font-family: var(--font);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    line-height: calc(1.45rem - 2px);
    text-align: center;
}

.context-menu__chevron {
    margin-left: auto;
}

.context-menu__item:hover,
.context-menu__item:focus-visible,
.context-menu__item:focus,
.context-menu__item[aria-expanded="true"] {
    background: var(--panel-hover);
    color: var(--text-strong);
    outline: none;
}

.context-menu__item:hover .icon,
.context-menu__item:focus .icon,
.context-menu__item[aria-expanded="true"] .icon {
    color: var(--accent);
}

.context-menu__item.is-active {
    color: var(--accent);
}

.context-menu__item--danger,
.context-menu__item--danger .icon {
    color: #f0736a;
}

.context-menu__item--danger:hover,
.context-menu__item--danger:focus {
    background: rgba(226, 33, 47, 0.14);
    color: #ff8a80;
}

.context-menu__item--danger:hover .icon,
.context-menu__item--danger:focus .icon {
    color: inherit;
}

.context-menu__item[aria-disabled="true"] {
    opacity: 0.45;
    cursor: default;
    pointer-events: none;
}

.context-menu__separator {
    height: 1px;
    margin: 0.25rem 0.3rem;
    background: var(--border);
}

/* ---------------------------------------------------------------- Toolbar */

/*
 * The bar over the table: what is listed and how much of it on the left, what
 * can be done in the folder on the right.
 */
.browser-toolbar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 56px;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--border);
}

.browser-toolbar__title {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
    line-height: 1.3;
}

.browser-toolbar__title strong {
    color: var(--text-strong);
    font-size: 0.98rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* On a phone the actions of the toolbar are their symbols; the words stay in their titles. */
@media (max-width: 640px) {
    .browser-toolbar .button-label {
        display: none;
    }
}

/*
 * The bar which takes the place of the toolbar while files are chosen: how
 * many there are, with the box which chooses all of them, and what can be
 * done with the lot.
 */
.browser-selection {
    position: sticky;
    top: var(--header-height);
    z-index: 4;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.45rem 0.75rem;
    /*
     * The tint of the accent over the colour of the panel, rather than the
     * tint alone: the bar stays under the header while a long folder is
     * scrolled, and the rows passing behind a translucent bar would show
     * through its words.
     */
    background: linear-gradient(var(--accent-faint), var(--accent-faint)), var(--panel);
    border-bottom: 1px solid var(--border);
}

/*
 * Clipped rather than hidden. A flush panel hides what sticks out past its
 * rounded corners, and anything which hides its overflow becomes what a
 * sticky child sticks to - the panel itself, which never scrolls, so the bar
 * above would scroll away with the rows. Clipping cuts the corners the same
 * way and leaves the bar to the window.
 */
.file-browser.panel--flush {
    overflow: clip;
}

.browser-selection__all {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: var(--text-strong);
    font-size: 0.8rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}

.browser-selection .button-row {
    flex-wrap: nowrap;
    gap: 0.3rem;
}

/* ------------------------------------------------------------------- Rows */

/*
 * The states of a row. The row the menu is about is framed, the rows which
 * are chosen are tinted in the accent, and the row whose file the viewer is
 * showing is the one lit. Written on the row of the table: an outline is the
 * one frame a row of a table can carry.
 */
table.file-table tbody tr.is-context {
    background: var(--panel-hover);
    outline: 1px solid var(--border-strong);
    outline-offset: -1px;
}

table.file-table tbody tr.is-active {
    background: var(--accent-soft);
}

table.file-table tbody tr.is-selected {
    background: rgba(227, 79, 14, 0.1);
}

/*
 * The row the keyboard is in. A click anywhere in a row puts it there, and
 * so do the arrows: the row is what Enter opens, F2 renames and Delete
 * deletes (see files/browser.ts). Lit faintly for the mouse, framed for the
 * keyboard - the frame of a row somebody merely clicked would be noise.
 */
table.file-table tbody tr:focus {
    outline: none;
}

table.file-table tbody tr:focus-within {
    background: rgba(255, 255, 255, 0.045);
}

table.file-table tbody tr.is-selected:focus-within {
    background: var(--accent-soft);
}

table.file-table tbody tr:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* -------------------------------------------------- States of the listing */

/*
 * The listing being read again with its rows kept on screen: a thin orange
 * line running along the bottom of the toolbar.
 */
.file-browser {
    position: relative;
}

.file-browser.is-busy .browser-toolbar {
    position: relative;
}

.file-browser.is-busy .browser-toolbar::after {
    content: "";
    position: absolute;
    right: 0;
    bottom: -1px;
    left: 0;
    height: 2px;
    background: linear-gradient(90deg, transparent, var(--accent), transparent);
    background-size: 40% 100%;
    background-repeat: no-repeat;
    animation: browser-busy 1.1s ease-in-out infinite;
}

@keyframes browser-busy {
    from {
        background-position: -40% 0;
    }

    to {
        background-position: 140% 0;
    }
}

/* ------------------------------------------------ How full the storage is */

/*
 * How full the storage being browsed is, at the very foot of its listing: the
 * disk, the percent, the bar and how much of how much. The bar itself is the
 * `usage-bar` of main.css, the one the administration draws.
 */
.storage-usage {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    padding: 0.7rem 0.95rem 0.85rem;
    border-top: 1px solid var(--border);
    background: var(--bg-alt);
    cursor: default;
}

.storage-usage__head {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    color: var(--text-dim);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.storage-usage__label {
    flex: 1 1 auto;
    min-width: 0;
}

.storage-usage__percent {
    color: var(--text-strong);
    font-size: 0.72rem;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
}

.storage-usage__text {
    color: var(--text-dim);
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
}

.storage-usage[data-level="warn"] .storage-usage__percent {
    color: var(--warn);
}

.storage-usage[data-level="critical"] .storage-usage__percent,
.storage-usage[data-level="full"] .storage-usage__percent,
.storage-usage[data-level="critical"] .storage-usage__text,
.storage-usage[data-level="full"] .storage-usage__text {
    color: var(--danger);
}

/* ------------------------------------------------- The page of a listing */

/*
 * What the markup of a listing needs before anything else of the screen is
 * drawn: the row of the pills with the measure of the open storage beside
 * it, the way down to a folder, the name of an entry with its symbol, and the
 * pages under the rows. Written with the pages themselves (partials/listing.html,
 * pages/files.html); everything else of the file screen follows below it.
 */

/*
 * The pills of the storages and, beside them, how full the one being shown
 * is. The pills take the room and fill it row by row, their last row on the
 * line the measure ends at; the measure keeps its width, framed like a pill
 * itself, and moves under them on a phone.
 */
.browser-head {
    display: flex;
    align-items: flex-end;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.browser-head .storage-switcher {
    flex: 1 1 auto;
    min-width: 0;
    margin-bottom: 0;
}

.browser-head .storage-usage {
    flex: none;
    justify-content: center;
    width: 15rem;
    padding: 0.5rem 0.8rem;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

@media (max-width: 640px) {
    .browser-head {
        flex-direction: column;
        align-items: stretch;
    }

    .browser-head .storage-usage {
        width: auto;
    }
}

/* The buttons of the toolbar keep their width; the way down to the folder wraps in what is left. */
.browser-toolbar .button-row {
    flex: none;
}

/*
 * The way down to the folder being shown: a link for every folder on it, and
 * the folder itself in the colour of where the reader stands. It wraps rather
 * than scrolls, so the deepest folder is never the one cut off.
 */
.breadcrumbs {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.15rem;
    min-width: 0;
    font-size: 0.88rem;
}

.breadcrumbs a,
.breadcrumbs strong {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.1rem 0.4rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    overflow-wrap: anywhere;
}

.breadcrumbs a {
    color: var(--text-dim);
}

.breadcrumbs a:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.breadcrumbs strong {
    color: var(--text-strong);
    font-weight: 600;
}

.breadcrumbs__separator {
    flex: none;
    color: var(--text-dim);
}

/*
 * The name of an entry with the symbol of its kind in front of it. The symbol
 * is dim until the pointer is over the link, and then turns with the name.
 */
.file-link {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    min-width: 0;
    /* By its middle, like the badges beside it: set on the line of the text, they stood a little lower than the name. */
    vertical-align: middle;
}

.file-link .icon {
    flex: none;
    color: var(--text-dim);
}

.file-link:hover .icon {
    color: var(--accent);
}

/*
 * The lock beside the name of a link which asks for a password. In the
 * colour of the share whether or not the pointer is over the name: it says
 * something about the link, and is no part of what the name looks like.
 */
.share-guard {
    display: inline-flex;
    flex: none;
}

.file-link .share-guard .icon {
    color: var(--accent);
}

/* What a row says about its entry beside the name: that it is public, that links lead to it. */
table.file-table td > .badge {
    margin-left: 0.35rem;
}

/* The pages of a folder, inside the panel of its rows rather than under it. */
.file-browser .pagination {
    margin: 0;
    padding: 0.9rem;
    border-top: 1px solid var(--border);
}

/* ----------------------------------------------------------- Upload queue */

/*
 * The files on their way into the folder, between the toolbar and the rows:
 * a head which says how far the queue is, and under it a row for every file
 * (see files/upload.ts). Drawn only while it holds something; the element is
 * on the page from the start, empty and hidden.
 */
.upload-queue {
    background: var(--bg-alt);
    border-bottom: 1px solid var(--border);
}

.upload-queue__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.4rem 0.6rem 0.4rem 0.9rem;
    border-bottom: 1px solid var(--border);
}

.upload-queue__summary {
    min-width: 0;
    color: var(--text-strong);
    font-size: 0.8rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

/* A few files are shown whole; a long queue scrolls inside itself rather than push the rows off the screen. */
.upload-queue__list {
    max-height: 14.5rem;
    margin: 0;
    padding: 0.25rem 0;
    overflow-y: auto;
    list-style: none;
    scrollbar-width: thin;
}

/*
 * One file: its symbol, its name, its size, the bar, the percent, and the
 * button which calls it off. Every part is given its column outright: the
 * percent goes away once a file is refused, and the button after it must
 * not slide into its place.
 */
.upload-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto minmax(5rem, 11rem) 3.1rem auto;
    align-items: center;
    column-gap: 0.7rem;
    row-gap: 0.15rem;
    padding: 0.2rem 0.6rem 0.2rem 0.9rem;
    font-size: 0.83rem;
}

.upload-row__symbol {
    display: inline-flex;
    grid-column: 1;
    grid-row: 1;
    color: var(--text-dim);
}

.upload-row__name {
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
    color: var(--text-strong);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.upload-row__size {
    grid-column: 3;
    grid-row: 1;
}

/* The bar is the `usage-bar` of main.css: the same pill, filled by what has left instead of by what is stored. */
.upload-row__bar {
    grid-column: 4;
    grid-row: 1;
}

.upload-row__bar > span {
    transition: width 0.2s ease, background-color 0.2s ease;
}

.upload-row__percent {
    grid-column: 5;
    grid-row: 1;
    text-align: right;
}

.upload-row__size,
.upload-row__percent {
    color: var(--text-dim);
    font-size: 0.76rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.upload-row__cancel {
    grid-column: 6;
    grid-row: 1;
}

/* What the server said about a file it refused, under the name it is about. */
.upload-row__message {
    grid-column: 2 / -1;
    grid-row: 2;
    padding-bottom: 0.2rem;
    color: var(--danger);
    font-size: 0.78rem;
    overflow-wrap: anywhere;
}

.upload-row.is-waiting .upload-row__bar {
    opacity: 0.45;
}

.upload-row.is-running .upload-row__symbol {
    color: var(--accent);
}

.upload-row.is-done .upload-row__symbol {
    color: var(--ok);
}

.upload-row.is-failed .upload-row__symbol {
    color: var(--danger);
}

/* -------------------------------------------------------- Frame of a drop */

/*
 * What is shown while files are dragged over the window: a dashed frame
 * around all of it, saying that letting go uploads them. Over the window
 * rather than over the panel - a listing is often longer than the screen, and
 * a frame around all of it would have its words where nobody is looking.
 *
 * It takes no pointer: the drag goes on being reported by what lies under
 * it, and a frame which caught it would flicker between shown and hidden.
 */
.browser-drop {
    position: fixed;
    inset: 0.75rem;
    z-index: 250;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    padding: 1.5rem;
    background: rgba(17, 17, 17, 0.9);
    border: 2px dashed var(--accent);
    border-radius: var(--radius);
    color: var(--text-dim);
    font-size: 0.88rem;
    text-align: center;
    pointer-events: none;
}

.file-browser.is-dropping .browser-drop {
    display: flex;
}

/* Said in as many words: an icon is as large as the letters around it unless it is told otherwise. */
.browser-drop .icon {
    width: 34px;
    height: 34px;
    margin-bottom: 0.5rem;
    color: var(--accent);
}

.browser-drop strong {
    color: var(--text-strong);
    font-size: 1.05rem;
    font-weight: 600;
}

/* ----------------------------------------------------------------- Viewer */

/*
 * The viewer of pictures, films, recordings and texts (partials/viewer.html,
 * see files/viewer.ts): the widest dialog of the share, as tall as the window
 * lets it be, with a bar across the top - the name, which file of how many,
 * the download, the whole screen, the way out - and under it the stage.
 *
 * The dialog keeps one size whatever is on the stage, so walking through
 * pictures of every shape does not make it jump; only a recording, which has
 * nothing to look at, is given a dialog as small as its player.
 *
 * The stage, what stands on it and the line above it are drawn the same
 * where they stand on a page instead - the look at one shared file.
 */
.viewer {
    height: calc(100vh - 2rem);
    height: calc(100svh - 2rem);
    background: var(--bg-alt);
}

.viewer:has(.viewer__stage[data-kind="audio"]) {
    height: fit-content;
}

.viewer__inner {
    position: relative;
    gap: 0;
    height: 100%;
    padding: 0;
    overflow: hidden;
}

.viewer__bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.55rem 0.6rem 0.55rem 1rem;
    background: var(--panel);
    border-bottom: 1px solid var(--border);
}

/* One line, cut short with three dots: a name as long as a sentence must not push the buttons out. */
.viewer__name {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.95rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.viewer__count {
    flex: none;
    color: var(--text-dim);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}

.viewer__bar .button-row {
    flex: none;
    flex-wrap: nowrap;
    gap: 0.3rem;
}

/*
 * The line under the bar: what the browser could not do with a file it shows
 * all the same - a film without its sound, a text cut short - with the
 * download at its end.
 */
.viewer__note {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0;
    padding: 0.5rem 0.6rem 0.5rem 1rem;
    background: var(--panel-alt);
    border-bottom: 1px solid var(--border);
    color: var(--text);
    font-size: 0.82rem;
    line-height: 1.45;
}

.viewer__note > .icon {
    flex: none;
    color: var(--warn);
}

.viewer__note > span {
    flex: 1 1 auto;
    min-width: 0;
}

.viewer__note > .button {
    flex: none;
}

/*
 * Written with the frame in front of it on purpose: the parts of a dialog are
 * told never to shrink (`.modal__inner > *`), and the stage is the one part
 * which has to - it takes what the bar leaves, and what it shows fits into
 * that.
 */
.viewer__inner > .viewer__stage {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

.viewer__inner > .viewer__stage[data-kind="audio"] {
    flex: none;
    min-height: 13rem;
}

/* A picture and a film are shown whole, however large they are: never cropped, never stretched. */
.viewer__media {
    display: block;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

video.viewer__media {
    width: 100%;
    height: 100%;
}

.viewer__audio {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.4rem;
    width: min(100%, 32rem);
    padding: 2.25rem 1.5rem;
    color: var(--text-dim);
}

.viewer__audio .icon {
    width: 56px;
    height: 56px;
}

.viewer__audio .viewer__media {
    width: 100%;
}

/*
 * A film the browser plays the sound of and no pictures: its controls alone,
 * the size of the player of a recording, rather than a black box over them.
 */
.viewer__stage[data-kind="audio"] > video {
    width: min(100% - 3rem, 32rem);
    height: 3.4rem;
}

/*
 * A text, as it is written: every space and every line kept, in letters of
 * one width, and a line longer than the stage broken where it ends rather
 * than scrolled sideways. The text itself is what scrolls, so the stage
 * keeps its size and the bar stays where it is.
 */
.viewer__text {
    flex: 1 1 auto;
    align-self: stretch;
    min-width: 0;
    margin: 0;
    padding: 1rem 1.25rem;
    color: var(--text-strong);
    font-family: var(--mono);
    font-size: 0.8rem;
    line-height: 1.55;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    overflow: auto;
    overscroll-behavior: contain;
    tab-size: 4;
}

.viewer__text:focus-visible {
    outline: 1px solid var(--accent);
    outline-offset: -1px;
}

/*
 * A text is written out in pieces (see `piecesOf` of files/viewer.ts), each
 * as wide as the text and laid out only once it is scrolled to: a browser
 * which laid out every line of a long log before drawing the first would
 * have its reader wait for lines nobody is looking at. Within the line
 * rather than a block of its own, so a text copied out of several pieces
 * gains no line the file does not have.
 */
.viewer__piece {
    display: inline-block;
    width: 100%;
    vertical-align: top;
    content-visibility: auto;
    contain-intrinsic-size: auto 40em;
}

/* Until the first of a file has arrived there is nothing to look at but a turning ring. It turns on the `spin` of main.css. */
.viewer__stage.is-loading::after {
    content: "";
    position: absolute;
    width: 1.7rem;
    height: 1.7rem;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
    pointer-events: none;
}

.viewer__stage.is-loading img.viewer__media {
    opacity: 0;
}

/*
 * What stands in the place of a file which is not shown - one the browser
 * cannot play, one which did not arrive, one which is empty: the symbol, a
 * sentence, and the download which still works.
 */
.viewer__state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.8rem;
    max-width: 28rem;
    padding: 2rem 1.5rem;
    color: var(--text-dim);
    font-size: 0.88rem;
    line-height: 1.5;
    text-align: center;
}

.viewer__state > .icon {
    width: 34px;
    height: 34px;
    opacity: 0.6;
}

.viewer__state p {
    margin: 0;
}

/*
 * The arrows to the file before and the file after, at the two edges of the
 * stage, half way up - which is above the controls a film draws along its
 * bottom. Half the height of the bar lower than the middle of the dialog,
 * because the stage begins under the bar.
 */
.viewer__step {
    position: absolute;
    top: calc(50% + 1.6rem);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.6rem;
    height: 3.6rem;
    padding: 0;
    background: rgba(17, 17, 17, 0.72);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text-strong);
    cursor: pointer;
    transform: translateY(-50%);
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.viewer__step .icon {
    width: 22px;
    height: 22px;
}

.viewer__step:hover {
    background: var(--panel-hover);
    border-color: var(--accent);
    color: var(--accent);
}

.viewer__step--previous {
    left: 0.6rem;
}

.viewer__step--next {
    right: 0.6rem;
}

/*
 * Beside a text rather than over it: a picture is seen around an arrow, and
 * the words of a line lying under one could not be read at all.
 */
.viewer__inner:has(> .viewer__step:not([hidden])) > .viewer__stage[data-kind="text"] {
    padding: 0 3.3rem;
}

/* On the whole screen the dialog is the screen: no frame, no corners, nothing around it. */
.viewer:fullscreen {
    width: 100vw;
    height: 100vh;
    max-height: none;
    border: 0;
    border-radius: 0;
}

/* ------------------------------------------------------------ Move dialog */

/*
 * The dialog which asks where entries go (see files/moveDialog.ts): the
 * folder it stands in with the way back out, and the folders lying in it as
 * a list to walk on from.
 */
.move-dialog__path {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.4rem 0.6rem 0.4rem 0.4rem;
    background: var(--panel-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text-dim);
}

.move-dialog__path .icon {
    flex: none;
}

.move-dialog__path strong {
    min-width: 0;
    color: var(--text-strong);
    font-size: 0.86rem;
    font-weight: 600;
    overflow-wrap: anywhere;
}

/*
 * The list keeps a height of its own, so the dialog does not shrink and grow
 * with every folder walked into. Written after the frame it sits in: the
 * scrolling body of a dialog reaches half a rem past its edges, which a
 * framed list must not.
 */
.modal__inner > .move-dialog__list {
    gap: 0.1rem;
    height: 15rem;
    margin: 0;
    padding: 0.3rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    transition: opacity 0.15s ease;
}

.move-dialog__list[aria-busy="true"] {
    opacity: 0.5;
}

.move-dialog__folder {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    min-height: 2.3rem;
    padding: 0.3rem 0.6rem;
    background: transparent;
    border: 0;
    border-radius: var(--radius);
    color: var(--text);
    font-family: inherit;
    font-size: 0.86rem;
    text-align: left;
    cursor: pointer;
}

.move-dialog__folder .icon {
    flex: none;
    color: var(--accent);
}

.move-dialog__folder span {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.move-dialog__folder .move-dialog__chevron {
    color: var(--text-dim);
}

.move-dialog__folder:hover,
.move-dialog__folder:focus-visible {
    background: var(--panel-hover);
    color: var(--text-strong);
    outline: none;
}

/* A folder which is itself being moved leads nowhere the entries could go. */
.move-dialog__folder[disabled] {
    opacity: 0.45;
    cursor: default;
    pointer-events: none;
}

.move-dialog__empty {
    margin: auto;
    padding: 1rem;
    color: var(--text-dim);
    font-size: 0.85rem;
    text-align: center;
}

/* ----------------------------------------------------------- Share dialog */

/*
 * The links of an entry (see files/shareDialog.ts): what is shared, a box
 * for every link which leads to it - the address with its buttons, and under
 * it when it ends, how often it was used, whether it asks for a password -
 * and the form of a new one below a rule.
 */
.share-dialog__entry {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
    color: var(--text-dim);
    font-size: 0.9rem;
}

.share-dialog__entry .icon {
    flex: none;
}

.share-dialog__entry strong {
    min-width: 0;
    color: var(--text-strong);
    font-weight: 600;
    overflow-wrap: anywhere;
}

.share-links {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    transition: opacity 0.15s ease;
}

.share-links:empty {
    display: none;
}

.share-links[aria-busy="true"] {
    opacity: 0.5;
}

.share-link {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.65rem 0.7rem;
    background: var(--panel-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

/* An address is read letter by letter and copied whole, so it is written in the face in which every letter is its own width. */
.share-link__address input {
    font-family: var(--mono);
    font-size: 0.8rem;
}

.share-link__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem 0.9rem;
    color: var(--text-dim);
    font-size: 0.76rem;
}

.share-link__fact {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.share-link__note {
    min-width: 0;
    font-style: italic;
    overflow-wrap: anywhere;
}

/* A link which has run out opens nothing: its address is struck through, and it stays listed to be revoked. */
.share-link.is-expired .share-link__address input {
    color: var(--text-dim);
    text-decoration: line-through;
}

.share-link.is-expired .share-link__fact:first-child {
    color: var(--warn);
}

.share-form {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    margin-top: 0.3rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
}

.share-form__title {
    margin: 0;
    color: var(--text-dim);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* ---------------------------------------------------- Address in a toast */

/*
 * The address an entry is public at, under the sentence of the toast which
 * says so, and beside it the button which copies it (see files/browser.ts).
 * On a line of its own, so it breaks where it has to rather than after the
 * last word of the sentence.
 */
.flash__address {
    display: block;
    margin-top: 0.3rem;
    font-family: var(--mono);
    font-size: 0.78rem;
    overflow-wrap: anywhere;
}

/* The button takes the colour of the cross beside it; a toast colours its symbol, and this one is no symbol of the toast. */
.flash .flash__copy {
    display: inline-flex;
    align-items: center;
    padding: 0.15rem 0.2rem;
}

.flash .flash__copy .icon {
    width: 15px;
    height: 15px;
    margin-top: 0;
    color: inherit;
}

/* ------------------------------------------------ Public and shared pages */

/*
 * The page a link to one file opens (pages/shared_file.html): the panel of a
 * listing with one file in it. The bar across its top is the bar of a
 * folder - the name of the file where the way down to the folder stands, the
 * download where its buttons are - and under it come a look at the file,
 * when the browser can show it, and what a row would say of it.
 */

/* The name of the one file of the page is read whole, however long: it wraps rather than ending in three dots. */
.shared-file .browser-toolbar__title strong {
    white-space: normal;
    overflow-wrap: anywhere;
}

.shared-file .file-name {
    padding: 0.2rem 0.15rem;
}

/*
 * The look at the file, edge to edge under the bar: a stage of the viewer
 * which stands on the page. A picture and a film are shown whole inside a
 * box no taller than two thirds of the window, so the bar above them and
 * the size below stay on the screen without scrolling.
 */
.shared-file__preview {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 9rem;
    background: var(--bg-alt);
    border-bottom: 1px solid var(--border);
    overflow: hidden;
}

.shared-file__preview img,
.shared-file__preview video {
    display: block;
    max-width: 100%;
    max-height: 66vh;
    max-height: 66svh;
    object-fit: contain;
}

.shared-file__preview[data-kind="video"] video {
    width: 100%;
}

/* A picture is a link into the viewer, and says so to the pointer. */
.shared-file__preview > a {
    display: block;
    cursor: zoom-in;
}

.shared-file__preview[data-kind="audio"] {
    padding: 1.5rem;
}

.shared-file__preview audio {
    width: min(100%, 32rem);
}

/* A long text scrolls inside its box, so the page stays the length of a page. */
.shared-file__preview .viewer__text {
    max-height: 66vh;
    max-height: 66svh;
}

/* What a row of a listing says of its file, under the headings of its columns. */
.shared-file__facts {
    padding: 1rem 1.15rem 1.1rem;
}

.shared-file__facts dt .icon {
    color: var(--text-dim);
}

/* ---------------------------------------------------------- Narrow screens */

@media (max-width: 640px) {
    /* The way down to the folder takes the line, and the buttons the next one. */
    .browser-toolbar {
        flex-wrap: wrap;
    }

    .browser-toolbar__title {
        flex-basis: 100%;
    }

    .browser-selection .button-label {
        display: none;
    }

    /*
     * A row is a card, and the box which chooses it sits in the corner of
     * the card rather than taking a line of its own above the name.
     */
    table.data--stack.file-table tr {
        position: relative;
    }

    table.data--stack.file-table td.file-table__check {
        position: absolute;
        top: 0.75rem;
        right: 0.9rem;
        width: auto;
        padding: 0;
    }

    table.data--stack.file-table tr:has(> .file-table__check) td:has(> .file-link) {
        padding-right: 2rem;
    }

    /* The name and what is said beside it wrap together, close to one another. */
    table.data--stack.file-table td:has(> .file-link) {
        flex-wrap: wrap;
        gap: 0.35rem;
    }

    table.data--stack.file-table td > .badge {
        margin-left: 0;
    }

    /* Too narrow for six columns: the bar moves under the name, and the size is left to the listing. */
    .upload-row {
        grid-template-columns: auto minmax(0, 1fr) auto auto;
    }

    .upload-row__size {
        display: none;
    }

    .upload-row__percent {
        grid-column: 3;
    }

    .upload-row__cancel {
        grid-column: 4;
    }

    .upload-row__bar {
        grid-column: 2 / -1;
        grid-row: 2;
    }

    .upload-row__message {
        grid-row: 3;
    }

    /*
     * The bar of a shared file: its name takes the line like the way down to
     * a folder does, and the download the whole of the next one - it is what
     * the page is for, and a thumb finds it without aiming.
     */
    .shared-file .browser-toolbar .button-row {
        flex: 1 1 100%;
    }

    .shared-file .browser-toolbar .button--primary {
        flex: 1 1 auto;
        order: -1;
    }

    .shared-file__facts {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* The viewer is the window. */
    .viewer {
        width: 100vw;
        max-width: 100vw;
        height: 100vh;
        height: 100svh;
        max-height: none;
        border: 0;
        border-radius: 0;
    }

    .viewer__step {
        width: 2.2rem;
        height: 3rem;
    }

    .viewer__step--previous {
        left: 0.3rem;
    }

    .viewer__step--next {
        right: 0.3rem;
    }

    .viewer__inner:has(> .viewer__step:not([hidden])) > .viewer__stage[data-kind="text"] {
        padding: 0 2.6rem;
    }

    .viewer__inner:has(> .viewer__step:not([hidden])) .viewer__text {
        padding-right: 0.35rem;
        padding-left: 0.35rem;
    }
}
