/* =====================================================================
   3270Web — accounts layer
   ---------------------------------------------------------------------
   Sign-in, first-run setup and account administration.

   Loaded after style.css and ui-polish.css. Every colour here comes from
   the per-theme custom properties those files define (--bg, --panel,
   --panel-2, --border, --fg, --fg-muted, --accent, --accent-2), so the
   built-in themes and user-authored ones pick these screens up with no
   per-theme work. There are no literal colours below except where a
   translucent tint is derived from a token with color-mix.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Centred single-purpose pages (sign in, setup, password change)
   --------------------------------------------------------------------- */

.auth-wrap {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 32px 20px;
  position: relative;
  z-index: 1;
}

.auth-card {
  width: 100%;
  max-width: 460px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg, 16px);
  box-shadow: var(--elev-4, var(--shadow));
  padding: 32px;
  position: relative;
  overflow: hidden;
}

/* A hairline of accent along the top edge. The card is otherwise very
   plain, and this is what ties it to the terminal chrome elsewhere. */
.auth-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg,
      transparent,
      var(--accent) 22%,
      var(--accent-2, var(--accent)) 62%,
      transparent);
  opacity: 0.85;
}

.auth-card--wide {
  max-width: 560px;
}

.auth-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 6px;
}

.auth-head .brand-mark {
  flex: 0 0 auto;
}

.auth-title {
  margin: 0;
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.2;
}

.auth-sub {
  margin: 0 0 24px;
  color: var(--fg-muted);
  font-size: 0.9rem;
  line-height: 1.5;
}

/* ---------------------------------------------------------------------
   2. Forms
   --------------------------------------------------------------------- */

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* style.css styles `label` alongside the controls (`label, select, button,
   input`), giving it a panel background, border and padding. That is right
   for the toolbar's inline labels and wrong for a stacked form, where it
   makes every caption look like an empty input above the real one. Reset it
   here rather than narrowing the base rule, which the terminal chrome
   depends on. */
.field > label {
  font-size: 0.72rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-muted);
  font-weight: 600;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}

.field > input,
.field > select {
  width: 100%;
  font-family: var(--mono);
  font-size: 0.95rem;
  padding: 11px 12px;
  color: var(--fg);
  background: color-mix(in srgb, var(--bg) 55%, var(--panel));
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 8px);
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

.field > input:hover:not(:disabled),
.field > select:hover:not(:disabled) {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
}

.field > input:focus,
.field > select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* The same rule the admin dialogs further down this file already apply, for
   the same reason and on the pages people actually meet first.
   0.95rem is about 15px, and below 16px iOS Safari zooms the page in when a
   field takes focus and does not zoom back out. On a sign-in form that means
   tapping Username scrolls Password and the Sign in button off the side of the
   screen, and the first thing somebody does with 3270Web on a phone is pinch
   their way back to a button they can no longer see. Setup and the forced
   password change are the same form and had the same problem.

   16px rather than 1rem: the threshold is the browser's and is measured in
   pixels, so a root font-size the operator has changed must not move it. */
@media (pointer: coarse) {
  .field > input,
  .field > select {
    font-size: 16px;
    min-height: 44px;
    touch-action: manipulation;
  }

  .field > input.code-input {
    /* Keeps its larger, proportional size — the spacing is what makes a setup
       code readable in groups — with the same floor underneath it. */
    font-size: max(16px, 1.05rem);
  }

  .auth-submit {
    min-height: 44px;
    touch-action: manipulation;
  }
}

.field-hint {
  font-size: 0.78rem;
  color: var(--fg-muted);
  line-height: 1.45;
}

/* The setup code is transcribed from a log, so it gets room to breathe and
   wide tracking that makes character-by-character comparison easy. */
.field > input.code-input {
  letter-spacing: 0.32em;
  text-transform: uppercase;
  font-size: 1.05rem;
  text-align: center;
  padding: 13px 12px;
}

.auth-submit {
  width: 100%;
  margin-top: 4px;
  padding: 12px 16px;
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--bg);
  background: linear-gradient(135deg, var(--accent), var(--accent-2, var(--accent)));
  border: 1px solid transparent;
  border-radius: var(--r-sm, 8px);
  cursor: pointer;
  transition: filter 120ms ease, transform 60ms ease;
}

.auth-submit:hover:not(:disabled) {
  filter: brightness(1.1);
}

