h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}


/* ---------------------------------------------------------------------------
 * ToptalAPK Admin — two palettes, matched to the public site's tokens.
 *
 * The dark set below is the default; `[data-theme="light"]` redefines the same
 * variables as a pale set. Which one applies comes from the theme.adminMode
 * setting, written onto the shell element by MainLayout — separate from the
 * public site's own mode, because the panel is a tool rather than part of the
 * site it edits.
 *
 * The attribute goes on .admin-shell rather than <html>: the shell is rendered
 * by the layout, which is where the setting is already loaded, and it covers
 * the viewport. Everything else reads these variables by inheritance.
 * ------------------------------------------------------------------------- */

:root {
    color-scheme: dark;
    --ink: #050506;
    --surface: #0b0c0e;
    --surface-2: #121417;
    --line: #22262c;
    --fg: #f2f4f7;
    --muted: #98a1ae;
    --faint: #6b7480;
    /* Long-form text in the article editor's preview. */
    --prose: #d5dae1;
    --accent: #3ddc84;
    --accent-strong: #23c26b;
    --accent-dim: #0f2a1c;
    /* Text drawn on an accent fill. */
    --on-accent: #04150b;
    --danger: #ff6b6b;
    --warn: #ffb84d;
}

[data-theme="light"] {
    color-scheme: light;
    --ink: #f4f6f8;
    --surface: #ffffff;
    --surface-2: #f1f3f6;
    --line: #dee3e9;
    --fg: #10131a;
    --muted: #58616f;
    --faint: #7c8593;
    --prose: #2b323d;
    /*
     * The green is a glow-on-black colour — about 1.6:1 on white, and it is the
     * link and button colour throughout. Mixed halfway to black it keeps the
     * hue and clears 4.5:1, so the fill it sits on takes white text instead.
     */
    --accent: color-mix(in srgb, #3ddc84 50%, #04120a);
    --accent-strong: color-mix(in srgb, #23c26b 40%, #04120a);
    --accent-dim: color-mix(in srgb, #3ddc84 13%, #ffffff);
    --on-accent: #ffffff;
    --danger: #c62d2d;
    --warn: #a35c00;
}

/*
 * The shell paints its own ground: <body> sits outside it and would otherwise
 * keep the dark background behind a light panel.
 */
.admin-shell[data-theme] {
    background: var(--ink);
    color: var(--fg);
}

/* The sign-in screen's green wash is a dark-theme effect; on paper it stains. */
.login-page[data-theme="light"] {
    background: var(--ink);
    color: var(--fg);
}

/*
 * Padding and borders count inside a width, everywhere.
 *
 * This is the one thing a CSS framework would have given us for free, and its
 * absence caused the whole class of layout fault this panel had: any control
 * set to width:100% rendered 100% *plus* its padding *plus* its border, so
 * every labelled field overflowed its container by about twenty pixels. It
 * showed as controls hanging past the edge of a card and as columns that
 * would not line up.
 *
 * Three lines here rather than box-sizing repeated on each element, so a
 * control added later cannot miss it.
 */
*,
*::before,
*::after {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
    background: var(--ink);
    color: var(--fg);
}

a {
    color: var(--accent);
}

/* Login ------------------------------------------------------------------ */

.login-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: radial-gradient(48rem 20rem at 50% 0, rgba(61, 220, 132, 0.1), transparent 70%), var(--ink);
}

.login-card {
    background: var(--surface);
    border: 1px solid var(--line);
    padding: 2.5rem;
    border-radius: 14px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
    width: 340px;
}

.login-card h1 {
    margin: 0 0 1.5rem;
    font-size: 1.4rem;
    letter-spacing: -0.01em;
}

.login-card label {
    display: block;
    margin-bottom: 1rem;
    font-size: 0.85rem;
    color: var(--muted);
}

.login-card input {
    width: 100%;
    padding: 0.6rem 0.75rem;
    margin-top: 0.35rem;
    box-sizing: border-box;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--fg);
}

.login-card input:focus {
    border-color: var(--accent);
    outline: none;
}

.login-card button {
    width: 100%;
    padding: 0.65rem;
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
    border: none;
    border-radius: 999px;
    cursor: pointer;
}

.login-card button:hover {
    background: var(--accent-strong);
}

.alert-error {
    background: rgba(255, 107, 107, 0.12);
    border: 1px solid rgba(255, 107, 107, 0.35);
    color: var(--danger);
    padding: 0.6rem 0.8rem;
    border-radius: 8px;
    margin-bottom: 1rem;
    font-size: 0.85rem;
}

/* Shell ------------------------------------------------------------------ */

.admin-shell {
    display: flex;
    min-height: 100vh;
}

.admin-nav {
    width: 230px;
    background: var(--surface);
    border-right: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    padding: 1.5rem 1rem;
}

.admin-nav-brand {
    font-weight: 700;
    margin-bottom: 1.5rem;
    color: var(--fg);
}

.admin-nav a {
    color: var(--muted);
    text-decoration: none;
    padding: 0.5rem 0.7rem;
    display: block;
    border-radius: 8px;
    font-size: 0.9rem;
}

.admin-nav a:hover {
    background: var(--surface-2);
    color: var(--fg);
}

.admin-nav a.active {
    color: var(--accent);
    background: var(--accent-dim);
    font-weight: 600;
}

/* Pinned to the foot of the sidebar, separated from the navigation links. */
/*
   The account bar, top right of the content area.

   Right-aligned and pushed up against the top so it reads as chrome rather
   than as part of whatever page is below it. Wraps on a narrow screen so the
   name drops above the buttons instead of squeezing the page heading.
*/
.admin-topbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.75rem;
    margin: -0.5rem 0 1.5rem;
    padding-bottom: 0.85rem;
    border-bottom: 1px solid var(--line);
}

