/* =========================================================================
   ArcMCP catalog — stylesheet
   =========================================================================
   Design tokens hand-transcribed from the GrowthArc marketplace design
   system (MCP-Marketplace, branch revamp/marketplace-ui-react — Tailwind
   config + src/index.css). No Tailwind, no CDN, no build step: every value
   below is a plain CSS custom property or a literal rule.

   Sections:
     1. Fonts (self-hosted @font-face)
     2. Design tokens (:root)
     3. Reset & base elements
     4. Global motion / scrollbars / selection
     5. Page shell (header, main)
     6. Cards + grid + the signature conic-gradient hover border
     7. Status dot + badges
     8. Buttons
     9. URL chip
     10. Table (admin)
     11. Notices + empty state
     12. Misc (row notes)
     13. Responsive
     14. Details page (panels, entry lists, key/value list, tabs)
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. Fonts
   Self-hosted, latin-only woff2 files under /static/fonts (served by the
   app's own StaticFiles mount — no runtime CDN request). font-display:
   swap shows the fallback stack immediately and swaps in the real face
   when it loads, so there is never invisible text while a font is
   in flight; if a file fails to load outright, the fallback stack (system
   sans/serif/monospace) simply stays in place indefinitely — a safe,
   fully-functional degradation with no layout break.
   ------------------------------------------------------------------------- */
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/static/fonts/ibm-plex-sans-latin-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/static/fonts/ibm-plex-sans-latin-500-normal.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/static/fonts/ibm-plex-sans-latin-600-normal.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "EB Garamond";
  src: url("/static/fonts/eb-garamond-latin-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "EB Garamond";
  src: url("/static/fonts/eb-garamond-latin-500-normal.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "EB Garamond";
  src: url("/static/fonts/eb-garamond-latin-600-normal.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/static/fonts/jetbrains-mono-latin-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/static/fonts/jetbrains-mono-latin-500-normal.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* -------------------------------------------------------------------------
   2. Design tokens
   Categories: canvas/surfaces, text, borders, brand/accent, semantic status
   ramps, type scale, spacing scale, radii, shadows, gradients, motion.
   Palette entries that exist in the source system purely for its per-server
   icon "tint" feature (sand, gold, warmsky, purple, sage) are intentionally
   omitted — this app has no icon-tint component, and carrying unused
   tokens forward would just be dead CSS. Everything actually exercised by
   a card, badge, button, table or status dot below is transcribed in full.
   ------------------------------------------------------------------------- */
:root {
  /* --- fonts --- */
  --font-serif: "EB Garamond", Georgia, serif;       /* editorial: page/section headings */
  --font-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; /* interface/body */
  --font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; /* technical identifiers */

  /* --- colour: canvas & surfaces --- */
  --color-canvas: #FFFCF2;         /* page background — warm cream, never pure white */
  --color-surface-card: #FFFFFF;   /* card / popover / raised surface */
  --color-surface-tertiary: #F6F1E9; /* footer, hover tint, secondary surface */
  --color-surface-inset: #EDE6DA;  /* inset surface: code chips, input backgrounds */

  /* --- colour: text --- */
  --color-text: #1E3C53;           /* primary text / foreground */
  --color-text-muted: #6B7A86;     /* captions, descriptions, meta */
  --color-text-inverse: #FFFFFF;   /* text on primary/brand surfaces */

  /* --- colour: borders --- */
  --color-border: #E0D9CE;         /* default border / divider */
  --color-border-strong: #C9BFAF;  /* emphasis border, dashed empty-state, scrollbar thumb */
  --color-border-subtle: #EDE6DA;  /* divider inside grouped lists */
  --color-ring: #1E3C53;           /* focus ring */

  /* --- colour: brand ramp (navy-teal) --- */
  --color-brand-100: #F4F9EC;
  --color-brand-200: #EAF4DB;
  --color-brand-300: #D4E8B8;
  --color-brand-400: #ACC384;
  --color-brand-450: #7A9955;
  --color-brand-500: #1E3C53;      /* brand/primary accent */
  --color-brand-600: #162D3F;      /* hover */
  --color-brand-650: #0E1E2B;      /* pressed */

  /* --- colour: cyan ramp (AI / interactive accent) --- */
  --color-cyan-100: #E0F4FF;
  --color-cyan-300: #7AC8F0;
  --color-cyan-400: #3AABE5;
  --color-cyan-500: #0693E3;
  --color-cyan-600: #0577B8;
  --color-cyan-650: #045C8E;

  /* --- colour: mint (success / "up" accent) --- */
  --color-mint-500: #00D084;

  /* --- colour: semantic ramps --- */
  --color-success-100: #DCF8E6;
  --color-success-300: #60D69E;
  --color-success-500: #3BB85D;
  --color-success-600: #236D40;
  --color-warning-100: #FFF4E0;
  --color-warning-300: #FFBD60;
  --color-warning-500: #EF9400;
  --color-warning-600: #9C5D00;
  --color-danger-100: #FFE5E0;
  --color-danger-300: #FA7E5A;
  --color-danger-500: #E94E2D;     /* == destructive */
  --color-danger-600: #A2341F;
  --color-info-100: #CFE0FF;
  --color-info-300: #3389FF;
  --color-info-500: #006DE7;
  --color-info-600: #004687;

  /* --- colour: shadcn-style semantic aliases --- */
  --color-secondary: #F6F1E9;
  --color-secondary-foreground: #1E3C53;
  --color-accent: #EAF1F0;
  --color-accent-foreground: #1E3C53;
  --color-destructive: #E94E2D;
  --color-destructive-foreground: #FFFFFF;

  /* --- status dot vocabulary (catalog.status.HealthState) ---
     Kept as their own named variables so the dot vocabulary is stable even
     though the underlying palette above changed; "up" is the only breathing
     state, everything else is a steady colour (see §7). */
  --up: var(--color-mint-500);
  --degraded: var(--color-warning-500);
  --sleeping: var(--color-text-muted);
  --down: var(--color-danger-500);
  --unknown: var(--color-border-strong);

  /* --- type scale (Tailwind's default rem scale, plus two custom sizes) --- */
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;
  --text-4xl: 2.25rem;
  --text-5xl: 3rem;
  --text-caption: 0.65rem;   /* meta/footnote microcopy */
  --text-overline: 0.8rem;  /* uppercase eyebrow labels */

  /* --- line heights --- */
  --leading-xs: 1.2;
  --leading-sm: 1.44;
  --leading-md: 1.52;   /* default body copy */
  --leading-lg: 1.84;
  --leading-xl: 2;
  --leading-tight: 1.05; /* hero/heading leading */
  --leading-caption: 1.4;

  /* --- letter spacing --- */
  --tracking-tight: -0.025em;
  --tracking-normal: 0;
  --tracking-wide: 0.025em;
  --tracking-overline: 0.08em;

  /* --- weights (only 400/500/600 are vendored for any family) --- */
  --weight-normal: 400;
  --weight-medium: 500;
  --weight-semibold: 600;

  /* --- spacing scale (Tailwind default: n * 0.25rem) --- */
  --space-1: 0.25rem;
  --space-1-5: 0.375rem;
  --space-2: 0.5rem;
  --space-2-5: 0.625rem;
  --space-3: 0.75rem;
  --space-3-5: 0.875rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-7: 1.75rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-14: 3.5rem;
  --space-16: 4rem;
  --space-20: 5rem;
  --space-24: 6rem;

  /* --- radii --- */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;   /* buttons, inputs */
  --radius-lg: 1rem;
  --radius-xl: 1.25rem;  /* cards, dialogs */
  --radius-full: 9999px; /* pills/badges */

  /* --- shadows: warm neutral black at 4-10% opacity, two tinted "glow" shadows --- */
  --shadow-card: 0 1px 3px rgb(0 0 0 / 0.04);
  --shadow-card-hover: 0 4px 12px rgb(0 0 0 / 0.08);
  --shadow-dropdown: 0 4px 6px rgb(0 0 0 / 0.07);
  --shadow-dialog: 0 20px 25px rgb(0 0 0 / 0.1);
  --shadow-glow-cyan: 0 0 20px rgba(6, 147, 227, 0.15);
  --shadow-glow-mint: 0 0 18px rgba(0, 208, 132, 0.18);
  --shadow-header: 0 4px 12px rgba(30, 60, 83, 0.08);

  /* --- gradients (primary-action surfaces, not flat fills) --- */
  --gradient-action: linear-gradient(90deg, #1E3C53, #162D3F);
  --gradient-ai: linear-gradient(135deg, #0693E3, #9B51E0);
  --gradient-brand: linear-gradient(135deg, #1E3C53, #162D3F);
  --gradient-mint: linear-gradient(135deg, #00D084, #0693E3);

  /* --- motion --- */
  --duration-fast: 150ms;
  --duration-medium: 225ms;
  --duration-slow: 275ms;
  --ease-soft: cubic-bezier(0.48, 0, 0.48, 1);
  --ease-calm: cubic-bezier(0.08, 0, 0.35, 1);
  --ease-energetic: cubic-bezier(0.64, 0, 0.92, 1);

  /* --- layout --- */
  --container-max: 1346px;
  --container-gutter: var(--space-6);
}

/* Registers --angle as an animatable <angle> so the conic-gradient card
   border can be spun in §6. Unknown-at-rule-safe: a browser that doesn't
   understand `@property` simply ignores this whole block (per the CSS
   spec's handling of unrecognised at-rules) and falls straight through to
   the "no @property" degradation described in §6 — no breakage, just no
   spin. This is also the mechanism the design system itself relies on. */
@property --angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

/* -------------------------------------------------------------------------
   3. Reset & base elements
   ------------------------------------------------------------------------- */
* { box-sizing: border-box; }

html {
  scroll-behavior: smooth; /* well-supported; unsupported browsers just jump — harmless */
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: var(--leading-md);
  font-weight: var(--weight-normal);
  color: var(--color-text);
  background-color: var(--color-canvas);
  /* Warm, organic backdrop: two faint radial washes (cyan top-right, navy
     bottom-left) rather than a flat fill. `background-attachment: fixed`
     keeps them anchored to the viewport instead of scrolling with content;
     a few mobile engines (older iOS Safari) historically ignore `fixed`
     and treat it as `scroll` instead — the washes then move with the page,
     which is a purely cosmetic difference, not a functional regression. */
  background-image:
    radial-gradient(60rem 40rem at 85% -10%, rgba(6, 147, 227, 0.06), transparent 60%),
    radial-gradient(50rem 40rem at -10% 10%, rgba(30, 60, 83, 0.05), transparent 55%);
  background-attachment: fixed;
  /* overflow-x safety net: nothing on the page should ever need to scroll
     horizontally (wide content gets its own scroll region, see §10). */
  overflow-x: hidden;
}

h1, h2, h3 {
  font-family: var(--font-serif);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  margin: 0;
}

h1 { font-size: var(--text-xl); line-height: var(--leading-tight); }
h2 { font-size: var(--text-base); }

a {
  color: var(--color-cyan-600);
  text-decoration: none;
}
a:hover { text-decoration: underline; }

code, .mono {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

/* -------------------------------------------------------------------------
   4. Global motion, scrollbars, selection
   ------------------------------------------------------------------------- */

/* Respect the user's reduced-motion preference globally: every animation
   and transition on the page (the conic-spin, the breathing status dot,
   hover transitions) collapses to effectively-instant. Browsers that don't
   support the `prefers-reduced-motion` media feature never match this
   block, so they simply keep full motion by default — the correct
   degradation, since there is no way to know the user's preference. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

::selection { background: rgba(6, 147, 227, 0.18); color: #162D3F; }
::-moz-selection { background: rgba(6, 147, 227, 0.18); color: #162D3F; } /* older Firefox */

/* Warm custom scrollbars. `scrollbar-width`/`scrollbar-color` are the
   standard properties (Firefox, and current Chrome/Edge); the
   `::-webkit-scrollbar*` pseudo-elements cover Chrome/Safari/older Edge.
   A browser supporting neither just renders its native scrollbar —
   cosmetic-only fallback, no loss of function. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--color-border-strong) transparent;
}
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--color-border-strong); border-radius: 500px; }
*::-webkit-scrollbar-thumb:hover { background: var(--color-brand-500); }

/* -------------------------------------------------------------------------
   5. Page shell
   ------------------------------------------------------------------------- */
.site-header {
  background: var(--color-surface-card);
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-3-5) var(--space-6);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  box-shadow: var(--shadow-header);
  flex-wrap: wrap;
}
.site-header__brand { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.site-header__logo { display: block; border-radius: var(--radius-sm); flex-shrink: 0; }
.site-header__meta { color: var(--color-text-muted); font-size: var(--text-xs); }
.site-header__nav { display: flex; gap: var(--space-4); flex-wrap: wrap; align-items: center; }
.site-header__nav a { color: var(--color-cyan-600); }

/* Breadcrumb above the connector's title card. This is where you go back from a
   details page: sitting with the title, it names what you are inside of, which a
   bare arrow in the top bar does not. One back affordance per page -- a second at
   the foot of a tabbed page sits a different distance away depending on the open
   tab, which is what makes a back link feel unreliable. */
.breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
  min-width: 0;
}
.breadcrumb__back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  color: var(--color-cyan-600);
  font-weight: var(--weight-medium);
  text-decoration: none;
  white-space: nowrap;
}
.breadcrumb__back:hover, .breadcrumb__back:focus-visible { text-decoration: underline; }
.breadcrumb__sep { color: var(--color-border-strong); }
.breadcrumb__current {
  color: var(--color-text-muted);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The arrow nudges back on hover, in the direction the link takes you. */
.breadcrumb__arrow { transition: transform var(--duration-fast) var(--ease-soft); }
.breadcrumb__back:hover .breadcrumb__arrow { transform: translateX(-2px); }
@media (prefers-reduced-motion: reduce) {
  .breadcrumb__arrow { transition: none; }
  .breadcrumb__back:hover .breadcrumb__arrow { transform: none; }
}

main {
  max-width: var(--container-max);
  margin: var(--space-6) auto;
  padding: 0 var(--container-gutter);
}

/* -------------------------------------------------------------------------
   6. Cards, grid, and the signature conic-gradient hover border
   ------------------------------------------------------------------------- */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--space-5);
}

.card {
  position: relative;
  /* The whole card is a click target; see .card__title-link::after below. */
  cursor: pointer;
  isolation: isolate; /* new stacking context so the ::before ring can sit at z-index:-1 *within* the card */
  min-width: 0;
  background: var(--color-surface-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  box-shadow: var(--shadow-card);
  transition: transform var(--duration-medium) var(--ease-soft),
              box-shadow var(--duration-medium) var(--ease-soft),
              border-color var(--duration-medium) var(--ease-soft);
}
.card:hover, .card:focus-within {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card-hover);
  /* Plain, unconditional fallback affordance: a subtle border tint that
     works even where the masked ring below is unsupported. */
  border-color: var(--color-brand-500);
}

/* The signature element: an animated conic-gradient ring that fades in on
   hover/focus. Implementation: a full-bleed ::before whose background is
   the conic-gradient, cut down to a ring using CSS masking (both the
   unprefixed `mask-composite: exclude` and the legacy
   `-webkit-mask-composite: xor` forms are provided; every evergreen
   browser understands at least one). Gated behind `@supports` so that a
   browser with neither masking syntax never paints the unmasked, full-box
   gradient over the card — it just keeps the plain border-color fallback
   above instead. */
@supports (mask-composite: exclude) or (-webkit-mask-composite: xor) {
  .card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1.25px;
    background: conic-gradient(
      from var(--angle, 0deg),
      var(--color-brand-500),
      var(--color-cyan-500),
      var(--color-mint-500),
      var(--color-cyan-500),
      var(--color-brand-500)
    );
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    opacity: 0;
    z-index: -1;
    transition: opacity var(--duration-slow) var(--ease-calm);
  }
  .card:hover::before, .card:focus-within::before {
    opacity: 1;
    /* Animating --angle requires the @property registration above. Without
       it, this custom property isn't interpolatable, so the browser holds
       it at its initial value (0deg) for the whole animation — the ring
       still fades in, it just never rotates. That static ring *is* the
       required no-@property fallback; no extra CSS is needed to provide it. */
    animation: conic-spin 4s linear infinite;
  }
}
@keyframes conic-spin {
  to { --angle: 360deg; }
}
@media (prefers-reduced-motion: reduce) {
  .card:hover::before, .card:focus-within::before { animation: none; }
}

.card__title {
  overflow-wrap: anywhere;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-family: var(--font-sans); /* card titles are interface text, not editorial */
  font-weight: var(--weight-semibold);
}
/* The whole card is a click target for its details page, without nesting a
   button inside a link (invalid, and it would swallow the copy button's click).
   The title stays the one real anchor -- so a screen reader announces a link
   named after the connector, and keyboard focus lands somewhere meaningful --
   and its ::after is stretched over the card to catch the clicks. Anything in
   the card that must stay independently interactive or selectable is lifted
   above that overlay with position/z-index below. */
.card__title-link { color: inherit; text-decoration: none; cursor: pointer; }
.card__title-link:hover, .card__title-link:focus-visible { text-decoration: underline; }
.card__title-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  z-index: 1;
}
/* Above the overlay: the endpoint (people select and copy this text by hand)
   and its copy button, plus the docs link in the meta line. */
.card .url-chip,
.card .muted a { position: relative; z-index: 2; }

.card__counts {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}
/* Not a link any more -- the stretched title anchor above covers the card --
   but it must still read as the affordance that tells you the card is clickable. */
.card__details-link {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  color: var(--color-brand-500);
}
.card:hover .card__details-link,
.card:focus-within .card__details-link { text-decoration: underline; }

.desc { margin: 0; color: var(--color-text); }
.muted { color: var(--color-text-muted); font-size: var(--text-xs); }

/* -------------------------------------------------------------------------
   7. Status dot + badges
   ------------------------------------------------------------------------- */
.status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.status__dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--unknown);
  flex-shrink: 0;
}
/* Halo: a second, larger circle behind the dot, coloured via currentColor
   so each state only needs to set one colour. Only "up" turns it on. */
.status__dot::before {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0;
}
.status[data-state="up"] { color: var(--color-success-600); }
.status[data-state="up"] .status__dot { background: var(--up); color: var(--up); }
.status[data-state="up"] .status__dot::before {
  opacity: 0.55;
  animation: status-breathing 3s ease-in-out infinite;
}
.status[data-state="degraded"] .status__dot { background: var(--degraded); }
.status[data-state="sleeping"] .status__dot { background: var(--sleeping); }
.status[data-state="down"] { color: var(--color-danger-600); }
.status[data-state="down"] .status__dot { background: var(--down); }
.status[data-state="unknown"] .status__dot { background: var(--unknown); }

@keyframes status-breathing {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.55; transform: scale(0.82); }
}
@media (prefers-reduced-motion: reduce) {
  .status__dot::before { animation: none !important; opacity: 0.35; }
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: 1px var(--space-2-5);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  background: var(--color-surface-card);
  white-space: nowrap;
}

