/*
 * Hallmark · Cobalt theme · Supabase-inspired professional admin UI
 */

* { box-sizing: border-box; }

html, body {
    margin: 0; padding: 0;
    overflow-x: clip;
    background: var(--color-paper);
    color: var(--color-ink);
    font-family: var(--font-body);
    font-size: var(--text-base);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

*:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
    border-radius: var(--radius);
}

/* ── Application shell ──────────────────────────────────────────────────── */

.app-shell { display: flex; min-height: 100vh; }

.sidebar {
    width: 240px;
    background: var(--color-sidebar);
    border-right: 1px solid var(--color-border);
    padding: var(--space-4) var(--space-2);
    display: flex; flex-direction: column; gap: 2px;
    flex-shrink: 0;
    transition: width var(--dur-med) var(--ease-out);
    overflow: hidden;
}

.sidebar.collapsed { width: 56px; }

.sidebar-brand {
    display: flex; align-items: center; gap: var(--space-3);
    padding: 0 var(--space-3) var(--space-4);
    font-size: var(--text-sm); font-weight: 700;
    color: var(--color-ink);
    line-height: 1.3;
    white-space: nowrap;
}

.sidebar-collapsed-icon {
    display: flex; justify-content: center;
    padding: 0 0 var(--space-4);
}
.sidebar-collapsed-icon svg { width: 28px; height: 28px; }

.sidebar-brand svg { width: 28px; height: 28px; flex-shrink: 0; }
.sidebar-brand-text { display: flex; flex-direction: column; }
.sidebar-brand-name { font-size: var(--text-sm); font-weight: 700; }
.sidebar-brand-sub { font-size: 10px; color: var(--color-ink-3); font-weight: 500; }

.sidebar-spacer { flex: 1; }

.collapse-toggle { margin-top: var(--space-2); }

.nav-label { white-space: nowrap; }

.hidden { display: none !important; }

.main-area { flex: 1; display: flex; flex-direction: column; min-width: 0; }

.topbar {
    height: 52px;
    background: var(--color-paper-2);
    border-bottom: 1px solid var(--color-border);
    display: flex; align-items: center; justify-content: space-between;
    padding: 0 var(--space-6);
    flex-shrink: 0;
}

.topbar-title { font-size: var(--text-md); font-weight: 600; color: var(--color-ink); }
.topbar-right { display: flex; align-items: center; gap: var(--space-2); }

.content {
    flex: 1;
    padding: var(--space-5) var(--space-6);
    max-width: 1280px;
    overflow-x: auto;
}

/* ── Nav links ──────────────────────────────────────────────────────────── */

.nav-link {
    display: flex; align-items: center; gap: var(--space-2);
    padding: 7px var(--space-3);
    border-radius: var(--radius);
    color: var(--color-ink-2);
    text-decoration: none;
    font-size: var(--text-sm); font-weight: 500;
    cursor: pointer; border: none; background: none;
    width: 100%; text-align: left;
    transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.nav-link:hover { background: var(--color-paper-3); color: var(--color-ink); }
.nav-link.active {
    background: var(--color-accent-light);
    color: var(--color-accent);
    font-weight: 600;
}
.nav-icon { width: 18px; height: 18px; opacity: 0.7; flex-shrink: 0; }
.nav-link.active .nav-icon { opacity: 1; }

/* ── KPI cards ──────────────────────────────────────────────────────────── */

.kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

.kpi-card {
    background: var(--color-paper-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-3) var(--space-4);
}

.kpi-label {
    font-size: 11px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--color-ink-3);
    margin-bottom: 2px;
}

.kpi-value {
    font-size: var(--text-lg); font-weight: 700;
    color: var(--color-ink);
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}
/* A line under a value: what it counts, or who it is about. */
.kpi-note { font-size: var(--text-xs); color: var(--color-ink-3); margin-top: 2px; }
.kpi-value.ok { color: var(--color-ok); }
.kpi-value.warn { color: var(--color-warn); }
.kpi-value.err { color: var(--color-err); }

/* ── Tables ──────────────────────────────────────────────────────────────── */

.table-wrap {
    background: var(--color-paper-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    /* Scroll a table too wide for the viewport rather than clip its last
       columns: the Fleet table's action buttons fall off a 1280px screen once
       the Paths and Traffic columns are added, with overflow:hidden. */
    overflow-x: auto;
}

.tbl { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }

.tbl th {
    padding: 8px var(--space-3);
    text-align: left;
    font-size: 11px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--color-ink-3);
    background: var(--color-paper-3);
    border-bottom: 1px solid var(--color-border);
}

.tbl td {
    padding: 7px var(--space-3);
    border-bottom: 1px solid var(--color-border);
    color: var(--color-ink);
}

.tbl tbody tr:nth-child(even) { background: var(--color-paper); }
.tbl tbody tr:hover { background: var(--color-accent-light); }
.tbl tbody tr:last-child td { border-bottom: none; }

