/* ============================================================================
   Kylth — application stylesheet
   Consumes the tokens in tokens.css. Organised as:
     1. font + reset + base
     2. layout shell (sidebar, topbar, content)
     3. primitives (buttons, inputs, badges, cards, code, tables)
     4. components (stat cards, project cards, tabs, menus, modals, toasts,
        empty states, skeletons, meters)
     5. page-specific bits
     6. responsive
   ========================================================================= */

/* ---------------------------------------------------------------- 1. base */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/InterVariable.woff2") format("woff2");
}

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

html { -webkit-text-size-adjust: 100%; }

/* The `hidden` attribute only carries `display:none` from the UA sheet, so any
   component with its own `display` (`.project-card` is `flex`) silently beat
   it. app.js's status filter sets `.hidden = true` on cards, and they carried
   on rendering next to the "no projects match this filter" empty state. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-base);
  font-feature-settings: "cv05" 1, "ss03" 1, "tnum" 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, p, figure, ul, ol, dl, dd { margin: 0; }
dd { margin-inline-start: 0; }
ul, ol { padding: 0; list-style: none; }

a {
  color: var(--c-accent);
  text-decoration: none;
  transition: color var(--t-fast);
}
a:hover { color: var(--c-accent-hover); }

svg { display: block; flex: none; }

hr {
  border: 0;
  border-top: 1px solid var(--c-border);
  margin: var(--sp-6) 0;
}

/* Consistent, visible focus for keyboard users on every interactive thing.
   Deliberately does NOT set a border-radius: doing so overrode the radius of
   whatever was focused (pill buttons and badges squared off on tab-focus). */
:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}
:focus:not(:focus-visible) { outline: none; }
/* Inside the sidebar the outline-offset would clip against the panel edge. */
.sidebar :focus-visible { outline-offset: 1px; }

.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;
}

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

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--c-scroll-thumb);
  border-radius: var(--r-full);
  /* transparent border + background-clip keeps the inset on any surface,
     instead of painting the app-canvas colour over sidebar/card scrollers */
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--c-scroll-thumb-hover);
  background-clip: padding-box;
}
* { scrollbar-color: var(--c-scroll-thumb) transparent; }

/* --------------------------------------------------------------- 2. shell */

/* Two explicit rows, with the sidebar spanning both. Without the explicit
   placement the full-height sidebar sizes row 1 to 100vh and the main content
   gets pushed a whole viewport down the page. */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  min-height: 100vh;
  min-height: 100dvh;
}

/* ---- sidebar ---- */

.sidebar {
  grid-column: 1;
  grid-row: 1 / span 2;
  display: flex;
  flex-direction: column;
  background: var(--c-surface);
  border-right: 1px solid var(--c-border);
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100dvh;
  z-index: var(--z-drawer);
}

.sidebar__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  height: var(--topbar-h);
  padding: 0 var(--sp-5);
  border-bottom: 1px solid var(--c-border-subtle);
  flex: none;
  color: var(--c-text);
}
.sidebar__brand:hover { color: var(--c-text); }