/* Name over role, so the pair reads as one block beside the buttons. */
.admin-account-who {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    line-height: 1.25;
    min-width: 0;
    text-align: right;
}

/* A long name must not push the buttons off the row. */
.admin-account-name {
    overflow: hidden;
    max-width: 16rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--fg);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.admin-account-role {
    font-size: 0.75rem;
    color: var(--faint);
}

/*
   Quiet, both of them. These sit above every screen in the panel, and a button
   that draws the eye on every page draws it away from the page.
*/
.admin-topbar-link {
    padding: 0.4rem 0.9rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--muted);
    font-size: 0.85rem;
    text-decoration: none;
    white-space: nowrap;
}

.admin-topbar-link:hover,
.admin-topbar-link.active {
    border-color: var(--accent);
    color: var(--accent);
}

.admin-logout {
    margin: 0;
}

/*
   Overrides the accent fill that .admin-content button[type="submit"] gives
   every other submit button: this one is now inside the content area, and a
   bright green Log out on every screen reads as the thing to press.
*/
.admin-logout button,
.admin-content .admin-logout button[type="submit"] {
    padding: 0.4rem 0.9rem;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 400;
    cursor: pointer;
    margin: 0;
}

.admin-logout button:hover,
.admin-content .admin-logout button[type="submit"]:hover {
    background: transparent;
    border-color: var(--accent);
    color: var(--accent);
}

.admin-content {
    flex: 1;
    padding: 2rem;
    min-width: 0;
}

.admin-content h1 {
    letter-spacing: -0.02em;
}

/* Cards and tables -------------------------------------------------------- */

.admin-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 14px;
    padding: 1.5rem;
    margin-bottom: 1.5rem;
}

.admin-card h3 {
    margin-top: 0;
}

/*
   Heading and its primary action on one line, at the top of a listing card —
   "Sites (4)" on the left, "Add site" on the right.

   The pages open on the list and keep the entry form behind that button, so
   the action has to sit where the list begins rather than above a form the
   reader has to scroll past. Wraps on a narrow screen so the button drops
   under the heading instead of squeezing it.
*/
.admin-card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.admin-card-head h3 {
    margin: 0;
}

/* The publish-at row that opens under a post or app in the list. */
.schedule-picker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
}

.schedule-picker label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  white-space: nowrap;
}

.schedule-picker input {
  width: auto;
}

.schedule-picker .field-hint {
  margin: 0;
}

table.admin-table {
    width: 100%;
    border-collapse: collapse;
    background: transparent;
}

table.admin-table th,
table.admin-table td {
    text-align: left;
    padding: 0.65rem 0.6rem;
    border-bottom: 1px solid var(--line);
    font-size: 0.9rem;
}

table.admin-table th {
    color: var(--faint);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

table.admin-table tbody tr:hover {
    background: var(--surface-2);
}

/* Status pills ----------------------------------------------------------- */

.status-pill {
    display: inline-block;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 600;
    border: 1px solid var(--line);
    color: var(--muted);
}

.status-pill.status-published {
    background: var(--accent-dim);
    border-color: rgba(61, 220, 132, 0.35);
    color: var(--accent);
}

.status-pill.status-draft {
    background: var(--surface-2);
}

.status-pill.status-scheduled {
    background: rgba(255, 184, 77, 0.12);
    border-color: rgba(255, 184, 77, 0.35);
    color: var(--warn);
}

.status-pill.status-archived {
    background: rgba(255, 107, 107, 0.1);
    border-color: rgba(255, 107, 107, 0.3);
    color: var(--danger);
}

/* Breadcrumb --------------------------------------------------------------
   The way back from a page that used to be a dialog. An editor who opened a
   post from the list needs the same obvious exit the dialog's × used to be,
   and on these pages the browser's Back button is no longer the only one. */

.breadcrumb {
    margin: 0 0 0.75rem;
    font-size: 0.875rem;
}

.breadcrumb a {
    color: var(--muted);
    text-decoration: none;
}

.breadcrumb a:hover {
    color: var(--accent);
    text-decoration: underline;
}

/* Forms and buttons ------------------------------------------------------- */

.admin-content input,
.admin-content textarea,
.admin-content select {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--fg);
    padding: 0.45rem 0.6rem;
    font-family: inherit;
    font-size: 0.9rem;
    /*
     * Without this, width:100% means 100% *plus* 1.2rem of padding and 2px of
     * border — so every field set to fill its label overflowed its container
     * by about twenty pixels. Visible as controls hanging past the right edge
     * of a card, and as columns that do not line up with each other.
     */
    box-sizing: border-box;
    min-width: 18rem;
    max-width: 100%;
}

.admin-content input:focus,
.admin-content textarea:focus,
.admin-content select:focus {
    border-color: var(--accent);
    outline: none;
}

