/* ══════════════════════════════════════════════════════════════════════════
   CareraManage — Material Design 3
   ──────────────────────────────────────────────────────────────────────────
   Layered as: tokens → base → components → app-specific → utilities.

   Tokens follow the M3 naming (`--md-sys-*`).  Never hard-code a colour in a
   component or a template: pick the role token that describes the job
   (`--md-sys-color-primary`, `--md-sys-color-surface-container`, …).  Roles
   are what makes light/dark themes work for free — the dark block at the top
   is the *only* place the palette flips.

   Theme resolution: OS preference by default, overridable per-document with
   `<html data-theme="dark">` / `data-theme="light"`.
   ══════════════════════════════════════════════════════════════════════════ */

/* ═══ 1. Design tokens ════════════════════════════════════════════════════ */

:root {
  /* ── Colour roles — light scheme (seed #0061A4) ── */
  --md-sys-color-primary: #0061a4;
  --md-sys-color-on-primary: #ffffff;
  --md-sys-color-primary-container: #d1e4ff;
  --md-sys-color-on-primary-container: #001d36;

  --md-sys-color-secondary: #535f70;
  --md-sys-color-on-secondary: #ffffff;
  --md-sys-color-secondary-container: #d7e3f7;
  --md-sys-color-on-secondary-container: #101c2b;

  --md-sys-color-tertiary: #6b5778;
  --md-sys-color-on-tertiary: #ffffff;
  --md-sys-color-tertiary-container: #f2daff;
  --md-sys-color-on-tertiary-container: #251431;

  --md-sys-color-error: #ba1a1a;
  --md-sys-color-on-error: #ffffff;
  --md-sys-color-error-container: #ffdad6;
  --md-sys-color-on-error-container: #410002;

  /* Non-standard but needed for "committed / added / passing" states.
     Mirrors the M3 role shape so it themes like the rest. */
  --md-sys-color-success: #1b6c2f;
  --md-sys-color-on-success: #ffffff;
  --md-sys-color-success-container: #a5f6ac;
  --md-sys-color-on-success-container: #00210a;

  /* Warning: a caution that is not an error. A truncated result and a metric
     computed from a subset of values are both correct answers that would be
     misread as complete — amber says "read the caveat", where error would say
     "this failed" and nothing at all would say nothing. */
  --md-sys-color-warning: #8a5000;
  --md-sys-color-on-warning: #ffffff;
  --md-sys-color-warning-container: #ffdcbe;
  --md-sys-color-on-warning-container: #2c1600;

  --md-sys-color-background: #fdfcff;
  --md-sys-color-on-background: #1a1c1e;
  --md-sys-color-surface: #fdfcff;
  --md-sys-color-on-surface: #1a1c1e;
  --md-sys-color-surface-variant: #dfe2eb;
  --md-sys-color-on-surface-variant: #43474e;

  --md-sys-color-surface-container-lowest: #ffffff;
  --md-sys-color-surface-container-low: #f7f9fc;
  --md-sys-color-surface-container: #f1f4f9;
  --md-sys-color-surface-container-high: #ebeef3;
  --md-sys-color-surface-container-highest: #e5e8ee;

  --md-sys-color-outline: #73777f;
  --md-sys-color-outline-variant: #c3c7cf;

  --md-sys-color-inverse-surface: #2f3033;
  --md-sys-color-inverse-on-surface: #f1f0f4;
  --md-sys-color-inverse-primary: #9ecaff;

  --md-sys-color-shadow: #000000;
  --md-sys-color-scrim: #000000;

  /* Shadow tint — dark themes need denser shadows to read at all. */
  --md-sys-shadow-umbra: 0, 0, 0;

  /* ── Typography ── */
  --md-sys-typescale-font: Roboto, "Helvetica Neue", -apple-system,
    BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --md-sys-typescale-font-mono: "Roboto Mono", ui-monospace, SFMono-Regular,
    "SF Mono", Menlo, Consolas, monospace;

  /* ── Shape scale ── */
  --md-sys-shape-corner-none: 0;
  --md-sys-shape-corner-xs: 4px;
  --md-sys-shape-corner-s: 8px;
  --md-sys-shape-corner-m: 12px;
  --md-sys-shape-corner-l: 16px;
  --md-sys-shape-corner-xl: 28px;
  --md-sys-shape-corner-full: 9999px;

  /* ── Elevation ── */
  --md-sys-elevation-0: none;
  --md-sys-elevation-1: 0 1px 2px 0 rgba(var(--md-sys-shadow-umbra), .30),
                        0 1px 3px 1px rgba(var(--md-sys-shadow-umbra), .15);
  --md-sys-elevation-2: 0 1px 2px 0 rgba(var(--md-sys-shadow-umbra), .30),
                        0 2px 6px 2px rgba(var(--md-sys-shadow-umbra), .15);
  --md-sys-elevation-3: 0 1px 3px 0 rgba(var(--md-sys-shadow-umbra), .30),
                        0 4px 8px 3px rgba(var(--md-sys-shadow-umbra), .15);
  --md-sys-elevation-4: 0 2px 3px 0 rgba(var(--md-sys-shadow-umbra), .30),
                        0 6px 10px 4px rgba(var(--md-sys-shadow-umbra), .15);
  --md-sys-elevation-5: 0 4px 4px 0 rgba(var(--md-sys-shadow-umbra), .30),
                        0 8px 12px 6px rgba(var(--md-sys-shadow-umbra), .15);

  /* ── State layer opacities ── */
  --md-sys-state-hover-opacity: 0.08;
  --md-sys-state-focus-opacity: 0.10;
  --md-sys-state-pressed-opacity: 0.10;
  --md-sys-state-dragged-opacity: 0.16;
  --md-sys-state-disabled-content-opacity: 0.38;
  --md-sys-state-disabled-container-opacity: 0.12;

  /* ── Motion ── */
  --md-sys-motion-easing-standard: cubic-bezier(0.2, 0, 0, 1);
  --md-sys-motion-easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
  --md-sys-motion-easing-decelerate: cubic-bezier(0, 0, 0, 1);
  --md-sys-motion-easing-accelerate: cubic-bezier(0.3, 0, 1, 1);
  --md-sys-motion-duration-short: 100ms;
  --md-sys-motion-duration-medium: 200ms;
  --md-sys-motion-duration-long: 400ms;

  color-scheme: light;
}

/* ── Colour roles — dark scheme ── */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --md-sys-color-primary: #9ecaff;
    --md-sys-color-on-primary: #003258;
    --md-sys-color-primary-container: #00497d;
    --md-sys-color-on-primary-container: #d1e4ff;

    --md-sys-color-secondary: #bbc7db;
    --md-sys-color-on-secondary: #253140;
    --md-sys-color-secondary-container: #3b4858;
    --md-sys-color-on-secondary-container: #d7e3f7;

    --md-sys-color-tertiary: #d6bee4;
    --md-sys-color-on-tertiary: #3b2948;
    --md-sys-color-tertiary-container: #523f5f;
    --md-sys-color-on-tertiary-container: #f2daff;

    --md-sys-color-error: #ffb4ab;
    --md-sys-color-on-error: #690005;
    --md-sys-color-error-container: #93000a;
    --md-sys-color-on-error-container: #ffdad6;

    --md-sys-color-success: #8ad995;
    --md-sys-color-on-success: #003912;
    --md-sys-color-success-container: #00531e;
    --md-sys-color-on-success-container: #a5f6ac;

    --md-sys-color-warning: #ffb870;
    --md-sys-color-on-warning: #4a2700;
    --md-sys-color-warning-container: #693c00;
    --md-sys-color-on-warning-container: #ffdcbe;

    --md-sys-color-background: #111416;
    --md-sys-color-on-background: #e2e2e6;
    --md-sys-color-surface: #111416;
    --md-sys-color-on-surface: #e2e2e6;
    --md-sys-color-surface-variant: #43474e;
    --md-sys-color-on-surface-variant: #c3c7cf;

    --md-sys-color-surface-container-lowest: #0c0e11;
    --md-sys-color-surface-container-low: #1a1c1e;
    --md-sys-color-surface-container: #1e2022;
    --md-sys-color-surface-container-high: #282a2d;
    --md-sys-color-surface-container-highest: #333537;

    --md-sys-color-outline: #8d9199;
    --md-sys-color-outline-variant: #43474e;

    --md-sys-color-inverse-surface: #e2e2e6;
    --md-sys-color-inverse-on-surface: #2f3033;
    --md-sys-color-inverse-primary: #0061a4;

    color-scheme: dark;
  }
}

/* Explicit opt-in overrides the OS preference in both directions. */
:root[data-theme="dark"] {
  --md-sys-color-primary: #9ecaff;
  --md-sys-color-on-primary: #003258;
  --md-sys-color-primary-container: #00497d;
  --md-sys-color-on-primary-container: #d1e4ff;

  --md-sys-color-secondary: #bbc7db;
  --md-sys-color-on-secondary: #253140;
  --md-sys-color-secondary-container: #3b4858;
  --md-sys-color-on-secondary-container: #d7e3f7;

  --md-sys-color-tertiary: #d6bee4;
  --md-sys-color-on-tertiary: #3b2948;
  --md-sys-color-tertiary-container: #523f5f;
  --md-sys-color-on-tertiary-container: #f2daff;

  --md-sys-color-error: #ffb4ab;
  --md-sys-color-on-error: #690005;
  --md-sys-color-error-container: #93000a;
  --md-sys-color-on-error-container: #ffdad6;

  --md-sys-color-success: #8ad995;
  --md-sys-color-on-success: #003912;
  --md-sys-color-success-container: #00531e;
  --md-sys-color-on-success-container: #a5f6ac;

  --md-sys-color-warning: #ffb870;
  --md-sys-color-on-warning: #4a2700;
  --md-sys-color-warning-container: #693c00;
  --md-sys-color-on-warning-container: #ffdcbe;

  --md-sys-color-background: #111416;
  --md-sys-color-on-background: #e2e2e6;
  --md-sys-color-surface: #111416;
  --md-sys-color-on-surface: #e2e2e6;
  --md-sys-color-surface-variant: #43474e;
  --md-sys-color-on-surface-variant: #c3c7cf;

  --md-sys-color-surface-container-lowest: #0c0e11;
  --md-sys-color-surface-container-low: #1a1c1e;
  --md-sys-color-surface-container: #1e2022;
  --md-sys-color-surface-container-high: #282a2d;
  --md-sys-color-surface-container-highest: #333537;

  --md-sys-color-outline: #8d9199;
  --md-sys-color-outline-variant: #43474e;

  --md-sys-color-inverse-surface: #e2e2e6;
  --md-sys-color-inverse-on-surface: #2f3033;
  --md-sys-color-inverse-primary: #0061a4;

  color-scheme: dark;
}

/* ═══ 2. Base ═════════════════════════════════════════════════════════════ */

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

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

