/* ============================================================================
   V2PRO — components
   Depends on tokens.css + base.css. Elevation is always taken from --e1..--e4
   so depth stays consistent; glass only on surfaces that float over content.
   ========================================================================== */

/* ============================================================================
   Buttons
   ========================================================================== */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 36px;
  padding: 0 var(--sp-4);
  border-radius: var(--r-1);
  font-size: var(--fs-13);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  user-select: none;
  background: var(--sf-2);
  color: var(--ink-1);
  border: 1px solid var(--line-2);
  box-shadow: var(--rim), var(--shade), 0 1px 2px rgba(2, 4, 10, .35);
  transition:
    background-color var(--dur-2) var(--ease-out),
    border-color var(--dur-2) var(--ease-out),
    box-shadow var(--dur-2) var(--ease-out),
    color var(--dur-2) var(--ease-out),
    transform var(--dur-1) var(--ease-out);
}

.btn svg {
  width: 15px;
  height: 15px;
  stroke-width: 1.9;
}

.btn:hover:not(:disabled) {
  background: var(--sf-3);
  border-color: var(--line-3);
}

/* Press-in: the shadow inverts and the button moves toward the surface. */
.btn:active:not(:disabled) {
  transform: translateY(1px);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .3);
}

/* --- primary --- */
.btn--primary {
  background: linear-gradient(180deg, var(--brand-2), var(--brand));
  color: var(--brand-ink);
  border-color: transparent;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .3),
    inset 0 -1px 0 rgba(0, 0, 0, .18),
    0 1px 2px rgba(2, 4, 10, .4),
    0 6px 18px -6px rgba(34, 211, 238, .5);
}

.btn--primary:hover:not(:disabled) {
  background: linear-gradient(180deg, var(--brand-hi), var(--brand-2));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .38),
    inset 0 -1px 0 rgba(0, 0, 0, .18),
    0 2px 4px rgba(2, 4, 10, .4),
    0 10px 26px -8px rgba(34, 211, 238, .6);
}

[data-theme='light'] .btn--primary {
  background: linear-gradient(180deg, var(--brand-2), var(--brand));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .25),
    0 1px 2px rgba(20, 35, 75, .15),
    0 6px 18px -6px rgba(11, 124, 146, .4);
}

/* --- danger --- */
.btn--danger {
  background: var(--crit-soft);
  color: var(--crit);
  border-color: color-mix(in srgb, var(--crit) 34%, transparent);
  box-shadow: var(--rim), 0 1px 2px rgba(2, 4, 10, .3);
}

.btn--danger:hover:not(:disabled) {
  background: color-mix(in srgb, var(--crit) 20%, transparent);
  border-color: var(--crit);
}

/* --- ghost / quiet --- */
.btn--ghost {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  color: var(--ink-2);
  font-weight: 500;
}

.btn--ghost:hover:not(:disabled) {
  background: var(--sf-2);
  color: var(--ink-1);
  border-color: transparent;
}

.btn--ghost:active:not(:disabled) {
  box-shadow: none;
}

/* --- sizes --- */
.btn--sm {
  min-height: 30px;
  padding: 0 var(--sp-3);
  font-size: var(--fs-12);
}

.btn--sm svg {
  width: 13px;
  height: 13px;
}

.btn--lg {
  min-height: 44px;
  padding: 0 var(--sp-5);
  font-size: var(--fs-14);
}

/* --- icon-only ---
   Always paired with aria-label; never the only affordance for a row action. */
.btn--icon {
  width: 34px;
  min-height: 34px;
  padding: 0;
}

.btn--icon.btn--sm {
  width: 30px;
  min-height: 30px;
}

.btn--block {
  width: 100%;
}

/* --- pending ---
   The label stays laid out (so width does not jump) but is hidden while the
   spinner runs. aria-busy is what screen readers hear. */
.btn[aria-busy='true'] {
  color: transparent;
  pointer-events: none;
}

.btn[aria-busy='true'] > * {
  opacity: 0;
}

.btn[aria-busy='true']::after {
  content: '';
  position: absolute;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  color: var(--ink-1);
  animation: spin 700ms linear infinite;
}

.btn--primary[aria-busy='true']::after {
  color: var(--brand-ink);
}

@keyframes spin {
  to { rotate: 360deg; }
}

@media (max-width: 767px) {
  .btn {
    min-height: 42px;
  }

  .btn--sm {
    min-height: 36px;
  }

  .btn--icon {
    width: 42px;
    min-height: 42px;
  }
}

/* --- segmented control (density, filters, tabs-as-buttons) --- */
.seg {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  border-radius: var(--r-1);
  background: var(--bg-1);
  border: 1px solid var(--line-2);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .2);
}

.seg > button {
  min-height: 26px;
  padding: 0 var(--sp-3);
  border-radius: 4px;
  font-size: var(--fs-12);
  font-weight: 500;
  color: var(--ink-2);
  transition: background-color var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out);
}

.seg > button:hover {
  color: var(--ink-1);
}

.seg > button[aria-pressed='true'] {
  background: var(--sf-3);
  color: var(--ink-1);
  font-weight: 600;
  box-shadow: var(--rim), 0 1px 2px rgba(0, 0, 0, .3);
}

/* ============================================================================
   Cards — the base solid object
   ========================================================================== */
.card {
  position: relative;
  border-radius: var(--r-2);
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  box-shadow: var(--e1);
  /* No transform transition: nothing moves a card any more. */
  transition:
    box-shadow var(--dur-2) var(--ease-out),
    border-color var(--dur-2) var(--ease-out);
}

.card__head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-4) 0;
}

.card__head h3 {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-14);
  font-weight: 600;
}

.card__body {
  padding: var(--sp-4);
}

.card__foot {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--line-1);
  background: var(--bg-1);
  border-radius: 0 0 var(--r-2) var(--r-2);
}

/* Cards do not move. An earlier revision tracked the cursor with a radial
   highlight and lifted the card on hover; it read as a gimmick on a dense
   operations screen, where the eye is trying to compare figures across cards
   rather than admire them. Hover now changes only border and shadow — enough
   to signal "this is clickable", nothing that shifts layout or chases the
   pointer. */
