/* The one composer row: 67px resting height (50px row + the reference's own gp-footer
   padding, 17px 0 0 with --side-gutter sides). The image-previews strip, the
   failure/dictation status lines, and the overflow sheet are arranged around this row.
   .composer-status is an in-flow row directly above the composer row inside .composer,
   so the page content above shrinks and nothing is covered. The status rail (above) is
   also in the flow above the row: attached to the pill's top, it grows this box and
   therefore the measured reservation with it.

   Flat (INV-6): #command-bar (above) now draws no surface of its own -- the composer pill
   (.composer-row, below) is what floats directly on the wallpaper, the reference's own
   arrangement, so .composer here stays a plain padded row rather than a card: no
   background, border or shadow of its own. The bottom padding is 0, not symmetric with
   the top -- the dock below (.dock-nav) supplies its own 10px top padding for the gap
   between the pill and the dock, the same split the reference's own gp-footer/gp-nav pair
   uses, rather than doubling that gap here too. background/border/shadow are written
   explicitly rather than simply omitted, so nothing the cascade adds later (a button skin,
   a focus style) fills them in unnoticed. */
.composer {
  position: relative;
  padding: var(--composer-pad-top) var(--side-gutter) var(--space-r2);
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

/* [hidden] is not set on the composer by anything today: the command bar is visible on
   every display tab, Today included -- there is no more per-tab hide to guard against.
   This rule is kept only as a cheap safety net should [hidden] ever be set for an
   unrelated reason; it must not be reintroduced as a way to hide the composer per tab. */
.composer[hidden] {
  display: none;
}

/* flex-end, not center, and this is INV-3 rather than taste: the field grows upward as it
   fills (to five lines, then it scrolls internally), which makes this row taller. Centred,
   the mic and send anchors would ride up by half of every pixel the field gained -- measured
   at 22.5px by three lines in a real browser, which no JSDOM test can see. Pinned to the
   bottom they stay at one screen coordinate in every state, which is what the composer
   promises: the two anchors never move, whatever the field does above them. */
/* The reference's own pill composer: one rounded surface holding every control, rather
   than a bare row of icons on the bar's own ground. align-items stays flex-end (INV-3's
   own reasoning, restated below) even though the pill's padding is even on every side --
   a multi-line textarea still grows the row taller than its neighbours, and flex-end is
   what keeps the mic and Send pinned to one screen coordinate while it does. */
.composer-row {
  display: flex;
  align-items: flex-end;
  min-width: 0;
  min-height: 50px;
  gap: 7px;
  padding: var(--space-md);
  background: var(--panel-subtle);
  border-radius: var(--radius-surface);
  box-shadow: var(--shadow-inset-highlight);
}

/* Typed text spans the pill while its controls sit below. */
.composer-row:has(#message:not(:placeholder-shown)) {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: end;
}

.composer-row:has(#message:not(:placeholder-shown)) .composer-input {
  grid-column: 1 / -1;
  grid-row: 1;
}

.composer-row:has(#message:not(:placeholder-shown)) .composer-overflow {
  grid-column: 1;
  grid-row: 2;
}

.composer-row:has(#message:not(:placeholder-shown)) .dictate {
  grid-column: 3;
  grid-row: 2;
}

.composer-row:has(#message:not(:placeholder-shown)) #send {
  grid-column: 4;
  grid-row: 2;
}

/* All apps needs the room for its tile rows. */
:root:has(#apps:not([hidden])) .composer-row:has(#message:not(:placeholder-shown)) {
  display: flex;
  align-items: flex-end;
}

:root:has(#apps:not([hidden])) .composer-row:has(#message:not(:placeholder-shown)) .composer-input,
:root:has(#apps:not([hidden])) .composer-row:has(#message:not(:placeholder-shown)) .composer-overflow,
:root:has(#apps:not([hidden])) .composer-row:has(#message:not(:placeholder-shown)) .dictate,
:root:has(#apps:not([hidden])) .composer-row:has(#message:not(:placeholder-shown)) #send {
  grid-column: auto;
  grid-row: auto;
}

:root:has(#apps:not([hidden])) .composer-row:has(#message:not(:placeholder-shown)) .composer-input {
  flex: 1 1 0;
  min-width: 0;
}

:root:has(#apps:not([hidden])) .composer-row:has(#message:not(:placeholder-shown)) textarea {
  white-space: pre;
  overflow-x: hidden;
  width: 100%;
}

/* The All-apps field stays one unwrapped line, and the live-words layer must break lines the
   same way -- not at all -- or its words would sit where the field's do not. */
:root:has(#apps:not([hidden])) .composer-row:has(#message:not(:placeholder-shown)) .composer-live {
  white-space: pre;
  overflow-x: hidden;
}

/* .composer-row is repeated in these selectors so they outrank the later .icon-button base. */
.composer-row .composer-overflow,
.composer-row .dictate {
  flex: none;
  width: var(--touch-min);
  min-width: var(--touch-min);
  min-height: var(--touch-min);
}

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

.podcast-mini-chip.composer-agent {
  position: absolute;
  left: var(--side-gutter);
  right: auto;
  top: var(--composer-pad-top);
  color: var(--ink);
  background: var(--player-progress-track);
  overflow: visible;
}

/* The composer height is measured into --command-bar-space, so the chip's row is reserved for every pane instead of floating over content. */
.composer:has(.composer-agent:not([hidden])) {
  padding-top: calc(var(--composer-pad-top) + var(--touch-min) + var(--stack-gap));
}

.composer-agent svg {
  width: 55%;
  height: 55%;
}

.composer-agent[data-working]::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  pointer-events: none;
  animation: chip-spin 2.4s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .composer-agent[data-working]::after {
    animation: none;
    border-color: var(--accent);
  }
}

.composer-agent-badges {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  display: flex;
  gap: var(--space-2xs);
}

.composer-agent-count {
  display: inline-grid;
  place-items: center;
  width: calc(var(--text-caption) + var(--space-xs));
  height: calc(var(--text-caption) + var(--space-xs));
  border-radius: 50%;
  color: var(--on-accent);
  font-size: var(--text-caption);
  font-weight: 600;
  line-height: 1;
  padding: 0;
}

.composer-agent-count[data-count-capped] {
  width: auto;
  padding-inline: var(--space-2xs);
}

.composer-agent-count[hidden] { display: none; }
.composer-agent-count-working { background: var(--accent); }
.composer-agent-count-done { background: var(--ok); }
.composer-agent-count-attention { background: var(--error); }

/* The mic is the row's one filled control: a gold square in the accent. */
.composer-row .dictate {
  color: var(--mic-ink);
  background: var(--mic-fill);
  border-color: var(--mic-fill);
  border-radius: var(--radius-tile);
}

.composer-row .dictate:hover:not(:disabled),
.composer-row .dictate:focus-visible {
  color: var(--mic-ink);
  background: var(--mic-fill);
  filter: brightness(0.94);
}

/* Empty field: the arrow steps aside and the mic is the only action. It returns once there
   is text, an attached image, or a dictation in progress (the cases chat.js enables Send). */
.composer-row:has(#message:placeholder-shown):not(:has(#image-previews:not([hidden]))):not(:has(.dictate[data-state="listening"], .dictate[data-state="starting"], .dictate[data-state="stopping"])) #send {
  display: none;
}

#message::placeholder {
  color: var(--muted);
  font-size: var(--text-caption);
  opacity: 1;
}

/* The stopping field is still a preview until the final transcript settles it. */
.composer-row:has(.dictate[data-state="stopping"]) #message {
  color: var(--muted);
}

/* While listening, the field holds the full live text but paints none of it: its glyphs and caret
   go transparent and the .composer-live layer draws the same words in their place. The layer is
   absolutely positioned over .composer-input (the textarea's own parent, so `font: inherit` lands
   the same family, size, line-height and tracking the textarea gets) and shares its padding,
   border width and wrapping, so the two texts sit glyph on glyph. */
.composer-live {
  display: none;
  position: absolute;
  inset: 0;
  padding: var(--composer-field-pad);
  font: inherit;
  font-size: var(--text-input);
  border: 1px solid transparent;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  overflow: hidden;
  pointer-events: none;
  color: var(--ink);
}

.composer-live .dictation-soft {
  color: var(--muted);
}

.composer-row:has(.dictate[data-state="listening"]) .composer-live {
  display: block;
}

.composer-row:has(.dictate[data-state="listening"]) #message {
  color: transparent;
  caret-color: transparent;
}

