/* Sahaba Club — Connect
   ------------------------------------------------------------
   Shared by app/member.html, app/connect.html and app/inbox.html. Loaded
   after styles.css and built entirely on its variables (--bg, --text,
   --text-secondary, --text-muted, --glass, --glass-border, --violet, --cyan,
   --gold, --radius, --max-width), so light mode comes for free: styles.css
   redefines those under html.light-mode and everything here follows.

   Every class in this file is prefixed cx-. Nothing here overrides a
   styles.css rule, and nothing in the Connect pages invents a class that
   isn't either here or in styles.css.

   Mobile first throughout — most of this is read on a phone. The wider
   layouts are additive, inside min-width queries. */

/* The pages hide their filter bar and inbox grid with the `hidden` attribute
   until there's something to show. A class that sets `display` outranks the
   user-agent's `[hidden] { display: none }`, so .cx-filters and .cx-inbox
   were staying on screen while empty — an empty country dropdown above a
   "Connect isn't switched on yet" message. Restore the attribute's meaning
   for anything inside these pages. */
[hidden] { display: none !important; }

.cx-wrap {
  max-width: 780px;
  margin: 0 auto;
  padding: 28px 16px 90px;
}
.cx-wrap-wide { max-width: var(--max-width); }

/* ---- Page heading ---- */

.cx-hero { margin-bottom: 20px; }
.cx-hero h1 { font-size: 24px; margin: 0 0 6px; line-height: 1.25; }
.cx-hero p {
  color: var(--text-secondary);
  font-size: 14.5px;
  margin: 0;
  max-width: 60ch;
}
.cx-back {
  display: inline-block;
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 12px;
}
.cx-back:hover { color: var(--text-secondary); }

/* ---- Panels ---- */

.cx-card {
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  padding: 18px;
}
.cx-card + .cx-card { margin-top: 14px; }

.cx-section { margin-top: 22px; }
.cx-section-title {
  font-size: 12px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 10px;
  font-weight: 700;
}

.cx-note {
  font-size: 12.5px;
  color: var(--text-muted);
  line-height: 1.6;
  margin: 12px 0 0;
}

/* ---- States: loading, empty, error ---- */

