/* Fonts, color themes, and spacing/radius/timing variables live in
   core/tokens.css now — the one file the future landing page needs to
   share this product's visual identity without the rest of the app. */
@import "../core/tokens.css";
@import "../core/primitives.css";

* {
  font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  height: 100%;
  overflow: hidden;
  /* Base size for any text without its own explicit font-size — every
     other rule in this file caps at 13-14px (16px for headers), so
     this closes the gap rather than falling back to the browser's
     16px UA default. */
  font-size: 13px;
}
a {
  color: var(--accent);
  text-decoration: none;
}
:where(a, button, input, select, textarea, [role="button"]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
button,
[role="button"] {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Themed scrollbars — every scroll container (sidebar, main, modals,
   .table-wrap, .event-feed, textareas) picks these up automatically
   since they're plain descendant rules using the current theme's own
   CSS vars, not a separate per-theme override. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-2) var(--bg-2);
}
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: var(--bg-2);
}
::-webkit-scrollbar-thumb {
  background: var(--border-2);
  border-radius: var(--radius);
  border: 0;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-3);
}
::-webkit-scrollbar-corner {
  background: var(--bg-2);
}
[data-theme="cyberpunk"] ::-webkit-scrollbar-thumb:hover {
  box-shadow: var(--glow);
}
button,
input,
select,
textarea {
  font-family: inherit;
  color: inherit;
}

/* Scanline overlay (cyberpunk) */
[data-theme="cyberpunk"] body::after {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--scanline);
  pointer-events: none;
  z-index: 9999;
  opacity: 0.16;
}
[data-theme="cyberpunk"] .glitch {
  animation: glitch 0.3s infinite;
}
@keyframes glitch {
  0% {
    text-shadow: 2px 0 #FF003C, -2px 0 #00FFFF;
  }
  25% {
    text-shadow: -2px 0 #FF003C, 2px 0 #00FFFF;
  }
  50% {
    text-shadow: 2px 2px #FF003C, -2px -2px #00FFFF;
  }
  75% {
    text-shadow: -1px 0 #FF003C, 1px 0 #00FFFF;
    transform: skewX(0.5deg);
  }
  100% {
    text-shadow: 2px 0 #FF003C, -2px 0 #00FFFF;
  }
}
[data-theme="cyberpunk"] .panel {
  position: relative;
  box-shadow: inset 0 0 20px rgba(252,233,3,0.025);
  clip-path: polygon(8px 0%, 100% 0%, 100% calc(100% - 8px), calc(100% - 8px)
    100%, 0% 100%, 0% 8px);
}
/* clip-path chamfers the top-left/bottom-right corners but can't paint a
   border stroke along the new diagonal edge — it only masks, it never
   adds paint — so the panel's 1px border simply stops at each cut,
   leaving the frame visibly open right at those two corners. These two
   slivers are the same border color/thickness as the panel's own
   border, rotated to exactly bridge each chamfer's hypotenuse (8px cut
   on each side → 8·√2 ≈ 11.3px), so the frame reads as one continuous
   line again. */
[data-theme="cyberpunk"] .panel::before,
[data-theme="cyberpunk"] .panel::after {
  content: '';
  position: absolute;
  width: 11.5px;
  /* A 45°-rotated thin line anti-aliases across a wider band at reduced
     per-row opacity than an axis-aligned border of the same nominal
     width, so it reads visibly thinner/fainter — bumped up to
     compensate so the connector reads as the same weight as the
     panel's own straight border. */
  height: 2px;
  background: var(--border);
  transform: translate(-50%, -50%) rotate(-45deg);
}
[data-theme="cyberpunk"] .panel::before {
  /* Absolutely-positioned children anchor to the padding edge, not the
     border edge — .panel's own 1px border already insets that edge by
     1px, so the true border-box corner sits 1px further out than these
     offsets alone would suggest. 4px (half the chamfer) minus that 1px
     border = 3px. */
  top: 3px;
  left: 3px;
}
[data-theme="cyberpunk"] .panel::after {
  top: calc(100% - 3px);
  left: calc(100% - 3px);
}
/* Same chamfer treatment as .panel — modals and the login box (itself a
   .panel) are the only other bordered containers in the app, so this
   keeps the cut-corner look consistent everywhere instead of stopping
   at panels. */
[data-theme="cyberpunk"] .modal {
  position: relative;
  clip-path: polygon(8px 0%, 100% 0%, 100% calc(100% - 8px), calc(100% - 8px)
    100%, 0% 100%, 0% 8px);
}
[data-theme="cyberpunk"] .modal::before,
[data-theme="cyberpunk"] .modal::after {
  content: '';
  position: absolute;
  width: 11.5px;
  height: 2px;
  background: var(--border-2);
  transform: translate(-50%, -50%) rotate(-45deg);
}
[data-theme="cyberpunk"] .modal::before {
  top: 3px;
  left: 3px;
}
[data-theme="cyberpunk"] .modal::after {
  top: calc(100% - 3px);
  left: calc(100% - 3px);
}
[data-theme="cyberpunk"] .cursor::after {
  content: '█';
  animation: blink 1s step-end infinite;
  color: var(--accent);
}
@keyframes blink {
  50% {
    opacity: 0;
  }
}
[data-theme="cyberpunk"] .btn:hover {
  background: var(--accent);
  color: #000;
  box-shadow: var(--glow);
  transform: none;
}
[data-theme="cyberpunk"] .account-profile,
[data-theme="cyberpunk"] .appearance-choice[aria-pressed="true"] {
  box-shadow: var(--glow);
}
[data-theme="cyberpunk"] .row-active {
  border-left: 0;
  box-shadow: inset 4px 0 8px -4px rgba(0,255,255,0.6);
}
[data-theme="cyberpunk"] .row-failed {
  border-left: 0;
  box-shadow: inset 4px 0 8px -4px rgba(255,0,60,0.6);
}
[data-theme="cyberpunk"] .logo {
  color: #FCE903;
  animation: none;
}
/* Dense workspaces update often. Keep the cyberpunk framing for page
   panels, but use flat, low-compositing surfaces for data-heavy work. */
[data-theme="cyberpunk"] .workspace-panel,
[data-theme="cyberpunk"] .analytics-chart-panel {
  clip-path: none;
  box-shadow: none;
}
[data-theme="cyberpunk"] .workspace-panel::before,
[data-theme="cyberpunk"] .workspace-panel::after,
[data-theme="cyberpunk"] .analytics-chart-panel::before,
[data-theme="cyberpunk"] .analytics-chart-panel::after {
  display: none;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
  }
  [data-theme="cyberpunk"] body::after { display: none; }
}
@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.6;
  }
}

/* ── LAYOUT ──────────────────────────────────────────────
   IDE-app shell: fixed to the viewport, never itself scrolls.
   Only `main` (the per-route content pane) scrolls internally —
   same model as an editor's sidebar/panel staying put while the
   content pane scrolls, rather than a website's long scrolling
   page. */
#app {
  display: flex;
  flex-direction: column;
  /* 100vh is the *largest* possible viewport (mobile Safari's address
     bar collapsed) — with overflow:hidden below, the gap between that
     and the *actual* (often shorter, address bar expanded) viewport
     clips this shell's bottom edge, which is exactly where the status
     bar and sidebar footer live. 100dvh tracks the real, current
     viewport instead; the 100vh line stays first as a fallback for
     browsers that don't support dvh (silently ignored otherwise). */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}
.shell {
  display: flex;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.sidebar {
  width: 252px;
  flex-shrink: 0;
  /* Flat/dark — one step darker than the nav-section-card surfaces below,
     so each section reads as its own card sitting on top of the sidebar
     rather than the sidebar and its links being one continuous panel. */
  background: var(--bg);
  /* No right-hand rule — the content column starts with its own
     background step, which is separation enough now that the sidebar
     itself is just floating cards on flat background. */
  border-right: 0;
  display: flex;
  flex-direction: column;
  padding: 18px 12px;
  position: sticky;
  top: 0;
  height: 100%;
  overflow-y: auto;
  /* Collapsing navigation is an immediate state change. Width animation
     causes a whole-workspace reflow and adds no useful feedback. */
  transition: none;
}
@media (min-width: 901px) {
  .shell.sidebar-collapsed .sidebar {
    border-right-color: transparent;
    overflow: hidden;
    padding-left: 0;
    padding-right: 0;
    width: 0;
  }
}
.sidebar .logo {
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.02em;
  margin-bottom: 24px;
  padding: 0 12px;
}
.sidenav {
  flex: 1;
  display: flex;
  flex-direction: column;
  /* Space between section groups (label+card), not between links within
     one card — see .nav-section-group/.nav-section-card's own gaps. */
  gap: 22px;
}
.nav-section-group {
  display: flex;
  flex-direction: column;
  gap: 9px;
}
/* Each {section, links} group is its own floating card — a small step
   lighter than the sidebar's own flattened background (--bg above).
   Border starts transparent and only appears on hover, so the card reads
   quietly until you're actually pointing at it. overflow:hidden lets the
   links inside go edge-to-edge (no side padding of their own) while still
   getting clipped to the card's rounded corners at the top and bottom.
   Links sit flush against each other — no gap — so the card reads as one
   solid block, only separated by each link's own hover/active fill. */
.nav-section-card {
  background: var(--bg-2);
  border: 1px solid transparent;
  border-radius: 14px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: var(--transition);
}
.nav-section-card:hover {
  border-color: var(--border);
}
.sidenav a {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  color: var(--text-2);
  border: 0;
  /* No radius of its own — .nav-section-card's overflow:hidden clips the
     first/last link to the card's rounded corners instead. */
  border-radius: 0;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
  transition: var(--transition);
}
/* No color-coded border/indicator for the active item — a plain
   background fill against the same gray border every row already
   has is enough to read as selected without a second visual signal. */
.sidenav a:hover {
  color: var(--text);
  border-color: transparent;
  /* Same color as the card's own hover border (.nav-section-card:hover)
     — one consistent "you're pointing at this" color across the card
     and the item, instead of two different grays. */
  background: var(--border);
}
/* The selected page reads as the primary action it is — same accent
   fill as .btn.primary — rather than just a slightly-lighter gray. */
.sidenav a.active {
  color: #fff;
  border-color: transparent;
  background: var(--accent);
  font-weight: 600;
}
.nav-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  flex-shrink: 0;
  opacity: 0.85;
  text-transform: none;
  letter-spacing: normal;
}
/* One color per icon, never repeated between two icons in the same card
   (colors do repeat across different cards — e.g. "history" is always
   violet whether it's Operations' Activity or Help's Activity) — a quick
   visual-scan aid distinct from the active/selected state below, which
   stays reserved for --accent so it's never ambiguous which one you're
   looking at. Fixed hex values, not theme tokens: these are identity
   colors for specific icons, not part of the light/dark/cyberpunk
   palette swap. */