.admin-content input[readonly] {
    color: var(--faint);
}

.admin-content label {
    display: inline-block;
    font-size: 0.85rem;
    color: var(--muted);
}

.admin-content button {
    background: var(--surface-2);
    border: 1px solid var(--line);
    color: var(--fg);
    border-radius: 999px;
    padding: 0.4rem 0.9rem;
    font-size: 0.85rem;
    cursor: pointer;
    margin-right: 0.35rem;
}

.admin-content button:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.admin-content button[type="submit"] {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
}

.admin-content button[type="submit"]:hover {
    background: var(--accent-strong);
    color: var(--on-accent);
}

/* Dashboard stats --------------------------------------------------------- */

/* Names the kind of site under its name, so the heading can be the site's own
   name rather than the word "Dashboard". */
.dashboard-subtitle {
    color: var(--muted);
    font-size: 0.85rem;
    margin: -0.5rem 0 1.5rem;
}

.stat-cards {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.stat-card {
    flex: 1;
    min-width: 12rem;
    background: var(--surface);
    background-image: linear-gradient(160deg, rgba(61, 220, 132, 0.07), transparent 45%);
    border: 1px solid var(--line);
    border-radius: 14px;
    padding: 1.25rem;
}

.stat-card .value {
    font-size: 1.9rem;
    font-weight: 700;
    color: var(--accent);
}

.stat-card .label {
    color: var(--muted);
    font-size: 0.8rem;
}

/* Blog approval queue ----------------------------------------------------- */

.review-count {
    color: var(--muted);
    font-size: 0.85rem;
    margin-bottom: 0.75rem;
}

.review-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.review-head h3 {
    margin: 0 0 0.4rem;
}

.review-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    margin: 0;
    font-size: 0.78rem;
    color: var(--faint);
}

.review-excerpt {
    color: var(--muted);
    font-size: 0.9rem;
    line-height: 1.6;
}

.review-preview {
    margin-top: 1rem;
    padding: 1rem 1.25rem;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 10px;
    max-height: 26rem;
    overflow-y: auto;
    font-size: 0.9rem;
    line-height: 1.7;
    color: var(--prose);
}

.review-preview h2,
.review-preview h3 {
    color: var(--fg);
}

.review-preview a {
    color: var(--accent);
}

.review-preview blockquote {
    border-left: 3px solid var(--accent);
    margin-left: 0;
    padding-left: 1rem;
    color: var(--muted);
}

.review-tags {
    color: var(--faint);
    font-size: 0.78rem;
}

.review-actions {
    margin-top: 1.25rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* Form hints -------------------------------------------------------------- */

.field-hint {
    display: block;
    margin-top: 0.3rem;
    color: var(--faint);
    font-size: 0.75rem;
    max-width: 40rem;
}

.admin-content hr {
    border: none;
    border-top: 1px solid var(--line);
    margin: 1.5rem 0;
}

.admin-content h4 {
    margin-bottom: 0.75rem;
}

.admin-content input[type="checkbox"] {
    min-width: auto;
    margin-right: 0.35rem;
    accent-color: var(--accent);
}

.admin-content input[type="file"] {
    min-width: auto;
    padding: 0.35rem;
    font-size: 0.8rem;
}

/* Roles & permissions ----------------------------------------------------- */

.permission-group {
    margin-top: 1.25rem;
}

.permission-group h4 {
    margin: 0 0 0.5rem;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--faint);
}

.permission-row {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.35rem 0;
    font-size: 0.85rem;
    color: var(--fg);
}

.permission-row code {
    font-size: 0.8rem;
    color: var(--accent);
}

.permission-row .field-hint {
    margin-top: 0.1rem;
}

.permission-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-top: 0.75rem;
}

/* Site settings ----------------------------------------------------------- */

/*
 * Fields in columns.
 *
 * The minimum matches the 18rem minimum the inputs themselves carry. At 14rem
 * it did not: the grid happily made a 14rem column and the input inside refused
 * to be narrower than 18rem, so on a wide screen — where auto-fit makes the
 * most columns — every field in the row overflowed its cell and the last one
 * ran past the edge of the card.
 *
 * align-items: start so a two-row textarea does not stretch the single-line
 * fields beside it to its own height, which is what made rows look ragged.
 */
.settings-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: 1.25rem 1.5rem;
    align-items: start;
}

/*
 * Inside a column, a field fills the column it was given rather than insisting
 * on its own minimum. The minimum above keeps columns wide enough to be usable;
 * this keeps a field from overflowing one that is narrower — a phone, or a
 * column the grid had to shrink to fit.
 */
.settings-grid input,
.settings-grid textarea,
.settings-grid select,
.settings-grid label {
    min-width: 0;
    max-width: 100%;
}

/* A long hint must wrap inside its column rather than run across the next. */
.settings-grid .field-hint {
    max-width: 100%;
}

.color-field label {
    display: block;
    margin-bottom: 0.35rem;
}

