/* =============================================================================
   SafeLine WAF Console — Visual Design System
   -----------------------------------------------------------------------------
   ONE token system. Light values live in :root, dark overrides live in
   html[data-theme="dark"]. Nothing below those two blocks may carry a
   theme-specific literal colour — every surface, hairline and ink reads a token.

   Palette source: SafeLine CE production MUI theme.
     light accent : #0fc6c2 (flat teal)
     dark  accent : linear-gradient(153deg, #F4D1B4 0%, #EBBB97 100%)  (sand)
   Geometry matches SafeLine's live measurements: 220px rail, 64px topbar,
   8px card radius, 6px control radius, hairline + soft shadow elevation.
   Flat and quiet by design — no glass, no glow, no large radii.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1. TOKENS — LIGHT (default)
   ------------------------------------------------------------------------ */
:root {
  color-scheme: light;

  /* Brand / accent ------------------------------------------------------- */
  --accent:          #0fc6c2;
  --accent-grad:     linear-gradient(153deg, #0fc6c2 0%, #0fc6c2 100%);
  --accent-hover:    #0ab5b2;
  --accent-ink:      #059ea1;              /* accent used as text on a tint   */
  --accent-soft:     rgba(15, 198, 194, .10);   /* bg.primary2 / chip checked */
  --accent-tint:     #c9f4f1;                   /* bg.primary1                */
  --accent-border:   rgba(15, 198, 194, .28);
  --accent-shadow:   rgba(15, 198, 194, .20);
  --accent-hover-bg: rgba(15, 198, 194, .18);   /* menu hover                 */
  --on-accent:       #ffffff;
  /* brand mark — the two concentric arcs. Outer takes the accent, inner is
     pushed one step deeper so the ring stays legible against --card. */
  --brand-arc-1:     #0fc6c2;
  --brand-arc-2:     #059ea1;
  /* legacy aliases still read by app.js / charts.js */
  --teal:            #0fc6c2;
  --teal-hover:      #0ab5b2;
  --teal-dark:       #059ea1;
  --teal-soft:       rgba(15, 198, 194, .10);
  --teal-border:     rgba(15, 198, 194, .28);

  /* Series & semantics --------------------------------------------------- */
  --blue:      #2463c5;
  --orange:    #ff8859;
  --red:       #ff5576;
  --amber:     #f5bd0c;
  --green:     #10b981;
  --purple:    #722ed1;
  --magenta:   #c41d7f;
  --neutral:   rgba(0, 0, 0, .45);

  --blue-ink:    #1d4ed8;
  --orange-ink:  #d4600c;
  --red-ink:     #d92c46;
  --amber-ink:   #b47c06;
  --green-ink:   #07966f;
  --purple-ink:  #6821c0;
  --magenta-ink: #c41d7f;
  --neutral-ink: rgba(0, 0, 0, .55);

  --globe: #b2f7f2;
  --globe-halo: url("vendor/globel_bg.png");

  /* Layout --------------------------------------------------------------- */
  --sidebar-w: 228px;
  --topbar-h: 66px;
  --r: 4px;          /* MUI shape.borderRadius — cards, tabs, chips, inputs */
  --r-sm: 4px;
  --gap: 16px;
  --dash-right-qps-h: 130px;
  --dash-right-status-h: 187px;
  --card-head-h: 72px;       /* 20 top + 36 title row + 16 bottom */
  --ease: cubic-bezier(.2, .8, .2, 1);

  /* Surfaces ------------------------------------------------------------- */
  --bg:          rgb(247, 248, 250);   /* bg.paper0 — page canvas            */
  --sidebar-bg:  rgb(247, 248, 250);   /* the rail IS the canvas — no divider */
  --card:        #ffffff;              /* bg.paper                           */
  --card-inner:  #f7f8fa;              /* bg.tips / inset panels             */
  --card-inner-2:#E9ECF0;              /* bg.paper3                          */
  --chip-bg:     #F4F6F8;              /* bg.chip                            */
  --legend-bg:   #F7F8FA;              /* bg.paper0 — donut legend panel     */
  --dialog-panel:#F6F8FA;              /* bg.paper2 — MORE dialog panel      */
  --legend-name: rgba(0, 0, 0, .5);    /* text.auxiliary — legend labels     */
  --circle-bg:   #E6E8EC;              /* bg.circle                          */
  --row:         #ffffff;
  --row-hover:   #F9FAFB;
  --border:      #E3E8EF;              /* divider                            */
  --border-soft: #F3F4F5;              /* table cell border                  */
  --table-head-bg:  #F4F6F8;
  --table-head-ink: rgba(0, 0, 0, .70);
  --overlay:     rgba(0, 0, 0, .40);
  --code-bg:     #F4F6F8;
  --code-ink:    rgba(0, 0, 0, .78);

  /* Ink ------------------------------------------------------------------ */
  --ink:          rgba(0, 0, 0, .70);   /* text.secondary — body             */
  --ink-strong:   #000000;              /* text.primary                      */
  --ink-2:        rgba(0, 0, 0, .70);
  --muted:        rgba(0, 0, 0, .50);   /* text.auxiliary                    */
  --faint:        rgba(0, 0, 0, .30);   /* text.unimportance                 */
  --ink-disabled: rgba(0, 0, 0, .15);

  /* Elevation ------------------------------------------------------------ */
  --shadow:        rgba(145, 158, 171, .20) 0 4px 10px;
  --shadow-md:     rgba(145, 158, 171, .28) 0 8px 22px;
  --shadow-lg:     rgba(145, 158, 171, .34) 0 18px 44px;
  --shadow-rail:   none;
  --shadow-topbar: 0 1px 0 var(--border), 0 4px 16px rgba(0, 4, 10, .025);
  --shadow-accent: 0 4px 12px var(--accent-shadow);

  /* Chrome --------------------------------------------------------------- */
  --scroll-thumb:       #d4d9e0;
  --scroll-thumb-hover: #bcc3cd;
  --topbar-bg:          rgba(255, 255, 255, .96);
  --login-bg:           rgb(247, 248, 250);
  --on-solid:           #ffffff;   /* text on a saturated semantic fill */
  --select-chevron:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238c929b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------------------
   2. TOKENS — DARK OVERRIDES
   ------------------------------------------------------------------------ */
html[data-theme="dark"] {
  color-scheme: dark;

  --accent:          #EBBB97;
  --accent-grad:     linear-gradient(153deg, #F4D1B4 0%, #EBBB97 100%);
  --accent-hover:    #F4D1B4;
  --accent-ink:      #F1D6BF;
  --accent-soft:     rgba(241, 214, 191, .12);
  --accent-tint:     #37312E;
  --accent-border:   rgba(235, 187, 151, .35);
  --accent-shadow:   rgba(235, 187, 151, .20);
  --accent-hover-bg: rgba(241, 214, 191, .18);
  --on-accent:       rgba(0, 0, 0, .82);
  /* the sand accent is lighter than its teal counterpart, so the inner arc
     steps lighter rather than deeper to keep the same separation. */
  --brand-arc-1:     #EBBB97;
  --brand-arc-2:     #F4D1B4;
  --teal:            #EBBB97;
  --teal-hover:      #F4D1B4;
  --teal-dark:       #F1D6BF;
  --teal-soft:       rgba(241, 214, 191, .12);
  --teal-border:     rgba(235, 187, 151, .35);

  --blue:      #69a8ff;
  --orange:    #ff9a70;
  --red:       #ff7089;
  --amber:     #ffca2c;
  --green:     #34d399;
  --purple:    #b37feb;
  --magenta:   #ff85c0;
  --neutral:   rgba(255, 255, 255, .45);

  --blue-ink:    #8fc0ff;
  --orange-ink:  #ffb08c;
  --red-ink:     #ff8fa3;
  --amber-ink:   #ffd45c;
  --green-ink:   #5ce0b0;
  --purple-ink:  #c9a2f5;
  --magenta-ink: #ff9fcd;
  --neutral-ink: rgba(255, 255, 255, .65);

  --globe: #14524f;
  --globe-halo: url("vendor/globel_bg_dark.png");

  --bg:          #181818;
  --sidebar-bg:  rgb(24, 24, 24);      /* identical to the canvas             */
  --card:        #232323;
  --card-inner:  #333333;              /* bg.tips                            */
  --card-inner-2:#434343;              /* bg.paper3 / bg.info                */
  --chip-bg:     #333333;
  --legend-bg:   #333333;
  --dialog-panel:#2b2b2b;
  --legend-name: rgba(255, 255, 255, .5);
  --circle-bg:   #3a3a3a;
  --row:         #232323;
  --row-hover:   rgba(255, 255, 255, .05);
  --border:      #333333;
  --border-soft: rgba(57, 57, 57, 1);
  --table-head-bg:  #37312E;
  --table-head-ink: rgba(255, 255, 255, .70);
  --overlay:     rgba(0, 0, 0, .60);
  --code-bg:     #1b1b1b;
  --code-ink:    rgba(255, 255, 255, .78);

  --ink:          rgba(255, 255, 255, .70);
  --ink-strong:   #ffffff;
  --ink-2:        rgba(255, 255, 255, .70);
  --muted:        rgba(255, 255, 255, .50);
  --faint:        rgba(255, 255, 255, .30);
  --ink-disabled: rgba(255, 255, 255, .15);

  --shadow:        none;                             /* SafeLine: boxShadow none */
  --shadow-md:     0 8px 22px rgba(0, 0, 0, .45);
  --shadow-lg:     0 18px 44px rgba(0, 0, 0, .55);
  --shadow-rail:   none;
  --shadow-topbar: 0 1px 0 var(--border);
  --shadow-accent: 0 4px 12px var(--accent-shadow);

  --scroll-thumb:       #383838;
  --scroll-thumb-hover: #4a4a4a;
  --topbar-bg:          rgba(28, 28, 28, .96);
  --login-bg:           #181818;
  --on-solid:           #ffffff;
  --select-chevron:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239a9a9a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------------------
   3. BASE
   ------------------------------------------------------------------------ */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

html { font-size: 16px; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: Inter, -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

[hidden] { display: none !important; }

.mono {
  font-family: "JetBrains Mono", SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

.bold { font-weight: 600; }

/* Tabular numerals — every metric in the console lines up column-wise. */
.num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ---------------------------------------------------------------------------
   4. BUTTONS & FORM CONTROLS
   ------------------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 34px;
  padding: 0 16px;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background-color 160ms var(--ease), color 160ms var(--ease),
              border-color 160ms var(--ease), box-shadow 160ms var(--ease);
  font-family: inherit;
  white-space: nowrap;
}

.btn-teal {
  background: var(--accent-grad);
  color: var(--on-accent);
  box-shadow: none;
}

.btn-teal:hover { box-shadow: var(--shadow-accent); }

.btn-outline {
  background: var(--card);
  color: var(--ink);
  border-color: var(--border);
}

.btn-outline:hover {
  border-color: var(--accent);
  color: var(--accent-ink);
}

.btn-sm {
  height: 30px;
  padding: 0 10px;
  font-size: 12px;
}

.btn-block { width: 100%; height: 40px; }

.btn-link {
  background: transparent;
  border: 0;
  padding: 0;
  color: var(--accent-ink);
  font-weight: 500;
  font-size: 13px;
  cursor: pointer;
  font-family: inherit;
}

.btn-link:hover { text-decoration: underline; }

.icon-btn,
.icon-btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--r-sm);
  cursor: pointer;
  color: var(--muted);
  transition: color 160ms ease, background-color 160ms ease, border-color 160ms ease;
}

.icon-btn {
  border: 1px solid var(--border);
  background: var(--card);
}

.icon-btn-ghost {
  border: 0;
  background: transparent;
}

.icon-btn:hover,
.icon-btn-ghost:hover {
  color: var(--accent-ink);
  background: var(--chip-bg);
  border-color: var(--accent-border);
}

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

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.field label,
.sl-label {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
}

.field input,
.sl-input {
  height: 32px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--card);
  color: var(--ink);
  font-size: 13px;
  font-family: inherit;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

/* SafeLine's outlined MUI "small" select — used for the application picker
   and the time-range picker sitting beside it. */
.sl-select,
select,
input,
textarea {
  color: var(--ink);
  background-color: var(--card);
  border: 1px solid var(--border);
  font-family: inherit;
}

.sl-select {
  height: 32px;
  min-width: 140px;
  padding: 0 28px 0 10px;
  border-radius: var(--r-sm);
  background-image: var(--select-chevron);
  background-repeat: no-repeat;
  background-position: right 10px center;
  -webkit-appearance: none;
  appearance: none;
  font-size: 12px;
  padding-block: 2px;
  cursor: pointer;
  outline: none;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.sl-select:hover,
select:hover,
input:hover,
textarea:hover { border-color: var(--accent-border); }

.sl-select:focus,
select:focus,
input:focus,
textarea:focus,
.field input:focus,
.sl-input:focus {
  border-color: var(--accent);
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-soft);
}

.sl-select-site { min-width: 200px; }

/* Application picker wrapper in the dashboard sub-tab row. */
.sl-app-picker {
  display: flex;
  align-items: center;
  gap: 8px;
}

.dash-controls {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Segmented controls -------------------------------------------------------*/
.seg {
  display: inline-flex;
  align-items: center;
  height: 36px;
  background: var(--chip-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 5px;
  gap: 2px;
}

.seg button,
.seg .seg-btn {
  border: 0;
  background: transparent;
  color: var(--muted);
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 500;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background-color 140ms ease, color 140ms ease;
  font-family: inherit;
  white-space: nowrap;
}

.seg button:hover,
.seg .seg-btn:hover { color: var(--ink-strong); }

.seg button[aria-pressed="true"],
.seg .seg-btn.active {
  background: var(--accent-grad);
  color: var(--on-accent);
  box-shadow: none;
}

/* Variants: same control, tuned to where it sits. */
.seg-globe .seg-btn { min-width: 34px; letter-spacing: .2px; }
.seg-mode  .seg-btn { letter-spacing: .4px; text-transform: uppercase; }
.seg-sites .seg-btn { padding: 4px 14px; }

/* A metric this window cannot supply is disabled, not shown-but-empty: a globe
   coloured by an unknown metric is indistinguishable from a quiet hour, and
   "nothing happened" is the one reading an operator must not reach by
   accident. The title carries the reason. */
.seg .seg-btn:disabled {
  opacity: .4;
  cursor: not-allowed;
}
.seg .seg-btn:disabled:hover { background: transparent; }

/* ---------------------------------------------------------------------------
   5. LOGIN
   ------------------------------------------------------------------------ */
#login-screen {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 24px;
  background:
    radial-gradient(circle at 18% 18%, var(--accent-soft), transparent 30%),
    radial-gradient(circle at 84% 82%, var(--accent-soft), transparent 30%),
    var(--login-bg);
}

.login-card {
  width: 100%;
  max-width: 400px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 36px 32px;
  box-shadow: var(--shadow-lg);
}

.login-brand {
  display: flex;
  align-items: center;
  /* The mark's painted ink runs close to its box edge, so the optical gap to
     the wordmark reads tighter than 12px. 20px is what actually looks even. */
  gap: 20px;
  margin-bottom: 8px;
}

.brand-logo-wrap {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
}

.brand-mark { width: 100%; height: 100%; display: block; }

/* Two concentric arcs. Both strokes are painted from tokens rather than
   literals, so the mark follows --accent into dark mode on its own — the same
   trick .qps-bars-icon uses. No JS is involved; data-theme does the work.
   Deliberately unscoped: the tab icon and the coming-soon mark reuse these
   classes so every instance of the brand reads from the same two tokens. */
.mk-arc {
  fill: none;
  stroke-width: 4.4;
  stroke-linecap: round;
}
.mk-arc-outer { stroke: var(--brand-arc-1); }
.mk-arc-inner { stroke: var(--brand-arc-2); }

/* The arcs run edge to edge of their box, so they read optically larger than
   the old inset shield did and need slightly less room than it was given. */
.sidebar .brand-logo-wrap { width: 34px; height: 34px; }
.login-brand .brand-logo-wrap { width: 46px; height: 46px; }
.login-brand .brand-name { font-size: 24px; }

.brand-name {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -.3px;
  color: var(--ink-strong);
  margin: 0;
}

.brand-sub {
  font-size: 11px;
  color: var(--muted);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .5px;
}

.login-sub,
.field label {
  color: var(--muted);
  font-size: 13px;
}

.login-sub { margin: 0 0 24px; }

.form-error {
  margin: 0 0 16px;
  padding: 8px 12px;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--red) 12%, transparent);
  color: var(--red-ink);
  font-size: 13px;
}

/* ---------------------------------------------------------------------------
   6. SHELL — SIDEBAR
   ------------------------------------------------------------------------ */
#app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
  background: var(--bg);
}

/* No divider, no shadow, no white: the rail is the same canvas as the page.
   Separation comes from the white cards sitting on the grey field. */
.sidebar {
  background: var(--sidebar-bg);
  border-right: 0;
  box-shadow: none;
  padding: 16px 12px 16px 16px;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  z-index: 100;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 48px;
  padding: 0 8px;
  margin-bottom: 8px;
  flex: 0 0 auto;
}

.brand-text-block {
  display: flex;
  align-items: center;
  gap: 8px;
}

.brand-badge {
  font-size: 10px;
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent-soft);
  border: 1px solid var(--accent-border);
  padding: 1px 5px;
  border-radius: 4px;
}

