.activity-expand,
.activity-collapse {
  flex: none;
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  padding: 0;
  color: var(--muted);
  background: transparent;
  border-color: transparent;
  border-radius: var(--radius-pill);
}

.activity-expand:hover,
.activity-expand:focus-visible,
.activity-collapse:hover,
.activity-collapse:focus-visible {
  color: var(--ink);
  background: var(--code);
}

.browser-job-stop,
.browser-job-dismiss {
  flex: none;
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  padding: 0 0.7rem;
  margin-left: 0;
  color: var(--muted);
  background: transparent;
  border-color: transparent;
  border-radius: var(--radius-pill);
}

.browser-job-stop {
  min-width: 48px;
  min-height: 48px;
}

@media (prefers-reduced-motion: reduce) {
  .app-run .chip-spinner { animation-duration: 2.4s; }
}

.browser-job-stop:hover,
.browser-job-stop:focus-visible,
.browser-job-dismiss:hover,
.browser-job-dismiss:focus-visible {
  color: var(--ink);
  background: var(--code);
}

/* One line per remembered change under the reply: "Saved: ... · Undo". */
.memory-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-r1);
  padding: 0 0.6rem;
  font-size: var(--text-detail-body);
  color: var(--ink);
}

.memory-note-text {
  min-width: 0;
  overflow-wrap: break-word;
}

.memory-note-sep {
  flex: none;
  color: var(--muted);
}

.memory-note-undo {
  flex: none;
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  padding: 0 0.7rem;
  color: var(--muted);
  background: transparent;
  border-color: transparent;
  border-radius: var(--radius-pill);
}

.memory-note-undo:hover,
.memory-note-undo:focus-visible {
  color: var(--ink);
  background: var(--code);
}

.memory-note-error {
  flex-basis: 100%;
  color: var(--muted);
}

/* The pages-builder preview card (pages-card.js). */
.pages-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-r2);
  min-width: 0;
  padding: var(--space-r2) 0.7rem;
  border: 1px solid var(--card-line);
  border-radius: var(--card-radius);
  background: var(--panel-subtle);
  box-shadow: none;
}

.pages-card-lines {
  display: flex;
  flex-direction: column;
  gap: var(--space-r1);
}

.pages-card-line,
.pages-card-status,
.pages-card-error {
  margin: 0;
  font-size: var(--text-detail-body);
  color: var(--muted);
}

.pages-card-error {
  color: var(--error);
}

.pages-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-r2);
}

.pages-card-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-r2);
  justify-content: center;
  min-height: var(--touch-min);
}
.pages-card-button[hidden] { display: none; }

.browser-job-answer {
  flex-basis: 100%;
  min-width: 0;
  display: flex;
  gap: 0.4rem;
  padding: 0 0.6rem 0.4rem;
}

.browser-job-answer input {
  flex: 1;
  min-width: 0;
}
.browser-job-answer[hidden] { display: none; }

/* One thread component, three variants (data-variant="reply" | "needs-input" | "failed")
   differing only in which controls the .thread-body carries -- this structure (header,
   dismiss, body) is shared by all three.

   The expanded thread is a section of the thread zone's one opaque panel, not its own card,
   so it draws no background, border or shadow of its own. The header and body own their
   shared dialog spacing. */
.thread {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  color: var(--ink);
  background: transparent;
  border: none;
  border-radius: var(--radius-overlay);
}

/* The shared dialog header stays pinned to the thread zone's scroller and keeps an opaque
   fill behind the header while the detail body moves below it. */
.thread-header {
  position: sticky;
  top: -0.25rem;
  z-index: 1;
  background: var(--panel-subtle);
  flex-wrap: nowrap;
}

.thread-header h2.thread-header-title {
  font-weight: bold;
  font-size: var(--text-section);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
  align-self: center;
}

/* The one dismiss x shared by all three variants (reply, needs-input, failed). */
.thread-dismiss:not(.app-dialog-x) {
  flex: none;
  width: var(--touch-min);
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  padding: 0;
  color: var(--muted);
  background: transparent;
  border-color: transparent;
  border-radius: var(--radius-pill);
}

.thread-dismiss:not(.app-dialog-x):hover,
.thread-dismiss:not(.app-dialog-x):focus-visible {
  color: var(--ink);
  background: var(--code);
}

.thread-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-r2);
  min-width: 0;
  font-size: var(--text-detail-body);
  overflow-wrap: anywhere;
}

/* reply: a prose answer, or a cancelled proposal's Review control. */
.thread-reply-text {
  overflow-wrap: anywhere;
}

/* The body is a flex column, so a paragraph's default 1em margins never collapse; the
   column's gap alone spaces one reply from the next. A reply paragraph holds
   markdown-rendered HTML, so the inner element's first/last margins are zeroed too. */
