/* =============================================================================
   Administrace — the operator's half of the site

   The public site is a carved, gilded night and it is allowed to breathe. This
   is the same material worked differently: the same ink, the same brass hair
   lines, the same two typefaces — and none of the space. An operator looking
   for the payment that did not settle is reading a table, and the design's job
   there is to get out of the way and keep the columns aligned.

   Three rules hold the section together:

   1. **Fraunces speaks once per screen.** The page title, and the big number on
      a KPI tile. Everything else — labels, rows, buttons, chrome — is
      Montserrat, because a table set in a display serif is a poster.
   2. **A hairline separates, a plate elevates.** Rows are divided by brass at
      16 % and nothing else; a box only gets a frame when it is a thing you can
      act on. The screens that went wrong before had a border around every
      paragraph.
   3. **Numbers are tabular, right-aligned and never wrap.** A column of amounts
      that does not line up is a column nobody can scan, and scanning is the
      whole reason the screen exists.

   Everything new here is prefixed `.adm-`. Everything unprefixed
   (`.admin-shell`, `.data-table`, `.kpi`, `.badge`) is a shared component
   declared in components.css and only re-tuned for this density.
   ============================================================================= */

.is-admin {
    /* Wider than the public measure: a nine-column table at 1450 px wraps, and
       the administration has no illustration competing for the room. */
    --adm-width: 1600px;

    /* The top bar is sticky, so every sticky thing below it has to know how
       tall it is. Declared once. */
    --adm-topbar: 52px;

    --adm-hairline: rgb(153 105 59 / 22%);
    --adm-zebra: rgb(150 160 200 / 5%);
    --adm-plate: rgb(12 16 30 / 72%);
    --adm-code-bg: #020610;

    background-color: var(--color-background-deep);
}

.is-admin .container {
    width: min(100% - 2 * var(--space-5), var(--adm-width));
}

/* =============================================================================
   Top bar

   Sticky, because the administration's pages are long and the way back to
   another section should not be a scroll to the top. Its own darker ground and
   a single brass hairline underneath — the same seam the public header uses,
   without the ornament.
   ============================================================================= */

.is-admin .admin-header {
    position: sticky;
    inset-block-start: 0;
    z-index: 40;
    background:
        linear-gradient(180deg, rgb(6 10 22 / 97%), rgb(3 8 18 / 97%));
    backdrop-filter: blur(6px);
    border-bottom: 1px solid var(--color-border-soft);
}

.is-admin .admin-header__inner {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--space-4);
    align-items: center;
    min-height: var(--adm-topbar);
    padding-block: 0;
}

.is-admin .admin-brand {
    display: inline-flex;
    gap: var(--space-2);
    align-items: center;
    font-family: var(--font-sans);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--color-muted);
    white-space: nowrap;
    transition: color var(--transition-fast);
}

.is-admin .admin-brand:hover {
    color: var(--color-text);
}

.admin-brand__sigil {
    flex: 0 0 auto;
    color: var(--color-gold);
}

.admin-brand__name {
    font-family: var(--font-serif);
    font-size: var(--text-lg);
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: none;
    color: var(--color-text);
}

/* The status strip sits in the middle column and stays there: it is the one
   place on the page that says what this deployment is. */
.adm-status {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    min-width: 0;
}

.adm-env,
.adm-health {
    display: inline-flex;
    gap: var(--space-2);
    align-items: center;
    min-height: 26px;
    padding-inline: var(--space-3);
    font-size: var(--text-2xs);
    font-weight: 620;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    white-space: nowrap;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-pill);
    color: var(--color-muted);
}

.adm-env__dot,
.adm-health__dot {
    width: 6px;
    height: 6px;
    background: currentColor;
    border-radius: 50%;
    box-shadow: 0 0 0 3px rgb(255 255 255 / 6%);
}

/* Production is the one that has to be unmistakable from the corner of an eye:
   brass on brass. Dev and test are deliberately quiet — the badge is there to
   stop a refund being pressed on the wrong deployment, and the deployment where
   that matters is exactly one. */
.adm-env--prod {
    color: var(--color-gold-bright);
    background: rgb(200 145 76 / 14%);
    border-color: var(--color-gold);
}

.adm-env--dev,
.adm-env--test {
    color: var(--color-info);
    border-color: rgb(143 179 221 / 35%);
}

/* Filled in only by the page that actually measured something — see the comment
   in layout/admin.html.twig. */
