/* main.css - Non-critical CSS for Admin Panel
 *
 * Primary stylesheet for the admin panel. Contains all non-critical styles that are not
 * essential for the initial above-the-fold rendering of the page.
 *
 * Critical styles (for header, navigation, and basic layout) are included in critical.css,
 * which is inlined in the HTML <head> for faster first paint.
 * This file is loaded asynchronously after the critical CSS to enhance the page with additional
 * styling and layout for the full admin interface.
*/


/* ─── 1. Design tokens ───────────────────────────────────────────────────────── */

:root {
  /* Backgrounds */
  --bg-0: #070a12;
  --bg-1: #0b1020;

  /* Panels & strokes */
  --panel:   rgba(255, 255, 255, .07);
  --panel-2: rgba(255, 255, 255, .10);
  --stroke:  rgba(255, 255, 255, .16);

  /* Typography */
  --text:  rgba(235, 244, 241, .92);
  --muted: rgba(235, 244, 241, .68);

  /* Brand colours */
  --brand-a: #8cc4ae;
  --brand-b: #e8de6a;

  /* Accent colours */
  --accent-cyan: #4de8ff;
  --violet:      #7c5cff;
  --pink:        #ff5fd7;
  --danger:      rgba(255, 90, 120, .95);

  /* Semantic status colours, reused across sections via .pill. Two separate
     axes, deliberately different hues so they're never confused:
     health (is it working right now: good/bad/warn) vs. toggle (is a
     setting turned on: on/off — "enabled" and "ok" answer different
     questions and used to share the same teal, which read as one thing). */
  --status-good:    #4ade80;
  --status-good-bg: rgba(74, 222, 128, .13);
  --status-bad:     #ff8d92;
  --status-bad-bg:  rgba(255, 110, 120, .13);
  --status-warn:    #f0b429;
  --status-warn-bg: rgba(240, 180, 41, .13);
  --status-on:      var(--accent-cyan);
  --status-on-bg:   rgba(77, 232, 255, .13);

  /* Glows */
  --glow-primary: rgba(140, 196, 174, .55);
  --glow-accent:  rgba(77, 232, 255, .26);

  /* Radii */
  --radius:    18px;
  --radius-lg: calc(var(--radius) - 6px);   /* 12px */
  --radius-md: 12px;

  /* Shadows */
  --shadow-soft:   0 2px  8px rgba(0, 0, 0, .20);
  --shadow-strong: 0 6px 18px rgba(0, 0, 0, .35);

  /* Fonts */
  --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', 'Courier New', monospace;

  /* Flat panel fill — calmer than --panel, used by the shared section framework */
  --panel-flat: rgba(255, 255, 255, .03);
}


/* ─── 2. Base reset & typography ─────────────────────────────────────────────── */

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  font-size: 1rem;
  line-height: 1.45;
  color: var(--text);
  background:
    radial-gradient(900px 650px at 12%  8%, rgba(77, 232, 255, .12), transparent 58%),
    radial-gradient(920px 680px at 88% 18%, rgba(255, 95, 215, .08), transparent 60%),
    linear-gradient(180deg, var(--bg-0), var(--bg-1));
  background-attachment: fixed;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}


/* ─── 3. Layout ──────────────────────────────────────────────────────────────── */

.admin-main {
  max-width: 1760px;
  margin: 20px auto;
  /* Horizontal padding matches the header's 24px so the content's left/right
     edges line up with the wordmark and nav above it (they used to sit at 8px
     vs the header's 24px — a visible misalignment on desktop). The ≤800px
     media query below resets this for mobile. */
  padding: 0 24px;
}

.admin-section {
  margin-bottom: 24px;
}

/* Shared glass panel */
.panel-section {
  background: linear-gradient(180deg, rgba(255, 255, 255, .02), rgba(255, 255, 255, .01));
  border: 1px solid var(--stroke);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
  box-shadow: var(--shadow-strong);
  margin-bottom: 14px;
}

/* Panel controls row */
.panel-controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 10px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--stroke);
}
.panel-controls-left  { flex: 1; min-width: 0; }
.panel-controls-right { flex: 0 0 auto; }
.panel-controls .panel-eyebrow { padding-bottom: 0; margin-bottom: 0; border-bottom: 0; }


