/* MorseHub web app. Every color comes from tokens.css (DESIGN.md). No new colors here. */
@import url("tokens.css");

*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
html { -webkit-tap-highlight-color: transparent; }
body {
  margin: 0; background: var(--canvas); color: var(--ink);
  font: 400 var(--t-body)/var(--lh) var(--font);
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
[hidden] { display: none !important; }
h1, h2, h3, p { margin: 0; }
button, input, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--accent) 28%, transparent); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.skip { position: absolute; left: var(--s2); top: -100px; background: var(--card); color: var(--accent); padding: var(--s2) var(--s3); border-radius: var(--r-pill); border: 1px solid var(--line); z-index: 10; }
.skip:focus { top: var(--s2); }
/* Thin scrollbars in the palette, everywhere something scrolls. */
* { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--line); border-radius: var(--r-pill); }
::-webkit-scrollbar-track { background: transparent; }
.mono { font-family: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace; }
.icon { display: block; flex: none; }

.wordmark { font-size: var(--t-mark); font-weight: 700; letter-spacing: -0.01em; display: inline-flex; align-items: center; gap: var(--s2); }
/* The mark (UIcons "comment dots", DESIGN.md "Logo"): Signal blue beside the ink wordmark. */
.mark { display: block; flex: none; }
.wordmark .mark { width: 22px; height: 22px; color: var(--accent); }
/* Typing dots: first load and first sync only, never for sends or status. */
.mark.typing .dot { animation: mh-typing 1.2s ease-in-out infinite; }
.mark.typing .dot:nth-of-type(2) { animation-delay: .15s; }
.mark.typing .dot:nth-of-type(3) { animation-delay: .3s; }
@keyframes mh-typing { 0%, 60%, 100% { transform: translateY(0); opacity: .4; } 30% { transform: translateY(-2.5px); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .mark.typing .dot { animation: none; } }
.loading-mark { display: grid; justify-items: center; gap: var(--s3); color: var(--ink-2); }
.loading-mark .mark { color: var(--accent); }

/* ---------- buttons and inputs ---------- */
.btn {
  min-height: var(--tap); padding: 0 var(--s5); border-radius: var(--r-pill);
  font-size: var(--t-small); font-weight: 600; border: 1px solid transparent; background: none;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2); white-space: nowrap;
  transition: background var(--fast) var(--ease), opacity var(--fast) var(--ease);
}
.btn.fill { background: var(--accent-fill); color: var(--on-accent); }
.btn.line { border-color: var(--accent); color: var(--accent); }
.btn.plain { color: var(--ink); border-color: var(--line); }
.btn.wide { width: 100%; }
.btn:disabled { opacity: .5; cursor: default; }
.btn.small { min-height: 32px; padding: 0 var(--s3); font-size: var(--t-meta); }
/* Small buttons keep a 44 px hit area around them. */
.btn.small { position: relative; }
.btn.small::after { content: ""; position: absolute; inset: -6px -2px; }

.icon-btn {
  width: var(--tap); height: var(--tap); border-radius: var(--r-pill); border: 0; background: none;
  display: inline-flex; align-items: center; justify-content: center; color: var(--ink); flex: none;
  transition: background var(--fast) var(--ease);
}

.input {
  width: 100%; min-height: var(--tap); padding: var(--s2) var(--s4);
  border: 1px solid var(--line); border-radius: var(--r-card); background: var(--card);
}
.input { font-size: var(--t-body); }
.input::placeholder { color: var(--ink-2); }
.input { caret-color: var(--accent); }
.input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent; }

.label { display: block; font-size: var(--t-meta); font-weight: 600; color: var(--ink-2); margin: 0 0 var(--s1); }
.help { color: var(--ink-2); }
.note, .row-help { color: var(--ink-2); font-size: var(--t-small); }
.error { color: var(--ink); font-size: var(--t-small); font-weight: 600; display: flex; gap: var(--s2); align-items: flex-start; }
.error .icon { margin-top: 1px; }

/* ---------- full screens (pairing, revoked) ---------- */
.screen { min-height: 100%; display: grid; place-items: center; padding: var(--s6) var(--s4); }
.screen-card {
  width: 100%; max-width: 440px; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: var(--s6); display: grid; gap: var(--s4);
}
.screen-card h1 { font-size: var(--t-body); font-weight: 600; }
.screen-card .input { font-size: var(--t-body); letter-spacing: .04em; text-transform: uppercase; }
/* Paused screen and Settings, Subscription: the link to Stripe shows here in the desktop apps only. */
#paused-out:empty, #sub-out:empty { display: none; }
#paused-title:focus { outline: none; }

