/* --- Mobile --- */
@media (max-width: 768px) {
  /* No sidebar on a phone, and so no button to open one. It was a slide-in
     drawer, and everything it held has somewhere else to be here: the dashboard
     lists the sessions and its cards carry the same ⋯ menu, the filter is
     mirrored above those cards, and Settings, New Session and the archive
     toggle are sections of the bar.
     The one thing that does not survive: a lead's subagents are reachable from
     the session you are IN, through the bar, and no longer from a card for a
     lead you are not in. That was the drawer's last exclusive, and losing it is
     the price of the screen. */
  #sidebar,
  #sidebar-toggle { display: none; }

  .msg-user .bubble { max-width: 90%; }
  .tool-call-body { max-height: 200px; }
}

@media (min-width: 769px) {
  #sidebar.collapsed {
    width: 0;
    min-width: 0;
    border-right: none;
  }
}

/* --- Theme picker --- */
/* --- Settings --- */
.sidebar-bottom-btns {
  display: flex;
  gap: 8px;
  margin: 8px;
}
/* The two square doors at the foot of the sidebar. Both lead somewhere that
   belongs to the whole app rather than to any session, and they are one rule
   because they are one kind of button — a second copy of it would be a second
   place for the pair to stop matching. */
#settings-btn,
#saved-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-muted);
  cursor: pointer;
  font-size: 16px;
  padding: 8px 12px;
  transition: color 0.15s, background 0.15s;
}
@media (hover: hover) {
  #settings-btn:hover,
  #saved-btn:hover { color: var(--text); background: var(--bg-hover); }
}
.sidebar-bottom-btns #new-session-btn {
  flex: 1;
  margin: 0;
}
.settings-section {
  margin-bottom: 16px;
}
.settings-label {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 8px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.settings-hint {
  font-size: 12px;
  color: var(--text-muted);
  margin: 6px 0 0;
}
/* Settings → Storage → Docker images, folded under the agency image's row: the
   version record, a name and its version per row. Two columns, the names as wide
   as the longest. */
/* The image's own line (reference · size · built) — drawn ONLY where the fold
   stands on its own, with no agency row above it to say those three things.
   A line rather than a row of the list: as a row it would get only the column
   the longest NAME leaves — 153px of the 288px sheet at 320px — and
   "agency:latest · 4.1 GB · built 10 Sep 2026" wrapped to three lines there
   (measured), against two on a line of its own with the whole width. The
   reference is the fact being stated, so it keeps the text colour and the two
   numbers stay muted beside it. */
.image-meta {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}
.image-meta .image-ref { color: var(--text); }
/* The date moves to the next line whole: "built Sep 10," with the year alone
   under it is what the wrap did at 320px without this. */
.image-meta .image-built { white-space: nowrap; }
.image-versions {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: 12px;
  row-gap: 1px;
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
}
.image-versions dt { color: var(--text-muted); }
.image-versions dd { margin: 0; overflow-wrap: anywhere; }
/* What the VM restart is doing, under the button that asked for it. Three
   states and each says which it is in words: nothing here is a spinner that
   could equally mean "wedged". No thresholds and no colour on the ordinary
   ones — only the failure is worth a red word, because only it needs acting on. */
#vm-restart-state {
  margin-top: 8px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-muted);
}
#vm-restart-state.vm-state-failed { color: var(--red); }
/* Settings → Machine → Agent wrappers: one red sentence, and only when a
   wrapper on PATH is an older copy of run.sh than the relay's checkout. Red
   for the same reason the line above is: it is the state someone has to act
   on, and the section is not drawn at all when nobody has to. */
#wrappers-stale {
  margin: 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--red);
  overflow-wrap: anywhere;
}
#theme-grid,
#diff-default-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}
#agent-font-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 6px;
}
#agent-size-grid,
#agent-weight-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}

/* Brightness slider: −100 (dim) … 0 (theme default) … +100 (bright). */
.agent-bright-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.agent-bright-row .bright-end {
  font-size: 11px;
  color: var(--text-muted);
  flex: 0 0 auto;
}
#agent-bright-slider {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
  outline: none;
  cursor: pointer;
}
#agent-bright-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
}
#agent-bright-slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
}
#agent-bright-reset {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 11px;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
}
#agent-bright-reset:disabled {
  opacity: 0.4;
  cursor: default;
  text-decoration: none;
}

