/* Luqas console — the product-side dashboard, to the Figma design.
 *
 * Light, unlike the ops dashboard it replaced, and that is the design's call
 * rather than a whim: this
 * page is read in bursts to answer a question about an account, not left open
 * on a wall, so the reason the ops dashboard is dark does not apply.
 *
 * FONTS
 * -----
 * The design specifies SF Pro Display and Manrope. Neither is loaded, because
 * the CSP this page is served under has no `font-src` and therefore inherits
 * `default-src 'none'` — a webfont would be blocked whether it came from a CDN
 * or from this origin. `-apple-system` resolves to SF Pro on exactly the
 * machines the design was drawn on, and the stack below is a close match
 * elsewhere. Adding a font means adding `font-src 'self'` to the CSP and
 * vendoring the file; it is a deliberate choice not to.
 *
 * Values here are the design's own: colours, radii and spacing were read out of
 * the Figma node rather than eyeballed from the screenshot.
 */

:root {
  /* Surfaces */
  --page: #fafafa;
  --card: #ffffff;
  --shell-radius: 22px;

  /* The sidebar's two-stop gradient, over a texture bitmap. */
  --nav-base: #181a4d;
  --nav-deep: #0d1b3a;
  --nav-ink: #ffffff;
  --nav-ink-dim: #aab6ca;

  /* Type */
  --ink: #15121b;
  --ink-2: #1e1e1e;
  --ink-3: #334054;
  --ink-muted: #475467;
  --ink-faint: #a3adbc;

  /* Accents */
  --accent: #6868b0;
  --accent-strong: #4e54c8;
  --gold: #d4a95f;
  --line: #e2e8f0;

  /* Status. Inherited from the ops dashboard, which is now retired — the
     two agreed about what
   * green means, rather than each inventing a palette. */
  --ok: #12a150;
  --ok-bg: #e8f7ef;
  --warn: #b7791f;
  --warn-bg: #fdf3e3;
  --bad: #d64545;
  --bad-bg: #fdecec;
  --info: #4059ad;
  --info-bg: #eaeefb;

  --radius: 14px;
  --radius-sm: 10px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI",
          Roboto, "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }

/* The `hidden` attribute has to beat the display rules further down.
 *
 * `hidden` works through the browser's own `[hidden] { display: none }`, and
 * ANY author rule that sets `display` overrides it — a user-agent declaration
 * loses to an author one whatever the specificity. `#app` below sets
 * `display: grid`, so `#app hidden` rendered anyway and the whole dashboard
 * shell sat underneath the sign-in card, reachable by scrolling.
 *
 * Nothing was exposed — every figure on the page is fetched after a session
 * exists, so the shell was empty — but a login screen you can scroll past is
 * not a login screen. `!important` because the point is to outrank the rules
 * below it, including the ID selector. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: #eef0f4;
  color: var(--ink);
  font: 14px/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* ------------------------------------------------------------------ gate */

/* Off-screen until focused, rather than display:none — a hidden element is
   not focusable, so a skip link that is truly hidden is a skip link nobody can
   reach. First thing in the tab order, and the only thing between a keyboard
   and nine sidebar buttons on every navigation. */
.skip-link {
  position: absolute;
  left: 8px;
  top: -60px;
  z-index: 200;
  padding: 10px 16px;
  border-radius: 8px;
  background: var(--accent);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: top .12s ease;
}
.skip-link:focus { top: 8px; }

/* The skip target is focusable so the browser actually moves focus there and
   not just the scroll position. It should not then draw a ring around the
   whole page. */
#views:focus { outline: none; }

/* Somebody who has asked for less movement gets none of it. */
@media (prefers-reduced-motion: reduce) {
  .skip-link { transition: none; }
}

.gate {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
  background: linear-gradient(160deg, var(--nav-deep), var(--nav-base));
}

.gate-card {
  width: min(420px, 100%);
  background: var(--card);
  border-radius: var(--radius);
  padding: 36px 32px;
  box-shadow: 0 24px 60px rgba(13, 27, 58, 0.35);
}

.gate-logo { width: 56px; display: block; margin-bottom: 18px; }
.gate-card h1 { font-size: 24px; margin: 0 0 6px; letter-spacing: -0.4px; }
.gate-sub { color: var(--ink-muted); margin: 0 0 22px; }

.gate-card label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-3);
  margin: 14px 0 6px;
}

.gate-card input {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  background: #fbfbfd;
}
.gate-card input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  background: #fff;
}

.hint { font-size: 12px; color: var(--ink-faint); margin: 10px 0 0; }

/* The six-digit code, spaced out so a transposed pair is visible before it is
   submitted. Fixed-width digits, because a code that reflows as you type is
   one you check twice. */
#totp-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 20px;
  letter-spacing: 0.35em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
#totp-cancel { margin-top: 8px; width: 100%; }

/* Lets you read a generated password before committing to a value that is
 * hashed and cannot be read back. Overrides the block/full-width input rules
 * above, which would otherwise render the checkbox as a full-width row. */