.thread-body > .thread-reply-text {
  margin-block: 0;
}

.thread-reply-text > :first-child {
  margin-top: 0;
}

.thread-reply-text > :last-child {
  margin-bottom: 0;
}

.thread-review {
  align-self: flex-start;
}

/* reply: a research turn's own numbered source list, matching the [n] markers already in
   .thread-reply-text above -- a compact, muted list, never styled as prose. */
.thread-sources {
  display: flex;
  flex-direction: column;
  gap: var(--space-r1);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-detail-meta);
  color: var(--muted);
}

.thread-source {
  overflow-wrap: anywhere;
}

/* The one sources row that is a button, not a link (see renderReplyBody in chat.js): it opens
   a Library item inside the app rather than a new tab. It must still read as one of the list's
   own rows, so the global button pill -- which carries padding, an accent background and a
   44px min-height -- is overridden away declaration by declaration. */
.thread-source-open {
  padding: 0;
  min-height: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
  text-align: left;
}

/* needs-input: a question, up to 3 tap-to-answer rows inline, the rest behind
   .thread-options-more, and an option to answer in the composer instead. */
.thread-question {
  font-weight: var(--w-title);
  overflow-wrap: anywhere;
}

.thread-options {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.thread-option {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: var(--space-r2) 0.7rem;
  color: var(--ink);
  text-align: left;
  background: var(--panel-subtle);
  border: 1px solid var(--card-line);
  border-radius: var(--card-radius);
  overflow-wrap: anywhere;
}

/* Artwork beside the row's text, bounded -- podcast art is routinely 600px square and
   an unbounded <img> sets the row's height to its own intrinsic size. */
.thread-option-art {
  width: 40px;
  height: 40px;
  flex: none;
  object-fit: cover;
  border-radius: var(--radius-control);
}

.thread-option:hover,
.thread-option:focus-visible {
  background: var(--panel);
}

.thread-options-more {
  align-self: flex-start;
  color: var(--muted);
  background: transparent;
  border-color: transparent;
  font-size: var(--text-detail-meta);
}

/* failed: the original utterance, what was understood, and Retry. */
.thread-original {
  color: var(--muted);
}

.thread-original,
.thread-understood {
  overflow-wrap: anywhere;
}

.thread-retry {
  align-self: flex-start;
}

/* The expanded review panel owns the whole display band, whether or not a thread already
   exists in the thread zone -- there is one content scroller at a time. */
.chat.email-review.review-expanded .chat-upper {
  flex: 1 1 0;
}

.chat.email-review.review-expanded .chat-lower {
  flex: none;
  /* Takes back .chat's 0.75rem flex gap between the panel and this (now empty) reservation,
     so the panel's scroller ends where a .chat-lower scroller does, not a gap's worth
     further. */
  margin-top: calc(-1 * var(--shell-gap));
}

h1 {
  margin: 0;
  font-size: var(--text-caption);
  font-weight: var(--w-title);
  color: var(--muted);
}

/* Carries only the visually hidden <h1> now -- navigation moved to the dock and the All
   launcher (see .dock-nav and .apps below). Kept, rather than deleted along with the old
   ribbon, because WALL_MODE still hides it as one element (chat.js) and a page needs an
   h1 somewhere regardless of what else is on screen. display: contents because that h1 is
   .visually-hidden (position: absolute): a box of its own would be an in-flow 0px flex item
   with .chat-upper's gap beside it. The h1 stays in the accessibility tree. */
.chat-header {
  display: contents;
}

/* THE DOCK. Four destinations, always one tap away, whatever pane is showing: Today,
   Calendar, Inbox and All. Same size selected or not -- no growing button here, unlike
   the ribbon this replaces -- so switching destinations never reflows the shell around
   it. It sits on the wallpaper, not on a card ground, the same way the reference's own
   .gp-nav does: the dock is shell chrome in its own grid track, not a zone of the
   composer's bar.

   This padding-bottom is the one owner of var(--safe-bottom) in portrait: the dock, not
   the command bar, meets the screen's bottom edge, so it lifts its own buttons clear of
   the home indicator itself. */
.dock-nav {
  display: grid;
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--space-xs);
  padding: var(--space-lg) var(--side-gutter) calc(var(--space-md) + var(--safe-bottom));
}

/* Every slot (five Favorites plus All) shows in one row while arranging; the Favorites label spans the row. */
#dock-nav[data-editing] {
  grid-auto-flow: row;
  grid-template-columns: repeat(6, minmax(0, 1fr));
}

/* While the software keyboard is up for the composer (portrait only, so the landscape
   side column keeps its width), the dock's destinations give their height to the input
   and any reply above it; they return the moment the keyboard goes or focus moves to
   another text field. Blurring the composer alone does not bring them back: the visual
   viewport still reports the keyboard until it finishes leaving, and the dock would
   flash above it. */
