/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* ── Accessibility ── */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

/* Visible keyboard focus for interactive controls. :focus-visible keeps the
   ring off pointer clicks; inputs/selects/textareas already style :focus. */
a:focus-visible,
button:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible,
[role="button"]:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--cyan-bright, #38BDF8);
  outline-offset: 2px;
  border-radius: 3px;
}
/* Programmatic-focus targets (view headings get tabindex=-1 so screen readers
   announce the new view) must not paint a keyboard focus ring. */
[tabindex="-1"]:focus { outline: none; }
/* Inset the folder-tab focus ring so it isn't clipped by the panel edge. */
.settings-tab:focus-visible {
  outline-offset: -2px;
}

/* ── Design tokens — Constellation ── */
:root {
  --bg:          #060914;
  --bg-deep:     #030510;
  --surface:     #0E1426;
  --surface-2:   #141B32;
  --surface-3:   #1B2444;
  --line:        #1E2A4A;
  --line-2:      #2B3A63;
  --text:        #E8EEFF;
  --text-soft:   #A8B4D4;
  --text-muted:  #6B7AA3;
  --text-dim:    #3E4C74;
  --brand-primary: #38BDF8;
  --cyan:        #7DD3FC;
  --cyan-bright: #38BDF8;
  --sparkle:     #BAE6FD;
  --electric:    #60A5FA;
  --accent:      #38BDF8;        /* legacy alias used by JS-set styles */
  --accent-glow: rgba(56,189,248,0.22);
  --accent-dim:  rgba(56,189,248,0.12);
  --green:       #4ADE80;
  --green-bg:    rgba(74,222,128,0.10);
  --red:         #F87171;
  --red-bg:      rgba(248,113,113,0.12);
  --yellow:      #FBBF24;
  --yellow-bg:   rgba(251,191,36,0.12);
  --standby:     #A5B4FC;                 /* indigo: hibernated — asleep, wakes on demand */
  --standby-bg:  rgba(129,140,248,0.14);
  --gray-bg:     rgba(107,122,163,0.12);
  /* RGB triplets for translucent "glass" surfaces (cards, modals, insets):
     rgba(var(--surface-rgb), a) themes the colour while each site keeps its
     own alpha. --surface-2-rgb/--surface-rgb mirror --surface-2/--surface;
     --inset-rgb is the recessed inset base (a touch below the card). */
  --surface-2-rgb: 20, 27, 50;
  --surface-rgb:   14, 20, 38;
  --inset-rgb:   6, 9, 20;
  /* Drop shadow for a lifted card. Themed, because a shadow tuned for a near-black
     page reads as a grey smudge on a light one. */
  --shadow-card: 0 24px 60px -20px rgba(0,0,0,0.6);
  --radius:      8px;
  --radius-lg:   14px;
  --font:        'Manrope', -apple-system, system-ui, 'Segoe UI', sans-serif;
  --mono:        'Space Mono', 'JetBrains Mono', 'Cascadia Code', Consolas, monospace;
  --ease:        200ms cubic-bezier(.2,.7,.2,1);
  color-scheme: dark;
}

/* ── Light theme ──────────────────────────────────────────────────────────
   Applied when <html data-theme="light"> (set by the no-flash inline script in
   index.html and views/theme.js). Only the semantic colour tokens are overridden;
   the whole UI reads through them, so the palette flips wholesale. Brand cyan and
   the status colours are darkened to keep text at WCAG AA contrast on light
   surfaces (the dark-theme brights fail on white). Radii, fonts, and timing are
   theme-independent and inherited from :root. */
:root[data-theme="light"] {
  --bg:          #F4F7FC;
  --bg-deep:     #E8EDF6;
  --surface:     #FFFFFF;
  --surface-2:   #F3F6FB;
  --surface-3:   #E9EEF7;
  --line:        #DBE2EE;
  --line-2:      #C6D0E0;
  --text:        #16203A;
  --text-soft:   #45526E;
  --text-muted:  #5B6784;
  --text-dim:    #93A0B8;
  --brand-primary: #0369A1;
  --cyan:        #0369A1;
  --cyan-bright: #0369A1;
  --sparkle:     #0284C7;
  --electric:    #1D4ED8;
  --accent:      #0369A1;
  --accent-glow: rgba(3,105,161,0.14);
  --accent-dim:  rgba(3,105,161,0.07);
  --green:       #166534;
  --green-bg:    rgba(22,101,52,0.10);
  --red:         #DC2626;
  --red-bg:      rgba(220,38,38,0.10);
  --yellow:      #B45309;
  --yellow-bg:   rgba(180,83,9,0.13);
  --standby:     #4F46E5;
  --standby-bg:  rgba(79,70,229,0.11);
  --gray-bg:     rgba(100,116,139,0.12);
  --surface-2-rgb: 243, 246, 251;
  --surface-rgb:   255, 255, 255;
  --inset-rgb:   232, 237, 246;
  --shadow-card: 0 18px 40px -24px rgba(22,32,58,0.28);
  color-scheme: light;
}

/* The decorative starfield is a dark-space motif; hide it on the light theme. */
:root[data-theme="light"] .bg-stars { opacity: 0; }

/* Replace the dark deep-space wash with a light, barely-there cool gradient (the
   default's hardcoded dark tail and saturated glows are a dark-mode motif). */
:root[data-theme="light"] body::before {
  background:
    radial-gradient(900px 500px at 85% 0%, rgba(3,105,161,0.05), transparent 60%),
    radial-gradient(700px 600px at 10% 100%, rgba(37,99,235,0.04), transparent 60%),
    linear-gradient(180deg, var(--bg-deep) 0%, var(--bg) 100%);
}

/* ── Base ── */
html { background: var(--bg-deep); }

body {
  font-family: var(--font);
  background: var(--bg-deep);
  color: var(--text);
  min-height: 100vh;
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.005em;
  position: relative;
  overflow-x: hidden;
}

/* Deep-space gradient */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  background:
    radial-gradient(900px 500px at 85% 0%, rgba(56,189,248,0.14), transparent 60%),
    radial-gradient(700px 600px at 10% 100%, rgba(96,165,250,0.10), transparent 60%),
    radial-gradient(500px 400px at 50% 50%, rgba(139,92,246,0.05), transparent 70%),
    linear-gradient(180deg, var(--bg-deep) 0%, #050B1C 100%);
  z-index: 0;
  pointer-events: none;
}

/* Drifting star-field */
.bg-stars {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 17%, rgba(255,255,255,0.80) 50%, transparent 100%),
    radial-gradient(1px 1px at 38% 8%, rgba(255,255,255,0.65) 50%, transparent 100%),
    radial-gradient(1px 1px at 75% 23%, rgba(186,230,253,0.85) 50%, transparent 100%),
    radial-gradient(2px 2px at 92% 65%, rgba(255,255,255,0.55) 50%, transparent 100%),
    radial-gradient(1px 1px at 5% 72%, rgba(255,255,255,0.45) 50%, transparent 100%),
    radial-gradient(1.5px 1.5px at 58% 82%, rgba(186,230,253,0.65) 50%, transparent 100%),
    radial-gradient(1px 1px at 22% 90%, rgba(255,255,255,0.55) 50%, transparent 100%),
    radial-gradient(1px 1px at 67% 48%, rgba(255,255,255,0.50) 50%, transparent 100%),
    radial-gradient(1.5px 1.5px at 88% 34%, rgba(186,230,253,0.55) 50%, transparent 100%),
    radial-gradient(1px 1px at 43% 62%, rgba(255,255,255,0.45) 50%, transparent 100%);
  opacity: 0.85;
  animation: drift 180s linear infinite;
}
@keyframes drift { from { transform: translateY(0); } to { transform: translateY(-80px); } }

#app-shell, #mobile-topbar, main, .log-pane, .modal-overlay { position: relative; z-index: 2; }

/* ── Sparkle icon (4-pointed Shiny star) ── */
.sparkle {
  display: inline-block;
  width: 1em;
  height: 1em;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 0 C12 7 12 7 24 12 C12 17 12 17 12 24 C12 17 12 17 0 12 C12 7 12 7 12 0 Z'/></svg>") center/contain no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 0 C12 7 12 7 24 12 C12 17 12 17 12 24 C12 17 12 17 0 12 C12 7 12 7 12 0 Z'/></svg>") center/contain no-repeat;
  vertical-align: -0.12em;
}

@keyframes pulse-glow {
  0%, 100% { filter: drop-shadow(0 0 5px rgba(56,189,248,0.25)); opacity: 0.95; }
  50%      { filter: drop-shadow(0 0 12px rgba(56,189,248,0.85)); opacity: 1; }
}

.sparkle-brand {
  color: var(--cyan-bright);
  font-size: 1.35em;
  animation: pulse-glow 3.5s ease-in-out infinite;
  align-self: center;
}

/* ── App shell: persistent left sidebar + content column ── */
:root { --sidebar-w: 248px; --sidebar-w-collapsed: 68px; }

#app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: 100vh;
}
body.sidebar-collapsed #app-shell { grid-template-columns: var(--sidebar-w-collapsed) minmax(0, 1fr); }
/* Pre-auth and boot: no sidebar chrome, single full-width column. The login
   view and dashboard chrome only belong to the resolved out/in states, so they
   stay hidden until the session check decides which to show. */
[data-auth="out"] #app-shell,
[data-auth="loading"] #app-shell { grid-template-columns: minmax(0, 1fr); }
[data-auth="out"] #sidebar,
[data-auth="out"] #mobile-topbar,
[data-auth="loading"] #sidebar,
[data-auth="loading"] #mobile-topbar { display: none; }
/* The login form must paint only in the resolved logged-out state, never during
   boot (data-auth="loading") or for an already-authenticated, server-stamped
   shell (data-auth="in"). This is what kills the login-flash-before-dashboard. */
[data-auth="loading"] #login-view,
[data-auth="in"] #login-view { display: none; }

/* Boot splash: a quiet branded hold while the session check is in flight. Shown
   only for data-auth="loading"; an authenticated shell is stamped "in" by the
   server and skips straight to the dashboard chrome. */
#boot-splash { display: none; }
[data-auth="loading"] #boot-splash {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: boot-splash-pulse 1.5s ease-in-out infinite;
}
[data-auth="loading"] #boot-splash .brand { font-size: 1.5rem; }
@keyframes boot-splash-pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  [data-auth="loading"] #boot-splash { animation: none; opacity: 0.85; }
}

#sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 1.1rem 0.7rem 0.8rem;
  background: rgba(var(--inset-rgb),0.72);
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  border-right: 1px solid var(--line);
  overflow: hidden;
}

.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 0.55rem;
  font-weight: 800;
  font-size: 1rem;
  color: var(--text);
  letter-spacing: -0.02em;
  font-family: var(--font);
  text-transform: none;
}
.brand-hub { color: var(--cyan-bright); font-weight: 300; letter-spacing: -0.02em; }

.sidebar-brand { display: flex; align-items: center; padding: 0.3rem 0.6rem 1rem; }

/* Primary section nav */
#primary-nav { display: flex; flex-direction: column; gap: 0.12rem; }
.nav-item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius);
  color: var(--text-muted);
  text-decoration: none;
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  transition: color var(--ease), background var(--ease);
}
/* The .nav-item display:flex (author origin) outranks the UA [hidden]
   display:none, so role-gated nav items (hidden via the attribute in app.js)
   need this explicit higher-specificity guard to actually hide. */
.nav-item[hidden] { display: none; }
.nav-item:hover { color: var(--text-soft); background: var(--surface-2); }
.nav-item:focus-visible,
.sidebar-app:focus-visible,
.group-disclosure-toggle:focus-visible,
.sidebar-collapse:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px rgba(56, 189, 248, 0.55);
}
.nav-item.tab-active {
  color: var(--text);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line);
}
.nav-ico { width: 18px; height: 18px; flex-shrink: 0; color: var(--text-muted); }
.nav-item.tab-active .nav-ico { color: var(--cyan-bright); }
.nav-label { overflow: hidden; text-overflow: ellipsis; }

/* App list region: the only inner scroll area. */
.sidebar-apps {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  margin-top: 0.7rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--line);
}
.sidebar-apps:empty { border-top: none; }
.sidebar-apps-heading {
  margin: 0 0 0.4rem;
  padding: 0 0.7rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.sidebar-apps-count,
.sidebar-project-group-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.45rem;
  min-width: 1.45rem;
  height: 1.15rem;
  padding: 0;
  box-sizing: border-box;
  border-radius: 999px;
  color: var(--text-soft);
  background: var(--surface-2);
  font-family: var(--mono);
  font-size: 0.62rem;
  line-height: 1.35;
  text-align: center;
}
.sidebar-project-group { margin-top: 0.4rem; }
.sidebar-project-group-header { display: flex; align-items: center; }
.sidebar-project-group-heading { flex: 1 1 auto; min-width: 0; }
.sidebar-project-group-toggle {
  width: 100%;
  min-height: 30px;
  padding: 0.28rem 0.42rem;
  border-radius: 6px;
  color: var(--text-soft);
  font-size: 0.75rem;
  font-weight: 650;
  letter-spacing: -0.005em;
}
.sidebar-project-group-toggle:hover { color: var(--text); background: var(--surface-2); }
.sidebar-project-group-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sidebar-project-group-icon { flex: none; font-size: 0.88rem; line-height: 1; }
.sidebar-project-group-count { margin-left: auto; margin-right: 0.28rem; }
.sidebar-project-group-body {
  display: flex;
  flex-direction: column;
  margin: 0.08rem 0 0 0.72rem;
  padding-left: 0.5rem;
  border-left: 1px solid var(--line);
}
.sidebar-app {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.4rem 0.7rem;
  border-radius: var(--radius);
  color: var(--text-muted);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 500;
}
.sidebar-app:hover { color: var(--text-soft); background: var(--surface-2); }
.sidebar-app.active {
  color: var(--text);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line);
}
.sidebar-app-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--text-muted); }
.sb-dot-running, .sb-dot-healthy { background: var(--green); box-shadow: 0 0 6px var(--green); }
.sb-dot-deploying, .sb-dot-waking, .sb-dot-pending { background: var(--yellow); }
.sb-dot-degraded, .sb-dot-crashed, .sb-dot-failed { background: var(--red); box-shadow: 0 0 6px var(--red); }
.sb-dot-hibernated { background: var(--standby); box-shadow: 0 0 6px var(--standby); }
.sb-dot-new { background: var(--cyan-bright); box-shadow: 0 0 6px rgba(56,189,248,0.5); }
/* Viewer-facing dots: openable apps get a neutral bullet (no status color),
   only an app the viewer cannot open is muted. Keeps internal state hidden. */
.sb-dot-ok { background: var(--text-soft); }
.sb-dot-unavailable { background: var(--text-muted); }
.sidebar-apps-empty { padding: 0.3rem 0.7rem; color: var(--text-muted); font-size: 0.8rem; }

/* Shared project disclosure behavior. Surface-specific rules above/below own
   the typography and spacing; these rules keep the affordance consistent. */
.group-disclosure-toggle {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-family: inherit;
  text-align: left;
  background: transparent;
  border: 0;
  cursor: pointer;
}
.group-disclosure-chevron {
  flex: none;
  width: 13px;
  height: 13px;
  color: var(--text-muted);
  transform: rotate(90deg);
  transition: transform var(--ease), color var(--ease);
}
.group-disclosure-toggle:hover .group-disclosure-chevron { color: var(--cyan-bright); }
.group-disclosure-toggle[aria-expanded="false"] .group-disclosure-chevron { transform: rotate(0); }
.group-disclosure-body[hidden] { display: none; }

/* Footer: host badge, user, logout, collapse toggle */
.sidebar-footer {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.6rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--line);
}
/* Identity card: clickable profile entry. Scoped under .sidebar-footer so it
   overrides the generic ".sidebar-footer button" border/padding below. */