.brand-mark {
  width: 28px; height: 28px;
  border-radius: var(--r-sm);
  background: linear-gradient(140deg, var(--c-accent) 0%, #7b5cff 100%);
  display: grid;
  place-items: center;
  color: #fff;
  flex: none;
  box-shadow: 0 2px 8px rgba(79, 140, 255, 0.28);
}
.brand-name {
  display: block;
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  letter-spacing: -0.015em;
  line-height: 1.2;
}
.brand-sub {
  display: block;
  font-size: var(--fs-2xs);
  color: var(--c-text-3);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: var(--fw-medium);
  line-height: 1;
  margin-top: 2px;
}

.sidebar__nav {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--sp-4) var(--sp-3);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Shared micro-label treatment. Every uppercase caption in the app (nav
   groups, stat cards, resource strips, org stats) uses exactly this scale --
   they had drifted across 11px/12px and two different letter-spacings. */
.label-micro,
.nav-group-label,
.stat__label,
.res-strip__label,
.org-stat__label {
  font-size: var(--fs-2xs);
  line-height: 1.3;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: var(--fw-semi);
  color: var(--c-text-3);
}

.nav-group-label {
  padding: 0 var(--sp-3) var(--sp-2);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  color: var(--c-text-2);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  transition: background var(--t-fast), color var(--t-fast);
  position: relative;
}
.nav-item:hover {
  background: var(--c-surface-3);
  color: var(--c-text);
}
.nav-item svg { color: var(--c-text-3); transition: color var(--t-fast); }
.nav-item:hover svg { color: var(--c-text-2); }

.nav-item--active {
  background: var(--c-accent-soft);
  color: var(--c-text);
  font-weight: var(--fw-semi);
}
.nav-item--active svg { color: var(--c-accent); }
.nav-item--active::before {
  content: "";
  position: absolute;
  left: calc(var(--sp-3) * -1);
  top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 18px;
  border-radius: 0 var(--r-full) var(--r-full) 0;
  background: var(--c-accent);
}

.nav-item__count {
  margin-left: auto;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  color: var(--c-text-3);
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border);
  border-radius: var(--r-full);
  padding: 0 var(--sp-2);
  min-width: 20px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.nav-item--active .nav-item__count {
  color: var(--c-accent);
  background: var(--c-accent-soft);
  border-color: rgba(79, 140, 255, 0.30);
}

.sidebar__footer {
  flex: none;
  border-top: 1px solid var(--c-border-subtle);
  padding: var(--sp-4) var(--sp-3) var(--sp-3);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

/* ---- host status: a section OF the navigation, not a widget parked under
   it. Same horizontal rhythm as .nav-item so labels line up down the rail. */

.vps-widget {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: 0 var(--sp-3) var(--sp-3);
}
.vps-widget__head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: var(--fw-semi);
  color: var(--c-text-3);
  min-width: 0;
}
.vps-widget__head .spacer { margin-left: auto; }
.vps-widget__host {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: none;
  letter-spacing: 0;
  color: var(--c-text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.vps-widget__row {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.vps-widget__row-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--c-text-2);
}
.vps-widget__row-top b {
  color: var(--c-text);
  font-weight: var(--fw-semi);
  font-variant-numeric: tabular-nums;
}
.vps-widget__row-sub {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-2);
  font-size: var(--fs-2xs);
  color: var(--c-text-3);
  font-variant-numeric: tabular-nums;
}

.meter {
  height: 4px;
  border-radius: var(--r-full);
  background: var(--c-surface-inset);
  overflow: hidden;
  border: 1px solid var(--c-border-subtle);
}
.meter__fill {
  height: 100%;
  border-radius: var(--r-full);
  background: var(--c-accent);
  transition: width var(--t-slow);
}
.meter__fill--ok     { background: var(--c-ok); }
.meter__fill--warn   { background: var(--c-warn); }
.meter__fill--danger { background: var(--c-danger); }

.meter--lg { height: 6px; }

/* ---- account row ---- */

.account {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-2) 0 var(--sp-3);
  border-top: 1px solid var(--c-border-subtle);
}
.avatar {
  width: var(--ctl-icon); height: var(--ctl-icon);
  border-radius: var(--r-full);
  background: var(--c-surface-3);
  border: 1px solid var(--c-border-strong);
  display: grid;
  place-items: center;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--c-text-2);
  flex: none;
}
.account__meta { min-width: 0; line-height: 1.25; flex: 1; }
.account__name {
  display: block;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--c-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.account__sub {
  display: block;
  font-size: var(--fs-2xs);
  color: var(--c-text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.account form { margin-left: auto; }

/* ---- topbar ---- */

.topbar {
  grid-column: 2;
  grid-row: 1;
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 0 var(--sp-6);
  background: rgba(10, 11, 13, 0.82);
  backdrop-filter: saturate(160%) blur(12px);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--c-border);
}

.icon-btn {
  display: grid;
  place-items: center;
  width: var(--ctl-icon); height: var(--ctl-icon);
  border-radius: var(--r-sm);
  background: transparent;
  border: 1px solid transparent;
  color: var(--c-text-2);
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast),
              border-color var(--t-fast);
  flex: none;
  padding: 0;
}
.icon-btn:hover {
  background: var(--c-surface-3);
  color: var(--c-text);
  border-color: var(--c-border);
}
.icon-btn[aria-expanded="true"] {
  background: var(--c-surface-3);
  color: var(--c-text);
  border-color: var(--c-border);
}
/* app.js flags a successful clipboard write; without this the class was dead */
.icon-btn.is-copied {
  color: var(--c-ok);
  background: var(--c-ok-soft);
  border-color: var(--c-ok-border);
}

.sidebar-toggle { display: none; }

.breadcrumbs {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--c-text-3);
  min-width: 0;
}
.breadcrumbs a { color: var(--c-text-2); }
.breadcrumbs a:hover { color: var(--c-text); }
.breadcrumbs .crumb-current {
  color: var(--c-text);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.breadcrumbs .sep { color: var(--c-border-strong); }

.topbar__right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* Compact mirror of the sidebar's host section, shown only when the sidebar
   is an off-canvas drawer (<=1024px) — above that it was a straight duplicate
   of the rail, which is exactly the "bolted-on widget" feel to avoid. */
.host-pill {
  display: none;
  align-items: center;
  gap: var(--sp-2);
  height: var(--ctl-h-sm);
  padding: 0 var(--sp-3);
  border-radius: var(--r-full);
  border: 1px solid var(--c-border);
  background: var(--c-surface-2);
  font-size: var(--fs-xs);
  color: var(--c-text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.host-pill b { color: var(--c-text); font-weight: var(--fw-semi); }
.host-pill .sep { color: var(--c-border-strong); }
@media (max-width: 1024px) { .host-pill { display: inline-flex; } }

/* ---- alert bell ----
   Sits beside the host-pill because both are global and session-spanning, not
   scoped to the current page. Borrows .icon-btn's geometry rather than
   inventing a new control size; the count is positioned rather than inline so
   the icon does not shift sideways the moment an alert appears. */
.bell {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--ctl-icon); height: var(--ctl-icon);
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  color: var(--c-text-2);
  flex: none;
  transition: background var(--t-fast), color var(--t-fast),
              border-color var(--t-fast);
}
.bell:hover {
  background: var(--c-surface-3);
  color: var(--c-text);
  border-color: var(--c-border);
}
.bell--active { color: var(--c-text); }
.bell__count {
  position: absolute;
  top: -3px; right: -4px;
  min-width: 16px; height: 16px;
  padding: 0 4px;
  border-radius: var(--r-full);
  display: grid;
  place-items: center;
  font-size: 10px;
  font-weight: var(--fw-semi);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  border: 1px solid var(--c-bg);
}
.bell__count--danger { background: var(--c-danger); color: #fff; }
.bell__count--warn   { background: var(--c-warn);   color: #10130f; }
.bell__count--ok     { background: var(--c-neutral); color: #fff; }

/* ---- content ---- */

.content {
  grid-column: 2;
  grid-row: 2;
  padding: var(--sp-8) var(--sp-6) var(--sp-16);
}
.content__inner {
  max-width: var(--content-max);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-7);
}

/* ---- page header ---- */

.page-head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
.page-head__text { min-width: 0; flex: 1 1 340px; }
/* .detail-head is a column flex container, where the 340px flex-basis above
   would be read as a minimum *height*. Reset it there. */
.detail-head > .page-head__text { flex: 0 0 auto; }
.page-title {
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  line-height: var(--lh-tight);
  color: var(--c-text);
}
.page-subtitle {
  margin-top: var(--sp-2);
  font-size: var(--fs-base);
  color: var(--c-text-3);
  max-width: 68ch;
}
.page-head__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.page-head__filter { margin-top: var(--sp-3); }

.section {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.section__head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
.section-title {
  font-size: var(--fs-lg);
  font-weight: var(--fw-semi);
  letter-spacing: -0.01em;
  color: var(--c-text);
}
.section__head .spacer { margin-left: auto; }
.section-sub {
  font-size: var(--fs-sm);
  color: var(--c-text-3);
}

/* ---------------------------------------------------------- 3. primitives */

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

/* Every variant drives the same four custom properties, so hover/active are
   explicit colour steps rather than a brightness() filter (which also washed
   out the border and the label, and behaved differently on translucent
   surfaces than on solid ones). */
.btn {
  --btn-bg:    var(--c-surface-3);
  --btn-bg-hi: var(--c-surface-3-hi);
  --btn-bg-lo: var(--c-surface-3-lo);
  --btn-fg:    var(--c-text);
  --btn-bd:    var(--c-border-strong);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  height: var(--ctl-h);
  padding: 0 var(--sp-4);
  border-radius: var(--r-sm);
  border: 1px solid var(--btn-bd);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t-fast), border-color var(--t-fast),
              color var(--t-fast), opacity var(--t-fast);
  position: relative;
  user-select: none;
}
.btn:hover  { color: var(--btn-fg); background: var(--btn-bg-hi); }
.btn:active { background: var(--btn-bg-lo); }
.btn svg { opacity: 0.9; }

.btn--primary {
  --btn-bg:    var(--c-accent);
  --btn-bg-hi: var(--c-accent-hover);
  --btn-bg-lo: var(--c-accent-active);
  --btn-fg:    #fff;
  --btn-bd:    transparent;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255,255,255,0.14);
}
.btn--primary svg { opacity: 1; }

.btn--secondary {
  --btn-bg:    var(--c-surface-2);
  --btn-bg-hi: var(--c-surface-3);
  --btn-bg-lo: var(--c-surface-2-lo);
  --btn-bd:    var(--c-border-strong);
}
.btn--secondary:hover { border-color: var(--c-border-hover); }

.btn--ghost {
  --btn-bg:    transparent;
  --btn-bg-hi: var(--c-surface-3);
  --btn-bg-lo: var(--c-surface-2);
  --btn-bd:    transparent;
  --btn-fg:    var(--c-text-2);
}
.btn--ghost:hover { --btn-fg: var(--c-text); }

.btn--danger {
  --btn-bg:    var(--c-danger);
  --btn-bg-hi: var(--c-danger-hover);
  --btn-bg-lo: #e85f5f;
  --btn-fg:    #2a0505;
  --btn-bd:    transparent;
  font-weight: var(--fw-semi);
}
.btn--danger svg { opacity: 1; }

.btn--danger-outline {
  --btn-bg:    var(--c-danger-soft);
  --btn-bg-hi: rgba(249, 106, 106, 0.20);
  --btn-bg-lo: rgba(249, 106, 106, 0.26);
  --btn-fg:    var(--c-danger);
  --btn-bd:    var(--c-danger-border);
}
.btn--danger-outline:hover { --btn-fg: var(--c-danger-hover); }

.btn--sm { height: var(--ctl-h-sm); padding: 0 var(--sp-3); font-size: var(--fs-xs); gap: 6px; }
.btn--lg { height: var(--ctl-h-lg); padding: 0 var(--sp-5); font-size: var(--fs-base); }
.btn--block { width: 100%; }
.btn--icon { width: var(--ctl-h); padding: 0; }
.btn--sm.btn--icon { width: var(--ctl-h-sm); }

/* Disabled buttons keep pointer events so the `title` explaining *why* they
   are disabled ("all running slots are in use") actually surfaces on hover —
   a real <button disabled> still cannot be activated. */
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn:disabled:hover { background: var(--btn-bg); }
a.btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }

/* button loading state — the label fades, a spinner is centred over it */
.btn.is-loading { pointer-events: none; color: transparent !important; }
.btn.is-loading > * { visibility: hidden; }
.btn.is-loading::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-color: rgba(255,255,255,0.85) transparent rgba(255,255,255,0.85) transparent;
  animation: spin 640ms linear infinite;
  visibility: visible;
}
.btn--secondary.is-loading::after,
.btn--ghost.is-loading::after {
  border-color: var(--c-text-2) transparent var(--c-text-2) transparent;
}

@keyframes spin { to { transform: rotate(360deg); } }

.inline-form { display: inline-flex; margin: 0; }

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

.field { display: flex; flex-direction: column; gap: var(--sp-2); }
.field__label {
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--c-text-2);
}
.field__hint {
  font-size: var(--fs-xs);
  color: var(--c-text-3);
  line-height: var(--lh-snug);
}
/* icons used inline in prose must not inherit the global `svg{display:block}`
   or they push the sentence onto the next line */