.reveal {
  display: flex !important;
  align-items: center;
  gap: 8px;
  margin: 12px 0 0 !important;
  font-weight: 400 !important;
  color: var(--ink-muted) !important;
  cursor: pointer;
}
.reveal input {
  width: auto !important;
  margin: 0;
  padding: 0;
  accent-color: var(--accent-strong);
}
.gate-error { color: var(--bad); font-size: 13px; margin-top: 12px; min-height: 18px; }

/* ------------------------------------------------------------------ shell */

#app {
  display: grid;
  grid-template-columns: 224px 1fr;
  gap: 14px;
  padding: 12px;
  min-height: 100vh;
  background: var(--page);
  border-radius: var(--shell-radius);
}

/* ---------------------------------------------------------------- sidebar */

.sidebar {
  position: sticky;
  top: 15px;
  height: calc(100vh - 30px);
  border-radius: var(--radius);
  background:
    linear-gradient(to top, rgba(13, 27, 58, 0.6) 12.78%, var(--nav-deep) 47.85%),
    url("/static/v2/assets/sidebar-texture.png") center/cover no-repeat,
    var(--nav-base);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 20px 0 0 20px;
}
.brand-arc { width: 44px; height: 25px; }
.brand-word {
  display: inline-flex;
  align-items: baseline;
  color: #fff;
  font-size: 18px;
  letter-spacing: -0.3px;
  text-transform: uppercase;
}
/* The Q in LUQAS is the logo glyph, not a letter. Sized explicitly — an
 * `auto` height here renders the SVG at its intrinsic size, which is large. */
.brand-mark { width: 13px; height: 15px; margin: 0 1px; align-self: center; }

.sidebar-rule { display: block; width: 100%; margin: 14px 0 0; opacity: 0.4; }

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

nav button {
  display: flex;
  align-items: center;
  gap: 12px;
  width: calc(100% - 16px);
  margin: 0 8px;
  padding: 8px 12px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--nav-ink-dim);
  font: inherit;
  font-size: 14px;
  text-align: left;
  text-transform: capitalize;
  cursor: pointer;
  position: relative;
}

nav button img {
  width: 15px;
  height: 15px;
  /* The exported icons are dark; the sidebar is not. Flatten to white and dim
   * with opacity, rather than the long hue-rotate filter this used to carry:
   * that filter was hand-tuned to one specific grey, so it had to be re-tuned
   * every time the label colour moved. Opacity tracks the text instead. */
  filter: brightness(0) invert(1);
  opacity: .68;
}

nav button:hover { color: #fff; }
nav button:hover img { opacity: .9; }

nav button.active {
  color: var(--nav-ink);
  background: linear-gradient(90deg, rgba(78, 84, 200, 0.24), rgba(78, 84, 200, 0.14));
}
nav button.active img { opacity: 1; }

/* The gold rule down the left of the selected item. */
nav button.active::before {
  content: "";
  position: absolute;
  left: -8px;
  top: 5px;
  bottom: 5px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--gold);
}

nav button[hidden] { display: none; }

.sidebar-foot { padding: 8px 20px 14px; }
.sidebar-link {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--nav-ink-dim);
  text-decoration: none;
  font-size: 14px;
}
.sidebar-link:hover { color: #c8cfdb; }
.sidebar-link img {
  width: 14px; height: 14px;
  filter: brightness(0) invert(1);
  opacity: .68;
}

/* ----------------------------------------------------------------- topbar */

.main { min-width: 0; padding-right: 12px; }

.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 6px 0 10px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 14px;
}

.search {
  flex: 1;
  max-width: 560px;
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 7px 12px;
}
.search img { width: 18px; height: 18px; opacity: 0.5; }
.search input {
  flex: 1;
  border: 0;
  background: none;
  font: inherit;
  color: var(--ink);
  outline: none;
}
.search input::placeholder { color: var(--ink-faint); }

/* ------------------------------------------------------------ search panel */

/* Anchored to the pill, which therefore has to establish a containing block.
   z-index above the sticky topbar so results are not clipped by it. */
.search { position: relative; }

.search-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 60;
  max-height: 60vh;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(20, 24, 40, .14);
  padding: 6px;
}

.search-group {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: 8px 10px 4px;
}

.search-note {
  font-size: 12px;
  color: var(--warn);
  background: var(--warn-bg);
  border-radius: 8px;
  padding: 8px 10px;
  margin-bottom: 4px;
}

.search-item {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 7px 10px;
  border-radius: 8px;
  cursor: pointer;
}
.search-item b { font-size: 13px; color: var(--ink); font-weight: 600; }
.search-item span { font-size: 11.5px; color: var(--ink-faint); }
/* One highlight class for hover and for keyboard, so the two cannot drift into
   looking like two different kinds of selection. */
.search-item:hover,
.search-item.on { background: #f4f6fd; }

/* The outline is removed from the input because the ring belongs on the pill
   around it — not because this control should be invisible to focus, which is
   what `outline: none` on its own would have meant. */
.search:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.topbar-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }

#window {
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 9px;
  padding: 6px 9px;
  font: inherit;
  color: var(--ink-3);
}