/* Fleet: once Paths and Traffic are shown the table outgrows a narrow
   viewport and .table-wrap scrolls it. Pin the per-row actions to the right
   edge so Deploy/Revoke/Retire never scroll off; the data columns slide under
   them. Each sticky cell carries its row's own background (odd rows show the
   table-wrap's paper-2 through, even rows paper, hover accent-light) so the
   scrolling content cannot bleed through. */
.fleet-tbl th.col-actions,
.fleet-tbl td.col-actions {
    position: sticky;
    right: 0;
    z-index: 1;
    box-shadow: -1px 0 0 var(--color-border);
}
.fleet-tbl th.col-actions { background: var(--color-paper-3); }
.fleet-tbl td.col-actions { background: var(--color-paper-2); }
.fleet-tbl tbody tr:nth-child(even) td.col-actions { background: var(--color-paper); }
.fleet-tbl tbody tr:hover td.col-actions { background: var(--color-accent-light); }

/* ── Search bar ──────────────────────────────────────────────────────── */

.table-toolbar {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-3);
    border-bottom: 1px solid var(--color-border);
}

.search-bar {
    position: relative;
    display: flex; align-items: center;
    flex: 1; max-width: 320px;
}

.search-icon {
    position: absolute; left: 10px;
    width: 14px; height: 14px;
    color: var(--color-ink-3);
    pointer-events: none;
}

.search-input {
    width: 100%;
    padding: 6px 10px 6px 32px;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius);
    font-size: var(--text-sm);
    font-family: var(--font-body);
    color: var(--color-ink);
    background: var(--color-paper-2);
    outline: none;
    transition: border-color var(--dur-fast) var(--ease-out);
}

.search-input:focus {
    border-color: var(--color-accent);
}

.search-input::placeholder { color: var(--color-ink-3); }

/* ── Pagination ──────────────────────────────────────────────────────── */

.pagination-bar {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--color-border);
    font-size: 12px; color: var(--color-ink-3);
}

/* "1–20 of 137". Secondary to the controls beside it: it is orientation, not
   something to act on. */
.pagination-info {
    font-size: var(--text-sm);
    color: var(--color-ink-3);
    font-variant-numeric: tabular-nums;
}
.pagination-controls { display: flex; align-items: center; gap: var(--space-2); }
.pagination-page { font-variant-numeric: tabular-nums; min-width: 60px; text-align: center; }

/* ── Status pills ──────────────────────────────────────────────────────── */

.pill {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 8px;
    border-radius: 99px;
    font-size: 11px; font-weight: 600;
    white-space: nowrap;
    background: var(--color-paper-3); color: var(--color-ink-2);
}
.pill-ok   { background: var(--color-ok-bg);   color: var(--color-ok); }
.pill-warn { background: var(--color-warn-bg); color: var(--color-warn); }
.pill-err  { background: var(--color-err-bg);  color: var(--color-err); }
.pill-info { background: var(--color-info-bg); color: var(--color-info); }
.pill::before {
    content: ''; width: 5px; height: 5px;
    border-radius: 50%; background: currentColor; flex-shrink: 0;
}

/* ── Buttons ──────────────────────────────────────────────────────────── */

.btn {
    display: inline-flex; align-items: center; gap: var(--space-2);
    padding: 7px var(--space-3);
    border-radius: var(--radius);
    border: 1px solid var(--color-border-strong);
    background: var(--color-paper-2);
    color: var(--color-ink);
    font-family: var(--font-body); font-size: var(--text-sm); font-weight: 500;
    cursor: pointer;
    transition: all var(--dur-fast) var(--ease-out);
}
.btn:hover { background: var(--color-paper-3); border-color: var(--color-ink-3); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }

.btn-sm { padding: 4px 8px; font-size: 12px; min-width: 28px; justify-content: center; }

.btn-primary {
    background: var(--color-accent); color: white;
    border-color: var(--color-accent); font-weight: 600;
}
.btn-primary:hover { filter: brightness(1.1); }

/* Row action cluster: keeps a table cell's buttons from stacking or stretching
   the row height. */
.row-actions {
    display: flex;
    gap: var(--space-2);
    justify-content: flex-end;
    white-space: nowrap;
}

/* Button group: joined buttons sharing their borders. The Fleet row's is a
   split button, the primary action and a caret that opens the rest; four
   separate buttons per row made the table outgrow a 1280px screen. */
