/* Pulse — light, bold, simple. */
* { margin: 0; padding: 0; box-sizing: border-box; }
[hidden] { display: none !important; }
:root {
  --bg: #ffffff;
  --bg-soft: #f5f5f7;
  /* Page backdrop behind the cards — distinct from --bg-soft, which also
     tints small surfaces (pills, secondary buttons, code). */
  --page: #d3f2ec;
  --ink: #111114;
  --ink-2: #5c5c66;
  --ink-3: #9a9aa4;
  --ink-inv: #ffffff;
  --line: #ececf0;
  --glass: rgba(255, 255, 255, 0.92);
  --accent: #6C5CE7;
  --good: #00B894;
  --warn: #E17055;
  --card-shadow: 0 1px 2px rgba(17, 17, 20, 0.04), 0 6px 20px rgba(17, 17, 20, 0.05);
  --radius: 20px;
  --radius-s: 12px;
  --maxw: 600px;
  --tabbar-h: 62px;
  font-size: 16px;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #17171c;
    --bg-soft: #0f0f13;
    --page: #0b2429;
    --ink: #f2f2f7;
    --ink-2: #a3a3af;
    --ink-3: #6e6e7a;
    --ink-inv: #111114;
    --line: #26262e;
    --glass: rgba(23, 23, 28, 0.92);
    --card-shadow: none;
  }
}
html { scroll-behavior: smooth; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  background: var(--page);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}
a { color: inherit; text-decoration: none; }
button { font: inherit; border: 0; background: none; cursor: pointer; color: inherit; }
input, textarea, select {
  font: inherit; color: var(--ink); background: var(--bg);
  border: 2px solid var(--line); border-radius: var(--radius-s);
  padding: 10px 12px; width: 100%;
}
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--ink); }
label { font-size: 0.8rem; font-weight: 700; color: var(--ink-2); display: block; margin: 12px 0 4px; }

/* ---------- layout ---------- */
#topbar {
  position: sticky; top: 0; z-index: 20;
  background: var(--glass); backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
.topbar-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 12px 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 54px;
}
.brand { font-weight: 900; font-size: 1.35rem; letter-spacing: -0.02em; display: flex; align-items: center; gap: 8px; }
.brand-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent); display: inline-block; }
/* Actions take the space left over from the brand; a wide action (the filter
   chip) shrinks to fit rather than pushing the bar out of shape. */
#topbar-actions { display: flex; gap: 8px; align-items: center; flex: 1; min-width: 0; justify-content: flex-end; }
#topbar-actions > * { flex: none; }

.view {
  max-width: var(--maxw); margin: 0 auto;
  padding: 16px 16px calc(var(--tabbar-h) + 32px + env(safe-area-inset-bottom));
  min-height: 70vh;
}

#tabbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
  background: var(--glass); backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
  display: flex; justify-content: center; gap: 4px;
  padding-bottom: env(safe-area-inset-bottom);
}
.tab {
  flex: 1; max-width: 110px; height: var(--tabbar-h);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  color: var(--ink-3); font-size: 0.68rem; font-weight: 700; position: relative;
}
.tab svg { width: 24px; height: 24px; fill: currentColor; stroke: none; }
.tab.active { color: var(--ink); }
.tab-badge {
  position: absolute; top: 8px; right: calc(50% - 20px);
  background: var(--warn); color: #fff; font-size: 0.62rem; font-weight: 800;
  min-width: 17px; height: 17px; border-radius: 9px; display: flex; align-items: center; justify-content: center; padding: 0 4px;
}

