/* talk's own scheme, and the fallback when no theme is picked. The alternatives
   live in themes.css, which loads after this file so a [data-theme] block wins
   the tie on specificity. Everything below is written in tokens only. */
/* Powerline separators, and nothing else. agnoster — the theme on the box —
   draws its prompt out of  (U+E0B0) and  (U+E0A0), which live in the
   Private Use Area, and none of ui-monospace, SF Mono or Menlo carries a glyph
   there, so every prompt on /terminal came back a row of tofu. The whole fix is
   a font, because the server side was already right: assistant.service sets
   LANG=C.UTF-8 and the bytes always arrived intact.

   Last in --font-mono rather than first, so per-glyph fallback does the work:
   Latin still renders in the system mono, xterm still measures its cell from
   the same first family it always did, and only the PUA falls through to here.
   Its 38 glyphs are squeezed to a 0.6em advance to match a mono cell (see
   tools/gen-powerline.sh); at a full 1em the browser would pay a cell and a
   half for a separator and shove the rest of the prompt line sideways. */
@font-face {
  font-family: powerline;
  src: url('/vendor/fonts/powerline.woff2') format('woff2');
  /* the range is the point: it must never win a character the system font has */
  unicode-range: U+E0A0-E0D4;
  font-display: block;
}

:root {
  --bg-0: #111113;
  --bg-1: #18181b;
  --bg-2: #1c1c1f;
  --bg-3: #26262b;
  --bg-hover: #222228;
  --bg-active: #2a2a30;
  --text-0: #e6e6e9;
  --text-1: #c9c9d0;
  --text-2: #8a8a92;
  --text-3: #6a6a72;
  --border-0: #232328;
  --border-1: #2a2a2f;
  --border-2: #3a3a42;
  --accent: #2b6cb0;
  --accent-hover: #3d7cc0;
  --accent-text: #ffffff;
  --danger: #d07070;
  --warning: #c9a44f;
  --success: #44aa99;
  --shadow: rgba(0, 0, 0, 0.5);
  /* Not --shadow. That is the colour a raised thing casts, and the light themes
     define it as a pale wash, so a modal borrowing it dimmed nothing there. A
     scrim's job is to put the page behind a dialog out of reach, which is the
     same dark in every palette. */
  --scrim: rgba(0, 0, 0, 0.45);
  --cool: #4a86b8;
  --warm: #c8894a;
  --info: #4a9ab0;
  --pill-bg-s: 34%;
  --pill-bg-l: 16%;
  --pill-fg-s: 60%;
  --pill-fg-l: 74%;
  --pill-bd-s: 30%;
  --pill-bd-l: 30%;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, powerline, monospace;
  --mono-bg-s: 32%;
  --mono-bg-l: 20%;
  --soft-mix: 42%;
  --tint-mix: 14%;
  --line-mix: 55%;
  --scheme: dark;
}

/* Derived once, from whichever palette is in force: a filled button (soft), a
   panel wash (tint), and the hairline that edges it (line). The mix ratios are
   per-theme because a light scheme reaches its colour far sooner than a dark
   one does. */
:root {
  --accent-soft: color-mix(in srgb, var(--accent) var(--soft-mix), var(--bg-1));
  --accent-tint: color-mix(in srgb, var(--accent) var(--tint-mix), var(--bg-1));
  --accent-line: color-mix(in srgb, var(--accent) var(--line-mix), var(--bg-1));
  --danger-btn: color-mix(in srgb, var(--danger) var(--soft-mix), var(--bg-1));
  --danger-tint: color-mix(in srgb, var(--danger) var(--tint-mix), var(--bg-1));
  --danger-line: color-mix(in srgb, var(--danger) var(--line-mix), var(--bg-1));
  --success-btn: color-mix(in srgb, var(--success) var(--soft-mix), var(--bg-1));
  --success-tint: color-mix(in srgb, var(--success) var(--tint-mix), var(--bg-1));
  --success-line: color-mix(in srgb, var(--success) var(--line-mix), var(--bg-1));
  --warning-tint: color-mix(in srgb, var(--warning) var(--tint-mix), var(--bg-1));
  --warning-line: color-mix(in srgb, var(--warning) var(--line-mix), var(--bg-1));
  --info-btn: color-mix(in srgb, var(--info) var(--soft-mix), var(--bg-1));
  --info-tint: color-mix(in srgb, var(--info) var(--tint-mix), var(--bg-1));
  --info-line: color-mix(in srgb, var(--info) var(--line-mix), var(--bg-1));
}

* { box-sizing: border-box; margin: 0; }

/* author display rules below would otherwise beat the hidden attribute */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  background: var(--bg-0);
  color: var(--text-0);
  font: 16px/1.5 -apple-system, system-ui, sans-serif;
}

#gate {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
  justify-content: center;
  padding: 32px;
  max-width: 560px;
  margin: 0 auto;
}

#gate input, #gate button {
  font-size: 18px;
  padding: 12px;
  border-radius: 10px;
  border: 1px solid var(--border-1);
  background: var(--bg-2);
  color: inherit;
}

#gate-msg { color: var(--danger); font-size: 13px; min-height: 18px; }

/* /login reuses #gate's centred column; its two steps are columns in their own
   right so the same 12px gap applies inside whichever one is showing. */
#step-email, #step-code { display: flex; flex-direction: column; gap: 12px; }
#step-email[hidden], #step-code[hidden] { display: none; }
#lede, #sent { margin: 0; font-size: 15px; color: var(--text-1); }
#gate .quiet {
  font-size: 14px;
  padding: 8px;
  border: 0;
  background: none;
  color: var(--text-1);
  text-decoration: underline;
}