/* -------------------------------------------------------------------------
   8. Buttons
   ------------------------------------------------------------------------- */
.btn {
  font: inherit;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  cursor: pointer;
  transition: box-shadow var(--duration-fast) var(--ease-soft),
              filter var(--duration-fast) var(--ease-soft),
              background-color var(--duration-fast) var(--ease-soft),
              color var(--duration-fast) var(--ease-soft);
}
.btn:focus-visible {
  outline: 2px solid var(--color-ring);
  outline-offset: 2px;
}

.btn--copy {
  background: var(--color-surface-inset);
  border-color: var(--color-border);
  color: var(--color-text);
}
.btn--copy:hover { border-color: var(--color-brand-500); color: var(--color-brand-500); }

.btn--primary {
  background-image: var(--gradient-action);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-card);
}
.btn--primary:hover:not(:disabled) {
  box-shadow: var(--shadow-card-hover);
  filter: brightness(1.08);
}

/* Disabled must read as *disabled*, not merely dimmed: no gradient, no
   shadow, a flat neutral fill and a not-allowed cursor — visually a
   different control, not a faded version of the active one. */
.btn:disabled {
  background-image: none;
  background-color: var(--color-surface-inset);
  color: var(--color-text-muted);
  border-color: var(--color-border);
  box-shadow: none;
  filter: none;
  cursor: not-allowed;
}