.composer-row[data-pending] #message {
  animation: dictation-pending-pulse 1.4s ease-in-out infinite;
}

/* The field never drops below 16px (iOS zooms the page on focus under it). */
.composer-input textarea {
  font-size: var(--text-input);
}

#send {
  flex: none;
  width: var(--touch-min);
  min-width: var(--touch-min);
  min-height: var(--touch-min);
}

/* The sheet the `+` button opens: camera and photo library, plus the compact answer-model
   picker (.composer-model-picker) -- nothing else lives here. It is anchored above the
   row it opens from rather than growing the composer; .composer's own position: relative
   supplies its containing block. The composer status is in flow. */
.composer-overflow-sheet {
  position: absolute;
  z-index: 1;
  left: 0.75rem;
  bottom: calc(100% + 0.5rem);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-r2);
  padding: var(--space-r2);
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  box-shadow: var(--shadow-sheet);
}

.composer-overflow-sheet[hidden] {
  display: none;
}

/* The sheet's items carry their visible names beside their icons: an icon-only item in a
   menu is a guess, and this sheet is the app's reference pattern for every overflow menu
   ("Add images", "Take photo" -- aria-labels alone never did the naming here). Row-shaped
   rather than the square .icon-button default, on the same 44px floor. */
.composer-overflow-sheet .icon-button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  width: auto;
  min-width: var(--control-min);
  padding: 0 0.7rem;
}