.card--lift:hover {
  box-shadow: var(--e2);
  border-color: var(--line-2);
}

.card--flush .card__body {
  padding: 0;
}

/* ============================================================================
   Stat card
   ========================================================================== */
.stat {
  position: relative;
  overflow: hidden;
  padding: var(--sp-4);
  border-radius: var(--r-2);
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  box-shadow: var(--e1);
}

/* Stat cards are read, not clicked — they get no hover treatment at all.
   Depth comes from the static elevation shadow. */

/* Tone bar along the top edge, catching the light source. */
.stat::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--stat-tone, var(--brand)), transparent);
  opacity: .8;
}

.stat[data-tone='ok'] { --stat-tone: var(--ok); }
.stat[data-tone='warn'] { --stat-tone: var(--warn); }
.stat[data-tone='neg'] { --stat-tone: var(--neg); }
.stat[data-tone='crit'] { --stat-tone: var(--crit); }
.stat[data-tone='violet'] { --stat-tone: var(--violet); }

.stat__top {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.stat__label {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-12);
  font-weight: 500;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stat__icon {
  flex: none;
  width: 28px;
  height: 28px;
  display: grid;
  place-content: center;
  border-radius: var(--r-1);
  background: color-mix(in srgb, var(--stat-tone, var(--brand)) 14%, transparent);
  color: var(--stat-tone, var(--brand));
  box-shadow: var(--rim);
}

.stat__icon svg {
  width: 15px;
  height: 15px;
}

.stat__value {
  font-size: var(--fs-26);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -.3px;
}

.stat__value small {
  font-size: var(--fs-13);
  font-weight: 500;
  color: var(--ink-3);
  margin-inline-start: 4px;
}

.stat__meta {
  margin-top: var(--sp-2);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-12);
  color: var(--ink-3);
}

.stat__delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-weight: 600;
}

.stat__delta[data-dir='up'] { color: var(--ok); }
.stat__delta[data-dir='down'] { color: var(--crit); }
.stat__delta svg { width: 12px; height: 12px; }

/* ============================================================================
   Badges & status
   ========================================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 21px;
  padding: 0 8px;
  border-radius: var(--r-pill);
  background: var(--sf-2);
  border: 1px solid var(--line-2);
  color: var(--ink-2);
  font-size: var(--fs-11);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}

.badge svg {
  width: 11px;
  height: 11px;
}

.badge[data-tone='ok'] {
  background: var(--ok-soft);
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 30%, transparent);
}

.badge[data-tone='warn'] {
  background: var(--warn-soft);
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 30%, transparent);
}

.badge[data-tone='neg'] {
  background: var(--neg-soft);
  color: var(--neg);
  border-color: color-mix(in srgb, var(--neg) 30%, transparent);
}

.badge[data-tone='crit'] {
  background: var(--crit-soft);
  color: var(--crit);
  border-color: color-mix(in srgb, var(--crit) 30%, transparent);
}

.badge[data-tone='brand'] {
  background: var(--brand-soft);
  color: var(--brand);
  border-color: var(--brand-line);
}

.badge[data-tone='violet'] {
  background: var(--violet-soft);
  color: var(--violet);
  border-color: color-mix(in srgb, var(--violet) 30%, transparent);
}

/* Status dot: shape + colour, so colour is never the only signal.
   Paired with a text label in every use site. */
.dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink-3);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--ink-3) 22%, transparent);
}

.dot[data-tone='ok'] {
  background: var(--ok);
  box-shadow: 0 0 0 2px var(--ok-soft), 0 0 8px var(--ok);
}

.dot[data-tone='warn'] {
  background: var(--warn);
  box-shadow: 0 0 0 2px var(--warn-soft);
}

.dot[data-tone='crit'] {
  background: var(--crit);
  box-shadow: 0 0 0 2px var(--crit-soft);
  animation: pulse 1.9s var(--ease-out) infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}

/* ============================================================================
   Meters — pool usage, CPU/RAM. Threshold colour + a numeric label always.
   ========================================================================== */
.meter {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.meter__top {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  font-size: var(--fs-12);
}

.meter__label {
  flex: 1;
  min-width: 0;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.meter__value {
  font-weight: 700;
  color: var(--ink-1);
}

.meter__track {
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--bg-1);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .3);
  overflow: hidden;
}

.meter__fill {
  height: 100%;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--meter-tone, var(--brand-3)), var(--meter-tone2, var(--brand)));
  box-shadow: 0 0 10px -2px var(--meter-tone2, var(--brand));
  transition: width var(--dur-4) var(--ease-out), background-color var(--dur-3) var(--ease-out);
}

.meter[data-tone='ok'] { --meter-tone: var(--ok-deep); --meter-tone2: var(--ok); }
.meter[data-tone='warn'] { --meter-tone: var(--warn-deep); --meter-tone2: var(--warn); }
.meter[data-tone='neg'] { --meter-tone: var(--neg-deep); --meter-tone2: var(--neg); }
.meter[data-tone='crit'] { --meter-tone: var(--crit-deep); --meter-tone2: var(--crit); }

.meter--lg .meter__track {
  height: 10px;
}

/* ============================================================================
   Tables
   No backdrop-filter here — blur behind dense text is unreadable.
   ========================================================================== */
.tablewrap {
  border-radius: var(--r-2);
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  box-shadow: var(--e1);
  /* `clip`, not `hidden`. Both clip the rounded corners, but `hidden` makes
     this a scroll container, and the sticky `<th>` below then resolves its
     `top: var(--topbar-h)` against *this* box instead of the page. Since the
     box never scrolls, the header simply sat 56px lower than the table it
     heads — a dead band above every table in the app, most obvious on the
     short dashboard cards where there is no toolbar to disguise it.
     `clip` is not a scroll container, so the offset goes back to meaning
     "stop under the topbar" while the page scrolls. */
  overflow: clip;
}

/* Toolbar above a table: search, filters, bulk actions, density. */
.toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  padding: var(--sp-3);
  border-bottom: 1px solid var(--line-1);
  background: linear-gradient(180deg, var(--sf-2), var(--sf-1));
}