/* -------------------------------------------------------------------------
   9. URL chip
   ------------------------------------------------------------------------- */
.url-chip {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  min-width: 0;
}
.url-chip__value {
  flex: 1;
  min-width: 0;
  font-size: var(--text-xs);
  background: var(--color-surface-inset);
  border: 1px solid var(--color-border);
  padding: var(--space-1-5) var(--space-2-5);
  border-radius: var(--radius-sm);
  /* Long URLs scroll *inside* the chip rather than widening the card or
     the page — a deliberate, contained horizontal scroll region. */
  overflow-x: auto;
  white-space: nowrap;
}

/* -------------------------------------------------------------------------
   10. Table (admin)
   ------------------------------------------------------------------------- */
.table-scroll {
  /* Wide cells (long image refs, revisions) scroll within this box instead
     of ever widening the page body — see the horizontal-scroll constraint
     in the brief. */
  overflow-x: auto;
}
table {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  background: var(--color-surface-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
th, td {
  text-align: left;
  padding: var(--space-2-5) var(--space-3-5);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
  vertical-align: top;
}
th {
  color: var(--color-text-muted);
  font-weight: var(--weight-semibold);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-overline);
}
tbody tr:hover { background: var(--color-surface-tertiary); }

/* -------------------------------------------------------------------------
   11. Notices + empty state
   ------------------------------------------------------------------------- */
.notice {
  background: var(--color-cyan-100);
  border: 1px solid var(--color-cyan-300);
  color: var(--color-brand-600);
  padding: var(--space-2-5) var(--space-3-5);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-4);
}
.notice.danger {
  background: var(--color-danger-100);
  border-color: var(--color-danger-300);
  color: var(--color-danger-600);
}