.color-field-inputs {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.color-field-inputs input[type="color"] {
    min-width: auto;
    width: 2.75rem;
    height: 2.25rem;
    padding: 0.15rem;
    cursor: pointer;
}

.color-field-inputs input:not([type="color"]) {
    min-width: 0;
    flex: 1;
    font-family: ui-monospace, monospace;
}

.admin-content textarea {
    width: 100%;
    max-width: 40rem;
}

/* Light / dark switches --------------------------------------------------- */

.theme-choice {
    display: grid;
    grid-template-columns: 10rem auto;
    align-items: center;
    gap: 0.5rem 1rem;
    margin-bottom: 1.25rem;
}

.theme-choice-label {
    font-weight: 600;
}

/* The hint sits under both, spanning the full width. */
.theme-choice .field-hint {
    grid-column: 1 / -1;
    max-width: 44rem;
}

.theme-toggle {
    display: inline-flex;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 3px;
    gap: 3px;
}

.theme-toggle button {
    border: 0;
    border-radius: 999px;
    padding: 0.4rem 1.1rem;
    font-size: 0.85rem;
    font-weight: 600;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
}

.theme-toggle button:hover {
    color: var(--fg);
}

.theme-toggle button.on {
    background: var(--accent);
    color: var(--on-accent);
}

/* Inline link buttons (error boundary actions) ---------------------------- */

.btn-link {
    color: var(--accent);
    text-decoration: none;
    font-size: 0.9rem;
}

.btn-link:hover {
    text-decoration: underline;
}

/* Busy spinners and status toast ------------------------------------------ */

.spinner {
    display: inline-block;
    width: 0.9em;
    height: 0.9em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    vertical-align: -0.1em;
    margin-right: 0.45em;
    animation: spin 0.7s linear infinite;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

.loading-row {
    color: var(--faint);
}

.loading-row .spinner {
    color: var(--accent);
}

.admin-toast {
    position: fixed;
    right: 1.5rem;
    bottom: 1.5rem;
    z-index: 1000;
    max-width: 26rem;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    background: var(--surface-2);
    border: 1px solid var(--accent);
    border-radius: 12px;
    padding: 0.9rem 1.1rem;
    box-shadow: 0 8px 30px rgb(0 0 0 / 0.5);
    animation: toast-in 0.18s ease-out;
}

.admin-toast.toast-error {
    border-color: var(--danger);
}

.admin-toast .toast-close {
    background: none;
    border: none;
    color: var(--faint);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.1rem;
}

.admin-toast .toast-close:hover {
    color: var(--text, #fff);
}

@keyframes toast-in {
    from { opacity: 0; transform: translateY(0.5rem); }
    to { opacity: 1; transform: none; }
}

/* Add-user form ----------------------------------------------------------- */

/* Two columns on a wide screen so the six fields do not become a long ladder,
   collapsing to one on anything narrow. */
.user-form {
    display: grid;
    gap: 0 1.5rem;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    margin-bottom: 0.5rem;
}

.user-form p {
    margin: 0 0 0.75rem;
}

.checkbox-label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Site switcher ----------------------------------------------------------- */

.site-switcher {
    margin-bottom: 1.25rem;
    padding-bottom: 1.25rem;
    border-bottom: 1px solid var(--line);
}

.site-switcher label {
    display: block;
    margin-bottom: 0.35rem;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--faint);
}

.site-switcher select {
    width: 100%;
    min-width: 0;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--fg);
    padding: 0.4rem 0.5rem;
    font-size: 0.9rem;
}

.site-switcher select:focus {
    border-color: var(--accent);
    outline: none;
}

/* What an Editor sees in place of the dropdown. Deliberately not styled as a
   disabled control — nothing here is going to become editable, so it reads as
   a statement of context rather than something waiting on a permission. */
.site-switcher-fixed {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--fg);
}

.site-switcher .field-hint {
    margin-top: 0.3rem;
}

/* --- Rich text editor ---------------------------------------------------- */

.field-block {
    margin: 0 0 1rem;
}

.field-block > label {
    display: block;
    margin-bottom: 0.35rem;
}

.rte {
    /*
     * Deliberately thin. SunEditor draws its own frame, and wrapping it in a
     * second bordered box put two borders round every editor. No overflow rule
     * either — the full-screen button escapes its container, and a clipped
     * ancestor is what stops it.
     */
    margin: 0;
}

.rte-media {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.4rem;
}

/*
 * SunEditor in the panel's colours.
 *
 * It ships a light theme, and the admin is dark by default — untouched, the
 * editor was a white slab in the middle of a dark page, which is both ugly and
 * genuinely hard to write in at night. These override the parts that carry
 * colour, using the same variables as everything else, so the editor follows
 * the light and dark themes along with the rest of the panel.
 */
.sun-editor {
    background: var(--surface-2) !important;
    border: 1px solid var(--line) !important;
    border-radius: 6px !important;
    color: var(--fg) !important;
}

.sun-editor .se-toolbar,
.sun-editor .se-toolbar-separator-vertical,
.sun-editor .se-resizing-bar {
    background: var(--surface) !important;
    border-color: var(--line) !important;
    outline: none !important;
}

.sun-editor .se-btn,
.sun-editor .se-btn-select,
.sun-editor .se-toolbar .se-btn-module-border {
    color: var(--fg) !important;
    background: transparent !important;
    border-color: var(--line) !important;
}

.sun-editor .se-btn:enabled:hover,
.sun-editor .se-btn:enabled:focus,
.sun-editor .se-btn-select:enabled:hover {
    background: var(--surface-2) !important;
    color: var(--accent) !important;
}

.sun-editor .se-btn:enabled.active {
    color: var(--accent) !important;
}

