/* ============================================================================
   MiniCRM design system
   ----------------------------------------------------------------------------
   Layers, in order: tokens -> reset -> layout shell -> components -> utilities.
   Two rules hold the whole thing together:
     1. Nothing hard-codes a colour, radius, shadow or duration; everything
        reads a token, so a change lands everywhere at once.
     2. Every interactive element has a visible :focus-visible ring. The app is
        operable end to end without a mouse.
   ========================================================================== */

:root {
  color-scheme: dark;

  /* --- surface ramp: each step sits visually above the last --------------- */
  --bg: #080b14;
  --bg-grad: radial-gradient(1200px 640px at 78% -10%, rgba(77, 124, 255, 0.10), transparent 62%);
  --panel: #101627;
  --panel-2: #161e33;
  --panel-3: #1c2540;
  --field: #0b1120;

  --border: #212b45;
  --border-strong: #2e3a5c;

  --text: #e9edfa;
  --text-dim: #b6c0dd;
  --muted: #8f9cbe;
  --faint: #64719a;

  /* --- accents ----------------------------------------------------------- */
  --accent: #5b86ff;
  --accent-hi: #7ea0ff;
  --accent-press: #4a72e8;
  --accent-soft: rgba(91, 134, 255, 0.14);
  --accent-line: rgba(91, 134, 255, 0.34);

  --green: #3ecf8e;
  --amber: #f5b544;
  --red: #f2635f;
  --purple: #a978ff;

  /* --- shape ------------------------------------------------------------- */
  --r-sm: 7px;
  --r: 10px;
  --r-lg: 14px;
  --r-xl: 20px;
  --r-pill: 999px;

  /* --- elevation --------------------------------------------------------- */
  --sh-1: 0 1px 2px rgba(0, 0, 0, .28);
  --sh-2: 0 4px 14px -6px rgba(0, 0, 0, .5), 0 1px 2px rgba(0, 0, 0, .3);
  --sh-3: 0 18px 44px -18px rgba(0, 0, 0, .68), 0 2px 8px rgba(0, 0, 0, .34);
  --sh-4: 0 40px 90px -28px rgba(0, 0, 0, .8), 0 4px 14px rgba(0, 0, 0, .4);

  /* --- motion ------------------------------------------------------------ */
  --dur-1: 110ms;
  --dur-2: 190ms;
  --dur-3: 300ms;
  --ease: cubic-bezier(.32, .72, .28, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  /* --- metrics ----------------------------------------------------------- */
  --sidebar-w: 232px;
  --topbar-h: 56px;
  --tap: 40px;

  --mono: ui-monospace, SFMono-Regular, "Cascadia Mono", "Cascadia Code", Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --focus: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent-hi);
}

/* ------------------------------- reset --------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-color: var(--border-strong) transparent;
}

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

body {
  background: var(--bg);
  background-image: var(--bg-grad);
  background-attachment: fixed;
  color: var(--text);
  font: 14px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-wrap: break-word;
}

body.no-scroll { overflow: hidden; }

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

h1, h2, h3, h4 { letter-spacing: -0.021em; }

::selection { background: var(--accent-line); color: #fff; }

/* One focus treatment for everything that can take focus. */
:focus { outline: none; }
:focus-visible {
  outline: none;
  box-shadow: var(--focus);
  border-radius: var(--r-sm);
  position: relative;
  z-index: 3;
}

/* Scrollbars, so long lists do not look like a different app. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--r-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: #3b4a75; background-clip: padding-box; }

.skip-link {
  position: fixed; top: 8px; left: 8px; z-index: 200;
  transform: translateY(-160%);
  padding: 10px 16px; border-radius: var(--r);
  background: var(--accent); color: #fff; font-weight: 600;
  box-shadow: var(--sh-3);
  transition: transform var(--dur-2) var(--ease-out);
}
.skip-link:focus-visible { transform: translateY(0); text-decoration: none; }

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

#app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  grid-template-areas: "sidebar main";
  min-height: 100vh;
  min-height: 100dvh;
}

/* Both only exist below the drawer breakpoint; see the 860px block. */
.topbar { display: none; }
.nav-scrim { display: none; }