.composer-overflow-label {
  color: var(--ink);
  font-size: var(--text-detail-body);
  font-weight: var(--w-meta);
  white-space: nowrap;
}

/* The one non-button item in the overflow sheet: a plain .app-field select, sized to the
   same row rather than growing the sheet. Scoped narrow -- this menu is a small popover,
   not a settings page -- and truncates a long label rather than wrapping the sheet wider. */
.chat-model-select {
  max-width: 12rem;
  min-height: var(--control-min);
}

.composer-input {
  /* The field's one padding value: the textarea and the .composer-live layer over it carry it
     together so their texts sit glyph on glyph. */
  --composer-field-pad: var(--composer-field-pad-y, 0.35rem) calc(0.3rem + 5px);
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
  flex: 1 1 auto;
}

/* The border is not the shared rule's: the pill itself is the field's whole boundary now
   (.composer-row), so the textarea inside it draws none of its own -- unlike every other
   textarea in the app, which still keeps the shared rule's accent outline. */
.composer-input textarea {
  border-color: transparent;
}

.message-clear {
  position: relative;
  flex: none;
  align-self: flex-start;
  margin-top: calc(0.35rem + (var(--text-input) * var(--leading-detail-body) - 1.5rem) / 2);
  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);
}

/* The visible x stays 1.5rem; the overlay grows the hit area to --touch-min, with the
   button's relative position as its containing block. */
.message-clear::after {
  content: '';
  position: absolute;
  inset: calc((var(--touch-min) - 1.5rem) / -2);
}

.message-clear[hidden] {
  display: none;
}

.composer-row:has(.dictate[data-state="starting"], .dictate[data-state="listening"], .dictate[data-state="stopping"]) .message-clear[hidden] {
  display: block;
  visibility: hidden;
  pointer-events: none;
}