.empty-state {
  text-align: center;
  border: 1px dashed var(--color-border-strong);
  background: var(--color-surface-tertiary);
  border-radius: var(--radius-xl);
  padding: var(--space-16) var(--space-6);
}
.empty-state__title {
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  margin: 0 0 var(--space-2);
}
.empty-state__desc { color: var(--color-text-muted); margin: 0; }

/* -------------------------------------------------------------------------
   12. Misc: row notes
   ------------------------------------------------------------------------- */
.danger { color: var(--color-danger-600); }

/* A short, single-line, CSS-truncatable status line under a table cell:
   plain failure text (.danger alone), a calm "not deployed" line (no
   modifier), or the actionable missing-grant warning (.missing-grant),
   which gets a filled warning chip instead of a bare line so it doesn't
   read as decorative. */
.error-line {
  display: block;
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-xs);
  margin-top: var(--space-1);
}
.error-line.missing-grant {
  display: inline-block;
  color: var(--color-warning-600);
  background: var(--color-warning-100);
  border-radius: var(--radius-sm);
  padding: 1px var(--space-2);
  font-weight: var(--weight-medium);
}

/* -------------------------------------------------------------------------
   13. Responsive
   ------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .site-header { padding: var(--space-3) var(--space-4); }
  main { margin: var(--space-4) auto; padding: 0 var(--space-4); }
  .grid { grid-template-columns: 1fr; }
}

/* -------------------------------------------------------------------------
   14. Details page (panels, entry lists, key/value list, tabs)
   ------------------------------------------------------------------------- */