a.adm-health {
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

a.adm-health:hover {
    color: var(--color-text);
    border-color: var(--color-border);
}

.adm-health--ok {
    color: var(--color-success);
    border-color: rgb(121 194 148 / 40%);
}

.adm-health--warn {
    color: var(--color-warning);
    border-color: rgb(224 168 91 / 45%);
}

.adm-health--bad {
    color: var(--color-danger);
    background: rgb(229 128 121 / 10%);
    border-color: rgb(229 128 121 / 50%);
}

.is-admin .admin-whoami {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    justify-content: flex-end;
    margin: 0;
    font-size: var(--text-xs);
}

.is-admin .admin-whoami .user-chip {
    max-width: 22rem;
    min-height: 32px;
}

.adm-whoami__mail {
    overflow: hidden;
    text-overflow: ellipsis;
}

.adm-whoami__exit {
    color: var(--color-muted);
    white-space: nowrap;
    transition: color var(--transition-fast);
}

.adm-whoami__exit:hover {
    color: var(--color-accent-text);
}

/* =============================================================================
   Shell and rail
   ============================================================================= */

.is-admin .admin-shell {
    grid-template-columns: 196px minmax(0, 1fr);
    gap: var(--space-6);
    padding-block: var(--space-5) var(--space-8);
}

.admin-rail {
    position: sticky;
    inset-block-start: calc(var(--adm-topbar) + var(--space-4));
    align-self: start;
}

.is-admin .admin-rail__list {
    position: static;
    gap: 1px;
}

.adm-rail__group {
    padding: var(--space-4) var(--space-3) var(--space-1);
    font-size: var(--text-2xs);
    font-weight: 620;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: rgb(170 166 164 / 62%);
}

.adm-rail__group:first-child {
    padding-block-start: var(--space-2);
}

.is-admin .admin-rail__link {
    position: relative;
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-sm);
    font-weight: 520;
    letter-spacing: 0.01em;
    color: var(--color-text-soft);
    border-radius: var(--radius-sm);
    background: transparent;
}

.is-admin .admin-rail__link:hover {
    color: var(--color-text);
    background: rgb(200 145 76 / 7%);
}

/* The gold marker from the public navigation, stood on its end. Same gradient
   idea — dark brass into bright brass and back — so the administration's
   "you are here" is recognisably the site's. */
.is-admin .admin-rail__link.is-current {
    color: var(--color-gold-bright);
    background: linear-gradient(90deg, rgb(200 145 76 / 15%), rgb(200 145 76 / 2%));
    box-shadow: none;
}

.is-admin .admin-rail__link.is-current::before {
    position: absolute;
    inset-block: 2px;
    inset-inline-start: 0;
    width: 2px;
    content: '';
    background: linear-gradient(180deg,
        rgb(115 82 58) 0%,
        rgb(233 177 114) 30%,
        rgb(245 204 128) 55%,
        rgb(201 143 90) 80%,
        rgb(70 52 45) 100%);
    border-radius: 2px;
}

/* The fold. Above 62rem the summary is not drawn and the panel is forced open;
   both of the mechanisms a browser may use to hide `<details>` content are
   overridden, because Chromium moved from `display` to `::details-content`
   partway through the versions this has to run on. */
.adm-rail-fold__summary {
    display: none;
}

@media (width >= 62rem) {
    .adm-rail-fold > .admin-rail__list {
        display: grid;
    }

    .adm-rail-fold::details-content {
        content-visibility: visible;
        block-size: auto;
    }
}

@media (width < 62rem) {
    .is-admin .admin-shell {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-4);
    }

    .admin-rail {
        position: static;
    }

    .adm-rail-fold {
        border: 1px solid var(--color-border-soft);
        border-radius: var(--radius-sm);
    }

    .adm-rail-fold__summary {
        display: flex;
        gap: var(--space-3);
        align-items: center;
        min-height: 40px;
        padding-inline: var(--space-3);
        font-size: var(--text-2xs);
        font-weight: 620;
        text-transform: uppercase;
        letter-spacing: 0.16em;
        color: var(--color-muted);
        cursor: pointer;
        list-style: none;
    }

    .adm-rail-fold__summary::-webkit-details-marker {
        display: none;
    }

    .adm-rail-fold__bars {
        width: 14px;
        height: 9px;
        border-block: 1px solid currentColor;
        border-block-end-width: 1px;
        box-shadow: inset 0 4px 0 -1px currentColor;
    }

    .adm-rail-fold__here {
        margin-inline-start: auto;
        color: var(--color-gold-bright);
    }

    .is-admin .admin-rail__list {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-1);
        padding: var(--space-2) var(--space-3) var(--space-3);
        border-top: 1px solid var(--color-border-soft);
    }

    .adm-rail__group {
        flex-basis: 100%;
        padding: var(--space-3) 0 0;
    }

    .adm-rail__group:first-child {
        padding-block-start: 0;
    }

    .is-admin .admin-rail__link.is-current::before {
        inset: auto 4px 0;
        width: auto;
        height: 2px;
        background: linear-gradient(90deg,
            rgb(115 82 58), rgb(245 204 128) 50%, rgb(115 82 58));
    }
}

