:root {
  --bg: #111318;
  --bg-nav: #0c0e12;
  --panel: #1a1d24;
  --panel-2: #22262f;
  --line: #2c313c;
  --text: #eceef2;
  --muted: #9aa3b2;
  --accent: #7aa2ff;
  --accent-press: #5b86e8;
  --on-accent: #081018;
  --owner: #3d6fe0;
  --owner-text: #f4f7ff;
  --ok: #3dd68c;
  --bad: #ff6b6b;
  --warn: #f5c542;
  --sel: #1e2838;
  --hover: #181c24;
  --shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --font: system-ui, "Segoe UI", sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
  --focus: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.45;
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; background: var(--bg); color: var(--text); }
::selection { background: #2d4a86; color: var(--text); }
:focus { outline: none; }
:focus-visible { outline: none; box-shadow: none; }
/* ChatGPT-style fields: border/background only. Halo stays on controls with no own activation chrome. */
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="hidden"]):focus-visible,
textarea:focus-visible,
select:focus-visible {
  border-color: var(--accent);
  background: var(--panel-2);
  box-shadow: none;
}
a:focus-visible,
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible,
.icon-btn:focus-visible,
.toast-copy:focus-visible,
.latest-del:focus-visible,
.compose-plus:focus-visible,
.attach-remove:focus-visible,
.img-tool:focus-visible,
.lightbox-close:focus-visible,
.search-hit:focus-visible {
  box-shadow: var(--focus);
}
.switch input:focus-visible + .switch-ui { box-shadow: var(--focus); }
.hidden { display: none !important; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); border: 0;
}

.screen { min-height: 100%; }
.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 28px;
  box-shadow: var(--shadow);
}
.login-card { max-width: 460px; margin: 12vh auto; }
.login-card h1 { margin: 0 0 10px; font-size: 28px; font-weight: 600; letter-spacing: -0.02em; }
.login-card button { margin-top: 16px; }