.sidebar {
  grid-area: sidebar;
  background: linear-gradient(180deg, var(--panel) 0%, #0d1220 100%);
  border-right: 1px solid var(--border);
  padding: 16px 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.brand {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 10px 22px;
  font-weight: 700; font-size: 16.5px; letter-spacing: -0.03em;
}
.brand .dot {
  width: 11px; height: 11px; border-radius: 50%;
  background: linear-gradient(140deg, var(--accent-hi), var(--accent));
  box-shadow: 0 0 0 3px var(--accent-soft), 0 0 12px var(--accent-line);
  flex: none;
}

.nav-item {
  /* justify-content is spelled out because the base `button` rule centres its
     content, which would push the <button> sign-out row out of line with the
     <a> rows beside it. */
  display: flex; align-items: center; justify-content: flex-start; gap: 11px;
  min-height: var(--tap);
  padding: 9px 11px;
  border: 0; width: 100%;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted);
  font: inherit; font-weight: 500; text-align: left;
  cursor: pointer; user-select: none;
  position: relative;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.nav-item:hover { background: var(--panel-2); color: var(--text); text-decoration: none; }
.nav-item:active { background: var(--panel-3); }
.nav-item.active { background: var(--accent-soft); color: #d3deff; }
/* The active rail reads at a glance without relying on colour alone. */
/* Inset from the sidebar edge, so it reads as a rail rather than a stray mark
   pinned to the very edge of the window. */
.nav-item.active::before {
  content: ""; position: absolute; left: -6px; top: 50%;
  width: 3px; height: 18px; margin-top: -9px;
  border-radius: var(--r-pill);
  background: var(--accent-hi);
}
.nav-item .ico { width: 18px; text-align: center; opacity: .85; flex: none; font-size: 14px; }
.nav-item.active .ico { opacity: 1; }
.nav-spacer { flex: 1 1 auto; min-height: 12px; }

/* ---------------------------- network switcher --------------------------- */

/* Built by actionMenu, so it arrives carrying .menu-trigger.has-text — which
   sets its own padding, gap and letter-spacing for a table row. Put the nav
   item's geometry back, and give it a surface: this one names where you are
   rather than linking to a page, and should not read as a sixth destination. */
.nav-item.nav-switch {
  gap: 11px;
  padding: 9px 11px;
  letter-spacing: normal;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  margin-bottom: 6px;
}
.nav-item.nav-switch:hover { background: var(--panel-3); border-color: var(--border-strong); color: var(--text); }

/* The icon comes from CSS because actionMenu's text mode renders a label and a
   caret and nothing else; the alternative is teaching it about icons for one
   caller. */
.nav-item.nav-switch::before {
  content: "⇄";
  width: 18px; flex: none;
  text-align: center; font-size: 14px; opacity: .85;
}
.nav-item.nav-switch .menu-trigger-text {
  flex: 1 1 auto; min-width: 0;
  max-width: none;              /* the sidebar, not 15ch, is the constraint here */
  font-weight: 600;
}
.nav-item.nav-switch .caret { flex: none; margin-left: 4px; }

.main {
  grid-area: main;
  padding: 34px 26px 72px;
  min-width: 0;
}

/* ---------------------------- corner badges ----------------------------- */

/* The build badge and the update notification share this row. It owns the
   fixed positioning so the badges themselves stay ordinary inline elements and
   can also be dropped into the app bar on a phone. */
.corner-badges {
  position: fixed;
  top: 12px; right: 18px;
  z-index: 40;
  display: flex; align-items: center; gap: 8px;
}

.version-badge {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 28px;
  padding: 4px 11px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  backdrop-filter: blur(10px);
  color: var(--muted);
  font-size: 11.5px; font-family: var(--mono);
  cursor: pointer; user-select: none;
  box-shadow: var(--sh-1);
  transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.version-badge:hover { color: var(--text); border-color: var(--border-strong); transform: translateY(-1px); }
.version-badge:active { transform: translateY(0); }
.version-badge .env { color: var(--accent-hi); font-weight: 600; }
.version-badge .hash { opacity: .7; }
.version-badge .dot-live {
  width: 6px; height: 6px; border-radius: 50%; background: var(--green);
  box-shadow: 0 0 0 3px rgba(62, 207, 142, .16);
}

/* --------------------------- update available --------------------------- */

/* Sits to the left of the build badge and is the only accent-coloured thing in
   the corner, so it reads as "something is waiting for you" without being an
   interruption. Nothing here blocks the page — the operator picks the moment. */
.update-badge {
  order: -1;
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 28px;
  padding: 4px 12px;
  border-radius: var(--r-pill);
  border: 1px solid var(--accent-line);
  background: var(--accent-soft);
  color: var(--accent-hi);
  font-size: 11.5px; font-weight: 600;
  cursor: pointer; user-select: none;
  box-shadow: var(--sh-1);
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.update-badge:hover { background: color-mix(in srgb, var(--accent) 26%, transparent); transform: translateY(-1px); }
.update-badge:active { transform: translateY(0); }

.update-badge-dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent-hi);
  animation: update-pulse 2.4s var(--ease) infinite;
}
@keyframes update-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(126, 160, 255, .5); }
  50%      { box-shadow: 0 0 0 5px rgba(126, 160, 255, 0); }
}
/* A pulsing dot is an accessibility problem for anyone who asked the system to
   stop moving things. The badge is still visible; it just holds still. */
@media (prefers-reduced-motion: reduce) {
  .update-badge-dot { animation: none; }
}

.update-versions {
  display: flex; align-items: center; gap: 18px;
  padding: 14px 16px; margin-bottom: 18px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
}
.update-arrow { color: var(--muted); font-size: 18px; }
.update-h4 { margin: 0 0 10px; font-size: 13px; font-weight: 620; }

.change-list { margin: 0; padding: 0; list-style: none; max-height: 220px; overflow-y: auto; }
.change-list li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.change-list li:last-child { border-bottom: 0; }
.change-subject { min-width: 0; overflow-wrap: anywhere; }

/* The consequence of pressing the button, stated before the button is pressed. */
.update-warn {
  margin-top: 18px; padding: 12px 14px;
  border-left: 3px solid var(--amber);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--amber) 9%, transparent);
  font-size: 13px; color: var(--text-dim);
}
.update-warn strong { color: var(--text); }

.update-progress-panel { padding: 4px 0; }

.progress-bar {
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--field);
  border: 1px solid var(--border);
  overflow: hidden;
}
.progress-fill {
  height: 100%; width: 0%;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--accent), var(--accent-hi));
  transition: width .9s var(--ease);
}
/* Before the first reading there is no number to show, so the bar says
   "something is happening" rather than claiming a value it does not have. */
.progress-bar.unknown .progress-fill {
  width: 34% !important;
  opacity: .6;
  animation: progress-sweep 1.5s ease-in-out infinite;
}
@keyframes progress-sweep { 0% { margin-left: -34%; } 100% { margin-left: 100%; } }
.progress-bar.failed .progress-fill { background: var(--amber); animation: none; }

.progress-meta {
  display: flex; justify-content: space-between;
  margin-top: 9px;
  font-size: 12.5px; color: var(--faint);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .progress-fill { transition: none; }
  .progress-bar.unknown .progress-fill { animation: none; }
}

/* ------------------------------ fraud score ----------------------------- */

.fraud-pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 3px 10px 3px 4px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text-dim);
  font-size: 11.5px; font-weight: 600;
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.fraud-pill:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.fraud-pill.static { cursor: default; }
.fraud-pill.static:hover { transform: none; }

