/* The palette is a single switch: `:root` is the light theme and `:root[data-theme="dark"]` overrides
   the same tokens. Nothing below reads a literal colour, so a rule that renders in one theme renders
   in both. `data-theme` is written by the pre-paint script in index.html and then owned by ThemeToggle
   (public/app.js), which stores the user's choice under the same localStorage key. */

/* The `hidden` attribute must win over any layout rule below. Without this, `.field { display: grid }`
   beats the user-agent's `[hidden] { display: none }`, so `field.hidden = true` in app.js — the Group
   field of a group account, the ISP picker, the "no ISP online" note — silently stayed on screen. */
[hidden] {
  display: none !important;
}

:root {
  color-scheme: light;

  --bg: #f2f4f8;
  --panel: #ffffff;
  --soft-line: #ccd3df;
  --accent: #2563eb;
  --accent-strong: #1749c8;
  --section: #eef3f9;
  --text: #081225;
  --muted: #556174;

  --focus-ring: rgba(37, 99, 235, 0.16);
  --field-bg: #ffffff;
  --field-border: #aab4c5;
  --field-disabled-bg: #f1f5f9;
  --field-disabled-text: #64748b;
  --button-bg: #ffffff;
  --button-text: #003bed;
  --button-hover-bg: #eef4ff;
  --notice-bg: #fff8e6;
  --notice-border: #f2b84b;
  --notice-text: #713f12;
  --notice-muted: #854d0e;
  --notice-button-bg: #b45309;
  --notice-button-hover: #92400e;
  --row-running: #eef6ff;
  --row-queued: #fff8e6;
  --row-time-text: #4a5361;
  --status-done: #12733d;
  --status-failed: #b42318;
  --status-active: #1749c8;
  --status-aborted: #d32f2f;
  --status-expired: #b45309;
  --history-row-bg: #fff8f0;
  --history-row-border: #e2a86a;
  --clear-button-bg: #e67e22;
  --clear-button-hover: #ca6f1e;
  --clear-button-disabled-bg: #d8cfc4;
  --clear-button-disabled-text: #6b5b4b;
  --stop-button-bg: #d32f2f;
  --stop-button-hover: #b71c1c;
  --dot-idle: #cbd5e1;
  --dot-online: #16a34a;
  --dot-online-ring: rgba(22, 163, 74, 0.18);
  --dot-offline: #dc2626;
  --dot-offline-ring: rgba(220, 38, 38, 0.15);
  --link: #005cff;
  --panel-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
  /* The sheet behind a *loaded* report: Playwright/MonoCart HTML is built for a light page, so the
     viewer keeps a light surface for it in both themes. The empty viewer uses --panel instead. */
  --report-bg: #ffffff;
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0d1117;
  --panel: #161b22;
  --soft-line: #2c3542;
  --accent: #3b82f6;
  --accent-strong: #2f6fe0;
  --section: #1c2430;
  --text: #e6edf6;
  --muted: #97a3b6;

  --focus-ring: rgba(59, 130, 246, 0.35);
  --field-bg: #0f1620;
  --field-border: #38424f;
  --field-disabled-bg: #1a2129;
  --field-disabled-text: #7d8794;
  --button-bg: #1b2330;
  --button-text: #7ea8ff;
  --button-hover-bg: #232f41;
  --notice-bg: #3a2f13;
  --notice-border: #8a6d2a;
  --notice-text: #f4d79b;
  --notice-muted: #d9bb79;
  --notice-button-bg: #b45309;
  --notice-button-hover: #92400e;
  --row-running: #16243a;
  --row-queued: #33290f;
  --row-time-text: #9aa8bb;
  --status-done: #4ade80;
  --status-failed: #f87171;
  --status-active: #7aa7ff;
  --status-aborted: #ef5350;
  --status-expired: #fbbf24;
  --history-row-bg: #2a2113;
  --history-row-border: #7a6134;
  --clear-button-bg: #e67e22;
  --clear-button-hover: #ca6f1e;
  --clear-button-disabled-bg: #39414c;
  --clear-button-disabled-text: #8d8579;
  --stop-button-bg: #d32f2f;
  --stop-button-hover: #b71c1c;
  --dot-idle: #3a4657;
  --dot-online: #22c55e;
  --dot-online-ring: rgba(34, 197, 94, 0.22);
  --dot-offline: #ef4444;
  --dot-offline-ring: rgba(239, 68, 68, 0.2);
  --link: #6ea8fe;
  --panel-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  --report-bg: #ffffff;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: Arial, Helvetica, sans-serif;
  font-size: 14px;
}