.icon-btn {
  position: relative;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--card);
  cursor: pointer;
}
.icon-btn img { width: 15px; height: 15px; }
.icon-btn .badge {
  position: absolute;
  top: 8px; right: 8px;
  min-width: 16px; height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--bad);
  color: #fff;
  font-size: 10px;
  line-height: 16px;
  text-align: center;
}

.who {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 9px 4px 4px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--card);
  cursor: pointer;
}
/* Initials, not a photo. The design shows a stock avatar; real operators have
 * no uploaded image and a broken <img> is worse than a monogram. */
.avatar {
  width: 26px; height: 26px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, var(--accent), var(--accent-strong));
  color: #fff;
  font-size: 13px;
  font-weight: 600;
}
.who-text { display: flex; flex-direction: column; line-height: 1.25; }
.who-text strong { font-size: 12.5px; }
.who-text small { color: var(--ink-faint); font-size: 11px; }
.who-chev { width: 12px; height: 12px; opacity: 0.6; }

.who-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 180px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 10px;
  box-shadow: 0 12px 30px rgba(21, 18, 27, 0.12);
  z-index: 30;
}
.who-role {
  display: block;
  font-size: 12px;
  color: var(--ink-faint);
  padding: 4px 8px 10px;
  text-transform: capitalize;
}
.who-menu button {
  width: 100%;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  padding: 8px;
  border-radius: 8px;
  cursor: pointer;
  color: var(--bad);
}
.who-menu button:hover { background: var(--bad-bg); }

/* ------------------------------------------------------------------ views */

.view { display: none; }
.view.active { display: block; }

.page-head { margin-bottom: 12px; }

/* Only the heads that carry an action. Every other .page-head is a bare h1 and
   p, and making the base rule flex would lay those out side by side — which is
   the kind of change that looks like a rendering bug on five pages nobody
   edited. */
.page-head.with-action {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.page-head.with-action > .btn { flex: none; }
.page-head h1 { font-size: 21px; margin: 0 0 2px; letter-spacing: -0.4px; color: var(--ink); }
.page-head p { margin: 0; color: var(--ink-muted); font-size: 13px; }

/* Breadcrumb. Replaces the old .back button: it is a real link now, carrying
 * the list's filters in its href, so returning from an account lands on the
 * search you left rather than an unfiltered table. */
.crumbs { padding: 0 0 14px; }
.crumbs a {
  color: var(--accent);
  text-decoration: none;
  font-size: 14px;
}
.crumbs a:hover { text-decoration: underline; }

/* Sidebar section labels. The nav is grouped by the job being done, because a
 * flat list will not survive the screens still to come. */
.nav-group {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(106, 121, 143, .75);
  padding: 0 0 3px 20px;
  margin-top: 11px;
}
.nav-group:first-child { margin-top: 0; }

/* -------------------------------------------------------------------- KPI */

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

.kpi {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 11px 13px;
}
.kpi-top { display: flex; align-items: flex-start; gap: 9px; }
.kpi-icon {
  width: 27px; height: 27px;
  border-radius: 7px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
}
.kpi-icon img { width: 14px; height: 14px; }
.kpi h3 { margin: 0; font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.kpi .delta { font-size: 10.5px; color: var(--ink-faint); }
.kpi .delta b { font-weight: 600; }
.kpi .delta .up { color: var(--ok); }
.kpi .delta .down { color: var(--bad); }
.kpi .value {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.4px;
  margin-top: 7px;
  color: var(--ink);
}

/* ------------------------------------------------------------------ cards */

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 13px 15px;
  margin-bottom: 12px;
}
.card h2 { margin: 0; font-size: 14.5px; letter-spacing: -0.2px; }
.card-sub { margin: 2px 0 9px; color: var(--ink-muted); font-size: 12px; }
.card-head { display: flex; align-items: center; justify-content: space-between; }
.table-card { padding: 2px 0 0; }
.table-card .card-head { padding: 9px 15px; }

.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  gap: 12px;
}
.grid-2 > .card { margin-bottom: 12px; }

/* ---------------------------------------------------------------- filters */

/* -------------------------------------------------- small multiples */

/* A segmented control, which is the honest shape for "one of these two" —
   a checkbox would imply the off state is an absence rather than the other
   choice. */
.seg {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
  flex: none;
}
.seg button {
  border: 0;
  background: none;
  font: inherit;
  font-size: 11.5px;
  color: var(--ink-faint);
  padding: 5px 11px;
  cursor: pointer;
}
.seg button.on { background: var(--accent); color: #fff; font-weight: 600; }
.seg button:not(.on):hover { color: var(--ink-3); }
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* auto-fit so two regions sit side by side on a wide screen and stack on a
   narrow one, without a media query naming a number of regions this file
   should not know. */
.multiples {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}
.multiple-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  font-size: 12px;
}
/* No height here on purpose. Every chart on this page is a fixed viewBox with
   `height: auto`, so a panel scales with its column and a pixel height would
   fight the aspect ratio rather than set it. */