.sun-editor .se-btn:disabled {
    opacity: 0.4 !important;
}

/* The writing surface itself, and the dialogs that open over it. */
.sun-editor .se-wrapper .se-wrapper-inner,
.sun-editor-editable,
.sun-editor .se-dialog-inner .se-dialog-content,
.sun-editor .se-controller,
.sun-editor .se-submenu {
    background: var(--surface-2) !important;
    color: var(--fg) !important;
}

.sun-editor .se-dialog-inner .se-dialog-content,
.sun-editor .se-controller,
.sun-editor .se-submenu {
    border-color: var(--line) !important;
}

.sun-editor .se-list-layer .se-list-inner li button:hover {
    background: var(--surface) !important;
    color: var(--accent) !important;
}

.sun-editor .se-dialog input,
.sun-editor .se-dialog select,
.sun-editor .se-dialog textarea,
.sun-editor .se-input-form {
    color: var(--fg) !important;
    background: var(--surface) !important;
    border-color: var(--line) !important;
}

.sun-editor .se-resizing-bar .se-navigation,
.sun-editor .se-resizing-bar .se-char-counter-wrapper {
    color: var(--muted) !important;
}

/* The code view, where the stored HTML is read rather than written. */
.sun-editor .se-wrapper .se-wrapper-code {
    background: var(--ink) !important;
    color: var(--fg) !important;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace !important;
}

/* Content styling inside the editor, so a draft looks like the published page. */
.sun-editor-editable blockquote {
    border-left: 3px solid var(--accent);
    margin: 1rem 0;
    padding: 0.25rem 0 0.25rem 1rem;
    color: var(--muted);
}

.sun-editor-editable pre {
    background: var(--ink);
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 0.85rem 1rem;
    overflow-x: auto;
}

.sun-editor-editable a {
    color: var(--accent);
}

.sun-editor-editable img,
.sun-editor-editable video {
    max-width: 100%;
    height: auto;
}

/* --- Modal --------------------------------------------------------------- */

.modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 3rem 1rem;
    overflow-y: auto;
    background: rgba(0, 0, 0, 0.72);
}

.modal-panel {
    width: min(60rem, 100%);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 8px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);

    /*
       Capped, with the body scrolling rather than the whole dialog. The entry
       forms behind the Add buttons run long — an app has icon, description, SEO
       and store fields — and letting the panel grow past the viewport pushes
       the heading and its close button off the top, leaving no visible way out
       but the Escape-less backdrop.
    */
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 6rem);
}

.modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.85rem 1.25rem;
    border-bottom: 1px solid var(--line);
}

.modal-head h3 {
    margin: 0;
}

.modal-close {
    font-size: 1.4rem;
    line-height: 1;
    color: var(--muted);
    background: transparent;
    border: 0;
    cursor: pointer;
}

.modal-close:hover {
    color: var(--fg);
}

.modal-body {
    padding: 1.25rem;
    /* The part that scrolls when the panel hits its cap; the head above it
       stays put so the close button is always reachable. */
    overflow-y: auto;
}

.tab-row {
    display: flex;
    gap: 0.25rem;
    padding: 0 1.25rem;
    border-bottom: 1px solid var(--line);
}

.tab {
    padding: 0.6rem 0.9rem;
    color: var(--muted);
    background: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    cursor: pointer;
}

.tab:hover {
    color: var(--fg);
}

.tab-active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* --- Media library ------------------------------------------------------- */

.media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: 1rem;
}

.media-tile {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.5rem;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 6px;
}

.media-thumb {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 7rem;
    padding: 0;
    overflow: hidden;
    background: var(--ink);
    border: 1px solid var(--line);
    border-radius: 4px;
    cursor: pointer;
}

.media-thumb:hover {
    border-color: var(--accent);
}

.media-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.media-video-badge {
    font-size: 1.75rem;
    color: var(--accent);
}

.media-meta {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

/* Filenames run long; clip rather than let a tile stretch the grid. */
.media-name {
    overflow: hidden;
    font-size: 0.85rem;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.media-delete {
    padding: 0.3rem;
    font-size: 0.8rem;
    color: var(--danger);
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 4px;
    cursor: pointer;
}

.media-delete:hover {
    border-color: var(--danger);
}

/* Site settings: scope banner, tabs, image fields ------------------------- */

/* The settings page looks identical whichever site is selected, so the banner
   naming that site is the one thing stopping an admin editing the wrong one.
   Accent edge and tint make it read as context rather than another form card. */
.settings-scope {
    border-left: 3px solid var(--accent);
    background: var(--accent-dim);
}

.settings-scope p {
    margin: 0;
}

.settings-scope p + p {
    margin-top: 0.5rem;
}

/* The modal tab row sits inside a padded panel; the page-level one has to
   carry its own spacing and sit flush with the cards below it. */
.tab-row-page {
    flex-wrap: wrap;
    padding: 0;
    margin-bottom: 1.5rem;
}

.tab-row-page .tab {
    font-size: 0.9rem;
    font-weight: 600;
}

.image-field {
    margin-bottom: 1.5rem;
}

.image-field > label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 600;
}

.image-field-row {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}

.image-field-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 5.5rem;
    height: 5.5rem;
    overflow: hidden;
    /* Checker-free dark plate: most logos here are drawn for a dark header, so
       previewing them on one shows what the site will actually render. */
    background: var(--ink);
    border: 1px solid var(--line);
    border-radius: 10px;
}