.nav {
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1 1 auto;
  overflow-y: auto;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 50px;
  padding: 8px 16px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  text-align: left;
  width: 100%;
  font-family: inherit;
  transition: background-color 160ms var(--ease), color 160ms var(--ease);
}

.nav-icon { flex: 0 0 auto; opacity: .85; }

/* Hover does NOT fill — label and icon simply take the brand colour. */
.nav-item:hover {
  background: transparent;
  color: var(--accent-ink);
}

.nav-item:hover .nav-icon { opacity: 1; }

/* A nav row that owns a sub-group below it. */
.nav-parent { position: relative; }

/* SafeLine's signature filled active pill. */
.nav-item[aria-current="page"] {
  background: var(--accent-grad);
  color: var(--on-accent);
  font-weight: 600;
  box-shadow: 0 10px 25px var(--accent-shadow);
}

.nav-item[aria-current="page"] .nav-icon {
  opacity: 1;
  stroke: currentColor;
}

.nav-badge {
  margin-left: auto;
  font-size: 11px;
  font-weight: 700;
  background: var(--red);
  color: var(--on-solid);
  width: 8px;
  height: 8px;
  padding: 0;
  overflow: hidden;
  font-size: 0;
  border-radius: 50%;
}

.nav-subgroup {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 2px 0 6px;
}

.nav-sub-label {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 32px;
  padding-left: 44px;
  font-size: 13px;
  color: var(--faint);
  cursor: default;
}

.nav-sub-label::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--circle-bg);
  flex: 0 0 auto;
}

.nav-spacer { flex: 1 1 auto; }

.nav-foot {
  padding: 12px 8px 0;
  border-top: 1px solid var(--border);
  background: var(--sidebar-bg);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.user-profile {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.user-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  border: 1px solid var(--accent-border);
  flex: 0 0 auto;
}

.user-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.user-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.user-role {
  font-size: 11px;
  color: var(--muted);
}

/* ---------------------------------------------------------------------------
   7. SHELL — TOPBAR
   ------------------------------------------------------------------------ */
.main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* The reference's header is not a plate: it is 66px of the page canvas itself
   (rgb(247,248,250), no border, no shadow), with the breadcrumb sitting on the
   same 24px gutter as the content below it. Ours used to be a white bar with a
   rule under it and an 18px title, which stuck out of the layout at the left
   edge. It stays sticky and keeps a faint blur so scrolled content does not
   show through the controls. */
.topbar {
  height: var(--topbar-h);
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  border-bottom: 0;
  box-shadow: none;
  padding: 0 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: sticky;
  top: 0;
  z-index: 90;
  backdrop-filter: blur(10px);
}

.topbar-left {
  display: flex;
  align-items: center;
}

/* "Console > Dashboard": 14px throughout — a bold root, a quiet chevron, the
   current page in regular black — instead of a 13px grey root and 18px title. */
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 14px;
  line-height: 21px;
}

.breadcrumb-root {
  font-size: 14px;
  font-weight: 700;
  color: color-mix(in srgb, var(--ink-strong) 70%, transparent);
}

/* The "/" glyph in the markup is replaced by a drawn chevron. */
.breadcrumb-sep {
  position: relative;
  width: 6px;
  height: 10px;
  font-size: 0;
  flex: 0 0 auto;
}
.breadcrumb-sep::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 6px;
  height: 6px;
  border-top: 1.5px solid var(--legend-name);
  border-right: 1.5px solid var(--legend-name);
  transform: translateY(-50%) rotate(45deg) scale(.85);
  transform-origin: 70% 30%;
}

.page-title {
  font-size: 14px;
  font-weight: 400;
  line-height: 21px;
  color: var(--ink-strong);
  margin: 0;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Status plate + round icon buttons, after the reference's top-right cluster:
   a white 4px-radius plate (33px tall) and 30-32px round white buttons in
   rgba(0,0,0,.7). app.js swaps the class set on #live-dot (is-stale /
   is-down), so the plate keeps its shape and only the dot and the lead word
   change colour — no green wash, no emoji. */
.live-status-pill,
.live-dot {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 33px;
  padding: 0 14px;
  border-radius: 4px;
  background: var(--card);
  box-shadow: var(--shadow);
  font-size: 12px;
  font-weight: 400;
  color: var(--legend-name);
  white-space: nowrap;
}
.live-status-pill .live-label b {
  margin-right: 6px;
  color: var(--ink-strong);
  font-size: 14px;
  font-weight: 700;
}
.live-status-pill.is-stale .pulse-dot { background: var(--amber); animation: none; }
.live-status-pill.is-down  .pulse-dot { background: var(--red);   animation: none; }
.live-status-pill.is-stale .live-label b { color: var(--amber-ink); }
.live-status-pill.is-down  .live-label b { color: var(--red-ink); }

.pulse-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  animation: pulse 1.8s infinite;
}

@keyframes pulse {
  0%   { transform: scale(.9);  opacity: .8; }
  50%  { transform: scale(1.3); opacity: 1; }
  100% { transform: scale(.9);  opacity: .8; }
}

.theme-toggle-btn,
.topbar-right .icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--card);
  box-shadow: var(--shadow);
  color: var(--legend-name);
  cursor: pointer;
  transition: color 160ms ease, background-color 160ms ease, transform 300ms var(--ease);
}
.theme-toggle-btn:hover,
.topbar-right .icon-btn:hover { color: var(--accent); }
.theme-toggle-btn:active { transform: rotate(-20deg); }
.topbar-right .icon-btn[aria-busy="true"] svg { animation: spinOnce 900ms linear infinite; }
@keyframes spinOnce { to { transform: rotate(360deg); } }

