/*
 * Share Portal - application styles.
 *
 * Logical properties only (inline/block, start/end). Nothing here refers to left or
 * right, so the same rules lay out correctly in both English (ltr) and Arabic (rtl);
 * the <html dir> attribute is the only switch.
 *
 * Self-hosted by definition: the font stack is whatever the operating system already
 * has, so there is no web font and no external request of any kind.
 */

:root {
    /* --accent is set inline by the layout from the admin-configurable setting. */
    --accent: #0e7490;

    --surface: #ffffff;
    --surface-sunken: #f1f5f9;
    --ink: #0f172a;
    --ink-muted: #475569;
    --line: #cbd5e1;
    --danger: #b91c1c;
    --danger-surface: #fef2f2;

    --radius: 8px;
    --gap: 1rem;
    --measure: 46rem;

    /*
     * A listing is not prose. --measure is a reading width - the length of line a person
     * reads comfortably - and it is the right width for a form, a message thread or a page
     * of settings. A table of seven columns read inside one is squeezed until a name breaks
     * letter by letter and the table scrolls sideways at a window width where its content
     * fits three times over. The pages that hold a table are laid out to this width instead.
     */
    --measure-wide: 72rem;

    --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans Arabic",
        "Segoe UI Arabic", "Dubai", Tahoma, "Helvetica Neue", Arial, sans-serif;
}

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--surface-sunken);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 1rem;
    line-height: 1.6;
}

h1 {
    margin-block: 0 0.5rem;
    font-size: 1.5rem;
    line-height: 1.3;
}

p {
    margin-block: 0 0.75rem;
}

p:last-child {
    margin-block-end: 0;
}

a {
    color: var(--accent);
    text-underline-offset: 0.2em;
}

a:focus-visible,
button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Visible only once focused, so keyboard users can jump past the header. */
.skip-link {
    position: absolute;
    inset-block-start: -3rem;
    inset-inline-start: 0.5rem;
    z-index: 10;
    padding: 0.5rem 0.75rem;
    border-radius: 0 0 var(--radius) var(--radius);
    background: var(--surface);
    color: var(--accent);
    transition: inset-block-start 120ms ease-out;
}

.skip-link:focus {
    inset-block-start: 0;
}

.site-header {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap);
    align-items: center;
    justify-content: space-between;
    padding-block: 0.9rem;
    padding-inline: var(--gap);
    border-block-end: 1px solid var(--line);
    /* The accent reads as a brand rule along the top of every page. */
    box-shadow: inset 0 3px 0 0 var(--accent);
    background: var(--surface);
}

.site-name {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 600;
}

.header-tools {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap);
    align-items: center;
}

.site-identity {
    display: flex;
    flex-direction: column;
    margin: 0;
    line-height: 1.3;
}

.lang-switch,
.theme-switch,
.sign-out {
    display: flex;
    gap: 0.5rem;
    margin: 0;
}

.sign-out button,
.theme-switch button,
.lang-switch button {
    padding-block: 0.35rem;
    padding-inline: 0.85rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink-muted);
    font: inherit;
    cursor: pointer;
}

.sign-out button:hover,
.theme-switch button:hover,
.lang-switch button:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/*
 * The account menu. The bar shows one circle - the account's mark, its initial - and
 * the panel behind it holds what used to sit in the bar as loose controls: who is
 * signed in, the language switch, the way out. A <details>, so it needs no script to
 * open or close; assets/shell.js only adds outside-click and Escape.
 */
.account-menu {
    position: relative;
}

.account-trigger {
    display: inline-flex;
    border-radius: 50%;
    cursor: pointer;
    /* The disclosure triangle a <summary> draws by itself: the circle is the control. */
    list-style: none;
}

.account-trigger::-webkit-details-marker {
    display: none;
}

.account-trigger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.account-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 2.5rem;
    block-size: 2.5rem;
    border-radius: 50%;
    background: var(--accent);
    color: var(--surface);
    font-weight: 600;
    user-select: none;
}

.account-trigger:hover .account-avatar,
.account-menu[open] .account-avatar {
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}

.account-panel {
    position: absolute;
    inset-block-start: calc(100% + 0.5rem);
    inset-inline-end: 0;
    z-index: 20;
    display: grid;
    gap: 0.75rem;
    min-inline-size: 14rem;
    padding: 1rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
}

/* The identity block reads as the panel's header: a line under it separates who you
   are from what you can do. */
.account-panel .site-identity {
    padding-block-end: 0.75rem;
    border-block-end: 1px solid var(--line);
}

/* Inside the panel the forms become full-width rows rather than bar buttons. */
.account-panel .lang-switch,
.account-panel .theme-switch,
.account-panel .sign-out {
    display: grid;
}

.account-panel .lang-switch button,
.account-panel .theme-switch button,
.account-panel .sign-out button {
    inline-size: 100%;
    text-align: center;
}

.site-main {
    max-inline-size: var(--measure);
    margin-block: 2rem;
    margin-inline: auto;
    padding-inline: var(--gap);
}

/*
 * The wider width, asked for by the content rather than by the page: a view that holds a
 * data table gets it, and no controller, no view and no extra class has to carry the
 * knowledge. The administrator's bar is widened by the same question so that it keeps the
 * panel's own edge - the bar and the panel below it start together on every page.
 *
 * A browser that does not know :has() ignores both rules and lays these pages out at the
 * reading width, which is what they did before: narrower than they deserve, never broken.
 */
.site-main:has(.data-table) {
    max-inline-size: var(--measure-wide);
}

body:has(.data-table) .admin-bar-list {
    max-inline-size: var(--measure-wide);
}

.panel {
    padding-block: 1.5rem;
    padding-inline: 1.5rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
}

.panel-error .status-code {
    margin-block-end: 0.25rem;
    color: var(--ink-muted);
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.08em;
}

/* A form reads better narrow than the full measure. */
.panel-form {
    max-inline-size: 26rem;
    margin-inline: auto;
}

.form-stack {
    margin-block: 1.25rem 0;
}

.field {
    margin-block: 0 1rem;
}

.field:last-child {
    margin-block-end: 0;
}

.field label {
    display: block;
    margin-block-end: 0.3rem;
    color: var(--ink-muted);
    font-size: 0.9rem;
    font-weight: 600;
}

.field input {
    inline-size: 100%;
    padding-block: 0.55rem;
    padding-inline: 0.7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
}