/* Banners and social cards are landscape; a square well crops them to nothing. */
.image-field-preview-wide {
    width: 14rem;
    height: 5.5rem;
}

.image-field-preview img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.image-field-controls {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    flex: 1 1 auto;
    min-width: 0;
}

.image-field-controls input {
    width: 100%;
}

.image-field-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.image-field-controls .field-hint {
    margin-top: 0;
}

/* Secondary action next to a primary one — "Remove" should not compete with
   "Upload or choose". */
.btn-quiet {
    color: var(--muted);
    background: transparent;
    border: 1px solid var(--line);
}

.btn-quiet:hover {
    color: var(--fg);
    border-color: var(--accent);
}

@media (max-width: 40rem) {
    .image-field-row {
        flex-direction: column;
    }

    .image-field-preview,
    .image-field-preview-wide {
        width: 100%;
    }
}

/* Sidebar sections, icons and form alignment ------------------------------ */

/*
 * A caption above each run of links.
 *
 * The sidebar grew to eighteen entries one screen at a time, and an unbroken
 * column gives no clue which of them are about writing and which are about
 * running the site. The headings cost one line each and make it scannable.
 */
.admin-nav-heading {
    margin: 1.1rem 0 0.3rem;
    padding: 0 0.7rem;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--faint);
}

.admin-nav-heading:first-of-type {
    margin-top: 0.6rem;
}

/* Links become rows so the icon and the label share a baseline. */
.admin-nav a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

/*
 * A fixed column for the icon, so the labels line up whatever the emoji is.
 * Emoji vary in width, and without this the text starts at a different place
 * on every row, which reads as a wobble down the whole sidebar.
 */
.admin-nav-icon {
    flex: 0 0 1.25rem;
    text-align: center;
    font-size: 0.95rem;
    line-height: 1;
}

/*
 * A label wrapping a control stacks: caption above, control below.
 *
 * These forms were written as `<label>Caption <input /></label>`, which with an
 * inline-block label puts the control immediately after the text — so every row
 * starts at a different x depending on how long its caption is. Stacking gives
 * one left edge down the form.
 *
 * Checkboxes and radios are excluded: their caption belongs beside the box, not
 * above it.
 */
.admin-content label:has(> input:not([type="checkbox"]):not([type="radio"])),
.admin-content label:has(> textarea),
.admin-content label:has(> select) {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    max-width: 34rem;
}

/*
 * Controls fill their label. The 18rem minimum above is what makes an input
 * overflow its cell inside a table, so it is dropped where the label sets the
 * width instead.
 */
.admin-content label > input,
.admin-content label > textarea,
.admin-content label > select {
    width: 100%;
    min-width: 0;
}

/* Consistent spacing between fields, rather than whatever <p> defaults to. */
.admin-content form p {
    margin: 0 0 1rem;
}

/* The icon picker in Site Settings → Menu. */
.icon-field {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.icon-preview {
    flex: 0 0 2rem;
    height: 2rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    line-height: 1;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 8px;
}

/* The picker itself needs no more room than its widest label. */
.admin-table-menu select {
    min-width: 11rem;
}

.admin-table-menu td {
    vertical-align: middle;
}

/* Help page --------------------------------------------------------------- */

.help-entry {
    border-top: 1px solid var(--line);
    padding: 0.2rem 0;
}

.help-entry:first-of-type {
    border-top: none;
}

.help-entry > summary {
    cursor: pointer;
    padding: 0.7rem 0.2rem;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--fg);
    /* The default triangle is tiny and sits oddly; a list-style marker is
       easier to line up with the text than a replacement glyph. */
    list-style-position: outside;
}

.help-entry > summary:hover {
    color: var(--accent);
}

.help-answer {
    padding: 0 0.2rem 0.8rem 1.1rem;
    max-width: 46rem;
}

.help-answer p {
    margin: 0 0 0.7rem;
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--muted);
}

.help-answer p:last-child {
    margin-bottom: 0;
}

/*
 * The sidebar stays on screen and scrolls on its own.
 *
 * Grouping the links under headings made the menu taller than a 900px-high
 * laptop screen, which pushed Help, My Profile and Log out below the fold —
 * reachable only by scrolling the whole page, and invisible to anyone who did
 * not think to try. Pinning it to the viewport and letting it scroll within
 * itself keeps the account block at the bottom of the sidebar rather than at
 * the bottom of whatever page happens to be open.
 */
.admin-nav {
    position: sticky;
    top: 0;
    height: 100vh;
    /* dvh where it is supported: a mobile browser's toolbars change the usable
       height, and 100vh alone leaves the last link under them. */
    height: 100dvh;
    overflow-y: auto;
    /* A long menu scrolling under the brand looks like a rendering fault. */
    overscroll-behavior: contain;
}

/* Thin, and only while the menu is actually too tall to fit. */
.admin-nav::-webkit-scrollbar {
    width: 6px;
}

.admin-nav::-webkit-scrollbar-thumb {
    background: var(--line);
    border-radius: 999px;
}

