/*
  RISE stylesheet. The single light token block lives further down (search
  "v2 token vocabulary"), with its dark counterpart below that — those two
  blocks are the only place a colour is defined per-theme.

  An earlier "consulting notebook" palette used to sit here and was dead
  code: a second :root{} later in the file redefined every one of its
  tokens, so nothing it declared ever applied. Removed in the v2 rebuild.
*/

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--font-body);
  background: var(--paper);
  color: var(--ink);
}

.view {
  min-height: 100vh;
  display: flex;
}

.view[hidden],
#login-view.view[hidden],
#projects-view.view[hidden],
#chat-view.view[hidden],
#usage-view.view[hidden],
#space-view.view[hidden],
#agents-view.view[hidden],
#agent-editor-view.view[hidden],
#login-view[hidden],
#projects-view[hidden],
#chat-view[hidden],
#usage-view[hidden],
#space-view[hidden],
#agents-view[hidden],
#agent-editor-view[hidden] {
  display: none !important;
}

/* ---- Shared elements ---- */
.subtitle {
  margin: 0 0 1.5rem;
  color: var(--ink-muted);
  font-size: 0.92rem;
  line-height: 1.5;
}

label {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink-muted);
  margin-top: 0.9rem;
}

input {
  margin-top: 0.4rem;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 0.65rem 0.85rem;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
}

input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Leftovers from when the bare selector styled every button as a primary:
   a light label colour meant for an accent ground, 0.7rem/1.1rem padding and
   a 0.95rem size. Nothing bare should carry those — the reset further down is
   the only global button rule now, and controls opt in by class. */

/* The bare button:hover rule lives further down, next to the other global
   button styling. Two of them was how a "fixed" hover kept coming back: the
   later one silently won and the earlier one looked authoritative. */

button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---- One button system --------------------------------------------------
   There used to be three, and which one you got depended on the page:
   .button-* (projects, build panel), .btn/.btn--* (account & usage) and
   .modal-btn--* (dialogs). Three fills, two radii, three heights for the same
   job. Worse, .button-primary and .button-secondary were each defined TWICE
   ~3800 lines apart — the later block silently won while the earlier one
   looked authoritative, which is how "+ New Project" ended up a gradient at
   rest and a flat colour on hover.

   The aliases below all resolve to the same three variants. Prefer .btn +
   .btn--primary/--ghost/--danger in new markup; the rest are kept so the
   existing HTML keeps working. */
.button-primary,
.button-secondary,
.button-danger,
.button-ghost,
.btn,
.modal-card .modal-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 36px;
  padding: 0 15px;
  border-radius: 9px;
  border: 1px solid transparent;
  font-family: inherit;
  font-size: 12.8px;
  font-weight: 650;
  line-height: 1;
  cursor: pointer;
  box-shadow: none;
  transform: none;
  transition: background-color 0.13s ease, border-color 0.13s ease, color 0.13s ease;
}

/* Ghost — the neutral secondary. Declared BEFORE the variants below: `.btn`
   and `.btn--primary` have equal specificity, so an element carrying both
   takes whichever comes later in the file. */
.button-secondary,
.button-ghost,
.btn,
.modal-card .modal-btn--ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--border-strong);
}

.button-secondary:hover,
.button-ghost:hover,
.btn:hover,
.modal-card .modal-btn--ghost:hover {
  background: var(--surface-2);
  border-color: var(--accent);
  color: var(--text);
  box-shadow: none;
  transform: none;
}

/* The button system sets `display`, which outranks the browser's [hidden] UA
   rule, so anything toggleable needs its own companion — see frontend/CLAUDE.md.
   #build-reset is hidden until a deployment exists. */
.button-primary[hidden],
.button-secondary[hidden],
.button-danger[hidden],
.button-ghost[hidden],
.btn[hidden],
.modal-card .modal-btn[hidden] {
  display: none !important;
}

/* Primary — one flat accent. No gradient: it existed on exactly one of the
   three families, and its own hover replaced it with a flat colour, so the
   fill changed shape on hover rather than just shade. */
.button-primary,
.btn--primary,
.modal-card .modal-btn--primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.button-primary:hover,
.btn--primary:hover,
.modal-card .modal-btn--primary:hover {
  background: var(--accent-hover);
  color: var(--accent-ink);
  box-shadow: none;
  transform: none;
}

/* Danger — an outline, not a filled slab. Destroy/Reset/Disconnect were solid
   red, which made the destructive control the most inviting thing on screen,
   next to the action you actually want. */
.button-danger,
.btn--danger,
.modal-card .modal-btn--danger {
  background: transparent;
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 45%, transparent);
}

.button-danger:hover,
.btn--danger:hover,
.modal-card .modal-btn--danger:hover {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border-color: var(--danger);
  color: var(--danger);
  box-shadow: none;
  transform: none;
}

.link-button {
  background: none;
  border: none;
  color: var(--ink-muted);
  font-size: 0.85rem;
  text-decoration: underline;
  padding: 0;
  cursor: pointer;
}

.link-button:disabled {
  background: none;
  color: var(--ink-faint);
  cursor: default;
  text-decoration: none;
}

.link-button:hover {
  color: var(--ink);
  background: none;
}

.error {
  color: var(--error);
  font-size: 0.85rem;
  margin: 0.9rem 0 0;
}

.auth-card {
  width: 100%;
  max-width: 340px;
}

.auth-step[hidden] {
  display: none;
}

.auth-step {
  display: flex;
  flex-direction: column;
}

.auth-step button[type="submit"] {
  margin-top: 1.5rem;
}

/* ---- Projects (styled in v2 block) ---- */

/* Badge on a project owned by someone else. Sits beside the phase chip and
   reads quieter than it — this is provenance, not status. */
.project-list__shared {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.3rem 0.65rem;
  border-radius: 999px;
  white-space: nowrap;
  background: var(--paper);
  color: var(--ink-faint);
  border: 1px solid var(--hairline);
}

/* Required alongside any author `display` — an author-stylesheet declaration
   beats the UA's own [hidden] rule regardless of specificity, so the element
   would still render with `hidden` correctly set. */
.project-list__shared[hidden] {
  display: none;
}

/* ---- Modal (new project) ---- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  z-index: 10;
}

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

.modal-card {
  width: 100%;
  max-width: 440px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
  display: flex;
  flex-direction: column;
  /* The three parts below own all internal spacing. Padding here would fight
     the footer's full-bleed divider. */
  padding: 0;
  overflow: hidden;
}

/* ---- Dialog shell: head / body / foot ----------------------------------
   Every dialog used to stack its children straight into .modal-card, so each
   one hand-tuned its own spacing (two carried `style="margin-top:-8px"` to
   claw it back). These three parts are the only thing that sets it now. */
.modal-card__head {
  padding: 20px 22px 16px;
}

.modal-card h2,
.modal-card__head h2 {
  margin: 0;
  font-size: 16.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* Supporting line, not a second headline: it was previously body-sized and
   outweighed the roster it introduced. */
.modal-card__head p {
  margin: 6px 0 0;
  font-size: 12.8px;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 46ch;
}

.modal-card__body {
  padding: 4px 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* A long roster must not push the footer off-screen. */
  max-height: min(340px, 50vh);
  overflow-y: auto;
}

.modal-card__foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 14px 22px;
  border-top: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface-2) 60%, transparent);
}

/* ---- One field treatment, selects included ----------------------------
   There was no `select` rule anywhere in this stylesheet, so the category
   dropdown fell through to the native OS control — different height, fill and
   font from the input directly above it. */
.modal-field {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* The agent editor's own highlight for a field a 400 (or a browser-side
   check) names -- covers both the form and the draft-intent box above it.
   setError() adds this class to the closest .modal-field ancestor of the
   offending control: a <label> for a plain input, or the <fieldset> itself
   for the icon/tools/starters groups. */
.agent-editor .modal-field--invalid input[type="text"],
.agent-editor .modal-field--invalid textarea {
  border-color: var(--danger);
}

.agent-editor__fieldset.modal-field--invalid {
  border: 1px solid var(--danger);
  border-radius: 10px;
  padding: 8px;
}

.modal-field > span,
.modal-card__body > label {
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 0;
}

.modal-overlay .modal-card input[type="text"],
.modal-overlay .modal-card input[type="password"],
.modal-overlay .modal-card textarea,
.modal-overlay .modal-card select {
  width: 100%;
  height: 42px;
  padding: 0 14px;
  margin: 0;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--field-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 13.6px;
}

.modal-overlay .modal-card textarea {
  height: auto;
  min-height: 96px;
  padding-block: 11px;
  resize: vertical;
  line-height: 1.45;
}

.modal-overlay .modal-card input::placeholder {
  color: var(--text-faint);
}

.modal-overlay .modal-card input:focus,
.modal-overlay .modal-card textarea:focus,
.modal-overlay .modal-card select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}

.publication-modal__card {
  max-width: 520px;
}

.publication-status,
.publication-error {
  margin: 0;
  font-size: 12.8px;
  line-height: 1.5;
}

.publication-status {
  color: var(--text-muted);
}

.publication-error {
  padding: 10px 12px;
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
  border-radius: 10px;
  background: var(--danger-bg);
  color: var(--text);
}

.publication-error[hidden] {
  display: none;
}

.publication-modal__foot {
  align-items: center;
}

.publication-modal__spacer {
  flex: 1;
}

.modal-field__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* Overrides the uppercase/11px look inherited from .modal-field > span (the
   parent it lives inside) -- the label text keeps that look, the button next
   to it reads as a normal-case action, not a second heading. */
.publication-ai-btn {
  font-size: 12px;
  font-weight: 650;
  letter-spacing: normal;
  text-transform: none;
  color: var(--accent);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

.publication-ai-btn:hover:not(:disabled) {
  color: var(--accent-hover);
  text-decoration: underline;
}

.publication-ai-btn:disabled {
  color: var(--text-faint);
  cursor: not-allowed;
}

.publication-ai-btn[hidden] {
  display: none !important;
}

.modal-field small {
  color: var(--text-muted);
  font-size: inherit;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}

/* Chevron drawn in CSS rather than shipping an asset, and in currentColor so
   it tracks the theme. */
.modal-overlay .modal-card select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 38px;
  cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 19px) 19px, calc(100% - 14px) 19px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* The closed select tracks the theme through `color-scheme`, but the open
   option list is a native popup — on dark theme, `appearance: none` on the
   select above can make a browser fall back to a plain light popup even
   though `color-scheme: dark` is set on the root. Setting `option` colours
   directly makes each option readable on its own, regardless of how a given
   browser paints the popup shell. */
.modal-overlay .modal-card select option {
  background: var(--surface);
  color: var(--text);
}

/* Dialog buttons are part of the one button system near the top of this file. */

/* ---- Consequence callout ----------------------------------------------
   Delete and destroy are the only irreversible actions in the product, and
   previously the sole signal was the button's colour. */
/* ---- One alert, everywhere ----------------------------------------------
   There were four treatments: this one, a hardcoded #f7ece8 budget banner with
   every word in error red and no icon, a bare .message.error, and a rail
   warning in hardcoded amber. They are one component now, with a --warn
   variant for things that are cautionary rather than wrong. Body text is
   normal colour; only the icon and <strong> carry the alert hue, because a
   whole paragraph in red is harder to read and says nothing extra. */
.modal-warn,
.build-alert,
.budget-banner,
.message.error,
.rail__warning,
.chat-busy-hint.is-alert {
  display: flex;
  gap: 11px;
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--danger-bg);
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
  color: var(--text);
  font-size: 12.8px;
  line-height: 1.5;
}

.modal-warn svg,
.build-alert svg,
.budget-banner svg,
.message.error svg,
.rail__warning svg,
.chat-busy-hint.is-alert svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--danger);
  margin-top: 1px;
}

.modal-warn strong,
.build-alert strong,
.budget-banner strong,
.message.error strong,
.rail__warning strong,
.chat-busy-hint.is-alert strong {
  color: var(--danger);
  font-weight: 700;
}

/* Cautionary, not wrong. */
.rail__warning {
  background: color-mix(in srgb, var(--warn) 13%, transparent);
  border-color: color-mix(in srgb, var(--warn) 32%, transparent);
}

.rail__warning svg { color: var(--warn); }

/* Secondary detail under a warning — the reassurance, not the consequence.
   Deliberately unboxed so it doesn't compete with .modal-warn. */
.modal-note {
  margin: 10px 0 0;
  font-size: 12.8px;
  line-height: 1.5;
  color: var(--text-muted);
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
}

.modal-actions button {
  margin-top: 0;
}


/* ---- Pending question card ---------------------------------------------
   Sits between the transcript and the composer. Warn-toned rather than accent:
   it is a state the user has to clear, not a normal part of the conversation. */
.ask {
  width: 92%;
  max-width: 1180px;
  margin: 0 auto 12px;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-2);
}

.ask[hidden] {
  display: none;
}

.ask__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px 0;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}

.ask__head svg {
  width: 13px;
  height: 13px;
}

.ask__pager {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px 2px;
}

.ask__pager[hidden] {
  display: none;
}

.ask__count {
  font-size: 11.5px;
  color: var(--text-muted);
}

.ask__count b {
  color: var(--text);
}

.ask__dots {
  display: inline-flex;
  gap: 4px;
  margin-right: auto;
}

.ask__dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--text) 22%, transparent);
}

.ask__dots i.on {
  background: var(--accent);
}

/* Both arrows in one group: split across the row they read as two unrelated
   controls rather than one prev/next pair. */
.ask__nav {
  display: inline-flex;
  gap: 4px;
}

.ask__pg {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: none;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.ask__pg:hover:not(:disabled) {
  color: var(--text);
  border-color: var(--border-strong);
  /* The bare `button:hover` rule supplies a #4742bf fill AND a matching indigo
     glow plus a 1px lift. Overriding only the background left the glow, which
     read as a purple button. Reset all three. */
  background: var(--surface);
  box-shadow: none;
  transform: none;
}

.ask__pg:disabled {
  opacity: 0.35;
  cursor: default;
}

.ask__q {
  padding: 8px 14px 0;
}

.ask__text {
  font-size: 13px;
  font-weight: 600;
  margin: 3px 0 9px;
  line-height: 1.45;
}

/* A row each, not chips: a full-sentence answer cannot wrap sensibly inside a
   pill, and three-word options are rarely real answers. */
.ask__opts {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.ask__opt {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  width: 100%;
  padding: 10px 13px;
  border-radius: 10px;
  cursor: pointer;
  text-align: left;
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: none;
  color: var(--text);
  font-family: inherit;
  font-size: 12.6px;
  font-weight: 500;
  line-height: 1.45;
  transition: border-color 0.12s, background-color 0.12s, color 0.12s;
}

.ask__opt:hover {
  background: var(--surface);
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  /* Same reset as .ask__pg — see the note there. */
  box-shadow: none;
  transform: none;
}

.ask__opt[aria-pressed="true"] {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border-color: var(--accent);
  color: var(--accent);
}

.ask__tick {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  margin-top: 2px;
  opacity: 0;
}

.ask__opt[aria-pressed="true"] .ask__tick {
  opacity: 1;
}

.ask__opt-body {
  min-width: 0;
}

.ask__rec {
  display: block;
  margin-top: 3px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.ask__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding: 11px 14px;
  border-top: 1px solid var(--border);
}

.ask__hint {
  font-size: 11.5px;
  color: var(--text-muted);
}

/* The bare `button:hover` rule supplies an indigo fill, a matching glow and a
   1px lift; overriding only the background is how the pager arrow once shipped
   looking purple. Reset all three. */
.ask__skip {
  flex: none;
  padding: 5px 10px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: none;
  transform: none;
  cursor: pointer;
}

.ask__skip:hover {
  color: var(--text);
  background: var(--surface-2);
  border-color: var(--border-strong);
  box-shadow: none;
  transform: none;
}

.ask__skip[aria-pressed="true"] {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

.ask__todo {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 14px 11px;
}

.ask__todo[hidden] {
  display: none;
}

.ask__todo-label {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent);
}

.ask__todo-item {
  padding: 3px 9px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 999px;
  box-shadow: none;
  transform: none;
  cursor: pointer;
}

.ask__todo-item:hover {
  background: color-mix(in srgb, var(--accent) 26%, transparent);
  color: var(--accent);
  box-shadow: none;
  transform: none;
}

/* Ranked above needs-review, and warn-toned to match the card. */
.pcard__status.is-asking,
.status.is-asking {
  background: color-mix(in srgb, var(--warn) 22%, transparent);
  color: var(--warn-chip);
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
}

/* ---- Project members (chat header button + roster modal) ---- */
.members-button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  /* Fill, radius and colour come from the later .members-button block — see
     the note on the bare `button` rule above. */
  border: 1px solid var(--hairline);
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 500;
  box-shadow: none;
  cursor: pointer;
}

.members-button:hover {
  background: var(--paper);
  color: var(--ink);
}

.members-button svg {
  display: block;
}

/* Every selector below that sets `display` needs its own [hidden] rule: an
   author declaration beats the UA's default [hidden] no matter the
   specificity, so the element would render despite `hidden` being set. */
.members-button[hidden] {
  display: none;
}

.members-modal {
  max-width: 480px;
}

.members-list {
  list-style: none;
  /* The dialog body's gap owns the spacing now. */
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.members-list[hidden] {
  display: none;
}

.members-list__row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--field-bg);
  font-size: 13.4px;
}

.members-list__row[hidden] {
  display: none;
}

.members-list__who {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}

.members-list__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Matches the account avatar and the chat bubbles' initials treatment. */
.members-list__avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 750;
  /* Same treatment as the account avatar — these are people too. */
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-muted);
}

/* A pill, like the status chips on project cards — role is a state, and it
   was previously indistinguishable from the name beside it. */
.members-list__role {
  flex-shrink: 0;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 4px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent);
  white-space: nowrap;
}

.members-list__remove {
  flex-shrink: 0;
  margin: 0;
  padding: 0;
  font-size: 0.78rem;
}

.members-add {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

/* Hidden wholesale for an editor — this one really is toggled. */
.members-add[hidden] {
  display: none;
}

.members-add__label {
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.members-add__row {
  display: flex;
  gap: 0.5rem;
}

.members-add__row[hidden] {
  display: none;
}

.members-add__row input {
  flex: 1;
  min-width: 0;
  margin: 0;
}

.members-add__row button {
  flex-shrink: 0;
  margin: 0;
}

/* Match the input it sits beside — it was a default-height button against a
   42px field. Needs `.modal-card` in the selector: `.modal-card .modal-btn`
   sets height 36px at (0,2,0), so a bare `.members-add__find` at (0,1,0) was
   written, shipped, and silently lost the cascade. */
.modal-card .members-add__find {
  height: 42px;
}

.members-confirm {
  margin: 0.2rem 0 0;
  font-size: 0.83rem;
  color: var(--ink);
}

.members-add #member-add {
  margin-top: 0.4rem;
  align-self: flex-start;
}

/* ---- Chat: two-column phase frame (details rail + tabbed chat/docs) ---- */
#chat-view.view {
  position: relative;
  width: 100%;
  /* height, not min-height: this is a fixed app shell. With min-height it grows
     to fit the tallest message and the whole PAGE scrolls, which also stops
     .messages' own overflow from ever engaging. dvh so mobile browser chrome
     does not push the composer off-screen; vh first as the fallback. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  padding: 0;
  align-items: stretch;
  justify-content: stretch;
}

#chat-view.view[hidden] {
  display: none !important;
}

.phase-frame {
  position: relative;
  z-index: 1;
  flex: 1;
  display: flex;
  min-height: 0;
  height: 100%;
  gap: 14px;
  padding: 14px;
  width: 100%;
  max-width: none;
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  overflow: hidden;
}

/* -- Scrollbar hidden but scrolling still works, on internally-scrolling panes -- */
.details,
.messages,
.doc-pane__content,
.chat-input__field textarea {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.details::-webkit-scrollbar,
.messages::-webkit-scrollbar,
.doc-pane__content::-webkit-scrollbar,
.chat-input__field textarea::-webkit-scrollbar {
  display: none;
}

/* ==========================================================================
   CLOUD JOURNEY RAIL
   The rail used to be a fixed dark surface in BOTH themes — the one sanctioned
   exception to the var(--token) rule. That was reversed 2026-08-18: in light
   mode a dark rail read as a hole in the page, and it broke outright once the
   rail became a bottom sheet on mobile, where its hardcoded light-on-dark text
   landed on whatever ground the sheet was given.
   It now themes like everything else. Use var(--token); no fixed hexes.
   ========================================================================== */
.details {
  width: 292px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  border-radius: 20px;
  /* Translucent over the blob background in both themes, rather than a fixed
     dark plate. */
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  border: 1px solid var(--border);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
  color: var(--text);
  min-height: 0;
  max-height: 100%;
}

/* .rail-top, NOT :first-child.

   #rail-close — the mobile sheet's close button — is the first child of
   .details, so `.details__section:first-child` stopped matching the moment
   that button was added, and the journey section silently lost its 20/22/24
   padding on desktop as well. The phase dots then sat flush against the card's
   left border while Artifacts below kept its inset, because
   `.details__section:last-child` still matched. That asymmetry is the tell.

   :first-child is structural — `display: none` does not free the slot — so any
   new element at the top of .details would break it again. The class is stable
   against that. */
.rail-top,
.details__section:first-child {
  padding: 20px 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* .rail__back.link-button, not .rail__back: the button carries both classes,
   and `.link-button { color: var(--accent-strong) }` further down the file wins
   on source order at equal specificity — which painted this dark indigo on the
   rail's dark ground in light mode. The rail is a fixed dark surface in both
   themes, so its contents must not read themed tokens. */
.rail__back,
.rail__back.link-button {
  /* Was a bare 12.5px text run with no padding and no hover — a ~60x15px hit
     area that read as a caption above the project name, not a control. */
  border: 1px solid color-mix(in srgb, var(--text) 10%, transparent);
  background: color-mix(in srgb, var(--text) 4%, transparent);
  color: var(--text);
  border-radius: 999px;
  padding: 9px 16px 9px 13px;
  font-size: 12.5px;
  font-weight: 650;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
  align-self: flex-start;
}

.rail__back:hover,
.rail__back.link-button:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--text) 8%, transparent);
  border-color: var(--border-strong);
}