.field input:focus-visible {
    border-color: var(--accent);
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.button-primary {
    padding-block: 0.55rem;
    padding-inline: 1.4rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--accent);
    color: #ffffff;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

.button-primary:hover {
    filter: brightness(1.08);
}

.button-link {
    display: inline-block;
    padding-block: 0.5rem;
    padding-inline: 1.2rem;
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    color: var(--accent);
    font-weight: 600;
    text-decoration: none;
}

.button-link:hover {
    background: var(--accent);
    color: #ffffff;
}

/*
 * Icons. Every symbol lives in one hand-drawn sprite
 * (share_app/views/partials/icon-sprite.php), and a page draws one with
 * <svg class="icon"><use href="#ic-pdf"></use></svg>.
 *
 * The sprite element itself draws nothing: it is taken out of the flow rather than given
 * display: none, because a <use> reference into a display: none tree is not drawn by every
 * browser. Sizes are in em where the icon sits in a line of text and in rem where it is
 * the picture on a tile, so an icon beside a word grows with the word.
 */
.icon-sprite {
    position: absolute;
    inline-size: 0;
    block-size: 0;
    overflow: hidden;
}

.icon {
    inline-size: 1.25em;
    block-size: 1.25em;
    /* Sits on the text baseline rather than above it, beside a word. */
    vertical-align: -0.28em;
    /* Never squashed when it sits in a flex row beside a long name. */
    flex: none;
}

/* The picture on a tile: the same drawing, sized to be read across a grid. */
.icon-tile {
    inline-size: 2.75rem;
    block-size: 2.75rem;
    vertical-align: baseline;
}

/* And on the page of one file, where it is the heading's own picture. */
.icon-file {
    inline-size: 3.25rem;
    block-size: 3.25rem;
    vertical-align: baseline;
}

/* The accent bar sits on the start edge, so it flips with the document direction. */
.form-error {
    margin-block: 0 1rem;
    padding-block: 0.6rem;
    padding-inline: 0.8rem;
    border-inline-start: 3px solid var(--danger);
    border-start-end-radius: var(--radius);
    border-end-end-radius: var(--radius);
    background: var(--danger-surface);
    color: var(--danger);
}

.identity-name {
    font-size: 0.95rem;
    font-weight: 600;
}

.identity-meta {
    color: var(--ink-muted);
    font-size: 0.8rem;
}

/*
 * The folder tree, which is now the administrator's tree manager and nothing else - the
 * client pages browse as a grid of tiles. The indent is a padding on the inline start
 * edge, so it steps away from the reading edge in English and Arabic alike without a
 * second rule.
 */
.folder-tree {
    margin-block-start: 1rem;
}

.tree-list {
    margin-block: 0;
    padding-inline-start: 1.25rem;
    list-style: none;
}

.folder-tree > .tree-list {
    padding-inline-start: 0;
}

.tree-item {
    margin-block: 0.15rem;
    border-inline-start: 1px solid var(--line);
    padding-inline-start: 0.75rem;
}

.folder-tree > .tree-list > .tree-item {
    margin-block: 0.6rem;
    border-inline-start: 3px solid var(--accent);
    font-weight: 600;
}

.folder-tree > .tree-list > .tree-item .tree-item {
    font-weight: 400;
}

/* Breadcrumb: a plain list with a separator drawn before every step but the first. */
.breadcrumb {
    margin-block-end: 0.75rem;
    font-size: 0.9rem;
}

.breadcrumb-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.breadcrumb-item + .breadcrumb-item::before {
    content: "/";
    margin-inline-end: 0.35rem;
    color: var(--ink-muted);
}

.breadcrumb-item [aria-current="page"] {
    color: var(--ink-muted);
}

.section-title {
    margin-block: 1.5rem 0.5rem;
    font-size: 1rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink-muted);
}

.empty-state {
    margin-block: 0;
    padding-block: 0.75rem;
    color: var(--ink-muted);
}

/*
 * Said only to a screen reader: the word that tells a tile apart from its neighbour when
 * the symbol cannot be seen. Clipped rather than hidden, because display: none and
 * visibility: hidden are not announced at all.
 */
.visually-hidden {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/*
 * Browsing: a toolbar over a grid of tiles. The grid fills the panel with as many columns
 * as fit, so the same rules lay out four tiles on a phone and a dozen on a desktop, and
 * the tiles start from the edge the language reads from without a rule of their own.
 */
.explorer-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--gap);
    margin-block-end: 1rem;
}

.toolbar-title {
    margin-block: 0;
    /* A folder name is a label: isolated, so an Arabic name in an English page - or the
       other way round - does not drag the toolbar around with it. */
    unicode-bidi: isolate;
}

.toolbar-button {
    /* Rendered hidden and shown by the script: without it the button would do nothing. */
    flex: none;
}

.explorer-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
    gap: 0.5rem;
    margin-block: 0;
    padding-inline-start: 0;
    list-style: none;
}

.explorer-item {
    display: flex;
}

.tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    inline-size: 100%;
    padding-block: 0.9rem 0.75rem;
    padding-inline: 0.5rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    color: var(--ink);
    text-align: center;
    text-decoration: none;
}

a.tile:hover,
a.tile:focus-visible {
    border-color: var(--line);
    background: var(--surface-sunken);
}

.tile .icon-tile {
    color: var(--accent);
}

.tile-name {
    /* Two lines, then an ellipsis: the whole name is on the tile's title attribute, and
       the file's own page carries it in full. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    font-size: 0.9rem;
    line-height: 1.35;
    word-break: break-word;
    /* A name is whatever somebody called their file: isolated from the page around it, so
       an Arabic name reads correctly in an English grid and the other way round. */
    unicode-bidi: isolate;
}

/* A narrow window scrolls a table, never the page. */
.table-scroll {
    overflow-x: auto;
}

/*
 * The administrator's listings. One table style for accounts and for every file in the
 * portal - the client pages browse as a grid and have no table at all.
 */
.data-table {
    inline-size: 100%;
    border-collapse: collapse;
    font-size: 0.95rem;
}

.data-table th,
.data-table td {
    padding-block: 0.5rem;
    padding-inline: 0.6rem;
    border-block-end: 0;
    text-align: start;
    vertical-align: top;
}

/* One line per row, drawn once. */
.data-table tbody tr {
    border-block-end: 1px solid var(--line);
}