#app {
  height: 100%;
  display: flex;
  flex-direction: column;
  /* THE ONE PLACE THE SAFE AREA IS PAID FOR, on anything that sits in this
     element's flow. Four children used to add it again on top, which is
     invisible in a browser tab (the insets are zero there, the chrome having
     already taken that space) and doubles in a standalone PWA, where the status
     bar and the home indicator become the page's problem. Measured on a
     simulated notch: 94px consumed above the terminal's bar where 47 is right,
     and 68 below its key row where 34 is.

     Note the form the bottom ones take: max(), not calc(+). The inset is a
     keep-out distance rather than an extra margin, so a bar that already sits
     10px off the edge needs 34 in total and not 44. Adding them stacks the
     design's own spacing on top of the hardware's and is how a fixed gap
     becomes padding AND a gap. The modals in this file have always used max();
     the bars now match them.
     Absolutely and fixed positioned children are NOT in this flow — an
     absolute child's top: 0 resolves against the padding box, which includes
     this padding rather than sitting below it — so the chat topbar, the toasts
     and every modal still buy their own, and must.

     THE BOTTOM IS THE EXCEPTION, and for a reason the top does not share. Paid
     here, the home indicator's strip is this element's padding, which is
     transparent, so the terminal's key bar ended above 34px of page background
     in a different colour and read as a gap under the bar. A bottom bar has to
     paint THROUGH the safe area with its content held above it, so the inset
     belongs to whichever bar is last: #keys on the terminal, the composer or
     the talk row on the chat. The top has no such bar — the chat's is absolute
     and buys its own, and the terminal's sits below a strip that is meant to
     read as page — so it stays here. */
  padding: env(safe-area-inset-top) 0 0;
  /* Full-bleed, the way /terminal always was. The 760 column was there to keep
     a line of prose readable, which is a job for the bubble rather than for the
     whole app: capping the shell also capped the composer, the cards and the
     key rows, and left a desktop window mostly empty either side of them. */
  max-width: none;
  position: relative;
}

#transcript {
  flex: 1;
  overflow-y: auto;
  /* the topbar is absolute over a scrim, so the transcript buys its own
     clearance; --strip is 0 until the tab strip is showing */
  padding: calc(64px + var(--strip, 0px)) 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overscroll-behavior: contain;
}


/* The strip of open things: conversations here, tmux sessions on /terminal.
   One tap to switch instead of three through the drawer, and a count you can
   read without opening anything. Behaviour is tabs.js; this is where it sits.

   Horizontal and scrolling rather than shrink-to-fit: a name that has been cut
   to four characters tells you nothing, and forty conversations would cut them
   all. Off the end is honest, and the active one is scrolled back into view.

   Absolute here, because the chat page's topbar is absolute over a scrim and a
   strip in flow would sit underneath it. The terminal's topbar is in flow and
   puts this back to static. */
/* A second row of the bar, not a band under it: #topbar wraps, so the strip is
   inside the scrim that already exists and there is no offset to keep in step
   with it. It spans the full width because a wrapped flex child with a 100%
   basis has nowhere else to go. */
/* The strip is a fixed frame with a scroller inside it, not a scroller itself.
   A phone fits about four tabs, and + is the only one on either page, so it and
   the index have to stay pinned to the right while the tabs run off the edge
   underneath them. */
#tabs {
  position: relative;
  flex: 0 0 100%;
  /* A flex item's min-width is auto, which resolves to min-content, so a basis
     of 100% is a floor rather than a width: the row grew to fit every tab and
     shoved + and the index past the right edge of the screen. The same trap
     .tab-name fell into one level down. */
  min-width: 0;
  max-width: 100%;
  display: flex;
  align-items: center;
  gap: 4px;
  /* No edge bleed. It was here so the row could scroll to the very edge, which
     stopped being worth anything once + and the index took that edge; and a
     100%-basis child with negative margins is wider than the bar it sits in, so
     it pushed those two off-screen and gave the whole page a sideways scroll. */
}
#tabs[hidden] { display: none; }
.tab-scroll {
  flex: 1;
  min-width: 0;
  display: flex;
  gap: 4px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  /* horizontal panning stays the browser's; tabs.js refuses it mid-gesture on
     the frames where a drag has started instead */
  touch-action: pan-x;
}
.tab-scroll::-webkit-scrollbar { display: none; }

/* The index of what is open, including what has scrolled off. Anchored to the
   strip rather than the button so it can be wider than a 26px control. */