.rail__project-name {
  font-size: 16.5px;
  font-weight: 700;
  margin-top: 11px;
  margin-bottom: 0;
  color: var(--text);
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rail-label {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
  margin-top: 20px;
  margin-bottom: 0;
}

.rail__phases {
  margin-top: 12px;
  margin-bottom: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
}

.rail__phase {
  position: relative;
  padding-left: 27px;
  display: flex;
  flex-direction: column;
  min-height: 34px;
  justify-content: flex-start;
  background: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
}

.rail__phase::before {
  content: "";
  position: absolute;
  left: 8.5px;
  top: 26px;
  bottom: 0;
  width: 1px;
  z-index: 1;
}

.rail__phase:last-child::before {
  display: none;
}

/* Accent-derived, not fixed teal. These two survived the rail's conversion to
   tokens as literal rgba(69,188,195,…) — dark mode's accent — so the connector
   between completed phases stayed teal on the light theme, whose accent is
   indigo. It was the one line in the journey not agreeing with the dots it
   joins. See frontend/CLAUDE.md: no fixed hexes in the rail. */
.rail__phase.is-done::before {
  background: color-mix(in srgb, var(--accent) 40%, transparent);
}

.rail__phase.is-current::before {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}

.rail__phase:not(.is-done):not(.is-current)::before {
  background: repeating-linear-gradient(to bottom, color-mix(in srgb, var(--text) 26%, transparent) 0 3px, transparent 3px 7px);
}

.rail__phase-icon {
  position: absolute;
  left: 0;
  top: 8px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  z-index: 2;
  box-sizing: border-box;
}

.rail__phase.is-done .rail__phase-icon {
  background: var(--accent);
  color: var(--accent-ink);
  border: none;
}

.rail__phase.is-current .rail__phase-icon {
  background: transparent;
  border: 2px solid var(--accent);
}

.rail__phase:not(.is-done):not(.is-current) .rail__phase-icon {
  background: transparent;
  border: 1.5px solid var(--border-strong);
  color: var(--text-muted);
}

.rail__phase-icon svg {
  width: 10px;
  height: 10px;
  display: block;
}

.rail__phase-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  padding: 8px 0;
  line-height: 1.3;
}

.rail__phase.is-done .rail__phase-label {
  color: var(--text);
}

.rail__phase.is-current .rail__phase-label {
  color: var(--accent);
  font-weight: 700;
}

.rail__phase:not(.is-done):not(.is-current) .rail__phase-label {
  color: var(--text-muted);
  cursor: not-allowed;
}

.rail__phase-current-tag {
  display: none;
}

.rail__substeps {
  width: 100%;
}

.rail__substeps-title {
  display: none;
}

/* An empty substep list still painted its border-left over its own 12px of
   padding, leaving a connector stub dangling below the last phase and stopping
   in mid-air. It only ever showed on Build & Deploy, the one rail step with no
   substeps of its own — every other phase fills the list, so the same border
   reads as the spine it is meant to be. That is also why no fixture caught it:
   they all seed a current phase that HAS substeps.

   The list is populated by app.js and its markup carries no whitespace, so
   :empty matches exactly when there is nothing to draw a spine beside. */
.substeps:empty {
  display: none;
}

.substeps {
  padding: 2px 0 10px 3px;
  margin-left: -18.5px;
  /* Accent-derived. This was rgba(69,188,195,.22) — the dark theme's teal,
     literal — so the substep spine stayed teal on the light theme, whose
     accent is indigo, and disagreed with the journey connector it continues. */
  border-left: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  display: flex;
  flex-direction: column;
  gap: 8px;
  list-style: none;
}

.substep {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 12.4px;
  color: var(--text-muted);
  padding-left: 13px;
}

.substep.is-done,
.substep.done {
  color: var(--text);
}

.substep i,
.substep__glyph {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1.5px solid var(--border-strong);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}

.substep.is-done i,
.substep.done i,
.substep.is-done .substep__glyph,
.substep.done .substep__glyph {
  background: var(--accent);
  border-color: var(--accent);
}

.substep i svg,
.substep__glyph svg {
  width: 8px;
  height: 8px;
  color: var(--accent-ink);
  opacity: 0;
}

.substep.is-done i svg,
.substep.done i svg,
.substep.is-done .substep__glyph svg,
.substep.done .substep__glyph svg {
  opacity: 1;
}

/* Per-step status under a substep (Understanding Requirements mirrors Spec Gen
   Studio's business lane: approved / awaiting approval / needs fixes / ...). */
.substep__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.substep__status {
  font-size: 11px;
  color: var(--text-faint);
  line-height: 1.3;
}

.substep.is-done .substep__status {
  color: var(--success);
}

.substep--review i {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.substep--review .substep__status {
  color: var(--accent);
}

.substep--blocked i,
.substep--stale i {
  border-color: var(--warn);
}

.substep--blocked .substep__status,
.substep--stale .substep__status {
  color: var(--warn-chip);
}

.substep--failed i {
  border-color: var(--danger);
}

.substep--failed .substep__status {
  color: var(--danger);
}

.substep--running i {
  border-color: var(--accent);
  border-top-color: transparent;
  animation: substep-spin 0.9s linear infinite;
}

.substep--running .substep__status {
  color: var(--accent);
}

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

@media (prefers-reduced-motion: reduce) {
  .substep--running i { animation: none; }
}

.substeps__summary {
  margin: 0 0 6px -5px;
  padding-left: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

/* -- Right column: tab bar + chat/document panes (themed via var(--token)) -- */
.tabs-column {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  border-radius: 20px;
  overflow: hidden;
  background: color-mix(in srgb, var(--surface) 62%, transparent);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  box-shadow: 0 24px 60px rgba(20, 10, 60, 0.16);
}

.chat-header {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  /* The theme toggle is one fixed control for the whole app (top: 16px,
     right: 18px, 38px wide). The projects header escapes it because its
     content sits in a max-width column, but this header spans the full width,
     so its last action — the account avatar — landed underneath it. Reserve
     the toggle's own width plus a gap. */
  padding: 0 68px 0 20px;
  border-bottom: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  min-height: 48px;
  background: transparent;
  backdrop-filter: none;
  flex-wrap: nowrap;
  box-sizing: border-box;
}

.chat-header .account-menu {
  align-self: center;
  position: relative;
}

.chat-header__actions {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-shrink: 0;
}

.tab-bar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 10px 0 0;
  margin-bottom: -1px;
}

.tab-bar__tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: none !important;
  background: transparent !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  transform: none !important;
  padding: 10px 13px !important;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--text-faint);
  cursor: pointer;
  border-bottom: 2px solid transparent !important;
  transition: color 0.15s ease, border-color 0.15s ease;
  white-space: nowrap;
  min-height: auto;
}

.tab-bar__tab:hover {
  color: var(--text);
  background: transparent !important;
  box-shadow: none !important;
  transform: none !important;
}

.tab-bar__tab.is-active,
.tab-bar__tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent) !important;
  background: transparent !important;
  box-shadow: none !important;
  transform: none !important;
}

.tab-bar__tab[hidden] {
  display: none !important;
}

.tab-bar__icon {
  width: 14px;
  height: 14px;
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
  flex-shrink: 0;
}

.tab-bar__tab-close {
  margin-left: 6px;
  font-size: 14px;
  opacity: 0.6;
  cursor: pointer;
  color: inherit;
}

.tab-bar__tab-close:hover {
  opacity: 1;
  color: var(--danger);
}

/* One definition. This was declared twice — 6px on --accent here and 7px on
   --accent-strong ~2000 lines later — so the dot's real size and colour came
   from whichever rule you had not read. */
.tab-bar__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-strong);
  margin-left: 0.4rem;
  vertical-align: middle;
  display: inline-block;
}

.tab-bar__dot[hidden] {
  display: none !important;
}

.members-button {
  height: 28px;
  padding: 0 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 50%, transparent);
  color: var(--text-muted);
  font-size: 11.5px;
  font-weight: 600;
  box-shadow: none !important;
  transform: none !important;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.members-button:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: color-mix(in srgb, var(--surface) 75%, transparent);
  box-shadow: none !important;
  transform: none !important;
}

/* The .chat-header and .usage-who avatar overrides that lived here are gone —
   they were the reason the same control looked like three different things on
   three pages. The canonical rule above owns shape and fill. */

.tab-pane {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 0;
}

.tab-pane[hidden] {
  display: none !important;
}

.doc-pane__content {
  flex: 1;
  overflow-y: auto;
  padding: 24px max(4%, calc((100% - 1180px) / 2));
  font-size: 13.4px;
  line-height: 1.6;
  color: var(--text);
}

.doc-pane__hint {
  margin: 0 0 1.25rem;
  text-align: center;
  font-size: 12px;
  color: var(--text-faint);
}

.chat-loading {
  margin: 1rem auto;
  text-align: center;
  color: var(--text-faint);
}

.messages {
  flex: 1;
  /* Required, not decorative: a flex item's automatic minimum size is its
     content, so without this the list grows instead of scrolling and the
     overflow-y below is dead. */
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 24px max(4%, calc((100% - 1180px) / 2));
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.message {
  position: relative;
  max-width: 90%;
  padding: 12px 15px;
  border-radius: 12px;
  font-size: 13.4px;
  line-height: 1.6;
  box-sizing: border-box;
  animation: reveal 0.15s ease-out;
}


@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(3px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .message {
    animation: none;
  }
}


/* AI Message */
/* No margin-left. .message-row--agent already carries 36px to clear the avatar,
   and this added a second 36px on top of it — so the agent's avatar sat 46px
   from its bubble while the user's sat 10px from theirs. A leftover from when
   the avatar was an in-flow element rather than a pseudo-element positioned off
   the row. */
.message.agent,
.message.ai {
  align-self: flex-start;
  background: color-mix(in srgb, var(--surface) 68%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
  box-shadow: none;
  white-space: normal;
}

/* Same treatment as the user avatar opposite it and as the account avatar in
   the header: a bordered surface disc, not a teal gradient tile. The two sides
   of the same conversation were drawn as two different components. */
.message-row--agent::before {
  content: "V";
  position: absolute;
  left: -36px;
  top: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
  box-sizing: border-box;
  color: var(--text-muted);
  font-size: 10px;
  font-weight: 750;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 26px;
  text-align: center;
}

/* User Message */
/* ---- Long-message clamp ------------------------------------------------
   A pasted spec can be thousands of pixels tall, which turns the conversation
   into one endless column and buries the reply under it. Any message past the
   threshold is collapsed to a fixed height with a fade and a toggle, so the
   transcript keeps its alternating left/right rhythm no matter what is pasted
   in. app.js measures each message and only clamps the ones that overflow, so
   short messages are untouched and never get a pointless button. */
.message.is-clamped {
  max-height: var(--message-clamp, 320px);
  overflow: hidden;
  /* Reserve the toggle's own strip, or the last visible line runs underneath
     it. The fade below covers the same band so the text recedes into it. */
  padding-bottom: 34px;
}

/* The fade is a real child, not ::after — .message.user::after is already the
   avatar and .message.agent::before the "V" badge, so both pseudo-elements on
   a bubble are taken. Tinted to that bubble's own background so it reads as
   the text receding, not a grey band laid over it. */
.message__fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 96px;
  pointer-events: none;
  border-bottom-left-radius: inherit;
  border-bottom-right-radius: inherit;
}

.message.agent .message__fade {
  background: linear-gradient(
    to bottom,
    transparent,
    color-mix(in srgb, var(--surface) 92%, transparent) 70%,
    color-mix(in srgb, var(--surface) 96%, transparent)
  );
}

.message.user .message__fade {
  background: linear-gradient(to bottom, transparent, var(--hover-surface) 62%, var(--hover-surface));
}

/* The toggle is anchored inside the bubble, over the fade, rather than
   floating below it: as a separate pill it inherited the global button styling
   (accent fill and a glow), sat visually detached from the message it belonged
   to, and picked up the blob background through its translucent fill. */
.message__more {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  width: 100%;
  padding: 22px 15px 10px;
  border: none;
  border-radius: 0;
  background: none;
  box-shadow: none;
  font-family: inherit;
  font-size: 11.6px;
  font-weight: 650;
  letter-spacing: 0.01em;
  text-align: left;
  cursor: pointer;
  transition: color 0.13s;
}

.message.agent .message__more {
  color: var(--accent-strong);
}

/* The comment that used to sit here — "on the accent bubble, accent-on-accent
   would vanish" — described a bubble that no longer exists. The user bubble
   became a --hover-surface TINT with normal text colour, and this rule never
   followed, so --accent-ink was rendering near-black on a dark teal tint in
   dark mode and near-white on pale lavender in light. Measured 1.17:1 both
   ways: "Show more" was invisible, which is exactly what it looked like.

   --accent-strong, not --accent: the tint ground is light in light mode, and
   plain --accent measured 4.44:1 there — a hair under the 4.5 needed for text
   this size. The agent side sits on --surface and is fine either way, so both
   use the text-weight accent now rather than differing for no reason. */
.message.user .message__more {
  color: var(--accent-strong);
  opacity: 1;
  text-align: right;
}

.message__more:hover {
  background: none;
  box-shadow: none;
  text-decoration: underline;
}

.message.user .message__more:hover {
  text-decoration: underline;
}

/* Expanded, the control returns to the flow at the end of the message so it
   does not cover the last line. No horizontal padding: the bubble's own 15px
   gutter is the alignment reference, and adding more indented the control past
   the text it belongs to. */
.message:not(.is-clamped) .message__more {
  position: static;
  padding: 12px 0 0;
}

.message-row {
  position: relative;
  display: flex;
  flex-direction: column;
  /* fit-content, not stretch: the avatars are positioned off the row's own
     edges, so the row has to hug the bubble or they float away from it. */
  width: fit-content;
  /* The width cap lives here, not on the bubble: the row is fit-content, so a
     percentage max-width on a child would resolve against a shrink-wrapped
     parent. Against .messages it is predictable. 68ch is a reading measure —
     a user bubble is solid --accent and people paste whole specs in. */
  max-width: min(90%, 68ch);
}

.message-row--user {
  align-self: flex-end;
  align-items: flex-end;
  /* Room for the avatar sitting outside the row. */
  margin-right: 36px;
}

.message-row--agent {
  align-self: flex-start;
  align-items: flex-start;
  margin-left: 36px;
  /* Wider than a user row: agent replies carry tables and review cards. */
  max-width: min(90%, 88ch);
}

.message.user {
  background: var(--hover-surface);
  color: var(--text);
  border: none;
  box-shadow: none;
  white-space: pre-wrap;
  max-width: 100%;
}

.message-row--user::after {
  content: attr(data-initials);
  position: absolute;
  right: -36px;
  top: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
  box-sizing: border-box;
  color: var(--text-muted);
  font-size: 10px;
  font-weight: 750;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 24px;
  text-align: center;
}

/* A message from another project member — same right-aligned position as
   your own, but visually distinct so the two are never confused (see the
   root CLAUDE.md's Project collaboration section). */
.message-row__sender {
  font-size: 11px;
  font-weight: 650;
  color: var(--text-muted);
  padding-right: 2px;
}

.message-row--peer .message.user {
  background: var(--surface-2);
  border: 1px solid var(--border);
}

/* The name label above a peer bubble pushes the bubble below the row's own
   top edge, so the row-level avatar above would float beside the name
   instead of the bubble. Suppressed here and re-anchored to the bubble
   itself, which keeps position: relative from the base .message rule. */
.message-row--peer::after {
  content: none;
}

.message-row--peer .message.user::after {
  content: attr(data-initials);
  position: absolute;
  right: -36px;
  top: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
  box-sizing: border-box;
  color: var(--text-muted);
  font-size: 10px;
  font-weight: 750;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 24px;
  text-align: center;
}

.message.pending {
  color: var(--text-muted);
  font-style: italic;
}

.message.error {
  box-shadow: none;
}

/* The model's own reasoning, streamed live while a turn is still working and
   collapsed to a "Thought for Ns" line once the reply starts. Sits above the
   agent bubble as its own block rather than inside it: the bubble's content is
   replaced wholesale by renderMessageContent on every delta, which would wipe
   any child element out from under itself. Created by app.js's
   createTurnBubble only when reasoning actually arrives — the Haiku agents
   never emit any, so on phases 1, 2 and 6 this markup simply never exists. */
.turn-thinking {
  margin: 0 0 6px 44px;
  max-width: min(680px, 78%);
  font-size: 13px;
  color: var(--text-muted);
}

.turn-thinking > summary {
  cursor: pointer;
  list-style: none;
  padding: 4px 0;
  color: var(--text-muted);
  user-select: none;
}

.turn-thinking > summary::-webkit-details-marker {
  display: none;
}

.turn-thinking > summary::before {
  content: "▸";
  display: inline-block;
  width: 14px;
  transition: transform 0.15s ease;
}

.turn-thinking[open] > summary::before {
  transform: rotate(90deg);
}

.turn-thinking > summary:hover {
  color: var(--text);
}

.turn-thinking__body {
  /* pre-wrap, not pre: reasoning arrives as prose with its own newlines, and
     `pre` would give every paragraph its own horizontal scrollbar. */
  white-space: pre-wrap;
  /* The min-width: auto trap — see .build-console and .details. A pre-wrapped
     block inside a flex/grid ancestor still contributes its min-content width
     upward unless this is set explicitly. */
  min-width: 0;
  max-height: 220px;
  overflow-y: auto;
  padding: 8px 12px;
  border-left: 2px solid var(--border);
  line-height: 1.5;
  opacity: 0.85;
}

/* -- Markdown rendered content (frontend/markdown.js) -- */
.markdown-body > :first-child {
  margin-top: 0;
}

.markdown-body > :last-child {
  margin-bottom: 0;
}

.markdown-body p {
  margin: 0.6em 0;
}

.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4 {
  margin: 0.8em 0 0.4em;
  font-weight: 700;
  line-height: 1.3;
}

.markdown-body h1 {
  font-size: 1.15rem;
}

.markdown-body h2 {
  font-size: 1.08rem;
}

.markdown-body h3,
.markdown-body h4 {
  font-size: 1rem;
}

.markdown-body ul,
.markdown-body ol {
  margin: 0.5em 0;
  padding-left: 1.4em;
}

.markdown-body li {
  margin: 0.2em 0;
}

.markdown-body code {
  background: var(--code-inline-bg);
  color: var(--code-inline-text);
  border-radius: 4px;
  padding: 0.1em 0.35em;
  font-size: 0.88em;
  font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
}

.markdown-body pre {
  /* Solid, not translucent: these sit inside a glass bubble which is itself
     over the blob background, and stacking three translucent layers washed the
     block out. Its own tokens rather than a surface grey, so the text contrast
     is guaranteed instead of inherited. */
  background: var(--code-bg);
  color: var(--code-text);
  border: 1px solid var(--code-border);
  border-radius: 8px;
  padding: 0.7em 0.85em;
  margin: 0.6em 0;
  overflow-x: auto;
}

/* ---- Rendered mermaid diagrams ----
   A drawing, not a code block: it gets a light plate in both themes rather
   than the dark --code-bg, because mermaid's own "dark" theme already supplies
   its palette and stacking the two made the lines unreadable. */
.markdown-body .diagram {
  margin: 0.7em 0;
  padding: 14px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  /* A wide diagram must scroll inside its own plate. `min-width: 0` is the
     load-bearing half: without it a flex/grid ancestor resolves to the SVG's
     min-content width and the whole panel scrolls sideways instead — the trap
     documented in .claude/rules/build-console-ui.md. */
  overflow-x: auto;
  min-width: 0;
  text-align: center;
}

.markdown-body .diagram svg {
  max-width: 100%;
  height: auto;
}

/* ---- Transcript event cards ----
   A stage change or a queued change request is something that happened *to*
   the project, not something the assistant said — so no avatar, and a card
   rather than a bubble. Replaces `— Moved to X. —`, which had no styling
   behind it and rendered as an ordinary assistant message. */
.message-row--event {
  width: 100%;
  max-width: 100%;
  align-items: stretch;
}

/* The avatar is drawn from the row, so it has to be suppressed here. */
.message-row--event::before,
.message-row--event::after {
  content: none;
}

.event-card {
  border-radius: 12px;
  padding: 13px 16px;
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.event-card__head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
}

.event-card__head svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

.event-card__step {
  margin-left: auto;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-weight: 650;
}

.event-card__flow {
  display: flex;
  align-items: baseline;
  gap: 9px;
  flex-wrap: wrap;
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.4;
}

/* Struck through, because it is the stage being left behind. */
.event-card__from {
  color: var(--text-muted);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  font-weight: 500;
}

.event-card__arrow { color: var(--text-muted); }
.event-card__to { color: var(--text); }

.event-card__detail {
  margin: 7px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-muted);
}

/* Reset box-shadow and transform: the bare `button:hover` rule supplies an
   indigo fill, a glow and a 1px lift, and overriding only the background is
   how the pager arrow once shipped looking purple. */