h1 { margin: 0 0 4px; font-size: 22px; font-weight: 600; letter-spacing: -0.02em; }
h2 { font-size: 16px; margin: 20px 0 8px; font-weight: 600; }
.muted { color: var(--muted); font-size: 13px; line-height: 1.45; }
label { display: block; font-size: 12px; color: var(--muted); margin: 14px 0 8px; }
input, textarea, select {
  width: 100%; background: var(--bg); color: var(--text);
  border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; font: inherit;
  transition: border-color 160ms var(--ease-out), background-color 160ms var(--ease-out);
}
input::placeholder, textarea::placeholder { color: #7d8696; }
button {
  background: var(--accent); color: var(--on-accent); border: 0;
  border-radius: 10px; padding: 10px 14px; font: inherit; font-weight: 600; cursor: pointer;
  transition: transform 140ms var(--ease-out), background 140ms var(--ease-out), border-color 140ms var(--ease-out), color 140ms var(--ease-out);
}
button:hover { background: var(--accent-press); }
button:active { transform: scale(0.97); }
button:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
button.ghost {
  background: transparent; color: var(--text); border: 1px solid var(--line);
}
button.ghost:hover { background: var(--hover); }
.error { color: var(--bad); min-height: 1.2em; }
code, pre { font-family: var(--mono); font-size: 12px; word-break: break-word; }
pre { background: var(--bg); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; overflow: auto; }

#app { display: grid; grid-template-columns: 232px 1fr; height: 100%; }
#app.nav-collapsed { grid-template-columns: 72px 1fr; }
.shell-nav {
  border-right: 1px solid var(--line);
  padding: 22px 16px;
  background: var(--bg-nav);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.nav-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.nav-brand-row {
  display: flex; align-items: center; gap: 4px;
  margin-bottom: 14px;
}
.nav-brand-row .brand {
  flex: 1; margin: 0;
  font-size: 15px; font-weight: 650;
  letter-spacing: -0.03em; color: var(--text);
}
.icon-btn {
  width: 32px; height: 32px; padding: 0;
  display: grid; place-items: center;
  border-radius: 8px; flex: 0 0 auto;
}
.nav-toggle { padding: 6px; width: 32px; height: 32px; display: grid; place-items: center; }
.brand { margin: 0; font-size: 12px; color: var(--muted); letter-spacing: 0.02em; }
.shell-nav h1 { margin: 4px 0 18px; font-size: 20px; }
.shell-nav nav { display: flex; flex-direction: column; gap: 2px; flex: 0 0 auto; }
.latest-block {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  margin-top: 16px;
}
.latest-label {
  margin: 0 10px 6px;
  font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.latest-list { overflow: auto; display: grid; gap: 2px; padding-bottom: 8px; }
.latest-row {
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  border-radius: 10px;
}
.latest-row:hover, .latest-row:focus-within { background: var(--hover); }
.latest-row.active { background: var(--sel); }
.latest-item, .search-hit {
  width: 100%; text-align: left;
  background: transparent; color: var(--text);
  border: 0; border-radius: 10px;
  padding: 10px 10px; font-weight: 500;
  display: grid; gap: 2px;
}
.latest-item:hover, .search-hit:hover { background: transparent; }
.search-hit:hover { background: var(--hover); }
.latest-item.active { background: transparent; }
.latest-del {
  width: 28px; height: 28px; padding: 0; margin-right: 4px;
  opacity: 0; color: var(--muted);
}
.latest-row:hover .latest-del,
.latest-row:focus-within .latest-del { opacity: 1; }
.latest-del:hover { color: var(--bad); background: var(--hover); }
.latest-item .t, .search-hit .t {
  display: block;
  font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.latest-item .s, .search-hit .s {
  font-size: 11px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Sidebar polish: the relative-time tag only shows on hover, and a title too
   long for its row scrolls once to reveal the tail (chatgpt.com-style),
   instead of both permanently competing with the title for width. */
.latest-item .s {
  opacity: 0;
  transition: opacity 140ms var(--ease-out);
}
.latest-row:hover .latest-item .s,
.latest-row:focus-within .latest-item .s {
  opacity: 1;
}
@media (hover: hover) and (pointer: fine) {
  .latest-row:hover .latest-item .t.marquee-eligible {
    text-overflow: clip;
    animation: latest-marquee 3.6s linear infinite;
  }
}
@keyframes latest-marquee {
  0%, 10% { transform: translateX(0); }
  45%, 65% { transform: translateX(var(--marquee-dist, 0)); }
  100% { transform: translateX(0); }
}
.shell-nav nav button {
  margin: 0; text-align: left; background: transparent; color: var(--text);
  border: 0; font-weight: 500; padding: 8px 10px; border-radius: 8px;
  display: flex; align-items: center; gap: 10px;
}
.shell-nav nav button svg { flex: 0 0 auto; opacity: 0.85; }
.shell-nav nav button:hover { background: var(--hover); }
.shell-nav nav button.active { background: var(--sel); color: var(--text); }
.account-wrap { margin-top: auto; position: relative; }
.account-btn {
  width: 100%; display: flex; align-items: center; gap: 10px;
  background: transparent; color: var(--text); border: 0; padding: 8px;
  border-radius: 10px; font-weight: 500; text-align: left;
}
.account-btn:hover { background: var(--hover); }
.account-meta { display: grid; min-width: 0; }
.account-meta span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.avatar.owner { background: var(--owner); color: var(--owner-text); }
.account-menu {
  position: absolute; left: 0; right: 0; bottom: 56px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: 6px; box-shadow: var(--shadow);
  display: grid; gap: 2px;
}
.account-menu button {
  margin: 0; width: 100%; text-align: left; background: transparent;
  color: var(--text); font-weight: 500;
}
.account-menu button:hover { background: var(--hover); }
#app.nav-collapsed .shell-nav h1,
#app.nav-collapsed .nav-label,
#app.nav-collapsed .account-meta,
#app.nav-collapsed .latest-block { display: none; }
#app.nav-collapsed .brand {
  display: block; flex: none;
  font-size: 12px; font-weight: 700;
}
#app.nav-collapsed .nav-brand-row { flex-direction: column; }
#app.nav-collapsed .shell-nav { padding: 16px 10px; align-items: center; }
#app.nav-collapsed .shell-nav nav button { justify-content: center; padding: 10px; }
#app.nav-collapsed .account-btn { justify-content: center; }

main { display: flex; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }
.top {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 20px; border-bottom: 1px solid var(--line); gap: 12px;
}
#agents-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.view { padding: 20px 24px; overflow: auto; flex: 1; min-height: 0; }
.view-home { display: grid; place-items: center; }
.home-hero { position: relative; text-align: center; width: min(768px, 100%); max-width: 768px; }
.home-hero h1 { font-size: 32px; letter-spacing: -0.03em; margin: 0 0 10px; }
.home-hero .muted { margin: 0 0 22px; }
.home-hero .compose-dock { width: 100%; margin: 0; }
.home-hero .compose { text-align: left; }
.home-hero button.busy, button.busy { position: relative; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
.tile { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 16px; }
.pill { font-size: 11px; border: 1px solid var(--line); border-radius: 99px; padding: 4px 10px; color: var(--muted); }
.pill.ok { color: var(--ok); border-color: #2a6a4a; }
.pill.bad { color: var(--bad); border-color: #7a3434; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { color: var(--muted); font-weight: 550; }

/* Conversation — chat column, not a log table */
.view-conversation {
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.conv-rail {
  border-right: 1px solid var(--line);
  background: var(--bg-nav);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.conv-rail-head { padding: 16px 14px 10px; }
.conv-rail-head h2 { margin: 0 0 10px; font-size: 15px; }
.conv-list { overflow: auto; padding: 0 8px 16px; flex: 1; }
.conv-item {
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--text);
  border: 0;
  border-radius: 10px;
  padding: 10px 10px;
  font-weight: 500;
  display: grid;
  gap: 2px;
}
.conv-item:hover { background: var(--hover); }
.conv-item.active { background: var(--sel); }
.conv-item .t { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.conv-item .s { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.conv-empty { padding: 16px 12px; color: var(--muted); font-size: 13px; }

.conv-thread {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  min-height: 0;
  background:
    radial-gradient(1200px 400px at 50% -10%, rgba(122, 162, 255, 0.06), transparent 50%),
    var(--bg);
}
/* Item 8: floats over the stream (position, not a filled bar) instead of
   sitting in normal flow with an opaque background — the conversation
   view's own header.top substitute (see #app.conv-mode below) folds title,
   model picker, and agent status into one compact row.
   Item 9: this is the other half of the backdrop-filter glass fallback (see
   the long comment on .compose) — a real-time WebGL sample of a stream that
   scrolls and gets new messages under it is exactly the case that turned
   out not to work, so this stays a compositor-level blur, no canvas capture. */
.conv-head {
  position: relative;
  z-index: 2;
  padding: 10px 20px;
  border-bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--bg) 60%, transparent), color-mix(in srgb, var(--bg) 25%, transparent));
  backdrop-filter: blur(18px) saturate(1.5);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.05);
}
@supports not (backdrop-filter: blur(1px)) {
  .conv-head { background: var(--bg); }
}
.conv-head-copy { min-width: 0; flex: 1 1 auto; }
.conv-head h2 { margin: 0 0 2px; font-size: 15px; }
.conv-head .muted { margin: 0; font-size: 12px; }
.conv-model {
  width: auto; max-width: 220px; min-width: 120px;
  border: 0; background: transparent; padding: 6px 4px;
  color: var(--muted); font-size: 13px; font-weight: 550;
}
/* The global .top bar (agent pills + ws pill) is shared chrome for every
   other view; the conversation view replaces it with the compact readout
   below, so it steps aside and gives the height back to the stream. */
#app.conv-mode header.top { display: none; }
.conv-agent-dots { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.agent-dot {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10px; font-weight: 650; letter-spacing: 0.02em;
  color: var(--muted); text-transform: uppercase;
  cursor: default;
}
.agent-dot i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--muted); flex: 0 0 auto;
}
.agent-dot.ok i { background: var(--ok); }
.agent-dot.bad i { background: var(--bad); }
.conv-ws-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--muted); flex: 0 0 auto;
  transition: background-color 200ms var(--ease-out);
}
.conv-ws-dot.ok { background: var(--ok); }

.stream {
  flex: 1;
  overflow: auto;
  overflow-anchor: none;
  /* Extra bottom room keeps the last message — including its new per-message
     copy button below the bubble, and a future edit affordance — clear of the
     floating .compose-dock overlay (see conversation view below); this is
     plain padding inside the scrollable box, so scrollHeight/clientHeight
     math used by the stickBottom tracking is unaffected. */
  padding: 20px 20px 190px;
  scroll-behavior: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.stream-inner {
  width: min(760px, 100%);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-anchor: none;
}
.stream-empty {
  margin: auto;
  max-width: 36ch;
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.5;
}

.row {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  padding: 6px 0;
  max-width: 100%;
}
.row.in { justify-content: flex-start; }
.row.out { justify-content: flex-end; }
.row.sys { justify-content: center; padding: 10px 0; }
.row.enter {
  animation: msg-in 180ms var(--ease-out);
}
.avatar {
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 10px; font-weight: 700; letter-spacing: 0.02em;
  flex: 0 0 auto;
  background: var(--panel-2); color: var(--text);
}
.avatar.director { background: #2a3348; color: #c5d4ff; }
.avatar.developer { background: #2a3a32; color: #b7e0c8; }
.bubble-col { min-width: 0; }
.row.out .bubble-col { max-width: min(72ch, 78%); display: flex; flex-direction: column; align-items: flex-end; }
.row.in .bubble-col { max-width: 100%; width: 100%; }
.who-line { font-size: 11px; color: var(--muted); margin: 0 2px 4px; }
.bubble {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 22px;
  padding: 10px 14px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
  overflow-wrap: anywhere;
}
.bubble.pill { border-radius: 22px; }
.bubble.tall { border-radius: 12px; }
/* Corner symmetry: when a reference image sits directly above the caption
   bubble (item 2), the adjoining corners both shrink to the same radius so
   the image and the bubble read as one continuous shape, not two
   independently-rounded pieces stacked together. */
.row.out .bubble.has-atts { border-top-right-radius: 8px; }
.owner-images-adjoin .msg-figure:last-child .msg-image { border-bottom-right-radius: 8px; }
.row.out .bubble {
  background: var(--owner);
  border-color: transparent;
  color: var(--owner-text);
}
/* ChatGPT grammar: only the Owner's message is a bubble. Director/Developer/System
   replies with attachments are plain text on the background — no box, no avatar,
   full width up to the stream's own max-width (see .stream-inner). */
.row.in .bubble {
  background: transparent;
  border: 0;
  box-shadow: none;
  padding: 0;
  border-radius: 0;
  max-width: none;
}
.row.sys .bubble {
  background: transparent;
  border: 0;
  box-shadow: none;
  color: var(--muted);
  font-size: 12px;
  text-align: center;
  padding: 0;
  max-width: 56ch;
}
.bubble p { margin: 0 0 0.7em; }
.bubble p:last-child { margin-bottom: 0; }
.bubble ul, .bubble ol { margin: 0.3em 0 0.7em; padding-left: 1.2em; }
.bubble pre {
  margin: 8px 0;
  background: #0c0e12;
  color: #e7ecf3;
  border-radius: 10px;
}
.row.out .bubble pre, .row.out .bubble code { color: #f4f7ff; background: rgba(8, 16, 24, 0.28); }
.bubble a { color: inherit; text-underline-offset: 2px; }
.md-code {
  position: relative;
  margin: 8px 0;
}
.md-code pre { margin: 0; padding: 30px 12px 10px; }
.md-code-lang {
  position: absolute; top: 9px; left: 14px;
  font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.md-code button {
  position: absolute; top: 6px; right: 6px;
  padding: 4px 8px; font-size: 11px; font-weight: 550;
  background: var(--panel-2); color: var(--text);
  border-radius: 6px; border: 0; cursor: pointer;
  display: flex; align-items: center; gap: 4px;
}
.md-code button:hover { background: var(--hover); }
.md-code button.copied { color: var(--ok); }
.md-quote {
  margin: 8px 0;
  padding: 2px 14px;
  border-left: 3px solid var(--accent);
  background: rgba(122, 162, 255, 0.08);
  color: var(--muted);
  border-radius: 0 8px 8px 0;
}
.row.out .md-quote {
  border-left-color: rgba(255, 255, 255, 0.55);
  background: rgba(255, 255, 255, 0.1);
  color: var(--owner-text);
}
.session-sep {
  display: flex;
  justify-content: center;
  margin: 18px 0 6px;
}
.session-sep span {
  font-size: 12px;
  color: var(--muted);
  padding: 2px 10px;
}

.typing {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 0 16px;
  color: var(--muted); font-size: 13px;
}
.typing-copy { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.typing:not(.image-wait) { align-items: center; }
.typing:not(.image-wait) .typing-copy { flex-direction: row; align-items: center; gap: 10px; }
.image-skel {
  width: min(280px, 70vw);
  aspect-ratio: 1;
  border-radius: 14px;
  background: var(--panel-2);
}
.dots { display: inline-flex; gap: 4px; }
.dots i {
  width: 6px; height: 6px; border-radius: 50%; background: var(--muted);
  animation: dots 1.05s linear infinite;
  display: block;
}
.dots i:nth-child(2) { animation-delay: 0.15s; }
.dots i:nth-child(3) { animation-delay: 0.3s; }

.compose-dock {
  width: min(768px, calc(100% - 32px));
  margin: 8px auto 16px;
}
/* Conversation view: the dock floats over the stream instead of sitting in
   its own bordered row, so the chat fills the pane edge-to-edge (item 9).
   The stream keeps extra bottom padding (see .stream) so messages never
   render underneath it. */
.conv-thread .compose-dock {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0 auto 18px;
  z-index: 2;
}
.conv-thread .compose-dock::before {
  content: "";
  position: absolute;
  left: -20px;
  right: -20px;
  bottom: 100%;
  height: 28px;
  background: linear-gradient(to bottom, transparent, var(--bg) 85%);
  pointer-events: none;
}
/* Item 10: a soft, animated glow fanning up from behind the compose box —
   a quiet "this is an intelligent interface" accent, never a theme change.
   Sits behind .compose-dock (lower z-index), never intercepts clicks. */
.compose-glow {
  position: absolute;
  left: 50%;
  bottom: -36px;
  width: min(720px, 92%);
  height: 200px;
  transform: translateX(-50%);
  pointer-events: none;
  z-index: 1;
  background:
    radial-gradient(55% 140% at 28% 100%, rgba(122, 162, 255, 0.22), transparent 70%),
    radial-gradient(45% 160% at 72% 100%, rgba(122, 162, 255, 0.18), transparent 70%),
    radial-gradient(75% 100% at 50% 100%, rgba(122, 162, 255, 0.26), transparent 62%);
  filter: blur(26px);
  animation: compose-glow-breathe 7s var(--ease-in-out) infinite;
}
@keyframes compose-glow-breathe {
  0%, 100% { opacity: 0.5; transform: translateX(-50%) scale(0.96); }
  50% { opacity: 0.85; transform: translateX(-50%) scale(1.04); }
}
/* Item 9: liquid-glass-js (WebGL + html2canvas real-time refraction) was
   vendored and prototyped against a mock of this exact conversation view
   (120 mixed text/code/image rows, same styles.css) rather than a static
   screenshot. Findings, in full in the PR description: a single
   html2canvas(document.body) capture averaged ~630ms (590-760ms across 5
   runs) against that DOM — and the library only ever takes that snapshot
   once, then fakes "live" scroll by re-offsetting the same frozen texture
   (confirmed cheap, ~1.2ms/tick — that part was never the problem). Against
   a stream that gets new messages, image hydration, and typing indicators
   independently of scroll, that means either a 600ms+ freeze on every
   re-snapshot or a glass panel showing a visibly stale ghost of the page.
   Neither is acceptable on chrome sitting directly over a live thread, so
   this ships the backdrop-filter fallback the task green-lit instead:
   semi-transparent panel, blur + saturate, and a soft inset top highlight
   standing in for the sheen. Nothing here is a WebGL/html2canvas dependency. */
.compose {
  background: color-mix(in srgb, var(--panel) 72%, transparent);
  backdrop-filter: blur(20px) saturate(1.6);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  border: 1px solid color-mix(in srgb, white 8%, var(--line));
  border-radius: 28px;
  padding: 12px 14px 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 0 0 1px rgba(255, 255, 255, 0.02), var(--shadow);
  transition: border-color 160ms var(--ease-out);
  position: relative;
}
@supports not (backdrop-filter: blur(1px)) {
  .compose { background: var(--panel); }
}
.compose:focus-within { border-color: #4a5160; }
.compose textarea {
  border: 0; background: transparent; resize: none;
  min-height: 28px; max-height: 200px; padding: 4px 4px 0;
  line-height: 1.5; font-size: 16px;
  flex: 1 1 auto;
  min-width: 0;
  /* Item 3: text content growing past one line already drives this via JS
     (box.style.height, set from scrollHeight in grow()); transitioning it
     is the smooth-grow cue. height has no transform equivalent here — same
     tolerated exception RECIPES.md carves out for accordions. */
  transition: height 160ms var(--ease-out);
}
.compose textarea:focus-visible {
  border-color: transparent;
  background: transparent;
}
/* Item 3: single row by default (plus / textarea / send inline, matching
   source order) so an empty or one-line compose box reads as compact as
   ChatGPT's. JS toggles .is-multiline on .compose (see grow() in app.js)
   only once typed text actually wraps past one line — at that point the
   textarea claims the full row width and drops to its own line, pushing
   the tools/send row below it, which is the growth this task asks for. */
.compose-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.compose.is-multiline .compose-row textarea {
  order: -1;
  flex-basis: 100%;
}
.compose-tools { display: flex; align-items: center; gap: 8px; }
.compose-plus, .compose-send {
  width: 36px; height: 36px; padding: 0;
  border-radius: 50%;
  display: grid; place-items: center;
}
.compose-plus {
  background: transparent; color: var(--text);
  border: 1px solid var(--line);
}
.compose-plus:hover { background: var(--hover); }
.compose-plus:disabled { opacity: 0.45; cursor: not-allowed; }
.compose-plus:active { transform: scale(0.97); }
.compose.drop { border-color: var(--accent); }
.compose-previews {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: 2px 4px 6px;
}
.compose-previews.hidden { display: none; }
.compose-limit {
  margin: 0 4px 8px;
  font-size: 12px;
  line-height: 1.3;
  color: var(--muted);
}
.compose-limit.hidden { display: none; }
.attach-chip {
  display: flex; align-items: center; gap: 8px;
  max-width: 220px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 6px 8px 6px 6px;
}
.attach-thumb-wrap {
  position: relative;
  width: 36px; height: 36px; flex: none;
}
.attach-thumb {
  width: 36px; height: 36px; border-radius: 10px;
  object-fit: cover; background: var(--bg); flex: none;
}
.attach-file-icon {
  width: 36px; height: 36px; border-radius: 10px;
  display: grid; place-items: center; flex: none;
  background: var(--bg); color: var(--muted);
}
.attach-progress {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: rgba(8, 10, 16, 0.48);
  pointer-events: none;
}
.attach-progress svg {
  width: 22px;
  height: 22px;
  transform: rotate(-90deg);
}
.attach-progress-track,
.attach-progress-fill {
  fill: none;
  stroke-width: 2.5;
}
.attach-progress-track { stroke: rgba(236, 238, 242, 0.22); }
.attach-progress-fill {
  stroke: var(--accent);
  stroke-linecap: round;
  stroke-dasharray: 56.55;
  stroke-dashoffset: calc(56.55 * (1 - var(--p, 0)));
  transition: stroke-dashoffset 220ms var(--ease-out);
}
.attach-chip.is-error {
  border-color: color-mix(in srgb, var(--bad) 55%, var(--line));
}
.attach-chip.is-error .attach-size {
  color: var(--bad);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.attach-meta { min-width: 0; flex: 1; }
.attach-name {
  margin: 0; font-size: 12px; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.attach-size { margin: 2px 0 0; font-size: 11px; color: var(--muted); }
.attach-remove {
  width: 22px; height: 22px; padding: 0; border-radius: 50%;
  background: transparent; color: var(--muted); border: 0;
  display: grid; place-items: center; flex: none;
}
.attach-remove:hover { background: var(--hover); color: var(--text); }
.attach-remove:active { transform: scale(0.97); }
.msg-files {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 6px;
}
.msg-file {
  display: flex; align-items: center; gap: 10px;
  margin: 0;
  min-width: 180px;
  max-width: 260px;
  padding: 8px 12px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  text-decoration: none;
  font-size: 13px;
}
.msg-file:hover { background: var(--hover); }
.msg-file-icon {
  width: 32px; height: 32px; border-radius: 8px;
  display: grid; place-items: center; flex: none;
  background: var(--bg); color: var(--muted);
}
.msg-file-meta { min-width: 0; display: grid; gap: 1px; }
.msg-file-name {
  font-size: 13px; font-weight: 550;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.msg-file-kind { font-size: 11px; color: var(--muted); }
.msg-gallery {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 8px;
}
.msg-gallery .msg-figure {
  width: 112px;
  height: 112px;
  max-width: 112px;
  margin: 0;
  overflow: hidden;
  border-radius: 12px;
}
.msg-gallery .msg-image {
  width: 100%;
  height: 100%;
  max-height: none;
  object-fit: cover;
  border-radius: 12px;
}
.msg-gallery .msg-image-tools {
  top: 6px;
  right: 6px;
}
.msg-gallery .img-tool {
  width: 28px;
  height: 28px;
}
.compose-plus-wrap { position: static; }
/* Item 4: anchor the dropdown to the whole compose box (.compose, the
   nearest positioned ancestor now that .compose-plus-wrap no longer creates
   its own positioning context), not just the plus button's small wrapper —
   so "bottom: 100%" means "above the box" in every layout the box can be in
   (single-line row, wrapped multi-row, home vs conversation composer). */
.compose-plus-menu {
  position: absolute;
  left: 14px;
  bottom: calc(100% + 8px);
  min-width: 220px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 6px;
  box-shadow: var(--shadow);
  display: grid;
  gap: 2px;
  z-index: 3;
}
.compose-menu-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  background: transparent;
  color: var(--text);
  border: 0;
  border-radius: 8px;
  padding: 9px 10px;
  font-size: 13px;
  font-weight: 500;
  text-align: left;
}
.compose-menu-item svg:first-child { flex: 0 0 auto; opacity: 0.85; }
.compose-menu-item span { flex: 1; }
.compose-menu-item:hover { background: var(--hover); }
.compose-menu-check { flex: 0 0 auto; opacity: 0; color: var(--accent); }
.compose-image {
  font-size: 13px;
}
.compose-image.active { color: var(--accent); background: var(--sel); }
.compose-image.active .compose-menu-check { opacity: 1; }
.msg-figure {
  position: relative;
  display: inline-block;
  max-width: min(100%, 420px);
  margin: 4px 0;
}
.msg-image {
  display: block;
  max-width: 100%;
  max-height: 420px;
  border-radius: 14px;
  background: var(--panel-2);
  cursor: zoom-in;
}
.msg-image-tools {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  gap: 4px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms var(--ease-out);
}
.msg-figure:hover .msg-image-tools,
.msg-figure:focus-within .msg-image-tools {
  opacity: 1;
  pointer-events: auto;
}
/* Owner's reference photo for an edit stands on its own above the caption
   bubble (no shared background), matching ChatGPT's separated layout. */
.owner-images { margin: 4px 0 8px; display: flex; flex-direction: column; align-items: flex-end; }
.owner-images .msg-figure { margin: 0; }
/* Generated/edited images keep an always-visible Edit pill (bottom-left) and
   download control (bottom-right) directly on the image, instead of the
   hover-only .msg-image-tools used for plain attachments. */
.gen-figure .gen-edit-pill,
.gen-figure .gen-dl-pill {
  position: absolute;
  bottom: 8px;
  opacity: 1;
  pointer-events: auto;
}
.gen-edit-pill {
  left: 8px;
  height: 30px;
  padding: 0 14px;
  border-radius: 15px;
  background: rgba(12, 14, 18, 0.72);
  color: #f4f7ff;
  border: 0;
  font-size: 12px;
  font-weight: 600;
}
.gen-edit-pill:hover { background: rgba(12, 14, 18, 0.9); }
.gen-edit-pill:active { transform: scale(0.97); }
.gen-dl-pill { right: 8px; }
.msg-op-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 2px 0 6px;
}
/* Shared style for every message-level copy affordance (image-prompt copy in
   .msg-op-row and the plain per-message text copy in .msg-copy-row): no fill
   or border until hover, matching chatgpt.com's minimal icon buttons — this
   overrides the .ghost class's default border/transparent-bg combo. */
.msg-copy-btn {
  width: 26px; height: 26px; color: var(--muted);
  background: transparent; border: 0;
}
.msg-copy-btn:hover { background: var(--hover); color: var(--text); }
.msg-copy-btn.copied { color: var(--ok); }
.msg-copy-row { margin: 4px 2px 0; }
.op-chip {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 10px;
}
.img-tool,
.lightbox-close {
  width: 32px;
  height: 32px;
  padding: 0;
  border-radius: 8px;
  display: grid;
  place-items: center;
  background: rgba(12, 14, 18, 0.72);
  color: #f4f7ff;
  border: 0;
}
.img-tool:hover,
.lightbox-close:hover { background: rgba(12, 14, 18, 0.9); }
.img-tool:active,
.lightbox-close:active { transform: scale(0.97); }
.drop-overlay {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(8, 10, 14, 0.62);
}
.drop-overlay-card {
  width: min(520px, 100%);
  padding: 36px 28px;
  text-align: center;
  border-radius: 22px;
  border: 1.5px dashed var(--accent);
  background: var(--panel);
  box-shadow: var(--shadow);
}
.drop-overlay-title {
  margin: 0 0 8px;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.02em;
}
.drop-overlay-sub {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.45;
}
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 22;
  display: grid;
  place-items: center;
  padding: 48px 24px 24px;
  background: rgba(8, 10, 14, 0.82);
}
.lightbox img {
  max-width: min(92vw, 1100px);
  max-height: calc(100vh - 96px);
  border-radius: 12px;
  box-shadow: var(--shadow);
}
.lightbox-close {
  position: absolute;
  top: 16px;
  right: 16px;
}
.lightbox-tools {
  position: absolute;
  top: 16px;
  right: 56px;
  display: flex;
  gap: 4px;
}
.compose-send {
  background: #f4f6fb; color: #111318;
}
.compose-send:hover { background: #ffffff; }
.compose-send:disabled {
  background: var(--panel-2); color: var(--muted);
  opacity: 1; cursor: not-allowed; transform: none;
}
.compose-hint {
  margin: 8px 0 0;
  text-align: center;
  font-size: 11px;
  color: var(--muted);
}
.jump-latest {
  position: absolute;
  left: 50%;
  bottom: 132px;
  transform: translateX(-50%);
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  font-size: 12px;
  padding: 6px 12px;
}
.jump-latest:active { transform: translateX(-50%) scale(0.97); }

@keyframes msg-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@keyframes dots {
  0%, 80%, 100% { opacity: 0.28; transform: translateY(0); }
  40% { opacity: 1; transform: translateY(-2px); }
}

@media (hover: hover) and (pointer: fine) {
  .conv-item:hover { background: var(--hover); }
}

/* Item 7: Settings modal — left category list, search, right pane. Reuses
   the .modal / .modal-card overlay pattern already established by the
   search and TOTP modals below, so it opens from anywhere without a route
   of its own (see openSettings()/closeSettings() in app.js). */
.settings-modal-card {
  width: min(880px, calc(100% - 40px));
  max-height: min(700px, calc(100vh - 64px));
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.settings-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.settings-modal-head h2 { margin: 0; }
.settings-modal-search {
  padding: 12px 20px;
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.settings-modal-search input { font-size: 14px; }
.settings-modal-hint { margin: 8px 0 0; }
.settings-modal-body {
  display: flex;
  flex: 1;
  min-height: 0;
}
.settings-categories {
  width: 168px;
  flex: 0 0 auto;
  border-right: 1px solid var(--line);
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: auto;
  transition: opacity 160ms var(--ease-out);
}
.settings-categories.searching { opacity: 0.45; }
.settings-cat {
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--text);
  border: 0;
  font-weight: 500;
  font-size: 13px;
  padding: 9px 10px;
  border-radius: 8px;
}
.settings-cat:hover { background: var(--hover); }
.settings-cat.active { background: var(--sel); }
.settings-pane {
  flex: 1;
  min-width: 0;
  overflow: auto;
  padding: 4px 20px 20px;
}
.settings-empty { padding: 28px 4px; }
.settings-section {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  margin: 16px 0;
  overflow: hidden;
}
.settings-section-head { padding: 16px 18px 8px; }
.settings-section-head h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.settings-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 16px;
  align-items: center;
  padding: 14px 18px;
  border-top: 1px solid var(--line);
}
.settings-title { margin: 0 0 4px; font-weight: 550; }
.settings-actions { display: flex; gap: 8px; align-items: center; min-width: 260px; }
.settings-actions input, .settings-actions select { margin: 0; }
.settings-actions button { flex: 0 0 auto; }
.settings-row select, .settings-row input[type="password"], .settings-row input[type="text"] {
  min-width: 220px;
}
.multi { min-width: 280px; max-width: 360px; }
.multi-filter { margin-bottom: 8px; }
.multi-list { max-height: 220px; overflow: auto; display: grid; gap: 2px; }
.multi-item {
  display: flex; align-items: center; gap: 8px;
  margin: 0; padding: 6px 8px; border-radius: 8px; color: var(--text);
}
.multi-item:hover { background: var(--hover); }
.multi-item input { width: auto; }

.modal {
  position: fixed; inset: 0; background: rgba(8, 10, 14, 0.56);
  display: grid; place-items: center; z-index: 20;
}
.modal.hidden { display: none !important; }
.modal-card {
  width: min(400px, calc(100% - 32px));
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 16px; padding: 22px; box-shadow: var(--shadow);
}
.modal-card h2 { margin: 0 0 8px; }
.modal-actions { display: flex; gap: 8px; margin-top: 16px; }
.search-card {
  width: min(560px, calc(100% - 32px));
  padding: 16px;
}
.search-card input[type="search"] { font-size: 16px; }
.search-results {
  margin-top: 12px; display: grid; gap: 2px;
  max-height: 50vh; overflow: auto;
}
.totp-digits {
  display: flex; justify-content: center; gap: 10px;
  margin: 22px 0 8px;
}
.totp-cell {
  width: 42px; height: 56px; padding: 0;
  border: 0; border-bottom: 2px solid var(--line);
  border-radius: 0; background: transparent;
  text-align: center; font-size: 28px; font-weight: 600;
  transition: border-bottom-color 180ms var(--ease-out), color 180ms var(--ease-out);
}
.totp-cell:focus, .totp-cell:focus-visible {
  border-bottom-color: var(--accent); background: transparent; box-shadow: none;
}
.totp-digits.error .totp-cell { border-bottom-color: var(--bad); color: var(--bad); }
.totp-digits.ok .totp-cell { border-bottom-color: var(--ok); color: var(--ok); }
.totp-status {
  text-align: center;
  min-height: 1.2em;
  margin: 4px 0 0;
  font-size: 13px;
  font-weight: 550;
  color: var(--bad);
}
.totp-status:empty { color: transparent; }
.totp-status.ok {
  color: var(--ok);
  animation: totp-ok-in 220ms var(--ease-out);
}
.totp-check {
  display: inline-block;
  width: 14px; height: 14px;
  margin-right: 6px;
  vertical-align: -2px;
}
.totp-card.shake { animation: shake 360ms var(--ease-out); }
.totp-submit { margin-top: 12px; width: 100%; }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-8px); }
  40% { transform: translateX(8px); }
  60% { transform: translateX(-5px); }
  80% { transform: translateX(5px); }
}
@keyframes totp-ok-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.skel {
  height: 14px; border-radius: 8px;
  background: var(--panel-2);
  overflow: hidden; position: relative;
}
.skel.short { width: 40%; }
.skel.msg { height: 48px; width: min(60%, 420px); margin: 10px 0; }
.skel.latest { height: 36px; margin: 6px 8px; }
.skel-table { display: grid; gap: 10px; max-width: 760px; }
.skel-table .skel { height: 18px; }
.skel-title { height: 18px; width: 120px; color: transparent; }
.skel-pill { min-width: 72px; height: 22px; color: transparent; border-color: transparent; }
.shimmer { position: relative; overflow: hidden; }
.shimmer::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent);
  animation: shimmer 1.2s ease-in-out infinite;
}
@keyframes shimmer {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); }
}
.settings-row.saving { position: relative; }
.settings-row.saving::after {
  content: "";
  position: absolute; top: 16px; right: 18px;
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}
.settings-row.saving .settings-title { color: var(--muted); }
@keyframes spin { to { transform: rotate(360deg); } }

.switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; margin: 0; }
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch-ui {
  width: 42px; height: 26px; border-radius: 99px;
  background: var(--panel-2); border: 1px solid var(--line);
  position: relative; transition: background 160ms var(--ease-out), border-color 160ms var(--ease-out);
}
.switch-ui::after {
  content: "";
  position: absolute; top: 2px; left: 2px;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--text);
  transition: transform 160ms var(--ease-out);
}
.switch input:checked + .switch-ui { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .switch-ui::after { transform: translateX(16px); background: var(--on-accent); }

@media (prefers-reduced-motion: reduce) {
  .row.enter { animation: none; }
  .dots i { animation: none; opacity: 0.7; }
  button:active { transform: none; }
  .switch-ui, .switch-ui::after { transition: none; }
  .shimmer::after, .totp-card.shake, .totp-status.ok,
  .settings-row.saving::after, .image-skel.shimmer::after { animation: none; }
  .msg-image-tools { transition: none; }
  .attach-progress-fill { transition: none; }
  .latest-row:hover .latest-item .t.marquee-eligible { animation: none; text-overflow: ellipsis; }
  .compose textarea { transition: none; }
  .compose-glow { animation: none; opacity: 0.55; transform: translateX(-50%); }
  .toast {
    transition: opacity 160ms var(--ease-out);
    transform: none;
  }
  .toast.toast-leaving { transform: none; }
}

@media (max-width: 900px) {
  /* Desktop-first (Phase 12 is PWA). Keep usable, no mobile chrome. */
  #app { grid-template-columns: 1fr; }
  .shell-nav { display: none; }
  .view-conversation { display: flex; }
  .conv-rail { display: none; }
}

#toast-stack {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 15;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 8px;
  width: min(380px, calc(100vw - 32px));
  max-height: calc(100vh - 40px);
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: start;
  padding: 12px 10px 12px 14px;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow);
  cursor: default;
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 220ms var(--ease-out),
    transform 220ms var(--ease-out);
}
@starting-style {
  .toast {
    opacity: 0;
    transform: translateY(100%);
  }
}
.toast.toast-leaving {
  opacity: 0;
  transform: translateY(100%);
}
.toast-msg {
  margin: 0;
  font-size: 13px;
  line-height: 1.4;
  overflow-wrap: anywhere;
  user-select: text;
  cursor: text;
}
.toast-error .toast-msg { color: var(--bad); }
.toast-ok .toast-msg { color: var(--ok); }
.toast-warn .toast-msg { color: var(--warn); }
.toast-actions {
  display: flex;
  gap: 2px;
  flex: 0 0 auto;
}
.toast-copy,
.toast-close {
  width: 28px;
  height: 28px;
  padding: 0;
  color: var(--muted);
}
.toast-copy:hover,
.toast-close:hover { color: var(--text); background: var(--hover); }
.toast-copy:active,
.toast-close:active { transform: scale(0.97); }
.toast-copy.copied { color: var(--ok); }