/* Custom colour row: native swatch + hex field + reset. */
.agent-color-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
#agent-color-picker {
  -webkit-appearance: none;
  appearance: none;
  width: 36px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: none;
  cursor: pointer;
  flex: 0 0 auto;
}
#agent-color-picker::-webkit-color-swatch-wrapper { padding: 2px; }
#agent-color-picker::-webkit-color-swatch { border: none; border-radius: 4px; }
#agent-color-picker::-moz-color-swatch { border: none; border-radius: 4px; }
#agent-color-hex {
  flex: 1;
  min-width: 0;
  padding: 6px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--mono);
  font-size: 13px;
  outline: none;
}
#agent-color-hex:focus { border-color: var(--accent); }
#agent-color-hex.invalid { border-color: var(--red); }
#agent-color-reset {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 11px;
  cursor: pointer;
  padding: 0 2px;
  text-decoration: underline;
  flex: 0 0 auto;
}
#agent-color-reset:disabled {
  opacity: 0.4;
  cursor: default;
  text-decoration: none;
}
.theme-option,
.layout-option {
  padding: 8px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 13px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
@media (hover: hover) {
  .theme-option:hover,
  .layout-option:hover { background: var(--bg-hover); }
}
.theme-option.active,
.layout-option.active {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

/* Every other button a section puts on screen, given the options' look above by
   default: most of them already ask for .layout-option, and the two that never
   did (Your handle's Edit/Save, Restart server) came out at the browser's own
   size — measured 21px tall beside a 33px option button, which read as a link
   rather than a button.
   Kept at ONE class + one type, so a section's own rule for its own buttons
   still wins on document order: the compact rows (.auth-row, .allowed-row,
   .auth-login-row) keep their smaller text and padding, and the underlined text
   buttons (#agent-bright-reset, #agent-color-reset) are ids and outrank it
   outright. :where() is what keeps the exception below from raising that
   specificity — the sheets that live INSIDE a section (#auth-logins' Log in and
   Paste sheets) have .dialog-actions rows of their own, and those buttons are
   that dialog's, not this section's. */
.settings-section button:not(:where(.settings-section dialog button)) {
  padding: 8px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 13px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
@media (hover: hover) {
  .settings-section button:not(:where(.settings-section dialog button)):hover { background: var(--bg-hover); }
}
/* Rare and destructive, so it wears what the action rows give their danger
   button (12-dialogs.css): a red fill, not a red word. The border stays in the
   box — transparent, not none — because the height above counts it. */
.settings-section button.danger {
  background: var(--red);
  border-color: transparent;
  color: white;
}
@media (hover: hover) {
  .settings-section button.danger:hover { background: color-mix(in srgb, var(--red), black 20%); }
}

/* Settings → Your handle: the hex field's look, with a Save beside it. */
.operator-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
#operator-handle {
  flex: 1;
  min-width: 0;
  padding: 6px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--mono);
  font-size: 13px;
  outline: none;
}
#operator-handle:focus { border-color: var(--accent); }
#operator-handle.invalid { border-color: var(--red); }
#operator-status.invalid { color: var(--red); }

/* Settings → Machine → This install (public/js/install.js). The name field is
   the handle field's box without the Save beside it — this one commits on the
   way out — and the swatches below it are the icon's big disc at the size a
   fingertip wants. */
#install-name {
  width: 100%;
  padding: 6px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--mono);
  font-size: 13px;
  outline: none;
}
#install-name:focus { border-color: var(--accent); }
#install-name-hint.invalid { color: var(--red); }
.install-schemes {
  display: flex;
  gap: 10px;
  margin-top: 10px;
}
/* The colour is the icon's own paint, handed down from the relay as `--swatch`
   on each button (the hexes live in scripts/gen-icons.py and src/install.ts,
   never here), so what these show is what the PNGs hold and not the current
   theme's idea of the same word. A disc, because that is the shape the icon
   changes.

   Addressed through #install-schemes so these beat the panel's generic
   `.settings-section button` above — which would otherwise paint every swatch
   var(--bg), and var(--bg-hover) under the pointer, i.e. all four the same. */
#install-schemes .install-swatch {
  width: 32px;
  height: 32px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid transparent;
  background: var(--swatch);
  cursor: pointer;
}
/* The ring sits OUTSIDE the disc, so choosing one changes no size and the row
   never shifts under a press. */