.toolbar__search {
  position: relative;
  flex: 1;
  min-width: 180px;
  max-width: 320px;
}

.toolbar__search svg {
  position: absolute;
  inset-inline-start: 10px;
  top: 50%;
  translate: 0 -50%;
  width: 15px;
  height: 15px;
  color: var(--ink-3);
  pointer-events: none;
}

.toolbar__search input {
  padding-inline-start: 32px;
}

.toolbar select {
  width: auto;
  min-width: 128px;
  min-height: 34px;
  font-size: var(--fs-12);
}

/* Bulk bar replaces the toolbar contents when rows are selected. */
.bulkbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  padding: var(--sp-3);
  border-bottom: 1px solid var(--brand-line);
  background: var(--brand-soft);
  font-size: var(--fs-13);
}

.bulkbar b {
  color: var(--brand);
}

.table {
  width: 100%;
  font-size: var(--fs-13);
}

.table thead th {
  position: sticky;
  top: var(--topbar-h);
  z-index: 1;
  padding: 10px var(--sp-3);
  background: var(--sf-2);
  border-bottom: 1px solid var(--line-2);
  box-shadow: var(--rim);
  color: var(--ink-2);
  font-size: var(--fs-12);
  font-weight: 600;
  white-space: nowrap;
}

.table tbody td {
  padding: 11px var(--sp-3);
  border-bottom: 1px solid var(--line-1);
  vertical-align: middle;
}

.table tbody tr:last-child td {
  border-bottom: 0;
}

.table tbody tr {
  transition: background-color var(--dur-1) var(--ease-out);
}

.table tbody tr:hover {
  background: var(--sf-2);
}

.table tbody tr[aria-selected='true'] {
  background: var(--brand-soft);
}

/* Rows changed by a live poll flash once, so an in-place patch is visible
   without the view repainting. */
.table tbody tr[data-flash] {
  animation: rowflash var(--dur-4) var(--ease-out);
}

@keyframes rowflash {
  from { background: var(--brand-soft); }
  to { background: transparent; }
}

/* Rows are focused programmatically by the j/k cursor, where :focus-visible is
   unreliable — so the marker is an explicit attribute, and the browser's own
   focus ring is suppressed to avoid two competing outlines. */
.table tbody tr:focus {
  outline: none;
}

.table tbody tr[data-cursor] {
  background: var(--sf-2);
  outline: 1px solid var(--brand-line);
  outline-offset: -1px;
}

.table--dense tbody td {
  padding: 6px var(--sp-3);
}

.table--dense thead th {
  padding: 7px var(--sp-3);
}

/* Sortable header: the whole cell is the button, aria-sort on the <th>. */
.th-sort {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: inherit;
  font: inherit;
  border-radius: 4px;
  transition: color var(--dur-1) var(--ease-out);
}

.th-sort:hover {
  color: var(--ink-1);
}

.th-sort svg {
  width: 12px;
  height: 12px;
  opacity: .35;
  transition: opacity var(--dur-1) var(--ease-out), rotate var(--dur-2) var(--ease-out);
}

[aria-sort='ascending'] .th-sort,
[aria-sort='descending'] .th-sort {
  color: var(--brand);
}

[aria-sort='ascending'] .th-sort svg,
[aria-sort='descending'] .th-sort svg {
  opacity: 1;
}

[aria-sort='descending'] .th-sort svg {
  rotate: 180deg;
}

.td-num {
  text-align: left;
}

.td-actions {
  width: 1%;
  white-space: nowrap;
}

.td-actions .row {
  flex-wrap: nowrap;
  justify-content: flex-end;
  gap: var(--sp-1);
}

/* Primary cell: name + secondary line. */
.cell-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.cell-main b {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cell-main span {
  font-size: var(--fs-11);
  color: var(--ink-3);
}

/* Pagination */
.pager {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border-top: 1px solid var(--line-1);
  background: var(--bg-1);
  font-size: var(--fs-12);
  color: var(--ink-2);
}

/* --- card layout below 920px ---
   Instead of horizontal scroll, each row becomes a stacked card. Headers move
   into ::before from data-label, so no duplicate markup is needed. */
@media (max-width: 920px) {
  .table thead {
    display: none;
  }

  .table,
  .table tbody,
  .table tr,
  .table td {
    display: block;
    width: auto;
  }

  .table tbody tr {
    padding: var(--sp-3);
    margin: var(--sp-3);
    border-radius: var(--r-2);
    background: var(--sf-2);
    border: 1px solid var(--line-1);
    box-shadow: var(--e1);
  }

  .table tbody tr:hover {
    background: var(--sf-2);
  }

  .table tbody td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: 5px 0;
    border-bottom: 0;
    text-align: start;
  }

  .table tbody td::before {
    content: attr(data-label);
    flex: none;
    font-size: var(--fs-11);
    font-weight: 600;
    color: var(--ink-3);
  }

  /* The first cell is the card title — full width, no label. */
  .table tbody td:first-child {
    padding-bottom: var(--sp-2);
    margin-bottom: var(--sp-2);
    border-bottom: 1px solid var(--line-1);
  }

  .table tbody td:first-child::before {
    display: none;
  }

  .td-actions {
    width: auto;
    padding-top: var(--sp-3) !important;
    margin-top: var(--sp-2);
    border-top: 1px solid var(--line-1);
  }

  .td-actions::before {
    display: none;
  }

  .td-actions .row {
    flex: 1;
    justify-content: stretch;
  }

  .td-actions .btn:not(.btn--icon) {
    flex: 1;
  }

  .toolbar__search {
    max-width: none;
  }
}

/* ============================================================================
   Overflow menu (the "…" beside a row's primary action)
   ========================================================================== */
.menu {
  position: relative;
}

.menu__panel {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + 5px);
  z-index: var(--z-dropdown);
  min-width: 184px;
  padding: 5px;
  border-radius: var(--r-2);
  background: var(--sf-3);
  border: 1px solid var(--line-2);
  box-shadow: var(--e3);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px) scale(.97);
  transform-origin: top right;
  transition:
    opacity var(--dur-2) var(--ease-out),
    transform var(--dur-2) var(--ease-out),
    visibility var(--dur-2);
}