.sidebar-footer .identity-card {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.42rem 0.45rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  text-align: left;
  transition: background var(--ease), border-color var(--ease), box-shadow var(--ease);
}
.sidebar-footer .identity-card:hover {
  background: rgba(56,189,248,0.06);
  border-color: var(--line-2);
  box-shadow: none;
}
.sidebar-footer .identity-card:hover .identity-cog {
  color: var(--cyan-bright);
  transform: rotate(35deg);
}
.identity-avatar {
  --avatar-hue: 205;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: #fff;
  text-transform: uppercase;
  user-select: none;
  background: linear-gradient(145deg, hsl(var(--avatar-hue) 65% 56%), hsl(var(--avatar-hue) 74% 40%));
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.14), 0 2px 6px rgba(0,0,0,0.35);
}
.identity-meta {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.04rem;
}
.identity-name {
  color: var(--text);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.identity-role {
  color: var(--text-muted);
  font-size: 0.7rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.identity-role:empty { display: none; }
.identity-cog {
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--text-dim);
  transition: color var(--ease), transform var(--ease);
}
.sidebar-footer button {
  background: transparent;
  border: 1px solid var(--line-2);
  color: var(--text-soft);
  padding: 0.42rem 0.8rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.78rem;
  font-family: var(--font);
  font-weight: 500;
  transition: all var(--ease);
}
.sidebar-footer button:hover {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56,189,248,0.08);
}
.sidebar-collapse { display: flex; align-items: center; gap: 0.6rem; width: 100%; }
.sidebar-collapse .nav-ico { width: 16px; height: 16px; transition: transform var(--ease); }

/* Collapsed icon rail: labels/text hidden, icons centered. */
body.sidebar-collapsed .nav-label,
body.sidebar-collapsed .sidebar-apps,
body.sidebar-collapsed .identity-meta,
body.sidebar-collapsed .identity-cog { display: none; }
body.sidebar-collapsed .nav-item,
body.sidebar-collapsed .sidebar-collapse { justify-content: center; }
body.sidebar-collapsed .identity-card { justify-content: center; gap: 0; padding-left: 0; padding-right: 0; }
body.sidebar-collapsed .sidebar-collapse .nav-ico { transform: rotate(180deg); }
body.sidebar-collapsed .sidebar-brand { justify-content: center; padding: 0.3rem 0 1rem; }
body.sidebar-collapsed .sidebar-brand .brand { font-size: 0; gap: 0; }
body.sidebar-collapsed .sidebar-brand .sparkle { width: 22px; height: 22px; }

/* Mobile top bar (drawer trigger), hidden on desktop. */
#mobile-topbar {
  display: none;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 1rem;
  background: rgba(var(--inset-rgb),0.85);
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 50;
}
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  background: transparent;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  color: var(--text-soft);
  cursor: pointer;
  transition: all var(--ease);
}
.icon-btn:hover { border-color: var(--cyan-bright); color: var(--cyan-bright); }
.icon-btn svg { width: 20px; height: 20px; }

#sidebar-backdrop { display: none; }

/* ── Content column + main ── */
.app-content { min-width: 0; }
main {
  padding: 2.75rem 2.25rem 4rem;
  max-width: 1180px;
  margin: 0 auto;
}

/* ── Mobile: sidebar becomes an off-canvas drawer ── */
@media (max-width: 860px) {
  #mobile-topbar { display: flex; }
  #app-shell,
  body.sidebar-collapsed #app-shell { grid-template-columns: minmax(0, 1fr); }
  #sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(86vw, 320px);
    z-index: 60;
    transform: translateX(-100%);
    transition: transform var(--ease);
  }
  body.sidebar-open #sidebar { transform: translateX(0); }
  /* The collapsed icon-rail mode does not apply inside the mobile drawer. */
  body.sidebar-collapsed .nav-label,
  body.sidebar-collapsed .sidebar-apps,
  body.sidebar-collapsed .identity-meta,
  body.sidebar-collapsed .identity-cog { display: revert; }
  body.sidebar-collapsed .nav-item { justify-content: flex-start; }
  body.sidebar-collapsed .identity-card { justify-content: flex-start; gap: 0.6rem; padding-left: 0.45rem; padding-right: 0.45rem; }
  body.sidebar-collapsed .sidebar-brand .brand { font-size: 1rem; gap: 0.55rem; }
  body.sidebar-open #sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 55;
  }
  main { padding: 1.75rem 1.25rem 3rem; }
}
@media (prefers-reduced-motion: reduce) {
  #sidebar { transition: none; }
  .group-disclosure-chevron { transition: none; }
}

/* ── Login ── */
/* Signed out, the card is the only thing on screen, so it is centred in the
   viewport instead of hanging from the top. The stretch chain (body → app-shell
   → content → main) is scoped to the signed-out state; every other state keeps
   main's normal document flow. An operator footer (branding.footer_links) is the
   last flex item, so it stays in view below the card. */
body[data-auth="out"] {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}
[data-auth="out"] #app-shell { flex: 1; min-height: 0; }
[data-auth="out"] .app-content { display: flex; flex-direction: column; }
/* main centres itself with `margin: 0 auto`; auto cross-axis margins stop a flex
   item from stretching, so the signed-out state clears them and lets the grid
   below do the centring. */
[data-auth="out"] main { flex: 1; display: flex; width: 100%; max-width: none; margin: 0; padding: 1.5rem; }
[data-auth="out"] #login-view { flex: 1; display: grid; place-items: center; padding: 0; }
/* The third shadow layer is an ambient halo, so the card reads as lit from within
   the star field rather than pasted on top of it. The second declaration tints it
   with --brand-primary (which branding.theme.primary_color overrides) so an
   operator's colour reaches the front door; browsers without color-mix keep the
   first declaration's stock cyan. */
[data-auth="out"] .login-box {
  margin: 0;
  width: 100%;
  padding: 2.5rem 2.25rem;
  box-shadow:
    var(--shadow-card),
    0 0 0 1px rgba(56,189,248,0.04),
    0 0 120px -30px var(--accent-glow);
  box-shadow:
    var(--shadow-card),
    0 0 0 1px rgba(56,189,248,0.04),
    0 0 120px -30px color-mix(in srgb, var(--brand-primary) 30%, transparent);
}

/* The card arrives after the session check resolves, straight out of the boot
   splash. A short rise makes that handoff read as intentional, not a flicker. */
@keyframes login-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
[data-auth="out"] .login-box { animation: login-rise 320ms cubic-bezier(.2,.7,.2,1) both; }
@media (prefers-reduced-motion: reduce) {
  [data-auth="out"] .login-box { animation: none; }
}

/* Brand slot: the stock wordmark, or the operator's logo/site title once
   views/branding.js swaps it in. */
.login-brand {
  display: flex;
  justify-content: center;
  margin-bottom: 1.35rem;
}
.login-brand .brand { font-size: 1.45rem; }
/* A logo replaces the wordmark, so it is sized for the card, not the sidebar,
   and clamped so a wide operator lockup cannot overflow. */
.login-brand .brand-logo { height: 2.5rem; max-width: 100%; object-fit: contain; }

/* A rejected sign-in gets a real surface rather than loose red text under the
   button, matching the .crash-banner treatment used elsewhere for failures. */
#login-error {
  margin-top: 0.9rem;
  padding: 0.55rem 0.7rem;
  background: var(--red-bg);
  border: 1px solid var(--red);
  border-radius: var(--radius);
  font-size: 0.82rem;
  line-height: 1.45;
}

#login-view { padding-top: 2rem; }

.login-box {
  max-width: 400px;
  margin: 4rem auto;
  background: linear-gradient(180deg, var(--surface-2) 0%, var(--surface) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 2.25rem 2rem;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: var(--shadow-card), 0 0 0 1px rgba(56,189,248,0.04);
  position: relative;
  overflow: hidden;
}

.login-box::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, var(--cyan-bright) 50%, transparent 100%);
  opacity: 0.5;
}

/* No heading rule here: the login card's title is now the brand lockup and its
   <h1> is sr-only, and the route-error card that shares .login-box keeps the
   global h1 styling. */

.login-box form { display: grid; gap: 0.6rem; }
/* The form display:grid outranks the UA [hidden] rule, so restore hidden
   semantics for the SSO-only case where the password form is disabled. */
.login-box form[hidden] { display: none; }

.login-box .login-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: -0.3rem;
}

.login-box input {
  display: block;
  width: 100%;
  background: var(--bg-deep);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 0.65rem 0.9rem;
  border-radius: var(--radius);
  font-size: 0.88rem;
  font-family: var(--font);
  transition: all var(--ease);
  outline: none;
}

.login-box input:focus {
  border-color: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56,189,248,0.12);
}
.login-box input::placeholder { color: var(--text-muted); }

.login-box button[type="submit"] {
  width: 100%;
  background: linear-gradient(135deg, var(--brand-primary) 0%, var(--electric) 100%);
  color: var(--bg-deep);
  border: none;
  padding: 0.7rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.88rem;
  font-weight: 700;
  font-family: var(--font);
  margin-top: 0.35rem;
  letter-spacing: -0.005em;
  transition: all var(--ease);
  box-shadow: 0 4px 20px rgba(56,189,248,0.3), inset 0 1px 0 rgba(255,255,255,0.25);
}

.login-box button[type="submit"]:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 28px rgba(56,189,248,0.45), inset 0 1px 0 rgba(255,255,255,0.35);
}

.login-separator {
  text-align: center;
  margin: 1.25rem 0 0.5rem;
  color: var(--text-muted);
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-family: var(--mono);
  position: relative;
}

.login-separator::before,
.login-separator::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 38%;
  height: 1px;
  background: var(--line);
}

.login-separator::before { left: 0; }
.login-separator::after  { right: 0; }

.github-login,
.google-login,
.oidc-login {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.6rem;
  padding: 0.65rem 1rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: var(--radius);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 500;
  font-family: var(--font);
  transition: all var(--ease);
}

/* The button display:flex (author origin) outranks the UA [hidden]{display:none}
   rule, so the attribute alone would not hide an unconfigured provider. Restore
   the hidden semantics explicitly (same pattern as .nav-item[hidden]). */
.github-login[hidden],
.google-login[hidden],
.oidc-login[hidden] { display: none; }

.github-login:hover,
.google-login:hover,
.oidc-login:hover {
  border-color: var(--cyan-bright);
  background: var(--surface-3);
  color: var(--cyan-bright);
}

/* ── Error ── */
.error { margin-top: 0.75rem; color: var(--red); font-size: 0.8rem; }

/* ── Toolbar ── */
.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 2rem;
  gap: 1rem;
}

.toolbar h1 {
  font-size: 2.6rem;
  font-weight: 200;
  letter-spacing: -0.04em;
  line-height: 1;
  background: linear-gradient(100deg, var(--text) 0%, var(--cyan-bright) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.toolbar button {
  background: linear-gradient(135deg, var(--brand-primary) 0%, var(--electric) 100%);
  color: var(--bg-deep);
  border: none;
  padding: 0.6rem 1.25rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.8rem;
  font-weight: 700;
  font-family: var(--font);
  letter-spacing: -0.005em;
  transition: all var(--ease);
  box-shadow: 0 4px 18px rgba(56,189,248,0.3), inset 0 1px 0 rgba(255,255,255,0.2);
}

.toolbar button:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 28px rgba(56,189,248,0.45), inset 0 1px 0 rgba(255,255,255,0.3);
}

/* ── App Grid ── */
.app-grid {
  display: flex;
  flex-direction: column;
  gap: 1.4rem;
}

.app-grid-flat,
.app-grid-group-body {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 1.15rem;
}

.app-grid-group { min-width: 0; }
.app-grid-group-header {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-height: 44px;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--line);
}
.app-grid-group-heading { flex: 0 1 auto; min-width: 0; margin: 0; }
.app-grid-group-toggle {
  /* The negative margin lets the chevron align with the section edge. Give
     that offset back to the button so short project names never ellipsize. */
  max-width: calc(100% + 0.4rem);
  color: var(--text-soft);
  font-size: 0.9rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  border-radius: 6px;
  padding: 0.3rem 0.4rem;
  margin-left: -0.4rem;
}
.app-grid-group-toggle:hover { color: var(--text); background: var(--surface-2); }
.app-grid-group-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.app-grid-group-heading {
  color: var(--text-soft);
}

.app-grid-group-icon {
  font-size: 1rem;
  line-height: 1;
}
.app-grid-group-count {
  flex: none;
  color: var(--text-soft);
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  padding: 0.12rem 0.42rem;
  border-radius: 999px;
  background: var(--surface-2);
}
.app-grid-group-body { padding-top: 0.9rem; }

/* Project editing is secondary. Keep it beside the count as a quiet contextual
   action instead of repeating a labelled toolbar button at the far edge of
   every section. Keyboard focus and touch always reveal it. */
.app-grid-group-edit {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  color: var(--text-muted);
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  opacity: 0;
  transform: translateX(-3px);
  transition: opacity var(--ease), transform var(--ease), color var(--ease), background var(--ease);
}
.app-grid-group-edit svg { width: 15px; height: 15px; }
.app-grid-group-header:hover .app-grid-group-edit,
.app-grid-group-header:focus-within .app-grid-group-edit {
  opacity: 1;
  transform: translateX(0);
}
.app-grid-group-edit:hover {
  color: var(--text);
  background: var(--accent-dim);
}
.app-grid-group-edit:focus-visible { opacity: 1; transform: translateX(0); }

@media (hover: none), (pointer: coarse), (max-width: 700px) {
  .group-disclosure-toggle { min-height: 44px; }
  .app-card-title { min-height: 44px; align-items: center; }
  .app-grid-group-edit { width: 44px; height: 44px; opacity: 0.78; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .app-grid-group-edit { transition: none; transform: none; }
}

.app-card {
  display: flex;
  flex-direction: column;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 0.875rem 1rem;
  max-width: 640px;
  min-height: 148px;
  position: relative;
  /* overflow stays visible so the card's kebab dropdown can escape the card
     bounds instead of being clipped at the bottom edge. The decorative
     ::before accent is inset from the rounded corners so it does not poke out. */
  overflow: visible;
  transition: background var(--ease), border-color var(--ease);
}

/* Raise a card (and its open dropdown) above its grid neighbours so the menu
   is never painted under the next card. */
.app-card.kebab-open { z-index: 30; }

.app-card::before {
  content: none;
}

.app-card::after {
  content: none;
}

.app-card:hover {
  background: var(--surface-3);
  border-color: var(--line-2);
}

.app-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: flex-start;
  gap: 0.75rem;
}

.app-card-identity {
  min-width: 0;
}

.app-card-title {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  min-height: 32px;
  max-width: 100%;
  color: var(--text);
  text-decoration: none;
  border-radius: 4px;
}

.app-card-title strong {
  min-width: 0;
  font-size: 0.95rem;
  line-height: 1.35;
  font-weight: 650;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}

.app-card-manage-arrow {
  flex: none;
  color: var(--text-muted);
  font-size: 1rem;
  transform: translateX(0);
  transition: color var(--ease), transform var(--ease);
}

.app-card-title:hover { color: var(--cyan-bright); }
.app-card-title:hover .app-card-manage-arrow { color: currentColor; transform: translateX(2px); }
.app-card-title:focus-visible { outline: 2px solid var(--cyan-bright); outline-offset: 3px; }

.app-card-slug {
  margin-top: 0.1rem;
  overflow: hidden;
  color: var(--text-soft);
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.35;
  text-overflow: ellipsis;
  white-space: nowrap;
  user-select: text;
}

.app-header-badges {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

/* Status is information, not decoration. A dot-and-label reads consistently
   without creating a row of differently sized capsules. */
.app-header-badges > .badge:first-child {
  padding: 0.1rem 0;
  border-radius: 0;
  background: transparent;
  letter-spacing: 0;
  font-size: 0.72rem;
  line-height: 1.35;
  font-weight: 600;
}

.app-card-facts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.3rem 0;
  min-height: 1.2rem;
  margin-top: 0.65rem;
  color: var(--text-soft);
  font-size: 0.72rem;
  line-height: 1.5;
}

.app-card-fact + .app-card-fact::before {
  content: '·';
  padding: 0 0.42rem;
  color: var(--text-muted);
}
.app-card-fact.is-attention { color: var(--yellow); }
.app-card-fact.is-danger { color: var(--red); font-weight: 600; }

.app-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.4rem;
  min-height: 40px;
  margin-top: auto;
  padding-top: 0.65rem;
  flex-wrap: nowrap;
}

.app-actions a,
.app-actions button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  height: 40px;
  font-size: 0.76rem;
  padding: 0 0.8rem;
  border-radius: var(--radius);
  cursor: pointer;
  border: 1px solid var(--line-2);
  background: rgba(var(--surface-rgb),0.5);
  color: var(--text-soft);
  text-decoration: none;
  font-family: var(--font);
  font-weight: 600;
  transition: background var(--ease), border-color var(--ease), color var(--ease);
}