/* ─── 4. Header ──────────────────────────────────────────────────────────────── */

.admin-header {
  position: relative;
  z-index: 50;
  width: 100%;
  background: linear-gradient(180deg, rgba(255, 255, 255, .06), rgba(255, 255, 255, .03));
  border-bottom: 1px solid var(--stroke);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow-soft);
}

.header-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  max-width: 1760px;
  height: 60px;
  margin: 0 auto;
  padding: 0 24px;
}

.brand-center {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  /* No gap here: "Spiri"+"ONE" are separate spans (for the two-tone colour)
     but must read as one word. Only .admin-title gets its own margin below.
     center (not baseline): the bordered .admin-title chip has its own
     padding/border, so lining up text baselines put it visibly off-centre
     against the much taller wordmark. */
}

/* Matches public/gui/assets/css/gui.css .logo / .logo--a / .logo--b exactly
   (same font, weight, letter-spacing, colors) — only the size differs, scaled
   for the persistent header bar the same way .corner-brand .logo is on the
   public site (2.65rem hero size * 1.15 / 1.7, not the raw hero size). */
.logo {
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  font-family: LoremIpsum, Inter, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  font-size: calc(1.7rem * 1.15);
  font-style: normal;
  font-weight: normal;
  text-transform: none !important;
  letter-spacing: .02em;
  display: inline-block;
  line-height: 1;
  /* LoremIpsum's own ascent/descent metrics sit noticeably higher than its
     ink — flexbox centers the (correctly-sized) line box, not the visible
     glyphs, so the wordmark reads as riding above center next to the
     hamburger icon. Measured via canvas actualBoundingBoxAscent/Descent
     against the font's own fontBoundingBoxAscent/Descent: ~0.08em of the
     ink sits above where the box's true center is. em-based so it scales
     with the logo's own responsive font-size. */
  transform: translateY(.08em);
}
.logo--a { color: var(--text); }
.logo--b {
  color: var(--brand-b);
  text-shadow: 0 0 24px rgba(232, 222, 106, .55);
}

.admin-title {
  margin-left: 10px;
  font-size: .62rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .08em;
  white-space: nowrap;
  border: 1px solid var(--stroke);
  border-radius: 5px;
  padding: 3px 7px;
}

/* Section title + description, as a slim row inside the header instead of
   each section repeating it as its own boxed panel. */
.admin-subheader { border-top: 1px solid rgba(255, 255, 255, .06); }
.admin-subheader-inner {
  max-width: 1760px;
  margin: 0 auto;
  padding: 8px 24px;
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: .82rem;
}
.admin-subheader-inner strong { color: var(--text); font-weight: 600; flex: none; }
.admin-subheader-desc { color: var(--muted); opacity: .85; }
.admin-subheader-desc code {
  font-family: var(--mono);
  font-size: .92em;
  background: rgba(255, 255, 255, .06);
  padding: 1px 5px;
  border-radius: 4px;
}
.admin-subheader-desc em { font-style: normal; color: var(--text); }


/* ─── 5. Navigation — quiet underline tabs (desktop), hamburger dropdown (mobile) */

.nav {
  display: flex;
  align-items: stretch;
  height: 100%;
  flex: 1 1 auto;
  min-width: 0;
}

.nav__toggle {
  display: none; /* shown only under the mobile breakpoint */
}

.nav__list {
  display: flex;
  align-items: stretch;
  height: 100%;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 4px;
}

.nav__item { display: flex; }
.nav__item--auth { margin-left: auto; }

.nav__link {
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 14px;
  margin-bottom: -1px; /* overlaps .admin-header's border-bottom */
  color: var(--muted);
  text-decoration: none;
  font-weight: 500;
  font-size: .9rem;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: color .15s ease, border-color .15s ease;
}
.nav__link:hover { color: var(--text); }
.nav__link--active {
  color: var(--text);
  border-bottom-color: var(--brand-a);
}

/* Auth item (Login/Logout): a quiet text action, not a page tab */
.nav__item--auth .nav__link {
  border-bottom: none;
  color: var(--muted);
  font-size: .85rem;
}
.nav__item--auth .nav__link:hover { color: var(--text); }

.nav__logout-form { margin: 0; }

.inline-form { display: inline; }