.btn-group { display: inline-flex; }
.btn-group > .btn { border-radius: 0; }
.btn-group > .btn:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.btn-group > .btn:last-child { border-radius: 0 var(--radius) var(--radius) 0; }
.btn-group > .btn:only-child { border-radius: var(--radius); }
.btn-group > .btn + .btn { margin-left: -1px; }
/* Over its neighbour's shared border while hovered or focused. */
.btn-group > .btn:hover, .btn-group > .btn:focus-visible { position: relative; z-index: 1; }
.btn-caret { min-width: 0; padding-inline: 5px; }
.btn-menu { gap: 4px; }
.btn-chevron { width: 12px; height: 12px; flex: none; transition: transform var(--dur-fast) var(--ease-out); }
.btn[aria-expanded="true"] { background: var(--color-paper-3); border-color: var(--color-ink-3); }
.btn[aria-expanded="true"] .btn-chevron { transform: rotate(180deg); }

/* A row menu. Fixed to the viewport at its caret, so the scrolling table
   cannot clip it; the transparent backdrop under it takes the click that
   closes it, and sits below the confirmation modal (z-index 100). */
.menu-backdrop { position: fixed; inset: 0; z-index: 59; }
.menu {
    position: fixed; z-index: 60;
    min-width: 184px;
    padding: var(--space-1);
    background: var(--color-paper-2);
    border-radius: var(--radius);
    box-shadow: 0 1px 2px var(--color-shadow-hover), 0 8px 24px var(--color-shadow-hover);
    animation: menu-in var(--dur-fast) var(--ease-out);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) {
    .menu { animation: none; }
    .btn-chevron { transition: none; }
}
.menu-item {
    display: flex; align-items: center; width: 100%;
    padding: 6px var(--space-3);
    border: 0; border-radius: 4px;
    background: none; color: var(--color-ink);
    font-family: var(--font-body); font-size: var(--text-sm); text-align: left;
    white-space: nowrap; cursor: pointer;
}
.menu-item:hover { background: var(--color-paper-3); }
.menu-item:focus-visible { outline: none; background: var(--color-accent-light); box-shadow: inset 0 0 0 1px var(--color-focus); }
.menu-item-danger { color: var(--color-err); }
.menu-item-danger:hover, .menu-item-danger:focus-visible { background: var(--color-err-bg); }
.menu-item-danger:focus-visible { box-shadow: inset 0 0 0 1px var(--color-err); }
.menu-sep { height: 1px; margin: var(--space-1) 0; background: var(--color-border); }

/* Destructive action. Outlined rather than filled: retiring a node is
   irreversible, so it should read as a deliberate choice, not the obvious
   default sitting next to a harmless one. */
.btn-danger {
    color: var(--color-err);
    border-color: var(--color-err);
    background: var(--color-paper-2);
}
.btn-danger:hover { background: var(--color-err-bg); }
.btn-danger:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Skeleton ─────────────────────────────────────────────────────────── */

.skeleton {
    display: block; height: 20px;
    border-radius: var(--radius);
    background: linear-gradient(90deg, var(--color-paper-3) 25%, var(--color-border) 50%, var(--color-paper-3) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s infinite;
}
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) {
    .skeleton { animation: none; }
    .btn, .nav-link { transition: none; }
}

/* ── Login ────────────────────────────────────────────────────────────── */

.login-screen {
    display: flex; align-items: center; justify-content: center;
    min-height: 100vh; background: var(--color-paper);
}
.login-card {
    background: var(--color-paper-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-6) var(--space-5);
    text-align: center;
    max-width: 340px;
}
.login-brand { margin: 0 auto var(--space-3); width: 48px; height: 48px; }
.login-title { font-size: var(--text-md); font-weight: 700; margin: 0 0 2px; }
.login-subtitle { font-size: var(--text-sm); color: var(--color-ink-3); margin: 0 0 var(--space-4); }

/* One button per configured identity provider. The buttons carry the provider
   names, which vary in length, so they stack rather than sit in a row. */
.login-error {
    font-size: var(--text-sm);
    color: var(--color-danger, #b3402f);
    text-align: left;
    margin: 0 0 var(--space-4);
    line-height: 1.45;
}
.login-providers { display: flex; flex-direction: column; gap: var(--space-2); }
.login-provider {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-2);
    padding: 10px var(--space-3);
    text-align: left;
}
/* A fixed slot, present whether or not we ship a mark for the provider, so
   every label starts at the same x and the buttons read as a column rather
   than three separately centred rows. */