/* ---------- app shell ---------- */
#app { height: 100vh; height: 100dvh; display: flex; flex-direction: column; }
.bar {
  height: var(--bar); flex: none; display: flex; align-items: center; gap: var(--s3);
  padding: 0 var(--s2) 0 var(--s4); width: 100%; max-width: var(--max); margin: 0 auto;
}
.bar .wordmark { margin-right: auto; }
.pill {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: 32px; padding: 0 var(--s3);
  border: 1px solid var(--line); border-radius: var(--r-pill); font-size: var(--t-meta); font-weight: 600;
  color: var(--ink-2); background: var(--card); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 60vw;
}
.pill .dot { width: 8px; height: 8px; border-radius: var(--r-pill); border: 1.5px solid var(--ink); flex: none; }
.pill.on .dot { background: var(--ink); }
.pill.on { color: var(--ink); }
/* Phone status colors (Chris 2026-10-02, the one exception to "no red, one accent"): green when the
   phone is connected, red when it is offline. The words always say it too. Nowhere else. */
.pill.ok { color: var(--ok); background: var(--ok-wash); border-color: var(--ok); }
.pill.ok .dot { background: var(--ok); border-color: var(--ok); }
.pill.bad { color: var(--bad); background: var(--bad-wash); border-color: var(--bad); }
.pill.bad .dot { border-color: var(--bad); }