/* Tones for the tiles that carry a judgement. The sub-label beside each says
   the same thing in words — "covered" / "subsidised" — so the colour is
   emphasis and never the message. */
.mini-tile.t-ok b { color: var(--ok); }
.mini-tile.t-warn b { color: var(--warn); }
.mini-tile.t-bad b { color: var(--bad); }

/* Infrastructure stacks a lot of cards; give the tiles room inside one. */
.card > .mini-tiles { padding: 0 18px 18px; }

/* A card body that is not a table still needs the card's own gutter. */
.pad { padding: 0 18px 14px; }
dl.facts.pad { padding: 0 18px 14px; }

/* --------------------------------------------------------- service health */

/* The attention panel sits above everything and is the only card on the page
   that says anything in words. Given a left rule rather than a coloured fill:
   it is always present, so a permanently red box would be wallpaper within a
   week and stop being read. */
.attention { border-left: 3px solid var(--line); }
.attention-list { list-style: none; margin: 0; padding: 0 18px 18px; }

.att {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 0;
  font-size: 13px;
  border-top: 1px solid var(--line);
}
.att:first-child { border-top: 0; }
/* The pill beside it carries the word, so severity is never colour alone. */
.att span { color: var(--ink-3); }

.region-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 12px;
  padding: 0 18px 18px;
}
.region-tile {
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 14px;
}
.region-tile-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}
.region-tile-head b { font-size: 14px; }

.region-facts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 12px;
}
.region-facts b {
  display: block;
  font-size: 16px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.region-facts span { font-size: 11px; color: var(--ink-faint); }

/* Wraps, because a fingerprint title is a stack frame and truncating it to one
   line hides the half that identifies the fault. */
.issue-title {
  margin-left: 8px;
  white-space: normal;
  word-break: break-word;
}

/* ---------------------------------------------------------- saved views */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: -4px 0 12px;
  min-height: 0;
}
.chips:empty { margin: 0; }

.chip {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
  font-size: 12px;
  background: #fff;
}
/* Lit when the address matches what the chip saved. Without it there is no way
   to tell a filter that was recalled from one rebuilt by hand into something
   that only looks the same. */
.chip.on {
  border-color: var(--accent);
  background: #f4f6fd;
}
.chip.on .chip-go { color: var(--accent-strong, var(--accent)); font-weight: 600; }

.chip-go,
.chip-x,
.chip-add {
  border: 0;
  background: none;
  font: inherit;
  font-size: 12px;
  color: var(--ink-3);
  cursor: pointer;
  padding: 5px 10px;
}
.chip-go { padding-right: 4px; }
.chip-x {
  padding: 5px 9px 5px 4px;
  color: var(--ink-faint);
  line-height: 1;
}
.chip-x:hover { color: var(--bad); }
.chip-go:hover { color: var(--ink); }

.chip-add {
  border: 1px dashed var(--line);
  border-radius: 999px;
  color: var(--ink-faint);
}
.chip-add:hover { color: var(--ink-3); border-color: var(--ink-faint); }

.chip-go:focus-visible,
.chip-x:focus-visible,
.chip-add:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* -------------------------------------------------------------- shortcuts */

.shortcuts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 14px;
  align-items: center;
  margin: 0 0 4px;
  font-size: 13px;
}
.shortcuts dt { display: flex; gap: 4px; }
.shortcuts dd { margin: 0; color: var(--ink-3); }

kbd {
  display: inline-block;
  min-width: 20px;
  text-align: center;
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: #fbfbfd;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px;
  color: var(--ink);
}

#modal-form h3 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-faint);
  margin: 16px 0 8px;
}

.filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(145px, 1fr));
  gap: 8px;
  margin-bottom: 11px;
}
.filters label {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink-3);
}
.filters input,
.filters select {
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  font: inherit;
  font-weight: 400;
  color: var(--ink);
}
.filters input::placeholder { color: var(--ink-faint); }

/* ----------------------------------------------------------------- tables */