.event-card__action {
  margin-top: 11px;
  padding: 6px 13px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--accent-ink, #fff);
  background: var(--accent);
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  box-shadow: none;
  transform: none;
}

.event-card__action:hover {
  color: var(--accent-ink, #fff);
  background: color-mix(in srgb, var(--accent) 86%, #000);
  box-shadow: none;
  transform: none;
}

/* ---- Diagram toolbar (expand / download) ----
   Hidden until hover so a transcript full of diagrams isn't a wall of chrome;
   always shown on touch, which has no hover. */
.markdown-body .diagram {
  position: relative;
  cursor: zoom-in;
}

.diagram__bar {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  gap: 5px;
  opacity: 0;
  transition: opacity 0.13s;
  pointer-events: none;
}

.diagram:hover .diagram__bar,
.diagram:focus-within .diagram__bar {
  opacity: 1;
  pointer-events: auto;
}

@media (hover: none) {
  .diagram__bar { opacity: 1; pointer-events: auto; }
}

/* The bare `button:hover` rule supplies an indigo fill, a matching glow and a
   1px lift. Reset all three, or these read as the wrong control entirely. */
.diagram__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  font: inherit;
  font-size: 12px;
  font-weight: 650;
  color: var(--text);
  background: color-mix(in srgb, var(--surface-2) 94%, transparent);
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.16);
  transform: none;
}

.diagram__btn:hover {
  color: var(--text);
  background: var(--surface-2);
  border-color: var(--accent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  transform: none;
}

.diagram__btn svg { width: 14px; height: 14px; }

/* ---- Diagram viewer ----
   z-index 200: above the modals (10), below the theme toggle (250), so the
   theme can still be switched while a diagram is open. */
.dgmv {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(3px);
}

/* Must outrank .dgmv's own display, which ties it on specificity. */
.dgmv[hidden] { display: none !important; }

.dgmv__bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 12px 16px;
  /* Clear of the fixed theme toggle in the top-right. */
  padding-right: 68px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
}

.dgmv__title {
  font-size: 13.5px;
  font-weight: 650;
  margin-right: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dgmv__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 11px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  box-shadow: none;
  transform: none;
}

.dgmv__btn:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--text) 9%, var(--surface-2));
  border-color: var(--border-strong);
  box-shadow: none;
  transform: none;
}

.dgmv__btn svg { width: 14px; height: 14px; }

.dgmv__pct {
  min-width: 52px;
  text-align: center;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}

.dgmv__stage {
  flex: 1;
  /* Without this a flex child refuses to shrink below its content, so the
     stage grows past the viewport instead of clipping. */
  min-height: 0;
  overflow: hidden;
  /* Not `display: grid; place-items: center` — a grid item wider than its
     container aligns to start, not centre, so an oversized diagram sat hard
     against the left edge and ran off the right. Centred explicitly below. */
  position: relative;
  cursor: grab;
  /* Or the browser pans/zooms the page under the diagram. */
  touch-action: none;
}

.dgmv__stage.is-panning { cursor: grabbing; }

.dgmv__plate {
  /* Anchored at the stage centre, then translate(-50%,-50%) puts its own
     centre there. Scaling about that centre leaves it put, so fit and zoom are
     both stable regardless of how far the diagram exceeds the stage. */
  position: absolute;
  left: 50%;
  top: 50%;
  width: max-content;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 18px;
  transform-origin: center center;
  will-change: transform;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.35);
}

.dgmv__plate svg { display: block; }

/* Kept legible when a diagram fails to draw and we fall back to its source. */
.diagram-failed {
  margin: 0.7em 0 0.3em;
  font-size: 12.5px;
  color: var(--text-muted);
}

.markdown-body pre.diagram-fallback {
  border-color: color-mix(in srgb, var(--warn) 35%, transparent);
}

.markdown-body pre code {
  background: none;
  /* Inherit the panel's colour, not the bubble's — a user bubble sets
     --accent-ink on its descendants. */
  color: inherit;
  padding: 0;
  white-space: pre;
}

/* Chat & Document Markdown Tables */
.markdown-body table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.6px;
  margin: 12px 0;
  display: block;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
}

.markdown-body th {
  text-align: left;
  font-size: 10.2px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  padding: 9px 13px;
  background: color-mix(in srgb, var(--text) 5%, transparent);
  white-space: nowrap;
  border-top: none;
}

.markdown-body td {
  padding: 10px 13px;
  border-top: 1px solid var(--border);
  vertical-align: top;
}

.markdown-body tbody tr:hover {
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.markdown-body td:not(:first-child) {
  font-variant-numeric: tabular-nums;
}

.markdown-body th:last-child,
.markdown-body td:last-child {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.markdown-body tfoot td {
  padding: 10px 13px;
  border-top: 1px solid var(--border-strong);
  font-weight: 700;
}

.markdown-body blockquote {
  margin: 0.6em 0;
  padding: 0.1em 0 0.1em 0.9em;
  border-left: 3px solid var(--accent);
  color: var(--text-muted);
}

.markdown-body a {
  color: var(--accent);
  text-decoration: underline;
}

.markdown-body hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 0.8em 0;
}

.doc-pane__content.markdown-body {
  font-size: 0.95rem;
  line-height: 1.6;
}

.doc-pane__content img {
  max-width: 100%;
  height: auto;
  display: block;
  border-radius: 8px;
  border: 1px solid var(--border);
}

.doc-pane__content:empty::before {
  content: "Loading…";
  color: var(--text-faint);
  font-size: 0.85rem;
}

/* -- Composer -- */
.chat-input {
  border-top: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  padding: 14px 24px 18px;
  margin: 0;
  background: transparent;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.chat-input__field {
  width: 92%;
  max-width: 1180px;
  margin: 0 auto;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 60%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-radius: 12px;
  /* Uniform 8px, deliberately diverging from the mockup's `8px 8px 8px 14px`.
     That extra left inset exists there because the mockup's composer starts
     with bare text, which needs room off the rounded edge. Ours leads with the
     24px attach button, so the asymmetry just pushed the icon off-centre
     against the Send button's 8px on the right. */
  padding: 8px;
  box-shadow: none;
  min-width: 0;
}

.chat-input__field:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}

.chat-input__field textarea {
  flex: 1;
  border: none;
  background: transparent;
  resize: none;
  outline: none;
  color: var(--text);
  font-family: inherit;
  font-size: 13.4px;
  /* 2px of left padding so the caret does not sit on top of the first glyph
     of the placeholder. The vertical padding centres a single line inside the
     34px row height set by the Send button: (34 - 18.76 line-height) / 2. The
     row is bottom-aligned, so a shorter textarea would put all its slack above
     the text and leave the field visibly top-heavy. */
  padding: 7.6px 0 7.6px 2px;
  line-height: 1.4;
  min-height: 34px;
  max-height: 160px;
  overflow-y: auto;
}

.chat-input__field textarea:focus {
  outline: none;
}

.chat-input__attach {
  width: 24px;
  height: 24px;
  border-radius: 6px;
  border: none;
  background: transparent;
  color: var(--text-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  /* The bare `label {}` rule sets margin-top: 0.9rem for form labels. This is a
     button, not a caption, and that inherited 14.4px made the attach control
     42.4px tall — which became the composer's content height, inflating the
     field to 60px and leaving 26.6px above the text against 15px below. */
  margin-top: 0;
  /* The row is bottom-aligned so the controls stay put as the textarea grows.
     This 24px icon then sits above the first text line; 5px lines the two up. */
  margin-bottom: 5px;
  flex-shrink: 0;
  transition: color 0.15s;
}

.chat-input__attach:hover {
  color: var(--text);
}

.chat-input__attach svg {
  width: 16px;
  height: 16px;
  stroke: currentColor;
  stroke-width: 1.8;
}

.chat-input__send {
  height: 34px;
  padding: 0 16px;
  border-radius: 9px;
  border: none;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 12.6px;
  font-weight: 650;
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  box-shadow: none;
  transition: filter 0.15s ease;
}

.chat-input__send:hover {
  /* Explicit, because the bare button:hover sets a background this rule would
     otherwise inherit — the exact reason Send went purple in dark mode. */
  background: var(--accent-hover);
  filter: none;
}

.chat-input__send svg {
  display: none;
}

.chat-input__approve {
  white-space: nowrap;
  flex-shrink: 0;
  height: 34px;
  padding: 0 14px;
  border-radius: 9px;
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  font-size: 12.4px;
  font-weight: 650;
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  transition: background 0.15s ease;
}

.chat-input__approve:hover {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.chat-input__approve[hidden] {
  display: none !important;
}

/* -- Rail Files Section & Drop Zone -- */
.details__divider {
  display: none;
}

/* Artifacts follows the journey directly.

   It used to carry `margin-top: auto`, pinning it to the floor of the rail and
   opening a ~490px void mid-panel on a normal-length journey — the real defect
   behind the "seam" reported twice. The tinted ground was an attempt to make
   that pinned block read as a deliberate footer; with the void gone there is no
   footer to justify, and a tint here would read as a band across the middle of
   the card. A hairline is all the separation two stacked sections need.

   Leftover space now collects at the bottom of the card, where it reads as
   breathing room instead of a gap between two things that belong together. */
.rail-files,
.details__section:last-child {
  padding: 18px 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  border-top: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  background: transparent;
}

.rail-sec,
.panel__title {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 0 0 9px;
}

.panel__empty {
  margin: 0 0 0.5rem;
  font-size: 12px;
  color: var(--text-muted);
}

.file-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.file-list:empty {
  display: none;
}

.file-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--text) 5%, transparent);
  font-size: 12px;
  color: var(--text);
  cursor: pointer;
  transition: background 0.15s ease;
  border: none;
}

.file-item:hover,
.file-item.is-open {
  background: color-mix(in srgb, var(--text) 9%, transparent);
}

.file-item__icon {
  width: 13px;
  height: 13px;
  color: var(--accent);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.file-item__icon svg {
  width: 13px;
  height: 13px;
  color: var(--accent);
}

.file-item__main {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-item__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-item__badge {
  display: none;
}

.file-item__download,
.file-item__download-pdf {
  color: var(--text-muted);
  font-size: 11px;
  text-decoration: none;
  background: transparent;
  border: none;
  padding: 2px 4px;
  cursor: pointer;
  border-radius: 4px;
}

/* The download affordance is line art now, matching .file-item__icon rather
   than the ⬇️ emoji, which the OS painted as a filled blue tile. Sized to the
   file glyph beside it so the row reads as one icon set. */
.file-item__download {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 3px;
}

.file-item__download svg {
  width: 13px;
  height: 13px;
  display: block;
}

.file-item__download:hover,
.file-item__download-pdf:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--text) 10%, transparent);
}

.drop-zone,
.rail-drop {
  width: 100%;
  margin-top: 8px;
  padding: 10px;
  border: 1.5px dashed var(--border-strong);
  border-radius: 9px;
  background: transparent;
  color: var(--text-muted);
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  transition: border-color 0.15s, color 0.15s;
}

.drop-zone:hover,
.rail-drop:hover {
  border-color: var(--accent);
  color: var(--text);
  background: transparent;
}

/* ---- Account menu (avatar + dropdown, shared by projects/chat headers) ---- */
.account-menu {
  position: relative;
  flex-shrink: 0;
}

/* ---- Account avatar -----------------------------------------------------
   ONE definition. This was six rules across the file producing three
   different-looking controls depending on which page you were on: a 30px
   teal-gradient rounded square on projects, a 28px bordered circle in chat,
   and a 34px rounded square on Account & Usage. Two of those were page-scoped
   (.chat-header ..., .usage-who ...) and so outranked the base rule — the same
   specificity trap as the buttons.

   The circle is the one that was right: an avatar is a person, and a filled
   accent tile competed with the primary button sitting next to it. Only SIZE
   may be scoped per page below; never shape or fill. */
.account-menu__avatar {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  padding: 0;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 750;
  letter-spacing: 0.02em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: none;
  transform: none;
  cursor: pointer;
}

.account-menu__avatar:hover {
  background: var(--hover-surface);
  border-color: var(--border-strong);
  color: var(--text);
  box-shadow: none;
  transform: none;
}

.account-menu__dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 20;
  /* 220px forced "Account & Usage" to wrap onto two lines, which then collided
     with the spend figure sharing its row. */
  width: 264px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.28);
  padding: 6px;
  overflow: hidden;
}

.account-menu__dropdown[hidden] {
  display: none;
}

.account-menu__who {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 10px 12px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--border);
}



.account-menu__id {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.account-menu__name {
  font-size: 13px;
  font-weight: 650;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-menu__email {
  font-size: 11.5px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-menu__email[hidden] {
  display: none;
}

.account-menu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 8px;
  background: none;
  box-shadow: none;
  color: var(--text);
  font-family: inherit;
  font-size: 12.8px;
  font-weight: 550;
  text-align: left;
  cursor: pointer;
}

.account-menu__item svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--text-faint);
}

.account-menu__label {
  flex: 1;
  min-width: 0;
}

.account-menu__item:hover {
  background: var(--field-bg);
}

.account-menu__item:hover svg {
  color: var(--accent);
}

/* The usage row is a two-line block: label and spend, then the budget bar. */
.account-menu__item--usage {
  display: block;
  padding: 9px 10px 11px;
}

.account-menu__row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.account-menu__usage-inline {
  font-size: 11.5px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.account-menu__bar {
  display: block;
  margin-top: 8px;
  height: 4px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 10%, transparent);
  overflow: hidden;
}

.account-menu__bar i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.2s ease;
}

/* "Soon", not a silently greyed row — disabled with no explanation reads as
   broken rather than not-yet-built. */
.account-menu__soon {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--field-bg);
  border: 1px solid var(--border);
  color: var(--text-faint);
}

.account-menu__sep {
  height: 1px;
  background: var(--border);
  margin: 6px 4px;
}

.account-menu__item[hidden] {
  display: none !important;
}

.account-menu__item:disabled {
  color: var(--text-faint);
  cursor: default;
}

.account-menu__item:disabled svg {
  opacity: 0.5;
}

.account-menu__item:disabled:hover {
  background: none;
}

.account-menu__usage-inline {
  font-size: 0.72rem;
  color: var(--ink-faint);
  font-weight: 500;
}

/* ---- Budget-exceeded banner (chat view) ---- */


.budget-banner[hidden] {
  display: none;
}

/* ---- "A turn is still running" hint (chat view) ----
   Deliberately quieter than .budget-banner: that one is a hard block the user
   must act on, this is transient nudge that clears itself. */
.chat-busy-hint {
  /* Same box as .ask, so the hint lines up with the question card it sits on
     top of rather than running full-bleed past it. */
  width: 92%;
  max-width: 1180px;
  margin: 0 auto 8px;
  padding: 0.55rem 0.85rem;
  border-radius: 10px;
  background: var(--accent-soft);
  border: 1px solid var(--hairline);
  color: var(--ink-muted);
  font-size: 0.8rem;
}

/* A refused send is an error, not the quiet "still replying" nudge this
   element also serves — so it borrows the delete dialog's danger callout
   rather than sharing the muted treatment. */


.chat-busy-hint.is-alert svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--danger);
  margin-top: 1px;
}

.chat-busy-hint.is-alert strong {
  color: var(--danger);
  font-weight: 700;
}

/* Must outrank .chat-busy-hint.is-alert's own display — equal specificity
   would otherwise leave it visible while hidden. */
.chat-busy-hint[hidden] {
  display: none !important;
}

/* ---- Account & Usage view ---- */
#usage-view.view {
  flex-direction: column;
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 2.5rem 1.25rem;
}

.usage {
  width: 100%;
}

.usage__header {
  margin-bottom: 1.5rem;
}

.usage__back {
  /* inline-flex, not block: .rail__back is a pill whose chevron and label are
     laid out as flex children, and `display: block` collapsed that. */
  display: inline-flex;
  margin-bottom: 0.6rem;
}

.usage__header h1 {
  font-weight: 720;
  font-size: 1.6rem;
  /* --text, not accent: no other h1 in the product is coloured. */
  color: var(--text);
  margin: 10px 0 0;
}

/* The page is named for an account, so it should say which one. Mirrors the
   menu it opens from. */
/* Identity in the heading. The avatar and the title share a row, with the
   name and email as one quiet line beneath — replacing a separate .usage-who
   block that existed only to hold them. */
.usage__identity {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-top: 18px;
}

.usage__identity-text { min-width: 0; }

.usage__identity h1 {
  margin: 0;
  line-height: 1.2;
}

.usage__whoami {
  margin: 2px 0 0;
  font-size: 12.8px;
  color: var(--text-muted);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Separator between name and email, drawn only when both are present — the
   email is hidden when it duplicates the name. Spaced with margins, not with
   spaces inside `content`: leading/trailing whitespace in a generated string
   collapses, which rendered it as "Name ·email". */
.usage__whoami #usage-email::before {
  content: "\00B7";
  margin: 0 0.45em;
}
.usage-who__email[hidden] { display: none; }


/* Size only — this one is a profile display rather than a menu trigger, so it
   is a little larger. Shape and fill still come from the canonical rule. */
.usage__identity .account-menu__avatar {
  width: 42px;
  height: 42px;
  font-size: 13.5px;
}

.usage__subheading {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 26px 0 10px;
}

.usage-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  padding: 1.4rem 1.5rem;
}

.usage-card__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 0.2rem;
}

.usage-card__figure {
  display: flex;
  align-items: baseline;
  gap: 9px;
  flex-wrap: wrap;
}

/* The figure leads. It was previously the same size as its own label. */
.usage-card__amount {
  font-size: 26px;
  font-weight: 720;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.usage-card__of {
  font-size: 12.5px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.usage-bar {
  height: 6px;
  border-radius: 999px;
  /* Tokenised: --hairline resolved to a light track that stayed light on a
     dark page. */
  background: color-mix(in srgb, var(--text) 10%, transparent);
  overflow: hidden;
  margin-top: 12px;
}

.usage-bar__fill {
  height: 100%;
  background: var(--accent);
  border-radius: 999px;
  transition: width 0.2s ease;
}

.usage-bar__fill.is-over-budget {
  background: var(--error);
}

.usage-card__note {
  margin: 0.6rem 0 0;
}

.usage-table {
  border: 1px solid var(--hairline);
  border-radius: 12px;
  overflow: hidden;
}

/* A real table: the three unlabelled columns gave no clue that the middle one
   was a phase, and cost and tokens shared a cell so nothing lined up. */
.usage-tbl {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.8px;
}

.usage-tbl th {
  text-align: left;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding: 9px 14px;
  background: color-mix(in srgb, var(--text) 5%, transparent);
  white-space: nowrap;
}

.usage-tbl td {
  padding: 11px 14px;
  border-top: 1px solid var(--border);
}

.usage-tbl__num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.usage-tbl__phase {
  color: var(--text-muted);
}

.usage-tbl tfoot td {
  border-top: 1px solid var(--border-strong);
  font-weight: 700;
}

.usage-tbl tfoot[hidden] {
  display: none;
}

.usage-table__row.is-deleted {
  color: var(--text-faint);
  font-style: italic;
}

.usage-table__row.is-deleted .usage-tbl__phase {
  color: inherit;
}

.usage-table__row--head {
  background: var(--rail);
  font-weight: 700;
  color: var(--ink-muted);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.connection-card__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
}

.usage-card__note + .connection-card,
.connection-card + .connection-card {
  margin-top: 1rem;
}

/* margin-top: 0 is load-bearing. Only margin-bottom was set, so the browser's
   default `h3 { margin-top: 1em }` still applied — ~17px at this size — and it
   did NOT collapse away because the parent is a flex container. That was the
   gap above the team name. */
.connection-card__head h3 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  margin-top: 0;
  margin-bottom: 0.15rem;
}

/* "Optional" next to a connection's name — this connection selects an engine
   rather than gating anything, so it must not read as a required step. */
.connection-card__badge {
  display: inline-block;
  vertical-align: middle;
  margin-left: 0.45rem;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--rail);
  color: var(--ink-muted);
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.connection-card__status {
  font-size: 0.85rem;
  color: var(--ink-muted);
  white-space: nowrap;
}

.connection-card__status.is-connected {
  color: var(--success);
  font-weight: 600;
}

/* ----- Team (Account & Usage) ----- */

.team-card + .team-card,
.usage-card__note + .team-card,
.team-error + .team-card {
  margin-top: 1rem;
}

/* Every selector in this block that sets `display` needs its own [hidden]
   rule: an author declaration beats the UA's default [hidden] regardless of
   specificity. .team-card and .team-error are listed even though their own
   rules set no display, so that adding one later cannot silently un-hide
   them. */
.team-card[hidden],
.team-error[hidden],
.team-form[hidden],
#team-choose[hidden],
#team-gateway-url-row[hidden],
#team-gateway-help[hidden],
#team-key-row[hidden],
#team-key-copy[hidden] {
  display: none;
}

/* The key itself. It can be long, so it wraps rather than widening the card
   or overflowing it -- see .claude/rules/build-console-ui.md for the
   min-width: auto trap this avoids. */
.team-key__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.4rem;
}

.team-key__value {
  flex: 1 1 16rem;
  min-width: 0;
  overflow-wrap: anywhere;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface-sunken, var(--surface));
  color: var(--ink);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.85rem;
}

.team-meter__label {
  margin: 1.1rem 0 0.4rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
}

/* The first meter sits directly under the card head, which already has its
   own spacing. */
.team-card .team-meter__label:first-of-type {
  margin-top: 0.6rem;
}

.team-error {
  color: var(--danger, #b3261e);
}

.team-form {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.9rem;
}

.team-form__label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
}

