/* Warm Sage app layer. Tokens and ws-* components come from css/ws/ (copied from the design system, never edited).
   This file holds the shell, page furniture and every class the generator's role mapping uses that the DS lacks
   (index §3.2). Plans add sections; keep one section per concern. */

[hidden] { display: none !important; }

body { margin: 0; background: var(--bg-page); color: var(--text-body); font-family: var(--font-body); }
a { color: var(--text-link); }
.mono { font-family: var(--font-mono); }
.meta { font: var(--type-body-sm); color: var(--text-secondary); }
/* Times and shas set in mono keep it inside .meta, whose font shorthand would reset the family. */
.meta.mono, .meta .mono { font-family: var(--font-mono); }

/* Accessibility */
.skip-link {
  position: absolute; left: var(--space-4); top: -100px; z-index: 100;
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-md);
  background: var(--surface-card); color: var(--text-link); box-shadow: var(--shadow-md);
}
.skip-link:focus { top: var(--space-4); }
#page-title:focus { outline: none; }

/* Shell: sidebar + main (signed-in pages) */
.app-shell { display: grid; grid-template-columns: var(--sidebar-width, 264px) minmax(0, 1fr); min-height: 100vh; }
.app-aside {
  position: sticky; top: 0; height: 100vh; overflow-y: auto; box-sizing: border-box;
  padding: var(--space-6) var(--space-3); border-right: 1px solid var(--border-subtle); background: var(--surface-card);
  display: flex; flex-direction: column; gap: var(--space-4);
}
.app-main { min-width: 0; }
.app-main__inner {
  display: flex; flex-direction: column; gap: 28px; box-sizing: border-box;
  max-width: var(--content-max, 1120px); margin: 0 auto; padding: 32px 40px 96px;
}

/* Card shell: sign-in, setup, errors */
.card-shell { min-height: 100vh; display: grid; place-items: center; box-sizing: border-box; padding: var(--space-8) var(--space-4); }
.card-shell__inner { width: min(440px, 100%); display: flex; flex-direction: column; gap: var(--space-6); }
.wordmark { font-family: var(--font-display); font-weight: var(--weight-bold); font-size: var(--text-xl); color: var(--text-strong); text-decoration: none; }
.display-title { font: var(--type-display); color: var(--text-strong); margin: 0; }
.lede { font: var(--type-body); color: var(--text-secondary); margin: 0; }

@media (max-width: 860px) {
  .app-shell { grid-template-columns: minmax(0, 1fr); }
  .app-aside { position: static; height: auto; border-right: 0; border-bottom: 1px solid var(--border-subtle); }
  .app-main__inner { padding: 24px 16px 64px; }
}

/* Sidenav pieces the DS leaves to the app */
/* The generated sidenav is one div.ws-sidenav inside the aside: it fills the rail so the foot's margin-top: auto reaches
   the bottom, and spaces brand, switcher, nav and foot apart. Its nav inherits the gap, so the nav takes back the DS's 2px. */