.fraud-pill-score {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 20px; padding: 0 5px;
  border-radius: var(--r-pill);
  background: var(--panel-3);
  font-family: var(--mono); font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.fraud-pill-label { padding-right: 2px; }


/* Colour carries the same information as the label, never instead of it —
   the band name is always present for anyone who cannot separate the hues. */
.fraud-pill.clean      { border-color: rgba(62, 207, 142, .4); color: var(--green); }
.fraud-pill.clean      .fraud-pill-score { background: rgba(62, 207, 142, .16); }
.fraud-pill.suspicious { border-color: rgba(245, 181, 68, .42); color: var(--amber); }
.fraud-pill.suspicious .fraud-pill-score { background: rgba(245, 181, 68, .16); }
.fraud-pill.high       { border-color: rgba(242, 99, 95, .45); color: var(--red); }
.fraud-pill.high       .fraud-pill-score { background: rgba(242, 99, 95, .16); }
.fraud-pill.fraud      { border-color: var(--red); color: #fff; background: rgba(242, 99, 95, .22); }
.fraud-pill.fraud      .fraud-pill-score { background: var(--red); color: #2a0a0a; }

/* The row itself, so a fraud lead is obvious while scanning even when the
   Fraud column is hidden. A left edge plus a wash, not a full red fill: the
   row must stay readable. */
tbody tr.row-fraud > td { background: rgba(242, 99, 95, .07); }
tbody tr.row-fraud:hover > td { background: rgba(242, 99, 95, .12); }
tbody tr.row-fraud > td:first-child { box-shadow: inset 3px 0 0 var(--red); }

.fraud-headline {
  display: flex; align-items: center; gap: 18px;
  padding: 16px 18px; margin-bottom: 18px;
  border-radius: var(--r);
  border: 1px solid var(--border);
  background: var(--panel-2);
}
.fraud-headline-score {
  font-size: 34px; font-weight: 680; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; line-height: 1;
  min-width: 62px; text-align: center;
}
.fraud-headline.clean      { border-color: rgba(62, 207, 142, .38); }
.fraud-headline.clean      .fraud-headline-score { color: var(--green); }
.fraud-headline.suspicious { border-color: rgba(245, 181, 68, .4); }
.fraud-headline.suspicious .fraud-headline-score { color: var(--amber); }
.fraud-headline.high       { border-color: rgba(242, 99, 95, .42); }
.fraud-headline.high       .fraud-headline-score { color: var(--red); }
.fraud-headline.fraud      { border-color: var(--red); background: rgba(242, 99, 95, .12); }
.fraud-headline.fraud      .fraud-headline-score { color: var(--red); }

.fraud-escalated {
  padding: 12px 14px; margin-bottom: 4px;
  border-left: 3px solid var(--amber);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--amber) 9%, transparent);
  font-size: 13px;
}
.fraud-escalated ul { margin: 6px 0 0; padding-left: 18px; }

.fraud-section { padding: 14px 0; border-top: 1px solid var(--border); }
.fraud-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.fraud-section-head h4 { margin: 0; font-size: 13.5px; font-weight: 620; }

.fraud-flags { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 6px; }
.fraud-flags li { display: flex; align-items: baseline; gap: 8px; font-size: 13px; color: var(--text-dim); }
.fraud-flag-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--amber); }
.fraud-flags li.critical { color: var(--text); }
.fraud-flags li.critical .fraud-flag-dot { background: var(--red); }

.fraud-detail td { padding: 5px 10px 5px 0; vertical-align: top; }
.fraud-detail tr + tr td { border-top: 1px solid var(--border); }

/* ------------------------------ page head ------------------------------- */

.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; margin-bottom: 22px; flex-wrap: wrap;
}
.page-head h1 { margin: 0; font-size: 24px; letter-spacing: -0.03em; font-weight: 660; }
.page-head .sub { margin: 5px 0 0; color: var(--muted); font-size: 13px; }
.head-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* ------------------------------ surfaces -------------------------------- */

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 17px;
  box-shadow: var(--sh-1);
}
.card h3 { margin: 0 0 13px; font-size: 14px; font-weight: 620; }

/* Stacked cards need breathing room, but the same rule fires between grid
   items too, where the gap already handles it — there it pushed every card
   after the first 14px down inside its own (stretched) track. */
.card + .card { margin-top: 14px; }
.grid > .card + .card { margin-top: 0; }

/* Row of secondary actions at the foot of a card. */
.card-actions { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; }
@media (max-width: 560px) {
  .card-actions > button, .card-actions > .btn { flex: 1 1 calc(50% - 4px); justify-content: center; }
}

.grid { display: grid; gap: 14px; }
/* Cards of genuinely different length (a 2-row list beside a 12-row one) look
   broken when stretched to match — hundreds of pixels of empty panel. */