.login-provider-logo {
    flex: 0 0 22px;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
/* The marks are full-colour and some are near-black, so they sit on a white
   chip; directly on the accent fill they would not read. */
.login-provider-logo img {
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    padding: 2px;
    background: #fff;
    border-radius: var(--radius-sm, 4px);
    object-fit: contain;
}
.login-provider-label { flex: 1 1 auto; }

/* ── Empty state ────────────────────────────────────────────────────────── */

.empty-state {
    padding: var(--space-6) var(--space-4);
    text-align: center; color: var(--color-ink-3);
    font-size: var(--text-sm);
}

/* ── Responsive ──────────────────────────────────────────────────────────── */

@media (max-width: 768px) {
    .app-shell { flex-direction: column; }
    .sidebar { width: 100%; flex-direction: row; overflow-x: auto; border-right: none; border-bottom: 1px solid var(--color-border); padding: var(--space-2); }
    .sidebar-brand { display: none; }
    .nav-link { white-space: nowrap; width: auto; }
    .content { padding: var(--space-3); }
    .table-toolbar { flex-wrap: wrap; }
    .search-bar { max-width: 100%; }
}

/* ── Sort headers ──────────────────────────────────────────────────────── */

.sort-header {
    display: inline-flex; align-items: center; gap: 4px;
    background: none; border: none; cursor: pointer;
    font-size: 11px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.04em;
    color: inherit; padding: 0;
    transition: color var(--dur-fast) var(--ease-out);
}
.sort-header:hover { color: var(--color-accent); }
.sort-arrow { font-size: 10px; opacity: 0; transition: opacity var(--dur-fast); }
.sort-arrow.visible { opacity: 1; color: var(--color-accent); }

/* ── Collapse button ────────────────────────────────────────────────────── */

.collapse-btn {
    display: flex; align-items: center; justify-content: center;
    width: 32px; height: 32px;
    border: 1px solid var(--color-border); border-radius: var(--radius);
    background: var(--color-paper-2); color: var(--color-ink-2);
    cursor: pointer; flex-shrink: 0;
    transition: all var(--dur-fast) var(--ease-out);
    align-self: flex-end; margin-left: auto; margin-top: var(--space-2);
}
.collapse-btn:hover { background: var(--color-paper-3); color: var(--color-ink); border-color: var(--color-ink-3); }

/* ── Brand image ────────────────────────────────────────────────────────── */

.brand-img { width: 28px; height: 28px; border-radius: var(--radius); flex-shrink: 0; object-fit: contain; }
.login-brand { width: 56px; height: 56px; border-radius: var(--radius-lg); margin: 0 auto var(--space-3); display: block; }

/* ── Button fix: dark grey primary ────────────────────────────────────── */

.btn-primary {
    background: #2d333b !important;
    color: #ffffff !important;
    border-color: #2d333b !important;
}
.btn-primary:hover { background: #3d444c !important; border-color: #3d444c !important; }
.btn-primary:disabled { background: #6a737d !important; border-color: #6a737d !important; }

/* ============================================================
   Modal dialog
   Reuses the .table-wrap card recipe so it matches the rest of
   the surface treatment.
   ============================================================ */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: oklch(20% 0.02 260 / 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  z-index: 100;
}

.modal-card {
  background: var(--color-paper-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  width: min(640px, 100%);
  max-height: 85vh;
  overflow-y: auto;
}

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

.modal-title {
  font-size: var(--text-lg);
  font-weight: 600;
  margin: 0;
}

/* Topbar identity. A button rather than a span because it copies the full
   subject on click — the label itself is elided. */
.identity-chip {
  background: none;
  border: none;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius);
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}

.identity-chip:hover {
  background: var(--color-paper-3);
  color: var(--color-ink);
}

/* Which customer the console is acting on.
   Given a border and its own background rather than styled as another quiet
   chip: every destructive action on these pages lands on somebody's production
   network, so this is the one control in the bar that should read as a mode,
   not as metadata. */
.tenant-select {
  background: var(--color-paper-3);
  border: 1px solid var(--color-line);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius);
  color: var(--color-ink);
  font-size: var(--text-sm);
  cursor: pointer;
  max-width: 16rem;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.tenant-select:hover,
.tenant-select:focus-visible {
  border-color: var(--color-accent, var(--color-ink-2));
}

.modal-close {
  background: none;
  border: none;
  color: var(--color-ink-2);
  cursor: pointer;
  font-size: var(--text-lg);
  line-height: 1;
  padding: var(--space-1);
  border-radius: var(--radius);
  transition: background var(--dur-fast) var(--ease-out);
}

.modal-close:hover {
  background: var(--color-paper-3);
  color: var(--color-ink);
}

.modal-body {
  padding: var(--space-4);
}

/* Secret / command blocks inside the dialog */
.token-field {
  display: flex;
  gap: var(--space-2);
  align-items: stretch;
  margin-bottom: var(--space-3);
}

.token-value {
  flex: 1;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--text-sm);
  background: var(--color-paper-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-2);
  overflow-wrap: anywhere;
  min-width: 0;
}

.command-block {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--text-sm);
  background: var(--color-paper-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-3);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: 0 0 var(--space-3);
}

.modal-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink-2);
  margin-bottom: var(--space-2);
}

/* Policy detail and editor. A full-width pane rather than a modal: .modal-card
   is capped at 640px, and a policy bundle is 13 KB of JSON. */