.app-aside > .ws-sidenav { flex: 1 0 auto; gap: var(--space-4); }
.app-aside .gen-sidenav-nav { gap: 2px; }
.sidenav-brand { display: flex; align-items: center; gap: var(--space-2); padding: 4px 12px 0; font-family: var(--font-display); font-weight: var(--weight-bold); font-size: var(--text-lg); color: var(--text-strong); text-decoration: none; }
.sidenav-section-title { font: var(--type-caption); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--text-secondary); padding: 16px 12px 6px; margin: 0; }
/* Avatar, name over role, sign-out button in a row (prototype aside foot). */
.sidenav-foot { margin-top: auto; display: flex; align-items: center; gap: var(--space-3); padding: 12px 12px 0; border-top: 1px solid var(--border-subtle); }
.sidenav-foot > div { flex: 1; min-width: 0; }
.sidenav-foot > div > div { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidenav-foot > div > div:first-child { font: var(--type-label); color: var(--text-strong); }
.sidenav-foot .meta { font: var(--type-caption); }
.sidenav-foot form { margin: 0; }
/* The generated switcher is the button itself: a "Team" caption over the team's name, the chevrons on the right. */
.team-switcher { position: relative; }
button.team-switcher {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: var(--space-3);
  width: 100%; min-height: 52px; padding: 8px 12px; box-sizing: border-box;
  border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--surface-card);
  color: var(--text-body); font: inherit; text-align: left; cursor: pointer;
}
button.team-switcher:hover { background: var(--surface-hover); }
button.team-switcher > .meta { grid-column: 1; font: var(--type-caption); }
button.team-switcher > strong { grid-column: 1; font: var(--type-label); font-size: 17px; color: var(--text-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
button.team-switcher > .ws-icon { grid-column: 2; grid-row: 1 / span 2; color: var(--text-secondary); }
.team-switcher__trigger {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; min-height: 44px; padding: 0 12px; box-sizing: border-box;
  border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--surface-card);
  color: var(--text-strong); font: inherit; font-weight: var(--weight-semibold); text-align: left; cursor: pointer;
}
.team-switcher__trigger:hover { background: var(--surface-muted); }

/* Menus (team switcher, account) */
.ws-menu {
  min-width: 220px; margin: 0; padding: var(--space-1); list-style: none;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md); background: var(--surface-card); box-shadow: var(--shadow-md);
}
.ws-menu__item {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; min-height: 40px; padding: 0 12px; box-sizing: border-box;
  border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-body); font: inherit; text-align: left; text-decoration: none; cursor: pointer;
}
.ws-menu__item:hover, .ws-menu__item[aria-current="page"] { background: var(--surface-muted); color: var(--text-strong); }

/* Page head */
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
.page-head h1, .page-title { font: var(--type-h1); color: var(--text-strong); margin: 0; }
.page-head__sub { font: var(--type-body); color: var(--text-secondary); margin: 6px 0 0; }
/* The environment page's body adds its subtitle after the generated head, not inside it: pull it back up past the
   main column's 28px gap so it sits under the title as everywhere else. */
.page-head:not(:has(.page-head__sub)) + .page-head__sub { margin-top: calc(6px - 28px); }
.page-head__actions { display: flex; gap: var(--space-3); flex-wrap: wrap; }

/* Crumbs */
/* The generated crumbs are nav.crumbs > ol > li, each li after the first opening with its "/" separator. */
.crumbs { font: var(--type-body-sm); }
.crumbs > ol { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin: 0; padding: 0; list-style: none; }
.crumbs li { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.crumbs__link { color: var(--text-secondary); text-decoration: none; }
a.crumbs__link:hover { color: var(--text-link); text-decoration: underline; }
.crumbs__current { color: var(--text-strong); font-weight: var(--weight-semibold); }
.crumbs__sep { color: var(--text-muted); }

/* Whole-row links (index lists) */
.row-link {
  display: flex; align-items: center; gap: var(--space-4); padding: var(--space-4) var(--space-6);
  color: inherit; text-decoration: none; border-top: 1px solid var(--border-subtle);
}
.row-link:first-child { border-top: 0; }
.row-link:hover { background: var(--surface-muted); }
.row-link:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -3px; }
/* On a narrow screen the title keeps a usable width and the row's meta wraps beneath it. */
.row-link { flex-wrap: wrap; }
.row-link__main { flex: 1; min-width: min(12rem, 100%); display: flex; flex-direction: column; gap: 2px; }
.row-link__title { font-weight: var(--weight-semibold); color: var(--text-strong); }
.row-link__meta { font: var(--type-body-sm); color: var(--text-secondary); }
.row-link__chevron { color: var(--text-muted); font-size: 20px; }