.menu[data-open='1'] .menu__panel {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* Row menus inside a table need to escape `.tablewrap`'s `overflow: hidden`,
   which clips its own rounded corners and used to swallow the menu of every
   row near the bottom — the actions were rendered, focusable, and completely
   invisible. Fixed positioning is not clipped by an ancestor, but it also no
   longer follows the trigger, so `place()` in ui/table.js supplies the
   coordinates and refreshes them while the menu is open.

   Scoped to a modifier because the topbar account menu is not inside a
   clipping context and is positioned purely by CSS. */
.menu__panel--float {
  position: fixed;
  inset-inline-end: auto;
  top: auto;
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: 32px;
  padding: 0 var(--sp-3);
  border-radius: var(--r-1);
  color: var(--ink-2);
  font-size: var(--fs-13);
  text-align: start;
  transition: background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}

.menu__item svg {
  width: 14px;
  height: 14px;
}

.menu__item:hover,
.menu__item:focus-visible {
  background: var(--sf-4);
  color: var(--ink-1);
}

.menu__item[data-tone='crit'] {
  color: var(--crit);
}

.menu__item[data-tone='crit']:hover {
  background: var(--crit-soft);
}

.menu__sep {
  height: 1px;
  margin: 5px 0;
  background: var(--line-1);
}

/* ============================================================================
   Modal — for confirmations only. Anything with a form uses a drawer.
   ========================================================================== */
.scrim {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  place-items: center;
  padding: var(--sp-4);
  background: var(--scrim);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
}

.scrim[data-open='1'] {
  opacity: 1;
}

.modal {
  width: min(480px, 100%);
  max-height: min(86dvh, 720px);
  display: flex;
  flex-direction: column;
  border-radius: var(--r-3);
  background: var(--sf-2);
  border: 1px solid var(--line-2);
  box-shadow: var(--e4);
  /* Scales out from the trigger's rect; --ox/--oy are set by modal.js. */
  transform-origin: var(--ox, 50%) var(--oy, 50%);
  transform: scale(.94) translateY(10px);
  opacity: 0;
  transition:
    transform var(--dur-3) var(--ease-spring),
    opacity var(--dur-2) var(--ease-out);
}

.scrim[data-open='1'] .modal {
  transform: none;
  opacity: 1;
}

.modal--wide {
  width: min(720px, 100%);
}

.modal__head,
.drawer__head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border-bottom: 1px solid var(--line-1);
}

.modal__head h2,
.drawer__head h2 {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-16);
  font-weight: 700;
}

.modal__head p,
.drawer__head p {
  margin-top: 3px;
  font-size: var(--fs-12);
  color: var(--ink-3);
  font-weight: 400;
}

.modal__body,
.drawer__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-4);
}

.modal__foot,
.drawer__foot {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--line-1);
  background: var(--bg-1);
}

.modal__foot .spacer,
.drawer__foot .spacer {
  flex: 1;
}

.close {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-content: center;
  border-radius: var(--r-1);
  color: var(--ink-3);
  transition: background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}

.close:hover {
  background: var(--sf-4);
  color: var(--ink-1);
}

.close svg {
  width: 16px;
  height: 16px;
}

/* ============================================================================
   Drawer — centred dialog, replaces every stacked form modal

   It began as an edge-anchored side sheet. In an RTL document that put every
   form against the left edge of the screen while the content it related to sat
   on the right, so the eye had to cross the whole viewport to follow its own
   action. Centred, it opens where attention already is.

   Still called "drawer" because it is a distinct role from `.modal`: modals are
   confirmations, drawers carry forms and detail views and can swap their own
   body in place. Only the placement changed.
   ========================================================================== */
.drawer {
  /* A grid child of .scrim, which is already `place-items: center` — no
     positioning of its own, so it cannot drift off-axis. */
  position: relative;
  z-index: var(--z-drawer);
  width: min(560px, 100%);
  max-height: min(88dvh, 900px);
  display: flex;
  flex-direction: column;
  background: var(--sf-1);
  border: 1px solid var(--line-2);
  border-radius: var(--r-4);
  box-shadow: var(--e4);
  overflow: hidden;
  /* Scale from slightly small rather than sliding: a centred panel that flies
     in from an edge reads as two different motions at once.
     No perspective/rotateY — the 3D swing forced the panel onto its own
     composited layer, which rasterised text at the wrong scale and made every
     label in the form look soft. */
  opacity: 0;
  scale: .97;
  translate: 0 8px;
  transition:
    opacity var(--dur-3) var(--ease-out),
    scale var(--dur-3) var(--ease-out),
    translate var(--dur-3) var(--ease-out);
}

/* data-open lives on the .scrim container (set by overlay.js), not on the
   panel. The old `.drawer[data-open='1']` selector never matched, so the
   drawer never left its hidden position. */
.scrim[data-open='1'] .drawer {
  opacity: 1;
  scale: 1;
  translate: 0 0;
}

.drawer--wide {
  width: min(860px, 100%);
}

/* Below 600px a centred dialog wastes the top and bottom of a phone screen and
   puts the primary action out of thumb reach, so it becomes a bottom sheet. */
@media (max-width: 600px) {
  .scrim:has(.drawer) {
    place-items: end center;
    padding: 0;
  }

  .drawer,
  .drawer--wide {
    width: 100%;
    max-height: 94dvh;
    border-radius: var(--r-4) var(--r-4) 0 0;
    border-inline: 0;
    border-bottom: 0;
    scale: 1;
    translate: 0 100%;
  }

  .scrim[data-open='1'] .drawer {
    translate: 0 0;
  }

  /* Grab handle for the swipe-to-dismiss affordance. */
  .drawer__head::before {
    content: '';
    position: absolute;
    top: 7px;
    left: 50%;
    translate: -50% 0;
    width: 38px;
    height: 4px;
    border-radius: var(--r-pill);
    background: var(--line-3);
  }

  .drawer__head {
    position: relative;
    padding-top: var(--sp-5);
  }
}

/* ============================================================================
   Forms
   ========================================================================== */
.fields {
  display: grid;
  gap: var(--sp-4);
}

.fieldset {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border-radius: var(--r-2);
  background: var(--bg-1);
  border: 1px solid var(--line-1);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .02);
}