/* ---------- generic ---------- */
.card {
  background: var(--bg); border-radius: var(--radius);
  border: 1px solid var(--line); overflow: hidden; margin-bottom: 16px;
  box-shadow: var(--card-shadow);
}
.section-title { font-size: 1.5rem; font-weight: 900; letter-spacing: -0.02em; margin: 8px 0 16px; }
.muted { color: var(--ink-2); }
.tiny { font-size: 0.75rem; color: var(--ink-3); font-weight: 600; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--bg-soft); border-radius: 999px; padding: 5px 12px;
  font-size: 0.78rem; font-weight: 700; color: var(--ink-2);
}
a.chip:active { transform: scale(0.97); }
.pill {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 0.68rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em;
}
.pill.active { background: #e3f8f1; color: #00785f; }
.pill.idea { background: #f0edff; color: #5b4bd6; }
.pill.paused { background: #fff3e0; color: #b06000; }
.pill.shipped { background: #e3f2fd; color: #0b63b5; }
.pill.archived { background: #f0f0f3; color: var(--ink-2); }
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--ink); color: var(--ink-inv); font-weight: 800;
  border-radius: 999px; padding: 12px 22px; font-size: 0.92rem;
}
.btn:active { transform: scale(0.98); }
.btn.secondary { background: var(--bg-soft); color: var(--ink); }
.btn.small { padding: 8px 14px; font-size: 0.8rem; }
.btn.danger { background: #fdecec; color: #c0392b; }
.icon-btn {
  width: 38px; height: 38px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--bg-soft); font-size: 1.1rem; font-weight: 800;
}
/* Shared inline SVG icon — scales with its text; context rules (tabbar, sheet,
   post-menu…) that target `svg` still win by specificity. */
.ic { width: 1.05em; height: 1.05em; vertical-align: -0.15em; flex: none; }
.icon-btn .ic { width: 22px; height: 22px; }
h4 .ic { margin-right: 5px; }
.hero-actions .btn .ic, .article-actions .btn .ic { margin-right: 2px; }
.empty {
  text-align: center; padding: 56px 24px; color: var(--ink-2);
}
.empty .big { font-size: 2.6rem; margin-bottom: 12px; }
.empty h3 { font-weight: 800; color: var(--ink); margin-bottom: 6px; }
.empty p { font-size: 0.9rem; max-width: 420px; margin: 0 auto; line-height: 1.5; }

/* ---------- markdown ---------- */
.md { line-height: 1.6; font-size: 0.95rem; }
.md p, .md ul, .md ol, .md blockquote, .md pre { margin-bottom: 10px; }
.md p:last-child, .md ul:last-child, .md pre:last-child { margin-bottom: 0; }
.md h3, .md h4, .md h5, .md h6 { margin: 14px 0 6px; font-weight: 800; letter-spacing: -0.01em; }
.md ul, .md ol { padding-left: 22px; }
.md code { background: var(--bg-soft); border-radius: 6px; padding: 2px 6px; font-size: 0.85em; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.md pre { background: #16161c; color: #f2f2f7; border-radius: var(--radius-s); padding: 14px; overflow-x: auto; }
.md pre code { background: none; color: inherit; padding: 0; }
.md a { color: var(--accent); font-weight: 700; }
.md blockquote { border-left: 4px solid var(--line); padding-left: 12px; color: var(--ink-2); }
.md hr { border: 0; border-top: 1px solid var(--line); margin: 14px 0; }
.md img { max-width: 100%; border-radius: 12px; display: block; }

/* tables — the wrapper scrolls so a wide table never widens the article */
.md-table-wrap {
  margin: 12px 0; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line); border-radius: var(--radius-s);
}
.md-table { border-collapse: collapse; width: 100%; font-size: 0.86rem; line-height: 1.45; }
.md-table th, .md-table td { padding: 8px 12px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line); }
.md-table thead th { background: var(--bg-soft); font-weight: 800; color: var(--ink); white-space: nowrap; }
.md-table tbody tr:last-child td { border-bottom: 0; }
.md-table td { color: var(--ink-2); }
.md-table code { white-space: nowrap; }

/* in-article images & albums */
.md-album { display: grid; gap: 4px; margin: 12px 0; grid-template-columns: 1fr 1fr; border-radius: var(--radius-s); overflow: hidden; }
.md-album.n1 { grid-template-columns: 1fr; }
.md-album img, .md-album .img-pending { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 0; cursor: zoom-in; }
.md-album:not(.n1) img, .md-album:not(.n1) .img-pending { aspect-ratio: 1; }
.md-album.n3 > :first-child { grid-column: 1 / -1; aspect-ratio: 2 / 1; }
.md-album figcaption { grid-column: 1 / -1; font-size: 0.8rem; color: var(--ink-3); padding: 6px 4px 0; }
.img-pending {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  background: var(--bg-soft); color: var(--ink-3); border: 1.5px dashed var(--line);
  border-radius: 12px; min-height: 130px; padding: 12px; text-align: center;
  font-size: 0.8rem; font-weight: 600; cursor: default;
}
.img-pending .ico { font-size: 1.7rem; }

/* in-article audio tracks & the post-level "read this article" player */
.md-audio, .md-audio-block { margin: 12px 0; }
.md-audio { display: block; }
.md-audio .cap { display: block; font-size: 0.82rem; font-weight: 700; color: var(--ink-2); margin-bottom: 6px; }
.md-audio audio, .post-audio audio { width: 100%; height: 40px; display: block; }
.md-audio.pending, .post-audio.pending {
  display: flex; align-items: center; gap: 8px;
  background: var(--bg-soft); color: var(--ink-3); border: 1.5px dashed var(--line);
  border-radius: 12px; padding: 12px 14px; font-size: 0.82rem; font-weight: 600;
}
.md-audio.pending .ico, .post-audio.pending .ico { font-size: 1.3rem; }
.post-audio {
  margin: 0 0 12px; padding: 10px 12px; border-radius: var(--radius-s);
  background: var(--bg-soft); border: 1px solid var(--line);
}
.post-body > .post-audio:last-child { margin-bottom: 0; }
.post-audio .cap { display: block; font-size: 0.78rem; font-weight: 800; color: var(--ink-2); margin-bottom: 6px; letter-spacing: .01em; }

/* "see the session this came out of" — the link from an article to the work behind it */
.post-session {
  display: flex; align-items: center; gap: 10px; margin-top: 18px;
  padding: 11px 13px; border: 1px solid var(--line); border-radius: var(--radius-s);
  background: var(--bg-soft); color: var(--ink); text-decoration: none;
}
.post-session:hover { border-color: var(--accent); }
.post-session .ico { display: flex; color: var(--accent); }
.post-session .ic { width: 20px; height: 20px; }
.post-session .txt { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
.post-session .txt b { font-size: 0.88rem; }
.post-session .txt .tiny { color: var(--ink-3); }
.post-session .chev { margin-left: auto; color: var(--ink-3); font-size: 0.95rem; }

/* lightbox */
.lightbox { position: fixed; inset: 0; z-index: 100; background: rgba(10, 10, 14, 0.93); display: flex; align-items: center; justify-content: center; }
.lightbox figure { display: flex; flex-direction: column; align-items: center; gap: 10px; max-width: 96vw; }
.lightbox img { max-width: 96vw; max-height: 84vh; object-fit: contain; border-radius: 8px; }
.lightbox figcaption { color: #eee; font-size: 0.85rem; text-align: center; max-width: 80vw; }
.lightbox button {
  position: absolute; z-index: 1; width: 42px; height: 42px; border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, 0.14); color: #fff; font-size: 1.3rem;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.lb-close { top: max(14px, env(safe-area-inset-top)); right: 14px; }
.lb-prev { left: 10px; top: 50%; transform: translateY(-50%); }
.lb-next { right: 10px; top: 50%; transform: translateY(-50%); }
.lb-count {
  position: absolute; bottom: max(18px, env(safe-area-inset-bottom)); left: 50%; transform: translateX(-50%);
  color: #fff; font-size: 0.8rem; font-weight: 700; opacity: 0.85;
}

/* ---------- feed ---------- */
.feed-group {
  font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-3); margin: 22px 4px 10px;
}
#feed-list > .feed-group:first-child { margin-top: 2px; }
.post-cover { position: relative; aspect-ratio: 16/9; display: block; }
.post-cover img, .post-cover svg { width: 100%; height: 100%; object-fit: cover; display: block; }
.cover-kind {
  position: absolute; top: 12px; left: 12px;
  background: rgba(255,255,255,0.9); backdrop-filter: blur(6px);
  border-radius: 999px; padding: 4px 12px; font-size: 0.68rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.post-body { padding: 16px; }
.post-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.avatar {
  width: 36px; height: 36px; border-radius: 12px; flex: none;
  display: flex; align-items: center; justify-content: center; font-size: 1.15rem;
  position: relative; overflow: hidden;
}
/* Icon-image avatar: a plain white tile (favicons are drawn for a light
   background), with a hairline ring so the tile stays visible on white cards. */
.avatar--img {
  background: #fff;
  box-shadow: inset 0 0 0 1px var(--line);
}
/* Custom icon image (an app's favicon/logo) filling the tile. object-fit: cover
   keeps it crisp at any avatar size; if it fails to load, the onerror handler
   replaces it with the emoji fallback. */
.avatar-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; border-radius: inherit; background: inherit;
}

/* Size variants of the one icon component. Every place that shows a channel,
   project or chat icon picks one of these instead of styling its own glyph, so
   an uploaded icon and an emoji fallback line up wherever they turn up. */
.avatar--sm { width: 28px; height: 28px; border-radius: 9px; font-size: 0.95rem; }
.avatar--xs { width: 24px; height: 24px; border-radius: 8px; font-size: 0.85rem; }
/* A page title that leads with its channel's icon. */
.section-title.with-icon { display: flex; align-items: center; gap: 10px; }

/* Channel/project icon picker in the edit modal: a live avatar preview beside
   the emoji field and the upload/remove controls. */
.icon-picker { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 4px; }
.icon-picker .avatar { width: 56px; height: 56px; border-radius: 16px; font-size: 1.7rem; }
.icon-picker-controls { flex: 1; min-width: 0; }
.icon-picker-controls input { margin: 0; }
.icon-picker-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.icon-picker-actions label.btn { margin: 0; cursor: pointer; }
.icon-picker .tiny { margin-top: 6px; }
.btn.ghost { background: transparent; color: var(--ink-2); }
.post-head .who { flex: 1; min-width: 0; }
.post-head .who b { display: block; font-size: 0.88rem; font-weight: 800; }
.post-title { font-size: 1.22rem; font-weight: 900; letter-spacing: -0.02em; line-height: 1.25; margin-bottom: 8px; }
.post.hero .post-title { font-size: 1.35rem; }
.post-tags { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 6px; }
.post-excerpt {
  font-size: 0.9rem; color: var(--ink-2); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.post-excerpt + .post-audio { margin-top: 12px; }

/* compact card — routine kinds (update, note) in the feed */
.post.compact .post-title { font-size: 1rem; margin-bottom: 4px; }
.post.compact .post-excerpt { font-size: 0.85rem; }
.compact-main { display: flex; gap: 12px; align-items: flex-start; }
.compact-text { flex: 1; min-width: 0; }
.post-thumb {
  flex: none; width: 74px; height: 74px; border-radius: var(--radius-s);
  overflow: hidden; display: block; background: var(--bg-soft);
}
.post-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* course-check strip — one slim row in the feed */
.post.course { border-left: 4px solid var(--good); }
.post.course.drifting { border-left-color: var(--warn); }
.course-strip { display: flex; align-items: center; gap: 12px; padding: 12px 16px; }
.course-strip .ico { font-size: 1.25rem; }
.course-strip .info { flex: 1; min-width: 0; }
.course-strip .info b { display: block; font-size: 0.88rem; font-weight: 800; }
.post.course.on_track .info b { color: #00785f; }
.post.course.drifting .info b { color: #b3491f; }
.course-strip .note {
  display: block; font-size: 0.8rem; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.course-banner {
  display: flex; align-items: center; gap: 10px; padding: 12px 16px;
  font-weight: 800; font-size: 0.9rem;
}
.course-banner.on_track { background: #e3f8f1; color: #00785f; }
.course-banner.drifting { background: #fdeee7; color: #b3491f; }
.feed-loader { text-align: center; padding: 24px; color: var(--ink-3); font-weight: 700; }

/* hero cards go edge-to-edge on phones — covers get the full width */
@media (max-width: 599px) {
  .post.hero {
    margin-left: -16px; margin-right: -16px;
    border-radius: 0; border-left: 0; border-right: 0;
  }
}

/* ---------- projects ---------- */
.project-row {
  display: flex; align-items: center; gap: 14px; padding: 16px;
}
.project-row .avatar { width: 52px; height: 52px; border-radius: 16px; font-size: 1.6rem; }
.project-row .info { flex: 1; min-width: 0; }
.project-row .info b { font-size: 1.05rem; font-weight: 800; display: block; }
.project-row .info .tagline { display: block; font-size: 0.82rem; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.unread-dot { min-width: 20px; height: 20px; border-radius: 10px; background: var(--warn); color: #fff; font-size: 0.7rem; font-weight: 800; display: flex; align-items: center; justify-content: center; padding: 0 6px; }

.project-hero { padding: 24px 16px 16px; text-align: center; }
.project-hero .avatar { width: 84px; height: 84px; border-radius: 26px; font-size: 2.6rem; margin: 0 auto 12px; }
.project-hero h1 { font-size: 1.7rem; font-weight: 900; letter-spacing: -0.02em; }
.project-hero .tagline { color: var(--ink-2); margin-top: 4px; font-size: 0.95rem; }
.hero-actions { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 16px; }
.link-row { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 16px 16px; justify-content: center; }
.detail-section { padding: 16px; border-top: 1px solid var(--line); }
.detail-section h4 {
  font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-3); margin-bottom: 8px;
}
.direction-card {
  margin: 0 16px 16px; padding: 14px 16px; border-radius: var(--radius-s);
  background: linear-gradient(135deg, #f6f4ff, #eef7ff); border: 1px solid #e4defc;
}
.direction-card h4 { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em; color: #5b4bd6; margin-bottom: 6px; }
.tech-chips { display: flex; flex-wrap: wrap; gap: 6px; }

/* ---------- chats ---------- */
.conv-row { display: flex; align-items: center; gap: 12px; padding: 14px 16px; }
.conv-row .info { flex: 1; min-width: 0; }
.conv-row .info b { display: block; font-weight: 800; font-size: 0.95rem; }
.conv-row .last { display: block; font-size: 0.82rem; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-context {
  display: block; width: 100%; text-align: left; margin: 0 0 14px; padding: 11px 13px;
  border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--bg);
  cursor: pointer; box-shadow: var(--card-shadow);
}
.chat-context .cap {
  display: flex; align-items: center; gap: 8px; font-size: 0.7rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-3); margin-bottom: 5px;
}
.chat-context .edit-link { margin-left: auto; color: var(--accent); text-transform: none; letter-spacing: 0; font-weight: 700; }
.chat-context .md { font-size: 0.86rem; line-height: 1.5; color: var(--ink-2); }
.chat-context .md > :last-child { margin-bottom: 0; }
.chat-head { cursor: pointer; }
.chat-head-info { flex: none; color: var(--ink-3); font-size: 1.15rem; }
.kv { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; font-size: 0.88rem; }
.kv + .kv { border-top: 1px solid var(--line); }
.kv .k { color: var(--ink-3); font-weight: 600; flex: none; }
.kv .v { color: var(--ink); text-align: right; overflow: hidden; text-overflow: ellipsis; }
.chat-thread { display: flex; flex-direction: column; gap: 12px; padding-bottom: 76px; }
/* one message = the bubble (nothing but the message itself) + a meta line under
   it carrying the time, the processed state and the reactions */
.msg { display: flex; flex-direction: column; align-items: flex-start; max-width: 86%; align-self: flex-start; }
.msg.user { align-items: flex-end; align-self: flex-end; }
.bubble {
  position: relative; max-width: 100%;
  padding: 10px 14px; border-radius: 18px; font-size: 0.93rem; line-height: 1.5;
  background: var(--bg); border: 1px solid var(--line);
}
.msg.user .bubble { background: var(--ink); color: var(--ink-inv); border: 0; }
.msg.user .bubble .md a, .msg.user .bubble .md code { color: var(--ink-inv); background: rgba(128,128,140,0.25); }
.msg.bot .bubble { border-color: #cdeee6; background: #f2fbf8; }
/* press-and-hold is the message menu, so don't let the OS hijack it with its own
   text-selection UI on a touch screen */
@media (pointer: coarse) {
  .bubble { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
}

/* long messages fold to four lines, fading out at the bottom of the bubble */
.bubble.folded:not(.open) .md {
  max-height: 6em; overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, #000 3.9em, transparent 5.9em);
  mask-image: linear-gradient(to bottom, #000 3.9em, transparent 5.9em);
}
.bubble .expand {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  width: 100%; margin: 2px 0 -4px; padding: 0; cursor: pointer;
  background: none; border: 0; color: inherit; opacity: 0.55; line-height: 1;
}
.bubble .expand .ic { width: 18px; height: 18px; flex: none; }
/* how much the fold is hiding, so it's a choice and not a surprise */
.bubble .expand .ex-label { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.01em; }
.bubble .expand:hover { opacity: 0.9; }
.bubble.open .expand .ic { transform: rotate(180deg); }

/* settled = your message the agent worked through (or you took back), and any
   agent message answering an already-settled stretch. It stays a proper bubble
   — same shape, just quieter and clipped to two lines — with a "✓ Processed"
   note on its meta line. Squashed onto a single line it read as a rendering
   glitch rather than as a message. Tap it to open it back up. */
.msg.settled { max-width: 80%; }
.msg.settled .bubble {
  padding: 7px 12px; border-radius: 15px; font-size: 0.86rem; line-height: 1.45;
  opacity: 0.5; cursor: pointer;
}
/* two lines, then the browser's own ellipsis — enough to recognise which
   message this is without it taking the room of a live one */
.msg.settled .bubble .md {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
/* everything in the preview flattens to plain inline text — a list, a heading or
   a code block drawing its own box would break straight out of the two lines */
.msg.settled .bubble .md * {
  display: inline; margin: 0; padding: 0; border: 0; border-radius: 0;
  background: none; list-style: none; font-size: inherit; font-weight: inherit; color: inherit;
}
/* blocks run together once they're inline — keep the words apart */
.msg.settled .bubble .md * + *::before { content: " "; }
.msg.settled .bubble .md img, .msg.settled .bubble .md hr { display: none; }
.msg.settled .bubble .reply-quote, .msg.settled .bubble .expand { display: none; }
.msg.settled:hover .bubble { opacity: 0.78; }
/* a run of settled messages closes up a little — it's one block of "done" */
.msg.settled + .msg.settled { margin-top: -4px; }
/* the meta line stays, quieter: it's what says this is a message, and a done one */
.msg.settled .meta { margin-top: 2px; font-size: 0.64rem; opacity: 0.8; }
.msg.settled .meta .reaction { font-size: 0.74rem; padding: 1px 3px; }
/* the note already carries the ✓, so the tappable status dot would just repeat
   it — it comes back when you open the message */
.msg.settled .mstat { display: none; }
/* "✓ Processed" under a settled bubble — the whole point of the shrink */
.settled-note { display: none; align-items: center; gap: 3px; font-weight: 800; color: #2e9e6b; }
.settled-note .ic { width: 12px; height: 12px; }
.msg.settled .settled-note { display: inline-flex; }
.msg.settled.retracted .settled-note { color: var(--ink-3); }
/* widen the tail window — the thread only loads its newest messages */
.load-earlier {
  align-self: center; margin-bottom: 4px; padding: 5px 14px; cursor: pointer;
  font-size: 0.72rem; font-weight: 800; color: var(--ink-3);
  background: var(--bg-soft); border: 1px dashed var(--line); border-radius: 999px;
}
.load-earlier:hover { color: var(--ink-2); }
.load-earlier:disabled { opacity: 0.6; cursor: default; }

/* meta line: agent name · time · processed state · reactions */
.msg .meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  margin-top: 4px; padding: 0 4px; font-size: 0.68rem; color: var(--ink-3); min-height: 16px;
}
.msg.user .meta { flex-direction: row-reverse; }
.msg .meta .who { font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em; }
.msg .meta time { font-size: 0.68rem; color: var(--ink-3); }
.chat-input {
  position: fixed; bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); left: 0; right: 0; z-index: 25;
  background: var(--glass); backdrop-filter: blur(12px); border-top: 1px solid var(--line);
}
.chat-input .inner { max-width: var(--maxw); margin: 0 auto; padding: 10px 16px; display: flex; gap: 8px; align-items: flex-end; }
.chat-input textarea { resize: none; max-height: 120px; border-radius: 18px; }
.chat-input .btn { flex: none; height: 44px; }
/* while the on-screen keyboard is up (visualViewport-driven): pin the composer
   just above it and tuck the tab bar out of the way — like a native chat app */
body.kb-open #tabbar { transform: translateY(100%); }
body.kb-open .chat-input { transition: bottom 0.12s ease-out; }
body.kb-open .chat-input .async-note { display: none; }
.async-note { text-align: center; font-size: 0.72rem; color: var(--ink-3); padding: 6px 16px 2px; font-weight: 600; }

/* reply quote inside a bubble — what this message is answering */
.reply-quote {
  display: block; margin-bottom: 6px; padding: 4px 10px; cursor: pointer;
  border-left: 3px solid var(--accent); border-radius: 6px;
  background: var(--bg-soft); font-size: 0.8rem; line-height: 1.35; text-align: left;
}
.reply-quote .rq-who { display: block; font-weight: 800; color: var(--accent); font-size: 0.7rem; }
.reply-quote .rq-text { display: block; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg.user .bubble .reply-quote { background: rgba(128,128,140,0.22); border-left-color: rgba(255,255,255,0.7); }
.msg.user .bubble .reply-quote .rq-who { color: var(--ink-inv); }
.msg.user .bubble .reply-quote .rq-text { color: var(--ink-inv); opacity: 0.8; }

/* emoji reactions (yours + the agent's) — small chips on the meta line. An agent
   reaction only ever appears on a message it marked processed. */
.reaction {
  font-size: 0.86rem; line-height: 1; padding: 2px 4px; border-radius: 7px; cursor: pointer;
  background: var(--bg-soft); border: 1px solid var(--line);
}
.reaction.mine { background: color-mix(in srgb, var(--accent) 18%, transparent); border-color: transparent; }

/* has the agent dealt with this message? Spelled out on every message you sent:
   ○ to process → the agent hasn't touched it, ✓ processed → it worked it through
   (and only then is it allowed to react). */
.mstat {
  display: inline-flex; align-items: center; justify-content: center; padding: 0; cursor: pointer;
  background: none; border: 0; line-height: 0;
}
.mstat .ic { width: 14px; height: 14px; }
.mstat.todo { color: var(--ink-3); opacity: 0.65; }        /* hollow ring — untouched */
.mstat.done { color: #2e9e6b; }                            /* check — the agent worked it through */
.mstat.unsent { color: var(--ink-3); opacity: 0.5; cursor: default; }
.mstat.todo:hover, .mstat.done:hover { opacity: 1; }
/* cancelled sends stay visible to you only — greyed out and dashed */
.msg.retracted .bubble {
  background: var(--bg-soft); color: var(--ink-3); border: 1px dashed var(--line);
  box-shadow: none; opacity: 0.75;
}
.msg.retracted .bubble .md a, .msg.retracted .bubble .md code { color: var(--ink-3); background: none; }
/* hint under the thread about the press-and-hold menu */
.thread-hint { text-align: center; font-size: 0.7rem; color: var(--ink-3); margin: 2px 0 84px; }
/* the quoted message at the top of the per-message action sheet */
.sheet-quote {
  font-size: 0.8rem; color: var(--ink-3); background: var(--bg-soft); border-radius: var(--radius-s);
  padding: 8px 12px; margin-bottom: 8px; overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
/* "still to process" count on a chat row */
.proc-chip {
  display: inline-flex; align-items: center; gap: 2px; font-size: 0.7rem; font-weight: 800;
  color: var(--ink-3); background: var(--bg-soft); border: 1px solid var(--line);
  border-radius: 999px; padding: 1px 8px; white-space: nowrap;
}

/* emoji picker sheet */
.emoji-row { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 4px 4px; justify-content: center; }
.emoji-opt {
  font-size: 1.5rem; line-height: 1; padding: 8px; border-radius: 14px;
  background: var(--bg-soft); border: 1px solid transparent; cursor: pointer;
}
.emoji-opt.on { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 15%, transparent); }
.emoji-opt:active { transform: scale(0.92); }
/* reactions already on the message — tap one to take it off, the agent's included */
.emoji-opt.placed { display: flex; flex-direction: column; align-items: center; gap: 2px; font-size: 1.25rem; padding: 6px 10px; }
.emoji-opt.placed .by { font-size: 0.6rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-3); }
.sheet-cap { font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-3); padding: 6px 4px 0; }

/* how many of my messages the agent hasn't seen yet */
.pending-chip {
  display: inline-block; margin-top: 5px; font-size: 0.72rem; font-weight: 700;
  color: var(--ink-3); background: var(--bg-soft); border: 1px solid var(--line);
  border-radius: 999px; padding: 2px 9px; white-space: nowrap;
}
/* the chat's processed ledger: how many of your messages the agent took, and how
   many are still sitting there */
.pending-strip {
  display: flex; justify-content: center; gap: 8px; flex-wrap: wrap;
  font-size: 0.74rem; font-weight: 800; color: var(--ink-3);
  background: var(--bg-soft); border: 1px dashed var(--line); border-radius: var(--radius-s);
  padding: 7px 12px; margin-bottom: 12px;
}
.pending-strip .ps-todo, .pending-strip .ps-done { display: inline-flex; align-items: center; gap: 5px; }
.pending-strip .ps-todo { color: var(--ink-3); }
.pending-strip .ps-done { color: #2e9e6b; }
.pending-strip .ic { width: 13px; height: 13px; }
/* link-only chat notice + lock indicators */
.private-strip {
  display: flex; align-items: center; gap: 7px; font-size: 0.76rem; font-weight: 600; color: var(--ink-3);
  background: var(--bg-soft); border: 1px solid var(--line); border-radius: var(--radius-s);
  padding: 8px 12px; margin-bottom: 12px;
}
.private-strip svg { width: 15px; height: 15px; flex: none; }
.lock-glyph { display: inline-flex; vertical-align: -2px; color: var(--ink-3); }
.lock-glyph svg { width: 13px; height: 13px; }
/* the "the agent can find this one" counterpart to the lock — quieter, since
   discoverable is the normal state */
.lock-glyph.open { opacity: 0.5; }
/* stacked label + subtext inside a bottom-sheet action */
.sheet-label { display: flex; flex-direction: column; min-width: 0; }
.sheet-sub { display: block; font-size: 0.74rem; font-weight: 500; color: var(--ink-3); margin-top: 2px; }
@keyframes bubbleFlash { 0% { box-shadow: 0 0 0 3px var(--accent); } 100% { box-shadow: 0 0 0 3px transparent; } }
.msg.flash .bubble { animation: bubbleFlash 1.1s ease-out; }

/* ---------- today ---------- */
.plan-date { font-size: 0.8rem; font-weight: 800; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; }
.focus-item { display: flex; gap: 12px; padding: 14px 16px; align-items: flex-start; }
.focus-item .num {
  width: 28px; height: 28px; border-radius: 50%; flex: none; margin-top: 2px;
  background: var(--ink); color: var(--ink-inv); font-weight: 800; font-size: 0.8rem;
  display: flex; align-items: center; justify-content: center;
}
.focus-item .goal { font-weight: 800; font-size: 0.97rem; }
.focus-item .note { font-size: 0.83rem; color: var(--ink-2); margin-top: 2px; line-height: 1.45; }
.focus-item .chip { margin-top: 8px; }

/* ---------- sheet / modal / toast ---------- */
#sheet-backdrop, #modal-backdrop {
  position: fixed; inset: 0; background: rgba(17,17,20,0.4); z-index: 40;
}
#sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 41;
  background: var(--bg); border-radius: 24px 24px 0 0; padding: 8px 16px calc(20px + env(safe-area-inset-bottom));
  max-width: var(--maxw); margin: 0 auto;
  animation: slideup 0.18s ease;
  /* A sheet can hold more rows than a phone screen (every channel, say): the
     head stays put and the body scrolls inside the sheet, keeping that scroll
     to itself so the feed underneath stays where it was. */
  max-height: 82vh; max-height: 82dvh;
  display: flex; flex-direction: column; overflow: hidden;
}
.sheet-body {
  overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  margin: 0 -16px; padding: 0 16px;
}
/* Title (and search box) for a long sheet — pinned above the scrolling rows. */
.sheet-head {
  flex: none; margin: -6px -16px 6px; padding: 2px 16px 10px; border-bottom: 1px solid var(--line);
}
.sheet-head h4 { font-size: 0.78rem; font-weight: 800; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.05em; }
.sheet-search {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  background: var(--bg-soft); border-radius: var(--radius-s); padding: 0 12px; color: var(--ink-3);
}
.sheet-search input {
  flex: 1; border: 0; background: none; padding: 11px 0; font-size: 0.95rem; font-weight: 600;
}
.sheet-search input:focus { outline: none; }
.sheet-none { padding: 22px 8px; text-align: center; color: var(--ink-3); font-weight: 700; font-size: 0.88rem; }
/* The filter picker is the same .chatlist rows as the Channels tab and the
   chats list — only the trimmings differ: a count and a tick instead of a
   chevron, and the row you're filtered to filled in rather than merely ticked
   at the far edge. */
.chan-row .chan-n { font-size: 0.75rem; font-weight: 800; color: var(--ink-3); }
.chan-row.active { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent); }
.chan-row.active .last { color: var(--accent); opacity: 0.75; }
.chan-row .chan-check { color: var(--accent); flex: none; display: flex; }
.chan-row .chan-check svg { width: 22px; height: 22px; display: block; }
.chan-row .conv-meta { flex-direction: row; align-items: center; gap: 8px; }
@keyframes slideup { from { transform: translateY(40px); opacity: 0; } to { transform: none; opacity: 1; } }
.sheet-handle { width: 40px; height: 4px; border-radius: 2px; background: var(--line); margin: 6px auto 14px; }
.sheet-item {
  display: flex; align-items: center; gap: 14px; width: 100%; padding: 14px 8px;
  font-weight: 800; font-size: 1rem; border-radius: var(--radius-s); text-align: left;
}
.sheet-item:active { background: var(--bg-soft); }
.sheet-item .ico { width: 40px; height: 40px; border-radius: 14px; background: var(--bg-soft); display: flex; align-items: center; justify-content: center; font-size: 1.2rem; }
#modal {
  position: fixed; inset: 0; z-index: 41; display: flex; align-items: center; justify-content: center; padding: 20px;
  pointer-events: none;
}
.modal-card {
  background: var(--bg); border-radius: var(--radius); padding: 20px;
  width: 100%; max-width: 480px; max-height: 86vh; overflow-y: auto;
  pointer-events: auto; animation: slideup 0.18s ease;
}
.modal-card h3 { font-weight: 900; font-size: 1.2rem; margin-bottom: 4px; letter-spacing: -0.01em; }
/* Modal buttons wrap instead of running off a phone screen, and share the width
   evenly once they do. */
.modal-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; justify-content: flex-end; }
.modal-actions .btn { flex: 1 1 auto; min-width: 120px; justify-content: center; }

/* ---------- AI media badge & info ---------- */
/* One mark for everything a model made — a picture, a narration — always in the
   top-right corner of the thing it describes. A plain round "AI": at this size
   the two letters say it better than any icon next to them would, and it's a
   big enough target to hit with a thumb. */
.ai-badge {
  position: absolute; top: 10px; right: 10px; z-index: 4;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border-radius: 50%; cursor: pointer;
  font-size: 0.72rem; font-weight: 900; letter-spacing: 0.04em; line-height: 1;
  color: #fff; background: rgba(23, 23, 28, 0.68); border: 1px solid rgba(255, 255, 255, 0.25);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.ai-badge:hover { background: rgba(23, 23, 28, 0.88); }
.ai-badge:active { transform: scale(0.95); }
/* the same mark, sitting still in the panel's heading */
.ai-badge.static {
  position: static; width: 28px; height: 28px; font-size: 0.62rem;
  vertical-align: middle; margin-right: 4px; cursor: default;
}
/* an audio player is a light block, not a photo — the mark keeps its shape but
   drops the glass, and the player's caption keeps clear of it */
.md-audio, .post-audio { position: relative; }
.md-audio > .ai-badge, .post-audio > .ai-badge {
  top: 6px; right: 6px; width: 30px; height: 30px; font-size: 0.66rem;
  color: var(--ink-inv); background: var(--ink); border-color: transparent;
}
.md-audio > .ai-badge + .cap, .post-audio > .ai-badge + .cap { padding-right: 38px; }
/* wrapper that lets the badge sit over an inline/album image */
.md-img { position: relative; display: inline-block; }
.md-img > img { display: block; width: 100%; }
.md .md-album .md-img { display: block; }
.md .md-album:not(.n1) .md-img { width: 100%; height: 100%; aspect-ratio: 1; }
.md .md-album.n3 > .md-img:first-child { aspect-ratio: 2 / 1; grid-column: 1 / -1; }
.md .md-album .md-img > img { object-fit: cover; cursor: zoom-in; }
.md .md-album:not(.n1) .md-img > img { height: 100%; }
/* a picture being re-rendered: it stays visible, veiled, with a spinner — you
   can see what you have and that something sharper is on its way */
.img-working {
  /* purely a veil: it must never swallow the tap meant for the image, its link
     or the AI badge above it */
  pointer-events: none;
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  background: rgba(23, 23, 28, 0.45); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  color: #fff; font-size: 0.72rem; font-weight: 800; letter-spacing: 0.02em; text-align: center; padding: 8px;
}
.img-working .spin {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  border: 2.5px solid rgba(255,255,255,0.35); border-top-color: #fff;
  animation: imgSpin 0.9s linear infinite;
}
@keyframes imgSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .img-working .spin { animation-duration: 3s; } }
.ai-prompt {
  background: var(--bg-soft); border-radius: var(--radius-s); padding: 10px 12px;
  font-size: 0.86rem; line-height: 1.5; color: var(--ink); white-space: pre-wrap;
}
.ai-meta { margin-top: 12px; display: flex; flex-direction: column; gap: 2px; }
.ai-meta > div { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; border-top: 1px solid var(--line); }
.ai-meta dt { font-size: 0.8rem; font-weight: 700; color: var(--ink-2); }
.ai-meta dd { font-size: 0.86rem; font-weight: 700; text-align: right; word-break: break-word; }
/* the request exactly as it went out, folded away for whoever wants it */
.ai-raw { margin-top: 12px; }
.ai-raw summary { cursor: pointer; font-size: 0.78rem; font-weight: 800; color: var(--ink-3); }
.ai-raw summary:hover { color: var(--ink-2); }
.ai-raw pre {
  margin-top: 8px; padding: 10px 12px; border-radius: var(--radius-s); background: var(--bg-soft);
  font-size: 0.74rem; line-height: 1.5; overflow-x: auto; white-space: pre;
}
#toast {
  position: fixed; bottom: calc(var(--tabbar-h) + 20px + env(safe-area-inset-bottom));
  left: 50%; transform: translateX(-50%); z-index: 60;
  background: var(--ink); color: var(--ink-inv); font-weight: 700; font-size: 0.85rem;
  padding: 10px 18px; border-radius: 999px; box-shadow: 0 8px 24px rgba(0,0,0,0.18);
  display: flex; align-items: center; gap: 16px; max-width: calc(100vw - 32px);
}
.toast-undo {
  color: #b6adfa; font-weight: 900; font-size: 0.85rem; flex: none;
  padding: 2px 2px; margin: -4px 0;
}
.toast-undo:active { opacity: 0.7; }

/* ---------- connect page ---------- */
.code-box {
  background: #16161c; color: #e8e8f0; border-radius: var(--radius-s);
  padding: 14px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem; overflow-x: auto; white-space: pre; position: relative; line-height: 1.5;
}
.copy-row { display: flex; gap: 8px; align-items: center; margin: 8px 0 16px; }
.copy-row .value {
  flex: 1; background: var(--bg-soft); border-radius: var(--radius-s); padding: 10px 12px;
  font-family: ui-monospace, Menlo, monospace; font-size: 0.78rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.token-row { display: flex; align-items: center; gap: 10px; padding: 12px 16px; }
.token-row .info { flex: 1; min-width: 0; }
.token-row .info b { display: block; font-size: 0.9rem; font-weight: 800; }
.token-row .info span { font-size: 0.72rem; color: var(--ink-3); font-family: ui-monospace, Menlo, monospace; }

/* ---------- reactions & taste profile ---------- */
.react-bar { display: flex; gap: 8px; margin-top: 12px; }
.react {
  border: 1.5px solid var(--line); border-radius: 999px; padding: 6px 12px;
  font-size: 0.78rem; font-weight: 800; color: var(--ink-2); background: var(--bg);
}
.react:active { transform: scale(0.96); }
.react.active { border-color: var(--accent); color: var(--accent); background: #f0edff; }

/* detail: reactions slide up from the bottom once you reach the article's end */
.react-pop {
  position: fixed; left: 0; right: 0; z-index: 25;
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  background: var(--glass); backdrop-filter: blur(12px); border-top: 1px solid var(--line);
  transform: translateY(130%); transition: transform 0.24s cubic-bezier(.2,.7,.3,1);
}
.react-pop.show { transform: none; }
.react-pop-inner { max-width: var(--maxw); margin: 0 auto; padding: 10px 16px; }
.react-pop .react-bar { margin-top: 0; }
/* reserve room so the pinned reaction bar never hides the article's end */
#article-end { height: 60px; }

/* segmented picker (note target) */
.seg { display: flex; gap: 8px; margin: 6px 0 12px; }
.seg-btn {
  flex: 1; text-align: left; background: var(--bg); color: var(--ink-2);
  border: 2px solid var(--line); border-radius: var(--radius-s); padding: 10px 12px;
}
.seg-btn b { display: block; font-size: 0.9rem; font-weight: 800; color: var(--ink); margin-bottom: 1px; }
.seg-btn span { font-size: 0.72rem; font-weight: 700; color: var(--ink-3); }
.seg-btn.active { border-color: var(--accent); background: #f0edff; }
.seg-btn:active { transform: scale(0.98); }
/* one-line variant: a plain switch between two ways of showing the same list
   (grouped or flat channels), not two options with explanations under them */
.seg.seg-sm { margin: -4px 0 12px; }
.seg.seg-sm .seg-btn {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 8px 10px; font-size: 0.82rem; font-weight: 800; color: var(--ink-2);
}
.seg.seg-sm .seg-btn.active { color: var(--accent); }
.seg.seg-sm .seg-btn .ic { width: 16px; height: 16px; flex: none; }

/* feed order toggle in the top bar */
.sort-toggle { gap: 6px; }
.sort-toggle svg { width: 15px; height: 15px; }
.new-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); vertical-align: middle; margin-left: 2px;
}
.hidden-tag { color: var(--warn); font-weight: 800; }

/* per-post ⋯ overflow menu (read/unread · not interested) */
.post-menu {
  flex: none; width: 34px; height: 34px; margin: -4px -6px -4px 0;
  border-radius: 50%; color: var(--ink-3);
  display: flex; align-items: center; justify-content: center;
}
.post-menu svg { width: 20px; height: 20px; display: block; }
.post-menu:active { background: var(--bg-soft); transform: scale(0.94); }
.sheet-item .ico svg { width: 22px; height: 22px; display: block; }
.sheet-item .ico.danger { color: #c0392b; }
/* read cards read a touch quieter than unread ones */
.post.compact.seen .post-title { color: var(--ink-2); }
.signal-row { display: flex; gap: 12px; padding: 14px 16px; align-items: flex-start; }
.signal-row .ico {
  width: 34px; height: 34px; border-radius: 12px; flex: none; background: var(--bg-soft);
  display: flex; align-items: center; justify-content: center; font-size: 1rem;
}
.signal-row .info { flex: 1; min-width: 0; }
.signal-row .info b { display: block; font-weight: 800; font-size: 0.9rem; line-height: 1.35; }
.signal-row .resolution {
  margin-top: 6px; font-size: 0.82rem; color: var(--ink-2); line-height: 1.45;
  background: var(--bg-soft); border-radius: var(--radius-s); padding: 8px 10px;
}

/* ---------- auth screen ---------- */
body.auth-screen #tabbar, body.auth-screen #topbar { display: none; }
body.auth-screen .view { padding-top: 8vh; }
.auth-wrap { max-width: 400px; margin: 0 auto; }
.auth-hero { text-align: center; margin-bottom: 24px; }
/* All caps: swap the tight display tracking for a little positive spacing,
   which uppercase needs to stay legible. */
.auth-hero h1 { font-size: 2.2rem; font-weight: 900; text-transform: uppercase; letter-spacing: 0.02em; margin-top: 10px; }
.auth-hero p { color: var(--ink-2); margin-top: 6px; line-height: 1.5; font-weight: 600; font-size: var(--auth-sub-size, 1rem); }
.auth-error {
  margin-top: 12px; background: #fdecec; color: #c0392b;
  border-radius: var(--radius-s); padding: 10px 12px; font-size: 0.85rem; font-weight: 700;
}
.auth-switch { display: block; width: 100%; text-align: center; margin-top: 14px; font-weight: 800; font-size: 0.85rem; color: var(--accent); }

@media (min-width: 720px) {
  :root { --maxw: 640px; }
  .view { padding-top: 24px; }
}

/* ---------- dark mode — components with hardcoded pastel colors ---------- */
@media (prefers-color-scheme: dark) {
  .pill.active { background: #0e3b30; color: #5ee6c4; }
  .pill.idea { background: #272047; color: #a99df5; }
  .pill.paused { background: #3d2a10; color: #f0b46a; }
  .pill.shipped { background: #10304a; color: #7cc3f2; }
  .pill.archived { background: #26262e; color: var(--ink-2); }
  .course-banner.on_track { background: #0e3b30; color: #5ee6c4; }
  .course-banner.drifting { background: #42210f; color: #f09a76; }
  .post.course.on_track .info b { color: #5ee6c4; }
  .post.course.drifting .info b { color: #f09a76; }
  .btn.danger { background: #42191a; color: #f19999; }
  .sheet-item .ico.danger { color: #f19999; }
  .react.active { background: #272047; }
  .seg-btn.active { background: #272047; }
  .auth-error { background: #42191a; color: #f19999; }
  .direction-card { background: linear-gradient(135deg, #211d33, #16222e); border-color: #2e2a4a; }
  .direction-card h4 { color: #a99df5; }
  .msg.bot .bubble { border-color: #1d4038; background: #12211d; }
  .mstat.done, .pending-strip .ps-done, .settled-note { color: #5ee6c4; }
  .cover-kind { background: rgba(23, 23, 28, 0.82); color: var(--ink); }
  .md pre { background: #0c0c10; }
}

/* ---------- channels & feed states ---------- */
/* Channel picker is an icon button in the top bar; it opens a bottom sheet.
   Highlighted when a channel filter is active. */
.icon-btn.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }

/* ---------- active feed filter ---------- */
/* Top-bar chip: an on filter says its own name, right next to the sort button,
   and stays on screen (the bar is sticky) however far you scroll. */
/* The one action allowed to give up width on a narrow phone — its name
   ellipsises instead of shoving the sort button off the bar. */
#topbar-actions > .filter-chip { flex: 0 1 auto; min-width: 72px; }
.filter-chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 46vw;
  height: 38px; padding: 0 10px 0 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent); font-weight: 800; font-size: 0.85rem;
}
.filter-chip:active { transform: scale(0.97); }
.filter-chip .fc-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filter-chip .ic { width: 15px; height: 15px; opacity: 0.75; flex: none; }
/* Strip above the first card: the full channel name and the way out. */
.filter-strip {
  display: flex; align-items: center; gap: 10px; margin-bottom: 14px;
  padding: 10px 12px; border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--accent) 9%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
}
/* The strip is where the channel's name is spelled out in full — it wraps
   rather than truncating, since the top-bar chip is the short version. */
.filter-strip .fs-text {
  flex: 1; min-width: 0; font-size: 0.85rem; font-weight: 600; color: var(--ink-2);
  line-height: 1.35; overflow-wrap: anywhere;
}
.filter-strip .fs-text b { font-weight: 800; color: var(--ink); }
.filter-strip .fs-clear {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  padding: 6px 10px; border-radius: 999px; background: var(--bg);
  font-size: 0.78rem; font-weight: 800; color: var(--accent);
}
.filter-strip .fs-clear .ic { width: 13px; height: 13px; }
.filter-strip .fs-clear:active { transform: scale(0.97); }

/* Header badges for engagement state. */
.reading-tag { color: var(--accent); font-weight: 800; }
.saved-tag { color: var(--good); font-weight: 800; }

/* Soft "that's today's feed" divider — a marker, not a wall. */
.feed-divider {
  display: flex; align-items: center; text-align: center;
  gap: 12px; margin: 26px 4px 20px; color: var(--ink-3);
}
.feed-divider::before, .feed-divider::after {
  content: ""; flex: 1; height: 2px; border-radius: 2px;
  background: linear-gradient(to var(--dir, right), var(--line), transparent);
}
.feed-divider::before { --dir: left; }
.feed-divider span {
  font-size: 0.74rem; font-weight: 800; letter-spacing: 0.02em;
  text-transform: uppercase; white-space: nowrap;
}

/* Explicit read / save-for-later actions at the end of an article. */
.article-actions {
  display: flex; flex-wrap: wrap; gap: 10px; margin: 8px 0 96px;
}
/* Read + save split the first row, the share pill takes the second — three
   actions fit a phone without squeezing the labels into two lines. */
.article-actions .btn { flex: 1; }
.article-actions .share-btn { flex: 1 0 100%; }
.article-actions .read-btn.done { background: var(--good); color: #fff; }
.article-actions .save-btn.done { border: 2px solid var(--good); color: var(--good); }
.article-actions .share-btn.done { border: 2px solid var(--accent); color: var(--accent); }

/* "Anyone with the link can read this" — shown under an article once its public
   link is live, with the way to take it back. It carries the bottom clearance
   the actions bar normally provides, so the two never double up. */
.article-actions:has(+ .share-state:not([hidden])) { margin-bottom: 10px; }
.share-state {
  display: flex; align-items: center; gap: 10px; margin: 0 0 96px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-s);
  background: var(--bg-soft);
}
.share-state .txt { flex: 1; min-width: 0; }
.share-state button {
  font-size: 0.76rem; font-weight: 800; color: var(--warn);
  background: none; border: none; padding: 4px; white-space: nowrap;
}

/* ---------- shared article (/s/<token>): the app chrome, minus the app ---------- */
.share-page .view { padding-bottom: 40px; }
.share-page .post-title { font-size: 1.42rem; margin-bottom: 12px; }
.share-foot {
  margin: 18px 4px 0; text-align: center;
  font-size: 0.78rem; color: var(--ink-3); font-weight: 600;
}

/* Channels management rows. */
.channel-row { display: flex; align-items: center; gap: 12px; padding: 14px 16px; width: 100%; text-align: left; }
.channel-row .avatar { width: 46px; height: 46px; border-radius: 14px; font-size: 1.4rem; }
.channel-row .info { flex: 1; min-width: 0; }
.channel-row .info b { display: block; font-size: 0.95rem; font-weight: 800; }
.channel-row .info .tagline { display: block; font-size: 0.82rem; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- messenger-style chat list: one white surface, hairline dividers ---------- */
.chatlist {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--card-shadow); margin-bottom: 12px;
}
.chatlist .conv-row {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
  padding: 13px 16px; border-bottom: 1px solid var(--line);
  background: transparent; color: inherit; transition: background 0.12s ease;
}
.showmore { width: 100%; margin: -4px 0 14px; color: var(--ink-2); }
.chatlist .conv-row:last-child { border-bottom: 0; }
.chatlist a.conv-row:active, .chatlist button.conv-row:active { background: var(--bg-soft); }
.chatlist .avatar { width: 50px; height: 50px; border-radius: 50%; font-size: 1.45rem; flex: none; }
/* stands in for an avatar on rows that mean an action ("Chat", "Articles")
   rather than a channel or a person */
.row-glyph {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 50px; height: 50px; border-radius: 50%; color: var(--ink-2);
  background: var(--bg-soft); border: 1px solid var(--line);
}
.row-glyph .ic { width: 22px; height: 22px; }
.chatlist .info { flex: 1; min-width: 0; }
.chatlist .info b { display: block; font-weight: 800; font-size: 0.98rem; }
.chatlist .last { display: block; font-size: 0.85rem; color: var(--ink-2); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conv-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; flex: none; }
.conv-meta .tiny { color: var(--ink-3); }
.conv-meta .chev { color: var(--ink-3); font-size: 1.15rem; line-height: 1; display: flex; }
/* where a channel sits, on a flat list that has dropped the indentation */
.chatlist .last .row-in { font-weight: 800; color: var(--ink-3); }
/* fold a branch of the channel tree open or shut. It sits inside the row but is
   its own button: the row still opens the channel, this only unfolds it. */
/* Circled, where the "go to this channel" chevron is bare: collapsed, the two
   point the same way, and only the button-ness tells them apart. */
.row-fold {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 32px; height: 32px; margin: -4px -4px -4px 0; border-radius: 50%;
  color: var(--ink-2); background: var(--bg-soft); border: 1px solid var(--line); cursor: pointer;
  transform: rotate(-90deg); transition: transform 0.15s ease, color 0.12s ease;
}
.row-fold.open { transform: none; }
/* with a fold button the right-hand side is a row, not a stack */
.conv-meta:has(.row-fold) { flex-direction: row; align-items: center; gap: 8px; }
.row-fold:hover { color: var(--ink); background: var(--bg-soft); }
.row-fold .ic { width: 20px; height: 20px; }
.pending-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--ink-3); opacity: 0.45; }