.icon--inline { display: inline-block; vertical-align: -0.16em; }
.field__error { font-size: var(--fs-xs); color: var(--c-danger); }

.input,
.select,
.textarea {
  width: 100%;
  height: var(--ctl-h);
  padding: 0 var(--sp-3);
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-sm);
  color: var(--c-text);
  font-family: inherit;
  font-size: var(--fs-sm);
  transition: border-color var(--t-fast), box-shadow var(--t-fast),
              background var(--t-fast);
}
.textarea { height: auto; padding: var(--sp-3); resize: vertical; }
.input::placeholder, .textarea::placeholder { color: var(--c-text-3); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--c-border-hover); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--c-accent);
  box-shadow: 0 0 0 3px var(--c-accent-soft);
}
.input:disabled, .select:disabled { opacity: 0.5; cursor: not-allowed; }

.input--mono { font-family: var(--font-mono); font-size: var(--fs-sm); }

.select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--sp-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a2abba' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
  cursor: pointer;
}

.input-group { display: flex; align-items: stretch; }
.input-group .input { border-radius: var(--r-sm) 0 0 var(--r-sm); }
.input-group__suffix {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--sp-3);
  background: var(--c-surface-3);
  border: 1px solid var(--c-border-strong);
  border-left: 0;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  color: var(--c-text-3);
  font-size: var(--fs-sm);
  font-family: var(--font-mono);
  white-space: nowrap;
}

/* ---- code / mono ---- */

code, kbd, samp, pre { font-family: var(--font-mono); }

.code {
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border);
  border-radius: var(--r-xs);
  padding: 1px var(--sp-2);
  font-size: 0.86em;
  color: var(--c-text-2);
  word-break: break-word;
}

.code-block {
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  padding: var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--c-text-2);
  white-space: pre-wrap;
  word-break: break-all;
  overflow-x: auto;
  margin: 0;
}

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

.card {
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-xs);
}
.card__head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--c-border-subtle);
}
.card__head .spacer { margin-left: auto; }
.card__title {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--c-text);
  letter-spacing: -0.01em;
}
.card__head svg { color: var(--c-text-3); }
.card__body { padding: var(--sp-5); }
.card__body--tight { padding: var(--sp-4); }
.card__foot {
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--c-border-subtle);
  background: var(--c-wash-foot);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}
/* danger-zone framing, previously an inline style on the detail page */
.card--danger { border-color: var(--c-danger-border); }
.card--danger .card__head svg { color: var(--c-danger); }
.card--danger .card__head { border-bottom-color: var(--c-danger-border); }

.callout {
  display: flex;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--c-border);
  background: var(--c-surface-2);
  font-size: var(--fs-sm);
  color: var(--c-text-2);
  line-height: var(--lh-snug);
}
.callout svg { margin-top: 1px; color: var(--c-text-3); }
.callout--danger {
  border-color: var(--c-danger-border);
  background: var(--c-danger-soft);
  color: #ffc4c4;
}
.callout--danger svg { color: var(--c-danger); }
.callout--warn {
  border-color: var(--c-warn-border);
  background: var(--c-warn-soft);
  color: #f6dda8;
}
.callout--warn svg { color: var(--c-warn); }
.callout--info {
  border-color: rgba(79,140,255,0.28);
  background: var(--c-accent-soft);
  color: #cfdeff;
}
.callout--info svg { color: var(--c-accent); }
.callout__title { font-weight: var(--fw-semi); color: inherit; display: block; }

/* ---- status badge ---- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-full);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  line-height: 1;
  border: 1px solid var(--c-neutral-border);
  background: var(--c-neutral-soft);
  color: var(--c-neutral);
  white-space: nowrap;
  vertical-align: middle;
  font-variant-numeric: tabular-nums;
}
.badge--lg { height: 26px; padding: 0 var(--sp-3); font-size: var(--fs-sm); gap: var(--sp-2); }

.badge .dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.badge .spinner { width: 10px; height: 10px; }

.badge--ok      { border-color: var(--c-ok-border);     background: var(--c-ok-soft);     color: var(--c-ok); }
.badge--warn    { border-color: var(--c-warn-border);   background: var(--c-warn-soft);   color: var(--c-warn); }
.badge--danger  { border-color: var(--c-danger-border); background: var(--c-danger-soft); color: var(--c-danger); }
.badge--accent  { border-color: rgba(79,140,255,0.30);  background: var(--c-accent-soft); color: var(--c-accent); }
.badge--neutral { border-color: var(--c-neutral-border);background: var(--c-neutral-soft);color: var(--c-neutral); }

/* Environment label. Same pill as every other badge -- an environment is
   another fact about the same project, and giving it its own component would
   make the two harder to read together, exactly as alert_badge() argues.

   The one structural difference is deliberate: no leading dot or spinner.
   Every health badge above always has one, so the *shape* of this badge reads
   as "not health" before its colour registers at all -- which is also what
   keeps the distinction working for a colourblind reader, where hue alone
   would not. .badge--env has no `.dot` rule for the same reason: the markup
   never emits one. */
.badge--env     { border-color: var(--c-env-border);    background: var(--c-env-soft);    color: var(--c-env); }
.badge--env-unset { border-color: var(--c-border-strong); background: transparent; color: var(--c-text-3); }
/* Environment badges are links into ?env=; keep the pill from picking up the
   global link colour, which would undo the token above. */
a.badge--env:hover { border-color: var(--c-env); color: var(--c-env); }

/* A soft halo marks a live status. This used to be an infinite pulse
   animation; with a grid of running projects that is a screenful of
   permanently blinking dots, which reads as noise rather than as health.
   The static ring says the same thing and matches .status-dot--ok exactly. */
.badge--ok .dot     { box-shadow: 0 0 0 2.5px var(--c-ok-soft); }
.badge--warn .dot   { box-shadow: 0 0 0 2.5px var(--c-warn-soft); }
.badge--danger .dot { box-shadow: 0 0 0 2.5px var(--c-danger-soft); }