@media (width < 46rem) {
    .is-admin .admin-header__inner {
        grid-template-columns: minmax(0, 1fr) auto;
        gap: var(--space-2) var(--space-3);
        padding-block: var(--space-2);
    }

    .adm-status {
        grid-row: 2;
        grid-column: 1 / -1;
    }

    .adm-whoami__mail {
        display: none;
    }

    /* "· administrace" is what the rail already says on every screen. Below
       46rem it is the difference between a wordmark and a wordmark clipped
       behind the operator's avatar. */
    .is-admin .admin-brand {
        min-width: 0;
        overflow: hidden;
    }

    .is-admin .admin-brand > span:not(.admin-brand__name) {
        display: none;
    }
}

/* =============================================================================
   Page head

   The one place Fraunces is allowed to be large. The rule underneath is the
   brass hairline the public pages use to close a section, borrowed here to open
   one.
   ============================================================================= */

.is-admin .admin-head {
    gap: var(--space-2) var(--space-5);
    align-items: end;
    padding-block: 0 var(--space-3);
    margin-block-start: var(--space-2);
    border-bottom: 1px solid var(--color-border-soft);
}

.is-admin .admin-head h1 {
    font-family: var(--font-serif);
    font-size: var(--text-2xl);
    font-weight: 400;
    line-height: 1.1;
    letter-spacing: -0.01em;
    /* Half these titles are an e-mail address or a ULID, and neither offers a
       break opportunity a browser will take on its own. On a phone the account
       page's own heading was running off the side. */
    overflow-wrap: anywhere;
}

.is-admin .admin-head h2 {
    font-family: var(--font-serif);
    font-size: var(--text-xl);
    font-weight: 400;
}

.is-admin .admin-head__note {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    font-size: var(--text-xs);
    line-height: 1.5;
    color: var(--color-muted);
}

.is-admin .admin-head__note strong {
    font-variant-numeric: tabular-nums;
    color: var(--color-text);
}

.is-admin .admin-main h2 {
    font-family: var(--font-sans);
    font-size: var(--text-md);
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--color-text);
}

.is-admin .admin-main h3 {
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    font-weight: 620;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--color-muted);
}

/* A section heading with a brass rule running off to the right of it — the
   cheapest way to give a long page a horizon without drawing another box. */
.is-admin .admin-main > section > h2 {
    display: flex;
    gap: var(--space-4);
    align-items: center;
}

.is-admin .admin-main > section > h2::after {
    flex: 1;
    height: 1px;
    content: '';
    background: var(--rule-gold-fade);
}

/* =============================================================================
   Notices, tabs, filters
   ============================================================================= */

.is-admin .admin-notice {
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-sm);
    line-height: 1.6;
    color: var(--color-text-soft);
    background: rgb(200 145 76 / 6%);
    border: 1px solid transparent;
    border-inline-start: 2px solid var(--color-gold);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.is-admin .admin-notice ul {
    padding-inline-start: var(--space-5);
    margin-block: var(--space-2) 0;
}

.is-admin .admin-tabs {
    gap: 0;
    padding-block-end: 1px;
    border-bottom: 1px solid var(--color-border-soft);
}

/* Tabs inside the administration are a strip under a title, not pills floating
   in space: an underline is a cheaper mark than a border, and reads as "second
   level of the thing above" rather than "another set of buttons". */
.is-admin .admin-tabs a {
    min-height: 34px;
    padding-inline: var(--space-4);
    font-size: var(--text-2xs);
    letter-spacing: 0.14em;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: inset 0 -1px 0 transparent;
}

.is-admin .admin-tabs a:hover {
    color: var(--color-text);
    box-shadow: inset 0 -1px 0 var(--color-border);
}

.is-admin .admin-tabs a.is-current {
    color: var(--color-gold-bright);
    background: rgb(200 145 76 / 8%);
    box-shadow: inset 0 -2px 0 var(--color-gold);
}

