/* ============================================================================
   Console — the operator surface.

   Layer 3 semantic names only. If a primitive is wanted here, the semantic
   layer is missing a name; add it to tokens.css instead. Dark mode gets no
   rules in this file at all — the ramps beneath these names already flipped.

   Logical properties throughout. test_css_contract.py fails the build on a
   physical property that cannot mirror, and the five shipped stylesheets are
   at zero violations. This is the sixth and stays there.

   Restraint is the brief. This is the same instrument as the reader surface,
   seen from behind: porcelain ground, hairlines, one teal. An operator console
   that looks like a different product would be a different product.
   ========================================================================= */

.admin-body {
  min-block-size: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-page);
  color: var(--fg-primary);
  font-family: var(--font-sans);
}

/* Visible only on focus. A console is a keyboard surface before it is a mouse
   one, and the tablist is several stops in. */
.skip-link {
  position: absolute;
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
  z-index: var(--z-toast);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--fg-primary);
  box-shadow: var(--shadow-md);
  transform: translateY(-200%);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

/* ── Top bar ─────────────────────────────────────────────────────────────── */

.admin-bar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-3) var(--gutter);
  background: var(--bg-surface);
  border-block-end: 1px solid var(--hairline);
}

.admin-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  text-decoration: none;
  color: var(--fg-primary);
  font-weight: var(--fw-display);
}

.admin-brand .icon {
  color: var(--signal);
}

.admin-brand-sep,
.admin-brand-role {
  color: var(--fg-muted);
  font-weight: var(--fw-body);
}

.admin-bar-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-inline-start: auto;
}

/* An email address is a machine-reported fact, so it takes the mono face and
   is isolated LTR — the element also carries dir="ltr", because bidi reorders
   an address dropped into an Arabic bar. */
.admin-whoami {
  font-family: var(--font-mono);
  font-size: var(--fs-200);
  color: var(--fg-secondary);
  unicode-bidi: isolate;
}

/* ── Main ────────────────────────────────────────────────────────────────── */

.admin-main {
  flex: 1 1 auto;
  inline-size: 100%;
  max-inline-size: var(--page-max);
  margin-inline: auto;
  padding: var(--space-6) var(--gutter);
}

.admin-gate {
  display: flex;
  align-items: center;
  justify-content: center;
  min-block-size: 40vh;
}

.admin-gate-text {
  margin: 0;
  color: var(--fg-secondary);
  text-align: center;
  max-inline-size: 48ch;
}

/* ── Tabs ────────────────────────────────────────────────────────────────── */

.admin-tabs {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
  border-block-end: 1px solid var(--hairline);
  margin-block-end: var(--space-5);
}

.admin-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border: 0;
  background: none;
  color: var(--fg-secondary);
  font: inherit;
  font-weight: var(--fw-medium);
  cursor: pointer;
  /* The 2px weight is the system's one meaningful rule, and an active tab is
     exactly what it is reserved for. Transparent at rest so selecting a tab
     shifts nothing. */
  border-block-end: 2px solid transparent;
  margin-block-end: -1px;
}

.admin-tab .icon {
  color: var(--fg-muted);
}

.admin-tab:hover {
  color: var(--fg-primary);
}

.admin-tab[aria-selected="true"] {
  color: var(--fg-primary);
  border-block-end-color: var(--signal);
}

.admin-tab[aria-selected="true"] .icon {
  color: var(--signal);
}

/* ── Panels ──────────────────────────────────────────────────────────────── */

.admin-heading {
  font-family: var(--font-display);
  font-size: var(--fs-500);
  font-weight: var(--fw-display);
  margin-block: 0 var(--space-4);
}

.admin-panel-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.admin-empty {
  margin: 0;
  padding: var(--space-6);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  color: var(--fg-muted);
  text-align: center;
}

/* ── Forms ───────────────────────────────────────────────────────────────── */

.admin-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  max-inline-size: 34rem;
}

.admin-form-hint {
  margin: 0;
  color: var(--fg-muted);
  font-size: var(--fs-200);
  max-inline-size: 60ch;
}