.editor { display: flex; flex-direction: column; gap: var(--space-4); }
.editor-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.editor-title { margin: 0; font-size: 16px; font-weight: 600; color: var(--color-ink-1); }
.editor-sub { margin: 4px 0 0; font-size: 12px; color: var(--color-ink-3); max-width: 70ch; }
.draft-offer {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--color-warn-bg); border: 1px solid var(--color-warn);
  border-radius: var(--radius-md); font-size: 13px; color: var(--color-ink-1);
}
.draft-offer-note { margin: 4px 0 0; font-size: 12px; color: var(--color-ink-2); max-width: 70ch; }
.draft-offer-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
@media (max-width: 640px) {
  .draft-offer { flex-direction: column; }
}

.editor-strip {
  display: flex; flex-wrap: wrap; gap: var(--space-5);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.editor-strip-label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--color-ink-3); margin-bottom: 2px; }
.editor-area {
  width: 100%; min-height: 46vh; resize: vertical; box-sizing: border-box;
  padding: var(--space-3); font-size: 12px; line-height: 1.5;
  color: var(--color-ink-1); background: var(--color-surface-1);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
}
.editor-area:focus { outline: none; border-color: var(--color-accent); }
.editor-area[readonly] { color: var(--color-ink-2); background: var(--color-surface-2); }
.editor-status { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.editor-hash { font-size: 12px; color: var(--color-ink-3); }
.editor-actions { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.diff-list { margin: var(--space-2) 0; padding-left: var(--space-4); font-size: 12px; color: var(--color-ink-2); }
.rollout {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4); border: 1px solid var(--color-border);
  border-radius: var(--radius-md); background: var(--color-surface-2);
}
.tbl tbody tr.clickable { cursor: pointer; }

/* Structured policy form: the same bundle as the textarea, projected. */
.form-panel { display: flex; flex-direction: column; gap: var(--space-3); }
.form-tabs { display: flex; flex-wrap: wrap; gap: 4px; border-bottom: 1px solid var(--color-border); }
.form-tab {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px; font: inherit; font-size: 13px; cursor: pointer;
  color: var(--color-ink-2); background: none; border: none;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.form-tab:hover { color: var(--color-ink-1); }
.form-tab.active { color: var(--color-accent); border-bottom-color: var(--color-accent); }
.form-tab-count {
  font-size: 11px; padding: 1px 6px; border-radius: 999px;
  color: var(--color-ink-3); background: var(--color-surface-2);
}
.chip-row { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  padding: 5px 10px; font: inherit; font-size: 12px; cursor: pointer;
  color: var(--color-ink-2); background: var(--color-surface-1);
  border: 1px solid var(--color-border); border-radius: 999px;
}
.chip:hover { color: var(--color-ink-1); }
.chip-on { color: #fff; background: var(--color-accent); border-color: var(--color-accent); }
/* Explanatory prose under a field. Sits below the input it belongs to, so it
   needs the tighter leading and the quieter colour that mark it as guidance
   rather than a value. */
.form-hint {
    margin: 4px 0 0;
    font-size: var(--text-sm);
    line-height: 1.5;
    color: var(--color-ink-3);
    max-width: 68ch;
}

.form-grid {
  display: grid; gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.form-grid label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-ink-3);
}
.form-derived {
  padding: 8px 0; font-size: 13px; text-transform: none; letter-spacing: 0;
  color: var(--color-ink-1);
}

/* ── Topology ───────────────────────────────────────────────────────────── */

.topo-wrap { padding: var(--space-4); }

/* The legend earns its place: four stroke styles are not self-explanatory,
   and the one that matters most -- a link policy asks for and no edge
   reports -- is the one nobody would guess. */
.topo-legend {
    display: flex; flex-wrap: wrap; gap: var(--space-4);
    margin-bottom: var(--space-3);
    font-size: var(--text-sm); color: var(--color-ink-3);
}
.topo-key { display: inline-flex; align-items: center; gap: var(--space-2); }
.topo-swatch { display: inline-block; width: 22px; height: 0; border-top-width: 2px; }

/* Drawn at its natural size, which the page sets as max-width, and smaller
   only to fit; below these widths the names and numbers would shrink past
   reading, so the picture scrolls instead. */
.topo-scroll { overflow-x: auto; }
.topo-canvas { display: block; width: 100%; height: auto; margin: 0 auto; }
.topo-canvas-columns { min-width: 860px; }
.topo-canvas-ring { min-width: 640px; }

.topo-node { cursor: default; }
.topo-dot { stroke: var(--color-paper); stroke-width: 3; }
.topo-dot-ok { fill: var(--color-ok); }
.topo-dot-warn { fill: var(--color-warn); }
.topo-dot-err { fill: var(--color-err); }
.topo-dot-muted { fill: var(--color-ink-3); opacity: 0.45; }

/* A relay gets a ring rather than its own fill, so it can still show whether
   it is online -- which is the more urgent fact about it. */
.topo-relay-ring {
    fill: none; stroke: var(--color-accent); stroke-width: 1.5;
    stroke-dasharray: 4 3; opacity: 0.8;
}


/* ── Network ────────────────────────────────────────────────────────────── */

.net-banner { margin-bottom: var(--space-4); }
.net-tabs { margin-bottom: var(--space-4); }
.net-hint { margin: 0 0 var(--space-3); }

.net-select {
    background: var(--color-paper-2);
    border: 1px solid var(--color-border);
    padding: 6px var(--space-2);
    border-radius: var(--radius);
    color: var(--color-ink);
    font: inherit;
    font-size: var(--text-sm);
    cursor: pointer;
    max-width: 18rem;
    transition: border-color var(--dur-fast) var(--ease-out);
}
.net-select:hover,
.net-select:focus-visible { border-color: var(--color-accent); }

/* One pill per group the path is judged against; they wrap rather than
   widen the table. */
.sla-list { display: flex; flex-wrap: wrap; gap: 4px; }

/* Routes: one small table per prefix, headed by what the source does with
   it. */
.route-list { display: flex; flex-direction: column; gap: var(--space-3); }
.route-head {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--color-paper-3);
    border-bottom: 1px solid var(--color-border);
}
.route-prefix { font-weight: 600; color: var(--color-ink); }
.route-domain { font-size: var(--text-xs); color: var(--color-ink-3); }

/* Every prefix renders as its own table, so pin them all to one column
   template with table-layout:fixed. The five columns then line up down the
   whole list instead of each card sizing itself to its own content. */
.route-tbl { table-layout: fixed; }
.route-tbl th:nth-child(1), .route-tbl td:nth-child(1) { width: 26%; }
.route-tbl th:nth-child(2), .route-tbl td:nth-child(2) { width: 16%; }
.route-tbl th:nth-child(3), .route-tbl td:nth-child(3) { width: 14%; }
.route-tbl th:nth-child(4), .route-tbl td:nth-child(4) { width: 14%; }
.route-tbl th:nth-child(5), .route-tbl td:nth-child(5) { width: 30%; }

/* ── Charts ─────────────────────────────────────────────────────────────── */

.chart-wrap { padding: var(--space-4); margin-top: var(--space-4); }
.chart-title {
    margin: 0 0 var(--space-2);
    font-size: var(--text-sm); font-weight: 600; color: var(--color-ink);
}
.chart-legend {
    display: flex; flex-wrap: wrap; gap: var(--space-4);
    margin-bottom: var(--space-3);
    font-size: var(--text-xs); color: var(--color-ink-2);
}
.chart-key { display: inline-flex; align-items: center; gap: var(--space-2); }
.chart-swatch-dot {
    display: inline-block; width: 10px; height: 10px;
    border-radius: 50%; background: var(--chart-1);
}
.chart-swatch-min { display: inline-block; width: 2px; height: 14px; background: var(--color-ink-2); }
.chart-swatch-ref { display: inline-block; width: 0; height: 14px; border-left: 2px dashed var(--chart-ref); }

.chart-svg { max-width: 100%; height: auto; display: block; font-family: var(--font-body); }
/* Recessive chrome: the dots are the data. */
.chart-grid { stroke: var(--chart-grid); stroke-width: 1; }
.chart-ref { stroke: var(--chart-ref); stroke-width: 1.5; stroke-dasharray: 4 3; }
.chart-axis-label {
    font-size: 11px; fill: var(--color-ink-3);
    text-anchor: middle; font-variant-numeric: tabular-nums;
}
.chart-row-label {
    font-size: 12px; fill: var(--color-ink-2);
    text-anchor: end; dominant-baseline: middle;
}
/* The hit target is the whole row band, so a tooltip is easy to reach. */
.chart-hit { fill: transparent; }
.chart-row:hover .chart-hit { fill: var(--color-accent-light); }
.chart-min { stroke: var(--color-ink-2); stroke-width: 2; stroke-linecap: round; }
/* A surface ring keeps a dot legible where it overlaps the minimum tick. */
.chart-dot { fill: var(--chart-1); stroke: var(--color-paper-2); stroke-width: 2; }
/* A breach is a status, so it takes the status colour -- and a text note
   beside it, so colour is never the only signal. */
.chart-dot-bad { fill: var(--color-err); }
.chart-note { font-size: 11px; fill: var(--color-ink); dominant-baseline: middle; }

/* Hubs and spokes as cards. A hub is the larger, shaded card down one side;
   a spoke a slim card in the middle column. The name sits inside, where no
   line can cross it, and the status is a dot beside it rather than the
   card's fill, so the name never competes with it. */
.topo-node, .topo-link-group { cursor: pointer; outline: none; }
.topo-card { fill: var(--color-paper-2); stroke: var(--color-border); stroke-width: 1.2; }
.topo-card-hub { fill: var(--color-accent-light); stroke: var(--color-accent); stroke-width: 1.6; }
.topo-card-spoke { fill: var(--color-paper-2); }
/* Has not reported the peers it is configured for: its links are policy's
   word alone. */
.topo-card-unreported { stroke-dasharray: 5 4; }
.topo-card-selected,
.topo-node:focus-visible .topo-card { stroke: var(--color-accent); stroke-width: 2.8; }
.topo-card-name { font-size: 13px; font-weight: 600; fill: var(--color-ink); }
.topo-card-hub-name { font-size: 14px; font-weight: 700; fill: var(--color-accent); }
.topo-card-sub {
    font-size: var(--text-xs); fill: var(--color-ink-2);
    letter-spacing: 0.04em; font-variant-numeric: tabular-nums;
}
.topo-card-state { font-size: var(--text-xs); font-weight: 600; fill: var(--color-warn); }
.topo-card-state-err { fill: var(--color-err); }
.topo-status { stroke: var(--color-paper); stroke-width: 1.5; }
/* Between a spoke's port cells and its name. */
.topo-card-divider { stroke: var(--color-border); stroke-width: 1; }
.topo-dot-selected,
.topo-node:focus-visible .topo-dot { stroke: var(--color-accent); stroke-width: 4; }
/* A name on the ring, anchored outward by the layout: no text-anchor here,
   or the stylesheet would override it. */
.topo-ring-label {
    font-size: 13px; font-weight: 600; fill: var(--color-ink);
    paint-order: stroke; stroke: var(--color-paper-2); stroke-width: 3px; stroke-linejoin: round;
}

/* One line per linked pair. Colour is how its measured paths stand and the
   dash pattern repeats it, so colour is never the only signal; a line with
   no measurement is grey, and policy's kind sets its dash. Width is the
   busiest path's utilization, set as an attribute. */
.topo-link { fill: none; stroke-linecap: round; }
.topo-link-preferred { stroke-dasharray: none; }
.topo-link-backup { stroke-dasharray: 7 5; }
.topo-link-permitted { opacity: 0.45; }
.topo-link-policy { stroke: var(--color-ink-3); }
.topo-link-healthy { stroke: var(--color-ok); }
.topo-link-reduced { stroke: var(--color-warn); }
.topo-link-degraded { stroke: var(--color-warn); stroke-dasharray: 8 4; }
.topo-link-probing { stroke: var(--color-info); stroke-dasharray: 3 5; }
.topo-link-down { stroke: var(--color-err); stroke-dasharray: 2 5; }
/* Policy asks for it and both ends report no session: the loudest line. */
.topo-link-missing { stroke: var(--color-err); stroke-dasharray: 3 4; }
.topo-link-selected,
.topo-link-group:focus-visible .topo-link { stroke-width: 4.5; }
/* A wide transparent twin, so a thin line is easy to hit. */
.topo-hit { fill: none; stroke: transparent; stroke-width: 14; pointer-events: stroke; }
/* A link's numbers, in its port: a cell of the spoke card facing the other
   end, where no line runs. The chip under the text is the card's own colour
   when all is well, and tinted when the link needs a look, so a problem reads
   from across the room; over the arc between two hubs it also keeps the
   lines out from behind the text. */
.topo-port-group { cursor: pointer; }
.topo-port { fill: var(--color-paper-2); }
.topo-port-warn { fill: var(--color-warn-bg); }
.topo-port-err { fill: var(--color-err-bg); }
.topo-port-info { fill: var(--color-info-bg); }
.topo-port-text {
    font-size: var(--text-xs); fill: var(--color-ink-2);
    font-variant-numeric: tabular-nums;
}
.topo-port-text-warn { fill: var(--color-warn); font-weight: 600; }
.topo-port-text-err { fill: var(--color-err); font-weight: 600; }
.topo-port-text-info { fill: var(--color-info); font-weight: 600; }
.topo-port-text-muted { fill: var(--color-ink-3); }
.topo-port-text-selected { font-weight: 700; }
/* What a selection leaves out. */
.topo-dim { opacity: 0.16; }

.topo-swatch.topo-link-healthy { border-top: 3px solid var(--color-ok); }
.topo-swatch.topo-link-reduced { border-top: 3px solid var(--color-warn); }
.topo-swatch.topo-link-degraded { border-top: 3px dashed var(--color-warn); }
.topo-swatch.topo-link-probing { border-top: 3px dotted var(--color-info); }
.topo-swatch.topo-link-down { border-top: 3px dotted var(--color-err); }
.topo-swatch.topo-link-missing { border-top: 2.5px dashed var(--color-err); }
.topo-swatch.topo-link-policy { border-top: 2px solid var(--color-ink-3); }
.topo-swatch.topo-link-backup { border-top: 2px dashed var(--color-ink-3); }
.topo-chip {
    display: inline-block; width: 22px; height: 12px; border-radius: 3px;
    border: 1.2px solid var(--color-border); background: var(--color-paper-2);
}
.topo-chip-hub { border: 1.6px solid var(--color-accent); background: var(--color-accent-light); }
.topo-chip-unreported { border-style: dashed; }

/* The pane under the picture: every number the picture leaves out, for
   whatever is selected. */
.topo-details { margin-top: var(--space-4); }
.topo-details-head {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--space-2) var(--space-3); margin-bottom: var(--space-3);
}
.topo-details-title { font-size: var(--text-base); font-weight: 700; color: var(--color-ink); }
.topo-details-meta { font-size: var(--text-sm); color: var(--color-ink-3); }
.topo-hint, .topo-note { font-size: var(--text-sm); color: var(--color-ink-3); }
.topo-hint { margin: var(--space-3) 0 0; }
.topo-note { margin: 0 0 var(--space-3); }