.is-admin .admin-filters {
    gap: var(--space-3) var(--space-4);
    align-items: end;
    padding: var(--space-3) var(--space-4);
    background: var(--adm-plate);
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-sm);
}

.is-admin .admin-filters .field {
    gap: var(--space-1);
    min-width: 11rem;
    margin: 0;
}

.is-admin .admin-filters .label,
.is-admin .admin-filters > .label {
    font-size: var(--text-2xs);
    font-weight: 620;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--color-muted);
}

.is-admin .admin-filters .input,
.is-admin .admin-filters .select {
    min-height: 34px;
    padding-block: var(--space-1);
    font-size: var(--text-sm);
}

.is-admin .admin-filters .field__help {
    display: none;
}

.is-admin .admin-filters .btn {
    align-self: end;
}

/* =============================================================================
   Buttons

   The shared button is a purple plate with an inner glow, drawn that way for the
   mockup's hero pair. In a table cell it is a lozenge that outweighs the row it
   acts on, and a screen with six of them is a screen of buttons. Here they are
   flat, small and quiet — except `--primary`, which is filled brass, because
   every one of these screens has exactly one action worth pressing and it
   should be findable without reading the labels.
   ============================================================================= */

.is-admin .btn,
.is-admin .button {
    min-height: 34px;
    padding-inline: var(--space-4);
    font-size: var(--text-2xs);
    font-weight: 620;
    letter-spacing: 0.12em;
    color: var(--color-text-soft);
    background: rgb(255 255 255 / 4%);
    border-color: var(--color-border-soft);
    box-shadow: none;
}

.is-admin .btn:hover,
.is-admin .button:hover {
    color: var(--color-gold-bright);
    background: rgb(200 145 76 / 12%);
    border-color: var(--color-border);
    transform: none;
}

.is-admin .btn--primary,
.is-admin .button--primary {
    min-height: 34px;
    font-size: var(--text-2xs);
    color: var(--color-text-inverse);
    background: linear-gradient(180deg, var(--color-gold-light), var(--color-gold));
    border-color: var(--color-gold-light);
}

.is-admin .btn--primary:hover,
.is-admin .button--primary:hover {
    color: var(--color-text-inverse);
    background: linear-gradient(180deg, var(--color-gold-bright), var(--color-gold-light));
}

.is-admin .btn--ghost {
    color: var(--color-muted);
    background: transparent;
}

.is-admin .btn--sm {
    min-height: 28px;
    padding-inline: var(--space-3);
    font-size: 10px;
}

/* A destructive action is not a colour the palette owns, so it is the muted
   button with a warm rim — enough to make somebody read the label twice. */
.is-admin .btn--danger {
    color: var(--color-danger);
    background: rgb(229 128 121 / 8%);
    border-color: rgb(229 128 121 / 45%);
}

.is-admin .filter-chip,
.is-admin .filter-chips a {
    min-height: 30px;
    font-size: var(--text-2xs);
}

/* =============================================================================
   Tables

   Sticky header rows, zebra by the faintest surface change the eye can still
   resolve, and no vertical rules.

   The header sticks to the *table's* box and not to the page, and that is a
   consequence rather than a preference: `.table-scroll` has `overflow-x: auto`
   so a nine-column table can be read at 360px, and a box that scrolls in any
   axis is the scroll container its own sticky children resolve against. So the
   box is given a height — a screen minus the chrome — and a long table scrolls
   inside it under a pinned header, which is the behaviour that was wanted
   anyway: the operator keeps the column names and the page keeps its place.
   Short tables are unaffected; `max-block-size` is a ceiling, not a height.
   ============================================================================= */

.is-admin .table-scroll {
    max-block-size: calc(100dvh - 10rem);
    overflow: auto;
    overscroll-behavior: contain;
    background: var(--adm-plate);
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-sm);
}

.is-admin .card > .table-scroll,
.is-admin .panel > .table-scroll {
    background: transparent;
}

.is-admin .data-table {
    font-size: var(--text-sm);
}

/* `.data-table` holds a 34rem floor so a nine-column log stays legible and
   scrolls instead of squashing. A two-column table — a queue depth, a fact
   list — has no such problem, and below 544px that floor would push a single
   number off the side of its own box. */
.is-admin .adm-table--narrow,
.is-admin .reading-facts {
    min-width: 0;
}

@media (width < 40rem) {
    .is-admin .data-table tbody th[scope='row'] {
        width: 8rem;
    }
}