/* The form describes what is stored; this line is for when that is not what is
   answering. Warning rather than danger: nothing is broken, the change simply
   has not reached generation yet. */
.admin-form-hint.is-warning {
  color: var(--warning);
  font-weight: var(--fw-medium);
}

.admin-field {
  display: grid;
  /* Label and origin marker share a row; the control spans beneath both, so a
     long Arabic label never squeezes the input. */
  grid-template-columns: 1fr auto;
  gap: var(--space-1) var(--space-3);
  align-items: baseline;
  padding-block-end: var(--space-4);
  border-block-end: 1px solid var(--hairline);
}

.admin-field-label {
  font-weight: var(--fw-medium);
  margin: 0;
}

.admin-input {
  grid-column: 1 / -1;
  /* background-COLOR, not the shorthand: `.form-select` carries its chevron as
     a background-image, and the shorthand would clear it — leaving a select
     that looks exactly like a text input and gives no sign it can be opened. */
  background-color: var(--bg-surface);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--fg-primary);
  padding: var(--space-2) var(--space-3);
  font: inherit;
}

/* Machine-reported values take the mono face with tabular figures, so a digit
   does not shift as it changes. */
.admin-input[type="number"] {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* Room for the chevron. The rule above sets padding symmetrically, which would
   otherwise run a long model name underneath it. */
select.admin-input {
  padding-inline-end: var(--space-7);
}

/* Bootstrap pins the chevron with a physical `right` and we load the LTR
   build, so under RTL it stays on the right — over the text, which is now
   right-aligned. Moved to the other side, and the padding with it.
   background-position takes no logical keywords, so this is the mirror. */
[dir="rtl"] select.admin-input {
  background-position: left var(--space-3) center; /* physical-ok: background-position has no logical form; this IS the RTL branch */
  padding-inline-end: var(--space-3);
  padding-inline-start: var(--space-7);
}

.admin-input:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px var(--focus-ring-shadow);
}

/* Whether this value was chosen or merely inherited. Quiet by default, because
   most settings are defaults and a page of badges says nothing. */
.admin-field-origin {
  font-size: var(--fs-100);
  color: var(--fg-faint);
  text-align: end;
}

.admin-field-origin.is-override {
  color: var(--signal);
}

.admin-field-error {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--fs-200);
  color: var(--danger);
}

.admin-field.has-error .admin-input {
  border-color: var(--danger);
}

.admin-form-actions {
  display: flex;
  gap: var(--space-3);
}

/* ── Tables ──────────────────────────────────────────────────────────────── */

/* Wide content scrolls inside its own container. The page body must never
   scroll sideways — a horizontal scrollbar on the document is how a table
   takes the whole layout with it on a narrow screen. */
.admin-panel-body:has(.admin-table) {
  overflow-x: auto;
}

.admin-table {
  inline-size: 100%;
  border-collapse: collapse;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  font-size: var(--fs-200);
}

.admin-table th,
.admin-table td {
  /* `start`, not `left`: this table carries emails and timestamps and has to
     mirror wholesale under RTL. */
  text-align: start;
  padding: var(--space-3) var(--space-4);
  border-block-end: 1px solid var(--hairline);
  vertical-align: top;
}

.admin-table thead th {
  background: var(--bg-sunken);
  color: var(--fg-secondary);
  font-weight: var(--fw-medium);
  font-size: var(--fs-100);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
}

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

/* Machine-reported values: timestamps, addresses, the diff. Mono with tabular
   figures so digits do not jitter between rows, and each cell carries its own
   dir="ltr" because bidi reorders an address or a date dropped into Arabic. */
.admin-cell-machine {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--fg-secondary);
  white-space: pre-line;
  unicode-bidi: isolate;
}