.theme-toggle-btn svg { display: block; }
html[data-theme="dark"] .theme-icon-light { display: inline-flex; }
html[data-theme="dark"] .theme-icon-dark  { display: none; }
html:not([data-theme="dark"]) .theme-icon-light { display: none; }
html:not([data-theme="dark"]) .theme-icon-dark  { display: inline-flex; }

/* ---------------------------------------------------------------------------
   8. CONTENT SHELL — CARDS & GRIDS
   ------------------------------------------------------------------------ */
.content {
  padding: 0 24px 44px;   /* same 24px gutter as the topbar above it */
  flex: 1 1 auto;
}

/* Route roots. Each template mounts under one of these; they own the vertical
   rhythm between the stacked regions of a page. */
.dash-root,
.events-root,
.sites-root {
  display: block;
  min-width: 0;
}

.events-root,
.sites-root { padding-top: 2px; }

.card {
  background: var(--card);
  border: 0;
  border-radius: var(--r);
  box-shadow: var(--shadow);
  position: relative;
}

/* SafeLine cards carry no head divider: 24px gutters, a 36px title row and
   20px of air beneath it. Total head block = var(--card-head-h). */
.card-head {
  padding: 20px 24px 0;
  margin-bottom: 16px;
  min-height: 36px;
  border-bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.card-head-title-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.card-head-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.card-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-strong);
  margin: 0;
  white-space: nowrap;
}

.card-link {
  font-size: 12px;
  font-weight: 600;
  color: var(--accent-ink);
  cursor: pointer;
  white-space: nowrap;
}

.card-link:hover { text-decoration: underline; }

.card-sub {
  font-size: 12px;
  color: var(--faint);
}

/* Confirmed live on the reference: "Max" and its value are the same size
   and weight as the card title (14px/500) — only colour tells them apart,
   not a smaller label. Splitting this off from .card-sub, which really is
   meant to be small secondary text elsewhere (row counts, staleness). */
.max-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--muted);
}

.max-label b {
  color: var(--ink-strong);
  font-weight: 500;
}

.grid { display: grid; gap: var(--gap); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* API-error / notice strips injected into #banners by app.js. */
.banner {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  padding: 9px 14px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--card-inner);
  color: var(--ink);
  font-size: 13px;
}

.banner.err {
  border-color: color-mix(in srgb, var(--red) 32%, transparent);
  background: color-mix(in srgb, var(--red) 10%, transparent);
  color: var(--red-ink);
}

/* Quiet "no data" state. Never dressed up to look like a real zero. */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 18px 12px;
  text-align: center;
  color: var(--faint);
}

.empty .dash {
  font-size: 20px;
  font-weight: 500;
  line-height: 1;
  color: var(--ink-disabled);
}

.empty .lbl {
  font-size: 12px;
  color: var(--faint);
}

/* The BASIC / ADVANCED switch above the dashboard. */
.viewbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

.viewbar-hint {
  font-size: 12px;
  color: var(--faint);
}

/* ---------------------------------------------------------------------------
   9. DASHBOARD — SUB-TABS
   ------------------------------------------------------------------------ */
/* The bar is one white plate (measured on the reference: 42px tall, 4px
   radius, soft shadow). Inside it, the tab group is a 36px strip whose active
   pill is a SEPARATE element that slides — moving left/width — rather than
   a background on the tab itself, so switching tabs animates instead of
   snapping. */
.dash-subtabs-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin: 2px 0 var(--gap);
  padding: 3px 16px 3px 0;
  border-radius: 4px;
  background: var(--card);
  box-shadow: var(--shadow);
}

.dash-tabs-group {
  display: flex;
  align-items: center;
  min-width: 0;
}

.dash-subtabs {
  position: relative;
  display: flex;
  padding: 5px;
  border: 1px solid transparent;   /* 24 + 2x5 + 2x1 = the reference's 36px strip */
  border-radius: 4px;
}

.dash-tab-indicator {
  position: absolute;
  top: 5px;
  left: 0;
  width: 0;
  height: 24px;
  border-radius: 4px;
  background: var(--accent);
  opacity: 0;
  transition: left 260ms var(--ease), width 260ms var(--ease), opacity 120ms ease;
  pointer-events: none;
}
.dash-subtabs.has-indicator .dash-tab-indicator { opacity: 1; }

.dash-tab {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 4px 12px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: color 200ms ease;
}
.dash-tab { color: color-mix(in srgb, var(--ink-strong) 70%, transparent); }
.dash-tab:hover { color: var(--ink-strong); }
.dash-tab.active,
.dash-tab.active:hover { color: var(--on-accent); }

/* Before the indicator has been measured (first paint, or no JS), the active
   tab paints its own pill so it never flashes unstyled. */
.dash-subtabs:not(.has-indicator) .dash-tab.active { background: var(--accent); }

/* A tab that carries a small mark after its label (Data Dashboard). */
.dash-tab svg { margin-left: 6px; flex: 0 0 auto; }

/* Security Posture / Data Dashboard have no page yet. Everything below the tab
   bar that belongs to Traffic Analysis is hidden, the placeholder takes its
   place, and the site/range pickers — which only steer Traffic Analysis —
   step out of the way. Driven by one attribute on the dashboard root so the
   render code never has to know which blocks to hide. */
.dash-root[data-tab="security"] .dash-main-grid,
.dash-root[data-tab="security"] .pro-grid,
.dash-root[data-tab="security"] .dash-controls,
.dash-root[data-tab="security"] .scope-note,
.dash-root[data-tab="data"] .dash-main-grid,
.dash-root[data-tab="data"] .pro-grid,
.dash-root[data-tab="data"] .dash-controls,
.dash-root[data-tab="data"] .scope-note { display: none; }

.coming-soon {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 460px;
  padding: 48px 24px;
  text-align: center;
}
.coming-soon[hidden] { display: none; }
.coming-soon-mark { margin-bottom: 4px; animation: soonFloat 4s ease-in-out infinite; }
@keyframes soonFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
.coming-soon-pill {
  padding: 3px 12px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.coming-soon-title { margin: 0; color: var(--ink-strong); font-size: 20px; font-weight: 700; }
.coming-soon-text  { margin: 0; max-width: 440px; color: var(--legend-name); font-size: 14px; line-height: 1.6; }

/* Which parts of the page the site picker actually narrows. */
.scope-note {
  margin: 0 0 var(--gap);
  padding: 8px 16px;
  border-radius: 4px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-size: 13px;
  line-height: 1.5;
}
.scope-note[hidden] { display: none; }
.scope-note b { font-weight: 700; }

/* After the user changes the range or the site, the slow blocks (map, clients,
   pages) are stale until their data lands; dim them so the old figures do not
   pass for the new selection. Background polls never set this. */
#page.is-scope-loading .geo-card,
#page.is-scope-loading .pro-grid,
#page.is-scope-loading .kpi-row-1,
#page.is-scope-loading .kpi-row-2 { opacity: .55; transition: opacity 160ms ease; pointer-events: none; }
#page .geo-card, #page .pro-grid, #page .kpi-row-1, #page .kpi-row-2 { transition: opacity 160ms ease; }

/* ---------------------------------------------------------------------------
   10. DASHBOARD — LAYOUT GRID
   ------------------------------------------------------------------------ */
.dash-main-grid {
  display: grid;
  /* 869 : 279 — the reference's left column and its QPS/status column. */
  grid-template-columns: minmax(0, 869fr) minmax(248px, 279fr);
  gap: var(--gap);
  align-items: stretch;
}

.dash-col-left,
.dash-col-right { min-width: 0; }

.dash-col-left {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* The three cards here carry their own fixed heights (24 + 21 + 20 + chart
   + bottom gutter), exactly as the reference does, so the column is sized by
   its content rather than by stretchy 1fr rows — the old minmax(...,1fr)
   rows had nothing definite to resolve against and were part of the runaway
   height this column used to hit. align-content:start keeps any spare height
   at the bottom instead of inflating the gaps. */
.dash-col-right {
  display: grid;
  grid-template-rows: auto auto auto;
  align-content: start;
  gap: var(--gap);
}

.sl-hero {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(300px, 1.4fr);
  gap: var(--gap);
  margin-bottom: var(--gap);
}

/* ---------------------------------------------------------------------------
   11. DASHBOARD — KPI ROWS
   ------------------------------------------------------------------------ */
.kpi-row-sl {
  display: grid;
  grid-template-columns: 2.2fr 1.4fr 1.2fr;
  gap: var(--gap);
  margin-bottom: var(--gap);
}

/* 574 : 279 with a 16px gutter = the reference's 869px left column split
   (traffic card, attack card). Expressed as fr ratios so it holds at any
   width, and minmax(0, ...) so a long value can never push a track wider. */
.kpi-row-1 {
  display: grid;
  grid-template-columns: minmax(0, 574fr) minmax(0, 279fr);
  gap: var(--gap);
  margin-bottom: var(--gap);
}

.kpi-row-2 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
  margin-bottom: var(--gap);
}

/* KPI card, measured on the reference: 89px tall = 16 + 57 + 16. Tiles are
   equal columns, each inset 16px on the left and split by a 1px rule; inside a
   tile a 21px label row, 6px, then a 30px value row (57px). */
.kpi {
  display: flex;
  align-items: stretch;
  padding: 16px 0;
}

.kpi-stat {
  flex: 1 1 0;
  min-width: 0;
  padding: 0 0 0 16px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  position: relative;
}

.kpi-stat:not(:first-child) {
  border-left: 1px solid var(--border);
}

.kpi-label {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 21px;
  font-size: 14px;
  line-height: 21px;
  color: var(--legend-name);    /* text.auxiliary, rgba(0,0,0,.5) */
  font-weight: 400;
  margin-bottom: 6px;
  white-space: nowrap;
}

.kpi-value {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 30px;
  font-size: 20px;
  font-weight: 700;
  color: var(--ink-strong);     /* text.primary */
  line-height: 30px;
}

.kpi-val-text {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* SafeLine's 16x16 tinted icon bubble beside a KPI label. The glyph inside is
   a 6px disc in the semantic colour; the bubble is that colour at 10%. */
.sl-kpi-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
}

.sl-kpi-dot::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.dot-teal  { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent); }
.dot-amber { background: color-mix(in srgb, var(--amber) 10%, transparent);  color: var(--amber); }
.dot-red   { background: color-mix(in srgb, var(--red) 10%, transparent);    color: var(--red); }

/* Accessible KPI help: keyboard focus and hover reveal the same styled hint. */
.metric-tip {
  position: relative;
  cursor: help;
  outline: none;
}

.metric-tip::after {
  content: attr(data-tooltip);
  position: absolute;
  z-index: 40;
  left: 50%;
  bottom: calc(100% + 8px);
  width: max-content;
  max-width: min(240px, 70vw);
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  box-shadow: var(--shadow-md);
  color: var(--ink-strong);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.35;
  white-space: normal;
  transform: translate(-50%, 3px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 120ms ease, transform 120ms ease, visibility 120ms ease;
}

.metric-tip:hover::after,
.metric-tip:focus-visible::after {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

.metric-tip:focus-visible { box-shadow: 0 0 0 2px var(--accent-border); }

/* SafeLine's small circled "i" beside a KPI label; native title tooltip. */
.kpi-info-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  font-size: 16px;
  line-height: 1;
  color: var(--accent);
  cursor: help;
  transition: opacity 140ms ease;
}

.kpi-info-circle:hover { opacity: .75; }

.kpi-info-tip {
  font-size: 11px;
  color: var(--muted);
  cursor: help;
}

/* Footnote app.js inserts after a KPI grid. */
.kpi-note {
  margin: 8px 0 0;
  font-size: 12px;
  color: var(--faint);
}

.kpi-ip-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--amber) 16%, transparent);
  color: var(--amber-ink);
  border: 1px solid color-mix(in srgb, var(--amber) 35%, transparent);
  letter-spacing: .5px;
}