.team-form__input {
  width: 100%;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
}

.team-form__input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Buttons in these forms size to their text rather than stretching the full
   card width the flex column would otherwise give them. */
.team-form .btn {
  align-self: flex-start;
  margin-top: 0.3rem;
}

.connection-card__help {
  margin: 0.75rem 0;
  font-size: 0.9rem;
  color: var(--ink-muted);
}

.connection-card__help summary {
  cursor: pointer;
  color: var(--accent-strong);
}

.connection-card__help ol {
  margin: 0.5rem 0 0 1.25rem;
  display: grid;
  gap: 0.35rem;
}

.connection-card__note {
  margin: 0.5rem 0 0;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  border-radius: 6px;
}

.connection-card__permissions {
  margin: 0.35rem 0 0.35rem 0;
  padding-left: 1rem;
  display: grid;
  gap: 0.25rem;
  list-style: disc;
}

.connection-card__screenshot {
  display: block;
  max-width: 480px;
  width: 100%;
  margin: 0.5rem 0;
  border: 1px solid var(--hairline);
  border-radius: 8px;
}

.connection-card__permission-level {
  display: inline-block;
  margin-left: 0.35rem;
  padding: 0.05rem 0.5rem;
  border: 1px solid var(--accent-strong);
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-weight: 600;
  font-size: 0.85em;
}

.connection-card__controls {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.connection-card__controls input {
  flex: 1;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--hairline);
  border-radius: 6px;
  background: var(--surface-solid);
  font-size: 0.9rem;
}

.connection-card__error {
  margin-top: 0.5rem;
  font-size: 0.85rem;
  color: var(--error);
}


@media (max-width: 960px) {
  .phase-frame {
    grid-template-columns: 1fr;
    /* An "auto" row here fights .details' own overflow-y:auto (which zeroes its
       automatic minimum size for grid track sizing purposes) and can collapse
       to near-zero height depending on engine track-sizing quirks. Matching the
       row track to .details' own max-height below keeps the row size definite
       and sidesteps that whole class of bug. */
    grid-template-rows: 45vh 1fr;
  }

  .details {
    border-right: none;
    border-bottom: 1px solid var(--hairline);
    max-height: 45vh;
  }

  .rail-top,
  .details__section:first-child {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  .rail__phases,
  .rail__substeps {
    flex: 1 1 100%;
  }

  .message {
}
    max-width: 92%;
  }


.build-pane {
  overflow-y: auto;
}

/* One definition. A second further down set a different max-width (860px) and
   different padding, so the column width depended on which rule you read. */
.build-pane__scroll {
  max-width: 820px;
  margin: 0 auto;
  padding: 24px 20px 48px;
  display: grid;
  gap: 14px;
}

/* A grid item's default min-width:auto is its min-content width, and
   .build-console's `white-space: pre` makes min-content equal max-content —
   so one long build log line stretches its whole card past this 720px column
   instead of scrolling inside the console. That overflow then surfaces as a
   *panel-wide* horizontal scrollbar, because .build-pane's `overflow-y: auto`
   makes the computed overflow-x `auto` too, dragging the repo card and step
   timeline sideways with it. Same min-*-auto trap as .details above. */
.build-pane__scroll > * {
  min-width: 0;
}

/* The mockup's .panel, verbatim: 14px radius, 18px/20px padding, a plain
   --surface ground and a hairline. This selector was defined FOUR separate
   times across the file (radius 18px here, background and a drop shadow 750
   lines later, radius again after that, and a dark-theme override on top), so
   its final look was assembled from four places and matched none of them.

   Three of these sections also carried .usage-card, borrowed purely for its
   padding back when .build-card had none. That class then won on later
   ordering and handed them the usage page's 18px radius and its own ground,
   so half the build panels were shaped like usage cards. The class is off
   them now; this rule is the only thing styling a build panel.

   No box-shadow: the mockup's panels sit flat on the page ground, and the
   shadow that was here read as a glow against the dark theme. */
.build-card {
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
  padding: 18px 20px;
  box-shadow: none;
}

.build-card h3 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  margin-bottom: 0.35rem;
}

/* Matches .build-console exactly: same ground, same border, same 9px radius,
   same type. It had no font-family of its own, so a <code> element fell back
   to the UA's generic `monospace` — Courier on macOS — while the console two
   inches away rendered in SF Mono. That is the "suddenly a different font":
   two code surfaces on one panel in two different typefaces, at 13.6px and
   11.5px. It was also the sharpest corner on the pane at 6px. */
.build-clone-cmd {
  display: block;
  padding: 10px 12px;
  border: 1px solid var(--code-border);
  border-radius: 9px;
  background: var(--code-bg);
  color: var(--code-text);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px;
  line-height: 1.65;
  overflow-x: auto;
  white-space: nowrap;
  margin: 0.5rem 0;
}

/* Every key ./deploy.sh wrote to outputs.json, not only app_url. Title
   matches .design-picker__title's register; each row's value reuses
   .build-clone-cmd's exact code-box treatment (same ground, border, radius,
   type) rather than a bare <dl>/<dd> -- a value here is often a long
   unbroken ARN or URL, and a browser's own dl/dd indent reads as plain
   wrapped text with no visual separation between entries. */
.build-outputs {
  margin: 0.9rem 0 0.35rem;
}

.build-outputs__title {
  cursor: pointer;
  list-style: none;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--ink);
  user-select: none;
}

.build-outputs__title::-webkit-details-marker {
  display: none;
}

.build-outputs__title::before {
  content: "\25b8";
  display: inline-block;
  width: 14px;
  transition: transform 0.15s ease;
}

.build-outputs[open] > .build-outputs__title::before {
  transform: rotate(90deg);
}

.build-outputs__list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 0.35rem;
}

.build-output-row {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.build-output-row__key {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
}

.build-output-row__value {
  display: block;
  padding: 6px 10px;
  border: 1px solid var(--code-border);
  border-radius: 9px;
  background: var(--code-bg);
  color: var(--code-text);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px;
  line-height: 1.5;
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* Scoped through the list wrapper, not just the element+class pair, so this
   beats .build-card a on specificity. Both rules matched an output link at
   equal specificity (one class + one element each); source order then let
   the later .build-card a win, repainting the link in --accent-strong — a
   dark indigo in light mode — over the code box's dark --code-bg. Nearly
   unreadable: dark text on a near-black box, in light mode only, and only
   for an output value that happens to look like a URL. */
.build-outputs__list a.build-output-row__value {
  color: var(--accent);
  text-decoration: none;
}

.build-outputs__list a.build-output-row__value:hover {
  text-decoration: underline;
}

/* The second .tab-bar__dot definition that lived here is folded into the
   canonical rule above. */

/* An author `display` declaration beats the UA [hidden] rule at any
   specificity, so anything hideable needs its own opt-out. */
/* Not optional: the rule above sets `display`, which outranks the UA's
   [hidden]. */
.tab-bar__dot[hidden] {
  display: none !important;
}

/* Live build output. Every element here can receive `hidden`, and an author
   `display` declaration silently beats the browser's [hidden] UA rule — so
   each display-setting selector ships its own [hidden] companion. */
/* No margin-top: .build-live is a flex column with its own 14px gap, and this
   margin stacked on top of it. Between "Started 2m ago" and the timeline there
   were four separate spacing mechanisms in a row — the meta's 24px bottom
   margin, the column's gap, this margin, and .build-steps' own 6px — adding up
   to a 56px dead band mid-card. The gap owns the spacing now. */
.build-run__live {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.build-run__live[hidden] { display: none; }

/* ---- Build step timeline -------------------------------------------------
   Values from specs/reference/mockups (build & deploy states, section 5).

   These were five detached pills on a wrapping flex row: at a narrow width
   they reflowed into two ragged lines and the order — the only thing a step
   list conveys — stopped being readable.

   The connector is drawn per row, not once for the whole list, so the first
   and last rows can render a half segment and the sequence stops cleanly at
   both ends instead of trailing into space. */
.build-steps {
  display: grid;
  margin: 0;
  list-style: none;
  padding: 0;
}
.build-steps[hidden] { display: none; }

.build-step {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 12px;
  position: relative;
  padding: 7px 0;
}

.build-step__rail {
  position: relative;
  display: flex;
  justify-content: center;
}

/* Overshoots by exactly the row's padding so consecutive segments meet. */
.build-step__rail::before {
  content: "";
  position: absolute;
  top: -7px;
  bottom: -7px;
  width: 2px;
  background: var(--border-strong);
}
.build-step:first-child .build-step__rail::before { top: 50%; }
.build-step:last-child .build-step__rail::before { bottom: 50%; }

.build-step__dot {
  position: relative;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  color: var(--text-faint);
  box-sizing: border-box;
}

.build-step.is-done .build-step__dot {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.build-step.is-current .build-step__dot {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
}

.build-step.is-failed .build-step__dot {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--danger-ink);
}

.build-step__body {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
}

.build-step__name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

.build-step:not(.is-done):not(.is-current):not(.is-failed) .build-step__name {
  color: var(--text-faint);
  font-weight: 500;
}

.build-step.is-current .build-step__name { color: var(--accent); }
.build-step.is-failed .build-step__name { color: var(--danger); }

/* Per-step timing, right-aligned. The backend does not report it yet, so this
   renders only when a duration is actually present rather than showing a
   placeholder. */
.build-step__time {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.build-step__log {
  grid-column: 2;
  margin: 8px 0 2px;
}

.build-console {
  background: var(--code-bg);
  color: var(--code-text);
  border: 1px solid var(--code-border);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px;
  line-height: 1.65;
  border-radius: 9px;
  padding: 12px 14px;
  margin: 0;
  /* The mockup shows 150px against a static four-line sample. This is a live
     log that the user reads while a build runs, so it keeps the taller cap. */
  max-height: 260px;
  overflow: auto;
  white-space: pre;
}
.build-console[hidden] { display: none; }

/* ── Build & Deploy: live hero, pending approvals, history ──
   Direction A + C's pending block — see
   docs/superpowers/specs/2026-07-31-build-panel-layout-design.md.
   Every display-setting selector below ships a [hidden] companion: an author
   `display` declaration silently beats the browser's [hidden] UA rule. */

.build-live {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.build-live[hidden] { display: none; }

.build-live__head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.build-live__head[hidden] { display: none; }

/* ---- Status chips --------------------------------------------------------
   One vocabulary, used by the live run header AND every history row, so a
   running run and a finished one report themselves the same way. Values from
   the mockup's .chip: bordered rather than a bare tint, which is what lets a
   chip hold its edge on both the card ground and the page ground.

   --wait is the amber one, deliberately: an approval that is blocking the
   pipeline is a genuine "waiting on you" state. (The question card in chat is
   NOT amber — that one is neutral + accent; different component, see
   frontend/CLAUDE.md.) */
.build-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* line-height: normal, not 1.45. The chip is uppercase-only, so it has no
     descenders — but a 1.45 line box still reserves descender space beneath
     them, and `align-items: center` centres that BOX, not the ink. Measured
     1.22px of it, which is what reads as the label sitting high in the pill.
     Font-metric line-height plus symmetric padding measures 0.00px off. */
  padding: 4px 9px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
  line-height: normal;
}
.build-chip[hidden] { display: none; }

.build-chip--run,
.build-chip--building,
.build-chip--diagnosing {
  color: var(--accent-strong);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 34%, transparent);
}

.build-chip--wait,
.build-chip--pending_approval {
  color: var(--warn-chip);
  background: color-mix(in srgb, var(--warn) 15%, transparent);
  border-color: color-mix(in srgb, var(--warn) 35%, transparent);
}

.build-chip--ok,
.build-chip--deployed {
  color: var(--success);
  background: color-mix(in srgb, var(--success) 14%, transparent);
  border-color: color-mix(in srgb, var(--success) 34%, transparent);
}

.build-chip--bad,
.build-chip--failed {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, transparent);
  border-color: color-mix(in srgb, var(--danger) 34%, transparent);
}

.build-chip--off,
.build-chip--cancelled,
.build-chip--destroyed {
  color: var(--text-faint);
  background: color-mix(in srgb, var(--text) 7%, transparent);
  border-color: var(--border);
}

.build-pending {
  display: flex;
  flex-direction: column;
  gap: 10px;
  border-color: var(--accent);
  background: var(--accent-soft);
}
.build-pending[hidden] { display: none; }

.build-pending__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.build-pending__head[hidden] { display: none; }

/* Cancel sits left of Approve so the destructive-of-intent action is never
   the one under the thumb, and both stay together when the head wraps. */
.build-pending__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.build-pending__actions[hidden] { display: none; }

.build-pending__items {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding-left: 22px;
  font-size: 13px;
}
.build-pending__items[hidden] { display: none; }
.build-pending__note { font-size: 12px; }

/*
  A budget refusal on a build action. Deliberately NOT styled as .error (plain
  red text): nothing is broken and nothing else is blocked — it's a spending
  limit the user can act on. Reads as a warm notice, terracotta accent rather
  than the error red, so it's visually distinct from a real failure.
  The [hidden] override is mandatory — this rule sets `display`, which
  otherwise silently beats the UA stylesheet's [hidden] rule (the trap
  .tab-bar__tab hit before).
*/
.build-blocked {
  display: block;
  margin: 0.9rem 0 0;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--hairline);
  border-radius: 6px;
  /* Same swap as .connection-card__note (bea002e): the accent side-bar and
     asymmetric radius are gone in favour of an even hairline box. The accent
     tint stays, unlike on that note — this one is a refusal, and the neutral
     surface-2 treatment would read as ordinary explanatory copy. */
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-size: 0.85rem;
  line-height: 1.5;
}
.build-blocked[hidden] { display: none; }

.build-history {
  display: flex;
  flex-direction: column;
}
.build-history[hidden] { display: none; }

/* ---- History rows --------------------------------------------------------
   Flex, not the old six-column grid. The grid existed to line up a
   right-aligned outcome sentence across rows; with the chip leading instead,
   the status column lines up on its own and the run name is free to be as long
   as it is. Values from the mockup's .hist__row. */
.build-history__row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  /* No horizontal padding: rows sit flush with the card's own content edge, in
     line with the card title and with every section above. The 2px it used to
     carry, plus an always-rendered empty connector column, pushed the whole
     list 26px right of everything else. Vertical 11px is the mockup's. */
  padding: 11px 0;
  border: none;
  border-top: 1px solid var(--border);
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
  /* So a failure preview can take its own line beneath the row. */
  flex-wrap: wrap;
  position: relative;
}
.build-history__row[hidden] { display: none; }
.build-history__row:first-of-type { border-top: none; }
.build-history__row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The hover/expanded fill is inset and rounded. It used to be a square-edged
   band running the full width of the card — the fill inherited the row's
   `border-radius: 0`, so a rounded 14px panel had a hard rectangle stamped
   across it. Drawn on a pseudo-element so the separator above stays a straight
   hairline: putting the radius on the row itself curves the border-top too. */
.build-history__row::before {
  content: "";
  position: absolute;
  inset: 1px -8px 1px -8px;
  border-radius: 9px;
  background: transparent;
  pointer-events: none;
  z-index: 0;
}
.build-history__row:hover::before,
.build-history__row[aria-expanded="true"]::before {
  background: var(--hover-surface);
}
/* Content above the fill. */
.build-history__row > * { position: relative; z-index: 1; }

/* A retry/proposed_fix/push_redeploy is always a reaction to the run before
   it, never a user's own first action — nudged in with its connector glyph so
   a failure -> retry -> fix chain reads as one story rather than three
   unrelated rows. */
/* A follow-up is nested by its connector glyph and the flex gap, not by extra
   padding — otherwise the indent is applied twice. */
.build-history__row--followup { padding-left: 0; }

.build-history__connector {
  flex: none;
  width: 14px;
  color: var(--text-faint);
  font-size: 13px;
  text-align: center;
}