.is-admin .data-table th {
    position: sticky;
    inset-block-start: 0;
    z-index: 2;
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-2xs);
    font-weight: 650;
    letter-spacing: 0.12em;
    color: var(--color-muted);
    background: #0a0e1b;
    border-bottom: 1px solid var(--color-border-soft);
}

/* A row header (`<th scope="row">`) is a label in the first column, not a
   column head — it must not stick and it must not shout. */
.is-admin .data-table tbody th[scope='row'] {
    position: static;
    z-index: auto;
    width: 15rem;
    font-size: var(--text-2xs);
    color: var(--color-muted);
    background: transparent;
    border-bottom: 1px solid var(--adm-hairline);
}

.is-admin .data-table td {
    padding: var(--space-2) var(--space-4);
    line-height: 1.5;
    color: var(--color-text-soft);
    vertical-align: baseline;
    border-bottom: 1px solid var(--adm-hairline);
}

.is-admin .data-table tbody tr:nth-child(even) td,
.is-admin .data-table tbody tr:nth-child(even) th[scope='row'] {
    background: var(--adm-zebra);
}

.is-admin .data-table tbody tr:hover td,
.is-admin .data-table tbody tr:hover th[scope='row'] {
    background: rgb(200 145 76 / 7%);
}

.is-admin .data-table--dense th,
.is-admin .data-table--dense td {
    padding: 7px var(--space-3);
    font-size: var(--text-sm);
}

.is-admin .data-table__num {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.01em;
}

/* `.data-table th` in components.css sets `text-align: start` and outweighs the
   `.data-table__num` on the cell, so a numeric column's heading has to say it
   again here. A heading that does not sit over its own column is a heading the
   eye has to re-find on every row. */
.is-admin .data-table th.data-table__num {
    text-align: end;
}

/* For the one or two columns whose value is a short name that must not be
   broken across lines — a plan, a transport. The table already scrolls. */
.is-admin .data-table .adm-nowrap {
    white-space: nowrap;
}

.is-admin .data-table__when {
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    color: var(--color-muted);
}

/* The secondary line under a cell's main value: a class name, an event key, a
   count of attempts. Grey, small, on its own line — it is context, and context
   that fights the value for attention is noise. */
.is-admin .data-table .field__help {
    display: block;
    margin-block-start: 1px;
    font-size: var(--text-2xs);
    line-height: 1.4;
    color: rgb(170 166 164 / 72%);
}

.is-admin .data-table code {
    font-size: var(--text-xs);
    color: var(--color-text);
    white-space: nowrap;
}

/* A public id is usually both a link and a `<code>`, and the two rules above
   would otherwise leave the link looking like plain text. */
.is-admin .data-table a code {
    color: inherit;
}

/* components.css drops the bottom rule on the last row's cells; a row header is
   a cell too, and without this the last line of a fact table is drawn three
   quarters of the way across. */
.is-admin .data-table tbody tr:last-child th[scope='row'] {
    border-bottom: 0;
}

/* An e-mail address, a public id, a payment reference. Chrome's auto table
   layout will happily break one of these across two lines to give a date column
   room it does not need; a reference somebody has to compare against a support
   mail must be one string. */
.is-admin .data-table a {
    white-space: nowrap;
}

/* app.css underlines every link inside a `<td>`, which is right in prose and
   wrong in a list of eighty rows: the underlines become the texture of the
   table and the column stops being scannable. The affordance moves to hover,
   where it costs nothing. */
.is-admin .data-table td a:not(.btn, .button, .badge) {
    color: var(--color-accent-text);
    text-decoration: none;
}