.data-table th {
    color: var(--ink-muted);
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.data-table tbody tr:last-child {
    border-block-end: 0;
}

.file-name {
    font-weight: 600;
    word-break: break-word;
    /*
     * A name is whatever somebody called their file, so it is isolated from the text
     * around it: an Arabic name reads correctly inside an English table and the other way
     * round, and neither one drags the column's alignment with it. Isolate rather than
     * plaintext deliberately - plaintext would align each row by its own direction and
     * leave one column reading from both edges.
     */
    unicode-bidi: isolate;
}

.file-size,
.file-date {
    white-space: nowrap;
    /*
     * Sizes and dates stay in Western digits and read the same way in both languages.
     * The direction is what makes that true: without it a stamp written as two runs
     * ("2026-08-12" and "21:48") is laid out by the page's direction and the two swap
     * places on an Arabic page. See the [dir="rtl"] block near the end of this file for
     * what that costs and how the alignment is given back.
     */
    direction: ltr;
    unicode-bidi: isolate;
    text-align: start;
}

/*
 * A name is a label, like the file name beside it (see .file-name): the person who
 * uploaded a file, and each company in the list an account may reach. Isolated, so a
 * name written in one direction cannot take the separators beside it with it.
 */
.file-uploader,
.company-name {
    unicode-bidi: isolate;
}

/*
 * In a table, and only there: a name is what a listing is read by, so it is not the first
 * thing squeezed when a row also holds a long address or a long path. The automatic layout
 * hands each column room in proportion to the longest thing in it, and one 69-character
 * address beside them was enough to leave a two-word name breaking over three lines.
 *
 * Minimums, not widths - the columns still size themselves to what they hold - and only
 * inside .data-table, because the same two classes label a value in the remove-file
 * confirmation, where the list is a pair of columns rather than seven. At phone width the
 * table scrolls inside .table-scroll rather than taking the page with it.
 */
.data-table .file-name {
    min-inline-size: 12rem;
}

.data-table .file-uploader {
    min-inline-size: 7rem;
}

/*
 * The page of one file: its icon and details, what may be done with it, and the thread
 * of messages people have written on it.
 */
.file-detail .breadcrumb {
    /* The last step here is the file's own name, which can be a long one: the path gets
       a little more room before the heading than it needs on a folder page. */
    margin-block-end: 1rem;
}

.detail-header {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    margin-block-end: 1rem;
}

.detail-heading {
    /* Lets a long name wrap instead of pushing the icon out of the panel. */
    min-inline-size: 0;
}

.detail-name {
    margin-block: 0 0.5rem;
    font-size: 1.35rem;
    word-break: break-word;
    /* A name is a label, so it is isolated from the page around it (see .tile-name). */
    unicode-bidi: isolate;
}

.detail-meta {
    display: grid;
    /*
     * minmax(0, 1fr), not 1fr: a grid track's automatic minimum is the widest thing that
     * cannot be broken, so one long address or one unbroken file name would otherwise push
     * the whole list out through the side of its panel instead of wrapping inside it.
     */
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 0.2rem 1rem;
    margin: 0;
    font-size: 0.9rem;
}

.detail-meta dt {
    color: var(--ink-muted);
}

.detail-meta dd {
    margin: 0;
    unicode-bidi: isolate;
    /* An address or a file name has no space in it to wrap at; it wraps anyway. */
    overflow-wrap: anywhere;
}

/* The actions sit between the file and its thread, fenced off from both. */
.detail-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-block: 1rem;
    padding-block: 0.75rem;
    border-block: 1px solid var(--line);
}

.action-button {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

/* The one action on this row that takes something away reads as exactly that. */
.action-danger {
    color: var(--danger);
    border-color: var(--danger);
}

.action-danger:hover {
    background: var(--danger-surface);
    border-color: var(--danger);
    color: var(--danger);
}

/* The replace zone sits between the actions and the thread, like the folder page's
   upload zone sits under its toolbar. */
.replace-zone {
    margin-block-start: 1rem;
}

/* Why there is no View button for this file, where that button would have been. */
.detail-note {
    margin: 0;
    color: var(--ink-muted);
    font-size: 0.9rem;
}

/*
 * The thread. Append-only, oldest first, one card per line somebody wrote. Laid out with
 * logical properties like everything else here, so the accent on a reader's own lines and
 * the stamp at the end of a row mirror in Arabic without a second rule.
 */
.chatter {
    /* Set apart from the actions above it: what is done to a file and what is said about
       it are two different things. */
    margin-block-start: 1.5rem;
}

.thread {
    margin-block: 0 1rem;
    padding-inline-start: 0;
    list-style: none;
}

.message {
    margin-block-end: 0.6rem;
    padding-block: 0.55rem;
    padding-inline: 0.8rem;
    border-radius: var(--radius);
    background: var(--surface-sunken);
}

/* A reader's own lines carry the accent on the edge their language starts from. */
.message.is-mine {
    border-inline-start: 3px solid var(--accent);
}

.message-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    margin-block-end: 0.25rem;
    font-size: 0.8rem;
    color: var(--ink-muted);
}

.message-author {
    font-weight: 600;
    color: var(--ink);
    unicode-bidi: isolate;
}

.message-mine {
    padding-inline: 0.4em;
    border-radius: 999px;
    background: var(--accent);
    color: #ffffff;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.message-date {
    margin-inline-start: auto;
    /* A stamp is a number in both languages, like every other number the portal shows. */
    direction: ltr;
    unicode-bidi: isolate;
    white-space: nowrap;
}

.message-text {
    margin-block: 0;
    /* A message is typed, so the line breaks a person made are the ones they meant. */
    white-space: pre-wrap;
    word-break: break-word;
    /*
     * A message is a paragraph in whatever language it was written in, and it can sit
     * beside messages in the other one. Plaintext here (not isolate, as on a file name):
     * a paragraph is laid out and aligned by its own first letter, so an Arabic message
     * keeps its full stop on the correct side of an English page.
     */
    unicode-bidi: plaintext;
}

.composer {
    margin-block-start: 0.5rem;
}

.composer-label {
    display: block;
    margin-block-end: 0.25rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--ink-muted);
}

.composer-input {
    inline-size: 100%;
    padding-block: 0.5rem;
    padding-inline: 0.6rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font-family: inherit;
    font-size: 0.95rem;
    line-height: 1.5;
    resize: vertical;
}

.composer-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-block-start: 0.5rem;
}

.composer-status {
    margin-block: 0;
    font-size: 0.85rem;
    color: var(--ink-muted);
}

.composer-status.is-error {
    color: var(--danger);
}

/*
 * Upload zone. The widget brings its own stylesheet (vendored, unedited); these rules
 * only place it on the page and let it inherit the portal's colours, so the drop zone
 * looks like part of the panel it sits in rather than a component dropped on top.
 */
.upload-zone {
    margin-block-end: 0.5rem;
}

.upload-zone .filepond--root {
    margin-block-end: 0;
    font-family: inherit;
}

.upload-zone .filepond--panel-root {
    border: 1px dashed var(--line);
    background: var(--surface-sunken);
}

.upload-zone .filepond--drop-label {
    color: var(--ink-muted);
}

.upload-zone .filepond--label-action {
    color: var(--accent);
    text-decoration-color: var(--accent);
}

/*
 * The one place the portal has to argue with the library it vendored.
 *
 * filepond.min.css pins the widget to one direction: `.filepond--root` ships
 * `direction: ltr` with `text-align: left`. On an Arabic page that leaves an island of
 * the other direction inside a right-to-left document - the name of the file at the far
 * edge from the reading one, and the state of the upload at the near one. These rules
 * turn that island round: the name and the size read from the edge Arabic starts at, the
 * state follows them, and each column keeps its room for the controls beside it.
 *
 * Three properties of them are deliberate. They are scoped to [dir="rtl"] and to the
 * portal's own .upload-zone, so an English page and everything outside the zone are
 * untouched. They are written with logical properties, like every other rule here. And
 * they are HERE rather than in the vendored file: public/assets/filepond/ stays the
 * published bytes, which scripts/tests/upload_assets.test.php pins by hash. Every
 * selector outranks the vendored rule it overrides, so this holds wherever the two
 * stylesheets sit in the load order.
 *
 * What is NOT here, and why: the row's controls stay on the edges the library pins them
 * to. The library measures the position of the button that removes a file and slides the
 * name aside by that measurement, along the physical axis - so a stylesheet that moves
 * that button moves the name with it, off the panel (measured: a 613px slide on a 622px
 * row). Those positions are the library's own `styleButtonRemoveItemPosition` and
 * `styleProgressIndicatorPosition` options, which is where a mirrored control column
 * belongs; the margins below keep each column clear of them in the meantime.
 */