.tab-pop {
  position: absolute;
  top: calc(100% + 5px);
  right: 10px;
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 170px;
  max-width: min(280px, calc(100vw - 24px));
  max-height: 50vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 5px;
  border: 1px solid var(--border-1);
  border-radius: 13px;
  background: var(--bg-2);
  box-shadow: 0 12px 30px var(--shadow);
}
.tab-pop[hidden] { display: none; }
.tab-pop-row {
  border: none;
  border-radius: 9px;
  background: none;
  color: var(--text-1);
  font-size: 13.5px;
  text-align: left;
  padding: 8px 10px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tab-pop-row:active { background: var(--bg-3); }
.tab-pop-row.on { color: var(--text-0); background: var(--accent-tint); }

/* Sized off the drawer's rows rather than invented: .conv-name is 14px, so a
   tab is one step down at 13, and the selected state is the same accent tint
   .conv-row.on carries. The point is that the strip reads as the drawer lying
   on its side. */
.tab, .tab-new, .tab-menu {
  flex: none;
  display: flex;
  align-items: center;
  gap: 4px;
  max-width: 128px;
  padding: 3px 9px;
  border: 1px solid var(--border-1);
  border-radius: 7px;
  background: var(--bg-2);
  color: var(--text-2);
  font-size: 12px;
  line-height: 1.5;
  white-space: nowrap;
  /* a tab is held to pick it up, and a held word offers to select itself */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.tab.on {
  background: var(--accent-tint);
  border-color: var(--accent-line);
  color: var(--text-0);
}
.tab:active, .tab-new:active, .tab-menu:active { background: var(--bg-3); }
/* Off the strip's own flow while it follows a finger, so the tabs it is being
   dragged past close the gap behind it rather than shuffling around it. */
.tab.dragging {
  z-index: 2;
  border-color: var(--accent);
  box-shadow: 0 6px 18px var(--shadow);
  opacity: 0.94;
}
/* min-width, or nothing truncates: a flex item defaults to min-width auto and
   refuses to shrink below its own text, so the name ran straight out of the tab
   and the ellipsis never had a chance to appear. */
.tab-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tab-close {
  flex: none;
  margin-right: -3px;
  padding: 0 2px;
  border: 0;
  background: none;
  color: var(--text-3);
  font-size: 11px;
  line-height: 1;
}
.tab-close:active { color: var(--danger); }
.tab-new, .tab-menu { flex: none; padding: 3px 8px; color: var(--text-3); }
.tab-menu { font-size: 10px; }
.tab-menu[hidden] { display: none; }
.tab-menu.on { color: var(--text-0); border-color: var(--accent-line); background: var(--accent-tint); }

/* Bubbles sit on the page rather than in it: the same lift as every other
   pressable thing, and a corner radius that reads as one family. The tail
   corner stays tight so a run of bubbles still has a direction. */
/* 85% of a phone, and a readable measure of anything wider: a bubble that spans
   a desktop window is a line the eye loses its place in. ch is the right unit
   for it, being the text's own width rather than a guess in pixels. */
.line { max-width: min(85%, 72ch); padding: 10px 14px; border-radius: 18px; white-space: pre-wrap; word-break: break-word; }
.line.you { align-self: flex-end; border-bottom-right-radius: 7px; background: var(--accent-soft); }
.line.talk { align-self: flex-start; border-bottom-left-radius: 7px; background: var(--bg-3); }
.line.sys { align-self: center; max-width: 100%; font-size: 11px; color: var(--text-3); background: none; padding: 0 4px; box-shadow: none; }
.line.err { align-self: stretch; max-width: 100%; font-size: 12px; color: var(--danger); background: var(--danger-tint); border-radius: 12px; padding: 8px 12px; }

/* tool output is machinery, not speech: it sinks into the page instead of
   sitting on it, which is the one place an inset earns its keep */
.line.tool {
  align-self: stretch;
  max-width: 100%;
  font: 12px/1.6 var(--font-mono);
  color: var(--text-2);
  background: var(--bg-1);
  border-left: 2px solid var(--info-line);
  border-radius: 10px;
  padding: 7px 11px;
}
.line.tool.result { border-left-color: var(--border-2); color: var(--text-3); }

details.line.tool-group {
  align-self: stretch;
  max-width: 100%;
  padding: 0;
  background: none;
}
/* A run reads the same open or shut: one chip sitting on a hairline rule. Open
   just drops the steps underneath it, so nothing jumps or changes shape. */
details.line.tool-group > summary {
  cursor: pointer;
  list-style: none;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font: 12px/1.6 var(--font-mono);
  color: var(--text-2);
  padding: 2px 0;
}
details.line.tool-group > summary::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  border-top: 1px solid var(--border-0);
}
details.line.tool-group > summary::-webkit-details-marker { display: none; }
details.line.tool-group > summary .group-chip {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  max-width: 100%;
  background: var(--bg-0);
  border: 1px solid var(--border-1);
  border-radius: 999px;
  padding: 3px 10px;
}
details.line.tool-group > summary .group-pills { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; min-width: 0; }
details.line.tool-group > summary .group-label { color: var(--text-2); }
/* a triangle centres on the text baseline where a chevron glyph sits high */
details.line.tool-group > summary .group-more {
  flex-shrink: 0;
  color: var(--text-3);
  font-size: 9px;
  line-height: 1;
  display: flex;
  align-items: center;
}
details.line.tool-group > summary .group-more::after { content: '▾'; }
details.line.tool-group[open] > summary .group-more::after { content: '▴'; }
details.line.tool-group[open] > summary { margin-bottom: 6px; }
details.line.tool-group > .line { margin-top: 4px; }

.tool-pill {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font: 11px/1.5 var(--font-mono);
  padding: 1px 7px;
  border: 1px solid hsl(var(--h) var(--pill-bd-s) var(--pill-bd-l));
  border-radius: 999px;
  white-space: nowrap;
  color: hsl(var(--h) var(--pill-fg-s) var(--pill-fg-l));
  background: hsl(var(--h) var(--pill-bg-s) var(--pill-bg-l));
}
/* inside the chip the names are the only colour they need to be; the pills on
   the step lines below keep their full treatment */
.group-chip .tool-pill {
  padding: 0;
  border-color: transparent;
  background: none;
}
.group-chip .tool-pill + .tool-pill::before { content: '· '; color: var(--text-3); }
.tool-times { font-size: 10px; opacity: 0.75; }
.tool-detail { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; min-width: 0; }

details.line.tool summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
details.line.tool summary::-webkit-details-marker { display: none; }
details.line.tool[open] summary .tool-detail { white-space: normal; opacity: 0.7; }
details.line.tool pre { white-space: pre-wrap; word-break: break-word; margin: 6px 0 2px; color: var(--text-2); }

#jump {
  position: absolute;
  bottom: 168px;
  right: 20px;
}
body.text-mode #jump { bottom: 104px; }
#jump {
  padding: 8px 15px;
  border: none;
  border-radius: 999px;
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  color: inherit;
  font-size: 13px;
}

#approval, #rewind, .reminder-card {
  margin: 0 16px 8px;
  padding: 14px;
  border-radius: 16px;
}
#approval { border: 1px solid var(--warning-line); background: var(--warning-tint); }
#rewind { border: 1px solid var(--accent-line); background: var(--accent-tint); }
#rewind pre { white-space: pre-wrap; word-break: break-word; font-size: 13px; margin-bottom: 10px; color: var(--text-2); }
#rewind .row button { background: var(--accent-soft); }
#rewind .row button.danger { background: var(--bg-3); }
.line.you { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.reminder-card { border: 1px solid var(--info-line); background: var(--info-tint); }
#approval pre, .reminder-card pre { white-space: pre-wrap; word-break: break-all; font-size: 13px; margin-bottom: 10px; }

.row { display: flex; gap: 10px; }
.row button { flex: 1; font-size: 15px; padding: 11px; border-radius: 12px; border: none; background: var(--success-btn); color: inherit; }
.row button:active { filter: brightness(0.92); }
.reminder-card .row button { background: var(--info-btn); }
.row button.danger { background: var(--danger-btn); }
.reminder-card .row button.danger { background: var(--bg-3); }


#meter {
  height: 4px;
  margin: 0 16px;
  border-radius: 3px;
  background: var(--bg-2);
  overflow: hidden;
}
#meter-bar {
  height: 100%;
  background: var(--success);
  transform: scaleX(0);
  transform-origin: left;
}