.scroll { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
thead th {
  text-align: left;
  padding: 7px 15px;
  color: var(--ink-3);
  font-weight: 600;
  white-space: nowrap;
}
tbody td {
  padding: 7px 15px;
  border-top: 1px solid #f1f3f7;
  color: var(--ink-3);
  vertical-align: middle;
}
tbody tr.clickable { cursor: pointer; }
tbody tr.clickable:hover { background: #fafbfd; }

/* A focused row is the keyboard's equivalent of :hover, and it has to be
   louder — a hover follows a pointer the reader is already looking at, and a
   focus ring is the only thing telling somebody where they are. outline
   alone would be clipped by the scroll container the wide tables sit in, so
   the background carries it too. */
tbody tr.clickable:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  background: #f4f6fd;
}

.cell-user { display: flex; align-items: center; gap: 8px; }
.cell-user .avatar { width: 24px; height: 24px; font-size: 10px; }
.cell-user b { display: block; color: var(--ink); font-weight: 600; }
.cell-user small { color: var(--ink-faint); font-size: 12px; font-family: var(--mono); }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
}
.pill::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.pill.plain::before { display: none; }
.pill.ok      { color: var(--ok);   background: var(--ok-bg); }
.pill.warn    { color: var(--warn); background: var(--warn-bg); }
.pill.bad     { color: var(--bad);  background: var(--bad-bg); }
.pill.info    { color: var(--info); background: var(--info-bg); }
.pill.muted   { color: var(--ink-faint); background: #f3f4f7; }

.region-tag {
  font-family: var(--mono);
  font-size: 11px;
  text-transform: uppercase;
  color: var(--ink-faint);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 2px 6px;
}

.pager {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 15px;
  border-top: 1px solid #f1f3f7;
  color: var(--ink-muted);
  font-size: 11.5px;
}
.pager button {
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 8px;
  padding: 6px 12px;
  font: inherit;
  cursor: pointer;
}
.pager button:disabled { opacity: 0.45; cursor: default; }

/* ----------------------------------------------------------------- detail */

.detail-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 13px;
}
.detail-head .avatar { width: 40px; height: 40px; font-size: 14px; }
.detail-head h1 { font-size: 19px; margin: 0 0 1px; letter-spacing: -0.35px; }
.detail-head p { margin: 0; color: var(--ink-muted); }

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(145px, 1fr));
  gap: 9px 16px;
}
.fact dt { font-size: 10.5px; color: var(--ink-faint); margin-bottom: 2px; }
.fact dd { margin: 0; font-size: 13px; color: var(--ink); word-break: break-word; }

/* ----------------------------------------------------------------- charts */

.chart { width: 100%; position: relative; }
.chart svg { display: block; width: 100%; height: auto; overflow: visible; }
.chart svg:focus { outline: none; }
.chart svg:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
.chart .grid-line { stroke: #eef0f4; stroke-width: 1; }
.chart .axis-label { fill: var(--ink-faint); font-size: 11px; }
.chart .bar { fill: #fdf3e3; cursor: pointer; }
.chart .bar.peak { fill: var(--gold); }
.chart .bar:hover, .chart .bar:focus-visible, .chart .bar.hover { opacity: 0.72; }
.chart .dot { fill: var(--accent-strong); }

/* The crosshair — line/area charts only. A hairline, not a mark: readers aim
 * at a date, not at a 2px target, so it tracks the pointer and snaps to the
 * nearest data position rather than needing to be hit precisely. */
.chart .crosshair { stroke: var(--ink-faint); stroke-width: 1; pointer-events: none; }
.chart .crosshair-dot { fill: var(--card); stroke-width: 2; pointer-events: none; }

/* The hover/focus tooltip shared by every chart primitive. Positioned in JS
 * (it has to track the pointer or the nearest data point), styled here. */
.chart-tip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(21, 18, 27, 0.12);
  padding: 8px 10px;
  font-size: 12px;
  white-space: nowrap;
  transform: translate(-50%, calc(-100% - 10px));
}
.chart-tip-x {
  color: var(--ink-faint);
  font-size: 11px;
  margin-bottom: 4px;
}
.chart-tip-row { display: flex; align-items: center; gap: 6px; }
.chart-tip-row + .chart-tip-row { margin-top: 3px; }
/* A short stroke, not a filled box — at tooltip density a box is data-weight
 * ink doing a label's job. */
.chart-tip-key { display: inline-block; width: 12px; height: 2px; border-radius: 1px; flex: none; }
.chart-tip-row b { color: var(--ink); font-variant-numeric: tabular-nums; }
.chart-tip-row span { color: var(--ink-muted); }

.legend {
  display: flex;
  gap: 18px;
  justify-content: flex-end;
  font-size: 12px;
  color: var(--ink-muted);
  margin-bottom: 6px;
}
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }

/* The dashed series gets a dashed swatch. Two blue-ish lines on one axis are
   one pair of eyes away from being the same line; the dash is what survives
   colour blindness and a monochrome print, and it has to be visible in the key
   as well as on the chart or it explains nothing. */
.legend i.dash {
  width: 15px;
  height: 0;
  border-radius: 0;
  border-top-width: 2px;
  border-top-style: dashed;
  background: none;
}

/* ---------------------------------------------------------------- buttons */