[dir="rtl"] .upload-zone .filepond--root {
    direction: rtl;
    text-align: start;
}

/* The name and the size read from the edge the language starts at, clear of the control. */
[dir="rtl"] .upload-zone .filepond--file-info {
    margin-inline: 2.25em 0.5em;
}

/*
 * A name is a label, so it is isolated from the row around it (see .file-name). Isolate
 * rather than plaintext, and for the same reason as there: the rows are a list, and
 * plaintext would lay each name out by its own first letter and leave the list reading
 * from both edges at once - which is what a browser shows if this says plaintext.
 */
[dir="rtl"] .upload-zone .filepond--file-info-main {
    unicode-bidi: isolate;
}

/* What the upload is doing follows the name, with room for the control past it. */
[dir="rtl"] .upload-zone .filepond--file .filepond--file-status {
    margin-inline: auto 2.25em;
    text-align: end;
}

/*
 * The states where the library slides the name aside to clear the button that removes a
 * file: 2.1875em of it (that button's own 1.625em beside its 0.5625em inset), applied to
 * the physical axis, which on a mirrored row moves the name towards the control instead
 * of away from it. The room is given back here, so the name still stops short of the
 * control - measured in the same two lengths the slide is made of, so the two cannot
 * drift apart. The state names are the library's own, and the last two are the ones its
 * stylesheet still carries from an earlier release.
 */
[dir="rtl"] .upload-zone [data-filepond-item-state="idle"] .filepond--file-info,
[dir="rtl"] .upload-zone [data-filepond-item-state*="error"] .filepond--file-info,
[dir="rtl"] .upload-zone [data-filepond-item-state*="invalid"] .filepond--file-info,
[dir="rtl"] .upload-zone [data-filepond-item-state="cancelled"] .filepond--file-info {
    margin-inline-start: calc(2.25em + 2.1875em);
}

/*
 * Administrator area. Everything here is laid out with logical properties like the rest of
 * the stylesheet, so the bar, the tree controls and the tables mirror in Arabic without a
 * second rule anywhere.
 */
.site-brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.site-logo {
    /* Bounded in both directions: a logo is whatever somebody uploaded, and the header
       has to keep its height whether that is a wide wordmark or a square badge. */
    max-block-size: 2.2rem;
    max-inline-size: 12rem;
    inline-size: auto;
    block-size: auto;
}

.admin-bar {
    border-block-end: 1px solid var(--line);
    background: var(--surface);
}

.admin-bar-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1rem;
    max-inline-size: var(--measure);
    margin-block: 0;
    margin-inline: auto;
    padding-block: 0.5rem;
    padding-inline: var(--gap);
    list-style: none;
}

.admin-bar-item a {
    font-size: 0.9rem;
    text-decoration: none;
}

.admin-bar-item a:hover {
    text-decoration: underline;
}

.panel-intro,
.panel-actions {
    color: var(--ink-muted);
}

.admin-sections {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-block: 1rem 0;
    padding-inline-start: 0;
    list-style: none;
}

/* The dashboard: five cards, then what happened lately, then the panels above. */
.dashboard-heading {
    margin-block: 1.75rem 0.5rem;
    font-size: 1.05rem;
}

.stat-cards {
    display: grid;
    /* As many cards as fit, never narrower than a number and its label. */
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.75rem;
    margin-block: 1rem 0;
    padding-inline-start: 0;
    list-style: none;
}

.stat {
    padding-block: 0.8rem;
    padding-inline: 0.9rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-sunken);
}

.stat-value {
    margin-block: 0 0.15rem;
    font-size: 1.8rem;
    font-weight: 700;
    line-height: 1.1;
    /*
     * A count is a number in both languages, like every other number the portal shows,
     * and one run of digits is drawn left to right whatever the page's direction is. No
     * direction of its own, on purpose: it would decide where `start` is for this box too
     * and leave the number sitting under the far end of its own label in Arabic.
     */
    unicode-bidi: isolate;
}

.stat-label {
    margin-block: 0;
    font-size: 0.9rem;
}

.stat-note {
    margin-block: 0.25rem 0;
    color: var(--ink-muted);
    font-size: 0.8rem;
}

.activity-list {
    margin-block: 0;
    padding-inline-start: 0;
    list-style: none;
}

.activity {
    border-block-end: 1px solid var(--line);
}

.activity:last-child {
    border-block-end: 0;
}

/* The whole row is the link: a thing that happened has one thing to open. */
.activity-link {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    padding-block: 0.6rem;
    padding-inline: 0.5rem;
    border-radius: var(--radius);
    color: var(--ink);
    text-decoration: none;
}

.activity-link:hover {
    background: var(--surface-sunken);
}

.activity-text {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem;
    /*
     * A flex item refuses by default to be drawn narrower than the widest unbreakable thing
     * inside it, and a file name can be sixty characters with nowhere to break. Without this
     * the row is as wide as that name, the panel is as wide as the row, and on a phone the
     * whole page slides sideways - measured at 390px: a 615px row inside a 293px box took
     * the document 281px past its own width.
     */
    min-inline-size: 0;
}

.activity-actor {
    font-weight: 600;
}

.activity-action {
    color: var(--ink-muted);
}

.activity-file {
    /* A name is a label: isolated, so an Arabic file name cannot drag an English row
       around it, nor the other way round. */
    unicode-bidi: isolate;
    /* And a name with no space in it wraps anyway, rather than setting the row's width. */
    overflow-wrap: anywhere;
}

.activity-date {
    font-size: 0.8rem;
    color: var(--ink-muted);
    direction: ltr;
    unicode-bidi: isolate;
    white-space: nowrap;
    /*
     * The stamp ends the row whether or not the row fitted on one line. space-between only
     * decides where things sit while they share a line: a row holding a long file name
     * wraps, and the stamp then begins a line of its own at the edge the reading starts
     * from - so a list of short rows and long rows had its stamps down both sides at once.
     * The auto margin says "as far along the row as this can go" in either case, and it is
     * said again below in the page's terms, because this box keeps a direction of its own.
     */
    margin-inline-start: auto;
}

.form-done {
    margin-block: 0 1rem;
    padding-block: 0.6rem;
    padding-inline: 0.8rem;
    border-inline-start: 3px solid var(--accent);
    border-start-end-radius: var(--radius);
    border-end-end-radius: var(--radius);
    background: var(--surface-sunken);
}