body {
  margin: 0;
  background: var(--md-sys-color-background);
  color: var(--md-sys-color-on-background);
  font-family: var(--md-sys-typescale-font);
  /* body-medium */
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a {
  color: var(--md-sys-color-primary);
  text-decoration: none;
}
a:hover { text-decoration: underline; }

/* M3 focus indicator: a 3dp ring offset from the component. */
:focus-visible {
  outline: 3px solid var(--md-sys-color-primary);
  outline-offset: 2px;
  border-radius: var(--md-sys-shape-corner-xs);
}

code, .mono {
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 12px;
  letter-spacing: 0;
}

hr {
  border: none;
  border-top: 1px solid var(--md-sys-color-outline-variant);
  margin: 16px 0;
}

::selection {
  background: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container);
}

/* ── Type scale ──
   Use these classes rather than restyling headings ad hoc. */
.md-display-large  { font-size: 57px; line-height: 64px; font-weight: 400; letter-spacing: -0.25px; }
.md-display-medium { font-size: 45px; line-height: 52px; font-weight: 400; letter-spacing: 0; }
.md-display-small  { font-size: 36px; line-height: 44px; font-weight: 400; letter-spacing: 0; }

.md-headline-large  { font-size: 32px; line-height: 40px; font-weight: 400; letter-spacing: 0; }
.md-headline-medium { font-size: 28px; line-height: 36px; font-weight: 400; letter-spacing: 0; }
.md-headline-small  { font-size: 24px; line-height: 32px; font-weight: 400; letter-spacing: 0; }

.md-title-large  { font-size: 22px; line-height: 28px; font-weight: 400; letter-spacing: 0; }
.md-title-medium { font-size: 16px; line-height: 24px; font-weight: 500; letter-spacing: 0.15px; }
.md-title-small  { font-size: 14px; line-height: 20px; font-weight: 500; letter-spacing: 0.1px; }

.md-body-large  { font-size: 16px; line-height: 24px; font-weight: 400; letter-spacing: 0.5px; }
.md-body-medium { font-size: 14px; line-height: 20px; font-weight: 400; letter-spacing: 0.25px; }
.md-body-small  { font-size: 12px; line-height: 16px; font-weight: 400; letter-spacing: 0.4px; }

.md-label-large  { font-size: 14px; line-height: 20px; font-weight: 500; letter-spacing: 0.1px; }
.md-label-medium { font-size: 12px; line-height: 16px; font-weight: 500; letter-spacing: 0.5px; }
.md-label-small  { font-size: 11px; line-height: 16px; font-weight: 500; letter-spacing: 0.5px; }

h1 { font-size: 28px; line-height: 36px; font-weight: 400; letter-spacing: 0; margin: 0 0 8px; }
h2 { font-size: 22px; line-height: 28px; font-weight: 400; letter-spacing: 0; margin: 32px 0 12px; }
h3 { font-size: 16px; line-height: 24px; font-weight: 500; letter-spacing: 0.15px; margin: 0 0 8px; }

/* ── State layer ──
   Every interactive M3 surface carries a translucent overlay of its own
   content colour.  `isolation` + `z-index:-1` paints it above the component
   background but beneath the label, so text keeps full contrast.
   `.badge` is deliberately absent: it is a static label, not a target. */
.md-state-layer,
.btn, .chip, .list-item, .account-action, .avatar-btn,
.folder-summary, .file-entry, .nav-item, .icon-btn, .wb-nav-item, .item-tab {
  position: relative;
  isolation: isolate;
}
.md-state-layer::after,
.btn::after, .chip::after, .list-item::after,
.account-action::after, .avatar-btn::after, .folder-summary::after,
.file-entry::after, .nav-item::after, .icon-btn::after,
.wb-nav-item::after, .item-tab::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: currentColor;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--md-sys-motion-duration-short)
              var(--md-sys-motion-easing-standard);
}
.md-state-layer:hover::after,
.btn:hover::after, .chip:hover::after, .list-item:hover::after,
.account-action:hover::after, .avatar-btn:hover::after,
.folder-summary:hover::after, .file-entry:hover::after,
.nav-item:hover::after, .icon-btn:hover::after,
.wb-nav-item:hover::after, .item-tab:hover::after {
  opacity: var(--md-sys-state-hover-opacity);
}
.md-state-layer:active::after,
.btn:active::after, .chip:active::after, .list-item:active::after,
.account-action:active::after, .avatar-btn:active::after,
.folder-summary:active::after, .file-entry:active::after,
.nav-item:active::after, .icon-btn:active::after,
.wb-nav-item:active::after {
  opacity: var(--md-sys-state-pressed-opacity);
}

/* ── Ripple (driven by m3.js) ── */
.md-ripple {
  position: absolute;
  border-radius: 50%;
  transform: scale(0);
  background: currentColor;
  opacity: var(--md-sys-state-pressed-opacity);
  pointer-events: none;
  z-index: -1;
  animation: md-ripple-expand var(--md-sys-motion-duration-long)
             var(--md-sys-motion-easing-decelerate) forwards;
}
@keyframes md-ripple-expand {
  to { transform: scale(1); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ═══ 3. Components ═══════════════════════════════════════════════════════ */

/* ── Top app bar ──
   M3 small top app bar: 64dp, surface at rest, surface-container when the
   page scrolls under it (m3.js toggles `.scrolled`). */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 64px;
  padding: 0 16px;
  background: var(--md-sys-color-surface);
  color: var(--md-sys-color-on-surface);
  transition: background var(--md-sys-motion-duration-medium)
                var(--md-sys-motion-easing-standard),
              box-shadow var(--md-sys-motion-duration-medium)
                var(--md-sys-motion-easing-standard);
}
.nav.scrolled {
  background: var(--md-sys-color-surface-container);
  box-shadow: var(--md-sys-elevation-2);
}
.nav-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--md-sys-color-on-surface);
  text-decoration: none;
  margin-right: auto;
  padding: 4px 8px;
  border-radius: var(--md-sys-shape-corner-s);
}
.nav-brand:hover { text-decoration: none; }
.nav-brand .brand-mark { width: 40px; height: 40px; }
/* The app bar's global destinations. They sit between the brand (which takes
   the leading space) and the account switcher. */
.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
}
.nav-user {
  color: var(--md-sys-color-on-surface-variant);
  font-size: 14px;
  letter-spacing: 0.25px;
}
.logout-form { display: inline; }

/* ── Icon button (40dp target, full round) ── */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 8px;
  border: none;
  border-radius: var(--md-sys-shape-corner-full);
  background: transparent;
  color: var(--md-sys-color-on-surface-variant);
  cursor: pointer;
  flex-shrink: 0;
}

/* A 32dp icon button, for the controls that sit inside a card header or a
   builder row. Below the 48dp target, and deliberately: these are secondary
   controls on a surface whose primary controls are full size, and a row of
   40dp buttons in a 34dp-tall header would not fit. */
.icon-btn-sm { width: 32px; height: 32px; padding: 6px; }

/* ── Main layout ── */
.main {
  max-width: 1080px;
  margin: 0 auto;
  padding: 24px 24px 96px;
}
/* Pages whose content is a workspace rather than a document — the three-pane
   file workbench — need the width a reading measure deliberately gives up. */
.main-wide { max-width: 1600px; }

/* ── Page header ── */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 24px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.page-header h1 {
  margin: 0;
  /* headline-medium */
  font-size: 28px;
  line-height: 36px;
  font-weight: 400;
  letter-spacing: 0;
}

/* ── Breadcrumb ── */
.breadcrumb {
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
  color: var(--md-sys-color-on-surface-variant);
  margin-bottom: 16px;
}
.breadcrumb a { color: var(--md-sys-color-primary); }
.breadcrumb span {
  color: var(--md-sys-color-on-surface);
  font-weight: 500;
}

/* ── Buttons ──
   Default `.btn` is an M3 outlined button.  Variants:
     .btn-primary  filled
     .btn-tonal    filled tonal
     .btn-ghost    text
     .btn-elevated elevated
     .btn-danger   filled, error role
   Height 40dp, full corner radius, label-large. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 40px;
  padding: 0 24px;
  /* label-large */
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;
  letter-spacing: 0.1px;
  font-family: inherit;
  border: 1px solid var(--md-sys-color-outline);
  border-radius: var(--md-sys-shape-corner-full);
  background: transparent;
  color: var(--md-sys-color-primary);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  user-select: none;
  transition: box-shadow var(--md-sys-motion-duration-short)
              var(--md-sys-motion-easing-standard);
}
.btn:hover { text-decoration: none; }
.btn:disabled,
.btn[aria-disabled="true"] {
  cursor: default;
  pointer-events: none;
  color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);
  border-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);
  background: transparent;
}

.btn-primary {
  background: var(--md-sys-color-primary);
  border-color: transparent;
  color: var(--md-sys-color-on-primary);
}
.btn-primary:hover { box-shadow: var(--md-sys-elevation-1); }

.btn-tonal {
  background: var(--md-sys-color-secondary-container);
  border-color: transparent;
  color: var(--md-sys-color-on-secondary-container);
}
.btn-tonal:hover { box-shadow: var(--md-sys-elevation-1); }

.btn-elevated {
  background: var(--md-sys-color-surface-container-low);
  border-color: transparent;
  color: var(--md-sys-color-primary);
  box-shadow: var(--md-sys-elevation-1);
}
.btn-elevated:hover { box-shadow: var(--md-sys-elevation-2); }

.btn-ghost,
.btn-text {
  background: transparent;
  border-color: transparent;
  color: var(--md-sys-color-primary);
  padding: 0 12px;
}

.btn-danger {
  background: var(--md-sys-color-error);
  border-color: transparent;
  color: var(--md-sys-color-on-error);
}

.btn-full { width: 100%; }
.btn-sm { height: 32px; padding: 0 16px; font-size: 13px; }
.btn-sm.btn-ghost, .btn-sm.btn-text { padding: 0 8px; }
/* Buttons with a leading icon get a tighter leading edge, per spec. */
.btn > svg { flex-shrink: 0; }
.btn > svg:first-child:not(:only-child) { margin-left: -8px; }

/* ── FAB ── */
.fab {
  position: fixed;
  right: 24px;
  bottom: 24px;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  height: 56px;
  padding: 0 20px;
  border: none;
  border-radius: var(--md-sys-shape-corner-l);
  background: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container);
  box-shadow: var(--md-sys-elevation-3);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.1px;
  cursor: pointer;
  z-index: 90;
}
.fab:hover { box-shadow: var(--md-sys-elevation-4); text-decoration: none; }

.link-muted { color: var(--md-sys-color-on-surface-variant); }
.link-muted:hover { color: var(--md-sys-color-primary); }

/* ── Cards ──
   .card         filled  (surface-container-highest, no shadow)
   .card-elevated elevated (surface-container-low + elevation 1)
   .card-outlined outlined (surface + 1dp outline) */