.app-actions .kebab-menu > button { width: 40px; height: 40px; padding: 0; }

.app-actions a:hover,
.app-actions button:hover {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.08);
}

/* Opening an app is the preferred card action, but the page-level CTA remains
   primary. A restrained tint keeps the hierarchy local to the card. */
.app-actions > a {
  gap: 0.35rem;
  background: rgba(56,189,248,0.08);
  border-color: rgba(56,189,248,0.28);
  color: var(--cyan-bright);
  font-weight: 650;
  box-shadow: none;
}
.app-actions > a:hover {
  filter: none;
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.13);
  border-color: rgba(56,189,248,0.48);
}

.dashboard-emptystate {
  margin: 2rem auto 0;
  text-align: left;
}

.dashboard-emptystate h2 {
  font-size: 1.35rem;
  font-weight: 300;
  letter-spacing: -0.03em;
  color: var(--text);
  margin: 0 0 0.45rem;
  line-height: 1.3;
}

.dashboard-emptystate h2 strong {
  font-weight: 700;
  color: var(--cyan-bright);
}

/* ── Badges ── */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.7rem;
  padding: 0.24rem 0.6rem;
  border-radius: 99px;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-family: var(--font);
  text-transform: none;
  white-space: nowrap;
}

.badge::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

/* Status tiers. Color encodes the family; the dot encodes urgency — only a
   live app's dot breathes, attention/working/standby states hold a steady
   glow, and fully-off states stay quiet. */
.badge-running   { background: var(--green-bg);    color: var(--green); }
.badge-healthy   { background: var(--green-bg);    color: var(--green); }
.badge-deploying { background: var(--yellow-bg);   color: var(--yellow); }
.badge-waking    { background: var(--yellow-bg);   color: var(--yellow); }
.badge-degraded  { background: var(--red-bg);      color: var(--red); }
.badge-crashed   { background: var(--red-bg);      color: var(--red); }
.badge-hibernated { background: var(--standby-bg); color: var(--standby); }
.badge-stopped   { background: var(--gray-bg);     color: var(--text-soft); }
.badge-suspended { background: var(--gray-bg);     color: var(--text-soft); }
.badge-unknown   { background: var(--gray-bg);     color: var(--text-soft); }
.badge-new       { background: var(--accent-glow); color: var(--cyan-bright); }

/* Live: a gently breathing dot, mirroring the detail-header pill. */
.badge-running::before,
.badge-healthy::before {
  box-shadow: 0 0 8px currentColor;
  animation: pulse-dot 2.2s ease-in-out infinite;
}
/* Working, attention, and standby hold a steady glow — present, not moving. */
.badge-deploying::before,
.badge-waking::before,
.badge-degraded::before,
.badge-crashed::before,
.badge-failed::before,
.badge-hibernated::before { box-shadow: 0 0 8px currentColor; }

@media (prefers-reduced-motion: reduce) {
  .badge-running::before,
  .badge-healthy::before { animation: none; }
}

/* Crash banner: a crashed app's failure reason + Restart on the detail Overview. */
.crash-banner {
  background: var(--red-bg);
  border: 1px solid var(--red);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 16px;
}
.crash-banner-title { color: var(--red); font-weight: 600; margin-bottom: 8px; }
.crash-banner-reason {
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.82em;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 240px;
  overflow: auto;
  margin: 0 0 12px;
}
.crash-banner-restart { margin-top: 4px; }

/* Connectivity banner: an amber warning when an app serves pages but its
   WebSocket never connects (usually a reverse proxy blocking the upgrade). Same
   rhythm as the crash banner but amber with a heavier left accent, so it reads
   as "attention", not "dead". */
.conn-banner {
  background: var(--yellow-bg);
  border: 1px solid var(--yellow);
  border-left-width: 3px;
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 16px;
}
.conn-banner-title { color: var(--yellow); font-weight: 600; margin-bottom: 8px; }
.conn-banner-body {
  color: var(--text);
  font-size: 0.9em;
  line-height: 1.5;
  margin: 0 0 12px;
  max-width: 60ch;
}
.conn-banner-link { color: var(--yellow); font-weight: 500; text-decoration: none; }
.conn-banner-link:hover { text-decoration: underline; }

/* Deployment status */
.badge-active   { background: var(--accent-glow); color: var(--cyan-bright); }
.badge-success  { background: var(--green-bg);    color: var(--green); }
.badge-failed   { background: var(--red-bg);      color: var(--red); }
.badge-pending  { background: var(--yellow-bg);   color: var(--yellow); }

/* Fleet ownership. The detail header shows the full "managed by fleet:<id>";
   a fleet id is capped at 64 chars of [a-z0-9-] (ValidFleetID), so the widest
   possible badge is far narrower than the 1100px detail column and needs no
   truncation. */
.badge-fleet    { background: var(--accent-glow); color: var(--cyan-bright); }

/* On a card the badge is the bare word "fleet", so it is dressed down to match
   the autoscale chip beside it: this is metadata about how the app is governed,
   not a state the operator should read as competing with the status badge. */
.app-header-badges .badge-fleet {
  background: var(--gray-bg);
  color: var(--text-soft);
  font-size: 0.65rem;
  padding: 0.05rem 0.35rem;
  border-radius: 0.4rem;
}
.app-detail-fleet {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  margin-top: 0.5rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.fleet-digest-value { font-family: ui-monospace, monospace; }
.fleet-digest-note  { margin: 0.15rem 0 0; font-size: 0.72rem; color: var(--text-muted); }

/* Schedule DST fall-back double-fire advisory (inline in the cron cell) */
.dst-advisory {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.05rem 0.4rem;
  border-radius: 0.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  white-space: nowrap;
  background: var(--yellow-bg);
  color: var(--yellow);
  cursor: help;
}

/* Audit action badges — grouped by semantic color:
   green = deploy/restart/rollback
   blue  = config changes (app, user, env, data, schedule)
   amber = security-sensitive (token, access grant/revoke)
   red   = destructive or failure
   gray  = auth / informational */
.badge-action-deploy              { background: var(--green-bg);   color: var(--green); }
.badge-action-restart             { background: var(--green-bg);   color: var(--green); }
.badge-action-rollback            { background: var(--green-bg);   color: var(--green); }
.badge-action-login               { background: var(--gray-bg);    color: var(--text-soft); }
.badge-action-login_failed        { background: var(--red-bg);     color: var(--red); }
.badge-action-logout              { background: var(--gray-bg);    color: var(--text-soft); }
.badge-action-create_app          { background: var(--accent-dim); color: var(--electric); }
.badge-action-update_app          { background: var(--accent-dim); color: var(--electric); }
.badge-action-delete_app          { background: var(--red-bg);     color: var(--red); }
.badge-action-stop                { background: var(--yellow-bg);  color: var(--yellow); }
.badge-action-set_access          { background: var(--yellow-bg);  color: var(--yellow); }
.badge-action-create_user         { background: var(--accent-dim); color: var(--electric); }
.badge-action-update_user         { background: var(--accent-dim); color: var(--electric); }
.badge-action-delete_user         { background: var(--red-bg);     color: var(--red); }
.badge-action-reset_user_password { background: var(--yellow-bg);  color: var(--yellow); }
.badge-action-create_token        { background: var(--yellow-bg);  color: var(--yellow); }
.badge-action-delete_token        { background: var(--yellow-bg);  color: var(--yellow); }
.badge-action-env-set             { background: var(--accent-dim); color: var(--electric); }
.badge-action-env-delete          { background: var(--red-bg);     color: var(--red); }
.badge-action-data-push           { background: var(--accent-dim); color: var(--electric); }
.badge-action-data-delete         { background: var(--red-bg);     color: var(--red); }
.badge-action-schedule_create     { background: var(--accent-dim); color: var(--electric); }
.badge-action-schedule_delete     { background: var(--red-bg);     color: var(--red); }
.badge-action-grant_access        { background: var(--yellow-bg);  color: var(--yellow); }
.badge-action-revoke_access       { background: var(--yellow-bg);  color: var(--yellow); }
.badge-action-shared_data_grant   { background: var(--accent-dim); color: var(--electric); }
.badge-action-shared_data_revoke  { background: var(--red-bg);     color: var(--red); }
.badge-action-autoscale_scale_up   { background: var(--accent-dim); color: var(--electric); }
.badge-action-autoscale_scale_down { background: var(--accent-dim); color: var(--electric); }
.badge-action-deploy_rejected_quota { background: var(--red-bg);   color: var(--red); }
.badge-action-default             { background: var(--gray-bg);    color: var(--text-soft); }

/* Grid card autoscale indicator */
.badge-autoscale {
  background: var(--accent-dim);
  color: var(--electric);
  font-size: 0.65rem;
  padding: 0.05rem 0.35rem;
  border-radius: 0.4rem;
}

/* Kill-switch warning on the autoscale summary card */
.autoscale-killswitch-warning {
  margin-top: 0.5rem;
  padding: 0.4rem 0.6rem;
  border-radius: 0.4rem;
  background: var(--yellow-bg);
  color: var(--yellow);
  font-size: 0.8rem;
}

/* ── Audit table ── */
#audit-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  margin-bottom: 0.5rem;
  background: linear-gradient(180deg, rgba(var(--surface-2-rgb),0.55) 0%, rgba(var(--surface-rgb),0.35) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

#audit-table th {
  text-align: left;
  padding: 0.9rem 1rem 0.85rem;
  color: var(--text-muted);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-family: var(--mono);
  background: rgba(var(--inset-rgb),0.4);
  border-bottom: 1px solid var(--line);
}

#audit-table td {
  padding: 0.7rem 1rem;
  border-top: 1px solid var(--line);
  vertical-align: middle;
  color: var(--text-soft);
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: -0.01em;
}

#audit-table tbody tr { transition: background var(--ease); }
#audit-table tbody tr:hover { background: rgba(56,189,248,0.04); }

/* ── Users table ── */
#users-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  margin-bottom: 0.5rem;
  background: linear-gradient(180deg, rgba(var(--surface-2-rgb),0.55) 0%, rgba(var(--surface-rgb),0.35) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

#users-table th {
  text-align: left;
  padding: 0.9rem 1rem 0.85rem;
  color: var(--text-muted);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-family: var(--mono);
  background: rgba(var(--inset-rgb),0.4);
  border-bottom: 1px solid var(--line);
}

#users-table td {
  padding: 0.7rem 1rem;
  border-top: 1px solid var(--line);
  vertical-align: middle;
  color: var(--text-soft);
  font-size: 0.82rem;
}

#users-table td.users-username {
  font-family: var(--mono);
  font-weight: 600;
  color: var(--text);
}
/* Friendly display name shown under the username in the admin Users table. */
.users-display-name {
  display: block;
  margin-top: 0.18rem;
  font-family: var(--font);
  font-weight: 400;
  font-size: 0.74rem;
  color: var(--text-muted);
}

#users-table td.users-created {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--text-muted);
}

#users-table tbody tr { transition: background var(--ease); }
#users-table tbody tr:hover { background: rgba(56,189,248,0.04); }

.users-actions-col { width: 1%; white-space: nowrap; }
#users-table td:last-child { white-space: nowrap; }

.users-row-actions {
  display: flex;
  gap: 0.4rem;
  justify-content: flex-end;
  flex-wrap: nowrap;
}

.users-row-actions .btn-row {
  background: transparent;
  border: 1px solid var(--line-2);
  color: var(--text-soft);
  padding: 0.38rem 0.9rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.74rem;
  font-family: var(--font);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  transition: all var(--ease);
}

.users-row-actions .btn-row:hover:not(:disabled) {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.06);
}

.users-row-actions .btn-row.btn-row-danger:hover:not(:disabled) {
  border-color: var(--red);
  color: var(--red);
  background: var(--red-bg);
}

.users-row-actions .btn-row:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.users-row-role {
  background: var(--bg-deep);
  border: 1px solid var(--line-2);
  color: var(--text-soft);
  padding: 0.3rem 0.6rem;
  border-radius: var(--radius);
  font-size: 0.75rem;
  font-family: var(--font);
  cursor: pointer;
}

.users-row-role:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.users-self-tag {
  margin-left: 0.4rem;
  color: var(--text-muted);
  font-size: 0.7rem;
  font-family: var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* Reserved/synthetic account marker (e.g. the deploy-token identity __deploy__),
   whose row is read-only. */
.users-reserved-tag {
  margin-left: 0.4rem;
  padding: 1px 7px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  color: var(--text-muted);
  font-size: 0.66rem;
  font-family: var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: help;
}

/* ── Pagination ── */
.pagination {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 1rem 0;
  font-size: 0.8rem;
  color: var(--text-muted);
  font-family: var(--mono);
  letter-spacing: -0.01em;
}

.pagination button {
  background: transparent;
  border: 1px solid var(--line-2);
  color: var(--text-soft);
  padding: 0.4rem 1rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.76rem;
  font-family: var(--font);
  font-weight: 600;
  transition: all var(--ease);
}

.pagination button:hover:not(:disabled) {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.06);
}

.pagination button:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* ── Log pane ── */
.log-pane {
  position: fixed;
  inset: 0;
  background: var(--bg-deep);
  color: var(--cyan);
  display: flex;
  flex-direction: column;
  z-index: 100;
  font-family: var(--mono);
}

.log-pane[hidden] { display: none; }

.log-pane-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.95rem 1.4rem;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
  background: var(--surface);
}

.log-pane-title {
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--text);
  font-family: var(--mono);
  letter-spacing: -0.01em;
}

.log-pane-header button {
  background: transparent;
  border: 1px solid var(--line-2);
  color: var(--text-soft);
  padding: 0.32rem 0.85rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.76rem;
  font-family: var(--font);
  font-weight: 600;
  transition: all var(--ease);
}

.log-pane-header button:hover {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
}

.log-pane-body {
  flex: 1;
  overflow-y: auto;
  padding: 1rem 1.4rem;
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.65;
  white-space: pre-wrap;
  word-break: break-all;
  color: var(--cyan);
}

/* ── Modal ── */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(3,5,16,0.78);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.modal-overlay[hidden] { display: none; }

.modal-card {
  background: linear-gradient(180deg, rgba(var(--surface-2-rgb),0.92) 0%, rgba(var(--surface-rgb),0.88) 100%);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 560px;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 32px 80px rgba(0,0,0,0.7), 0 0 0 1px rgba(56,189,248,0.08);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  position: relative;
}

.modal-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--cyan-bright), transparent);
  opacity: 0.5;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem 1.4rem;
  border-bottom: 1px solid var(--line);
}

.modal-title {
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: -0.02em;
  color: var(--text);
}

.modal-header button {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  line-height: 1;
  /* 32×32 minimum hit target (WCAG 2.5.5) */
  min-width: 32px;
  min-height: 32px;
  padding: 0.25rem;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: all var(--ease);
}

.modal-header button:hover {
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.08);
}

.modal-body {
  padding: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 1.3rem;
}

/* Visibility radios */
.access-level { border: none; padding: 0; margin: 0; }

/* ── Settings blocks ────────────────────────────────────────────────────────
   Each config / access section is a stacked block: a plain heading + a short
   description, then its controls, separated from the next block by a hairline
   rule with generous space. Replaces the uppercase-eyebrow + side-stripe
   pattern (both generic tells). The detail tab panel lays these out directly. */
.settings-block {
  border: 0;
  margin: 0;
  padding: 1.9rem 0;
  border-top: 1px solid var(--line);
}
.settings-block:first-child { border-top: none; padding-top: 0; }
.settings-block:last-child { padding-bottom: 0.5rem; }
.settings-block-title,
fieldset.settings-block > legend {
  display: block;
  float: none;
  width: 100%;
  margin: 0 0 0.3rem;
  padding: 0;
  font-family: var(--font);
  font-weight: 600;
  font-size: 1.05rem;
  line-height: 1.3;
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--text);
}
.settings-block-desc {
  margin: 0 0 1.4rem;
  max-width: 64ch;
  color: var(--text-muted);
  font-size: 0.875rem;
  line-height: 1.55;
}
.settings-block-desc code {
  font-family: var(--mono);
  font-size: 0.85em;
  color: var(--text-soft);
}
.settings-block-body {
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  min-width: 0;
  max-width: 44rem;
}
/* Fieldsets used as settings blocks (radio groups) must not impose the UA
   min-content width or default chrome. */