.message-clear:hover,
.message-clear:focus-visible {
  color: var(--ink);
  background: var(--code);
}

/* The staged-attachment strip and the failure/dictation status lines: an in-flow row
   directly above the composer row inside .composer, so the page content above shrinks
   and nothing is covered. */
.composer-status {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding-bottom: var(--space-r2);
}

.composer-status:empty {
  display: none;
}

.composer-status:not(:has(> :not([hidden]):not(:empty))) {
  display: none;
}

.image-previews {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  padding: 0.1rem 0.1rem 0;
  min-width: 0;
}

.image-previews[hidden],
.image-message[hidden],
#agent-task-context[hidden] {
  display: none;
}

.image-preview {
  position: relative;
  width: 4rem;
  height: 4rem;
}

.image-preview img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-control);
}

.remove-image {
  position: absolute;
  top: -0.35rem;
  right: -0.35rem;
  width: 1.75rem;
  min-width: 1.75rem;
  min-height: 1.75rem;
  padding: 0;
  color: var(--ink);
  background: var(--panel);
  border: 2px solid var(--line);
  font-size: 1.15rem;
  line-height: 1;
}

/* The 1.75rem button stays as drawn; the overlay grows its hit area to --touch-min. It
   sits in the corner of the 4rem preview, so it only reaches past the preview's edge. */
.remove-image::after {
  content: '';
  position: absolute;
  inset: calc((var(--touch-min) - 1.75rem) / -2);
}

.composer-strip {
  display: flex;
  align-items: center;
  gap: var(--space-r2);
  min-height: var(--touch-floor);
  margin: 0;
  color: var(--error);
  font-size: var(--text-detail-body);
  overflow-wrap: anywhere;
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  padding: 0.3rem var(--space-r2);
}

.composer-strip .app-status-retry {
  margin-left: auto;
  min-height: var(--touch-floor);
  min-width: var(--touch-floor);
  text-decoration: none;
  padding: 0 var(--space-r2);
  background: var(--panel-inset);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
}

.dictation-status:empty {
  display: none;
}

.dictation-status[data-kind="interim"] {
  color: var(--muted);
}

textarea {
  flex: 1;
  min-width: 0;
  min-height: 0;
  max-height: 15rem;
  width: 100%;
  padding: var(--composer-field-pad);
  font: inherit;
  color: inherit;
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  resize: none;
  overflow-y: hidden;
}

textarea:focus {
  outline: none;
}

select:focus-visible,
button:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

html.keyboard-focus textarea:focus-visible {
  outline: 2px solid var(--muted);
  outline-offset: 2px;
  border-color: var(--accent);
  box-shadow: none;
}

/* The global default every button in the app inherits unless it overrides away (.icon-button,
   .app-quiet-button, etc.): a solid accent rounded rectangle. Ink is --on-accent, not the page ground --
   the two used to be the same white, so this rule went black-on-blue the moment the page
   ground stopped being white in dark mode. */
button {
  min-height: var(--touch-min);
  flex: none;
  padding: 0.7rem 1.2rem;
  font: inherit;
  font-weight: var(--w-title);
  color: var(--on-accent);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  cursor: pointer;
}

/* 44px everywhere (SLICE K): the composer's `+`, its overflow sheet's two controls, and
   both mic buttons (composer and Groceries) all reuse this one base rather than a
   phone-only bump -- "one size everywhere is fine" was the person's own call, and the app
   already has one narrower breakpoint (the TV block below) that overrides up from here, so
   a second, downward one would be the odd rule out. */
.icon-button {
  display: grid;
  place-items: center;
  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);
}

.icon-button[hidden] {
  display: none;
}

.icon-button:hover,
.icon-button:focus-visible {
  color: var(--ink);
  background: var(--code);
}

.dictate[data-state="listening"] {
  color: var(--ink);
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  border-color: var(--accent);
}

/* The level ring: dictation.js drives --mic-level (0..1) from the mic analyser, so the ring
   follows the voice from the first instant of listening instead of pulsing on a timer. */
