/* MailGuard stylesheet — generated by scripts/build-css.php.
   Do not edit. Edit the modules in assets/css/ and rebuild. */

/* ── 00-theme.css ── */
/* MailGuard — theme tokens
   Concatenated by scripts/build-css.php into assets/app.css.
   Edit here, never the built file. */

/* ================================================================
   Theme
   ================================================================

   Dark is the product's default because that is the interface
   administrators know. Light is a full, separately designed palette,
   not an inversion: the accent and semantic hues are darkened for it,
   because #22d3ee on white measures about 1.6:1 and is unreadable as
   text however good it looks on #090d16.

   Resolution order, weakest to strongest:
     1. :root                          → dark, the default
     2. prefers-color-scheme: light    → the operating system's choice
     3. [data-theme="light|dark"]      → the toggle, which always wins

   Components must only ever read these variables. A component that
   hard-codes a colour will look correct in one theme and wrong in the
   other, and nothing in review will catch it.
   ================================================================ */

:root {
  color-scheme: dark;

  /* Backgrounds */
  --mg-bg:              #090d16;
  --mg-bg-secondary:    #0f1422;
  --mg-bg-tertiary:     #161d2d;

  /* Surfaces — light tint over a dark ground */
  --mg-surface:         rgba(255,255,255,0.03);
  --mg-surface-hover:   rgba(255,255,255,0.06);
  --mg-surface-strong:  rgba(255,255,255,0.08);

  /* Borders */
  --mg-border:          rgba(255,255,255,0.06);
  --mg-border-strong:   rgba(255,255,255,0.12);
  --mg-border-hover:    rgba(34,211,238,0.30);

  /* Primary (purple) */
  --mg-primary:         #8b5cf6;
  --mg-primary-light:   #a78bfa;
  --mg-primary-dark:    #7c3aed;
  --mg-primary-glow:    rgba(139,92,246,0.25);
  --mg-primary-soft:    rgba(139,92,246,0.12);

  /* Accent (cyan) */
  --mg-accent:          #22d3ee;
  --mg-accent-light:    #67e8f9;
  --mg-accent-glow:     rgba(34,211,238,0.25);
  --mg-accent-soft:     rgba(34,211,238,0.12);

  /* Semantic */
  --mg-danger:          #ef4444;
  --mg-danger-soft:     rgba(239,68,68,0.12);
  --mg-success:         #10b981;
  --mg-success-soft:    rgba(16,185,129,0.12);
  --mg-warning:         #f59e0b;
  --mg-warning-soft:    rgba(245,158,11,0.12);
  --mg-info:            #3b82f6;
  --mg-info-soft:       rgba(59,130,246,0.12);
  --mg-surface-subtle:  rgba(255,255,255,0.015);
  --mg-danger-strong:   #dc2626;
  --mg-danger-light:    #f87171;
  --mg-warning-light:   #fb923c;
  --mg-success-light:   #34d399;

  /* Chart series -- identity, not status.
   *
   * Chart.php has referenced --chart-1..7 since it was written and no
   * stylesheet ever defined them, so every chart in the product has drawn with
   * the hardcoded fallbacks in its own source: one palette for both themes,
   * chosen by eye, from a class whose comment promises the page's theme wins.
   *
   * These are computed rather than picked. Six hues evenly spaced around the
   * OKLCH wheel with lightness alternating inside this mode's band, because hue
   * alone cannot separate six slots for a protan or deutan reader -- the
   * red-green axis collapses and neighbours converge, so every adjacent pair
   * differs in lightness as well as hue.
   *
   * Verified, not assumed: tests/chart-palette.php runs the five computable
   * checks -- lightness band, chroma floor, colourblind separation of adjacent
   * pairs, the normal-vision floor, and contrast against this mode's chart
   * surface -- and fails the suite on any of them.
   *
   * Status colours are deliberately absent. Delivered, held and refused are
   * states rather than series: they wear --mg-success, --mg-warning and
   * --mg-danger, always with a written label beside them.
   */
  --chart-1:            #aa442d;
  --chart-2:            #aa8d06;
  --chart-3:            #047e44;
  --chart-4:            #02a0b9;
  --chart-5:            #515fb8;
  --chart-6:            #c16cb2;
  --chart-7:            #7c8794;
  --chart-surface:      #0f1422;
  --chart-grid:         rgba(255,255,255,0.08);

  /* Text */
  --mg-text:            #f8fafc;
  --mg-text-secondary:  #cbd5e1;
  --mg-text-muted:      #64748b;
  --mg-text-inverse:    #0f172a;

  /* Shadows */
  --mg-shadow:          0 4px 24px rgba(0,0,0,0.30);
  --mg-shadow-lg:       0 8px 40px rgba(0,0,0,0.45);
  --mg-shadow-sm:       0 2px 8px rgba(0,0,0,0.20);

  /* Chrome */
  --mg-header-bg:       rgba(15,20,34,0.85);
  --mg-scrim:           rgba(0,0,0,0.60);
  --mg-scrollbar:       rgba(255,255,255,0.08);
  --mg-scrollbar-hover: rgba(255,255,255,0.14);

  /* Typography */
  --mg-font:            system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  --mg-mono:            ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /* Radii */
  --mg-radius:          12px;
  --mg-radius-md:       8px;
  --mg-radius-sm:       6px;
  --mg-radius-full:     9999px;

  /* Layout */
  --mg-sidebar-width:   272px;
  --mg-topbar-height:   68px;
  --mg-content-max:     1380px;

  /* Motion */
  --mg-ease:            cubic-bezier(0.4, 0, 0.2, 1);
  --mg-duration:        0.2s;
}

/* ----------------------------------------------------------------
   Light palette
   ----------------------------------------------------------------
   The neutrals carry a slight violet bias toward the primary rather
   than being a pure grey, so the ground reads as chosen. Surfaces
   invert direction: a dark tint over a light ground, because a white
   tint over white is invisible.
   ---------------------------------------------------------------- */
@media (prefers-color-scheme: light) {
  :root {
    color-scheme: light;

    --mg-bg:              #f6f7fb;
    --mg-bg-secondary:    #ffffff;
    --mg-bg-tertiary:     #eef1f7;

    --mg-surface:         rgba(15,23,42,0.020);
    --mg-surface-hover:   rgba(15,23,42,0.045);
    --mg-surface-strong:  rgba(15,23,42,0.070);

    --mg-border:          rgba(15,23,42,0.10);
    --mg-border-strong:   rgba(15,23,42,0.18);
    --mg-border-hover:    rgba(124,58,237,0.40);

    /* Darkened so they remain legible as text on white */
    --mg-primary:         #7c3aed;
    --mg-primary-light:   #6d28d9;
    --mg-primary-dark:    #5b21b6;
    --mg-primary-glow:    rgba(124,58,237,0.18);
    --mg-primary-soft:    rgba(124,58,237,0.10);

    --mg-accent:          #0e7490;
    --mg-accent-light:    #0891b2;
    --mg-accent-glow:     rgba(14,116,144,0.18);
    --mg-accent-soft:     rgba(14,116,144,0.10);

    --mg-danger:          #dc2626;
    --mg-danger-soft:     rgba(220,38,38,0.10);
    --mg-success:         #047857;
    --mg-success-soft:    rgba(4,120,87,0.10);
    --mg-warning:         #b45309;
    --mg-warning-soft:    rgba(180,83,9,0.10);
    --mg-info:            #2563eb;
    --mg-info-soft:       rgba(37,99,235,0.10);
    --mg-surface-subtle:  rgba(15,23,42,0.012);
    --mg-danger-strong:   #b91c1c;
    --mg-danger-light:    #dc2626;
    --mg-warning-light:   #c2410c;
    --mg-success-light:   #059669;

    /* Chart series -- the same construction, stepped for this mode's surface. */
    --chart-1:            #9e2e21;
    --chart-2:            #a07f03;
    --chart-3:            #046e30;
    --chart-4:            #0a94a5;
    --chart-5:            #3b51ae;
    --chart-6:            #b45ead;
    --chart-7:            #5b6674;
    --chart-surface:      #ffffff;
    --chart-grid:         rgba(15,23,42,0.10);

    --mg-text:            #0f172a;
    --mg-text-secondary:  #334155;
    --mg-text-muted:      #64748b;
    --mg-text-inverse:    #f8fafc;

    --mg-shadow:          0 4px 16px rgba(15,23,42,0.08);
    --mg-shadow-lg:       0 12px 32px rgba(15,23,42,0.12);
    --mg-shadow-sm:       0 1px 3px rgba(15,23,42,0.06);

    --mg-header-bg:       rgba(255,255,255,0.85);
    --mg-scrim:           rgba(15,23,42,0.45);
  --mg-scrollbar:       rgba(15,23,42,0.15);
    --mg-scrollbar-hover: rgba(15,23,42,0.28);
  }
}

/* Explicit choice from the toggle. Repeated rather than shared because
   a media query cannot be overridden by an attribute selector alone. */
:root[data-theme="light"] {
  color-scheme: light;

  --mg-bg:              #f6f7fb;
  --mg-bg-secondary:    #ffffff;
  --mg-bg-tertiary:     #eef1f7;

  --mg-surface:         rgba(15,23,42,0.020);
  --mg-surface-hover:   rgba(15,23,42,0.045);
  --mg-surface-strong:  rgba(15,23,42,0.070);

  --mg-border:          rgba(15,23,42,0.10);
  --mg-border-strong:   rgba(15,23,42,0.18);
  --mg-border-hover:    rgba(124,58,237,0.40);

  --mg-primary:         #7c3aed;
  --mg-primary-light:   #6d28d9;
  --mg-primary-dark:    #5b21b6;
  --mg-primary-glow:    rgba(124,58,237,0.18);
  --mg-primary-soft:    rgba(124,58,237,0.10);

  --mg-accent:          #0e7490;
  --mg-accent-light:    #0891b2;
  --mg-accent-glow:     rgba(14,116,144,0.18);
  --mg-accent-soft:     rgba(14,116,144,0.10);

  --mg-danger:          #dc2626;
  --mg-danger-soft:     rgba(220,38,38,0.10);
  --mg-success:         #047857;
  --mg-success-soft:    rgba(4,120,87,0.10);
  --mg-warning:         #b45309;
  --mg-warning-soft:    rgba(180,83,9,0.10);
  --mg-info:            #2563eb;
  --mg-info-soft:       rgba(37,99,235,0.10);
  --mg-surface-subtle:  rgba(15,23,42,0.012);
  --mg-danger-strong:   #b91c1c;
  --mg-danger-light:    #dc2626;
  --mg-warning-light:   #c2410c;
  --mg-success-light:   #059669;

  /* Chart series -- the same construction, stepped for this mode's surface. */
  --chart-1:            #9e2e21;
  --chart-2:            #a07f03;
  --chart-3:            #046e30;
  --chart-4:            #0a94a5;
  --chart-5:            #3b51ae;
  --chart-6:            #b45ead;
  --chart-7:            #5b6674;
  --chart-surface:      #ffffff;
  --chart-grid:         rgba(15,23,42,0.10);

  --mg-text:            #0f172a;
  --mg-text-secondary:  #334155;
  --mg-text-muted:      #64748b;
  --mg-text-inverse:    #f8fafc;

  --mg-shadow:          0 4px 16px rgba(15,23,42,0.08);
  --mg-shadow-lg:       0 12px 32px rgba(15,23,42,0.12);
  --mg-shadow-sm:       0 1px 3px rgba(15,23,42,0.06);

  --mg-header-bg:       rgba(255,255,255,0.85);
  --mg-scrim:           rgba(15,23,42,0.45);
  --mg-scrollbar:       rgba(15,23,42,0.15);
  --mg-scrollbar-hover: rgba(15,23,42,0.28);
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --mg-bg:              #090d16;
  --mg-bg-secondary:    #0f1422;
  --mg-bg-tertiary:     #161d2d;

  --mg-surface:         rgba(255,255,255,0.03);
  --mg-surface-hover:   rgba(255,255,255,0.06);
  --mg-surface-strong:  rgba(255,255,255,0.08);

  --mg-border:          rgba(255,255,255,0.06);
  --mg-border-strong:   rgba(255,255,255,0.12);
  --mg-border-hover:    rgba(34,211,238,0.30);

  --mg-primary:         #8b5cf6;
  --mg-primary-light:   #a78bfa;
  --mg-primary-dark:    #7c3aed;
  --mg-primary-glow:    rgba(139,92,246,0.25);
  --mg-primary-soft:    rgba(139,92,246,0.12);

  --mg-accent:          #22d3ee;
  --mg-accent-light:    #67e8f9;
  --mg-accent-glow:     rgba(34,211,238,0.25);
  --mg-accent-soft:     rgba(34,211,238,0.12);

  --mg-danger:          #ef4444;
  --mg-danger-soft:     rgba(239,68,68,0.12);
  --mg-success:         #10b981;
  --mg-success-soft:    rgba(16,185,129,0.12);
  --mg-warning:         #f59e0b;
  --mg-warning-soft:    rgba(245,158,11,0.12);
  --mg-info:            #3b82f6;
  --mg-info-soft:       rgba(59,130,246,0.12);
  --mg-surface-subtle:  rgba(255,255,255,0.015);
  --mg-danger-strong:   #dc2626;
  --mg-danger-light:    #f87171;
  --mg-warning-light:   #fb923c;
  --mg-success-light:   #34d399;

  /* Chart series -- the same construction, stepped for this mode's surface. */
  --chart-1:            #aa442d;
  --chart-2:            #aa8d06;
  --chart-3:            #047e44;
  --chart-4:            #02a0b9;
  --chart-5:            #515fb8;
  --chart-6:            #c16cb2;
  --chart-7:            #7c8794;
  --chart-surface:      #0f1422;
  --chart-grid:         rgba(255,255,255,0.08);

  --mg-text:            #f8fafc;
  --mg-text-secondary:  #cbd5e1;
  --mg-text-muted:      #64748b;
  --mg-text-inverse:    #0f172a;

  --mg-shadow:          0 4px 24px rgba(0,0,0,0.30);
  --mg-shadow-lg:       0 8px 40px rgba(0,0,0,0.45);
  --mg-shadow-sm:       0 2px 8px rgba(0,0,0,0.20);

  --mg-header-bg:       rgba(15,20,34,0.85);
  --mg-scrim:           rgba(0,0,0,0.60);
  --mg-scrollbar:       rgba(255,255,255,0.08);
  --mg-scrollbar-hover: rgba(255,255,255,0.14);
}

/* ----------------------------------------------------------------
   Confidence — a third palette, not a third inversion
   ----------------------------------------------------------------
   Light interfaces in this class of product usually read as the dark
   one with the lights on. This one is designed from its own premise:
   a near-white ground, white cards lifted by shadow rather than by
   border, and a single blue doing all the work that purple and cyan
   split between them elsewhere. One accent means a coloured thing on
   screen is always the thing to press.

   The neutrals carry a blue bias toward that accent, so the greys
   read as chosen rather than inherited. Radii open up slightly and
   shadows carry the elevation, because on a white ground a border
   strong enough to define a card is strong enough to fence it in.
   ---------------------------------------------------------------- */
:root[data-theme="confidence"] {
  color-scheme: light;

  --mg-bg:              #eef2f7;
  --mg-bg-secondary:    #ffffff;
  --mg-bg-tertiary:     #f5f8fc;

  --mg-surface:         rgba(37,99,235,0.030);
  --mg-surface-hover:   rgba(37,99,235,0.060);
  --mg-surface-strong:  rgba(37,99,235,0.095);
  --mg-surface-subtle:  rgba(37,99,235,0.016);

  --mg-border:          #e3e9f2;
  --mg-border-strong:   #cbd6e6;
  --mg-border-hover:    rgba(37,99,235,0.45);

  /* One accent. Primary and accent are deliberately the same blue:
     two competing highlight colours is how an operator loses track of
     which coloured thing is the button. */
  --mg-primary:         #2563eb;
  --mg-primary-light:   #3b82f6;
  --mg-primary-dark:    #1d4ed8;
  --mg-primary-glow:    rgba(37,99,235,0.22);
  --mg-primary-soft:    rgba(37,99,235,0.10);

  --mg-accent:          #2563eb;
  --mg-accent-light:    #3b82f6;
  --mg-accent-glow:     rgba(37,99,235,0.22);
  --mg-accent-soft:     rgba(37,99,235,0.10);

  /* Semantic hues stay distinct from the accent, so "good" and
     "pressable" never look like the same claim. */
  --mg-danger:          #dc2626;
  --mg-danger-soft:     rgba(220,38,38,0.09);
  --mg-danger-strong:   #b91c1c;
  --mg-danger-light:    #ef4444;
  --mg-success:         #059669;
  --mg-success-soft:    rgba(5,150,105,0.10);
  --mg-success-light:   #10b981;

  /* Chart series -- the same construction, stepped for this mode's surface. */
  --chart-1:            #9e2e21;
  --chart-2:            #a07f03;
  --chart-3:            #046e30;
  --chart-4:            #0a94a5;
  --chart-5:            #3b51ae;
  --chart-6:            #b45ead;
  --chart-7:            #5b6674;
  --chart-surface:      #ffffff;
  --chart-grid:         rgba(15,23,42,0.10);
  --mg-warning:         #d97706;
  --mg-warning-soft:    rgba(217,119,6,0.10);
  --mg-warning-light:   #f59e0b;
  --mg-info:            #2563eb;
  --mg-info-soft:       rgba(37,99,235,0.10);

  --mg-text:            #0f1b2d;
  --mg-text-secondary:  #47566b;
  --mg-text-muted:      #8494a8;
  --mg-text-inverse:    #ffffff;

  /* Elevation instead of outline: cards sit on the ground, they are
     not drawn on it. */
  --mg-shadow:          0 2px 10px rgba(15,27,45,0.06), 0 1px 2px rgba(15,27,45,0.04);
  --mg-shadow-lg:       0 12px 34px rgba(15,27,45,0.12);
  --mg-shadow-sm:       0 1px 2px rgba(15,27,45,0.05);

  --mg-header-bg:       rgba(255,255,255,0.92);
  --mg-scrim:           rgba(15,27,45,0.40);
  --mg-scrollbar:       rgba(15,27,45,0.14);
  --mg-scrollbar-hover: rgba(15,27,45,0.26);

  --mg-radius:          14px;
  --mg-radius-md:       10px;
  --mg-radius-sm:       8px;
}

/* Panels earn their edge from shadow here; the hairline is kept only
   where a table needs its rows delimited. */
:root[data-theme="confidence"] .panel,
:root[data-theme="confidence"] .tile,
:root[data-theme="confidence"] .coverage-card {
  box-shadow: var(--mg-shadow);
}

/* The active navigation row: a soft blue field with the accent's own
   text, which is how the mockup marks position without a heavy bar. */
:root[data-theme="confidence"] .nav-item.active {
  background: var(--mg-primary-soft);
  color: var(--mg-primary-dark);
  font-weight: 600;
}

/* ── 01.css ── */
/* MailGuard stylesheet module 01
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ================================================================
   MailGuard WHM Plugin — Design System
   Adapted from MailShield OSM Pro dark premium theme
   ================================================================ */


/* ----------------------------------------------------------------

   2. Reset & Base
   ---------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background: var(--mg-bg);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  margin: 0;
  overflow-x: hidden;
  background: var(--mg-bg);
  color: var(--mg-text);
  font: 400 15px/1.55 var(--mg-font);
}

a { color: var(--mg-accent); text-decoration: none; }
a:hover { color: var(--mg-accent-light); text-decoration: underline; }

img, svg { max-width: 100%; height: auto; }

h1, h2, h3, h4, h5, h6 { margin: 0; color: var(--mg-text); }

p { margin: 0; }

a, button, input, select, textarea {
  transition:
    border-color var(--mg-duration) var(--mg-ease),
    background-color var(--mg-duration) var(--mg-ease),
    color var(--mg-duration) var(--mg-ease),
    box-shadow var(--mg-duration) var(--mg-ease),
    transform var(--mg-duration) var(--mg-ease),
    opacity var(--mg-duration) var(--mg-ease);
}

:focus-visible {
  outline: 2px solid var(--mg-primary);
  outline-offset: 2px;
}

::selection {
  background: var(--mg-primary-glow);
  color: var(--mg-text);
}


/* ----------------------------------------------------------------
   3. Skip Link
   ---------------------------------------------------------------- */
.skip {
  position: fixed;
  top: -80px;
  left: calc(var(--mg-sidebar-width) + 16px);
  z-index: 9999;
  padding: 12px 16px;
  border-radius: var(--mg-radius-md);
  background: var(--mg-bg-tertiary);
  color: var(--mg-text);
  font-weight: 600;
  font-size: 13px;
  box-shadow: var(--mg-shadow);
  text-decoration: none;
}

.skip:focus {
  top: 12px;
}


/* ----------------------------------------------------------------

   4. App Shell
   ---------------------------------------------------------------- */
.app-shell {
  display: flex;
  min-height: 100vh;
}

/* ── 02.css ── */
/* MailGuard stylesheet module 02
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------
   5. Sidebar
   ---------------------------------------------------------------- */
.sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 100;
  width: var(--mg-sidebar-width);
  padding: 22px 16px 18px;
  display: flex;
  flex-direction: column;
  color: var(--mg-text);
  background: var(--mg-bg-secondary);
  border-right: 1px solid var(--mg-border);
  overflow: hidden;
}

/* --- Brand --- */
.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 8px 20px;
  color: var(--mg-text);
  text-decoration: none;
  border-bottom: 1px solid var(--mg-border);
}

.brand-icon {
  width: 42px;
  height: 42px;
  flex: 0 0 42px;
  display: grid;
  place-items: center;
  border: 1px solid var(--mg-border-strong);
  border-radius: 11px;
  background: linear-gradient(135deg, var(--mg-primary-soft), var(--mg-accent-soft));
}

.brand-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.brand-name {
  font-size: 16px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--mg-text);
}

.brand-sub {
  color: var(--mg-text-muted);
  font-size: 11px;
}