/* Desktop nav polish — pill tabs instead of the old quiet underline tabs, plus
   a sticky header. Scoped to min-width:801px so the mobile hamburger dropdown
   (styled in the ≤800px block further down) is left completely untouched. */
@media (min-width: 801px) {
  .admin-header {
    position: sticky;
    top: 0;
  }

  /* Three real zones: logo hard-left, nav dead-centre, auth hard-right.
     A 1fr / auto / 1fr grid keeps the centre column visually centred in the
     whole bar regardless of how wide the logo or the auth button are (equal
     1fr side columns balance each other). The auth item lives inside
     .nav__list for the mobile dropdown, so on desktop it's lifted out of the
     centred flow and pinned to the right edge instead — see below. */
  .header-content {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    position: relative;
  }
  .brand-center { justify-self: start; }

  .nav {
    justify-content: center;
    flex: none;
  }

  .nav__list { gap: 6px; }

  .nav__item { align-items: center; }

  /* Each tab is a self-contained pill, vertically centred in the bar rather
     than filling its full height (the underline-tab model). */
  .nav__link {
    height: auto;
    align-self: center;
    padding: 8px 15px;
    margin-bottom: 0;
    border-bottom: none;
    border-radius: 9px;
    color: var(--muted);
    transition: background-color .15s ease, color .15s ease, box-shadow .15s ease;
  }
  .nav__link:hover {
    color: var(--text);
    background: rgba(255, 255, 255, .06);
  }
  .nav__link--active {
    color: var(--text);
    border-bottom: none;
    background: linear-gradient(180deg, rgba(140, 196, 174, .20), rgba(140, 196, 174, .10));
    box-shadow: inset 0 0 0 1px rgba(140, 196, 174, .30);
  }
  .nav__link--active:hover {
    background: linear-gradient(180deg, rgba(140, 196, 174, .26), rgba(140, 196, 174, .14));
  }

  /* Auth action (Login/Logout): a quiet outlined button, pinned to the far
     right of the bar and lifted out of the centred nav flow so it doesn't drag
     the tab group off-centre. right:24px matches the header's own 24px padding,
     so its right edge lines up with the logo's left edge. */
  .nav__item--auth {
    position: absolute;
    right: 24px;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 0;
  }
  .nav__item--auth .nav__link {
    font-size: .85rem;
    padding: 7px 15px;
    border: 1px solid var(--stroke);
    border-radius: 9px;
    color: var(--muted);
  }
  .nav__item--auth .nav__link:hover {
    color: var(--text);
    border-color: var(--brand-a);
    background: rgba(140, 196, 174, .08);
  }
}


/* ─── 6. Config rows ─────────────────────────────────────────────────────────── */

.config-key         { font-weight: 600; color: var(--text); margin-bottom: 6px; }
.config-description { color: var(--muted); font-size: .9rem; }

/* Prevent inputs from stretching the row while in edit mode */
.edit-mode .edit-input,
.edit-mode .edit-description,
.edit-mode input,
.edit-mode select {
  max-width: 100%;
  box-sizing: border-box;
}

.edit-mode--hidden { display: none; }
.edit-mode--mt8 { margin-top: 8px; }

/* Save/Cancel start hidden — editRow()/cancelEdit() toggle these via inline style.
   Scoped selector (not just .save-btn/.cancel-btn) so it outranks the later,
   equal-specificity .btn { display: inline-block } rule regardless of source order. */
.tool-row-actions .save-btn, .tool-row-actions .cancel-btn { display: none; }


/* ─── 7. Section headings ────────────────────────────────────────────────────── */
.admin-section > .panel-section > h2,
.panel > .panel-section > h2 {
  display: inline-block;
  color: var(--text);
  font-weight: 300;
  border-bottom: 3px solid var(--accent-cyan);
  padding-bottom: 8px;
  margin-bottom: 8px;
  text-shadow: 0 0 10px var(--glow-accent);
}


/* ─── 7. Buttons ─────────────────────────────────────────────────────────────── */

/* Shared action-button base */
action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 14px;
  font-size: 14px;
  border-radius: 10px;
  min-width: 78px;
  box-sizing: border-box;
  cursor: pointer;
  border: 1px solid transparent;
}