.card,
.card-elevated,
.card-outlined {
  border-radius: var(--md-sys-shape-corner-m);
  overflow: hidden;
}
.card {
  background: var(--md-sys-color-surface-container-highest);
  color: var(--md-sys-color-on-surface);
}
.card-elevated {
  background: var(--md-sys-color-surface-container-low);
  color: var(--md-sys-color-on-surface);
  box-shadow: var(--md-sys-elevation-1);
}
.card-outlined {
  background: var(--md-sys-color-surface);
  color: var(--md-sys-color-on-surface);
  border: 1px solid var(--md-sys-color-outline-variant);
}
.card-body { padding: 16px; }

/* ── Auth pages ── */
.auth-body {
  background: var(--md-sys-color-surface-container-low);
  min-height: 100vh;
}
.auth-wrap {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 32px 16px;
}
.auth-logo {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 28px;
}
/* The Carrera3D mark is a self-contained app-icon tile (white rounded
   square, baked-in shadow/gradient) rather than a currentColor stroke, so
   it stays legible on its own regardless of theme. The lockup below it
   names the suite above the app. */
.brand-mark {
  flex-shrink: 0;
}
.brand-lockup {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}
.brand-suite {
  /* label-small, letterspaced as a suite eyebrow */
  font-size: 11px;
  line-height: 16px;
  font-weight: 500;
  letter-spacing: 1.5px;
  color: var(--md-sys-color-on-surface-variant);
}
.brand-app {
  /* title-large */
  font-size: 22px;
  line-height: 28px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--md-sys-color-on-surface);
}
.auth-logo .brand-mark { width: 72px; height: 72px; }
.auth-logo .brand-app {
  /* headline-small */
  font-size: 24px;
  line-height: 32px;
  font-weight: 400;
}
.auth-card {
  width: 100%;
  max-width: 400px;
  background: var(--md-sys-color-surface-container-lowest);
  border-radius: var(--md-sys-shape-corner-xl);
  padding: 32px;
  box-shadow: var(--md-sys-elevation-1);
}
.auth-title {
  margin: 0 0 24px;
  /* headline-small */
  font-size: 24px;
  line-height: 32px;
  font-weight: 400;
  text-align: center;
  color: var(--md-sys-color-on-surface);
}
.auth-footer {
  margin: 24px 0 0;
  text-align: center;
  font-size: 14px;
  letter-spacing: 0.25px;
  color: var(--md-sys-color-on-surface-variant);
}

/* ── Text fields (M3 outlined) ──
   The templates use a visible `.form-label` above the field rather than a
   floating label; the outline geometry still follows the outlined-field spec. */
.form { display: flex; flex-direction: column; }
.form-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 20px;
}
.form-label {
  /* body-small, on-surface-variant — matches the M3 resting label */
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--md-sys-color-on-surface-variant);
}
/* A label with a trailing affordance — "Password / Forgot password?". The
   link sits on the label baseline rather than below the field so it is found
   at the moment the user realises they can't remember it. */
.form-label-row {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.form-label-link {
  font-weight: 500;
  color: var(--md-sys-color-primary);
}
.form-input,
.select {
  height: 48px;
  padding: 0 16px;
  font-size: 16px;
  line-height: 24px;
  letter-spacing: 0.5px;
  font-family: inherit;
  border: 1px solid var(--md-sys-color-outline);
  border-radius: var(--md-sys-shape-corner-xs);
  background: transparent;
  color: var(--md-sys-color-on-surface);
  transition: border-color var(--md-sys-motion-duration-short)
                var(--md-sys-motion-easing-standard),
              box-shadow var(--md-sys-motion-duration-short)
                var(--md-sys-motion-easing-standard);
}
.form-input::placeholder { color: var(--md-sys-color-on-surface-variant); }
.form-input:hover,
.select:hover { border-color: var(--md-sys-color-on-surface); }
.form-input:focus,
.select:focus {
  outline: none;
  /* M3 focused outline is 2dp; emulate with an inset ring so the box
     geometry (and therefore the layout) does not shift. */
  border-color: var(--md-sys-color-primary);
  box-shadow: inset 0 0 0 1px var(--md-sys-color-primary);
}
.form-input:disabled,
.select:disabled {
  color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);
  border-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);
  cursor: not-allowed;
}
.form-input[aria-invalid="true"] { border-color: var(--md-sys-color-error); }
.form-textarea {
  height: auto;
  min-height: 112px;
  padding: 12px 16px;
  resize: vertical;
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 13px;
  line-height: 20px;
  letter-spacing: 0;
}
.form-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
}
.form-hint {
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  color: var(--md-sys-color-on-surface-variant);
  padding: 0 16px;
}

/* ── Select ── */
.select {
  appearance: none;
  padding-right: 40px;
  cursor: pointer;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='%2373777f' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 20px;
}
.select-sm { height: 40px; font-size: 14px; padding: 0 36px 0 12px; }

/* ── Divider ── */
.divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 24px 0;
  color: var(--md-sys-color-on-surface-variant);
  font-size: 12px;
  letter-spacing: 0.5px;
}
.divider::before, .divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--md-sys-color-outline-variant);
}

/* ── OAuth buttons (outlined buttons, full width) ── */
.oauth-buttons { display: flex; flex-direction: column; gap: 12px; }
.btn-oauth {
  width: 100%;
  background: transparent;
  border: 1px solid var(--md-sys-color-outline);
  color: var(--md-sys-color-on-surface);
}
.btn-oauth svg { flex-shrink: 0; }

/* ── Alerts / banners ── */
.alert {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px;
  border-radius: var(--md-sys-shape-corner-m);
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
  margin-bottom: 20px;
}
.alert-error {
  background: var(--md-sys-color-error-container);
  color: var(--md-sys-color-on-error-container);
}
.alert-success {
  background: var(--md-sys-color-success-container);
  color: var(--md-sys-color-on-success-container);
}
.alert-info {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}

/* ── Chips / badges (M3 assist chip) ── */
.badge,
.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 12px;
  /* label-large */
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;
  letter-spacing: 0.1px;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-s);
  background: transparent;
  color: var(--md-sys-color-on-surface-variant);
  white-space: nowrap;
}
.chip-selected,
.badge-selected {
  background: var(--md-sys-color-secondary-container);
  border-color: transparent;
  color: var(--md-sys-color-on-secondary-container);
}
.chip-sm, .badge-sm { height: 24px; padding: 0 8px; font-size: 11px; letter-spacing: 0.5px; }

/* A caveat, not a failure: truncated results, broken references, metrics
   computed from a subset. Loud enough to be read, quiet enough that a page
   carrying one does not look broken. */
.badge-warning, .chip-warning {
  background: var(--md-sys-color-warning-container);
  border-color: transparent;
  color: var(--md-sys-color-on-warning-container);
}
.alert-warning {
  background: var(--md-sys-color-warning-container);
  color: var(--md-sys-color-on-warning-container);
}

/* A chip wrapping a checkbox: the box itself is hidden and the chip's selected
   state carries the meaning, which is how M3 filter chips behave. The input
   stays in the DOM so the form still posts it and a keyboard still reaches it. */
.chip-check { cursor: pointer; gap: 6px; }
.chip-check input { position: absolute; opacity: 0; width: 0; height: 0; }
.chip-check:has(input:focus-visible) { outline: 3px solid var(--md-sys-color-primary); outline-offset: 2px; }

/* Chips that label a fixed set of values (public/private, states) live in a
   column, so they share a width — otherwise the column edge zigzags. */
.badge-fixed, .chip-fixed { min-width: 72px; justify-content: center; }

/* ── Tables (M3 data table) ── */
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
}
.table th {
  padding: 14px 16px;
  text-align: left;
  /* title-small */
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.1px;
  color: var(--md-sys-color-on-surface);
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
  background: transparent;
}
.table td {
  padding: 14px 16px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
  vertical-align: middle;
  color: var(--md-sys-color-on-surface);
}
.table tr:last-child td { border-bottom: none; }
.table tbody tr {
  transition: background var(--md-sys-motion-duration-short)
              var(--md-sys-motion-easing-standard);
}
.table tbody tr:hover {
  background: color-mix(in srgb, var(--md-sys-color-on-surface) 5%, transparent);
}
.table-compact th, .table-compact td { padding: 10px 16px; }

/* ── Inset dividers ──
   For tables that sit inside a card, where a full-bleed rule would run into
   the card's edge.  A cell's `border-bottom` always spans the whole cell,
   padding included, so the rule is painted as a pseudo-element instead and
   pulled in at the first and last cell.  The inset matches the cell padding,
   which lines the divider up with the row's text. */
.table-inset { --md-table-divider-inset: 24px; }
.table-compact.table-inset { --md-table-divider-inset: 16px; }
.table-inset td { border-bottom: none; position: relative; }
.table-inset tbody tr:not(:last-child) td::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--md-sys-color-outline-variant);
}
.table-inset tbody tr:not(:last-child) td:first-child::after {
  left: var(--md-table-divider-inset);
}
.table-inset tbody tr:not(:last-child) td:last-child::after {
  right: var(--md-table-divider-inset);
}

/* Any table that needs to stay readable on narrow screens. */
.table-wrap { width: 100%; overflow-x: auto; }

/* ── Lists (M3 list item) ── */
.list { display: flex; flex-direction: column; }
.list-item {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 56px;
  padding: 8px 16px;
  color: var(--md-sys-color-on-surface);
  text-decoration: none;
}
.list-item:hover { text-decoration: none; }
.list-item-headline { font-size: 16px; line-height: 24px; letter-spacing: 0.15px; }
.list-item-supporting {
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
  color: var(--md-sys-color-on-surface-variant);
}
.list-item-trailing { margin-left: auto; }