.detail {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  max-width: 860px;
}
.detail__header { display: flex; flex-direction: column; gap: var(--space-3); }
.detail__heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.detail__title {
  overflow-wrap: anywhere;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-family: var(--font-sans); /* interface text, matches .card__title */
  font-weight: var(--weight-semibold);
}
.detail__badges { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.detail__footer-links { display: flex; gap: var(--space-2); }

/* A static surface with the same card treatment (surface, border, radius,
   shadow) as `.card`, but without the grid card's hover lift/conic ring --
   those are tuned for a small tile you scan in a grid, not a full-width
   content section on a single-connector page. */
.panel {
  background: var(--color-surface-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  padding: var(--space-5);
  box-shadow: var(--shadow-card);
}
.panel h2 { margin: 0 0 var(--space-3); }

/* Operational detail is deliberately the visually quietest panel on the
   page: flat surface, no shadow, an uppercase muted eyebrow heading. */
.panel--secondary {
  background: var(--color-surface-tertiary);
  box-shadow: none;
}
.panel--secondary h2 {
  font-family: var(--font-sans);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-overline);
  color: var(--color-text-muted);
  font-weight: var(--weight-semibold);
}

/* A scannable list for tools/prompts/resources/resource templates: a plain
   row per entry rather than a wall of text, still legible at 25+ rows. */
.entry-list { list-style: none; margin: var(--space-3) 0 0; padding: 0; }
.entry-list__item {
  padding: var(--space-2-5) 0;
  border-bottom: 1px solid var(--color-border-subtle);
}
.entry-list__item:last-child { border-bottom: none; }
.entry-list__head { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.entry-list__head code { font-size: var(--text-sm); }
.entry-list__desc { margin: var(--space-1) 0 0; color: var(--color-text-muted); font-size: var(--text-xs); }
.entry-list__empty { color: var(--color-text-muted); margin: var(--space-3) 0 0; }

/* Key/value rows for the operational-detail panel: a two-column grid with
   `display: contents` rows so `<dt>`/`<dd>` line up as real grid columns
   even though each pair is wrapped in its own `<div>` (valid dl markup). */
.kv-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-1-5) var(--space-4);
  margin: var(--space-3) 0 0;
}
.kv-list__row { display: contents; }
.kv-list dt { color: var(--color-text-muted); font-size: var(--text-xs); font-weight: var(--weight-medium); }
.kv-list dd { margin: 0; font-size: var(--text-xs); overflow-wrap: anywhere; }