/* spinner glyph used for provisioning/deleting */
.spinner {
  width: 11px; height: 11px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  border-right-color: transparent;
  border-bottom-color: transparent;
  animation: spin 720ms linear infinite;
  flex: none;
}
.spinner--md { width: 16px; height: 16px; border-width: 2px; }
.spinner--lg { width: 22px; height: 22px; border-width: 2px; }

/* standalone status dot (used in card titles / lists) */
.status-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--c-neutral);
}
.status-dot--ok     { background: var(--c-ok);     box-shadow: 0 0 0 3px var(--c-ok-soft); }
.status-dot--warn   { background: var(--c-warn);   box-shadow: 0 0 0 3px var(--c-warn-soft); }
.status-dot--danger { background: var(--c-danger); box-shadow: 0 0 0 3px var(--c-danger-soft); }
.status-dot--neutral{ background: var(--c-neutral);box-shadow: 0 0 0 3px var(--c-neutral-soft); }

/* ---- tables (kept for genuinely tabular data) ---- */

.table-wrap {
  overflow-x: auto;
  border-radius: var(--r-md);
  border: 1px solid var(--c-border);
}
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
  min-width: 460px;
}
.table th {
  text-align: left;
  font-weight: var(--fw-medium);
  color: var(--c-text-3);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-border);
  white-space: nowrap;
}
.table td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--c-border-subtle);
  color: var(--c-text-2);
  vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover td { background: var(--c-hover); }

/* definition list — the "not a giant table" replacement for key/value data */
.dl { display: grid; gap: var(--sp-3) var(--sp-5); }
.dl__row {
  display: grid;
  grid-template-columns: minmax(120px, 168px) minmax(0, 1fr);
  gap: var(--sp-4);
  align-items: baseline;
}
.dl__key {
  font-size: var(--fs-sm);
  color: var(--c-text-3);
  font-weight: var(--fw-normal);
  display: flex;
  align-items: center;
  gap: 6px;
}
.dl__key svg { color: var(--c-text-3); opacity: 0.8; }
/* key rendered as an identifier (env var names) rather than prose */
.dl__key--code {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: -0.01em;
}
.dl__val {
  font-size: var(--fs-sm);
  color: var(--c-text);
  min-width: 0;
  word-break: break-word;
}
.dl__val--mono { font-family: var(--font-mono); font-size: var(--fs-sm); }
.dl__val-note { margin-bottom: var(--sp-2); }
/* Tighter rhythm when every row is a control rather than a line of text, and
   a wider key column: env-var names such as SUPABASE_PUBLISHABLE_KEY overran
   the 168px prose column and collided with the value field next to them. */
.dl--controls { gap: var(--sp-2) var(--sp-5); }
.dl--controls .dl__row {
  align-items: center;
  grid-template-columns: minmax(0, 212px) minmax(0, 1fr);
  gap: var(--sp-3);
}
.dl__key--code { white-space: nowrap; }

/* ---------------------------------------------------------- 4. components */

/* ---- stat cards ---- */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--sp-4);
}

.stat {
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-5) var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  box-shadow: var(--sh-xs);
  transition: border-color var(--t-base);
}
.stat:hover { border-color: var(--c-border-strong); }

.stat__head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--c-text-3);
}
.stat__head .spacer { margin-left: auto; }

.stat__value {
  font-size: var(--fs-2xl);
  font-weight: var(--fw-bold);
  letter-spacing: -0.025em;
  line-height: 1.05;
  color: var(--c-text);
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
}
.stat__unit {
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  color: var(--c-text-3);
  letter-spacing: 0;
}
.stat__detail {
  font-size: var(--fs-xs);
  color: var(--c-text-3);
  font-variant-numeric: tabular-nums;
  line-height: var(--lh-snug);
  overflow-wrap: anywhere;
}
/* meter last and pushed down so the bars line up across cards of differing
   detail-text length */
.stat__meter { margin-top: auto; padding-top: var(--sp-1); }

/* ---- project card grid ---- */

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--sp-4);
}

.project-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-xs);
  transition: border-color var(--t-base), box-shadow var(--t-base);
  overflow: hidden;
}
/* No lift on hover: cards used to translate 1px while the surrounding
   .card / .stat surfaces did not, so a page with both felt inconsistent.
   Border + shadow alone is the quieter, uniform affordance. */
.project-card:hover {
  border-color: var(--c-border-strong);
  box-shadow: var(--sh-sm);
}
.project-card:focus-within { border-color: var(--c-accent); }

.project-card__top {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-4) var(--sp-3);
}
.project-card__id { min-width: 0; flex: 1; }
.project-card__name {
  display: block;
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--c-text);
  letter-spacing: -0.01em;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.project-card__org {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: 3px;
  font-size: var(--fs-xs);
  color: var(--c-text-3);
  min-width: 0;
}
.project-card__org span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.project-card__badges {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: none;
}

.project-card__body {
  padding: 0 var(--sp-4) var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  flex: 1;
}

.kv-line {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--c-text-3);
  min-width: 0;
}
.kv-line svg { color: var(--c-text-3); flex: none; }
.kv-line .val {
  min-width: 0;
  color: var(--c-text-2);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.res-strip {
  display: flex;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border-subtle);
}
.res-strip__item { flex: 1; min-width: 0; }
.res-strip__value {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--c-text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.project-card__foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--c-border-subtle);
  background: var(--c-wash-foot);
}
.project-card__foot .spacer { margin-left: auto; }

.inline-link { display: inline-flex; }

.card-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--c-accent);
}
.card-link svg { transition: transform var(--t-fast); }
.card-link:hover svg { transform: translateX(2px); }

/* ---- compact list rows (recent projects, service health) ---- */

.list { display: flex; flex-direction: column; }

.list__row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--c-border-subtle);
  color: var(--c-text);
  transition: background var(--t-fast);
}
.list__row:last-child { border-bottom: 0; }
a.list__row:hover { background: var(--c-hover); color: var(--c-text); }
/* rows used as plain content inside a card body, not as a full-bleed list */
.list--flush .list__row { padding-left: 0; padding-right: 0; }
.list--flush .list__row:first-child { padding-top: 0; }

.list__icon { color: var(--c-text-3); flex: none; }
.list__main { min-width: 0; flex: 1; }
.list__title {
  display: block;
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  color: var(--c-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.list__sub {
  display: block;
  font-size: var(--fs-xs);
  color: var(--c-text-3);
  font-family: var(--font-mono);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.list__aside {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: none;
}
.list__chev { color: var(--c-text-3); transition: transform var(--t-fast), color var(--t-fast); }
a.list__row:hover .list__chev { transform: translateX(2px); color: var(--c-text-2); }

/* summary strip above a health list — real counts, no invented metrics */
.list__summary {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--c-border-subtle);
  font-size: var(--fs-xs);
  color: var(--c-text-3);
  font-variant-numeric: tabular-nums;
}
.list__summary b { color: var(--c-text-2); font-weight: var(--fw-semi); }
.list__summary .sep { color: var(--c-border-strong); }

.tnum { font-variant-numeric: tabular-nums; }
.hide-sm { display: revert; }
@media (max-width: 720px) { .hide-sm { display: none !important; } }

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

.tabs {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1);
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: var(--ctl-h-sm);
  padding: 0 var(--sp-3);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--c-text-3);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t-fast), color var(--t-fast);
}
.tab:hover { background: var(--c-surface-3); color: var(--c-text-2); }
.tab[aria-selected="true"] {
  background: var(--c-surface-3);
  color: var(--c-text);
  box-shadow: var(--sh-xs);
}
.tab__count {
  font-size: var(--fs-2xs);
  font-variant-numeric: tabular-nums;
  padding: 0 5px;
  min-width: 18px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-full);
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border);
  color: inherit;
}
.tab[aria-selected="true"] .tab__count {
  background: var(--c-accent-soft);
  border-color: rgba(79,140,255,0.3);
  color: var(--c-accent);
}