/* ── Menu (M3 menu surface) ── */
.account-switcher {
  position: relative;
  margin-left: auto;
  display: flex;
  align-items: center;
}
.avatar-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
  border-radius: var(--md-sys-shape-corner-full);
  line-height: 0;
}
.avatar-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--md-sys-shape-corner-full);
  color: #fff;
  font-size: 16px;
  font-weight: 500;
  letter-spacing: 0.15px;
  flex-shrink: 0;
  text-transform: uppercase;
  user-select: none;
}
.avatar-sm { width: 28px; height: 28px; font-size: 13px; }
.account-menu {
  display: none;
  position: absolute;
  right: 0;
  top: 52px;
  min-width: 260px;
  padding: 8px 0;
  background: var(--md-sys-color-surface-container);
  color: var(--md-sys-color-on-surface);
  border-radius: var(--md-sys-shape-corner-xs);
  box-shadow: var(--md-sys-elevation-2);
  z-index: 1000;
}
.account-menu.open {
  display: block;
  animation: md-menu-in var(--md-sys-motion-duration-medium)
             var(--md-sys-motion-easing-decelerate);
}
@keyframes md-menu-in {
  from { opacity: 0; transform: scale(.9); transform-origin: top right; }
  to   { opacity: 1; transform: scale(1);  transform-origin: top right; }
}
.account-current {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
}
.account-info { display: flex; flex-direction: column; min-width: 0; }
.account-name {
  font-size: 16px;
  line-height: 24px;
  letter-spacing: 0.15px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.account-email {
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
  color: var(--md-sys-color-on-surface-variant);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.account-divider {
  margin: 8px 0;
  border: none;
  border-top: 1px solid var(--md-sys-color-outline-variant);
}
.account-section-label {
  padding: 8px 16px 4px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--md-sys-color-on-surface-variant);
}
.account-action {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 0 16px;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.1px;
  font-weight: 500;
  font-family: inherit;
  text-decoration: none;
  color: var(--md-sys-color-on-surface);
  width: 100%;
  cursor: pointer;
  background: none;
  border: none;
  text-align: left;
  white-space: nowrap;
}
.account-action:hover { text-decoration: none; }

/* ── Radio / fieldset ── */
.fieldset {
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  padding: 8px 16px 12px;
  margin: 0 0 20px;
}
.fieldset-legend {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.5px;
  padding: 0 4px;
  color: var(--md-sys-color-on-surface-variant);
}
.radio-label {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-height: 48px;
  padding: 12px 0;
  cursor: pointer;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
}
.radio-label + .radio-label { border-top: 1px solid var(--md-sys-color-outline-variant); }
.radio-label input[type="radio"],
.radio-label input[type="checkbox"] {
  margin-top: 1px;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  accent-color: var(--md-sys-color-primary);
}

/* ── Sections ── */
.section-heading {
  /* title-small, uppercase — the section label pattern */
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;
  letter-spacing: 0.1px;
  color: var(--md-sys-color-on-surface-variant);
  margin: 0 0 12px;
}
.settings-section {
  margin-bottom: 32px;
  padding-bottom: 32px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.settings-section:last-child { border-bottom: none; }
.settings-section h2 { margin-top: 0; }
.status-msg {
  font-size: 12px;
  letter-spacing: 0.4px;
  color: var(--md-sys-color-success);
}

/* ── Dense form controls (repeated-row tables) ──
   The admin panel edits rule payloads as tables of rows. A 48dp field in a
   six-column row would push the table into a horizontal scroll on every
   screen, so the inputs inside those rows are 40dp — the same height as a
   button, which is the smallest M3 treats as a target. */
.form-input-sm {
  height: 40px;
  padding: 0 12px;
  font-size: 14px;
}
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}
.form-grid .form-group { margin-bottom: 0; }

/* ── Checkbox rows ── */
.checkbox-label {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  cursor: pointer;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
  color: var(--md-sys-color-on-surface);
}
.checkbox-label input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--md-sys-color-primary);
  flex-shrink: 0;
}
.checkbox-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 20px;
  margin-top: 4px;
}

/* ── Rule fieldsets ──
   A rule payload is several controls that only make sense together, so they
   are grouped and given the one sentence that explains the grouping. */
.rule-fieldset { margin-bottom: 0; }
.rule-hint {
  margin: 0 0 12px;
  padding-left: 0;
}

/* ── Progress (M3 linear indicator) ── */
.progress {
  height: 4px;
  border-radius: var(--md-sys-shape-corner-full);
  background: var(--md-sys-color-surface-container-highest);
  overflow: hidden;
}
.progress-bar {
  height: 100%;
  border-radius: inherit;
  background: var(--md-sys-color-primary);
  transition: width var(--md-sys-motion-duration-medium) var(--md-sys-motion-easing-standard);
}
/* A job that cannot know its total reports -1 rather than a fabricated
   percentage, so the bar sweeps instead of lying about how far along it is. */
.progress-indeterminate {
  width: 40%;
  animation: progress-sweep 1.4s var(--md-sys-motion-easing-standard) infinite;
}
@keyframes progress-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
@media (prefers-reduced-motion: reduce) {
  .progress-indeterminate { animation: none; width: 100%; opacity: 0.5; }
}

/* ── Badge variants ──
   Rule badges, job statuses and violations are all assist chips; only the
   container role changes, so each is one rule rather than a new component. */
.badge-rule-required,
.badge-rule-flag {
  background: var(--md-sys-color-secondary-container);
  border-color: transparent;
  color: var(--md-sys-color-on-secondary-container);
}
.badge-rule-fixed,
.badge-rule-locked {
  background: var(--md-sys-color-tertiary-container);
  border-color: transparent;
  color: var(--md-sys-color-on-tertiary-container);
}
.badge-rule-fail,
.badge-violation {
  background: var(--md-sys-color-error-container);
  border-color: transparent;
  color: var(--md-sys-color-on-error-container);
}
.badge-job-succeeded {
  background: var(--md-sys-color-success-container);
  border-color: transparent;
  color: var(--md-sys-color-on-success-container);
}
.badge-job-failed {
  background: var(--md-sys-color-error-container);
  border-color: transparent;
  color: var(--md-sys-color-on-error-container);
}
.badge-job-running,
.badge-job-queued {
  background: var(--md-sys-color-secondary-container);
  border-color: transparent;
  color: var(--md-sys-color-on-secondary-container);
}

/* ── Inline forms ──
   Several one-field actions share a table row or a menu, and each needs its
   own <form> because they post to different places. */
.inline-form { display: inline-block; margin: 0; }
.inline-stack {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  flex-wrap: wrap;
}

/* ── Row menu (a <details> acting as a per-row action sheet) ──
   Not the .account-menu: that is an absolutely positioned overlay, and a
   table row needs its actions to push the row open rather than float over
   the rows beneath it. */
.row-menu > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
}
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: flex-start;
  margin-top: 8px;
  padding: 12px;
  text-align: left;
  background: var(--md-sys-color-surface-container);
  border-radius: var(--md-sys-shape-corner-m);
}

/* ── Danger zone ── */
.danger-zone {
  border: 1px solid var(--md-sys-color-error);
  border-radius: var(--md-sys-shape-corner-m);
  padding: 24px;
  margin-top: 32px;
}
.danger-zone h2 { margin-top: 0; }

/* ── Page header actions ── */
.page-header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* ── Two-column section pair ── */
.two-col {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 32px;
}
.two-col .settings-section { border-bottom: none; padding-bottom: 0; }

/* ── Narrow page (forms, job detail) ── */
.main-narrow { max-width: 720px; }

/* ── Notification bell ──
   Sits in the app bar beside the avatar. The count is a badge positioned over
   the icon rather than beside it, so the bar's width does not change when a
   notification arrives. */
.nav-bell {
  position: relative;
  margin-left: auto;
  display: flex;
  align-items: center;
}
.bell-count {
  position: absolute;
  top: -2px;
  right: -4px;
  background: var(--md-sys-color-error);
  color: var(--md-sys-color-on-error);
  border-color: var(--md-sys-color-error);
  pointer-events: none;
}

/* ── Card grid ──
   A responsive row of cards, used by the milestone list. Same shape as
   .admin-grid, named for content rather than for the one page that had it
   first — a milestone card is not an admin tile. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
  margin-bottom: 24px;
}

/* ── Timeline row ──
   The activity feed. The leading bar colours by source, so six kinds of event
   are scannable without reading each label. */
.timeline-row { box-shadow: inset 4px 0 0 var(--md-sys-color-outline-variant); }
.timeline-commit { box-shadow: inset 4px 0 0 var(--md-sys-color-primary); }
.timeline-state_change { box-shadow: inset 4px 0 0 var(--md-sys-color-secondary); }
.timeline-release { box-shadow: inset 4px 0 0 var(--md-sys-color-success); }
.timeline-revision { box-shadow: inset 4px 0 0 var(--md-sys-color-tertiary); }

/* ── Tag chips ──
   A tag stores a theme ROLE, never a hex: a hex is wrong in one of the two
   themes, and html/template rewrites an interpolated style attribute to
   ZgotmplZ. models.Tag.ColourClass maps the role onto these. */
.chip-tag-default { }
.chip-tag-primary {
  background: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container);
  border-color: transparent;
}
.chip-tag-secondary {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
  border-color: transparent;
}
.chip-tag-tertiary {
  background: var(--md-sys-color-tertiary-container);
  color: var(--md-sys-color-on-tertiary-container);
  border-color: transparent;
}
.chip-tag-error {
  background: var(--md-sys-color-error-container);
  color: var(--md-sys-color-on-error-container);
  border-color: transparent;
}
.chip-tag-success {
  background: var(--md-sys-color-success-container);
  color: var(--md-sys-color-on-success-container);
  border-color: transparent;
}

/* ── Chip row ──
   A filter strip of assist chips, used by the inbox and any page whose views
   are mutually exclusive. */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}

/* An unread list row. The leading bar rather than a background alone, because a
   background this light is invisible to anyone who cannot see the difference. */
.list-item-unread {
  background: var(--md-sys-color-surface-container-low);
  box-shadow: inset 4px 0 0 var(--md-sys-color-primary);
}

/* ═══ 4. App-specific surfaces ════════════════════════════════════════════ */

/* ── Personal workspace hero (elevated card) ── */
.workspace-hero {
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-l);
  box-shadow: var(--md-sys-elevation-1);
  margin-bottom: 32px;
  overflow: hidden;
}
.workspace-hero-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 20px 24px;
}
.workspace-hero-title { display: flex; align-items: center; gap: 16px; }
.workspace-icon {
  width: 48px;
  height: 48px;
  background: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container);
  border-radius: var(--md-sys-shape-corner-m);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.workspace-name {
  margin: 0;
  /* title-large */
  font-size: 22px;
  line-height: 28px;
  font-weight: 400;
}
.workspace-org-slug { display: block; }
.workspace-hero .table td { padding: 12px 24px; }
.workspace-hero .table th { padding: 12px 24px; }
.workspace-hero .table tr:last-child td { border-bottom: none; }
.workspace-footer {
  padding: 12px 24px;
  border-top: 1px solid var(--md-sys-color-outline-variant);
}
.workspace-empty {
  padding: 48px 24px;
  text-align: center;
}
.empty-icon { color: var(--md-sys-color-outline-variant); margin-bottom: 16px; }
.empty-heading {
  /* title-medium */
  font-size: 16px;
  line-height: 24px;
  font-weight: 500;
  letter-spacing: 0.15px;
  margin: 0 0 4px;
}
.empty-state {
  text-align: center;
  padding: 64px 24px;
  color: var(--md-sys-color-on-surface-variant);
}