.dictate[data-state="listening"],
.dictate[data-state="starting"] {
  box-shadow: 0 0 0 calc(var(--mic-level, 0) * 0.5rem) color-mix(in srgb, var(--accent) 35%, transparent);
  transition: box-shadow 80ms linear;
}

.dictate[data-state="stopping"] {
  color: var(--muted);
  background: var(--code);
  border-color: var(--muted);
}

.dictate[data-state="starting"] {
  color: var(--muted);
  background: var(--code);
  border-color: var(--accent);
  border-style: dashed;
}

.dictate:disabled {
  color: color-mix(in srgb, var(--muted) 55%, var(--page));
  background: transparent;
  border-color: transparent;
}

@keyframes dictation-pending-pulse {
  50% { opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
  .composer-row[data-pending] #message { animation: none; }
}

/* A gold icon, not the generic button rule's filled pill: Send sits beside the mic as a
   quiet control on the composer's own pill (see .composer-row), and the reference draws
   it in the one accent colour rather than a solid accent disc. */
#send {
  display: grid;
  place-items: center;
  padding: 0;
  color: var(--accent);
  background: transparent;
  border-color: transparent;
}

#send:hover:not(:disabled),
#send:focus-visible {
  color: var(--accent-hover);
  background: var(--code);
}

/* 20px, not the old 18px: the glyph grows with the box (SLICE K's 40px -> 44px bump)
   instead of floating in extra empty square. */
.icon-button svg,
#send svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.message-clear svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

button:disabled,
textarea:disabled,
select:disabled {
  opacity: 0.6;
  cursor: default;
}

.take-photo:disabled {
  color: color-mix(in srgb, var(--muted) 55%, var(--page));
  background: transparent;
  border-color: transparent;
}

.remove-image:hover:not(:disabled) {
  color: var(--ink);
  background: var(--code);
  border-color: var(--muted);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* Every dialog shares one frame; each keeps only the classes its own content needs
   beside these (typography, its footer's extra layout). */
.app-dialog {
  width: min(40rem, 90%);
  max-height: min(82vh, 46rem);
  max-height: min(82dvh, 46rem, var(--dialog-max-height));
  padding: 0;
  color: var(--ink);
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: var(--radius-overlay);
}

/* The report question floats above the page instead of taking layout space. */
.outcome-reports-dialog[open] {
  position: fixed;
  inset: 0;
  margin: auto;
}

.app-dialog::backdrop {
  background: transparent;
}

/* The class is set by app-dialog.js while a modal dialog is open. The scrim lives in the app area (contain: layout, overflow: clip), so it never dims the dock's own track; the transparent backdrop still blocks clicks. */
body.app-scrimmed .app-area::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2147483000;
  pointer-events: none;
}

body.app-scrimmed .app-area::after,
body.app-scrimmed-open .app-area::after {
  background: var(--scrim);
}

/* A non-modal dialog cannot use ::backdrop, so the scrim is a plain layer instead of a 100vmax box-shadow. */
body.app-scrimmed-open .app-area::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.app-dialog-frame {
  display: flex;
  max-height: min(82vh, 46rem);
  max-height: min(82dvh, 46rem, var(--dialog-max-height));
  flex-direction: column;
  min-height: 0;
}

html.field-keyboard-open .app-dialog[open] {
  position: fixed;
  inset-block: auto;
  top: var(--app-viewport-top, 0px);
  margin-block: max(0.5rem, var(--safe-top)) 0;
  max-height: calc(var(--app-viewport-height, var(--vh100)) - max(0.5rem, var(--safe-top)) - 0.5rem);
}

html.field-keyboard-open .app-dialog[open] .app-dialog-frame {
  max-height: calc(var(--app-viewport-height, var(--vh100)) - max(0.5rem, var(--safe-top)) - 0.5rem);
}

.app-dialog-header {
  display: flex;
  flex: none;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-r4);
  padding: 0.8rem 0.9rem;
  border-bottom: 1px solid var(--line);
}