[data-theme='light'] .fieldset {
  box-shadow: none;
}

.fieldset > legend,
.fieldset__title {
  display: block;
  margin-bottom: var(--sp-1);
  font-size: var(--fs-12);
  font-weight: 700;
  color: var(--ink-2);
  letter-spacing: .3px;
}

.field {
  display: grid;
  gap: 5px;
  min-width: 0;
}

.field__label {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-12);
  font-weight: 500;
  color: var(--ink-2);
}

.field__req {
  color: var(--crit);
  font-weight: 700;
}

/* Helper text is persistent, not a placeholder that vanishes on focus. */
.field__help {
  font-size: var(--fs-11);
  color: var(--ink-3);
  line-height: 1.5;
}

.field__error {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-11);
  font-weight: 500;
  color: var(--crit);
}

.field__error svg {
  width: 12px;
  height: 12px;
  flex: none;
}

.field--row {
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: var(--sp-2);
}

.field--inline {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  cursor: pointer;
}

.field--inline .field__label {
  cursor: pointer;
  color: var(--ink-1);
  font-size: var(--fs-13);
}

.fields--2 {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

@media (max-width: 520px) {
  .fields--2 {
    grid-template-columns: 1fr;
  }
}

/* Switch */
.switch {
  position: relative;
  flex: none;
  width: 38px;
  height: 21px;
  border-radius: var(--r-pill);
  background: var(--sf-3);
  border: 1px solid var(--line-2);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, .3);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
}

.switch::after {
  content: '';
  position: absolute;
  top: 2px;
  inset-inline-start: 2px;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--ink-2);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .4);
  transition: translate var(--dur-2) var(--ease-spring), background-color var(--dur-2) var(--ease-out);
}

.switch[aria-checked='true'] {
  background: var(--brand);
  border-color: transparent;
}

.switch[aria-checked='true']::after {
  translate: -17px 0;
  background: var(--brand-ink);
}

/* Picker list (inbounds, profiles) — a scrollable multi-select. */
.picker {
  max-height: 220px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-2);
  border-radius: var(--r-1);
  background: var(--sf-1);
  border: 1px solid var(--line-2);
}

.picker__row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 34px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-1);
  cursor: pointer;
  font-size: var(--fs-13);
  transition: background-color var(--dur-1) var(--ease-out);
}

.picker__row:hover {
  background: var(--sf-2);
}

.picker__row span {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.picker__row small {
  color: var(--ink-3);
  font-size: var(--fs-11);
}

/* Disclosure — the per-user config list inside the sub-links drawer. */
details > summary {
  list-style: none;
  color: var(--brand);
  font-weight: 600;
  padding: var(--sp-2) 0;
}

details > summary::-webkit-details-marker {
  display: none;
}

details > summary::before {
  content: '›';
  display: inline-block;
  margin-inline-end: 6px;
  /* RTL: the marker points left when closed, down when open. */
  rotate: 180deg;
  transition: rotate var(--dur-2) var(--ease-out);
}

details[open] > summary::before {
  rotate: 270deg;
}

details > summary:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--r-1);
}

/* Amount chips (wallet top-up presets). */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.chip {
  min-height: 30px;
  padding: 0 var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--sf-2);
  border: 1px solid var(--line-2);
  color: var(--ink-2);
  font-size: var(--fs-12);
  font-weight: 600;
  transition: all var(--dur-2) var(--ease-out);
}

.chip:hover {
  border-color: var(--brand-line);
  color: var(--ink-1);
}

.chip[aria-pressed='true'] {
  background: var(--brand-soft);
  border-color: var(--brand-line);
  color: var(--brand);
}

/* ============================================================================
   Notices — inline messaging inside forms and drawers
   ========================================================================== */
.notice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--r-1);
  background: var(--sf-2);
  border: 1px solid var(--line-2);
  border-inline-start: 3px solid var(--ink-3);
  font-size: var(--fs-12);
  line-height: 1.6;
  color: var(--ink-2);
}

.notice svg {
  flex: none;
  width: 15px;
  height: 15px;
  margin-top: 2px;
}

.notice b {
  color: var(--ink-1);
}

.notice[data-tone='ok'] { border-inline-start-color: var(--ok); background: var(--ok-soft); }
.notice[data-tone='warn'] { border-inline-start-color: var(--warn); background: var(--warn-soft); }
.notice[data-tone='crit'] { border-inline-start-color: var(--crit); background: var(--crit-soft); }
.notice[data-tone='brand'] { border-inline-start-color: var(--brand); background: var(--brand-soft); }

/* ============================================================================
   Toasts
   ========================================================================== */
.toasts {
  position: fixed;
  bottom: var(--sp-4);
  inset-inline-start: var(--sp-4);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: min(360px, calc(100vw - var(--sp-6)));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--r-2);
  background: var(--sf-3);
  border: 1px solid var(--line-2);
  box-shadow: var(--e3);
  font-size: var(--fs-13);
  pointer-events: auto;
  animation: toast-in var(--dur-3) var(--ease-out);
}

.toast[data-leaving='1'] {
  animation: toast-out var(--dur-2) var(--ease-in) forwards;
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(12px) scale(.96); }
}

@keyframes toast-out {
  to { opacity: 0; transform: translateX(-24px) scale(.96); }
}

.toast__icon {
  flex: none;
  width: 20px;
  height: 20px;
  display: grid;
  place-content: center;
  border-radius: 50%;
  background: var(--brand-soft);
  color: var(--brand);
}

.toast__icon svg {
  width: 12px;
  height: 12px;
  stroke-width: 2.4;
}

.toast[data-tone='ok'] .toast__icon { background: var(--ok-soft); color: var(--ok); }
.toast[data-tone='warn'] .toast__icon { background: var(--warn-soft); color: var(--warn); }
.toast[data-tone='crit'] .toast__icon { background: var(--crit-soft); color: var(--crit); }

.toast__text {
  flex: 1;
  min-width: 0;
  line-height: 1.5;
}

.toast__text b {
  display: block;
  font-weight: 600;
}

.toast__text span {
  color: var(--ink-2);
  font-size: var(--fs-12);
}