.btn-small {
  padding: 6px 10px;
  font-size: 13px;
  border-radius: 8px;
  border: 1px solid var(--stroke);
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
}
.btn-small:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-soft);
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--muted);
  border-radius: 0; /* override any inherited border-radius */
}
.icon-btn svg { width: 19px; height: 19px; display: block; }
.icon-btn:hover           { color: var(--brand-a); }
.icon-btn:focus           { outline: none; box-shadow: none; }
.icon-btn:focus-visible   { outline: 2px solid var(--brand-a); outline-offset: 2px; }


/* ─── 8. Form controls ───────────────────────────────────────────────────────── */

.edit-input,
.edit-description {
  background: var(--bg-1);
  color: var(--text);
  border: 1px solid var(--stroke);
  padding: 6px 8px;
  border-radius: 6px;
}
.edit-input::placeholder,
.edit-description::placeholder { color: var(--muted); }

.form-row          { margin-bottom: 12px; }
.form-row label    { display: block; font-size: .95rem; color: var(--muted); margin-bottom: 6px; }
.form-row input    { width: 100%; padding: 10px; border-radius: 8px; border: 1px solid var(--stroke); background: var(--bg-1); color: var(--text); }
.form-actions      { display: flex; gap: 12px; align-items: center; margin-top: 18px; }

.field-hint          { font-size: .82rem; margin-top: 6px; color: var(--muted); }
.field-hint--good    { color: var(--status-good); }
.field-hint--bad     { color: var(--status-bad); }


/* ─── 9. Toasts ──────────────────────────────────────────────────────────────── */

.admin-toast {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 1100;
  padding: 12px 18px;
  border-radius: 10px;
  background: rgba(0, 0, 0, .7);
  color: var(--text);
  box-shadow: 0 8px 30px rgba(0, 0, 0, .6);
  opacity: 0;
  transform: translateY(8px);
  transition: all .25s ease;
}
.admin-toast--visible { opacity: 1; transform: translateY(0); }
.admin-toast--success { background: linear-gradient(90deg, var(--accent-cyan), #5ee5a9); color: var(--bg-0); }
.admin-toast--error   { background: linear-gradient(90deg, var(--danger), #a33);       color: #fff; }




/* ─── 11. Log viewer ─────────────────────────────────────────────────────────── */

/* Each log row and its preview are wrapped in a .log-item; the preview expands
   inline directly beneath its own row (accordion) instead of in one panel at
   the bottom of the list. The row/preview divider is the preview's own dashed
   border-top, so the item divider moves off .tool-row and onto .log-item —
   otherwise an expanded row would show a double line (solid row border +
   dashed preview border) between the row and its content. */
.log-item { border-bottom: 1px solid var(--stroke); }
.log-item:last-child { border-bottom: 0; }
.log-item .tool-row { border-bottom: 0; padding: 14px 0; }
.log-item:first-child .tool-row { padding-top: 0; }

.log-preview {
  margin: 12px 0 2px;
  padding-top: 12px;
  border-top: 1px dashed var(--stroke);
}
.log-preview[hidden] { display: none; }

.log-preview-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}
.log-preview-title {
  min-width: 0;
  font-family: var(--mono);
  font-size: .85rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.log-copy-btn { flex: none; }

/* Delete is a destructive action — tint it on hover so it reads apart from the
   neutral View/Download buttons. */
.log-delete-btn:hover { color: var(--status-bad); }

.log-view {
  white-space: pre-wrap;
  max-height: 520px;
  overflow: auto;
  background: var(--bg-1);
  border: 1px solid var(--stroke);
  padding: 12px;
  border-radius: 8px;
  color: var(--text);

  /* Custom scrollbar */
  scrollbar-width: thin;
  scrollbar-color: var(--stroke) var(--bg-0);
}
.log-view::-webkit-scrollbar        { width: 12px; }
.log-view::-webkit-scrollbar-track  { background: var(--bg-0); }
.log-view::-webkit-scrollbar-thumb  { background: var(--stroke); border-radius: 6px; }
.log-view::-webkit-scrollbar-thumb:hover { background: var(--brand-a); }


/* ─── 12. Login page ─────────────────────────────────────────────────────────── */

.login-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: linear-gradient(180deg, var(--bg-0), var(--bg-1));
}

.login-card {
  width: 100%;
  max-width: 520px;
  background: var(--panel-flat);
  border: 1px solid var(--stroke);
  border-radius: var(--radius-md);
  padding: 28px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, .6);
  color: var(--text);
}
.login-note { margin-top: 16px; color: var(--muted); font-size: .9rem; }

.error-box {
  background: var(--status-bad-bg);
  color: var(--status-bad);
  padding: 10px;
  border-radius: 8px;
  margin-bottom: 12px;
  border: 1px solid rgba(255, 141, 146, .35);
}

.success-box {
  background: var(--status-good-bg);
  color: var(--status-good);
  padding: 10px;
  border-radius: 8px;
  margin-bottom: 12px;
  border: 1px solid rgba(74, 222, 128, .35);
}

.muted-link { color: var(--muted); font-size: .9rem; }


/* ─── 13. Confirmation modal ─────────────────────────────────────────────────── */

#confirmModal {
  display: none; /* JS switches to flex */
  position: fixed;
  inset: 0;       /* shorthand for top/right/bottom/left: 0 */
  z-index: 10000;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, .4);
}
#confirmModal .confirm-dialog {
  background: var(--bg-0); /* use solid background to avoid translucent dialog */
  border: 1px solid var(--stroke);
  padding: 16px;
  border-radius: 8px;
  max-width: 480px;
  margin: 0 auto;
  color: var(--text);
}
#confirmModal .confirm-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
#confirmModal .modal-cancel  { background: var(--panel);   color: var(--text); border-color: var(--stroke); }
#confirmModal .modal-confirm { background: var(--brand-b); color: var(--bg-0); border-color: var(--brand-b); }
#confirmModal .confirm-dialog h3 { margin: 0 0 8px; }
#confirmModal #confirmModalBody  { margin-bottom: 12px; }