.auth-submit:active:not(:disabled) {
  transform: translateY(1px);
}

.auth-submit:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* The identity-provider button is a link rather than a submit, since it
   navigates away instead of posting — which is what a screen reader should
   announce it as. Styled as the primary action because on an instance that has
   SSO it is the way in for everybody except the one local account kept for
   when the provider cannot be reached.
 *
 * Written as .auth-card .auth-sso, and restating colour and font-style that
 * .auth-submit already sets, because a theme may style every anchor: one ships
 * links in italic gold, and `body.theme-x a` outranks a single class. Being a
 * link is what makes this control need the extra weight. */
.auth-card .auth-sso {
  display: block;
  box-sizing: border-box;
  margin-bottom: 4px;
  padding: 11px 14px;
  color: var(--bg);
  font-style: normal;
  font-size: 0.86rem;
  letter-spacing: 0.02em;
  line-height: 1.35;
  text-align: center;
  text-decoration: none;
}

/* A labelled rule, so the password form below reads as the alternative rather
   than as a second thing to fill in. */
.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 4px;
  color: var(--fg-dim, #8a8a8a);
  font-size: 0.8rem;
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border, rgba(128, 128, 128, 0.35));
}

/* ---------------------------------------------------------------------
   3. Messages
   --------------------------------------------------------------------- */

.auth-alert {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 11px 13px;
  margin-bottom: 18px;
  border-radius: var(--r-sm, 8px);
  font-size: 0.88rem;
  line-height: 1.45;
  border: 1px solid;
}

.auth-alert::before {
  font-weight: 700;
  line-height: 1.35;
}