/* The frame spans the whole viewport instead of a centered 1180px column. */
.runner-shell {
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 6px 16px 24px;
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 10px;
}

h1 {
  margin: 0;
  font-size: 24px;
  line-height: 1.25;
  letter-spacing: 0;
}

/* Sits in the page header; it is the visible half of the theme switch whose state lives on
   document.documentElement[data-theme]. */
.theme-toggle {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 32px;
  padding: 0 13px;
  border: 1px solid var(--soft-line);
  border-radius: 999px;
  background: var(--panel);
  color: var(--text);
  font-size: 12px;
  font-weight: 700;
}

.theme-toggle:hover {
  background: var(--button-hover-bg);
}

.theme-toggle-icon {
  font-size: 14px;
  line-height: 1;
}

.domain-update-notice {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 10px;
  padding: 12px 14px;
  border: 1px solid var(--notice-border);
  border-radius: 8px;
  background: var(--notice-bg);
  color: var(--notice-text);
}

.domain-update-notice strong {
  display: block;
  margin-bottom: 3px;
  font-size: 14px;
}

.domain-update-notice p {
  margin: 0;
  color: var(--notice-muted);
  line-height: 1.4;
}

.domain-update-button {
  flex: 0 0 auto;
  min-height: 36px;
  padding: 9px 14px;
  border: 1px solid var(--notice-button-bg);
  border-radius: 6px;
  background: var(--notice-button-bg);
  color: #fff;
  font-weight: 700;
  text-decoration: none;
}

.domain-update-button:hover {
  background: var(--notice-button-hover);
  color: #fff;
}

.domain-panel,
.report-panel {
  background: var(--panel);
  border: 1px solid var(--soft-line);
  border-radius: 8px;
  box-shadow: var(--panel-shadow);
}

/* The tool frame is a grid of three tracks: the compact command column (tool/group/brand selects and
   the ISP picker, bounded at the form's own width), a flexible middle track that only separates the
   controls from the roster, and the worker roster pinned to the frame's right edge. The middle track is
   also what lets the full-width rows below span the whole frame. */
.domain-panel {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) 220px;
  align-items: start;
  gap: 12px;
  padding: 12px;
  margin-bottom: 14px;
}

/* The form and the ISP picker stack in one column, so the roster's height can never stretch them apart:
   their spacing is this column's own gap. */
.command-column {
  display: grid;
  gap: 12px;
  min-width: 0;
}

/* Fixed tracks: the fields used to be `fr` tracks, so they stretched with the window and would now
   span the whole screen. The controls keep their own width and the row stays left-aligned. The first
   track is the Tool Name select: it holds the longest option label ("Main Business Flow + Games",
   ~186px at 14px Arial) plus the select's 22px padding and the native dropdown arrow, so it is wider
   than the other two selects — anything below ~230px clips that label. */
.domain-form {
  display: grid;
  grid-template-columns: 250px 170px 220px 120px;
  justify-content: start;
  gap: 12px;
  align-items: end;
}

.field {
  display: grid;
  gap: 6px;
  min-width: 0;
}

.field span {
  font-weight: 700;
  font-size: 12px;
}

input,
select {
  width: 100%;
  height: 36px;
  border: 1px solid var(--field-border);
  border-radius: 5px;
  padding: 0 11px;
  font: inherit;
  color: var(--text);
  background: var(--field-bg);
}

input:focus,
select:focus {
  border-color: var(--accent);
  outline: 2px solid var(--focus-ring);
}