/* "3/4 eligible" in the Fleet table: one phrase, so it never breaks. */
.path-count { white-space: nowrap; }
/* The Fleet Paths column's one pill: its counts line up down the column. */
.path-pill { font-variant-numeric: tabular-nums; }
/* Last seen, revision, credential: short values that wrapped ("1s" over
   "ago") and made every row two lines tall. */
.fleet-tbl td.mono { white-space: nowrap; }
.fleet-tbl td.nowrap { white-space: nowrap; }
/* Ten columns: at 8px a side, not 12, they fit a 1280px screen beside the
   sidebar without the horizontal scroll that hid Credential and Deployment. */
.fleet-tbl th, .fleet-tbl td { padding-inline: var(--space-2); }
/* An edge's name wraps only after a dot (name_parts in fleet.rs). */
.edge-name > span { white-space: nowrap; }
/* Live rates, TX over RX, so the Traffic column is half as wide. */
.traffic-pair { display: inline-grid; line-height: 1.35; white-space: nowrap; }

/* ── Latency ────────────────────────────────────────────────────────────── */

/* The Latency tab's selects stand above the charts, not inside a table. */
.net-toolbar { padding: 0 0 var(--space-3); border-bottom: none; }
.net-subhead { margin: var(--space-5) 0 var(--space-2); }

