/* Admin dashboard. Layout only — every colour comes from the theme files, so this
 * follows the site's colour schemes and day/night mode like everything else. */

/* Wider than the site's reading pages on purpose. This is a dashboard, not
   prose: six per-row actions plus the account columns need about 950px before
   something has to be clipped, and at the site's usual 900 the Delete button
   fell off the right-hand edge.

   The cap that actually binds is .site-wrapper, which is site-wide, so the
   modifier goes on this page's wrapper rather than on the shared rule. Every
   other page keeps its 900px measure. */
.site-wrapper.site-wrapper-wide {
    max-width: 1200px;
}

.admin-content {
    max-width: 1200px;
}

.admin-controls {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
    margin-bottom: 12px;
}

.admin-search {
    flex: 1 1 220px;
    min-width: 0;
    padding: 6px 8px;
    border: var(--border-light) solid 1px;
    border-radius: 5px;
    font-size: 0.95em;
    background-color: var(--bg-input);
    color: var(--text-primary);
}

.admin-select {
    padding: 6px 8px;
    border: var(--border-light) solid 1px;
    border-radius: 5px;
    font-size: 0.95em;
    background-color: var(--bg-input);
    color: var(--text-primary);
}

/* Sortable headers. The arrow is appended in JS so the label stays plain text
   for anyone reading the markup. */
.admin-sort {
    cursor: pointer;
    user-select: none;
}

.admin-sort:hover {
    text-decoration: underline;
}

.admin-sort.admin-sorted {
    background-color: var(--bg-secondary);
}

/* The table scrolls inside its own box rather than pushing the page sideways
   on a phone. */
.admin-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* THESE HAVE TO BE DECLARED TABLES AGAIN, AND THAT IS THE WHOLE BUG.
 *
 * assets/stylesheet.css sets, site-wide:
 *
 *     table { display: inline-block; }
 *     tr    { display: flex; flex-direction: row; }
 *
 * which is load-bearing elsewhere: the roller uses tables as flex rows and
 * those rules are why it lays out the way it does. They are not touched here.
 *
 * The consequence for a real table is that it stops being one. Every row
 * becomes an independent flex container, so a cell's width is decided by its
 * own contents and nothing at all connects it to the heading above it. That is
 * exactly what the admin tables were doing: measured on the users table, the
 * Email heading was 64px wide over a 195px cell, the columns totalled 913px
 * inside a 1112px table, and the rows stopped a long way short of the card.
 *
 * It also explains why the obvious fixes did nothing. `width: 100%` on a cell,
 * and later `table-layout: fixed`, are both instructions to a table layout
 * algorithm that was not running. The computed `display` was `inline-block`
 * the whole time.
 *
 * So: put the display values back, scoped to this page's tables only. Every
 * other table on the site keeps the global behaviour.
 *
 * `table-layout: fixed` on top of that: the first row decides the columns and
 * the body has no say, so a heading and its values share a column by
 * construction rather than by luck. The trade is that content no longer widens
 * a column, so anything that can be long has to say what to do instead — see
 * .admin-ellipsis below. Widths are per table, the three having different
 * columns. */
.admin-table {
    display: table;
    width: 100%;
    table-layout: fixed;
    border-collapse: collapse;
    font-size: 0.9em;
    color: var(--text-primary);
}

.admin-table thead { display: table-header-group; }
.admin-table tbody { display: table-row-group; }
.admin-table tr    { display: table-row; }

.admin-table th,
.admin-table td {
    display: table-cell;
}

/* One column per table is left `auto` and absorbs whatever is left over, which
   is how the table reaches the far edge of the card instead of stopping short.
   It is the address column in all three, that being the one where extra room
   is worth something. */

/* Users: ID, Email, Verified, Admin, Lists, Joined, Last login, Actions. */
/* Every column here is squeezed to the width its own heading needs and no
   more, so that column 2 keeps enough room to show a whole address. Adding
   Last login pushed the addresses into an ellipsis at the first attempt, and
   the address is the one value on the row you actually read. */
.admin-table-users th:nth-child(1) { width: 56px; }
.admin-table-users th:nth-child(2) { width: auto; }
.admin-table-users th:nth-child(3) { width: 76px; }
.admin-table-users th:nth-child(4) { width: 68px; }
.admin-table-users th:nth-child(5) { width: 56px; }
.admin-table-users th:nth-child(6) { width: 104px; }
.admin-table-users th:nth-child(7) { width: 112px; }
.admin-table-users th:nth-child(8) { width: 272px; }

/* Blocks: Blocked, Endpoint, Identifier, Tries, Until, Actions. */
.admin-table-blocks th:nth-child(1) { width: 110px; }
.admin-table-blocks th:nth-child(2) { width: 170px; }
.admin-table-blocks th:nth-child(3) { width: auto; }
.admin-table-blocks th:nth-child(4) { width: 70px; }
.admin-table-blocks th:nth-child(5) { width: 200px; }
.admin-table-blocks th:nth-child(6) { width: 100px; }