/* --- Navigation --- */
.sidebar-nav {
  flex: 1;
  padding-top: 12px;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--mg-border) transparent;
}

.nav-section-label {
  margin: 8px 0 4px;
  padding: 8px 12px 4px;
  color: var(--mg-text-muted);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.nav-item {
  min-height: 44px;
  margin: 2px 0;
  padding: 8px 12px;
  display: grid;
  grid-template-columns: 24px 1fr auto;
  gap: 8px;
  align-items: center;
  border-radius: var(--mg-radius-md);
  border-left: 3px solid transparent;
  color: var(--mg-text-secondary);
  font-size: 13px;
  font-weight: 550;
  text-decoration: none;
}

.nav-icon {
  display: flex;
  align-items: center;
  justify-content: center;
}

.nav-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-badge {
  padding: 2px 8px;
  border-radius: var(--mg-radius-full);
  background: var(--mg-danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
}

.nav-item:hover,
.nav-item:focus-visible {
  color: var(--mg-text);
  background: var(--mg-surface-hover);
  text-decoration: none;
}

.nav-item.active {
  color: var(--mg-text);
  background: var(--mg-primary-soft);
  border-left-color: var(--mg-primary);
}

.nav-item.active .nav-icon {
  color: var(--mg-primary-light);
}

/* --- Sidebar Footer --- */
.sidebar-footer {
  margin-top: auto;
  padding: 12px;
  border: 1px solid var(--mg-border);
  border-radius: 11px;
  background: var(--mg-surface);
}

.daemon-indicator {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 8px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--mg-border);
  font-size: 13px;
  color: var(--mg-text);
}

.daemon-dot {
  width: 8px;
  height: 8px;
  flex: 0 0 8px;
  border-radius: 50%;
}

.daemon-dot.online {
  background: var(--mg-success);
  box-shadow: 0 0 0 4px var(--mg-success-soft);
  animation: pulse-ring 2s ease-in-out infinite;
}

.daemon-dot.offline {
  background: var(--mg-text-muted);
  box-shadow: 0 0 0 4px rgba(100,116,139,0.12);
}

.daemon-label {
  font-size: 13px;
}

.server-info {
  padding-top: 2px;
}

.hostname {
  display: block;
  color: var(--mg-text-muted);
  font-size: 11px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ----------------------------------------------------------------
   6. Pulse & Server Dot
   ---------------------------------------------------------------- */
.pulse,
.server-dot {
  width: 8px;
  height: 8px;
  flex: 0 0 8px;
  border-radius: 50%;
  background: var(--mg-success);
  box-shadow: 0 0 0 4px var(--mg-success-soft);
  animation: pulse-ring 2s ease-in-out infinite;
}

@keyframes pulse-ring {
  0%, 100% { box-shadow: 0 0 0 4px var(--mg-success-soft); }
  50%      { box-shadow: 0 0 0 7px rgba(16,185,129,0.06); }
}


/* ----------------------------------------------------------------

   7. Main Area
   ---------------------------------------------------------------- */
.main-area {
  flex: 1;
  min-width: 0;
  margin-left: var(--mg-sidebar-width);
  display: flex;
  flex-direction: column;
}

/* ── 03.css ── */
/* MailGuard stylesheet module 03
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------
   8. Header
   ---------------------------------------------------------------- */
.header {
  position: sticky;
  top: 0;
  z-index: 50;
  min-height: var(--mg-topbar-height);
  padding: 0 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border-bottom: 1px solid var(--mg-border);
  background: var(--mg-header-bg);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

.header-gradient {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--mg-primary), var(--mg-accent), var(--mg-primary));
  background-size: 200% 100%;
  animation: gradient-shift 4s linear infinite;
}

@keyframes gradient-shift {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}

.header-left {
  display: flex;
  align-items: center;
  gap: 14px;
}

.header-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--mg-text);
}

.header-subtitle {
  margin-top: 2px;
  color: var(--mg-text-muted);
  font-size: 12px;
}

.header-right {
  display: flex;
  align-items: center;
  gap: 12px;
}

.sidebar-toggle {
  display: none;
  width: 36px;
  height: 36px;
  place-items: center;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: transparent;
  color: var(--mg-text-secondary);
  cursor: pointer;
}

.sidebar-toggle:hover {
  background: var(--mg-surface-hover);
  color: var(--mg-text);
}

.mode-badge {
  padding: 8px 12px;
  border: 1px solid var(--mg-primary-soft);
  border-radius: var(--mg-radius-full);
  background: var(--mg-primary-soft);
  color: var(--mg-primary-light);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
}

.content {
  flex: 1;
}


/* ----------------------------------------------------------------
   9. Page Wrap & Head
   ---------------------------------------------------------------- */
.page-wrap {
  width: min(var(--mg-content-max), calc(100% - 64px));
  margin: auto;
  padding: 34px 0 64px;
}

.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 24px;
}

.eyebrow {
  color: var(--mg-primary-light);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.page-head h1 {
  margin: 8px 0 4px;
  font-size: clamp(28px, 3vw, 36px);
  line-height: 1.14;
  letter-spacing: -0.035em;
  color: var(--mg-text);
}

.page-head p,
.panel-head p {
  margin: 0;
  color: var(--mg-text-muted);
  font-size: 14px;
}


/* Section 10 (Buttons) moved to section 15, see .mg-btn classes */


/* ----------------------------------------------------------------

   11. Notice / Banner
   ---------------------------------------------------------------- */
.notice {
  padding: 16px 18px;
  display: flex;
  align-items: center;
  gap: 14px;
  border: 1px solid var(--mg-border);
  border-left: 4px solid var(--mg-success);
  border-radius: var(--mg-radius);
  background: var(--mg-surface);
  box-shadow: var(--mg-shadow-sm);
}

.notice.ready {
  border-left-color: var(--mg-success);
}

.notice.warning {
  border-left-color: var(--mg-warning);
}

.notice-dot {
  width: 10px;
  height: 10px;
  flex: 0 0 10px;
  border-radius: 50%;
  background: var(--mg-success);
  box-shadow: 0 0 0 5px var(--mg-success-soft);
}

.notice.warning .notice-dot {
  background: var(--mg-warning);
  box-shadow: 0 0 0 5px var(--mg-warning-soft);
}

.notice strong {
  font-size: 14px;
  color: var(--mg-text);
}

.notice p {
  margin: 2px 0 0;
  color: var(--mg-text-muted);
  font-size: 13px;
}

/* ── 04.css ── */
/* MailGuard stylesheet module 04
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------

   12. Stat Cards Grid
   ---------------------------------------------------------------- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin: 16px 0;
}

.stat-card {
  position: relative;
  min-height: 134px;
  padding: 20px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface);
  box-shadow: var(--mg-shadow-sm);
  overflow: hidden;
  transition:
    transform var(--mg-duration) var(--mg-ease),
    box-shadow var(--mg-duration) var(--mg-ease),
    border-color var(--mg-duration) var(--mg-ease);
}

.stat-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  opacity: 0;
  transition: opacity var(--mg-duration) var(--mg-ease);
}

.stat-card:hover {
  transform: translateY(-2px);
  border-color: var(--mg-border-hover);
}

.stat-card:hover::before {
  opacity: 1;
}

.stat-card.stat-accent::before  { background: linear-gradient(90deg, var(--mg-accent), var(--mg-accent-light)); }
.stat-card.stat-success::before { background: linear-gradient(90deg, var(--mg-success), var(--mg-success-light)); }
.stat-card.stat-primary::before { background: linear-gradient(90deg, var(--mg-primary), var(--mg-accent)); }
.stat-card.stat-danger::before  { background: linear-gradient(90deg, var(--mg-danger), var(--mg-danger-light)); }

.stat-card.stat-accent:hover  { box-shadow: var(--mg-shadow), 0 0 30px var(--mg-accent-glow); }
.stat-card.stat-success:hover { box-shadow: var(--mg-shadow), 0 0 30px rgba(16,185,129,0.25); }
.stat-card.stat-primary:hover { box-shadow: var(--mg-shadow), 0 0 30px var(--mg-primary-glow); }
.stat-card.stat-danger:hover  { box-shadow: var(--mg-shadow), 0 0 30px rgba(239,68,68,0.25); }

.stat-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.stat-label {
  color: var(--mg-text-muted);
  font-size: 13px;
  font-weight: 600;
}

.stat-icon {
  color: var(--mg-text-muted);
  opacity: 0.5;
}

.stat-value {
  display: block;
  margin: 10px 0 4px;
  font-size: 30px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.03em;
  color: var(--mg-text);
  font-variant-numeric: tabular-nums;
}

.stat-desc {
  display: block;
  color: var(--mg-text-muted);
  font-size: 12px;
}


/* ----------------------------------------------------------------
   Theme toggle
   ----------------------------------------------------------------
   Both icons live in the DOM; the stylesheet reveals whichever is
   relevant to the active theme. Swapping markup instead would mean
   the button flickers on every page load before script runs.
   ---------------------------------------------------------------- */
.theme-toggle {
  width: 36px;
  height: 36px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: transparent;
  color: var(--mg-text-secondary);
  cursor: pointer;
}

.theme-toggle:hover {
  background: var(--mg-surface-hover);
  border-color: var(--mg-border-strong);
  color: var(--mg-text);
}

.theme-icon {
  grid-area: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Dark is the default, so offer the sun; light offers the moon. */
.theme-icon-moon { display: none; }
.theme-icon-sun  { display: flex; }

@media (prefers-color-scheme: light) {
  .theme-icon-sun  { display: none; }
  .theme-icon-moon { display: flex; }
}

:root[data-theme="light"] .theme-icon-sun  { display: none; }
:root[data-theme="light"] .theme-icon-moon { display: flex; }
:root[data-theme="dark"]  .theme-icon-sun  { display: flex; }
:root[data-theme="dark"]  .theme-icon-moon { display: none; }

/* ── 05.css ── */
/* MailGuard stylesheet module 05
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------

   13. Grid & Panels (Glassmorphism Cards)
   ---------------------------------------------------------------- */
.panel-grid {
  display: grid;
  grid-template-columns: 0.8fr 1.2fr;
  gap: 16px;
}

.panel {
  padding: 22px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface-subtle);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: var(--mg-shadow-sm);
  transition: border-color var(--mg-duration) var(--mg-ease);
}

.panel:hover {
  border-color: var(--mg-border-hover);
}

.panel-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
}

.panel-head p {
  margin: 0;
  color: var(--mg-text-muted);
  font-size: 14px;
}

.panel-link {
  color: var(--mg-primary-light);
  font-size: 13px;
  font-weight: 650;
  text-decoration: none;
  white-space: nowrap;
}

.panel-link:hover {
  color: var(--mg-accent);
  text-decoration: underline;
}

.panel h2 {
  margin: 0 0 4px;
  font-size: 18px;
  line-height: 1.3;
}

.panel-body {
  margin-top: 16px;
}

/* Key-Value List */
.kv-list {
  display: flex;
  flex-direction: column;
}

.kv-item {
  display: flex;
  justify-content: space-between;
  padding: 12px 0;
  border-top: 1px solid var(--mg-border);
}

.kv-key {
  color: var(--mg-text-muted);
  font-size: 14px;
}

.kv-value {
  font-size: 15px;
  font-weight: 600;
  color: var(--mg-text);
  font-variant-numeric: tabular-nums;
}

/* Checks */
.checks {
  display: flex;
  flex-direction: column;
}

.check-item {
  padding: 12px 0;
  display: grid;
  grid-template-columns: 36px 1fr auto;
  gap: 12px;
  align-items: center;
  border-top: 1px solid var(--mg-border);
}

.check-icon {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: var(--mg-radius-md);
  font-weight: 800;
  font-size: 14px;
}

.check-icon.ok {
  color: var(--mg-success);
  background: var(--mg-success-soft);
}

.check-icon.fail {
  color: var(--mg-warning);
  background: var(--mg-warning-soft);
}

.check-text strong,
.check-text small {
  display: block;
}

.check-text strong {
  font-size: 14px;
  color: var(--mg-text);
}

.check-text small {
  margin-top: 2px;
  color: var(--mg-text-muted);
  font-size: 12px;
}

.check-status {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.check-status.ok {
  color: var(--mg-success);
}

.check-status.fail {
  color: var(--mg-warning);
}


/* ----------------------------------------------------------------

   14. Report Grid
   ---------------------------------------------------------------- */
.report-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

.report-card {
  padding: 24px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface);
  box-shadow: var(--mg-shadow-sm);
  transition: border-color var(--mg-duration) var(--mg-ease),
              transform var(--mg-duration) var(--mg-ease);
}

.report-card:hover {
  border-color: var(--mg-border-hover);
  transform: translateY(-2px);
}

.report-period {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: var(--mg-primary-soft);
  color: var(--mg-primary-light);
  font-size: 13px;
  font-weight: 800;
}

.report-card h2 {
  margin: 16px 0 6px;
  font-size: 18px;
}

.report-card p {
  min-height: 64px;
  color: var(--mg-text-muted);
  font-size: 14px;
}

/* ── 06.css ── */
/* MailGuard stylesheet module 06
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------
   15. Buttons
   ---------------------------------------------------------------- */
/*
 * One button.
 *
 * There were three: this rule, a second .mg-btn in the product module that
 * quietly won on radius, border and weight, and a third :disabled in the
 * chrome module that won on opacity. Nothing looked broken -- each override
 * landed where its author was working -- but the button an operator sees was
 * the sum of three files, and changing it here changed nothing.
 *
 * The values below are the ones that were already winning, gathered into the
 * one place that claims to define them. The look is unchanged on purpose;
 * what changes is that it can now be changed.
 *
 * inline-flex with a min-height rather than padding alone, because half these
 * are <a> and half are <button>: left to their own box models the two sit at
 * different heights in the same row, which is the commonest reason a toolbar
 * looks subtly wrong without anyone being able to say why.
 */
.mg-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--mg-border-strong);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-surface);
  color: var(--mg-text-secondary);
  font: 500 13px/1 var(--mg-font);
  cursor: pointer;
  gap: 8px;
  text-decoration: none;
  box-sizing: border-box;
}

/*
 * The keyboard's turn.
 *
 * No button in this product had a focus style. The browser's default outline
 * is drawn in the system's colour, which on this dark ground is a thin dark
 * ring on a dark surface -- present, and invisible. An operator tabbing
 * through a row of actions could not see which one the Enter key would press.
 */
.mg-btn:focus-visible {
  outline: 2px solid var(--mg-primary);
  outline-offset: 2px;
}

.mg-btn:hover {
  background: var(--mg-surface-hover);
  border-color: var(--mg-border-hover);
  color: var(--mg-text);
  text-decoration: none;
}

.mg-btn:active {
  transform: translateY(1px);
}

.mg-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.mg-btn-primary {
  background: var(--mg-primary);
  border-color: var(--mg-primary);
  color: #fff;
}

.mg-btn-primary:hover {
  background: var(--mg-primary-dark);
  border-color: var(--mg-primary-dark);
  box-shadow: 0 0 20px var(--mg-primary-glow);
  color: #fff;
}

.mg-btn-ghost {
  background: transparent;
  border-color: var(--mg-border);
  color: var(--mg-text-muted);
}

.mg-btn-ghost:hover {
  background: var(--mg-surface);
  color: var(--mg-text-secondary);
}


/* ----------------------------------------------------------------

   16. Empty State
   ---------------------------------------------------------------- */
.empty-panel {
  margin-top: 16px;
}

.empty-state {
  padding: 68px 24px;
  text-align: center;
}

.empty-icon {
  margin: 0 auto 16px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.empty-state h2 {
  font-size: 20px;
  color: var(--mg-text);
}

.empty-state p {
  max-width: 560px;
  margin: 8px auto 16px;
  color: var(--mg-text-muted);
  font-size: 14px;
}

.coming-badge {
  display: inline-block;
  padding: 6px 10px;
  border-radius: var(--mg-radius-full);
  background: var(--mg-primary-soft);
  color: var(--mg-primary-light);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}


/* ----------------------------------------------------------------
   17. Coverage Stats
   ---------------------------------------------------------------- */
.coverage-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-bottom: 20px;
}

.coverage-card {
  padding: 18px 20px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

.coverage-card strong,
.coverage-card span,
.coverage-card small {
  display: block;
}

.coverage-card strong {
  margin-bottom: 4px;
  color: var(--mg-text);
  font-size: 16px;
  font-variant-numeric: tabular-nums;
}

.coverage-card span {
  color: var(--mg-text-secondary);
  font-size: 12px;
  font-weight: 600;
}

.coverage-card small {
  margin-top: 6px;
  color: var(--mg-text-muted);
  font-size: 11px;
}


/* Sections 16–21 (filters, tables, tags, status, pagination, coverage)
   are in tables.css to avoid duplication. */


/* ----------------------------------------------------------------

   22. Forms (General)
   ---------------------------------------------------------------- */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="number"],
input[type="url"],
input[type="date"],
textarea,
select {
  min-height: 40px;
  padding: 8px 12px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-md);
  background: var(--mg-bg-tertiary);
  color: var(--mg-text);
  font: 400 14px/1.4 var(--mg-font);
}

input::placeholder,
textarea::placeholder {
  color: var(--mg-text-muted);
}

input:hover,
textarea:hover,
select:hover {
  border-color: var(--mg-border-hover);
}

input:focus,
textarea:focus,
select:focus {
  border-color: var(--mg-primary);
  box-shadow: 0 0 0 3px var(--mg-primary-glow);
  outline: none;
}

textarea {
  min-height: 100px;
  resize: vertical;
}

label {
  color: var(--mg-text-secondary);
  font-size: 13px;
  font-weight: 600;
}

/* ── 07.css ── */
/* MailGuard stylesheet module 07
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------

   23. Modals
   ---------------------------------------------------------------- */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--mg-scrim);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--mg-duration) var(--mg-ease),
              visibility var(--mg-duration) var(--mg-ease);
}

.modal-overlay.open {
  opacity: 1;
  visibility: visible;
}

.modal {
  width: 100%;
  max-width: 520px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-bg-secondary);
  box-shadow: var(--mg-shadow-lg);
  transform: translateY(12px) scale(0.97);
  transition: transform var(--mg-duration) var(--mg-ease);
}

.modal-overlay.open .modal {
  transform: translateY(0) scale(1);
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 22px;
  border-bottom: 1px solid var(--mg-border);
}

.modal-head h2 {
  font-size: 16px;
}

.modal-close {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: var(--mg-radius-sm);
  background: transparent;
  color: var(--mg-text-muted);
  font-size: 18px;
  cursor: pointer;
}

.modal-close:hover {
  background: var(--mg-surface-hover);
  color: var(--mg-text);
}

.modal-body {
  padding: 22px;
}

.modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 22px;
  border-top: 1px solid var(--mg-border);
}


/* ----------------------------------------------------------------

   24. Toast Notifications
   ---------------------------------------------------------------- */
.toast-container {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}

.toast {
  min-width: 280px;
  max-width: 420px;
  padding: 14px 18px;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-bg-tertiary);
  box-shadow: var(--mg-shadow-lg);
  pointer-events: auto;
  animation: toast-in 0.3s var(--mg-ease) forwards;
}

.toast.removing {
  animation: toast-out 0.2s var(--mg-ease) forwards;
}

@keyframes toast-in {
  from { opacity: 0; transform: translateX(40px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes toast-out {
  from { opacity: 1; transform: translateX(0); }
  to   { opacity: 0; transform: translateX(40px); }
}

.toast-icon {
  flex: 0 0 20px;
  font-size: 18px;
  line-height: 1;
}

.toast-body strong {
  display: block;
  font-size: 13px;
  color: var(--mg-text);
}

.toast-body p {
  margin-top: 2px;
  color: var(--mg-text-muted);
  font-size: 12px;
}

.toast.success { border-left: 3px solid var(--mg-success); }
.toast.error   { border-left: 3px solid var(--mg-danger); }
.toast.warning { border-left: 3px solid var(--mg-warning); }
.toast.info    { border-left: 3px solid var(--mg-info); }


/* ----------------------------------------------------------------

   25. Progress Bars
   ---------------------------------------------------------------- */
.progress {
  width: 100%;
  height: 6px;
  border-radius: var(--mg-radius-full);
  background: var(--mg-border);
  overflow: hidden;
}

.progress-bar {
  height: 100%;
  border-radius: var(--mg-radius-full);
  background: linear-gradient(90deg, var(--mg-primary), var(--mg-accent));
  transition: width 0.5s var(--mg-ease);
}

.progress-bar.danger  { background: var(--mg-danger); }
.progress-bar.success { background: var(--mg-success); }
.progress-bar.warning { background: var(--mg-warning); }

/* Indeterminate progress */
.progress.indeterminate .progress-bar {
  width: 30%;
  animation: progress-slide 1.5s var(--mg-ease) infinite;
}

@keyframes progress-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(400%); }
}


/* ----------------------------------------------------------------
   26. Tooltips
   ---------------------------------------------------------------- */
[data-tooltip] {
  position: relative;
}

[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 10px;
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-tertiary);
  border: 1px solid var(--mg-border);
  color: var(--mg-text-secondary);
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  box-shadow: var(--mg-shadow);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s var(--mg-ease);
  z-index: 500;
}

[data-tooltip]:hover::after {
  opacity: 1;
}

/* ── 08.css ── */
/* MailGuard stylesheet module 08
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------
   27. Loading Spinner
   ---------------------------------------------------------------- */
.spinner {
  display: inline-block;
  width: 24px;
  height: 24px;
  border: 3px solid var(--mg-border);
  border-top-color: var(--mg-primary);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

.spinner.lg {
  width: 40px;
  height: 40px;
  border-width: 4px;
}

.spinner.sm {
  width: 16px;
  height: 16px;
  border-width: 2px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* Page-level loading overlay */
.loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--mg-bg) 70%, transparent);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  z-index: 10;
}


/* ----------------------------------------------------------------
   28. Skeleton Loading
   ---------------------------------------------------------------- */
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--mg-surface);
  border-radius: var(--mg-radius-sm);
}