/* ─── 14. Shared section framework (panel, stat row, pill, table, buttons) ────── */
/* Used across every section — introduced with the Dashboard rework, now also
   carrying Tools, Logs, Config and Donations. */

.panel {
  background: var(--panel-flat);
  border: 1px solid var(--stroke);
  border-radius: var(--radius-md);
  padding: 16px 18px;
}
.panel--flat { background: transparent; border: none; padding: 2px 0 0; }

.panel-eyebrow {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--brand-a);
  padding-bottom: 10px;
  margin: 0 0 14px;
  border-bottom: 1px solid var(--stroke);
}

/* Eyebrow + tap-to-reveal info toggle (has to work on touch, not just hover) */
.panel-eyebrow-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-bottom: 10px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--stroke);
}
.panel-eyebrow-row .panel-eyebrow { padding-bottom: 0; margin-bottom: 0; border-bottom: 0; }
.info-btn {
  flex: none;
  width: 15px;
  height: 15px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--stroke);
  background: transparent;
  color: var(--muted);
  font-size: .6rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.info-btn:hover, .info-btn[aria-expanded="true"] { border-color: var(--brand-a); color: var(--brand-a); }
.panel-info {
  font-size: .8rem;
  color: var(--muted);
  background: rgba(255, 255, 255, .03);
  border: 1px solid rgba(255, 255, 255, .06);
  border-radius: 6px;
  padding: 8px 10px;
  margin: -2px 0 10px;
}

/* Label/value fact row — the base unit most panels are built from */
.stat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid rgba(255, 255, 255, .06);
}
.stat-row:last-child { border-bottom: 0; padding-bottom: 0; }
.stat-row:first-child { padding-top: 0; }
.stat-label { color: var(--muted); font-size: .88rem; flex: none; }
.stat-value {
  display: flex; align-items: center; gap: 8px; font-size: .88rem;
  min-width: 0; /* flex items default to min-width:auto — without this, long
                   content (a URL, raw JSON) pushes the row wider than its
                   panel instead of shrinking to fit it */
  justify-content: flex-end;
  text-align: right;
}
.stat-value.mono, .stat-value .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* A stat-value's own long text (URL, JSON, "12m ago · 3 pending") truncates
   with an ellipsis; a pill sibling in the same row stays full-size and never
   gets clipped (.pill itself is flex:none — see the pill rules below). */