.grid.align-start { align-items: start; }
.grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.stat {
  background: linear-gradient(168deg, var(--panel-2) 0%, var(--panel) 62%);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 15px 17px;
  box-shadow: var(--sh-1);
  transition: border-color var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
.stat:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.stat .label {
  color: var(--muted); font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .075em;
}
.stat .value {
  font-size: 28px; font-weight: 660; margin-top: 7px;
  letter-spacing: -0.035em; font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.stat .foot { color: var(--faint); font-size: 12px; margin-top: 5px; }

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

button, .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font: inherit; font-weight: 550;
  min-height: 34px;
  padding: 7px 13px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  cursor: pointer; white-space: nowrap;
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              transform var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
button:hover, .btn:hover {
  background: var(--panel-3); border-color: var(--border-strong);
  text-decoration: none; color: var(--text);
}
button:active, .btn:active { transform: translateY(1px); }
button:disabled, .btn[aria-disabled="true"] {
  opacity: .5; cursor: not-allowed; transform: none; background: var(--panel-2);
}

.btn-primary {
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  border-color: var(--accent-press);
  color: #fff;
  font-weight: 620;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .16) inset, var(--sh-1);
}
.btn-primary:hover { background: linear-gradient(180deg, #8fadff, #6b92ff); border-color: var(--accent); color: #fff; }
.btn-primary:disabled { background: var(--accent); }

.btn-danger { color: var(--red); }
.btn-danger:hover { background: rgba(242, 99, 95, .12); border-color: rgba(242, 99, 95, .4); color: #ff8b87; }

.btn-sm { min-height: 30px; padding: 4px 10px; font-size: 12.5px; }
.btn-ghost { background: transparent; border-color: transparent; }
.btn-ghost:hover { background: var(--panel-2); border-color: var(--border); }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--tap); height: var(--tap);
  padding: 0; flex: none;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: transparent; color: var(--text-dim);
  cursor: pointer;
}
.icon-btn:hover { background: var(--panel-2); border-color: var(--border); color: var(--text); }

/* ------------------------------- inputs --------------------------------- */

input, select, textarea {
  font: inherit; width: 100%;
  min-height: 34px;
  padding: 7px 10px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--field);
  color: var(--text);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
input::placeholder, textarea::placeholder { color: var(--faint); }
input:hover, select:hover, textarea:hover { border-color: var(--border-strong); }
input:focus, select:focus, textarea:focus,
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input[type="checkbox"], input[type="radio"] {
  width: 17px; height: 17px; min-height: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
input[type="color"] { padding: 3px; height: 34px; cursor: pointer; }
/* The native date/time glyphs are black by default; invert them for a dark field. */
input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(.72); cursor: pointer; }
input[type="date"] { min-width: 0; }

textarea { min-height: 92px; font-family: var(--mono); font-size: 12.5px; line-height: 1.55; resize: vertical; }

select {
  appearance: none;
  padding-right: 30px;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 15px) calc(50% + 1px), calc(100% - 10px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

label.f { display: block; margin-bottom: 13px; }
label.f > span { display: block; font-size: 12px; font-weight: 550; color: var(--text-dim); margin-bottom: 6px; }
label.f > small { display: block; color: var(--faint); margin-top: 5px; font-size: 11.5px; line-height: 1.5; }

.inline { display: flex; align-items: center; gap: 8px; }
.inline input[type=checkbox] { width: 17px; }
label.inline { cursor: pointer; min-height: var(--tap); }

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

.table-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  /* Fading edges signal "there is more sideways" without an extra element. */
  background:
    linear-gradient(to right, var(--panel) 30%, transparent) left / 34px 100% no-repeat local,
    linear-gradient(to left, var(--panel) 30%, transparent) right / 34px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, .38), transparent) left / 14px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, .38), transparent) right / 14px 100% no-repeat scroll;
}

table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; }
th, td {
  text-align: left; padding: 10px 11px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
/**
 * `position: sticky` on a header only works against a scrollport that actually
 * scrolls in that axis. `.table-wrap` is `overflow-x: auto`, which makes it a
 * scroll container in both axes, so a page-level scroll never triggers it —
 * the header just scrolled away. `.table-wrap.tall` gives the container its own
 * vertical scroll, which is what the sticky header needs, and keeps the filters
 * and pager on screen while you move through rows.
 */
.table-wrap.tall {
  max-height: min(68dvh, 760px);
  overflow-y: auto;
}

thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--panel-2);
  color: var(--muted); font-weight: 620; font-size: 11px;
  text-transform: uppercase; letter-spacing: .06em;
  white-space: nowrap;
  border-bottom: 1px solid var(--border-strong);
}
tbody tr { transition: background var(--dur-1) var(--ease); }
tbody tr:hover { background: rgba(255, 255, 255, .026); }
tbody tr:last-child td { border-bottom: 0; }

/* Sort controls are real buttons so the keyboard reaches them. */
.th-sort {
  display: inline-flex; align-items: center; justify-content: flex-start; gap: 5px;
  min-height: 0; padding: 2px 4px; margin: -2px -4px;
  background: none; border: 0; color: inherit;
  font: inherit; text-transform: inherit; letter-spacing: inherit;
  cursor: pointer;
}
.th-sort:hover { background: none; color: var(--text); border: 0; transform: none; }
.th-sort .caret { font-size: 9px; opacity: .9; }
th[aria-sort] .th-sort { color: var(--text); }

td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.num { font-variant-numeric: tabular-nums; }

.empty { padding: 40px 24px; text-align: center; color: var(--muted); }
.empty strong { display: block; color: var(--text); font-size: 15px; margin-bottom: 6px; font-weight: 600; }