/* `dir="ltr"` sets the direction of the BOX, not only of the text inside it —
   and `text-align: start` resolves against the box's own direction. So on an
   Arabic page every machine cell hugged the LEFT edge of its column while its
   own column heading, and every neighbouring cell, hugged the right: the two
   readings of one fact ended up on opposite sides.

   The isolate has to stay — it is what stops an address or a stamp being
   reordered — so the alignment is restored explicitly instead. This is a
   direction branch, which this system otherwise avoids, but the alternatives
   are worse: `inline-size: fit-content` does not shrink-wrap a table cell, and
   moving the isolate to an inner element would change the DOM that
   `test_admin_browser.py` and the confirmation copy in `handlers.js` both read.

   `end`, never `right` — test_css_contract.py fails the build on the physical
   form. On an inline `.admin-cell-machine` (the account heading's address) this
   is inert, because text-align applies to block containers. */
[dir="rtl"] .admin-cell-machine,
[dir="rtl"] .admin-fact-exact {
  text-align: end;
}

/* ── Footer ──────────────────────────────────────────────────────────────── */

.admin-footer {
  padding: var(--space-5) var(--gutter);
  border-block-start: 1px solid var(--hairline);
}

.admin-independence {
  margin: 0 auto;
  max-inline-size: 70ch;
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--bg-sunken);
  color: var(--fg-secondary);
  font-size: var(--fs-200);
  text-align: center;
}

/* ── Row actions ─────────────────────────────────────────────────────────── */

.admin-row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.admin-row-action:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* The design system has no danger button variant — logout, the one genuinely
   irreversible control in the reader app, is a quiet ghost. A destructive
   action here stays a ghost too and takes the alert ramp only on hover, so a
   table of accounts does not read as a wall of warnings. The weight is carried
   by the confirmation and the record. */
.admin-row-action.is-destructive:hover:not(:disabled) {
  color: var(--danger);
  border-color: var(--danger);
}

/* "you", beside your own address. Not a badge: it is an aside, not a status. */
.admin-you {
  color: var(--fg-faint);
  font-family: var(--font-sans);
}

/* A withdrawn permission is a state, not an error — the alert ramp would make
   a deliberate decision look like a fault in the table. */
.admin-flag-off {
  color: var(--fg-muted);
  font-weight: var(--fw-medium);
}

/* Reverting is staged, not immediate — the form submits whole. The control sits
   with the field it belongs to rather than in the actions row, because it acts
   on one value and Save acts on all of them. */
.admin-field-revert {
  grid-column: 1 / -1;
  justify-self: start;
  padding-inline: 0;
  font-size: var(--fs-100);
  color: var(--fg-muted);
  border: 0;
  background: none;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.admin-field-revert:hover {
  color: var(--signal);
}

/* A stated reason, under the state it explains. Quiet: it is context for the
   row, not a second column competing with it. */
.admin-cell-note {
  display: inline-block;
  margin-block-start: var(--space-1);
  color: var(--fg-muted);
  font-family: var(--font-sans);
  font-size: var(--fs-100);
  max-inline-size: 28ch;
}

/* ── The users list ──────────────────────────────────────────────────────────
   The corridor to the account page, and only that. Role and access controls
   used to sit on every row AND on the detail; TODO.md asks for one home for
   everything done to an account, and a control in two places is two places to
   keep true. What is left here is a list you read, with one way in. */

/* Used by the search field's label and the profile form's. It was referenced in
   admin.html and defined in no stylesheet, which is why "Search by email"
   rendered as unstyled body text. */
.admin-label {
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--fg-muted);
}

.admin-people-search {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-block-end: var(--space-5);
  max-inline-size: 22rem;
}

/* A whole row opens its account. The button stays because a keyboard and a
   screen reader need a real control to land on, but a five-column row where
   only the first cell responds is a target the eye has to aim at. */
.admin-table tbody tr[data-user-id] {
  cursor: pointer;
}

.admin-table tbody tr[data-user-id]:hover {
  background: var(--bg-sunken);
}

/* The way in has to read as a control while staying a machine-reported fact:
   the mono face and LTR isolation of an address, in the colour that means
   "actionable" everywhere else in this system. */
.admin-account-open {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--signal);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  cursor: pointer;
}

.admin-account-open:hover {
  color: var(--signal-hover);
}

/* A chevron in the last column, pointing the way the row goes. Decorative — the
   row and the address are both already controls — so it is aria-hidden and
   takes the faint ink until the row is under the cursor. */