/* ---- underline tabs (project detail) ---- */

.utabs {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  border-bottom: 1px solid var(--c-border);
  overflow-x: auto;
  scrollbar-width: none;
}
.utabs::-webkit-scrollbar { display: none; }

.utab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--sp-3) var(--sp-3);
  border: 0;
  background: transparent;
  color: var(--c-text-3);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--t-fast);
}
/* the underline spans the full tab, not an inset of it — it used to stop
   short of the label on both sides */
.utab::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  bottom: -1px;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: transparent;
  transition: background var(--t-fast);
}
.utab:hover { color: var(--c-text-2); }
.utab:hover::after { background: var(--c-border-strong); }
.utab[aria-selected="true"] { color: var(--c-text); font-weight: var(--fw-semi); }
.utab[aria-selected="true"]::after,
.utab[aria-selected="true"]:hover::after { background: var(--c-accent); }
.utab__soon {
  font-size: var(--fs-2xs);
  padding: 0 5px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--r-xs);
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border);
  color: var(--c-text-3);
  font-weight: var(--fw-medium);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.tabpanel[hidden] { display: none; }
.tabpanel {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  animation: fade-in var(--t-base);
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

/* ---- overflow menu ---- */

.menu { position: relative; }
.menu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 208px;
  padding: var(--sp-1);
  background: var(--c-surface-3);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-md);
  box-shadow: var(--sh-lg);
  z-index: var(--z-overlay);
  display: flex;
  flex-direction: column;
  gap: 1px;
  animation: menu-in 120ms cubic-bezier(0.16, 1, 0.3, 1);
  transform-origin: top right;
}
.menu__panel--up { top: auto; bottom: calc(100% + 6px); transform-origin: bottom right; }
.menu__panel--left { right: auto; left: 0; transform-origin: top left; }
.menu__panel[hidden] { display: none; }

@keyframes menu-in {
  from { opacity: 0; transform: scale(0.96) translateY(-4px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--c-text-2);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-normal);
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t-fast), color var(--t-fast);
}
.menu__item:hover { background: var(--c-hover-strong); color: var(--c-text); }
.menu__item svg { color: var(--c-text-3); }
.menu__item:hover svg { color: var(--c-text-2); }
.menu__item--danger { color: var(--c-danger); }
.menu__item--danger svg { color: var(--c-danger); }
.menu__item--danger:hover { background: var(--c-danger-soft); color: var(--c-danger-hover); }
.menu__item--danger:hover svg { color: var(--c-danger-hover); }
/* pointer-events kept so the title= explaining the disabled reason shows */
.menu__item[disabled] { opacity: 0.4; cursor: not-allowed; }
.menu__item[disabled]:hover { background: transparent; color: var(--c-text-2); }
.menu__sep { height: 1px; background: var(--c-border); margin: var(--sp-1) 0; }
.menu form { display: contents; }

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

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(4, 5, 7, 0.72);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  z-index: var(--z-modal);
  display: grid;
  place-items: center;
  padding: var(--sp-4);
  animation: fade-in var(--t-base);
  overflow-y: auto;
}
.modal-backdrop[hidden] { display: none; }

.modal {
  width: 100%;
  max-width: 460px;
  background: var(--c-surface-2);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-xl);
  animation: modal-in var(--t-slow);
  max-height: calc(100dvh - var(--sp-8));
  display: flex;
  flex-direction: column;
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}

.modal__head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
}
.modal__icon {
  width: 34px; height: 34px;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  flex: none;
  background: var(--c-danger-soft);
  border: 1px solid var(--c-danger-border);
  color: var(--c-danger);
}
.modal__icon--warn { background: var(--c-warn-soft); border-color: var(--c-warn-border); color: var(--c-warn); }
.modal__icon--info { background: var(--c-accent-soft); border-color: rgba(79,140,255,0.3); color: var(--c-accent); }
.modal__title {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--c-text);
  line-height: 1.35;
}
.modal__desc {
  margin-top: var(--sp-1);
  font-size: var(--fs-sm);
  color: var(--c-text-3);
  line-height: var(--lh-snug);
}
.modal__body {
  padding: 0 var(--sp-5) var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  overflow-y: auto;
}
.modal__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--c-border-subtle);
  background: var(--c-wash-foot);
  border-radius: 0 0 var(--r-xl) var(--r-xl);
}

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

.toast-region {
  position: fixed;
  bottom: var(--sp-5);
  right: var(--sp-5);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  pointer-events: none;
  max-width: min(380px, calc(100vw - var(--sp-8)));
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-surface-3);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-md);
  box-shadow: var(--sh-lg);
  font-size: var(--fs-sm);
  color: var(--c-text);
  pointer-events: auto;
  animation: toast-in var(--t-slow);
}
.toast.is-out { animation: toast-out 180ms ease-in forwards; }
@keyframes toast-in  { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateX(16px); } }
.toast__icon { flex: none; margin-top: 1px; }
.toast--ok     .toast__icon { color: var(--c-ok); }
.toast--danger .toast__icon { color: var(--c-danger); }
.toast--warn   .toast__icon { color: var(--c-warn); }
.toast--info   .toast__icon { color: var(--c-accent); }
.toast__msg { flex: 1; line-height: var(--lh-snug); }
.toast__close {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--c-text-3); flex: none;
}
.toast__close:hover { color: var(--c-text); }

/* ---- empty state ---- */

/* One geometry for both "there is nothing here yet" (.empty) and "Kylth does
   not implement this yet" (.soon). They were built in the same pass but had
   drifted to different padding, icon sizes and type scales. */
.empty,
.soon {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--sp-3);
  padding: var(--sp-10) var(--sp-6);
  border: 1px dashed var(--c-border-strong);
  border-radius: var(--r-lg);
  background: var(--c-surface-2);
}
.empty {
  background:
    radial-gradient(ellipse 60% 70% at 50% 0%, rgba(79,140,255,0.05), transparent 70%),
    var(--c-surface-2);
}
.empty__icon,
.soon__icon {
  width: 44px; height: 44px;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  background: var(--c-surface-3);
  border: 1px solid var(--c-border);
  color: var(--c-text-3);
  margin-bottom: var(--sp-1);
}
.empty__title,
.soon__title {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--c-text);
  letter-spacing: -0.01em;
}
.soon__title { color: var(--c-text-2); }
.empty__desc,
.soon__desc {
  font-size: var(--fs-sm);
  color: var(--c-text-3);
  max-width: 46ch;
  line-height: var(--lh-snug);
}
.empty .btn { margin-top: var(--sp-2); }
.soon .badge { margin-top: var(--sp-2); }

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

.skeleton {
  background: linear-gradient(90deg,
    var(--c-surface-3) 0%,
    var(--c-skeleton-hi) 40%,
    var(--c-surface-3) 80%);
  background-size: 300% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
  border-radius: var(--r-xs);
  display: block;
}
@keyframes shimmer {
  0%   { background-position: 140% 0; }
  100% { background-position: -40% 0; }
}
.skeleton--text { height: 10px; }
.skeleton--line { height: 14px; }