.form-warning {
    margin-block: 0 1rem;
    padding-block: 0.6rem;
    padding-inline: 0.8rem;
    border-inline-start: 3px solid var(--danger);
    border-start-end-radius: var(--radius);
    border-end-end-radius: var(--radius);
    background: var(--danger-surface);
    color: var(--danger);
}

.field-hint {
    display: block;
    margin-block-start: 0.25rem;
    color: var(--ink-muted);
    font-size: 0.8rem;
    font-weight: 400;
}

.field-error {
    display: block;
    margin-block: 0.25rem 0;
    color: var(--danger);
    font-size: 0.85rem;
}

.field select,
.field textarea {
    inline-size: 100%;
    padding-block: 0.55rem;
    padding-inline: 0.7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
}

.field-check label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 400;
    color: var(--ink);
}

.field-check input,
.check-row input {
    inline-size: auto;
}

.field-group {
    margin-block: 0 1rem;
    padding-block: 0.5rem 0.75rem;
    padding-inline: 0.9rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

.field-group legend {
    padding-inline: 0.35rem;
    color: var(--ink-muted);
    font-size: 0.9rem;
    font-weight: 600;
}

.check-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-block: 0.2rem;
}

/*
 * A folder in the grant picker that is ticked because a folder above it is granted: the
 * access is real, and it is not edited here - the grant is changed at the folder that
 * carries it. The box says so by being disabled, which the browser draws on its own; the
 * row says so by going quiet.
 *
 * The ordinary colour is spelled out at this weight on purpose. `.field label` already
 * paints every label in a field --ink-muted, so a rule carrying the weight of one class
 * would leave a covered row looking exactly like a row somebody can still tick. Both
 * tokens are redefined by [data-theme="dark"], so this pair dresses both palettes.
 */
.grant-tree .check-row {
    color: var(--ink);
}

.grant-tree .check-row.muted {
    color: var(--ink-muted);
}

.form-buttons {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
}

.form-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-block: 1rem;
}

.form-inline input {
    flex: 1 1 12rem;
    padding-block: 0.5rem;
    padding-inline: 0.7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
}

.cancel-link {
    color: var(--ink-muted);
    font-size: 0.9rem;
}

.button-danger {
    padding-block: 0.55rem;
    padding-inline: 1.4rem;
    border: 1px solid var(--danger);
    border-radius: var(--radius);
    background: var(--danger-surface);
    color: var(--danger);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

.danger-link {
    color: var(--danger);
    font-size: 0.9rem;
}

.panel-danger {
    border-color: var(--danger);
}

.badge {
    display: inline-block;
    padding-inline: 0.5em;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    /*
     * A state is one thing, so it is drawn as one thing: "Can sign in" broken over three
     * lines inside a pill reads as three states, and the row grows to the height of the
     * pill rather than the height of its text. The column is sized to hold the longest of
     * the words this can say, in either language.
     */
    white-space: nowrap;
}

.badge-on {
    background: var(--surface-sunken);
    color: var(--ink);
}

.badge-off {
    background: var(--danger-surface);
    color: var(--danger);
}

.badge-root {
    margin-inline-start: 0.4rem;
    background: var(--accent);
    color: #ffffff;
}

.muted {
    color: var(--ink-muted);
}

/* An email address is an identifier: it reads the same way in both languages. */
.user-identifier {
    direction: ltr;
    unicode-bidi: isolate;
    text-align: start;
    /*
     * And it wraps rather than setting its column's width. An address has no space in it,
     * so a column sized to hold one whole is as wide as the longest address anybody has:
     * measured at 1366px with a 69-character address, the accounts table asked for 1114px
     * inside a 1070px card and put a scrollbar under it. Wrapped, it asks for what is left.
     */
    overflow-wrap: anywhere;
}

.summary-list {
    display: grid;
    /* minmax(0, 1fr) for the reason .detail-meta gives: measured on the screen that shows a
       new account, a 69-character address left the list 82px wider than the panel holding
       it and the address was drawn across the panel's own edge. */
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 0.35rem 1rem;
    margin-block: 0 1rem;
}

.summary-list dt {
    color: var(--ink-muted);
    font-size: 0.85rem;
}

.summary-list dd {
    margin: 0;
    /*
     * A value in a labelled list is a label of its own - a name, an address, a path - so
     * it is isolated from the list around it and still starts where its own label does.
     * Isolate rather than a dir attribute, and for the same reason as .file-name: the
     * list is a column, and letting each value choose its own side leaves the column
     * reading from both edges at once.
     */
    unicode-bidi: isolate;
    /* An address or a path has no space in it to wrap at; it wraps anyway. */
    overflow-wrap: anywhere;
}

.one-time-password {
    inline-size: 100%;
    padding-block: 0.6rem;
    padding-inline: 0.7rem;
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    /* A password is read character by character, so it is set in a face where the
       characters cannot be confused with one another, and never mirrored. */
    font-family: ui-monospace, "Cascadia Mono", Consolas, "Liberation Mono", monospace;
    font-size: 1.05rem;
    letter-spacing: 0.08em;
    direction: ltr;
    unicode-bidi: isolate;
    text-align: start;
}

/* The tree manager: the tree from the client pages, with a panel of forms on each node. */
.admin-tree .node-name {
    font-weight: 600;
}

.node-actions {
    margin-block: 0.15rem 0.4rem;
}

.node-actions summary {
    color: var(--ink-muted);
    font-size: 0.8rem;
    font-weight: 400;
    cursor: pointer;
}

.node-forms {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-block-start: 0.5rem;
    padding-block: 0.6rem;
    padding-inline: 0.75rem;
    border-radius: var(--radius);
    background: var(--surface-sunken);
}

.node-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
}

.node-form input,
.node-form select {
    flex: 1 1 10rem;
    /*
     * A field will not be drawn narrower than the text it can hold unless it is told it
     * may. Deep in the tree there is little room left - every level takes 2rem of the
     * indent - and without this the field keeps its own width and is drawn off the side of
     * a phone: measured at 390px on the sixth level, a 188px row in 56px of space, with the
     * field reaching 79px past the edge of the screen and no way to scroll to it.
     */
    min-inline-size: 0;
    padding-block: 0.35rem;
    padding-inline: 0.5rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
    font-size: 0.9rem;
}

.node-form button {
    padding-block: 0.3rem;
    padding-inline: 0.8rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink-muted);
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
}

.node-form button:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.node-danger button {
    border-color: var(--danger);
    color: var(--danger);
}

.node-label {
    flex: 0 0 100%;
    color: var(--ink-muted);
    font-size: 0.8rem;
    font-weight: 600;
}

.node-note {
    margin: 0;
    color: var(--ink-muted);
    font-size: 0.8rem;
}