.build-history__type {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Which step it failed at. This was inside the chip ("Failed during the deploy
   step"), where it made the chip a sentence; as quiet meta beside the name it
   stays available without competing with the status.

   NOT .build-history__detail — that class already names the expanded detail
   panel below the row, and reusing it silently restyled the panel too. */
.build-history__failedat {
  color: var(--text-faint);
}

.build-history__when {
  margin-left: auto;
  flex: none;
  color: var(--text-faint);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* A static chevron rotated by aria-expanded, the row's own state -- signals
   the row is expandable at all, which nothing previously indicated. */
.build-history__chevron {
  flex: none;
  color: var(--text-faint);
  font-size: 15px;
  text-align: center;
  transition: transform 0.15s ease;
}
.build-history__row[aria-expanded="true"] .build-history__chevron {
  transform: rotate(90deg);
}

/* A short, always-visible reason preview beneath the main row -- spans
   every column so it isn't squeezed into any one of them. Ellipsis rather
   than JS-side truncation so it degrades at any panel width. */
/* Its own line under the row. `grid-column: 1 / -1` did this while the row was
   a grid; the row is flex now, so full width comes from the basis instead —
   left as an inline element it simply sat beside the timestamp. */
/* Slightly smaller and quieter than the detail prose — it is a truncated
   teaser inside the collapsed row, not the detail itself — but the same grey
   family rather than the legacy --ink-faint alias. */
.build-history__preview {
  flex-basis: 100%;
  margin-top: 2px;
  padding-left: 26px;
  color: var(--text-faint);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Links on the build panel.

   styles.css has no global `a` rule at all, so every link here — the repo
   name, "Live app", "Deployed at" — fell through to the UA default and
   rendered in browser blue/purple with a default underline. That is three
   links on one panel not speaking the design language.

   Scoped to the component rather than fixed globally: a bare `a {}` rule would
   reach chat markdown, artifacts and the login view at once, and .markdown-body
   already defines its own. */
.build-card a {
  color: var(--accent-strong);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  word-break: break-all;
}

.build-card a:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ---- Build panel body copy ------------------------------------------------
   The build pane uses .subtitle in nine places for its explanatory copy — the
   locked notice, the bootstrap blurb, "Iterate locally...", "Started 2m ago",
   the diagnosing line, the pending copy and note, and the empty-history state.
   .subtitle is the LOGIN CARD's lede: 0.92rem (14.7px), --ink-muted, and a
   1.5rem bottom margin. At that size it outweighs the 12.8px it sits beside,
   and its margin stacked on top of the flex gaps in .build-live to open a 56px
   dead band between "Started 2m ago" and the timeline.

   Retreated to the mockup's .panel__meta rather than renamed in nine places:
   the markup is fine, the borrowed class was the mistake. Zero margin inside
   .build-live, which is a flex column that owns its own spacing. */
.build-card .subtitle {
  font-size: 12.8px;
  line-height: 1.5;
  color: var(--text-muted);
  margin: 0 0 14px;
}

.build-live .subtitle {
  margin: 0;
}

/* ---- One type treatment for everything inside an expanded row -------------
   The detail panel had four: .subtitle borrowed from the login card (14.7px,
   --ink-muted, plus a 1.5rem bottom margin fighting the flex gap), the
   deployed-at line at 12.8px --text-muted, the in-row preview at 12px
   --ink-faint, and the console at 11.5px mono. Four sizes and three greys for
   what is all one register: quiet supporting detail under a row.

   Prose is 12.8px --text-muted, margin 0 (the flex column's gap owns spacing).
   The console keeps its own mono treatment — that is a different register, not
   a fifth variant. */
.build-history__note,
.build-history__deployed-at,
.build-history__detail p {
  margin: 0;
  font-size: 12.8px;
  line-height: 1.5;
  color: var(--text-muted);
}

/* The teaser is the collapsed state's job. Once the row is open the same
   sentence is right below it in full, so leaving both on screen showed the
   failure twice in two different treatments — the single loudest source of the
   "too many designs" read on this panel. */
.build-history__row[aria-expanded="true"] .build-history__preview {
  display: none;
}

.build-history__detail {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 2px 14px;
}

/* Children stay stretched. `align-items: flex-start` was set here to stop
   Retry deploy spanning the card, and it sized every child to its CONTENT
   instead — so the console, a <pre> with `white-space: pre`, grew to the width
   of its longest line and pushed straight out of the card. Only the button
   needs to shrink-wrap, so only the button says so.

   min-width: 0 is the other half: a flex child's automatic minimum size is its
   min-content width, which for a `pre` equals its max-content width. The
   child's own `overflow: auto` zeroes that for the child's own scrolling but
   not for its contribution to the parent. Same trap as the build console's
   2026-08-01 sideways-scrolling panel — see .claude/rules/build-console-ui.md. */
.build-history__detail > * {
  min-width: 0;
  max-width: 100%;
}

/* Actions sit on one row, shrink-wrapped. A flex column stretches its
   children, so a lone button spanned the whole card and read as a banner. */
.build-history__actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-self: flex-start;
}

.build-history__detail > button {
  align-self: flex-start;
}
.build-history__detail[hidden] { display: none; }

/* ==========================================================================
   RISE visual system - soft glass, cloud wash, and enterprise clarity.
   Palette: 70% neutral wash / 20% primary purple / 10% navy + warm orange.
   The application remains dependency-free; this layer only changes presentation.
   ========================================================================== */

:root {
  color-scheme: light;

  /* -- v2 token vocabulary. The reference mockup artifact (v5-artifact-mockups.html)
     is authoritative for both light and dark themes. The dark block far below
     redefines these same names and nothing else, so a component written against
     var(--token) needs no per-theme override rule. Legacy aliases at the end of
     this block keep the pre-v2 names working while call sites migrate. -- */
  --bg: #F6F6FB;
  --surface: #ffffff;
  --surface-2: #F1F1F8;
  /* Code blocks are a dark panel in both themes — same call as the rail. A
     surface-derived grey barely separates from the bubble in dark, and it is
     what made pasted code unreadable. Fixed values on purpose. */
  --code-bg: #1B2027;
  --code-text: #E7EDF1;
  --code-border: color-mix(in srgb, var(--text) 10%, transparent);
  --code-inline-bg: rgba(20, 20, 50, 0.07);
  --code-inline-text: #2C2A56;
  --surface-solid: #ffffff;
  --rail: rgba(248, 248, 255, 0.9);
  --border: rgba(20,20,50,0.12);
  --border-strong: rgba(20,20,50,0.2);
  --text: #14142F;
  --text-muted: #66667f;
  --text-faint: #9797ab;
  --field-bg: rgba(20,20,50,0.03);
  --accent: #5B54F5;
  /* Hover ground for neutral controls, and the user's own chat bubble — the
     same value, because a hover is "this is the thing you're pointing at" and
     a user bubble is "this is the thing you said". */
  --hover-surface: #EDEBFC;
  /* Primary buttons darken their own accent instead of jumping to an unrelated
     indigo, which is what the old hardcoded button:hover did. */
  --accent-hover: #4A44E0;
  /* Modal/overlay scrim. */
  --scrim: rgba(15, 46, 48, 0.35);
  --accent-2: #000099;
  --accent-ink: #fff;
  --accent-soft: rgba(91, 84, 245, 0.1);
  --accent-strong: #322e87;
  --success: #218A67;
  --success-bg: #DEF3EA;
  --warn: #FF955A;
  --warn-ink: #7A3B12;
  --warn-bg: #FFE7D8;
  --warn-chip: #8A431A;
  --danger: #C4433F;
  --danger-ink: #fff;
  --danger-bg: #FBE4E2;

  /* Backgrounds: the login hero gradient, the login particle-network's
     colour/alpha, and the three blurred blobs behind projects/chat. */
  --hero-gradient: linear-gradient(160deg, #EEEDFD 0%, #DCE4FF 45%, #D8F0EC 100%);
  --particle-rgb: 91,84,245;
  --particle-line-alpha: 0.22;
  --particle-dot-alpha: 0.4;
  --blob-a: #A8C1FF;
  --blob-b: #5B54F5;
  --blob-c: #FF955A;
  --blob-opacity-a: 0.4;
  --blob-opacity-b: 0.22;
  --blob-opacity-c: 0.2;

  --shadow: 0 10px 28px rgba(39,36,110,0.1);
  --shadow-card: 0 22px 70px rgba(39, 36, 110, 0.13), 0 3px 12px rgba(39, 36, 110, 0.06);
  --shadow-soft: 0 12px 36px rgba(39, 36, 110, 0.1);

  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-display: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Legacy aliases — pre-v2 names still referenced by call sites not yet
     migrated. Defined in terms of the new tokens so both stay in sync and
     dark mode only ever has to redefine the new vocabulary above. */
  --paper: var(--bg);
  --hairline: var(--border);
  --ink: var(--text);
  --ink-muted: var(--text-muted);
  --ink-faint: var(--text-faint);
  --deep-navy: var(--accent-2);
  --success-soft: rgba(33, 138, 103, 0.11);
  --error: var(--danger);
  --sky-tint: #a8c1ff;
  --warm-orange: var(--warn);
}

html {
  background: var(--bg);
}

/* The page ground. This is the one place a literal multi-stop gradient is
   still spelled out rather than tokenised — it is the hero ground itself, and
   --hero-gradient is exactly that value, redefined per theme. */
body {
  position: relative;
  overflow-x: hidden;
  background: var(--hero-gradient);
  color: var(--text);
}

/* body::before (a dotted-grid texture) and body::after (a fixed, ANIMATED
   purple blob) used to live here. Both hardcoded light-mode colours —
   rgba(168,193,255), rgba(148,144,248), rgba(91,84,245) — and both were
   switched off in dark with a `display: none` override rather than removed.

   That is the shape of the bug, not just its symptom: a decoration that only
   one theme can show is a decoration the design does not actually want. Hiding
   it in the other theme keeps a 16s infinite animation running in the DOM of
   every page, and leaves the two themes rendering different pages.

   Gone. Atmosphere now comes from .blob elements, which are token-driven and
   therefore the same code in both themes. */

.view {
  position: relative;
  z-index: 1;
}


h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  letter-spacing: -0.025em;
}

/* A bare element selector, so this reaches every <label> in the app whether or
   not it opted in. The hardcoded #4e5368 is why labels stayed slate-grey on
   the dark theme while everything around them lightened. */
label {
  color: var(--text-muted);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.045em;
  text-transform: uppercase;
}

/* Bare element selectors, so these reach every field in the app. They used to
   hardcode a near-white ground and an indigo-tinted border mixed for the light
   page, overriding the tokenised `input` rule near the top of the file — which
   is why fields stayed pale on the dark theme. */
input,
textarea {
  background: var(--field-bg);
  border-color: var(--border);
  border-radius: 12px;
}

input:hover {
  border-color: color-mix(in srgb, var(--accent) 36%, transparent);
}

input:focus-visible {
  outline: 3px solid rgba(91, 84, 245, 0.2);
  border-color: var(--accent);
}

/* ---- Bare <button> is a RESET, not a style -------------------------------
   This used to paint every <button> in the app as if it were a primary: an
   --accent ground, a 12px radius, a 0 8px 20px accent glow and a hover lift.

   Real buttons never showed it — the class-based system (.button-*, .btn,
   .modal-btn) sets its own fill and `box-shadow: none`, and a class beats an
   element selector. So it only ever surfaced on the <button>s that are NOT
   controls in that sense: history rows, project rows, tab strips, icon
   buttons. That is precisely why the glows looked random — they appeared on
   the things that don't look like buttons.

   The rule's own comment recorded that it had already eaten five separate
   component fixes. It is a reset now: anything that should look like a button
   asks for it by class. */
button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
  transform: none;
  cursor: pointer;
}

button:hover,
button:active {
  box-shadow: none;
  transform: none;
}

button:focus-visible {
  outline: 3px solid rgba(168, 193, 255, 0.72);
  outline-offset: 2px;
}

.button-secondary,
.link-button,
.project-list__trash,
.account-menu__item,
.tab-bar__tab,
.chat-input__approve,
.file-item__download,
.file-item__download-pdf {
  box-shadow: none;
}

.link-button {
  color: var(--accent-strong);
  font-weight: 650;
  text-decoration: none;
}

.link-button:hover {
  color: var(--accent);
  transform: none;
}


/* Usage dashboard */
#usage-view.view {
  width: 100%;
  max-width: none;
  padding: clamp(1.25rem, 4vw, 3.5rem);
}

.usage {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
}

/* The page heading. It used to be a saturated blue gradient slab with
   `color: white` — but `.usage__header h1` sets `color: var(--text)`, which is
   near-black in light, so the title read as dark-on-blue and was effectively
   unreadable. The dark theme already neutralised the whole thing
   (`background: none`), so the two themes were showing different designs.

   Now there is one design: a heading on the page ground, in both themes. That
   also retires the ::after blob and the white-alpha link colour, which only
   existed to sit on the gradient. */
.usage__header {
  position: relative;
  padding: clamp(1.25rem, 3vw, 2rem) 0 0;
  margin-bottom: 1.5rem;
}

/* ---- Stale light-only overrides, tokenised -------------------------------
   Everything below used to hardcode a colour mixed for a white page —
   rgba(255,255,255,.92) card grounds, an rgba(9,9,57,.46) scrim, a
   near-opaque white dropdown — while an earlier rule for the same selector
   already used the right token. The later rule won, so these surfaces never
   themed: the account dropdown in particular rendered a white panel with
   var(--ink) text on the dark theme. Same duplicate-definition trap as the
   buttons and the build console. */
/* .usage-card and .usage-table were left dangling here by a string-based edit
   that removed .build-card from what was a GROUPED selector — it matched the
   tail of the group, deleted `.build-card {` and its body, and merged the two
   orphaned selectors into the .modal-overlay rule below. They were painted
   with --scrim, the modal-overlay wash, plus an 8px backdrop blur: a dark teal
   over a light page, which is why every card on Account & Usage rendered as a
   grey slab and its table header fell to 1.18:1.

   Do not edit CSS by matching a rule's text without checking whether its
   selector is part of a comma-separated group. */
.usage-card,
.usage-table {
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-card);
}

.modal-overlay {
  background: var(--scrim);
  backdrop-filter: blur(8px);
}

.modal-card {
  border: 1px solid var(--border-strong);
  border-radius: 22px;
  box-shadow: var(--shadow);
}

/* The gradient fill that used to be declared here is gone. It made the avatar
   read as a second primary button beside "+ New Project". */
.account-menu__avatar:active {
  transform: none;
}


.account-menu__dropdown {
  z-index: 100;
  color: var(--text);
  background: var(--surface-solid);
  border-color: var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow);
}

.rail__phase-icon img {
  display: block;
  width: 22px;
  height: 22px;
  object-fit: contain;
}


@media (prefers-reduced-motion: reduce) {
  #projects-view::before,
  #projects-view::after {
    animation: none;
  }
}


/* Usage and build surfaces */
.usage {
  max-width: 980px;
}

.usage-card,
.usage-table {
  border-radius: 18px;
}

.usage-table__row--head {
  color: var(--text-muted);
  background: var(--surface-2);
}

/* Was #17674d — a light-mode green that stayed dark green on the dark theme's
   dark chip ground, i.e. unreadable exactly where it mattered. */
/* .build-chip--deployed used to be bundled in here, redeclaring a colour the
   chip vocabulary already owns from outside that block. Connections only. */
.connection-card__status.is-connected {
  color: var(--success);
}



/* The mockup's "Ready for your review" callout. It shows this inline in the
   chat log, but agents have no way to emit such a card today (that would
   need prompt changes across all four runtimes) — so the treatment is
   applied to the one real review moment the app already has: the Build
   tab's pending-approval card. */
.build-pending {
  border: 1px solid color-mix(in srgb, var(--accent) 42%, transparent);
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  border-radius: 12px;
  padding: 13px 15px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.build-pending[hidden] {
  display: none !important;
}

.build-pending::before {
  content: "Ready for your review";
  display: block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--accent);
}

.build-pending h3,
.build-pending h4,
#build-pending-title {
  font-size: 13.4px;
  font-weight: 680;
  margin: 0;
  color: var(--text);
}

.build-pending .subtitle,
#build-pending-copy,
#build-pending-note {
  font-size: 11.8px;
  color: var(--text-muted);
  margin: 0;
}

.build-pending button,
#build-pending-approve {
  margin-top: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: 9px;
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  font-size: 12.4px;
  font-weight: 680;
  cursor: pointer;
  align-self: flex-start;
  transition: background 0.13s ease, color 0.13s ease;
}

.build-pending button:hover,
#build-pending-approve:hover {
  background: var(--accent);
  color: var(--accent-ink);
}

/* A second .build-console definition lived here — a hardcoded #111134 indigo
   that silently beat the canonical rule ~700 lines above and never themed. The
   canonical one now owns the console's colour; see it for the tokens. */


@media (max-width: 960px) {
  .phase-frame {
    flex-direction: column;
    height: auto;
    min-height: 100vh;
  }

  .details {
    width: 100%;
    max-height: 42vh;
  }

  #login-view.view {
    align-items: flex-start;
    padding: 0;
  }

  .usage__header {
    align-items: stretch;
    flex-direction: column;
  }

  .chat-input {
    flex-wrap: wrap;
    margin: 0 0.75rem 0.75rem;
    padding: 0.75rem;
  }

  .chat-input__field {
    flex-basis: 100%;
  }

  .chat-input__approve {
    flex: 1;
  }

  .message {
    max-width: 92%;
  }

}

@media (max-width: 520px) {
  #usage-view.view {
    padding: 0.75rem;
  }

  .phase-frame {
    /* NOT 100vh. On iOS Safari 100vh is the chrome-*hidden* height, so the
       frame ends up ~190px taller than the visible area and the composer sits
       below the fold — the exact failure the dvh on .phase-frame above exists
       to prevent, undone here. */
    height: calc(100dvh - 1.4rem);
    border-radius: 18px;
  }

  .rail-top,
  .details__section:first-child {
    align-items: flex-start;
  }

  .chat-header {
    padding: 0 0.75rem;
  }

  .messages,
  .doc-pane__content {
    padding: 1rem;
  }

  .chat-input__approve {
    display: none;
  }
}


/* =========================================================
   THEME SWITCHING & DARK MODE OVERRIDES
   ========================================================= */

/* Theme Visibility Rules */
:root[data-theme="light"] .dark-only,
html:not([data-theme="dark"]) .dark-only {
  display: none !important;
}

/* Theme asset swap: hide light logo variant on dark ground */
:root[data-theme="dark"] .light-only {
  display: none !important;
}



/* ==========================================================================
   v2: theme toggle, login card, particle canvas, glass surface
   All colours come from the token blocks — no per-theme rules needed here.
   ========================================================================== */

/* One icon-only control, same fixed spot on every view. */
.theme-toggle {
  position: fixed;
  top: 16px;
  right: 18px;
  z-index: 250;
  width: 38px;
  height: 38px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow);
  transition: color 0.15s ease, border-color 0.15s ease;
}

.theme-toggle:hover {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  transform: none;
}

.theme-toggle svg {
  width: 16px;
  height: 16px;
}

/* Shared translucent surface used by the login card, project rows, the chat
   rail and the composer. */
.glass {
  background: color-mix(in srgb, var(--surface) 62%, transparent);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  box-shadow: var(--shadow-card);
}

/* ---- Login ---- */
/* An id+class selector setting `display` outranks .view[hidden]'s
   `display:none`, so hiding this view would silently do nothing without its
   own [hidden] rule at equal-or-higher specificity. Same trap the Build tab
   hit before — any rule here that sets display needs this companion. */
#login-view.view[hidden] {
  display: none;
}

#login-view.view {
  position: relative;
  overflow: hidden;
  min-height: 100vh;
  display: flex;
  /* Top-anchored, not centred: the brand holds its position when the card
     grows (a sign-in error line appearing), instead of the whole block
     sliding up the screen. */
  align-items: flex-start;
  justify-content: center;
  padding: 0;
  background: var(--hero-gradient);
}

#login-view #net {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Deliberately NOT a card: no card container around the login column —
   the gradient is the page. */
.login-card {
  position: relative;
  z-index: 1;
  width: min(384px, 90vw);
  padding: clamp(48px, 11vh, 104px) 0 72px;
  margin: 0;
  text-align: center;
  align-self: flex-start;
  box-sizing: border-box;
}

.login-card__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.login-card__logo {
  height: 92px;
  width: auto;
  max-width: none;
  display: block;
  flex-shrink: 0;
}

.login-card__wordmark {
  margin-top: 18px;
  font-size: 25px;
  font-weight: 750;
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  line-height: 1;
  color: var(--text);
  display: block;
}

.login-card__tagline {
  margin-top: 12px;
  margin-bottom: 0;
  font-size: 13px;
  font-weight: 450;
  color: var(--text-muted);
}

.login-card .auth-step {
  text-align: left;
}

.auth-step[hidden] {
  display: none;
}

#login-form[hidden] {
  display: none;
}

.auth-step-stack {
  display: grid;
  margin-top: 18px;
}

.auth-step-stack > .auth-step {
  grid-area: 1 / 1;
  min-width: 0;
  margin-top: 0;
}

.auth-step-stack > .auth-step[hidden] {
  display: block;
  visibility: hidden;
  pointer-events: none;
}

.auth-step-stack:not(:has(> .auth-step:not([hidden]))) {
  display: none;
}

/* Field rhythm matches the mockup: exactly one 18px gap per field group, set
   on the label, with the input contributing no margin of its own. The app's
   markup is a flat label/input sequence rather than the mockup's wrapper div,
   so the wrapper's `margin-top: 18px` lands on the label instead. Inherited
   margins from the generic input rules are zeroed here or they stack (they
   were adding up to a 105px label-to-label step against the mockup's 82px). */
.login-card .field-label {
  display: block;
  font-size: 11px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  margin-top: 18px;
  margin-bottom: 7px;
  text-align: left;
}

.login-card .field,
.login-card .auth-step input {
  margin-top: 0;
  margin-bottom: 0;
}

/* .auth-step-stack already supplies the 18px below the brand block, so the
   first label must not add a second one (that stacked to 36px). */
.login-card .auth-step > .field-label:first-child {
  margin-top: 0;
}

/* 24px from the last input to the submit, per the mockup's .btn-submit. */
.login-card .auth-step .btn.button-primary {
  margin-top: 24px;
}

.login-card .field,
.auth-step input {
  width: 100%;
  padding: 13px 15px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--field-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  outline: none;
  margin-bottom: 18px;
  box-sizing: border-box;
  min-height: 0;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.login-card .field:focus,
.auth-step input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}

.login-card .field::placeholder,
.auth-step input::placeholder {
  color: var(--text-faint);
}

.login-card .helper {
  font-size: 11.2px;
  color: var(--text-faint);
  margin-top: -12px;
  margin-bottom: 18px;
  line-height: 1.45;
  text-align: left;
}

/* Shape only — full-width and taller than a toolbar button, because it is the
   one action on the card. The fill, the weight and the casing come from the
   canonical block like every other button. This rule used to carry a gradient,
   `text-transform: uppercase` and a 999px pill, which is why the sign-in button
   was the only SHOUTING button in the app while every other primary read
   "New Project" in sentence case. */
.login-card .button-primary,
.auth-step button[type="submit"] {
  width: 100%;
  height: 46px;
  margin-top: 6px;
  padding: 0 15px;
  border-radius: 12px;
  font-size: 13.5px;
}

.login-card .error {
  color: var(--danger);
  font-size: 12px;
  margin-top: 12px;
  text-align: center;
}

.login-card .error[hidden] {
  display: none;
}

/* Dark Theme Overrides */
/* The ONLY dark-mode token block. Redefines the same names :root sets above
   and nothing else — every component is written against var(--token), so it
   themes automatically without a per-component dark override rule.

   --accent moves to teal (var(--accent)) here, resolving a pre-v2 inconsistency:
   the token used to say blue (#4b86dd) while the dark-mode illustration
   assets and dls-* icons already hardcoded teal, so the CSS variable and
   what you actually saw on screen disagreed. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0a0c0f;
  --surface: #14171b;
  --surface-2: #191c20;
  --code-bg: #0E1114;
  --code-text: #E7EDF1;
  --code-border: color-mix(in srgb, var(--text) 9%, transparent);
  --code-inline-bg: color-mix(in srgb, var(--text) 8%, transparent);
  --code-inline-text: #C7F0F2;
  --surface-solid: #191c20;
  --rail: #191c20;
  --border: color-mix(in srgb, var(--text) 10%, transparent);
  --border-strong: rgba(255, 255, 255, 0.18);
  --text: #f2f4f6;
  --text-muted: #9aa3ac;
  --text-faint: #666e75;
  --field-bg: color-mix(in srgb, var(--text) 4%, transparent);
  --accent: #45bcc3;
  --hover-surface: #0F2E30;
  /* A shade lighter than --accent, not darker: on a dark ground a primary
     button lifts toward the light. */
  --accent-hover: #62C6CD;
  --scrim: rgba(15, 46, 48, 0.55);
  --accent-2: #123047;
  --accent-ink: #04211f;
  --accent-soft: rgba(69, 188, 195, 0.15);
  --accent-strong: #3aa7ad;
  --success: #5edac4;
  --success-bg: #1d3a35;
  --warn: #f3b36f;
  --warn-ink: #191c20;
  --warn-bg: #3a2f1e;
  --warn-chip: #f3b36f;
  --danger: #f98d8d;
  --danger-ink: #2a1414;
  --danger-bg: #3a2323;

  --hero-gradient: linear-gradient(160deg, #0a141c 0%, #123047 42%, #1b4d52 100%);
  --particle-rgb: 255, 255, 255;
  --particle-line-alpha: 0.18;
  --particle-dot-alpha: 0.6;
  /* Light spreads three distinct hues — cool blue, indigo, warm orange — which
     is what gives it depth. Dark had teal, indigo and #123047: a near-navy
     that is almost the page ground, so it contributed nothing, at roughly half
     the opacity. Two of the three were effectively invisible.

     Same spread now, tuned for a dark ground: teal, violet, warm ember.

     Teal is --accent and carries the most weight. The violet and the ember are
     deliberately muted and held at lower opacity: neither is in the dark
     palette, so they are here to give the ground depth, not to read as a
     second and third brand colour competing with the accent. */
  --blob-a: #45bcc3;
  --blob-b: #4f47a8;
  --blob-c: #a3634a;
  --blob-opacity-a: 0.30;
  --blob-opacity-b: 0.20;
  --blob-opacity-c: 0.16;

  /* Previously undefined for dark, so these silently rendered light mode's
     purple-tinted shadows on a near-black ground. */
  --shadow: 0 10px 28px rgba(0, 0, 0, 0.4);
  --shadow-card: 0 22px 70px rgba(0, 0, 0, 0.5), 0 3px 12px rgba(0, 0, 0, 0.3);
  --shadow-soft: 0 12px 36px rgba(0, 0, 0, 0.38);

  --success-soft: rgba(94, 218, 196, 0.12);
  --sky-tint: #8fe0d8;
}


/* Exact "shared v4 background recipe" from
   specs/reference/mockups/v4-page-mockups.html's .bg-teal-right — near-black
   base, teal weighted to one side, faint purple whisper. No animation. */