/* Client-configuration tabs: pure CSS via the radio + `:checked ~` sibling
   pattern, so they work with JavaScript disabled and under a CSP with no
   `unsafe-inline` (no inline `<script>`/`style=` is used anywhere here).
   The radio inputs stay in the tab order and keep native left/right
   arrow-key behaviour (a browser's built-in radio-group navigation); only
   their visual box is hidden, via an off-screen size rather than
   `display:none`, so they remain focusable. */
.tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.tabs__radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.tabs__tab {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-inset);
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  cursor: pointer;
  user-select: none;
}
.tabs__radio:checked + .tabs__tab {
  background: var(--color-brand-500);
  color: var(--color-text-inverse);
  border-color: var(--color-brand-500);
}
.tabs__radio:focus-visible + .tabs__tab {
  outline: 2px solid var(--color-ring);
  outline-offset: 2px;
}
.tabs__panels { flex: 1 0 100%; margin-top: var(--space-3); }
.tabs__panel { display: none; }
#tab-claude:checked ~ .tabs__panels #panel-claude,
#tab-vscode:checked ~ .tabs__panels #panel-vscode,
#tab-url:checked ~ .tabs__panels #panel-url {
  display: block;
}

/* The copy-paste config block: a wrapped, contained code block (never the
   page body) with its copy button overlaid rather than pushing width. */