/* Rows with their own actions */
.list-rows { list-style: none; margin: 0; padding: 0; }
.list-row { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-4) var(--space-6); border-top: 1px solid var(--border-subtle); flex-wrap: wrap; }
.list-row:first-child { border-top: 0; }
/* A row that stacks its content (a heading over a form field) fills its width instead of centring it. */
.list-row.stack { align-items: stretch; }
/* On a narrow screen the row's text keeps a usable width and its actions wrap beneath it. */
.list-row__main { flex: 1; min-width: min(12rem, 100%); display: flex; flex-direction: column; gap: 2px; }
.list-row__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; justify-content: flex-end; }

/* A list's head: its title and intro on the left, the Add button on the right (prototype variables head) */
.list-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.list-head > div { display: flex; flex-direction: column; gap: 4px; max-width: 66ch; }
.list-head h2 { font: var(--type-h3); margin: 0; }

/* Empty state */
.empty-state { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); padding: var(--space-8) var(--space-6); text-align: center; color: var(--text-secondary); }
.empty-state h2 { font: var(--type-h3); color: var(--text-strong); margin: 0; }

/* Cards. The DS pads only .ws-card__head/__body/__foot, but most cards here (and every generated one) put their content
   straight in the card. Such a card gets a body's padding and stacks its children. :where keeps it at the specificity of
   .ws-card alone, so a card's own layout class (.stack, .summary-grid, .stage) still decides its display and gap. A card
   that is all rows (.list-rows, .row-link) stays flush, and so does one that is its own body (class="ws-card ws-card__body"). */
.ws-card:where(:not(.ws-card__body):not(:has(> .ws-card__head, > .ws-card__body, > .ws-card__foot, > .list-rows, > .row-link))) {
  display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-6);
}
.ws-card--compact:where(:not(.ws-card__body):not(:has(> .ws-card__head, > .ws-card__body, > .ws-card__foot))) { gap: var(--space-2); padding: 16px 18px; }
/* Rows and tables inside a padded card run to its edges, as they do in a flush one. */
.ws-card:where(:not(.ws-card__body):not(:has(> .ws-card__head, > .ws-card__body, > .ws-card__foot, > .list-rows, > .row-link))) > :is(.list-rows, .data-table) {
  margin-inline: calc(-1 * var(--space-6)); width: calc(100% + 2 * var(--space-6)); max-width: none;
}
.ws-card:where(:not(.ws-card__body):not(:has(> .ws-card__head, > .ws-card__body, > .ws-card__foot, > .list-rows, > .row-link))) > :is(.list-rows, .data-table):last-child { margin-bottom: calc(-1 * var(--space-6)); }
.ws-card > .data-table :is(th, td):first-child { padding-left: var(--space-6); }
.ws-card > .data-table :is(th, td):last-child { padding-right: var(--space-6); }
.ws-card > .data-table tr:last-child td { border-bottom: 0; }
/* Generated cards use .empty-state for a heading's subtitle; there it is plain secondary text, not a centred empty message. */
.ws-card > :is(h1, h2) + p.empty-state { display: block; padding: 0; text-align: left; font: var(--type-body); }
/* A stacked card stretches its children; a lone button keeps its own width. */
.ws-card > .ws-btn { align-self: flex-start; }
.ws-card > :is(h1, h2, h3, p) { margin: 0; }
/* Generated create forms (new project, new environment) are a bare form holding the card and then its button, as the
   hand-written ones are a form.stack: space them the same, and keep the button its own width. */