/*
 * The one destination list, which every Move button on the page posts to. It belongs to the
 * whole tree rather than to the folder it happens to sit above, so it is drawn as its own
 * band - the same sunken shape a node's own forms get - and not as part of the first node
 * in the list. Its label, select and hint are all styled by .node-form above; this rule only
 * sets the band apart from the tree beneath it.
 */
.folder-move-form {
    margin-block-end: 0.75rem;
    padding-block: 0.6rem;
    padding-inline: 0.75rem;
    border-radius: var(--radius);
    background: var(--surface-sunken);
}

/* The path to a file, in the all-files table. */
.file-where {
    font-size: 0.9rem;
    unicode-bidi: isolate;
}

.path-separator {
    margin-inline: 0.2rem;
    color: var(--ink-muted);
}

.pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
    margin-block-start: 1rem;
}

.pager-position {
    color: var(--ink-muted);
    font-size: 0.9rem;
}

/*
 * "1 / 3": two numbers with a separator between them, which is one run and is laid out
 * by the direction of the text around it - so on an Arabic page the page somebody is on
 * and the number of pages swap places and the line says 3 of 1. Given a direction of its
 * own, the pair keeps its order in both languages; it carries no logical property, so
 * nothing has to be said again under [dir="rtl"].
 */
.pager-count {
    direction: ltr;
    unicode-bidi: isolate;
}

.setting-key {
    /* A settings key is an identifier the portal reads, not a phrase: same in both. */
    direction: ltr;
    unicode-bidi: isolate;
    font-family: ui-monospace, "Cascadia Mono", Consolas, "Liberation Mono", monospace;
}

.setting-range {
    margin-inline-start: 0.6rem;
    direction: ltr;
    unicode-bidi: isolate;
}

.logo-preview {
    margin-block: 0.5rem 1rem;
    padding-block: 0.75rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-sunken);
}

.logo-preview img {
    max-inline-size: 100%;
    max-block-size: 6rem;
    inline-size: auto;
    block-size: auto;
}

/*
 * Notifications: the bell in the header, and the page it opens.
 *
 * The badge is pinned to the bell's own box (position: relative on the link), on the block
 * start and inline end corners - logical, so it sits over the corner the reading direction
 * puts last in both languages. It is drawn only when something is waiting, so there is no
 * empty circle to style.
 */
.bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding-block: 0.35rem;
    padding-inline: 0.35rem;
    border-radius: var(--radius);
    color: var(--ink);
    line-height: 1;
    text-decoration: none;
}

.bell:hover {
    background: var(--surface-sunken);
}

.bell .icon {
    inline-size: 1.4rem;
    block-size: 1.4rem;
}

.bell-badge {
    position: absolute;
    inset-block-start: -0.15rem;
    inset-inline-end: -0.15rem;
    min-inline-size: 1.1rem;
    padding-inline: 0.25rem;
    border-radius: 999px;
    background: var(--accent);
    color: #ffffff;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.1rem;
    text-align: center;
    /*
     * A count is a number in both languages, like every other number the portal shows,
     * and one run of digits is drawn left to right whatever the page's direction is. No
     * direction of its own, on purpose: an inset is resolved in the box's own direction,
     * so a direction here would pin the badge to the same corner in both languages
     * instead of the corner the reading ends at.
     */
    unicode-bidi: isolate;
}

.notifications-actions {
    display: flex;
    justify-content: flex-end;
    margin-block: 0 1rem;
}

.notification-list {
    margin-block: 0;
    padding-inline-start: 0;
    list-style: none;
}

.notification {
    border-block-end: 1px solid var(--line);
}

.notification:last-child {
    border-block-end: 0;
}

/* The whole row is the link: a notification has one thing to do. */
.notification-link {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    padding-block: 0.7rem;
    padding-inline: 0.5rem;
    border-radius: var(--radius);
    color: var(--ink);
    text-decoration: none;
}

.notification-link:hover {
    background: var(--surface-sunken);
}

.notification-text {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem;
    /* Narrower than the longest name it holds, when the row has no more room - see
       .activity-text for the measurement this answers. */
    min-inline-size: 0;
}

.notification-actor {
    font-weight: 600;
}

.notification-action {
    color: var(--ink-muted);
}

.notification-file {
    /* A name is a label: isolated, so an Arabic file name cannot drag an English row
       around it, nor the other way round. */
    unicode-bidi: isolate;
    /* And a name with no space in it wraps anyway, rather than setting the row's width. */
    overflow-wrap: anywhere;
}

.notification-meta {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    font-size: 0.8rem;
    color: var(--ink-muted);
    /* Ends the row on a wrapped line too - see .activity-date. This box keeps no direction
       of its own, so the one logical margin holds in both languages. */
    margin-inline-start: auto;
}

.notification-date {
    direction: ltr;
    unicode-bidi: isolate;
    white-space: nowrap;
}

/* Unread is a rule on the reading edge and a word beside the date, never colour alone. */
.notification.is-unread .notification-link {
    border-inline-start: 3px solid var(--accent);
    background: var(--surface-sunken);
}