fieldset.settings-block { min-width: 0; }
/* Radio options inside a settings block, grouped with their own rhythm. */
.access-radios { display: flex; flex-direction: column; gap: 0.4rem; }
/* A scoped note inside a block (e.g. the native-runtime caveat under Resources):
   reads as advisory, not an error. */
.settings-inline-note {
  margin: -0.5rem 0 1.1rem;
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius);
  background: rgba(56,189,248,0.06);
  border: 1px solid var(--line-2);
  color: var(--text-soft);
  font-size: 0.83rem;
  line-height: 1.5;
}
.settings-inline-note code { font-family: var(--mono); font-size: 0.85em; color: var(--cyan-bright); }
/* Inline warning under a field (e.g. keep-warm floor clamp). */
.scaling-option-warn { color: var(--color-warning, #fbbf24); }
/* Small muted metadata (e.g. data-dir quota usage). */
.settings-meta { color: var(--text-muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }

.access-radios label {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.88rem;
  padding: 0.5rem 0.7rem;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background var(--ease);
  color: var(--text);
  clear: both;
}

.access-radios label:hover { background: var(--surface-2); }

.access-radios input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  width: 15px;
  height: 15px;
  border: 1.5px solid var(--line-2);
  border-radius: 50%;
  background: var(--bg-deep);
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
  transition: all var(--ease);
}

.access-radios input[type="radio"]:checked {
  border-color: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56,189,248,0.15);
}

.access-radios input[type="radio"]:checked::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--cyan-bright);
  box-shadow: 0 0 6px var(--cyan-bright);
}

/* Members section */
/* (Section-header typography for these is defined in the consolidated rule
   above, alongside legends and the env/danger headers.) */

#members-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}

#members-list:empty::after {
  content: 'No members';
  display: block;
  font-size: 0.8rem;
  color: var(--text-muted);
  padding: 0.35rem 0.7rem;
  font-style: italic;
}

#members-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0.7rem;
  border-radius: var(--radius);
  transition: background var(--ease);
}

#members-list li:hover { background: var(--surface-2); }

#members-list li .member-name {
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.82rem;
  letter-spacing: -0.01em;
}

#members-list li .member-role {
  color: var(--text-soft);
  font-size: 0.66rem;
  margin-left: 0.5rem;
  font-family: var(--mono);
  background: var(--surface-3);
  padding: 0.12rem 0.5rem;
  border-radius: 99px;
  border: 1px solid var(--line);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

#members-list li button {
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 0.72rem;
  padding: 0.25rem 0.5rem;
  border-radius: var(--radius);
  font-family: var(--font);
  font-weight: 600;
  transition: all var(--ease);
}

#members-list li button:hover {
  color: var(--red);
  background: var(--red-bg);
}

/* Grant row */
.grant-row { display: flex; gap: 0.5rem; }

.grant-row input {
  flex: 1;
  padding: 0.55rem 0.85rem;
  background: rgba(var(--inset-rgb),0.65);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 0.84rem;
  font-family: var(--mono);
  outline: none;
  letter-spacing: -0.01em;
  transition: all var(--ease);
}

.grant-row input:focus {
  border-color: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56,189,248,0.12);
}
.grant-row input::placeholder { color: var(--text-muted); }

.grant-row button {
  padding: 0.55rem 1.1rem;
  background: linear-gradient(135deg, var(--brand-primary), var(--electric));
  color: var(--bg-deep);
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.8rem;
  font-weight: 700;
  font-family: var(--font);
  transition: all var(--ease);
  box-shadow: 0 0 12px rgba(56,189,248,0.25);
}

.grant-row button:hover:not(:disabled) {
  filter: brightness(1.1);
  transform: translateY(-1px);
}

.grant-row button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* On an undeployed card, Deploy is the recommended next action. Keep it tonal:
   it should not compete with the page-level New app button. */
.app-actions .btn-primary {
  background: rgba(56,189,248,0.08);
  color: var(--cyan-bright);
  border-color: rgba(56,189,248,0.28);
  font-weight: 650;
  box-shadow: none;
}

.app-actions .btn-primary:hover {
  background: rgba(56,189,248,0.13);
  color: var(--cyan-bright);
  border-color: rgba(56,189,248,0.48);
}

/* ── Buttons inside any modal-actions row ── */
.modal-actions button {
  padding: 0.58rem 1.1rem;
  border-radius: var(--radius);
  font-family: var(--font);
  font-size: 0.83rem;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--line-2);
  background: rgba(var(--surface-rgb),0.55);
  color: var(--text-soft);
  transition: all var(--ease);
  letter-spacing: -0.005em;
}

.modal-actions button:hover:not(:disabled) {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.08);
}

.modal-actions button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(56,189,248,0.25);
  border-color: var(--cyan-bright);
}

.modal-actions button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Primary action — submit or explicitly .btn-primary inside a modal */
.modal-actions button[type="submit"],
.modal-actions .btn-primary {
  background: linear-gradient(135deg, var(--brand-primary), var(--electric));
  border-color: transparent;
  color: var(--bg-deep);
  font-weight: 700;
  box-shadow: 0 4px 18px rgba(56,189,248,0.28), inset 0 1px 0 rgba(255,255,255,0.2);
}

.modal-actions button[type="submit"]:hover:not(:disabled),
.modal-actions .btn-primary:hover:not(:disabled) {
  filter: brightness(1.08);
  transform: translateY(-1px);
  background: linear-gradient(135deg, var(--brand-primary), var(--electric));
  border-color: transparent;
  color: var(--bg-deep);
  box-shadow: 0 8px 28px rgba(56,189,248,0.42), inset 0 1px 0 rgba(255,255,255,0.3);
}

.modal-actions button[type="submit"]:disabled,
.modal-actions .btn-primary:disabled {
  background: var(--surface-2);
  color: var(--text-dim);
  box-shadow: none;
  transform: none;
  filter: none;
  border-color: var(--line);
}

/* ── Danger zone (Access modal) ── */
/* Danger zone is a settings block rendered as a red-tinted card for emphasis
   (overrides the plain hairline-rule block style). */
.danger-zone.settings-block {
  margin-top: 0.75rem;
  padding: 1.4rem 1.5rem 1.5rem;
  border: 1px solid rgba(248,113,113,0.35);
  border-radius: var(--radius-lg);
  background: rgba(248,113,113,0.05);
}
.danger-zone .settings-block-title { color: var(--red); }

.danger-zone-lead {
  margin: 0 0 0.8rem;
  font-size: 0.8rem;
  color: var(--text-soft);
  line-height: 1.45;
}

.danger-zone-label {
  display: block;
  font-size: 0.78rem;
  color: var(--text-soft);
  margin-bottom: 0.4rem;
}

.danger-zone-label code {
  color: var(--text);
  background: rgba(var(--inset-rgb),0.65);
  padding: 0.05rem 0.4rem;
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: 0.78rem;
}

.danger-zone-controls {
  display: flex;
  gap: 0.5rem;
}

.danger-zone-controls input {
  flex: 1;
  padding: 0.55rem 0.85rem;
  background: rgba(var(--inset-rgb),0.65);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.82rem;
  transition: border-color var(--ease);
}

.danger-zone-controls input:focus {
  outline: none;
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(248,113,113,0.18);
}

/* Global button chrome for the .btn-primary / .btn-row design classes. Without
   these, a .btn-primary used outside .app-actions or .modal-actions (the only
   two contexts that styled it) fell back to the unstyled user-agent button —
   e.g. the app-detail header "Deploy", "+ Add schedule" and "+ Mount data from
   another app". Context blocks have higher specificity and still override. */
.btn-primary {
  padding: 0.5rem 1.1rem;
  background: linear-gradient(135deg, var(--brand-primary), var(--electric));
  color: var(--bg-deep);
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.83rem;
  font-weight: 700;
  font-family: var(--font);
  transition: all var(--ease);
  box-shadow: 0 4px 14px rgba(56,189,248,0.25);
}
.btn-primary:hover:not(:disabled) {
  filter: brightness(1.08);
  transform: translateY(-1px);
}
.btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
  filter: none;
  transform: none;
}
.btn-row {
  padding: 0.35rem 0.75rem;
  background: rgba(var(--surface-rgb),0.55);
  color: var(--text-soft);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.83rem;
  font-weight: 600;
  font-family: var(--font);
  transition: all var(--ease);
}
.btn-row:hover:not(:disabled) {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
}
.btn-row:disabled { opacity: 0.45; cursor: not-allowed; }

.btn-danger {
  padding: 0.58rem 1.1rem;
  border-radius: var(--radius);
  font-family: var(--font);
  font-size: 0.83rem;
  font-weight: 700;
  cursor: pointer;
  background: var(--red);
  color: var(--bg-deep);
  border: 1px solid var(--red);
  transition: all var(--ease);
  letter-spacing: -0.005em;
  white-space: nowrap;
}

.btn-danger:hover:not(:disabled) {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: 0 6px 20px rgba(248,113,113,0.35);
}

.btn-danger:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(248,113,113,0.28);
}

.btn-danger:disabled {
  background: var(--surface-2);
  color: var(--text-dim);
  border-color: var(--line);
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
  filter: none;
}

/* ── History modal ── */
#history-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

#history-list li {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.85rem 0;
  border-top: 1px solid var(--line);
}

#history-list li:first-child { border-top: none; }

.deployment-meta { flex: 1; }

.deployment-version {
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--text);
  margin-right: 0.5rem;
  letter-spacing: -0.01em;
}

.deployment-date {
  font-size: 0.72rem;
  color: var(--text-muted);
  margin-top: 0.3rem;
  font-family: var(--mono);
  letter-spacing: -0.01em;
}

#history-list li button {
  background: transparent;
  border: 1px solid var(--line-2);
  color: var(--text-soft);
  padding: 0.35rem 0.85rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.74rem;
  font-family: var(--font);
  font-weight: 600;
  transition: all var(--ease);
}

#history-list li button:hover {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.06);
}

/* ── Responsive ── */
@media (max-width: 640px) {
  main { padding: 1.75rem 1rem 3rem; }

  .toolbar {
    flex-wrap: wrap;
    gap: 0.75rem;
  }

  .toolbar h1 {
    font-size: 2rem;
  }

  .app-grid-flat,
  .app-grid-group-body { grid-template-columns: minmax(0, 1fr); }

  .app-actions {
    flex-wrap: wrap;
  }

  .app-actions a,
  .app-actions button,
  .app-actions .kebab-menu > button { min-height: 44px; height: 44px; }

  /* Search field shrinks instead of pushing the toolbar buttons off-screen. */
  .apps-search { width: auto; min-width: 0; flex: 1 1 8rem; }

  /* Detail header: the flex bar wraps the action cluster below the title and the
     stat tiles collapse to two columns on narrow screens. */
  .app-detail-header { padding-bottom: 16px; }
  .app-detail-actions { flex-wrap: wrap; }
  /* 2x2 tiles on mobile. The extra .app-detail-header qualifier outranks the
     later base .app-detail-stats .stat rule (media queries add no specificity,
     so source order would otherwise win). */
  .app-detail-header .app-detail-stats .stat { min-width: calc(50% - 0.375rem); max-width: none; }

  /* Wide admin tables scroll within themselves rather than overflowing body.
     :not([hidden]) so these ID-specificity rules don't override the hidden
     state of an empty Workers/Audit table (display:block would outrank the
     UA [hidden]{display:none} rule). */
  #audit-table:not([hidden]),
  #users-table:not([hidden]),
  #workers-table:not([hidden]) {
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
  }

  /* The toolbar wraps the title above the action cluster; the cluster itself
     must also wrap so Search + sorts + New app + Refresh don't run off-screen. */
  .toolbar-actions { flex-wrap: wrap; width: 100%; }

  /* The 7-item app-section tab bar scrolls horizontally instead of overflowing
     the page; each tab keeps its width rather than being squeezed unreadable.
     scroll-snap gives crisp stops; the edge fade (data-overflow, maintained in
     app-detail.js) hints that more tabs exist on the side that's clipped. */
  .settings-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    scroll-snap-type: x proximity;
  }
  .settings-tab { flex: 0 0 auto; scroll-snap-align: center; }
  .settings-tabs[data-overflow="start"] {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 1.75rem), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 1.75rem), transparent);
  }
  .settings-tabs[data-overflow="mid"] {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 1.75rem, #000 calc(100% - 1.75rem), transparent);
            mask-image: linear-gradient(to right, transparent, #000 1.75rem, #000 calc(100% - 1.75rem), transparent);
  }
  .settings-tabs[data-overflow="end"] {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 1.75rem);
            mask-image: linear-gradient(to right, transparent, #000 1.75rem);
  }

  /* Settings rows stack: the label/help sits above a full-width control instead
     of being crushed next to a fixed-width input. */
}

/* === New-app modal === */

.toolbar-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* Apps grid search + sort controls */
.apps-search {
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.82rem;
  padding: 0.35rem 0.65rem;
  width: 180px;
  transition: border-color var(--ease);
}
.apps-search:focus {
  outline: none;
  border-color: var(--accent);
}
.apps-search::placeholder { color: var(--text-muted); }

/* Loading placeholder shown in data grids/tables during the initial fetch. */
.grid-loading {
  color: var(--text-muted);
  font-family: var(--mono);
  font-size: 0.85rem;
  padding: 1.25rem 0;
}

.apps-sort {
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.82rem;
  padding: 0.35rem 0.65rem;
  cursor: pointer;
  transition: border-color var(--ease);
}
.apps-sort:focus {
  outline: none;
  border-color: var(--accent);
}

.modal-card form label,
.settings-tab-panel form label {
  display: block;
  margin-top: 0.75rem;
  font-size: 0.82rem;
  color: var(--text);
}

.modal-card form input[type="text"],
.modal-card form input[type="password"],
.modal-card form input[type="email"],
.modal-card form input[type="number"],
.modal-card form input:not([type]),
.modal-card form select,
.modal-card form textarea,
.settings-tab-panel input[type="text"],
.settings-tab-panel input[type="password"],
.settings-tab-panel input[type="email"],
.settings-tab-panel input[type="number"],
.settings-tab-panel select,
.settings-tab-panel textarea {
  width: 100%;
  margin-top: 0.25rem;
  padding: 0.55rem 0.75rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.82rem;
  box-sizing: border-box;
  transition: border-color var(--ease), box-shadow var(--ease);
}

.modal-card form textarea,
.settings-tab-panel textarea {
  resize: vertical;
  min-height: 4.5rem;
  line-height: 1.4;
}

.modal-card form input[type="text"]:focus,
.modal-card form input[type="password"]:focus,
.modal-card form input[type="email"]:focus,
.modal-card form input[type="number"]:focus,
.modal-card form input:not([type]):focus,
.modal-card form select:focus,
.modal-card form textarea:focus,
.settings-tab-panel input[type="text"]:focus,
.settings-tab-panel input[type="password"]:focus,
.settings-tab-panel input[type="email"]:focus,
.settings-tab-panel input[type="number"]:focus,
.settings-tab-panel select:focus,
.settings-tab-panel textarea:focus {
  outline: none;
  border-color: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56,189,248,0.15);
}

.modal-card form input[type="checkbox"],
.settings-tab-panel input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 15px;
  height: 15px;
  margin: 0 0.5rem 0 0;
  border: 1.5px solid var(--line-2);
  border-radius: 4px;
  background: var(--bg-deep);
  cursor: pointer;
  vertical-align: middle;
  position: relative;
  flex-shrink: 0;
  transition: all var(--ease);
}

.modal-card form input[type="checkbox"]:checked,
.settings-tab-panel input[type="checkbox"]:checked {
  border-color: var(--cyan-bright);
  background: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56,189,248,0.15);
}

.modal-card form input[type="checkbox"]:checked::after,
.settings-tab-panel input[type="checkbox"]:checked::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 4px;
  width: 4px;
  height: 8px;
  border: solid var(--bg-deep);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.modal-card form input[type="checkbox"]:focus-visible,
.settings-tab-panel input[type="checkbox"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(56,189,248,0.25);
}

/* Plain checkbox toggle rows lay out as an inline flex (checkbox + label). The
   :not(.scaling-row) guard leaves settings-row controls on their own grid so a
   checkbox-bearing row (e.g. Enable autoscale) still aligns with sibling fields. */