.app-dialog-header h2 {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: var(--text-detail-title);
  overflow-wrap: break-word;
}

.app-dialog-header button {
  min-width: var(--touch-min);
  min-height: var(--touch-min);
}

.app-dialog-x {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-min);
  min-height: var(--touch-min);
  margin-inline-start: auto;
  padding: 0;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-radius: var(--radius-control);
}

.app-dialog-x:hover,
.app-dialog-x:focus-visible {
  background: var(--panel);
}

.app-dialog-x svg {
  width: calc(var(--touch-min) / 2);
  height: calc(var(--touch-min) / 2);
  stroke-width: 2;
}

/* The body carries the detail body step for every dialog, so a dialog's own prose
   inherits it and a new dialog needs no font-size of its own. Only a heading inside one
   still declares a size, and it declares --text-detail-title. */
.app-dialog-body {
  min-height: 0;
  padding: 0.9rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  font-size: var(--text-detail-body);
  line-height: var(--leading-detail-body);
}

.app-dialog-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  flex: none;
  gap: 0.55rem;
  padding: 0.8rem 0.9rem;
  border-top: 1px solid var(--line);
}

@media (max-width: 47.99rem) {
  .app-dialog-footer > button {
    flex: 1 1 auto;
  }
}

/* Centring only where a footer's items are all one-line controls; the footers that can
   grow a wrapping note or error line also wrap. hide-preference-footer joined
   this list along with its own error paragraph -- both are
   dialogs a failure can arrive in while already open, the same shape event-preview-footer
   and recipe-footer already have one of. */
.calendar-proposal-footer,
.event-preview-footer,
.recipe-footer,
.hide-preference-footer,
.meals-proposal-footer,
.music-proposal-footer,
.choice-footer {
  align-items: center;
}

.choice-footer:not(:has(> :not([hidden], :empty))) { display: none; }

.event-preview-footer,
.recipe-footer,
.hide-preference-footer,
.music-proposal-footer,
#native-app-dialog .app-dialog-footer {
  flex-wrap: wrap;
}

/* Three actions ('Get it from Google Play', 'Remove from selector', 'Close') do not fit one
   row on a phone: each takes its own full-width row, 44px or taller. */
#native-app-dialog .app-dialog-footer > button {
  flex: 1 1 100%;
  min-height: var(--touch-min);
}

#native-app-dialog .app-dialog-footer > [hidden],
#native-app-dialog .app-dialog-footer > button[hidden] {
  display: none;
}

/* Same fix as .email-review-detail above, scoped to this dialog body only -- not to the
   shared .app-dialog-body class every other dialog also uses. */
.email-detail-body {
  overflow-x: hidden;
  touch-action: pan-y;
}

.email-detail-body h3,
.email-detail-body p,
.email-detail-body ul {
  margin-block: 0 0.65rem;
}

.email-detail-body li,
.email-detail-body p,
.email-detail-body a {
  overflow-wrap: anywhere;
}

.email-detail-body a {
  color: var(--link);
}

.email-detail-body li,
.email-detail-body p {
  line-height: 1.5;
}

.email-detail-source-excerpt {
  white-space: pre-wrap;
}

.email-detail-footnote {
  color: var(--muted);
}

.email-detail-error {
  color: var(--error);
}

.hide-preference-error {
  flex: 1 1 100%;
  overflow-wrap: anywhere;
}

/* Full width in the wrapping footer so a long reason never squeezes beside the buttons; colour and size come from .app-status / .app-status-failure; an empty line takes no flex slot. */
.app-dialog-failure {
  flex: 1 1 100%;
  overflow-wrap: anywhere;
}

.app-dialog-failure:empty {
  display: none;
}

.event-preview-body {
  display: grid;
  gap: 0.75rem;
}

.event-preview-body p {
  margin: 0;
}

.event-preview-form {
  display: grid;
  gap: var(--space-r3);
}