#stop {
  margin: 8px 16px 0;
  padding: 12px;
  font-size: 15px;
  border: none;
  border-radius: 14px;
  background: var(--danger-btn);
  color: inherit;
}
#stop:active { filter: brightness(0.92); }

#talkrow {
  display: flex;
  gap: 8px;
  /* the last thing on the page, so it carries the home indicator */
  margin: 8px 16px max(12px, env(safe-area-inset-bottom));
}

/* The one button that matters, so it gets the full lift; holding it presses it
   into the page, which is the whole point of a push-to-talk key. */
#ptt {
  flex: 1;
  padding: 26px;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: 0.01em;
  border: none;
  border-radius: 22px;
  background: var(--bg-3);
  color: inherit;
  touch-action: none;
  transition: background 0.15s;
  -webkit-user-select: none;
  user-select: none;
}

#ptt:disabled { opacity: 0.5; }
#ptt.recording { background: var(--danger-btn); }
#ptt.responding { background: var(--accent-soft); }

#to-text {
  width: 58px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: 22px;
  background: var(--bg-3);
  color: var(--text-2);
}
#to-text:active { background: var(--bg-active); }

#composer {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* likewise: whichever of these two is showing is the last thing */
  margin: 8px 12px max(10px, env(safe-area-inset-bottom));
}
#composer-box {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  padding: 6px 6px 6px 16px;
  border-radius: 24px;
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  transition: border-color 0.15s;
}
#composer-box:focus-within { border-color: var(--accent-line); }
#composer textarea {
  flex: 1;
  min-width: 0;
  resize: none;
  border: none;
  outline: none;
  background: none;
  color: inherit;
  font-family: inherit;
  font-size: 16px;
  line-height: 1.45;
  padding: 8px 0;
  max-height: 40vh;
}
#composer textarea::placeholder { color: var(--text-3); }
#composer-actions { display: flex; align-items: center; gap: 4px; flex: none; }
#composer-actions button {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--text-2);
}
#composer-actions button:active { background: var(--bg-3); }
#send { background: var(--accent); color: var(--accent-text); }
#send:active { background: var(--accent-hover); }
#send:disabled { background: var(--bg-3); color: var(--text-3); }
/* the soft buttons are a wash over the surface, so page ink still reads on them */
#send.stop { background: var(--danger-btn); color: var(--text-0); }
#send.stop svg { display: none; }
#send.stop::after { content: '■'; font-size: 11px; }

#attach-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  /* anchored under the + button, which sits at the right of the box */
  right: 76px;
  z-index: 30;
  display: flex;
  flex-direction: column;
  min-width: 150px;
  padding: 6px;
  border: 1px solid var(--border-1);
  border-radius: 14px;
  background: var(--bg-2);
  box-shadow: 0 12px 30px var(--shadow);
}
#attach-menu button {
  border: none;
  background: none;
  color: var(--text-1);
  text-align: left;
  font-size: 14px;
  padding: 9px 10px;
  border-radius: 9px;
}
#attach-menu button:active { background: var(--bg-3); }

#attachments { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 4px; }
.attach-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 220px;
  padding: 5px 8px 5px 5px;
  border: 1px solid var(--border-1);
  border-radius: 12px;
  background: var(--bg-2);
}
.attach-chip.busy { opacity: 0.6; }
.attach-chip.failed { border-color: var(--danger-line); }
.attach-thumb { width: 34px; height: 34px; border-radius: 8px; object-fit: cover; flex: none; }
.attach-icon { width: 34px; height: 34px; display: grid; place-items: center; border-radius: 8px; background: var(--border-0); flex: none; }
.attach-meta { min-width: 0; }
.attach-name { font-size: 12.5px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.attach-size { font-size: 11px; color: var(--text-2); }
.attach-x { border: none; background: none; color: var(--text-2); font-size: 16px; line-height: 1; padding: 2px 4px; flex: none; }

.file-grid { display: grid; grid-template-columns: 1fr; gap: 4px; padding: 4px; }
.file-grid.many { grid-template-columns: 1fr 1fr; }
.file-shot { width: 100%; max-height: 260px; object-fit: cover; border-radius: 10px; display: block; cursor: pointer; }
.file-row { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-top: 1px solid var(--border-0); }
.file-row:first-child { border-top: none; }

body.dropping #composer-box { border-color: var(--accent); background: var(--accent-tint); }

/* The bar carries the three things that are true of the app at all times: which
   thread you are in, what it is doing, and the two ways out of here. The scrim
   is what lets the transcript run full-bleed underneath it. */
#topbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  /* Enough air that the tab row reads as its own line, little enough that it
     still belongs to the bar rather than floating under it. */
  row-gap: 7px;
  z-index: 3;
  padding: calc(8px + env(safe-area-inset-top)) 10px 26px;
  pointer-events: none;
  /* The stops are tied to the buttons, not to percentages of the bar: solid
     until a few pixels past the button row, then a soft tail. A percentage
     fade started mid-button and left lines showing through behind them.
     --strip carries the tab row's height so the solid part grows with it;
     without that the fade began behind the tabs and the transcript showed
     through them. */
  background: linear-gradient(
    var(--bg-0) calc(46px + var(--strip, 0px) + env(safe-area-inset-top)),
    color-mix(in srgb, var(--bg-0) 55%, transparent) calc(58px + var(--strip, 0px) + env(safe-area-inset-top)),
    transparent
  );
}
#topbar > * { pointer-events: auto; }
/* Unfilled. A solid rounded square here sat directly above a row of solid
   rounded tabs and read as one of them that had come loose; the drawer is a way
   out of the page rather than a thing in it, so it is drawn as chrome. */
#menu {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  margin-left: -4px;
  padding: 0;
  border: none;
  border-radius: 10px;
  background: none;
  color: var(--text-3);
}
#menu:active { background: var(--bg-2); color: var(--text-1); }
/* the live state, in the one place it belongs: next to the thread it describes */
#state-chip {
  flex: none;
  /* the title used to hold this over here with flex: 1; it holds itself now */
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  color: var(--text-3);
  padding-right: 2px;
}
#dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--text-3); transition: background 0.3s; }
#dot.on { background: var(--success); }
#dot.wait { background: var(--warning); }
#dot.off { background: var(--danger); }