/* Small multiples: one chart per path, all on one y scale, so their heights
   compare across the grid. */
.lat-grid {
    display: grid; gap: var(--space-3);
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}
.lat-card {
    background: var(--color-paper-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-3);
}
.lat-title {
    margin-bottom: var(--space-2);
    font-size: var(--text-sm); font-weight: 600; color: var(--color-ink);
}
.lat-svg { width: 100%; height: auto; display: block; font-family: var(--font-body); }
.lat-line {
    fill: none; stroke: var(--chart-1); stroke-width: 2;
    stroke-linejoin: round; stroke-linecap: round;
}
.lat-axis {
    font-size: 10px; fill: var(--color-ink-3);
    text-anchor: end; dominant-baseline: middle; font-variant-numeric: tabular-nums;
}

/* The state strip under each line: the worst state in each bucket. A hairline
   in the surface colour separates the cells; a bucket with no row is a gap. */
.strip-cell { stroke: var(--color-paper-2); stroke-width: 0.5; }
.strip-eligible { fill: var(--color-ok); }
.strip-degraded { fill: var(--color-warn); }
.strip-failed { fill: var(--color-err); }
.strip-probing { fill: var(--color-info); opacity: 0.6; }

/* ── Traffic ────────────────────────────────────────────────────────────── */