form:has(> .ws-card) { display: flex; flex-direction: column; gap: var(--space-4); }
form:has(> .ws-card) > .ws-btn { align-self: flex-start; }
/* A card inside a card (a generated list inside a section that already has a title) draws no second frame. */
.ws-card .ws-card:not(.ws-card--compact, .ws-card--muted) { border: 0; box-shadow: none; background: transparent; padding: 0; }
/* Long words (an invite's email address) wrap rather than run out of a signed-out card. */
.card-shell .ws-card :is(p, h1) { overflow-wrap: anywhere; }
/* A table wider than a phone scrolls inside its card rather than out of it. */
/* position: relative keeps the header's absolutely placed screen-reader labels inside the scroll, not widening the page. */
.ws-card:has(> .data-table) { overflow-x: auto; position: relative; }
/* Buttons written side by side in a paragraph (the invite's Sign in / Create an account) keep a gap when they wrap. */
p:has(> .ws-btn + .ws-btn) { display: flex; flex-wrap: wrap; gap: var(--space-3); }
/* Generated forms put .ws-control on the input or select itself, not on a wrapper: it is then the whole control, as wide
   as the form and in body type, and the form's button keeps its own width. */
:is(input, select).ws-control { display: block; width: 100%; box-sizing: border-box; font: var(--type-body); color: var(--text-body); }
form:has(> :is(input, select).ws-control) > .ws-btn { align-self: flex-start; }
/* A status pill fits its text in a column (stack, grid cell) rather than stretching across it. */
.ws-badge { justify-self: start; }
:is(.stack, .ws-field) > .ws-badge { align-self: flex-start; }

/* Layout primitives */
.stack { display: flex; flex-direction: column; gap: var(--space-4); }
.cluster { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
/* A labelled field beside its button (What each app gets: App + Show): line the button up with the control, not the label. */
.cluster:has(> .ws-field) { align-items: flex-end; }
.cluster--between { justify-content: space-between; }
.cluster--end { align-items: flex-end; }
/* A list laid out as a cluster: its items carry their own labels, so no markers. */
:is(ol, ul).cluster { list-style: none; margin: 0; padding: 0; }
/* A filter box beside the select it narrows (Import: a repository for each container). */
.filter-pick { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--space-3); }
@media (max-width: 640px) { .filter-pick { grid-template-columns: minmax(0, 1fr); } }
/* The columns are a default (:where, no specificity), so a generated page's own `grid cols N` / `md N` classes win. */
.grid { display: grid; gap: var(--space-4); }
:where(.grid) { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.grid__cell { min-width: 0; }

/* Progress bar: style="--p:42" (percent). Inline style is why style-src keeps 'unsafe-inline'. */
.progress { height: 8px; border-radius: var(--radius-pill); background: var(--surface-muted); overflow: hidden; }
.progress::after { content: ""; display: block; height: 100%; width: calc(var(--p, 0) * 1%); background: var(--action-primary); transition: width var(--duration-slow) var(--ease-standard); }

/* Toasts stack */
.ws-toasts { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 200; display: flex; flex-direction: column; gap: 10px; width: min(480px, calc(100vw - 32px)); } /* the prototype's toast region; info has no DS modifier, so ws-toast--info falls back to the default sage tone */

/* DS shims */
.ws-tab[aria-current="page"] { color: var(--sage-800); border-bottom-color: var(--action-primary); font-weight: var(--weight-bold); }
.ws-tab[aria-current="page"] .ws-tab__count { background: var(--sage-100); color: var(--sage-800); }
a.ws-btn, a.ws-tab, a.ws-navitem { text-decoration: none; }
dialog.ws-dialog { padding: 0; border: 0; }
dialog.ws-dialog:not([open]) { display: none; }
dialog.ws-dialog::backdrop { background: var(--overlay-scrim); }
/* Dialog sizes (inventory §5.3) as classes, not an inline --dialog-width. md is 580px, wider than the DS default (520px). */
.ws-dialog--sm { --dialog-width: 440px; }
.ws-dialog--md { --dialog-width: 580px; }
.ws-dialog--lg { --dialog-width: 640px; }
/* A danger-zone heading (the environment page's "Delete this environment"): the DS has no text-colour utility, and the CSP refuses inline style. */
.heading-danger { color: var(--error-fg); }
/* .ws-sidenav__section is the DS caption label (uppercase, letter-spaced, padded). The generated sidenav uses it to
   wrap a group of items (nav.gen-sidenav-nav > section > ul.gen-navlist > li > a.ws-navitem), so there it is a plain
   group and its label is .sidenav-section-title. */
.gen-sidenav-nav .ws-sidenav__section { font: inherit; letter-spacing: normal; text-transform: none; color: inherit; padding: 0; }
.gen-navlist { display: flex; flex-direction: column; gap: 2px; }

/* Shell (plan 05): focus targets, the signed-out card's padding and the sign-in button (inventory §1.1, §2). */
.app-main:focus:not(:focus-visible),
.card-shell:focus:not(:focus-visible) { outline: none; }

/* The generated Members page's tables (class role "table"). */
.data-table { width: 100%; border-collapse: collapse; }
.data-table th { text-align: left; font-weight: 600; color: var(--text-muted); padding: 8px 12px; border-bottom: 1px solid var(--border-subtle); }
.data-table td { padding: 10px 12px; border-bottom: 1px solid var(--border-subtle); vertical-align: middle; }

/* Visually hidden, still read by screen readers (the generated Members page maps its "srOnly" role here). */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }

/* New app wizard (plan 11): numbered cards, the open step outlined. */
.wizard { list-style: none; margin: 0; padding: 0; }
.wizard__step h2 { display: flex; align-items: center; gap: var(--space-3); }
.wizard__step[aria-current="step"] { border-color: var(--border-selected); }
.wizard__dot { inline-size: 24px; block-size: 24px; border-radius: 50%; display: inline-grid; place-items: center;
  font-size: var(--text-sm); font-weight: var(--weight-semibold); background: var(--surface-muted); color: var(--text-muted); }
.wizard__step[aria-current="step"] .wizard__dot { background: var(--action-primary); color: var(--text-on-primary); }

/* Run progress (plan 07): numbered dots, done steps filled, the current one tinted. */
.run-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.run-step { display: grid; grid-template-columns: 28px 1fr; gap: var(--space-3); align-items: start; }
.run-step__dot { inline-size: 28px; block-size: 28px; border-radius: 50%; display: grid; place-items: center;
  font-size: var(--text-sm); font-weight: var(--weight-semibold); background: var(--surface-muted); color: var(--text-muted); }
.run-step--done .run-step__dot { background: var(--action-primary); color: var(--text-on-primary); }
.run-step[aria-current="step"] .run-step__dot { background: var(--sage-100); color: var(--text-strong); }
.run-step__body { min-inline-size: 0; }
.run-step__body code { overflow-wrap: anywhere; }
.run-step__log summary { cursor: pointer; color: var(--text-strong); }
.run-step__log .log-box { margin-block-start: var(--space-2); font-size: 14px; max-height: 320px; }