/*
 * A label naming the control that follows it stacks too.
 *
 * These forms use two shapes: `<label>Caption <input /></label>`, and
 * `<label for="x">Caption</label><select id="x">` where a `for` is needed.
 * Only the first was being stacked, so a single form mixed both alignments —
 * on the FAQ screen, Section and Question sat above their boxes while Language
 * and Shown on sat beside theirs.
 *
 * Labels wrapping a checkbox keep their caption beside the box, so anything
 * containing a control is left alone.
 */
.admin-content form p > label:not(:has(input, textarea, select)),
.admin-content .settings-grid p > label:not(:has(input, textarea, select)) {
    display: block;
    margin-bottom: 0.35rem;
}

/* Launch check ------------------------------------------------------------ */

.launch-site-key {
    margin-left: 0.55rem;
    padding: 0.1rem 0.45rem;
    border: 1px solid var(--line);
    border-radius: 6px;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--faint);
    vertical-align: middle;
}

/* The reason is the useful column, so it gets the room. */
.launch-detail {
    max-width: 34rem;
    line-height: 1.5;
    color: var(--muted);
}

.launch-where {
    white-space: nowrap;
    font-size: 0.82rem;
    color: var(--faint);
}

/* Dashboard --------------------------------------------------------------- */

/* The tiles became links, so they need to stop looking like body text. */
a.stat-card {
    display: block;
    color: inherit;
    text-decoration: none;
    transition: border-color 0.15s ease, transform 0.15s ease;
}

a.stat-card:hover {
    border-color: var(--accent);
    transform: translateY(-1px);
}

/* The breakdown under a headline figure: published, draft, scheduled. */
.stat-split {
    margin-top: 0.35rem;
    font-size: 0.78rem;
    color: var(--faint);
}

.attention-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.attention-item {
    display: grid;
    /* Count, then the link, then the reason — the reason takes what is left so
       the first two columns stay in a straight line down the list. */
    grid-template-columns: auto auto 1fr;
    align-items: baseline;
    gap: 0.7rem;
    padding: 0.7rem 0;
    border-top: 1px solid var(--line);
}

.attention-item:first-child {
    border-top: none;
}

.attention-item > a {
    font-weight: 600;
    color: var(--fg);
    text-decoration: none;
    white-space: nowrap;
}

.attention-item > a:hover {
    color: var(--accent);
    text-decoration: underline;
}

.attention-item .field-hint {
    margin: 0;
}

/* Recent work beside the shortcuts, stacking when there is no room. */
.dashboard-columns {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: 1rem;
    align-items: start;
}

@media (max-width: 1100px) {
    .dashboard-columns {
        grid-template-columns: 1fr;
    }
}

.dashboard-when {
    white-space: nowrap;
    color: var(--faint);
    font-size: 0.85rem;
}

.quick-actions {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
    gap: 0.5rem;
}

.quick-action {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.6rem 0.7rem;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 10px;
    color: var(--muted);
    text-decoration: none;
    font-size: 0.85rem;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.quick-action:hover {
    border-color: var(--accent);
    color: var(--fg);
}

.dashboard-blurb {
    margin-top: 1rem;
}

/* Charts ------------------------------------------------------------------ */

.chart {
    margin: 0;
}

/* Scales to the column it is placed in; the SVG keeps its own proportions. */
.chart-svg {
    width: 100%;
    height: auto;
    display: block;
    overflow: visible;
}

.chart-bar {
    fill: var(--accent);
    opacity: 0.85;
    transition: opacity 0.15s ease;
}

.chart-bar:hover {
    opacity: 1;
}

.chart-grid {
    stroke: var(--line);
    stroke-width: 0.5;
}

.chart-grid-label,
.chart-axis-label {
    fill: var(--faint);
    /* Small because the SVG is scaled up by the browser: at 320 units wide in a
       600px column, 5 units renders at roughly 9px. */
    font-size: 5px;
    font-family: inherit;
}

.trend-up {
    color: var(--accent);
    font-weight: 600;
}

.trend-down {
    color: var(--danger);
    font-weight: 600;
}

/* Ranked lists with the bar behind the label ------------------------------ */

.bar-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.bar-list-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.45rem 0.6rem;
    margin-bottom: 0.25rem;
    border-radius: 7px;
    /* Its own stacking context, so the fill below sits behind the text without
       needing a z-index on every label. */
    isolation: isolate;
}

.bar-list-fill {
    position: absolute;
    inset: 0 auto 0 0;
    z-index: -1;
    border-radius: 7px;
    background: var(--accent-dim);
}