.nav-icon-layout-dashboard,
.nav-icon-history { color: #a78bfa; } /* violet */
.nav-icon-chart-line,
.nav-icon-users { color: #fb7185; } /* rose */
.nav-icon-globe,
.nav-icon-plug { color: #45d6a4; } /* teal */
.nav-icon-map-pin,
.nav-icon-settings-2 { color: #f1b75b; } /* amber */
.nav-icon-route,
.nav-icon-info { color: #38bdf8; } /* sky */
.nav-icon-server,
.nav-icon-shield-check { color: #94a3b8; } /* slate */
.nav-icon-radio { color: #fb923c; } /* coral */
/* Selected state overrides every icon color back to white — no per-icon
   color is ever more important than "this is the current page." */
.sidenav a.active .nav-icon { color: #fff; }
/* cb-button/cb-icon-btn/cb-badge/cb-table/cb-card/cb-stat-tile/cb-field's
   display:contents|block rules moved to core/primitives.css, imported
   above — they're primitive infrastructure, not dashboard-specific. */
.ui-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: 0 0 auto;
  vertical-align: -0.13em;
}
.nav-icon .ui-icon { width: 14px; height: 14px; vertical-align: 0; }
.nav-section-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-3);
  /* Sits above its card now (.nav-section-group), not inside it — no
     margin needed, .nav-section-group's own gap handles the spacing. */
  margin: 0;
  padding: 0 6px;
}
.page-tabs {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-top: 16px;
  padding: 3px;
  max-width: 100%;
  overflow-x: auto;
  background: var(--bg-3);
  border: 0;
  border-radius: var(--radius);
}
.page-tabs a {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 0 10px;
  color: var(--text-2);
  border: 0;
  border-radius: calc(var(--radius) - 2px);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.page-tabs a:hover { color: var(--text); background: var(--bg-2); }
.page-tabs a.active {
  color: var(--text);
  font-weight: 600;
  background: var(--bg-2);
  border-color: var(--border-2);
}
.sidebar-footer {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 4px;
}
.account-trigger {
  align-items: center;
  background: transparent;
  border: 0;
  color: var(--text-2);
  cursor: pointer;
  display: flex;
  gap: 6px;
  min-height: 30px;
  padding: 4px 2px;
  text-align: left;
  transition: var(--transition);
  width: 100%;
}
.account-trigger:hover, .account-trigger:focus-visible {
  background: transparent;
  color: var(--text);
  box-shadow: none;
}
.account-trigger-copy { align-items: baseline; display: flex; gap: 6px; min-width: 0; }
.account-trigger-name {
  color: inherit;
  display: inline;
  font-size: 11px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.account-trigger-role {
  color: var(--text-3);
  display: inline;
  font-size: 10px;
  margin-top: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}
.account-trigger-arrow { color: var(--text-3); font-size: 15px; }
.user-account-trigger {
  gap: 8px;
  min-height: 32px;
  padding: 5px 2px;
}
.user-account-trigger .account-trigger-copy { gap: 7px; }
.user-account-label {
  color: var(--text-2);
  font-size: 11px;
  font-weight: 600;
}
.user-account-trigger .account-trigger-name {
  color: var(--text-3);
  font-size: 10px;
}
.user-account-trigger .account-trigger-arrow {
  margin-left: auto;
  opacity: 0.65;
}
.subscription-detail-footer { align-items: center; display: flex; justify-content: space-between; gap: 10px; }
.subscription-detail-footer .btn { flex: 0 0 auto; }
.appearance-choice-grid {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.appearance-choice-grid.language-choice-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.appearance-choice {
  background: var(--bg-3);
  border: 0;
  color: var(--text-2);
  cursor: pointer;
  min-height: 68px;
  padding: 9px 8px;
  text-align: left;
  transition: var(--transition);
}
.appearance-choice:hover {
  border-color: var(--border-2);
  color: var(--text);
}
.appearance-choice:focus-visible {
  border-color: var(--accent);
  color: var(--text);
}
.appearance-choice[aria-pressed="true"] {
  background: var(--bg-4);
  border-color: var(--border-2);
  font-weight: 600;
  color: var(--text);
}
.appearance-choice strong { display: block; font-size: 11px; letter-spacing: .04em; }
.appearance-choice span { color: var(--text-3); display: block; font-size: 10px; line-height: 1.35; margin-top: 4px; }
.appearance-choice[aria-pressed="true"] span { color: var(--text-2); }
.account-profile {
  align-items: center;
  background: var(--bg-3);
  border: 0;
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
  padding: 13px;
}
.account-profile-mark {
  align-items: center;
  background: var(--bg-4);
  border: 0;
  color: var(--text);
  display: inline-flex;
  flex: 0 0 auto;
  font-size: 20px;
  height: 40px;
  justify-content: center;
  width: 40px;
}
.account-profile-copy { min-width: 0; }
.account-profile-name { color: var(--text); font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-profile-meta { align-items: center; color: var(--text-2); display: flex; flex-wrap: wrap; font-size: 11px; gap: 6px; margin-top: 5px; }
.account-role-badge { background: var(--bg-4); border: 0; color: var(--text-2); font-size: 10px; letter-spacing: .06em; padding: 2px 6px; text-transform: uppercase; }
.account-preference-section { border-top: 0; padding-top: 14px; }
.account-preference-section + .account-preference-section { margin-top: 16px; }
.account-preference-heading { align-items: baseline; display: flex; gap: 8px; justify-content: space-between; margin-bottom: 9px; }
.account-preference-heading strong { color: var(--text); font-size: 12px; }
.account-preference-heading span { color: var(--text-3); font-size: 10px; text-align: right; }
.account-admin-link {
  align-items: center;
  background: var(--bg-3);
  border: 0;
  color: var(--text);
  cursor: pointer;
  display: flex;
  gap: 10px;
  padding: 11px 12px;
  text-align: left;
  transition: var(--transition);
  width: 100%;
}
.account-admin-link:hover { border-color: var(--border-2); }
.account-admin-link:focus-visible { border-color: var(--accent); }
.account-admin-link strong { display: block; font-size: 12px; }
.account-admin-link span { color: var(--text-2); display: block; font-size: 10px; line-height: 1.4; margin-top: 3px; }
.account-admin-link .account-trigger-arrow { margin-left: auto; }
.hamburger-btn {
  display: none;
  background: var(--bg-2);
  border: 0;
  color: var(--text);
  font-size: 16px;
  line-height: 1;
  border-radius: var(--radius);
  cursor: pointer;
  height: 44px;
  padding: 0;
  touch-action: manipulation;
  width: 44px;
  -webkit-tap-highlight-color: transparent;
}
.sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 59;
}
.notification-backdrop {
  background: rgba(0, 0, 0, 0.38);
  display: none;
  inset: 0;
  position: fixed;
  z-index: 63;
}
.notification-backdrop.open { display: block; }
.notification-drawer {
  background: var(--bg-2);
  border: 0;
  border-radius: var(--radius);
  bottom: 52px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
  display: flex;
  flex-direction: column;
  max-width: calc(100vw - 24px);
  position: fixed;
  right: 12px;
  top: 12px;
  transform: translateX(calc(100% + 24px));
  transition: transform var(--transition);
  width: 390px;
  z-index: 64;
}
.notification-drawer.open { transform: translateX(0); }
.notification-drawer-header {
  align-items: center;
  border-bottom: 0;
  display: flex;
  gap: 10px;
  justify-content: space-between;
  padding: 14px;
}
.notification-drawer-header h2 { font-size: 15px; margin: 0; }
.notification-drawer-header p { color: var(--text-3); font-size: 11px; margin: 3px 0 0; }
.notification-drawer-actions { display: flex; gap: 6px; }
.notification-filter { border-bottom: 0; display: flex; gap: 6px; padding: 10px 14px; }
.notification-filter button { font-size: 10px; }
.notification-list { min-height: 0; overflow: auto; padding: 8px; }
.notification-item {
  border-left: 0;
  display: grid;
  gap: 7px;
  padding: 11px 10px;
}
.notification-item + .notification-item { border-top: 0; }
.notification-item.critical { border-left-color: transparent; }
.notification-item.warning { border-left-color: transparent; }
.notification-item.info { border-left-color: transparent; }
.notification-item.acknowledged { opacity: 0.7; }
.notification-item-top { align-items: start; display: flex; gap: 8px; justify-content: space-between; }
.notification-item-title { color: var(--text); font-size: 12px; font-weight: 700; }
.notification-item-meta { color: var(--text-3); font-size: 10px; text-align: right; white-space: nowrap; }
.notification-item-message { color: var(--text-2); font-size: 11px; line-height: 1.45; }
.notification-item-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.notification-item-actions .btn { font-size: 10px; padding: 4px 7px; }
.notification-badge {
  align-items: center;
  background: var(--danger);
  border-radius: 9px;
  color: #fff;
  display: none;
  font-size: 9px;
  font-weight: 700;
  height: 15px;
  justify-content: center;
  min-width: 15px;
  padding: 0 4px;
  pointer-events: none;
  position: absolute;
  right: -4px;
  top: -3px;
}
.notification-center-toggle { position: relative; }
.notification-center-toggle.has-unread .notification-badge { display: inline-flex; }
.content-col {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  /* Without this, a flex item defaults to min-width: auto — it won't
     shrink below its content's natural width (e.g. a wide table row
     that doesn't wrap). .shell (this element's row-flex parent) then
     gets forced wider than the viewport, and since .shell has
     overflow: hidden, that excess is silently clipped rather than
     reachable via scroll. min-width: 0 lets this column shrink to the
     space .shell actually has, so .table-wrap's own overflow-x: auto
     (further down the tree) is what handles wide content instead. */
  min-width: 0;
}
.status-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 6px 18px;
  /* The shell keeps this bar mounted while routes replace only <main>.
     A stable minimum height also keeps it visually steady as live
     status values change. */
  min-height: 40px;
  /* iPhone home-indicator gesture bar sits right under this row (it's
     the last thing in #app's column) — env() only resolves to a
     non-zero value with the viewport-fit=cover meta tag above; falls
     back to the plain 8px on every other browser. */
  padding-bottom: max(8px, env(safe-area-inset-bottom));
  background: var(--bg-2);
  border-top: 1px solid var(--border);
  font-size: 12px;
  flex-shrink: 0;
  width: 100%;
  overflow: hidden;
  white-space: nowrap;
}
.status-primary,
.status-metric,
.status-toggle-button,
.status-session-button {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
}
button.status-primary,
button.status-metric {
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  padding: 0;
}
.status-primary {
  color: var(--text);
  font-weight: 600;
  min-width: 0;
  padding: 0;
}
.status-primary.signal-only { flex: 0 0 auto; }
.status-primary-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Shared base for every icon-only button (status bar toggle/notifications,
   modal close, and any future one) — one 28x28 box, one radius, one mobile
   tap-target bump, instead of each call site declaring its own near-copy. */
.icon-btn {
  align-items: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-2);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  height: 28px;
  justify-content: center;
  line-height: 1;
  padding: 0;
  width: 28px;
}
.icon-btn:hover,
.icon-btn:focus-visible {
  color: var(--text);
}
@media (max-width: 680px) {
  .icon-btn {
    min-height: var(--tap-target);
    min-width: var(--tap-target);
  }
}
/* .status-toggle-button and .status-toolbar-action now get their box
   model entirely from .icon-btn (see above) — these two rules only carry
   what's specific to this one: no hover rule of its own is needed since
   .icon-btn already defines it identically. */
.status-metric {
  margin-left: 0;
  padding: 0;
}
.status-metric .value {
  font-weight: 600;
}
.status-metric .value.warning {
  color: var(--warning);
}
.status-metric .value.danger {
  color: var(--danger);
}
.status-metric.clickable,
.status-primary.clickable {
  cursor: pointer;
}
.status-metric.clickable:hover,
.status-primary.clickable:hover {
  color: var(--text);
}
.status-metric.clickable:focus-visible,
.status-primary.clickable:focus-visible,
.status-toolbar-action:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.status-actions {
  align-items: center;
  display: flex;
  flex-shrink: 0;
  gap: 8px;
  margin-left: auto;
  /* A light divider reads better than relying on margin-left:auto's gap
     alone to separate live metrics (data) from actions (navigation) —
     the two groups otherwise use identical button styling and blend
     together at a glance. */
  padding-left: 14px;
  border-left: 1px solid var(--border);
}
.toolbar-action-icon {
  fill: none;
  height: 16px;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.75;
  width: 16px;
}
.worker-list-signal .ui-icon { width: 13px; height: 13px; vertical-align: -0.18em; }
.empty-state .ui-icon { width: 22px; height: 22px; }
.modal-close .ui-icon { width: 16px; height: 16px; vertical-align: 0; }
/* The status bar's account icon (shell.js, next to the notifications
   bell) is the sole account entry point on mobile now — the sidebar's
   own trigger is redundant there and this app's sidebar is usually
   collapsed behind the hamburger menu on narrow screens anyway. Desktop
   keeps both: the toolbar icon for one-click access without opening the
   sidebar, and this trigger for its extra at-a-glance username/role. */
@media (max-width: 900px) {
  .sidebar-account-trigger { display: none; }
}
main {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  min-width: 0;
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding: 18px;
  /* A generous ceiling, not a centered content column — this is an
     app panel that should use the space it's given, not a website
     article width. Only matters at all on genuinely ultra-wide
     displays; every normal viewport fills edge-to-edge. */
  max-width: 2400px;
  width: 100%;
  /* Route content already receives a loading state when needed. Fading
     the entire scroll surface on every navigation makes rapid use feel
     like a refresh, and costs extra compositing work on mobile. */
}
/* Directories and policy tables should belong to the document, not an
   independently scrolling, viewport-sized panel.  This is deliberately
   opt-in: high-churn operational lists can still use the denser live
   workspace treatment below. */
.natural-workspace {
  flex: 0 0 auto;
  min-height: auto;
}
.natural-workspace > .panel-header {
  position: static;
  background: transparent;
}
.natural-workspace .table-wrap {
  flex: 0 0 auto;
  height: auto;
  max-height: none;
}
.natural-workspace .table-wrap table thead th {
  position: static;
}
.workspace-loading {
  display: grid;
  gap: 10px;
  min-height: 220px;
  padding: 14px 0;
}
/* Generic route-loading placeholder — shown by render() the instant a
   route's async fetches start, replaced once the route's own
   main.innerHTML write lands. Not page-specific (the router doesn't
   know what shape a given route's content will take), just enough
   shimmer to read as "loading" instead of a blank flash on a slow
   connection. */
.skeleton-row {
  height: 14px;
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--bg-3) 25%, var(--bg-4) 50%,
    var(--bg-3) 75%);
  background-size: 200% 100%;
  animation: skeletonShimmer 1.4s ease-in-out infinite;
  margin-bottom: 10px;
}
@keyframes skeletonShimmer {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

.table-wrap {
  overflow-x: auto;
  /* Long operational tables retain their compact scroll pane, but their
     headers remain part of the document flow. A floating table heading
     can overlap rows and page titles as the surrounding workspace moves. */
  overflow-y: auto;
  max-height: 420px;
  /* Prevent a scrollbar appearing after a live update from moving
     columns sideways by a few pixels. */
  scrollbar-gutter: stable;
}
.table-wrap table thead th {
  position: static;
}
.group-header td {
  cursor: pointer;
  background: var(--bg-3);
  font-weight: 600;
}
.group-header:hover td {
  background: var(--border);
}
.pager {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: flex-end;
  margin-top: 10px;
  font-size: 12px;
}
.pager select {
  /* Matched to the pager's own .btn.small Prev/Next buttons — same
     mismatch as the header-row .control-sm fix (the base select
     rule's 13px font is sized for modal forms, not a compact
     toolbar), just not caught there since this is a separate rule. */
  padding: 3px 6px;
  font-size: 11px;
  width: auto;
  flex-shrink: 0;
}

.panel {
  background: var(--bg-2);
  border: 0;
  border-radius: var(--radius);
  padding: 14px;
  margin-bottom: 12px;
}
/* A route with one primary workspace should use the canvas rather than
   look like a small card placed inside a larger empty card. Multi-panel
   dashboards keep the normal panel chrome, where it conveys grouping. */
main > .panel:only-child {
  flex: 1;
  min-height: 0;
  margin-bottom: 0;
  padding: 0;
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
main > .panel:only-child > .panel-header {
  padding: 0 0 12px;
  border-bottom-color: transparent;
}
.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-2);
  border-bottom: 0;
  padding-bottom: 8px;
}
/* Workspace headers remain in normal document flow. This keeps the page
   hierarchy predictable while scrolling long tables and avoids a second
   floating bar competing with the fixed status toolbar. */
.workspace-panel > .panel-header {
  position: static;
  background: transparent;
  padding-bottom: 8px;
  margin-top: 0;
}
.workspace-panel .table-wrap {
  border-top: 0;
}
/* Shared operational-table contract. Keep column geometry quiet while
   values refresh, reserve full detail for the row/modal, and make an
   interactive row feel like one deliberate target instead of a row of
   competing buttons. */
.workspace-panel .table-wrap > table,
.natural-workspace .table-wrap > table {
  min-width: 100%;
}
.workspace-panel th,
.workspace-panel td,
.natural-workspace th,
.natural-workspace td {
  font-variant-numeric: tabular-nums;
}
.detail-row:hover td,
.worker-list-row:hover td {
  background: var(--bg-3);
}
.detail-row:focus-visible td,
.worker-list-row:focus-visible td {
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-2));
}
.detail-row > td:first-child,
.worker-list-row > td:first-child {
  box-shadow: inset 2px 0 transparent;
}
.detail-row:hover > td:first-child,
.worker-list-row:hover > td:first-child {
  box-shadow: inset 2px 0 var(--border-2);
}
.detail-row:focus-visible > td:first-child,
.worker-list-row:focus-visible > td:first-child {
  box-shadow: inset 2px 0 var(--accent);
}
.table-wrap table.workspace-wide {
  min-width: 960px;
}
.workspace-wide .system-cell {
  min-width: 250px;
  white-space: normal;
  line-height: 1.45;
}
.workspace-wide .last-seen-cell {
  min-width: 130px;
  white-space: normal;
  line-height: 1.35;
}
.diagnostic-cell {
  max-width: 440px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.failure-table th:last-child,
.failure-table td:last-child {
  width: 64px;
  min-width: 64px;
  text-align: right;
}

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.stat-tile {
  background: var(--bg-2);
  /* Same fix as .btn: border:0 left every stat tile relying on bg-2 vs.
     its panel's own bg-2/bg-3 for definition, which is barely visible in
     the dark theme — and it made the .attention/.warning variants below
     unable to show a distinguishing border since 0-width ignores color. */
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}
.stat-tile .label {
  font-size: 11px;
  color: var(--text-2);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}
.stat-tile .value {
  font-size: 16px;
  font-weight: 600;
  margin-top: 4px;
  white-space: nowrap;
}
.stat-tile .value.warning {
  color: var(--warning);
}
.stat-tile .value.danger {
  color: var(--danger);
}
.route-health-bar {
  display: flex;
  height: 16px;
  overflow: hidden;
  background: var(--bg-3);
  border: 0;
  border-radius: var(--radius-sm);
  margin: 14px 0 12px;
}
.route-health-bar span { min-width: 0; }
.route-health-bar .ready { background: var(--success); }
.route-health-bar .recovering { background: var(--warning); }
.route-health-bar .attention { background: var(--danger); }
.availability-timeline-bar {
  display: flex;
  height: 16px;
  overflow: hidden;
  background: var(--bg-3);
  border: 0;
  border-radius: var(--radius-sm);
  margin-bottom: 12px;
}
.availability-segment { min-width: 2px; }
.availability-segment.up { background: var(--success); }
.availability-segment.down { background: var(--danger); }
.availability-timeline-list { display: grid; gap: 8px; }
.availability-timeline-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}
.history-trigger {
  margin-left: 8px;
  padding: 2px 6px;
  vertical-align: middle;
}
.route-health-metrics {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.route-health-metric {
  background: var(--bg-3);
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text);
  cursor: pointer;
  padding: 9px;
  text-align: left;
}
.route-health-metric:hover { border-color: var(--border-2); }
.route-health-metric .label {
  color: var(--text-2);
  display: block;
  font-size: 10px;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.route-health-metric strong { display: block; font-size: 17px; margin-top: 4px; }
.route-health-metric.attention strong { color: var(--danger); }
.route-health-metric.recovering strong { color: var(--warning); }
.health-context {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin-top: 14px;
  font-size: 12px;
}
.health-context button { padding: 0; border: 0; background: none; color: var(--text-2); cursor: pointer; font: inherit; }
.health-context button:hover { color: var(--text); }
.attention-list { margin-top: 12px; border-top: 0; }
.attention-row {
  align-items: center;
  border-bottom: 0;
  display: grid;
  gap: 10px;
  grid-template-columns: minmax(0, 1fr) auto;
  padding: 9px 0;
}
.attention-row .reason { color: var(--text-2); font-size: 11px; margin-top: 3px; }
.stat-detail { color: var(--text-3); display: block; font-size: 10px; line-height: 1.35; margin-top: 5px; text-transform: none; letter-spacing: normal; white-space: normal; }
button.stat-tile { color: var(--text); cursor: pointer; font: inherit; text-align: left; }
button.stat-tile:hover { border-color: var(--border-2); transform: translateY(-1px); }
.dashboard-intro { margin-bottom: 14px; }
.dashboard-action-panel { margin-bottom: 16px; }
.dashboard-secondary-grid .panel { min-height: 0; }
.dashboard-country-table {
  max-height: 420px;
  overflow-y: auto;
}
.providers-workspace {
  flex: 0 1 auto;
}
.provider-intro > div { min-width: 0; }
.provider-intro .page-tabs { margin-bottom: 0; }
#discoveryStats .stat-tile { min-width: 0; }
.providers-table { min-width: 620px; }
.providers-table td { vertical-align: middle; }
.provider-name { align-items: center; display: flex; flex-wrap: wrap; gap: 6px; }
.provider-name strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.provider-role,
.provider-source-tag {
  border: 1px solid transparent;
  color: var(--text-3);
  display: inline-flex;
  font-size: 9px;
  letter-spacing: .06em;
  padding: 2px 5px;
  text-transform: uppercase;
  white-space: nowrap;
}
.provider-role.fallback { border-color: var(--warning); color: var(--warning); }
.provider-source-tag { color: var(--text-2); }
.provider-metric { color: var(--text); font-variant-numeric: tabular-nums; font-weight: 600; }
.provider-detail { color: var(--text-3); font-size: 10px; margin-top: 3px; }
.provider-summary { align-items: center; color: var(--text); display: flex; flex-wrap: wrap; font-size: 12px; font-variant-numeric: tabular-nums; font-weight: 600; gap: 5px; min-width: 0; white-space: nowrap; }
.provider-summary-separator { color: var(--text-3); font-weight: 400; }
.provider-state-dot { background: var(--success); border-radius: 50%; display: inline-block; height: 7px; width: 7px; }
.provider-inline-state { border: 0; font-size: 9px; letter-spacing: .06em; padding: 2px 5px; text-transform: uppercase; }
.provider-inline-state.warning { color: var(--warning); }
.provider-inline-state.danger { color: var(--danger); }
.providers-workspace .table-wrap {
  max-height: min(460px, 55dvh);
}
.workspace-toolbar {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 12px;
}
.workspace-toolbar .control-sm { min-width: 170px; }
.workspace-toolbar .muted { font-size: 11px; margin-left: auto; }
.discovery-note {
  border-left: 0;
  color: var(--text-2);
  display: grid;
  font-size: 12px;
  gap: 4px;
  line-height: 1.45;
  margin-top: 14px;
  padding: 8px 10px;
}
.discovery-note strong { color: var(--text); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; }

table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
}
th {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 10px;
  text-align: left;
  border-bottom: 0;
  color: var(--text-3);
  padding: 6px 10px;
  white-space: nowrap;
}
td {
  border-bottom: 0;
  padding: 8px 10px;
  white-space: nowrap;
}
/* Shared by Configs/Tunnels/Users' trailing action-button column —
   one width across all three so the column reads as "the same kind
   of thing" on every list page rather than each sizing to its own
   row content (2 buttons vs. Tunnels' 3). */
.col-actions {
  min-width: 190px;
}
tr:hover td {
  background: var(--bg-3);
}

.btn {
  background: var(--bg-3);
  /* A button is an interactive control, not passive content — bg-3 alone
     barely contrasts against a panel's own bg-2/bg-3 fill, so unpressed
     buttons in a toggle group (e.g. geo-controls' Traffic/Availability/
     Readiness) read as nearly invisible next to a pressed sibling that
     only stands out because its fill (bg-4) happens to contrast more.
     A light border makes every button legible as a button regardless of
     what it's sitting on, independent of fill contrast. */
  border: 1px solid var(--border);
  color: var(--text);
  padding: 6px 12px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 12px;
  min-height: var(--control-height);
  /* .btn is applied to both <button> (inline-block) and <a> (inline) —
     neither vertically centers its own text once min-height stretches
     the box taller than one line, so text/icon content sat at the top
     instead of the middle (most visible on short-label pill buttons
     like "Open countries"). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  white-space: nowrap;
  flex-shrink: 0;
  transition: var(--transition);
}
.btn:hover {
  border-color: var(--border-2);
  color: var(--text);
}
.btn:active {
  transform: scale(0.97);
}
.btn.danger {
  border-color: var(--danger);
  color: var(--danger);
}
.btn.danger:hover {
  background: var(--danger);
  color: #fff;
}
.btn.danger {
  background: color-mix(in srgb, var(--danger) 8%, var(--bg-3));
}
.btn.primary {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
  font-weight: 600;
}
.btn.primary:hover {
  color: #fff;
  border-color: var(--accent);
  filter: brightness(1.08);
}
.btn.small {
  min-height: 28px;
  padding: 3px 8px;
  font-size: 11px;
}
/* Reserved for the single most important action per screen (Sign In,
   Save/Create in a modal footer) — distinguishes it from a plain
   .primary button by size, not just color, since color alone is easy
   to miss against the higher-contrast themes. */
.btn.large {
  padding: 10px 20px;
  font-size: 14px;
}

input[type=text],
input:not([type]),
input[type=search],
input[type=url],
input[type=password],
input[type=email],
input[type=number],
input[type=file],
select,
textarea {
  background: var(--bg-3);
  /* Same fix as .btn/.stat-tile: bg-3 alone barely contrasts against a
     modal or panel's own bg-2/bg-3 fill, so a form field read as bare
     unbordered space rather than an input until it was focused. */
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 10px;
  border-radius: var(--radius);
  font-size: 13px;
  min-height: var(--control-height);
  min-width: 0;
  width: 100%;
}
input[type=file] {
  cursor: pointer;
  padding: 6px 8px;
}
input[type=file]::file-selector-button {
  background: var(--bg-4);
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text);
  cursor: pointer;
  font: inherit;
  margin-right: 8px;
  padding: 4px 8px;
}
input[type=file]::file-selector-button:hover { border-color: var(--border-2); color: var(--text); }
/* Every other input type is a name/URL/free-text field that genuinely
   benefits from the full row width — a number field never holds more
   than a handful of digits (a port, a retry count, a GB limit), so
   stretching it edge to edge just leaves the rest of the row looking
   like unfinished layout. Each of these .field wrappers stacks its
   label above the input already, so a narrower input just leaves
   trailing whitespace on the same line — not a layout break. */
input[type=number] {
  width: 140px;
}
input::placeholder,
textarea::placeholder {
  color: var(--text-3);
  opacity: 1;
}
/* Keep native checkboxes accessible while giving their checked state the
   active theme color in every dashboard theme. */
input[type=checkbox] {
  accent-color: var(--accent);
}
/* Header-row search boxes and filter/group-by selects, sized to match
   the .btn.small buttons they always sit next to in these toolbars
   (Recheck All, + Import, + New User) — the base input/select rule's
   8px/13px sizing is tuned for modal form fields, and next to a
   small button it renders visibly taller (measured ~36px vs. the
   button's ~22px) rather than sharing one toolbar height. min-height
   is set explicitly (not just padding+font-size) since the base rule's
   min-height: var(--control-height) otherwise wins regardless of the
   smaller padding — that was the actual gap, not the padding value. */
input.control-sm,
select.control-sm {
  padding: 3px 8px;
  font-size: 11px;
  min-height: 28px;
}
input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent);
  /* The border-color swap alone is a 1px difference — easy to miss for
     keyboard-only navigation, especially on the darker themes. A glow
     ring makes "this is the focused field" readable at a glance. */
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
}
label {
  font-size: 11px;
  color: var(--text-2);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  display: block;
  margin-bottom: 4px;
  /* Not nowrap — a long descriptive label (e.g. this modal's "Dashboard
     Login: Max Attempts (blank = disabled)") has nowhere to go on a
     narrow viewport and forced .modal wider than its own max-width: 90vw
     cap, since only overflow-y is handled there. Wrapping to a second
     line costs nothing visually for the short single-word labels
     elsewhere that never wrapped anyway. */
}
.field {
  margin-bottom: 12px;
}
/* Settings: one .settings-section-group per group (see pages/settings-
   page.js), a single column of cb-fields inside each cb-card — capped to
   a readable form width so a label and its input never drift far apart
   on a wide screen, and so one long settings form doesn't visually
   compete with a two-column layout the way the previous grid-2 sub-
   layouts did. */