:root[data-theme="dark"] body {
  background: linear-gradient(150deg, #0d0f12 0%, #0a0c0f 55%, #08090b 100%);
  background-attachment: fixed;
  color: var(--text);
}


/* ════════════════════════════════════════════════════════════════════════════
   SGS design language (Carbon) — <html data-dls="sgs">
   Chosen by a Platform Admin in Spec Gen Studio (Admin → RISE appearance; the
   platform setting `rise_dls`), served by rise-api's GET /ui-config and applied
   by index.html. Absent attribute = RISE's own design language, unchanged.

   Only the token vocabulary is redefined (IBM Carbon: g10 light, g100 dark,
   blue-60 interactive, IBM Plex), plus the flat ground and square corners that
   Carbon's shapes need. Both selectors carry the theme so each outranks the
   :root[data-theme="dark"] block above in its own mode and never the other's.
   ════════════════════════════════════════════════════════════════════════════ */
:root[data-dls="sgs"]:not([data-theme="dark"]) {
  --bg: #f4f4f4;
  --surface: #ffffff;
  --surface-2: #f4f4f4;
  --surface-solid: #ffffff;
  --code-inline-bg: #e0e0e0;
  --code-inline-text: #161616;
  --rail: #ffffff;
  --border: #e0e0e0;
  --border-strong: #8d8d8d;
  --text: #161616;
  --text-muted: #525252;
  --text-faint: #6f6f6f;
  --field-bg: #ffffff;
  --accent: #0f62fe;
  --hover-surface: #edf5ff;
  --accent-hover: #0050e6;
  --scrim: rgba(22, 22, 22, 0.5);
  --accent-2: #002d9c;
  --accent-ink: #ffffff;
  --accent-soft: rgba(15, 98, 254, 0.1);
  --accent-strong: #0043ce;
  --success: #24a148;
  --success-bg: #defbe6;
  --warn: #f1c21b;
  --warn-ink: #161616;
  --warn-bg: #fcf4d6;
  --warn-chip: #8e6a00;
  --danger: #da1e28;
  --danger-ink: #ffffff;
  --danger-bg: #fff1f1;
  --hero-gradient: linear-gradient(#f4f4f4, #f4f4f4);
  --particle-rgb: 15, 98, 254;
  --particle-line-alpha: 0.12;
  --particle-dot-alpha: 0.25;
  --blob-opacity-a: 0;
  --blob-opacity-b: 0;
  --blob-opacity-c: 0;
  --shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.1);
  --shadow-soft: 0 2px 6px rgba(0, 0, 0, 0.15);
  --success-soft: rgba(36, 161, 72, 0.1);
  --sky-tint: #a6c8ff;
}

:root[data-dls="sgs"][data-theme="dark"] {
  --bg: #161616;
  --surface: #262626;
  --surface-2: #393939;
  --surface-solid: #262626;
  --code-inline-bg: #393939;
  --code-inline-text: #f4f4f4;
  --rail: #262626;
  --border: #393939;
  --border-strong: #6f6f6f;
  --text: #f4f4f4;
  --text-muted: #c6c6c6;
  --text-faint: #8d8d8d;
  --field-bg: #262626;
  --accent: #4589ff;
  --hover-surface: #333333;
  --accent-hover: #78a9ff;
  --scrim: rgba(0, 0, 0, 0.6);
  --accent-2: #0043ce;
  --accent-ink: #ffffff;
  --accent-soft: rgba(69, 137, 255, 0.16);
  --accent-strong: #78a9ff;
  --success: #42be65;
  --success-bg: #044317;
  --warn: #f1c21b;
  --warn-ink: #161616;
  --warn-bg: #302400;
  --warn-chip: #f1c21b;
  --danger: #fa4d56;
  --danger-ink: #ffffff;
  --danger-bg: #520408;
  --hero-gradient: linear-gradient(#161616, #161616);
  --particle-rgb: 120, 169, 255;
  --particle-line-alpha: 0.12;
  --particle-dot-alpha: 0.35;
  --blob-opacity-a: 0;
  --blob-opacity-b: 0;
  --blob-opacity-c: 0;
  --shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-soft: 0 2px 6px rgba(0, 0, 0, 0.4);
  --success-soft: rgba(66, 190, 101, 0.14);
  --sky-tint: #78a9ff;
}

:root[data-dls="sgs"] {
  --font-body: "IBM Plex Sans", "Helvetica Neue", Arial, sans-serif;
  --font-display: "IBM Plex Sans", "Helvetica Neue", Arial, sans-serif;
}

/* Flat ground: Carbon has no gradient page background (the dark theme's is a
   literal gradient on body, not a token). */
:root[data-dls="sgs"] body,
:root[data-dls="sgs"][data-theme="dark"] body {
  background: var(--bg);
}


/* ── SGS DLS branding: IBM RAD (Rapid Application Development) ─────────────────
   Elements marked rise-only / sgs-only swap with the design language; brand
   TEXT swaps via data-brand-sgs (index.html's applyBrand). The IBM logo is a
   mask filled with --text, so it is black in light mode and white in dark. */
:root:not([data-dls="sgs"]) .sgs-only,
:root[data-dls="sgs"] .rise-only {
  display: none !important;
}

.login-card__ibm,
.app-nav__ibm {
  flex: none;
  background: var(--text);
  -webkit-mask: url("assets/ibm-logo.svg") center / contain no-repeat;
  mask: url("assets/ibm-logo.svg") center / contain no-repeat;
}

.login-card__ibm {
  display: block;
  width: 150px;
  height: 56px;
}

.app-nav__ibm {
  display: block;
  width: 44px;
  height: 17px;
}

/* "IBM RAD" reads as a name, not the spaced-out RISE monogram. */
:root[data-dls="sgs"] .login-card__wordmark {
  letter-spacing: 0.02em;
  text-indent: 0;
  font-weight: 600;
}

/* ==========================================================================
   v2: Projects view - Scene ground, blobs, 940px wrap, header, buttons,
   chips, bulk bar, glass cards, icons, steps, status pills, kebab,
   opaque overflow menu, select pick, and legend.
   ========================================================================== */

/* Three large blurred circles behind the projects and chat views. */
/* fixed, not absolute. As absolutes they were bounded by the view's own box,
   so anything below it — a short page, or the part you reach by scrolling —
   showed the bare body gradient instead. That edge is the two background
   shades: not two colours, one colour and the end of the blobs.

   Viewport-anchored, they behave as ambient light: the tint is wherever you
   are looking, at any scroll position and any content height. */
.blob {
  position: fixed;
  border-radius: 50%;
  filter: blur(70px);
  pointer-events: none;
}

.blob.a,
.blob--a {
  width: 460px;
  height: 460px;
  background: var(--blob-a);
  opacity: var(--blob-opacity-a);
  top: -160px;
  left: -140px;
}

.blob.b,
.blob--b {
  width: 420px;
  height: 420px;
  background: var(--blob-b);
  opacity: var(--blob-opacity-b);
  bottom: -180px;
  right: -120px;
}

.blob.c,
.blob--c {
  width: 300px;
  height: 300px;
  background: var(--blob-c);
  opacity: var(--blob-opacity-c);
  top: 40%;
  left: 60%;
}

#projects-view.view {
  position: relative;
  width: 100%;
  min-height: 100vh;
  overflow-x: hidden;
  overflow-y: auto;
  background: var(--bg);
  flex-direction: column;
  padding: 0;
  max-width: none;
  margin: 0;
}

#projects-view.view[hidden] {
  display: none;
}

.projects-geometry {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  color: rgb(var(--particle-rgb));
  pointer-events: none;
}

.projects-geometry__shape {
  --geometry-x: 18px;
  --geometry-y: -14px;
  --geometry-rotate: -6deg;
  position: absolute;
  overflow: visible;
  animation: projects-geometry-drift 18s ease-in-out infinite alternate;
  transform-origin: center;
}

.projects-geometry__shape path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  opacity: var(--particle-line-alpha);
  vector-effect: non-scaling-stroke;
}

.projects-geometry__shape circle {
  fill: currentColor;
  opacity: var(--particle-dot-alpha);
}

.projects-geometry__shape--a {
  top: 8%;
  left: -56px;
  width: 260px;
}

.projects-geometry__shape--b {
  --geometry-x: -22px;
  --geometry-y: 16px;
  --geometry-rotate: 8deg;
  top: 14%;
  right: -72px;
  width: 300px;
  animation-duration: 22s;
  animation-direction: alternate-reverse;
}

.projects-geometry__shape--c {
  --geometry-x: 12px;
  --geometry-y: -20px;
  --geometry-rotate: 4deg;
  bottom: -16px;
  left: 48%;
  width: 230px;
  animation-duration: 20s;
  animation-delay: -9s;
}

@keyframes projects-geometry-drift {
  from {
    transform: translate3d(0, 0, 0) rotate(var(--geometry-rotate));
  }
  to {
    transform: translate3d(var(--geometry-x), var(--geometry-y), 0)
      rotate(calc(var(--geometry-rotate) + 3deg));
  }
}

/* One page shell for every top-level screen (Build/Space/Agents), so the
   header sits at the same position no matter which screen is active — a
   different max-width or top padding per screen is what made the title
   jump on every nav click. Keep these five selectors in this one rule; do
   not give any of them their own max-width/margin/padding again. */
.projects,
.space,
.agents,
.agent-editor,
.agent-detail {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 940px;
  margin: 0 auto;
  padding: 64px 28px 60px;
}

.projects__header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  margin-bottom: 20px;
  gap: 16px;
  flex-wrap: wrap;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

.projects__header h1 {
  font-size: 23px;
  font-weight: 700;
  color: var(--text);
  margin: 0;
  line-height: 1.2;
}

.projects__header .chat-header__tagline {
  color: var(--text-muted);
  font-size: 13px;
  margin: 5px 0 0;
  line-height: 1.4;
}

.projects__header-actions {
  display: flex;
  align-items: center;
  gap: 9px;
}

/* Size only. These used to redeclare the whole fill — and because
   `.projects__header-actions .button-primary` is (0,2,0) against the base
   rule's (0,1,0), the gradient here outranked the one button system no matter
   what the canonical block said. That is why "+ New Project" stayed a gradient
   after the families were merged: not a duplicate selector, a more specific
   one. Never set `background`/`color`/`border` on a button outside the
   canonical block — scope size and spacing here, nothing else. */
.projects__header-actions .button-primary,
.projects__header-actions .button-ghost {
  height: 38px;
  padding: 0 17px;
  border-radius: 10px;
}

/* Selection mode on — a state, not a page-scoped skin, so it is declared on the
   variant itself rather than under .projects__header-actions. */
.button-ghost.is-active,
.btn-ghost.is-active,
.btn-ghost.active {
  background: var(--text);
  color: var(--bg);
  border-color: var(--text);
}

/* ---- Filter chips ---- */
.filters {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 32px;
  font-size: 12px;
  font-weight: 620;
  line-height: 1;
  padding: 0 14px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--text-muted);
  cursor: pointer;
  box-shadow: none;
  transition: background-color 0.13s ease, color 0.13s ease, border-color 0.13s ease;
}

.chip:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--surface) 75%, transparent);
  transform: none;
}

.chip.is-active,
.chip.active {
  background: var(--text);
  color: var(--bg);
  border-color: var(--text);
}

/* Count sits inside the chip so it moves with it and inherits its colour. */
.chip__count,
.filters__archived .chip__count {
  margin-left: 7px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  opacity: 0.6;
}

.chip__count:empty {
  display: none;
  margin-left: 0;
}

.chip__count[hidden] {
  display: none;
}

/* Archived is a place, not a status. Pushed to the end of the row and styled
   as a quiet link rather than a chip, so it does not read as a fifth peer of
   the status filters. */
.filters__archived {
  margin-left: auto;
  border: none;
  background: none;
  box-shadow: none;
  padding: 7px 4px;
  color: var(--text-faint);
  font-family: inherit;
  font-size: 12px;
  font-weight: 620;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
}

.filters__archived:hover {
  background: none;
  box-shadow: none;
  transform: none;
  color: var(--text);
}

.filters__archived.is-active {
  color: var(--accent);
}


/* ---- Bulk action bar ---- */
.bulk-bar,
#bulk-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
  padding: 10px 14px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}

.bulk-bar[hidden],
#bulk-bar[hidden] {
  display: none;
}

.bulk-bar span,
#bulk-count {
  font-size: 12.5px;
  font-weight: 680;
  color: var(--accent);
}

.bulk-bar__actions {
  display: flex;
  gap: 8px;
  align-items: center;
}

.bulk-bar__actions button,
.bulk-bar button {
  font-size: 12px;
  font-weight: 650;
  padding: 7px 13px;
  border-radius: 8px;
  border: none;
  cursor: pointer;
  box-shadow: none;
}

.bulk-bar [data-bulk="archive"],
/* The bulk bar used to re-skin its own buttons — a solid var(--danger) slab for
   Delete, contradicting the outline danger every other destructive control in
   the app uses. Selecting projects is not a context that earns its own button
   language; both take the canonical system now. */

.bulk-bar #bulk-cancel,
.bulk-bar .link-button {
  background: transparent;
  color: var(--text-muted);
  border: none;
}

/* ---- Project card list ---- */
.project-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.project-list__item {
  position: relative;
  list-style: none;
}

.project-list__button,
.pcard {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-height: 0;
  padding: 15px 17px;
  border-radius: 16px;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  box-shadow: 0 14px 34px rgba(20, 10, 60, 0.1);
  cursor: pointer;
  text-align: left;
  color: var(--text);
  transition: transform 0.13s ease, box-shadow 0.13s ease, border-color 0.13s ease;
  font-family: inherit;
}

.project-list__button:hover,
.pcard:hover {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  box-shadow: 0 18px 40px rgba(20, 10, 60, 0.16);
  background: color-mix(in srgb, var(--surface) 55%, transparent);
}

.project-list__button:hover .pcard__name,
.pcard:hover .pcard__name,
.pcard:hover .pname {
  color: var(--accent);
}

.pcard__icon {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
}

.pcard__icon svg {
  width: 18px;
  height: 18px;
}

.pcard__body {
  flex: 1;
  min-width: 0;
}

.pcard__name {
  display: block;
  font-size: 14px;
  font-weight: 650;
  color: var(--text);
  transition: color 0.13s ease;
}

.pcard__meta {
  display: block;
  font-size: 11.5px;
  color: var(--text-faint);
  margin-top: 2px;
}

.pcard__steps {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-right: 2px;
  flex-shrink: 0;
}

.pcard__step,
.pstep {
  width: 17px;
  height: 6px;
  border-radius: 3px;
  box-sizing: border-box;
}

.pcard__step.is-done,
.pcard__step.done,
.pstep.done {
  background: color-mix(in srgb, var(--accent) 48%, transparent);
}

.pcard__step.is-current,
.pcard__step.current,
.pstep.current {
  background: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .projects-geometry__shape {
    animation: none;
  }
}

.pcard__step.is-todo,
.pcard__step.todo,
.pstep.todo {
  background: transparent;
  border: 1.5px solid var(--border-strong);
}

.pcard__status,
.status {
  font-size: 10.5px;
  font-weight: 700;
  padding: 7px 13px;
  border-radius: 999px;
  white-space: nowrap;
  flex-shrink: 0;
}

.pcard__status.is-active,
.status.active {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent);
}

.pcard__status.is-review,
.status.review {
  background: var(--warn-bg);
  color: var(--warn-chip);
}

.pcard__status.is-complete,
.pcard__status.is-done,
.status.done {
  background: var(--success-bg);
  color: var(--success);
}

.pcard__status.is-archived {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text-muted);
}

.pcard__kebab,
.kebab {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--text-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}

.pcard__kebab:hover,
.kebab:hover {
  background: var(--field-bg);
  color: var(--text);
}

.pcard__kebab svg,
.kebab svg {
  width: 16px;
  height: 16px;
}

.pcard__menu,
.ovf {
  position: absolute;
  top: 46px;
  right: 14px;
  z-index: 20;
  width: 176px;
  padding: 5px;
  border-radius: 11px;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 16px 40px rgba(20, 10, 60, 0.2);
  display: none;
  flex-direction: column;
  gap: 1px;
}

.pcard__menu.is-open,
.ovf.open {
  display: flex;
}

.pcard__menu[hidden],
.ovf[hidden] {
  display: none;
}

.pcard__menu button,
.ovf button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 11px;
  border: none;
  background: transparent;
  border-radius: 7px;
  font-size: 12.6px;
  font-weight: 570;
  color: var(--text);
  cursor: pointer;
  text-align: left;
  box-shadow: none;
  font-family: inherit;
}

.pcard__menu button:hover,
.ovf button:hover {
  background: var(--field-bg);
  transform: none;
}

.pcard__menu button.is-danger,
.pcard__menu button[data-action="delete"],
.ovf button.danger {
  color: var(--danger);
}

.pcard__menu button.is-danger:hover,
.pcard__menu button[data-action="delete"]:hover,
.ovf button.danger:hover {
  background: var(--danger-bg);
}

/* Selection checkbox */
.pcard__pick,
.pick {
  width: 19px;
  height: 19px;
  border-radius: 6px;
  border: 1.5px solid var(--border-strong);
  flex-shrink: 0;
  display: none;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background: var(--surface);
  box-sizing: border-box;
}

.projects.is-selecting .pcard__pick,
.projects.is-selecting .pick,
.pcard.selecting .pick {
  display: flex;
}

.projects.is-selecting .pcard__kebab,
.projects.is-selecting .kebab,
.pcard.selecting .kebab {
  display: none;
}

.pcard__pick.is-on,
.pcard__pick.on,
.pick.on {
  background: var(--accent);
  border-color: var(--accent);
}

.pcard__pick svg,
.pick svg {
  width: 11px;
  height: 11px;
  color: var(--accent-ink);
  opacity: 0;
}

.pcard__pick.is-on svg,
.pcard__pick.on svg,
.pick.on svg {
  opacity: 1;
}

/* Legend */
.legend {
  display: flex;
  align-items: center;
  gap: 15px;
  margin-top: 18px;
  /* Separates the legend from whatever follows it,
     which otherwise butts straight up against it. */
  margin-bottom: 32px;
  font-size: 11.2px;
  color: var(--text-faint);
}

/* Companion rule: `.legend` sets `display`, so without this the element still
   renders when app.js sets `hidden`. This trap has bitten this repo repeatedly. */
.legend[hidden] {
  display: none;
}

.legend div {
  display: flex;
  align-items: center;
  gap: 6px;
}

.empty,
/* ---- Loading skeleton ------------------------------------------------ */
/* Rows shaped like .pcard so the list does not reflow when real data lands. */
.skeleton-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.skeleton-list[hidden] {
  display: none;
}

.skeleton-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 15px 17px;
  border-radius: 16px;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
}

.skeleton-card__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* A sheen sweeping over a flat base, rather than the whole block pulsing —
   pulsing several rows at once reads as a broken page. */
.skeleton {
  display: block;
  border-radius: 6px;
  background:
    linear-gradient(
      90deg,
      transparent 0%,
      color-mix(in srgb, var(--text) 6%, transparent) 50%,
      transparent 100%
    ),
    color-mix(in srgb, var(--text) 7%, transparent);
  background-size: 200% 100%, 100% 100%;
  background-repeat: no-repeat;
  animation: skeleton-sheen 1.4s ease-in-out infinite;
}

.skeleton--icon { width: 38px; height: 38px; border-radius: 10px; flex-shrink: 0; }
.skeleton--title { width: 42%; height: 13px; }
.skeleton--meta { width: 26%; height: 10px; }
.skeleton--pill { width: 92px; height: 26px; border-radius: 999px; flex-shrink: 0; }

@keyframes skeleton-sheen {
  from { background-position: -120% 0, 0 0; }
  to   { background-position: 220% 0, 0 0; }
}

/* Respect a reduced-motion preference: keep the placeholder, drop the sweep. */
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
}

/* ---- Empty state ----------------------------------------------------- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 56px 20px 64px;
  gap: 6px;
}

.empty-state[hidden] {
  display: none;
}

.empty-state__glyph {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 6px;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}

.empty-state__glyph svg { width: 22px; height: 22px; }

.empty-state__title {
  font-size: 15px;
  font-weight: 650;
  color: var(--text);
}

.empty-state__body {
  font-size: 13px;
  color: var(--text-muted);
  max-width: 42ch;
  line-height: 1.5;
}

.empty-state__action { margin-top: 14px; }

/* The error variant reuses the same layout — only the wording and the accent
   colour change, so a failed load still reads as a considered state. */
.empty-state.error .empty-state__glyph {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border-color: color-mix(in srgb, var(--danger) 28%, transparent);
}

/* Companion [hidden] rules: ensure author display declarations never outrank [hidden] */
#new-project-modal[hidden],
#delete-project-modal[hidden],
#destroy-deployment-modal[hidden],
#members-modal[hidden],
#chat-loading[hidden],
#budget-banner[hidden],
#chat-busy-hint[hidden],
#artifact-empty[hidden],
#quick-approve[hidden],
#chat-pane[hidden],
#doc-pane[hidden],
#build-pane[hidden],
#build-tab-button[hidden],
#build-tab-dot[hidden],
#build-locked[hidden],
#build-bootstrap[hidden],
#build-repo[hidden],
#build-live[hidden],
#build-diagnosing[hidden],
#build-pending[hidden],
#build-app-url-row[hidden],
#build-start-run[hidden],
#build-destroy[hidden],
#build-pending-approve[hidden],
#build-pending-note[hidden],
#build-error[hidden],
#build-blocked[hidden],
.build-card[hidden],
.usage-card[hidden],
#project-list-loading[hidden],
.pcard__kebab[hidden],
.kebab[hidden],
.pcard__pick[hidden],
.pick[hidden],
.project-list__trash[hidden],
#projects-account-dropdown[hidden],
#chat-account-dropdown[hidden],
#members-error[hidden],
#members-add-section[hidden],
#member-confirm[hidden],
#member-add[hidden],
#member-add-error[hidden],
#usage-project-empty[hidden],
#connections-github-form[hidden],
#connections-github-error[hidden],
#connections-github-disconnect[hidden],
#login-error[hidden] {
  display: none !important;
}