input:disabled {
  background: var(--field-disabled-bg);
  color: var(--field-disabled-text);
}

button {
  height: 36px;
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 0 14px;
  background: var(--button-bg);
  color: var(--button-text);
  font-weight: 700;
  cursor: pointer;
}

button:hover {
  background: var(--button-hover-bg);
}

button:disabled {
  cursor: wait;
  opacity: 0.7;
}

.start-button {
  background: var(--accent);
  color: #fff;
}

.start-button:hover {
  background: var(--accent-strong);
}

/* Spans both grid columns; the frame's own `gap` provides the spacing, so no top margin here. */
.job-row {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
  align-items: center;
  min-height: 24px;
  font-size: 16px;
}

.job-row strong {
  font-weight: 800;
}

.alive-note {
  color: var(--muted);
  font-size: 13px;
}

/* The group filter lives in the Group column header rather than in a row of its own, so the control
   sits directly above the values it filters. The global input/select sizing (36px tall, full width)
   is meant for the command form, so it is overridden here to fit inside a header cell. */
.group-column-label {
  display: block;
  margin-bottom: 4px;
}

.job-table th select {
  width: 100%;
  height: 24px;
  padding: 0 4px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 400;
  background: var(--field-bg);
}

.queue-panel {
  grid-column: 1 / -1;
  overflow-x: auto;
}

/* `table-layout: fixed` is what keeps the header still: the auto layout used to size every column from
   the longest value currently displayed, so a new job id or brand moved the whole header. The column
   widths below are shares of the table rather than of its content, and the table fills the frame so it
   lines up with the ISP picker above it and the report panel below. There is deliberately **no**
   `min-width`: the grid scales down with the window instead of forcing a horizontal scrollbar inside
   the frame, and a value that no longer fits is ellipsised — the job id, the timestamps and the failure
   reason all keep their full text in the cell title. */
.job-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  font-size: 13px;
}

.job-table th:nth-child(1) {
  width: 20%;
}

.job-table th:nth-child(2) {
  width: 10%;
}

.job-table th:nth-child(3) {
  width: 10%;
}

.job-table th:nth-child(4) {
  width: 8%;
}

.job-table th:nth-child(5),
.job-table th:nth-child(6) {
  width: 12%;
}

.job-table th:nth-child(7) {
  width: 28%;
}

.job-table th,
.job-table td {
  border: 1px solid var(--soft-line);
  padding: 7px 8px;
  text-align: left;
  white-space: nowrap;
  /* A value that no longer fits its fixed column is ellipsised instead of widening the column. */
  overflow: hidden;
  text-overflow: ellipsis;
}

.job-table th {
  background: var(--section);
  font-size: 12px;
}

/* Created and Finished are compared column against column, so their digits have to line up; the
   compact format also keeps both of them narrower than the single Created column used to be. */
.job-table td.job-time {
  color: var(--row-time-text);
  font-variant-numeric: tabular-nums;
}

.job-table tr[data-status="RUNNING"] {
  background: var(--row-running);
}

.job-table tr[data-status="QUEUED"] {
  background: var(--row-queued);
}

.status-done {
  color: var(--status-done);
  font-weight: 700;
}

.status-failed {
  color: var(--status-failed);
  font-weight: 700;
}

.status-running,
.status-queued {
  color: var(--status-active);
  font-weight: 700;
}

.status-aborted {
  color: var(--status-aborted);
  font-weight: 700;
}

.report-button {
  height: 28px;
  padding: 0 10px;
}

/* Failure reasons are long paths and stack-free messages: keep the row height stable and let the
   cell title carry the full text. */
.job-error {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--status-failed);
  font-size: 12px;
}

/* When the reason sits under an Open button the report exists but is empty, so the row has to
   read as a failure rather than as a normal report waiting to be opened. */
td .job-error {
  margin-top: 4px;
}

.stop-button {
  height: 28px;
  padding: 0 10px;
  background-color: var(--stop-button-bg);
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-weight: 700;
}