/* The menu is a drawer, not a popover: the conversation list has no natural
   length, so it needs a full-height panel that can scroll. */
#menu-modal {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  background: var(--scrim);
  /* the panel is opaque, so the blur is only doing the two millimetres either
     side of its edge; enough to say the page behind is out of reach */
  backdrop-filter: blur(2px);
}
/* A panel with an edge rather than a wall with a line down it: the border ran
   the full height and read as the seam of a second window. It is one surface
   lifted off the page, so it casts instead. */
#menu-panel {
  display: flex;
  flex-direction: column;
  width: min(86vw, 340px);
  height: 100%;
  /* full height, so both edges are its own; max() for the same reason the
     bars use it, since the inset is a keep-out distance and not an extra */
  padding: max(6px, env(safe-area-inset-top)) 0 max(10px, env(safe-area-inset-bottom));
  background: var(--bg-1);
  border-radius: 0 18px 18px 0;
  box-shadow: 0 0 44px var(--shadow);
}
/* The uppercase micro-label belonged to a drawer that was mostly a switcher.
   The strip does the switching now and this is where you come to manage the
   list, so it gets the same title the preferences panel has rather than a
   caption a third of its size. */
#menu-panel header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 10px 12px 16px;
}
#menu-panel h2 { font-size: 15px; font-weight: 600; color: var(--text-0); }
#menu-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 9px;
  background: none;
  color: var(--text-3);
  font-size: 14px;
}
#menu-close:active { background: var(--bg-2); color: var(--text-1); }
/* The drawer is the list of everything now that the strip holds what is open,
   so it needs finding rather than only scrolling. Full width of the panel's
   gutter, and quiet until focused: it is a tool, not the first thing to look
   at. */
#convs-find {
  flex: none;
  margin: 0 10px 8px;
  padding: 9px 11px;
  border: 1px solid var(--border-1);
  border-radius: 11px;
  background: var(--bg-2);
  color: var(--text-0);
  font-size: 13.5px;
  font-family: inherit;
}
#convs-find::placeholder { color: var(--text-3); }
#convs-find:focus { outline: none; border-color: var(--accent-line); background: var(--bg-0); }
/* iOS draws its own clear affordance and a second one is clutter */
#convs-find::-webkit-search-cancel-button { filter: grayscale(1); opacity: 0.5; }

/* The tail of a long list, on demand. It says how many rather than "more",
   because the number is what decides whether you tap it or type instead. */
#convs-more {
  flex: none;
  margin: 6px 10px 0;
  padding: 9px 12px;
  border: 1px solid var(--border-0);
  border-radius: 11px;
  background: none;
  color: var(--text-3);
  font-size: 13px;
}
#convs-more:active { background: var(--bg-2); color: var(--text-1); }
#convs-more[hidden] { display: none; }

/* A search flattens the tree: a branch whose parent did not match would arrive
   indented under nothing, its arrow pointing at a row that is not there. */
#convs-list.flat .conv-row.branched { margin-left: 0; }
#convs-list.flat .conv-row.branched::before { content: none; }

/* the list is the part that grows, so it is the part that scrolls */
#convs-list { flex: 0 1 auto; overflow-y: auto; overscroll-behavior: contain; display: flex; flex-direction: column; gap: 2px; padding: 0 10px; }
.menu-foot { flex: none; margin-top: auto; padding: 8px 10px 0; border-top: 1px solid var(--border-0); }
.menu-foot button {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 11px 12px;
  border: none;
  border-radius: 12px;
  background: none;
  color: var(--text-2);
  font-size: 14px;
  text-align: left;
}
.menu-foot button:active { background: var(--bg-2); }

/* A drawer that is simply there when you look is a drawer you are not sure you
   opened. Cheap, and it also covers the frame where the panel paints before the
   scrim does. */
@keyframes drawer-in { from { transform: translateX(-16px); opacity: 0; } }
@keyframes scrim-in { from { opacity: 0; } }
#menu-modal:not([hidden]) { animation: scrim-in 0.16s ease-out; }
#menu-modal:not([hidden]) #menu-panel { animation: drawer-in 0.19s cubic-bezier(0.2, 0.8, 0.2, 1); }
@media (prefers-reduced-motion: reduce) {
  #menu-modal:not([hidden]), #menu-modal:not([hidden]) #menu-panel { animation: none; }
}


#prefs-modal {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: max(16px, env(safe-area-inset-top)) 16px max(16px, env(safe-area-inset-bottom));
  background: var(--scrim);
  backdrop-filter: blur(3px);
}
#prefs-panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 520px;
  max-height: 100%;
  border-radius: 22px;
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  overflow: hidden;
}
#prefs-panel header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--bg-3);
}
#prefs-panel h2 { font-size: 15px; font-weight: 600; }
#prefs-close { border: none; background: none; color: var(--text-2); font-size: 15px; padding: 4px 6px; }
#prefs-body { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 14px 16px 18px; }
.prefs-hint { font-size: 12.5px; line-height: 1.45; color: var(--text-2); margin-bottom: 12px; }

.convs-hint { flex: none; font-size: 11px; color: var(--text-3); margin: 10px 16px 0; }
.conv-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 6px 5px 12px;
  border-radius: 12px;
  background: none;
  border: 1px solid transparent;
}
.conv-row:active { background: var(--bg-2); }
.conv-row.on { background: var(--accent-tint); border-color: var(--accent-line); }
/* a branch sits under whatever it came from, so the fork reads as a fork */
.conv-row.branched { margin-left: 16px; }
.conv-row.branched::before { content: '↳'; color: var(--text-3); font-size: 13px; margin-left: -14px; }
.conv-open {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  color: inherit;
  text-align: left;
  padding: 6px 0;
}
.conv-name { font-size: 14.5px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.conv-meta { font-size: 11.5px; color: var(--text-3); margin-top: 1px; }
.conv-when { flex: none; font-size: 11.5px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.conv-del { flex: none; border: none; border-radius: 8px; background: none; color: var(--text-3); font-size: 12px; padding: 9px 7px; }
.conv-del:active { color: var(--danger); }

.rewind-note { font-size: 11.5px; line-height: 1.4; color: var(--text-3); margin: 8px 0 8px; }
#rewind #rewind-cancel { width: 100%; font-size: 14px; padding: 9px; border: none; border-radius: 10px; background: var(--bg-3); color: inherit; }

.prefs-section { margin-bottom: 22px; }
.prefs-section:last-child { margin-bottom: 4px; }
.field { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 14px; color: var(--text-1); padding: 7px 2px; }
.field + .field { border-top: 1px solid var(--border-0); }
.field-unit { display: flex; align-items: baseline; gap: 6px; font-size: 12.5px; color: var(--text-3); }
.field select, .field input {
  background: var(--bg-2);
  color: var(--text-0);
  border: 1px solid var(--border-1);
  border-radius: 10px;
  padding: 7px 9px;
  font-size: 14px;
  font-family: inherit;
}
.field input { width: 66px; text-align: right; }
#usage { font-size: 12.5px; color: var(--text-3); padding: 2px 2px 12px; }
#signout {
  width: 100%;
  padding: 11px;
  border: none;
  border-radius: 12px;
  background: var(--danger-tint);
  border: 1px solid var(--danger-line);
  color: var(--danger);
  font-size: 14px;
}
.prefs-section h3 { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-3); margin-bottom: 9px; }