.res-strip.is-loading .res-strip__value { color: transparent; }

/* ---- copy button ---- */

.copy-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  /* keeps the credentials block from stretching the full content width and
     dominating the detail page on a 1440p display */
  max-width: 620px;
}
.copy-value {
  display: flex;
  align-items: center;
  height: var(--ctl-icon);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--c-text-2);
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-3);
  overflow-x: auto;
  white-space: nowrap;
  flex: 1;
  min-width: 0;
  scrollbar-width: none;
}
.copy-value::-webkit-scrollbar { display: none; }
.copy-value.is-masked {
  color: transparent;
  text-shadow: 0 0 7px rgba(233, 236, 241, 0.62);
  user-select: none;
}
/* a revealed secret stays visually flagged as a secret */
.copy-row--secret .copy-value {
  border-color: rgba(245, 181, 68, 0.24);
  background:
    linear-gradient(0deg, rgba(245, 181, 68, 0.045), rgba(245, 181, 68, 0.045)),
    var(--c-surface-inset);
}
.copy-row--secret .copy-value.is-masked { color: transparent; }

/* credentials block: keys read as identifiers, secrets are flagged */
.creds-key {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.creds-key svg { color: var(--c-warn); opacity: 0.8; }
.creds-conn {
  padding-top: var(--sp-4);
  border-top: 1px solid var(--c-border-subtle);
}
.creds-conn .field__label svg { color: var(--c-warn); opacity: 0.8; }
.creds-conn .field__hint { max-width: 62ch; }

/* small explanatory paragraphs inside a card, spaced consistently */
.panel-note { margin-top: var(--sp-3); line-height: var(--lh-snug); }
.panel-lede { max-width: 76ch; line-height: var(--lh-snug); }
.provision-lede { margin-bottom: var(--sp-4); }

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

.muted     { color: var(--c-text-3); }
.text-sm   { font-size: var(--fs-sm); }
.text-xs   { font-size: var(--fs-xs); }
.mono      { font-family: var(--font-mono); }
.nowrap    { white-space: nowrap; }
.spacer    { margin-left: auto; }
.row       { display: flex; align-items: center; gap: var(--sp-2); }
.row--tight{ gap: var(--sp-1); }
.row-wrap  { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.stack-2   { display: flex; flex-direction: column; gap: var(--sp-2); }
.stack-3   { display: flex; flex-direction: column; gap: var(--sp-3); }
.stack-4   { display: flex; flex-direction: column; gap: var(--sp-4); }
.stack-5   { display: flex; flex-direction: column; gap: var(--sp-5); }
.flex-1    { flex: 1 1 300px; min-width: 0; }

/* metadata line: label · label · label, with the separators dropping out
   rather than dangling at a line end once the row wraps */
.meta-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
  font-size: var(--fs-xs);
  color: var(--c-text-3);
}
.meta-row .row { gap: 5px; }
.meta-row .sep { color: var(--c-border-strong); }

/* Exactly two columns, not "as many as fit". auto-fit was silently making
   four ~280px columns on the Settings page at >=1920, which squeezed every
   definition-list value into a 60px gutter and wrapped mid-word. */
.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4);
  align-items: start;
}
@media (max-width: 900px) {
  .grid-2 { grid-template-columns: minmax(0, 1fr); }
}

.error-box {
  background: var(--c-danger-soft);
  border: 1px solid var(--c-danger-border);
  color: #ffc0c0;
  padding: var(--sp-3);
  border-radius: var(--r-md);
  white-space: pre-wrap;
  word-break: break-word;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: 1.5;
  max-height: 260px;
  overflow: auto;
  margin: 0;
}

/* ------------------------------------------------------ 5. specific pages */

/* ---- auth (login / setup) ---- */

.auth-body {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--sp-6) var(--sp-4);
  background:
    radial-gradient(ellipse 70% 50% at 50% -10%, rgba(79,140,255,0.10), transparent 60%),
    radial-gradient(ellipse 50% 40% at 90% 100%, rgba(123,92,255,0.07), transparent 60%),
    var(--c-bg);
}
.auth-shell {
  width: 100%;
  max-width: 380px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}
.auth-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  justify-content: center;
}
.auth-brand .brand-mark { width: 34px; height: 34px; border-radius: var(--r-md); }
.auth-brand .brand-name { font-size: 1.25rem; }

.auth-card {
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-xl);
  padding: var(--sp-6);
  box-shadow: var(--sh-lg);
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}
.auth-card__head { text-align: center; }
.auth-card__title {
  font-size: var(--fs-lg);
  font-weight: var(--fw-semi);
  letter-spacing: -0.01em;
}
.auth-card__desc {
  margin-top: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--c-text-3);
  line-height: var(--lh-snug);
}
.auth-foot {
  text-align: center;
  font-size: var(--fs-xs);
  color: var(--c-text-3);
}

/* the sign-in / setup forms are the one place a taller control reads better */
.auth-card .input { height: var(--ctl-h-lg); font-size: var(--fs-base); }

.pw-wrap { position: relative; }
.pw-wrap .input { padding-right: 42px; }
.pw-toggle {
  position: absolute;
  right: 5px; top: 50%;
  transform: translateY(-50%);
  width: var(--ctl-h-sm); height: var(--ctl-h-sm);
  display: grid; place-items: center;
  background: none; border: 0; padding: 0;
  color: var(--c-text-3);
  cursor: pointer;
  border-radius: var(--r-xs);
}
.pw-toggle:hover { color: var(--c-text-2); background: var(--c-surface-3); }

/* ---- project detail header ---- */

.detail-head {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--c-text-3);
  width: fit-content;
}
.back-link:hover { color: var(--c-text-2); }
.back-link svg { transition: transform var(--t-fast); }
.back-link:hover svg { transform: translateX(-2px); }

.detail-head__main {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
.detail-title-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
.detail-title {
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  line-height: var(--lh-tight);
}
.detail-meta {
  display: flex;
  align-items: center;
  gap: var(--sp-1) var(--sp-3);
  margin-top: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--c-text-3);
  flex-wrap: wrap;
}
.detail-meta .row { gap: 5px; }
.detail-meta .sep { color: var(--c-border-strong); }

/* ---- coming soon panel ---- */

/* ---- provisioning banner ---- */

.provisioning-banner {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border-radius: var(--r-lg);
  border: 1px solid var(--c-warn-border);
  background:
    linear-gradient(90deg, rgba(245,181,68,0.10), rgba(245,181,68,0.03));
  overflow: hidden;
  position: relative;
}
.provisioning-banner::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  height: 2px; width: 100%;
  background: linear-gradient(90deg, transparent, var(--c-warn), transparent);
  background-size: 40% 100%;
  background-repeat: no-repeat;
  animation: sweep 1.6s ease-in-out infinite;
}
@keyframes sweep {
  0%   { background-position: -40% 0; }
  100% { background-position: 140% 0; }
}
.provisioning-banner > .spinner { color: var(--c-warn); }
.provisioning-banner__text { min-width: 0; }
.provisioning-banner__title {
  font-size: var(--fs-base);
  font-weight: var(--fw-semi);
  color: var(--c-warn);
}
.provisioning-banner__detail {
  font-size: var(--fs-sm);
  color: var(--c-text-2);
  margin-top: 2px;
}
.provisioning-banner--danger {
  border-color: var(--c-danger-border);
  background: linear-gradient(90deg, rgba(249,106,106,0.10), rgba(249,106,106,0.03));
}
.provisioning-banner--danger > .spinner,
.provisioning-banner--danger .provisioning-banner__title { color: var(--c-danger); }
.provisioning-banner--danger::after {
  background: linear-gradient(90deg, transparent, var(--c-danger), transparent);
  background-size: 40% 100%;
  background-repeat: no-repeat;
}
.callout .error-box { margin-top: var(--sp-2); }