@media (orientation: portrait) {
  body:has(.chat.keyboard-open) .dock-nav {
    display: none;
  }
}

/* While another text field has the software keyboard (app-parts.js sets the class on <html>),
   the mini player, composer and dock all hide. The html class plus an ID outranks
   .chat.keyboard-open's rule and any plain-class display rule; #playback-mini sits inside
   #composer but is named too so it stays hidden if it is ever moved out. */
html.field-keyboard-open #playback-mini,
html.field-keyboard-open #composer,
html.field-keyboard-open #dock-nav {
  display: none;
}

.dock-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  min-height: 48px;
  min-width: 0;
  padding: var(--space-2xs) 0;
  color: var(--ink-on-wall-muted);
  background: transparent;
  border: 0;
  border-radius: 0;
  font-size: var(--text-dock-label);
  line-height: 1.25;
  text-align: center;
  text-shadow: var(--text-shadow-on-wall);
  -webkit-tap-highlight-color: transparent;
}

/* An empty Favorites slot is hidden outside editing; display: flex above would beat the UA
   [hidden] rule. */
.dock-button[hidden] {
  display: none;
}

/* A native app cloned into a Favorites slot: the same inset rounded icon as its launcher tile. */
.dock-icon .app-tile-native-img {
  width: calc(var(--dock-tile) * var(--launcher-native-inset));
  height: calc(var(--dock-tile) * var(--launcher-native-inset));
  border-radius: var(--radius-nested);
}

/* Labels stay whole and clip at the cell edge if a future label is too wide. */
.dock-label {
  font-size: var(--text-dock-label);
  letter-spacing: var(--text-dock-label-tracking, 0);
  min-width: 0;
  width: 100%;
  max-width: 100%;
  white-space: normal;
  overflow: clip;
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
  text-align: center;
}

/* The "task finished" toast (agent-result-toast.js): fixed inside .app-area, above the
   command bar wherever the keyboard has left it, so showing it never reflows a pane.
   Dialogs sit in the top layer and cover it. */
.agent-result-toast {
  position: fixed;
  z-index: 45;
  left: 0;
  right: 0;
  margin-inline: auto;
  width: min(var(--shell-max), 100% - 2 * var(--space-r4));
  bottom: calc(var(--chat-overlay-bottom, 0px) + var(--command-bar-space) + var(--space-r2));
  display: flex;
  align-items: center;
  gap: var(--space-r2);
  padding: var(--space-md) var(--space-r3);
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  box-shadow: var(--shadow-sheet);
  font-size: var(--text-detail-body);
}

.agent-result-toast[hidden] {
  display: none;
}

.agent-result-toast-text {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.agent-result-toast-status {
  flex: none;
  color: var(--muted);
}

.agent-result-toast .app-pill {
  margin-left: auto;
  flex: none;
}

/* Current is never colour alone on its own label -- the icon ground carries a ring too --
   but the dock is a four-item destination list beside a
   fourteen-tile launcher, not a place a colour-blind reader has to disambiguate past
   that; the launcher's own tiles are never "current" in this sense at all. `[aria-current]`
   rather than `[aria-current="page"]` alone: the All button also reads current, as
   aria-current="true", whenever the chosen display is one the dock itself has no button
   for (see chat.js's syncNav()) -- "you are inside All" is exactly as much a current
   state as "you are on Today". */
.dock-button[aria-current] {
  color: var(--dock-ink-current);
}

.dock-button[aria-current] .dock-icon {
  color: var(--on-accent);
  background: var(--dock-current-ground);
  box-shadow: var(--shadow-tile-icon-dock), 0 0 0 1.5px var(--dock-current-ring);
}

/* In landscape orientation, the dock sits on the left side full height instead of
   bottom: the shell's first column, with the app area beside it in the second. */
@media (orientation: landscape) {
  :root {
    --dock-side-width: max(calc(var(--dock-tile) * 1.5), 5.25rem);
  }

  body.app-shell {
    grid-template-columns: var(--dock-side-width) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
  }

  .app-area {
    grid-column: 2;
    margin-bottom: var(--safe-bottom);
  }

  .app-wallpaper {
    grid-column: 2;
    grid-row: 1;
  }

  .dock-nav {
    grid-row: 1;
    grid-column: 1;
    width: auto;
    justify-self: stretch;
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    padding: calc(var(--space-md) + var(--safe-top)) var(--space-xs) calc(var(--space-md) + var(--safe-bottom));
    overflow-y: auto;
  }

  .dock-button {
    flex: 1 1 0;
    min-height: 0;
    max-height: calc(var(--dock-tile) * 1.9);
    justify-content: center;
  }
}