.config-block { position: relative; }
.config-block__code {
  margin: 0;
  padding: var(--space-3);
  padding-right: var(--space-10);
  background: var(--color-surface-inset);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--text-xs);
  white-space: pre-wrap;
  word-break: break-word;
}
.config-block .btn--copy { position: absolute; top: var(--space-2); right: var(--space-2); }

/* Page-level tabs (Details & config / Tools / Prompts / Resources): the
   "underlined" style, built with the same radio + `:checked ~` sibling
   pattern as the client-configuration tabs above, under a distinct radio
   `name` ("page-tab" vs "config-tab") so the two groups never interfere.
   The radios are direct children of `.detail-tabs`, siblings of both
   `.detail-tabs__row` (the visible label row) and `.detail-tabs__panels` --
   each `<label for>` still targets its radio across that gap, so the row
   can carry its own border/scroll behaviour without breaking the sibling
   selectors below. As with `.tabs__radio`, the inputs stay focusable (an
   off-screen size, not `display:none`) and keep native left/right
   arrow-key group navigation for free. */
.detail-tabs { display: flex; flex-direction: column; }
.detail-tabs__radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.detail-tabs__row {
  display: flex;
  gap: var(--space-5);
  border-bottom: 1px solid var(--color-border);
  overflow-x: auto;
  overflow-y: hidden;
}
.detail-tabs__tab {
  flex: none;
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) var(--space-1) var(--space-3);
  margin-bottom: -1px; /* the label's own border sits on top of the row's 1px rule */
  border-bottom: 2px solid transparent;
  color: var(--color-text-muted);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}