/* ---- org card ---- */

.org-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-xs);
  transition: border-color var(--t-base), box-shadow var(--t-base);
  position: relative;
}
.org-card:hover {
  border-color: var(--c-border-strong);
  box-shadow: var(--sh-sm);
}
.org-card:hover .list__chev { color: var(--c-text-2); transform: translateX(2px); }
.org-card__top { display: flex; align-items: center; gap: var(--sp-3); }
.org-avatar {
  width: 38px; height: 38px;
  border-radius: var(--r-md);
  display: grid; place-items: center;
  background: linear-gradient(140deg, rgba(79,140,255,0.22), rgba(123,92,255,0.18));
  border: 1px solid rgba(79,140,255,0.28);
  color: #b9cdff;
  font-weight: var(--fw-bold);
  font-size: var(--fs-base);
  flex: none;
  text-transform: uppercase;
}
.org-card__id { min-width: 0; }
.org-card__name {
  display: block;
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--c-text);
  letter-spacing: -0.01em;
}
a.org-card__name:hover { color: var(--c-accent); }
.org-card:focus-within { border-color: var(--c-accent); }
.org-card__slug { display: block; font-size: var(--fs-xs); color: var(--c-text-3); font-family: var(--font-mono); }
.org-stats {
  display: flex;
  gap: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--c-border-subtle);
}
/* fixed value row height so the numeric stats and the date stat share one
   baseline instead of being nudged apart with a padding hack */
.org-stat__value {
  display: flex;
  align-items: baseline;
  height: 22px;
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--c-text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.015em;
  line-height: 1.1;
}
.org-stat__value--ok { color: var(--c-ok); }
.org-stat__value--date {
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--c-text-2);
  letter-spacing: 0;
}
.org-stat__label { margin-top: 3px; }
/* whole-card click target without nesting interactive elements */
.stretched-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

/* ---- new project preview ---- */

.preview-url {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border-radius: var(--r-sm);
  background: var(--c-surface-inset);
  border: 1px solid var(--c-border);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: var(--c-text-3);
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}
.preview-url::-webkit-scrollbar { display: none; }
.preview-url b { color: var(--c-accent); font-weight: var(--fw-semi); }
.preview-url .placeholder { color: var(--c-text-3); opacity: 0.6; font-style: italic; }

.form-narrow { max-width: 560px; }

/* -------------------------------------------------------- 6. responsive */

/* tablet & below: sidebar becomes an off-canvas drawer */
@media (max-width: 1024px) {
  .shell { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }

  .sidebar {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: min(280px, 84vw);
    transform: translateX(-102%);
    transition: transform var(--t-slow);
    box-shadow: none;
    grid-column: 1;
    grid-row: 1 / span 2;
  }
  body.nav-open .sidebar {
    transform: none;
    box-shadow: var(--sh-xl);
  }

  .nav-scrim {
    grid-area: 1 / 1 / span 2 / span 2;
    position: fixed;
    inset: 0;
    background: rgba(4,5,7,0.6);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    z-index: calc(var(--z-drawer) - 1);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--t-base);
  }
  body.nav-open .nav-scrim { opacity: 1; pointer-events: auto; }

  .topbar  { grid-column: 1; grid-row: 1; }
  .content { grid-column: 1; grid-row: 2; }
  .sidebar-toggle { display: grid; }
  body.nav-open { overflow: hidden; }
}

@media (min-width: 1025px) {
  .nav-scrim { display: none; }
}

@media (max-width: 720px) {
  .content { padding: var(--sp-5) var(--sp-4) var(--sp-16); }
  .content__inner { gap: var(--sp-6); }
  .topbar { padding: 0 var(--sp-4); gap: var(--sp-2); }
  .page-title { font-size: 1.25rem; }
  .stat-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--sp-3); }
  .stat { padding: var(--sp-3) var(--sp-4) var(--sp-4); gap: var(--sp-2); }
  .stat__value { font-size: 1.5rem; }
  .card-grid { grid-template-columns: minmax(0, 1fr); }
  .card__body { padding: var(--sp-4); }
  .card__head { padding: var(--sp-3) var(--sp-4); }
  .card__foot { padding: var(--sp-3) var(--sp-4); }
  .list__row, .list__summary { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  .dl__row { grid-template-columns: minmax(0, 1fr); gap: var(--sp-1); }
  .dl { gap: var(--sp-4); }
  /* must repeat the selector: .dl--controls .dl__row outranks .dl__row, and
     without this the 212px key column stays and squeezes the copy field */
  .dl--controls { gap: var(--sp-3); }
  .dl--controls .dl__row {
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
    gap: var(--sp-1);
  }
  .copy-row { max-width: none; }
  .page-head__actions { width: 100%; }
  .page-head__actions .btn { flex: 1; }
  .toast-region { left: var(--sp-4); right: var(--sp-4); bottom: var(--sp-4); max-width: none; }
  .modal__foot { flex-direction: column-reverse; }
  .modal__foot .btn { width: 100%; }
  .org-stats { gap: var(--sp-5); }
  /* the slot-usage badge should follow the filter tabs, not be flung to the
     far edge of its own line */
  .section__head--filters > .spacer { display: none; }
  /* separators dangle at the end of a wrapped line; the gap carries the
     grouping on its own at this width */
  .meta-row .sep { display: none; }
  .host-pill { padding: 0 var(--sp-2); }
  .host-pill .host-pill__slots, .host-pill .sep { display: none; }
  .empty, .soon { padding: var(--sp-8) var(--sp-5); }
}

@media (max-width: 420px) {
  .stat-grid { grid-template-columns: minmax(0, 1fr); }
  .breadcrumbs .crumb-hide-sm { display: none; }
  .breadcrumbs .sep-hide-sm { display: none; }
}

/* ============================================================================
   charts — the historical monitoring section
   Layout and chrome only. Every colour here is an existing token: the marks
   themselves are painted by app/charts.py, which owns the one validated
   categorical trio and reuses --c-accent / the status tones for everything
   else. No new hue is introduced at this layer.
   ========================================================================= */

.trends { display: grid; gap: var(--sp-5); }

.trends__controls {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

/* Refetch keeps the frame: while a range click is in flight the panel dims
   rather than collapsing to a skeleton, so nothing jumps. */
.trends.htmx-request { opacity: 0.55; transition: opacity var(--t-fast); }

.chart { margin: 0; display: grid; gap: var(--sp-2); }
.chart:focus-visible {
  outline: 2px solid var(--c-accent-ring);
  outline-offset: 4px;
  border-radius: var(--r-sm);
}

.chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  font-size: var(--fs-xs);
  color: var(--c-text-2);
}
.chart__legend-item { display: inline-flex; align-items: center; gap: 6px; }

/* A short stroke, not a filled box — the mark these charts draw is a line. */
.chart__key {
  width: 12px;
  height: 2px;
  border-radius: 1px;
  display: inline-block;
  flex: none;
}
.chart__key--none { background: var(--c-border-strong); opacity: 0.45; }

.chart__plot { position: relative; }
.chart__svg { display: block; overflow: visible; touch-action: none; }