.toast__action {
  flex: none;
  padding: 0 var(--sp-2);
  color: var(--brand);
  font-size: var(--fs-12);
  font-weight: 600;
}

.toast__action:hover {
  color: var(--brand-2);
}

/* ============================================================================
   Skeletons — shape-matched, not a generic spinner
   ========================================================================== */
.sk {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-1);
  background: var(--sf-2);
}

.sk::after {
  content: '';
  position: absolute;
  inset: 0;
  /* RTL: the sweep runs right-to-left to match reading direction. */
  background: linear-gradient(-90deg, transparent, rgba(255, 255, 255, .055), transparent);
  animation: shimmer 1.35s ease-in-out infinite;
}

[data-theme='light'] .sk::after {
  background: linear-gradient(-90deg, transparent, rgba(20, 35, 75, .05), transparent);
}

@keyframes shimmer {
  from { translate: -100% 0; }
  to { translate: 100% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .sk::after {
    animation: none;
    opacity: .5;
  }
}

.sk--text { height: 11px; }
.sk--line { height: 13px; }
.sk--title { height: 19px; width: 40%; }
.sk--pill { height: 21px; width: 68px; border-radius: var(--r-pill); }
.sk--box { height: 100%; border-radius: var(--r-2); }
.sk--circle { border-radius: 50%; }

.sk-rows {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-3);
}

.sk-row {
  display: grid;
  gap: var(--sp-3);
  align-items: center;
}

/* ============================================================================
   Empty / error states
   ========================================================================== */
.state {
  display: grid;
  justify-items: center;
  gap: var(--sp-3);
  padding: var(--sp-7) var(--sp-4);
  text-align: center;
}

.state__icon {
  width: 46px;
  height: 46px;
  display: grid;
  place-content: center;
  border-radius: var(--r-2);
  background: var(--sf-2);
  border: 1px solid var(--line-2);
  box-shadow: var(--e1);
  color: var(--ink-3);
}

.state__icon svg {
  width: 21px;
  height: 21px;
}

.state[data-tone='crit'] .state__icon {
  background: var(--crit-soft);
  border-color: color-mix(in srgb, var(--crit) 30%, transparent);
  color: var(--crit);
}

.state h3 {
  font-size: var(--fs-14);
  font-weight: 600;
}

.state p {
  max-width: 44ch;
  font-size: var(--fs-13);
  color: var(--ink-2);
  line-height: 1.6;
}

.state pre {
  max-width: 100%;
  max-height: 140px;
  overflow: auto;
  padding: var(--sp-3);
  border-radius: var(--r-1);
  background: var(--bg-1);
  border: 1px solid var(--line-1);
  color: var(--ink-3);
  font-size: var(--fs-11);
  text-align: left;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ============================================================================
   Command palette
   ========================================================================== */
.cmdk {
  position: fixed;
  inset: 0;
  z-index: var(--z-cmdk);
  display: grid;
  align-items: start;
  justify-items: center;
  padding: min(14vh, 120px) var(--sp-4) var(--sp-4);
  background: var(--scrim);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}

.cmdk[data-open='1'] {
  opacity: 1;
}

.cmdk__panel {
  width: min(600px, 100%);
  max-height: 62dvh;
  display: flex;
  flex-direction: column;
  border-radius: var(--r-3);
  background: var(--sf-3);
  border: 1px solid var(--line-2);
  box-shadow: var(--e4), var(--glow-brand);
  overflow: hidden;
  transform: scale(.97) translateY(-10px);
  opacity: 0;
  transition: transform var(--dur-3) var(--ease-spring), opacity var(--dur-2) var(--ease-out);
}

.cmdk[data-open='1'] .cmdk__panel {
  transform: none;
  opacity: 1;
}

.cmdk__input {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 0 var(--sp-4);
  border-bottom: 1px solid var(--line-1);
  flex: none;
}

.cmdk__input svg {
  width: 17px;
  height: 17px;
  color: var(--ink-3);
  flex: none;
}

.cmdk__input input {
  height: 52px;
  padding: 0;
  background: none;
  border: 0;
  box-shadow: none;
  font-size: var(--fs-16);
}

.cmdk__input input:focus {
  background: none;
  box-shadow: none;
}

.cmdk__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-2);
}

.cmdk__group {
  padding: var(--sp-2) var(--sp-3) var(--sp-1);
  font-size: var(--fs-11);
  font-weight: 600;
  color: var(--ink-3);
  letter-spacing: .3px;
}

.cmdk__item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 40px;
  padding: 0 var(--sp-3);
  border-radius: var(--r-1);
  color: var(--ink-2);
  font-size: var(--fs-13);
  text-align: start;
  transition: background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}

.cmdk__item svg {
  width: 15px;
  height: 15px;
  flex: none;
}

.cmdk__item b {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cmdk__item span {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-11);
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Selection is driven by aria-selected, not :hover — the keyboard is primary
   here and the mouse must not fight it. */
.cmdk__item[aria-selected='true'] {
  background: var(--sf-4);
  color: var(--ink-1);
  box-shadow: var(--rim);
}

.cmdk__item[aria-selected='true'] svg {
  color: var(--brand);
}

.cmdk__foot {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-4);
  border-top: 1px solid var(--line-1);
  background: var(--bg-1);
  font-size: var(--fs-11);
  color: var(--ink-3);
}

.cmdk__foot span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

/* ============================================================================
   Tabs
   ========================================================================== */
.tabs {
  display: flex;
  gap: var(--sp-1);
  border-bottom: 1px solid var(--line-1);
  overflow-x: auto;
  scrollbar-width: none;
}

.tabs::-webkit-scrollbar {
  display: none;
}

.tab {
  position: relative;
  min-height: 38px;
  padding: 0 var(--sp-3);
  color: var(--ink-2);
  font-size: var(--fs-13);
  font-weight: 500;
  white-space: nowrap;
  transition: color var(--dur-2) var(--ease-out);
}

.tab:hover {
  color: var(--ink-1);
}

.tab[aria-selected='true'] {
  color: var(--brand);
  font-weight: 600;
}