.notification-unread {
    padding-block: 0.05rem;
    padding-inline: 0.35rem;
    border-radius: var(--radius);
    background: var(--accent);
    color: #ffffff;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/*
 * The few boxes that keep their own direction, on an Arabic page.
 *
 * A stamp, a size, an address and a range are read the same way in both languages, so
 * each of those rules sets `direction: ltr` on the box that holds one. That is what keeps
 * "2026-08-12 21:48" from having its two halves swapped on an Arabic page - but it also
 * decides, for that same box, which edge `start` and `end` mean: a logical property on an
 * element is resolved in the element's OWN direction, not the page's. So `text-align:
 * start` on a stamp inside an Arabic table hangs it off the far edge of its column while
 * the heading above it sits on the near one, and `margin-inline-start: auto` on a stamp
 * in a message row pushes it the wrong way.
 *
 * These rules say the same intention again, in the page's terms, and only where the two
 * disagree. The alternative - dropping the direction - is what .stat-value and
 * .bell-badge do, and it is only safe there because a count is one run of digits with
 * nothing beside it to be reordered against.
 */
[dir="rtl"] .file-size,
[dir="rtl"] .file-date,
[dir="rtl"] .user-identifier {
    text-align: end;
}

[dir="rtl"] .message-date,
[dir="rtl"] .activity-date {
    margin-inline: 0 auto;
}

[dir="rtl"] .setting-range {
    margin-inline: 0 0.6rem;
}

/*
 * Phone width, and the one thing that changes there: how big a control has to be to be hit
 * with a thumb. Everything else in this stylesheet already answers a narrow window on its
 * own - the grids reflow, the tables scroll inside their own box - so this is the only
 * width question the portal has to be asked directly.
 *
 * Measured at 390px before this rule: the button that marks every notification read was
 * 33px tall, the link to the next page of files 26px, the link that leaves a form without
 * saving 23px. 2.5rem is the smallest target a thumb reliably finds. The controls keep
 * their look - a link is still a link - they are only given room around their text.
 */
@media (max-width: 30rem) {
    .button-primary,
    .button-link,
    .button-danger,
    .pager-step,
    .cancel-link,
    .sign-out button,
    .theme-switch button,
    .lang-switch button,
    .node-form button,
    /* The way off an error page is the only thing on it that can be pressed. */
    .panel-error a {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-block-size: 2.5rem;
    }

    /*
     * And the tree steps in smaller. Each level costs 2rem of the width on a wide screen,
     * which is right there and far too much on a phone: measured at 390px, the sixth level
     * of a tree had 80px left to hold a field and three buttons. Half the step gives that
     * node 176px and still draws the shape of the tree.
     */
    .tree-list {
        padding-inline-start: 0.5rem;
    }

    .tree-item {
        padding-inline-start: 0.5rem;
    }
}

/*
 * The dark palette: the same tokens, given the values a dark page needs. One block, and
 * it redefines custom properties and nothing else - a component needing a rule of its own
 * here would be a component reading a colour the tokens do not carry, and that is a gap to
 * close at the token.
 *
 * Attached to an attribute, not to the device preference query that used to stand here.
 * The portal ships light and a signed-in person chooses otherwise from the account menu,
 * which share_app/views/layout.php writes onto <html> from the account's own column. The
 * two cannot stand together: `:root` inside a preference query and `[data-theme="dark"]`
 * carry the same weight, so on a device set to dark the query's values would apply to an
 * account that had chosen light and nothing here would put them back - the switch would
 * work in one direction only. Keeping both would mean a second block repeating the light
 * palette to undo the first, which is the same seven values written twice.
 *
 * --accent is not among them on purpose: it is the company's colour, set inline by the
 * layout from branding, and it means the same thing on both palettes.
 *
 * Measured against WCAG AA (4.5:1) on the four main pairs: ink on surface 14.33:1, ink on
 * sunken 15.12:1, ink-muted on surface 6.99:1, ink-muted on sunken 7.37:1.
 */
[data-theme="dark"] {
    --surface: #111827;
    --surface-sunken: #0b1220;
    --ink: #e5e7eb;
    --ink-muted: #9ca3af;
    --line: #334155;
    --danger: #fca5a5;
    --danger-surface: #2a1414;
}

/*
 * The sign-in card.
 *
 * One card, two faces: the portal's own sign-in page and a company's door render the
 * same view, and the name, logo and accent on it are whatever Branding was pointed at.
 * Nothing below asks which of the two it is dressing.
 *
 * It sits in the ordinary main element rather than in a shell of its own, so the app bar
 * above it - and the language switch a guest needs - stay exactly where they are on
 * every other page.
 */
.auth-card {
    max-inline-size: 26rem;
    margin-block: 2.5rem;
    margin-inline: auto;
    padding-block: 2rem;
    padding-inline: 1.75rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    text-align: center;
}

/* Capped on the block axis, so a tall logo and a wide one both keep the card's shape. */
.auth-logo {
    margin-block: 0 1.25rem;
}

.auth-logo img {
    max-block-size: 5rem;
    max-inline-size: 100%;
    object-fit: contain;
}

.auth-title {
    margin-block: 0 0.35rem;
    font-size: 1.5rem;
    line-height: 1.25;
}

.auth-subtitle {
    margin-block: 0 1.75rem;
    color: var(--ink-muted);
}

/*
 * The form reads from the edge the language starts at, inside a card whose heading block
 * is centred. Without this the labels would centre too, and a label that is not against
 * the start of its own field is a label that has to be searched for.
 */
.auth-card .form-stack,
.auth-card .form-error {
    text-align: start;
}

.auth-card .field {
    margin-block-end: 1.25rem;
}

.auth-card .button-primary {
    inline-size: 100%;
}

/*
 * The reveal control sits inside the password field, on the edge the line ends at, and
 * the field's own padding on that edge keeps the typed characters clear of it. Both flip
 * with the document direction because both are logical.
 */
.password-field {
    display: block;
    position: relative;
}

.password-field input {
    padding-inline-end: 2.75rem;
}

.password-toggle {
    display: flex;
    position: absolute;
    inset-block: 1px;
    inset-inline-end: 1px;
    align-items: center;
    justify-content: center;
    inline-size: 2.5rem;
    padding: 0;
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: var(--ink-muted);
    cursor: pointer;
}

.password-toggle[hidden] {
    display: none;
}

.password-toggle:hover {
    color: var(--ink);
}

.password-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* Pressed means the characters are showing, and the control says so in colour as well
   as in its label and its aria-pressed state. */
.password-toggle[aria-pressed="true"] {
    color: var(--accent);
}

.password-eye {
    inline-size: 1.25rem;
    block-size: 1.25rem;
}

/* --------------------------------------------------------------------------------------
   The companies list and the form that makes one.

   A row here is mostly an address somebody is about to hand over, so the address is what
   the layout protects: it has nothing to wrap at, and a column sized to hold one whole
   would be as wide as the longest door anybody publishes.
   -------------------------------------------------------------------------------------- */

/* Logo-or-initial and the name, as one thing that may be drawn narrower than its content. */
.company-mark {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-inline-size: 0;
}

.company-thumb {
    inline-size: 1.75rem;
    block-size: 1.75rem;
    object-fit: contain;
    flex: none;
}

/* Stands in for a logo, so it keeps the same footprint the thumbnail has. */
.company-initial {
    display: flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    flex: none;
    border-radius: var(--radius);
    background: var(--accent);
    color: #ffffff;
    font-weight: 600;
    line-height: 1;
}

/* The banner hands the address over as a row of its own; the table cell stays a cell,
   or the column it sits in stops being laid out with the others. */
p.company-door,
.company-created .company-door {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    min-inline-size: 0;
}

/* An address is read character by character and is the same way round in both languages. */
.door-url {
    direction: ltr;
    unicode-bidi: isolate;
    font-family: ui-monospace, "Cascadia Mono", Consolas, "Liberation Mono", monospace;
    font-size: 0.9em;
    overflow-wrap: anywhere;
    min-inline-size: 0;
}

.company-count {
    white-space: nowrap;
}

.company-published {
    white-space: nowrap;
}

/* Big enough for a thumb to find at any width, because this is the control the page is
   for: the address beside it is what somebody came to take away. */
.copy-control {
    min-block-size: 2.5rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    cursor: pointer;
    flex: none;
}

.copy-control[hidden] {
    display: none;
}

.copy-control:hover {
    border-color: var(--accent);
}

.copy-control:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Said in colour as well as in the label it swaps to, so the confirmation does not depend
   on somebody having been watching the word. */
.copy-control.is-copied {
    border-color: var(--accent);
    color: var(--accent);
}

/* The banner a company is handed over on, straight after it is created. */
.company-created-name {
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* One company's page: the header that hands over its address, and the people panel. */

.company-header {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-block-end: 0.75rem;
    border-block-end: 1px solid var(--line);
}

/* The heading sits inside the mark so the logo and the name are one line that wraps
   together; the mark's own rule already lets it be drawn narrower than its content. */
.company-header h1 {
    margin: 0;
    min-inline-size: 0;
    overflow-wrap: anywhere;
}

/* The colour, as a colour. A hex code beside it is what somebody types; this is what
   they chose. */
.accent-chip {
    inline-size: 1rem;
    block-size: 1rem;
    flex: none;
    border: 1px solid var(--line);
    border-radius: 999px;
}

.company-rename {
    flex-wrap: wrap;
    gap: 0.5rem;
}

.company-people {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Name, address and state on one row that may be drawn narrower than the address in it. */
.company-person {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding-block: 0.4rem;
    min-inline-size: 0;
    border-block-end: 1px solid var(--line);
}

.company-person:last-child {
    border-block-end: 0;
}

.company-person a {
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------------------
   The file viewer: the stored file itself, on top of its own page.

   A dialog rather than a page of its own, because the thread beside it is the reason
   somebody opened the file - checking a document against what was written about it should
   not cost the conversation. Everything is sized on the logical axes, so the bar reads the
   same way round in both languages with one set of rules.
   -------------------------------------------------------------------------------------- */

.viewer {
    inline-size: min(64rem, 92vw);
    max-inline-size: 92vw;
    block-size: min(46rem, 88vh);
    max-block-size: 88vh;
    padding: 0;
    border: 0;
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    /* Centres on both axes without naming either edge. */
    margin: auto;
    overflow: hidden;
}

.viewer::backdrop {
    background: rgb(15 23 42 / 62%);
}

.viewer-shell {
    display: flex;
    flex-direction: column;
    block-size: 100%;
    min-block-size: 0;
}

.viewer-bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    padding-block: 0.6rem;
    padding-inline: 0.9rem;
    border-block-end: 1px solid var(--line);
    background: var(--surface-sunken);
}

/* The name may be drawn narrower than itself; the actions keep to the end of the row
   whether or not the row wrapped. */
.viewer-name {
    margin: 0;
    min-inline-size: 0;
    overflow-wrap: anywhere;
    font-weight: 600;
}

.viewer-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-inline-start: auto;
    flex: none;
}

.viewer-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: 2.5rem;
    min-block-size: 2.5rem;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: none;
    color: var(--ink-muted);
    cursor: pointer;
}

.viewer-close:hover {
    color: var(--ink);
    border-color: var(--line);
}

.viewer-close:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* The one box that scrolls: the shell above it is fixed to the dialog. */
.viewer-body {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 0.75rem;
    flex: 1;
    min-block-size: 0;
    padding: 0.9rem;
    overflow: auto;
}

.viewer-body.is-loading {
    color: var(--ink-muted);
}

.viewer-image {
    max-inline-size: 100%;
    max-block-size: 100%;
    object-fit: contain;
}

.viewer-frame-embed {
    inline-size: 100%;
    block-size: 100%;
    min-block-size: 24rem;
    border: 0;
}

.viewer-video {
    max-inline-size: 100%;
    max-block-size: 100%;
}

.viewer-audio {
    inline-size: min(28rem, 100%);
}

/* Text arrives as text: it wraps rather than sizing the dialog, and keeps its own
   direction so a file written in either language reads as it was written. */
.viewer-text {
    margin: 0;
    inline-size: 100%;
    max-block-size: 100%;
    padding: 0.75rem;
    border-radius: var(--radius);
    background: var(--surface-sunken);
    font-family: ui-monospace, "Cascadia Mono", Consolas, "Liberation Mono", monospace;
    font-size: 0.9rem;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    unicode-bidi: plaintext;
    overflow: auto;
}

.viewer-note,
.viewer-loading {
    margin: 0;
    color: var(--ink-muted);
    font-size: 0.9rem;
}

.viewer-error {
    margin: 0;
    color: var(--danger);
}


/* --------------------------------------------------------------------------------------
   The companies table, made readable: one line per value.
   -------------------------------------------------------------------------------------- */

/* An address is the longest thing in the row and the least worth wrapping: it is cut to a
   readable width, and the control beside it carries the whole of it either way. */
.data-table .door-url {
    display: inline-block;
    max-inline-size: 26ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
    overflow-wrap: normal;
}

/* The count and what it weighs are one fact, so they sit on one line with a separator
   between them rather than stacking. */
.company-count .field-hint {
    display: inline;
    color: var(--ink-muted);
}

.company-count .field-hint::before {
    content: "\00b7";
    margin-inline: 0.35rem;
}

/* Small enough to sit inside a row, still big enough to press. */
.data-table .copy-control {
    min-block-size: 2rem;
    padding-inline: 0.5rem;
    vertical-align: middle;
}
/* -------------------------------------------------------------------------
   The folders editor: several changes prepared in the page and saved as one.
   Drawn by public/assets/admin-folders.js over the server-rendered forms it
   hides, so nothing here is reachable on a page that never ran the script. */

.folders-editor {
    margin-block: 0 1.5rem;
}

/* The staging line and the one control that sends it, kept together at the top
   of the panel so the state and the way to act on it are read as one thing. */
.editor-tools {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
    justify-content: space-between;
    margin-block-end: 1rem;
    padding-block: 0.6rem;
    padding-inline: 0.9rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-sunken);
}