#page-tab-details:checked ~ .detail-tabs__row label[for="page-tab-details"],
#page-tab-tools:checked ~ .detail-tabs__row label[for="page-tab-tools"],
#page-tab-prompts:checked ~ .detail-tabs__row label[for="page-tab-prompts"],
#page-tab-resources:checked ~ .detail-tabs__row label[for="page-tab-resources"] {
  color: var(--color-text);
  font-weight: var(--weight-semibold);
  border-bottom-color: var(--color-brand-500);
}
#page-tab-details:focus-visible ~ .detail-tabs__row label[for="page-tab-details"],
#page-tab-tools:focus-visible ~ .detail-tabs__row label[for="page-tab-tools"],
#page-tab-prompts:focus-visible ~ .detail-tabs__row label[for="page-tab-prompts"],
#page-tab-resources:focus-visible ~ .detail-tabs__row label[for="page-tab-resources"] {
  /* Inset (negative offset), unlike the client-config tabs' outward ring: `.detail-tabs__row`
     has `overflow-x: auto`, which per spec forces its `overflow-y` to compute away from
     `visible` too, so an outward ring here would have its top/bottom sliced off by that
     clipping. A ring drawn inward never exceeds the label's own box, so it can't be clipped. */
  outline: 2px solid var(--color-ring);
  outline-offset: -2px;
}
.detail-tabs__panels { margin-top: var(--space-5); }
.detail-tabs__panel { display: none; flex-direction: column; gap: var(--space-5); }
#page-tab-details:checked ~ .detail-tabs__panels #panel-page-details,
#page-tab-tools:checked ~ .detail-tabs__panels #panel-page-tools,
#page-tab-prompts:checked ~ .detail-tabs__panels #panel-page-prompts,
#page-tab-resources:checked ~ .detail-tabs__panels #panel-page-resources {
  display: flex;
}

/* -------------------------------------------------------------------------
   15. Configuration status (admin) and the card's "configuration missing"
   ------------------------------------------------------------------------- */
.badge--warn {
  color: var(--color-warning-600);
  background: var(--color-warning-100);
  border-color: var(--color-warning-300);
}
.badge--danger {
  color: var(--color-danger-600);
  background: var(--color-danger-100);
  border-color: var(--color-danger-300);
}
.badge--ok { color: var(--color-text); }
.config-link { text-decoration: none; }
/* The configuration table has four or five columns: use the page's full width. */
.detail--wide { max-width: none; }

/* `.env.example` help keeps its own line breaks (a generate-a-key command, say). */
.config-help {
  white-space: pre-line;
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  max-width: 320px;
}
.config-state { font-weight: var(--weight-semibold); }
.config-state[data-state="set"] { color: var(--color-text); }
.config-state[data-state="missing"] { color: var(--color-danger-600); }
.config-state[data-state="automatic"],
.config-state[data-state="unknown"] { color: var(--color-text-muted); font-weight: var(--weight-medium); }
.config-form { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; width: 16rem; }
.config-form__input {
  font: inherit;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  flex: 1 1 100%;
  min-width: 0;
  box-sizing: border-box;
  padding: var(--space-1-5) var(--space-2-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-inset);
  color: var(--color-text);
}
.config-form__input:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 1px; }
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