.is-admin .data-table td a:not(.btn, .button, .badge):hover {
    color: var(--color-gold-bright);
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* An action cell holds forms, and a form is a block. Without this every button
   in the last column starts its own line. */
.is-admin .data-table td > form {
    display: inline;
}

.is-admin .data-table .cluster {
    gap: var(--space-2);
    flex-wrap: nowrap;
}

.is-admin .data-table .btn--sm {
    min-height: 28px;
    padding-inline: var(--space-3);
    font-size: var(--text-2xs);
}

/* A `<pre>` spanning the whole width inside a table row — the gateway payload
   under a payment event. It has no business carrying the row's zebra. */
.is-admin .data-table td > .admin-pre {
    margin-block: var(--space-1) var(--space-2);
}

/* =============================================================================
   Badges

   The administration reuses the shared badge and only tightens it: at this
   density the public badge is a lozenge in a row of text.
   ============================================================================= */

/* Metrics only. Colour is left entirely to the shared modifiers, because a
   background declared here would outweigh `.badge--muted` — a whole column of
   supposedly quiet badges came back brass the first time this rule tried to
   tone the default down. The emphasis is a template decision (which modifier a
   state gets), not a stylesheet one. */
.is-admin .badge {
    min-height: 20px;
    padding-inline: var(--space-2);
    font-size: 10px;
    letter-spacing: 0.1em;
    white-space: nowrap;
    vertical-align: middle;
}

/* A checkbox row is one control, not a panel. The shared `.choice` draws a
   frame the width of the form, which at this density reads as another input. */
.is-admin .choice {
    justify-self: start;
    padding-inline: var(--space-3);
    font-size: var(--text-sm);
    background: transparent;
}

/* =============================================================================
   KPI tiles

   Big number, small label, one line of provenance under it. No sparkline: a
   sparkline over eight points is a decoration that implies a trend nobody
   measured, and the two windows on the dashboard already say what changed.
   ============================================================================= */

.is-admin .kpi-grid {
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 0;
    overflow: hidden;
    background: linear-gradient(160deg, rgb(24 21 38 / 55%), rgb(9 13 26 / 75%));
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-sm);
}

/* Tiles share hairlines rather than each carrying its own frame: eight framed
   boxes in a row read as eight things, one ruled block reads as one reading.

   Two borders and a negative margin rather than `gap: 1px` over a coloured
   grid: `auto-fit` leaves the last row short, and a coloured gap paints those
   empty cells as a solid brass block. This way an empty cell is simply the
   block's own ground. */
.is-admin .kpi {
    gap: var(--space-1);
    padding: var(--space-4);
    margin-block-start: -1px;
    margin-inline-start: -1px;
    background: transparent;
    border: 0;
    border-block-start: 1px solid var(--color-border-soft);
    border-inline-start: 1px solid var(--color-border-soft);
    border-radius: 0;
}

.is-admin .kpi__label {
    font-size: var(--text-2xs);
    font-weight: 620;
    letter-spacing: 0.14em;
    color: rgb(170 166 164 / 80%);
}

.is-admin .kpi__value {
    font-size: 2rem;
    font-weight: 300;
    line-height: 1.05;
    letter-spacing: -0.015em;
    color: var(--color-text);
}

/* Said again because the rule above outweighs the modifiers in
   components.css, and a blocked account whose tile is the same colour as a
   working one is a tile that has stopped saying anything. */
.is-admin .kpi__value--positive {
    color: var(--color-success);
}

.is-admin .kpi__value--negative {
    color: var(--color-danger);
}

.is-admin .kpi__meta {
    font-size: var(--text-2xs);
    line-height: 1.45;
    color: rgb(170 166 164 / 72%);
}

/* =============================================================================
   Plates: card, panel, forms

   `.card` is the administration's only container and it is a plate, not a box:
   the same gradient the public reading cards use, at a fraction of the padding.
   ============================================================================= */

.is-admin .admin-main .card,
.is-admin .admin-main .adm-panel {
    padding: var(--space-4) var(--space-5);
    background: var(--plate-fill-soft);
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-sm);
    box-shadow: none;
}

/* A card holding one sentence is an empty state, not a plate. Dashed, centred,
   and the same height whatever the sentence says. */
.is-admin .admin-main .card > p:only-child {
    padding-block: var(--space-3);
    margin: 0;
    font-size: var(--text-sm);
    color: var(--color-muted);
}

.is-admin .card > h2:first-child {
    margin-block-end: var(--space-1);
}

/* Two columns for the short fields, one for anything that holds prose. The
   administration's forms are long — a product has nine fields, a prompt version
   six plus three editors — and a single column of them is a page nobody can see
   the end of. */
.adm-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: var(--space-4) var(--space-5);
    align-items: start;
}

.adm-form-grid > .adm-span {
    grid-column: 1 / -1;
}

.is-admin .field {
    gap: var(--space-2);
}

.is-admin .label {
    font-size: var(--text-xs);
    font-weight: 620;
    letter-spacing: 0.06em;
    color: var(--color-text-soft);
}

.is-admin .input,
.is-admin .select,
.is-admin .textarea {
    font-size: var(--text-sm);
    background: var(--adm-code-bg);
    border-color: var(--color-border-neutral);
}

.is-admin .input:focus-visible,
.is-admin .select:focus-visible,
.is-admin .textarea:focus-visible {
    border-color: var(--color-gold);
}