/* Dark Mode Overrides (Usage & Empty state) */
/* Usage header background and padding reset on dark ground */
/* Hide light-mode underline decoration in dark mode */
/* Muted text color for back link in usage header */
/* Translucent surface and hairline border for usage/build cards on dark ground */
:root[data-theme="dark"] .usage-card,
:root[data-theme="dark"] .usage-table,
/* The dark .build-card override that lived here is gone — --surface already
   resolves per theme, so the card needed no second definition. */

/* Ghost button border and muted text for roster trigger on dark ground */
:root[data-theme="dark"] .members-button {
  border-color: rgba(255,255,255,0.12);
  color: var(--text-muted);
}

/* Ghost button hover background on dark ground */
:root[data-theme="dark"] .members-button:hover {
  background: rgba(255,255,255,0.06);
  color: var(--text);
}

/* Roster list row background and border on dark ground */
:root[data-theme="dark"] .members-list__row {
  background: rgba(255,255,255,0.04);
  border-color: rgba(255,255,255,0.12);
}

/* Roster add section border on dark ground */
:root[data-theme="dark"] .members-add {
  border-top-color: rgba(255,255,255,0.12);
}

/* Onboarding SVG platform fill gradient on dark ground */
:root[data-theme="dark"] .proj-platform {
  fill: url(#projPlatformGrad);
}

/* Onboarding SVG node glow on dark ground */
:root[data-theme="dark"] .proj-node {
  fill: #EAFBFA;
  filter: url(#projNodeGlow);
}

/* Onboarding SVG connecting path glow animation on dark ground */
:root[data-theme="dark"] .proj-path-glow {
  stroke-dasharray: 5 11;
  animation: proj-path-flow 1.6s linear infinite;
}

/* Onboarding SVG connecting path core animation on dark ground */
:root[data-theme="dark"] .proj-path-core {
  stroke-dasharray: 5 11;
  animation: proj-path-flow 1.6s linear infinite;
}


  to { stroke-dashoffset: -32; }

  .proj-path-glow, .proj-path-core { animation: none !important; }/* Onboarding phase pill chip styling on dark ground */
:root[data-theme="dark"] .pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  padding: 6px 13px 6px 9px;
  border-radius: 20px;
  background: rgba(255,255,255,0.06);
  color: #C9CCD3;
  border: 1px solid rgba(255,255,255,0.09);
  white-space: nowrap;
}

/* Onboarding phase pill icon styling on dark ground */
:root[data-theme="dark"] .pill-icon {
  width: 16px;
  height: 16px;
  object-fit: contain;
  filter: grayscale(0.3);
  opacity: 0.85;
}



/* Modal card dark ground background and border */
:root[data-theme="dark"] .modal-card {
  background: #191c20;
  border: 1px solid rgba(255,255,255,0.12);
  color: var(--text);
}

/* Account menu dropdown dark ground background and border */
:root[data-theme="dark"] .account-menu__dropdown {
  background: #191c20;
  border-color: rgba(255,255,255,0.12);
  box-shadow: 0 18px 44px rgba(0,0,0,0.45);
}

/* Document upload drop zone background and border in dark mode */
:root[data-theme="dark"] .drop-zone {
  background: rgba(255,255,255,0.03);
  border-color: rgba(255,255,255,0.12);
}

/* The dark .button-secondary pair that used to live here is gone. It restated
   the canonical ghost rule verbatim except for a hardcoded rgba hover, so dark
   mode alone skipped --hover-surface. Both themes now take the base rule. */

/* Form input dark translucent ground and border */
:root[data-theme="dark"] input {
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.1);
  color: var(--text);
}

/* Generic label reset for dark ground */
:root[data-theme="dark"] label {
  color: var(--text-muted);
}


/* ---- Design picker (pre-bootstrap) ----
   Live previews of the platform's style references, scaled down inside a
   fixed-height shell. The iframe is pointer-events: none so a click selects
   the card rather than landing inside the frame. */
#design-picker[hidden] {
  display: none;
}

.design-picker__title {
  margin: 1.5rem 0 0.25rem;
  font-size: 0.95rem;
  font-weight: 700;
}

.design-picker__options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.design-card {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.65rem;
  text-align: left;
  font: inherit;
  color: var(--ink);
  background: var(--surface-solid);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  cursor: pointer;
}

.design-card:hover {
  border-color: var(--accent);
}

.design-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Selection is a ring plus a colour change, never colour alone. */
.design-card--selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}

.design-card__shell {
  display: block;
  height: 132px;
  overflow: hidden;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface-solid);
}

.design-card__preview {
  width: 320%;
  height: 420px;
  border: 0;
  transform: scale(0.31);
  transform-origin: top left;
  pointer-events: none;
}

.design-card strong {
  font-size: 0.92rem;
  font-weight: 700;
}

.design-card__copy {
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--ink-muted);
}

/* Shown when a project's phase is not one the app knows. The rail still draws
   something (routing must stay safe), so this says the drawing may be wrong
   rather than letting it quietly misreport the project's stage. */
.rail__warning { margin: 0 0 10px; }

/* ============================================================
   Mobile
   Measured on a 393x844 phone before this: the rail took 354px
   (42vh, and still internally scrolling at 562px of content),
   leaving the conversation 309px — a third of the screen for the
   thing people opened the app to read.
   ============================================================ */

/* The collapsed-journey control is mobile-only; above the breakpoint the rail
   is a sidebar with room for the full list. */

@media (max-width: 960px) {







  /* Collapsed: the strip alone. The heading goes too — the summary already
     names the journey, and repeating it wastes a line on the scarcest screen.
     The documents panel collapses with it: its drag-and-drop zone is useless
     on a phone and it was taking 171px, half of what the collapse had just
     reclaimed. The summary carries a document count so it stays findable. */


  /* Expanded on a phone it is still a panel, not the whole screen. */
  .details.rail { max-height: 46vh; }

  /* Touch targets. Measured below 32px before this: the artifact download
     icons at 18-19px, and the header controls at 28px. */
  .members-button,
  #chat-account-trigger,
  #projects-account-trigger,
  .account-menu__avatar {
    min-width: 40px;
    min-height: 40px;
  }

  .file-item__download,
  .file-item__download-pdf {
    min-width: 40px;
    min-height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}

/* iOS zooms the whole page when a focused field's text is under 16px, and
   never zooms back out. This is the composer, so it happened on every reply. */
@media (max-width: 960px) {
  #chat-message,
  .chat-input textarea,
  .modal-card input,
  .modal-card select,
  .field {
    font-size: 16px;
  }

  #chat-message {
    /* Two lines of 16px text without clipping the placeholder, which was cut
       mid-word at the old height. */
    min-height: 44px;
    line-height: 1.35;
  }
}

/* The diagram toolbar is hover-revealed on a pointer device; with no hover it
   is permanently visible, and it sat on top of the drawing it belongs to.
   Below it instead, where it covers nothing. */
@media (hover: none) {
  /* `.markdown-body .diagram` sets padding at (0,2,0); a bare `.diagram` here
     is (0,1,0) and silently loses, which left the toolbar sitting on top of
     the drawing exactly as before. Match the specificity. */
  .markdown-body .diagram { padding-bottom: 46px; }

  .diagram__bar {
    top: auto;
    bottom: 8px;
    right: 8px;
  }

  .diagram__btn { min-height: 36px; }
}

/* Attachment chips sit above the composer. A file waits here until the user
   sends, so every upload arrives with a message.

   Same box as .chat-busy-hint and .ask (92% / 1180px), so the row lines up
   with the field it belongs to instead of running full-bleed past it. The
   markup is a sibling ABOVE #chat-form rather than a child of it: .chat-input
   is a flex row, so a child lands beside the field and piles up to its left. */
.attachment-chips {
  width: 92%;
  max-width: 1180px;
  margin: 0 auto 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0;
  list-style: none;
}

/* Author display rules outrank the UA [hidden] rule, so every toggleable
   element needs its own companion — see frontend/CLAUDE.md's [hidden] trap. */
.attachment-chips[hidden] {
  display: none !important;
}

.attachment-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-size: 12px;
}

.attachment-chip__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}

.attachment-chip__remove {
  border: 0;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  padding: 0;
}

.attachment-chip__remove:hover {
  color: var(--danger);
}

/* An error hint used to sit for 8 seconds with no control, which reads as
   stuck. It now fades out after 5s and carries an explicit dismiss. */
.chat-busy-hint {
  transition: opacity 400ms ease;
}

.chat-busy-hint.is-fading {
  opacity: 0;
}

.chat-busy-hint__dismiss {
  margin-left: auto;
  align-self: flex-start;
  flex-shrink: 0;
  border: 0;
  background: none;
  padding: 0 0 0 8px;
  color: var(--ink-muted);
  cursor: pointer;
  font-size: 13px;
  line-height: 1.2;
}

.chat-busy-hint__dismiss:hover {
  color: var(--danger);
}

@media (prefers-reduced-motion: reduce) {
  /* The fade is decoration. A user who turned motion off still needs the
     hint to go away, so only the transition is dropped. */
  .chat-busy-hint {
    transition: none;
  }
}

/* ============================================================
   Mobile, option A
   The rail becomes a bottom sheet, the journey becomes one strip,
   and everything that was fighting for the header moves into ⋯.
   ============================================================ */

.chat-more,
.chat-menu,
.chat-stage,
.chat-back,
.chat-title,
.rail-close,
.rail-backdrop { display: none; }

@media (max-width: 960px) {
  /* ---- the ⋯ menu replaces four controls in a 393px bar ---- */
  .chat-more {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    color: var(--text-muted);
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    box-shadow: none;
    transform: none;
  }

  .chat-more:hover,
  .chat-more[aria-expanded="true"] {
    color: var(--text);
    background: var(--surface-2);
    border-color: var(--border-strong);
    box-shadow: none;
    transform: none;
  }

  /* The theme toggle is fixed at top:16 right:18, 38px wide. The 520px block
     resets .chat-header's padding shorthand and wipes any reservation made
     earlier in the file, so it has to be re-made here — after it. */
  .chat-header { padding-right: 62px; }

  .chat-header__actions { position: relative; }

  /* ---- mobile header: back, title, menu ---- */
  .chat-back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    flex: none;
    margin-left: -6px;
    padding: 0;
    color: var(--text-muted);
    background: transparent;
    border: 0;
    border-radius: 10px;
    cursor: pointer;
    box-shadow: none;
    transform: none;
  }

  .chat-back:hover {
    color: var(--text);
    background: var(--surface-2);
    box-shadow: none;
    transform: none;
  }

  .chat-title {
    display: block;
    flex: 1;
    min-width: 0;
    font-size: 14px;
    font-weight: 650;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* One tab is not a tab bar. Two is — and it is a far more obvious way to
     move between Chat and Build & Deploy than an item inside ⋯. */
  #tab-bar { display: none; }

  /* Its own row. Inline, two tabs plus the title plus ⋯ plus the theme toggle
     crushed "Document Builder" down to "D". */
  .chat-header { flex-wrap: wrap; }

  /* The tab bar sits after the title but before the actions in the DOM, so a
     100%-basis wrap pushed ⋯ onto a third row. Order puts the tabs last
     visually while ⋯ stays on the title's row. */
  .chat-back { order: 0; }
  .chat-title { order: 1; }
  .chat-header__actions { order: 2; }

  #tab-bar.has-two {
    order: 3;
    display: flex;
    flex-basis: 100%;
    gap: 4px;
    margin-top: 2px;
    border-top: 1px solid var(--border);
    padding-top: 4px;
  }

  #tab-bar.has-two .tab-bar__tab { padding: 7px 10px; font-size: 12.5px; }

  /* At the terminal phase the strip would read "Build & Deploy 5/5" directly
     under a tab that says Build & Deploy. The tabs win the row. */
  #tab-bar.has-two ~ .chat-stage,
  .chat-header:has(#tab-bar.has-two) + .chat-stage { display: none; }

  /* The sheet needs something above it to tap. Without this the only way to
     close it was its own top padding or the Escape key. */
  .rail-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 55;
    background: var(--scrim);
  }

  .rail-backdrop[hidden] { display: none !important; }

  .rail-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 10px;
    right: 12px;
    width: 40px;
    height: 40px;
    padding: 0;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 50%;
    cursor: pointer;
    box-shadow: none;
    transform: none;
  }

  .rail-close:hover {
    color: var(--text);
    background: var(--hover-surface);
    box-shadow: none;
    transform: none;
  }

  .details.rail { position: fixed; }

  /* ---- the sheet is a sheet, not a transplanted sidebar ----
     The back button, the project title and the "Cloud journey" heading all
     belong to the desktop rail. In a sheet opened from a chat that already
     shows the title, they are duplication — and the back button being in here
     was the only route to the projects list, which is why it had to move into
     the header first. */
  .details.rail.is-sheet-open .rail__back,
  .details.rail.is-sheet-open .rail__project-name,
  .details.rail.is-sheet-open .rail-label { display: none; }

  .details.rail.is-sheet-open .details__section { padding: 0 4px 8px; }

  /* margin-top:auto pins this to the bottom of a full-height sidebar. In a
     sheet, whose height is its content, it just opens a gap between the
     journey and the documents. */
  .details.rail.is-sheet-open .rail-files {
    margin-top: 14px;
    padding-top: 14px;
  }

  .details.rail.is-sheet-open .rail-top { padding-bottom: 0; }

  /* Members and the avatar live in the menu now. */
  .chat-header__actions .members-button,
  .chat-header__actions .account-menu { display: none; }

  .chat-menu {
    display: block;
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 40;
    min-width: 194px;
    padding: 5px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
  }

  .chat-menu[hidden] { display: none !important; }

  .chat-menu button {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    min-height: 40px;
    padding: 8px 10px;
    font: inherit;
    font-size: 13.5px;
    color: var(--text);
    text-align: left;
    background: none;
    border: 0;
    border-radius: 8px;
    cursor: pointer;
    box-shadow: none;
    transform: none;
  }

  .chat-menu button:hover {
    color: var(--text);
    background: color-mix(in srgb, var(--text) 8%, transparent);
    box-shadow: none;
    transform: none;
  }

  .chat-menu button[hidden] { display: none !important; }
  .chat-menu hr { border: 0; border-top: 1px solid var(--border); margin: 4px 2px; }
  .chat-menu__count { margin-left: auto; font-size: 12px; color: var(--text-muted); }
  .chat-menu__dot {
    margin-left: auto;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--warn);
  }
  .chat-menu__dot[hidden] { display: none !important; }

  /* ---- the stage strip: one row instead of a 354px rail ---- */
  .chat-stage {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: none;
    padding: 8px 14px;
    border-bottom: 1px solid var(--border);
  }

  .chat-stage[hidden] { display: none !important; }

  .chat-stage__name {
    font-size: 11.5px;
    font-weight: 650;
    color: var(--accent);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }

  .chat-stage__track {
    flex: 1;
    min-width: 40px;
    height: 4px;
    border-radius: 99px;
    background: color-mix(in srgb, var(--text) 13%, transparent);
    overflow: hidden;
  }

  .chat-stage__track > span {
    display: block;
    height: 100%;
    border-radius: 99px;
    background: var(--accent);
    transition: width 0.25s;
  }

  .chat-stage__count {
    font-size: 10.5px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* ---- the rail becomes a bottom sheet ---- */
  .details.rail {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    z-index: 60;
    width: 100%;
    max-height: 76dvh;
    max-width: none;
    margin: 0;
    padding: 14px 16px 20px;
    border-radius: 18px 18px 0 0;
    /* Opaque, not the rail's translucent sidebar surface: a sheet sits over
       content rather than over the page background. */
    border: 1px solid var(--border-strong);
    border-bottom: 0;
    background: var(--surface);
    box-shadow: 0 -14px 40px rgba(0, 0, 0, 0.5);
    overflow-y: auto;
    /* Off-screen until asked for, rather than display:none, so it can animate
       and so its scroll position survives. */
    transform: translateY(100%);
    transition: transform 0.24s ease;
    visibility: hidden;
  }

  .details.rail.is-sheet-open {
    transform: translateY(0);
    visibility: visible;
  }

  /* A grab handle, so it reads as a sheet rather than a panel that fell over. */
  .details.rail::before {
    content: "";
    display: block;
    width: 34px;
    height: 4px;
    margin: 0 auto 12px;
    border-radius: 99px;
    background: color-mix(in srgb, var(--text) 32%, transparent);
  }


  /* ---- projects: the card stops fighting itself ---- */
  /* Five 15px dots said a stage number you could not read, next to a pill that
     already named the status — and they were what collided with the date. */
  .pcard__steps { display: none; }

  .pcard {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 6px 10px;
    align-items: center;
  }

  .pcard__body {
    grid-column: 2;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  /* It measured 0px wide and 84px tall, so every word wrapped to its own line
     and the dots drew straight over it. */
  .pcard__meta {
    min-width: 0;
    width: auto;
    font-size: 11.5px;
    white-space: normal;
  }

  .pcard__status {
    grid-column: 2 / -1;
    justify-self: start;
  }

  /* One row that scrolls, rather than two rows with Archived stranded. */
  .filters {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: 2px;
  }

  .filters::-webkit-scrollbar { display: none; }
  .filters .chip,
  .filters__archived { flex: none; }
}


/* ---- Jump to latest --------------------------------------------------------
   Appears only when the transcript is scrolled away from the bottom. Lives in a
   zero-height anchor directly under .messages so it floats over the end of the
   transcript regardless of how tall the composer, the ask card or the staged
   attachments happen to be. */
.jump-anchor {
  position: relative;
  height: 0;
  overflow: visible;
  z-index: 3;
}

.jump-btn {
  position: absolute;
  bottom: 14px;
  /* Centred by auto margins, NOT by `left: 50%; transform: translateX(-50%)`.
     The bare `button:hover { transform: none }` reset is (0,1,1) against this
     rule's (0,1,0), so on hover it stripped the centring transform and the
     button jumped right by half its own width. Auto margins are layout, not
     transform, so no hover rule can reach them. */
  left: 0;
  right: 0;
  margin: 0 auto;
  width: fit-content;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 13px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: var(--surface-solid);
  color: var(--text);
  font-family: inherit;
  font-size: 12px;
  font-weight: 650;
  cursor: pointer;
  box-shadow: var(--shadow);
  transition: background-color 0.13s ease, border-color 0.13s ease, opacity 0.13s ease;
}

/* The rule above sets `display`, which outranks the UA's [hidden]. */
.jump-btn[hidden] { display: none !important; }

.jump-btn:hover {
  background: var(--hover-surface);
  border-color: var(--accent);
}

/* Something arrived while the reader was away from the bottom. The button
   stops being a navigation affordance and becomes a notification, so it says
   so rather than relying on a dot the eye has to find. */
.jump-btn.has-new {
  border-color: var(--accent);
  color: var(--accent-strong);
}

.jump-btn.has-new::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}

@media (prefers-reduced-motion: reduce) {
  .jump-btn { transition: none; }
}


/* ---- A submitted round of answers -----------------------------------------
   The bubble used to be one undifferentiated block: question and answer at the
   same size, weight and colour, with the questions the longer half — so the
   thing the user actually said was the harder half to find.

   The question drops to a caption and the answer takes the weight, numbered to
   match the pager they were answered in. Display only: pendingQuestionPrefix()
   still sends the agent the same plain numbered text. */
.message--answered {
  /* The bubble is pre-wrap for typed text; structured content must not inherit
     that, or the whitespace between elements renders as blank lines. */
  white-space: normal;
}

.answered {
  display: grid;
  gap: 11px;
}

.answered__row {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: 11px;
  align-items: start;
  min-width: 0;
}

/* The number as a column, not as a prefix on the caption. As a prefix it took
   the caption's muted colour, shared its wrapping line, and left the answer
   aligned to nothing whenever the question ran to two lines. */
.answered__n {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 10.5px;
  font-weight: 700;
  /* Same reason as the status chip: a ratio line-height reserves descender
     space a digit never uses, and place-items centres the box, not the ink. */
  line-height: normal;
  /* So 1 and 4 occupy one width and the column stays true. */
  font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--text) 10%, transparent);
  color: var(--text-muted);
  margin-top: 1px;
}

/* Skipped is the state most likely to be revisited, and italic grey alone is
   easy to slide past. The badge column makes them countable at a glance. */
.answered__row--skipped .answered__n {
  background: transparent;
  border: 1px dashed color-mix(in srgb, var(--text) 26%, transparent);
}

.answered__body {
  min-width: 0;
  display: grid;
  gap: 3px;
}

.answered__q {
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-muted);
}

.answered__a {
  font-size: 13.4px;
  font-weight: 600;
  line-height: 1.5;
  /* A typed answer can be a sentence; it wraps rather than being clipped. */
  overflow-wrap: anywhere;
}

.answered__a--skipped {
  font-weight: 500;
  font-style: italic;
  color: var(--text-muted);
}

.answered__rule {
  height: 1px;
  background: color-mix(in srgb, var(--text) 12%, transparent);
}