#install-schemes .install-swatch[aria-pressed="true"] {
  outline: 2px solid var(--text);
  outline-offset: 3px;
}
#install-schemes .install-swatch:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
@media (hover: hover) {
  #install-schemes .install-swatch:hover {
    background: var(--swatch);
    border-color: var(--text);
  }
}

/* Settings' eight tabs (public/js/settings-tabs.js). One strip in the markup,
   two shapes: a side menu down the left of the dialog on a desktop, a row of
   tabs under the title on a phone. Plain text either way — eight bordered
   buttons across a 320px sheet is not a row, and down a desktop column they
   would read as eight things to press rather than one place you are in. */
#settings-dialog .sheet-head h2 { margin-bottom: 0; }
.settings-tab {
  background: none;
  border: none;
  padding: 0;
  color: var(--text-muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
/* No focus ring HERE, and nowhere else on that argument: in this strip focus and
   "active" are the same item and cannot part. The roving tabindex keeps the one
   tab that is on in the Tab order, and the arrow keys move focus and selection
   together (settings-tabs.js), so the ring only ever drew a second marker around
   the item the active style already marks — a tab that looked doubly on. Any
   control where focus can rest somewhere the styling does not say keeps its ring. */
.settings-tab,
.settings-tab:focus-visible { outline: none; }

/* --- Desktop: a side menu inside the dialog ---
   The dialog is a two-column grid — menu, panel — with the title and the action
   row across both. .sheet-head is display: contents so its children ARE those
   grid items (the back arrow is display: none here and so is no item at all);
   the alternative was lifting the strip out of the head in the markup, which
   would have cost the phone its one sticky row.
   Wider than the 400px every other dialog gets: a menu column plus the widest
   thing a panel holds (Storage's three columns, the allowed paths) does not fit
   in 352px of content. */
@media (min-width: 769px) {
  #settings-dialog { max-width: min(680px, calc(100vw - 32px)); }
  /* [open], and an id beats the UA's `dialog:not([open]) { display: none }`:
     without it this `display` applies to the CLOSED dialog too and Settings sits
     over the page, swallowing every click meant for what is underneath. Measured
     as exactly that — a dashboard card that could not be pressed. */
  #settings-dialog[open] {
    display: grid;
    grid-template-columns: 156px minmax(0, 1fr);
    column-gap: 20px;
    row-gap: 16px;
    align-content: start;
  }
  #settings-dialog .sheet-head { display: contents; }
  #settings-dialog .sheet-head h2,
  #settings-dialog > .dialog-actions { grid-column: 1 / -1; }
  #settings-dialog > .dialog-actions { margin-top: 0; }
  #settings-tabs {
    grid-column: 1;
    display: flex;
    flex-direction: column;
    gap: 2px;
    /* The menu stays while the panel scrolls: it is as tall as its own content,
       parked at the top of its row, and stuck there once that row scrolls past.
       (align-self: start is what makes it stickable — a stretched grid item is
       as tall as the row and has nothing to stick within.) */
    align-self: start;
    position: sticky;
    top: 0;
  }
  .settings-tab {
    padding: 7px 10px;
    border-radius: var(--radius);
    text-align: left;
  }
  @media (hover: hover) {
    .settings-tab:hover { color: var(--text); background: var(--bg-hover); }
  }
  .settings-tab.active {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    font-weight: 600;
  }
}

/* --- Phone: a row of tabs under the title ---
   Still inside the sticky .sheet-head, so it stays under the title while a panel
   scrolls. Seven labels are far wider than a 320px sheet ("Appearance" alone
   measures 70, the row 438), so it scrolls sideways instead of wrapping — and hides the bar it
   scrolls with, which on a phone is what every other horizontal strip here does. */
@media (max-width: 768px) {
  #settings-dialog .sheet-head {
    flex-wrap: wrap;
    row-gap: 8px;
  }
  #settings-tabs {
    flex-basis: 100%;
    display: flex;
    flex-wrap: nowrap;
    gap: 16px;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* Out to the sheet's padding edge and back, like the head itself, so the
       first and last tab scroll to the screen's own edges. */
    margin: 0 calc(-1 * var(--dlg-pad));
    padding: 0 var(--dlg-pad);
  }
  #settings-tabs::-webkit-scrollbar { display: none; }
  .settings-tab {
    padding: 4px 0;
    /* Always there, transparent until the tab is on: a border that appears would
       move the row by 2px on every tap. */
    border-bottom: 2px solid transparent;
  }
  .settings-tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
  }
}