.kpi-badge {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  background: var(--accent-soft);
  color: var(--accent-ink);
  border: 1px solid var(--accent-border);
}

.badge-blue   { background: color-mix(in srgb, var(--blue) 12%, transparent);    color: var(--blue-ink);    border: 1px solid color-mix(in srgb, var(--blue) 25%, transparent); }
.badge-teal   { background: var(--accent-soft);                                  color: var(--accent-ink);  border: 1px solid var(--accent-border); }
.badge-purple { background: color-mix(in srgb, var(--purple) 12%, transparent);  color: var(--purple-ink);  border: 1px solid color-mix(in srgb, var(--purple) 25%, transparent); }
.badge-green  { background: color-mix(in srgb, var(--green) 12%, transparent);   color: var(--green-ink);   border: 1px solid color-mix(in srgb, var(--green) 25%, transparent); }
.badge-red    { background: color-mix(in srgb, var(--red) 12%, transparent);     color: var(--red-ink);     border: 1px solid color-mix(in srgb, var(--red) 25%, transparent); }
.badge-orange { background: color-mix(in srgb, var(--orange) 12%, transparent);  color: var(--orange-ink);  border: 1px solid color-mix(in srgb, var(--orange) 25%, transparent); }
.badge-amber  { background: color-mix(in srgb, var(--amber) 14%, transparent);   color: var(--amber-ink);   border: 1px solid color-mix(in srgb, var(--amber) 28%, transparent); }
.badge-muted  { background: var(--chip-bg);                                      color: var(--muted);       border: 1px solid var(--border); }

.badge, .chip, .status-chip { box-shadow: none; }

/* ---------------------------------------------------------------------------
   12. DASHBOARD — GEO LOCATION CARD
   ------------------------------------------------------------------------ */
/* flex-grow here used to feed a circular height dependency: .geo-card grows
   to fill .dash-col-left's "available space", but that space is itself
   only as tall as the row .dash-col-left sits in (a CSS Grid row whose own
   height is intrinsic/auto, i.e. undefined until its items are measured).
   Blink resolved that indeterminate loop to its internal max layout size
   (2^24 px = 16777216), which the 3D globe then read via
   getBoundingClientRect() and baked into its canvas/scene inline styles —
   the whole page's scrollHeight became 16777216 as a result. Content-sized
   (flex: 0 0 auto) breaks the loop: every descendant's height:100% now
   resolves against this card's own definite height instead. */
.geo-card {
  flex: 0 0 auto;
  min-height: 468px;
}


.geo-body {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 1fr);   /* 75% / 25%        */
  align-items: center;
  gap: 20px;
  padding: 0 8px 0 0;                                     /* their pr: 1      */
  min-height: 474px;
}

.geo-stage {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 440px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}

/* 3D globe host. The halo ring around the globe IS this background image —
   it is not a CSS effect. Light and dark ship different plates. */
.geo-globe-wrap,
.geo-canvas-3d-wrap {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-bottom: 24px;
  background-image: var(--globe-halo);
  background-size: 438px;
  background-repeat: no-repeat;
  background-position: 50% 50%;
}

.geo-globe-mount {
  position: relative;
  top: 12px;
}

.geo-3d-canvas {
  width: 100%;
  height: 100%;
  cursor: grab;
}

.geo-3d-canvas:active { cursor: grabbing; }

/* 2D fallback: a full-width FLAT map. No circular clip, no sphere gradient. */
.geo-map-wrap,
.geo-canvas-2d-wrap {
  width: 100%;
  height: 100%;
  display: block;
}

.geo-map,
.geo-canvas {
  width: 100%;
  height: 474px;
  max-height: 100%;
  padding-right: 50px;
  padding-bottom: 20px;
  background: transparent;
  border-radius: 0;
  overflow: visible;
}

.geo-summary {
  display: block;
  margin-top: 4px;
}

/* SafeLine country ranking — label, value, hairline bar. No rank badges. */
.sl-rank,
.rank-list {
  width: 100%;
  align-self: center;
  border-radius: 4px;
  background: var(--card-inner);
  padding: 0 24px 16px;
}

.sl-rank-row {
  padding-top: 16px;
}

.sl-rank-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.sl-rank-label {
  font-size: 14px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sl-rank-val {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

.sl-rank-track {
  margin-top: 8px;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
  overflow: hidden;
}

.sl-rank-fill {
  display: block;
  height: 4px;
  border-radius: 2px;
  background: var(--accent-grad);
  transition: width 400ms var(--ease);
}

/* ---------------------------------------------------------------------------
   13. DASHBOARD — QPS CARD
   ------------------------------------------------------------------------ */
.qps-card,
.status-card {
  min-width: 0;
  overflow: hidden;
}

/* Geometry lifted from the reference card, measured live: 24px gutters all
   round, a 21px title row, 20px of air under it, then the chart. The chart
   sits inside the same 24px gutters as the title, so the line starts under
   the "R" of "Requests Status" rather than at the card's bare edge. */
.status-card .card-head {
  padding: 24px 24px 0;
  margin-bottom: 20px;
  min-height: 0;
}

/* Same 24px gutters / 20px title gap as the status cards below it, so all
   three cards in this column share one left edge. */
.qps-card .card-head {
  height: auto;
  min-height: 0;
  margin-bottom: 20px;
  padding: 24px 24px 0;
}

.qps-card .card-title {
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
}

.qps-head-left {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* On a narrow right column the title + badge + live dot no longer all fit; the
   title gives way (ellipsis) so the dot can never land on top of the badge. */
.qps-head-left .card-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qps-card .card-head-right { flex: 0 0 auto; }
/* 231px of content: 120 title + 12 + up to ~77 badge + 4 + 18 dot. Tighter gaps
   than the generic card-head keep the title whole for a 5-character value. */
.qps-card .card-head { gap: 4px; }

/* Reference chip, pixel-matched to the real card (rgba(0,0,0,.05), 21px
   tall, 14px activity glyph, 8px spacing, then the live value — number is
   a monospace weight 500, not the body sans, confirmed via computed style
   on the live demo card). */
.qps-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 21px;
  padding: 0 8px;
  border-radius: 4px;
  background: var(--chip-bg);
  flex: 0 0 auto;
  font-weight: 500;
}

.qps-bars-icon {
  display: block;
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  color: var(--accent);
}

.qps-bars-icon rect { fill: currentColor; }

.qps-badge-num {
  font-family: "JetBrains Mono", SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  font-weight: 500;
  line-height: 20px;
  color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
}

/* SafeLine's "this is live" tell: an 18px box holding a small red dot that
   pulses on a slow loop — not a control, just a status indicator. Their real
   card draws it as a Lottie animation; a CSS pulse reads the same at this
   size without pulling in an animation library for one dot. */
.qps-live-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
}

.qps-live-dot-core {
  position: relative;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--red, #ff5576);
}

.qps-live-dot-core::before {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  background: var(--red, #ff5576);
  opacity: .35;
  animation: qpsLivePulse 1.8s ease-out infinite;
}

@keyframes qpsLivePulse {
  0%   { transform: scale(.3); opacity: .45; }
  70%  { transform: scale(1);  opacity: 0; }
  100% { transform: scale(1);  opacity: 0; }
}
/* prefers-reduced-motion is handled globally (section 21) — the universal
   `*` rule there already flattens this animation to a single static frame. */

/* The reference chart occupies the full card width at exactly 130px high. */
.qps-body {
  height: var(--dash-right-qps-h);
  min-height: var(--dash-right-qps-h);
  padding: 0;
  margin: 0 24px;
}

.qps-sparkline-wrap {
  width: 100%;
  height: 130px;
  min-height: 130px;
}

/* The gutter lives on a wrapper, never on the chart element itself: ECharts
   pins an inline width/height onto its host after measuring it, so a margin
   there survives as an overflow (host width + gutters > card width) instead
   of insetting the chart. The wrapper carries the 24px gutters and the 7px
   bottom gap; the host just fills it. 187px is the reference chart height. */
.status-card .status-body {
  height: var(--dash-right-status-h);
  min-height: var(--dash-right-status-h);
  margin: 0 24px 7px;
}

.status-card .chart-status {
  width: 100%;
  height: 100%;
}

/* ---------------------------------------------------------------------------
   14. CHARTS, DONUTS & LEGENDS
   ------------------------------------------------------------------------ */
.chart,
.geo-map,
.geo-visual { background-color: transparent; }

.chart-lg {
  height: 165px;
  width: 100%;
  padding: 0 10px;
}

.chart-md { height: 160px; width: 140px; }
.chart-bars { height: 160px; width: 100%; padding: 0 10px; }

.donut-wrap {
  display: grid;
  grid-template-columns: minmax(112px, 140px) minmax(0, 1fr);
  padding: 8px 16px 12px;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.donut-wrap.is-empty { grid-template-columns: 1fr; }
.donut-wrap.is-empty .chart { display: none; }

.legend {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  max-height: 148px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  padding-right: 3px;
  font-size: 12px;
}

.legend.is-empty {
  max-height: none;
  overflow: hidden;
  padding-right: 0;
}

.legend.is-empty .empty { min-height: 140px; }

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

/* ---------------------------------------------------------------------------
   15. DASHBOARD — BOTTOM ROW (4 PRO CARDS)
   ------------------------------------------------------------------------ */
/* Two per row, as the reference lays them out (each card ~574px of a 1164px
   content width, 16px gutter) — not four across. */
.pro-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
  margin-top: var(--gap);
}

.pro-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.pro-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 16px 18px 0;
  min-width: 0;
}

.pro-card-icon {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  color: var(--accent);
  stroke: currentColor;
  fill: none;
}

.pro-card-head .card-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pro-card-head .card-link {
  margin-left: auto;
  flex: 0 0 auto;
  white-space: nowrap;
}

.pro-card[data-region="client"] .pro-card-desc,
.pro-card[data-region="response-status"] .pro-card-desc {
  min-height: 38px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.pro-card[data-region="popular-site"] .pro-card-body,
.pro-card[data-region="client"] .pro-card-body,
.pro-card[data-region="response-status"] .pro-card-body {
  justify-content: flex-start;
}

.pro-card-desc {
  margin: 6px 0 0;
  padding: 0 18px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--faint);
}

.pro-card-body {
  flex: 1 1 auto;
  min-height: 184px;
  padding: 8px 0 12px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}

.pro-card-body .donut-wrap { padding: 0 16px; }

/* Client / Response Status cards, cut to the reference's measured geometry
   (demo.waf.chaitin.com, computed styles, 574x262 card):
     24px card padding
     25px header row -> 8px gap
     181px body row: [donut] [grey legend panel]
   24 + 25 + 8 + 181 + 24 = 262, which is exactly the reference card height.
   The legend panel is 5 rows x (21px row + 2 x 6px margin) + 2 x 8px padding
   = 181px, so the body row is defined by the legend, not by the donut. */
.breakdown-card {
  display: block;
  padding: 24px;
  overflow: hidden;
}

.breakdown-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 25px;
  margin: 0 0 8px;
}