#prefs-themes { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 6px; }
.theme-card {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 11px;
  border: none;
  border-radius: 13px;
  background: var(--bg-2);
  border: 1px solid transparent;
  color: var(--text-2);
  font-size: 13px;
  text-align: left;
  min-width: 0;
}
.theme-card[aria-pressed='true'] { color: var(--text-0); background: var(--accent-tint); border-color: var(--accent-line); }
/* the swatch carries its own data-theme, so the dots below read that palette */
.theme-swatch { display: flex; flex: none; border-radius: 999px; overflow: hidden; border: 1px solid var(--border-1); }
.theme-swatch i { width: 13px; height: 20px; }
.theme-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

#prefs-rows { display: flex; flex-direction: column; gap: 8px; }
.pref-row { display: flex; align-items: center; gap: 8px; }
.pref-row input {
  min-width: 0;
  background: var(--bg-2);
  color: inherit;
  border: 1px solid var(--border-1);
  border-radius: 11px;
  padding: 10px 12px;
  font-size: 14px;
  font-family: inherit;
}
.pref-key { flex: 0 1 42%; }
.pref-value { flex: 1 1 58%; }
.pref-del { flex: none; border: none; background: none; color: var(--text-3); font-size: 13px; padding: 6px; }
.pref-del:active { color: var(--danger); }

#prefs-add {
  margin-top: 10px;
  padding: 10px 12px;
  border: none;
  border-radius: 13px;
  background: none;
  border: 1px dashed var(--border-2);
  color: var(--text-2);
  font-size: 13.5px;
  width: 100%;
}
#prefs-add:active { background: var(--bg-2); }
#prefs-presets { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.pref-chip {
  border: none;
  border-radius: 999px;
  background: var(--bg-2);
  border: 1px solid var(--border-1);
  color: var(--text-2);
  font-size: 12.5px;
  padding: 6px 12px;
}
.pref-chip:active { background: var(--bg-3); }

.prefs-notes { display: flex; flex-direction: column; gap: 6px; margin-top: 18px; font-size: 14px; color: var(--text-2); }
.prefs-notes textarea {
  background: var(--bg-2);
  color: var(--text-0);
  border: 1px solid var(--border-1);
  border-radius: 8px;
  padding: 9px;
  font-size: 13.5px;
  width: 100%;
  resize: vertical;
  font-family: inherit;
  line-height: 1.45;
}

#prefs-panel footer {
  display: flex;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid var(--bg-3);
  background: var(--bg-1);
}
#prefs-panel footer button { flex: 1; padding: 12px; border: none; border-radius: 13px; background: var(--accent-soft); color: var(--text-0); font-size: 14.5px; }
#prefs-panel footer button:active { filter: brightness(0.92); }
#prefs-panel footer button.ghost { background: var(--bg-1); }

.line.rich-card {
  align-self: stretch;
  /* match the reply bubbles' width cap so cards line up with the thread */
  max-width: 85%;
  padding: 0;
  overflow: hidden;
  /* overflow:hidden zeroes the flex min-size, letting the column squash it */
  flex-shrink: 0;
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  border-radius: 18px;
}
.card-title { font-weight: 600; font-size: 14px; padding: 10px 12px 4px; }
.tappable { cursor: pointer; }
.tappable:active { background: var(--bg-hover); }

/* weather: one hero, then a row per day on a shared temperature scale */
.wx-head { padding: 11px 14px 10px; border-bottom: 1px solid var(--border-0); }
.wx-place { font-size: 11.5px; font-weight: 500; letter-spacing: 0.03em; text-transform: uppercase; color: var(--text-2); }
.wx-hero { display: flex; align-items: baseline; gap: 9px; margin-top: 4px; }
.wx-icon { font-size: 26px; line-height: 1; }
.wx-temp { font-size: 34px; font-weight: 600; line-height: 1.05; letter-spacing: -0.02em; }
.wx-feels { font-size: 12.5px; color: var(--text-2); }
.wx-today { margin-left: auto; font-size: 12.5px; color: var(--text-2); font-variant-numeric: tabular-nums; }

.wx-row { display: flex; align-items: center; gap: 9px; padding: 5px 14px; }
.wx-row:last-child { padding-bottom: 11px; }
.wx-day { flex: 0 0 42px; font-size: 12.5px; color: var(--text-1); }
.wx-glyph { flex: 0 0 20px; font-size: 15px; text-align: center; }
.wx-lo, .wx-hi { flex: 0 0 30px; font-size: 12.5px; font-variant-numeric: tabular-nums; text-align: right; }
.wx-lo { color: var(--text-2); }
.wx-track { flex: 1; position: relative; height: 5px; border-radius: 3px; background: var(--border-0); min-width: 40px; }
.wx-bar { position: absolute; top: 0; height: 100%; border-radius: 3px; background: linear-gradient(90deg, var(--cool), var(--warm)); }
.wx-rain { flex: 0 0 34px; font-size: 11px; color: var(--cool); text-align: right; font-variant-numeric: tabular-nums; }