.chart__tooltip {
  position: absolute;
  top: var(--sp-2);
  z-index: 2;
  pointer-events: none;
  min-width: 132px;
  padding: var(--sp-2) var(--sp-3);
  background: var(--c-surface-3);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--sh-md);
}
.chart__tooltip[hidden] { display: none; }
.chart__tip-time {
  font-size: var(--fs-2xs);
  color: var(--c-text-3);
  font-variant-numeric: tabular-nums;
  margin-bottom: 4px;
  white-space: nowrap;
}
.chart__tip-rows { display: grid; gap: 3px; }
.chart__tip-row {
  display: grid;
  grid-template-columns: 12px 1fr auto;
  align-items: center;
  gap: var(--sp-2);
  white-space: nowrap;
}
/* Values lead, labels follow: here the reader already knows the series and
   wants the number, so the number is the strong element. */
.chart__tip-label { font-size: var(--fs-2xs); color: var(--c-text-3); }
.chart__tip-value {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  color: var(--c-text);
  font-variant-numeric: tabular-nums;
}

.chart-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--c-border);
  border-radius: var(--r-md);
  color: var(--c-text-3);
  font-size: var(--fs-sm);
  padding: var(--sp-5);
  text-align: center;
}

/* small multiples — the per-project comparison */
.chart-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--sp-4);
}
.chart-facet {
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  padding: var(--sp-3);
  background: var(--c-surface-2-lo);
  min-width: 0;
}
.chart-facet__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}
.chart-facet__title {
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--c-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chart-facet__title:hover { color: var(--c-accent); }

/* the table view under every chart */
.chart-data > summary {
  cursor: pointer;
  font-size: var(--fs-xs);
  color: var(--c-text-3);
  padding: var(--sp-1) 0;
  user-select: none;
}
.chart-data > summary:hover { color: var(--c-text-2); }
.chart-data[open] > summary { margin-bottom: var(--sp-3); }
.chart-data .table-wrap { max-height: 340px; overflow-y: auto; }

/* availability strip */
.avail { line-height: 0; }
.avail__svg { display: block; border-radius: var(--r-xs); }
.avail__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
}
.avail__legend > span { display: inline-flex; align-items: center; gap: 6px; }

@media (max-width: 640px) {
  .chart-grid { grid-template-columns: minmax(0, 1fr); }
}

/* project templates (Step 18) — two additive rules, no existing selector
   touched. The shared .modal is 460px, which is right for the 5-field forms it
   was built for and too narrow for this one: a 24-provider checklist and a
   multi-line SQL textarea in a 460px column is unusable. .checkbox-grid is the
   same idea as .chart-grid above, at checkbox scale. */
.modal--wide { max-width: 760px; }

.checkbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--sp-2) var(--sp-4);
}

/* print — mostly a courtesy; nobody prints a control panel, but a mangled
   print stylesheet looks worse than a plain one */
@media print {
  .sidebar, .topbar, .nav-scrim, .toast-region { display: none !important; }
  .shell { grid-template-columns: 1fr; }
  body { background: #fff; color: #000; }
}

/* ------------------------------------------- org card meta (MT Stage 3) --
   The role badge and the Members link on an organization card.

   z-index is load-bearing, not decoration. .org-card__name carries
   .stretched-link, whose ::after covers the entire card so the whole tile is
   one click target. Anything genuinely interactive that sits *inside* that
   card has to be lifted above the overlay or it is unreachable -- the link
   below would render, hover and do nothing at all. */
.org-card__meta {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  position: relative;
  z-index: 1;
}
.org-card__link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-left: auto;
  font-size: var(--fs-xs);
  color: var(--c-text-3);
  transition: color var(--t-base);
}
.org-card__link:hover { color: var(--c-accent); }

/* --------------------------------------------- org switcher (MT Stage 3) --
   Sits in the sidebar footer above the account row. Built from the same
   .select primitive the forms use rather than a bespoke dropdown, so it
   inherits focus rings, disabled styling and dark-mode tokens for free. */
.org-switch { display: flex; flex-direction: column; gap: var(--sp-2); }
.org-switch .select {
  width: 100%;
  font-size: var(--fs-xs);
  padding-top: var(--sp-2);
  padding-bottom: var(--sp-2);
}
.org-switch__single {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--r-md);
  background: var(--c-surface-2);
  font-size: var(--fs-xs);
  color: var(--c-text-2);
}
.org-switch__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.org-switch__role {
  margin-left: auto;
  color: var(--c-text-3);
  font-size: var(--fs-2xs, var(--fs-xs));
}


/* ============================================================================
   Edge Function editor — CodeMirror 5 skin.

   The vendored codemirror.css ships a light default theme, and Kylth's palette
   is dark-only (tokens.css defines one :root and no light variant). Rather than
   vendor one of CM's bundled themes and end up with a second palette nobody
   maintains, this maps CM's structural classes onto the tokens the rest of the
   app already uses, so the editor inherits the app's colours by construction.

   Scoped under [data-function-editor] so nothing here can affect a future page
   that embeds CodeMirror with different intentions.
   ========================================================================= */

[data-function-editor] .CodeMirror {
  height: 62vh;
  min-height: 320px;
  background: var(--c-surface-inset);
  color: var(--c-text);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: 1.65;
}
[data-function-editor] .CodeMirror-focused {
  border-color: var(--c-border-hover);
}

[data-function-editor] .CodeMirror-gutters {
  background: var(--c-surface-2);
  border-right: 1px solid var(--c-border-subtle);
}
[data-function-editor] .CodeMirror-linenumber { color: var(--c-text-3); }

[data-function-editor] .CodeMirror-cursor { border-left: 1.5px solid var(--c-text); }
[data-function-editor] .CodeMirror-selected { background: var(--c-surface-3); }
[data-function-editor] .CodeMirror-focused .CodeMirror-selected {
  background: var(--c-accent-soft);
}
[data-function-editor] .CodeMirror-matchingbracket {
  color: var(--c-accent) !important;
  border-bottom: 1px solid var(--c-accent);
}
[data-function-editor] .CodeMirror-nonmatchingbracket { color: var(--c-danger) !important; }

/* Syntax tokens. Deliberately a small set: the javascript mode (which also
   serves TypeScript and JSON here) emits these and little else. */
[data-function-editor] .cm-comment  { color: var(--c-text-3); font-style: italic; }
[data-function-editor] .cm-keyword,
[data-function-editor] .cm-atom     { color: #c58fff; }
[data-function-editor] .cm-string,
[data-function-editor] .cm-string-2 { color: #9ae6a0; }
[data-function-editor] .cm-number   { color: #ffb86b; }
[data-function-editor] .cm-def      { color: #7fd3ff; }
[data-function-editor] .cm-variable { color: var(--c-text); }
[data-function-editor] .cm-variable-2,
[data-function-editor] .cm-property { color: #8fc7ff; }
[data-function-editor] .cm-type     { color: #6fe3d0; }
[data-function-editor] .cm-operator { color: var(--c-text-2); }
[data-function-editor] .cm-meta     { color: var(--c-text-3); }
[data-function-editor] .cm-error    { color: var(--c-danger); }

/* The file tabs carry paths, which are longer than the filter labels .tab was
   sized for, and there can be a lot of them. */
[data-function-editor] .tabs { flex-wrap: wrap; }

/* No-JS fallback: the plain textarea should still be usable to type code in.
   Once CodeMirror attaches it is display:none and none of this applies. */
[data-function-editor] textarea[data-fn-editor] {
  min-height: 320px;
  line-height: 1.65;
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}