.admin-row-go {
  inline-size: 1px;
  color: var(--fg-faint);
  text-align: end;
}

.admin-row-go .icon {
  transform: scaleX(var(--flip));
}

.admin-table tbody tr[data-user-id]:hover .admin-row-go {
  color: var(--signal);
}

/* ── Admin People Pager ─────────────────────────────────────────────────── */

.admin-table-wrapper {
  position: relative;
  inline-size: 100%;
}

/* Visual busy state is added only after the 100ms threshold — see above. */
.admin-table.is-busy-visual tbody {
  opacity: 0.45;
  transition: opacity var(--duration-s) var(--ease-soft);
}

.admin-table tbody {
  transition: opacity var(--duration-s) var(--ease-soft);
}

.admin-table-wrapper.is-busy-visual::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 2px;
  background: var(--signal);
  border-start-start-radius: var(--radius-md);
  border-start-end-radius: var(--radius-md);
  animation: adminPagerPulse 1.2s var(--ease-soft) infinite;
  transform-origin: center; /* direction-neutral: symmetric in both LTR and RTL */
}

@keyframes adminPagerPulse {
  0% { transform: scaleX(0.1); opacity: 0.4; }
  50% { transform: scaleX(0.8); opacity: 1; }
  100% { transform: scaleX(1); opacity: 0; }
}

.admin-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-4);
  margin-block-start: var(--space-4);
  padding-block: var(--space-2);
}

.admin-pager-size {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex: 0 0 auto;
}

.admin-pager-size-label {
  font-family: var(--font-sans);
  font-size: var(--fs-200);
  color: var(--fg-muted);
  font-weight: var(--fw-body);
  white-space: nowrap;
}

.admin-pager-select {
  min-block-size: 32px;
  padding: var(--space-1) var(--space-3);
  padding-inline-end: var(--space-7);
  font-family: var(--font-mono);
  font-size: var(--fs-200);
  font-variant-numeric: tabular-nums;
  color: var(--fg-primary);
  background-color: var(--bg-surface);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: var(--btn-transition);
}

.admin-pager-select:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px var(--focus-ring-shadow);
}

.admin-pager-nav {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  flex: 0 0 auto;
  /* No margin-inline-start: auto here — an audit pass caught that this was a
     leftover from an earlier draft where the size group was appended first.
     Now that navGroup is the first child (DOM order corrected above), an auto
     margin on it would consume all the flex free space and push both groups
     to the same end, overriding .admin-pager's own `justify-content:
     space-between` below. The parent's space-between already places navGroup
     at inline-start and sizeGroup at inline-end without any margin here. */
}

.admin-pager-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--fs-200);
  color: var(--fg-secondary);
  white-space: nowrap;
}

.admin-pager-status .admin-cell-machine {
  font-family: var(--font-mono);
  font-size: var(--fs-200);
  font-variant-numeric: tabular-nums;
  color: var(--fg-primary);
  font-weight: var(--fw-medium);
}

.admin-pager-status:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 4px;
  border-radius: var(--radius-xs);
}

.admin-pager-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-block-size: 32px;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: var(--fs-200);
  font-weight: var(--fw-medium);
  color: var(--fg-secondary);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: var(--btn-transition);
}

.admin-pager-btn .icon {
  color: var(--fg-muted);
  transition: color var(--duration-s) var(--ease-soft),
              transform var(--duration-s) var(--ease-soft);
}

.admin-pager-btn:hover:not(:disabled) {
  background: var(--bg-sunken);
  border-color: var(--fg-faint);
  color: var(--fg-primary);
  transform: translateY(-1px);
}

.admin-pager-btn:hover:not(:disabled) .icon {
  color: var(--fg-primary);
}

.admin-pager-btn:active:not(:disabled) {
  transform: translateY(0);
}

.admin-pager-btn:focus-visible {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px var(--focus-ring-shadow);
  background: var(--bg-surface);
  color: var(--fg-primary);
}

.admin-pager-btn:disabled {
  opacity: 0.45;
  border-color: var(--hairline);
  color: var(--fg-faint);
  cursor: not-allowed;
  pointer-events: none;
  transform: none;
}