/* Project Apps tab (plan 08) */
.stages { display: flex; flex-wrap: wrap; gap: var(--space-2); list-style: none; padding: 0; margin: 0; align-items: stretch; }
.stages__arrow { display: flex; align-items: center; color: var(--text-muted); }
.stage { display: grid; gap: var(--space-1); min-width: 13rem; text-decoration: none; color: inherit; }
.progress { appearance: none; display: block; width: 100%; height: 8px; border: 0; border-radius: 999px; background: var(--stone-200); overflow: hidden; color: var(--action-primary); }
.progress::-webkit-progress-bar { background: var(--stone-200); }
.progress::-webkit-progress-value { background: var(--action-primary); }
.progress::-moz-progress-bar { background: var(--action-primary); }
/* Variables lists (generated `views list dialog`): one line a variable — name, value, then what kind it is. */
.list-row__main:has(> strong) { flex-direction: row; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
.list-row__main:has(> strong) > strong { font-family: var(--font-mono); font-size: var(--text-md); }
.list-row__main:has(> strong) code { display: inline-block; max-width: 32rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
/* "Build and runtime" is every variable's usual scope: only the narrower ones are worth saying. */
.list-row__main [data-scope="All"] { display: none; }
.list-row__main [data-scope] { padding: 1px var(--space-2); border-radius: var(--radius-pill); background: var(--surface-muted); font-size: var(--text-sm); }
.vars-how summary { cursor: pointer; color: var(--text-secondary); font: var(--type-body-sm); }
.vars-how p { margin: var(--space-2) 0 0; color: var(--text-secondary); font: var(--type-body-sm); }
.list-head > .list-row__actions { flex-direction: row; gap: var(--space-2); align-items: center; flex-wrap: wrap; max-width: none; }
.list-row:has(> .list-row__main > strong) { padding-block: var(--space-3); }
/* Edit as text: a wide dialog with a monospace editor, and its preview's changes. */
.vars-text { width: min(48rem, 94vw); }
.vars-text__control { padding: 0; }
.vars-text__control textarea { width: 100%; min-height: 18rem; padding: 10px 14px; border: 0; background: transparent; font-family: var(--font-mono); font-size: var(--text-md); line-height: 1.55; resize: vertical; }
.vars-text__problems { margin: 0; padding-left: var(--space-5); }
.vars-diff { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.vars-diff__row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.vars-diff__value { color: var(--text-secondary); overflow-wrap: anywhere; }
/* Import: the existing-project chooser above the form, and each container's variables. */
.import-into { padding: var(--space-4) var(--space-6); gap: var(--space-2); }
.import-vars { padding-top: var(--space-2); border-top: 1px dashed var(--border-subtle); }
.import-vars__table td { vertical-align: middle; }
.import-vars__value { display: inline-block; max-width: 28rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
/* Update from bunny.net: a variable's value beside its change. */
.sync-value { display: inline-block; max-width: 30rem; overflow-wrap: anywhere; color: var(--text-secondary); margin-left: var(--space-2); }
/* Usage: a card's headline figure, its number large and its words beside it. */
.usage-figure { margin: 0; color: var(--text-secondary); }
/* Sysadmin lists: a search box beside its button, above the table. */
.admin-filter { display: flex; gap: var(--space-3); max-width: 36rem; }
.admin-filter .ws-control { flex: 1; }
/* A generated page's own paragraph (`text "…"`): body copy at a readable measure. */
.page-text { margin: 0; color: var(--text-body); max-width: 72ch; }
.usage-figure strong { font: var(--type-h2); color: var(--text-strong); margin-right: var(--space-1); }
.app-rows { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-2); }
.app-row { display: grid; grid-template-columns: 1.2fr 1.4fr 1.6fr auto; gap: var(--space-3); align-items: start; padding: var(--space-3) 0; border-top: 1px solid var(--border-subtle); }
.app-row > div { display: grid; gap: var(--space-1); }
/* A ghost button for the link, but its text lines up with the type line beneath it. */
.app-row__name { justify-self: start; font-weight: 600; padding-inline: 0; }
.error-link { color: var(--error-fg); }
.error-text { color: var(--error-fg); }
.warning-text { color: var(--warning-fg); }
@media (max-width: 720px) { .app-row { grid-template-columns: 1fr; } }
.activity { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-3); }
.activity__item { display: grid; grid-template-columns: 1fr auto auto; gap: var(--space-2); align-items: start; }
.activity__main { display: grid; gap: var(--space-1); }
.activity__title { font-size: var(--text-lg); font-weight: var(--weight-medium); }
.activity__children { list-style: none; margin: 0; padding-left: var(--space-3); border-left: 2px solid var(--border-subtle); }
.activity__age { text-align: right; }

.summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: var(--space-4); }
.log-box { background: var(--stone-100); font-family: var(--font-mono); font-size: 15px; max-height: 440px; overflow: auto; white-space: pre-wrap; padding: var(--space-3); border-radius: var(--radius-md); }

/* Theme switch, under the side navigation: three icon buttons, the chosen one pressed. */
.theme-switch { display: flex; gap: 2px; padding: var(--space-2) var(--space-3); }
.theme-switch [aria-pressed="true"] { background: var(--surface-selected); color: var(--text-on-selected); }

/* Servers: what each server runs, under its status lines. */
.server-workloads { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.server-workloads > li { display: flex; flex-direction: column; gap: 2px; }
.server-workloads .cluster { gap: var(--space-2); }
.list-row__main > .ws-error { margin-top: var(--space-1); }

/* Project shell: the project's app list (240px, on the card surface) beside the page, whose content keeps the usual cap. */
.project-shell { display: grid; grid-template-columns: 240px minmax(0, 1fr); min-height: 100vh; }
.project-shell__main { min-width: 0; }
.app-list {
  position: sticky; top: 0; height: 100vh; overflow-y: auto; box-sizing: border-box;
  display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-6) var(--space-3);
  background: var(--surface-card); border-right: 1px solid var(--border-subtle);
}
.app-list__project { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding: 0 var(--space-3); min-height: 32px; }
.app-list__project > a:first-child { font: var(--type-body-sm); color: var(--text-secondary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-list__project > a:first-child:hover, .app-list__project > a[aria-current="page"] { color: var(--text-strong); text-decoration: underline; text-underline-offset: 3px; }
.app-list__rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.app-list__row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); min-height: 44px; box-sizing: border-box;
  padding: 0 var(--space-3); border-radius: var(--radius-md); color: var(--text-body); text-decoration: none;
}
.app-list__row:hover { background: var(--surface-hover); }
.app-list__row[aria-current="page"] { background: var(--sage-100); color: var(--text-strong); font-weight: var(--weight-semibold); }
.app-list__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-list__state { flex: none; font: var(--type-caption); font-weight: var(--weight-semibold); color: var(--text-secondary); }
.app-list__state--danger { color: var(--error-fg); }
.app-list__state--warning { color: var(--warning-fg); }
.app-list__state--accent { color: var(--sage-700); }
.app-list__new { justify-content: flex-start; color: var(--text-secondary); }
.app-list__new .ws-icon { font-size: 18px; }
/* Too narrow for a third column: the list sits above the page, its apps in a wrapping row. */
@media (max-width: 1100px) {
  .project-shell { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .app-list { position: static; height: auto; border-right: 0; border-bottom: 1px solid var(--border-subtle); padding: var(--space-4) var(--space-6); }
  .app-list__project { padding: 0; }
  .app-list__rows { flex-direction: row; flex-wrap: wrap; gap: var(--space-2); }
  .app-list__row { min-height: 40px; }
  .build-row { grid-template-columns: minmax(0, 1fr); }
}

/* "More" menu: a disclosure whose menu drops below its button. */
.menu-button { position: relative; }
.menu-button > summary { list-style: none; }
.menu-button > summary::-webkit-details-marker { display: none; }
.menu-button__menu { position: absolute; right: 0; top: calc(100% + var(--space-1)); z-index: 20; }
.menu-button__menu form { margin: 0; }

/* App page: the pipeline strip, one card per stage with arrows between, the card holding the next action outlined. */
.pipeline-strip { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-2); list-style: none; margin: 0; padding: 0; }
.pipeline-strip__arrow { display: flex; align-items: center; align-self: stretch; max-height: 132px; color: var(--text-muted); }
.pipeline-strip__stage { display: flex; flex-direction: column; gap: var(--space-2); flex: 1 1 12rem; min-width: 12rem; max-width: 18rem; }
.pipeline-card { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-4); min-height: 132px; box-sizing: border-box; }
.pipeline-card > * { margin: 0; }
.pipeline-card__name { font: var(--type-label); color: var(--text-strong); }
.pipeline-card__status { gap: var(--space-2); }
/* The action sits at the foot of its card, whatever the context above it says. */
.pipeline-card > form, .pipeline-card > div.cluster, .pipeline-card > a.ws-btn { margin-top: auto; align-self: flex-start; }
.pipeline-card--next { border: 2px solid var(--sage-500); padding: calc(var(--space-4) - 1px); }
.pipeline-card--error { background: var(--error-bg); border-color: var(--error-border); }
.pipeline-card--error.pipeline-card--next { border-color: var(--error-fg); }
.pipeline-strip__under { margin: 0; padding: 0 var(--space-1); }
.pipeline-strip__under .ws-switch { white-space: nowrap; }