.breakdown-heading { min-width: 0; }
.breakdown-heading .card-title {
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* SafeLine's "MORE": a borderless text button, 14px/500 in the brand teal,
   uppercased by CSS (the source text is "More"), with a -8px right margin so
   its padding, not its glyphs, sits on the card's content edge. */
.breakdown-action {
  flex: 0 0 auto;
  height: 25px;
  margin: 0 -8px 0 0;
  padding: 0 8px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  text-transform: uppercase;
  cursor: pointer;
}
.breakdown-action:hover { background: var(--accent-soft); }

.breakdown-body {
  position: relative;
  height: 181px;
  padding: 0;
}

.breakdown-layout {
  display: flex;
  align-items: center;
  height: 100%;
  min-width: 0;
}

/* Client donut: a 140px box pulled 16px into the card padding. Response
   status: a 130px box whose CHART is nudged 10px left inside it — the two
   rings are drawn about differently-sized centres in the reference. Both hosts
   are sized explicitly rather than 100%: ECharts pins an inline width/height
   onto its host, so a percentage plus a negative margin would overflow. */
.donut-stage {
  position: relative;
  flex: 0 0 auto;
  width: 140px;
  height: 140px;
  margin-left: -16px;
}
.breakdown-chart { width: 100%; height: 100%; }

/* The reference legend panel: paper0 (#f7f8fa), 4px radius, 8/16 padding,
   growing to fill the row but never crowding the donut. */
.legend-panel {
  flex: 1 1 auto;
  min-width: 0;
  max-width: calc(100% - 123px);
  height: 181px;
  padding: 8px 16px;
  border-radius: 4px;
  background: var(--legend-bg);
  overflow: hidden;
}

/* Two independent columns (inner ring | outer ring), the second one pushed
   16px in. Each is a padded-to-five list, never one list wrapped by a grid. */
.breakdown-legend {
  display: flex;
  height: 100%;
}
.bd-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
}
.bd-col + .bd-col { padding-left: 16px; }

.breakdown-legend-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 21px;
  /* 6px above and below, nothing at the sides: measured on the reference the
     first dot sits exactly on the panel's 16px padding edge, i.e. the "8px"
     side margin in its stylesheet never insets the row. */
  margin: 6px 0;
}
.breakdown-legend-item .legend-dot {
  flex: 0 0 6px;
  width: 6px;
  height: 6px;
}
.breakdown-name {
  flex: 1 1 auto;
  min-width: 0;
  margin-left: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--legend-name);
  font-size: 14px;
  font-weight: 400;
  line-height: 21px;
}
.breakdown-count {
  flex: 0 0 auto;
  margin-left: 8px;
  color: var(--ink-strong);
  font-size: 14px;
  font-weight: 700;
  line-height: 21px;
  font-variant-numeric: tabular-nums;
}

/* Narrow cards: the row cannot hold donut + two legend columns, so the
   donut sits above the panel and the panel drops to a single column. This
   is keyed to the viewport only because the grid above it is: pro-grid is
   two-up until 1180px, then one card per row. */
@media (max-width: 1180px) {
  .breakdown-body { height: auto; }
  .breakdown-layout { flex-direction: column; align-items: stretch; height: auto; gap: 12px; }
  .donut-stage,
  .breakdown-card[data-region="response-status"] .donut-stage { margin: 0 auto; }
  .legend-panel { max-width: none; height: auto; }
}
@media (max-width: 760px) {
  .breakdown-legend { flex-direction: column; }
  .bd-col + .bd-col { padding-left: 0; }
}

/* MORE on the pro-card heads (Referring / Popular) is the same borderless text
   button as on the breakdown cards; in a head that also holds an icon and a
   title it just needs pushing to the right edge. */
.pro-card-head .breakdown-action { margin-left: auto; }

/* ---------------------------------------------------------------------------
   "MORE" dialog — measured on the reference's MUI dialogs:
     paper 800 / 600 / 1000 wide, 12px radius, 32px viewport margin
     title row: 32/24/16 padding, 16px/600, 36px round close button
     content: 0/32/20 padding, scrolls; grey (#f6f8fa) 4px-radius panel, 16px pad
     actions: a single 80x37 teal OK
   ------------------------------------------------------------------------ */
.info-dialog-root {
  position: fixed;
  inset: 0;
  z-index: 1300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
}
.info-dialog-root[hidden] { display: none; }

.info-dialog-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .8);
  animation: infoFade 180ms ease-out;
}

.info-dialog {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 800px;
  max-height: 100%;
  background: var(--card);
  border-radius: 12px;
  box-shadow: 0 11px 15px -7px rgba(0,0,0,.2), 0 24px 38px 3px rgba(0,0,0,.12), 0 9px 46px 8px rgba(0,0,0,.12);
  outline: none;
  animation: infoRise 200ms var(--ease);
}
.info-dialog.is-w600  { max-width: 600px; }
.info-dialog.is-w1000 { max-width: 1000px; }

@keyframes infoFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes infoRise { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: scale(1); } }

.info-dialog-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 0 0 auto;
  margin: 0;
  padding: 32px 24px 16px;
  color: var(--ink-strong);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.5;
}

.info-dialog-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 8px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--legend-name);
  cursor: pointer;
  transition: background-color 150ms ease;
}
.info-dialog-close:hover { background: var(--chip-bg); }

.info-dialog-content {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0 32px 20px;
  overflow-y: auto;
}

.info-dialog-panel {
  padding: 16px;
  border-radius: 4px;
  background: var(--dialog-panel);
}

.info-dialog-actions {
  display: flex;
  justify-content: flex-end;
  flex: 0 0 auto;
  padding: 0 32px 32px;
}

.info-dialog-ok {
  min-width: 80px;
  height: 37px;
  padding: 0 16px;
  border: 0;
  border-radius: 4px;
  background: var(--accent);
  color: #fff;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  text-transform: uppercase;
  cursor: pointer;
}
.info-dialog-ok:hover { filter: brightness(.94); }

/* Two independent lists (inner ring | outer ring); the right one is pushed
   in 24px, so both outer boxes stay equal. Rows are 24px on a 32px pitch. */
.info-cols { display: grid; grid-template-columns: 1fr 1fr; align-items: start; }
.info-cols > * + * { padding-left: 24px; }
.info-cols > * { min-width: 0; }

.info-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 24px;
  margin-bottom: 8px;
}
.info-row .legend-dot { flex: 0 0 6px; width: 6px; height: 6px; }
.info-row-name {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0 16px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--legend-name);
  font-size: 14px;
}
.info-row-count {
  flex: 0 0 auto;
  color: var(--ink-strong);
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Column-title strip used by the Response Status dialog. */
.info-strip {
  display: flex;
  margin: 0 0 8px;
  padding: 8px 16px 8px 0;
  border-radius: 4px;
  background: var(--chip-bg);
  color: var(--legend-name);
  font-size: 12px;
}
.info-strip > * { flex: 1 1 0; min-width: 0; padding-left: 8px; }
.info-strip > * + * { padding-left: 16px; }

/* Bar-list rows (Referring / Popular): label above a 6px track. */
.info-bar { margin-bottom: 4px; }
.info-bar-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 8px 0 4px;
  min-height: 24px;
}
.info-bar-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--legend-name);
  font-size: 12px;
}
.info-bar-track { height: 6px; border-radius: 4px; background: var(--border); overflow: hidden; }
.info-bar-fill  { height: 6px; border-radius: 4px; background: #65B8FF; }

.info-section-title {
  margin: 4px 0 0;
  color: var(--ink-strong);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.info-section-title + .info-bar { margin-top: 0; }
.info-section-gap { height: 12px; }

.info-empty {
  padding: 48px 0;
  text-align: center;
  color: var(--legend-name);
  font-size: 14px;
}

@media (max-width: 640px) {
  .info-dialog-root { padding: 12px; }
  .info-cols { grid-template-columns: 1fr; }
  .info-cols > * + * { padding-left: 0; }
  .info-dialog-title { padding: 24px 16px 12px; }
  .info-dialog-content { padding: 0 16px 16px; }
  .info-dialog-actions { padding: 0 16px 20px; }
}

/* KPI hover breakdown (the ⓘ next to Blocked and 5xx Errors). Same dark,
   slightly see-through plate the reference uses for its tooltips
   (rgba(97,97,97,.92), white 11px/500, 4px radius), sized up to hold a table. */
.kpi-info-circle { cursor: help; outline: none; }
.kpi-info-circle:focus-visible { box-shadow: 0 0 0 2px var(--accent-soft); border-radius: 50%; }

.kpi-tip {
  position: fixed;
  z-index: 1400;
  min-width: 260px;
  max-width: 320px;
  padding: 12px 16px 10px;
  border-radius: 4px;
  background: rgba(97, 97, 97, .92);
  color: #fff;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.4;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .25);
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 120ms ease, transform 120ms ease;
  pointer-events: none;
}
.kpi-tip.is-open { opacity: 1; transform: none; pointer-events: auto; }
.kpi-tip[hidden] { display: none; }

.kpi-tip-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 52px;
  align-items: center;
  column-gap: 16px;
  min-height: 28px;
}
.kpi-tip-row.is-head { font-weight: 700; min-height: 24px; }
.kpi-tip-row > :nth-child(2) { text-align: right; font-variant-numeric: tabular-nums; }
.kpi-tip-row > :nth-child(3) { text-align: right; }
.kpi-tip-row:not(.is-head) > :first-child { cursor: default; }
.kpi-tip-row:not(.is-head):hover > :first-child { text-decoration: underline dotted; text-underline-offset: 3px; }

.kpi-tip-view {
  padding: 0;
  border: 0;
  background: none;
  color: #fff;
  font: inherit;
  font-weight: 500;
  letter-spacing: .03em;
  cursor: pointer;
}
.kpi-tip-view:hover { text-decoration: underline; }

.kpi-tip-foot {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, .18);
  color: rgba(255, 255, 255, .75);
  min-height: 1.4em;
  transition: color 150ms ease;
}
.kpi-tip-foot.is-egg { color: #8ff0ec; }

/* Referring Application/Page — SafeLine's AQ bar-list component (spec B):
   40px rows, label at 0.8 scale above a 6px rounded bar on a light track,
   bold count right-aligned. Padded to a fixed row count (BAR_LIST_ROWS in
   app.js) with "-"/0 filler; today every row is filler, since request_logs
   carries no Referer column, but the markup and styling are the real thing,
   not a placeholder sentence. Written generically off [data-bars] so a
   Domain/Page split later is just another mount through the same CSS. */
.bar-list {
  display: flex;
  flex-direction: column;
  padding: 4px 18px 8px;
}

.bar-list-row {
  height: 40px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
}

.bar-list-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
}

.bar-list-label {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  color: var(--muted);
  font-size: 14px;
  transform: scale(.8);
  transform-origin: left center;
}

.bar-list-count {
  flex: 0 0 auto;
  color: var(--ink-strong);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.bar-list-track {
  height: 6px;
  border-radius: 3px;
  background: var(--card-inner);
  overflow: hidden;
}

/* SafeLine's fixed bar colour, both themes — not a themed accent. */
.bar-list-fill {
  display: block;
  height: 6px;
  border-radius: 3px;
  background: #65B8FF;
}

/* ---------------------------------------------------------------------------
   16. EVENTS (LOGS) PAGE
   ------------------------------------------------------------------------ */
.sl-filter-card {
  padding: 14px 20px;
  margin-bottom: var(--gap);
}

.sl-filter-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

/* The reference's filter controls are 37px tall at 14px — noticeably roomier
   than the 32px/12-13px compact pickers the dashboard header uses, so the
   size is scoped to this row rather than pushed onto .sl-input/.sl-select
   globally (those stay compact where the reference keeps them compact). */
.sl-filter-row .sl-input,
.sl-filter-row .sl-select {
  height: 37px;
  font-size: 14px;
}

.sl-filter-row .sl-select { padding-left: 14px; }

/* Cells whose content is a single fixed-width token must never wrap: with the
   reference's 16px column padding the Action pill and the "Detail >" link
   both broke onto two lines and stretched every row. */
td .badge-action-blocked,
td .btn-link,
td .sl-tag { white-space: nowrap; }

.sl-search-box {
  display: inline-flex;
  align-items: center;
  position: relative;
  min-width: 240px;
  flex: 1 1 240px;
}

.sl-search-icon {
  position: absolute;
  left: 10px;
  color: var(--faint);
  pointer-events: none;
}

.sl-search-box .sl-input {
  width: 100%;
  padding-left: 32px;
}

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

.sl-filter-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Table card: the table is flush to the card edge, so the corners clip. */
.table-card {
  overflow: hidden;
  padding: 0;
}

.table-head-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 0;
}