.modal-card form label:has(> input[type="checkbox"]),
.settings-tab-panel label:not(.scaling-row):has(> input[type="checkbox"]) {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.modal-card form select,
.settings-tab-panel select {
  font-family: var(--font);
  appearance: none;
  -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
                    linear-gradient(-45deg, transparent 50%, var(--text-muted) 50%);
  background-position: calc(100% - 18px) 55%, calc(100% - 12px) 55%;
  background-size: 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2rem;
  cursor: pointer;
}

.hint {
  color: var(--text-muted);
  font-size: 0.72rem;
  margin-top: 0.25rem;
}

.hint-inline {
  color: var(--text-muted);
  font-weight: 400;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 1rem;
}

/* === Hand-off card: wraps form + snippet as a single unit === */

.handoff-card {
  background: rgba(var(--inset-rgb),0.4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.1rem 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.onboarding-step {
  margin: 0 0 0.75rem;
  color: var(--text-soft);
  font-size: 0.78rem;
  font-weight: 600;
}

.new-app-optional {
  margin-top: 0.8rem;
}

.new-app-optional summary {
  color: var(--text-soft);
  cursor: pointer;
  font-size: 0.8rem;
  font-weight: 600;
}

.new-app-optional[open] summary {
  margin-bottom: 0.7rem;
}

.handoff-success {
  margin: 1rem 0 0.25rem;
  color: var(--text);
  font-size: 0.95rem;
  font-weight: 700;
}

.handoff-card-divider {
  border: 0;
  border-top: 1px dashed var(--line);
  margin: 0;
}

.handoff-section-label {
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-family: var(--mono);
  margin: 0;
}

.snippet {
  display: flex;
  flex-direction: column-reverse;
  background: var(--bg-deep);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}

.snippet pre {
  background: transparent;
  border: 0;
  padding: 0.75rem 0.85rem;
  margin: 0;
  color: var(--cyan-bright);
  font-family: var(--mono);
  font-size: 0.78rem;
  white-space: pre;
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--line-2) transparent;
}

.snippet pre::-webkit-scrollbar { height: 6px; }
.snippet pre::-webkit-scrollbar-track { background: transparent; }
.snippet pre::-webkit-scrollbar-thumb {
  background: var(--line-2);
  border-radius: 3px;
}

.snippet pre code { color: inherit; font-family: inherit; }

/* Toolbar row above the code; column-reverse on .snippet puts this on top. */
.copy-btn {
  align-self: flex-end;
  margin: 0.4rem 0.4rem 0;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: var(--surface);
  border: 1px solid var(--line-2);
  color: var(--text-soft);
  padding: 0.3rem 0.55rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.72rem;
  font-family: var(--font);
  font-weight: 600;
  transition: all var(--ease);
}

.copy-btn:hover {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
}

.copy-btn.is-copied {
  border-color: var(--green);
  color: var(--green);
  background: var(--green-bg);
}

.copy-btn svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

.copy-btn .copy-icon-check { display: none; }
.copy-btn.is-copied .copy-icon-clipboard { display: none; }
.copy-btn.is-copied .copy-icon-check { display: inline-block; }

/* Compact icon-only copy button used inline next to slugs / usernames */
.copy-btn-inline {
  align-self: center;
  margin: 0;
  padding: 0.1rem 0.2rem;
  background: transparent;
  border-color: transparent;
  opacity: 0;
  transition: opacity var(--ease), border-color var(--ease), color var(--ease);
}
.copy-btn-inline svg { width: 12px; height: 12px; }
/* Show on hover of the parent container */
.app-slug-wrap:hover .copy-btn-inline,
.users-username:hover .copy-btn-inline,
.copy-btn-inline:focus { opacity: 1; }

.app-slug-wrap {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}

/* === Empty-state page: never-deployed app (server-rendered) === */

.emptystate-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
}

.emptystate-card {
  width: 100%;
  max-width: 640px;
  background: linear-gradient(180deg, rgba(var(--surface-2-rgb),0.82) 0%, rgba(var(--surface-rgb),0.65) 100%);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-lg);
  padding: 2rem 1.75rem 1.85rem;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 32px 80px rgba(0,0,0,0.6), 0 0 0 1px rgba(56,189,248,0.08);
  position: relative;
  overflow: hidden;
}

.emptystate-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--cyan-bright), transparent);
  opacity: 0.5;
}

.emptystate-eyebrow {
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cyan-bright);
  font-family: var(--mono);
  margin: 0 0 0.55rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.emptystate-eyebrow .sparkle {
  color: var(--cyan-bright);
  font-size: 1em;
  animation: pulse-glow 3.5s ease-in-out infinite;
}

.emptystate-card h1 {
  font-size: 1.6rem;
  font-weight: 300;
  letter-spacing: -0.03em;
  color: var(--text);
  margin: 0 0 0.55rem;
  line-height: 1.25;
}

.emptystate-card h1 strong {
  font-weight: 700;
  background: linear-gradient(100deg, var(--text) 0%, var(--cyan-bright) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.emptystate-card .lead {
  color: var(--text-soft);
  font-size: 0.92rem;
  line-height: 1.55;
  margin: 0 0 1.35rem;
}

.emptystate-actions {
  display: flex;
  gap: 0.55rem;
  flex-wrap: wrap;
  margin-top: 1rem;
}

.emptystate-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.55rem 1rem;
  border-radius: var(--radius);
  font-size: 0.82rem;
  font-family: var(--font);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid var(--line-2);
  background: rgba(var(--surface-rgb),0.5);
  color: var(--text-soft);
  transition: all var(--ease);
}

.emptystate-btn:hover {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.08);
}

.emptystate-btn-primary {
  background: linear-gradient(135deg, var(--brand-primary), var(--electric));
  border-color: transparent;
  color: var(--bg-deep);
  box-shadow: 0 0 14px rgba(56,189,248,0.25);
}

.emptystate-btn-primary:hover {
  filter: brightness(1.08);
  color: var(--bg-deep);
  background: linear-gradient(135deg, var(--brand-primary), var(--electric));
  border-color: transparent;
}

.emptystate-scaffold {
  margin-top: 1.35rem;
  border-top: 1px dashed var(--line);
  padding-top: 1rem;
}

.emptystate-scaffold summary {
  cursor: pointer;
  color: var(--text-soft);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 0.25rem 0;
  list-style: none;
  user-select: none;
}

.emptystate-scaffold summary::marker,
.emptystate-scaffold summary::-webkit-details-marker { display: none; }

.emptystate-scaffold summary::before {
  content: '▸';
  display: inline-block;
  margin-right: 0.5rem;
  color: var(--text-muted);
  transition: transform var(--ease);
}

.emptystate-scaffold[open] summary::before {
  transform: rotate(90deg);
}

.emptystate-scaffold summary:hover { color: var(--cyan-bright); }

.emptystate-scaffold-body {
  padding-top: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.emptystate-scaffold-body p {
  color: var(--text-muted);
  font-size: 0.82rem;
  margin: 0;
}

.emptystate-scaffold-file-label {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  margin-bottom: 0.25rem;
}

/* === Deploy modal === */

.dropzone {
  border: 1.5px dashed var(--line);
  border-radius: 10px;
  padding: 2rem 1rem;
  text-align: center;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
  color: var(--text-muted);
}

.dropzone.dragover {
  border-color: var(--cyan-bright);
  background: color-mix(in srgb, var(--cyan-bright) 8%, transparent);
  color: var(--text);
}

.dropzone p {
  margin: 0.25rem 0;
  font-size: 0.88rem;
}

.dropzone code {
  font-family: var(--mono);
  background: var(--surface);
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
}

.linklike {
  background: transparent;
  border: none;
  color: var(--cyan-bright);
  cursor: pointer;
  font: inherit;
  padding: 0;
  text-decoration: underline;
}

.deploy-summary {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.3rem 0.75rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}

.deploy-summary li {
  display: contents;
}

.deploy-summary li span {
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.78rem;
  word-break: break-all;
}

.deploy-progress {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
}

.deploy-progress progress {
  flex: 1;
  height: 8px;
  appearance: none;
}

.deploy-progress progress::-webkit-progress-bar {
  background: var(--surface);
  border-radius: 4px;
}

.deploy-progress progress::-webkit-progress-value {
  background: var(--cyan-bright);
  border-radius: 4px;
}

.deploy-progress progress::-moz-progress-bar {
  background: var(--cyan-bright);
  border-radius: 4px;
}

#deploy-progress-text {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  min-width: 3em;
  text-align: right;
}

.deploy-result {
  margin-top: 1rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--line-2);
  border-radius: 6px;
  background: var(--surface);
}

.deploy-result-heading {
  margin: 0 0 0.4rem 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
}

.deploy-result-list {
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 0.8rem;
  color: var(--text-muted);
  font-family: var(--mono);
}

.deploy-result-list li {
  padding: 0.15rem 0;
}

/* ── Settings modal tabs ── */
/* Elevated folder tabs: inactive tabs are flat muted text; the active tab lifts
   into a surface card with a glowing cyan top cap, its bottom edge merging into
   the baseline so it reads as connected to the content area below. */
.settings-tabs {
  display: flex;
  gap: 0.3rem;
  align-items: flex-end;
  border-bottom: 1px solid var(--line);
  padding: 0 1.4rem;
  /* Separate the tab bar from the panel below. The panel is display:contents,
     so its content butts against this border without a margin here. */
  margin-bottom: 1.75rem;
}

.settings-tab {
  position: relative;
  text-decoration: none;
  background: transparent;
  /* All four borders are reserved (transparent) so toggling the active state
     adds no width and never shifts the row. */
  border: 1px solid transparent;
  border-radius: 9px 9px 0 0;
  padding: 0.55rem 1.05rem;
  /* Pull the bottom edge over the nav's baseline so the active card can break
     the line beneath it. */
  margin-bottom: -1px;
  cursor: pointer;
  color: var(--text-muted);
  font-family: var(--font);
  font-size: 0.84rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  transition: color var(--ease), background var(--ease), border-color var(--ease);
}

/* Inactive hover: a soft rounded lift hinting at the folder shape. */
.settings-tab:not(.active):hover {
  color: var(--text-soft);
  background: var(--surface-2);
}

.settings-tab:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px rgba(56, 189, 248, 0.55);
}

/* Active = a lifted surface card; the surface-coloured bottom border covers the
   baseline so the folder opens into the content area. */
.settings-tab.active {
  color: var(--text);
  background: var(--surface);
  border-color: var(--line);
  border-bottom-color: var(--surface);
}

/* Glowing cyan cap along the top edge of the active card. */
.settings-tab.active::after {
  content: '';
  position: absolute;
  top: -1px;
  left: -1px;
  right: -1px;
  height: 3px;
  background: var(--cyan-bright);
  border-radius: 9px 9px 0 0;
  box-shadow: 0 0 12px rgba(56, 189, 248, 0.55);
}

.settings-tab-panel {
  display: contents;
}

.settings-tab-panel[hidden] {
  display: none;
}

/* ── Environment tab ── */
.env-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.75rem;
}

/* env-header h4 sits in a flex row next to the "+ Add variable" button; the
   shared section-header typography applies, but drop the block margin so it
   stays vertically centred with the button. */
.env-header h4 {
  margin: 0;
}

.env-add-btn {
  padding: 0.4rem 0.8rem;
  background: var(--accent-dim);
  color: var(--cyan-bright);
  border: 1px solid rgba(56,189,248,0.25);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 600;
  font-family: var(--font);
  transition: all var(--ease);
}

.env-add-btn:hover {
  background: rgba(56,189,248,0.18);
  border-color: var(--cyan-bright);
}

.env-list {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1rem;
}

.env-list th {
  text-align: left;
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-family: var(--mono);
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--line);
}

.env-list td {
  padding: 0.5rem;
  border-bottom: 1px solid var(--line);
  font-size: 0.82rem;
  color: var(--text-soft);
  word-break: break-all;
}

.env-list td:first-child {
  font-family: var(--mono);
  color: var(--text);
  font-size: 0.8rem;
}

.env-list .env-secret {
  font-family: var(--mono);
  letter-spacing: 0.1em;
  color: var(--text-muted);
}

.env-list .env-actions {
  display: flex;
  gap: 0.25rem;
  justify-content: flex-end;
  white-space: nowrap;
}

.env-list .env-actions button {
  padding: 0.25rem 0.6rem;
  background: transparent;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.75rem;
  font-weight: 600;
  font-family: var(--font);
  color: var(--text-soft);
  transition: all var(--ease);
}

.env-list .env-actions button:hover {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.08);
}

.env-list .env-actions .env-btn-danger {
  color: var(--red);
  border-color: rgba(248,113,113,0.35);
}

.env-list .env-actions .env-btn-danger:hover {
  background: rgba(248,113,113,0.1);
  border-color: var(--red);
  color: var(--red);
}

.env-empty {
  color: var(--text-muted);
  font-size: 0.83rem;
  margin: 0.75rem 0 1rem;
}

.env-form {
  padding-top: 1rem;
  border-top: 1px solid var(--line);
}

.env-form h5 {
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-soft);
  font-family: var(--mono);
  margin-bottom: 0.85rem;
}

.env-form-row {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-bottom: 0.75rem;
}

.env-form-row label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-soft);
}

.env-form-row input[type="text"] {
  padding: 0.55rem 0.85rem;
  background: rgba(var(--inset-rgb),0.65);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.82rem;
  transition: border-color var(--ease);
}

.env-form-row input[type="text"]:focus {
  outline: none;
  border-color: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56,189,248,0.14);
}

.env-form-row input[type="text"]:read-only {
  color: var(--text-muted);
  cursor: not-allowed;
}

.env-form-checks {
  gap: 0.4rem;
  flex-direction: column;
}

.env-form-checks label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 400;
  cursor: pointer;
  color: var(--text-soft);
  font-size: 0.82rem;
}

.env-form-checks label input[type="checkbox"]:disabled + * {
  opacity: 0.5;
}

.env-form-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.env-btn-primary {
  padding: 0.5rem 1.1rem;
  background: linear-gradient(135deg, var(--brand-primary), var(--electric));
  color: var(--bg-deep);
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.83rem;
  font-weight: 700;
  font-family: var(--font);
  transition: all var(--ease);
  box-shadow: 0 4px 14px rgba(56,189,248,0.25);
}

.env-btn-primary:hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
}

.env-btn-secondary {
  padding: 0.5rem 1.1rem;
  background: rgba(var(--surface-rgb),0.55);
  color: var(--text-soft);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.83rem;
  font-weight: 600;
  font-family: var(--font);
  transition: all var(--ease);
}

.env-btn-secondary:hover {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.08);
}

/* ── Data tab ── */
#data-progress {
  width: 100%;
  height: 8px;
  appearance: none;
  margin-bottom: 0.5rem;
}

#data-progress::-webkit-progress-bar {
  background: var(--surface);
  border-radius: 4px;
}

#data-progress::-webkit-progress-value {
  background: var(--cyan-bright);
  border-radius: 4px;
}

#data-progress::-moz-progress-bar {
  background: var(--cyan-bright);
  border-radius: 4px;
}

.env-form-row input[type="file"] {
  color: var(--text-soft);
  font-size: 0.85rem;
  padding: 0;
  border: none;
  background: transparent;
}

.env-form-row input[type="file"]::file-selector-button {
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-soft);
  background: var(--bg-deep);
  border: 1px solid var(--line-2);
  border-radius: 6px;
  padding: 6px 12px;
  margin-right: 0.75rem;
  cursor: pointer;
  transition: all var(--ease);
}

.env-form-row input[type="file"]::file-selector-button:hover {
  color: var(--cyan-bright);
  border-color: var(--cyan-bright);
  background: rgba(56, 189, 248, 0.08);
}

.env-form-row input[type="file"]:focus-visible {
  outline: none;
}
.env-form-row input[type="file"]:focus-visible::file-selector-button {
  border-color: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.25);
}

/* ── Schedules & Shared Data tabs ── */

.schedules-table table { width: 100%; border-collapse: collapse; }
.schedules-table th,
.schedules-table td { padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--line); }
.schedules-table .table-actions { white-space: nowrap; }