.settings-columns {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 640px;
}
.settings-columns .panel {
  margin-bottom: 0;
}
/* Title+icon sit above the card — the same nav-section-label/
   nav-section-card language the sidebar uses, applied here via its own
   classes rather than cb-card's shared (and still-used-elsewhere)
   inside-the-card title/subtitle. */
.settings-section-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.settings-section-label {
  align-items: baseline;
  display: flex;
  justify-content: space-between;
  padding: 0 2px;
}
.settings-section-title {
  align-items: center;
  color: var(--text-2);
  display: flex;
  font-size: 11px;
  font-weight: 600;
  gap: 7px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.settings-section-title .ui-icon { width: 14px; height: 14px; }
.settings-section-label .muted { font-size: 11px; }
/* Same per-icon identity colors as the sidebar (ui/app/shell.js's
   nav-icon-* rules) — reused here for a consistent visual language
   across the app, not because these specific icons need to match the
   sidebar's actual nav items. */
.settings-section-icon-globe,
.settings-section-icon-plug { color: #45d6a4; } /* teal */
.settings-section-icon-search { color: #38bdf8; } /* sky */
.settings-section-icon-key-round { color: #94a3b8; } /* slate */
.settings-section-icon-users { color: #fb7185; } /* rose */
.settings-section-icon-wifi { color: #f1b75b; } /* amber */
.settings-section-icon-shield-check { color: #a78bfa; } /* violet */
.settings-section-icon-bell { color: #fb923c; } /* coral */
/* Same transparent-until-hover border + more-rounded corners as the
   sidebar's nav-section-card. */
.settings-columns cb-card {
  border: 1px solid transparent;
  border-radius: 14px;
  transition: var(--transition);
}
.settings-columns cb-card:hover {
  border-color: var(--border);
}
.settings-intro {
  align-items: flex-start;
  background: var(--bg-3);
  border: 0;
  border-radius: 14px;
  display: flex;
  gap: 9px;
  margin-bottom: 16px;
  max-width: 640px;
  padding: 11px 12px;
}
.settings-intro-mark { color: var(--accent); font-size: 14px; line-height: 1.2; }
.settings-intro-mark.ui-icon { width: 16px; height: 16px; }
.settings-intro strong { color: var(--text); display: block; font-size: 11px; margin-bottom: 3px; }
.settings-intro span:last-child { color: var(--text-2); display: block; font-size: 11px; line-height: 1.45; }
/* Inline "explain this" callout for use between fields inside a card —
   see settingsInfo() in pages/settings-page.js. Deliberately more compact
   than .settings-intro above (that one is a page-level banner; this sits
   inside a card next to other fields, so it stays to one line where
   possible and never grows past two). */
.settings-info {
  align-items: center;
  background: var(--bg-3);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--text-2);
  display: flex;
  font-size: 11px;
  gap: 7px;
  line-height: 1.4;
  margin-bottom: 12px;
  padding: 7px 10px;
}
.settings-info .ui-icon { color: var(--accent); flex-shrink: 0; height: 13px; width: 13px; }
/* Gives the Save/Discard row its own visual identity instead of two
   buttons floating in whitespace under the last card. */
.settings-footer {
  align-items: center;
  background: var(--bg-2);
  border: 1px solid transparent;
  border-radius: 14px;
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 4px;
  max-width: 640px;
  padding: 14px 16px;
  transition: var(--transition);
}
.settings-footer:hover {
  border-color: var(--border);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: var(--radius);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
}
/* Theoretical/inactive countries — same badge, faded rather than a
   different color scheme, so "active vs not yet connected" reads as
   one visual family. */
.badge.inactive {
  opacity: 0.45;
}
.badge.connected {
  color: var(--success);
  border: 0;
}
.badge.starting {
  color: var(--text-2);
  border: 0;
}
.badge.failed {
  color: var(--danger);
  border: 0;
}
.badge.reconnecting {
  color: var(--warning);
  border: 0;
}
.badge.permanently_failed {
  color: var(--danger);
  border: 0;
  font-weight: 600;
}
.badge.stopped {
  color: var(--text-3);
  border: 0;
}
.badge.online {
  color: var(--success);
}
.badge.offline {
  color: var(--danger);
}

.dropzone {
  border: 0;
  border-radius: var(--radius);
  padding: 30px;
  text-align: center;
  color: var(--text-2);
  font-size: 13px;
  cursor: pointer;
  margin-bottom: 16px;
}
.dropzone.dragover {
  border-color: transparent;
  color: var(--text);
  background: var(--bg-4);
}

.progress {
  background: var(--bg-3);
  border: 0;
  height: 14px;
  border-radius: var(--radius);
  overflow: hidden;
}
.progress .fill {
  height: 100%;
  background: var(--accent);
}
.progress .fill.danger {
  background: var(--danger);
}

/* Geography is a lightweight SVG projection, not a tile map. It keeps
   the dashboard self-contained, works without a domain/API key, and is
   cheap enough for the mobile admin surface. */
.geo-toolbar {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: space-between;
  margin-bottom: 12px;
}
.geo-controls { display: flex; flex-wrap: wrap; gap: 6px; }
.geo-controls .btn[aria-pressed="true"] {
  background: var(--bg-4);
  border-color: var(--border-2);
  color: var(--text);
  font-weight: 600;
}
.geo-map-shell {
  background: var(--bg-3);
  border: 0;
  min-height: 260px;
  overflow: hidden;
}
.geo-map {
  display: block;
  height: auto;
  max-height: 520px;
  width: 100%;
}
.geo-map-grid line { stroke: var(--border); stroke-opacity: .2; stroke-width: 1; }
.geo-map-grid path { fill: none; stroke: var(--border); stroke-opacity: .32; }
.geo-country {
  fill: var(--bg-2);
  stroke: var(--border);
  stroke-linejoin: round;
  stroke-width: .7;
  transition: fill .16s ease, stroke .16s ease;
}
.geo-country.has-data {
  fill: var(--geo-country-fill);
  fill-opacity: var(--geo-country-opacity, .78);
}
.geo-country-link { cursor: pointer; }
.geo-country-link:hover .geo-country, .geo-country-link:focus .geo-country {
  stroke: var(--text);
  stroke-width: 1.35;
  outline: none;
}
.geo-marker { cursor: pointer; stroke: var(--bg); stroke-width: 1.2; }
.geo-marker:hover, .geo-marker:focus { stroke: var(--text); stroke-width: 2.4; outline: none; }
.geo-map-legend {
  color: var(--text-2);
  display: flex;
  flex-wrap: wrap;
  font-size: 11px;
  gap: 10px 16px;
  margin-top: 10px;
}
.geo-map-legend span::before {
  background: currentColor;
  border-radius: 50%;
  content: "";
  display: inline-block;
  height: 8px;
  margin-right: 5px;
  width: 8px;
}
.geo-map-legend .good { color: var(--success); }
.geo-map-legend .warn { color: var(--warning); }
.geo-map-legend .bad { color: var(--danger); }
.geo-map-legend .info { color: var(--accent); }
.event-feed {
  max-height: 340px;
  overflow-y: auto;
  font-size: 12px;
}
.activity-item {
  align-items: start;
  background: transparent;
  border: 0;
  border-bottom: 0;
  border-left: 0;
  color: var(--text);
  cursor: pointer;
  display: grid;
  gap: 8px;
  grid-template-columns: 64px minmax(0, 1fr) auto;
  padding: 9px 6px;
  text-align: left;
  width: 100%;
}
.activity-item:hover { background: var(--bg-3); border-left-color: var(--border-2); }
.activity-item:focus-visible { background: var(--bg-3); border-left-color: transparent; }
.activity-item.failure { border-left-color: transparent; }
.activity-item.warning { border-left-color: transparent; }
.activity-time { color: var(--text-3); font-size: 10px; line-height: 1.45; padding-top: 1px; white-space: nowrap; }
.activity-copy { min-width: 0; }
.activity-title { color: var(--text); font-weight: 600; display: block; font-size: 11px; line-height: 1.3; }
.activity-item.failure .activity-title { color: var(--danger); }
.activity-item.warning .activity-title { color: var(--warning); }
.activity-summary { color: var(--text-2); display: -webkit-box; font-size: 11px; line-height: 1.4; margin-top: 3px; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.activity-open { color: var(--text-3); font-size: 15px; line-height: 1; padding-top: 3px; }
.event-detail-message { color: var(--text); line-height: 1.55; margin: 0; overflow-wrap: anywhere; white-space: pre-wrap; }
.event-detail-list { margin: 0; }
.event-detail-list > div { border-top: 0; display: grid; gap: 12px; grid-template-columns: 132px minmax(0, 1fr); padding: 8px 0; }
.event-detail-list > div:first-child { border-top: 0; padding-top: 0; }
.event-detail-list dt { color: var(--text-3); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
.event-detail-list dd { color: var(--text); margin: 0; overflow-wrap: anywhere; }

.terminal-input {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-3);
  border: 0;
  border-radius: var(--radius);
  padding: 6px 10px;
}
.terminal-input .prompt {
  color: var(--text-2);
}
.terminal-input input {
  background: none;
  border: 0;
  padding: 0;
}

.login-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
}
.login-box {
  width: 340px;
  padding: 24px;
}
.login-box h1 {
  margin: 0 0 20px;
  font-size: 16px;
  letter-spacing: 0.05em;
}
.error-text {
  color: var(--danger);
  font-size: 12px;
  margin-top: 8px;
}
.muted {
  color: var(--text-2);
  font-size: 12px;
}
.text-warning {
  color: var(--warning);
}
.text-danger {
  color: var(--danger);
}
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 28px 16px;
  color: var(--text-2);
  text-align: center;
}
.empty-state .empty-state-icon {
  font-size: 22px;
  opacity: 0.45;
  margin-bottom: 4px;
}
.empty-state .empty-state-text {
  font-size: 13px;
}
.empty-state .empty-state-hint {
  font-size: 12px;
  opacity: 0.75;
}
.empty-chart {
  min-height: 220px;
  border: 0;
  background: color-mix(in srgb, var(--bg-3) 70%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
}
.grid-2 {
  display: grid;
  /* minmax(0, 1fr), not bare 1fr — a grid track's implicit min-width
     is auto (its content's min-content size) unless overridden, so a
     wide-content sibling (e.g. a multi-column table) can force an
     even 1fr/1fr split wildly unequal, crushing the other column. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 12px;
}
.worker-grid,
.card-grid {
  display: grid;
  /* 220px wasn't wide enough for a hostname like "cb-proxy-worker-4"
     next to its health badge on the same line — it truncated to an
     ellipsis at nearly every practical column count. 260px gives that
     pairing room without needing to wrap the badge onto its own line. */
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}
/* Worker cards intentionally use natural page height. Unlike a data
   table, clipping this grid to a fill-height workspace hides healthy
   workers below the fold on phones. */
.workers-workspace { flex: 0 0 auto; min-height: auto; }
/* The Workers list is paged already, so an internal fixed-height
   scrollbar only truncates useful rows and leaves needless blank space.
   Let this table take its natural row height like the card view. */
.workers-workspace #workerView .table-wrap {
  max-height: none;
  overflow-y: visible;
}
.worker-fleet-summary { margin: 0 0 16px; }
.worker-fleet-summary .stat-tile { cursor: pointer; text-align: left; }
.worker-fleet-summary .stat-tile:hover { border-color: var(--border-2); }
.worker-filter-bar { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
.worker-filter-select { min-width: 156px; }
.worker-view-toggle { display: inline-flex; gap: 3px; }
.worker-view-toggle .btn { min-width: 34px; padding-left: 8px; padding-right: 8px; }
.worker-group { margin: 0 0 18px; }
.worker-group:last-child { margin-bottom: 0; }
.worker-group-title {
  align-items: center;
  color: var(--text-2);
  display: flex;
  font-size: 11px;
  gap: 8px;
  letter-spacing: .09em;
  margin: 0 0 9px;
  text-transform: uppercase;
}
.worker-group-title .count { color: var(--text-3); }
.worker-card { border-left: 0; }
.worker-card.watch { border-left-color: transparent; }
.worker-card.attention { border-left-color: transparent; }
.worker-card.worker-interactive,
.worker-list-row,
.detail-row { cursor: pointer; }
.worker-card.worker-interactive:focus-visible,
.worker-list-row:focus-visible,
.detail-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.worker-list-row { transition: background var(--transition), outline-color var(--transition); }
.worker-list-row:hover { background: var(--bg-3); }
.worker-list-row.attention { background: color-mix(in srgb, var(--danger) 4%, transparent); }
.worker-list-row.watch { background: color-mix(in srgb, var(--warning) 3%, transparent); }
.detail-row { transition: background var(--transition), outline-color var(--transition); }
.detail-row:hover { background: var(--bg-3); }
.detail-row td { vertical-align: middle; }
.detail-row .diagnostic-cell {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.worker-list-row td { vertical-align: middle; }
.worker-list-identity strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.worker-list-meta { color: var(--text-3); font-size: 10px; margin-top: 3px; }
.worker-list-health { display: flex; align-items: center; }
.worker-list-signal { align-items: center; display: inline-flex; font-size: 11px; font-weight: 600; gap: 6px; }
.worker-list-signal .signal-icon { font-size: 13px; line-height: 1; }
.worker-list-signal.healthy { color: var(--success); }
.worker-list-signal.watch { color: var(--warning); }
.worker-list-signal.attention { color: var(--danger); }
.worker-list-resources { color: var(--text-2); font-size: 11px; line-height: 1.45; max-width: 220px; }
.worker-health-line { align-items: center; display: flex; flex-wrap: wrap; gap: 7px; margin-top: 8px; }
.worker-health-reason { color: var(--warning); font-size: 11px; }
.worker-card.attention .worker-health-reason { color: var(--danger); }
.worker-resource-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.worker-resource-chip { background: var(--bg-4); color: var(--text-2); font-size: 10px; padding: 3px 6px; }
.worker-resource-chip.warning { color: var(--warning); }
.worker-resource-chip.danger { color: var(--danger); }
.card {
  background: var(--bg-3);
  border: 0;
  border-radius: var(--radius);
  padding: 12px;
  transition: var(--transition);
}
.card:hover {
  border-color: transparent;
  box-shadow: 0 4px 14px rgba(0,0,0,0.2);
}
.page-intro {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  margin: 2px 0 18px;
}
.page-intro h1 {
  margin: 0 0 6px;
  font-size: 20px;
  letter-spacing: -0.03em;
}
.page-intro p { margin: 0; color: var(--text-2); max-width: 720px; line-height: 1.55; }
/* Locations is an operational workspace, not a generic settings form.
   These classes deliberately only use the shared theme variables so the
   same hierarchy remains readable in dark, light, and cyberpunk modes. */
.locations-page .page-intro { margin-bottom: 16px; }
.locations-page .page-intro p { max-width: 760px; }
.locations-page .stat-tile { min-width: 0; }
.locations-page .stat-tile .value { display: flex; align-items: baseline; gap: 7px; }
.locations-page .stat-detail {
  color: var(--text-3);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}
.locations-page .stat-tile.attention { border-color: transparent; }
.locations-workspace > .panel-header { align-items: flex-start; }
.locations-workspace > .panel-header {
  position: static;
  background: transparent;
  padding-bottom: 8px;
}
.locations-workspace .panel-title { color: var(--text); font-weight: 600; }
.locations-workspace .panel-subtitle { color: var(--text-3); font-size: 11px; letter-spacing: 0; line-height: 1.4; max-width: 360px; text-align: right; text-transform: none; }
.locations-policy-guide {
  align-items: center;
  background: var(--bg-2);
  border: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 7px 10px;
  margin: 0 0 14px;
  padding: 8px 10px;
}
.locations-policy-guide > strong {
  color: var(--text);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.location-policy-copy { color: var(--text-2); font-size: 11px; line-height: 1.4; }
.location-toolbar {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 12px;
}
.location-search { flex: 1 1 280px; max-width: 460px; }
.location-filter { width: 180px; }
.location-result-count { color: var(--text-3); font-size: 11px; margin-left: auto; white-space: nowrap; }
.locations-workspace .table-wrap { max-height: none; }
.locations-table { min-width: 0; table-layout: fixed; width: 100%; }
.locations-table th:nth-child(1) { width: 25%; }
.locations-table th:nth-child(2) { width: 18%; }
.locations-table th:nth-child(3) { width: 12%; }
.locations-table th:nth-child(4) { width: 27%; }
.locations-table th:nth-child(5) { width: 18%; }
.locations-table th { background: var(--bg-2); }
.locations-table td { padding-bottom: 9px; padding-top: 9px; vertical-align: middle; }
.locations-table tr { transition: background var(--transition), outline-color var(--transition); }
.locations-table tbody tr[data-location-code] { cursor: pointer; }
.locations-table tbody tr:hover { background: var(--bg-3); }
.locations-table tbody tr:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.locations-table tbody tr.location-uncovered { background: color-mix(in srgb, var(--warning) 4%, transparent); }
.locations-table tbody tr.location-disabled { opacity: 0.58; }
.location-name { align-items: center; display: flex; gap: 8px; min-width: 0; }
.location-code {
  align-items: center;
  background: var(--bg-3);
  border: 0;
  color: var(--text);
  display: inline-flex;
  font-size: 11px;
  font-weight: 600;
  justify-content: center;
  letter-spacing: 0.05em;
  min-width: 31px;
  padding: 3px 5px;
}
.location-country { color: var(--text); font-weight: 500; }
.location-managed { color: var(--text-3); font-size: 10px; margin-top: 2px; }
.location-row-edit { color: var(--text-2); font-size: 10px; margin-left: auto; opacity: 0; transition: opacity var(--transition); }
.locations-table tbody tr:hover .location-row-edit,
.locations-table tbody tr:focus-visible .location-row-edit { opacity: 1; }
.location-policy-summary { align-items: center; display: flex; flex-wrap: wrap; gap: 6px; }
.location-policy-chip,
.location-readiness,
.location-coverage-state {
  align-items: center;
  /* Reserve a real 1px border so priority-chip variants below can
     actually show their border-color as a pill outline — 0-width
     ignores color regardless of what a more specific rule sets it to.
     .location-readiness/.location-coverage-state never set their own
     border-color, so they stay transparent and unaffected; they signal
     state through their dot + text instead. */
  border: 1px solid transparent;
  color: var(--text-2);
  display: inline-flex;
  font-size: 10px;
  font-weight: 600;
  gap: 5px;
  letter-spacing: .04em;
  line-height: 1;
  padding: 5px 7px;
  text-transform: uppercase;
  white-space: nowrap;
}
.location-policy-chip.priority-critical { border-color: var(--danger); color: var(--danger); }
.location-policy-chip.priority-high { border-color: var(--warning); color: var(--warning); }
.location-policy-chip.priority-normal { border-color: var(--accent); color: var(--accent); }
.location-policy-chip.priority-low { color: var(--text-3); }
.location-policy-order { color: var(--text-3); font-size: 10px; font-variant-numeric: tabular-nums; }
.location-policy-help { color: var(--text-3); font-size: 11px; line-height: 1.4; margin-top: 6px; }
.location-coverage-state { border-color: transparent; padding-left: 0; }
.location-state-dot { background: var(--text-3); border-radius: 50%; height: 7px; width: 7px; }
.location-coverage-state.coverage-on .location-state-dot,
.location-readiness.connected .location-state-dot { background: var(--success); }
.location-coverage-state.coverage-off .location-state-dot,
.location-readiness.stopped .location-state-dot { background: var(--danger); }
.location-readiness.starting .location-state-dot { background: var(--warning); }
.location-readiness.reconnecting .location-state-dot { background: var(--warning); }
.location-readiness { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.location-toggle { align-items: center; cursor: pointer; display: inline-flex; gap: 7px; }
.location-toggle input { height: 1px; opacity: 0; position: absolute; width: 1px; }
.location-toggle-track {
  background: var(--bg-4);
  border: 0;
  display: inline-flex;
  height: 18px;
  padding: 2px;
  transition: var(--transition);
  width: 31px;
}
.location-toggle-track::after { background: var(--text-3); content: ""; height: 12px; transition: var(--transition); width: 12px; }
.location-toggle input:checked + .location-toggle-track { background: color-mix(in srgb, var(--success) 18%, var(--bg-3)); border-color: transparent; }
.location-toggle input:checked + .location-toggle-track::after { background: var(--success); transform: translateX(11px); }
.location-toggle input:focus-visible + .location-toggle-track { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent); }
.location-toggle-label { color: var(--text-2); font-size: 10px; }
.location-status { display: flex; flex-direction: column; gap: 4px; max-width: 220px; min-width: 150px; white-space: normal; }
.location-constraint { color: var(--text-3); font-size: 10px; line-height: 1.35; }
.location-route-count { align-items: baseline; display: flex; gap: 4px; }
.location-route-count strong { color: var(--text); font-size: 13px; font-variant-numeric: tabular-nums; }
.location-route-count span { color: var(--text-3); font-size: 10px; }
.location-submetric { color: var(--text-3); font-size: 10px; margin-top: 3px; }
.location-inventory { color: var(--text); font-variant-numeric: tabular-nums; }
.location-inventory span { color: var(--text-3); }
@media (max-width: 900px) {
  .locations-page .page-intro .btn { margin-top: 12px; }
  .location-result-count { margin-left: 0; }
}
@media (max-width: 560px) {
  .location-filter { width: 100%; }
  .location-search { max-width: none; }
}
.page-single {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}
.page-single-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 18px;
  padding: 8px 0;
}
.access-hero {
  background: var(--bg-2);
  border-color: var(--border);
}
.credential-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 10px;
}
.credential-card { background: var(--bg-3); border: 0; border-radius: var(--radius-sm); padding: 12px; min-width: 0; }
.credential-card .label { color: var(--text-3); font-size: 10px; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 7px; }
.credential-value { display: flex; align-items: center; gap: 7px; min-width: 0; }
.credential-value code { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-size: 12px; }
.credential-value .secret-value { letter-spacing: .14em; }
.security-card { max-width: 560px; padding: 18px; }
.security-card .panel-header { margin-bottom: 16px; }
.country-group { padding: 2px 0; }
.country-group-title { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; color: var(--text-2); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }
.country-chip { display: inline-flex; align-items: center; gap: 7px; margin: 0 7px 7px 0; padding: 6px 9px; border: 0; border-radius: 999px; background: var(--bg-3); font-size: 12px; }
.country-chip .code { color: var(--text); font-weight: 600; }
.portal-steps { align-items: center; color: var(--text-3); display: flex; flex-wrap: wrap; font-size: 11px; gap: 8px; margin: 0 0 16px; }
.portal-steps span { align-items: center; display: inline-flex; gap: 8px; }
.portal-steps span:not(:last-child)::after { color: var(--border-2); content: "→"; }
.portal-steps .active { color: var(--text); font-weight: 600; }
.portal-summary { margin-bottom: 12px; }
.portal-summary .stat-tile { min-height: 0; padding: 13px 15px; }
.success-text { color: var(--success); }
.user-country-workspace { margin-bottom: 12px; }
.country-search { margin: 0 0 12px; max-width: 320px; }
.country-search input { width: 100%; }
.country-choice { color: var(--text); cursor: pointer; font: inherit; text-align: left; transition: var(--transition); }
.country-choice:hover { border-color: var(--border-2); color: var(--text); }
.country-choice:focus-visible { border-color: var(--accent); color: var(--text); }
.country-choice.selected { background: var(--bg-4); border-color: var(--border-2); font-weight: 600; }
.country-favorite { color: var(--warning); line-height: 1; }
.country-favorite .ui-icon { width: 12px; height: 12px; fill: currentColor; }
.country-list { border-top: 0; }
.country-list-row { align-items: center; background: transparent; border: 0; border-bottom: 0; color: var(--text); cursor: pointer; display: grid; font: inherit; gap: 10px; grid-template-columns: 34px minmax(0, 1fr) auto auto; padding: 10px 8px; text-align: left; transition: background var(--transition); width: 100%; }
.country-list-row:hover { background: var(--bg-3); }
.country-list-row:focus-visible { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.country-list-row.selected { background: var(--bg-4); }
.country-list-row .location-code { min-width: 0; }
.country-list-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.country-use { color: var(--text-3); font-size: 11px; }
.country-list-row.selected .country-use { color: var(--text); font-weight: 600; }
.connection-selection { align-items: center; display: flex; gap: 12px; justify-content: space-between; margin: 0 0 14px; }
.connection-selection > div { align-items: baseline; display: flex; flex-wrap: wrap; gap: 8px; min-width: 0; }
.connection-selection .label { color: var(--text-3); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; }
.connection-selection strong { color: var(--text); font-size: 14px; }
.connection-primary-actions { align-items: center; display: flex; flex-wrap: wrap; gap: 9px; margin: 0 0 14px; }
.portal-export { align-items: center; display: inline-flex; flex-wrap: wrap; gap: 7px; letter-spacing: normal; text-transform: none; }
.portal-routing-note { align-items: baseline; display: flex; flex-wrap: wrap; gap: 8px 12px; margin-bottom: 12px; padding: 15px 17px; }
.portal-routing-note .label { color: var(--text-3); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; }
.portal-routing-note strong { color: var(--text); }
.portal-integration-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.portal-integration-card { background: var(--bg-2); border: 0; border-radius: var(--radius); display: flex; flex-direction: column; gap: 12px; min-width: 0; padding: 16px; }
.portal-integration-card strong { color: var(--text); }
.portal-integration-card .muted { font-size: 12px; margin-top: 4px; }
.portal-snippet { background: var(--bg-3); border: 0; border-radius: var(--radius-sm); color: var(--text); font: 12px/1.55 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; margin: 0; max-height: 220px; min-height: 72px; overflow: auto; padding: 11px; white-space: pre; }
.portal-action-stack { align-items: flex-start; display: flex; flex-wrap: wrap; gap: 8px; }
.portal-status-panel { min-height: 150px; }
.portal-status-panel .empty-state { min-height: 120px; }
.usage-summary-panel .progress { margin-top: 0; }
.country-table-status { font-size: 11px; color: var(--text-2); }
.dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 6px;
}
.dot.online {
  background: var(--success);
  box-shadow: var(--glow);
}
.dot.offline {
  background: var(--danger);
}
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  animation: modalFadeIn 0.15s ease;
}
@keyframes modalFadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
.modal {
  background: var(--bg-2);
  border: 0;
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 380px;
  max-width: min(92vw, 720px);
  max-height: 85vh;
  overflow: hidden;
  box-shadow: 0 12px 40px rgba(0,0,0,0.35);
  animation: modalPopIn 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}
@keyframes modalPopIn {
  from {
    opacity: 0;
    transform: scale(0.96) translateY(6px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}
.modal-header {
  align-items: flex-start;
  border-bottom: 0;
  display: flex;
  gap: 12px;
  justify-content: space-between;
  padding: 17px 20px 14px;
}
.modal-header h3 {
  margin: 0;
  font-size: 16px;
  line-height: 1.25;
}
.modal-kicker {
  color: var(--text-3);
  font-size: 10px;
  letter-spacing: .1em;
  margin: 0 0 4px;
  text-transform: uppercase;
}
.modal-subtitle {
  color: var(--text-2);
  font-size: 12px;
  line-height: 1.4;
  margin: 5px 0 0;
}
/* .modal-close's box model comes entirely from .icon-btn now (see the
   shared declaration near .status-toggle-button) — same 28x28 box, same
   radius, same mobile tap-target bump every icon-only button gets. */
.modal-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 16px 20px; }
.modal-footer {
  background: color-mix(in srgb, var(--bg-2) 92%, var(--bg-3));
  border-top: 0;
  flex: 0 0 auto;
  padding: 12px 20px;
}
.modal-footer .flex-between { margin-top: 0 !important; gap: 10px; }
.modal-footer > div > div { display: flex; flex-wrap: wrap; gap: 8px; }
.modal.compact { width: 380px; }
.modal.wide {
  width: 700px;
}
/* Flat by design: a modal is already the card. Stacking a bordered box
   for every section inside it just nests cards within a card — the h4's
   bottom border is enough separation between sections. */
.modal-section {
  margin: 0 0 18px;
  padding: 0 0 18px;
  border-bottom: 0;
}
.modal-section:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: 0; }
.modal-section h4 {
  color: var(--text-2);
  font-size: 11px;
  letter-spacing: .09em;
  margin: 0 0 10px;
  text-transform: uppercase;
}
.modal-section .kv-grid { margin-top: 0; }
.modal-notice { background: var(--bg-3); border-radius: var(--radius); color: var(--text-2); font-size: 12px; line-height: 1.45; margin: 0 0 12px; padding: 8px 10px; }
.modal-notice.warning { color: var(--warning); }
.modal-danger-zone h4 { color: var(--danger); }
.modal-danger-zone .flex-between { gap: 10px; flex-wrap: wrap; }
.modal-loading { align-items: center; color: var(--text-2); display: flex; gap: 8px; min-height: 64px; }
.modal-loading::before {
  animation: modalSpinner .8s linear infinite;
  border: 2px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 50%;
  border-top-color: var(--accent);
  content: '';
  height: 14px;
  width: 14px;
}
@keyframes modalSpinner { to { transform: rotate(360deg); } }
.modal .error-text { margin-bottom: 0; }
/* Subscription intake is a capacity decision, not a generic three-field
   form. These components deliberately use only the global theme tokens,
   so the hierarchy survives dark, light, and cyberpunk modes. */
.subscription-account-form { display: grid; gap: 14px; }
.subscription-account-form .modal-section { margin: 0; }
.subscription-overview {
  align-items: stretch;
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.subscription-overview-item {
  background: color-mix(in srgb, var(--bg-3) 74%, transparent);
  border: 0;
  border-radius: var(--radius-sm);
  min-height: 72px;
  padding: 10px;
}
.subscription-overview-item strong {
  color: var(--text);
  display: block;
  font-size: 12px;
  margin-bottom: 4px;
}
.subscription-overview-item span { color: var(--text-2); font-size: 11px; line-height: 1.4; }
.subscription-field-help { color: var(--text-3); font-size: 11px; line-height: 1.45; margin-top: 6px; }
.subscription-capacity-row {
  align-items: start;
  display: grid;
  gap: 14px;
  grid-template-columns: 140px minmax(0, 1fr);
}
.subscription-capacity-row input[type=number] { width: 100%; }
.subscription-capacity-preview {
  background: var(--bg-3);
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-2);
  font-size: 11px;
  line-height: 1.45;
  min-height: 38px;
  padding: 9px 10px;
}
.subscription-capacity-preview strong { color: var(--text); font-weight: 600; }
.subscription-security-note {
  align-items: flex-start;
  background: color-mix(in srgb, var(--success) 7%, var(--bg-3));
  border: 0;
  border-radius: var(--radius-sm);
  display: flex;
  gap: 9px;
  padding: 10px;
}
.subscription-security-mark { color: var(--success); font-size: 15px; line-height: 1; padding-top: 1px; }
.subscription-security-mark.ui-icon { width: 15px; height: 15px; }
.subscription-security-note strong { color: var(--text); display: block; font-size: 11px; margin-bottom: 3px; }
.subscription-security-note span:last-child { color: var(--text-2); font-size: 11px; line-height: 1.45; }
.subscription-footer-actions { align-items: center; display: flex; gap: 8px; justify-content: flex-end; }
.subscription-rename-row { align-items: end; display: grid; gap: 8px; grid-template-columns: minmax(0, 1fr) auto; }
.subscription-rename-row .field { margin-bottom: 0; }
.subscription-file-note {
  background: var(--bg-3);
  border: 0;
  color: var(--text-2);
  font-size: 11px;
  line-height: 1.5;
  padding: 10px;
}
.subscription-file-note strong { color: var(--text); }
.subscription-upload-progress { color: var(--text-2); font-size: 11px; margin-top: 8px; min-height: 17px; }
[data-theme="cyberpunk"] .subscription-overview-item,
[data-theme="cyberpunk"] .subscription-capacity-preview,
[data-theme="cyberpunk"] .subscription-security-note { box-shadow: inset 0 0 13px rgba(0,255,255,0.035); }
@media (max-width: 620px) {
  .modal-backdrop { align-items: flex-end; }
  .modal, .modal.wide { border-radius: var(--radius) var(--radius) 0 0; max-width: 100vw; max-height: 92vh; width: 100%; }
  .modal-body { padding: 14px 16px; }
  .modal-header, .modal-footer { padding-left: 16px; padding-right: 16px; }
  .modal-footer .flex-between { align-items: stretch; flex-direction: column-reverse; }
  .modal-footer .flex-between > div, .modal-footer .btn { width: 100%; }
  .modal-footer > div > div { display: grid; grid-template-columns: 1fr 1fr; }
  .kv-grid { grid-template-columns: 116px minmax(0, 1fr); }
  .subscription-overview { grid-template-columns: 1fr; }
  .appearance-choice-grid { grid-template-columns: 1fr; }
  .appearance-choice-grid.language-choice-grid { grid-template-columns: 1fr 1fr; }
  .subscription-capacity-row { grid-template-columns: 1fr; gap: 8px; }
  .subscription-footer-actions, .subscription-detail-footer { display: grid; grid-template-columns: 1fr 1fr; }
  .subscription-rename-row { grid-template-columns: 1fr; }
}
.loading-bar {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 0;
  background: var(--accent);
  box-shadow: var(--glow);
  z-index: 400;
  transition: width 0.2s ease, opacity 0.3s ease;
  opacity: 0;
}
.loading-bar.active {
  opacity: 1;
}
.toast-container {
  position: fixed;
  bottom: 20px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 300;
  max-width: 340px;
}
.toast {
  background: var(--bg-3);
  border: 0;
  border-left: 0;
  color: var(--text);
  padding: 10px 14px;
  border-radius: var(--radius);
  font-size: 12px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.3);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.toast.show {
  opacity: 1;
  transform: translateY(0);
}
.toast.success {
  border-left-color: transparent;
}
.toast.error {
  border-left-color: transparent;
}
.kv-grid {
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: 8px 12px;
  margin-top: 12px;
  font-size: 13px;
}
.kv-grid > div {
  white-space: nowrap;
  overflow-x: auto;
}
/* Value cells are the second of each label/value pair kvRow() emits —
   giving them the primary text weight and the label its own dimmer
   color is what actually makes a dense stats grid like System Info
   scannable at a glance instead of reading as one flat block of text.
   (--muted was never a defined variable, so this rule silently did
   nothing before — labels and values rendered in the same color.) */
.kv-label {
  color: var(--text-3);
}
.kv-grid > div:nth-child(2n) {
  color: var(--text);
  font-weight: 500;
}
.flex-between {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.chart-wrap {
  height: clamp(190px, 27vh, 280px);
}
.qrcode-surface {
  align-items: center;
  display: flex;
  justify-content: center;
  width: 100%;
}
img.qrcode {
  display: block;
  height: auto;
  image-rendering: pixelated;
  max-width: 100%;
  width: min(100%, 280px);
}

/* ── MOBILE ──────────────────────────────────────────── */
/* Placed last so it wins the cascade over same-specificity
   component rules above (e.g. .grid-2's base 1fr-1fr definition)
   regardless of source order elsewhere in the file. */
@media (max-width: 900px) {
  /* The theme remains recognisably cyberpunk on touch devices, without
     a full-screen compositing overlay or animated text competing with
     scrolling and large operational tables. */
  [data-theme="cyberpunk"] body::after { display: none; }
  [data-theme="cyberpunk"] .glitch { animation: none; }
  .status-metric.secondary-hidden { display: none; }
  /* Navigation must not depend on the bottom system bar. iOS reserves
     the lower screen edge for browser/home gestures, so every mobile
     surface gets the same deliberately large top-left menu target. */
  .status-toggle-button { display: none; }
  .hamburger-btn {
    display: block;
    position: fixed;
    top: max(10px, env(safe-area-inset-top));
    left: max(10px, env(safe-area-inset-left));
    z-index: 62;
  }
  .hamburger-btn[aria-expanded="true"] {
    opacity: 0;
    pointer-events: none;
  }
  .notification-drawer {
    border-radius: 0;
    bottom: 0;
    max-width: none;
    right: 0;
    top: 0;
    width: 100vw;
  }
  .sidebar {
    position: fixed;
    left: 0;
    top: 0;
    height: 100vh;
    height: 100dvh;
    transform: translateX(-100%);
    /* Navigation needs to be immediately usable on touch devices. */
    transition: none;
    z-index: 61;
  }
  .sidebar.open {
    transform: translateX(0);
  }
  .sidebar-backdrop.open {
    display: block;
  }
  main {
    padding: 16px 12px;
    padding-top: calc(54px + env(safe-area-inset-top));
    touch-action: pan-y;
  }
  .grid-2 {
    /* Bare 1fr reintroduces the exact bug the base rule's minmax(0, 1fr)
       avoids: an implicit auto minimum lets a wide-content panel (e.g.
       On-Demand's Current Capacity list) force this single track wider
       than the viewport instead of shrinking to it. */
    grid-template-columns: minmax(0, 1fr);
  }
  .route-health-metrics { grid-template-columns: minmax(0, 1fr); }
  .page-intro { display: block; }
  .provider-intro { margin-bottom: 14px; }
  .provider-intro .btn.primary {
    margin-top: 12px;
    width: 100%;
  }
  #discoveryStats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  #discoveryStats .stat-tile { padding: 14px; }
  #discoveryStats .stat-tile .label { line-height: 1.3; white-space: normal; }
  #discoveryStats .stat-tile .muted { overflow-wrap: anywhere; }
  .providers-table { min-width: 0; }
  .providers-table tr { padding: 11px 0; }
  .providers-table td { padding-bottom: 6px; padding-top: 6px; }
  .providers-table td:first-child { display: block; padding-top: 2px; }
  .providers-table td:first-child::before { display: none; }
  .providers-table .provider-name { min-height: 22px; }
  .table-wrap {
    -webkit-overflow-scrolling: touch;
  }
  .workspace-toolbar .control-sm { flex: 1 1 180px; min-width: 0; }
}
@media (max-width: 680px) {
  .page-intro > .btn {
    display: flex;
    justify-content: center;
    margin-top: 12px;
    width: 100%;
  }
  .workers-workspace > .panel-header {
    align-items: stretch;
    flex-direction: column;
    gap: 10px;
  }
  .workers-workspace > .panel-header > span { width: 100%; }
  .workers-workspace #workerSearch {
    flex: 1 1 auto;
    min-width: 0;
    width: 100% !important;
  }
  .worker-filter-select { flex: 1 1 160px; }
  .worker-grid { grid-template-columns: minmax(0, 1fr); }
  .portal-steps { gap: 6px; margin-bottom: 14px; }
  .portal-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
  .portal-summary .stat-tile { padding: 11px; }
  .portal-summary .stat-tile .label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .portal-summary .stat-tile .value { font-size: 14px; overflow: hidden; text-overflow: ellipsis; }
  .user-country-workspace > .panel-header,
  .access-hero > .panel-header { align-items: flex-start; flex-direction: column; gap: 9px; }
  .country-search { max-width: none; }
  .country-list-row { grid-template-columns: 34px minmax(0, 1fr) auto; }
  .country-list-row .country-favorite { display: none; }
  .connection-selection { align-items: flex-start; flex-direction: column; }
  .connection-primary-actions { align-items: stretch; flex-direction: column; }
  .connection-primary-actions .btn { justify-content: center; width: 100%; }
  .portal-export { width: 100%; }
  .portal-integration-grid { grid-template-columns: minmax(0, 1fr); }
  .portal-action-stack .btn { justify-content: center; }
  .locations-table,
  .locations-table tbody,
  .locations-table tr,
  .locations-table td { display: block; width: 100%; }
  .locations-table { min-width: 0; }
  .locations-table thead { display: none; }
  .locations-table tr {
    border-bottom: 0;
    padding: 11px 0;
  }
  .locations-table tr:last-child { border-bottom: 0; }
  .locations-table td {
    border: 0;
    display: grid;
    gap: 8px;
    grid-template-columns: minmax(104px, .72fr) minmax(0, 1.28fr);
    padding: 6px 4px;
    text-align: left;
    white-space: normal;
  }
  .locations-table td::before {
    color: var(--text-3);
    content: attr(data-label);
    font-size: 10px;
    letter-spacing: .07em;
    text-transform: uppercase;
  }
  .locations-table td:first-child {
    display: block;
    padding-top: 2px;
  }
  .locations-table td:first-child::before { display: none; }
  .location-name,
  .location-status { min-width: 0; max-width: none; }
  .location-row-edit { opacity: 1; }
}
@media (max-width: 680px) {
  .providers-workspace .table-wrap { max-height: none; overflow: visible; }
  .providers-table,
  .providers-table tbody,
  .providers-table tr,
  .providers-table td { display: block; width: 100%; }
  .providers-table thead { display: none; }
  .providers-table tr { border-bottom: 0; padding: 11px 0; }
  .providers-table tr:last-child { border-bottom: 0; }
  .providers-table td { border: 0; display: grid; gap: 8px; grid-template-columns: minmax(92px, .65fr) minmax(0, 1.35fr); padding: 5px 4px; white-space: normal; }
  .providers-table td::before { color: var(--text-3); content: attr(data-label); font-size: 10px; letter-spacing: .07em; text-transform: uppercase; }
  .providers-table td:first-child { display: block; padding-top: 2px; }
  .providers-table td:first-child::before { display: none; }
  .providers-table .provider-summary { white-space: normal; }
}
/* A tighter cutoff than the 900px breakpoint above — that one handles
   the sidebar/grid/table shape change, this one only trims spacing
   that gets cramped on actual phone widths. The status bar's 5 stats
   at 24px gaps eat most of a 375px viewport before any scrolling even
   starts. */
@media (max-width: 480px) {
  #discoveryStats { grid-template-columns: minmax(0, 1fr); }
  .status-bar {
    padding: 8px 12px;
    padding-bottom: max(8px, env(safe-area-inset-bottom));
    font-size: 11px;
  }
  .status-primary { flex: 1 1 auto; }
  .status-metric { gap: 5px; }
  .page-tabs { display: flex; margin-left: -2px; margin-right: -2px; }
  .page-tabs a { flex: 0 0 auto; padding: 0 9px; }
  .stat-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .stat-tile { min-width: 0; padding: 12px; }
  .stat-tile .label { overflow: hidden; text-overflow: ellipsis; }
  .stat-tile .value { font-size: 15px; }
  .portal-steps span { font-size: 10px; }
  .portal-steps span:not(:last-child)::after { display: none; }
  .portal-summary { grid-template-columns: minmax(0, 1fr); }
  .portal-summary .stat-tile { align-items: baseline; display: flex; gap: 10px; justify-content: space-between; }
  .portal-summary .stat-tile .label { overflow: visible; }
  .workspace-toolbar { align-items: stretch; }
  .workspace-toolbar .control-sm { flex-basis: 100%; width: 100%; }
  .workspace-toolbar .muted { margin-left: 0; }
  .workspace-panel > .panel-header,
  .dashboard-action-panel > .panel-header {
    align-items: flex-start;
    flex-direction: column;
    gap: 5px;
  }
  /* Operational tables retain every field on a phone. They become a
     compact vertical record instead of forcing a precision-critical
     column to disappear behind an arbitrary breakpoint. */
  .table-wrap table.responsive-table,
  .table-wrap table.responsive-table tbody,
  .table-wrap table.responsive-table tr,
  .table-wrap table.responsive-table td { display: block; width: 100%; }
  .table-wrap table.responsive-table { min-width: 0; }
  .table-wrap table.responsive-table thead { display: none; }
  .table-wrap table.responsive-table tr { border-bottom: 0; padding: 8px 0; }
  .table-wrap table.responsive-table tr:last-child { border-bottom: 0; }
  .table-wrap table.responsive-table td { border: 0; display: grid; gap: 8px; grid-template-columns: minmax(96px, .7fr) minmax(0, 1.3fr); padding: 5px 4px; text-align: left; white-space: normal; }
  .table-wrap table.responsive-table td::before { color: var(--text-3); content: attr(data-label); font-size: 10px; letter-spacing: .07em; text-transform: uppercase; }
  .table-wrap table.responsive-table td:empty { display: none; }
}
/* Final surface pass: every long-lived page uses the same compact
   workspace grammar. These are deliberately shared instead of adding
   one more set of route-specific overrides before the UI is split. */
.workspace-toolbar,
.location-toolbar,
.worker-filter-bar {
  gap: var(--space-1);
}
/* 28px, not a new one-off number — matches .btn.small's own declared
   height so a plain .btn dropped into a toolbar becomes visually
   identical to its .small siblings instead of a third height in between. */
.workspace-toolbar .btn,
.location-toolbar .btn,
.worker-filter-bar .btn {
  min-height: 28px;
}
.workspace-panel > .panel-header,
.natural-workspace > .panel-header {
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.empty-state {
  min-height: 132px;
  padding: 22px;
}
.empty-state-text { max-width: 38ch; }
.empty-state-hint { max-width: 52ch; }
.modal-body {
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.modal-footer .btn.primary:last-child,
.modal-footer .btn.large:last-child {
  margin-left: auto;
}
.status-bar {
  gap: var(--space-2);
}
.status-actions {
  gap: var(--space-1);
}
@media (max-width: 680px) {
  .workspace-toolbar,
  .location-toolbar,
  .worker-filter-bar {
    align-items: stretch;
  }
  .workspace-toolbar .btn,
  .location-toolbar .btn,
  .worker-filter-bar .btn {
    flex: 1 1 auto;
    justify-content: center;
  }
  .detail-row,
  .worker-list-row,
  .worker-card.worker-interactive {
    -webkit-tap-highlight-color: color-mix(in srgb, var(--border-2) 45%, transparent);
  }
  .status-bar {
    gap: 8px;
    padding-left: max(12px, env(safe-area-inset-left));
    padding-right: max(12px, env(safe-area-inset-right));
  }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