/* ── Org cards (outlined cards) ── */
.org-card {
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  margin-bottom: 16px;
  overflow: hidden;
}
.org-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px;
  background: var(--md-sys-color-surface-container-low);
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.org-card-name {
  margin: 0;
  font-size: 16px;
  line-height: 24px;
  font-weight: 500;
  letter-spacing: 0.15px;
}
.org-card-name a { color: var(--md-sys-color-on-surface); }
.org-section { margin-bottom: 32px; }
.org-heading { font-size: 16px; font-weight: 500; margin: 0 0 12px; }
.repo-name { font-weight: 500; }

/* ── Repo page ── */
.repo-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}
.last-commit {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 16px;
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-m);
  margin-bottom: 24px;
  font-size: 14px;
  letter-spacing: 0.25px;
}
.commit-msg { font-weight: 500; flex: 1 1 200px; }

/* ── SHA ── */
.sha {
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 13px;
  letter-spacing: 0;
  color: var(--md-sys-color-on-surface-variant);
}
a.sha { color: var(--md-sys-color-primary); }
a.sha:hover { text-decoration: underline; }

/* ── Commit detail ── */
.commit-meta {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: 12px 16px;
  margin: 0 0 32px;
  font-size: 14px;
  letter-spacing: 0.25px;
}
.commit-meta dt { font-weight: 500; color: var(--md-sys-color-on-surface-variant); }
.commit-meta dd { margin: 0; }

/* ── File viewer ── */
.file-viewer {
  border-radius: var(--md-sys-shape-corner-m);
  overflow: auto;
  background: var(--md-sys-color-surface-container-low);
}
.file-viewer pre {
  margin: 0;
  padding: 16px;
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 13px;
  line-height: 1.6;
}
.file-path {
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 20px;
  letter-spacing: 0;
  word-break: break-all;
}

/* ── Embedded viewers (item preview, wasm model diff) ── */
.doc-preview-frame,
.diff-viewer {
  display: block;
  width: 100%;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  background: var(--md-sys-color-surface-container-lowest);
}
.doc-preview-frame { height: 85vh; }
.diff-viewer { height: 520px; background: var(--md-sys-color-inverse-surface); }
.model-diff { margin-top: 32px; }

/* ── File workbench (three panes: navigator / viewport / properties) ──
   The outer columns are driven by --wb-left and --wb-right, which
   workbench.js drags and persists. The container itself carries the vertical
   resize handle, so all three panes grow together. */
.workbench {
  --wb-left: 260px;
  --wb-right: 320px;
  display: grid;
  grid-template-columns: var(--wb-left) 12px minmax(0, 1fr) 12px var(--wb-right);
  height: 72vh;
  min-height: 320px;
  resize: vertical;   /* the viewport's height handle — no JS needed */
  overflow: hidden;   /* required for `resize` to take effect */
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  background: var(--md-sys-color-surface);
}

.wb-pane {
  min-width: 0;
  min-height: 0;
  overflow: auto;
  background: var(--md-sys-color-surface-container-low);
}
.wb-nav { padding: 8px 0; }
.wb-props { padding: 16px; }

/* ── Gutters ── */
.wb-gutter {
  position: relative;
  background: var(--md-sys-color-surface-container-low);
  border-left: 1px solid var(--md-sys-color-outline-variant);
  border-right: 1px solid var(--md-sys-color-outline-variant);
}
.wb-gutter-col { cursor: col-resize; }
/* The grip: a hairline that thickens on hover, rather than a visible bar
   competing with the pane dividers. */
.wb-gutter-col::after {
  content: "";
  position: absolute;
  inset: 40% 50% 40% 50%;
  width: 2px;
  margin-left: -1px;
  border-radius: var(--md-sys-shape-corner-full);
  background: var(--md-sys-color-outline-variant);
  transition: background var(--md-sys-motion-duration-short) var(--md-sys-motion-easing-standard);
}
.wb-gutter-col:hover::after,
.wb-gutter-col:focus-visible::after,
.wb-gutter-active::after { background: var(--md-sys-color-primary); }
/* While dragging, the iframe must not swallow the pointer. */
.wb-dragging { user-select: none; }
.wb-dragging iframe { pointer-events: none; }

/* ── Navigator ── */
.wb-nav-group { border-bottom: 1px solid var(--md-sys-color-outline-variant); }
.wb-nav-group:last-child { border-bottom: none; }
.wb-nav-group .folder-summary { min-height: 48px; padding: 12px 16px; }
.wb-nav-count { margin-left: auto; }
.wb-nav-list { padding-bottom: 8px; }
.wb-nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 8px 16px 8px 32px;
  color: var(--md-sys-color-on-surface);
  text-decoration: none;
  /* label-large */
  font-size: 14px;
  letter-spacing: 0.1px;
}
.wb-nav-name {
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wb-nav-item-active {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
  font-weight: 500;
}
.wb-nav-empty { padding: 16px; }

/* ── Viewport ── */
.wb-viewport {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--md-sys-color-surface-container-lowest);
}
.wb-frame {
  flex: 1;
  min-height: 0;
  width: 100%;
  border: 0;
  background: var(--md-sys-color-surface-container-lowest);
}
/* The wasm model viewer paints its own dark scene; match it so there is no
   flash of page background while the canvas boots. */
.wb-frame-dark { background: var(--md-sys-color-inverse-surface); }

.wb-image-stage {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  overflow: auto;
  background: var(--md-sys-color-surface-container);
}
.wb-image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--md-sys-shape-corner-s);
}

.wb-text {
  flex: 1;
  min-height: 0;
  overflow: auto;
  background: var(--md-sys-color-surface-container-low);
}
.wb-text pre {
  margin: 0;
  padding: 16px;
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 13px;
  line-height: 1.6;
}

/* Toolbar under the viewport — the SolidWorks preview picker lives here. */
.wb-viewport-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 16px;
  border-top: 1px solid var(--md-sys-color-outline-variant);
  background: var(--md-sys-color-surface-container-low);
}
.wb-viewport-label {
  /* label-medium */
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.5px;
  color: var(--md-sys-color-on-surface-variant);
}

.wb-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 32px;
  text-align: center;
}

/* ── Properties pane ── */
.wb-section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
  color: var(--md-sys-color-on-surface);
}
.wb-section-title + .wb-section-title,
.wb-facts + .wb-section-title,
.wb-subsection + .wb-section-title,
.wb-actions + .wb-section-title { margin-top: 24px; }

.wb-subsection { margin-bottom: 16px; }
.wb-subsection-title {
  margin: 0 0 8px;
  color: var(--md-sys-color-on-surface-variant);
}

.wb-facts {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: 4px 12px;
  margin: 0;
  font-size: 13px;
  line-height: 20px;
}
.wb-facts dt {
  color: var(--md-sys-color-on-surface-variant);
  overflow-wrap: anywhere;
}
.wb-facts dd {
  margin: 0;
  color: var(--md-sys-color-on-surface);
  overflow-wrap: anywhere;
}

.wb-chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.wb-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.wb-lock-note { margin-top: 16px; }

/* ── Folder tree (list items inside an outlined card) ── */
.folder-tree {
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  overflow: hidden;
  font-size: 14px;
  letter-spacing: 0.25px;
}
.folder-entry { border-bottom: 1px solid var(--md-sys-color-outline-variant); }
.folder-entry:last-child { border-bottom: none; }

.folder-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 12px 16px;
  cursor: pointer;
  user-select: none;
  list-style: none; /* hide default triangle in Firefox */
  background: var(--md-sys-color-surface-container-low);
  color: var(--md-sys-color-on-surface);
}
.folder-summary::-webkit-details-marker { display: none; } /* Chrome/Safari */
.folder-name { font-weight: 500; }
.folder-desc { margin-left: 2px; }

/* Metadata plate shown below the summary when a folder has folder.meta.json. */
.folder-meta-info {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 16px 8px 44px; /* 44px = icon width + gap + summary left pad */
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
  font-size: 12px;
  letter-spacing: 0.4px;
}
.folder-meta-label { font-weight: 500; color: var(--md-sys-color-on-surface); }
.folder-meta-props { color: var(--md-sys-color-on-surface-variant); }

.folder-children {
  padding-left: 16px;
  border-left: 2px solid var(--md-sys-color-outline-variant);
  margin-left: 24px;
}

.file-entry {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.file-entry:last-child { border-bottom: none; }
.file-size { margin-left: auto; }

.tree-icon { color: var(--md-sys-color-on-surface-variant); flex-shrink: 0; }

/* ── Feature-change table (commit page) ── */
.feature-changes { margin-top: 32px; }
.changes-legend {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 12px;
  letter-spacing: 0.4px;
  color: var(--md-sys-color-on-surface-variant);
  margin-bottom: 12px;
}
.change-badge {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: var(--md-sys-shape-corner-full);
  flex-shrink: 0;
}
.change-file {
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 13px;
  margin: 16px 0 6px;
}
.change-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  overflow: hidden;
}
.change-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
  flex-wrap: wrap;
}
.change-row:last-child { border-bottom: none; }
.change-type-label {
  font-size: 12px;
  letter-spacing: 0.4px;
  color: var(--md-sys-color-on-surface-variant);
  min-width: 88px;
}
.change-name { font-weight: 500; flex: 1; }
.change-word {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.5px;
  margin-left: auto;
}

/* `.change-{added,removed,modified}` is emitted by FeatureChange.ColorClass.
   A badge takes the role as its fill, a word takes it as its text colour. */
.change-badge.change-added    { background: var(--md-sys-color-success); }
.change-badge.change-removed  { background: var(--md-sys-color-error); }
.change-badge.change-modified { background: var(--md-sys-color-primary); }
.change-word.change-added    { color: var(--md-sys-color-success); }
.change-word.change-removed  { color: var(--md-sys-color-error); }
.change-word.change-modified { color: var(--md-sys-color-primary); }

.prop-changes { width: 100%; margin-top: 6px; }
.prop-changes summary {
  font-size: 12px;
  letter-spacing: 0.4px;
  color: var(--md-sys-color-on-surface-variant);
  cursor: pointer;
  padding: 6px 0;
}
.prop-changes summary:hover { color: var(--md-sys-color-on-surface); }
.prop-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  margin-top: 8px;
}
.prop-table th {
  text-align: left;
  font-weight: 500;
  letter-spacing: 0.5px;
  padding: 6px 12px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
  color: var(--md-sys-color-on-surface-variant);
}
.prop-table td {
  padding: 6px 12px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
  font-family: var(--md-sys-typescale-font-mono);
}
.prop-table tr:last-child td { border-bottom: none; }
.prop-table tr:nth-child(even) {
  background: color-mix(in srgb, var(--md-sys-color-on-surface) 4%, transparent);
}
.prop-before { color: var(--md-sys-color-error); }
.prop-after  { color: var(--md-sys-color-success); }

/* ── Admin panel ── */
.admin-nav {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}
.admin-intro { max-width: 68ch; }
.admin-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
}
.admin-tile { text-decoration: none; color: inherit; display: block; }
.admin-tile .md-display-small { margin: 8px 0; color: var(--md-sys-color-primary); }
.admin-tile p { margin: 0; }