.auth-alert--error {
  color: var(--term-color-red, #ff6b6b);
  border-color: color-mix(in srgb, var(--term-color-red, #ff6b6b) 45%, var(--border));
  background: color-mix(in srgb, var(--term-color-red, #ff6b6b) 12%, var(--panel));
}

.auth-alert--error::before {
  content: "!";
}

.auth-alert--info {
  color: var(--fg);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
  background: color-mix(in srgb, var(--accent) 10%, var(--panel));
}

.auth-alert--warn {
  color: var(--fg);
  border-color: color-mix(in srgb, var(--term-color-yellow, #ffd166) 42%, var(--border));
  background: color-mix(in srgb, var(--term-color-yellow, #ffd166) 10%, var(--panel));
}

.auth-alert--warn::before {
  content: "\26A0";
}

.auth-note {
  margin: 20px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 0.8rem;
  color: var(--fg-muted);
  line-height: 1.55;
}

.auth-note code {
  font-family: var(--mono);
  background: color-mix(in srgb, var(--bg) 60%, var(--panel));
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.95em;
  word-break: break-all;
}

/* ---------------------------------------------------------------------
   4. Password strength meter
   --------------------------------------------------------------------- */

.pw-meter {
  display: flex;
  gap: 4px;
  margin-top: 2px;
}

.pw-meter span {
  flex: 1;
  height: 3px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--fg-muted) 25%, transparent);
  transition: background 160ms ease;
}

.pw-meter[data-score="1"] span:nth-child(-n + 1),
.pw-meter[data-score="2"] span:nth-child(-n + 2),
.pw-meter[data-score="3"] span:nth-child(-n + 3),
.pw-meter[data-score="4"] span:nth-child(-n + 4) {
  background: var(--accent);
}

.pw-meter[data-score="1"] span:nth-child(-n + 1) {
  background: var(--term-color-red, #ff6b6b);
}

.pw-meter[data-score="2"] span:nth-child(-n + 2) {
  background: var(--term-color-yellow, #ffd166);
}

.pw-status {
  font-size: 0.76rem;
  color: var(--fg-muted);
  min-height: 1.1em;
}

.pw-status[data-state="ok"] {
  color: var(--accent);
}

.pw-status[data-state="bad"] {
  color: var(--term-color-red, #ff6b6b);
}

/* ---------------------------------------------------------------------
   4b. Generate / reveal / copy

   An administrator setting somebody else's temporary password has to invent
   one and then read it out. Left to themselves people reuse a house password
   across every account they create, so the button that invents a good one has
   to be right there — and useless unless the result can also be seen and
   copied, which is why the three ship together.
   --------------------------------------------------------------------- */

.pw-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}

.pw-row input {
  flex: 1 1 auto;
  min-width: 0;
}

.pw-tools {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 8px;
}

/* 44px is the smallest target most people hit reliably with a thumb, and these
   sit next to each other where a mis-hit reveals a password instead of
   generating one. */
.pw-tool {
  min-height: 44px;
  padding: 0 14px;
  font-size: 0.8rem;
  white-space: nowrap;
  touch-action: manipulation;
}

.pw-row .pw-tool {
  flex: 0 0 auto;
}

.pw-tool[aria-pressed="true"] {
  border-color: var(--accent);
  color: var(--accent);
}

.pw-note {
  margin-top: 6px;
  font-size: 0.76rem;
  color: var(--accent);
}

/* A generated password is read back character by character, so it is set in
   the mono face and allowed to break rather than scroll out of the box. */
.pw-row input[type="text"] {
  font-family: var(--mono, ui-monospace, monospace);
  letter-spacing: 0.02em;
}

/* ---------------------------------------------------------------------
   5. Account administration
   --------------------------------------------------------------------- */

.admin-page {
  max-width: 1080px;
  margin: 0 auto;
  padding: 28px 20px 64px;
  position: relative;
  z-index: 1;
}

/* The shared .card is sized for the connect and terminal pages: width:
   fit-content over a min-width of min(920px, 100%) — "as wide as my content
   wants, and at least 920px where there is room".

   Here the content is an account table whose four action buttons make it about
   740px wide at every viewport, so fit-content asked for 740px on a 412px
   phone, the card took it, and the document went with it. Everything then
   panned sideways — including the add-account dialog, which is position:fixed
   and therefore sized from that same over-wide initial containing block. Its
   right-hand end, where Cancel and Create account live, sat off the side of
   the screen: nothing to scroll, and no way back out of the dialog short of
   reloading the page.

   This page brings its own width (.admin-page above) and the table brings its
   own scroller, so the card here only ever needs to be as wide as the page. */
.admin-page > .card {
  width: 100%;
  min-width: 0;
  max-width: 100%;
}

.admin-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
}

.admin-count {
  font-size: 0.8rem;
  color: var(--fg-muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.admin-table-wrap {
  border: 1px solid var(--border);
  border-radius: var(--r-md, 12px);
  overflow: hidden;
  /* Paint containment as well as clipping.
     overflow:hidden stops the table from being *drawn* outside this box, but
     the 740px of table inside the scroller below still counted towards the
     scrollable overflow of every ancestor up to the document — which made a
     412px phone report a 654px page and pan sideways over it. Containment is
     what actually ends the propagation, and it changes nothing visible: the
     box already clipped to exactly this area. */
  contain: paint;
  background: var(--panel);
  box-shadow: var(--elev-2, none);
}

.admin-table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

table.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--mono);
  font-size: 0.88rem;
}

.admin-table th {
  text-align: left;
  padding: 11px 14px;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--fg-muted);
  background: color-mix(in srgb, var(--panel-2) 70%, var(--panel));
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.admin-table td {
  padding: 12px 14px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  vertical-align: middle;
}

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

.admin-table tbody tr {
  transition: background 120ms ease;
}

.admin-table tbody tr:hover {
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.admin-table tr[data-disabled="true"] td:not(.admin-actions-cell) {
  opacity: 0.5;
}

.admin-user {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Initial-letter avatar. Cheap to render, and it makes a long list of
   monospace names scannable at a glance. */
.admin-avatar {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--bg);
  background: linear-gradient(135deg, var(--accent), var(--accent-2, var(--accent)));
  text-transform: uppercase;
}

/* break-all would split a short name like "alice" across two lines as soon as
   the column is tight. `anywhere` only breaks a word that genuinely cannot
   fit, which is what a pathologically long username needs and an ordinary one
   never triggers. */
.admin-username {
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* Give the identity column room before anything else is allowed to squeeze
   it, and keep the timestamp on one line. */
.admin-table th:first-child,
.admin-table td:first-child {
  min-width: 200px;
}

.admin-table td:nth-child(4) {
  white-space: nowrap;
  color: var(--fg-muted);
}

.admin-self-tag {
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 7px;
  white-space: nowrap;
}

/* Where an account signs in from, when it is not from here. Same shape as the
   "you" tag, since both answer "what is different about this row". */
.admin-origin-tag {
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 7px;
  margin-left: 4px;
  white-space: nowrap;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid;
  white-space: nowrap;
}

.pill--admin {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.pill--user {
  color: var(--fg-muted);
  border-color: var(--border);
  background: transparent;
}

.pill--ok {
  color: var(--term-color-green, #5fcb96);
  border-color: color-mix(in srgb, var(--term-color-green, #5fcb96) 45%, var(--border));
  background: color-mix(in srgb, var(--term-color-green, #5fcb96) 10%, transparent);
}

.pill--off {
  color: var(--fg-muted);
  border-color: var(--border);
  background: color-mix(in srgb, var(--fg-muted) 8%, transparent);
}

.pill--warn {
  color: var(--term-color-yellow, #ffd166);
  border-color: color-mix(in srgb, var(--term-color-yellow, #ffd166) 45%, var(--border));
  background: color-mix(in srgb, var(--term-color-yellow, #ffd166) 10%, transparent);
}

.admin-actions-cell {
  text-align: right;
  white-space: nowrap;
}

.admin-actions {
  display: inline-flex;
  gap: 6px;
  justify-content: flex-end;
}

.admin-actions button {
  font-size: 0.78rem;
  padding: 5px 10px;
  border-radius: var(--r-xs, 6px);
}

.admin-empty {
  padding: 48px 24px;
  text-align: center;
  color: var(--fg-muted);
}

.admin-empty strong {
  display: block;
  color: var(--fg);
  font-size: 1rem;
  margin-bottom: 6px;
}

.admin-status {
  min-height: 1.3em;
  font-size: 0.85rem;
  margin-bottom: 14px;
}

.admin-status[data-state="ok"] {
  color: var(--accent);
}

.admin-status[data-state="error"] {
  color: var(--term-color-red, #ff6b6b);
}

/* Skeleton rows while the first fetch is in flight — steadier than a
   spinner, because the table does not jump when data arrives. */
.admin-skeleton td > span {
  display: block;
  height: 12px;
  border-radius: 3px;
  background: linear-gradient(90deg,
      color-mix(in srgb, var(--fg-muted) 14%, transparent),
      color-mix(in srgb, var(--fg-muted) 26%, transparent),
      color-mix(in srgb, var(--fg-muted) 14%, transparent));
  background-size: 200% 100%;
  animation: admin-shimmer 1.4s ease-in-out infinite;
}

@keyframes admin-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .admin-skeleton td > span {
    animation: none;
  }
}

/* ---------------------------------------------------------------------
   6. Dialogs
   --------------------------------------------------------------------- */

.admin-dialog[hidden] {
  display: none;
}

/* The backdrop scrolls, the panel does not stretch.

   place-items:center on its own overflows in both directions when the panel is
   taller than the viewport, and an overflowing fixed box cannot be scrolled to
   — which on a phone put Cancel and Create below the fold with no way to reach
   either, and no way out of the dialog but a page reload. The software
   keyboard makes it worse: opening it for the password box halves the space
   the dialog was already too tall for.

   So the backdrop is the scroller (safe-area padding included, since the panel
   can now reach the bottom edge), and the panel is centred by auto margins,
   which — unlike centring alignment — leave the top reachable when it does not
   fit. dvh rather than vh so the browser's collapsing address bar is counted. */
.admin-dialog {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 20px;
  padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(3px);
}

.admin-dialog-content {
  width: 100%;
  max-width: 420px;
  /* Centred without alignment: auto margins shrink to zero rather than going
     negative, so a panel taller than the viewport starts at the top. */
  margin: auto;
  min-height: min-content;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg, 16px);
  box-shadow: var(--elev-4, var(--shadow));
  padding: 24px;
}

.admin-dialog-content h2 {
  margin: 0 0 6px;
  font-size: 1.1rem;
  font-weight: 600;
}

/* A title row with its own close control. The dialog already had Cancel, but
   Cancel is at the far end of a form — on a phone that is a scroll away, and
   "how do I get out of this" should never need one. */
.admin-dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.admin-dialog-close {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  min-width: 36px;
  padding: 0;
  font-size: 1.3rem;
  line-height: 1;
  border-radius: 999px;
}

.admin-dialog-desc {
  margin: 0 0 18px;
  color: var(--fg-muted);
  font-size: 0.86rem;
  line-height: 1.5;
}

/* Finger-sized controls wherever a finger is what there is.
   These pages do not load touch.css — that is the terminal's — so the query
   stands in for it. font-size:16px is not cosmetic: below it iOS Safari zooms
   the page in when a field takes focus and never zooms back out, which leaves
   the right-hand half of the dialog off the side of the screen. */
@media (pointer: coarse) {
  .admin-dialog input,
  .admin-dialog select,
  .admin-dialog button,
  .admin-page .toolbar button,
  .admin-page .toolbar select,
  .admin-page .toolbar .button-link {
    min-height: 44px;
    touch-action: manipulation;
  }

  .admin-dialog input,
  .admin-dialog select {
    font-size: 16px;
  }

  .admin-dialog-close {
    width: 44px;
    height: 44px;
    min-width: 44px;
  }
}

.admin-dialog-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  margin-top: 22px;
}

/* ---------------------------------------------------------------------
   7. Signed-in indicator, shown in the app chrome
   --------------------------------------------------------------------- */

.user-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px 4px 4px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: color-mix(in srgb, var(--panel-2) 60%, transparent);
  font-size: 0.8rem;
  white-space: nowrap;
}

.user-chip .admin-avatar {
  width: 24px;
  height: 24px;
  font-size: 0.7rem;
}

.user-chip-name {
  font-weight: 600;
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
}

.user-chip form {
  display: inline;
}

.user-chip button {
  font-size: 0.72rem;
  padding: 3px 9px;
  border-radius: 999px;
}

/* ---------------------------------------------------------------------
   8. Narrow screens
   --------------------------------------------------------------------- */

@media (max-width: 720px) {
  .auth-card {
    padding: 24px 20px;
  }

  .admin-toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .admin-actions-cell {
    text-align: left;
  }

  .admin-actions {
    justify-content: flex-start;
    flex-wrap: wrap;
  }
}

/* ---------------------------------------------------------------------
   7. Audit trail
   --------------------------------------------------------------------- */

/* Six columns rather than the accounts table's five, and every one of them
   can hold something long — a hostname, a comma-separated detail list. */
.admin-page--wide {
  max-width: 1320px;
}

.audit-filters {
  gap: 8px;
}

.audit-filters input[type="search"] {
  min-width: 240px;
}

/* Six text columns with nothing wide in them — no buttons, no long usernames
   — so at a phone width the table would happily squeeze itself into unreadable
   slivers rather than overflow. A floor makes it overflow instead, which is
   what the scrolling container is there for. The accounts table needs none of
   this: its action buttons already push it past any narrow viewport.

   The accounts table also reserves its first column for a username and its
   fourth for a date. Neither is true here, so both are reset. */
.audit-table {
  min-width: 780px;
}

.audit-table th:first-child,
.audit-table td:first-child {
  min-width: 0;
}

.audit-table td:nth-child(4) {
  white-space: normal;
}

.audit-table .audit-when {
  white-space: nowrap;
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
}

.audit-table .audit-ip,
.audit-table .audit-detail {
  color: var(--fg-muted);
}

/* Addresses and detail lists have no spaces to break at, so they are told
   where they may break rather than being allowed to widen the table. */
.audit-table .audit-ip {
  white-space: nowrap;
}

.audit-table .audit-detail {
  overflow-wrap: anywhere;
  max-width: 30ch;
}

.audit-table td:nth-child(5) {
  overflow-wrap: anywhere;
  max-width: 26ch;
}

.audit-event {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  font-size: 0.74rem;
  white-space: nowrap;
}

/* A refusal is the row somebody is scanning for, so it is the one that
   carries colour. Success is the background state and stays quiet. */
.audit-event[data-outcome="denied"],
.audit-event[data-outcome="failure"] {
  border-color: color-mix(in srgb, #e5534b 55%, var(--border));
  color: color-mix(in srgb, #e5534b 75%, var(--fg));
  background: color-mix(in srgb, #e5534b 12%, transparent);
}

.audit-table tr[data-outcome] td {
  background: color-mix(in srgb, #e5534b 5%, transparent);
}

.audit-note {
  margin: 18px 0 0;
  font-size: 0.8rem;
}

.audit-note code {
  overflow-wrap: anywhere;
}

/* Group membership in the account table. Tags rather than a comma list, so a
   row with four teams is still scannable and a long name cannot run into the
   next column. */
.admin-groups {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.admin-group-tag {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 8px;
  white-space: nowrap;
}

.admin-groups-none {
  color: var(--fg-muted);
  opacity: 0.6;
}

/* ---------------------------------------------------------------------
   9. Administration area shell: navigation, overview tiles, activity
   --------------------------------------------------------------------- */

/* One strip of tabs on every admin page, so moving between the sections is
   one click and the underline answers "where am I". Anchors restate colour
   and font-style because a theme may style every link (see .auth-sso). */
.admin-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin: 0 0 22px;
  border-bottom: 1px solid var(--border);
}

.admin-nav a {
  padding: 9px 14px 8px;
  font-size: 0.76rem;
  font-style: normal;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--fg-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  border-radius: var(--r-xs, 6px) var(--r-xs, 6px) 0 0;
  transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
}

.admin-nav a:hover {
  color: var(--fg);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.admin-nav a[aria-current="page"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* The overview's headline numbers. Tiles rather than a sentence because the
   reading is a glance: four numbers, and only an odd one detains the eye. */
.admin-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px;
  margin-bottom: 8px;
}

.admin-stat {
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--r-md, 12px);
  background: color-mix(in srgb, var(--panel-2) 55%, var(--panel));
}

.admin-stat-label {
  font-size: 0.66rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--fg-muted);
}

.admin-stat-value {
  margin: 5px 0 3px;
  font-family: var(--mono);
  font-size: 1.65rem;
  font-weight: 700;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.admin-stat-sub {
  font-size: 0.74rem;
  line-height: 1.4;
  color: var(--fg-muted);
}

/* A refusal count above zero is the one number the page exists to surface. */
.admin-stat[data-state="warn"] {
  border-color: color-mix(in srgb, var(--term-color-red, #ff6b6b) 45%, var(--border));
  background: color-mix(in srgb, var(--term-color-red, #ff6b6b) 7%, var(--panel));
}

.admin-stat[data-state="warn"] .admin-stat-value {
  color: var(--term-color-red, #ff6b6b);
}

.admin-section {
  margin-top: 26px;
}

.admin-section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
}

/* A select in a section head takes its width from its longest option, and the
   sample app names are longer than anything else on the row. Capped so the
   thing it sits beside is still the primary action, and so a narrow screen
   gets two controls on one line rather than one control and a wrap. */
.admin-section-head .toolbar select {
  max-width: 14rem;
  font-size: 0.9rem;
}

.admin-section-head h2 {
  margin: 0;
  font-size: 0.8rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--fg-muted);
}

/* The last few audit lines, as a list rather than a table: three facts per
   row, and the full six-column view is one click away on the audit page. */
.admin-activity {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-md, 12px);
  background: var(--panel);
  overflow: hidden;
}

.admin-activity li {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 12px;
  padding: 9px 14px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  font-size: 0.84rem;
}

.admin-activity li:last-child {
  border-bottom: 0;
}

.admin-activity li[data-outcome] {
  background: color-mix(in srgb, #e5534b 5%, transparent);
}

.admin-activity time {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  min-width: 7.5ch;
}

.admin-activity-text {
  color: var(--fg-muted);
  overflow-wrap: anywhere;
}

.admin-activity-empty {
  justify-content: center;
  color: var(--fg-muted);
  padding: 24px 14px;
}

/* The live-session table's supporting facts. The ID is a correlation handle
   for log lines, not a name, so it stays small and quiet. */
.admin-session-id {
  font-size: 0.7rem;
  color: var(--fg-muted);
  letter-spacing: 0.05em;
}

.sessions-table .sessions-host {
  overflow-wrap: anywhere;
  max-width: 30ch;
}

.sessions-table .sessions-when {
  white-space: nowrap;
  color: var(--fg-muted);
}

/* Where an inherited role came from, under the role pill. */
.admin-role-via {
  margin-top: 3px;
  font-size: 0.68rem;
  color: var(--fg-muted);
  letter-spacing: 0.04em;
}

/* The group-roles dialog: one row per group, the select applying as it
   changes. */
.group-roles-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 6px;
  max-height: 320px;
  overflow-y: auto;
}

.group-role-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 8px);
  background: color-mix(in srgb, var(--panel-2) 45%, var(--panel));
}

.group-role-row select {
  font-family: var(--mono);
  font-size: 0.82rem;
  padding: 6px 8px;
  color: var(--fg);
  background: color-mix(in srgb, var(--bg) 55%, var(--panel));
  border: 1px solid var(--border);
  border-radius: var(--r-xs, 6px);
}

/* Preset audience tags: groups wear the shared tag; users and roles are
   tinted so a mixed audience reads as three kinds, not one list. */
.admin-tag--user {
  background: color-mix(in srgb, var(--term-color-green, #5fcb96) 10%, transparent);
}

.admin-tag--role {
  background: color-mix(in srgb, var(--term-color-yellow, #ffd166) 10%, transparent);
}

/* A preset that reaches nobody yet. Not a tag — it names an absence rather
   than an audience — and stated plainly, because the row looks in every other
   respect like one that is in use. */
.admin-tag--unoffered {
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--warn);
}

/* Whether a preset is offered at all sits above the audience it is offered
   to, and reads as the question it is rather than as one more checkbox. */
.preset-offer {
  margin-top: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 8px);
  background: color-mix(in srgb, var(--accent) 5%, transparent);
}

/* The group dialog's member and host pickers. A scrolling column of
   checkboxes: an instance with two hundred accounts must not push the save
   button off the bottom of the dialog, and a team is chosen by ticking names
   rather than by spelling each one the same way it was spelled elsewhere. */
.picker-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 220px;
  overflow-y: auto;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 8px);
  background: color-mix(in srgb, var(--bg) 45%, var(--panel));
}

.picker-list .preset-check {
  align-items: flex-start;
  padding: 5px 4px;
  border-radius: var(--r-xs, 6px);
}

.picker-list .preset-check:hover {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.picker-list .preset-check input[type="checkbox"] {
  margin-top: 3px;
}

/* A locked row is one the identity provider owns. Dimmed rather than hidden:
   "why is this person not in the list" is a worse question than "why can I not
   tick this", which the note above the list answers. */
.picker-list .preset-check--locked {
  opacity: 0.65;
  cursor: not-allowed;
}

.picker-list .admin-role-via {
  display: block;
}

/* The preset dialog: two columns where there is room, one where there is
   not, and the audience fieldset drawn as its own bordered panel. */
.preset-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px 16px;
  margin-bottom: 16px;
}

.preset-check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  color: var(--fg);
  background: none;
  border: 0;
  padding: 2px 0;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}

.preset-check input[type="checkbox"] {
  width: auto;
  accent-color: var(--accent);
}

.preset-audience {
  border: 1px solid var(--border);
  border-radius: var(--r-md, 12px);
  padding: 14px 16px 16px;
  margin: 0 0 6px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.preset-audience legend {
  padding: 0 6px;
  font-size: 0.72rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--fg-muted);
}

.preset-audience .field-hint {
  margin: 0;
}

.presets-table .presets-conn {
  color: var(--fg-muted);
  font-size: 0.8rem;
}

/* Version, uptime, mode: the footer facts a support ticket asks for. */
.admin-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin-top: 28px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 0.76rem;
  color: var(--fg-muted);
}

/* The selection screen preview. A character grid drawn in the terminal font,
   because it is one: a proportional preview of a 3270 screen would line up
   nowhere and mislead about the artwork. */
.admin-dialog-content--wide {
  max-width: 780px;
}

.branding-preview-wrap {
  overflow-x: auto;
  margin-bottom: 18px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 8px);
  background: var(--bg);
}

.branding-preview {
  margin: 0;
  padding: 12px 14px;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.25;
  color: var(--accent);
  white-space: pre;
  min-width: 68ch;
}

.admin-dialog-content--wide textarea {
  width: 100%;
  font-family: var(--mono);
  font-size: 0.8rem;
  padding: 10px 12px;
  color: var(--fg);
  background: color-mix(in srgb, var(--bg) 55%, var(--panel));
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 8px);
  resize: vertical;
  white-space: pre;
  overflow-x: auto;
}

/* A stacked form's label is uppercase and tracked; an "optional" marker inside
   one inherits that and reads as a second heading. Quieten it back down. */
.field > label .subtle {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  opacity: 0.75;
}

/* An import report is as long as the library it describes, which can be two
   hundred entries. Bounding it keeps the Import button — the thing somebody
   has come here to press — on the same screen as the summary of what it will
   do, instead of a page and a half below the list. */
.library-report .admin-table-scroll {
  max-height: 22rem;
  overflow-y: auto;
}

/* Whether the table is what would happen or what did. It is the difference
   between a preview and a receipt, and the words in the rows ("replaced")
   cannot carry it on their own. */
.library-report caption {
  caption-side: top;
  text-align: left;
  padding: 10px 14px;
  font-size: 0.8rem;
  color: var(--fg-muted);
  border-bottom: 1px solid var(--border);
}