.editor-status {
    margin: 0;
    color: var(--ink-muted);
}

.editor-status.is-dirty {
    color: var(--ink);
    font-weight: 600;
}

.editor-list {
    margin: 0;
    padding-inline-start: 1rem;
    list-style: none;
}

.editor-root {
    padding-inline-start: 0;
}

.editor-item {
    padding-block: 0.5rem;
    padding-inline-start: 0.75rem;
    border-inline-start: 2px solid var(--line);
}

.editor-root > .editor-item {
    border-inline-start: 0;
    padding-inline-start: 0;
}

.editor-head {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}

.editor-folder-name {
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* A folder staged for adding or removal is marked in words, not by colour alone. */
.badge-staged,
.badge-going {
    border: 1px solid var(--line);
    color: var(--ink-muted);
}

.editor-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    margin-block-start: 0.4rem;
}

/* Narrower than a form field: this box names one folder, and the row it sits in
   carries three controls beside it. min-inline-size: 0 so the row may be drawn
   narrower than the text inside the box. */
.editor-name {
    flex: 1 1 10rem;
    min-inline-size: 0;
    padding-block: 0.35rem;
    padding-inline: 0.5rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
}

.editor-controls button,
.editor-move {
    padding-block: 0.35rem;
    padding-inline: 0.7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink-muted);
    font: inherit;
    cursor: pointer;
}

.editor-controls button:hover:not(:disabled),
.editor-move:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.editor-delete:hover {
    border-color: var(--danger);
    color: var(--danger);
}

/* Nothing staged, nothing to send: the control says so rather than answering a
   press with nothing. */
.editor-save:disabled {
    opacity: 0.55;
    cursor: default;
}

.editor-item > .field-error {
    margin-block: 0.35rem 0;
}