.stop-button:hover {
  background-color: var(--stop-button-hover);
}

.history-action-row td {
  background: var(--history-row-bg);
  border-top: 2px dashed var(--history-row-border);
  text-align: right;
  padding: 6px 8px;
}

.clear-history-button {
  height: 28px;
  padding: 0 12px;
  background-color: var(--clear-button-bg);
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-weight: 700;
  font-size: 12px;
  transition: background-color 0.15s;
}

.clear-history-button:hover {
  background-color: var(--clear-button-hover);
}

/* The button stays visible as a lock instead of disappearing, so a missing history-clear feature is
   never mistaken for a bug. It is disabled in two cases: there is nothing finished left to delete, or
   the server's kill switch is off (Config.historyClearEnabled). A group account never sees the row at
   all — deleting stored history is an admin action. */
.clear-history-button:disabled {
  background-color: var(--clear-button-disabled-bg);
  color: var(--clear-button-disabled-text);
  cursor: not-allowed;
}

.clear-history-button:disabled:hover {
  background-color: var(--clear-button-disabled-bg);
}

/* Spans the frame like the table above it so the pager stays aligned with the table's right edge. */
.job-pagination {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  min-height: 34px;
  padding-top: 8px;
  font-size: 12px;
  color: var(--muted);
}

.job-pagination-buttons {
  display: flex;
  gap: 6px;
}

.page-button {
  min-width: 32px;
  height: 28px;
  padding: 0 9px;
  border-radius: 4px;
  font-size: 12px;
}

/* Elided page range: the history is never pruned, so the pager shows the first/last page plus a
   window around the current one instead of one button per page. */
.page-gap {
  align-self: center;
  padding: 0 2px;
  color: var(--muted);
}

.page-button[aria-current="page"] {
  background: var(--accent);
  color: #fff;
}


.status-expired {
  color: var(--status-expired);
  font-weight: 700;
}

/* Sits under the command form in the same compact column. Its surface is the frame's own surface
   (--panel, not a tinted --section) so the picker reads as part of the same frame instead of as a
   differently coloured block; only the dashed outline separates it from the fields above. */
.isp-field {
  padding: 10px 12px;
  border: 1px dashed var(--soft-line);
  border-radius: 8px;
  background: var(--panel);
}

.isp-label {
  display: block;
  margin-bottom: 8px;
  font-weight: 700;
  font-size: 12px;
}

.isp-options {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}

.isp-option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 700;
  cursor: pointer;
}

.isp-option input {
  width: auto;
  height: auto;
  margin: 0;
}

/* The "Random" default of the single-choice tools: visually distinct from a real worker line so
   it never reads as one of the ISP names. */
.isp-option.is-random {
  color: var(--muted);
  font-style: italic;
}

.isp-empty {
  margin: 6px 0 0;
  color: var(--muted);
  font-size: 12px;
}

/* Pinned to the frame's right edge (the flexible middle track is what pushes it there) and level with
   the command column at the top, so the machine list is read next to the ISP it will be bound to. It is
   a plain box on the frame surface rather than a floating panel, so it needs no fixed positioning,
   width or internal scrolling, and its own height never moves the controls beside it. */
.worker-panel {
  grid-column: 3;
  grid-row: 1;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--soft-line);
  border-radius: 10px;
}

.worker-panel h2 {
  margin: 0 0 10px;
  font-size: 15px;
}

.worker-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}

.worker-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--soft-line);
  border-radius: 10px;
}