.is-admin .field__help {
    font-size: var(--text-xs);
    line-height: 1.55;
    color: var(--color-muted);
}

.is-admin .divider,
.is-admin hr.divider {
    margin-block: var(--space-2);
    border: 0;
    border-top: 1px solid var(--color-border-soft);
}

/* =============================================================================
   Fact lists

   `.admin-facts` is the detail page. Term left, value right, a hairline between
   pairs — and on a wide screen two columns of pairs, because a payment has
   thirteen facts and thirteen rows of two words is a column of air.
   ============================================================================= */

.is-admin .admin-facts {
    grid-template-columns: minmax(8rem, max-content) minmax(0, 1fr);
    gap: 0;
    font-size: var(--text-sm);
}

.is-admin .admin-facts dt {
    /* Level with the first line of its value, not with the middle of a
       three-line one: a term floating beside the second line of a paragraph
       reads as belonging to that line. */
    align-self: start;
    padding-block: 7px;
    padding-inline-end: var(--space-4);
    font-size: var(--text-2xs);
    letter-spacing: 0.1em;
    color: var(--color-muted);
    border-top: 1px solid var(--adm-hairline);
}

.is-admin .admin-facts dd {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: baseline;
    padding-block: 7px;
    color: var(--color-text);
    /* A prompt code, an idempotency key, a gateway reference: values with no
       spaces to break at, which would otherwise run out of the plate. */
    overflow-wrap: break-word;
    border-top: 1px solid var(--adm-hairline);
}

.is-admin .admin-facts dt:first-of-type,
.is-admin .admin-facts dt:first-of-type + dd {
    border-top: 0;
}

.is-admin .admin-facts dd .field__help {
    flex-basis: 100%;
    font-size: var(--text-2xs);
}

.is-admin .admin-facts dd code {
    overflow-wrap: anywhere;
}

/* Below 34rem the two columns are 8rem of label and whatever is left, and
   whatever is left is not enough for `tarot.interpretation`. Stacked, every
   fact gets the full width of the plate. */
@media (width < 34rem) {
    .is-admin .admin-facts {
        grid-template-columns: minmax(0, 1fr);
    }

    .is-admin .admin-facts dt {
        padding-block: var(--space-3) 2px;
        padding-inline-end: 0;
        border-top: 1px solid var(--adm-hairline);
    }

    .is-admin .admin-facts dd {
        padding-block: 0 var(--space-1);
        border-top: 0;
    }

    .is-admin .admin-facts dt:first-of-type {
        padding-block-start: 0;
    }
}

@media (width >= 78rem) {
    /* Two pairs per row. `grid-auto-flow: row dense` is deliberately not used —
       the order of these facts is an argument (see the templates) and a browser
       reflowing them by size would break it. */
    .is-admin .adm-facts--split {
        grid-template-columns:
            minmax(8rem, max-content) minmax(0, 1fr)
            minmax(8rem, max-content) minmax(0, 1fr);
        column-gap: var(--space-6);
    }

    /* Two pairs sit on the first row, so two of them open the list and neither
       wants the rule that separates one row of facts from the next. */
    .is-admin .adm-facts--split dt:nth-of-type(-n + 2),
    .is-admin .adm-facts--split dt:nth-of-type(-n + 2) + dd {
        border-top: 0;
    }
}

/* =============================================================================
   Code, JSON and prompt text

   Monospace on the deepest ground in the palette, with a line-number gutter
   where the text is long enough for somebody to point at a line. Not a token
   colouriser: the JSON here is a payload to check against a gateway's docs, and
   a syntax theme would be the third typeface on the page.
   ============================================================================= */

.is-admin .admin-pre {
    max-height: 30rem;
    padding: var(--space-3) var(--space-4);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.65;
    color: var(--color-text-soft);
    background: var(--adm-code-bg);
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-sm);
    tab-size: 2;
}

/* A textarea that holds JSON or a prompt is an editor, and an editor is
   monospace. `white-space: pre` would be wrong — these wrap. */
.adm-code-input {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.65;
    tab-size: 2;
    white-space: pre-wrap;
}

.is-admin textarea.adm-code-input {
    min-height: 12rem;
    background: var(--adm-code-bg);
    caret-color: var(--color-gold-bright);
}

/* The line-numbered view of a stored prompt. A version of a prompt is evidence:
   the reason to look at one is to say "the third paragraph is what changed", and
   a paragraph you cannot point at by number is a paragraph two people argue
   about. Rendered as a table so the numbers cannot be selected with the text. */