/* ------------------------------- pills ---------------------------------- */

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: var(--r-pill);
  font-size: 11.5px; font-weight: 620; line-height: 1.65;
  background: var(--panel-2); color: var(--muted);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.pill.green { background: rgba(62, 207, 142, .14); color: #64e0ac; border-color: rgba(62, 207, 142, .26); }
.pill.red { background: rgba(242, 99, 95, .14); color: #ff8b87; border-color: rgba(242, 99, 95, .26); }
.pill.amber { background: rgba(245, 181, 68, .14); color: #f7c76a; border-color: rgba(245, 181, 68, .26); }
.pill.blue { background: var(--accent-soft); color: #a8c0ff; border-color: var(--accent-line); }
.pill.purple { background: rgba(169, 120, 255, .14); color: #bd9bff; border-color: rgba(169, 120, 255, .26); }

/* ------------------------------ switches -------------------------------- */

/* An on/off control that saves on click. Overrides the generic button chrome:
   the track is the affordance, so the surrounding box would only add noise. */
.switch {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 28px; padding: 3px 4px;
  border: 1px solid transparent; border-radius: var(--r-pill);
  background: none; color: var(--muted);
  font-size: 12px; font-weight: 620;
}
.switch:hover { background: none; border-color: transparent; color: var(--text-dim); }
.switch:active { transform: none; }
/* The shared focus ring squares its corners off; keep the pill shape. */
.switch:focus-visible { border-radius: var(--r-pill); }
.switch.on { color: #64e0ac; }
.switch.on:hover { color: #7be9bb; }

.switch-track {
  position: relative; flex: none;
  width: 34px; height: 20px;
  border-radius: var(--r-pill);
  background: var(--panel-3);
  border: 1px solid var(--border-strong);
  transition: background var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.switch-knob {
  position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--faint);
  transition: transform var(--dur-2) var(--ease), background var(--dur-2) var(--ease);
}
.switch:hover .switch-track { border-color: var(--accent-line); }
.switch.on .switch-track { background: rgba(62, 207, 142, .22); border-color: rgba(62, 207, 142, .5); }
.switch.on .switch-knob { transform: translateX(14px); background: var(--green); }
.switch[aria-busy="true"] { cursor: progress; opacity: .72; }

@media (prefers-reduced-motion: reduce) {
  .switch-track, .switch-knob { transition: none; }
}

.flag {
  width: 20px; height: 15px; border-radius: 2px; object-fit: cover;
  vertical-align: -2px; background: var(--panel-2); flex: none;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .07);
}
.country { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }

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

.filter-bar { margin-bottom: 16px; }

.filters {
  display: flex; gap: 9px; flex-wrap: wrap; align-items: flex-end;
}
.filters label.f { margin-bottom: 0; }
.filters .f-sm { width: 142px; }
.filters .f-md { width: 190px; }

/* Collapsed by default on small screens; the toggle button lives in the view. */
.filter-toggle { display: none; }

/* ------------------------------ utilities ------------------------------- */

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

.mono { font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums; }
.muted { color: var(--muted); }
.small { font-size: 12px; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.sep { height: 1px; background: var(--border); margin: 16px 0; border: 0; }
.tnum { font-variant-numeric: tabular-nums; }

/* max-width is ignored on a td in auto table layout, so clamp an inner span. */
.truncate {
  display: inline-block; max-width: 220px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  vertical-align: bottom;
}
td.truncate > * { max-width: 100%; }

pre.code {
  background: #070b16; border: 1px solid var(--border); border-radius: var(--r);
  padding: 13px; overflow: auto; font-family: var(--mono); font-size: 12px;
  max-height: 340px; margin: 0; line-height: 1.6;
  tab-size: 2;
}

/* -------------------------------- bars ---------------------------------- */

.bar-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; align-items: center; margin-bottom: 9px; }
.bar-label { display: flex; align-items: center; gap: 7px; font-size: 13px; min-width: 0; }
.bar-label .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { height: 6px; background: var(--panel-3); border-radius: var(--r-pill); overflow: hidden; margin-top: 5px; }
.bar-fill {
  height: 100%; border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--accent), var(--accent-hi));
  transform-origin: left;
  animation: bar-in var(--dur-3) var(--ease-out) both;
}
@keyframes bar-in { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.spark { display: flex; align-items: flex-end; gap: 2px; height: 96px; }
/* The columns are buttons so touch and keyboard can read their values; strip
   the button chrome back to a bare bar. */
.spark .col {
  flex: 1 1 0; min-width: 2px; min-height: 0;
  padding: 0; margin: 0;
  border: 0;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  border-radius: 3px 3px 0 0;
  opacity: .8;
  cursor: pointer;
  transition: opacity var(--dur-1) var(--ease);
  animation: col-in var(--dur-3) var(--ease-out) both;
}
.spark .col:hover, .spark .col:focus-visible { opacity: 1; transform: none; background: linear-gradient(180deg, #a3bcff, var(--accent-hi)); }
.spark .col:active { transform: none; }
@keyframes col-in { from { transform: scaleY(0); transform-origin: bottom; } to { transform: scaleY(1); } }

/* The chart card is paired with a list card of unknown length, so let the plot
   grow into whatever height the row ends up being instead of leaving a void. */
.card.stretch { display: flex; flex-direction: column; }
.chart-flex { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.chart-flex .spark { flex: 1 1 auto; min-height: 96px; }

.chart-axis {
  display: flex; justify-content: space-between; gap: 10px;
  margin-top: 9px; font-variant-numeric: tabular-nums;
}
.chart-readout {
  margin-top: 8px; padding-top: 9px;
  border-top: 1px solid var(--border);
  font-size: 12.5px; min-height: 34px;
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
}
@media (max-width: 860px) {
  /* Fat-finger targets: a 2px bar is not tappable. */
  .spark { gap: 3px; height: 110px; }
  .spark .col { min-width: 6px; border-radius: 4px 4px 0 0; }
}

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

.modal-backdrop {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(4, 7, 14, .66);
  backdrop-filter: blur(3px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 44px 20px;
  overflow-y: auto; overscroll-behavior: contain;
  animation: fade-in var(--dur-2) var(--ease-out) both;
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.modal {
  background: var(--panel);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  width: 100%; max-width: 620px;
  box-shadow: var(--sh-4);
  animation: modal-in var(--dur-3) var(--ease-out) both;
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(14px) scale(.985); }
  to { opacity: 1; transform: none; }
}
.modal.wide { max-width: 940px; }

.modal-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 18px; border-bottom: 1px solid var(--border);
}
.modal-head h2 { margin: 0; font-size: 16.5px; font-weight: 620; min-width: 0; }
.modal-body { padding: 19px; }
.modal-foot {
  display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap;
  padding: 14px 18px; border-top: 1px solid var(--border);
  background: rgba(255, 255, 255, .012);
  border-radius: 0 0 var(--r-xl) var(--r-xl);
}
.close-x {
  width: 32px; height: 32px; min-height: 0; padding: 0; flex: none;
  background: none; border: 1px solid transparent; color: var(--muted);
  font-size: 19px; line-height: 1;
}
.close-x:hover { background: var(--panel-2); border-color: var(--border); color: var(--text); }

/* ---------------------------- views + columns --------------------------- */

.btn-view.is-active {
  border-color: var(--accent-line);
  background: var(--accent-soft);
  color: #cddaff;
}
.btn-view.is-active:hover { background: rgba(91, 134, 255, .2); border-color: var(--accent); }

/* One column, not two: the list order *is* the table order, so a grid that
   wrapped into a second column would be showing an arrangement the table does
   not have. */
.col-list {
  display: flex; flex-direction: column; gap: 2px;
  max-height: min(56vh, 460px); overflow-y: auto;
  padding-right: 4px;
}
.col-row {
  display: flex; align-items: center; gap: 4px;
  min-height: 38px; padding: 2px 6px 2px 2px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: var(--panel);
}
.col-row:hover { background: var(--panel-2); }
.col-row.is-dragging { opacity: .5; border-color: var(--accent-line); background: var(--panel-3); }

.col-grip {
  flex: none; width: 22px; text-align: center;
  color: var(--faint); font-size: 15px; line-height: 1;
  cursor: grab; user-select: none;
}
.col-row:active .col-grip { cursor: grabbing; }

.col-toggle {
  display: flex; align-items: center; gap: 10px;
  flex: 1; min-width: 0; min-height: 34px; padding: 4px 6px;
  border-radius: var(--r-sm); cursor: pointer;
}
.col-toggle input { width: 17px; flex: none; }
.col-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.col-note { flex: none; }
.col-row.is-locked .col-toggle { cursor: default; opacity: .72; }

.col-moves { display: flex; gap: 2px; flex: none; }
.col-moves button { min-width: 28px; padding: 3px 6px; line-height: 1; }
.col-moves button:disabled { opacity: .3; cursor: default; }

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

/* --------------------------- tables as cards ---------------------------- */

/**
 * Below 560px a 16-column table is a horizontal-scrolling maze. `.as-cards`
 * restacks each row into a labelled card: the `data-label` on every cell
 * becomes its own heading, so the same markup serves both layouts and nothing
 * is hidden from the phone.
 */
@media (max-width: 560px) {
  .table-wrap.as-cards { overflow: visible; background: none; }
  .table-wrap.as-cards table,
  .table-wrap.as-cards tbody,
  .table-wrap.as-cards tr,
  .table-wrap.as-cards td { display: block; width: auto; }

  .table-wrap.as-cards thead { display: none; }

  .table-wrap.as-cards tr {
    position: relative;
    margin: 0 10px 10px;
    padding: 6px 4px 4px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--panel-2);
  }
  .table-wrap.as-cards tr:first-child { margin-top: 10px; }
  .table-wrap.as-cards tbody tr:hover { background: var(--panel-2); }

  .table-wrap.as-cards td {
    display: grid;
    grid-template-columns: minmax(88px, 34%) minmax(0, 1fr);
    gap: 12px; align-items: center;
    /* Without this a pill or a badge stretches to fill the value column. */
    justify-items: start;
    padding: 7px 10px;
    border-bottom: 1px solid var(--border);
    text-align: left;
  }
  .table-wrap.as-cards td:last-child { border-bottom: 0; }
  .table-wrap.as-cards td::before {
    content: attr(data-label);
    color: var(--muted); font-size: 11px; font-weight: 620;
    text-transform: uppercase; letter-spacing: .05em;
  }
  /* Cells with nothing to label (the row checkbox) should not reserve a column. */
  .table-wrap.as-cards td:not([data-label]) { display: block; }
  .table-wrap.as-cards td.num { text-align: left; }
  .table-wrap.as-cards td .truncate { max-width: 100%; }

  /* The select box rides in the corner instead of taking a whole row. */
  .table-wrap.as-cards td.cell-select {
    position: absolute; top: 6px; right: 8px;
    padding: 0; border: 0; width: auto;
  }
  .table-wrap.as-cards td[data-label="Lead ID"] { padding-right: 44px; }
  .table-wrap.as-cards td.cell-actions { text-align: left; }

  .table-wrap.as-cards .row-actions { justify-content: flex-start; flex-wrap: wrap; }
  .table-wrap.as-cards .row-actions > button { flex: 1 1 auto; }

  /* The nested scroller has nothing to scroll once rows are stacked. */
  .table-wrap.as-cards.tall { max-height: none; overflow-y: visible; }
}

/* ----------------------------- action menu ------------------------------ */

.menu-trigger { letter-spacing: 1px; padding-left: 9px; padding-right: 9px; }

.menu-panel {
  position: fixed; z-index: 90;
  min-width: 176px; padding: 5px;
  background: var(--panel-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  box-shadow: var(--sh-3);
  display: flex; flex-direction: column; gap: 1px;
  animation: menu-in var(--dur-2) var(--ease-out) both;
}
@keyframes menu-in {
  from { opacity: 0; transform: translateY(-4px) scale(.98); }
  to { opacity: 1; transform: none; }
}
.menu-trigger.has-text { letter-spacing: normal; gap: 6px; padding-left: 11px; padding-right: 9px; }
.menu-trigger .caret { font-size: 9px; opacity: .7; }
.menu-trigger-text { overflow: hidden; text-overflow: ellipsis; max-width: 15ch; }

.menu-item {
  display: grid;
  grid-template-columns: 14px minmax(0, 1fr) auto;
  gap: 8px; align-items: center;
  justify-content: flex-start; text-align: left;
  width: 100%; min-height: 36px;
  padding: 7px 11px;
  background: none; border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-weight: 500; color: var(--text-dim); white-space: nowrap;
}
.menu-item:hover { background: var(--panel-3); border-color: transparent; color: var(--text); transform: none; }
.menu-item:active { transform: none; }
.menu-item.danger { color: var(--red); }
.menu-item.danger:hover { background: rgba(242, 99, 95, .12); color: #ff8b87; }
.menu-item.is-checked { color: var(--text); }
.menu-check { color: var(--accent-hi); font-size: 11px; text-align: center; }
.menu-label { overflow: hidden; text-overflow: ellipsis; }
.menu-hint { color: var(--faint); font-size: 11.5px; font-weight: 500; }

.menu-sep { height: 1px; margin: 5px 4px; background: var(--border); }
.menu-heading {
  padding: 7px 11px 4px; color: var(--faint);
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
}

.row-actions { display: flex; gap: 6px; justify-content: flex-end; align-items: center; flex-wrap: nowrap; }

/* --------------------------------- logs --------------------------------- */

.log-list {
  display: flex; flex-direction: column; gap: 8px;
  max-height: 460px; overflow-y: auto; padding-right: 4px;
  overscroll-behavior: contain;
}
.log-entry {
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--panel-2); padding: 11px 13px;
  border-left: 3px solid var(--border-strong);
}
.log-entry.is-error { border-left-color: var(--red); }
.log-entry-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.log-msg { flex: 1; min-width: 160px; font-size: 13px; word-break: break-word; }

.log-details { margin-top: 10px; }
.log-details > summary {
  cursor: pointer; color: var(--muted); font-size: 12px;
  padding: 4px 0; user-select: none; list-style: none;
  min-height: 26px; display: flex; align-items: center; gap: 4px;
}
.log-details > summary::-webkit-details-marker { display: none; }
.log-details > summary::before { content: '▸'; transition: transform var(--dur-1) var(--ease); }
.log-details[open] > summary::before { transform: rotate(90deg); }
.log-details > summary:hover { color: var(--text); }

.log-block { margin-top: 10px; }
.log-block-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin-bottom: 6px;
}

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

#toasts {
  position: fixed; right: 18px; bottom: 18px; z-index: 100;
  display: flex; flex-direction: column; gap: 9px;
  max-width: min(400px, calc(100vw - 36px));
  pointer-events: none;
}
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 12px 11px 14px; border-radius: var(--r);
  font-size: 13px; line-height: 1.5;
  background: var(--panel-2); border: 1px solid var(--border-strong);
  box-shadow: var(--sh-3);
  pointer-events: auto;
  animation: toast-in var(--dur-3) var(--ease-out) both;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(10px) scale(.97); }
  to { opacity: 1; transform: none; }
}
.toast.out { opacity: 0; transform: translateY(6px); transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease); }
.toast.ok { border-left: 3px solid var(--green); }
.toast.err { border-left: 3px solid var(--red); }
.toast.info { border-left: 3px solid var(--accent); }
.toast-msg { flex: 1; min-width: 0; }
.toast-x {
  width: 22px; height: 22px; min-height: 0; padding: 0; flex: none;
  background: none; border: 0; color: var(--faint); font-size: 15px; line-height: 1;
}
.toast-x:hover { background: none; border: 0; color: var(--text); }

/* ------------------------------- banners -------------------------------- */

.banner {
  padding: 11px 14px; border-radius: var(--r); font-size: 13px; margin-bottom: 14px;
  background: rgba(245, 181, 68, .09);
  border: 1px solid rgba(245, 181, 68, .28);
  color: #f0c877;
  line-height: 1.55;
}
.banner.info { background: var(--accent-soft); border-color: var(--accent-line); color: #b3c7ff; }

.bulk-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.bulk-count { font-weight: 620; }
@media (max-width: 860px) {
  .bulk-bar {
    position: sticky; bottom: 10px; z-index: 20;
    background: var(--panel-3); border-color: var(--border-strong);
    box-shadow: var(--sh-3);
  }
  .bulk-bar > button { flex: 1 1 auto; }
  .bulk-count { flex: 1 1 100%; }
}
.banner strong { color: inherit; }

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

.tabs {
  display: flex; gap: 2px; border-bottom: 1px solid var(--border);
  margin-bottom: 18px; flex-wrap: wrap;
}
.tab {
  padding: 9px 14px; min-height: var(--tap);
  display: inline-flex; align-items: center;
  background: none; border: 0; border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--muted); font: inherit; font-weight: 550;
  cursor: pointer; border-radius: var(--r-sm) var(--r-sm) 0 0;
  transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.tab:hover { color: var(--text); background: var(--panel-2); border-bottom-color: var(--border-strong); }
.tab.active { color: var(--text); border-bottom-color: var(--accent-hi); background: none; }

/* ------------------------------ skeletons ------------------------------- */

.loading { padding: 40px; text-align: center; color: var(--muted); }

.skeleton {
  border-radius: var(--r-sm);
  background: linear-gradient(100deg, var(--panel-2) 22%, var(--panel-3) 42%, var(--panel-2) 62%);
  background-size: 260% 100%;
  animation: shimmer 1.5s linear infinite;
}
@keyframes shimmer { from { background-position: 160% 0; } to { background-position: -60% 0; } }

.sk-stat {
  height: 92px; border-radius: var(--r-lg);
  border: 1px solid var(--border); background: var(--panel);
  padding: 15px 17px;
}
.sk-line { height: 10px; border-radius: var(--r-pill); }
.sk-card { border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--panel); padding: 17px; }
.sk-row { height: 40px; border-radius: 0; margin: 0; }
.sk-table { border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; }
.sk-table > * + * { border-top: 1px solid var(--border); }

/* --------------------------- view transition ---------------------------- */

.view-enter { animation: view-in var(--dur-3) var(--ease-out) both; }
@keyframes view-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

/* ========================================================================= */
/*                              RESPONSIVE                                   */
/* ========================================================================= */

/* Wide desktop: give the shell more room to breathe. */
@media (min-width: 1600px) {
  :root { --sidebar-w: 250px; }
  .main { padding: 40px 42px 80px; }
}

/* Laptop / small desktop. */
@media (max-width: 1280px) {
  .grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Tablet: the 3-column dashboard rows are too cramped before this point. */
@media (max-width: 1100px) {
  :root { --sidebar-w: 200px; }
  .grid.cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .main { padding: 30px 20px 64px; }
}

/* ------------------------- phone / small tablet -------------------------- */

@media (max-width: 860px) {
  :root { --tap: 44px; }

  #app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "topbar" "main";
  }

  /* A real app bar, not a wrapped pile of links. */
  .topbar {
    grid-area: topbar;
    display: flex; align-items: center; gap: 10px;
    position: sticky; top: 0; z-index: 45;
    height: var(--topbar-h);
    padding: 0 10px 0 6px;
    background: color-mix(in srgb, var(--panel) 88%, transparent);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border);
  }
  .topbar .brand { padding: 0; font-size: 15.5px; }
  .topbar-spacer { flex: 1; }
  .badge-slot { display: flex; align-items: center; gap: 8px; flex: none; }
  /* Both badges have moved into the app bar; the fixed row would only be an
     invisible box in the corner. */
  .corner-badges { display: none; }

  /* Sidebar becomes an off-canvas drawer. */
  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 60;
    width: min(280px, 84vw); height: 100dvh;
    grid-area: auto;
    padding: 18px 14px;
    border-right: 1px solid var(--border-strong);
    box-shadow: var(--sh-4);
    transform: translateX(-102%);
    visibility: hidden;
    transition: transform var(--dur-3) var(--ease-out), visibility var(--dur-3);
  }
  #app.nav-open .sidebar { transform: none; visibility: visible; }
  .nav-item { min-height: 46px; font-size: 15px; }
  .nav-item.active::before { left: -7px; }

  .nav-scrim {
    display: block;
    position: fixed; inset: 0; z-index: 55;
    background: rgba(4, 7, 14, .6);
    backdrop-filter: blur(2px);
    opacity: 0; visibility: hidden;
    transition: opacity var(--dur-2) var(--ease), visibility var(--dur-2);
    border: 0;
  }
  #app.nav-open .nav-scrim { opacity: 1; visibility: visible; }

  .main { padding: 20px 15px 72px; }

  /* The badge stops floating and rides in the app bar instead. */
  .version-badge {
    position: static; margin: 0;
    backdrop-filter: none;
    background: var(--panel-2);
    font-size: 11px;
  }
  .version-badge .hash { display: none; }

  .page-head { align-items: flex-start; gap: 12px; margin-bottom: 18px; }
  .page-head h1 { font-size: 21px; }
  .head-actions { width: 100%; }
  .head-actions > * { flex: 1 1 auto; min-height: var(--tap); justify-content: center; }

  .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: minmax(0, 1fr); }

  .card { padding: 15px; border-radius: var(--r); }
  .stat { padding: 13px 15px; }
  .stat .value { font-size: 24px; }

  /* 16px is the threshold below which iOS Safari zooms the viewport on focus. */
  input, select, textarea { font-size: 16px; min-height: var(--tap); }
  textarea { font-size: 13px; }
  button, .btn { min-height: 38px; }
  .btn-sm { min-height: 34px; }
  label.f > span { font-size: 12.5px; }

  /* Filters collapse behind a toggle so data is above the fold. */
  .filter-toggle {
    display: inline-flex; width: 100%; justify-content: space-between;
    min-height: var(--tap); margin-bottom: 10px;
  }
  .filter-toggle .count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 20px; padding: 0 6px;
    border-radius: var(--r-pill);
    background: var(--accent); color: #fff; font-size: 11px; font-weight: 700;
  }
  .filters { display: none; }
  .filter-bar.open .filters {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 10px; align-items: end;
    padding: 14px; margin-bottom: 4px;
    border: 1px solid var(--border); border-radius: var(--r-lg);
    background: var(--panel);
  }
  .filters .f-sm, .filters .f-md { width: 100%; }
  .filters label.f { width: auto; }
  .filters > label.f:first-child { grid-column: 1 / -1; width: auto !important; }
  .filters > button { grid-column: 1 / -1; min-height: var(--tap); }

  /* Modals become bottom sheets. */
  .modal-backdrop { padding: 0; align-items: flex-end; }
  .modal, .modal.wide {
    max-width: none; max-height: 94dvh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    border-bottom: 0;
    display: flex; flex-direction: column;
    animation: sheet-in var(--dur-3) var(--ease-out) both;
  }
  @keyframes sheet-in {
    from { transform: translateY(28px); opacity: 0; }
    to { transform: none; opacity: 1; }
  }
  .modal-head {
    position: sticky; top: 0; z-index: 2;
    background: var(--panel); border-radius: var(--r-xl) var(--r-xl) 0 0;
  }
  /* A grab handle reads as a sheet rather than a clipped dialog. */
  .modal-head::before {
    content: ""; position: absolute; top: 6px; left: 50%;
    width: 36px; height: 4px; margin-left: -18px;
    border-radius: var(--r-pill); background: var(--border-strong);
  }
  .modal-head { padding-top: 18px; }
  .modal-body { overflow-y: auto; -webkit-overflow-scrolling: touch; flex: 1; padding: 16px; }
  .modal-foot { border-radius: 0; padding-bottom: max(14px, env(safe-area-inset-bottom)); }
  .modal-foot > button, .modal-foot > .btn { flex: 1 1 auto; min-height: var(--tap); }

  .grid.cols-2 > *, .grid.cols-3 > * { min-width: 0; }

  #toasts { right: 12px; left: 12px; bottom: max(12px, env(safe-area-inset-bottom)); max-width: none; }
  .toast { width: 100%; }

  .log-list { max-height: none; }
  .truncate { max-width: 190px; }

  table { font-size: 12.5px; }
  th, td { padding: 11px 10px; }
  /* A nested vertical scroller fights momentum scrolling on touch. */
  .table-wrap.tall { max-height: none; overflow-y: visible; }

  /* The sort control is 21px of text inside a 43px header cell. Grow its hit
     area to fill the cell without moving the label. */
  .th-sort { padding: 11px 5px; margin: -11px -5px; }
  .version-badge { min-height: 32px; }
}