/* Unlike the other wide admin tables (audit/users/workers), the schedules
   table has no fixed id — it is rebuilt by loadSchedules() into the
   .schedules-table container — so it was missing from the shared 640px
   overflow rule. Placed after the base rules (not in the earlier shared
   640px block) so it actually wins; see the .scaling-row precedent above. */
@media (max-width: 640px) {
  .schedules-table {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .schedules-table table { white-space: nowrap; }
}

.shared-data-table .shared-data-list { list-style: none; padding: 0; margin: 0; }
.shared-data-list li { display: flex; justify-content: space-between; align-items: center; padding: 4px 0; }

.status-pill { padding: 2px 8px; border-radius: 999px; font-size: 0.85em; }
.status-on  { background: var(--green-bg); color: var(--green); }
.status-off { background: var(--red-bg);   color: var(--red); }

.settings-help { color: var(--text-muted); font-size: 0.9em; margin: -4px 0 12px; }

.run-history-list { list-style: none; padding: 0; margin: 0; }
.run-history-list li { border-bottom: 1px solid var(--line); }
.run-history-btn { background: none; border: none; cursor: pointer; padding: 6px 0; color: var(--text); text-align: left; width: 100%; }
.run-history-btn:hover { color: var(--cyan-bright); }

.btn-danger-sm {
  padding: 2px 8px;
  font-size: 0.8em;
  background: transparent;
  border: 1px solid var(--red);
  color: var(--red);
  border-radius: 4px;
  cursor: pointer;
}
.btn-danger-sm:hover { background: var(--red); color: #fff; }

.toast {
  position: fixed;
  bottom: 20px;
  right: 20px;
  background: var(--surface-3);
  color: var(--text);
  padding: 10px 16px;
  border-radius: 4px;
  opacity: 0;
  transition: opacity 0.2s;
  z-index: 10000;
  pointer-events: none;
  max-width: 320px;
  font-size: 0.875rem;
}
.toast.show { opacity: 1; }
.toast-success { background: var(--green-bg);  color: var(--green);  border: 1px solid rgba(74,222,128,0.3); }
.toast-error   { background: var(--red-bg);    color: var(--red);    border: 1px solid rgba(248,113,113,0.3); }

/* General tab — hibernate options */
.hibernate-options label {
  align-items: flex-start;
  gap: 0.6rem;
}
.hibernate-options label > span {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.hibernate-option-title {
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--text);
}
.hibernate-option-help {
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.35;
}
.hibernate-custom-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.35rem;
}
.hibernate-custom-row input[type="number"] {
  width: 6rem;
  padding: 0.35rem 0.5rem;
  font-family: var(--mono);
  background: var(--bg-deep);
  color: var(--text);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
}
.hibernate-custom-row input[type="number"]:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.hibernate-custom-unit {
  font-size: 0.85rem;
  color: var(--text-muted);
}
.hibernate-status {
  font-size: 0.85rem;
  color: var(--green, #4ade80);
  margin-left: 0.6rem;
  align-self: center;
}
/* "Unsaved changes" hint shown next to a settings Save button while the section
   has pending edits (explicit-save model). */
.settings-dirty {
  font-size: 0.8rem;
  color: var(--color-warning, #fbbf24);
  margin-left: 0.6rem;
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.settings-dirty::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.settings-dirty[hidden] { display: none; }

/* Traces — an unsampled span was never exported to the backend, so its id is
   shown muted with no deep link (a link would resolve to "trace not found"). */
.trace-unsampled {
  color: var(--text-muted, #6B7AA3);
}
.trace-unsampled-tag {
  color: var(--text-muted, #6B7AA3);
  font-size: 0.75rem;
  font-style: italic;
}

/* General tab — scaling options */
.scaling-options {
  margin-top: 1.5rem;
}
.scaling-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 16rem;
  align-items: center;
  gap: 1.5rem;
}
/* All controls share the left edge of the fixed input column so number, text,
   and checkbox fields line up vertically regardless of their own width. */
.scaling-row > input { justify-self: start; }
.scaling-row > span {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.scaling-option-title {
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--text);
}
.scaling-option-help {
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.35;
}
.scaling-option-help code {
  background: var(--bg-deep);
  padding: 0 0.25rem;
  border-radius: 3px;
  font-size: 0.78rem;
}
.scaling-row input[type="number"] {
  width: 6rem;
  padding: 0.4rem 0.6rem;
  font-family: var(--mono);
  background: var(--bg-deep);
  color: var(--text);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
}
.scaling-row input[type="number"]:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* Text inputs in a settings row (General: display name, project slug) share the
   number-input chrome but are wider since they hold free text. */
.scaling-row input[type="text"] {
  width: 100%;
  padding: 0.4rem 0.6rem;
  font-family: var(--mono);
  background: var(--bg-deep);
  color: var(--text);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
}
.scaling-row input[type="text"]:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* Stack settings rows on narrow screens: label/help over a full-width control.
   Placed after the base .scaling-row rules so it actually wins (an earlier copy
   in the shared 640px block was overridden by source order). */
@media (max-width: 640px) {
  .scaling-row {
    grid-template-columns: 1fr;
    align-items: stretch;
    gap: 0.5rem;
  }
  .scaling-row input[type="text"],
  .scaling-row input[type="number"] { width: 100%; }
}
.scaling-ceiling {
  margin: 0.4rem 0 0;
  padding: 0.5rem 0.7rem;
  font-size: 0.85rem;
  color: var(--text-soft);
  background: var(--bg-deep);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
}
.scaling-ceiling strong { color: var(--cyan-bright); }
.scaling-ceiling:empty {
  display: none;
}
.scaling-ceiling strong {
  color: var(--text);
  font-weight: 600;
}

/* ── App detail page ── */

#app-detail-view {
  max-width: 1100px;
  margin: 0 auto;
  padding: 24px 32px 60px;
}

.breadcrumb {
  margin-bottom: 12px;
  font-size: 0.85rem;
}
.breadcrumb a {
  color: var(--text-muted);
  text-decoration: none;
}
.breadcrumb a:hover { color: var(--cyan-bright); }

.app-detail-header {
  border-bottom: 1px solid var(--line);
  padding-bottom: 22px;
  margin-bottom: 22px;
}
.app-detail-bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.app-detail-identity {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  min-width: 0;
}
.app-detail-title {
  font-size: 1.7rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0;
  color: var(--text);
}
.app-detail-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-shrink: 0;
}

/* Header status pill: a state-coloured dot + label. The dot is currentColor, so
   each state modifier colours both at once. Scoped to .app-detail-header so it
   never touches the pre-existing schedules .status-pill (status-on/off) above. */
.app-detail-header .status-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.26rem 0.7rem 0.26rem 0.62rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  border: 1px solid var(--line-2);
  background: var(--surface);
  color: var(--text-muted);
  white-space: nowrap;
}
.app-detail-header .status-pill::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.app-detail-header .status-pill.status-running,
.app-detail-header .status-pill.status-healthy { color: var(--green); background: var(--green-bg); border-color: transparent; }
.app-detail-header .status-pill.status-deploying,
.app-detail-header .status-pill.status-waking,
.app-detail-header .status-pill.status-pending { color: var(--yellow); background: var(--yellow-bg); border-color: transparent; }
.app-detail-header .status-pill.status-degraded,
.app-detail-header .status-pill.status-crashed,
.app-detail-header .status-pill.status-failed { color: var(--red); background: var(--red-bg); border-color: transparent; }
.app-detail-header .status-pill.status-new { color: var(--cyan-bright); background: var(--accent-glow); border-color: transparent; }
.app-detail-header .status-pill.status-hibernated { color: var(--standby); background: var(--standby-bg); border-color: transparent; }
.app-detail-header .status-pill.status-stopped,
.app-detail-header .status-pill.status-suspended,
.app-detail-header .status-pill.status-unknown { color: var(--text-muted); background: var(--gray-bg); }
/* Live (running) state: a gently breathing dot. */
.app-detail-header .status-pill.is-live::before { animation: pulse-dot 2.2s ease-in-out infinite; }
@keyframes pulse-dot {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(0.68); opacity: 0.5; }
}
@media (prefers-reduced-motion: reduce) {
  .app-detail-header .status-pill.is-live::before { animation: none; }
}

/* Meta line: slug + version mono chips, deploy count, deployed-ago. */
.app-detail-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.45rem 0.85rem;
  margin-top: 0.75rem;
  color: var(--text-muted);
  font-size: 0.85rem;
}
.meta-chip {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--text-soft);
  background: var(--bg-deep);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  padding: 0.1rem 0.45rem;
}
.meta-chip[hidden] { display: none; }
#app-detail-deployed:empty { display: none; }

/* Metric tiles: real fleet aggregates (CPU/Memory/Replicas/Sessions). */
.app-detail-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 1.15rem 0 0;
}
.app-detail-stats .stat {
  flex: 1 1 0;
  min-width: 8.5rem;
  max-width: 13rem;
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.6rem 0.85rem;
}
.stat-label {
  margin: 0;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-muted);
}
.stat-value {
  margin: 0.25rem 0 0;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
}
.stat-value.is-empty { color: var(--text-muted); }

.kebab-menu { position: relative; }
/* The "⋯" toggle, styled consistently wherever a kebab appears (dashboard card
   and app-detail header). Previously the detail-header toggle had no class-level
   style and fell back to the unstyled user-agent button. */
.kebab-menu > button {
  font-size: 0.95rem;
  line-height: 1;
  padding: 0.34rem 0.62rem;
  border-radius: var(--radius);
  cursor: pointer;
  border: 1px solid var(--line-2);
  background: rgba(var(--surface-rgb),0.5);
  color: var(--text-soft);
  font-family: var(--font);
  transition: background var(--ease), border-color var(--ease), color var(--ease);
}
.kebab-menu > button:hover,
.kebab-menu > button[aria-expanded="true"] {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.08);
}
.kebab-list {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  background: var(--bg-deep);
  border: 1px solid var(--line-2);
  border-radius: 6px;
  list-style: none;
  margin: 0;
  padding: 4px;
  min-width: 140px;
  z-index: 10;
}
.kebab-list button {
  display: block;
  width: 100%;
  min-height: 40px;
  padding: 8px 10px;
  background: transparent;
  border: none;
  text-align: left;
  color: var(--text-soft);
  cursor: pointer;
  border-radius: 4px;
}
.kebab-list button:focus-visible {
  outline: 2px solid var(--cyan-bright);
  outline-offset: -2px;
  background: rgba(56, 189, 248, 0.1);
  color: var(--cyan-bright);
}

/* ── App detail: Overview tab ─────────────────────────────────────────────── */

.overview-card { padding: 20px; border: 1px solid var(--line); border-radius: 8px; }
.overview-card + .overview-card { margin-top: 16px; }
.overview-dl { display: grid; grid-template-columns: max-content 1fr; gap: 8px 24px; margin: 16px 0; }
.overview-dl dt { color: var(--text-dim); }
.overview-version { font-family: 'Space Mono', monospace; }
.overview-links { display: flex; gap: 16px; margin-top: 12px; }
.overview-links a { color: var(--accent); text-decoration: none; }
.overview-links a:hover { text-decoration: underline; }

.trends-card h3 { margin: 0 0 4px; }
.trend-row { display: grid; grid-template-columns: 5.5rem 5rem 1fr; align-items: center; gap: 12px; padding: 7px 0; }
.trend-row + .trend-row { border-top: 1px solid var(--line); }
.trend-label { color: var(--text-dim); font-size: 0.9rem; }
.trend-value { font-family: 'Space Mono', monospace; color: var(--text); font-size: 0.95rem; text-align: right; }
.trend-row .sparkline { width: 100%; height: 28px; display: block; color: var(--accent); }
.trend-row .sparkline-instances { color: var(--cyan); }
.trends-empty { color: var(--text-muted); font-size: 0.9rem; padding: 6px 0; margin: 0; }