.worker-info {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.worker-name {
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.worker-isp {
  font-size: 11px;
  color: var(--muted);
}

.worker-dot {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--dot-idle);
}

.worker-item.is-online .worker-dot {
  background: var(--dot-online);
  box-shadow: 0 0 0 3px var(--dot-online-ring);
}

.worker-item.is-offline .worker-dot {
  background: var(--dot-offline);
  box-shadow: 0 0 0 3px var(--dot-offline-ring);
}

.worker-empty {
  color: var(--muted);
  font-size: 12px;
}

/* Below this width the compact command column and the 220px roster no longer fit side by side (the
   form's own tracks need ~800px, plus the roster, the two grid gaps and the frame padding = ~1064px of
   frame, which is why the threshold sits above 1080), so the frame collapses to one column and every
   child falls back into DOM order, filling the frame width. */
@media (max-width: 1100px) {
  .domain-panel {
    grid-template-columns: minmax(0, 1fr);
  }

  .command-column,
  .worker-panel,
  .job-row,
  .queue-panel {
    grid-column: 1;
    grid-row: auto;
  }

  .worker-list {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  }
}

.report-panel {
  height: min(640px, calc(100vh - 190px));
  min-height: 420px;
  overflow: hidden;
}

/* Until a report is opened the viewer is an empty iframe, so it stays on the frame's own surface
   instead of painting the light report sheet as a white block inside the dark theme. */
#report-frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--panel);
}

/* The light sheet belongs to a loaded report only: Playwright/MonoCart HTML is built for a light page,
   so it is not inverted, but nothing else gets that white rectangle. ReportViewer sets/removes the
   `src` attribute, which is exactly the "a report is open" signal. */
#report-frame[src] {
  background: var(--report-bg);
}

a {
  color: var(--link);
}

@media (max-width: 900px) {
  .runner-shell {
    padding: 6px 8px 24px;
  }

  /* Still fixed-width tracks rather than `1fr` columns, so the selects do not stretch on a tablet. The
     250px matches the Tool Name track above, which needs the room for its longest option label. */
  .domain-form {
    grid-template-columns: repeat(2, minmax(0, 250px));
  }

  .start-button {
    grid-column: 1 / -1;
  }

  /* The table now has the whole (narrow) frame, so it goes a size down and re-shares its columns: the
     status and the two timestamps take the space from the job id and group, which ellipsise gracefully
     and keep their full text in the cell title. */
  .job-table {
    font-size: 12px;
  }

  .job-table th,
  .job-table td {
    padding: 6px;
  }

  .job-table th:nth-child(1) {
    width: 16%;
  }

  .job-table th:nth-child(4) {
    width: 9%;
  }

  .job-table th:nth-child(5),
  .job-table th:nth-child(6) {
    width: 13%;
  }

  .job-table th:nth-child(7) {
    width: 29%;
  }
}

@media (max-width: 560px) {
  h1 {
    font-size: 20px;
  }

  .page-header {
    align-items: flex-start;
  }

  .domain-update-notice {
    align-items: stretch;
    flex-direction: column;
  }

  .domain-update-button {
    text-align: center;
  }

  .domain-form {
    grid-template-columns: minmax(0, 260px);
  }

  .job-row {
    gap: 10px;
    font-size: 14px;
  }

  .report-panel {
    min-height: 420px;
  }
}

/* ---- Sign-in page -------------------------------------------------------- */
/* The form is the only thing on the page, so it is centred, and it reuses the panel colours rather
   than redefining them. Every custom property carries a fallback: a renamed variable must never be
   able to paint an unreadable form. */
.login-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  margin: 0;
}