/* ── Space strip (org page) ── */
.space-strip {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  padding: 0 24px 16px;
}

/* ── Folder tree table ──
   Depth is a class rather than an inline style: html/template refuses a var()
   in a style attribute, and emitting a class from Go is the pattern the diff
   tree already uses. Past eight levels the rows stop indenting, which is a
   presentation limit, not a structural one. */
.folder-row .folder-name {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.folder-depth-1 .folder-name { padding-left: 20px; }
.folder-depth-2 .folder-name { padding-left: 40px; }
.folder-depth-3 .folder-name { padding-left: 60px; }
.folder-depth-4 .folder-name { padding-left: 80px; }
.folder-depth-5 .folder-name { padding-left: 100px; }
.folder-depth-6 .folder-name { padding-left: 120px; }
.folder-depth-7 .folder-name { padding-left: 140px; }
.folder-depth-8 .folder-name { padding-left: 160px; }
.folder-table td { vertical-align: top; }
/* The things inside a folder, one step further in and one step quieter than
   the folders themselves. */
.folder-child-row td { border-top: none; }
.folder-child-row .folder-name a { color: var(--md-sys-color-on-surface); }
.folder-table .badge + .badge { margin-left: 4px; }

/* ── Template tree editor ── */
.tree-editor td { vertical-align: top; }
.tree-editor .node-rules { min-width: 260px; }
.node-rules summary {
  cursor: pointer;
  color: var(--md-sys-color-primary);
}
.node-rules-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
  padding: 12px;
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-m);
}
.node-rules-body .form-label { margin: 0; }

/* ── Dry-run report ── */
.report-project { margin-bottom: 16px; }
.report-project-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 8px;
}
.change-list {
  margin: 4px 0 16px;
  padding-left: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 14px;
  line-height: 20px;
}
.change-tag {
  display: inline-block;
  min-width: 68px;
  padding: 2px 8px;
  border-radius: var(--md-sys-shape-corner-xs);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.5px;
  text-align: center;
}
.change-create {
  background: var(--md-sys-color-success-container);
  color: var(--md-sys-color-on-success-container);
}
.change-rename,
.change-move {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}
.change-rules {
  background: var(--md-sys-color-tertiary-container);
  color: var(--md-sys-color-on-tertiary-container);
}
.change-conflict {
  background: var(--md-sys-color-error-container);
  color: var(--md-sys-color-on-error-container);
}

/* ── Job card ── */
.job-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  margin-bottom: 16px;
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-m);
  box-shadow: var(--md-sys-elevation-1);
}
.job-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.job-card-meta {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}

/* ── Aggregation detail ── */
.fact-row {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}
.role-summary {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.state-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}
/* Members the viewer cannot read are omitted, never redacted — this line is
   the only thing that says they exist, so it is not styled as an aside. */
.hidden-members {
  padding: 12px 16px;
  border-left: 3px solid var(--md-sys-color-outline);
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-xs);
}
.version-card { margin-bottom: 16px; }
.version-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
}
.version-head .badge + .md-title-small { margin-left: 8px; }

/* ── Permission matrix ──
   The permissions screen is one table, because the model is one matrix and any
   other shape would be a translation the reader has to undo.

   Two states carry meaning and so are styled rather than written out: a cell
   with no access is dimmed to the variant role, and a cell the coarse role
   clipped carries a marker. "This role grants Edit; their project role of read
   caps it at View" is the most confusing thing in the model, and the fix for
   confusion is to make it visible. */

.perm-matrix .perm-cell {
  white-space: nowrap;
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;
}

.perm-cell-none {
  color: var(--md-sys-color-on-surface-variant);
  background: var(--md-sys-color-surface-container-low);
}

/* The clip marker. Tonal rather than an error colour: a capped permission is
   the system working as designed, not something broken. */
.perm-clip {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  padding: 0 8px;
  height: 20px;
  border-radius: var(--md-sys-shape-corner-full);
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
  font-size: 11px;
  line-height: 16px;
  font-weight: 500;
  letter-spacing: 0.5px;
  cursor: help;
}

/* An expired grant stays on the screen rather than vanishing: a supplier whose
   access lapsed is something an administrator should see, not something that
   silently disappeared. */
.perm-row-expired td { opacity: var(--md-sys-state-disabled-content-opacity); }

/* A form laid out along the row it sits in, for the single-field controls that
   belong beside their result rather than above it. */
.form-inline {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
}

.form-inline .form-group { margin-bottom: 0; }
.form-inline .form-actions { margin-top: 0; }


/* ── Library ──
   The org-scoped shelf of Materials, Contractors and whatever else an
   administrator has invented. A type rail beside a result table: the rail is
   the navigation, so it keeps its own scroll and the table keeps the page's. */
.library-layout {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 24px;
  align-items: start;
}

.library-rail {
  position: sticky;
  top: 88px;
  background: var(--md-sys-color-surface-container-low);
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  padding: 8px;
}

.library-rail-empty { padding: 8px 12px; }

/* The selected type. secondary-container rather than a tint of primary: the
   rail is a filter, not the page's action. */
.list-item-selected {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
  border-radius: var(--md-sys-shape-corner-s);
}

.library-new-type {
  border-top: 1px solid var(--md-sys-color-outline-variant);
  margin-top: 8px;
  padding: 12px 12px 4px;
}

.library-templates {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px;
}

.library-templates form { display: contents; }

.library-body { min-width: 0; }
.library-body-header { margin-bottom: 8px; }

.library-search {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-bottom: 16px;
}

.library-search .form-input { max-width: 360px; }

.library-new-entity { margin-bottom: 16px; }

.library-delete {
  margin-top: 32px;
  padding-top: 16px;
  border-top: 1px solid var(--md-sys-color-outline-variant);
}

.page-header-meta {
  display: flex;
  gap: 8px;
  align-items: center;
}

/* One card per relation type that touches the object, showing the far end and
   the EDGE's own properties inline — which is the whole payoff of an edge being
   an object rather than a foreign key. */
.relation-card { margin-top: 24px; }

.relation-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}

.relation-card-header h2 { margin: 0; }

.used-by-list { margin: 0; }

/* ── The relation editor ──
   The whole layout of the inline row. Everything else it needs — the card, the
   table, the text buttons, the property group — is already here. */
.relations-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 24px;
}

.relation-row {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  margin-top: 12px;
}

.relation-row-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* Quantity, role and the pin, side by side and wrapping — they are one
   statement about one edge, and a column of them reads as four questions. */
.relation-row-structural {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-end;
}

.relation-row-structural .form-group { margin-bottom: 0; }

/* Edit and Remove sit hard right and take only the width they need, so adding
   them does not squeeze the property columns beside them. */
.relation-row-actions {
  white-space: nowrap;
  text-align: right;
  width: 1%;
}

/* ── Property groups and the reference picker ──
   Groups are display only. This is the whole of their styling, which is the
   argument for their being a column rather than a table. */
.propgroup { margin-bottom: 24px; }
.propgroup-row { margin-bottom: 16px; }

/* A value that is shown and not edited: a computed property, or a reference the
   caller may not resolve. */
.form-static {
  margin: 0;
  padding: 10px 0;
  color: var(--md-sys-color-on-surface);
}

/* A computed value awaiting recompute. A dot, never a blank and never the old
   number: the one failure a computed property must not have is looking right. */
.stale-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-left: 6px;
  border-radius: var(--md-sys-shape-corner-full);
  background: var(--md-sys-color-tertiary);
  animation: stale-pulse 1.2s ease-in-out infinite;
}

@keyframes stale-pulse {
  0%, 100% { opacity: .35; }
  50%      { opacity: 1; }
}

/* A number and its unit. Two fields, because there is no conversion and the
   unit is therefore part of the value rather than a formatting choice. */
.unit-field {
  display: flex;
  gap: 8px;
}

.unit-field .form-input { min-width: 0; }
.unit-field-unit { max-width: 120px; }

.refpicker { position: relative; }

.refpicker-control {
  display: flex;
  gap: 4px;
  align-items: center;
}

.refpicker-input { min-width: 0; }
.refpicker-open { flex: 0 0 auto; }

/* The options list. Hidden until it has options, so an empty list does not
   leave a floating outline under every reference field on the page. */
.refpicker-options {
  list-style: none;
  margin: 4px 0 0;
  padding: 4px;
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  max-height: 280px;
  overflow-y: auto;
  background: var(--md-sys-color-surface-container);
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-xs);
  box-shadow: var(--md-sys-elevation-2);
}

.refpicker-options:empty { display: none; }

.refpicker-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 48px;
  padding: 8px 12px;
  border-radius: var(--md-sys-shape-corner-xs);
  cursor: pointer;
}

.refpicker-option[aria-selected="true"] {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}

.refpicker-option-name { color: var(--md-sys-color-on-surface); }
.refpicker-empty { padding: 12px; }

/* The declared-property panel inside the item workbench. Its pane is narrow, so
   the groups lose their fieldset inset and the unit field stacks. */
/* A caveat printed beside a control rather than in documentation nobody opens.
   It wraps onto its own line so the builder row keeps its shape. */
.qb-note { flex-basis: 100%; }

.wb-declared { margin-bottom: 16px; }
.wb-declared .propgroup { margin-bottom: 12px; }
.wb-declared .unit-field { flex-direction: column; }
.wb-declared .unit-field-unit { max-width: none; }

/* ═══ 5. Utilities ════════════════════════════════════════════════════════ */

.text-muted { color: var(--md-sys-color-on-surface-variant); }
.text-danger { color: var(--md-sys-color-error); }
.text-success { color: var(--md-sys-color-success); }
.text-right { text-align: right; }
.text-center { text-align: center; }
.small { font-size: 12px; line-height: 16px; letter-spacing: 0.4px; }

.surface        { background: var(--md-sys-color-surface); }
.surface-low    { background: var(--md-sys-color-surface-container-low); }
.surface-cont   { background: var(--md-sys-color-surface-container); }
.surface-high   { background: var(--md-sys-color-surface-container-high); }

.elevation-1 { box-shadow: var(--md-sys-elevation-1); }
.elevation-2 { box-shadow: var(--md-sys-elevation-2); }
.elevation-3 { box-shadow: var(--md-sys-elevation-3); }

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

/* ═══ 6. Responsive ═══════════════════════════════════════════════════════
   M3 window size classes: compact < 600, medium < 840, expanded ≥ 840. */