.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent,
    var(--mg-surface-hover),
    transparent
  );
  animation: skeleton-shimmer 1.5s ease-in-out infinite;
}

@keyframes skeleton-shimmer {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

.skeleton-text {
  height: 14px;
  margin-bottom: 8px;
  border-radius: 4px;
}

.skeleton-text.short { width: 40%; }
.skeleton-text.medium { width: 70%; }
.skeleton-text.long { width: 100%; }

.skeleton-heading {
  height: 24px;
  width: 50%;
  margin-bottom: 12px;
  border-radius: 4px;
}

.skeleton-card {
  height: 134px;
  border-radius: var(--mg-radius);
}


/* ----------------------------------------------------------------

   29. Scrollbar Styling
   ---------------------------------------------------------------- */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--mg-scrollbar) transparent;
}

*::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: var(--mg-scrollbar);
  border-radius: 3px;
}

*::-webkit-scrollbar-thumb:hover {
  background: var(--mg-scrollbar-hover);
}


/* ----------------------------------------------------------------
   30. Utility Classes
   ---------------------------------------------------------------- */
.text-primary   { color: var(--mg-primary-light); }
.text-accent    { color: var(--mg-accent); }
.text-success   { color: var(--mg-success); }
.text-danger    { color: var(--mg-danger); }
.text-warning   { color: var(--mg-warning); }
.text-info      { color: var(--mg-info); }
.text-muted     { color: var(--mg-text-muted); }
.text-secondary { color: var(--mg-text-secondary); }

.bg-success-soft { background: var(--mg-success-soft); }
.bg-danger-soft  { background: var(--mg-danger-soft); }
.bg-warning-soft { background: var(--mg-warning-soft); }
.bg-info-soft    { background: var(--mg-info-soft); }

.font-mono { font-family: var(--mg-mono); }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border-width: 0;
}

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ----------------------------------------------------------------

   Responsive — Tablet
   ---------------------------------------------------------------- */
@media (max-width: 900px) {
  :root {
    --mg-sidebar-width: 72px;
  }

  .sidebar {
    width: var(--mg-sidebar-width);
    padding: 18px 8px;
  }

  .brand-text,
  .nav-section-label,
  .nav-text,
  .nav-badge,
  .daemon-label,
  .server-info {
    display: none;
  }

  .sidebar-brand {
    justify-content: center;
    padding: 0 0 16px;
  }

  .nav-item {
    grid-template-columns: 1fr;
    place-items: center;
    padding: 10px 8px;
    border-left: none;
  }

  .nav-item.active {
    border-left: none;
    border-bottom: 2px solid var(--mg-primary);
  }

  .daemon-indicator {
    justify-content: center;
  }

  .main-area {
    margin-left: var(--mg-sidebar-width);
  }

  .page-wrap {
    width: calc(100% - 40px);
  }

  .stat-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .panel-grid,
  .report-grid {
    grid-template-columns: 1fr;
  }

  .coverage-stats {
    grid-template-columns: 1fr;
  }

  .skip {
    left: calc(var(--mg-sidebar-width) + 10px);
  }

  .sidebar-toggle {
    display: grid;
  }
}

/* ── 09.css ── */
/* MailGuard stylesheet module 09
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------
   Responsive — Mobile (sidebar hidden)
   ---------------------------------------------------------------- */
/* The drawer engages where the toggle appears, not 340px later.
   ------------------------------------------------------------------------
   The toggle became visible below 900px and the drawer only below 560px, so
   between the two the button was on screen and pressing it did nothing: the
   sidebar stayed fixed at 272px with the content offset by the same amount.
   On a 768px tablet that is a third of the width spent on navigation and a
   control that appears broken — which is how an external review described
   the page, and it was right about the symptom on a width it did not test. */
@media (max-width: 900px) {
  /* Gated on scripting, like the tabs are. The drawer is opened by a button
     and closed by a tap outside, both of which are JavaScript — so hiding the
     sidebar off-screen without it would leave a narrow window with no
     navigation at all and no way to get any. Without scripting the page is
     longer, never emptier: the same rule the rest of this interface follows. */
  :root.has-js .sidebar {
    position: fixed;
    width: var(--mg-sidebar-width);
    transform: translateX(-100%);
    transition: transform 0.3s var(--mg-ease);
    z-index: 200;
  }

  /* No script: the sidebar sits above the content and scrolls with it. */
  :root:not(.has-js) .sidebar {
    position: static;
    width: 100%;
  }

  /* Same specificity as the rule that hides it, or higher.
     :root.has-js .sidebar is three classes; .sidebar.open is two, so the
     closed state won every time and the drawer could not be opened at all.
     The button toggled the class, the class did nothing, and the menu looked
     broken — which is exactly what a screenshot from a real phone showed
     within an hour of shipping it. */
  :root.has-js .sidebar.open {
    transform: translateX(0);
    width: 272px;
  }
  .sidebar.open .brand-text,
  .sidebar.open .nav-section-label,
  .sidebar.open .nav-text,
  .sidebar.open .nav-badge,
  .sidebar.open .daemon-label,
  .sidebar.open .server-info {
    display: block;
  }
  .sidebar.open .nav-item {
    grid-template-columns: 24px 1fr auto;
  }
  .app-shell {
    flex-direction: column;
  }
  .main-area {
    width: 100%;
    margin-left: 0;
  }
  .skip {
    left: 10px;
  }
  .sidebar-toggle {
    display: grid;
  }
}

/* Phone-only: one column, tighter chrome. Applied at 900px these would
   throw away the width a tablet actually has. */
@media (max-width: 560px) {
  .page-wrap {
    width: calc(100% - 24px);
    padding-top: 24px;
  }
  .header {
    padding: 0 14px;
    gap: 8px;
  }

  /* The header ran off the right edge on a phone: a fixed row of title,
     refresh control and mode badge, none of which would give way. The
     controls shrink to what they need and the badge goes last — a word
     saying "monitoring" beside a banner that already says what the server
     does is the least of what the row is carrying. */
  .header-left,
  .header-right { min-width: 0; }

  .header-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .mode-badge { display: none; }
  .mode-badge {
    padding: 6px 8px;
    font-size: 9px;
  }
  .stat-grid {
    grid-template-columns: 1fr;
  }
  .report-grid {
    grid-template-columns: 1fr;
  }
  .coverage-stats {
    grid-template-columns: 1fr;
  }
  .toast-container {
    left: 16px;
    right: 16px;
  }
  .toast {
    min-width: auto;
    max-width: none;
  }
  .modal {
    max-width: none;
    margin: 0 8px;
  }
}


/* ----------------------------------------------------------------
   33. Reduced Motion
   ---------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ----------------------------------------------------------------
   34. Print Styles
   ---------------------------------------------------------------- */
@media print {
  *,
  *::before,
  *::after {
    background: transparent !important;
    color: #000 !important;
    box-shadow: none !important;
    text-shadow: none !important;
  }

  .sidebar,
  .header,
  .skip,
  .pagination,
  .filters button,
  .toast-container,
  .modal-overlay {
    display: none !important;
  }

  .main-area {
    margin-left: 0 !important;
  }

  .page-wrap {
    width: 100% !important;
    padding: 0 !important;
  }

  body {
    font-size: 12pt;
    line-height: 1.5;
  }

  h1 { font-size: 24pt; }
  h2 { font-size: 18pt; }

  table {
    border-collapse: collapse;
  }

  table th,
  table td {
    border: 1px solid #ccc !important;
    padding: 8px !important;
  }

  .stat-grid {
    grid-template-columns: repeat(2, 1fr) !important;
  }

  .panel,
  .stat-card,
  .table-panel {
    border: 1px solid #ccc !important;
    page-break-inside: avoid;
  }
}

/* ── 10.css ── */
/* MailGuard stylesheet module 10
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* ----------------------------------------------------------------

   Control-plane components
   Added with the router/controller split; classes used by the
   dashboard attention list, entity chips, health notes and reports.
   ---------------------------------------------------------------- */

/* --- Attention list (dashboard) --- */
.attention-list { display: flex; flex-direction: column; }

/* A rail on the edge, not a dot in the row.
   A dot with a halo is a decoration that happens to have a colour: it reads
   as punctuation, so the eye finds it only after reading the label — which
   is the wrong way round on a list whose whole job is to be scanned by
   severity. A rail against the row's edge is found before any word is read,
   and colour still never fills a surface, so a screen full of warnings
   stays a screen rather than an alarm. */
.attention-row {
  display: grid;
  grid-template-columns: 3px minmax(0, 1fr) auto auto;
  gap: 14px;
  align-items: stretch;
  padding: 0;
  border-top: 1px solid var(--mg-border);
  color: var(--mg-text-secondary);
  text-decoration: none;
  transition: color var(--mg-duration) var(--mg-ease),
              background var(--mg-duration) var(--mg-ease);
}

.attention-row:hover {
  color: var(--mg-text);
  text-decoration: none;
  background: var(--mg-surface-hover);
}

.attention-dot {
  width: 3px;
  align-self: stretch;
  border-radius: 0;
  background: var(--mg-text-muted);
}

.attention-row.level-critical .attention-dot { background: var(--mg-danger); }
.attention-row.level-high     .attention-dot { background: var(--mg-warning); }
.attention-row.level-medium   .attention-dot { background: var(--mg-info); }
.attention-row.level-low      .attention-dot { background: var(--mg-border-strong, var(--mg-text-muted)); }

/* The padding moved off the row and onto its content, so the rail can run
   the full height of the row rather than floating inside it. */
.attention-body,
.attention-count,
.attention-action { padding-block: 12px; }
.attention-action { padding-inline-end: 2px; }

.attention-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.attention-why {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--mg-text-muted);
}

/* The press, named. It is not a button — the whole row is the link — but it
   tells the operator what pressing gets them before they press. */
.attention-action {
  font-size: 12.5px;
  color: var(--mg-text-muted);
  white-space: nowrap;
  align-self: center;
}

.attention-row:hover .attention-action { color: var(--mg-primary, var(--mg-text)); }

@media (max-width: 720px) {
  .attention-row { grid-template-columns: 3px minmax(0, 1fr) auto; }
  .attention-action { display: none; }
}

.attention-label { font-size: 14px; }

.attention-count {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--mg-text);
}

/* --- Chips (quiet accounts / domains) --- */
.chip-list { display: flex; flex-wrap: wrap; gap: 8px; }

.chip {
  padding: 4px 12px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-full);
  background: var(--mg-surface);
  color: var(--mg-text-muted);
  font-size: 12px;
}

/* --- Inline notes (health warnings) --- */
.inline-note {
  margin-top: 16px;
  padding: 14px 16px;
  border: 1px solid var(--mg-border);
  border-left: 3px solid var(--mg-info);
  border-radius: var(--mg-radius-md);
  background: var(--mg-surface);
}

.inline-note.warn { border-left-color: var(--mg-warning); }
.inline-note.bad  { border-left-color: var(--mg-danger); }

.inline-note strong { display: block; font-size: 13px; color: var(--mg-text); }
.inline-note p,
.inline-note li { color: var(--mg-text-muted); font-size: 13px; }
.inline-note p { margin-top: 4px; }
.inline-note ul { margin: 8px 0 0; padding-left: 18px; }
.inline-note li { margin-top: 3px; }

/* --- Key-value emphasis --- */
.kv-value.good { color: var(--mg-success); }
.kv-value.bad  { color: var(--mg-danger); }
.kv-value.warn { color: var(--mg-warning); }

.kv-key .pick {
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: var(--mg-radius-full);
  background: var(--mg-primary-soft);
  color: var(--mg-primary-light);
  font-size: 9px;
  font-style: normal;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* --- Reports --- */
.report-figures {
  display: flex;
  gap: 22px;
  margin: 14px 0 16px;
  padding-top: 14px;
  border-top: 1px solid var(--mg-border);
}

.report-figures strong {
  display: block;
  font-size: 20px;
  font-weight: 700;
  color: var(--mg-text);
  font-variant-numeric: tabular-nums;
}

.report-figures span { color: var(--mg-text-muted); font-size: 11px; }

.reason-list { margin: 0; padding-left: 18px; color: var(--mg-text-muted); font-size: 14px; }
.reason-list li { margin-top: 6px; }

.muted-line { color: var(--mg-text-muted); font-size: 14px; }

/* --- Sparkline --- */
.spark {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 120px;
  padding-top: 4px;
}

.spark-col {
  flex: 1;
  min-width: 4px;
  display: flex;
  flex-direction: column-reverse;
  gap: 1px;
  height: 100%;
  justify-content: flex-start;
}

.spark-in,
.spark-out {
  display: block;
  border-radius: 2px;
  min-height: 1px;
}

.spark-in  { background: var(--mg-success); }
.spark-out { background: var(--mg-primary); }

.spark-legend {
  display: flex;
  gap: 18px;
  margin-top: 12px;
  color: var(--mg-text-muted);
  font-size: 12px;
}

.spark-legend i {
  display: inline-block;
  width: 10px; height: 10px;
  margin-right: 6px;
  border-radius: 2px;
  vertical-align: middle;
}

.spark-legend .k-in  { background: var(--mg-success); }
.spark-legend .k-out { background: var(--mg-primary); }

/* MailGuard WHM - Tables & Data Components
   Dark premium theme (MailShield design system)
   Uses --mg-* tokens defined in app.css
   ------------------------------------------------------------ */

/* The qualifier under a panel heading.
   ------------------------------------------------------------------------ */

/* A heading answers "is this the panel I want"; what follows is read after
   that decision, so it sits on its own line and one step quieter. Inline, it
   would rebuild the paragraph the split was meant to break up. */
.panel-head p > .muted-line {
  display: block;
  margin-top: 3px;
  font-size: 12.5px;
  line-height: 1.5;
}

/* The shape of a sending source, and why.
   ------------------------------------------------------------------------ */

.shape {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  padding: 1px 8px;
  border-radius: 999px;
  margin-top: 3px;
}

.shape-bad  { background: var(--mg-danger-soft);  color: var(--mg-danger); }
.shape-warn { background: var(--mg-warning-soft); color: var(--mg-warning); }
.shape-ok   { background: var(--mg-success-soft); color: var(--mg-success); }
.shape-mute { background: var(--mg-surface-hover); color: var(--mg-text-muted); }

/* The sentence the decision rests on, not hidden in a tooltip: a phone has no
   hover, and this is the part worth reading. */
.shape-why {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--mg-text-muted);
  max-width: 52ch;
}