/* TX and RX keep one colour each wherever they appear: a line's stroke, and
   through currentColor a legend swatch. */
.traffic-tx { stroke: var(--chart-1); color: var(--chart-1); }
.traffic-rx { stroke: var(--chart-2); color: var(--chart-2); }
.chart-swatch-line { display: inline-block; width: 14px; height: 0; border-top: 2px solid currentColor; }

/* The tunnel MTU in the MTU chart: hollow, so the network PMTU's dot reads
   first where the two meet. */
.chart-inner { fill: var(--color-paper-2); stroke: var(--chart-1); stroke-width: 2; }
.chart-swatch-inner {
    display: inline-block; width: 8px; height: 8px;
    border-radius: 50%; border: 2px solid var(--chart-1);
}

/* Mirrored totals: TX grows left of the centre line and RX right, on one
   scale. Each value sits at its bar's outer end. */
.bar-tx { fill: var(--chart-1); }
.bar-rx { fill: var(--chart-2); }
.bar-value {
    font-size: 11px; fill: var(--color-ink-2);
    dominant-baseline: middle; font-variant-numeric: tabular-nums;
}
.bar-value-tx { text-anchor: end; }

/* Egress by class: a 100 % bar, data in the series blue, overhead around it. */
.share-bar {
    display: flex; width: 220px; max-width: 100%; height: 10px;
    border-radius: 2px; overflow: hidden; background: var(--chart-grid);
}
.share-control { background: var(--chart-3); }
.share-data { background: var(--chart-1); }
.share-padding { background: var(--chart-4); }
.share-probe { background: var(--chart-2); }
.share-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; }
.share-note { font-size: var(--text-xs); color: var(--color-ink-3); }