@media (max-width: 839px) {
  .main { padding: 16px 16px 96px; }

  /* Three panes will not fit. Stack them and let the page scroll: the
     viewport keeps a usable height, the navigator and properties become
     ordinary sections above and below it. */
  .workbench {
    grid-template-columns: minmax(0, 1fr);
    height: auto;
    resize: none;
  }
  .wb-gutter { display: none; }
  .wb-pane { max-height: 320px; }
  .wb-viewport {
    height: 60vh;
    border-block: 1px solid var(--md-sys-color-outline-variant);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Data & Analytics — the query builder, results, metrics, dashboards, graph.

   All of it server-rendered: the builder is a form, the charts are inline SVG,
   and the tables are ordinary tables. There is no client-side grid and no
   charting library, so there is nothing here that a stylesheet cannot reach.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The query builder ──
   Nesting is carried by indentation and a left rule, with the group's AND/OR
   toggle sitting ON that rule — the rule is what shows which conditions the
   join applies to, so the control belongs on it. */
.qb-tree { margin-bottom: 20px; }

.qb-group {
  display: flex;
  gap: 12px;
  padding: 8px 0 8px 4px;
}
.qb-group-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  width: 56px;
}
/* The rule itself, drawn as a border on the rail so it grows with the group. */
.qb-group-rail::after {
  content: "";
  flex: 1;
  width: 2px;
  margin-top: 8px;
  border-radius: 1px;
  background: var(--md-sys-color-outline-variant);
}
.qb-join {
  height: 28px;
  padding: 0 10px;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-s);
  background: var(--md-sys-color-surface-container-low);
  color: var(--md-sys-color-on-surface-variant);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.5px;
  cursor: pointer;
}
.qb-join-not {
  background: var(--md-sys-color-error-container);
  color: var(--md-sys-color-on-error-container);
  border-color: transparent;
  cursor: default;
}
.qb-group-body {
  flex: 1;
  min-width: 0;
  padding: 8px 12px;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  background: var(--md-sys-color-surface-container-lowest);
}
/* Each level lifts one step up the surface ladder, so depth is legible
   without a shadow per level. Past four levels the steps stop, because the
   ladder runs out before a readable filter does. */
.qb-depth-1 > .qb-group-body { background: var(--md-sys-color-surface-container-low); }
.qb-depth-2 > .qb-group-body { background: var(--md-sys-color-surface-container); }
.qb-depth-3 > .qb-group-body,
.qb-depth-4 > .qb-group-body { background: var(--md-sys-color-surface-container-high); }

.qb-group-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}

.qb-condition {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
}
.qb-condition-error {
  padding: 6px 8px;
  border-radius: var(--md-sys-shape-corner-s);
  outline: 2px solid var(--md-sys-color-error);
}
.qb-field { min-width: 180px; flex: 1 1 180px; }
.qb-config { min-width: 150px; }
.qb-op { min-width: 150px; }

/* A relation traversal's operator is fixed, so it renders as a label. Same
   width as the select it replaces, so the rows below it still line up. */
.qb-op-fixed {
  min-width: 150px;
  align-self: center;
}

/* A traversal with no far-end predicate is a finished question — "has any
   relation of this kind" — so this reads as an invitation, not an error. */
.qb-related-empty {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.qb-value { min-width: 160px; flex: 1 1 160px; }
.qb-value-none { flex: 1 1 160px; }
.qb-depth { max-width: 92px; }

.qb-problem {
  flex-basis: 100%;
  margin: 0;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
}
.qb-problem-error { color: var(--md-sys-color-error); }
.qb-problem-warning { color: var(--md-sys-color-warning); }

/* ── Explore page furniture ── */
.explore-scope { margin-bottom: 16px; }
.explore-scope-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}
.explore-scope-row .form-group { margin-bottom: 0; }
.explore-note { margin: 12px 0 0; }
.explore-kinds {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
}
.explore-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 24px;
}

/* ── Results ── */
.results { margin-top: 8px; }
.results-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}
.results-count { color: var(--md-sys-color-on-surface); }
/* The hidden count is underlined on hover because it carries an explanation:
   two people running the same filter legitimately see different numbers, and
   this is where that is said. */
.results-hidden {
  color: var(--md-sys-color-on-surface-variant);
  text-decoration: underline dotted;
  cursor: help;
}
.results-kind-col { width: 84px; }
.results-more { display: flex; justify-content: center; margin-top: 16px; }

/* ── Library rows (filters, views, metrics, reports) ── */
.filter-row { align-items: flex-start; gap: 16px; }
.filter-main { flex: 1; min-width: 0; }
.filter-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.filter-meta { margin-bottom: 16px; }
.share-card { margin: 16px 0 24px; }
.share-form { margin-top: 16px; }
.definition-disclosure { margin: 16px 0 24px; }
.definition-disclosure summary { cursor: pointer; padding: 8px 0; }
.code-block {
  padding: 16px;
  border-radius: var(--md-sys-shape-corner-m);
  background: var(--md-sys-color-surface-container-low);
  color: var(--md-sys-color-on-surface-variant);
  overflow-x: auto;
}

/* A form laid out as a row of fields with the actions at the end. Used by the
   inline "create one of these" cards on every library page. */
.form-inline-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
  align-items: end;
}
.form-inline-grid .form-group { margin-bottom: 0; }
.form-inline-grid .form-group-wide { grid-column: 1 / -1; }
.form-inline-grid .form-actions {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 0;
}

/* ── Charts ──
   The SVG carries a viewBox and no width, so it fills whatever card it lands
   in. The text classes are here rather than on the elements because the SVG is
   generated in Go, and a colour interpolated into markup cannot be a token. */
.chart-wrap { margin: 16px 0; overflow-x: auto; }
.chart { display: block; width: 100%; height: auto; }
.chart-label {
  font-family: var(--md-sys-typescale-font);
  font-size: 12px;
  fill: var(--md-sys-color-on-surface-variant);
}
.chart-value {
  font-family: var(--md-sys-typescale-font-mono);
  font-size: 12px;
  fill: var(--md-sys-color-on-surface);
}
.chart-axis { stroke: var(--md-sys-color-outline-variant); stroke-width: 1; }
/* A bucket with no computable value: a dashed outline, never a zero-height
   bar. A zero bar is a claim that the answer is zero. */
.chart-bar-missing {
  fill: none;
  stroke: var(--md-sys-color-outline-variant);
  stroke-width: 1;
  stroke-dasharray: 4 3;
}

/* ── Metrics ── */
.metric-card { margin: 16px 0 24px; }
.metric-headline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px;
}
.metric-total { font-family: var(--md-sys-typescale-font-mono); }
/* The synthetic "Other" bucket is dimmed: it is a real number and not a
   measured group, and the two should not read identically. */
.metric-other td { color: var(--md-sys-color-on-surface-variant); font-style: italic; }

/* ── Dashboards ── */
.widget-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 16px;
}
.widget { min-width: 0; }
.widget-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.widget-header h2 { margin: 0; }
.widget-controls { display: flex; gap: 2px; flex-shrink: 0; }
.widget-error {
  padding: 16px;
  border-radius: var(--md-sys-shape-corner-s);
  background: var(--md-sys-color-surface-container-low);
}
.widget-kpi { display: flex; flex-direction: column; gap: 4px; padding: 8px 0; }
.kpi-value { font-family: var(--md-sys-typescale-font-mono); }
.kpi-caveat { margin: 8px 0 0; }
.widget-table-wrap { max-height: 320px; overflow-y: auto; }

/* ── Graph explorer ── */
.graph-controls { margin-bottom: 16px; }
.graph-summary { margin-bottom: 12px; }
.graph-tree {
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  overflow: hidden;
}
.graph-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 48px;
  padding: 4px 16px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.graph-row:last-child { border-bottom: none; }