/* Anything the user typed alongside the answers, kept verbatim. */
.answered__rest {
  margin: 12px 0 0;
  padding-top: 11px;
  border-top: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
  white-space: pre-wrap;
}

/* ---- App navigation ----
   One fixed element for the whole app. Sits under the theme toggle's z-index
   (250) and on the opposite side, so the two never collide. */
.app-nav {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 240;
  width: 216px;
  height: 100vh;
  padding: 16px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-right: 1px solid var(--border);
}

/* A display rule on an id selector outranks [hidden]. Without this the
   navigation stays on screen over the login view. */
#app-nav[hidden] {
  display: none !important;
}

.app-nav__brand {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 4px 8px 16px;
}

.app-nav__mark {
  width: 24px;
  height: 24px;
  flex: none;
  border-radius: 7px;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 12px;
  font-weight: 800;
}

.app-nav__word {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.04em;
}

.app-nav__group {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.app-nav__item {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 34px;
  padding: 0 9px;
  border-radius: 9px;
  color: var(--text-muted);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
}

.app-nav__item svg {
  width: 16px;
  height: 16px;
  flex: none;
}

.app-nav__item:hover:not(:disabled) {
  background: var(--hover-surface);
  color: var(--text);
}

.app-nav__item.is-active {
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-weight: 700;
}

.app-nav__item:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.app-nav__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.app-nav__soon {
  margin-left: auto;
  flex: none;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 6px;
}

.app-nav__sep {
  height: 1px;
  background: var(--border);
  margin: 12px 9px;
}

/* The three signed-in views make room for the fixed navigation. body stays a
   plain block: #login-view paints a full-viewport particle canvas whose mask
   is computed from a bounding box, and #chat-view runs its own two-pane
   layout. A body-level grid disturbs both for no gain.

   Matches the #id.view specificity (1,1,0) each view's own layout rule
   already uses — a plain #projects-view selector loses to
   "#projects-view.view { padding: 0 }" regardless of source order. */
#projects-view.view,
#chat-view.view,
#usage-view.view,
#space-view.view,
#agents-view.view,
#agent-detail-view.view,
#agent-editor-view.view {
  padding-left: 216px;
}

/* 960px is where the chat rail becomes a bottom sheet. A fixed left column at
   that width leaves the sheet nowhere to go, so the navigation moves to the
   bottom and the labels sit under the icons. */
@media (max-width: 960px) {
  .app-nav {
    top: auto;
    bottom: 0;
    width: 100%;
    height: auto;
    flex-direction: row;
    justify-content: space-around;
    gap: 0;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0px));
    border-right: none;
    border-top: 1px solid var(--border);
  }

  .app-nav__brand,
  .app-nav__sep {
    display: none;
  }

  .app-nav__group {
    flex-direction: row;
    gap: 2px;
  }

  .app-nav__item {
    flex-direction: column;
    gap: 3px;
    height: auto;
    padding: 5px 7px;
    font-size: 10.5px;
    text-align: center;
  }

  /* The badge would double the height of a stacked item and push the bar over
     the content. The disabled state already reads as unavailable. */
  .app-nav__soon {
    display: none;
  }

  #projects-view.view,
  #chat-view.view,
  #usage-view.view,
  #space-view.view,
  #agents-view.view,
  #agent-detail-view.view,
  #agent-editor-view.view {
    padding-left: 0;
    padding-bottom: 66px;
  }
}

.app-nav__foot {
  margin-top: auto;
  padding: 10px 9px 2px;
  border-top: 1px solid var(--border);
}

.app-nav__meter {
  margin: 10px 0 12px;
}

.app-nav__meter-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 10.5px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  margin-bottom: 5px;
}

.app-nav__meter-row b {
  color: var(--text-muted);
  font-weight: 650;
}

.app-nav__meter-bar {
  height: 4px;
  border-radius: 999px;
  background: var(--field-bg);
  overflow: hidden;
}

.app-nav__meter-bar i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
}

/* The footer is at the bottom of the column, so the dropdown opens upward.
   The base rule anchors it under its trigger. This holds on the bottom bar
   too, where upward is also the only direction with room. */
.app-nav .account-menu__dropdown {
  top: auto;
  bottom: calc(100% + 8px);
}

/* In the left column the avatar is at the left edge of the viewport. The
   base rule anchors the dropdown on the right, so the 264px menu opened
   leftward past x=0 and the name and email were cut off. Anchor it on the
   left here. The bottom bar keeps the right anchor, because there the
   avatar is the last item in the row. */
@media (min-width: 961px) {
  .app-nav .account-menu__dropdown {
    left: 0;
    right: auto;
  }
}

/* On the bottom bar the footer is one more item in the row, not a block at
   the end of a column. The meter needs a width it does not have there, so it
   goes; the avatar stays, because sign out must stay reachable on a phone. */
@media (max-width: 960px) {
  .app-nav__foot {
    margin-top: 0;
    padding: 0;
    border-top: none;
    display: flex;
    align-items: center;
  }

  .app-nav__meter {
    display: none;
  }
}

/* ===== RISE Space =======================================================
   Screen 2 of the 2026-09-16 redesign. Every value comes from the mock,
   which took its own values from the tokens above. */
/* padding-top/right/bottom used to be set here too, stacking on top of
   .space's own padding (the shared page-shell rule) and pushing this
   screen's header further down/right than Build's — the same class of bug
   as #projects-view.view below zeroing its own box model out and leaving
   it entirely to .projects. Box model now comes from .space alone. */
#space-view.view {
  flex-direction: column;
  width: 100%;
  max-width: none;
  padding-top: 0;
  padding-right: 0;
  padding-bottom: 0;
  overflow-y: auto;
}

/* Box model (max-width/margin/padding/position/z-index) for .space comes
   from the shared page-shell rule near line 5410
   (".projects, .space, .agents, .agent-editor, .agent-detail"). */

.space__head {
  margin-bottom: 1.25rem;
}

.space__head h1 {
  margin: 0 0 0.25rem;
  font-size: 23px;
  font-weight: 750;
  letter-spacing: -0.02em;
  color: var(--text);
}

.space__head p {
  margin: 0;
  font-size: 12.8px;
  color: var(--text-muted);
}

.space__searchbar {
  display: flex;
  gap: 9px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.space__search {
  flex: 1 1 280px;
  min-width: 200px;
  height: 38px;
  padding: 0 13px;
  border-radius: 10px;
  border: 1px solid var(--border-strong);
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--text-faint);
}

.space__search svg {
  width: 15px;
  height: 15px;
  flex: none;
}

.space__search input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 13px;
}

.space__search input:focus {
  outline: none;
}

.space__seg {
  display: flex;
  height: 38px;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  overflow: hidden;
}

.space__seg-item {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 0 14px;
  border: none;
  border-right: 1px solid var(--border);
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-size: 12.5px;
  font-weight: 650;
}

.space__seg-item:last-child {
  border-right: none;
}

.space__seg-item.is-on {
  background: var(--accent-soft);
  color: var(--accent-strong);
}

.space__seg-item:disabled {
  opacity: 0.55;
  cursor: default;
}

.space__soon {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 6px;
}

.space__chips {
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 16px;
}

.space__chip {
  height: 29px;
  padding: 0 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  color: var(--text-muted);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
}

.space__chip.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.space__mine {
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 12px 14px;
  margin-bottom: 20px;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
}

.space__mine-head {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.space__mine-soon {
  margin: 0;
  font-size: 12px;
  color: var(--text-faint);
}

.space__status {
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--text-muted);
  min-height: 1.2em;
}

.space__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 13px;
}

.space__more {
  display: block;
  margin: 18px auto 0;
  height: 36px;
  padding: 0 15px;
  border-radius: 9px;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 12.8px;
  font-weight: 650;
}

.space__more[hidden] {
  display: none !important;
}

.space__note {
  margin-top: 18px;
  padding: 11px 13px;
  border-radius: 11px;
  border: 1px solid var(--border);
  background: var(--field-bg);
  font-size: 12px;
  color: var(--text-muted);
}

.space-card {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 15px;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: color-mix(in srgb, var(--surface) 68%, transparent);
}

.space-card__top {
  display: flex;
  align-items: center;
  gap: 8px;
}

.space-card__kind {
  height: 21px;
  padding: 0 8px;
  display: inline-flex;
  align-items: center;
  border-radius: 6px;
  background: var(--field-bg);
  color: var(--text-muted);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.space-card__kind--agent {
  background: var(--accent-soft);
  color: var(--accent-strong);
}

.space-card__pill {
  margin-left: auto;
  height: 23px;
  padding: 0 9px;
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  background: var(--field-bg);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}

.space-card__pill.is-live {
  background: var(--success-bg);
  color: var(--success);
}

.space-card__title {
  margin: 0;
  font-size: 14.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--text);
}

.space-card__owner {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-size: 11.5px;
  color: var(--text-faint);
}

.space-card__avatar {
  width: 22px;
  height: 22px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-size: 9px;
  font-weight: 800;
}

.space-card__summary {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-muted);
}

.space-card__tags {
  display: flex;
  gap: 5px;
  flex-wrap: wrap;
}

.space-card__tag {
  padding: 1px 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-muted);
}

.space-card__foot {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: auto;
  padding-top: 11px;
  border-top: 1px solid var(--border);
}

.space-card__open {
  height: 30px;
  padding: 0 11px;
  border-radius: 9px;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 12px;
  font-weight: 650;
}

.space-card__open:disabled {
  opacity: 0.5;
  cursor: default;
}

.space-card__reuse {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  background: none;
  color: var(--accent-strong);
  font: inherit;
  font-size: 12px;
  font-weight: 650;
  opacity: 0.55;
  cursor: default;
}

@media (max-width: 960px) {
  .space__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 700px) {
  .space__grid {
    grid-template-columns: 1fr;
  }
}

/* ---- Agent Builder: the agent list and the one-agent editor ---- */

/* Box model (max-width/margin/padding/position/z-index) for .agents and
   .agent-editor comes from the shared page-shell rule near line 5410
   (".projects, .space, .agents, .agent-editor, .agent-detail"). */

.agents__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 1.25rem;
}

.agents__head h1 {
  margin: 0 0 0.25rem;
  font-size: 23px;
  font-weight: 750;
  letter-spacing: -0.02em;
  color: var(--text);
}

.agents__head p {
  margin: 0;
  font-size: 12.8px;
  color: var(--text-muted);
}

.agents__status {
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--text-muted);
  min-height: 1.2em;
}

.agents__status[hidden],
#agents-status[hidden] {
  display: none !important;
}

.agents__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 13px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.agents__card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  width: 100%;
  padding: 15px;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: color-mix(in srgb, var(--surface) 68%, transparent);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.agents__card:hover {
  border-color: var(--border-strong);
}

.agents__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-size: 16px;
  font-weight: 700;
}

.agents__name {
  font-size: 14.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.agents__desc {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.4;
}

.agents__tools {
  margin-top: auto;
  font-size: 11px;
  font-weight: 650;
  color: var(--text-faint);
}

#agents-empty[hidden] {
  display: none !important;
}

#agents-count[hidden] {
  display: none !important;
}

.app-nav__count {
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--field-bg);
  color: var(--text-muted);
  font-size: 10.5px;
  font-weight: 700;
}

/* ---- The one-agent editor ---- */

.agent-editor__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 14px;
  border: none;
  background: none;
  padding: 0;
  color: var(--text-muted);
  font: inherit;
  font-size: 12.5px;
  font-weight: 650;
  cursor: pointer;
}

.agent-editor__back:hover {
  color: var(--text);
}

.agent-editor__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 1.25rem;
}

.agent-editor__title {
  margin: 0;
  font-size: 21px;
  font-weight: 750;
  letter-spacing: -0.02em;
  color: var(--text);
}

.agent-editor__actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.agent-editor__draft {
  display: flex;
  gap: 9px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 18px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
}

.agent-editor__draft .modal-field {
  flex: 1 1 280px;
  min-width: 200px;
}

.agent-editor__error {
  margin: 0 0 14px;
  padding: 10px 13px;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--danger) 40%, var(--border));
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  color: var(--danger);
  font-size: 12.8px;
}

.agent-editor__error[hidden],
#agent-error[hidden] {
  display: none !important;
}

.agent-editor__form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 640px;
}

.agent-editor__form input[type="text"],
.agent-editor__form textarea {
  width: 100%;
  padding: 0 14px;
  margin: 0;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--field-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 13.6px;
  height: 42px;
}

.agent-editor__form input[type="text"]::placeholder {
  color: var(--text-faint);
}

.agent-editor__form textarea {
  height: auto;
  min-height: 220px;
  padding-block: 11px;
  resize: vertical;
  line-height: 1.5;
}

.agent-editor__form input:focus,
.agent-editor__form textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}

.agent-editor__fieldset {
  border: none;
  padding: 0;
  margin: 0;
}

.agent-editor__fieldset legend {
  padding: 0;
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.agent-editor__icons {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: 7px;
}

.agent-editor__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--field-bg);
  font-size: 16px;
  cursor: pointer;
}

.agent-editor__icon input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.agent-editor__icon:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.agent-editor__tools {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin-top: 7px;
}

.agent-editor__tool {
  display: flex;
  align-items: baseline;
  gap: 9px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  cursor: pointer;
}

.agent-editor__tool code {
  font-size: 12px;
  font-weight: 650;
  color: var(--text);
}

.agent-editor__tool span {
  font-size: 12px;
  color: var(--text-muted);
}

#delete-agent-modal[hidden] {
  display: none !important;
}

@media (max-width: 960px) {
  .agents__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 700px) {
  .agents__grid {
    grid-template-columns: 1fr;
  }

  .agent-editor__head {
    flex-direction: column;
    align-items: stretch;
  }
}

/* ---- Agent detail view (piece 3.2) ---- */

/* Box model (max-width/margin/padding/position/z-index) for .agent-detail
   comes from the shared page-shell rule near line 5410
   (".projects, .space, .agents, .agent-editor, .agent-detail"). */
.agent-detail {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.agent-detail__head {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.agent-detail__identity { flex: 1 1 auto; min-width: 0; }
.agent-detail__desc { color: var(--text-muted); margin: 4px 0 0; }

.agent-detail__actions {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
}

.agent-detail__starters,
.agent-detail__chats {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.agent-detail__starters { flex-direction: row; flex-wrap: wrap; }

.agent-detail__starter,
.agent-detail__chat {
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 14px;
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.agent-detail__starter { width: auto; }

.agent-detail__chat:hover,
.agent-detail__starter:hover { border-color: var(--accent); }

.agent-detail__chat-when {
  color: var(--text-muted);
  font-size: 0.85em;
  flex: 0 0 auto;
}

/* Piece 3.4's Drafts strip (on #agents-view) and Build conversation row (on
   #agent-detail-view). Same row shape as .agent-detail__chat above -- a
   journey opens the same way a chat does. */
.agent-drafts,
.agent-journeys {
  display: block;
  margin-bottom: 20px;
}

.agent-drafts__list,
.agent-journeys__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.agent-drafts__row,
.agent-journeys__row {
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 14px;
  color: var(--text);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.agent-drafts__row:hover,
.agent-journeys__row:hover { border-color: var(--accent); }

.agent-drafts__meta {
  color: var(--text-muted);
  font-size: 0.85em;
}

.agent-drafts[hidden],
.agent-journeys[hidden] { display: none !important; }

/* ---- The per-turn model picker (piece 3.2) ---- */

.chat-model {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.chat-model__label {
  color: var(--text-muted);
  font-size: 0.85em;
}

.chat-model__select {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 4px 8px;
}

/* An author `display` declaration beats the browser's default [hidden] rule
   whatever the selector specificity. This repository has hit that trap three
   times — .tab-bar__tab and the destroy modal on 2026-07-28, then the
   exhaustive audit of 2026-08-14. Every selector above that sets `display` and
   can receive `hidden` needs its own rule here. */
.agent-detail__starters[hidden],
.agent-detail__chats[hidden],
.agent-detail__head[hidden],
.agent-detail__actions[hidden],
.chat-model[hidden] { display: none !important; }

/* #chat-agent-head and #chat-project-head both carry .rail-top, which sets
   display: flex above (see that rule's own comment) — the same trap, on
   elements that can now be toggled with [hidden] for the first time since
   piece 3.2 added agent mode. Scoped to the two ids rather than widening
   .rail-top[hidden] globally, since neither section was ever hidden before
   this feature existed. */
#chat-agent-head[hidden],
#chat-project-head[hidden] { display: none !important; }

/* Piece 3.4's journey-mode rail addition: the seven-tool checklist below the
   stage list, and its divider. Same trap as above -- both set `display`
   below and both are toggled with [hidden] for the first time here. */
.journey-tools-divider {
  height: 1px;
  margin: 14px 0;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  display: block;
}

.journey-tools {
  width: 100%;
  display: block;
}

.journey-tools__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.journey-tools__row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-muted);
}

.journey-tools__row.is-on {
  color: var(--text);
}

.journey-tools__mark {
  width: 14px;
  flex-shrink: 0;
  text-align: center;
  color: var(--accent);
}

.journey-tools__row code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
}

.journey-tools-divider[hidden],
.journey-tools[hidden] { display: none !important; }

/* ---- Journey spec card --------------------------------------------------
   Same width/centering convention as .ask (the pending-question card it sits
   beside). Accent-toned rather than warn-toned: this is a normal, positive
   part of the conversation, not a state the user has to clear. Rendered only
   from the SAVED ROW -- a save_agent call that only narrates itself produces
   no card at all, so there is no "pending" visual state to design for. */
.spec-card {
  width: 92%;
  max-width: 1180px;
  margin: 0 auto 12px;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-2);
  display: block;
}

.spec-card[hidden] { display: none !important; }

.spec-card__head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
}

.spec-card__icon {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  font-size: 14px;
}

.spec-card__head b {
  font-size: 13px;
  font-weight: 700;
}

.spec-card__status {
  margin-left: auto;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--success);
  background: color-mix(in srgb, var(--success) 14%, transparent);
  border-radius: 999px;
  padding: 3px 9px;
}

.spec-card__rows {
  margin: 0;
  padding: 12px 14px;
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: 8px 14px;
}

.spec-card__rows dt {
  font-size: 11px;
  font-weight: 650;
  color: var(--text-muted);
}

.spec-card__rows dd {
  margin: 0;
  font-size: 12.5px;
  color: var(--text);
  min-width: 0;
}

.spec-card__prompt {
  margin: 0 14px 12px;
}

.spec-card__prompt summary {
  font-size: 11.5px;
  font-weight: 650;
  color: var(--text-muted);
  cursor: pointer;
}

.spec-card__prompt pre {
  margin: 8px 0 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 11.5px;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 220px;
  overflow-y: auto;
}

.spec-card__foot {
  display: flex;
  gap: 8px;
  padding: 11px 14px;
  border-top: 1px solid var(--border);
  background: var(--surface);
}

/* SGS DLS corner rules live LAST so they outrank equal-specificity RISE rules
   by order (the colour tokens stay with the theme blocks above). */
/* Carbon's shapes: square containers, fields and buttons. :where() keeps the list
   at (0,2,0) whatever it names, so the round-things rule below still wins. */
:root[data-dls="sgs"] :where(
  button, input, select, textarea, .modal-card, .message, .chat-input__field,
  .build-card, .build-pending, .design-card, .event-card, .spec-card, .space-card,
  .usage-card, .usage-table, .agents__card, .pcard, .account-menu__dropdown,
  .members-list__row, .ask, .ask__opt, .details, .tabs-column, .phase-frame,
  .agent-detail__chat, .agent-editor__draft, .agent-journeys__row, .skeleton-card,
  .markdown-body table, .markdown-body .diagram, .space__search, .space__seg,
  .space__note, .space__mine, .publication-error, .chat-busy-hint, .bulk-bar, .ovf
) {
  border-radius: 0;
}
:root[data-dls="sgs"] :is(.theme-toggle, [class*="avatar"], [class*="__dot"]) {
  border-radius: 50%;
}
:root[data-dls="sgs"] #bulk-bar {
  border-radius: 0;
}
:root[data-dls="sgs"] .auth-step :is(button[type="submit"], input) {
  border-radius: 0;
}

/* ---- Pro mode switch (chat header) ---- */

.pro-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  color: var(--text-muted);
  font-size: 0.85em;
}

.pro-toggle__input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.pro-toggle__track {
  position: relative;
  width: 30px;
  height: 18px;
  border-radius: 9px;
  background: var(--border);
  transition: background 0.15s ease;
  flex: none;
}

.pro-toggle__thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s ease;
}

.pro-toggle__input:checked + .pro-toggle__track { background: var(--accent); }
.pro-toggle__input:checked + .pro-toggle__track .pro-toggle__thumb { transform: translateX(12px); }
.pro-toggle__input:checked ~ .pro-toggle__label { color: var(--text); }
.pro-toggle__input:focus-visible + .pro-toggle__track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.pro-toggle__input:disabled ~ * { opacity: 0.55; cursor: not-allowed; }

.pro-toggle[hidden] { display: none !important; }

/* ---- Composer buttons while RISE is thinking ---- */
/* Send / Approve / Apply are disabled while a turn (or a long Spec Gen Studio
   step) is running (app.js syncChatControls); without this they looked exactly
   as clickable as ever. Last in the file so it wins over every theme's rule,
   including the SGS (Carbon) DLS blocks. */
.chat-input__send:disabled,
.chat-input__send:disabled:hover,
.chat-input__approve:disabled,
.chat-input__approve:disabled:hover {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.3);
}
.chat-input__send:disabled:hover { background: var(--accent); }
.chat-input__approve:disabled:hover { background: transparent; }