.seg-switch { display: inline-flex; border: 1px solid var(--mg-border-strong); border-radius: 7px; overflow: hidden; }
.seg-switch a { padding: 4px 10px; font-size: 12.5px; color: var(--mg-text-secondary); text-decoration: none; }
.seg-switch a + a { border-inline-start: 1px solid var(--mg-border-strong); }
.seg-switch a.on { background: var(--mg-primary); color: #fff; font-weight: 600; }

/* Incidents.
   ------------------------------------------------------------------------ */

.incident {
  border: 1px solid var(--mg-border);
  border-inline-start: 3px solid var(--mg-border-strong);
  border-radius: 0 var(--mg-radius, 10px) var(--mg-radius, 10px) 0;
  padding: 14px 16px;
  margin-bottom: 12px;
}

.incident.tone-bad  { border-inline-start-color: var(--mg-danger); }
.incident.tone-warn { border-inline-start-color: var(--mg-warning); }
.incident.tone-mute { border-inline-start-color: var(--mg-border-strong); }

.incident-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.incident-head strong { font-size: 15px; }
.incident-when { margin-inline-start: auto; font-size: 12px; color: var(--mg-text-muted); }
.incident-summary { margin: 6px 0 10px; color: var(--mg-text-secondary); }

.incident-evidence {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 10px;
  margin-bottom: 10px;
}

.p-ok   { background: var(--mg-success-soft); color: var(--mg-success); }
.p-warn { background: var(--mg-warning-soft); color: var(--mg-warning); }
.p-bad  { background: var(--mg-danger-soft);  color: var(--mg-danger); }
.p-mute { background: var(--mg-surface-hover); color: var(--mg-text-muted); }

/* The containment controls on an incident, set apart from closing it: one
   changes the server, the other changes the record. */
.incident-containment {
  padding: 10px 12px;
  margin: 0 0 10px;
  border: 1px dashed var(--mg-border-strong);
  border-radius: var(--mg-radius, 10px);
}

.incident-containment .mg-action-row { flex-wrap: wrap; gap: 8px 12px; }
.incident-containment small { flex-basis: 100%; }

/* ── 11.css ── */
/* MailGuard stylesheet module 11
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* === FILTER BAR === */

.filters {
  margin-bottom: 20px;
  padding: 20px 22px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

.filters form {
  display: flex;
  align-items: end;
  gap: 14px;
  flex-wrap: wrap;
}

.filters label {
  display: grid;
  gap: 6px;
}

.filters label > span {
  color: var(--mg-text-muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.filters select,
.filters input,
.mg-select,
.mg-input {
  height: 44px;
  min-width: 170px;
  padding: 0 14px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-secondary);
  color: var(--mg-text);
  font: 13px/1 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  transition: border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
}

.filters select:hover,
.filters input:hover {
  border-color: var(--mg-border-strong);
  background: var(--mg-bg-tertiary);
}

.filters select:focus,
.filters input:focus {
  border-color: var(--mg-primary);
  background: var(--mg-bg-tertiary);
  box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.2);
  outline: none;
}

.filters select option {
  background: var(--mg-bg-secondary);
  color: var(--mg-text);
}

.filters .search,
.filters .search-field {
  flex: 1;
}

.filters .search input,
.filters .search-field input {
  width: 100%;
  min-width: 260px;
}

.filter-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.filters button {
  height: 44px;
  padding: 0 20px;
  border: 1px solid var(--mg-primary);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-primary);
  color: #fff;
  font: 700 13px/1 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.filters button:hover {
  background: var(--mg-primary-dark);
  border-color: var(--mg-primary-dark);
  box-shadow: 0 4px 16px rgba(139, 92, 246, 0.3);
}

.filters button:focus-visible {
  outline: 3px solid rgba(139, 92, 246, 0.4);
  outline-offset: 2px;
}

.filters a {
  height: 44px;
  padding: 0 10px;
  display: inline-flex;
  align-items: center;
  color: var(--mg-text-muted);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  transition: color 0.2s ease;
}

.filters a:hover {
  color: var(--mg-text-secondary);
}

/* === TABLE PANEL === */

.table-panel {
  min-width: 0;
  max-width: 100%;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}

.table-panel .panel-head {
  padding: 20px 24px;
  border-bottom: 1px solid var(--mg-border);
}

.table-panel .panel-head h2 {
  margin: 0;
  color: var(--mg-text);
  font-size: 18px;
  line-height: 1.3;
}

.table-panel .panel-head p {
  margin: 4px 0 0;
  color: var(--mg-text-muted);
  font-size: 13px;
}

.result-count {
  padding: 4px 10px;
  border-radius: 14px;
  background: rgba(139, 92, 246, 0.12);
  color: var(--mg-primary);
  font-size: 11px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
}

/* ── 12.css ── */
/* MailGuard stylesheet module 12
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* === DATA TABLE === */

.table-scroll {
  width: 100%;
  max-width: 100%;
  overflow: auto;
  overscroll-behavior-x: contain;
}

/* Subtle scrollbar for dark theme */
.table-scroll::-webkit-scrollbar {
  height: 6px;
}

.table-scroll::-webkit-scrollbar-track {
  background: transparent;
}

.table-scroll::-webkit-scrollbar-thumb {
  background: var(--mg-scrollbar);
  border-radius: 3px;
}

.table-scroll::-webkit-scrollbar-thumb:hover {
  background: var(--mg-scrollbar-hover);
}

table {
  width: 100%;
  border-collapse: collapse;
  white-space: nowrap;
}

th {
  padding: 12px 16px;
  border-bottom: 1px solid var(--mg-border);
  background: var(--mg-surface-subtle);
  color: var(--mg-text-muted);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-align: left;
  text-transform: uppercase;
}

td {
  padding: 14px 16px;
  border-bottom: 1px solid var(--mg-surface);
  color: var(--mg-text-secondary);
  font-size: 13px;
  vertical-align: middle;
}

tbody tr {
  transition: background-color 0.15s ease;
}

/* Alternating row backgrounds (very subtle) */
tbody tr:nth-child(even) {
  background: var(--mg-surface-subtle);
}

tbody tr:hover {
  background: rgba(139, 92, 246, 0.04);
}

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

/* Column alignment helpers */
td.num,
th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

td.center,
th.center {
  text-align: center;
}

/* Cell content styles */
td strong,
td small {
  display: block;
}

td strong {
  max-width: 300px;
  overflow: hidden;
  color: var(--mg-text);
  font-size: 13px;
  font-weight: 600;
  text-overflow: ellipsis;
}

td small {
  max-width: 300px;
  overflow: hidden;
  margin-top: 3px;
  color: var(--mg-text-muted);
  font-size: 11px;
  text-overflow: ellipsis;
}

td code {
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(34, 211, 238, 0.08);
  color: var(--mg-accent);
  font: 11px/1.4 "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
}

/* === DIRECTION TAGS === */

.tag {
  display: inline-block;
  padding: 4px 8px;
  border-radius: 12px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.tag.inbound {
  background: rgba(16, 185, 129, 0.12);
  color: var(--mg-success);
}

.tag.outbound {
  background: rgba(139, 92, 246, 0.12);
  color: var(--mg-primary);
}

.tag.local {
  background: rgba(59, 130, 246, 0.12);
  color: var(--mg-info);
}

.tag.unknown {
  background: var(--mg-surface-hover);
  color: var(--mg-text-muted);
}

/* === STATUS BADGES === */

.status {
  display: inline-block;
  padding: 4px 8px;
  border-radius: 12px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.status.delivered {
  background: rgba(16, 185, 129, 0.12);
  color: var(--mg-success);
}

.status.received {
  background: rgba(34, 211, 238, 0.12);
  color: var(--mg-accent);
}

.status.rejected {
  background: rgba(239, 68, 68, 0.12);
  color: var(--mg-danger);
}

.status.deferred {
  background: rgba(245, 158, 11, 0.12);
  color: var(--mg-warning);
}

.status.bounced {
  background: rgba(239, 68, 68, 0.08);
  color: var(--mg-danger-strong);
}

.status.observed {
  background: rgba(59, 130, 246, 0.12);
  color: var(--mg-info);
}

.status.unknown {
  background: var(--mg-surface-hover);
  color: var(--mg-text-muted);
}

/* === GROUP ROWS === */

.group-row th {
  position: sticky;
  left: 0;
  padding: 10px 16px;
  border-top: 1px solid var(--mg-border);
  border-bottom: 1px solid var(--mg-border);
  border-left: 3px solid var(--mg-primary);
  background: rgba(139, 92, 246, 0.06);
  color: var(--mg-text-secondary);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: none;
}

.group-row span {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.group-row span::before {
  width: 7px;
  height: 7px;
  flex: 0 0 7px;
  border-radius: 50%;
  background: var(--mg-primary);
  box-shadow: 0 0 6px rgba(139, 92, 246, 0.4);
  content: "";
}

/* === NO DATA STATE === */

.no-data {
  padding: 56px 24px !important;
  color: var(--mg-text-muted) !important;
  font-size: 14px;
  text-align: center !important;
}

/* The export button, in the panel head beside the column menu. Quiet: it is
   an occasional convenience, not the thing the page is for. */
.table-export {
  margin-inline-start: 8px;
  font-size: 12.5px;
}

/* Tables as cards, where a table cannot be read.
   ------------------------------------------------------------------------
   A nine-column table at 390px is unreadable however it scrolls: the heading
   row is off-screen by the third column, so every value is a number with no
   name. Below this width each row becomes a card and each value carries its
   heading, which is the only arrangement that survives a phone.

   Gated on the labels the script writes. Without scripting the table stays a
   table and scrolls, which is worse to read and still works — the rule this
   interface keeps everywhere: never emptier without JavaScript, only longer. */

@media (max-width: 640px) {
  :root.has-js .table-scroll table,
  :root.has-js .table-scroll tbody,
  :root.has-js .table-scroll tr {
    display: block;
    width: 100%;
  }

  /* The heading row has done its job by being copied onto the cells. */
  :root.has-js .table-scroll thead { display: none; }

  :root.has-js .table-scroll tbody tr {
    border: 1px solid var(--mg-border);
    border-radius: var(--mg-radius, 10px);
    padding: 4px 0;
    margin-bottom: 10px;
    background: var(--mg-surface);
  }

  :root.has-js .table-scroll tbody td {
    display: flex;
    align-items: baseline;
    gap: 12px;
    border: 0;
    padding: 5px 12px;
    text-align: start;
  }

  :root.has-js .table-scroll tbody td[data-label]::before {
    content: attr(data-label);
    flex: 0 0 40%;
    max-width: 40%;
    font-size: 12px;
    color: var(--mg-text-muted);
    text-transform: none;
  }

  /* An action row is a row of buttons; a label above it teaches nothing, and
     the buttons need the whole width to stay pressable. */
  :root.has-js .table-scroll tbody td[data-nolabel] { justify-content: flex-start; }
  :root.has-js .table-scroll tbody td.row-actions { flex-wrap: wrap; gap: 6px; }

  /* Numbers stop being right-aligned when there is no column to align in. */
  :root.has-js .table-scroll tbody td.num { text-align: start; }
}

/* ── 13.css ── */
/* MailGuard stylesheet module 13
   Concatenated by scripts/build-css.php into assets/app.css.
   Split only at section boundaries so no comment is ever cut in half.
   Edit here, never the built file. */

/* === PAGINATION === */

.pagination {
  min-height: 64px;
  padding: 14px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  border-top: 1px solid var(--mg-border);
  background: var(--mg-surface-subtle);
}

.pagination > span {
  color: var(--mg-text-muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.pagination-pages {
  display: flex;
  align-items: center;
  gap: 4px;
}

.pg-btn,
.pagination a {
  min-width: 36px;
  min-height: 36px;
  padding: 0 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-xs);
  background: var(--mg-surface);
  color: var(--mg-text-secondary);
  font-size: 12px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}

.pagination a:hover {
  border-color: var(--mg-border-strong);
  background: var(--mg-surface-hover);
  color: var(--mg-text);
}

.pagination a.active,
.pagination a.current {
  border-color: var(--mg-primary);
  background: var(--mg-primary);
  color: #fff;
  box-shadow: 0 2px 10px rgba(139, 92, 246, 0.3);
}

.pagination a.disabled {
  pointer-events: none;
  opacity: 0.35;
}

/* === COVERAGE NOTE === */

.coverage-note {
  margin-bottom: 20px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  overflow: hidden;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}

.coverage-note > div {
  padding: 18px 20px;
  border-right: 1px solid var(--mg-border);
}

.coverage-note > div:last-child {
  border-right: 0;
}

.coverage-note strong,
.coverage-note span {
  display: block;
}

.coverage-note strong {
  margin-bottom: 4px;
  color: var(--mg-text);
  font-size: 16px;
  font-variant-numeric: tabular-nums;
}

.coverage-note span {
  color: var(--mg-text-muted);
  font-size: 12px;
}

/* === RESPONSIVE === */

@media (max-width: 700px) {
  .filters form {
    display: grid;
    grid-template-columns: 1fr;
  }

  .filters select,
  .filters input,
  .filters .search input {
    width: 100%;
    min-width: 0;
  }

  th,
  td {
    padding-left: 12px;
    padding-right: 12px;
  }

  .pagination {
    align-items: stretch;
    flex-direction: column;
    padding: 14px 18px;
  }

  .pagination div {
    overflow-x: auto;
  }

  .pagination a {
    min-width: 42px;
    min-height: 42px;
  }

  .coverage-note {
    grid-template-columns: 1fr;
  }

  .coverage-note > div {
    border-right: 0;
    border-bottom: 1px solid var(--mg-border);
  }

  .coverage-note > div:last-child {
    border-bottom: 0;
  }
}

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

/* === ENFORCEMENT STATUS (entity tables) === */

/* Rung colour follows the escalation ladder: cheap and reversible at the
   bottom, broad blast radius at the top. */
.status.clean   { background: var(--mg-surface-hover); color: var(--mg-text-muted); }
.status.rung-1  { background: rgba(34,211,238,0.12);  color: var(--mg-accent); }
.status.rung-2  { background: rgba(245,158,11,0.12);  color: var(--mg-warning); }
.status.rung-3  { background: rgba(251,146,60,0.14);  color: var(--mg-warning-light); }
.status.rung-4  { background: rgba(239,68,68,0.12);   color: var(--mg-danger); }
.status.rung-5  { background: rgba(220,38,38,0.18);   color: var(--mg-danger-light); }

.status.simulated { background: var(--mg-surface-hover); color: var(--mg-text-muted); }
.status.active    { background: rgba(16,185,129,0.12); color: var(--mg-success); }
.status.reversed  { background: var(--mg-surface-hover); color: var(--mg-text-muted); }

/* Draw the eye only when a rate is genuinely out of band. */
td.cell-bad { color: var(--mg-danger); font-weight: 650; }

/* === PAGINATION GAP === */
.pg-gap {
  min-width: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--mg-text-muted);
  font-size: 12px;
}

/* === GEO CELL (IP watch) === */
.geo-cell { display: flex; align-items: center; gap: 8px; }
.geo-flag { font-size: 15px; line-height: 1; }
.geo-text { min-width: 0; }
.geo-text strong { display: block; }
.geo-text small  { display: block; }

.asn-badge {
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(139,92,246,0.10);
  color: var(--mg-primary);
  font: 500 10px/1.4 "SFMono-Regular", Consolas, monospace;
}

.dc-badge {
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(245,158,11,0.12);
  color: var(--mg-warning);
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ── 17-dashboard.css ── */
/* MailGuard — dashboard
   Concatenated by scripts/build-css.php into assets/app.css.
   Edit here, never the built file. */

/* ----------------------------------------------------------------
   Sections
   ---------------------------------------------------------------- */
.dash-section { margin-bottom: 26px; }

.dash-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.dash-head h2 { font-size: 17px; letter-spacing: -0.02em; }

.dash-sub {
  color: var(--mg-text-muted);
  font-size: 13px;
}

/* Period switch sits right so it reads as a control, not a heading. */
.period-switch {
  margin-left: auto;
  display: flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-md);
  background: var(--mg-surface);
}

.period-btn {
  padding: 6px 12px;
  border-radius: var(--mg-radius-sm);
  color: var(--mg-text-muted);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.period-btn:hover { color: var(--mg-text); background: var(--mg-surface-hover); text-decoration: none; }

.period-btn.active {
  background: var(--mg-primary);
  color: #fff;
}

/* ----------------------------------------------------------------
   Tiles
   ----------------------------------------------------------------
   Tone encodes whether a figure needs attention, not what it counts.
   A zero rejection count is muted; a non-zero one is not.
   ---------------------------------------------------------------- */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px;
}

.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 16px 18px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface);
  box-shadow: var(--mg-shadow-sm);
  color: var(--mg-text-secondary);
  text-decoration: none;
  transition:
    transform var(--mg-duration) var(--mg-ease),
    border-color var(--mg-duration) var(--mg-ease),
    box-shadow var(--mg-duration) var(--mg-ease);
}

.tile::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  border-radius: var(--mg-radius) var(--mg-radius) 0 0;
  background: var(--tile-tone, transparent);
  opacity: 0;
  transition: opacity var(--mg-duration) var(--mg-ease);
}

.tile:hover {
  transform: translateY(-2px);
  border-color: var(--mg-border-strong);
  box-shadow: var(--mg-shadow);
  text-decoration: none;
}

.tile:hover::before { opacity: 1; }

.tile-icon {
  color: var(--tile-tone, var(--mg-text-muted));
  opacity: 0.85;
  margin-bottom: 10px;
}

.tile-value {
  font-size: 26px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.03em;
  color: var(--mg-text);
  font-variant-numeric: tabular-nums;
}

.tile-label {
  margin-top: 3px;
  font-size: 13px;
  font-weight: 600;
  color: var(--mg-text-secondary);
}

.tile-sub {
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--mg-text-muted);
}

.tile.tone-primary { --tile-tone: var(--mg-primary); }
.tile.tone-accent  { --tile-tone: var(--mg-accent); }
.tile.tone-success { --tile-tone: var(--mg-success); }
.tile.tone-warning { --tile-tone: var(--mg-warning); }
.tile.tone-danger  { --tile-tone: var(--mg-danger); }
.tile.tone-muted   { --tile-tone: var(--mg-text-muted); }

/* A muted tile is a figure at rest; it should not draw the eye. */
.tile.tone-muted .tile-value { color: var(--mg-text-secondary); }

/* ----------------------------------------------------------------
   Secondary figures
   ---------------------------------------------------------------- */
.figure-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 14px 22px;
  margin-top: 14px;
  padding: 16px 18px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-surface-subtle);
}

.figure-strip strong {
  display: block;
  font-size: 17px;
  font-weight: 650;
  color: var(--mg-text);
  font-variant-numeric: tabular-nums;
}

.figure-strip span {
  display: block;
  margin-top: 1px;
  font-size: 11.5px;
  color: var(--mg-text-muted);
}

/* ----------------------------------------------------------------
   Fill meter
   ---------------------------------------------------------------- */
.meter {
  width: 100%;
  max-width: 160px;
  height: 6px;
  border-radius: var(--mg-radius-full);
  background: var(--mg-surface-strong);
  overflow: hidden;
}

.meter i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--mg-radius-full);
  background: var(--mg-warning);
}

.meter-over i { background: var(--mg-danger); }

@media (max-width: 700px) {
  .period-switch { margin-left: 0; width: 100%; overflow-x: auto; }
  .tile-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}

/* ----------------------------------------------------------------
   Report detail
   ---------------------------------------------------------------- */
.report-lists {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 14px;
  margin-bottom: 20px;
}

.report-lists .kv-key {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 70%;
}

.btn-sm {
  min-height: 32px;
  padding: 0 12px;
  font-size: 12px;
}

/* Subject-kind tags reuse the direction tag shape. */
.tag.account { background: var(--mg-primary-soft); color: var(--mg-primary); }
.tag.domain  { background: var(--mg-accent-soft);  color: var(--mg-accent); }
.tag.mailbox { background: var(--mg-info-soft);    color: var(--mg-info); }

/* Outbound volume, as bars rather than a canvas.
   ------------------------------------------------------------------------ */

.volume-strip {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 72px;
}

.volume-bar {
  flex: 1;
  min-width: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--mg-primary-soft, var(--mg-border-strong));
  transition: background var(--mg-duration) var(--mg-ease);
}

/* The tallest bar named, because "when was the peak" is the question a
   volume strip is usually being asked. */
.volume-bar.is-peak { background: var(--mg-primary, var(--mg-text)); }

.volume-strip:hover .volume-bar { background: var(--mg-border-strong); }
.volume-strip .volume-bar:hover { background: var(--mg-primary, var(--mg-text)); }

.volume-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 6px;
  font-size: 12px;
  color: var(--mg-text-muted);
  font-variant-numeric: tabular-nums;
}

/* The overview's headline row and protection posture.
   ------------------------------------------------------------------------ */

.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}

.kpi {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 16px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius, 10px);
  background: var(--mg-surface);
}

.kpi-label { font-size: 12.5px; color: var(--mg-text-muted); }
.kpi-value { font-size: 27px; font-weight: 600; line-height: 1.15; font-variant-numeric: tabular-nums; }
.kpi-unit  { font-size: 12px; color: var(--mg-text-muted); }

/* Direction is not judgement. More mail may be a campaign or a compromise and
   the arrow does not know which, so it is coloured by movement only where
   movement itself is the fact — never green for up. */
.kpi-delta { margin-top: 6px; font-size: 12px; color: var(--mg-text-muted); }
.kpi-delta.up   { color: var(--mg-warning); }
.kpi-delta.down { color: var(--mg-text-secondary); }
.kpi-delta.none { font-style: italic; }

.posture-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px;
}

.posture {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 12px 14px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius, 10px);
  text-decoration: none;
  color: var(--mg-text-secondary);
}

.posture:hover { border-color: var(--mg-border-strong); text-decoration: none; }
.posture strong { color: var(--mg-text); font-size: 14px; }
.posture small  { font-size: 12px; color: var(--mg-text-muted); line-height: 1.45; }

.posture-state {
  width: max-content;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--mg-surface-hover);
  color: var(--mg-text-muted);
}

/* Five states, four tones. Degraded is the one that has to shout: a
   protection switched on that cannot run reads as defended on every other
   screen, and this card is where that stops. */
.posture.tone-ok   .posture-state { background: var(--mg-success-soft); color: var(--mg-success); }
.posture.tone-warn .posture-state { background: var(--mg-warning-soft); color: var(--mg-warning); }
.posture.tone-bad  .posture-state { background: var(--mg-danger-soft);  color: var(--mg-danger); }
.posture.tone-bad  { border-color: var(--mg-danger); }

/* Outcome shares.
   ------------------------------------------------------------------------ */

.outcome-bar {
  display: flex;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--mg-surface-hover);
  margin-bottom: 12px;
}

.outcome-bar .seg { display: block; }
.seg.tone-ok   { background: var(--mg-success); }
.seg.tone-warn { background: var(--mg-warning); }
.seg.tone-bad  { background: var(--mg-danger); }

.outcome-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.outcome-table td { padding: 6px 0; border-bottom: 1px solid var(--mg-border); }
.outcome-table tr:last-child td { border-bottom: 0; }
.outcome-table .num { text-align: right; font-variant-numeric: tabular-nums; }

.outcome-table .dot {
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 50%;
  margin-inline-end: 8px;
}
.dot.tone-ok   { background: var(--mg-success); }
.dot.tone-warn { background: var(--mg-warning); }
.dot.tone-bad  { background: var(--mg-danger); }

/* ── 18-chrome.css ── */
/* MailGuard — toolbar and collapsible rail
   Concatenated by scripts/build-css.php into assets/app.css.
   Edit here, never the built file. */

/* ----------------------------------------------------------------
   Header toolbar
   ---------------------------------------------------------------- */
.toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}

.header-titles { min-width: 0; }

.refresh-group {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 3px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-md);
  background: var(--mg-surface);
}

.refresh-btn {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: var(--mg-radius-sm);
  color: var(--mg-text-muted);
  text-decoration: none;
}

.refresh-btn:hover {
  background: var(--mg-surface-hover);
  color: var(--mg-text);
  text-decoration: none;
}

.select-sm {
  height: 30px;
  min-width: 72px;
  padding: 0 8px;
  border: 0;
  border-radius: var(--mg-radius-sm);
  background: transparent;
  color: var(--mg-text-secondary);
  font-size: 12px;
  font-weight: 600;
}

.select-sm:focus { outline: 2px solid var(--mg-primary); outline-offset: -2px; }

/* The countdown only takes space once it is running. */
.refresh-state {
  min-width: 0;
  padding: 0;
  color: var(--mg-accent);
  font: 600 11px/1 var(--mg-mono);
  font-variant-numeric: tabular-nums;
}

.refresh-state[data-active] { min-width: 30px; padding-right: 6px; }

/* The custom-interval field appears only when "Custom" is chosen. */
.refresh-custom {
  display: flex;
  align-items: center;
  gap: 2px;
  padding-right: 4px;
}

.refresh-custom[hidden] { display: none; }

.input-sm {
  width: 62px;
  height: 30px;
  min-width: 0;
  padding: 0 6px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-tertiary);
  color: var(--mg-text);
  font: 600 12px/1 var(--mg-mono);
  font-variant-numeric: tabular-nums;
}

.refresh-custom .unit {
  color: var(--mg-text-muted);
  font: 600 11px/1 var(--mg-mono);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border-width: 0;
}

/* ----------------------------------------------------------------
   Collapsible rail
   ----------------------------------------------------------------
   Distinct from the mobile drawer. The rail is a persistent choice on a
   wide screen; the drawer is a transient overlay on a narrow one. Both
   exist because they solve different problems.
   ---------------------------------------------------------------- */
.rail-toggle {
  margin-left: auto;
  width: 28px;
  height: 28px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: var(--mg-radius-sm);
  background: transparent;
  color: var(--mg-text-muted);
  cursor: pointer;
}

.rail-toggle:hover {
  border-color: var(--mg-border);
  background: var(--mg-surface-hover);
  color: var(--mg-text);
}

.version-line {
  display: block;
  margin-top: 4px;
  color: var(--mg-text-muted);
  font: 500 10.5px/1.4 var(--mg-mono);
}

:root.rail-collapsed { --mg-sidebar-width: 68px; }

:root.rail-collapsed .brand-text,
:root.rail-collapsed .nav-section-label,
:root.rail-collapsed .nav-text,
:root.rail-collapsed .nav-badge,
:root.rail-collapsed .daemon-label,
:root.rail-collapsed .hostname,
:root.rail-collapsed .version-line {
  display: none;
}

:root.rail-collapsed .sidebar { padding: 20px 8px 14px; }

:root.rail-collapsed .sidebar-brand {
  justify-content: center;
  padding: 0 0 16px;
}

:root.rail-collapsed .rail-toggle { margin-left: 0; }

:root.rail-collapsed .nav-item {
  grid-template-columns: 1fr;
  place-items: center;
  padding: 12px 6px;
  border-left: none;
}

:root.rail-collapsed .nav-item.active {
  border-left: none;
  box-shadow: inset 0 -2px 0 var(--mg-primary);
}

:root.rail-collapsed .sidebar-footer { padding: 10px 6px; }
:root.rail-collapsed .daemon-indicator {
  justify-content: center;
  padding-bottom: 0;
  margin-bottom: 0;
  border-bottom: 0;
}
:root.rail-collapsed .server-info { display: none; }

@media (max-width: 900px) {
  /* The rail is already the layout below this width; hide its control so it
     cannot be toggled into a state the breakpoint would override anyway. */
  .rail-toggle { display: none; }
}

@media (max-width: 700px) {
  .header-subtitle { display: none; }
  .refresh-state { display: none; }
  .toolbar { gap: 6px; }
}

/* ----------------------------------------------------------------
   DNS audit
   ---------------------------------------------------------------- */
.dns-card { margin-bottom: 14px; }

.dns-badges { display: flex; align-items: center; gap: 8px; flex: none; }

.score {
  min-width: 34px;
  padding: 4px 8px;
  border-radius: var(--mg-radius-sm);
  font: 700 13px/1 var(--mg-mono);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.score-good { background: var(--mg-success-soft); color: var(--mg-success); }
.score-mid  { background: var(--mg-warning-soft); color: var(--mg-warning); }
.score-bad  { background: var(--mg-danger-soft);  color: var(--mg-danger); }

.dns-findings { display: flex; flex-direction: column; }

.dns-finding {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 10px;
  padding: 12px 0;
  border-top: 1px solid var(--mg-border);
}

.dns-finding:first-child { border-top: 0; padding-top: 0; }

.dns-icon { display: flex; justify-content: center; padding-top: 1px; }

.level-ok   .dns-icon { color: var(--mg-success); }
.level-warn .dns-icon { color: var(--mg-warning); }
.level-fail .dns-icon { color: var(--mg-danger); }
.level-info .dns-icon { color: var(--mg-info); }

.dns-text strong { display: block; font-size: 13.5px; color: var(--mg-text); }
.dns-text p { margin-top: 3px; font-size: 13px; color: var(--mg-text-muted); }

/* A suggested record is shown to be copied, never applied. */
.dns-fix {
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid var(--mg-border);
  border-left: 2px solid var(--mg-primary);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-surface-subtle);
}