.stat-value > *:not(.pill) { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Big headline number for a panel's single most important fact */
.headline { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.headline-value { font-family: var(--mono); font-size: 1.9rem; font-weight: 600; letter-spacing: -.01em; color: var(--text); }
.headline-meta { color: var(--muted); font-size: .88rem; }

/* Semantic status pill — good/bad/warn/neutral only, never a brand accent */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .02em;
  padding: 3px 9px;
  border-radius: 20px;
  white-space: nowrap;
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.pill--good { background: var(--status-good-bg); color: var(--status-good); }
.pill--bad  { background: var(--status-bad-bg);  color: var(--status-bad); }
.pill--warn { background: var(--status-warn-bg); color: var(--status-warn); }
.pill--on   { background: var(--status-on-bg); color: var(--status-on); }

/* Off/neutral pills carry no hue, so a background tint alone reads too close
   to plain text at this size — a border gives them a real edge instead of
   blending into the paragraph around them. */
.pill--off, .pill--neutral {
  background: rgba(255, 255, 255, .05);
  color: var(--muted);
  border: 1px solid var(--stroke);
}
.pill--off::before, .pill--neutral::before { display: none; }

/* Compact action row — for a couple of utility buttons, not a status surface */
.action-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.action-bar .hint { color: var(--muted); opacity: .7; font-size: .82rem; margin-right: auto; }

/* Flat button set (separate from the older .btn-small used elsewhere for now) */
.btn {
  font-family: inherit;
  font-size: .83rem;
  font-weight: 600;
  border-radius: 7px;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
  transition: transform .12s ease, box-shadow .12s ease, filter .12s ease, border-color .12s ease, color .12s ease, background-color .12s ease;
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--brand-a); outline-offset: 2px; }

.btn--primary {
  background: linear-gradient(180deg, #f2ea8f, var(--brand-b));
  color: #26210a;
  border: 0;
  border-radius: 7px;
  padding: 6px 13px;
  font-size: .78rem;
  font-weight: 500;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .3) inset, 0 3px 8px rgba(232, 222, 106, .12);
}
.btn--primary:hover {
  box-shadow: 0 1px 0 rgba(255, 255, 255, .35) inset, 0 4px 12px rgba(232, 222, 106, .2);
  transform: translateY(-1px);
}
.btn--primary:active {
  transform: translateY(0);
  filter: brightness(.95);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .15) inset, 0 2px 5px rgba(232, 222, 106, .14);
}

.btn--secondary { background: rgba(255, 255, 255, .06); color: var(--text); border: 0; padding: 6px 13px; font-size: .8rem; }
.btn--secondary:hover { background: rgba(255, 255, 255, .12); }
.btn--secondary:active { background: rgba(255, 255, 255, .16); }

/* One row per item — name/description on the left, action(s) on the right.
   Introduced for Tools, reused by Logs for its file list. */
.tool-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 0;
  border-bottom: 1px solid var(--stroke);
}
.tool-row:first-child { padding-top: 0; }
.tool-row:last-child { border-bottom: 0; padding-bottom: 0; }
.tool-info { min-width: 0; }
.tool-name { font-weight: 600; color: var(--text); font-size: .92rem; margin-bottom: 3px; }
.tool-desc { color: var(--muted); font-size: .82rem; line-height: 1.4; }
.tool-row .btn { flex: none; }
.tool-row-actions { display: flex; gap: 8px; flex: none; }

/* Centered placeholder for an empty/loading/error list state */
.list-empty { padding: 20px 0; text-align: center; color: var(--muted); font-size: .88rem; }

@media (max-width: 500px) {
  .tool-row { flex-direction: column; align-items: stretch; gap: 8px; }
  .tool-row .btn, .tool-row-actions { align-self: flex-end; }
}

/* Config: one panel per category, each a .tool-row list of settings.
   Mobile default: panels just stack, one full-width column — extra breathing
   room between them since the standard 16px gap read as too tight once
   several categories stack in a row. */
#config .cfg-grid .panel + .panel { margin-top: 26px; }