.btn {
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 12px;
  padding: 12px 18px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  color: var(--ink-3);
}
.btn.primary {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: #fff;
  width: 100%;
  margin-top: 22px;
}
.btn.primary.sm { width: auto; margin: 0; padding: 9px 16px; }
.btn.danger { color: var(--bad); border-color: #f3d6d6; }
.btn:disabled { opacity: 0.6; cursor: default; }

.row-actions { display: flex; gap: 8px; }
.row-actions button {
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 8px;
  padding: 5px 10px;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}


/* A renderer that threw. Loud on purpose: the failure this replaces was a
 * spinner that never resolved, which is indistinguishable from a slow network
 * and tells nobody anything. */
.view-error {
  display: block;
  padding: 10px 14px;
  margin-bottom: 12px;
  border-radius: 10px;
  background: var(--bad-bg);
  color: var(--bad);
  font-size: 12.5px;
  line-height: 1.5;
}
.view-error b { font-weight: 600; }

/* ------------------------------------------------------------- SLA board */
/* Four queues side by side. A queue with something overdue is marked on the
 * card itself, not only on the row inside it, so the state survives a glance
 * from across a desk — which is the only way a morning check actually works. */

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

.queue {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.queue.has-overdue {
  border-color: #f0c9cb;
  box-shadow: inset 3px 0 0 var(--bad);
}

.queue-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.queue-head h3 { margin: 0; font-size: 13px; font-weight: 600; color: var(--ink); }
.queue-count { display: flex; align-items: baseline; gap: 6px; }
.queue-count b { font-size: 19px; font-weight: 600; letter-spacing: -0.4px; }

/* Which clock this is, and whose. An internal target shown in the same red as
 * a statutory one teaches an operator that red means nothing. */
.queue-law { font-size: 10.5px; color: var(--ink-faint); line-height: 1.35; }

.queue-items { display: flex; flex-direction: column; gap: 1px; }
.queue-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 0;
  border-top: 1px solid #f2f4f8;
}
.queue-item.clickable { cursor: pointer; }
.queue-item.clickable:hover { background: #fafbfd; }
.queue-item.clickable:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  background: #f4f6fd;
}
.qi-line {
  font-size: 12px;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 24ch;
}
.qi-note {
  font-size: 10.5px;
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 26ch;
}
.queue-more { font-size: 11px; color: var(--ink-faint); }
.queue-clear { font-size: 12px; color: var(--ok); }

.queue .btn.sm { align-self: flex-start; padding: 5px 10px; font-size: 11.5px; }

/* A count on the sidebar item, so the board does not have to be open for an
 * overdue obligation to be visible. */
.nav-badge {
  margin-left: auto;
  min-width: 17px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--bad);
  color: #fff;
  font-size: 10px;
  line-height: 17px;
  text-align: center;
  font-weight: 600;
}

/* ------------------------------------------------------------------ modal */

dialog {
  border: 0;
  border-radius: var(--radius);
  padding: 26px 28px;
  width: min(440px, 92vw);
  box-shadow: 0 24px 60px rgba(21, 18, 27, 0.25);
}
dialog::backdrop { background: rgba(13, 27, 58, 0.45); }
dialog h2 { margin: 0 0 18px; font-size: 20px; }
dialog label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-3);
  margin: 12px 0 6px;
}
dialog input, dialog select {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
}
.dialog-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 22px; }
.dialog-error { color: var(--bad); font-size: 13px; margin-top: 10px; min-height: 18px; }

/* ------------------------------------------------------------------ state */

.empty, .loading, .failed {
  padding: 34px 24px;
  text-align: center;
  color: var(--ink-faint);
}
.failed { color: var(--bad); }

/* A region that could not be reached is called out rather than silently
 * dropped: a total that quietly lost half the fleet is the one wrong answer a
 * dashboard must never give. The retired ops page followed the same rule. */
.partial {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  margin-bottom: 18px;
  border-radius: 12px;
  background: var(--warn-bg);
  color: var(--warn);
  font-size: 13px;
}

/* ----------------------------------------------------------------- narrow */

@media (max-width: 1080px) {
  #app { grid-template-columns: 1fr; padding: 8px; }
  .sidebar { position: static; height: auto; }
  nav { flex-direction: row; overflow-x: auto; padding: 14px 0; align-items: center; }
  nav button { width: auto; margin: 0 8px; white-space: nowrap; }
  /* The group labels are a vertical device; on one scrolling row they read as
     stray words between buttons. */
  .nav-group { display: none; }
  nav button.active::before { display: none; }
  .sidebar-foot { display: none; }
  .page-head h1 { font-size: 26px; }
}

/* ---------------------------------------------------------------- actions */

/* Wraps rather than scrolls: on a narrow window a horizontal scroller hides
 * the destructive buttons at the end, which are exactly the ones a person
 * should see before clicking anything. */
.action-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0 18px 18px;
}
.action-bar .btn { width: auto; margin: 0; padding: 8px 14px; }

/* Danger reads as danger before the click, not after. Border and text only -
 * a solid red button beside five neutral ones pulls the eye to the one action
 * that cannot be undone. */
.btn.danger:hover { background: #fdf3f3; border-color: var(--bad); }

.dialog-note {
  color: var(--ink-muted);
  font-size: 13px;
  line-height: 1.5;
  margin: 6px 0 16px;
}

#modal-form textarea {
  width: 100%;
  min-height: 84px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font: inherit;
  resize: vertical;
}

/* ------------------------------------------------------------------ notes */

.notes { padding: 0 18px 4px; }

.note {
  border-left: 2px solid var(--line);
  padding: 2px 0 2px 12px;
  margin-bottom: 14px;
}
.note-meta {
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-size: 12px;
  color: var(--ink-faint);
  margin-bottom: 3px;
}
.note-meta b { color: var(--ink-3); font-weight: 600; }
.note p { margin: 0; font-size: 13px; line-height: 1.55; white-space: pre-wrap; }