@media (max-width: 480px) {
  .main { padding: 16px 12px 72px; }
  .page-head h1 { font-size: 19px; }
  /* Share a row rather than stacking: three full-width buttons pushed the data
     itself below the fold. */
  .head-actions > * { flex: 1 1 auto; min-width: 0; font-size: 13px; padding-left: 8px; padding-right: 8px; }
  .filter-bar.open .filters { grid-template-columns: minmax(0, 1fr); }
  .truncate { max-width: 150px; }
}

/* Short landscape phones: the sheet should not eat the whole screen. */
@media (max-height: 480px) and (max-width: 860px) {
  .modal, .modal.wide { max-height: 100dvh; border-radius: 0; }
  .modal-head::before { display: none; }
}

/* --------------------------- user preferences ---------------------------- */

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

@media (prefers-contrast: more) {
  :root {
    --border: #3c4a72;
    --border-strong: #5a6a99;
    --muted: #a9b5d4;
    --faint: #8592b8;
  }
}

/* ---------------------------------- print -------------------------------- */

@media print {
  :root { --bg: #fff; --panel: #fff; --text: #000; --muted: #444; }
  body { background: #fff; color: #000; }
  .sidebar, .topbar, .version-badge, #toasts, .head-actions, .filters, .filter-toggle { display: none !important; }
  #app { grid-template-columns: 1fr; }
  .main { padding: 0; }
  .card, .stat { border: 1px solid #ccc; box-shadow: none; break-inside: avoid; }
  a { color: #000; text-decoration: underline; }
}