.dns-fix-label {
  display: block;
  margin-bottom: 4px;
  color: var(--mg-text-muted);
  font: 700 9.5px/1 var(--mg-font);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.dns-fix code {
  display: block;
  color: var(--mg-accent);
  font: 500 12px/1.5 var(--mg-mono);
  word-break: break-all;
}

.dns-fix small {
  display: block;
  margin-top: 4px;
  color: var(--mg-text-muted);
  font-size: 11px;
}

/* ----------------------------------------------------------------
   DNS apply
   ---------------------------------------------------------------- */
.dns-provider {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  font-size: 12.5px;
  color: var(--mg-text-muted);
}

.dns-provider strong { color: var(--mg-text-secondary); }

.fix-check,
.confirm-check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--mg-text-secondary);
  cursor: pointer;
}

.fix-check input,
.confirm-check input {
  width: 15px;
  height: 15px;
  accent-color: var(--mg-primary);
  cursor: pointer;
}

/* The confirmation reads as consequential, because it is: unticked the
   form rehearses, ticked it writes. */
.apply-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 16px;
  padding: 14px 16px;
  border: 1px solid var(--mg-border);
  border-left: 3px solid var(--mg-warning);
  border-radius: var(--mg-radius-md);
  background: var(--mg-surface);
}

.apply-note {
  margin-top: 8px;
  font-size: 11.5px;
  color: var(--mg-text-muted);
}

.apply-results { margin-bottom: 16px; }

.apply-row {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 10px;
  padding: 12px 0;
  border-top: 1px solid var(--mg-border);
}

.apply-row:first-child { border-top: 0; padding-top: 0; }
.apply-row code {
  display: block;
  margin-top: 4px;
  color: var(--mg-accent);
  font: 500 12px/1.5 var(--mg-mono);
  word-break: break-all;
}
.apply-row small { display: block; margin-top: 4px; color: var(--mg-text-muted); font-size: 11px; }

/* ----------------------------------------------------------------
   Compromise scan
   ---------------------------------------------------------------- */
.scan-card { margin-bottom: 14px; border-left: 3px solid var(--mg-border); }
.scan-card.tone-danger  { border-left-color: var(--mg-danger); }
.scan-card.tone-warning { border-left-color: var(--mg-warning); }
.scan-card.tone-success { border-left-color: var(--mg-success); }

.verdict {
  padding: 4px 12px;
  border-radius: var(--mg-radius-full);
  font: 800 10px/1.5 var(--mg-font);
  letter-spacing: 0.07em;
  white-space: nowrap;
}

.verdict-danger  { background: var(--mg-danger-soft);  color: var(--mg-danger); }
.verdict-warning { background: var(--mg-warning-soft); color: var(--mg-warning); }
.verdict-success { background: var(--mg-success-soft); color: var(--mg-success); }

.scan-reasons { margin-top: 14px; }
.scan-reasons ul { margin: 0; padding-left: 18px; }
.scan-reasons li {
  margin-top: 4px;
  font-size: 13px;
  color: var(--mg-text-secondary);
}

.scan-sample { margin-top: 14px; }
.scan-sample code {
  display: block;
  padding: 8px 10px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-surface-subtle);
  color: var(--mg-text-secondary);
  font: 400 12px/1.5 var(--mg-mono);
  word-break: break-word;
}

.scan-links { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }

/* Origin-type tags reuse the direction tag shape. */
.tag.script    { background: var(--mg-warning-soft); color: var(--mg-warning); }
.tag.auth_smtp { background: var(--mg-success-soft); color: var(--mg-success); }
.tag.system    { background: var(--mg-surface-hover); color: var(--mg-text-muted); }
.tag.forwarder { background: var(--mg-info-soft); color: var(--mg-info); }

/* ----------------------------------------------------------------
   Queue actions
   ---------------------------------------------------------------- */
.queue-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }

/* Removal is styled as consequential because it is the one thing here
   that cannot be undone. */
.mg-btn-danger {
  border-color: var(--mg-danger);
  background: var(--mg-danger-soft);
  color: var(--mg-danger);
}

.mg-btn-danger:hover {
  background: var(--mg-danger);
  border-color: var(--mg-danger);
  color: #fff;
}

td.center input[type="checkbox"],
th.center input[type="checkbox"] {
  width: 15px;
  height: 15px;
  accent-color: var(--mg-primary);
  cursor: pointer;
}

/* Exim's own words, shown verbatim rather than summarised. */
.detail-block {
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-surface-subtle);
  color: var(--mg-text-secondary);
  font: 400 11.5px/1.5 var(--mg-mono);
  white-space: pre-wrap;
  overflow-x: auto;
  max-height: 220px;
}

/* .mg-btn:disabled is defined with the rest of the button, in module 06. */
.queue-actions form { display: inline; }

/* ── 19-forms.css ── */
/* Settings forms.
 *
 * The existing input styling lives under .filters, which is a horizontal row
 * built for search and narrowing. A settings form is a vertical list where each
 * control carries an explanation, so it needs its own layout rather than a
 * filter bar bent into a shape it was not made for.
 *
 * Controls reuse .mg-input and .mg-select so a field here looks like a field
 * anywhere else; only the arrangement is new. Both themes come from the tokens,
 * so nothing below needs a dark-mode branch.
 */

.mg-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 620px;
}

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

.mg-field > .mg-input,
.mg-field > .mg-select {
  width: 100%;
}

.mg-field-name {
  font: 600 12.5px/1.3 var(--mg-font);
  color: var(--mg-text);
}

.mg-field small {
  font: 400 12px/1.55 var(--mg-font);
  color: var(--mg-text-muted);
}

/* Checkbox fields put the control beside the label, with the explanation
 * underneath it rather than beside the box — an explanation indented under a
 * checkbox reads as belonging to it, which is the point. */
.mg-field-toggle {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 4px 10px;
}

.mg-field-toggle input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin-top: 1px;
  accent-color: var(--mg-primary);
  cursor: pointer;
}

.mg-field-toggle .mg-field-name {
  cursor: pointer;
}

.mg-field-toggle small {
  grid-column: 2;
}

/* The same control inside a filter bar.
 *
 * The bar lays its fields out in a row and styles every `label > span` as an
 * uppercase field name, which turned this one's explanatory sentence into a
 * line of shouting that pushed the rest of the fields out of alignment. It is
 * not a field: it is a sentence with a box in front of it, so it takes its own
 * line under the fields and reads as prose. */
.filters .mg-field-toggle {
  order: 90;
  flex: 1 0 100%;
  align-items: center;
  max-width: 100%;
  padding-top: 4px;
}

.filters .mg-field-toggle > span {
  display: grid;
  gap: 2px;
  text-transform: none;
  letter-spacing: normal;
  font-size: 13px;
  font-weight: 500;
  color: var(--mg-text);
}

.filters .mg-field-toggle .mg-field-name {
  font-weight: 600;
}

.filters .mg-field-toggle small {
  grid-column: auto;
  font-size: 11.5px;
  font-weight: 400;
  color: var(--mg-text-muted);
}

/* Apply and Reset stay under everything, including that line. */
.filters .filter-actions {
  order: 100;
}

.mg-form-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* A row of independent single-button forms. Each action posts on its own so one
 * cannot pick up another's hidden fields. */
.mg-action-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.mg-form :disabled,
.mg-action-row :disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Addresses are shown whole. An address truncated with an ellipsis hides the
 * domain, which is the part an operator is usually checking, so these wrap
 * instead of being cut. */
.addr {
  display: block;
  font-family: var(--mg-mono);
  font-size: 12px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

.addr-more {
  margin-top: 3px;
}

.addr-more > summary {
  cursor: pointer;
  font-size: 11.5px;
  color: var(--mg-text-muted);
  list-style: none;
}

.addr-more > summary::before {
  content: "+";
  margin-inline-end: 3px;
}

.addr-more[open] > summary::before {
  content: "−";
}

.addr-more .addr {
  color: var(--mg-text-muted);
  padding-inline-start: 8px;
}

/* State plus its counter-actions, in one cell.
 *
 * Each button is its own form, so they are laid out here rather than by the
 * forms themselves — a form is a block and three of them stack without this. */
.state-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.state-actions form {
  display: inline;
}

.state-actions small {
  flex-basis: 100%;
  font-size: 11px;
}

/* The standalone login.
 *
 * Its own page rather than the app shell: there is no navigation to show
 * somebody who has not signed in, and rendering the chrome around a login form
 * suggests there is something behind it to look at. */
.mg-login-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 24px;
  background: var(--mg-bg);
}

.mg-login {
  width: 100%;
  max-width: 380px;
  padding: 32px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-bg-secondary);
}

.mg-login h1 {
  margin: 0 0 22px;
  font: 700 20px/1 var(--mg-font);
  color: var(--mg-text);
  letter-spacing: -0.01em;
}

.mg-login .notice {
  margin-bottom: 18px;
}

.mg-login .muted-line {
  margin-top: 20px;
  font-size: 11.5px;
}

/* Everything recorded about one message, in two columns.
 *
 * A definition list would be the semantic choice and reads worse here: these
 * are twelve short pairs an operator scans rather than a glossary. */
.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 6px 18px;
  margin-top: 8px;
  padding: 10px 12px;
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-tertiary);
}

.detail-grid > div {
  display: flex;
  gap: 8px;
  align-items: baseline;
  min-width: 0;
}

.detail-grid span {
  flex: 0 0 116px;
  font-size: 11px;
  color: var(--mg-text-muted);
}

.detail-grid strong {
  font-weight: 500;
  min-width: 0;
}

.message-detail > td {
  padding-top: 0;
}

/* Table headings that stay put.
 *
 * Every table here can be hundreds of rows, and a column heading that scrolls
 * away turns the eleventh row into a guess about which number is which. Sticky
 * inside .table-scroll, so it pins to the top of the scrolling area rather than
 * the window — the panel keeps its own frame.
 *
 * The background is opaque on purpose: rows sliding under a translucent
 * heading is unreadable at exactly the moment the heading is needed. */
.table-scroll {
  max-height: 70vh;
  overflow: auto;
}

.table-scroll thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--mg-bg-secondary);
  box-shadow: inset 0 -1px 0 var(--mg-border);
}

/* A group heading pins under the column headings rather than over them, so
 * both stay readable while a long group scrolls past. */
.table-scroll tbody tr.group-row th {
  position: sticky;
  top: 34px;
  z-index: 1;
  background: var(--mg-bg-tertiary);
}

/* A field that holds a number, not a sentence. */
.mg-input-narrow {
  max-width: 220px;
}

/* Row-count control, sitting with the pagination it belongs to. */
.page-size {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}

.page-size > span {
  font-size: 11.5px;
  color: var(--mg-text-muted);
  margin-inline-end: 3px;
}

/* A control that shows what is in force, not just what it does.
 *
 * "Protect" on a row said nothing about whether that row was protected, so an
 * operator read a column of identical grey words and learned nothing. Colour
 * carries the state and the label says what pressing it will do — the two
 * together are readable at a glance, which a column of forty rows has to be. */