.graph-name { font-weight: 500; }
.graph-path { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A node that does not match the highlight filter is dimmed rather than
   removed: removing it would break the path to a matching descendant and
   leave a tree with holes in it. */
.graph-dimmed { opacity: 0.55; }
.graph-depth-1 { padding-left: 40px; }
.graph-depth-2 { padding-left: 64px; }
.graph-depth-3 { padding-left: 88px; }
.graph-depth-4 { padding-left: 112px; }
.graph-depth-5 { padding-left: 136px; }
.graph-depth-6 { padding-left: 160px; }
.graph-depth-7 { padding-left: 184px; }
.graph-depth-8 { padding-left: 208px; }

.graph-svg .graph-edge { stroke: var(--md-sys-color-outline); stroke-width: 1.5; }
/* A cycle is drawn and marked, never hidden — a BOM with a loop in it is a
   data problem somebody has to fix. */
.graph-svg .graph-edge-cycle {
  stroke: var(--md-sys-color-error);
  stroke-dasharray: 5 3;
}
.graph-svg .graph-node rect {
  fill: var(--md-sys-color-surface-container);
  stroke: var(--md-sys-color-outline-variant);
}
.graph-svg .graph-node-dim rect { opacity: 0.5; }
.graph-svg .graph-node-dim .chart-label { opacity: 0.6; }
.graph-svg .graph-more { fill: var(--md-sys-color-warning); }

/* ── Dialog ──
   A native <dialog>, so focus trapping and Escape come from the platform
   rather than from a script that has to get them right. */
.m3-dialog {
  width: min(560px, calc(100vw - 32px));
  padding: 24px;
  border: none;
  border-radius: var(--md-sys-shape-corner-xl);
  background: var(--md-sys-color-surface-container-high);
  color: var(--md-sys-color-on-surface);
  box-shadow: var(--md-sys-elevation-3);
}
.m3-dialog::backdrop { background: rgb(0 0 0 / 0.4); }
.m3-dialog h2 { margin: 0 0 16px; }

@media (max-width: 839px) {
  .qb-group { gap: 8px; }
  .qb-group-rail { width: 44px; }
  .qb-field, .qb-op, .qb-config, .qb-value { flex-basis: 100%; min-width: 0; }
}

@media (max-width: 839px) {
  /* The type rail stops being a rail: on a medium window it is a row of
     destinations above the table it filters. */
  .library-layout { grid-template-columns: 1fr; }
  .library-rail { position: static; }
}

@media (max-width: 599px) {
  .nav { height: 56px; padding: 0 8px; }
  .unit-field { flex-direction: column; }
  .unit-field-unit { max-width: none; }
  /* The global destinations are reachable from the pages themselves; on a
     compact window the brand and the account switcher are what must fit. */
  .nav-links { display: none; }
  .nav-brand { gap: 8px; }
  .nav-brand .brand-mark { width: 32px; height: 32px; }
  .nav-brand .brand-suite { font-size: 10px; letter-spacing: 1.2px; }
  .nav-brand .brand-app { font-size: 18px; line-height: 24px; }
  .main { padding: 16px 12px 96px; }
  .page-header h1 { font-size: 24px; line-height: 32px; }
  .auth-card { padding: 24px; border-radius: var(--md-sys-shape-corner-l); }
  .table th, .table td { padding: 12px 8px; }
  .workspace-hero-header { padding: 16px; }
  .workspace-hero .table td, .workspace-hero .table th { padding: 12px 16px; }
  .commit-meta { grid-template-columns: 1fr; gap: 2px 0; }
  .commit-meta dd { margin-bottom: 10px; }
  .btn { padding: 0 16px; }
}

/* ── Compare Versions ──
   Full-viewport: the app shell is deliberately absent, because geometry needs
   the width and everything on the page is about the two versions in the
   header. */
.compare-body { margin: 0; height: 100vh; overflow: hidden; }
.compare-main {
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  height: 100vh;
}
.compare-header {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.compare-pickers { display: flex; align-items: center; gap: 8px; margin: 0; }
.segmented { display: flex; gap: 4px; margin-left: auto; }
.compare-warnings { margin: 8px 16px; }

.compare-body-grid {
  display: grid;
  grid-template-columns: minmax(200px, 280px) 1fr;
  min-height: 0;
}
.compare-tree {
  overflow-y: auto;
  padding: 12px 16px;
  border-right: 1px solid var(--md-sys-color-outline-variant);
}
.compare-viewport { position: relative; min-height: 0; }
.compare-viewport iframe { width: 100%; height: 100%; border: 0; display: block; }
.compare-noviewer { padding: 24px; max-width: 720px; }

/* The feature tree's change markers. Colour is paired with a glyph on purpose:
   added and removed must be distinguishable without relying on hue. */
.feature-tree { list-style: none; margin: 8px 0 0; padding: 0; }
.feature-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: var(--md-sys-shape-corner-s);
  cursor: pointer;
}
.feature-row:hover { background: var(--md-sys-color-surface-container); }
.feature-selected { background: var(--md-sys-color-secondary-container); }
.feature-marker { width: 1em; text-align: center; font-family: var(--md-sys-typescale-font-mono); }
.feature-added    .feature-marker { color: var(--md-sys-color-success, #2ed138); }
.feature-removed  .feature-marker { color: var(--md-sys-color-error); }
.feature-removed  .feature-name   { text-decoration: line-through; }
.feature-modified .feature-marker { color: var(--md-sys-color-primary); }
.feature-moved    .feature-marker { color: var(--md-sys-color-tertiary, #8a6fd0); }

/* The tab strip is three radios and a sibling selector: no script, no request,
   and it keeps working when the wasm module fails to load. */
.compare-tabs {
  border-top: 1px solid var(--md-sys-color-outline-variant);
  padding: 8px 16px 16px;
  max-height: 40vh;
  overflow-y: auto;
}
.compare-tabs > input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
.compare-panel { display: none; padding-top: 12px; }
#tab-params:checked ~ .compare-panel[data-tab="params"],
#tab-deps:checked   ~ .compare-panel[data-tab="deps"],
#tab-props:checked  ~ .compare-panel[data-tab="props"] { display: block; }
#tab-params:checked ~ label[for="tab-params"],
#tab-deps:checked   ~ label[for="tab-deps"],
#tab-props:checked  ~ label[for="tab-props"] {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}

/* The chunk heat map, shown where there is no geometry to tessellate. */
.heat-strip {
  display: flex;
  height: 16px;
  border-radius: var(--md-sys-shape-corner-s);
  overflow: hidden;
  margin: 12px 0;
}
.heat-run { display: block; }
.heat-same { background: var(--md-sys-color-surface-container-highest); }
.heat-changed { background: var(--md-sys-color-primary); }

/* Impact: a released row is the one that must never be below the fold. */
.graph-row-released { background: color-mix(in srgb, var(--md-sys-color-error) 8%, transparent); }

/* ── The tabbed item page ──
   One URL owns the item; the tabs are links that swap the pane. The header
   strip is the point of the page: state, branch, version, lock and alerts
   above the fold, everything else a click away. */
.item-header {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.item-header-title { display: flex; align-items: center; gap: 12px; }
.item-header-title h1 { margin: 0; }
.item-facts-row, .item-alerts { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }

/* State chips take their style from the workflow's EDIT POLICY, never from a
   slug list — states are configurable, so `pilot-build` has to look sane
   without a code change. Each chip carries its name as well, so the meaning
   survives monochrome. */
.chip-state-open {
  background: var(--md-sys-color-surface-container-high);
  color: var(--md-sys-color-on-surface);
}
.chip-state-limited {
  background: transparent;
  color: var(--md-sys-color-on-surface);
  border: 1px solid var(--md-sys-color-tertiary, var(--md-sys-color-outline));
}
.chip-state-frozen {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}

.item-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 12px 0 16px;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.item-tab {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  padding: 0 16px;
  color: var(--md-sys-color-on-surface-variant);
  text-decoration: none;
  border-bottom: 2px solid transparent;
}
.item-tab-active {
  color: var(--md-sys-color-primary);
  border-bottom-color: var(--md-sys-color-primary);
}
.item-pane { min-height: 240px; }
.item-pane.htmx-request { opacity: 0.6; }

.item-overview {
  display: grid;
  grid-template-columns: minmax(320px, 1fr) minmax(280px, 380px);
  gap: 24px;
  align-items: start;
}
.item-preview {
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-m);
  overflow: hidden;
  min-height: 320px;
}
.item-preview-frame { width: 100%; height: 480px; border: 0; display: block; }
.item-preview-image { width: 100%; height: auto; display: block; }

@media (max-width: 839px) {
  .item-overview { grid-template-columns: 1fr; }
}

/* ── Merge requests ──
   Conversation on the left, reviewers and the merge gate on the right. The
   gate is the only thing on the page that polls: a reviewer looking at a stale
   mergeability verdict is the one staleness here that misleads. */
.mr-layout {
  display: grid;
  grid-template-columns: minmax(360px, 1fr) minmax(280px, 360px);
  gap: 24px;
  align-items: start;
  margin-top: 16px;
}
.mr-side {
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-m);
  padding: 16px;
}

.mr-timeline { list-style: none; margin: 0 0 16px; padding: 0; }
.mr-event {
  display: flex;
  gap: 8px;
  align-items: baseline;
  padding: 6px 0;
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.mr-event-kind { color: var(--md-sys-color-on-surface-variant); }

.comment-thread {
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-m);
  padding: 12px 16px;
  margin-bottom: 12px;
}
.comment-resolved { opacity: 0.72; }
.comment-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.comment-body { margin: 8px 0; white-space: pre-wrap; }
.comment-reply {
  border-left: 2px solid var(--md-sys-color-outline-variant);
  padding-left: 12px;
  margin: 8px 0 8px 8px;
}

/* An anchor's standing. Each state carries its words as well as its colour —
   "may have moved" is the whole message, and a colour alone cannot say it. */
.anchor-current { background: var(--md-sys-color-surface-container-high); }
.anchor-moved, .anchor-stale {
  background: transparent;
  border: 1px solid var(--md-sys-color-tertiary, var(--md-sys-color-outline));
}
.anchor-lost {
  background: transparent;
  border: 1px dashed var(--md-sys-color-outline);
  color: var(--md-sys-color-on-surface-variant);
  text-decoration: line-through;
}

.conflict-choice {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 12px;
  min-height: 48px;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-s);
  margin-bottom: 8px;
  cursor: pointer;
}
.conflict-choice:has(input:checked) {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}

@media (max-width: 839px) {
  .mr-layout { grid-template-columns: 1fr; }
}

/* ── Releases ──
   The release check, the release page and the package status. Three concepts
   — version, revision, release — and the UI's job is to keep them visibly
   separate, which is why a revision chip and a release chip never look alike. */
.release-check {
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-m);
  padding: 16px;
}
.release-check-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

/* A blocker is an error, not a caveat: it is the one finding that cannot be
   clicked past, so it borrows the error role rather than the warning one. */
.chip-blocked, .badge-blocked {
  background: var(--md-sys-color-error-container);
  border-color: transparent;
  color: var(--md-sys-color-on-error-container);
}

.release-findings { list-style: none; margin: 0 0 12px; padding: 0; }
.release-finding {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  padding: 8px 12px;
  border-radius: var(--md-sys-shape-corner-s);
  margin-bottom: 6px;
  font-size: 14px;
  line-height: 20px;
}
.release-finding-blocked {
  background: var(--md-sys-color-error-container);
  color: var(--md-sys-color-on-error-container);
}
.release-finding-warn {
  background: var(--md-sys-color-warning-container);
  color: var(--md-sys-color-on-warning-container);
}
/* The glyph carries the meaning alongside the colour, so a blocker is still a
   blocker in monochrome or to a colour-blind reader. */
.release-glyph { flex: none; font-weight: 700; }
.release-finding-body { display: flex; flex-direction: column; gap: 6px; flex: 1; }
.release-ack {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 48px;
  cursor: pointer;
}
.form-input-sm { height: 40px; }

/* Tree depth, as classes rather than interpolated padding. Capped at eight:
   past that the indent stops carrying information and starts eating the
   column. */
.indent-0 { padding-left: 12px; }
.indent-1 { padding-left: 28px; }
.indent-2 { padding-left: 44px; }
.indent-3 { padding-left: 60px; }
.indent-4 { padding-left: 76px; }
.indent-5 { padding-left: 92px; }
.indent-6 { padding-left: 108px; }
.indent-7 { padding-left: 124px; }
.indent-8 { padding-left: 140px; }

.release-layout {
  display: grid;
  grid-template-columns: minmax(360px, 1fr) minmax(280px, 360px);
  gap: 24px;
  align-items: start;
  margin-top: 16px;
}
.release-side {
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-sys-shape-corner-m);
  padding: 16px;
}
/* A withdrawn release is dimmed and struck, never hidden: "why did we withdraw
   it" is answered by looking at it. */
.release-obsolete { opacity: 0.72; }

@media (max-width: 839px) {
  .release-layout { grid-template-columns: 1fr; }
}

/* ── Context menu (M3 menu surface, opened on right-click by app.js) ── */
.md-menu {
  min-width: 112px;
  max-width: min(360px, 90vw);
  padding: 8px 0;
  background: var(--md-sys-color-surface-container);
  color: var(--md-sys-color-on-surface);
  border-radius: var(--md-sys-shape-corner-xs);
  box-shadow: var(--md-sys-elevation-2);
}
.md-menu-item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 48px;
  padding: 0 12px;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.1px;
  text-align: left;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.md-menu-item:hover { background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, transparent); }
.md-menu-item:focus-visible {
  outline: none;
  background: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);
}
.md-menu-divider {
  height: 1px;
  margin: 8px 0;
  background: var(--md-sys-color-outline-variant);
}
.ctx-menu {
  position: fixed;
  visibility: hidden;
  z-index: 1100;
  animation: md-ctx-in var(--md-sys-motion-duration-short)
             var(--md-sys-motion-easing-decelerate);
}
@keyframes md-ctx-in {
  from { opacity: 0; transform: scale(.9); transform-origin: top left; }
  to   { opacity: 1; transform: scale(1);  transform-origin: top left; }
}
.ctx-menu > .row-menu-body { margin-top: 0; background: none; border-radius: 0; }