.table-head-bar:empty { display: none; }

.table-scroll {
  overflow-x: auto;
  border-radius: 0 0 var(--r) var(--r);
}

table,
.data-table { background: transparent; }

.sl-table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}

/* Table chrome measured off the reference's own table: a 24px head strip at
   12px/500 on #f4f6f8, then 14px rows at full-strength ink with 8px of
   vertical padding (ours were 16px, which stretched every row by ~9px) and
   16px between columns. */
.sl-table th,
th {
  height: 24px;
  padding: 0 16px;
  font-size: 12px;
  font-weight: 500;
  color: var(--table-head-ink);
  background: var(--table-head-bg);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.sl-table td,
td {
  padding: 8px 16px;
  font-size: 14px;
  line-height: 1.5;
  border-bottom: 1px solid var(--border-soft);
  color: var(--ink-strong);
  vertical-align: middle;
}

.sl-table tr:hover td,
tbody tr:hover td { background: var(--row-hover); }

.sl-table th:first-child,
.sl-table td:first-child { padding-left: 24px; }

.sl-table th:last-child,
.sl-table td:last-child { padding-right: 24px; }

.empty-state {
  color: var(--faint);
  text-align: center;
  padding: 28px 12px;
  font-size: 13px;
}

/* Attack-type tags -------------------------------------------------------- */
.sl-tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.tag-sqli      { background: color-mix(in srgb, var(--orange) 16%, transparent);  color: var(--orange-ink);  border: 1px solid color-mix(in srgb, var(--orange) 32%, transparent); }
.tag-xss       { background: color-mix(in srgb, var(--purple) 16%, transparent);  color: var(--purple-ink);  border: 1px solid color-mix(in srgb, var(--purple) 32%, transparent); }
.tag-traversal { background: color-mix(in srgb, var(--red) 16%, transparent);     color: var(--red-ink);     border: 1px solid color-mix(in srgb, var(--red) 32%, transparent); }
.tag-rce       { background: color-mix(in srgb, var(--magenta) 16%, transparent); color: var(--magenta-ink); border: 1px solid color-mix(in srgb, var(--magenta) 32%, transparent); }
.tag-bot       { background: color-mix(in srgb, var(--blue) 16%, transparent);    color: var(--blue-ink);    border: 1px solid color-mix(in srgb, var(--blue) 32%, transparent); }
.tag-ratelimit { background: var(--accent-soft);                                  color: var(--accent-ink);  border: 1px solid var(--accent-border); }
.tag-probe     { background: var(--chip-bg);                                      color: var(--neutral-ink); border: 1px solid var(--border); }

.badge-action-blocked,
.badge-action-passed {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.badge-action-blocked {
  background: color-mix(in srgb, var(--red) 14%, transparent);
  color: var(--red-ink);
  border: 1px solid color-mix(in srgb, var(--red) 32%, transparent);
}

.badge-action-passed {
  background: color-mix(in srgb, var(--green) 14%, transparent);
  color: var(--green-ink);
  border: 1px solid color-mix(in srgb, var(--green) 32%, transparent);
}

/* ---- events pagination and copy ----
   The pager sits under the table rather than in the header because the header
   is already carrying the filter row, and because "which slice of the rows am
   I looking at" belongs next to the rows. */
.sl-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 16px;
  border-top: 1px solid var(--border-soft);
}
.sl-pager-left,
.sl-pager-right {
  display: flex;
  align-items: center;
  gap: 8px;
}
.sl-pager-pos {
  font-size: 12px;
  color: var(--muted);
}

/* Modelled on .kpi-tip, the only element in this codebase that fades in AND
   out. A toast that only appears snaps into place, which reads as a glitch. */
.sl-toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  z-index: 1500;
  transform: translate(-50%, 6px);
  opacity: 0;
  transition: opacity 120ms ease, transform 120ms ease;
  pointer-events: none;
  max-width: min(560px, calc(100vw - 32px));
  padding: 9px 14px;
  border-radius: 8px;
  font-size: 13px;
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--border);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
}
.sl-toast.is-open { opacity: 1; transform: translate(-50%, 0); }
.sl-toast[hidden] { display: none; }
.sl-toast.is-error {
  border-color: var(--accent-border);
  color: var(--red-ink);
}

.country-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--chip-bg);
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
  margin-left: 6px;
  border: 1px solid var(--border);
}

.path-code {
  display: inline-block;
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 2px 6px;
  background: var(--chip-bg);
  border: 1px solid var(--border-soft);
  color: var(--accent-ink);
  border-radius: 4px;
  font-size: 12px;
}

/* ---------------------------------------------------------------------------
   17. EVENT DETAIL DRAWER
   ------------------------------------------------------------------------ */
.sl-drawer {
  position: fixed;
  inset: 0;
  z-index: 1000;
}

.sl-drawer-backdrop {
  position: absolute;
  inset: 0;
  background: var(--overlay);
}

.sl-drawer-panel {
  position: absolute;
  top: 0;
  right: 0;
  width: 580px;
  max-width: 90vw;
  height: 100%;
  background: var(--card);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  animation: slideIn 220ms var(--ease);
}

@keyframes slideIn {
  from { transform: translate3d(100%, 0, 0); }
  to   { transform: translate3d(0, 0, 0); }
}

.sl-drawer-header {
  padding: 18px 24px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.sl-drawer-header-left {
  display: flex;
  align-items: center;
  gap: 10px;
}

.sl-drawer-title {
  font-size: 16px;
  font-weight: 600;
  margin: 0;
  color: var(--ink-strong);
}

.sl-drawer-close {
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--muted);
  padding: 4px;
  border-radius: 4px;
}

.sl-drawer-close:hover {
  background: var(--chip-bg);
  color: var(--ink-strong);
}

.sl-drawer-body {
  padding: 24px;
  position: relative;
}

/* SafeLine's rotated BLOCKED stamp. */
.sl-stamp {
  position: absolute;
  top: 24px;
  right: 32px;
  border: 3px double var(--red);
  color: var(--red);
  font-size: 20px;
  font-weight: 800;
  letter-spacing: 2px;
  padding: 3px 12px;
  border-radius: 4px;
  transform: rotate(-8deg);
  opacity: .9;
  pointer-events: none;
}

.stamp-blocked {
  border-color: var(--red);
  color: var(--red-ink);
}

.stamp-passed {
  border-color: var(--green);
  color: var(--green-ink);
}

.stamp-unknown { border-color: var(--border); color: var(--faint); }

.sl-prop-group {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-bottom: 24px;
}

.sl-prop-row {
  display: flex;
  align-items: flex-start;
  font-size: 13px;
}

.sl-prop-row.full-width {
  flex-direction: column;
  gap: 6px;
}

.sl-prop-label {
  width: 120px;
  color: var(--muted);
  flex: 0 0 120px;
}

.sl-prop-val {
  color: var(--ink-strong);
  flex: 1 1 auto;
  word-break: break-all;
}

.module-pill {
  display: inline-block;
  background: color-mix(in srgb, var(--green) 16%, transparent);
  color: var(--green-ink);
  border: 1px solid color-mix(in srgb, var(--green) 32%, transparent);
  padding: 1px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 600;
}

.loc-badge {
  background: color-mix(in srgb, var(--red) 16%, transparent);
  color: var(--red-ink);
  border: 1px solid color-mix(in srgb, var(--red) 32%, transparent);
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
}

.payload-box {
  background: color-mix(in srgb, var(--red) 10%, transparent);
  color: var(--red-ink);
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  border-radius: var(--r-sm);
  padding: 10px 14px;
  font-size: 13px;
  white-space: pre-wrap;
  word-break: break-all;
}

.sl-package-section {
  border-top: 1px solid var(--border);
  padding-top: 18px;
}

.sl-package-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.sl-seg-small button {
  background: var(--accent-grad);
  color: var(--on-accent);
  border: 0;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 4px;
  font-family: inherit;
  cursor: pointer;
}

.sl-code-view {
  background: var(--code-bg);
  border: 1px solid var(--border);
  color: var(--code-ink);
  border-radius: var(--r-sm);
  padding: 14px;
  font-size: 12px;
  font-family: "JetBrains Mono", SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  overflow-x: auto;
  max-height: 280px;
  margin: 0;
  white-space: pre-wrap;
  word-break: break-all;
}

/* ---------------------------------------------------------------------------
   18. SITES (WEBSITE MANAGEMENT)
   ------------------------------------------------------------------------ */
.sl-sites-header {
  padding: 14px 20px;
  margin-bottom: var(--gap);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.sl-sites-header-left {
  display: flex;
  align-items: center;
  gap: 18px;
}

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

/* Site cards — the reference's Applications layout: two per row, each split
   into a narrow left rail (icon, mode, the two headline counters) and a body
   carrying the domain, its endpoints and the row of actions. */
.sites-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
}

.site-card {
  display: flex;
  align-items: stretch;
  padding: 0;
  overflow: hidden;
}

.site-card-rail {
  flex: 0 0 152px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 20px 16px;
  border-right: 1px solid var(--border-soft);
  min-width: 0;
}

.site-rail-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
  margin-top: 2px;
  text-align: center;
}

.site-rail-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.site-rail-label {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .04em;
  color: var(--faint);
}

.site-rail-stat b {
  font-size: 18px;
  font-weight: 600;
  color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
}

.site-rail-stat b.is-blocked { color: var(--red, #ff5576); }

.site-card-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 20px 20px 18px;
}

.site-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
}

.site-card-detail {
  flex: 0 0 auto;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .03em;
  color: var(--accent-ink);
  cursor: pointer;
}

.site-card-detail:hover { text-decoration: underline; }

.site-meta-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 13px;
  color: var(--ink);
}

.site-meta-key {
  flex: 0 0 auto;
  color: var(--faint);
  font-size: 12px;
  min-width: 62px;
}

.site-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: auto;
  padding-top: 4px;
}

/* Chip row, matching the reference's four small outlined action chips. */
.site-chip {
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--card);
  color: var(--muted);
  padding: 5px 10px;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .03em;
  cursor: pointer;
  white-space: nowrap;
}

.site-chip:hover { color: var(--accent-ink); border-color: var(--accent-border); }

.site-chip.is-static {
  cursor: default;
  color: var(--accent-ink);
  border-color: var(--accent-border);
  background: var(--accent-soft);
}

.site-card-empty { padding: 16px; grid-column: 1 / -1; }

.site-icon {
  width: 32px;
  height: 32px;
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  color: var(--accent-ink);
  border: 1px solid var(--accent-border);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

.site-domain-name {
  font-weight: 600;
  color: var(--ink-strong);
  font-size: 15px;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.site-port-pill {
  display: inline-block;
  font-size: 11px;
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--chip-bg);
  color: var(--muted);
  border: 1px solid var(--border-soft);
  font-weight: 500;
  white-space: nowrap;
}

.mode-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
}