/* Desktop: pack the category panels into a masonry instead of one very long
   column. Category size varies wildly (2 settings vs. 15+), so — unlike
   Dashboard's equal-height grid, which relies on its cards being roughly the
   same size — a CSS multi-column masonry is the fit here: it flows panels
   top-to-bottom per column and packs tightly without stretching a short
   category to match a tall neighbour. It's also inherently immune to the
   Dashboard mobile-grid bug (a grid track won't shrink below a long value's
   min-content width): a multicol column's width comes from the `columns`
   declaration itself, never from its content, so it can't be blown wider by
   a long config value. Scoped to min-width:801px — mobile keeps the single
   stacked column above. break-inside:avoid keeps each category whole. */
@media (min-width: 801px) {
  #config .cfg-grid {
    columns: 3 380px;
    column-gap: 18px;
  }
  #config .cfg-grid .panel {
    break-inside: avoid;
    margin-bottom: 18px;
  }
  /* Replaces the mobile stacking margin — column-gap handles the horizontal
     gap and margin-bottom the vertical one; a leftover margin-top would sit
     unevenly since it's based on DOM order, not each column's own flow. */
  #config .cfg-grid .panel + .panel { margin-top: 0; }
}

/* A category's own long, unbroken values (an API key, a file path) fit fine
   at full page width but can overflow a narrow masonry column — let them
   break rather than push the panel wider than its column. */
#config .cfg-value, #config .tool-name { overflow-wrap: anywhere; }
.cfg-type {
  display: inline-block;
  font-size: .65rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
  background: rgba(255, 255, 255, .06);
  padding: 1px 6px;
  border-radius: 4px;
  margin-left: 8px;
  vertical-align: middle;
}
.cfg-value { font-family: var(--mono); font-size: .9rem; margin-top: 6px; }
.cfg-locked {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  color: var(--muted);
}
.cfg-locked svg { width: 16px; height: 16px; }

/* Dashboard: grouped into titled sections, each a symmetric row of related
   cards. A real grid (not masonry) so every card in a section shares its row
   height and the columns line up dead-even — the layout reads as aligned and
   symmetric, which the old ragged 4-column masonry did not. Cards are grouped
   by relation: "System & data" (engine state, data sources, alerting) and
   "Release & activity" (deployment, donations, image search). */
#dashboard .dash-section { margin-top: 24px; }
#dashboard .dash-section:first-child { margin-top: 12px; }

#dashboard .dash-section-title {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  /* Same yellow as the "ONE" in the SpiriONE wordmark (--brand-b), with the
     same soft glow, so the section headers echo the brand mark. */
  color: var(--brand-b);
  text-shadow: 0 0 18px rgba(232, 222, 106, .45);
  margin: 0 0 14px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--stroke);
}

/* Mobile default: one card per row. minmax(0, 1fr) (not bare 1fr) — a grid
   track won't shrink below its content's min-content width otherwise, so a
   long stat value (image URL, stimuli JSON, an alerting recipient email)
   forces the track — and the whole page — wider than the viewport. */
#dashboard .dash-group {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
}

/* Desktop: three equal columns per section. Grid's default align-items:stretch
   makes every card in a row the same height, so rows stay level and symmetric
   regardless of how much each card holds. Scoped to min-width:801px so mobile
   keeps its single column. minmax(0, 1fr) (not bare 1fr) lets a long stat value
   — image URL, stimuli JSON — shrink instead of forcing the track wider. */
@media (min-width: 801px) {
  #dashboard .dash-group {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* Stimuli row truncation/right-alignment now comes for free from the generic
   .stat-value > *:not(.pill) rule (§ shared framework) — #system-stimuli is a
   child span, not .stat-value itself, same pattern as image-search-url. */

/* Stimuli expanded box */
#dashboard .stimuli-full {
  display: none;
  margin-top: 10px;
  background: var(--bg-0);
  border: 1px solid var(--stroke);
  padding: 8px;
  border-radius: 6px;
  font-family: var(--mono);
  font-size: .85rem;
  white-space: pre-wrap;
  max-height: 160px;
  overflow: auto;
}

/* Sources status row: the meta text (last record, plus "last attempt failed
   …" when a source is currently down) can run longer than fits on one line,
   especially on mobile. Let it wrap instead of the shared ellipsis-truncate
   rule cutting it off — this is diagnostic text the admin needs to read in
   full, not a label that's fine abbreviated. */
#sources-status .stat-row,
#alerting-status .stat-row { align-items: flex-start; }
#sources-status .stat-value > *:not(.pill),
#alerting-status .stat-value > *:not(.pill) {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