.bar-list-label {
    flex: 1;
    min-width: 0;
    /* A long path truncates rather than wrapping the row to two lines and
       breaking the rhythm of the list. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.85rem;
    color: var(--fg);
}

.bar-list-value {
    font-variant-numeric: tabular-nums;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--muted);
}

/* Form field widths ------------------------------------------------------- */

/*
 * One width for every control, whatever shape the markup happens to be.
 *
 * Width used to depend on whether the label wrapped its control. A wrapped
 * input picked up the 34rem cap on `label:has(> input)`; a select sitting
 * *beside* its label — which is how Category and Developer are written —
 * matched nothing and fell back to the 18rem minimum instead. So two fields in
 * the same column came out at 34rem and 18rem, and that mismatch is most of
 * what made these forms look untidy.
 *
 * Held in a variable so the whole panel moves together rather than through
 * three rules that have to be kept in step by hand.
 */
:root {
    --field-width: 46rem;
    /* Prose fields earn more room: a description is read as a paragraph. */
    --field-width-wide: 64rem;
}

.admin-content form p > input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.admin-content form p > select,
.admin-content form p > textarea,
.admin-content .field-block > input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.admin-content .field-block > select,
.admin-content .field-block > textarea {
    width: 100%;
    min-width: 0;
    max-width: var(--field-width);
}

/* The label-wrapped form of the same thing, kept to the same width. */
.admin-content label:has(> input:not([type="checkbox"]):not([type="radio"])),
.admin-content label:has(> textarea),
.admin-content label:has(> select) {
    max-width: var(--field-width);
}

.admin-content textarea {
    max-width: var(--field-width-wide);
}

/*
 * The rich text editor fills its column.
 *
 * SunEditor sizes itself from the textarea it replaces, and that textarea
 * carried the ordinary field cap — so the body editor for a whole article came
 * out the same width as a one-line Slug box, with half the screen unused
 * beside it. It holds long-form content and is the one control on the page
 * worth the full width.
 */
.admin-content .rte,
.admin-content .sun-editor {
    width: 100% !important;
    max-width: none !important;
}

/*
 * A column for the form itself, so fields are not stranded against the left
 * edge of a very wide screen while the editor runs the full width.
 */
.admin-content form {
    max-width: 100%;
}

/*
 * The tab row on the long editors.
 *
 * `.tabs` was used as a container on the catalogue and SEO screens but never
 * defined, so the buttons inside it were laid out by nothing at all — they
 * flowed as inline elements spaced by the 0.35rem margin every admin button
 * carries, which is why the row sat tight against the heading above it and
 * broke unevenly on a narrow window.
 *
 * Same shape as `.tab-row`, without the full-width rule that one carries for
 * the dialogs it was written for.
 */
.admin-content .tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 0 0 1.5rem;
    padding: 0;
    border-bottom: 1px solid var(--line);
    padding-bottom: 0.85rem;
}

/* The gap does the spacing; the shared button margin would double it. */
.admin-content .tabs button {
    margin: 0;
}

/*
 * Two fields to a row.
 *
 * One field per full-width row left most of a wide screen empty and made the
 * short forms twice as long as they needed to be — Name, Slug and Package name
 * are all short, and stacking them meant scrolling past nothing to reach the
 * next one.
 *
 * Exactly two rather than auto-fit: auto-fit packs as many columns as will fit,
 * which on a wide monitor gives four narrow ones and reads as a spreadsheet.
 * Two keeps each field wide enough to hold a package name.
 */
.admin-content .field-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.1rem 2rem;
    align-items: start;
    max-width: 82rem;
}

/* One column once two would leave each too narrow to read. */
@media (max-width: 64rem) {
    .admin-content .field-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Fields fill their column rather than keeping the single-column cap. */
.admin-content .field-grid p,
.admin-content .field-grid label,
.admin-content .field-grid input,
.admin-content .field-grid select,
.admin-content .field-grid textarea {
    max-width: 100%;
    min-width: 0;
}

.admin-content .field-grid > p {
    margin: 0;
}

/* A control that earns the whole row — a long description, or a wide picker. */
.admin-content .field-grid > .span-2 {
    grid-column: 1 / -1;
}

/* Narrow screens ---------------------------------------------------------- */

/*
 * The sidebar folds away below a laptop.
 *
 * It was a fixed 230px column at every width, so on a 375px phone about 145px
 * was left for everything else — and nothing behind it could be made to work
 * while it was there. Off-canvas is the one change the rest depended on.
 *
 * Hidden with transform rather than display:none so it animates, and so the
 * links stay in the accessibility tree in source order.
 */
.admin-nav-toggle {
    /* Desktop keeps the sidebar, so the control has nothing to do. */
    display: none;
}

.admin-nav-backdrop {
    display: none;
}

@media (max-width: 64rem) {
    .admin-nav {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        z-index: 300;
        width: min(17rem, 82vw);
        overflow-y: auto;
        transform: translateX(-100%);
        transition: transform 0.18s ease-out;
        box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
    }

    .admin-nav.is-open {
        transform: none;
    }

    .admin-nav-toggle {
        display: inline-flex;
        align-items: center;
        /* Pushes the account controls to the far side of the header. */
        margin-right: auto;
    }

    .admin-nav-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 290;
        background: rgba(0, 0, 0, 0.5);
    }

    .admin-content {
        padding: 1rem;
    }

    /*
     * Comfortable under a thumb. Only here: raising it on the desktop panel
     * would make every toolbar loose and waste the space this change is
     * trying to recover.
     */
    .admin-content button,
    .admin-topbar-link,
    .admin-logout button {
        min-height: 2.75rem;
    }
}

/*
 * Tables scroll rather than squash.
 *
 * The listings run from five columns to thirty-one, and no amount of wrapping
 * makes thirty-one fit a phone. display:block is what lets a table scroll on
 * its own; the alternative — rebuilding each listing as cards — is worth doing
 * for the few somebody would genuinely read on a phone, and is not worth doing
 * blind for all twelve.
 */
@media (max-width: 64rem) {
    table.admin-table {
        display: block;
        overflow-x: auto;
        white-space: nowrap;
    }
}