/* Purchases: When, Product, Email, Amount, Status, Account. */
.admin-table-purchases th:nth-child(1) { width: 125px; }
.admin-table-purchases th:nth-child(2) { width: 160px; }
.admin-table-purchases th:nth-child(3) { width: auto; }
.admin-table-purchases th:nth-child(4) { width: 100px; }
.admin-table-purchases th:nth-child(5) { width: 210px; }
.admin-table-purchases th:nth-child(6) { width: 230px; }

/* A fixed column cannot grow to fit its content, so a long address would spill
   over its neighbour. Clip it with an ellipsis instead; the full value is on
   the cell's title attribute for anyone who needs it. */
.admin-ellipsis {
    overflow: hidden;
    text-overflow: ellipsis;
}

.admin-table th,
.admin-table td {
    padding: 8px 10px;
    text-align: left;
    /* Every cell holds exactly one line, so centring lines them all up —
       including the action buttons, which are taller than a line of text.
       Top-aligning instead left the buttons hanging below everything else, and
       nothing in a cell is allowed to wrap or add a second line any more. */
    vertical-align: middle;
    border-bottom: 1px solid var(--border-light);
    white-space: nowrap;
}

/* Short values sit better centred than ranged left against a wide column. */
.admin-table th.admin-mid,
.admin-table td.admin-mid {
    text-align: center;
}

.admin-table th {
    color: var(--theme-primary);
    font-weight: 700;
    border-bottom: 2px solid var(--theme-primary);
}

/* Addresses are never wrapped. The wrapper scrolls horizontally, so a long
   address costs a little width rather than a second line — and a second line
   is what threw the row out of alignment in the first place. Every row is now
   one line tall, apart from a pending-change note, which hangs below. */
.admin-table td.admin-email {
    white-space: nowrap;
}

.admin-table td.admin-email .admin-you-tag {
    color: var(--text-secondary);
    font-weight: 400;
}

/* Sits on the same line as the address it replaces, so the row stays one line
   tall. The full meaning is in the title attribute. */
.admin-table td.admin-email .admin-pending {
    color: var(--text-secondary);
    font-style: italic;
}

.admin-table tr.admin-you td {
    background-color: var(--bg-secondary);
    font-weight: 700;
}

.admin-count {
    font-size: 0.7em;
    color: var(--text-secondary);
    font-weight: 400;
}

.admin-yes { color: var(--ok); font-weight: 700; }
.admin-no  { color: var(--text-secondary); }

/* State badges next to the address. Inline for the same reason .admin-pending
   is: the row stays one line tall and the table keeps its alignment. The two
   are deliberately different weights. Suspended is a decision somebody made
   and should catch the eye; locked is the rate limiter doing its ordinary job
   and clears itself, so it stays quiet. */
.admin-table td .admin-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 8px;
    font-size: 0.72em;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    vertical-align: middle;
    white-space: nowrap;
}

.admin-table td .admin-suspended {
    color: var(--text-on-accent);
    background-color: var(--theme-primary);
}

.admin-table td .admin-locked {
    color: var(--warn);
    background-color: var(--warn-bg);
    font-weight: 400;
}

.admin-btn {
    padding: 4px 10px;
    font-size: 0.85em;
    font-weight: 700;
    border-radius: 6px;
    border: 2px solid var(--border-light);
    background-color: var(--bg-button);
    color: var(--text-primary);
    cursor: pointer;
    margin-right: 4px;
}

.admin-btn:hover:not(:disabled) {
    background-color: var(--bg-secondary);
}

.admin-btn:disabled {
    opacity: 0.45;
    cursor: default;
}

.admin-btn.admin-danger {
    border-color: var(--theme-primary);
    color: var(--theme-primary);
}

/* Six actions do not fit one line, so they wrap inside a flex group rather
   than pushing the table sideways.

   The `width: 100%` that used to sit on the email cells is gone. It was there
   to make that column absorb the slack, it did not do that, and the column
   widths above do the job properly now: the actions column is declared 290px
   and the email column is the one left on auto. */
.admin-table td.admin-actions {
    white-space: normal;
}

.admin-action-group {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

/* Only the user rows carry six buttons and need the fixed width. The blocks
   table has one, and would otherwise reserve a 268px column for it. */
#admin-rows .admin-action-group {
    width: 250px;
}

/* The group's gap handles spacing now; the button's own right margin would
   add to it and break the alignment of the wrapped lines. */
.admin-action-group .admin-btn {
    margin-right: 0;
}

/* The blocks panel sits under the user table in the same card, so it needs a
   heading that separates the two without looking like a second page. */
.admin-subhead {
    margin: 28px 0 4px;
    font-size: 1.05em;
    color: var(--theme-primary);
}

.admin-subhead .admin-btn {
    margin-left: 10px;
    vertical-align: middle;
}

/* ── Purchases ───────────────────────────────────────────────────────
   The store panel under the blocks table. Nothing here has a colour of its
   own: every value is a the theme files custom property, so this follows day/night
   with the rest of the page. */

/* Paid. Green rather than the site red, because red on this page already means
   "somebody was stopped" and a completed sale is the opposite of that. */
.admin-table td .admin-ok {
    color: var(--text-on-accent);
    background-color: var(--ok);
}