.login-frame {
  width: min(360px, calc(100vw - 32px));
  padding: 24px;
  border: 1px solid var(--border, #d5d9e0);
  border-radius: 12px;
  background: var(--panel, #ffffff);
  box-shadow: 0 12px 32px rgb(0 0 0 / 12%);
}

.login-frame h1 {
  margin: 0 0 6px;
  font-size: 22px;
}

.login-note {
  margin: 0 0 18px;
  font-size: 14px;
  opacity: 0.75;
}

.login-form {
  display: grid;
  gap: 14px;
}

.login-form .field {
  display: grid;
  gap: 6px;
}

.login-form input {
  padding: 9px 10px;
  border: 1px solid var(--border, #d5d9e0);
  border-radius: 8px;
  background: var(--input-bg, transparent);
  color: inherit;
  font: inherit;
}

.login-form .start-button {
  width: 100%;
}

.login-error {
  margin: 14px 0 0;
  padding: 9px 10px;
  border-radius: 8px;
  background: var(--danger-bg, #fdecec);
  color: var(--danger-text, #a11b1b);
  font-size: 14px;
}

/* Who is signed in, and the way out. `margin-left: auto` keeps the theme button on the right edge
   next to it whatever the header's own layout does. */
.session-panel {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

.session-user {
  font-size: 13px;
  opacity: 0.8;
}

.logout-button {
  padding: 6px 12px;
  border: 1px solid var(--border, #d5d9e0);
  border-radius: 8px;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.logout-button:hover {
  border-color: var(--link, #2a5db0);
  color: var(--link, #2a5db0);
}

/* ---- Admin: account management ------------------------------------------- */
/* Only an admin ever renders this, below the report panel. It reuses the panel surface so the page
   still reads as one screen rather than a separate admin app. */
.admin-panel {
  margin-top: 18px;
  padding: 16px;
  border: 1px solid var(--soft-line, #ccd3df);
  border-radius: 12px;
  background: var(--panel, #ffffff);
}

.admin-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.admin-header h2 {
  margin: 0;
  font-size: 17px;
}

.admin-toggle {
  padding: 6px 12px;
  border: 1px solid var(--soft-line, #ccd3df);
  border-radius: 8px;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

/* Refresh + collapse sit together on the right of an admin panel header. */
.admin-header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.admin-hint {
  margin: 8px 0 14px;
  font-size: 13px;
  opacity: 0.75;
}

/* Collapsed keeps the header, so the panel can be reopened without a reload. */
.admin-panel.is-collapsed .account-form,
.admin-panel.is-collapsed .admin-table-wrap,
.admin-panel.is-collapsed .admin-note,
.admin-panel.is-collapsed .admin-hint {
  display: none;
}

.account-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  align-items: end;
  gap: 12px;
  margin-bottom: 14px;
}

.account-form .start-button,
.account-form .logout-button {
  height: 38px;
}

.admin-note {
  margin: 0 0 14px;
  padding: 9px 10px;
  border-radius: 8px;
  background: var(--section, #eef3f9);
  font-size: 13px;
}

.admin-note.is-error {
  background: var(--danger-bg, #fdecec);
  color: var(--danger-text, #a11b1b);
}

.admin-table-wrap {
  overflow-x: auto;
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.admin-table th,
.admin-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--soft-line, #ccd3df);
  text-align: left;
}

.admin-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.admin-action {
  padding: 4px 10px;
  border: 1px solid var(--soft-line, #ccd3df);
  border-radius: 7px;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.admin-action:hover {
  border-color: var(--accent, #2563eb);
  color: var(--accent, #2563eb);
}

.admin-action.is-danger:hover {
  border-color: var(--danger-text, #a11b1b);
  color: var(--danger-text, #a11b1b);
}

/* The audit wipe is the one admin action that does nothing while the list is empty; it stays visible
   as a lock rather than disappearing. */
.admin-action:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.admin-action:disabled:hover {
  border-color: var(--soft-line, #ccd3df);
  color: inherit;
}

/* ---- Clear-history confirmation dialog ----------------------------------- */
/* The dialog is the one place the group to delete is chosen (only an admin opens it, and it picks the
   group here), so it has to be a real dialog rather than a window.confirm. It is closed with Hủy or
   Escape, and the global [hidden] rule above is what keeps it off screen. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(15, 23, 42, 0.55);
}

.modal {
  width: min(440px, 100%);
  padding: 18px;
  border: 1px solid var(--soft-line, #ccd3df);
  border-radius: 12px;
  background: var(--panel, #ffffff);
  box-shadow: 0 18px 40px rgba(15, 23, 42, 0.35);
}

.modal h2 {
  margin: 0 0 12px;
  font-size: 17px;
}

.modal .field {
  margin-bottom: 12px;
}

.modal-summary {
  margin: 0 0 16px;
  font-size: 13px;
  line-height: 1.5;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

.modal-actions .logout-button,
.modal-actions .clear-history-button {
  height: 34px;
}