.admin-pager-btn:disabled .icon {
  color: var(--fg-faint);
}

.admin-pager-btn--prev .icon {
  transform: scaleX(calc(-1 * var(--flip)));
}

.admin-pager-btn--next .icon {
  transform: scaleX(calc(1 * var(--flip)));
}

.admin-pager-btn--prev:hover:not(:disabled) .icon {
  transform: scaleX(calc(-1 * var(--flip))) translateX(calc(-2px * var(--flip)));
}

.admin-pager-btn--next:hover:not(:disabled) .icon {
  transform: scaleX(calc(1 * var(--flip))) translateX(calc(2px * var(--flip)));
}

@media (max-width: 640px) {
  .admin-pager {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3);
  }

  .admin-pager-nav {
    inline-size: 100%;
    justify-content: space-between;
    margin-inline-start: 0;
  }

  .admin-pager-status {
    order: -1;
    flex-basis: 100%;
    inline-size: auto;
    justify-content: center;
    margin-block-end: var(--space-1);
  }

  .admin-pager-size {
    justify-content: center;
    inline-size: 100%;
  }
}

/* ── Account detail ──────────────────────────────────────────────────────────
   Four zones in increasing severity: who this is, what they told us about
   themselves, what can be done to them, and what has been done.

   It was a flat run of <dl> rows before, every fact at one weight — and two of
   the colours it asked for, --ink-secondary and --accent-ink, are not tokens in
   this system at all, so each label resolved to the same ink as its value and
   the page read as one paragraph. Same information, given a shape. */

.admin-detail {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.admin-backlink {
  align-self: start;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0;
  border: 0;
  background: none;
  color: var(--fg-secondary);
  font: inherit;
  font-size: var(--fs-200);
  font-weight: var(--fw-medium);
  cursor: pointer;
}

.admin-backlink:hover,
.admin-backlink:hover .icon {
  color: var(--signal);
}

/* chevron-right is the only chevron in the runtime icon subset, so "back" is it
   turned around. scaleX(-1 × --flip) points it against the reading direction in
   both scripts: -1 in LTR, +1 in RTL. transform has no logical form, and this
   is the documented escape hatch for that. */
.admin-backlink .icon {
  color: var(--fg-faint);
  transform: scaleX(calc(-1 * var(--flip)));
}

/* ── Zone 0: the account as an object ────────────────────────────────────── */

.admin-account-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3) var(--space-5);
  padding: var(--space-5);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
}

/* A withdrawn permission is a state, not an error, and this system has no
   danger variant by design — so a disabled account recedes onto the sunken
   fill rather than turning red. A whole card stepping back reads harder across
   a page than a coloured pill does, and it does not cry fault at a decision
   somebody made deliberately. */
.admin-account-head.is-off {
  background: var(--bg-sunken);
}

.admin-account-id {
  flex: 1 1 18rem;
  min-inline-size: 0;
}

/* The address leads, because it is what the operator searched for. Set as the
   machine-reported fact it is rather than in the display face — which would
   never have applied to it anyway, since the heading has always wrapped a mono
   span. */
.admin-account-address {
  margin: 0;
  font-size: var(--fs-400);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  overflow-wrap: anywhere;
}

.admin-account-address .admin-cell-machine {
  color: var(--fg-primary);
  font-size: inherit;
  font-weight: inherit;
}

.admin-account-address:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 4px;
  border-radius: var(--radius-xs);
}

/* The person under the address. An operator searching by email still has to
   work out who they are looking at, and this is the answer in one line. */
.admin-account-person {
  margin: var(--space-2) 0 0;
  color: var(--fg-secondary);
  font-size: var(--fs-200);
}

.admin-marks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.admin-mark {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  color: var(--fg-secondary);
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  white-space: nowrap;
}

/* The citation marker's resting treatment at control size, the same one
   `.new-chat-btn` borrows on the reader surface. Administrator is the one
   standing on this page that changes what somebody can do to everybody else,
   so it is the one that earns the signal. */
.admin-mark.is-signal {
  border-color: transparent;
  background: var(--signal-tint);
  color: var(--signal);
}