.cx-state {
  text-align: center;
  padding: 40px 18px;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
}
.cx-state-title { font-size: 17px; font-weight: 700; margin: 0 0 8px; }
.cx-state-text {
  font-size: 14px;
  color: var(--text-secondary);
  margin: 0 auto;
  max-width: 46ch;
  line-height: 1.65;
}
.cx-state-actions { margin-top: 16px; display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

.cx-status {
  font-size: 13px;
  padding: 10px 12px;
  border-radius: 10px;
  margin-top: 12px;
  line-height: 1.55;
}
.cx-status.is-ok  { background: rgba(34,197,94,0.14);  border: 1px solid rgba(34,197,94,0.4);  color: #4ade80; }
.cx-status.is-err { background: rgba(239,68,68,0.14);  border: 1px solid rgba(239,68,68,0.4);  color: #fca5a5; }
.cx-status.is-info{ background: rgba(56,189,248,0.14); border: 1px solid rgba(56,189,248,0.4); color: #7dd3fc; }
html.light-mode .cx-status.is-ok   { color: #166534; } /* #15803d measured 4.18:1 on the tint. */
html.light-mode .cx-status.is-err  { color: #b91c1c; }
html.light-mode .cx-status.is-info { color: #0369a1; }

/* ---- Buttons ---- */

.cx-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 40px;
  padding: 9px 18px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  border: 1px solid var(--glass-border);
  background: var(--glass);
  color: var(--text);
  text-decoration: none;
}
.cx-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.cx-btn-primary {
  background: linear-gradient(135deg, var(--violet), var(--cyan));
  border: none;
  color: #05070d;
  font-weight: 700;
}
html.light-mode .cx-btn-primary { color: #ffffff; }
.cx-btn-sm { min-height: 34px; padding: 6px 14px; font-size: 13px; }
/* Following: a filled state that still reads as "press to undo". */
.cx-btn.is-on {
  background: rgba(139, 92, 246, 0.16);
  border-color: rgba(139, 92, 246, 0.45);
  color: var(--text);
}

.cx-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; }

/* ---- Avatars ---- */

.cx-avatar {
  width: 68px;
  height: 68px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  flex: 0 0 auto;
}
.cx-avatar-initial {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26px;
  font-weight: 700;
  color: #05070d;
  background: linear-gradient(140deg, var(--violet), var(--cyan));
  border: none;
}
.cx-avatar-sm { width: 44px; height: 44px; }
.cx-avatar-sm.cx-avatar-initial { font-size: 17px; }

/* ---- Tags and topics ---- */

.cx-tags { display: flex; flex-wrap: wrap; gap: 7px; }
.cx-tag {
  font-size: 12px;
  padding: 4px 11px;
  border-radius: 999px;
  color: var(--text-secondary);
  background: var(--glass);
  border: 1px solid var(--glass-border);
}
/* Topics are derived from real attendance, so they get the accent that the
   typed-in tags don't. */
.cx-tag-topic {
  color: var(--text);
  background: rgba(139, 92, 246, 0.14);
  border-color: rgba(139, 92, 246, 0.38);
}
.cx-topic-line {
  font-size: 13.5px;
  color: var(--text-secondary);
  margin: 0 0 10px;
}

.cx-summary {
  font-size: 14px;
  color: var(--text-secondary);
  margin: 10px 0 0;
  line-height: 1.6;
}

/* ---- Profile header ---- */

.cx-head { display: flex; gap: 14px; align-items: flex-start; }
.cx-head-text { min-width: 0; }
.cx-name { font-size: 21px; margin: 0 0 3px; line-height: 1.25; word-break: break-word; }
.cx-headline {
  font-size: 14.5px;
  color: var(--text-secondary);
  margin: 0 0 6px;
  line-height: 1.5;
}
.cx-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: 12.5px;
  color: var(--text-muted);
}
.cx-meta span { white-space: nowrap; }

/* ---- Numbers ---- */

.cx-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-top: 16px;
}
@media (min-width: 520px) { .cx-stats { grid-template-columns: repeat(4, 1fr); } }
.cx-stat {
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: 12px;
  padding: 12px 10px;
  text-align: center;
}
.cx-stat-num {
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  display: block;
  line-height: 1.2;
}
.cx-stat-label {
  font-size: 11.5px;
  color: var(--text-muted);
  display: block;
  margin-top: 2px;
}

/* ---- Links out ---- */

.cx-links { display: flex; flex-wrap: wrap; gap: 8px; }
.cx-link {
  font-size: 12.5px;
  padding: 6px 13px;
  border-radius: 999px;
  border: 1px solid var(--glass-border);
  background: var(--glass);
  color: var(--text-secondary);
}
.cx-link:hover { color: var(--text); border-color: rgba(139, 92, 246, 0.45); }

/* ---- EduHackAI ----
   Three things a reader wants in this order: did they win, what did they do,
   and when. The award badge is sized for the first of those — these profiles
   get screenshotted and sent to the person they describe, and the placing is
   the reason why. */

.cx-hacks { display: grid; gap: 12px; }
.cx-hack {
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  padding: 16px;
}
/* A round they placed in gets a gold edge and tint, so an award reads from
   across the room rather than needing to be found. */
.cx-hack.is-award {
  border-color: rgba(224, 168, 62, 0.6); /* 3.85:1 on the plain card */
  background: rgba(224, 168, 62, 0.08);
}
/* The same tint and edge over a light page all but disappear — the composited
   0.45 gold border measured 1.32:1 against the card — so light mode carries
   both harder. Text on the tint is --text/--text-secondary either way, and
   both still clear AA on the result. */
html.light-mode .cx-hack.is-award {
  border-color: #a3701a; /* 3.73:1 on the plain card */
  background: rgba(224, 168, 62, 0.17);
}

.cx-hack-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 10px; }
.cx-hack-round {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  flex: 0 0 auto;
}
.cx-hack-name {
  font-size: 16.5px;
  font-weight: 700;
  margin: 0;
  line-height: 1.3;
  word-break: break-word;
}
.cx-hack-flags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 11px;
}
.cx-hack-what {
  font-size: 13.5px;
  color: var(--text-secondary);
  line-height: 1.55;
  margin: 11px 0 0;
}
.cx-hack-what strong { color: var(--text); font-weight: 700; }
.cx-hack-when {
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 7px 0 0;
  font-variant-numeric: tabular-nums;
}

/* The award itself. The medal colours are fixed rather than themed on
   purpose: --gold is a light #e0a83e in the dark theme and a dark #9a6413 in
   the light one, so a gradient built from it would flip to dark ink on a dark
   plate the moment somebody switched themes. A medal is the same colour in
   both, and the near-black ink clears AA against the darker stop of every
   gradient below — which is the stop that has to be scored, not the average
   and not whatever sits behind it.

   The rim is not decoration. A gold plate on the gold tint of an awarded card
   measured 1.79:1 in light mode: the badge's own edge was the thing that had
   disappeared, not its text. Each rim is the darker tone of its own metal and
   clears 3:1 against the awarded card in both themes. */
.cx-award {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 15px;
  border-radius: 999px;
  font-size: 13.5px;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: #241703;
  border: 1px solid #a3701a;
  background: linear-gradient(135deg, #f7de9a, #dda132);
  box-shadow: 0 2px 14px rgba(224, 168, 62, 0.3);
}
.cx-award-icon { width: 16px; height: 16px; flex: 0 0 auto; }
.cx-award-2 {
  color: #171b26;
  border-color: #6b7285;
  background: linear-gradient(135deg, #eef1f7, #b9c0d4);
  box-shadow: 0 2px 14px rgba(166, 172, 194, 0.28);
}
.cx-award-3 {
  color: #241203;
  border-color: #a9622c;
  background: linear-gradient(135deg, #f4cda6, #cf8b4e);
  box-shadow: 0 2px 14px rgba(207, 139, 78, 0.28);
}

/* Coach and Judge. Bigger than the roster badges on hackathons.html — this is
   a profile, not a list — and they have to hold their contrast sitting on the
   gold tint of an awarded round as well as on a plain card. */
.cx-badge {
  flex: 0 0 auto;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 999px;
}
.cx-badge.is-coach {
  color: #c4b5fd;
  border: 1px solid rgba(139, 92, 246, 0.45);
  background: rgba(139, 92, 246, 0.14);
}
html.light-mode .cx-badge.is-coach { color: var(--violet); }
.cx-badge.is-judge {
  color: #67e8f9;
  border: 1px solid rgba(34, 211, 238, 0.42);
  background: rgba(34, 211, 238, 0.13);
}
/* Light mode's --cyan (#0e7490) measured 4.33:1 on the badge's own cyan tint
   and 3.94:1 once that tint sits on an awarded card — both under AA. One step
   darker clears it in the worse of the two. */
html.light-mode .cx-badge.is-judge { color: #155e75; }

/* Self-declared roles, shown small so they read as the participant's own
   words rather than as anything the club assigned them. */
.cx-roles { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.cx-role {
  font-size: 11px;
  padding: 3px 9px;
  border-radius: 999px;
  color: var(--text-muted);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--glass-border);
}
html.light-mode .cx-role { background: rgba(0, 0, 0, 0.04); }

/* "Unknown" rather than "they didn't place" is the one distinction in this
   section that must not be read as small print, so it gets the same treatment
   as the same sentence on hackathons.html. */
.cx-no-placing {
  margin: 12px 0 0;
  padding: 10px 13px;
  border-left: 3px solid var(--glass-border);
  border-radius: 0 10px 10px 0;
  background: var(--glass);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--text-secondary);
  max-width: 70ch;
}
.cx-no-placing em { color: var(--text); font-style: normal; font-weight: 700; }

/* ---- Someone's own words ----
   `pre-line` because a bio is typed into a textarea and a prospect's is
   written by a model in paragraphs; collapsing them runs three thoughts into
   one block. `pre-wrap` would keep the leading spaces of an indented paste
   too, which is a different bug. */

.cx-bio {
  font-size: 14px;
  color: var(--text-secondary);
  line-height: 1.7;
  margin: 0;
  max-width: 68ch;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* ---- How you relate, and who hasn't arrived ----
   Two chips that have to be told apart at a glance, so they are told apart by
   more than colour: the relevance chip is filled, the not-joined chip is
   dashed and unfilled. A dashed outline is the same signal a form uses for
   something not filled in yet, which is exactly what this person is.

   Both are var()-driven rather than fixed hex, so light mode inherits the
   contrast decisions styles.css already made rather than needing a second set
   of measurements. Neither uses a gradient — a gradient would have to be
   scored per stop, and neither of these earns that. */

.cx-flags { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; }

/* The same violet plate as .cx-badge.is-coach, whose text colours are already
   measured. Sentence case rather than the badges' uppercase: this is a
   sentence about the reader, not a label.
   Text measured on the composited tint: #c4b5fd 8.8:1 dark, --violet 5.25:1
   light. The rim is where it differs from the coach badge — at 0.45 alpha the
   violet edge composites to 1.82:1 dark and 1.73:1 light against the card, and
   the 0.14 fill is only 1.15:1, so at that weight the chip has no visible
   boundary at all. Solid --violet gives 4.42:1 dark and 6.17:1 light, which is
   the same fix the award badges needed for the same reason. */
.cx-rel {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.01em;
  padding: 4px 11px;
  border-radius: 999px;
  color: #c4b5fd;
  border: 1px solid var(--violet);
  background: rgba(139, 92, 246, 0.14);
}
html.light-mode .cx-rel { color: var(--violet); }

/* --text-muted as a dashed rim measures 5.52:1 dark and 5.03:1 light against
   the card, and the label itself is --text-secondary at 8.28:1 / 6.46:1. */
.cx-notyet {
  font-size: 11.5px;
  font-weight: 600;
  padding: 4px 11px;
  border-radius: 999px;
  color: var(--text-secondary);
  border: 1px dashed var(--text-muted);
  background: none;
}

/* The card. Deliberately NOT dimmed: a faded card reads as "inactive member",
   which is the one thing these people are not — they never had the chance to
   be active. Same weight of card as everyone else, with a dashed edge instead
   of a solid one. */
.cx-member.is-notyet {
  border-style: dashed;
  border-color: var(--text-muted);
}

/* The panel that stands where Follow and Message would be on a member. It has
   to carry the invitation, because this page gets screenshotted and sent to
   the person it describes, so it is a panel and not a footnote. */
.cx-notyet-panel {
  margin-top: 16px;
  padding: 16px;
  border-radius: var(--radius);
  border: 1px dashed var(--text-muted);
  background: var(--glass);
}
.cx-notyet-title {
  font-size: 15.5px;
  font-weight: 700;
  margin: 0 0 8px;
  color: var(--text);
  line-height: 1.35;
}
.cx-notyet-text {
  font-size: 13.5px;
  color: var(--text-secondary);
  line-height: 1.65;
  margin: 0 0 10px;
  max-width: 66ch;
}
.cx-notyet-text strong { color: var(--text); font-weight: 700; }
/* The buttons inside the panel are left-aligned; .cx-state-actions centres
   them, which is right in an empty state and wrong under a paragraph. */
.cx-notyet-panel .cx-state-actions { justify-content: flex-start; margin-top: 14px; }

/* ---- Work history ----
   A timeline rather than a list: the rail and the dots are what make the order
   readable without reading the dates. The rail is drawn on the list and the
   dots on each item, so a single role still looks deliberate. */

.cx-timeline { list-style: none; margin: 0; padding: 2px 0 0 22px; position: relative; }
.cx-timeline::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 8px;
  bottom: 8px;
  width: 1px;
  background: var(--glass-border);
}
.cx-tl-item { position: relative; padding-bottom: 18px; }
.cx-tl-item:last-child { padding-bottom: 0; }
.cx-tl-item::before {
  content: "";
  position: absolute;
  left: -22px;
  top: 7px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--bg);
  border: 1px solid var(--text-muted);
}
.cx-tl-item.is-current::before {
  background: var(--violet);
  border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.22);
}
.cx-tl-role {
  font-size: 14.5px;
  font-weight: 700;
  line-height: 1.45;
  margin: 0;
  word-break: break-word;
}
.cx-tl-at { font-weight: 500; color: var(--text-muted); }
.cx-tl-now {
  display: inline-block;
  margin-left: 8px;
  vertical-align: 1px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  color: #c4b5fd;
  border: 1px solid rgba(139, 92, 246, 0.45);
  background: rgba(139, 92, 246, 0.14);
}
html.light-mode .cx-tl-now { color: var(--violet); }
.cx-tl-when {
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 3px 0 0;
  font-variant-numeric: tabular-nums;
}
.cx-tl-summary {
  font-size: 13.5px;
  color: var(--text-secondary);
  line-height: 1.6;
  margin: 7px 0 0;
  max-width: 68ch;
}

/* ---- Directory ---- */

.cx-filters { display: grid; gap: 10px; margin-bottom: 18px; }
.cx-input,
.cx-select {
  width: 100%;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: 10px;
  padding: 11px 13px;
  color: var(--text);
  font-size: 15px; /* 16px-ish keeps iOS from zooming the page on focus */
  font-family: inherit;
}
/* Five selects now that Connect lists people who haven't joined as well as
   members. auto-fit rather than a fixed count: at 720px five equal columns
   would be 130px each and every label would truncate, so they wrap to two rows
   of usable width instead and fill out to a single row when there is room. */
.cx-select-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (min-width: 720px) {
  .cx-select-row { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
}

.cx-count {
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 0 0 12px;
  font-variant-numeric: tabular-nums;
}

.cx-grid { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .cx-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 960px) { .cx-grid { grid-template-columns: repeat(3, 1fr); } }

.cx-member {
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cx-member-link { display: flex; gap: 12px; align-items: flex-start; min-width: 0; }
.cx-member-body { min-width: 0; flex: 1; }
.cx-member-name { font-size: 15.5px; font-weight: 700; margin: 0 0 2px; word-break: break-word; }
.cx-member-headline {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.45;
}
.cx-member-sub { font-size: 12px; color: var(--text-muted); margin: 4px 0 0; }
.cx-member-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

/* ---- Inbox ---- */

.cx-inbox { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 860px) {
  .cx-inbox { grid-template-columns: 300px 1fr; align-items: start; }
  /* Above this width both panes are on screen at once, so the phone's
     one-at-a-time switching stops applying. */
  .cx-inbox .cx-pane { display: block !important; }
}

.cx-threads { display: flex; flex-direction: column; gap: 8px; }
.cx-thread {
  display: flex;
  gap: 11px;
  align-items: center;
  width: 100%;
  text-align: left;
  padding: 11px;
  border-radius: 12px;
  border: 1px solid var(--glass-border);
  background: var(--glass);
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
}
.cx-thread.is-active { border-color: rgba(139, 92, 246, 0.5); background: rgba(139, 92, 246, 0.1); }
.cx-thread-body { min-width: 0; flex: 1; }
/* Both are spans rather than block elements so they can sit inside a button
   without invalid nesting — which means display has to be set here, or the
   name and the preview run together on one line. */
.cx-thread-name {
  display: block;
  font-size: 14px;
  font-weight: 600;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cx-thread-preview {
  display: block;
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 2px 0 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cx-thread-side { text-align: right; flex: 0 0 auto; }
.cx-thread-time { font-size: 11px; color: var(--text-muted); display: block; }
.cx-unread {
  display: inline-block;
  min-width: 19px;
  padding: 1px 6px;
  margin-top: 4px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  color: #05070d;
  background: linear-gradient(135deg, var(--violet), var(--cyan));
  font-variant-numeric: tabular-nums;
}
html.light-mode .cx-unread { color: #ffffff; }

.cx-convo {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 58vh;
  overflow-y: auto;
  padding: 4px 2px;
}
.cx-bubble {
  max-width: 84%;
  padding: 10px 13px;
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  border: 1px solid var(--glass-border);
  background: var(--glass);
}
.cx-bubble.is-mine {
  align-self: flex-end;
  background: rgba(139, 92, 246, 0.16);
  border-color: rgba(139, 92, 246, 0.36);
}
.cx-bubble-time {
  display: block;
  font-size: 10.5px;
  color: var(--text-muted);
  margin-top: 5px;
}

.cx-compose { display: flex; gap: 8px; align-items: flex-end; margin-top: 12px; }
.cx-compose textarea {
  flex: 1;
  min-height: 44px;
  max-height: 160px;
  resize: vertical;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: 12px;
  padding: 11px 13px;
  color: var(--text);
  font-size: 15px;
  font-family: inherit;
}

.cx-convo-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 12px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--glass-border);
}
.cx-convo-head-text { flex: 1; min-width: 0; }
.cx-convo-title {
  display: block;
  font-size: 15.5px;
  font-weight: 700;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cx-convo-sub {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  margin: 1px 0 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cx-icon-btn {
  background: none;
  border: 1px solid var(--glass-border);
  border-radius: 999px;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 12.5px;
  padding: 5px 12px;
  cursor: pointer;
  flex: 0 0 auto;
}
.cx-icon-btn:hover { color: var(--text); }

/* The phone shows one pane at a time; JS toggles cx-is-hidden, and the
   min-width rule above overrides it back on once both fit. */
.cx-is-hidden { display: none; }