/* The badges are written to sit after text elsewhere in the table, so they
   carry a left margin. In the status cell the first one starts the cell. */
.admin-table td .admin-badge:first-child {
    margin-left: 0;
}

/* Paid, but no account has linked it. That person does not have what they
   bought, so the row should not read as a completed sale at a glance. */
.admin-table td.admin-unclaimed {
    color: var(--text-secondary);
    font-style: italic;
}

/* Summary tiles above the purchase table. Auto-fit rather than a fixed column
   count: the number of tiles changes with the data (an unpaid tile only
   appears when there are unpaid rows), and a fixed grid would leave a hole. */
.admin-summary {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 10px;
    margin: 12px 0 16px;
}

.admin-tile {
    padding: 12px 14px;
    border: 1px solid var(--border-light);
    border-radius: 10px;
    background-color: var(--bg-secondary);
}

.admin-tile-value {
    font-size: 1.5em;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-primary);
}

.admin-tile-label {
    margin-top: 2px;
    font-size: 0.85em;
    font-weight: 700;
    color: var(--text-primary);
}

.admin-tile-note {
    margin-top: 2px;
    font-size: 0.78em;
    font-style: italic;
    color: var(--text-secondary);
}

/* ── Pagination ──────────────────────────────────────────────────────
   The user table renders every account at once. That was fine at one account
   and is not a plan: the row builder does real DOM work per row, and the
   sideways scroll it produces hides the controls above it. */
.admin-pager {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 12px;
}

.admin-pager-status {
    font-size: 0.85em;
    color: var(--text-secondary);
}

/* Pushes the page-size control to the far end, so the buttons stay together
   on the left where the eye already is after reading the table. */
.admin-pager-size {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.85em;
    color: var(--text-secondary);
}

/* ── Per-user detail ─────────────────────────────────────────────────
   Opens as a row underneath the account it belongs to, rather than a modal or
   a separate page. Keeping it in place means the table above it does not move
   and you can open two and compare them. */
.admin-table tr.admin-row-open > td {
    border-bottom: none;
}

.admin-detail-cell {
    padding: 0 !important;
    background-color: var(--bg-secondary);
}

.admin-detail {
    padding: 16px 18px 18px;
    border-left: 3px solid var(--theme-primary);
}

.admin-detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 18px;
}

.admin-detail-block h4 {
    /* Real heading: opts out of the inline h-tag reset in diceroller.css. */
    display: block;
    margin: 0 0 6px;
    font-size: 0.95em;
    color: var(--theme-primary);
}

.admin-detail-empty {
    font-size: 0.85em;
    font-style: italic;
    color: var(--text-secondary);
}

/* One line per list / session / purchase / block. Deliberately not a nested
   table: a table inside a table inherits the outer one's column widths and
   fights every layout rule above it. */
.admin-detail-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.admin-detail-list li {
    padding: 5px 0;
    border-bottom: 1px solid var(--border-light);
    font-size: 0.88em;
    color: var(--text-primary);
}

.admin-detail-list li:last-child {
    border-bottom: none;
}

.admin-detail-sub {
    display: block;
    font-size: 0.85em;
    color: var(--text-secondary);
}

/* The expand control in the ID column. A button rather than a click handler on
   the row: rows carry six buttons already, and a row-wide handler means every
   miss of a button toggles the panel. */
.admin-expand {
    padding: 0 6px;
    margin-right: 4px;
    border: none;
    background: none;
    cursor: pointer;
    font-weight: 700;
    font-size: 0.9em;
    color: var(--theme-primary);
}

/* ── Online indicator ────────────────────────────────────────────────
   A dot rather than another badge. The row already carries "suspended" and
   "locked" badges next to the address, and a third pill in the same style
   would read as a third kind of problem instead of the one piece of good news
   on the row. */
.admin-online-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    margin-right: 6px;
    border-radius: 50%;
    background-color: var(--ok);
    vertical-align: middle;
}

/* Colour is not the only signal: the word "online" is there in text, so this
   still reads for anyone who cannot tell the dot from a bullet. */
.admin-online {
    color: var(--ok);
    font-weight: 700;
}

/* ── Signup chart ────────────────────────────────────────────────────
   Thirty divs with a height. No chart library: the site is vanilla by rule and
   this does not need a dependency. */
.admin-chart {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 120px;
    margin: 10px 0 6px;
    padding: 8px 10px;
    border: 1px solid var(--border-light);
    border-radius: 10px;
    background-color: var(--bg-secondary);
}

.admin-bar-col {
    flex: 1 1 0;
    height: 100%;
    display: flex;
    align-items: flex-end;
    min-width: 0;
}

.admin-bar {
    width: 100%;
    border-radius: 3px 3px 0 0;
    background-color: var(--theme-primary);
}

/* A day with nothing still draws a sliver, in the muted colour, so the row
   reads as thirty days rather than as a gap where the data stops. */
.admin-bar-empty {
    background-color: var(--border-light);
}

.admin-bar-col:hover .admin-bar {
    background-color: var(--theme-primary-light);
}