.mg-btn-state-on {
  border-color: var(--mg-success, #22c55e);
  background: color-mix(in srgb, var(--mg-success, #22c55e) 16%, transparent);
  color: var(--mg-success, #22c55e);
  font-weight: 600;
}

.mg-btn-state-off {
  border-color: var(--mg-border);
  background: transparent;
  color: var(--mg-text-muted);
}

.mg-btn-state-block {
  border-color: var(--mg-danger, #ef4444);
  background: color-mix(in srgb, var(--mg-danger, #ef4444) 16%, transparent);
  color: var(--mg-danger, #ef4444);
  font-weight: 600;
}

/* The state as a word, beside the buttons rather than instead of them. */
.state-label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.state-label::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}

.state-label.on    { color: var(--mg-success, #22c55e); }
.state-label.off   { color: var(--mg-text-muted); }
.state-label.block { color: var(--mg-danger, #ef4444); }

/* Draggable column edges, and columns that can be put away.
 *
 * The tables carry very different things — an address, a subject, a count — and
 * no set of widths suits every operator or every screen.
 *
 * Note what is deliberately *not* here: a `position: relative` on the heading.
 * It was, and being the later rule it quietly replaced the `position: sticky`
 * declared above — the headings stopped pinning and nobody had touched the
 * sticky rule. A sticky box is a positioned box, so the grip below still has
 * something to be absolute against. */
.table-scroll thead th {
  padding-inline-end: 14px;
}

/* Only once an operator has actually set a width.
 *
 * An automatic layout sizes columns to their content, which means a width set
 * on a heading is a suggestion the browser is free to ignore — dragging looked
 * broken because it was. A fixed layout obeys the width, so it is switched on
 * with every column seeded from what it measured a moment earlier: the table
 * does not jump, and from then on the edges do what they look like they do. */
.table-scroll table.is-sized {
  table-layout: fixed;
}

.table-scroll table.is-sized td,
.table-scroll table.is-sized thead th {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A group heading spans the whole row, so it is not a column and must not be
 * clipped to one. */
.table-scroll table.is-sized tr.group-row th {
  overflow: visible;
}

.column-grip {
  position: absolute;
  top: 0;
  inset-inline-end: 0;
  width: 7px;
  height: 100%;
  cursor: col-resize;
  user-select: none;
}

.column-grip:hover {
  background: var(--mg-primary);
  opacity: 0.5;
}

/* While dragging, nothing else should select or highlight. */
body.is-resizing {
  cursor: col-resize;
  user-select: none;
}

.column-hidden {
  display: none;
}

.column-menu {
  position: relative;
  margin-inline-start: auto;
}

.column-menu > summary {
  cursor: pointer;
  list-style: none;
  padding: 4px 10px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  font-size: 11.5px;
  color: var(--mg-text-muted);
}

.column-menu[open] > summary {
  border-color: var(--mg-primary);
  color: var(--mg-text);
}

.column-menu-list {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + 4px);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 190px;
  padding: 12px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-secondary);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}

.column-menu-list label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  cursor: pointer;
}

.column-menu-list input {
  accent-color: var(--mg-primary);
}

/* A column heading that sorts.
 *
 * Sorting happens in the query, so this is a link, not a control that reorders
 * what is already on screen — a browser-side sort of fifty rows out of four
 * hundred puts the busiest account on page three and says nothing about it. */
.sort-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: inherit;
  text-decoration: none;
}

.sort-link:hover {
  color: var(--mg-text);
}

.sort-link.active {
  color: var(--mg-primary);
}

.sort-arrow {
  font-size: 9px;
  line-height: 1;
}

/* A form that holds a pasted message rather than a column of fields. */
.mg-form-wide {
  max-width: none;
}

.mg-textarea-code {
  height: auto;
  padding: 10px 14px;
  font-family: var(--mg-mono);
  font-size: 12px;
  line-height: 1.5;
  resize: vertical;
}

/* Profile cards: three decisions side by side, each naming what it writes. */
.profile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
  margin-bottom: 14px;
}

.profile-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius);
  background: var(--mg-bg-tertiary);
}

.profile-card.active {
  border-color: var(--mg-success, #22c55e);
}

.profile-card form {
  margin-top: auto;
}

.profile-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.profile-card p {
  margin: 0;
  font-size: 12.5px;
  color: var(--mg-text-secondary);
}

.profile-points {
  margin: 0;
  padding-inline-start: 18px;
  font-size: 12px;
  color: var(--mg-text-muted);
}

/* A number that lives inside a table cell. */
.mg-input-tiny {
  min-width: 0;
  width: 76px;
  height: 34px;
  padding: 0 8px;
}

/* A confirm tick that lives inside a table cell, beside its button. */
.inline-confirm {
  display: inline-grid;
  grid-template-columns: auto auto;
  align-items: center;
  gap: 4px;
}

.inline-confirm .mg-field-name {
  font-size: 11px;
  font-weight: 500;
  color: var(--mg-text-muted);
}

/* The bar a multi-selection is applied from. */
.bulk-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 16px;
  border-bottom: 1px solid var(--mg-border);
}

.bulk-bar .mg-select {
  height: 36px;
  min-width: 220px;
}

.bulk-master {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--mg-text-muted);
  cursor: pointer;
}

.bulk-master input {
  accent-color: var(--mg-primary);
}

/* The row handle: sits inside the row's own form, submits with the bar's. */
td.bulk-cell, th.bulk-cell {
  width: 30px;
  text-align: center;
}

.bulk-cell input {
  accent-color: var(--mg-primary);
}

/* The bar's bell: what the machine wants a person to decide. */
.notify-bell {
  position: relative;
  margin-inline-end: 12px;
}

.notify-bell > summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--mg-warning, #f59e0b);
  border-radius: var(--mg-radius-sm);
  color: var(--mg-warning, #f59e0b);
  cursor: pointer;
  list-style: none;
}

.notify-count {
  font-weight: 700;
  font-size: 12px;
}

.notify-list {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + 6px);
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 340px;
  max-height: 380px;
  overflow-y: auto;
  padding: 8px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-secondary);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}

.notify-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border-radius: var(--mg-radius-sm);
  border-inline-start: 3px solid var(--mg-warning, #f59e0b);
  text-decoration: none;
  color: var(--mg-text);
  background: var(--mg-bg-tertiary);
}

.notify-item.level-critical { border-inline-start-color: var(--mg-danger, #ef4444); }
.notify-item.level-info     { border-inline-start-color: var(--mg-info, #3b82f6); }

.notify-item small { color: var(--mg-text-muted); font-size: 11px; }

.notify-hint {
  padding: 6px 10px 2px;
  color: var(--mg-text-muted);
  font-size: 10.5px;
}

/* A mailbox suspended from sending, named in full under its account. */
.stopped-mailbox {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--mg-text-muted);
}

/* A mailbox record under its account: same columns, quieter presence. */
.mailbox-subrow td {
  background: var(--mg-bg-tertiary);
  font-size: 12px;
}

.mailbox-subrow .addr a {
  color: var(--mg-text-secondary);
  text-decoration: none;
}

/* The line an inline action leaves behind. */
.inline-toast {
  position: fixed;
  inset-block-end: 20px;
  inset-inline-end: 20px;
  z-index: 60;
  max-width: 420px;
  padding: 12px 16px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-secondary);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  font-size: 12.5px;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 140ms ease, transform 140ms ease;
  pointer-events: none;
}

.inline-toast.show { opacity: 1; transform: translateY(0); }
.inline-toast.ok   { border-inline-start: 3px solid var(--mg-success, #22c55e); }
.inline-toast.warn { border-inline-start: 3px solid var(--mg-danger, #ef4444); }

.inline-removed {
  opacity: 0;
  transition: opacity 180ms ease;
}

/*
 * A row that was acted on and stays.
 *
 * Marked rather than removed: holding a mailbox is the one thing the operator
 * most needs to keep seeing after they press it. The row still shows the state
 * it was rendered with, so it is dimmed to say that it is no longer current
 * rather than left looking authoritative.
 */
.inline-applied {
  opacity: 0.62;
  transition: opacity 180ms ease;
}

.mg-btn.is-applied {
  opacity: 0.7;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .inline-toast, .inline-removed, .inline-applied { transition: none; }
}

/* ── 20-tabs.css ── */
/* ==========================================================================
   Tabs.

   Panels are ordinary stacked sections until tabs.js marks the container
   tabs-ready; only then does anything collapse. A visitor without scripting
   gets the whole page in order, never a bar of dead buttons over nothing.
   ========================================================================== */

.tab-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 16px;
  padding: 4px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-secondary);
}

.tab-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 0;
  border-radius: calc(var(--mg-radius-sm) - 2px);
  background: transparent;
  color: var(--mg-text-secondary);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}

.tab-btn:hover {
  color: var(--mg-text-primary);
  background: var(--mg-bg-tertiary);
}

.tab-btn.is-active {
  color: var(--mg-text-primary);
  background: var(--mg-bg-primary);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

.tab-btn:focus-visible {
  outline: 2px solid var(--mg-accent);
  outline-offset: 1px;
}

/* The count says how much work waits behind a tab before it is opened. */
.tab-count {
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--mg-bg-tertiary);
  color: var(--mg-text-muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.tab-btn.is-active .tab-count,
.tab-btn.tab-warn .tab-count {
  background: var(--mg-warning-bg, rgba(217, 119, 6, 0.15));
  color: var(--mg-warning, #d97706);
}

/* Collapsing is gated on the script having run. */
.tabs-ready .tab-panel {
  display: none;
}

.tabs-ready .tab-panel.is-active {
  display: block;
}

/* ==========================================================================
   Theme picker.

   The swatches are the one place in this stylesheet allowed literal
   colours: they must show what a palette looks like, and drawing them
   from tokens would show what the *current* palette looks like three
   times over.
   ========================================================================== */

.theme-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 10px;
  margin: 4px 0 14px;
}

.theme-card {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 12px 14px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-md);
  background: var(--mg-surface);
  cursor: pointer;
}

.theme-card:hover {
  border-color: var(--mg-border-strong);
  background: var(--mg-surface-hover);
}

/* The current server default, marked without relying on the radio dot
   alone — a 12px circle is not a state an operator scanning finds. */
.theme-card.is-current {
  border-color: var(--mg-primary);
  box-shadow: 0 0 0 1px var(--mg-primary-soft);
}

.theme-card input[type="radio"] {
  margin-top: 3px;
  flex: 0 0 auto;
}

.theme-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.theme-text small {
  color: var(--mg-text-muted);
  font-size: 11.5px;
  line-height: 1.45;
}

.theme-swatch {
  display: flex;
  flex: 0 0 auto;
  width: 46px;
  height: 30px;
  overflow: hidden;
  border: 1px solid rgba(127, 140, 160, 0.35);
  border-radius: 6px;
}

.theme-swatch i {
  flex: 1;
}

/* ground / surface / accent, in each palette's own values */
.theme-swatch-dark       i:nth-child(1) { background: #090d16; }
.theme-swatch-dark       i:nth-child(2) { background: #161d2d; }
.theme-swatch-dark       i:nth-child(3) { background: #8b5cf6; }

.theme-swatch-light      i:nth-child(1) { background: #f6f7fb; }
.theme-swatch-light      i:nth-child(2) { background: #ffffff; }
.theme-swatch-light      i:nth-child(3) { background: #7c3aed; }

.theme-swatch-confidence i:nth-child(1) { background: #eef2f7; }
.theme-swatch-confidence i:nth-child(2) { background: #ffffff; }
.theme-swatch-confidence i:nth-child(3) { background: #2563eb; }

/* Auto shows both grounds meeting, which is what it does. */
.theme-swatch-auto       i:nth-child(1) { background: #090d16; }
.theme-swatch-auto       i:nth-child(2) { background: #f6f7fb; }
.theme-swatch-auto       i:nth-child(3) { background: linear-gradient(#8b5cf6, #2563eb); }

/* ==========================================================================
   Mail DNS — folded results.

   Native <details>: the fold survives a page with no JavaScript, answers
   the keyboard, and needs no state of its own. The controls that must
   work while a card is closed — the bulk checkbox and Hide — sit outside
   the element, because an interactive control inside <summary> toggles
   the card when pressed.
   ========================================================================== */

.dns-card-wrap {
  position: relative;
}

.dns-card-tools {
  position: absolute;
  inset-block-start: 14px;
  inset-inline-end: 16px;
  z-index: 2;
  display: flex;
  gap: 8px;
  align-items: center;
}

details.dns-card {
  padding: 0;
}

.dns-summary {
  display: flex;
  gap: 14px;
  align-items: center;
  justify-content: space-between;
  /* Room for the tools that float above the summary's right edge. */
  padding: 14px 210px 14px 16px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.dns-summary::-webkit-details-marker { display: none; }

.dns-summary:hover { background: var(--mg-surface-hover); }

.dns-summary:focus-visible {
  outline: 2px solid var(--mg-accent);
  outline-offset: -2px;
}

.dns-summary-name {
  display: flex;
  gap: 10px;
  align-items: center;
  min-width: 0;
}

.dns-summary-name strong {
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A caret that points where opening will take you, and turns when it has. */
.dns-chevron {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--mg-text-muted);
  border-bottom: 2px solid var(--mg-text-muted);
  transform: rotate(-45deg);
  transition: transform var(--mg-duration) var(--mg-ease);
}

details[open] > .dns-summary .dns-chevron {
  transform: rotate(45deg);
}

.dns-summary-verdict {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
}

details.dns-card > .panel-body {
  border-top: 1px solid var(--mg-border);
}

.dns-ownership {
  margin: 0 0 6px;
  color: var(--mg-text-secondary);
  font-size: 12.5px;
}

@media (max-width: 720px) {
  /* The tools drop under the badges rather than overlapping them. */
  .dns-card-tools {
    position: static;
    justify-content: flex-end;
    padding: 10px 16px 0;
  }

  .dns-summary {
    padding-inline-end: 16px;
    flex-wrap: wrap;
  }
}

@media (prefers-reduced-motion: reduce) {
  .dns-chevron { transition: none; }
}

/* The heading row over the folded DNS cards. Not a table header — there
   is no table — but it must read as one, so it borrows the same weight
   and case and sits flush above the first card. */
.dns-sortbar {
  display: flex;
  gap: 14px;
  align-items: center;
  justify-content: space-between;
  margin: 18px 0 6px;
  padding: 0 16px 8px;
  border-bottom: 1px solid var(--mg-border);
  color: var(--mg-text-muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.045em;
  text-transform: uppercase;
}

.dns-sortbar-rest {
  display: flex;
  gap: 22px;
  align-items: center;
  /* Aligned over the badges the summaries end with, clear of the tools
     that float at the card's right edge. */
  padding-inline-end: 194px;
}

@media (max-width: 720px) {
  .dns-sortbar-rest { padding-inline-end: 0; }
}

/* Level pickers on the account-exception form: five small squares rather
   than five wrapped checkbox labels, because the levels are a scale and
   should read as one. */
.rung-picks {
  display: flex;
  gap: 6px;
  align-items: center;
}

.rung-pick {
  display: inline-flex;
  gap: 4px;
  align-items: center;
  padding: 4px 8px;
  border: 1px solid var(--mg-border-strong);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-surface);
  font-size: 12px;
  cursor: pointer;
}

.rung-pick:hover { background: var(--mg-surface-hover); }

.rung-pick:has(input:checked) {
  border-color: var(--mg-primary);
  background: var(--mg-primary-soft);
  color: var(--mg-text);
  font-weight: 600;
}

/* ==========================================================================
   Escalation flow.

   Three or five steps read left to right with arrows between them. It
   wraps to a column on a narrow screen rather than scrolling sideways,
   because a diagram you have to pan is a diagram you do not read.
   ========================================================================== */

.flow {
  display: flex;
  gap: 6px;
  align-items: stretch;
  flex-wrap: wrap;
}

.flow-step {
  display: flex;
  flex: 1 1 150px;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: 14px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-md);
  background: var(--mg-surface);
}

/* The accent says what the level does to mail, not what number it is. */
.flow-monitor { border-inline-start: 3px solid var(--mg-success); }
.flow-hold    { border-inline-start: 3px solid var(--mg-warning); }
.flow-stop    { border-inline-start: 3px solid var(--mg-danger); }

/* A level that only rehearses is drawn as provisional rather than absent:
   it is still in the ladder and still writes what it would have done. */
.flow-step.is-rehearsal {
  border-style: dashed;
  opacity: 0.72;
}

.flow-gate {
  color: var(--mg-text-muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.flow-name {
  font-size: 13.5px;
  font-weight: 600;
}

.flow-what {
  color: var(--mg-text-secondary);
  font-size: 11.5px;
  line-height: 1.4;
}

.flow-step .status {
  align-self: flex-start;
  margin-top: 2px;
}

.flow-arrow {
  align-self: center;
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-top: 2px solid var(--mg-border-strong);
  border-right: 2px solid var(--mg-border-strong);
  transform: rotate(45deg);
}

@media (max-width: 860px) {
  .flow { flex-direction: column; }

  /* Pointing down once the steps stack. */
  .flow-arrow {
    transform: rotate(135deg);
    margin: 2px 0 2px 18px;
  }
}

/* One record's own Fix, sitting with the record it writes rather than in a
   bar at the bottom of the card — the button and the value it applies must
   be readable in one glance. */
.dns-fix-one {
  display: inline-flex;
  margin-inline-start: 8px;
}

/* A record given as the fields a DNS panel asks for, rather than as prose
   about a record. Name, type, TTL and value are what every provider's form
   wants, and the value gets the room because it is the part that must not
   be retyped. */
.dns-record-fields {
  display: flex;
  gap: 14px;
  align-items: flex-end;
  flex-wrap: wrap;
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-tertiary);
}

.dns-record-fields > div {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.dns-record-fields span {
  color: var(--mg-text-muted);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.dns-record-fields code {
  padding: 4px 8px;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-secondary);
  font-size: 12px;
  word-break: break-all;
}

.dns-record-value {
  flex: 1 1 320px;
}

/* ==========================================================================
   Two weights for a page's sections.

   Pages grew by accretion: every release added a panel, each justified on
   its own, until seven sat at identical weight with nothing saying which
   one the operator opened the page for. The working surface stays open;
   reference, capability and history fold — visible as a heading, one press
   from their contents, and out of the way of the thing that matters.
   ========================================================================== */

details.is-secondary {
  padding: 0;
  background: var(--mg-surface-subtle);
}

details.is-secondary > .section-summary {
  display: flex;
  gap: 14px;
  align-items: center;
  justify-content: space-between;
  padding: 12px 18px;
  cursor: pointer;
  list-style: none;
}

details.is-secondary > .section-summary::-webkit-details-marker { display: none; }

details.is-secondary > .section-summary:hover { background: var(--mg-surface-hover); }

details.is-secondary > .section-summary:focus-visible {
  outline: 2px solid var(--mg-accent);
  outline-offset: -2px;
}

.section-summary-title {
  display: flex;
  gap: 10px;
  align-items: baseline;
  min-width: 0;
}

/* A secondary heading is smaller than a primary one: the size difference
   is what tells an operator which is which before they read either. */
.section-summary-title h2 {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
}

.section-summary-title small {
  color: var(--mg-text-muted);
  font-size: 11.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.section-chevron {
  flex: 0 0 auto;
  align-self: center;
  width: 6px;
  height: 6px;
  border-right: 2px solid var(--mg-text-muted);
  border-bottom: 2px solid var(--mg-text-muted);
  transform: rotate(-45deg);
  transition: transform var(--mg-duration) var(--mg-ease);
}

details.is-secondary[open] > .section-summary .section-chevron {
  transform: rotate(45deg);
}

details.is-secondary[open] > .panel-body,
details.is-secondary[open] > .table-scroll {
  border-top: 1px solid var(--mg-border);
}

@media (prefers-reduced-motion: reduce) {
  .section-chevron { transition: none; }
}

/* The score, itemised. Points on the left in a fixed column so the losses
   line up and can be scanned without reading a word. */
.score-lines {
  display: flex;
  flex-direction: column;
}

.score-line {
  display: flex;
  gap: 14px;
  align-items: baseline;
  padding: 10px 0;
  border-bottom: 1px solid var(--mg-border);
}

.score-line:last-child { border-bottom: 0; }

.score-points {
  flex: 0 0 66px;
  text-align: right;
  font-size: 15px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.score-points small {
  color: var(--mg-text-muted);
  font-size: 11px;
  font-weight: 500;
}

.score-line.state-ok      .score-points { color: var(--mg-success); }
.score-line.state-partial .score-points { color: var(--mg-warning); }
.score-line.state-bad     .score-points { color: var(--mg-danger); }

.score-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.score-text span { font-size: 12.5px; color: var(--mg-text-secondary); }
.score-text small { font-size: 11.5px; color: var(--mg-text-muted); line-height: 1.45; }

/* ── 21-confidence.css ── */
/* ==========================================================================
   Confidence — the structure, not only the palette

   A palette swap repaints an interface; it does not restyle one. These
   rules are the second half: the metrics, weights and shapes that make
   the light theme read as an enterprise control panel rather than the
   dark one with the lights on. Scoped to the theme, so choosing it is
   the whole decision and no other palette shifts underneath an operator
   who never asked.
   ========================================================================== */

:root[data-theme="confidence"] {
  --mg-topbar-height: 64px;
}

/* -- sidebar --------------------------------------------------------------
   White, bordered, and quiet. The active row is a filled pill in the
   accent's own colour rather than a bar at its edge: on a white ground a
   3px rule reads as a scratch, a filled row reads as a position. */

:root[data-theme="confidence"] .sidebar {
  background: #fff;
  border-right: 1px solid var(--mg-border);
}

:root[data-theme="confidence"] .nav-item {
  min-height: 40px;
  padding: 8px 12px;
  border-left: 0;
  border-radius: 8px;
  color: #55627a;
  font-weight: 500;
}

:root[data-theme="confidence"] .nav-item:hover {
  background: #f2f6fc;
  color: var(--mg-text);
}

:root[data-theme="confidence"] .nav-item.active,
:root[data-theme="confidence"] .nav-item.active:hover {
  background: #e8f0fe;
  color: var(--mg-primary-dark);
  font-weight: 600;
}

:root[data-theme="confidence"] .nav-item.active .nav-icon svg {
  color: var(--mg-primary);
}

:root[data-theme="confidence"] .nav-group-label {
  color: #97a3b6;
  font-size: 10.5px;
  letter-spacing: 0.06em;
}

/* -- header ---------------------------------------------------------------
   A thin divider, not a shadow: the top bar belongs to the page here. */

:root[data-theme="confidence"] .topbar,
:root[data-theme="confidence"] .page-header {
  background: #fff;
  border-bottom: 1px solid var(--mg-border);
  box-shadow: none;
}

/* -- panels ---------------------------------------------------------------
   White cards on the grey ground, generous but not airy: this product is
   information-dense on purpose and the spacing must not fight that. */

:root[data-theme="confidence"] .panel {
  background: #fff;
  border: 1px solid var(--mg-border);
  margin-bottom: 22px;
}

:root[data-theme="confidence"] .panel-head {
  padding: 18px 22px;
  border-bottom: 1px solid var(--mg-border);
}

:root[data-theme="confidence"] .panel-head h2 {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

:root[data-theme="confidence"] .panel-head p {
  color: var(--mg-text-muted);
  font-size: 12.5px;
}

:root[data-theme="confidence"] .panel-body {
  padding: 20px 22px;
}

/* -- tables ---------------------------------------------------------------
   Separators, not a grid. A ruled cell on white encloses data; a single
   hairline under each row simply orders it. */

:root[data-theme="confidence"] table thead th {
  padding: 12px 14px;
  background: #f7f9fc;
  border-bottom: 1px solid var(--mg-border);
  color: #6b7a91;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.045em;
  text-transform: uppercase;
}

:root[data-theme="confidence"] table td {
  padding: 14px;
  border-bottom: 1px solid #eef2f7;
  font-size: 13px;
}

:root[data-theme="confidence"] table tbody tr:hover td {
  background: #f8fafd;
}

:root[data-theme="confidence"] table tbody tr:last-child td {
  border-bottom: 0;
}

/* -- status pills ---------------------------------------------------------
   Soft field, saturated text, no border. Severity is the only thing that
   earns colour here — everything else stays grey so the coloured pill is
   always the one worth reading. */

:root[data-theme="confidence"] .status {
  padding: 4px 10px;
  border: 0;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}

:root[data-theme="confidence"] .status.clean,
:root[data-theme="confidence"] .status.delivered {
  background: #e7f6ee;
  color: #0f7a4d;
}

:root[data-theme="confidence"] .status.deferred,
:root[data-theme="confidence"] .status.observed {
  background: #fdf3e2;
  color: #a2650a;
}

:root[data-theme="confidence"] .status.rejected,
:root[data-theme="confidence"] .status.bounced {
  background: #fdecec;
  color: #b3261e;
}

/* -- buttons --------------------------------------------------------------
   Three weights, and only one of them is loud. */

:root[data-theme="confidence"] .mg-btn {
  min-height: 36px;
  padding: 0 14px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 550;
}

:root[data-theme="confidence"] .mg-btn-primary {
  background: var(--mg-primary);
  border-color: var(--mg-primary);
  color: #fff;
  box-shadow: 0 1px 2px rgba(37, 99, 235, 0.25);
}

:root[data-theme="confidence"] .mg-btn-primary:hover {
  background: var(--mg-primary-dark);
  border-color: var(--mg-primary-dark);
}

:root[data-theme="confidence"] .mg-btn-ghost,
:root[data-theme="confidence"] .mg-btn-state-on,
:root[data-theme="confidence"] .mg-btn-state-off {
  background: #fff;
  border: 1px solid var(--mg-border-strong);
  color: var(--mg-text-secondary);
}

:root[data-theme="confidence"] .mg-btn-ghost:hover,
:root[data-theme="confidence"] .mg-btn-state-on:hover,
:root[data-theme="confidence"] .mg-btn-state-off:hover {
  background: #f5f8fc;
  color: var(--mg-text);
}

/* Danger reads as danger without shouting: pale ground, red word. A
   solid red button is pressed by accident far more often. */
:root[data-theme="confidence"] .mg-btn-danger,
:root[data-theme="confidence"] .mg-btn-state-block {
  background: #fef4f4;
  border: 1px solid #f6cfcf;
  color: #b3261e;
  box-shadow: none;
}

:root[data-theme="confidence"] .mg-btn-danger:hover,
:root[data-theme="confidence"] .mg-btn-state-block:hover {
  background: #fdeaea;
  border-color: #efb9b9;
}

/* -- inputs ---------------------------------------------------------------
   Production height, a real focus ring: a field an operator cannot see
   the focus of is a field they type into the wrong one of. */

:root[data-theme="confidence"] .mg-input,
:root[data-theme="confidence"] .mg-select {
  min-height: 40px;
  padding: 8px 12px;
  border: 1px solid var(--mg-border-strong);
  border-radius: 8px;
  background: #fff;
  color: var(--mg-text);
  font-size: 13px;
}

:root[data-theme="confidence"] .mg-input:focus,
:root[data-theme="confidence"] .mg-select:focus {
  outline: 0;
  border-color: var(--mg-primary);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}

/* -- toggles --------------------------------------------------------------
   A switch, drawn from the checkbox itself rather than added beside it,
   so the control the keyboard reaches is the control the eye sees. */

:root[data-theme="confidence"] .mg-field-toggle input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  flex: 0 0 auto;
  width: 36px;
  height: 20px;
  margin: 0;
  border: 0;
  border-radius: 999px;
  background: #cbd5e1;
  cursor: pointer;
  transition: background var(--mg-duration) var(--mg-ease);
}

:root[data-theme="confidence"] .mg-field-toggle input[type="checkbox"]::after {
  content: "";
  position: absolute;
  inset-block-start: 2px;
  inset-inline-start: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(15, 27, 45, 0.25);
  transition: transform var(--mg-duration) var(--mg-ease);
}

:root[data-theme="confidence"] .mg-field-toggle input[type="checkbox"]:checked {
  background: var(--mg-primary);
}

:root[data-theme="confidence"] .mg-field-toggle input[type="checkbox"]:checked::after {
  transform: translateX(16px);
}

:root[data-theme="confidence"] .mg-field-toggle input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--mg-primary);
  outline-offset: 2px;
}

/* -- tiles ----------------------------------------------------------------
   Figures first, label under, no decoration competing with the number. */

:root[data-theme="confidence"] .tile,
:root[data-theme="confidence"] .coverage-card {
  background: #fff;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-md);
}

:root[data-theme="confidence"] .tile-value {
  font-size: 26px;
  font-weight: 650;
  letter-spacing: -0.02em;
}

:root[data-theme="confidence"] .tile-label {
  color: var(--mg-text-secondary);
  font-size: 12.5px;
  font-weight: 550;
}

:root[data-theme="confidence"] .tile-sub {
  color: var(--mg-text-muted);
  font-size: 11.5px;
}

/* -- typography -----------------------------------------------------------
   One step down from the dark theme's display sizes: a control panel is
   read at arm's length, not presented. */

:root[data-theme="confidence"] .page-title,
:root[data-theme="confidence"] h1 {
  font-size: 22px;
  font-weight: 650;
  letter-spacing: -0.015em;
}

:root[data-theme="confidence"] body {
  font-size: 13.5px;
}

@media (prefers-reduced-motion: reduce) {
  :root[data-theme="confidence"] .mg-field-toggle input[type="checkbox"],
  :root[data-theme="confidence"] .mg-field-toggle input[type="checkbox"]::after {
    transition: none;
  }
}

/* ── 22-queue.css ── */
/* Per-row queue controls, and the delivery history behind them.
   ------------------------------------------------------------------------ */

/* The actions column holds four controls on a table that already scrolls
   sideways, so they stay on one line and the column keeps its own width
   rather than squeezing the recipient address. */
.row-actions {
  white-space: nowrap;
  text-align: right;
}

.row-actions .mg-btn + .mg-btn,
.row-actions .mg-btn + a.mg-btn,
.row-actions a.mg-btn + .mg-btn {
  margin-left: 4px;
}

/* Destructive, but not shouting. A red button per row on a queue of frozen
   bounces would make the whole page look like an emergency; the colour
   arrives on hover, when the pointer is already on the thing that deletes. */
.row-actions .mg-btn.danger:hover,
.row-actions .mg-btn.danger:focus-visible {
  color: var(--mg-danger);
  border-color: var(--mg-danger);
}

/* Exim's message log, shown as it was written. Wrapped rather than scrolled
   horizontally: an SMTP rejection carries the reason at the end of the line,
   which is exactly the part a horizontal scroll hides. */
.log-block {
  margin: 0;
  padding: 12px 14px;
  border-radius: 8px;
  background: var(--mg-surface-sunken, rgba(127, 127, 127, 0.08));
  border: 1px solid var(--mg-border);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 380px;
  overflow-y: auto;
}

/* A button asking for its second press.
   ------------------------------------------------------------------------ */

/* Deliberately loud, and deliberately the same size as before: a button that
   grows on arming shifts everything beside it, and an operator aiming at the
   second press would find the target had moved. */
.mg-btn.is-armed,
.row-actions .mg-btn.danger.is-armed {
  background: var(--mg-danger);
  border-color: var(--mg-danger);
  color: #fff;
  font-weight: 600;
}

/* The action row, inside the table's own panel.
   ------------------------------------------------------------------------ */

.queue-bulk {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  padding: 12px 16px;
  border-top: 1px solid var(--mg-border);
  background: var(--mg-surface-sunken, rgba(127, 127, 127, 0.04));
}

.queue-bulk .bulk-count {
  font-size: 13px;
  color: var(--mg-text-muted);
  min-width: 12ch;
}

.queue-bulk .bulk-count.has-selection {
  color: var(--mg-text);
  font-weight: 600;
}

.queue-bulk .queue-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* Pushed to its own line so the buttons stay on one row at any width. */
.queue-bulk .apply-note {
  flex-basis: 100%;
  margin: 0;
  font-size: 12px;
  color: var(--mg-text-muted);
}

/* ── 23-modern.css ── */
/* Modern — MailGuard security operations theme.
   The application structure remains shared; this module changes the visual
   language and responsive chrome without changing report semantics. */
:root[data-theme="modern"] {
  color-scheme: dark;
  --mg-bg: #071524;
  --mg-bg-secondary: #0b1c32;
  --mg-bg-tertiary: #10263e;
  --mg-surface: #10263e;
  --mg-surface-hover: #15324e;
  --mg-surface-strong: #193b59;
  --mg-surface-subtle: rgba(255,255,255,.025);
  --mg-border: #243d53;
  --mg-border-strong: #35536c;
  --mg-border-hover: #00a896;
  --mg-primary: #00a896;
  --mg-primary-light: #35c8b8;
  --mg-primary-dark: #007f72;
  --mg-primary-glow: rgba(0,168,150,.25);
  --mg-primary-soft: rgba(0,168,150,.13);
  --mg-accent: #35c8b8;
  --mg-accent-light: #68dfd2;
  --mg-accent-glow: rgba(53,200,184,.22);
  --mg-accent-soft: rgba(53,200,184,.12);
  --mg-danger: #ef476f;
  --mg-danger-strong: #cf334f;
  --mg-danger-light: #ff6b87;
  --mg-danger-soft: rgba(239,71,111,.13);
  --mg-success: #00a896;
  --mg-success-light: #35c8b8;
  --mg-success-soft: rgba(0,168,150,.13);
  --mg-warning: #e99822;
  --mg-warning-light: #ffb347;
  --mg-warning-soft: rgba(233,152,34,.14);
  --mg-info: #55a9ff;
  --mg-info-soft: rgba(85,169,255,.13);
  --mg-text: #f3f8fc;
  --mg-text-secondary: #c2d0dd;
  --mg-text-muted: #8297aa;
  --mg-text-inverse: #071524;
  --mg-header-bg: rgba(7,21,36,.94);
  --mg-scrim: rgba(3,10,18,.72);
  --mg-scrollbar: rgba(130,151,170,.22);
  --mg-scrollbar-hover: rgba(130,151,170,.40);
  --mg-shadow: 0 10px 28px rgba(0,0,0,.18);
  --mg-shadow-lg: 0 20px 55px rgba(0,0,0,.30);
  --mg-shadow-sm: 0 2px 8px rgba(0,0,0,.16);
  --mg-radius: 12px;
  --mg-radius-md: 9px;
  --mg-radius-sm: 7px;
}

:root[data-theme="modern"] .sidebar {
  background: #071a2e;
  border-right-color: #17334d;
  box-shadow: 10px 0 32px rgba(0,0,0,.14);
}

:root[data-theme="modern"] .brand-mark,
:root[data-theme="modern"] .brand-accent { color: #35c8b8; }

:root[data-theme="modern"] .nav-item {
  color: #b5c6d6;
  border-radius: 8px;
}

:root[data-theme="modern"] .nav-item:hover {
  background: rgba(53,200,184,.08);
  color: #f7fbff;
}

:root[data-theme="modern"] .nav-item.active {
  background: linear-gradient(90deg, rgba(0,168,150,.27), rgba(0,168,150,.11));
  color: #ffffff;
  box-shadow: inset 3px 0 #35c8b8;
}

:root[data-theme="modern"] .main-area,
:root[data-theme="modern"] .content { background: #0a192a; }

:root[data-theme="modern"] .topbar {
  background: var(--mg-header-bg);
  border-bottom-color: #213b53;
  backdrop-filter: blur(16px);
}

:root[data-theme="modern"] .panel,
:root[data-theme="modern"] .tile,
:root[data-theme="modern"] .coverage-card,
:root[data-theme="modern"] .filter-bar,
:root[data-theme="modern"] .table-shell {
  background: #10263e;
  border-color: #28455e;
  box-shadow: var(--mg-shadow-sm);
}

:root[data-theme="modern"] .panel-head,
:root[data-theme="modern"] .table-head,
:root[data-theme="modern"] thead th {
  background: #132a43;
  border-color: #2a4963;
}

:root[data-theme="modern"] thead th {
  color: #b9c9d8;
  letter-spacing: .035em;
}

:root[data-theme="modern"] tbody tr:hover { background: rgba(53,200,184,.055); }
:root[data-theme="modern"] a { color: #68dfd2; }
:root[data-theme="modern"] a:hover { color: #a3f2ea; }

:root[data-theme="modern"] .mg-btn-primary {
  background: #008f81;
  border-color: #00a896;
  color: #fff;
  box-shadow: 0 5px 14px rgba(0,168,150,.18);
}

:root[data-theme="modern"] .mg-btn-primary:hover { background: #00a896; }

:root[data-theme="modern"] .mg-input,
:root[data-theme="modern"] select,
:root[data-theme="modern"] textarea {
  background: #0b1e33;
  border-color: #31516c;
  color: #f3f8fc;
}

:root[data-theme="modern"] .mg-input:focus,
:root[data-theme="modern"] select:focus,
:root[data-theme="modern"] textarea:focus {
  border-color: #35c8b8;
  box-shadow: 0 0 0 3px rgba(53,200,184,.15);
}

:root[data-theme="modern"] .theme-card:has(input[value="modern"]:checked) {
  border-color: #35c8b8;
  box-shadow: 0 0 0 2px rgba(53,200,184,.18), var(--mg-shadow);
}

:root[data-theme="modern"] .state-label.on,
:root[data-theme="modern"] .badge.success,
:root[data-theme="modern"] .status-delivered {
  background: rgba(0,168,150,.15);
  color: #68dfd2;
}

@media (max-width: 760px) {
  :root[data-theme="modern"] .sidebar:not(.is-open) {
    transform: translateX(-100%);
    visibility: hidden;
  }
  :root[data-theme="modern"] .sidebar.is-open {
    transform: translateX(0);
    visibility: visible;
    width: min(86vw, 288px);
    box-shadow: 18px 0 48px rgba(0,0,0,.42);
  }
  :root[data-theme="modern"] .main-area { width: 100%; min-width: 0; }
  :root[data-theme="modern"] .page-wrap { padding-inline: 14px; }
}

/* ── 24-appearance.css ── */
/* Appearance: density, corners and motion, as tokens.
   ------------------------------------------------------------------------
   These are settings, not a theme. A theme decides what the surfaces are
   made of; these decide how much air is between them and how sharp their
   corners are — which is why they multiply against every theme rather than
   forking one. Both are stamped on the root element, so a preference and a
   palette never have to know about each other. */

:root[data-density="compact"] {
  --mg-pad-panel: 10px 12px;
  --mg-pad-head: 9px 12px;
  --mg-pad-cell: 6px 12px;
  --mg-gap-section: 12px;
  --mg-row-height: 30px;
}

:root[data-density="comfortable"] {
  --mg-pad-panel: 14px 16px;
  --mg-pad-head: 12px 15px;
  --mg-pad-cell: 10px 15px;
  --mg-gap-section: 18px;
  --mg-row-height: 40px;
}

:root[data-corners="none"]   { --mg-radius: 0;    --mg-radius-lg: 0;    --mg-radius-sm: 0; }
:root[data-corners="small"]  { --mg-radius: 7px;  --mg-radius-lg: 10px; --mg-radius-sm: 5px; }
:root[data-corners="medium"] { --mg-radius: 10px; --mg-radius-lg: 14px; --mg-radius-sm: 7px; }
:root[data-corners="large"]  { --mg-radius: 14px; --mg-radius-lg: 20px; --mg-radius-sm: 10px; }

/* Reduced motion as a choice, on top of the system's own request. Somebody
   who has not set the OS preference may still not want the interface moving
   while they read a table of failures. */
:root[data-motion="reduced"] *,
:root[data-motion="reduced"] *::before,
:root[data-motion="reduced"] *::after {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto !important;
}

/* Density applied where the eye actually measures it: panels, headings and
   table cells. Anything that does not read these tokens keeps its own
   spacing, which is why the scale is small and named rather than a blanket
   multiplier — a global scale moves borders and icons too, and the page
   stops being the same design at a different size. */
:root[data-density] .panel-body   { padding: var(--mg-pad-panel); }
:root[data-density] .panel-head   { padding: var(--mg-pad-head); }
:root[data-density] table th,
:root[data-density] table td      { padding: var(--mg-pad-cell); }
:root[data-density] .dash-section { margin-bottom: var(--mg-gap-section); }

:root[data-corners] .panel,
:root[data-corners] .tile,
:root[data-corners] .mg-btn,
:root[data-corners] .mg-input,
:root[data-corners] .mg-select { border-radius: var(--mg-radius); }

/* The chooser itself.
   ------------------------------------------------------------------------ */

.appearance-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.appearance-row .mg-field-name { min-width: 14ch; }

.appearance-choices { display: flex; gap: 6px; }

/* The chosen one is filled, not outlined: on a row of four the difference has
   to survive a glance from across a desk. */
.mg-btn.is-chosen {
  background: var(--mg-primary);
  border-color: var(--mg-primary);
  color: #fff;
  font-weight: 600;
}

.mg-select-narrow { max-width: 22ch; margin-inline-start: auto; }

/* ── 25-product.css ── */
/*
 * The reviewed design, as tokens.
 *
 * Written as a re-palette rather than a new stylesheet: every screen in the
 * mockup is a screen this product already renders, so what changed is colour,
 * weight and spacing — not markup. Overriding the tokens the rest of the
 * cascade already reads means one file decides the look of twenty-seven
 * routes, and a page nobody has reopened cannot drift away from it.
 *
 * Light is the default here because the mockup is light. Dark keeps its own
 * values below: the toggle stays honest, and a viewer who chose dark is not
 * quietly moved.
 */

:root {
  /* The scheme, first and explicitly.
   *
   * 00-theme.css declares color-scheme: dark on this same selector. Adding a
   * light palette here without correcting that produced a page with white
   * surfaces and browser-drawn controls, scrollbars and form widgets still
   * rendered for a dark UI — the two halves of an interface disagreeing about
   * which one it is. It shipped that way, and it looked exactly as bad as it
   * sounds.
   *
   * Declared on both so the toggle keeps working: light by default here, dark
   * restored in the dark block below. */
  color-scheme: light;

  /* Ground and surfaces. The app sits on a faintly blue grey and the panels
     are pure white — the separation is what makes a card read as a card
     without a shadow doing the work. */
  --mg-bg:              #f4f7fb;
  --mg-bg-secondary:    #ffffff;
  --mg-bg-tertiary:     #f8fafc;

  --mg-surface:         #ffffff;
  --mg-surface-hover:   #f8fafc;
  --mg-surface-strong:  #f1f5f9;

  --mg-border:          #e6ebf2;
  --mg-border-strong:   #d5dde8;
  --mg-border-hover:    #bfd0e8;

  /* One blue, used for the thing being acted on and nothing else. The
     mockup spends it on the active nav item and the Apply button; spending
     it anywhere else is what turns an interface into decoration. */
  --mg-primary:         #2563eb;
  --mg-primary-light:   #3b82f6;
  --mg-primary-dark:    #1d4ed8;
  --mg-primary-glow:    rgba(37, 99, 235, 0.18);
  --mg-primary-soft:    rgba(37, 99, 235, 0.08);

  --mg-accent:          #2563eb;
  --mg-accent-light:    #60a5fa;
  --mg-accent-glow:     rgba(37, 99, 235, 0.18);
  --mg-accent-soft:     rgba(37, 99, 235, 0.08);

  --mg-text:            #0f172a;
  --mg-text-secondary:  #475569;
  --mg-text-muted:      #64748b;
  --mg-text-faint:      #94a3b8;

  /* Semantic colours are separate from the accent and never used to mean
     "interactive". Green is a state, not a button. */
  --mg-success:         #059669;
  --mg-success-soft:    #ecfdf5;
  --mg-warning:         #d97706;
  --mg-warning-soft:    #fffbeb;
  --mg-danger:          #dc2626;
  --mg-danger-soft:     #fef2f2;
  --mg-info:            #0284c7;
  --mg-info-soft:       #f0f9ff;

  --mg-shadow:          0 1px 2px rgba(15, 23, 42, .04);
  --mg-shadow-lg:       0 2px 8px rgba(15, 23, 42, .06);
}

/* Dark keeps its own ground. Not an inversion of the above — an inverted
   light palette reads as grey mud; these are chosen for the dark ground. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --mg-bg:              #0b1017;
  --mg-bg-secondary:    #111823;
  --mg-bg-tertiary:     #161f2c;
  --mg-surface:         #111823;
  --mg-surface-hover:   #17202d;
  --mg-surface-strong:  #1c2735;
  --mg-border:          #222e3d;
  --mg-border-strong:   #2f3d4f;
  --mg-text:            #e8edf3;
  --mg-text-secondary:  #aab6c4;
  --mg-text-muted:      #8494a5;
  --mg-text-faint:      #66748540;
  --mg-primary:         #60a5fa;
  --mg-primary-dark:    #3b82f6;
  --mg-success:         #34d399;
  --mg-success-soft:    #10251d;
  --mg-warning:         #fbbf24;
  --mg-warning-soft:    #2a2011;
  --mg-danger:          #f87171;
  --mg-danger-soft:     #2b1616;
}

/* ------------------------------------------------------------- sidebar */

.sidebar {
  background: var(--mg-bg-secondary);
  border-right: 1px solid var(--mg-border);
}

/* The active item is a filled block, not a tinted one. In the mockup it is
   the only saturated shape on the screen, which is what makes "where am I"
   answerable without reading. */
.sidebar a.is-active,
.sidebar .nav-link.is-active,
.sidebar [aria-current="page"] {
  background: var(--mg-primary);
  color: #fff;
  border-radius: var(--mg-radius-sm, 7px);
}

.sidebar a.is-active svg,
.sidebar [aria-current="page"] svg { color: #fff; }

.sidebar .nav-section,
.sidebar .nav-group-label {
  font-size: .66rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--mg-text-faint);
  font-weight: 600;
}

/* A count beside a nav item, for the pages that carry one. Red because it
   is a backlog, not a decoration. */
.nav-badge {
  margin-left: auto;
  background: var(--mg-danger-soft);
  color: var(--mg-danger);
  font-size: .68rem;
  font-weight: 700;
  padding: .05rem .38rem;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- cards */

.panel,
.figure-strip > div,
.stat-card {
  background: var(--mg-surface);
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius, 10px);
  box-shadow: var(--mg-shadow);
}

/* The stat row: label above, figure large, comparison under it. Figures use
   tabular numerals so a column of them lines up — a stat row where the digits
   dance is a stat row nobody scans. */
.figure-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: .75rem;
  background: none;
  border: 0;
}

.figure-strip > div { padding: .9rem 1rem; display: block; }

.figure-strip strong {
  display: block;
  font-size: 1.65rem;
  font-weight: 700;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--mg-text);
  line-height: 1.15;
}

.figure-strip span {
  display: block;
  font-size: .78rem;
  color: var(--mg-text-muted);
  order: -1;
  margin-bottom: .15rem;
}

/* --------------------------------------------------------------- tables */

.data-table thead th,
.table-panel thead th {
  background: var(--mg-bg-tertiary);
  color: var(--mg-text-faint);
  font-size: .68rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  font-weight: 600;
  border-bottom: 1px solid var(--mg-border);
}

.data-table td,
.table-panel td { border-bottom: 1px solid var(--mg-border); }

.data-table tbody tr:hover,
.table-panel tbody tr:hover { background: var(--mg-surface-hover); }

.data-table td.num,
.table-panel td.num { font-variant-numeric: tabular-nums; }

/* Message identifiers read as links to their own report, and are the one
   place a monospace face earns its keep. */
.data-table code,
.table-panel code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .82em;
  color: var(--mg-primary);
}

/* ------------------------------------------------------- state and pills */

.pill, .status, .tag, .shape {
  display: inline-block;
  font-size: .72rem;
  font-weight: 600;
  padding: .16rem .5rem;
  border-radius: 5px;
  letter-spacing: .01em;
  border: 0;
}

.pill.ok,   .status.delivered, .tag.good, .shape-ok   { background: var(--mg-success-soft); color: var(--mg-success); }
.pill.warn, .status.deferred,  .tag.warn, .shape-warn { background: var(--mg-warning-soft); color: var(--mg-warning); }
.pill.bad,  .status.rejected,  .status.bounced,
.tag.bad,   .shape-bad                                { background: var(--mg-danger-soft);  color: var(--mg-danger); }
.pill.mute, .tag.mute, .shape-mute                    { background: var(--mg-surface-strong); color: var(--mg-text-muted); }
.pill.info                                            { background: var(--mg-info-soft);    color: var(--mg-info); }

/* -------------------------------------------------------------- buttons */

/* The base button, its hover, the primary and the ghost all lived here as
   well as in module 06, silently winning on radius, border and weight. They
   are defined once now, there. */

/* An irreversible action never borrows the accent. */
.mg-btn-danger {
  background: var(--mg-surface);
  border-color: var(--mg-danger);
  color: var(--mg-danger);
}

.mg-btn-danger:hover { background: var(--mg-danger-soft); }

/* --------------------------------------------------------- filter bars */

.filters {
  background: var(--mg-surface);
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius, 10px);
  padding: .85rem 1rem;
  gap: .85rem;
  align-items: end;
}

.filters label > span:first-child {
  font-size: .7rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--mg-text-faint);
  font-weight: 600;
}

.mg-select, .filters input[type="text"], .filters input[type="search"] {
  background: var(--mg-surface);
  border: 1px solid var(--mg-border-strong);
  border-radius: var(--mg-radius-sm, 7px);
  color: var(--mg-text);
}

.mg-select:focus, .filters input:focus {
  outline: 2px solid var(--mg-primary-glow);
  border-color: var(--mg-primary);
}

/* A count of what the filters produced, beside the heading it belongs to. */
.result-count {
  background: var(--mg-primary-soft);
  color: var(--mg-primary);
  font-size: .74rem;
  font-weight: 700;
  padding: .12rem .5rem;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------- header */

.header {
  background: var(--mg-bg-secondary);
  border-bottom: 1px solid var(--mg-border);
}

.header-gradient { display: none; }   /* the mockup has no gradient bar */

.header-title { font-size: 1.05rem; font-weight: 650; color: var(--mg-text); }
.header-subtitle { font-size: .8rem; color: var(--mg-text-muted); }

/* ------------------------------------------------------------- notices */

.notice {
  background: var(--mg-surface);
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius, 10px);
}

.notice.ready   { border-left: 3px solid var(--mg-success); }
.notice.warning { border-left: 3px solid var(--mg-warning); }

.panel-head h2 { font-size: .98rem; font-weight: 650; }
.panel-head p  { font-size: .82rem; color: var(--mg-text-muted); }

.muted-line { color: var(--mg-text-muted); }

/* ------------------------------------------------------------ stat tiles */
/*
 * The dashboard's top row, to the reviewed design.
 *
 * Icon in a tinted square on the left, then label, figure, note stacked
 * beside it. The square is what separates eight tiles in a row from reading
 * as one grey block, and its tint carries the tile's own state — a figure
 * that matters is coloured at the icon, not by shouting the number.
 */
.tile {
  display: flex;
  align-items: flex-start;
  gap: .8rem;
  padding: 1rem;
  background: var(--mg-surface);
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius, 10px);
  box-shadow: var(--mg-shadow);
  text-decoration: none;
  color: inherit;
}

.tile:hover { border-color: var(--mg-border-hover); box-shadow: var(--mg-shadow-lg); }

.tile-icon {
  flex: 0 0 auto;
  width: 2.6rem;
  height: 2.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: var(--mg-surface-strong);
  color: var(--mg-text-muted);
}

.tile-text { display: flex; flex-direction: column; min-width: 0; }

/* Label first and quiet; the figure is what the eye lands on. */
.tile-label {
  font-size: .78rem;
  color: var(--mg-text-muted);
  line-height: 1.3;
}

.tile-value {
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  color: var(--mg-text);
}

.tile-sub {
  font-size: .74rem;
  color: var(--mg-text-faint);
  line-height: 1.35;
  /* Wrapped, not clipped: this line is where the figure's units and its
     qualification live, and half of "% of attempts, per recipient" is
     worse than none of it. */
  white-space: normal;
}

/* State lives on the icon square. A tile whose whole background changes
   colour makes its own number harder to read, which is the opposite of what
   drawing attention to it is for. */
.tile.tone-success .tile-icon { background: var(--mg-success-soft); color: var(--mg-success); }
.tile.tone-warning .tile-icon { background: var(--mg-warning-soft); color: var(--mg-warning); }
.tile.tone-danger  .tile-icon { background: var(--mg-danger-soft);  color: var(--mg-danger); }
.tile.tone-info    .tile-icon { background: var(--mg-info-soft);    color: var(--mg-info); }

.tile.tone-danger  .tile-value { color: var(--mg-danger); }

@media (max-width: 520px) {
  .tile { padding: .8rem; gap: .6rem; }
  .tile-icon { width: 2.2rem; height: 2.2rem; }
  .tile-value { font-size: 1.35rem; }
}

/* ------------------------------------------------------ protection posture */
/*
 * Four cards saying what is protecting this server right now, to the reviewed
 * design: icon in a circle, name and consequence in the middle, state as a
 * pill on the right, chevron last.
 *
 * The state is a pill and not a coloured card for the same reason as the stat
 * tiles: a card that turns red makes the sentence inside it harder to read,
 * and that sentence is the part an operator has to act on.
 */
.posture-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: .75rem;
}

.posture {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .85rem .95rem;
  background: var(--mg-surface);
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius, 10px);
  text-decoration: none;
  color: inherit;
}

.posture:hover { border-color: var(--mg-border-hover); box-shadow: var(--mg-shadow); }

.posture-icon {
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--mg-surface-strong);
  color: var(--mg-text-muted);
}

.posture-body { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.posture-body strong { font-size: .92rem; font-weight: 600; line-height: 1.3; }

/* The consequence, wrapped. "Switched on but this host cannot carry it out" is
   the whole value of the card and it does not fit on one line. */
.posture-body small {
  font-size: .76rem;
  color: var(--mg-text-muted);
  line-height: 1.4;
  white-space: normal;
}

.posture-aside {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-left: auto;
}

.posture-state {
  font-size: .7rem;
  font-weight: 600;
  padding: .16rem .48rem;
  border-radius: 5px;
  white-space: nowrap;
  background: var(--mg-surface-strong);
  color: var(--mg-text-muted);
}

.posture-go { color: var(--mg-text-faint); font-size: 1.1rem; line-height: 1; }

/* State on the pill and the ring, never on the whole card. */
.posture.tone-ok   .posture-icon  { background: var(--mg-success-soft); color: var(--mg-success); }
.posture.tone-ok   .posture-state { background: var(--mg-success-soft); color: var(--mg-success); }
.posture.tone-warn .posture-icon  { background: var(--mg-warning-soft); color: var(--mg-warning); }
.posture.tone-warn .posture-state { background: var(--mg-warning-soft); color: var(--mg-warning); }

/* Degraded is the one that earns a mark on the card itself: a protection
   switched on that cannot run is this product failing its own promise, and a
   pill alone is too quiet for it. */
.posture.tone-bad  { border-left: 3px solid var(--mg-danger); }
.posture.tone-bad  .posture-icon  { background: var(--mg-danger-soft); color: var(--mg-danger); }
.posture.tone-bad  .posture-state { background: var(--mg-danger-soft); color: var(--mg-danger); }

@media (max-width: 420px) {
  .posture { align-items: flex-start; }
  .posture-aside { flex-direction: column; align-items: flex-end; gap: .25rem; }
}

/* ------------------------------------------------------------ share bars */
/*
 * The delivered share beside a sender, as a bar.
 *
 * A column of numbers has to be read row by row; a column of bars is scanned,
 * and the one short bar is findable without reading any of them. That is the
 * only job here, so the bar carries no figure of its own — the percentage is
 * already in the cell to its left, and repeating it would be two readings of
 * one fact.
 */
.share-col { width: 7rem; }

.share {
  display: block;
  height: 6px;
  border-radius: 3px;
  background: var(--mg-surface-strong);
  overflow: hidden;
}

.share-fill {
  display: block;
  height: 100%;
  background: var(--mg-success);
  border-radius: 3px;
}

/* Colour only where it means something. A bar that is short is already
   visible; turning it red as well is for the case an operator is scanning
   past at speed. */
.share-fill.is-poor { background: var(--mg-danger); }

@media (max-width: 720px) {
  /* The bar is the first thing to go on a narrow screen: it is a second
     rendering of a number that is still on the row. */
  .share-col { display: none; }
}

/* The build number beside the release name. Quieter than the name but present:
   during a beta it is the only thing on the line that changes, and it is what
   an operator checks after an update. */
.version-line .build-id {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .68rem;
  color: var(--mg-text-faint);
  letter-spacing: .01em;
}

/* --------------------------------------------------------- area chart */
/*
 * The volume line. Fill under a stroke, with the peak ruled and named.
 * Colours come from the token set so the chart follows the theme rather than
 * being the one element that ignores it.
 */
.chart-area {
  fill: var(--mg-primary);
  fill-opacity: .12;
  stroke: none;
}

.chart-line {
  fill: none;
  stroke: var(--mg-primary);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* Dashed, so it reads as a reference rather than as data. */
.chart-peak {
  stroke: var(--mg-text-faint);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}

.chart-point { fill: var(--mg-primary); }


/* The operating system's own preference, when the viewer has not chosen.
   Without this a dark-system viewer got the light palette and no way to know
   the toggle existed. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --mg-bg:              #0b1017;
    --mg-bg-secondary:    #111823;
    --mg-bg-tertiary:     #161f2c;
    --mg-surface:         #111823;
    --mg-surface-hover:   #17202d;
    --mg-surface-strong:  #1c2735;
    --mg-border:          #222e3d;
    --mg-border-strong:   #2f3d4f;
    --mg-text:            #e8edf3;
    --mg-text-secondary:  #aab6c4;
    --mg-text-muted:      #8494a5;
    --mg-text-faint:      #667485;
    --mg-primary:         #60a5fa;
    --mg-primary-dark:    #3b82f6;
    --mg-success:         #34d399;
    --mg-success-soft:    #10251d;
    --mg-warning:         #fbbf24;
    --mg-warning-soft:    #2a2011;
    --mg-danger:          #f87171;
    --mg-danger-soft:     #2b1616;
  }
}

/* ------------------------------------------------- room for the new tiles */
/*
 * The tile grid was sized for the old card: a small icon above a number, which
 * fitted 190px. The reviewed layout puts a 42px icon square beside the text, so
 * that width left about 130px for a 1.6rem figure and a label that then wrapped
 * onto three lines. Eight of those in a row is the cramped, unreadable result
 * the operator called out.
 *
 * Wider minimums, and the same correction on the narrow breakpoint — where 150px
 * was worse still.
 */
.tile-grid { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: .75rem; }

@media (max-width: 900px) {
  .tile-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
}

/* One per row below this: two cramped tiles side by side are harder to read
   than two roomy ones stacked, and a phone has the vertical space. */
@media (max-width: 560px) {
  .tile-grid { grid-template-columns: 1fr; }
}

/* Sections were spaced for a denser page. The reviewed design breathes, and
   panels that touch read as one long panel. */
.dash-section { margin-bottom: 1.75rem; }
.panel + .panel, .panel-grid + .panel, .panel + .panel-grid { margin-top: 1rem; }
.panel-grid { gap: 1rem; }

/* A warning that sits with the control it is about, not at the top of a page
   somebody has already scrolled past. */
.destructive-note {
  border-left: 2px solid var(--mg-danger);
  padding: .35rem 0 .35rem .7rem;
  margin: .5rem 0 0;
  font-size: .8rem;
  max-width: 72ch;
}

/* Two readings of one conflict, side by side. Side by side on purpose: they
   are alternatives, and stacking them makes the first look like the answer
   and the second like a footnote. */
.conflict-choices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: .6rem;
  margin-top: .6rem;
}

.conflict-choice {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  padding: .55rem .7rem;
  border: 1px solid var(--mg-border);
  border-radius: var(--mg-radius-sm, 7px);
  background: var(--mg-bg-tertiary);
  font-size: .78rem;
  line-height: 1.45;
}

.conflict-choice strong { font-size: .82rem; }

/* --------------------------------------------------------- route split */
/*
 * Where outbound mail went, as three plain figures rather than a chart. The
 * numbers are wildly uneven on a hosting server — tens of thousands local
 * against a few hundred relayed — and any proportional drawing of that is a
 * full bar beside an invisible sliver, which says less than the figures do.
 */
.route-split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: .75rem;
  margin-bottom: 1rem;
}

.route-leg {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  padding: .7rem .8rem;
  border: 1px solid var(--mg-border);
  border-left: 3px solid var(--mg-text-faint);
  border-radius: var(--mg-radius-sm, 7px);
  background: var(--mg-bg-tertiary);
}

.route-leg strong {
  font-size: 1.35rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}

.route-leg span { font-size: .8rem; color: var(--mg-text-secondary); }
.route-leg small { font-size: .74rem; color: var(--mg-text-muted); line-height: 1.4; margin-top: .2rem; }

/* The outward leg is the one that carries reputation, so it carries the mark. */
.route-leg.is-out     { border-left-color: var(--mg-primary); }
.route-leg.is-unknown { border-left-color: var(--mg-warning); }

/* ---------------------------------------------------------- card scale */
/*
 * A quarter smaller across the board.
 *
 * The figures were set for a page with four cards on it and the dashboard now
 * carries a dozen. Scaled by trimming padding and type together — shrinking
 * one without the other leaves either cramped text in a roomy box or a roomy
 * box around nothing.
 */
.tile { padding: .75rem; gap: .6rem; }
.tile-icon { width: 2rem; height: 2rem; border-radius: 8px; }
.tile-icon svg { width: 15px; height: 15px; }
.tile-label { font-size: .72rem; }
.tile-value { font-size: 1.2rem; }
.tile-sub { font-size: .68rem; }

.tile-grid { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: .6rem; }

.posture { padding: .65rem .75rem; gap: .6rem; }
.posture-icon { width: 1.95rem; height: 1.95rem; }
.posture-icon svg { width: 15px; height: 15px; }
.posture-body strong { font-size: .84rem; }
.posture-body small { font-size: .7rem; }
.posture-row { grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); gap: .6rem; }

.route-leg { padding: .55rem .65rem; }
.route-leg strong { font-size: 1.05rem; }
.route-leg span { font-size: .73rem; }
.route-leg small { font-size: .68rem; }

.figure-strip > div { padding: .7rem .8rem; }
.figure-strip strong { font-size: 1.25rem; }

/* ------------------------------------------------------------ per cent */
/*
 * A share, shown as a figure and a length at once.
 *
 * A percentage in prose is read and forgotten; a bar alone cannot be compared
 * against a threshold. Together the number answers "how much" and the bar
 * answers "is that a lot", which are the two questions actually being asked of
 * every share on these pages.
 *
 * Tabular numerals so a column of them lines up, and the track always drawn —
 * a 2% share with no track behind it looks like a rendering fault rather than
 * a small number.
 */
.pct {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.pct-value { font-weight: 600; font-size: .82rem; min-width: 3.2em; text-align: right; }

.pct-track {
  width: 3.5rem;
  height: 4px;
  border-radius: 2px;
  background: var(--mg-surface-strong);
  overflow: hidden;
  flex: 0 0 auto;
}

.pct-fill { display: block; height: 100%; border-radius: 2px; background: var(--mg-text-muted); }

/* Meaning, where the share has one. Good and bad are not the same direction
   for every figure — a high delivered share is good, a high refused share is
   not — so the tone is set by the caller, never inferred from the number. */
.pct.is-good .pct-fill { background: var(--mg-success); }
.pct.is-warn .pct-fill { background: var(--mg-warning); }
.pct.is-bad  .pct-fill { background: var(--mg-danger); }
.pct.is-good .pct-value { color: var(--mg-success); }
.pct.is-bad  .pct-value { color: var(--mg-danger); }

@media (max-width: 640px) {
  /* The bar goes first on a narrow screen: the number survives alone, the
     bar does not survive being squeezed to a few pixels. */
  .pct-track { display: none; }
}

/* The version, which is also the way to the Updates page. */
.version-line { display: block; text-decoration: none; color: inherit; }
.version-line:hover .build-id { color: var(--mg-primary); }

/* An update waiting. Coloured because it is an invitation, not an alarm — a
   newer build is good news, and red would put it beside the failures. */
.update-waiting {
  display: block;
  margin-top: .15rem;
  font-size: .68rem;
  font-weight: 600;
  color: var(--mg-primary);
}

/* ── 26-probe.css ── */
/*
 * Components the message probe introduced.
 *
 * A numbered procedure.
 *
 * Used where the order is real and the reader loses if they get it wrong --
 * the deliverability probe has to be copied, then sent, then looked for, and
 * a paragraph containing all three reads as one instruction nobody follows.
 * Deliberately not used for lists that merely have several items.
 */
.mg-steps {
  display: grid;
  gap: 0.85rem;
  margin: 0.9rem 0 1rem;
  padding-left: 1.35rem;
  list-style: decimal;
}

.mg-steps > li {
  padding-left: 0.35rem;
  line-height: 1.55;
}

.mg-steps > li::marker {
  font-weight: 700;
  color: var(--mg-accent);
}

/* The action, on its own line, so the step can be found without reading it. */
.mg-step-what {
  display: block;
  font-weight: 600;
}

/* The code and its button sit together rather than stacking. */
.mg-steps .dns-record-fields {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: 0.35rem;
}

/* The small button is .btn-sm, defined in module 17. This file briefly had
   a second one under another name, which is how a project ends up with two
   sizes that differ by a pixel. */

/*
 * The score, where it appears inside a result rather than as a panel heading.
 *
 * The probe result had no score at all on a panel headed "Score a message";
 * it now carries the same breakdown the fleet table produces, and needs the
 * same head -- name and headline on the left, the number on the right --
 * without the panel chrome that would nest a panel inside a note.
 */
.score-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 1rem 0 0.35rem;
}

.score-head p {
  margin: 0.15rem 0 0;
  font-size: 0.9em;
  opacity: 0.85;
}

/*
 * A panel head that survives its own contents.
 *
 * The head is a flex row of title-and-description against whatever controls
 * belong to the panel -- a count, and two more that tables.js appends at
 * runtime: an export button and a column menu. With a one-line description
 * and one control there was room. With a two-line description and three, the
 * items shrank below their own text and wrapped inside themselves: the count
 * pill broke across two lines and the export button rendered the word "page"
 * on top of the paragraph beside it.
 *
 * Nothing is truncated to make it fit. The row wraps, the text block is the
 * part allowed to give, and each control keeps its own width.
 */
.panel-head {
  flex-wrap: wrap;
}

.panel-head > div:first-child {
  flex: 1 1 320px;
  min-width: 0;
}

.panel-head > .result-count,
.panel-head > .table-export,
.panel-head > .column-menu,
.panel-head > .mg-btn {
  flex: 0 0 auto;
  white-space: nowrap;
}

/*
 * A cell holding prose or a record rather than a label.
 *
 * `table { white-space: nowrap }` is right for a table of counts and wrong
 * for the one column that explains something, and `td small` is clipped at
 * 300px with an ellipsis on top of that. The product already marked those
 * cells `class="wrap"` in a dozen places -- on DNS record values, on IP
 * notes, on the sentence a receiving server sent back -- and the class was
 * never defined anywhere, so every one of them was silently cut off at the
 * same 300 pixels.
 */
td.wrap,
th.wrap {
  white-space: normal;
}

td.wrap small,
td.wrap strong,
td.wrap code,
td.wrap p {
  max-width: none;
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  /* A DKIM public key has no spaces in it; without this it forces the whole
     table sideways rather than wrapping. */
  overflow-wrap: anywhere;
}

/* ── 27-charts.css ── */
/*
 * Charts.
 *
 * Chart.php has emitted .chart, .chart-tick, .chart-axis, .chart-total,
 * .chart-legend, .chart-key and .chart-with-key since it was written, and
 * only four of its class names were ever styled. The rest fell back to the
 * browser's defaults: SVG text is black at 16px whatever the theme, so every
 * axis figure in the product was rendered in the one colour that is invisible
 * on the dark ground it was drawn against.
 *
 * Text wears text tokens here, never a series colour. A figure beside a bar
 * is a label; the bar carries the identity.
 */

.chart {
  margin: 0;
}

.chart svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* Names and axis figures: recessive, and never the colour of the data. */
.chart-tick {
  fill: var(--mg-text-muted);
  font-family: var(--mg-font);
  font-size: 11px;
}

/* The number at the end of a row. Tabular, because a column of figures that
   do not line up is a column the eye cannot compare. */
.chart-figure {
  fill: var(--mg-text-secondary);
  font-family: var(--mg-font);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.chart-total {
  fill: var(--mg-text);
  font-family: var(--mg-font);
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* One hairline, not a grid: the figures are written on the marks. */
.chart-axis {
  stroke: var(--chart-grid, rgba(15, 23, 42, 0.10));
  stroke-width: 1;
}

.chart-empty {
  fill: var(--mg-text-muted);
  font-family: var(--mg-font);
  font-size: 12px;
}

/* -- legends ------------------------------------------------------------ */

.chart-with-key {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}

.chart-legend {
  display: grid;
  gap: 6px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
  font-size: 12px;
  color: var(--mg-text-secondary);
}

/* Two or three keys belong on one line under the chart they explain. */
.chart-legend-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
}

.chart-legend li {
  display: flex;
  align-items: center;
  gap: 8px;
}

.chart-legend strong {
  margin-inline-start: auto;
  font-variant-numeric: tabular-nums;
}

.chart-legend small {
  color: var(--mg-text-muted);
  font-variant-numeric: tabular-nums;
}

.chart-key {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: 0 0 auto;
}

/*
 * Forced colours, where the reader's own palette replaces ours.
 *
 * The marks keep their shape and the system supplies the ink. Without this
 * every bar is painted the same forced foreground and a chart whose whole
 * meaning is which bar is which becomes one solid block.
 */
@media (forced-colors: active) {
  .chart rect,
  .chart circle {
    forced-color-adjust: none;
    stroke: CanvasText;
    stroke-width: 1;
  }

  .chart-key {
    forced-color-adjust: none;
    border: 1px solid CanvasText;
  }
}

/*
 * A word that explains itself.
 *
 * Drawn by the stylesheet from a data attribute, because the CSP here is
 * script-src 'self' and a tooltip that needs a script is one more thing to
 * load before a table can be read -- and nothing at all on a printed page.
 *
 * Shown on focus as well as hover: a keyboard reaches these, and an
 * explanation only a mouse can get to is an explanation half the operators
 * never see.
 */
.hint {
  position: relative;
  border-bottom: 1px dashed var(--mg-border-strong);
  cursor: help;
}

.hint::after {
  content: attr(data-hint);
  position: absolute;
  z-index: 40;
  inset-inline-start: 0;
  top: calc(100% + 8px);
  width: max-content;
  max-width: 320px;
  padding: 10px 12px;
  border: 1px solid var(--mg-border-strong);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-bg-tertiary);
  box-shadow: var(--mg-shadow);
  color: var(--mg-text-secondary);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.5;
  /* The explanation is prose inside a table that is nowrap; without this it
     is one line running off the side of the screen. */
  white-space: normal;
  text-align: start;
  text-transform: none;
  letter-spacing: normal;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--mg-duration) var(--mg-ease);
}

.hint:hover::after,
.hint:focus-visible::after,
.hint:focus::after {
  opacity: 1;
  visibility: visible;
}

/* Near the right edge the box would open off-screen, so it opens the other
   way. Marked by the caller, because CSS cannot measure the viewport. */
.hint-end::after {
  inset-inline-start: auto;
  inset-inline-end: 0;
}

.hint:focus-visible {
  outline: 2px solid var(--mg-primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .hint::after {
    transition: none;
  }
}

/*
 * The figures beside a ring.
 *
 * A ring is read by area, and area is the one thing people judge badly -- a
 * 12% slice and an 18% slice look alike. The number is the answer; the ring
 * is only the shape of it, so the numbers get their own row underneath rather
 * than living in a legend nobody reads.
 */
.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 12px;
  margin-top: 18px;
}

.stat-card {
  display: grid;
  gap: 3px;
  padding: 12px 14px;
  border: 1px solid var(--mg-border);
  border-inline-start: 3px solid var(--chart-7, #5b6674);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-surface);
}

/* The stripe carries severity, the same as the bars do. */
.stat-card.state-good { border-inline-start-color: var(--mg-success); }
.stat-card.state-warn { border-inline-start-color: var(--mg-warning); }
.stat-card.state-bad  { border-inline-start-color: var(--mg-danger); }

.stat-card-figure {
  color: var(--mg-text);
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.stat-card-name {
  color: var(--mg-text-secondary);
  font-size: 13px;
  font-weight: 600;
}

.stat-card-count {
  color: var(--mg-text-muted);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}

/* The three readings of one page, as one control. */
.view-switch {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 18px;
}

/* ── 28-records.css ── */
/*
 * The record layouts, and the print sheet.
 *
 * Every class below was already in the markup and in no stylesheet. That is
 * not a cosmetic gap: a class the browser has never heard of does nothing and
 * looks like a decision, so the page renders as an unstyled list and the
 * markup insists it was designed.
 *
 * The worst of them was .visually-hidden on the Scripts page, which is meant
 * to hide a word from sighted readers and keep it for a screen reader. With
 * no rule it hid nothing, so every risk mark showed its symbol and its label
 * side by side. That one is gone -- it says .sr-only now, which exists.
 */

/* Inline muted text. .muted-line is the block version and sets a size with
   it, which is wrong inside a <small> that is already smaller. */
.muted {
  color: var(--mg-text-muted);
}

/* -- a held message, as a record ---------------------------------------- */

.held-list {
  display: grid;
  gap: 10px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}

.held-item {
  display: grid;
  gap: 6px;
  padding: 12px 14px;
  border: 1px solid var(--mg-border);
  border-inline-start: 3px solid var(--mg-warning);
  border-radius: var(--mg-radius-sm);
  background: var(--mg-surface);
}

/* The journey on one line: from, arrow, to. It wraps on a narrow screen
   rather than scrolling, because an address cut in half is not an address. */
.held-route {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 13px;
}

.held-from,
.held-to {
  color: var(--mg-text);
  font-family: var(--mg-mono);
  overflow-wrap: anywhere;
}

.held-arrow {
  color: var(--mg-text-muted);
}

.held-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 14px;
  color: var(--mg-text-muted);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}

.held-id {
  font-family: var(--mg-mono);
  font-size: 11px;
}

/* The reason, in full and never clipped: it is the line that says what to do
   next, and it is usually the other server's own wording. */
.held-issue {
  margin: 2px 0 0;
  color: var(--mg-text-secondary);
  font-size: 12.5px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

/* -- why a message did not arrive --------------------------------------- */

.failure-note {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}

.failure-words {
  color: var(--mg-text-muted);
  font-family: var(--mg-mono);
  font-size: 11px;
  line-height: 1.5;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* -- the record a domain should publish --------------------------------- */

.dns-wanted {
  padding: 2px 7px;
  border-radius: var(--mg-radius-sm);
  background: var(--mg-success-soft);
  color: var(--mg-success);
  font-family: var(--mg-mono);
  font-size: 12px;
  overflow-wrap: anywhere;
}

.panel-footer {
  padding: 14px 24px;
  border-top: 1px solid var(--mg-border);
  color: var(--mg-text-muted);
  font-size: 12px;
}

/*
 * Print.
 *
 * The Reports page has marked its controls .print-hide and its content
 * .print-keep since it was written, and no stylesheet ever read either -- so
 * a printed report carried the filter bar, the refresh control and the
 * navigation, and the operator got the interface rather than the report.
 */
@media print {
  .print-hide,
  .sidebar,
  .topbar,
  .view-switch,
  .filters,
  .table-export,
  .column-menu {
    display: none !important;
  }

  .print-keep,
  .panel {
    break-inside: avoid;
    border-color: #ccc;
    box-shadow: none;
  }

  /* Charts are SVG and print at the printer's resolution; the surface behind
     them is the page's, which on paper is white whatever the theme says. */
  body {
    background: #fff;
    color: #000;
  }
}

/* An expanded row belongs to the one above it, and should look subordinate
   rather than like the next record in the list. */
.detail-row > td {
  background: var(--mg-surface-subtle);
  border-top: 0;
}

/* A form is a block; inside the sidebar's footer that pushed its own line.
   The control is the whole of it, so the form should take no space of its
   own. */
.sidebar-signout {
  display: flex;
}

.sidebar-signout > * {
  flex: 1 1 auto;
}