.panes {
  flex: 1; min-height: 0; width: 100%; max-width: var(--max); margin: 0 auto;
  display: grid; grid-template-columns: var(--list) 1fr; gap: var(--s3); padding: 0 var(--s3) var(--s3);
}
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-card); }
.pane { min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.pane-head {
  min-height: 56px; display: flex; align-items: center; gap: var(--s2);
  padding: var(--s1) var(--s2) var(--s1) var(--s4); border-bottom: 1px solid var(--line); flex: none;
}
.pane-title { font-size: var(--t-body); font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.convo-head { padding-left: var(--s2); }
.convo-head .back { display: none; }
.convo-h { flex: 1; min-width: 0; font: inherit; }
/* Name and number together are one button that copies the number (44 px+ tall). */
.convo-title {
  display: grid; width: 100%; min-height: var(--tap); padding: var(--s1) var(--s2); margin-left: calc(var(--s1) * -1);
  border: 0; background: none; text-align: left; border-radius: var(--r-row); justify-items: start; align-content: center;
  transition: background var(--fast) var(--ease);
}
.convo-title:disabled { cursor: default; }
.convo-title .pane-title { max-width: 100%; }
.convo-head > .avatar { margin-left: var(--s2); }
.sub { font-size: var(--t-meta); font-weight: 400; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* ---------- banners ---------- */
.banner {
  margin: var(--s3); padding: var(--s3) var(--s4); border: 1px solid var(--line); border-radius: var(--r-card);
  display: grid; gap: var(--s2); font-size: var(--t-small);
}
.banner strong { font-weight: 600; font-size: var(--t-small); }
.banner p { color: var(--ink-2); }
.banner .actions { justify-content: flex-start; }
/* One line of text and its button side by side, so the banner stays short. */
.banner.row-banner { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.banner.row-banner .btn { flex: none; }
.convo-note { padding: var(--s2) var(--s4); font-size: var(--t-small); color: var(--ink-2); border-top: 1px solid var(--line); }

/* ---------- list header: "Mark all read" is a plain text button (it was a one-item menu) ---------- */
.list-head { gap: 0; }
.text-btn {
  min-height: var(--tap); padding: 0 var(--s3); border: 0; border-radius: var(--r-pill); background: none;
  font-size: var(--t-small); font-weight: 600; color: var(--accent); white-space: nowrap; flex: none;
}
.text-btn[aria-disabled="true"] { color: var(--ink-3); cursor: default; }

/* ---------- search: a filled field with the magnifier, like iMessage ---------- */
.search-row { padding: 0 var(--s3) var(--s2); flex: none; }
/* The label is the 44 px target; the filled field inside it is 36 px. */
.search-field { display: flex; align-items: center; min-height: var(--tap); cursor: text; position: relative; }
.search-icon { position: absolute; left: var(--s3); color: var(--ink-2); pointer-events: none; }
.search {
  width: 100%; height: 36px; border: 0; border-radius: var(--r-row); background: var(--wash);
  padding: 0 var(--s3) 0 34px; font-size: 16px; caret-color: var(--accent);
}
.search::placeholder { color: var(--ink-2); }
.search:focus { outline: 2px solid var(--accent); outline-offset: 0; }
.search::-webkit-search-cancel-button { cursor: pointer; }

/* ---------- thread list (iMessage rows, DESIGN.md "Thread list") ---------- */
.threads {
  list-style: none; margin: 0; padding: 0 var(--s2) var(--s2); overflow-y: auto; flex: 1;
}
.threads > li { position: relative; }
/* Inset hairline under each row: from the text column to the right padding. Not next to the
   selected or hovered row, whose filled shape needs no edge. */
.threads > li:not(.empty)::after {
  content: ""; position: absolute; left: calc(var(--s3) + 48px + var(--s3)); right: var(--s3); bottom: 0;
  height: 1px; background: var(--line); pointer-events: none;
}
.threads > li:last-child::after,
.threads > li.sel::after, .threads > li:has(+ li.sel)::after { display: none; }
.thread {
  width: 100%; display: grid; grid-template-columns: 48px 1fr; gap: 0 var(--s3); align-items: center;
  padding: var(--s3); background: none; border: 0; text-align: left; min-height: 72px;
  border-radius: var(--r-row); position: relative; color: var(--ink);
  transition: background var(--fast) var(--ease);
}
.thread:focus-visible { outline-offset: -2px; }
/* The open thread: a filled accent pill, all text white (white on #2c6bed is 4.7:1 in both themes). */
.thread[aria-current="true"], .thread[aria-current="true"]:hover { background: var(--accent-fill); color: var(--on-accent); }
.thread[aria-current="true"] .time, .thread[aria-current="true"] .preview { color: var(--on-accent); }
.thread[aria-current="true"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Unread: a dot in the left gutter, next to the avatar. */
.udot { position: absolute; left: 1px; top: 50%; width: 9px; height: 9px; margin-top: -4.5px; border-radius: var(--r-pill); }
.thread.unread .udot { background: var(--accent); }
.thread[aria-current="true"].unread .udot { background: var(--on-accent); }
.avatar {
  width: 48px; height: 48px; border-radius: var(--r-pill); background: var(--wash); color: var(--ink-2);
  display: grid; place-items: center; font-weight: 600; font-size: var(--t-body); letter-spacing: .02em; flex: none;
}
.avatar.hue-1 { background: var(--av1); color: var(--av1-ink); }
.avatar.hue-2 { background: var(--av2); color: var(--av2-ink); }
.avatar.hue-3 { background: var(--av3); color: var(--av3-ink); }
.avatar.hue-4 { background: var(--av4); color: var(--av4-ink); }
.avatar.hue-5 { background: var(--av5); color: var(--av5-ink); }
.avatar.hue-6 { background: var(--av6); color: var(--av6-ink); }
.avatar.small { width: 32px; height: 32px; font-size: var(--t-meta); }
.avatar.small .icon { width: 18px; height: 18px; }
.thread .row { min-width: 0; display: grid; gap: 2px; align-self: center; }
.thread .top { display: flex; align-items: baseline; gap: var(--s2); min-width: 0; }
.thread .name { flex: 1; min-width: 0; font-size: var(--t-body); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread .time { flex: none; font-size: var(--t-small); font-weight: 400; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.thread .preview {
  font-size: var(--t-small); line-height: 1.35; color: var(--ink-2); min-width: 0;
  display: flex; align-items: flex-start; gap: var(--s1);
}
.thread .preview .icon { margin-top: 2px; }
.thread .ptext strong { font-weight: 700; color: var(--ink); }
.thread[aria-current="true"] .ptext strong { color: var(--on-accent); }
.thread .ptext {
  overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
  overflow-wrap: anywhere; min-height: calc(2 * 1.35em);
}
.thread.unread .name { font-weight: 700; }
.thread.unread .preview { color: var(--ink); }
.thread[aria-current="true"].unread .preview { color: var(--on-accent); }
.empty { padding: var(--s6) var(--s4); color: var(--ink-2); text-align: center; font-size: var(--t-small); }

/* ---------- conversation ---------- */
.messages {
  flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: var(--s2) var(--s4) var(--s4); display: flex; flex-direction: column;
}
/* Short threads sit at the bottom, next to the composer, like every messenger. */
.messages::before { content: ""; flex: 1 0 auto; }
.messages:focus { outline: none; }
/* Holds the message list and the jump-to-newest button that floats over its bottom edge. */
.msgs-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }
.to-bottom {
  position: absolute; right: var(--s4); bottom: var(--s3); width: var(--tap); height: var(--tap);
  border-radius: var(--r-pill); border: 1px solid var(--line); background: var(--card); color: var(--ink);
  display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
}
.to-bottom[hidden] { display: none; }
/* Time separator above a run more than an hour after the one before: "Today 10:25 AM". */
.stamp { align-self: center; text-align: center; font-size: var(--t-meta); color: var(--ink-3); margin: var(--s5) 0 var(--s2); font-variant-numeric: tabular-nums; }
.stamp strong { font-weight: 600; }
.msg { display: flex; flex-direction: column; max-width: min(75%, 520px); position: relative; }
/* Runs from one side sit 2 px apart; a new run starts 12 px down. */
.msg.first { margin-top: var(--s3); }
.msg.cont { margin-top: 2px; }
/* Room for the reaction badge that sits on the bubble's top corner. */
.msg.reacted { margin-top: var(--s4); }
.stamp + .msg { margin-top: 0; }
.msg.in { align-self: flex-start; align-items: flex-start; }
.msg.out { align-self: flex-end; align-items: flex-end; }
.msg.new { animation: slide var(--fast) var(--ease); }
@keyframes slide { from { transform: translateY(6px); opacity: 0; } to { transform: none; opacity: 1; } }
.bw { position: relative; max-width: 100%; display: flex; flex-direction: column; align-items: inherit; }
.bubble {
  padding: var(--s2) var(--s3); border-radius: var(--r-bubble); white-space: pre-wrap; overflow-wrap: anywhere;
  font-size: var(--t-body); line-height: var(--lh);
}
.msg.in .bubble { background: var(--wash); }
.msg.out .bubble { background: var(--accent-fill); color: var(--on-accent); }
.bubble a { color: inherit; text-decoration: underline; text-underline-offset: 2px; overflow-wrap: anywhere; }
.msg.in .bubble a { color: var(--accent); }
/* Only the last bubble of a run has the tail corner. */
.msg.in.tail .bubble, .msg.in.tail .otp, .msg.in.tail .code-btn { border-bottom-left-radius: var(--r-tail); }
.msg.out.tail .bubble { border-bottom-right-radius: var(--r-tail); }
/* Not yet with the phone: outlined, full-contrast ink text (no opacity). Cancelled: hairline, secondary ink. */
.msg.pending .bubble { background: var(--card); color: var(--ink); border: 1px solid var(--accent); padding: calc(var(--s2) - 1px) calc(var(--s3) - 1px); }
.msg.muted .bubble { background: var(--card); color: var(--ink-2); border: 1px solid var(--line); padding: calc(var(--s2) - 1px) calc(var(--s3) - 1px); }
.otp {
  background: var(--wash); border-radius: var(--r-bubble);
  padding: var(--s3) var(--s4); display: grid; gap: var(--s2); justify-items: start;
}
.otp-code { font-size: 28px; font-weight: 600; line-height: 1.2; letter-spacing: .08em; font-variant-numeric: tabular-nums; user-select: all; }
.otp-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.otp-actions { justify-content: flex-start; }
.link {
  background: none; border: 0; padding: 0; min-height: 32px; color: var(--accent); font-size: var(--t-small); font-weight: 600;
  text-decoration: underline; text-underline-offset: 3px; position: relative;
}
.link::after { content: ""; position: absolute; inset: -6px -4px; }
.code-btn {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: var(--tap); padding: 0 var(--s4);
  border-radius: var(--r-bubble); border: 1px dashed var(--ink-3);
  background: var(--wash); font-weight: 600;
}
.meta { font-size: var(--t-meta); font-weight: 400; color: var(--ink-3); margin: 2px var(--s1) 0; display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; justify-content: flex-end; }
.msg.in .meta { justify-content: flex-start; }
.meta .problem { color: var(--ink); display: inline-flex; align-items: center; gap: var(--s1); }
.meta .status { color: var(--ink-3); }
.meta .actions { gap: var(--s1); }
.meta.quiet { margin: 0; }
.call { align-self: center; display: flex; align-items: center; gap: var(--s2); font-size: var(--t-small); color: var(--ink-2); padding: var(--s2) var(--s3); border: 1px solid var(--line); border-radius: var(--r-pill); margin: var(--s3) 0 var(--s1); }
.call .icon { width: 16px; height: 16px; }
/* Tapbacks: emoji on the bubble's top corner (away from the tail), like iMessage. */
.reacts {
  position: absolute; top: -14px; display: flex; gap: 2px; padding: 1px 6px; z-index: 1;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-pill);
  font-size: 13px; line-height: 20px; white-space: nowrap; pointer-events: none;
}
.msg.in .reacts { right: -10px; }
.msg.out .reacts { left: -10px; }
/* A reaction to a text we cannot find: one quiet line, not a bubble. */
.msg.note { max-width: 85%; }
.tapnote { font-size: var(--t-small); color: var(--ink-2); padding: 2px var(--s1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
/* Per-message time and Copy: beside the bubble on hover, under it after a tap. */
.aside { display: none; align-items: center; gap: var(--s1); font-size: var(--t-meta); color: var(--ink-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
.msg.show .bw > .aside { display: flex; margin: 2px var(--s1) 0; }
.mini {
  position: relative; min-height: 28px; padding: 0 var(--s3); border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--card); color: var(--ink); font-size: var(--t-meta); font-weight: 600;
}
.mini::after { content: ""; position: absolute; inset: -8px -4px; }
.hidden-note { align-self: center; font-size: var(--t-small); color: var(--ink-2); padding: var(--s2) 0; }

.composer { display: flex; padding: var(--s2) var(--s3) calc(var(--s2) + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--line); flex: none; }
/* Filled at rest, a 2 px accent ring on focus. 44 px tall exactly: 22 px line + 11 px padding. */
.compose-box {
  flex: 1; min-width: 0; display: flex; align-items: flex-end; background: var(--wash); border-radius: 22px;
}
.compose-box:focus-within { outline: 2px solid var(--accent); outline-offset: 0; }
.compose {
  flex: 1; min-width: 0; height: var(--tap); max-height: 160px; resize: none; border: 0; background: none;
  font-size: 16px; line-height: 22px; padding: 11px var(--s1) 11px var(--s4); caret-color: var(--accent);
}
.compose::placeholder { color: var(--ink-2); }
.compose:focus, .compose:focus-visible { outline: none; }
.compose:disabled { cursor: not-allowed; }
/* 32 px circle inside the field, 44 px hit area. Always there; grey and inert when there is nothing to send. */
.send-btn {
  position: relative; width: 32px; height: 32px; margin: 6px; border-radius: var(--r-pill); border: 0; flex: none;
  background: var(--accent-fill); color: var(--on-accent); display: grid; place-items: center;
  transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.send-btn::after { content: ""; position: absolute; inset: -6px; }
.send-btn:disabled { background: var(--ink-3); color: var(--card); cursor: default; }
.send-btn .icon { width: 18px; height: 18px; }

.to-row { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line); display: grid; gap: var(--s1); flex: none; }
.to-results { list-style: none; margin: var(--s2) 0 0; padding: 0; max-height: 50vh; overflow-y: auto; }
.to-results button {
  width: 100%; min-height: var(--tap); display: flex; align-items: center; gap: var(--s3); text-align: left;
  padding: var(--s2) var(--s2); border: 0; background: none; border-radius: var(--r-card);
}
.to-results .sub { font-weight: 400; }
.to-results .row-title { font-weight: 500; }

/* ---------- dialogs ---------- */
.dialog {
  width: min(480px, calc(100vw - 32px)); max-height: calc(100vh - 48px); padding: 0; border: 1px solid var(--line);
  border-radius: var(--r-card); background: var(--card); color: var(--ink); box-shadow: var(--shadow-dialog);
}
.dialog.small { width: min(400px, calc(100vw - 32px)); }
.dialog::backdrop { background: color-mix(in srgb, var(--canvas) 75%, transparent); }
.dialog-head { display: flex; align-items: center; padding: var(--s2) var(--s2) var(--s2) var(--s5); border-bottom: 1px solid var(--line); }
.dialog-head h2 { flex: 1; font-size: var(--t-body); font-weight: 600; }
.dialog-body { padding: var(--s5); display: grid; gap: var(--s4); }
.dialog-body h2 { font-size: var(--t-body); font-weight: 600; }
.set { display: grid; gap: var(--s3); }
.set + .set { border-top: 1px solid var(--line); padding-top: var(--s4); }
.set h3 { font-size: var(--t-meta); font-weight: 600; color: var(--ink-2); text-transform: uppercase; letter-spacing: .04em; }
.devices { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.device { display: flex; align-items: center; gap: var(--s3); min-height: var(--tap); }
.device > div { flex: 1; min-width: 0; }
.device .row-title { display: block; font-weight: 600; }
.row-toggle { display: flex; align-items: center; gap: var(--s4); justify-content: space-between; min-height: var(--tap); }
.row-toggle > .btn { flex: none; }
.row-toggle .switch[hidden] { display: none; }
/* Desktop app downloads: square tiles with the OS mark (Settings and the homepage). */
.os-row { display: grid; gap: var(--s3); }
.os-tiles { display: flex; gap: var(--s3); flex-wrap: wrap; }
.os-tile {
  width: 128px; aspect-ratio: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s2); border: 1px solid var(--line); border-radius: var(--r-card); background: var(--wash); color: var(--ink);
  text-decoration: none; transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.os-tile .icon { color: var(--accent); }
.os-tile .os-name { font-weight: 600; font-size: var(--t-small); }
.os-tile .os-sub { font-size: var(--t-meta); color: var(--ink-2); }
.os-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (hover: hover) { .os-tile:hover { border-color: var(--accent); } }
.row-title { display: block; font-weight: 600; }
.row-help { display: block; }
.actions { display: flex; gap: var(--s2); flex-wrap: wrap; justify-content: flex-end; }

.switch {
  appearance: none; -webkit-appearance: none; width: 48px; height: 28px; border-radius: var(--r-pill); flex: none;
  border: 1px solid var(--line); background: var(--wash); position: relative; cursor: pointer; margin: 8px 0;
  transition: background var(--fast) var(--ease);
}
.switch::before {
  content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: var(--r-pill);
  background: var(--card); border: 1px solid var(--line); transition: transform var(--fast) var(--ease);
}
.switch:checked { background: var(--accent-fill); border-color: var(--accent-fill); }
.switch:checked::before { transform: translateX(20px); border-color: var(--accent-fill); }

.seg { display: inline-flex; border: 1px solid var(--line); border-radius: var(--r-pill); padding: 3px; gap: 2px; justify-self: start; }
.seg label { position: relative; }
.seg input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.seg span { display: inline-flex; align-items: center; min-height: 38px; padding: 0 var(--s4); border-radius: var(--r-pill); font-size: var(--t-small); font-weight: 600; color: var(--ink-2); }
.seg input:checked + span { background: var(--wash); color: var(--ink); }
.seg input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }

.code-box {
  font-size: 28px; font-weight: 600; letter-spacing: .04em; line-height: 1.5; text-align: center; overflow-wrap: normal;
  padding: var(--s4); border: 1px solid var(--line); border-radius: var(--r-card); background: var(--canvas); user-select: all;
}
.code-box .grp { white-space: nowrap; }
/* Add a computer: the QR on a white card (scans in dark mode too), the code with a copy button beside it. */
.qr-wrap { display: flex; justify-content: center; }
.qr-wrap .qr { border-radius: var(--r-row); border: 1px solid var(--line); max-width: 100%; height: auto; }
.code-row { display: flex; align-items: center; justify-content: center; gap: var(--s2); }
.code-row .code-box { margin: 0; min-width: 0; }
.code-row .code-copy { flex: none; }
@media (max-width: 479.98px) { .code-box { font-size: 22px; } }
.countdown { text-align: center; font-size: var(--t-small); color: var(--ink-2); font-weight: 600; }
.warn { display: flex; gap: var(--s3); align-items: flex-start; font-size: var(--t-small); color: var(--ink); }
.warn .icon { width: 20px; height: 20px; margin-top: 1px; }

/* API keys (spec 13): name, access choice, the key once, the command to run. */
.api-form { display: grid; gap: var(--s4); }
.choices { border: 0; margin: 0; padding: 0; min-width: 0; display: grid; gap: var(--s1); }
.choices legend { padding: 0; }
.choice { display: flex; gap: var(--s3); align-items: flex-start; min-height: var(--tap); padding: var(--s2) 0; cursor: pointer; }
.choice input { flex: none; width: 20px; height: 20px; margin: 2px 0 0; accent-color: var(--accent-fill); cursor: pointer; }
.choice > span { min-width: 0; }
.cmd {
  font-size: var(--t-small); line-height: 1.5; padding: var(--s3); border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--canvas); overflow-wrap: anywhere; user-select: all;
}
.actions.center { justify-content: center; }

.toast {
  position: fixed; left: 50%; bottom: var(--s6); transform: translateX(-50%); max-width: calc(100vw - 32px);
  background: var(--ink); color: var(--card); padding: var(--s3) var(--s5); border-radius: var(--r-pill);
  font-size: var(--t-small); font-weight: 600; z-index: 20;
}

/* ---------- hover (only where there is a pointer that hovers) and pressed ---------- */
@media (hover: hover) {
  .btn.fill:hover { opacity: .9; }
  .btn.line:hover, .btn.plain:hover { background: var(--wash); }
  .icon-btn:hover { background: var(--wash); }
  .text-btn:not([aria-disabled="true"]):hover { background: var(--wash); }
  .thread:hover { background: color-mix(in srgb, var(--wash) 60%, var(--card)); }
  .threads > li:hover::after, .threads > li:has(+ li:hover)::after { display: none; }
  .to-results button:hover { background: var(--wash); }
  .convo-title:not(:disabled):hover { background: var(--wash); }
  .mini:hover { background: var(--wash); }
  /* Time and actions beside the bubble while the pointer is on it. The strip runs the full height of
     the message (padding bridges the gap), so sliding out sideways from anywhere on it keeps them up. */
  .msg:not(.show) .bw:hover > .aside { display: flex; position: absolute; top: 0; bottom: 0; align-items: flex-start; padding-top: 3px; }
  .msg.in:not(.show) .bw:hover > .aside { left: 100%; padding-left: var(--s2); }
  .msg.out:not(.show) .bw:hover > .aside { right: 100%; padding-right: var(--s2); flex-direction: row-reverse; }
}
.btn:active, .icon-btn:active, .text-btn:not([aria-disabled="true"]):active, .to-results button:active, .convo-title:not(:disabled):active, .mini:active {
  background: color-mix(in srgb, var(--ink) 10%, var(--wash));
}
.btn.fill:active, .send-btn:not(:disabled):active { opacity: 1; transform: scale(.96); }
.thread:active { background: color-mix(in srgb, var(--ink) 6%, var(--wash)); }
.thread[aria-current="true"]:active { background: var(--accent-fill); }

/* ---------- one pane under 720 px ---------- */
@media (max-width: 719.98px) {
  .panes { grid-template-columns: 1fr; padding: 0; gap: 0; }
  .card { border-radius: 0; border-left: 0; border-right: 0; border-bottom: 0; }
  .bar { padding-left: var(--s4); }
  body:not(.in-convo) #convo-pane { display: none; }
  body.in-convo #list-pane { display: none; }
  .convo-head .back { display: inline-flex; }
  .convo-head > .avatar { margin-left: 0; }
  .msg { max-width: 85%; }
  .pill { max-width: 52vw; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---------- pictures and group texts (spec 4a) ---------- */
.composer { flex-direction: column; gap: var(--s2); }
.compose-row { display: flex; align-items: flex-end; gap: var(--s1); }
.attach-btn { flex: none; color: var(--ink-2); }
.attach-btn:disabled { color: var(--ink-3); cursor: not-allowed; }
.attach-preview { display: flex; align-items: center; gap: var(--s2); }
.attach-preview img { width: 64px; height: 64px; object-fit: cover; border-radius: var(--r-card); border: 1px solid var(--line); }
.attach-preview .row-help { flex: 1; min-width: 0; }
.who { font-size: var(--t-meta); color: var(--ink-2); margin: 0 var(--s3) 2px; }
.pics { display: grid; gap: 2px; margin-bottom: 2px; }
.pic {
  display: block; padding: 0; border: 0; background: var(--wash); border-radius: var(--r-bubble); overflow: hidden;
  max-width: min(280px, 70vw); cursor: zoom-in;
}
.pic img { display: block; max-width: 100%; max-height: 320px; height: auto; }
.pic-state {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: var(--tap); padding: var(--s2) var(--s3);
  border-radius: var(--r-bubble); border: 1px dashed var(--ink-3); background: var(--wash); color: var(--ink);
  font-size: var(--t-small); text-align: left; max-width: min(280px, 70vw);
}
button.pic-state { cursor: pointer; font-weight: 600; }
.pic-state .icon { width: 18px; height: 18px; flex: none; }
.picview { width: min(96vw, 1100px); }
.picview img { display: block; max-width: 100%; max-height: 80vh; margin: 0 auto; }
.media { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.pic-video { display: block; max-width: min(280px, 70vw); max-height: 320px; border-radius: var(--r-bubble); background: var(--wash); }
.media audio { max-width: 200px; }
.banner.update.warn { border-color: var(--ink); }

/* ---------- delete from the web (spec 14) ---------- */
.icon-mini { padding: 0 var(--s1); display: inline-flex; align-items: center; justify-content: center; min-width: 26px; }
.icon-mini .icon { width: 16px; height: 16px; }
/* Menu sheet: a list of actions, 12 px rows (DESIGN.md menus). A bottom sheet at phone width. */
.menu .dialog-body { padding: var(--s3); gap: var(--s1); }
.menu-title { font-size: var(--t-small); font-weight: 600; color: var(--ink-2); padding: var(--s1) var(--s3) var(--s2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu-items { display: grid; gap: 2px; }
.menu-item {
  display: flex; align-items: center; gap: var(--s3); width: 100%; min-height: var(--tap); padding: 0 var(--s3);
  border: 0; border-radius: var(--r-row); background: none; color: var(--ink); text-align: left; font-size: var(--t-body);
}
.menu-item .icon { color: var(--ink-2); }
.menu-item.warn { font-weight: 600; }
.menu-item.warn .icon { color: var(--ink); }
.menu-item.cancel { justify-content: center; color: var(--ink-2); margin-top: var(--s1); border-top: 1px solid var(--line); border-radius: 0 0 var(--r-row) var(--r-row); }
.menu-item:active { background: color-mix(in srgb, var(--ink) 10%, var(--wash)); }
@media (hover: hover) { .menu-item:hover { background: var(--wash); } }
@media (max-width: 719.98px) {
  .dialog.menu { width: 100%; max-width: 100%; margin: auto 0 0; border-radius: var(--r-card) var(--r-card) 0 0; padding-bottom: env(safe-area-inset-bottom, 0px); }
}
/* Picking messages: a round check on the side away from the screen edge; picked = accent ring. */
.messages.selecting .msg, .messages.selecting .call { cursor: pointer; }
.messages.selecting .msg * { pointer-events: none; }
.messages.selecting .aside { display: none !important; }
.pick {
  position: absolute; top: 50%; width: 22px; height: 22px; margin-top: -11px; border-radius: var(--r-pill);
  border: 1.5px solid var(--ink-3); background: var(--card); display: grid; place-items: center; color: var(--on-accent);
}
.msg.in > .pick { right: -32px; }
.msg.out > .pick, .call > .pick { left: -32px; }
.call { position: relative; }
.picked > .pick { background: var(--accent-fill); border-color: var(--accent-fill); }
.msg.picked .bw { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-bubble); }
.call.picked { border-color: var(--accent); }
.unpickable { opacity: .55; }
.pickable:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: var(--r-bubble); }
.select-bar {
  display: flex; align-items: center; gap: var(--s2); flex: none; flex-wrap: wrap;
  padding: var(--s2) var(--s3) calc(var(--s2) + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--line);
}
.select-count { flex: 1; min-width: 0; font-size: var(--t-small); font-weight: 600; text-align: center; }
/* Long press on a touch screen opens the menu: no text-selection callout on the bubbles there. */
@media (hover: none) {
  .msg .bubble, .msg .pic img, .thread { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
}
.select-count { white-space: nowrap; }
/* Narrow: the count and Cancel on top, the two deletes side by side under them. */
@media (max-width: 559.98px) {
  .select-count { order: -2; flex: 1 1 calc(100% - 96px); text-align: left; padding: 0 var(--s1); }
  .select-bar #select-cancel { order: -1; }
  .select-bar #select-delete, .select-bar #select-perm { flex: 1 1 auto; }
}

/* ---------- emoji panel (composer smiley button) ---------- */
/* Placed by emoji-picker.js just above the composer (the text stays visible). Wide: a 360 px panel
   over the smiley button. Narrow: full width with square sides, like a sheet. No dimming. */
.emoji-panel {
  position: fixed; margin: 0; width: min(360px, calc(100vw - 16px)); overflow: hidden;
  height: min(440px, calc(100vh - 16px)); max-height: calc(100vh - 16px); /* older WebKitGTK: no dvh */
  height: min(440px, calc(100dvh - 16px)); max-height: calc(100dvh - 16px);
}
.emoji-panel[open] { display: flex; flex-direction: column; }
.emoji-panel::backdrop { background: transparent; }
.emo-search { padding: var(--s2) var(--s3) 0; flex: none; }
.emo-tabs { display: flex; gap: 2px; padding: var(--s1) var(--s2); border-bottom: 1px solid var(--line); flex: none; overflow-x: auto; scrollbar-width: none; }
.emo-tabs::-webkit-scrollbar { display: none; }
.emo-tab {
  flex: 1 0 36px; min-width: 36px; height: var(--tap); border: 0; border-radius: var(--r-row); background: none;
  font-size: 20px; line-height: 1; display: grid; place-items: center; position: relative; opacity: .6;
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", var(--font);
}
.emo-tab[aria-selected="true"] { opacity: 1; background: var(--wash); }
.emo-tab[aria-selected="true"]::after { content: ""; position: absolute; left: 25%; right: 25%; bottom: 3px; height: 2px; border-radius: var(--r-pill); background: var(--accent); }
.emo-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 var(--s2) var(--s2); }
.emo-head {
  position: sticky; top: 0; z-index: 1; background: var(--card);
  font-size: var(--t-meta); font-weight: 600; color: var(--ink-2); padding: var(--s2) var(--s2) var(--s1);
}
.emo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--tap), 1fr)); }
.emo {
  height: var(--tap); min-width: 0; border: 0; border-radius: var(--r-row); background: none; padding: 0;
  font-size: 26px; line-height: 1; display: grid; place-items: center;
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", var(--font);
  /* Off-screen emoji skip layout and paint, so a 400-emoji tab opens at once. */
  content-visibility: auto; contain-intrinsic-size: auto var(--tap) auto var(--tap);
}
.emo:active, .emo-tab:active { background: color-mix(in srgb, var(--ink) 10%, var(--wash)); }
.emo:focus-visible, .emo-tab:focus-visible { outline-offset: -2px; }
.emo-none { padding: var(--s5) var(--s3); color: var(--ink-2); font-size: var(--t-small); text-align: center; }
@media (hover: hover) { .emo:hover, .emo-tab:hover { background: var(--wash); opacity: 1; } }
@media (max-width: 719.98px) {
  .dialog.emoji-panel {
    width: 100%; max-width: 100%; height: min(50vh, 400px); height: min(50dvh, 400px);
    border-left: 0; border-right: 0; border-radius: var(--r-card) var(--r-card) 0 0;
  }
}