.mode-protected {
  background: var(--accent-soft);
  color: var(--accent-ink);
  border: 1px solid var(--accent-border);
}

.mode-monitoring {
  background: color-mix(in srgb, var(--amber) 15%, transparent);
  color: var(--amber-ink);
  border: 1px solid color-mix(in srgb, var(--amber) 32%, transparent);
}

.mode-offline {
  background: var(--chip-bg);
  color: var(--muted);
  border: 1px solid var(--border);
}

.healthy-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  display: inline-block;
}

.cert-valid {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--green-ink);
  font-size: 12px;
  font-weight: 500;
}

.ops-cell {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

/* ---------------------------------------------------------------------------
   19. MODALS
   ------------------------------------------------------------------------ */
.sl-modal {
  position: fixed;
  inset: 0;
  z-index: 1050;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.sl-modal-backdrop {
  position: absolute;
  inset: 0;
  background: var(--overlay);
}

.sl-modal-card {
  position: relative;
  width: 100%;
  max-width: 520px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  animation: modalScale 200ms var(--ease);
}

@keyframes modalScale {
  from { transform: scale(.96); opacity: 0; }
  to   { transform: scale(1);   opacity: 1; }
}

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

.sl-modal-title {
  font-size: 16px;
  font-weight: 600;
  margin: 0;
  color: var(--ink-strong);
}

.sl-modal-body { padding: 22px; }

.sl-modal-footer {
  padding: 14px 22px;
  background: var(--card-inner);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

/* ---------------------------------------------------------------------------
   20. SCROLLBARS
   ------------------------------------------------------------------------ */
::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

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

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

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

* { scrollbar-color: var(--scroll-thumb) transparent; scrollbar-width: thin; }

/* ---------------------------------------------------------------------------
   21. RESPONSIVE
   ------------------------------------------------------------------------ */

/* .kpi-row-1's "2fr 1fr" track pair has no minmax(0, ...) floor, so below
   ~1300px the "attack" card (Blocked/IP Addr) stops shrinking at its
   min-content width and the grid overflows its own column — at narrower
   widths that overflow lands underneath the Query Per Second card in
   dash-col-right, hiding the KPI values entirely rather than merely
   clipping them. kpi-row-2's three 2-tile cards hit a milder version of the
   same problem (tracks are correctly minmax(0, 1fr) already, but each card
   still gets too little width to hold a value + icon/percent without
   clipping). Stack kpi-row-1 to one column and kpi-row-2 to two below the
   threshold where either starts happening; the existing 960px rule already
   takes both the rest of the way to one column once dash-main-grid itself
   stacks. The tiles themselves no longer change size at these widths: they
   are already the reference's compact 89px cards, so shrinking them further
   only made the numbers unreadable. */
@media (max-width: 1300px) {
  .kpi-row-1 { grid-template-columns: 1fr; }
  .kpi-row-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 1200px) {
  .sl-hero { grid-template-columns: 1fr; }
}

@media (max-width: 1180px) {
  .dash-main-grid { grid-template-columns: minmax(0, 2.25fr) minmax(238px, 1fr); }
  /* pro-grid is already two across at full width; one card per row is the
     next step down, not a repeat of the base rule. */
  .pro-grid { grid-template-columns: 1fr; }
  .sites-grid { grid-template-columns: 1fr; }
}

@media (max-width: 1100px) {
  .kpi-row-sl { grid-template-columns: 1fr; }
}

@media (max-width: 1080px) {
  .grid-3 { grid-template-columns: 1fr; }
  .grid-2 { grid-template-columns: 1fr; }
}

@media (max-width: 960px) {
  .kpi-row-1,
  .kpi-row-2 { grid-template-columns: 1fr; }

  .dash-main-grid { grid-template-columns: 1fr; }

  /* Three across once the column unstacks from the main grid. The cards own
     their heights now, so the row track must be auto — pinning it to the
     chart height alone would clip the head off each card. */
  .dash-col-right {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: auto;
    margin-top: var(--gap);
  }
}

@media (max-width: 800px) {
  .geo-body {
    grid-template-columns: 1fr;
    padding: 0 8px 12px;
  }
  .geo-stage { min-height: 320px; }
  .sl-rank,
  .rank-list { margin: 0 8px; }
}

@media (max-width: 760px) {
  html, body { min-width: 0; }

  #app-shell { display: block; }

  .sidebar {
    position: fixed;
    inset: auto 0 0 0;
    width: 100%;
    height: 64px;
    padding: 0;
    background: var(--sidebar-bg);
    border: 0;
    border-top: 1px solid var(--border);
    box-shadow: var(--shadow-md);
    z-index: 200;
  }

  .brand, .nav-subgroup, .nav-spacer, .nav-foot { display: none; }

  .nav {
    flex-direction: row;
    padding: 7px 10px;
    overflow: visible;
    gap: 6px;
  }

  .nav-item {
    height: 50px;
    padding: 5px 8px;
    flex: 1;
    justify-content: center;
    flex-direction: column;
    gap: 2px;
    font-size: 10px;
  }

  .nav-icon { width: 19px; height: 19px; }

  .main { min-height: 100vh; padding-bottom: 64px; }

  .topbar { height: 58px; padding: 0 16px; }

  .breadcrumb-root, .breadcrumb-sep, .live-status-pill, .live-dot, .seg-range { display: none; }

  .page-title { font-size: 17px; }

  .content { padding: 16px 14px 28px; }

  .dash-subtabs-row { align-items: flex-start; }
  .dash-subtabs { width: 100%; overflow-x: auto; gap: 8px; }
  .dash-tab { flex: 0 0 auto; }

  .dash-controls { width: 100%; display: grid; grid-template-columns: 1fr; }
  .sl-select-site { width: 100%; }

  .kpi-row-1, .kpi-row-2, .grid-2, .grid-3 { grid-template-columns: 1fr; }
  .kpi { min-width: 0; }

  .card { border-radius: var(--r); }
  .card-head { padding: 14px 16px; }

  .dash-main-grid { display: block; }

  .dash-col-right {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    margin-top: var(--gap);
  }

  .pro-grid { grid-template-columns: 1fr; }

  .geo-card { min-height: 420px; }
  .geo-body { min-height: 0; gap: 12px; }
  .geo-stage { min-height: 300px; }
  .geo-globe-wrap,
  .geo-canvas-3d-wrap { background-size: 300px; }
  .geo-map, .geo-canvas { height: 300px; padding-right: 12px; }

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

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}
.faint { color: var(--faint); }

/* An allowed request the origin failed (502 from a dead backend and the like):
   still "allowed" by the WAF, but not something to read as healthy. */
.badge-action-passed.is-5xx {
  background: color-mix(in srgb, var(--orange, #f59e0b) 14%, transparent);
  color: var(--orange-ink, #b45309);
  border-color: color-mix(in srgb, var(--orange, #f59e0b) 36%, transparent);
}

/* ---- protection level (top bar) ---- */
.prot-wrap { position: relative; }
.prot-pill {
  display: inline-flex; align-items: center; gap: 8px;
  height: 33px; padding: 0 12px;
  border: 0; border-radius: 4px;
  background: var(--card); box-shadow: var(--shadow);
  font: inherit; font-size: 12px; color: var(--ink-strong);
  cursor: pointer; white-space: nowrap;
}
.prot-pill .prot-ico { color: var(--accent); }
.prot-pill .prot-caret { color: var(--faint); }
.prot-pill.is-high .prot-ico { color: var(--orange); }
.prot-pill.is-attack .prot-ico { color: var(--red); }
.prot-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.prot-panel {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 60;
  width: 380px; max-width: calc(100vw - 32px);
  padding: 16px; border-radius: 8px;
  background: var(--card); box-shadow: 0 10px 32px rgba(0,0,0,.18);
  border: 1px solid var(--border);
}
.prot-panel[hidden] { display: none; }
.prot-panel-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 12px; font-size: 14px; color: var(--ink-strong); }
.prot-panel-sub { font-size: 12px; color: var(--faint); }
.prot-list { display: flex; flex-direction: column; gap: 14px; max-height: 50vh; overflow: auto; }
.prot-row { display: flex; flex-direction: column; gap: 6px; }
.prot-row-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.prot-domain { font-size: 13px; font-weight: 600; color: var(--ink-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prot-attack { font-size: 11px; font-weight: 600; color: var(--red-ink); background: color-mix(in srgb, var(--red) 14%, transparent); padding: 1px 8px; border-radius: 10px; }
.prot-seg { display: grid; grid-template-columns: 1.4fr repeat(4, 1fr); gap: 4px; }
.prot-opt {
  height: 30px; border-radius: 4px; border: 1px solid var(--border);
  background: transparent; color: var(--ink); font: inherit; font-size: 12px; font-weight: 600;
  cursor: pointer;
}
.prot-opt:hover { border-color: var(--accent); }
.prot-opt.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.prot-opt.is-lock.active { background: var(--red); border-color: var(--red); }
.prot-opt:disabled { opacity: .5; cursor: progress; }
.prot-status { font-size: 12px; color: var(--faint); }
.prot-status.is-pending { color: var(--amber-ink, #b45309); }
.prot-legend { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); font-size: 12px; line-height: 1.6; color: var(--faint); }
.prot-legend b { color: var(--ink); }
.prot-empty { font-size: 13px; color: var(--faint); margin: 0; }
@media (max-width: 640px) { .prot-label { display: none; } }

/* ---- site status (top bar) ---- */
.site-pill .site-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--faint);
}
.site-pill.is-up .site-dot { background: var(--green); box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 22%, transparent); }
.site-pill.is-degraded .site-dot { background: var(--amber); }
.site-pill.is-down .site-dot { background: var(--red); animation: site-blink 1s steps(2, start) infinite; }
.site-pill.is-down { color: var(--red-ink); }
@keyframes site-blink { to { visibility: hidden; } }
@media (prefers-reduced-motion: reduce) { .site-pill.is-down .site-dot { animation: none; } }

.site-panel { width: 420px; }
.site-state { font-size: 11px; font-weight: 700; padding: 1px 8px; border-radius: 10px; text-transform: uppercase; letter-spacing: .3px; }
.site-state.is-up { color: var(--green-ink); background: color-mix(in srgb, var(--green) 14%, transparent); }
.site-state.is-degraded { color: var(--amber-ink); background: color-mix(in srgb, var(--amber) 16%, transparent); }
.site-state.is-down { color: var(--red-ink); background: color-mix(in srgb, var(--red) 14%, transparent); }
.site-state.is-unknown { color: var(--faint); background: var(--chip-bg); }
.site-reason { font-size: 12px; color: var(--red-ink); }
.site-checks { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; border-radius: 6px; background: var(--legend-bg); }
.site-check { display: grid; grid-template-columns: 10px 64px 1fr; align-items: center; gap: 8px; font-size: 12px; }
.site-check-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--faint); }
.site-check-dot.is-up { background: var(--green); }
.site-check-dot.is-down { background: var(--red); }
.site-check-name { color: var(--ink-strong); font-weight: 600; }
.site-check-val { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uptime-head { display: flex; justify-content: space-between; font-size: 11px; color: var(--faint); margin-top: 2px; }
.uptime-pct { color: var(--ink); }
.uptime-strip { display: grid; grid-template-columns: repeat(48, 1fr); gap: 2px; height: 22px; }
.uptime-bar { border-radius: 2px; background: var(--chip-bg); }
.uptime-bar.is-up { background: var(--green); }
.uptime-bar.is-degraded { background: var(--amber); }
.uptime-bar.is-down { background: var(--red); }
.site-down-banner { margin-bottom: 8px; }
#site-banner[hidden] { display: none; }

.prot-tags { display: inline-flex; gap: 6px; align-items: center; }
.site-auto { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; padding: 1px 6px; border-radius: 8px; color: var(--accent-ink); background: var(--accent-soft); }
.prot-edges { font-size: 11px; color: var(--faint); }

/* ---- Attacks (Events page), measured from SafeLine 9.4.2 —
   docs/SAFELINE_REFERENCE.md §4 ---- */
.att-root { display: flex; flex-direction: column; gap: var(--gap); }
.att-root.is-loading .att-table-card { opacity: .6; transition: opacity 120ms ease; }
.att-filter-card { padding: 16px 24px; display: flex; flex-direction: column; gap: 14px; }
.att-toolbar { display: flex; align-items: center; }
.att-mode .seg-btn { min-width: 72px; }
.att-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 16px; }
.att-field { display: flex; flex-direction: column; gap: 4px; min-width: 150px; }
.att-field > span { font-size: 12px; color: var(--legend-name); }
.att-field .sl-input, .att-field .sl-select { height: 36px; min-width: 150px; }
.att-field.att-time .sl-input { min-width: 200px; }
.att-root[data-mode="events"] .att-logs-only { display: none !important; }
.att-root:not(.att-more-open) [data-att-more] { display: none; }
.att-actions { display: flex; align-items: center; gap: 12px; margin-left: auto; }
.att-btn {
  height: 36px; padding: 0 14px; border-radius: 4px;
  border: 1px solid var(--accent); background: transparent; color: var(--accent);
  font: inherit; font-size: 14px; font-weight: 500; cursor: pointer; white-space: nowrap;
}
.att-btn:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.att-btn[aria-expanded="true"] { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.att-info { font-size: 12px; opacity: .8; }
.att-switch { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; color: var(--ink); cursor: pointer; user-select: none; }
.att-switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.att-switch-track { position: relative; width: 34px; height: 20px; border-radius: 10px; background: var(--border); transition: background 150ms ease; }
.att-switch-track::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25); transition: transform 150ms ease; }
.att-switch input:checked + .att-switch-track { background: var(--accent); }
.att-switch input:checked + .att-switch-track::after { transform: translateX(14px); }
.att-switch input:focus-visible + .att-switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }

.att-table td { vertical-align: middle; }
.att-row { cursor: pointer; }
.att-action { display: inline-flex; align-items: center; height: 24px; padding: 0 10px; border-radius: 4px; font-size: 12px; font-weight: 500; white-space: nowrap; }
.att-action.is-blocked { color: var(--red-ink); background: color-mix(in srgb, var(--red) 10%, transparent); border: 1px solid color-mix(in srgb, var(--red) 45%, transparent); }
.att-action.is-passed { color: var(--amber-ink); background: color-mix(in srgb, var(--amber) 14%, transparent); border: 1px solid color-mix(in srgb, var(--amber) 55%, transparent); }
.att-url { display: block; max-width: 520px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; color: var(--ink); }
.att-ip { font-size: 14px; color: var(--ink-strong); }
.att-country { font-size: 14px; font-weight: 400; color: var(--legend-name); }
.att-time { font-size: 14px; color: var(--ink); white-space: nowrap; }
.att-apps { display: flex; flex-wrap: wrap; gap: 4px; }
.att-app { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; padding: 0 6px; height: 22px; line-height: 22px; border-radius: 4px; background: var(--chip-bg); color: var(--ink); }
.att-app.is-more { color: var(--faint); }
.att-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; height: 24px; padding: 0 8px; border-radius: 4px;
  border: 1px solid var(--red); color: var(--red-ink); font-size: 14px; font-weight: 500;
}

.att-pager { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; padding: 16px 24px; }
.att-pager-left, .att-pager-right { display: flex; align-items: center; gap: 12px; }
.att-size { width: 63px; height: 37px; }
.att-total { font-size: 14px; color: var(--legend-name); }
.att-pages { display: flex; align-items: center; gap: 4px; }
.att-page {
  min-width: 32px; height: 32px; padding: 0 6px; border-radius: 4px; border: 0;
  background: transparent; color: var(--ink); font: inherit; font-size: 14px; cursor: pointer;
}
.att-page:hover:not(:disabled):not(.active) { background: var(--chip-bg); }
.att-page.active { background: var(--accent); color: #fff; }
.att-page:disabled { opacity: .35; cursor: default; }
.att-arrow { font-size: 18px; line-height: 1; }
.att-ellipsis { min-width: 24px; text-align: center; color: var(--faint); }
.att-goto { font-size: 14px; color: var(--legend-name); white-space: nowrap; }
.att-jump { width: 63px; height: 28px; padding: 0 6px; text-align: center; }

.att-confirm-backdrop { position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,.5); display: flex; align-items: center; justify-content: center; padding: 16px; }
.att-confirm {
  width: 401px; max-width: 100%; padding: 24px; border-radius: 12px; background: var(--card);
  box-shadow: rgba(0,0,0,.2) 0 11px 15px -7px, rgba(0,0,0,.12) 0 24px 38px 3px;
}
.att-confirm-title { font-size: 16px; font-weight: 600; color: var(--ink-strong); margin-bottom: 12px; }
.att-confirm p { margin: 0 0 24px; font-size: 14px; color: var(--ink); }
.att-confirm-foot { display: flex; justify-content: flex-end; gap: 8px; }
.att-dlg-cancel, .att-dlg-ok { height: 37px; padding: 0 18px; border-radius: 4px; font: inherit; font-size: 14px; font-weight: 500; cursor: pointer; }
.att-dlg-cancel { border: 0; background: transparent; color: var(--accent); }
.att-dlg-ok { border: 0; background: var(--accent); color: #fff; min-width: 80px; }

@media (max-width: 760px) {
  .att-filter-card { padding: 14px 16px; }
  .att-field, .att-field .sl-input, .att-field .sl-select { min-width: 0; width: 100%; }
  .att-actions { margin-left: 0; flex-wrap: wrap; }
  .att-pager { padding: 12px 16px; }
}

/* ---- ADD APPLICATION: dashboard-managed domains ---- */
.mg-card { padding: 16px 24px; margin-bottom: var(--gap); }
.mg-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 8px; }
.mg-list { display: flex; flex-direction: column; }
.mg-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr) auto; align-items: center; gap: 16px; padding: 10px 0; border-top: 1px solid var(--border-soft, var(--border)); }
.mg-row:first-child { border-top: 0; }
.mg-main { display: flex; flex-direction: column; min-width: 0; }
.mg-domain { font-size: 14px; font-weight: 600; color: var(--ink-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mg-meta { font-size: 12px; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mg-state { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink); min-width: 0; }
.mg-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--faint); }
.mg-state.is-ok .mg-dot { background: var(--green); }
.mg-state.is-warn .mg-dot { background: var(--amber); }
.mg-state.is-wait .mg-dot { background: var(--amber); animation: site-blink 1.4s steps(2, start) infinite; }
.mg-remove { color: var(--red-ink); }
.mg-domains { min-height: 72px; resize: vertical; font-family: var(--mono, ui-monospace, monospace); padding: 8px 10px; }
.mg-hint { font-size: 12px; color: var(--faint); }
.mg-radio { display: flex; flex-direction: column; gap: 6px; font-size: 14px; }
.mg-radio label { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.mg-note { font-size: 12px; color: var(--muted); margin: 4px 0 0; }
.mg-error { font-size: 13px; color: var(--red-ink); margin: 6px 0 0; }
@media (max-width: 760px) { .mg-row { grid-template-columns: 1fr; gap: 6px; } }

/* ---- Servers (ADD SERVER) ---- */
.servers-root { display: flex; flex-direction: column; gap: var(--gap); }
.servers-root .sl-sites-header-left { flex-direction: column; align-items: flex-start; gap: 4px; }
.srv-new { padding: 16px 24px; border: 1px solid var(--accent); }
.srv-help { font-size: 13px; color: var(--muted); margin: 0 0 10px; }
.srv-cmd { display: flex; gap: 10px; align-items: stretch; }
.srv-cmd code { flex: 1 1 auto; min-width: 0; padding: 10px 12px; border-radius: 4px; background: var(--legend-bg); font-size: 12.5px; line-height: 1.5; word-break: break-all; color: var(--ink-strong); user-select: all; }
.srv-wait { display: flex; align-items: center; gap: 8px; margin: 12px 0 0; font-size: 13px; color: var(--amber-ink); }
.srv-wait .mg-dot { background: var(--amber); animation: site-blink 1.4s steps(2, start) infinite; }
.srv-wait.is-done { color: var(--green-ink); }
.srv-wait.is-done .mg-dot, .mg-dot.is-ok-dot { background: var(--green); animation: none; }
.srv-list-card { padding: 8px 24px; }
.srv-list { display: flex; flex-direction: column; }
.srv-row { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) 110px auto; gap: 16px; align-items: center; padding: 12px 0; border-top: 1px solid var(--border-soft, var(--border)); }
.srv-row:first-child { border-top: 0; }
.srv-main { display: flex; flex-direction: column; min-width: 0; }
.srv-name { font-size: 14px; font-weight: 600; color: var(--ink-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srv-domains { font-size: 13px; color: var(--muted); }
.mg-state.is-down .mg-dot { background: var(--red); }
@media (max-width: 760px) { .srv-row { grid-template-columns: 1fr; gap: 6px; } .srv-cmd { flex-direction: column; } }

.srv-flow { display: flex; align-items: stretch; gap: 10px; padding: 16px 24px; flex-wrap: wrap; }
.srv-flow-step { flex: 1 1 200px; display: flex; flex-direction: column; gap: 4px; padding: 10px 14px; border-radius: 6px; background: var(--legend-bg); }
.srv-flow-step.is-here { background: color-mix(in srgb, var(--accent) 12%, transparent); outline: 1px solid var(--accent); }
.srv-flow-k { font-size: 14px; font-weight: 600; color: var(--ink-strong); }
.srv-flow-v { font-size: 12px; color: var(--muted); }
.srv-flow-arrow { align-self: center; font-size: 20px; color: var(--faint); }
.site-origin { font-size: 11px; font-weight: 600; padding: 1px 8px; border-radius: 10px; background: var(--chip-bg); color: var(--muted); white-space: nowrap; cursor: help; }
.site-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.site-card-state { margin: 6px 0 2px; }
.site-chip-danger { color: var(--red-ink); border-color: color-mix(in srgb, var(--red) 40%, transparent); }
.site-card.is-pending { opacity: .85; }
.mode-badge.mode-pending { color: var(--amber-ink); background: color-mix(in srgb, var(--amber) 14%, transparent); }
.mode-badge.mode-pending .healthy-dot { background: var(--amber); }
@media (max-width: 760px) { .srv-flow-arrow { transform: rotate(90deg); width: 100%; text-align: center; } }

/* ---- IP blocklist ---- */
.blocklist-root { display: flex; flex-direction: column; gap: var(--gap); }
.bl-add { padding: 16px 24px; }
.bl-add-row { display: flex; flex-wrap: wrap; gap: 12px 16px; align-items: flex-end; margin-top: 10px; }
.bl-ips { flex: 1 1 280px; }
.bl-note { flex: 1 1 200px; }
.bl-btn { height: 36px; }
.bl-card { padding: 16px 24px; }
.bl-table td { vertical-align: middle; }
#drawer-block-ip { margin-left: 8px; color: var(--red-ink); }