.event-preview-form[hidden],
.event-preview-summary[hidden] {
  display: none;
}

.event-preview-summary {
  display: grid;
  gap: var(--space-r1);
}

.event-preview-field {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-r1);
  min-width: 0;
}

.event-preview-field-label {
  color: var(--muted);
}

.event-preview-field > .app-field {
  min-width: 0;
  max-width: 100%;
  width: 100%;
  box-sizing: border-box;
  -webkit-appearance: none;
  appearance: none;
}

.event-preview-times {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}

.event-preview-times > * {
  min-width: 0;
}

.event-preview-allday {
  justify-content: flex-start;
  gap: 0.5rem;
}

/* Two time fields no longer fit side by side once the text is large enough to clip "PM". */
:root[data-text-size="large"] .event-preview-times {
  grid-template-columns: 1fr;
}

.hide-preference-body h3,
.hide-preference-body p,
.hide-preference-body ul {
  margin-block: 0 0.55rem;
}

.hide-preference-body h3 {
  font-size: var(--text-detail-title);
}

.hide-preference-option {
  display: flex;
  align-items: flex-start;
  gap: var(--space-r2);
  margin-block: 0 0.4rem;
}

.hide-preference-option input[type="radio"] {
  margin-top: 0.2rem;
  flex: none;
}

/* The caution reads at .app-notice's own ink and step -- a line the person should read
   first is not a caption, and it used to be --muted here for no reason the dialog gave. */
.hide-preference-caution {
  margin: 0;
}

.calendar-proposal-footer .calendar-add-failure {
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
  overflow-wrap: anywhere;
}

/* On its own line above Add/Cancel: the sentence plus its button need the footer's full
   width, and a modal hides the composer toast, so this is the one place a reminder-related
   failure or call-to-action can live. */
.calendar-reminder-notice {
  flex: 1 1 100%;
  margin: 0;
  color: var(--muted);
  font-size: var(--text-detail-body);
  overflow-wrap: anywhere;
}

/* Meals' own surface even though the dialog lives outside .meals-plan in the DOM, so
   its one accented line can use the pane's accent. */
.recipe-dialog {
  --pane-accent: var(--accent-meals);
}

/* The servings slider is the one accented datum in the dialog: app-key's weight, on the
   pane's own accent instead of the house blue. Its range takes the same look as the
   podcast and weather scrubbers, tinted with the pane accent. */
.recipe-servings {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin: 0 0 0.55rem;
  color: var(--pane-accent);
  font-size: var(--text-detail-meta);
}

.recipe-servings-count {
  font-variant-numeric: tabular-nums;
  min-width: 2ch;
  text-align: right;
}

/* The recipe body's two halves -- the facts and ingredients a person measures from, and
   the instructions they cook from -- arrive from meals.js as two plain stacked blocks, so
   every surface that shows a recipe (the Add Meals disclosure, the Meals review dialog)
   keeps the single column it has always had. The recipe dialog alone stands them side by
   side at the app's own 1100px breakpoint (the shared desktop block further down, beside
   Meals' own two-column rule there) and widens to hold it: one media query over the one
   markup, never a second renderer. */
.recipe-detail-main,
.recipe-detail-steps {
  min-width: 0;
}

/* Stacked below the breakpoint, the steps sit under the facts with the rhythm a section
   break gives; the desktop block stands them side by side and removes the break. */
.recipe-detail-steps {
  margin-top: 0.9rem;
}

/* The one `.agent-` rule that outlived the agent inspector, because it was never only the
   inspector's: the Household facts pane wraps its own saved lines in `.agent-household`
   too, and that pane is still on screen. Everything else this block held styled the
   dialog alone and went with it. The gold-and-blue card round moves it onto the shared
   .app-card surface (composed in the markup) rather than its own one-off inset -- its own
   rule now states only what .app-card does not: the gap above it. */
.agent-household {
  margin-top: var(--card-gap);
}

.agent-household p {
  color: var(--muted);
}