/* the list-card row: lead slot, body, trailing reading, chevron if it goes
   somewhere. Three type sizes only — name, line, sub — so trip, links and
   emails scan as one family instead of three. */
.row { display: flex; align-items: flex-start; gap: 10px; padding: 9px 12px; border-top: 1px solid var(--border-0); }
.row:first-child, .trip-day + .row { border-top: none; }
.row-mono { flex: 0 0 26px; height: 26px; border-radius: 8px; display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; background: hsl(var(--h) var(--mono-bg-s) var(--mono-bg-l)); color: hsl(var(--h) var(--pill-fg-s) var(--pill-fg-l)); }
.row-glyph { flex: 0 0 26px; font-size: 15px; text-align: center; line-height: 1.6; }
/* the start time reads as a column, so it is tabular and right-aligned */
.cal-time { flex: 0 0 40px; font-size: 12.5px; color: var(--text-1); font-variant-numeric: tabular-nums; text-align: right; padding-top: 1px; }
.row-body { flex: 1; min-width: 0; }
.row-trail { flex: 0 0 auto; font-size: 11px; color: var(--text-3); text-align: right; font-variant-numeric: tabular-nums; padding-top: 2px; }
.row-chev { flex: 0 0 auto; color: var(--border-2); font-size: 17px; line-height: 1.1; }
.row.tappable:active { background: var(--bg-2); }
.row-name { font-size: 13px; font-weight: 600; }
/* unread mail: a dot in the trailing column, where the eye already is */
.row.unread .row-trail::after { content: ''; display: block; width: 7px; height: 7px; border-radius: 50%; background: var(--cool); margin: 3px 0 0 auto; }
.row.unread .row-line { color: var(--text-0); }
.row-line { font-size: 13px; color: var(--text-1); margin-top: 1px; }
.row-sub { font-size: 12px; color: var(--text-2); margin-top: 2px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

.trip-when { font-size: 11.5px; color: var(--text-2); font-weight: 400; margin-top: 1px; }
.trip-day { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--cool); background: var(--bg-2); padding: 5px 12px; border-top: 1px solid var(--border-0); }
.trip-note { font-size: 12px; color: var(--text-2); margin-top: 3px; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.trip-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.trip-tag { font-size: 11px; padding: 2px 7px; border-radius: 7px; border: 1px solid var(--border-1); color: var(--text-2); }
.trip-tag.confirmed { color: var(--success); border-color: var(--success-line); background: var(--success-tint); }
.trip-tag.pencilled { color: var(--warning); border-color: var(--warning-line); background: var(--warning-tint); }
.trip-more, .card-more { font-size: 11.5px; color: var(--text-3); padding: 8px 12px; border-top: 1px solid var(--border-0); }
/* sits under the card title, before the content it qualifies */
.card-sub { font-size: 11.5px; color: var(--text-2); padding: 0 12px 6px; margin-top: -2px; }

/* gallery: several results in one card, one at a time */
.gallery { overflow: hidden; touch-action: pan-y pinch-zoom; }
.gallery__container { display: flex; align-items: stretch; }
.gallery__slide { flex: 0 0 100%; min-width: 0; display: flex; flex-direction: column; }
.gallery-nav { display: flex; align-items: center; gap: 8px; padding: 6px 10px 8px; border-top: 1px solid var(--border-0); }
.gallery-arrow { width: 28px; height: 28px; flex: none; border-radius: 8px; border: 1px solid var(--border-1); background: var(--bg-2); color: var(--text-1); font-size: 15px; line-height: 1; cursor: pointer; }
.gallery-arrow:disabled { opacity: 0.35; cursor: default; }
.gallery-dots { display: flex; gap: 6px; flex: 1; justify-content: center; }
.gallery-dot { width: 7px; height: 7px; padding: 0; border-radius: 50%; border: none; background: var(--border-2); cursor: pointer; }
.gallery-dot.on { background: var(--cool); }
.gallery-count { font-size: 11.5px; color: var(--text-2); font-variant-numeric: tabular-nums; }

.chart { padding: 4px 12px 12px; }
.chart-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.chart-label { flex: 0 0 88px; font-size: 11.5px; color: var(--text-2); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chart-track { flex: 1; position: relative; background: var(--border-0); border-radius: 4px; height: 14px; }
.chart-bar { position: absolute; top: 0; height: 100%; border-radius: 4px; background: var(--accent); min-width: 2px; }
.chart-bar.neg { background: var(--danger); }
/* shared zero line, drawn only when the series crosses it */
.chart-zero { position: absolute; top: -2px; bottom: -2px; width: 1px; background: var(--border-2); }
.chart-value { flex: 0 0 auto; font-size: 11.5px; color: var(--text-1); text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.chart-value.neg { color: var(--danger); }

.place-eta { font-size: 12.5px; color: var(--success); margin-top: 2px; }

.music-row { display: flex; gap: 12px; padding: 12px 12px 0; align-items: center; }
.music-art { width: 84px; height: 84px; border-radius: 10px; object-fit: cover; background: var(--bg-2); flex-shrink: 0; }
.music-info { min-width: 0; }
.music-title { font-weight: 600; font-size: 15px; }
.music-artist { font-size: 13px; color: var(--text-2); margin-top: 1px; }
.music-detail { font-size: 11.5px; color: var(--text-3); margin-top: 3px; text-transform: capitalize; }
.music-card .place-actions a.primary { background: var(--success-btn); }
.embla { overflow: hidden; touch-action: pan-y pinch-zoom; }
.embla__container { display: flex; }
.embla__slide { flex: 0 0 100%; min-width: 0; }
.embla__slide img { width: 100%; height: 180px; object-fit: cover; display: block; background: var(--bg-2); }
.place-info { padding: 10px 12px 2px; cursor: pointer; }
.place-name { font-weight: 600; font-size: 15px; }
.place-meta { font-size: 12.5px; color: var(--warning); margin-top: 2px; }
.place-addr { font-size: 12px; color: var(--text-2); margin-top: 2px; }
.place-actions { display: flex; gap: 8px; padding: 10px 12px 12px; }
.place-actions a {
  flex: 1;
  text-align: center;
  padding: 9px;
  border-radius: 9px;
  background: var(--bg-3);
  color: inherit;
  text-decoration: none;
  font-size: 14px;
}
.place-actions a.primary { background: var(--accent-soft); }

.product-row { display: flex; gap: 12px; padding: 12px 12px 0; align-items: flex-start; }
.product-art { width: 84px; height: 84px; border-radius: 10px; object-fit: cover; background: var(--bg-2); flex-shrink: 0; }
.product-info { min-width: 0; flex: 1; }
.product-title { font-weight: 600; font-size: 15px; }
.product-store { font-size: 12px; color: var(--text-2); margin-top: 1px; }
.product-priceRow { display: flex; align-items: baseline; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
.product-price { font-size: 15px; font-weight: 600; }
.product-stock { font-size: 11.5px; font-weight: 600; padding: 2px 8px; border-radius: 999px; }
.product-stock.in_stock { color: var(--success); background: var(--success-tint); }
.product-stock.out_of_stock { color: var(--danger); background: var(--danger-tint); }
.product-stock.unknown { color: var(--warning); background: var(--warning-tint); }
.product-variants { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 12px 2px; }
.product-variants .variant { font-size: 12px; padding: 3px 9px; border-radius: 8px; border: 1px solid var(--border-1); }
.product-variants .variant.in { color: var(--success); border-color: var(--success-line); background: var(--success-tint); }
.product-variants .variant.out { color: var(--text-3); text-decoration: line-through; opacity: 0.7; }
.product-note { font-size: 12px; color: var(--warning); padding: 8px 12px 0; }
.product-checked { font-size: 11px; color: var(--text-3); padding: 8px 12px 12px; }

.day-sep { align-self: center; font-size: 11px; color: var(--text-3); padding: 6px 0 2px; }
.line .when { display: block; font-size: 10px; color: var(--text-2); margin-top: 4px; }
.line.talk.live { opacity: 0.85; }

#toasts { position: fixed; top: calc(10px + env(safe-area-inset-top)); left: 50%; transform: translateX(-50%); z-index: 9; display: flex; flex-direction: column; gap: 6px; }
.toast { background: var(--bg-3); color: var(--text-0); font-size: 13px; padding: 9px 15px; border-radius: 999px; box-shadow: 0 6px 20px var(--shadow); }


/* ---- the app switcher, at the foot of the drawer ---- */

/* The drawer's foot holds links as well as buttons now. This rule lived in
   terminal.css for as long as the terminal was the only page with a link down
   there; it belongs beside .menu-foot button, which it otherwise duplicates
   line for line. */
.menu-foot a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 12px;
  border-radius: 12px;
  color: var(--text-1);
  font-size: 14px;
  text-decoration: none;
}
.menu-foot a:active { background: var(--bg-2); }

/* Above the preferences button and hairlined off from it: switching app and
   changing a setting are different kinds of leaving.
   The hairline belongs to the disclosure rather than the nav now, so it stays
   put whether the list is open or shut; on the nav it appeared and vanished
   with the list and read as the panel twitching. */
.menu-apps-wrap {
  padding-bottom: 6px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--border-0);
}
/* The summary is a row of the foot, not a control bolted above one: same
   padding, radius and colour as the preferences button beneath it, so the two
   read as siblings and the eye finds one list rather than two things. */
.menu-apps-wrap summary {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 12px;
  border-radius: 12px;
  color: var(--text-2);
  font-size: 14px;
  list-style: none;
  cursor: pointer;
}
/* Safari draws its own triangle through ::-webkit-details-marker and ignores
   list-style, which put two carets on the row. */
.menu-apps-wrap summary::-webkit-details-marker { display: none; }
.menu-apps-wrap summary:active { background: var(--bg-2); }
.menu-apps-wrap summary svg { flex: none; color: var(--text-3); }
.menu-caret {
  margin-left: auto;
  font-size: 10px;
  color: var(--text-3);
  transition: transform 0.16s ease;
}
.menu-apps-wrap[open] .menu-caret { transform: rotate(180deg); }
.menu-apps-wrap[open] summary { color: var(--text-1); }
@media (prefers-reduced-motion: reduce) {
  .menu-caret { transition: none; }
}

/* Indented under the summary that opened them, so an open list reads as
   belonging to the row above rather than as more feet. */
.menu-apps {
  display: flex;
  flex-direction: column;
  padding: 2px 0 4px 14px;
}
.app-link svg { flex: none; color: var(--text-2); }

/* The launcher's drawer has no conversations and no sessions in it, so the
   list is empty there and the foot would otherwise be pinned to the header by
   a border with nothing above it. */
#convs-list:empty { display: none; }
#convs-list:empty ~ .convs-hint { display: none; }

/* ---- the launcher ---- */

#launcher {
  display: flex;
  flex-direction: column;
  gap: 22px;
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
  /* #topbar is absolute over a scrim, so this buys its own clearance the same
     way #transcript does, and with the same 64px: the bar is the button row
     plus the fade below it, and anything less puts the product name under the
     hamburger. The safe-area insets are #app's, already applied by the time
     this is laid out, so adding them again here would double-count them and
     push the whole column down the screen on a notched phone. */
  /* The bottom inset moved out of #app and onto whichever element is last, so
     that a bar paints through the home indicator instead of ending above a
     strip of page. This is that element on this page. (Peer's rule, changed
     for that reason and flagged to them; the 64px clearance is untouched.) */
  padding: 64px 16px max(24px, env(safe-area-inset-bottom));
  /* enough apps to overflow is a good problem; scroll rather than clip */
  overflow-y: auto;
}
#launcher header { display: flex; align-items: center; gap: 12px; }
#launcher h1 { font-size: 17px; font-weight: 600; color: var(--text-0); }

/* The same auto-fill grid the theme picker uses, for the same reason: a phone
   gets one column, anything wider fills itself without a breakpoint. */
#app-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 8px; }
.app-tile {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  border: 1px solid transparent;
  border-radius: 16px;
  background: var(--bg-2);
  color: var(--text-1);
  font-size: 14px;
  text-decoration: none;
}
/* the mark is the tile's weight, so it is the thing that dims, not the label */
.app-tile svg { color: var(--text-2); }
.app-tile:active { background: var(--bg-3); border-color: var(--accent-line); }
.app-tile:active svg { color: var(--accent); }