.adm-lines {
    max-height: 34rem;
    overflow: auto;
    background: var(--adm-code-bg);
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-sm);
}

.adm-lines__table {
    width: 100%;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.65;
    border-collapse: collapse;
}

.adm-lines__no {
    width: 1%;
    padding: 0 var(--space-3);
    color: rgb(170 166 164 / 38%);
    text-align: end;
    vertical-align: baseline;
    user-select: none;
    background: rgb(255 255 255 / 2%);
    border-inline-end: 1px solid var(--color-border-soft);
}

.adm-lines__text {
    padding: 0 var(--space-4);
    color: var(--color-text-soft);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.adm-lines tr:hover .adm-lines__text {
    background: rgb(200 145 76 / 6%);
}

/* An empty line still needs its row height, or the numbers stop matching the
   text they count. */
.adm-lines__text:empty::before {
    content: '\200b';
}

/* The row that is answering calls right now — a prompt version, a price. Tinted
   and marked on its inside edge, so it is findable in a column of a dozen
   without a second colour entering the palette. */
.is-admin .data-table tbody tr.adm-row--live td {
    background: rgb(200 145 76 / 9%);
    box-shadow: inset 2px 0 0 var(--color-gold);
}

.is-admin .data-table tbody tr.adm-row--live:hover td {
    background: rgb(200 145 76 / 13%);
}

/* The head of one social slot: what it is on the left, what state it is in on
   the right, a hairline under both. */
.adm-slot-head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    align-items: center;
    justify-content: space-between;
    padding-block-end: var(--space-3);
    border-bottom: 1px solid var(--adm-hairline);
}

.adm-slot-head .overline {
    margin: 0;
    font-size: var(--text-2xs);
    letter-spacing: 0.16em;
    color: var(--color-accent-text);
}

/* =============================================================================
   Pager
   ============================================================================= */

.is-admin .admin-pager {
    gap: var(--space-2);
    justify-content: flex-end;
    padding-block: var(--space-3) 0;
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
}

.is-admin .admin-pager > span {
    padding-inline: var(--space-3);
    color: var(--color-muted);
}

.is-admin .admin-pager a {
    min-height: 30px;
    font-size: var(--text-xs);
}

/* =============================================================================
   Per-section touches
   ============================================================================= */

/* Dashboard — the two windows are the same eight numbers twice, so each one is
   introduced by its span of days rather than by a heading that repeats. */
.adm-window {
    display: grid;
    gap: var(--space-3);
}

.adm-window__head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    align-items: baseline;
}

.adm-window__title {
    font-family: var(--font-serif) !important;
    font-size: var(--text-xl) !important;
    font-weight: 400 !important;
    letter-spacing: 0 !important;
    text-transform: none !important;
    color: var(--color-text) !important;
}

.adm-window__note {
    font-size: var(--text-xs);
    color: var(--color-muted);
}

/* The funnel table's first column carries a name and its event key; the rest is
   three percentages that have to line up. */
.adm-funnel .data-table__num {
    width: 9rem;
}

/* Social — four slot plates, each with three copy boxes. Without this every
   `<pre>` is the width of the page and the button under it looks orphaned. */
.adm-copy {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-3);
    background: rgb(255 255 255 / 2%);
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-sm);
}

.adm-copy .card__eyebrow {
    margin: 0;
    font-size: var(--text-2xs);
    letter-spacing: 0.14em;
    color: var(--color-muted);
}

.adm-copy .admin-pre {
    max-height: 16rem;
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    line-height: 1.6;
    white-space: pre-wrap;
}

.adm-copy .btn {
    justify-self: start;
}

.adm-copy-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: var(--space-3);
    align-items: start;
}

/* The post itself takes the whole row and the three short pieces share the one
   beneath it. Left to flow, the tallest box sets the row height and the caption
   and the hashtags each sit over a hand's width of nothing. */
.adm-copy--wide {
    grid-column: 1 / -1;
}

/* =============================================================================
   Print

   Somebody will print a payment detail for a dispute. The chrome is not part of
   the evidence.
   ============================================================================= */

@media print {
    .is-admin .admin-header,
    .is-admin .admin-rail,
    .is-admin .admin-tabs,
    .is-admin .admin-filters,
    .is-admin .admin-pager {
        display: none;
    }

    .is-admin .admin-shell {
        display: block;
    }

    .is-admin .data-table th {
        position: static;
    }
}