.tab[aria-selected='true']::after {
  content: '';
  position: absolute;
  inset-inline: var(--sp-2);
  bottom: -1px;
  height: 2px;
  border-radius: var(--r-pill);
  background: var(--brand);
  box-shadow: 0 0 8px var(--brand);
}

/* ============================================================================
   Charts
   ========================================================================== */
.chart {
  position: relative;
  width: 100%;
}

.chart svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

.chart__grid line {
  stroke: var(--line-1);
  stroke-width: 1;
}

.chart__axis {
  fill: var(--ink-3);
  font-size: 10px;
  stroke: none;
}

.chart__line {
  fill: none;
  stroke: var(--brand);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.chart__area {
  fill: url(#chartFill);
  stroke: none;
}

.chart__pt {
  fill: var(--brand);
  stroke: var(--sf-1);
  stroke-width: 2;
  opacity: 0;
  transition: opacity var(--dur-1) var(--ease-out);
}

.chart__pt:hover,
.chart__pt:focus-visible,
.chart__pt[data-active='1'] {
  opacity: 1;
}

.chart__cursor {
  stroke: var(--line-3);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0;
}

.chart__cursor[data-active='1'] {
  opacity: 1;
}

.chart__tip {
  position: absolute;
  z-index: var(--z-dropdown);
  padding: 6px 10px;
  border-radius: var(--r-1);
  background: var(--sf-3);
  border: 1px solid var(--line-2);
  box-shadow: var(--e3);
  font-size: var(--fs-11);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(50%, -100%);
  transition: opacity var(--dur-1) var(--ease-out);
}

.chart__tip[data-active='1'] {
  opacity: 1;
}

.chart__tip b {
  color: var(--brand);
}

/* ============================================================================
   Server board — one tile per node
   Replaces the WebGL constellation host (`.net`). Auto-fill means a single
   server occupies one tile-width instead of stretching to fill the row, and
   forty servers wrap into a readable grid.
   ========================================================================== */
/* Fleet summary pills. Not `.chip` — that is an interactive filter control with
   hover and aria-pressed states, and these are static readouts. */
.fleet {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

.fleet__stat {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 28px;
  padding: 0 var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--sf-2);
  border: 1px solid var(--line-2);
  color: var(--ink-2);
  font-size: var(--fs-12);
  font-weight: 600;
}

.fleet__stat[data-tone='ok'] {
  background: var(--ok-soft);
  border-color: color-mix(in srgb, var(--ok) 34%, transparent);
  color: var(--ok);
}

.fleet__stat[data-tone='warn'] {
  background: var(--warn-soft);
  border-color: color-mix(in srgb, var(--warn) 34%, transparent);
  color: var(--warn);
}

.fleet__stat[data-tone='crit'] {
  background: var(--crit-soft);
  border-color: color-mix(in srgb, var(--crit) 34%, transparent);
  color: var(--crit);
}

.nodegrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
  gap: var(--sp-3);
}

.nodecard {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--r-2);
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  box-shadow: var(--e1);
  transition:
    border-color var(--dur-2) var(--ease-out),
    box-shadow var(--dur-2) var(--ease-out);
}

.nodecard:hover {
  border-color: var(--line-2);
  box-shadow: var(--e2);
}

/* An offline node is the one thing on this screen that needs to shout. */
.nodecard[data-state='off'] {
  border-color: color-mix(in srgb, var(--crit) 38%, var(--line-1));
  background: color-mix(in srgb, var(--crit) 5%, var(--sf-1));
}

.nodecard__top {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.nodecard__id {
  flex: 1;
  min-width: 0;
  display: grid;
  gap: 1px;
}

.nodecard__id b {
  font-size: var(--fs-13);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nodecard__id span {
  font-size: var(--fs-11);
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nodecard__bars {
  display: grid;
  gap: var(--sp-2);
}

.nodecard__offline {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-12);
  color: var(--crit);
}

.nodecard__foot {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--line-1);
  font-size: var(--fs-11);
  color: var(--ink-2);
}

.nodecard__foot span:last-child:not(:only-child) {
  margin-inline-start: auto;
  color: var(--ink-3);
}

/* ============================================================================
   Login
   ========================================================================== */
.auth {
  position: relative;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--sp-4);
  overflow: hidden;
}

/* Tier A: the first surface anyone sees, so it gets the glass treatment the
   dashboards use. Deliberately still a plain card underneath — no tilt, no
   pointer tracking. A credential prompt that reacts to the cursor is a toy. */
.auth__card {
  position: relative;
  z-index: 1;
  width: min(400px, 100%);
  padding: var(--sp-6) var(--sp-5) var(--sp-5);
  border-radius: var(--panel-radius);
  background: var(--panel-bg);
  border: 1px solid var(--panel-line);
  box-shadow: var(--panel-shadow), var(--e4);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}

/* Static top-centre sheen, matching the one light source the whole depth
   system uses. This used to be a radial gradient that followed the pointer;
   a login card that tilts and glows under the cursor is a toy, not a
   credential prompt. */
.auth__card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(
    460px circle at 50% 0%,
    rgba(34, 211, 238, .08),
    transparent 60%
  );
}

.auth__brand {
  display: grid;
  justify-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}

.auth__mark {
  width: 52px;
  height: 52px;
  display: grid;
  place-content: center;
  border-radius: var(--r-3);
  background: var(--grad-brand);
  color: var(--brand-ink);
  font-size: var(--fs-20);
  font-weight: 800;
  box-shadow: var(--rim-strong), 0 10px 28px -8px rgba(34, 211, 238, .6);
}

.auth__brand h1 {
  font-size: var(--fs-20);
  font-weight: 800;
  text-align: center;
}

.auth__brand p {
  font-size: var(--fs-12);
  color: var(--ink-3);
  text-align: center;
}

.auth__card form {
  display: grid;
  gap: var(--sp-4);
}

.auth__foot {
  margin-top: var(--sp-4);
  text-align: center;
  font-size: var(--fs-11);
  color: var(--ink-3);
}

/* Password reveal toggle sits inside the field. */
.pw {
  position: relative;
}

.pw input {
  padding-inline-start: 38px;
}