.overview-replicas h3 { display: flex; align-items: baseline; gap: 8px; }
.overview-replicas-cap { color: var(--text-muted); font-size: 0.85rem; font-weight: normal; }
.replicas-list { list-style: none; padding: 0; margin: 12px 0 0; }
.replicas-empty { color: var(--text-muted); font-size: 0.9rem; padding: 6px 0; }
.replica-row {
  display: grid;
  grid-template-columns: 3rem auto 1fr auto auto;
  gap: 12px;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
}
.replica-row:last-child { border-bottom: none; }
.replica-index { font-family: 'Space Mono', monospace; color: var(--cyan-bright); }
.replica-sessions { color: var(--text-soft); font-variant-numeric: tabular-nums; }
.replica-sessions-saturated { color: var(--danger, #f87171); font-weight: 600; }
.replica-reason { color: var(--danger, #f87171); font-size: 0.8rem; font-style: italic; }
.replica-reason:empty { display: none; }
.fleet-health { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 10px 14px; margin-bottom: 16px; border: 1px solid var(--line); border-radius: 8px; font-size: 0.9rem; }
.fleet-health[hidden] { display: none; }
.fleet-health-headline { color: var(--text-soft); font-variant-numeric: tabular-nums; }
.fleet-health-chip { color: var(--danger, #f87171); font-size: 0.8rem; padding: 2px 8px; border: 1px solid var(--line); border-radius: 12px; }
.fleet-health-lost { border-color: var(--danger, #f87171); }
.replica-cpu, .replica-ram { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.kebab-list button:hover { background: rgba(56, 189, 248, 0.1); color: var(--cyan-bright); }

/* ── App detail: Logs tab ─────────────────────────────────────────────────── */

.logs-workspace {
  min-width: 0;
}
.logs-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: end;
  margin-bottom: 10px;
  gap: 12px 16px;
  flex-wrap: wrap;
}
.logs-toolbar-fields,
.logs-toolbar-actions {
  display: flex;
  align-items: end;
  gap: 8px;
  flex-wrap: wrap;
}
.logs-toolbar-fields { flex: 1 1 30rem; }
.logs-toolbar-actions { flex: 0 0 auto; }
.logs-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 13rem;
  margin: 0;
  color: var(--text-soft);
  font-size: 0.75rem;
  font-weight: 600;
}
.logs-search-field { flex: 1 1 15rem; }
.settings-tab-panel .logs-field select,
.settings-tab-panel .logs-field input[type="search"] {
  width: 100%;
  min-height: 36px;
  margin: 0;
  padding: 0.46rem 0.68rem;
  background-color: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.82rem;
}
.settings-tab-panel .logs-field select { padding-right: 2rem; }
.settings-tab-panel .logs-field input[type="search"]::placeholder { color: var(--text-soft); }
.settings-tab-panel .logs-field input[type="search"]:focus {
  outline: none;
  border-color: var(--cyan-bright);
  box-shadow: 0 0 0 3px rgba(56,189,248,0.15);
}
.logs-stream-bar {
  min-height: 30px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 0 2px 7px;
}
.logs-stream-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  color: var(--text-muted);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}
.logs-status-dot {
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--text-muted);
}
.logs-stream-status.is-connected { color: var(--green); }
.logs-stream-status.is-connected .logs-status-dot {
  background: var(--green);
  animation: pulse-dot 2.2s ease-in-out infinite;
}
.logs-stream-status.is-reconnecting { color: var(--yellow); }
.logs-stream-status.is-reconnecting .logs-status-dot { background: var(--yellow); }
.logs-stream-status.is-degraded { color: var(--yellow); }
.logs-stream-status.is-degraded .logs-status-dot { background: var(--yellow); }
.logs-jump { padding-block: 0.24rem; }
.logs-external {
  margin-bottom: 10px;
  padding: 11px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  background: rgba(var(--surface-rgb), 0.48);
}
.logs-external[hidden] { display: none; }
.logs-external-intro,
.logs-external-identity {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
}
.logs-external-intro { margin-bottom: 9px; }
.logs-external-intro strong,
.logs-external-identity strong {
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 650;
}
.logs-external-intro span,
.logs-external-identity span {
  color: var(--text-muted);
  font-size: 0.74rem;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.logs-external-list { display: flex; flex-direction: column; gap: 7px; }
.logs-external-row {
  display: flex;
  min-width: 0;
  align-items: center;
  justify-content: space-between;
  gap: 10px 16px;
  padding-top: 7px;
  border-top: 1px solid var(--line);
}
.logs-external-actions {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}
.logs-external-actions a { text-decoration: none; }
.logs-external-command {
  flex: 1 0 100%;
  padding: 7px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  background: var(--surface);
  font-size: 0.72rem;
  line-height: 1.45;
  overflow-wrap: anywhere;
  user-select: all;
}
.detail-logs-body {
  height: clamp(24rem, 60vh, 46rem);
  overflow: auto;
  background: var(--bg-deep);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 0;
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.55;
  scrollbar-gutter: stable;
  contain: layout paint;
}
.log-entry {
  display: grid;
  grid-template-columns: 3.2rem minmax(0, 1fr);
  gap: 10px;
  min-height: 1.55em;
  padding: 0 12px;
}
.log-entry:hover { background: rgba(var(--surface-rgb), 0.5); }
.log-entry-source {
  font-weight: 700;
  text-align: right;
  user-select: none;
}
.log-source-0 { color: var(--cyan); }
.log-source-1 { color: var(--electric); }
.log-source-2 { color: var(--green); }
.log-source-3 { color: var(--standby); }
.log-source-4 { color: var(--sparkle); }
.log-source-5 { color: var(--yellow); }
.log-entry-message {
  min-width: 0;
  color: var(--text-soft);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  user-select: text;
}
.log-entry-event {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 8px 12px;
  padding: 5px 9px;
  min-height: 0;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: rgba(var(--surface-rgb), 0.55);
  color: var(--text-muted);
  font-family: var(--font);
  font-size: 0.75rem;
}
.logs-output-empty {
  max-width: 34rem;
  margin: 0;
  padding: 28px 20px;
  color: var(--text-muted);
  font-family: var(--font);
  font-size: 0.86rem;
}
.logs-output-summary {
  min-height: 1.2rem;
  margin: 7px 2px 0;
  color: var(--text-muted);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 700px) {
  .logs-toolbar-fields,
  .logs-toolbar-actions { width: 100%; }
  .logs-field { flex: 1 1 100%; min-width: 0; }
  .logs-toolbar-actions .btn-row { flex: 1 1 auto; }
  .logs-external-row { align-items: stretch; flex-direction: column; }
  .logs-external-actions { width: 100%; }
  .logs-external-actions .btn-row { flex: 1 1 auto; text-align: center; }
  .detail-logs-body { height: 56vh; min-height: 22rem; }
  .log-entry { grid-template-columns: 2.5rem minmax(0, 1fr); gap: 7px; padding-inline: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .logs-stream-status.is-connected .logs-status-dot { animation: none; }
}
.logs-empty {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 2rem 1.75rem;
  max-width: 44rem;
}
.logs-empty h3 {
  margin: 0 0 0.5rem;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
}
.logs-empty p {
  margin: 0 0 0.6rem;
  color: var(--text-muted);
  font-size: 0.9rem;
  line-height: 1.5;
}
.logs-empty p:last-child { margin-bottom: 0; }
.logs-empty a { color: var(--cyan-bright); text-decoration: none; }
.logs-empty a:hover { text-decoration: underline; }

/* ── App detail: Deployments tab ─────────────────────────────────────────── */

.deployments-list { list-style: none; padding: 0; margin: 0; }
.deployment-row,
.deployments-head {
  display: grid;
  grid-template-columns: minmax(11rem, max-content) 1fr auto;
  gap: 12px 16px;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.deployments-head {
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.66rem;
  color: var(--text-dim);
  padding-bottom: 8px;
}
.deployment-row:last-child { border-bottom: none; }
.deployment-row-current {
  background: linear-gradient(90deg, rgba(56,189,248,0.06), transparent 60%);
  border-radius: 6px;
}
.deployment-version {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 8px;
  white-space: nowrap;
}
.deployment-number {
  font-family: 'Space Mono', monospace;
  color: var(--cyan-bright);
  font-weight: 700;
}
.deployment-version-id {
  font-family: 'Space Mono', monospace;
  color: var(--text-muted);
  font-size: 0.8rem;
}
.deployment-current-badge,
.deployment-failed-badge,
.deployment-pending-badge {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border-radius: 999px;
  padding: 1px 8px;
  border: 1px solid;
}
.deployment-current-badge {
  color: var(--cyan-bright);
  border-color: rgba(56,189,248,0.5);
}
.deployment-failed-badge {
  color: var(--danger, #f87171);
  border-color: rgba(248,113,113,0.5);
  cursor: help;
}
.deployment-pending-badge {
  color: var(--text-muted);
  border-color: var(--line-2);
}
.deployment-when { color: var(--text-muted); font-size: 0.9rem; }
.deployment-user { color: var(--text-soft); font-size: 0.9rem; }
.deployment-action { justify-self: end; }
.deployment-live-note { color: var(--text-dim); font-size: 0.8rem; font-style: italic; }
.rollback-btn {
  padding: 5px 12px;
  font-size: 0.85rem;
  border-radius: var(--radius);
  cursor: pointer;
  border: 1px solid var(--line-2);
  background: rgba(var(--surface-rgb),0.5);
  color: var(--text-soft);
  font-family: var(--font);
  font-weight: 600;
  transition: all var(--ease);
}
.rollback-btn:hover:not(:disabled) {
  border-color: var(--cyan-bright);
  color: var(--cyan-bright);
  background: rgba(56,189,248,0.08);
}
.rollback-btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* ── Branding: operator footer and logo ── */
footer.brand-footer { display:flex; gap:1rem; justify-content:center;
  padding:1rem; font-size:.8rem; opacity:.7; }
footer.brand-footer a { color:inherit; }
.brand-logo { height:1.6rem; width:auto; display:block; }

/* ── Profile modal ── */
.profile-identity {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}
.identity-avatar-lg {
  width: 52px;
  height: 52px;
  border-radius: 13px;
  font-size: 1.02rem;
}
.profile-identity-meta {
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
  min-width: 0;
}
.profile-username {
  color: var(--text);
  font-size: 0.98rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.profile-role {
  color: var(--cyan);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
}
.profile-role:empty { display: none; }
.profile-login {
  color: var(--text-muted);
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.profile-login:empty { display: none; }
.profile-hint {
  color: var(--text-muted);
  font-size: 0.74rem;
  margin-top: 0.4rem;
}
.profile-password {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.6rem 1rem 1rem;
  margin-top: 1rem;
}
.profile-password[hidden] { display: none; }
.profile-password legend {
  padding: 0 0.45rem;
  color: var(--text-soft);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}
.profile-appearance {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.6rem 1rem 0.9rem;
  margin-top: 1rem;
}
.profile-appearance legend {
  padding: 0 0.45rem;
  color: var(--text-soft);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}
.theme-options { display: flex; gap: 0.4rem; }
.theme-option {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text-soft);
  font-size: 0.82rem;
  cursor: pointer;
  transition: border-color var(--ease), color var(--ease), background var(--ease);
}
.theme-option:hover { border-color: var(--line-2); color: var(--text); }
.theme-option input { accent-color: var(--accent); margin: 0; }
/* Selected pill highlight; the native radio still shows state where :has() is unsupported. */
.theme-option:has(input:checked) {
  border-color: var(--cyan-bright);
  color: var(--text);
  background: var(--accent-dim);
}
.theme-option:focus-within { outline: 2px solid var(--cyan-bright); outline-offset: 1px; }

.profile-success {
  color: var(--green);
  font-size: 0.8rem;
}
.profile-actions { justify-content: space-between; }
.modal-actions .profile-logout {
  color: var(--red);
  border-color: rgba(248,113,113,0.4);
}
.modal-actions .profile-logout:hover:not(:disabled) {
  background: var(--red-bg);
  border-color: var(--red);
  color: var(--red);
  box-shadow: none;
}

/* ───────────────────────────────────────────────────────────────────────────
   Overview (dashboard home). Extends the Constellation system: a health-first
   fleet pulse, an attention list, resource pressure, and recent activity.
   Body-size text uses --text-soft (the lower --text-muted fails AA on the dark
   surface); --text-muted is kept for large/secondary labels only.
   ─────────────────────────────────────────────────────────────────────────── */
.ov-grid { display: flex; flex-direction: column; gap: 16px; max-width: 1100px; }

/* Pulse — the signature panel: a fleet verdict, a proportional status bar, and
   a legend. Tone tints a faint glow + the dot, never a side stripe. */
.ov-pulse {
  position: relative;
  background:
    radial-gradient(420px 160px at 100% 0%, var(--tone-glow, transparent), transparent 70%),
    var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 22px 24px;
  overflow: hidden;
}
.ov-pulse--nominal  { --tone: var(--green);       --tone-glow: rgba(74,222,128,0.10); }
.ov-pulse--warning  { --tone: var(--yellow);      --tone-glow: rgba(251,191,36,0.10); }
.ov-pulse--critical { --tone: var(--red);         --tone-glow: rgba(248,113,113,0.13);
                      border-color: rgba(248,113,113,0.30); }

.ov-pulse-verdict { display: flex; align-items: center; gap: 14px; }
.ov-pulse-dot {
  flex: none; width: 12px; height: 12px; border-radius: 50%;
  background: var(--tone); box-shadow: 0 0 0 4px color-mix(in oklab, var(--tone) 22%, transparent);
  animation: ovPulse 2.4s ease-out infinite;
}
.ov-pulse--nominal .ov-pulse-dot { animation-duration: 3.4s; }
@keyframes ovPulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in oklab, var(--tone) 38%, transparent); }
  70%  { box-shadow: 0 0 0 9px color-mix(in oklab, var(--tone) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in oklab, var(--tone) 0%, transparent); }
}
.ov-pulse-headline { font-size: 1.2rem; font-weight: 700; color: var(--text); letter-spacing: -0.01em; }
.ov-pulse-detail { font-size: 0.85rem; color: var(--text-soft); margin-top: 2px; }

.ov-bar { display: flex; gap: 3px; height: 12px; margin-top: 18px; }
.ov-bar-seg {
  background: var(--seg); border-radius: 4px; min-width: 6px;
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--seg) 55%, transparent),
              0 0 10px color-mix(in oklab, var(--seg) 30%, transparent);
}
.ov-legend { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 14px; list-style: none; }
.ov-legend-item { display: inline-flex; align-items: center; gap: 7px; font-size: 0.8rem; color: var(--text-soft); }
.ov-legend-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--seg); }
.ov-legend-count { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }

/* Generic Overview panel + section title (normal-case, not an all-caps eyebrow). */
.ov-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 18px 20px; }
.ov-section-title {
  display: flex; align-items: center; gap: 8px;
  font-size: 0.95rem; font-weight: 650; color: var(--text); margin-bottom: 14px; letter-spacing: -0.01em;
}
.ov-section-count {
  font-size: 0.72rem; font-weight: 700; color: var(--red); background: var(--red-bg);
  padding: 1px 8px; border-radius: 999px; font-variant-numeric: tabular-nums;
}

/* Attention — actionable rows for crashed / degraded apps. */
.ov-attention { background:
  radial-gradient(520px 200px at 0% 0%, rgba(248,113,113,0.06), transparent 70%), var(--surface);
  border-color: rgba(248,113,113,0.22); }
.ov-attn-list { list-style: none; display: flex; flex-direction: column; gap: 9px; }
.ov-attn-row {
  display: flex; align-items: center; gap: 14px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 11px 14px; transition: border-color var(--ease), background var(--ease);
}
.ov-attn-row:hover { border-color: var(--line-2); background: var(--surface-3); }
.ov-attn-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.ov-attn-name { color: var(--text); font-weight: 600; text-decoration: none; }
.ov-attn-name:hover { color: var(--cyan-bright); }
.ov-attn-reason {
  font-size: 0.8rem; color: var(--text-soft); font-family: var(--mono);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.ov-attn-actions { display: flex; gap: 8px; flex: none; }

/* Small ghost button used across the Overview (button + anchor). */
.ov-btn {
  display: inline-flex; align-items: center; justify-content: center;
  font: inherit; font-size: 0.8rem; font-weight: 600; line-height: 1;
  color: var(--text-soft); background: transparent;
  border: 1px solid var(--line-2); border-radius: var(--radius);
  padding: 6px 13px; cursor: pointer; text-decoration: none;
  transition: border-color var(--ease), color var(--ease), background var(--ease);
}
.ov-btn:hover { color: var(--text); border-color: var(--cyan-bright); background: var(--accent-dim); }
.ov-btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* The author display above overrides the UA [hidden] rule, so an .ov-btn with
   the hidden attribute (e.g. the Remove icon button when no icon is set) would
   still show. Restore the hide. */
.ov-btn[hidden] { display: none; }

/* Footer — resource pressure + recent activity. */
.ov-footer { display: grid; grid-template-columns: 1.25fr 1fr; gap: 16px; }
.ov-footer--single { grid-template-columns: 1fr; }

.ov-res-line { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 12px; }
.ov-res-stat { display: inline-flex; align-items: baseline; gap: 6px; }
.ov-res-stat b { font-size: 1.15rem; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.ov-res-stat-label { font-size: 0.72rem; font-weight: 600; color: var(--text-muted); }
.ov-res-sep { color: var(--text-dim); }
.ov-res-across { font-size: 0.82rem; color: var(--text-soft); margin-left: auto; }
.ov-res-subhead { margin-top: 16px; margin-bottom: 9px; font-size: 0.78rem; font-weight: 600; color: var(--text-soft); }
.ov-res-clear { margin-top: 14px; font-size: 0.82rem; color: var(--text-soft); }

.ov-nearlimit { list-style: none; display: flex; flex-direction: column; gap: 9px; }
.ov-nearlimit-row { display: grid; grid-template-columns: minmax(70px, 0.9fr) 1.4fr auto; align-items: center; gap: 12px; }
.ov-nearlimit-name { color: var(--text-soft); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ov-nearlimit-name:hover { color: var(--cyan-bright); }
.ov-meter { position: relative; height: 6px; background: var(--surface-3); border-radius: 999px; overflow: hidden; }
.ov-meter-fill { position: absolute; inset: 0 auto 0 0; background: var(--yellow); border-radius: 999px; transition: width var(--ease); }
.ov-meter--hot .ov-meter-fill { background: var(--red); }
.ov-nearlimit-val { font-family: var(--mono); font-size: 0.76rem; color: var(--text-soft); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Recent activity timeline. */
.ov-timeline { list-style: none; display: flex; flex-direction: column; }
.ov-tl-row { display: flex; gap: 12px; padding: 8px 0; }
.ov-tl-row:not(:last-child) { border-bottom: 1px solid var(--line); }
.ov-tl-rail { flex: none; width: 7px; height: 7px; margin-top: 6px; border-radius: 50%; background: var(--electric); box-shadow: 0 0 8px var(--accent-glow); }
.ov-tl-main { min-width: 0; }
.ov-tl-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
.ov-tl-action { color: var(--text); font-weight: 600; font-size: 0.85rem; }
.ov-tl-target { font-family: var(--mono); font-size: 0.78rem; color: var(--cyan); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ov-tl-meta { display: flex; gap: 8px; font-size: 0.76rem; color: var(--text-soft); margin-top: 1px; }
.ov-tl-time { color: var(--text-muted); }
.ov-empty-note { font-size: 0.84rem; color: var(--text-soft); }

/* First run + error. */
.ov-firstrun { text-align: center; padding: 40px 24px; }
.ov-firstrun-title { font-size: 1.35rem; font-weight: 700; color: var(--text); margin-bottom: 10px; letter-spacing: -0.01em; text-wrap: balance; }
.ov-firstrun-body { font-size: 0.9rem; color: var(--text-soft); max-width: 52ch; margin: 0 auto 18px; line-height: 1.5; }
.ov-error { display: flex; align-items: center; gap: 14px; }
.ov-error-text { color: var(--text-soft); }

/* Loading skeleton (shimmer). */
.ov-skeleton .ov-skel { background:
  linear-gradient(100deg, var(--surface) 30%, var(--surface-2) 50%, var(--surface) 70%);
  background-size: 200% 100%; border: 1px solid var(--line); border-radius: var(--radius-lg);
  animation: ovShimmer 1.4s linear infinite; }
.ov-skel-pulse { height: 132px; }
.ov-skel-panel { height: 168px; }
@keyframes ovShimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

@media (max-width: 760px) {
  .ov-footer { grid-template-columns: 1fr; }
  .ov-attn-row { flex-wrap: wrap; }
  .ov-attn-actions { width: 100%; }
  .ov-nearlimit-row { grid-template-columns: 1fr auto; }
  .ov-nearlimit-row .ov-meter { grid-column: 1 / -1; }
}

@media (prefers-reduced-motion: reduce) {
  .ov-pulse-dot { animation: none; }
  .ov-skeleton .ov-skel { animation: none; }
}

/* ───────────────────────────────────────────────────────────────────────────
   Launchpad (viewer/consumer home). An app-forward gallery for launching apps.
   Warmth comes from the colorful per-app monogram avatars and generous tiles,
   not a palette change - it stays in the Constellation system.
   ─────────────────────────────────────────────────────────────────────────── */
.lp { display: flex; flex-direction: column; gap: 20px; max-width: 1100px; }

/* Admin "preview viewer home" banner: a clear, dismissible context strip. */
.lp-preview-banner {
  display: flex; align-items: center; justify-content: space-between; gap: 12px 18px; flex-wrap: wrap;
  margin-bottom: 4px; padding: 11px 16px; border-radius: var(--radius);
  background: var(--accent-dim); border: 1px solid var(--cyan-bright);
  color: var(--text); font-size: 0.85rem;
}
.lp-preview-text strong { font-weight: 700; color: var(--cyan-bright); }
.lp-preview-exit {
  flex: none; font-weight: 600; font-size: 0.82rem; color: var(--text);
  text-decoration: none; padding: 5px 12px; border-radius: var(--radius);
  border: 1px solid var(--line-2); transition: border-color var(--ease), background var(--ease);
}
.lp-preview-exit:hover { border-color: var(--cyan-bright); background: var(--surface-2); }

.lp-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px 24px; flex-wrap: wrap; }
.lp-welcome { font-size: 1.5rem; font-weight: 700; color: var(--text); letter-spacing: -0.02em; text-wrap: balance; }
.lp-sub { font-size: 0.9rem; color: var(--text-soft); margin-top: 3px; }
.lp-search {
  font: inherit; font-size: 0.9rem; color: var(--text);
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius);
  padding: 9px 14px; min-width: 220px; transition: border-color var(--ease), box-shadow var(--ease);
}
.lp-search::placeholder { color: var(--text-muted); }
.lp-search:focus { outline: none; border-color: var(--cyan-bright); box-shadow: 0 0 0 3px var(--accent-dim); }

.lp-section { font-size: 0.9rem; font-weight: 650; color: var(--text-soft); letter-spacing: -0.01em; margin: 6px 0 12px; }
.lp-section-icon { margin-right: 6px; }
.lp-results > .lp-grid:not(:last-child) { margin-bottom: 22px; }

.lp-group + .lp-group { margin-top: 22px; }
.lp-results > .lp-grid + .lp-group,
.lp-results > .lp-section + .lp-grid + .lp-group { margin-top: 22px; }
.lp-group-header {
  display: flex;
  align-items: center;
  min-height: 36px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--line);
}
.lp-group-heading { flex: 1 1 auto; min-width: 0; }
.lp-group-toggle {
  max-width: 100%;
  margin-left: -0.35rem;
  padding: 0.28rem 0.35rem;
  border-radius: 6px;
  color: var(--text-soft);
  font-size: 0.9rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}
.lp-group-toggle:hover { color: var(--text); background: var(--surface-2); }
.lp-group-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lp-group-icon { flex: none; line-height: 1; }
.lp-group-count {
  flex: none;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  color: var(--text-soft);
  background: var(--surface-2);
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
}

/* Responsive tile grid: fills the row, no media query needed. The 280px floor
   keeps tiles wide enough that a one-line description renders without clipping. */
.lp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }

.lp-tile {
  display: flex; align-items: center; gap: 14px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 16px 18px; text-decoration: none; color: inherit;
  transition: border-color var(--ease), background var(--ease), transform var(--ease), box-shadow var(--ease);
}
a.lp-tile:hover {
  border-color: var(--line-2); background: var(--surface-2);
  transform: translateY(-2px); box-shadow: 0 10px 30px -16px rgba(0,0,0,0.8);
}
a.lp-tile:hover .lp-tile-go { color: var(--cyan-bright); transform: translateX(3px); }
.lp-tile--down { opacity: 0.6; }

.lp-avatar {
  flex: none; width: 46px; height: 46px; border-radius: 12px;
  display: grid; place-items: center; font-weight: 700; font-size: 1.05rem; letter-spacing: -0.02em;
  background: oklch(0.42 0.09 var(--avatar-hue, 220));
  color: oklch(0.92 0.11 var(--avatar-hue, 220));
  box-shadow: inset 0 0 0 1px oklch(0.6 0.12 var(--avatar-hue, 220) / 0.4);
}
/* Uploaded-icon variant: the image fills the box; drop the monogram's hued
   background and letter styling, keep the rounded frame. */
.lp-avatar--img {
  background: var(--surface-3); object-fit: cover; object-position: center;
  box-shadow: inset 0 0 0 1px var(--line-2);
}

/* ── Shared app avatar on the detail header + the Configuration icon picker.
   Both reuse the monogram model (hued background + initials) and the same
   --img override for an uploaded icon. ── */
.app-detail-avatar, .icon-picker-preview {
  flex: none; display: grid; place-items: center;
  border-radius: 12px; font-weight: 700; letter-spacing: -0.02em;
  background: oklch(0.42 0.09 var(--avatar-hue, 220));
  color: oklch(0.92 0.11 var(--avatar-hue, 220));
  box-shadow: inset 0 0 0 1px oklch(0.6 0.12 var(--avatar-hue, 220) / 0.4);
}
.app-detail-avatar--img, .icon-picker-preview--img {
  background: var(--surface-3); object-fit: cover; object-position: center;
  box-shadow: inset 0 0 0 1px var(--line-2);
}
.app-detail-icon-slot { display: inline-flex; }
.app-detail-avatar { width: 40px; height: 40px; font-size: 0.95rem; border-radius: 11px; }
.app-detail-identity { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.icon-picker { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.icon-picker-preview { width: 52px; height: 52px; font-size: 1.15rem; }
.icon-picker-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.icon-picker-controls .hibernate-status { margin: 0; }

/* Emoji picker: a kebab-style popover (position:relative on the trigger's
   wrapper, position:absolute on the popover), not a modal - it closes on
   Escape/outside-click via the same wireKebab() every other popover uses. */
.emoji-picker { position: relative; display: inline-flex; }
.emoji-picker-popover {
  position: absolute;
  left: 0;
  top: calc(100% + 4px);
  background: var(--bg-deep);
  border: 1px solid var(--line-2);
  border-radius: 8px;
  padding: 10px;
  width: 272px;
  z-index: 10;
}
.emoji-picker-custom { display: flex; gap: 8px; margin-bottom: 8px; }
.emoji-picker-text-input {
  flex: 1; min-width: 0; font: inherit; font-size: 0.85rem;
  color: var(--text); background: var(--surface-3);
  border: 1px solid var(--line-2); border-radius: var(--radius);
  padding: 6px 10px;
}
.emoji-picker-clear { display: block; width: 100%; margin-bottom: 8px; }
.emoji-picker-grid-slot { display: block; }
.emoji-picker-grid {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px;
  max-height: 220px; overflow-y: auto;
}
.emoji-picker-cell {
  display: flex; align-items: center; justify-content: center;
  font-size: 1.1rem; line-height: 1; padding: 6px 0;
  background: transparent; border: 1px solid transparent; border-radius: 6px;
  cursor: pointer; transition: border-color var(--ease), background var(--ease);
}
.emoji-picker-cell:hover,
.emoji-picker-cell:focus-visible {
  border-color: var(--cyan-bright); background: rgba(56,189,248,0.08);
  outline: none;
}

/* Emoji-icon variant: the glyph reuses the monogram box, but the monogram's
   bold weight and condensed letter-spacing are tuned for 1-2 letters, not a
   glyph - reset them and size the glyph up to fill the box. overflow: hidden
   is load-bearing: when the local font has no single glyph for a ZWJ
   sequence (a skin-toned multi-person family, for example), it falls back to
   rendering the parts side by side, so one "emoji" becomes a wide run of
   glyphs. Without containment that run paints over the app name on the
   Launchpad tile and over the page heading on the app-detail header. The box
   is place-items: center, so an oversized run is center-cropped rather than
   spilling out; a cropped glyph is the honest result when the font cannot
   compose the sequence as one icon. */
.lp-avatar--emoji, .app-detail-avatar--emoji, .icon-picker-preview--emoji {
  font-size: 1.35em; font-weight: 400; letter-spacing: normal; overflow: hidden;
}

.lp-tile-main { min-width: 0; flex: 1; }
.lp-tile-name { font-size: 0.98rem; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lp-tile-desc {
  font-size: 0.84rem; color: var(--text-soft); margin-top: 2px; line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Only the "Unavailable" state renders a status line now; openable apps show
   none (the clickable tile + chevron are the affordance). */
.lp-ready { display: flex; align-items: center; gap: 6px; margin-top: 7px; font-size: 0.76rem; color: var(--text-muted); }
.lp-ready-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--red); flex: none; }

.lp-tile-go { flex: none; color: var(--text-dim); font-size: 1.1rem; transition: color var(--ease), transform var(--ease); }

.lp-noresults { font-size: 0.9rem; color: var(--text-soft); padding: 8px 2px; }

/* Empty / first-run. */
.lp-empty { text-align: center; padding: 52px 24px; }
.lp-empty-title { font-size: 1.4rem; font-weight: 700; color: var(--text); margin-bottom: 10px; letter-spacing: -0.01em; text-wrap: balance; }
.lp-empty-body { font-size: 0.92rem; color: var(--text-soft); max-width: 52ch; margin: 0 auto 18px; line-height: 1.55; }

/* Loading skeleton. */
.lp-skeleton .lp-skel { height: 80px; border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: linear-gradient(100deg, var(--surface) 30%, var(--surface-2) 50%, var(--surface) 70%);
  background-size: 200% 100%; animation: ovShimmer 1.4s linear infinite; }

@media (max-width: 600px) {
  .lp-search { width: 100%; }
  .lp-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  a.lp-tile:hover { transform: none; }
  a.lp-tile:hover .lp-tile-go { transform: none; }
  .lp-skeleton .lp-skel { animation: none; }
}

/* ── API tokens page ──────────────────────────────────────────────────────── */
.cli-connect-panel {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  max-width: 48rem;
  margin: 0 0 1.5rem;
  padding: 1.15rem 1.2rem;
  background: var(--surface);
  border: 1px solid var(--cyan-bright);
  border-radius: var(--radius-lg);
}
.cli-connect-panel[hidden] { display: none; }
.cli-connect-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 10px;
  background: var(--accent-dim);
  color: var(--cyan);
  font: 700 0.85rem/1 var(--mono);
}
.cli-connect-content { min-width: 0; flex: 1; }
.cli-connect-context {
  margin: 0 0 0.2rem;
  color: var(--cyan);
  font-size: 0.78rem;
  font-weight: 700;
}
.cli-connect-content h2 {
  margin: 0 0 0.45rem;
  color: var(--text);
  font-size: 1.12rem;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
.cli-connect-content > p:not(.cli-connect-context):not(.error):not(.cli-connect-success) {
  max-width: 65ch;
  color: var(--text-soft);
  line-height: 1.55;
}
.cli-connect-details {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem 1.5rem;
  margin: 0.9rem 0;
}
.cli-connect-details div { display: flex; align-items: baseline; gap: 0.45rem; }
.cli-connect-details dt { color: var(--text-muted); font-size: 0.78rem; }
.cli-connect-details dd { color: var(--text); font-weight: 600; overflow-wrap: anywhere; }
.cli-connect-details code {
  color: var(--cyan);
  font-family: var(--mono);
  letter-spacing: 0.04em;
}
.cli-connect-actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 1rem; }
.cli-connect-actions button { min-height: 2.35rem; padding: 0.45rem 0.85rem; }
.cli-connect-success { color: var(--green); font-weight: 600; }
@media (max-width: 540px) {
  .cli-connect-panel { flex-direction: column; }
  .cli-connect-actions { justify-content: stretch; }
  .cli-connect-actions button { flex: 1; }
}

.tokens-intro {
  color: var(--text-soft);
  max-width: 62ch;
  margin: 0 0 1.25rem;
  line-height: 1.55;
}
.tokens-intro code {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 0.05rem 0.35rem;
  font-size: 0.85em;
  color: var(--cyan);
}
.tokens-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.token-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.8rem 1rem;
}
.token-row:hover { border-color: var(--line-2); }
.token-meta { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.token-name {
  color: var(--text);
  font-weight: 600;
  overflow-wrap: anywhere;
}
.token-created { color: var(--text-muted); font-size: 0.82rem; }
.token-revoke { padding: 0.35rem 0.7rem; font-size: 0.85rem; flex: none; }
.tokens-empty {
  color: var(--text-muted);
  text-align: center;
  padding: 2.5rem 1rem;
  background: var(--surface);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

/* Reveal-once panel inside the new-token modal */
.token-reveal { display: flex; flex-direction: column; gap: 0.9rem; }
/* The explicit display above outranks the UA [hidden] rule, so restore it -
   otherwise the reveal panel shows (empty) before a token is created. */
.token-reveal[hidden] { display: none; }
.token-reveal-warning {
  margin: 0;
  padding: 0.7rem 0.9rem;
  background: var(--yellow-bg);
  border: 1px solid rgba(251,191,36,0.35);
  border-radius: var(--radius);
  color: var(--yellow);
  font-size: 0.9rem;
  line-height: 1.5;
}
.token-reveal .snippet code { overflow-wrap: anywhere; }

/* About dialog. Narrow, because it is a reference card rather than a form: the
   whole point is that one glance answers "which ShinyHub, and what can it run". */
.about-card { max-width: 26rem; }
.about-wordmark {
  margin: 0;
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}
/* The stock two-tone wordmark. Not a .brand slot: branding.js rewrites those,
   and this line has to survive a white-label. */
.about-wordmark-accent { color: var(--cyan-bright); }
.about-version {
  margin: 0.15rem 0 0;
  color: var(--text-soft);
  font-family: var(--mono);
  font-size: 1.05rem;
  letter-spacing: 0.01em;
  /* Selectable, because the point of the line is to be pasted into a report. */
  user-select: text;
}
/* An unknown version is a caveat, not a version number. */
.about-version.is-unknown { font-size: 0.85rem; color: var(--text-muted); }
.about-build {
  margin: 0.3rem 0 0;
  color: var(--text-dim);
  font-family: var(--mono);
  font-size: 0.72rem;
  user-select: text;
}

.about-section {
  margin-top: 1.4rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}
.about-section-title {
  margin: 0 0 0.7rem;
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.about-runtimes {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.45rem 1rem;
  margin: 0;
  align-items: baseline;
}
.about-runtime-name { color: var(--text-soft); font-size: 0.88rem; }
/* The executable the server looked for, so "Not installed" says what to install. */
.about-runtime-launcher {
  margin-left: 0.45rem;
  color: var(--text-dim);
  font-family: var(--mono);
  font-size: 0.72rem;
}
.about-runtime-state {
  margin: 0;
  justify-self: end;
  font-family: var(--mono);
  font-size: 0.76rem;
}
.about-runtime-state.is-ready { color: var(--green); }
.about-runtime-state.is-missing { color: var(--text-muted); }
/* Unknown is not "missing": the server never reported. Dimmest of the three. */
.about-runtime-state.is-unknown { color: var(--text-dim); }
.about-hint { margin: 0.75rem 0 0; color: var(--text-dim); font-size: 0.74rem; }

.about-links { margin: 1.3rem 0 0; }
.about-links a { color: var(--brand-primary); text-decoration: none; font-size: 0.84rem; }
.about-links a:hover { text-decoration: underline; }

/* Sidebar footer → About trigger. Reads as one of the quiet rail controls, not
   a nav destination, so it borrows the collapse button's layout. */
.sidebar-about { display: flex; align-items: center; gap: 0.6rem; width: 100%; }
.sidebar-about .nav-ico { width: 16px; height: 16px; }
body.sidebar-collapsed .sidebar-about { justify-content: center; }

/* Profile modal → tokens link */
.profile-tokens-link { margin: 0.25rem 0 0.5rem; }
.profile-tokens-link a { color: var(--brand-primary); text-decoration: none; font-size: 0.9rem; }
.profile-tokens-link a:hover { text-decoration: underline; }