/* Recent builds: one row per build, its log opening underneath. */
.build-rows { list-style: none; margin: 0; padding: 0; }
.build-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: var(--space-4); row-gap: var(--space-2);
  padding: var(--space-3) var(--space-6); border-top: 1px solid var(--border-subtle); }
.build-row:first-child { border-top: 0; }
.build-row__line { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); min-width: 0; }
.build-row__line > :not(.build-row__message) { flex: none; white-space: nowrap; }
.build-row__message { flex: 1 1 8rem; min-width: 8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.build-row__actions { display: flex; align-items: center; gap: var(--space-2); }
.build-row__actions form { margin: 0; }
.build-row__note, .build-row__log { grid-column: 1 / -1; margin: 0; }
.build-row__log { padding-bottom: var(--space-2); }
.build-row [data-log-toggle][aria-expanded="true"] { background: var(--surface-selected); }
.log-box { position: relative; }
.log-box mark[data-first-error] { background: var(--error-bg); color: var(--error-fg); font-weight: var(--weight-semibold); }

/* Setup: a plain list of rows, each opening its drawer. */
.setup-rows { list-style: none; margin: 0; padding: 0; }
.setup-rows > li + li { border-top: 1px solid var(--border-subtle); }
.setup-row {
  display: grid; grid-template-columns: 9rem minmax(0, 1fr) auto; align-items: center; gap: var(--space-4); width: 100%; min-height: 56px;
  padding: var(--space-3) var(--space-6); box-sizing: border-box; border: 0; background: transparent; color: var(--text-body); font: inherit; text-align: left; cursor: pointer;
}
.setup-row:hover { background: var(--surface-hover); }
.setup-rows > li:first-child .setup-row { border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.setup-rows > li:last-child .setup-row { border-radius: 0 0 var(--radius-lg) var(--radius-lg); }
.setup-row__label { font: var(--type-label); color: var(--text-strong); }
.setup-row__value { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.setup-row > .ws-icon { color: var(--text-muted); }

/* Drawers: a dialog against the right edge, full height. */
dialog.ws-dialog--drawer[open] {
  margin: 0 0 0 auto; width: min(600px, 100vw); height: 100vh; max-height: 100vh; border-radius: 0;
  display: flex; flex-direction: column; animation: none;
}
.ws-dialog--drawer .ws-dialog__body { flex: 1; overflow-y: auto; }
.drawer-section { border-top: 1px solid var(--border-subtle); padding-top: var(--space-4); }
.drawer-section > summary { cursor: pointer; font: var(--type-label); color: var(--text-strong); }
.drawer-section > .stack { margin-top: var(--space-4); }
.ws-dialog--drawer h3 { font: var(--type-h4); color: var(--text-strong); margin: 0; }
/* Sections of a drawer sit further apart than the lines inside them. */
.ws-dialog--drawer .ws-dialog__body > .stack { gap: var(--space-8); }
/* Rows run to the drawer's text edge, not a card's: their headings and hints are not inset. */
.ws-dialog--drawer .list-row { padding-inline: 0; }
.ws-dialog--drawer h4 { font: var(--type-label); color: var(--text-strong); margin: 0; }
.build-rows.ws-card, .setup-rows.ws-card { display: block; }
.ws-dialog--drawer .ws-dialog__foot { border-top: 1px solid var(--border-subtle); padding-top: var(--space-4); }
/* A heading inside a card's section, a step below the card's own title. */
.subheading { font: var(--type-label); font-size: var(--text-md); color: var(--text-strong); margin: 0; }