.note-form { display: flex; flex-direction: column; gap: 8px; padding: 6px 18px 18px; }
.note-form textarea {
  min-height: 64px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font: inherit;
  resize: vertical;
}
.note-form .btn { align-self: flex-end; width: auto; margin: 0; }

/* ----------------------------------------------------------------- queues */

.card-head .muted {
  margin: 3px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  max-width: 62ch;
}

/* Ids and error strings. Tabular so a column of them scans as a column. */
.mono-cell {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  color: var(--ink-muted);
}

/* ---------------------------------------------------------------- toasts */

/* Bottom-right, above everything, and it never blocks a click: an operator
 * running five actions in a row should not have to dismiss five confirmations,
 * nor find one sitting over the button they want next. */
#toasts {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
.toast {
  background: var(--nav-deep);
  color: #fff;
  padding: 11px 16px;
  border-radius: 10px;
  font-size: 13px;
  max-width: 380px;
  box-shadow: 0 6px 24px rgba(13, 27, 58, 0.28);
  animation: toast-in 160ms ease-out;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}

/* ----------------------------------------------------------- health charts */

.region-switch {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}
.region-switch .btn { width: auto; margin: 0; padding: 6px 14px; }
.region-switch .muted { font-size: 12px; }

.chart { padding: 4px 18px 18px; }
.foot { padding: 0 18px 16px; margin: -6px 0 0; font-size: 12px; max-width: 78ch; }

/* The remainder of a funnel bar. Pale rather than absent: the gap between one
 * stage and the next is the finding, and empty space reads as nothing. */