.admin-mark.is-off {
  background: var(--bg-sunken);
  color: var(--fg-muted);
}

/* Who withdrew access, when, and why — one sentence across the foot of the
   card. It was three more rows in the wall of definitions, which is where a
   reason nobody reads goes to be ignored. */
/* The rule spans the card; the sentence is held to a measure inside it. Both on
   one element meant `max-inline-size` cropped the border too, and a divider
   that stops halfway across a card reads as a mistake rather than a boundary. */
.admin-account-note {
  flex-basis: 100%;
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--hairline);
}

.admin-account-note p {
  margin: 0;
  max-inline-size: 70ch;
  color: var(--fg-secondary);
  font-size: var(--fs-200);
}

.admin-account-reason {
  color: var(--fg-primary);
}

/* ── Zones ───────────────────────────────────────────────────────────────── */

.admin-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.admin-section-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* The heading IS the label: a small tracked cap naming the zone, with a
   hairline running out to the edge so four zones down one column stay four
   zones. Not an eyebrow — there is no second heading under it. */
.admin-subheading {
  margin: 0;
  flex: 0 0 auto;
  font-family: var(--font-sans);
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--fg-muted);
}

.admin-section-head::after {
  content: "";
  flex: 1 1 auto;
  block-size: 1px;
  background: var(--hairline);
}

.admin-card {
  padding: var(--space-5);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
}

/* ── Zone 1: the readout ─────────────────────────────────────────────────── */

/* Each fact is a cell, not a row: the label small and tracked above it, the
   human reading of the time in ink, and the exact stamp beneath in mono.
   Both, deliberately — an operator wants "yesterday" to decide and
   "2026-08-14 04:13:28" to defend the decision afterwards. It is the split
   this product already makes between an answer and its citation. */
.admin-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-5) var(--space-4);
  margin: 0;
}

.admin-fact {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-inline-size: 0;
}

/* --fg-muted, not --fg-faint. Faint ink measures about 4.0:1 on the card white,
   which is under the 4.5:1 floor for text this size — and every word in this
   zone is content, not chrome. The step down from the value is carried by size,
   tracking and caps instead of by a colour nobody can read. */
.admin-fact dt {
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--fg-muted);
}

.admin-fact dd {
  margin: 0;
  color: var(--fg-primary);
  overflow-wrap: anywhere;
}

.admin-fact-exact {
  display: block;
  margin-block-start: var(--space-1);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-100);
  color: var(--fg-muted);
  unicode-bidi: isolate;
}

/* ── Zone 2: the profile ─────────────────────────────────────────────────── */

.admin-profile-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-4);
}

.admin-profile-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.admin-profile-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-block-start: var(--space-5);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--hairline);
}

/* ── Zone 3: what can be done ────────────────────────────────────────────── */

.admin-account-hint {
  margin: var(--space-4) 0 0;
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--hairline);
  color: var(--fg-secondary);
  font-size: var(--fs-200);
  max-inline-size: 70ch;
}

.admin-absent {
  margin-block-start: var(--space-4);
}

.admin-absent-title {
  display: block;
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--fg-muted);
}

.admin-absent-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
  max-inline-size: 70ch;
}

.admin-absent-list li {
  padding-inline-start: var(--space-4);
  border-inline-start: 1px solid var(--hairline-strong);
  color: var(--fg-muted);
  font-size: var(--fs-200);
}

/* A stated problem, not an alarm. The system has no danger variant by design,
   so this carries its weight through the sunken fill and the words. */
.admin-notice {
  padding: var(--space-4);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-md);
  background: var(--bg-sunken);
  max-inline-size: 70ch;
}

.admin-notice strong {
  display: block;
  margin-block-end: var(--space-2);
  color: var(--fg-primary);
}

.admin-notice p {
  margin: 0;
  color: var(--fg-secondary);
  font-size: var(--fs-200);
}

/* ── Zone 4: what has been done ──────────────────────────────────────────── */

/* Wide content scrolls inside its own container, never the page. */
.admin-scroll {
  overflow-x: auto;
}