/* Image search URL row */
#dashboard #image-search-url-row .stat-value {
  max-width: 60%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#dashboard #image-search-url {
  color: var(--muted);
  font-size: .85rem;
}


/* ─── 15. Utilities ──────────────────────────────────────────────────────────── */

.shadow-soft   { box-shadow: var(--shadow-soft); }
.shadow-strong { box-shadow: var(--shadow-strong); }
.radius-lg     { border-radius: var(--radius-lg); }
.radius-md     { border-radius: var(--radius-md); }
.text-muted    { color: var(--muted); }
.brand-a       { color: var(--brand-a); }
.brand-b       { color: var(--brand-b); }
.is-hidden     { display: none !important; }


/* ─── 17. Accessibility — reduced motion ─────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
}


/* ─── 18. Responsive — ≤ 800px ──────────────────────────────────────────────── */

@media (max-width: 800px) {
  .admin-main    { padding: 12px; margin: 18px auto; }

  .header-content { padding: 0 12px; height: 50px; }
  /* The title repeats the already-active nav tab right above it — drop it here
     and keep just the description as a quiet single caption, not a second
     bold heading that wraps onto its own line. */
  .admin-subheader-inner { padding: 6px 12px; font-size: .76rem; }
  .admin-subheader-inner strong { display: none; }
  .admin-title    { margin-left: 6px; padding: 2px 5px; font-size: .56rem; }

  /* Nav collapses behind a hamburger toggle — the tab strip would otherwise
     either wrap into multiple rows or need horizontal scrolling as sections
     are added, and neither reads well at phone width. */
  .nav { flex: 0 0 auto; justify-content: flex-end; }

  .nav__toggle {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    align-self: center; /* .nav's align-items:stretch falls back to flex-start
                            for a child with a fixed height like this one */
    gap: 4px;
    width: 34px;
    height: 34px;
    background: transparent;
    border: 1px solid var(--stroke);
    border-radius: 7px;
    cursor: pointer;
  }
  .nav__toggle-bar {
    display: block;
    width: 16px;
    height: 2px;
    background: var(--text);
    border-radius: 1px;
    transition: transform .18s ease, opacity .18s ease;
  }
  .nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(2) { opacity: 0; }
  .nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

  /* Dropdown panel: hidden by default, opened via JS toggling .is-open */
  .nav__list {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    height: auto;
    gap: 0;
    background: var(--bg-1);
    border-bottom: 1px solid var(--stroke);
    box-shadow: var(--shadow-strong);
  }
  .nav__list.is-open { display: flex; }

  .nav__item { display: block; }
  .nav__link {
    height: auto;
    width: 100%;
    padding: 13px 16px;
    margin-bottom: 0;
    border-bottom: 1px solid rgba(255, 255, 255, .05);
    border-left: 2px solid transparent;
  }
  .nav__link--active {
    border-bottom-color: rgba(255, 255, 255, .05);
    border-left-color: var(--brand-a);
    background: rgba(140, 196, 174, .06);
  }
  .nav__item--auth { margin-left: 0; }
  .nav__item--auth .nav__link { color: var(--muted); }

  /* Simpler background on mobile (no radial gradients for performance) */
  body { background: linear-gradient(180deg, var(--bg-0), var(--bg-1)); }

  /* Remove backdrop-filter on header (expensive on low-end devices) */
  .admin-header  { backdrop-filter: none; }

  /* Dashboard: single column (already the .dash-group default), tighter panel
     padding. The section-title border still reads fine at phone width. */
  #dashboard .dash-group { gap: 12px; }
  .panel { padding: 13px 14px; }

  /* Login card: breathing room instead of touching the screen edges */
  .login-page  { padding: 16px; }
  .login-card  { padding: 20px; }
}


/* ─── 19. Responsive — ≤ 420px (small phones) ───────────────────────────────── */

@media (max-width: 420px) {
  .nav__list .nav__link { padding: 11px 14px; font-size: .87rem; }
  .login-card    { padding: 16px; }
}

/* Matches public/gui/assets/css/gui.css's own .corner-brand .logo breakpoint */
@media (max-width: 380px) {
  .logo { font-size: 1.7rem; }
}