.funnel-track { fill: #eef0f6; }
.funnel-fill  { fill: var(--accent, #3a5cd6); }
.funnel-value { font-size: 12px; fill: var(--ink-3); font-weight: 600; }
.funnel-drop  { font-size: 10.5px; fill: var(--ink-faint); }

.hist-bar { fill: var(--accent, #3a5cd6); }
.hist-bar.bad { fill: #c0392b; }
.hist-value { font-size: 11px; fill: var(--ink-muted); }

/* ------------------------------------------------------------ cohort grid */

table.cohort { border-collapse: collapse; font-size: 12px; }
table.cohort th {
  padding: 7px 9px;
  text-align: left;
  font-weight: 600;
  color: var(--ink-faint);
  white-space: nowrap;
}
table.cohort td { padding: 6px 9px; white-space: nowrap; }

/* The signup-week column is a <th scope="row"> so a screen reader can say
   which cohort a percentage belongs to. It is still a data cell to look at. */
table.cohort tbody th {
  padding: 6px 9px;
  font-weight: 500;
  color: var(--ink);
}

/* Tabular figures so a column of percentages lines up as a column, which is
 * the only reason to put them in a grid rather than a chart. */
.cohort-cell {
  text-align: center;
  font-variant-numeric: tabular-nums;
  border-radius: 4px;
  min-width: 46px;
  color: var(--ink-3);
}
.cohort-empty { color: var(--ink-faint); background: none; }

/* ------------------------------------------------------------- mini tiles */

.mini-tiles {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 0 18px 8px;
}
.mini-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  min-width: 104px;
}
.mini-tile b { font-size: 18px; font-variant-numeric: tabular-nums; color: var(--ink); }
.mini-tile span { font-size: 11.5px; color: var(--ink-faint); }

/* A mini-tiles row living on its own, not inside a .card — the money page's
 * static KPI strip above its tabs. No left/right padding to eat into: it
 * already sits at the page's own margin. */
.mini-tiles.standalone { padding: 0 0 16px; }

/* ----------------------------------------------------------------- account */

.pw-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0 18px 18px;
  max-width: 380px;
}
.pw-form label { font-size: 12px; color: var(--ink-muted); margin-top: 6px; }
.pw-form input {
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font: inherit;
}
.pw-form .btn { align-self: flex-start; width: auto; margin: 6px 0 0; }

/* ---- two-step verification */

.twofa { padding: 0 18px 18px; }

.twofa-state {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  font-size: 13px;
}

/* The pill beside it already says on or off in words, so this row does not
   depend on telling green from amber. Same rule the charts follow. */
.twofa .pw-form { padding: 0; }

.twofa-enrol { display: flex; flex-direction: column; gap: 18px; }

.twofa-scan {
  display: flex;
  gap: 20px;
  align-items: flex-start;
  flex-wrap: wrap;
}
.twofa-scan p { margin: 0 0 8px; font-size: 13px; }

/* A fixed size and a white plate under it. The symbol is drawn on its own
   white background inside the SVG too - this is the padding a scanner needs
   between the code and whatever colour the card happens to be. */
.qr {
  width: 168px;
  height: 168px;
  flex: none;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 6px;
  box-sizing: content-box;
  image-rendering: pixelated;
}

/* Grouped in fours and selectable, because the fallback to the QR is somebody
   copying this into a password manager by hand. */
.twofa-key {
  display: inline-block;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  letter-spacing: 0.04em;
  background: var(--surface-sunk, rgba(0, 0, 0, 0.04));
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 10px;
  user-select: all;
  word-break: break-all;
}

.who-menu button { display: block; width: 100%; text-align: left; }

/* ==================================================== logs, money, dashboard
 *
 * Same tokens as everything above — no new colours, no new radii. The status
 * pairs are the ones defined at the top of this file, so a warning on
 * this screen is the same warning it is everywhere else.
 */

/* --- the dashboard's own sections ------------------------------------- */

/* A heading between the command-center sections and the growth screen that
 * used to be the whole page. Without it the KPI row reads as belonging to the
 * cost card above it. */
.section-head {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-2);
  margin: 26px 0 12px;
}

.attention {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 4px 0 2px;
}

/* A count that is also the way in. Sized so the number is what the eye lands
 * on and the label explains it, rather than the other way round. */
.attn {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  cursor: pointer;
  font: inherit;
  text-align: left;
}
.attn:hover { border-color: var(--accent); }
.attn-n { font-size: 20px; font-weight: 700; }
.attn-label { color: var(--ink-3); font-size: 13px; }
/* Colour is on the number only, and the label beside it carries the meaning
 * in words — the rule the accessibility audit enforces for every status in
 * this file. */
.attn.bad  .attn-n { color: var(--bad); }
.attn.warn .attn-n { color: var(--warn); }
.ok-line { color: var(--ok); font-size: 13px; padding: 6px 0; }
.warn-line { color: var(--warn); font-size: 13px; }

.shortcut-row { display: flex; flex-wrap: wrap; gap: 8px; padding: 4px 0; }
.shortcut {
  padding: 7px 13px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.shortcut:hover { border-color: var(--accent); color: var(--accent-strong); }

/* --- money tabs -------------------------------------------------------- */

.tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 18px;
}
.tabs button {
  padding: 9px 16px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--ink-3);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.tabs button.on {
  color: var(--accent-strong);
  border-bottom-color: var(--accent-strong);
  font-weight: 600;
}

/* --- log facets -------------------------------------------------------- */

.facets { margin-bottom: 14px; }
.facet-strip { display: flex; flex-wrap: wrap; gap: 8px; }

/* A facet is a count and a filter at once. `aria-pressed` carries the state
 * for a screen reader; the border carries it visually, and neither is the
 * only signal. */
.facet {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.facet:hover { border-color: var(--accent); }
.facet.on {
  border-color: var(--accent-strong);
  background: var(--accent-strong);
  color: #fff;
}
.facet.on .facet-n { color: #fff; }
.facet-label { font-weight: 500; }
.facet-n { color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* A level with nothing in it is drawn greyed rather than dropped: a strip that
 * loses its ERROR chip when there are no errors makes "no errors" and "no chip"
 * look identical. */
.facet.faint { opacity: 0.5; }
.facet.bad:not(.on)  .facet-n { color: var(--bad); }
.facet.warn:not(.on) .facet-n { color: var(--warn); }

/* --- the buffer pill --------------------------------------------------- */

/* New rows wait behind this instead of pushing the list down. It is the
 * consent step that makes a live tail readable. */
.pill-btn {
  display: block;
  margin: 0 auto 12px;
  padding: 7px 16px;
  border: 1px solid var(--accent-strong);
  border-radius: 999px;
  background: var(--accent-strong);
  color: #fff;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

/* --- log rows ---------------------------------------------------------- */

.log-block + .log-block { margin-top: 20px; }
.log-block-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
  margin-bottom: 4px;
}

.log-list { display: flex; flex-direction: column; }

.log-row {
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}
.log-row:hover { background: var(--page); }
/* The focus ring matters more here than anywhere else in the console: these
 * rows are walked with j/k and opened with Enter. */
.log-row:focus-visible {
  outline: 2px solid var(--accent-strong);
  outline-offset: -2px;
}

.log-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 4px;
  font-size: 13px;
}
.log-kind {
  color: var(--ink-3);
  font-family: var(--mono);
  font-size: 12px;
}
.log-ts { color: var(--ink-faint); font-size: 12px; white-space: nowrap; }
/* The summary takes the slack so the columns after it stay put down the page;
 * a jagged right edge is what makes a log list hard to scan. */
.log-summary {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-2);
}
.log-status, .log-ms {
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
}

.log-body { padding: 4px 4px 14px; }
.log-actions { display: flex; gap: 8px; margin-bottom: 10px; }

/* The payload, already scrubbed at write time by observability's denylist.
 * Scrolls inside itself rather than widening the page. */
.log-json {
  margin: 0;
  padding: 12px;
  border-radius: var(--radius-sm);
  background: var(--page);
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.5;
  overflow-x: auto;
  max-height: 380px;
  overflow-y: auto;
}

/* The include-admin tickbox sits in the same .filters row as the selects, but
 * a checkbox and a label stack badly under the shared rule. */
.filters label.check {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  align-self: end;
  padding-bottom: 9px;
  font-size: 13px;
  color: var(--ink-2);
}
.filters label.check input { width: auto; }