.pw__toggle {
  position: absolute;
  inset-inline-start: 5px;
  top: 50%;
  translate: 0 -50%;
  width: 28px;
  height: 28px;
  display: grid;
  place-content: center;
  border-radius: var(--r-1);
  color: var(--ink-3);
  transition: color var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out);
}

.pw__toggle:hover {
  color: var(--ink-1);
  background: var(--sf-3);
}

.pw__toggle svg {
  width: 15px;
  height: 15px;
}

/* ============================================================================
   Misc
   ========================================================================== */

/* Route-level progress bar (top of the viewport during a view fetch). */
.routebar {
  position: fixed;
  top: 0;
  inset-inline: 0;
  z-index: var(--z-toast);
  height: 2px;
  background: var(--brand);
  box-shadow: 0 0 10px var(--brand);
  transform-origin: right center;
  scale: 0 1;
  opacity: 0;
  transition: scale var(--dur-4) var(--ease-out), opacity var(--dur-2) var(--ease-out);
}

.routebar[data-loading='1'] {
  scale: .7 1;
  opacity: 1;
}

.routebar[data-loading='done'] {
  scale: 1 1;
  opacity: 0;
}

/* Copy-to-clipboard field. */
.copy {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2);
  border-radius: var(--r-1);
  background: var(--bg-1);
  border: 1px solid var(--line-2);
}

.copy code {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-12);
  color: var(--ink-2);
}

/* QR host — always on white; a QR on a dark surface fails to scan. */
/* `flex: none` is load-bearing. Inside a flex row next to a `flex: 1` block of
   link fields, the default `flex-shrink: 1` let this be squeezed to zero width
   — the QR was in the DOM, labelled, and invisible. */
.qr {
  flex: none;
  width: 168px;
  height: 168px;
  padding: var(--sp-2);
  border-radius: var(--r-2);
  background: #fff;
  box-shadow: var(--e2);
}

/* Centred above the links rather than beside them: the drawer is 560px wide
   and a phone camera wants the code as large as the panel allows. */
.qr--lg {
  width: 200px;
  height: 200px;
  margin-inline: auto;
}

.qr img,
.qr svg {
  width: 100%;
  height: 100%;
}

/* The base reset gives every <svg> `stroke: currentColor` for icons. A QR's
   viewBox is one unit per module, so an inherited 1-unit stroke is as wide as
   the module it outlines: the pattern bleeds into itself, the finder squares
   disappear, and the whole code takes on the pale ink colour. It renders — it
   just cannot be scanned. Fill only, no stroke. */
.qr svg,
.qr svg * {
  stroke: none;
}

/* Avatar / flag initial. */
.avatar {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-content: center;
  border-radius: var(--r-1);
  background: var(--sf-3);
  border: 1px solid var(--line-2);
  box-shadow: var(--rim);
  font-size: var(--fs-12);
  font-weight: 700;
  color: var(--ink-2);
}

.divider {
  height: 1px;
  background: var(--line-1);
  margin: var(--sp-3) 0;
}

/* Key/value list used across detail drawers. */
.kv {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-2) var(--sp-4);
  font-size: var(--fs-13);
}

.kv dt {
  color: var(--ink-3);
  font-size: var(--fs-12);
  white-space: nowrap;
}

.kv dd {
  min-width: 0;
  font-weight: 500;
  overflow-wrap: anywhere;
}

/* Progress bar with role="progressbar" on the track. */
.progress {
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--bg-1);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .3);
  overflow: hidden;
}

.progress__fill {
  height: 100%;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--brand-3), var(--brand));
  transition: width var(--dur-3) var(--ease-out);
}

.progress[data-tone='crit'] .progress__fill {
  background: linear-gradient(90deg, var(--crit-deep), var(--crit));
}

/* Locks body scroll while a modal/drawer/palette is open. */
body[data-lock='1'] {
  overflow: hidden;
}

/* ============================================================================
   Activity list
   Replaces a stack of bare route identifiers. Each row carries the icon and
   tone of what it did, so a deletion and a login are not the same grey line.
   ========================================================================== */
.actlist {
  display: grid;
  gap: var(--sp-1);
}

.act {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-1);
  background: var(--bg-1);
  border: 1px solid transparent;
  transition: border-color var(--dur-2) var(--ease-out), background-color var(--dur-2) var(--ease-out);
}

.act:hover {
  background: var(--sf-2);
  border-color: var(--line-1);
}

.act__icon {
  flex: none;
  display: grid;
  place-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-1);
  background: var(--chip-bg);
  color: var(--ink-2);
}

/* Tone is set only where the action has consequence, so the eye is drawn to
   deletions and suspensions rather than to every login. */
.act[data-tone='ok'] .act__icon { background: var(--ok-soft); color: var(--ok); }
.act[data-tone='warn'] .act__icon { background: var(--warn-soft); color: var(--warn); }
.act[data-tone='crit'] .act__icon { background: var(--crit-soft); color: var(--crit); }

.act__text {
  flex: 1;
  min-width: 0;
}

.act__label {
  font-size: var(--fs-13);
  font-weight: 500;
  color: var(--ink-1);
}

.act__target {
  font-size: var(--fs-11);
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.act__time {
  flex: none;
  font-size: var(--fs-11);
  color: var(--ink-3);
}

/* ============================================================================
   Identity block — the account header on a settings screen
   A name and an email in a two-column key/value grid read as configuration;
   they are neither, so they get an avatar and their own line.
   ========================================================================== */
.identity {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
}

.identity__avatar {
  flex: none;
  width: 52px;
  height: 52px;
  display: grid;
  place-content: center;
  border-radius: var(--r-3);
  background: var(--grad-brand);
  color: var(--grad-ink);
  font-size: var(--fs-20);
  font-weight: 800;
  box-shadow: var(--rim-strong), 0 8px 22px -8px rgba(34, 211, 238, .5);
}

.identity__text {
  flex: 1;
  min-width: 0;
}

.identity__name {
  font-size: var(--fs-16);
  font-weight: 700;
}

.identity__mail {
  font-size: var(--fs-12);
  color: var(--ink-3);
}

.identity__actions {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
}

@media (max-width: 560px) {
  .identity__actions { width: 100%; }
  .identity__actions .btn { flex: 1; }
}
