/* ==========================================================================
   Community — SAMPLE
   Chat, presence, profiles, discovery. Not wired into the site.

   Only tokens that exist in tokens.css. --s-9, --s-13 and --s-14 are NOT in
   the scale, and an undefined custom property invalidates the whole
   declaration at computed-value time — silently.
   ========================================================================== */

.page-cmx, .page-cmp {
  --cmx-max:   1500px;
  --cmx-hair:  rgb(255 255 255 / .09);
  --cmx-pane:  rgb(255 255 255 / .04);
  --cmx-pane2: rgb(255 255 255 / .07);
  --cmx-glass: blur(16px) saturate(1.3);
  --cmx-rail:  17rem;
  --cmx-side:  16rem;
  background: var(--surface-0);
  overflow-x: clip;
  /* Tells the browser to render NATIVE chrome dark: the document scrollbar,
     form controls, spinners. ::-webkit-scrollbar can restyle a scrolling
     element but cannot reach the document's own bar, which is why a dark page
     was still edged with a white slab. One declaration, and it is also what
     makes a date picker or a select look right without touching either. */
  color-scheme: dark;
}

/* ── AMBIENCE ──────────────────────────────────────────────────────────── */
.cmxSky { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.cmxSky__grid {
  position: absolute; inset: -20%;
  background-image:
    linear-gradient(to right, rgb(240 198 74 / .04) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(240 198 74 / .04) 1px, transparent 1px);
  background-size: 58px 58px;
  mask-image: radial-gradient(ellipse 70% 55% at 50% 25%, #000 12%, transparent 72%);
  transform: perspective(700px) rotateX(62deg) translateY(16%);
  transform-origin: 50% 100%;
}
.cmxSky__orb { position: absolute; border-radius: 50%; filter: blur(90px); opacity: .26;
  animation: cmxDrift 26s var(--ease-inout) infinite alternate; }
.cmxSky__orb--1 { width: 38vw; height: 38vw; left: -10vw; top: -12vw;
  background: radial-gradient(circle, rgb(240 198 74 / .5), transparent 70%); }
.cmxSky__orb--2 { width: 30vw; height: 30vw; right: -8vw; bottom: -10vw;
  background: radial-gradient(circle, rgb(232 84 74 / .3), transparent 70%); animation-delay: -13s; }
@keyframes cmxDrift { from { translate: 0 0; } to { translate: 3vw 4vh; } }

/* Sections lift above the ambience — never <main>, whose z-index would create
   a stacking context and trap every overlay inside it. */
.cmxShell, .cmxFlag, .cmpWrap { position: relative; z-index: 1; }

/* ── SHARED ────────────────────────────────────────────────────────────── */
/* --av-size is written inline by yc_avatar(). BOTH dimensions have to be set
   here: site.css carries `img { max-width:100%; height:auto }`, which beats the
   HTML height attribute, so a 256x340 portrait rendered 46 wide by 61 tall and
   border-radius:50% turned it into an ellipse. aspect-ratio is the backstop if
   the variable is ever missing. */
.ycAv {
  width: var(--av-size, 40px);
  height: var(--av-size, 40px);
  aspect-ratio: 1;
  border-radius: 50%; display: block; flex: none;
}
/* A hairline and a backing colour on PHOTO avatars only. A generated tile is
   always a light-on-colour gradient and needs neither; a real photograph can be
   almost black, and three of the first six people on the discover page looked
   like their avatar had failed to load. The ring gives a dark photo an edge. */
img.ycAv {
  object-fit: cover; object-position: 50% 30%;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .1), 0 0 0 1px rgb(255 255 255 / .06);
}

.cmxDot {
  width: .45rem; height: .45rem; border-radius: 50%; flex: none;
  background: var(--y-green); display: inline-block;
  box-shadow: 0 0 0 0 rgb(62 158 95 / .6);
  animation: cmxPulse 2.4s var(--ease-out) infinite;
}
@keyframes cmxPulse {
  0%   { box-shadow: 0 0 0 0 rgb(62 158 95 / .55); }
  70%  { box-shadow: 0 0 0 .5rem rgb(62 158 95 / 0); }
  100% { box-shadow: 0 0 0 0 rgb(62 158 95 / 0); }
}

.cmxBtn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: .6rem 1.1rem; border-radius: var(--radius-pill); cursor: pointer;
  border: 1px solid transparent; font: inherit; font-size: var(--fs-sm); font-weight: 600;
  text-decoration: none;
  transition: box-shadow var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out),
              filter var(--dur) var(--ease-out);
}
/* No translate on hover — see the note at .cmxRoom. A 2px lift on a 40px
   button means the bottom 2px of it flickers when the pointer rests there. The
   lift is faked with shadow, which cannot move the hit area. */
.cmxBtn--primary { background: var(--y-yellow); color: var(--y-black); box-shadow: var(--glow-yellow); }
.cmxBtn--primary:hover { box-shadow: var(--glow-yellow), 0 6px 18px -6px rgb(240 198 74 / .5); filter: brightness(1.06); }
.cmxBtn--ghost { border-color: var(--cmx-hair); color: var(--text-hi); background: var(--cmx-pane); }
.cmxBtn--ghost:hover { border-color: rgb(240 198 74 / .55); color: var(--accent-ink); }
.cmxBtn--on { background: var(--cmx-pane2); color: var(--text-mid); border-color: var(--cmx-hair); }

.cmxIcon {
  width: 2.3rem; height: 2.3rem; flex: none; display: grid; place-items: center;
  border-radius: 50%; cursor: pointer; font-size: 1rem; line-height: 1;
  border: 1px solid var(--cmx-hair); background: var(--cmx-pane); color: var(--text-mid);
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-spring);
}
.cmxIcon:hover { color: var(--accent-ink); border-color: var(--y-yellow); transform: scale(1.08); }

.cmxLink { border: 0; background: none; padding: 0; cursor: pointer; font: inherit;
  color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }

.cmxAnon {
  font-family: var(--font-mono); font-size: .55rem; letter-spacing: .1em;
  text-transform: uppercase; padding: .1rem .35rem; border-radius: 4px;
  background: var(--surface-2); color: var(--text-lo);
}

/* ══════════════════════════════════════════════════════════════════════════
   SHELL — three columns
   ══════════════════════════════════════════════════════════════════════════ */
.cmxShell {
  max-width: var(--cmx-max); margin-inline: auto;
  padding: var(--s-5) var(--gutter) 0;
  display: grid;
  grid-template-columns: var(--cmx-rail) minmax(0, 1fr) var(--cmx-side);
  grid-template-areas: "rail main side";
  gap: var(--s-5);
  /* The three columns each scroll independently; the PAGE does not. A chat
     that moves the whole document loses the composer off the bottom. */
  height: calc(100svh - 5.5rem);
}

/* ── RAIL ──────────────────────────────────────────────────────────────── */
.cmxRail {
  grid-area: rail; display: flex; flex-direction: column; gap: var(--s-4);
  min-height: 0;
}
.cmxRail__title { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  color: var(--text-hi); margin: 0; }
.cmxRail__sub { margin: .2rem 0 0; font-size: var(--fs-xs); color: var(--text-lo); }

.cmxRooms { display: grid; gap: var(--s-2); overflow-y: auto; min-height: 0; flex: 1; }
/* ── HOVER MUST NOT MOVE THE THING BEING HOVERED ────────────────────────────
   These cards used to shift 3px on :hover. That is a feedback loop, not a
   flourish: with the pointer near an edge, the card slides out from under it,
   :hover stops matching, the card slides back, :hover matches again — and it
   oscillates for as long as the mouse is held still. It reads as a rendering
   fault, which is exactly what it is.

   The rule that avoids it: never animate a property that changes the element's
   OWN hit area on a hover of that same element. So the motion lives in a
   pseudo-element and in the contents instead — an accent bar wipes in from the
   left and the row lifts its text, while the card's box never moves a pixel.
   Same read, no loop. --------------------------------------------------- */
.cmxRoom {
  position: relative; isolation: isolate;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-3) var(--s-3) calc(var(--s-3) + 3px);
  border-radius: var(--radius);
  border: 1px solid transparent; text-decoration: none;
  transition: background-color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
/* The accent bar. Scales from 0 rather than animating width, so it is a
   compositor-only change and cannot reflow the row it sits in. */
.cmxRoom::before {
  content: ''; position: absolute; z-index: -1;
  left: 0; top: .55rem; bottom: .55rem; width: 3px;
  border-radius: 3px; background: var(--y-yellow);
  scale: 1 0; transform-origin: 50% 50%;
  transition: scale var(--dur) var(--ease-spring);
}
.cmxRoom:hover { background: var(--cmx-pane); }
.cmxRoom:hover::before, .cmxRoom.is-on::before { scale: 1 1; }
.cmxRoom:hover .cmxRoom__emoji { scale: 1.14; }
.cmxRoom.is-on { background: var(--cmx-pane2); border-color: rgb(240 198 74 / .35); }
.cmxRoom__emoji { font-size: 1.1rem; transition: scale var(--dur) var(--ease-spring); }
.cmxRoom__body { min-width: 0; }
.cmxRoom__body b { display: block; font-size: var(--fs-sm); color: var(--text-hi); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmxRoom__body i { display: block; font-style: normal; font-size: var(--fs-xs); color: var(--text-lo);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmxRoom__live {
  display: inline-flex; align-items: center; gap: .3rem; flex: none;
  font-family: var(--font-mono); font-size: .62rem; color: var(--y-green);
}
.cmxRoom__idle { font-family: var(--font-mono); font-size: .6rem; color: var(--text-lo); flex: none; }
.cmxRail__foot { padding-bottom: var(--s-4); }

/* ══════════════════════════════════════════════════════════════════════════
   MAIN
   ══════════════════════════════════════════════════════════════════════════ */
.cmxMain {
  grid-area: main; min-height: 0; min-width: 0;
  display: grid;
  /* The COLUMN must be declared, not just the rows. With rows alone the
     implicit column resolves to max-content, so the header and the log sized
     themselves to their content — measured 382px wide inside a 351px parent
     at 375px viewport. This is the third time this exact defect has appeared
     in this project; rows-without-columns is always worth a second look. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  border-radius: var(--radius-lg); border: 1px solid var(--cmx-hair);
  background: linear-gradient(180deg, var(--cmx-pane2), transparent 30%), var(--surface-1);
  overflow: hidden;
}

.cmxTop {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--cmx-hair);
}
.cmxTop__emoji { font-size: 1.5rem; flex: none; }
.cmxTop__body { flex: 1; min-width: 0; }
.cmxTop__body h2 { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  color: var(--text-hi); margin: 0; line-height: 1.2; }
.cmxTop__body p { margin: .1rem 0 0; font-size: var(--fs-xs); color: var(--text-lo);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cmxWho { display: flex; align-items: center; gap: var(--s-3); flex: none; }
.cmxStack { display: flex; align-items: center; }
.cmxStack > * { margin-left: -.6rem; box-shadow: 0 0 0 2px var(--surface-1); }
.cmxStack > *:first-child { margin-left: 0; }
.cmxStack__more {
  width: 1.875rem; height: 1.875rem; border-radius: 50%; display: grid; place-items: center;
  background: var(--surface-2); color: var(--text-mid);
  font-family: var(--font-mono); font-size: .58rem;
}
.cmxWho__txt { display: inline-flex; align-items: center; gap: .4rem;
  font-size: var(--fs-xs); color: var(--text-lo); white-space: nowrap; }
.cmxWho__txt b { color: var(--text-hi); }

/* ── LOG ───────────────────────────────────────────────────────────────── */
.cmxLog {
  overflow-y: auto; min-height: 0;
  padding: var(--s-5) var(--s-5) var(--s-2);
  display: flex; flex-direction: column; gap: var(--s-3);
  scroll-behavior: smooth;
}
.cmxEmpty { text-align: center; color: var(--text-lo); padding: var(--s-16) 0; margin: 0; }

.cmxSys {
  align-self: center; margin: var(--s-2) 0;
  padding: .3rem .9rem; border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--text-lo);
  font-size: var(--fs-xs);
  animation: cmxSysIn .5s var(--ease-out);
}
@keyframes cmxSysIn { from { opacity: 0; scale: .9; } to { opacity: 1; scale: 1; } }

.cmxMsg {
  position: relative;
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s-3); align-items: start;
  animation: cmxIn .42s var(--ease-out);
}
/* A message from the same author immediately above loses its avatar and name.
   Repeating them turns a conversation into a list of business cards. */
.cmxMsg.is-group { grid-template-columns: 34px minmax(0, 1fr); }
.cmxMsg.is-group .cmxMsg__body { padding-top: 0; }
@keyframes cmxIn {
  from { opacity: 0; translate: 0 10px; scale: .98; }
  to   { opacity: 1; translate: 0 0;    scale: 1; }
}
.cmxMsg__av { grid-column: 1; }
.cmxMsg__body { grid-column: 2; min-width: 0; }

.cmxMsg__who { display: flex; align-items: baseline; gap: var(--s-2); margin: 0 0 .25rem; }
.cmxMsg__who b { font-size: var(--fs-sm); color: var(--text-hi); font-weight: 600; }
.cmxMsg__who time { font-family: var(--font-mono); font-size: .58rem; color: var(--text-lo); }

.cmxBubble {
  display: inline-block; max-width: 46rem;
  padding: .6rem .9rem; border-radius: 4px 14px 14px 14px;
  background: var(--surface-2); color: var(--text-mid);
  font-size: var(--fs-sm); line-height: var(--lh-body);
  overflow-wrap: anywhere;
}
/* ── YOUR messages sit on the right ───────────────────────────────────────
   Everything you receive stays left. This is the one layout convention every
   messaging app shares, and breaking it makes a chat genuinely hard to scan:
   without it you have to read the name on every bubble to know who is who. */
.cmxMsg.is-mine {
  grid-template-columns: minmax(0, 1fr) auto;
  justify-items: end;
}
.cmxMsg.is-mine .cmxMsg__av   { grid-column: 2; grid-row: 1; }
.cmxMsg.is-mine .cmxMsg__body { grid-column: 1; grid-row: 1; text-align: right; }
.cmxMsg.is-mine.is-group      { grid-template-columns: minmax(0, 1fr) 34px; }
.cmxMsg.is-mine .cmxMsg__who  { justify-content: flex-end; }
.cmxMsg.is-mine .cmxRx        { justify-content: flex-end; }
.cmxMsg.is-mine .cmxMsg__tools { right: auto; left: 0; }

.cmxMsg.is-mine .cmxBubble {
  background: linear-gradient(135deg, rgb(240 198 74 / .24), rgb(240 198 74 / .13));
  border: 1px solid rgb(240 198 74 / .32); color: var(--text-hi);
  /* The tail flips to the other corner with it. */
  border-radius: 14px 4px 14px 14px;
  text-align: left;
}

/* A message still in flight is dimmed, and one that failed says so. Without
   this a send that silently did nothing looks identical to one that worked. */
.cmxMsg.is-sending { opacity: .55; }
.cmxMsg.is-failed .cmxBubble { border-color: var(--y-red); color: var(--y-red-300); }

.cmxSticker { font-size: 2.6rem; line-height: 1; padding: .2rem 0;
  animation: cmxPop .45s var(--ease-spring); }
@keyframes cmxPop { from { scale: .3; rotate: -12deg; } to { scale: 1; rotate: 0deg; } }

.cmxGif {
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5); border-radius: var(--radius);
  border: 1px dashed var(--cmx-hair); background: var(--surface-2);
}
.cmxGif__tag { font-family: var(--font-mono); font-size: .55rem; letter-spacing: .12em;
  padding: .15rem .4rem; border-radius: 4px; background: var(--y-red); color: var(--y-white); }
.cmxGif__cap { font-size: var(--fs-sm); color: var(--text-mid); }

/* ── reactions ─────────────────────────────────────────────────────────── */
.cmxRx { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .35rem; }
.cmxRx:empty { display: none; }
.cmxRx__chip {
  display: inline-flex; align-items: center; gap: .25rem; cursor: pointer;
  padding: .1rem .45rem; border-radius: var(--radius-pill);
  border: 1px solid var(--cmx-hair); background: var(--cmx-pane);
  font: inherit; font-size: .72rem; color: var(--text-mid);
  transition: border-color var(--dur) var(--ease-out), scale var(--dur) var(--ease-spring);
}
.cmxRx__chip:hover { border-color: var(--y-yellow); scale: 1.08; }
.cmxRx__chip i { font-style: normal; font-family: var(--font-mono); font-size: .6rem; color: var(--text-lo); }
.cmxRx__chip.is-bump { animation: cmxBump .4s var(--ease-spring); }
@keyframes cmxBump { 0% { scale: 1; } 45% { scale: 1.3; } 100% { scale: 1; } }

/* ── MESSAGE TOOLS ──────────────────────────────────────────────────────────
   Absolute, so they never take layout space — a row that appeared on hover
   would reflow the whole log under the cursor.

   Anchored to the TOP-OUTER corner of the message: right for messages you
   received, left for your own, which is the empty half of the name row in each
   case. -.5rem used to lift it into the previous message's bubble; it now sits
   on its own row. ---------------------------------------------------------- */
.cmxMsg__tools {
  position: absolute; top: -.15rem; right: 0; z-index: 3;
  display: flex; align-items: center; gap: .15rem; padding: .2rem;
  border-radius: var(--radius-pill); border: 1px solid var(--cmx-hair);
  background: rgb(13 13 15 / .95); backdrop-filter: blur(8px);
  box-shadow: 0 6px 18px -10px rgb(0 0 0 / .9);
  opacity: 0; translate: 0 4px; pointer-events: none;
  transition: opacity var(--dur) var(--ease-out), translate var(--dur) var(--ease-out);
}
.cmxMsg:hover .cmxMsg__tools,
.cmxMsg:focus-within .cmxMsg__tools { opacity: 1; translate: 0 0; pointer-events: auto; }

.cmxTool {
  width: 1.7rem; height: 1.7rem; display: grid; place-items: center; cursor: pointer;
  border: 0; border-radius: 50%; background: none; font-size: .8rem; color: var(--text-mid);
  transition: background-color var(--dur) var(--ease-out), scale var(--dur) var(--ease-spring),
              color var(--dur) var(--ease-out);
}
.cmxTool:hover { background: var(--surface-2); scale: 1.15; }

/* A hairline between the playful actions and the consequential ones. Three
   reaction emoji and a delete sitting flush against each other is a misclick
   with no undo on one side of it. */
.cmxTool__sep { width: 1px; height: 1rem; background: var(--cmx-hair); margin-inline: .15rem; flex: none; }

.cmxTool--more svg { width: 1rem; height: 1rem; }
.cmxTool--more:hover { color: var(--text-hi); }

/* ── typing ────────────────────────────────────────────────────────────── */
.cmxTyping {
  display: flex; align-items: center; gap: .25rem;
  margin: 0; padding: 0 var(--s-5) var(--s-2);
  font-size: var(--fs-xs); color: var(--text-lo);
}
.cmxTyping[hidden] { display: none; }
.cmxTyping span {
  width: .3rem; height: .3rem; border-radius: 50%; background: var(--text-lo);
  animation: cmxBounce 1.3s var(--ease-inout) infinite;
}
.cmxTyping span:nth-child(2) { animation-delay: .16s; }
.cmxTyping span:nth-child(3) { animation-delay: .32s; }
.cmxTyping i { font-style: normal; margin-left: .4rem; }
@keyframes cmxBounce {
  0%, 60%, 100% { translate: 0 0; opacity: .4; }
  30%           { translate: 0 -4px; opacity: 1; }
}

/* ── composer ──────────────────────────────────────────────────────────── */
.cmxComposer { border-top: 1px solid var(--cmx-hair); padding: var(--s-3) var(--s-5) var(--s-4); }
.cmxAs { font-size: var(--fs-xs); color: var(--text-lo); margin-bottom: var(--s-2);
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.cmxAs b { color: var(--text-hi); }
.cmxGate { margin: 0; text-align: center; color: var(--text-mid); font-size: var(--fs-sm); }
.cmxGate a { color: var(--accent-ink); }

.cmxTray {
  display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-3);
  padding: var(--s-3); border-radius: var(--radius);
  border: 1px solid var(--cmx-hair); background: var(--surface-2);
  animation: cmxTrayIn .28s var(--ease-out);
}
.cmxTray[hidden] { display: none; }
@keyframes cmxTrayIn { from { opacity: 0; translate: 0 8px; } to { opacity: 1; translate: 0 0; } }
.cmxTraySticker {
  width: 2.6rem; height: 2.6rem; display: grid; place-items: center; cursor: pointer;
  border: 1px solid transparent; border-radius: var(--radius); background: none; font-size: 1.5rem;
  transition: background-color var(--dur) var(--ease-out), scale var(--dur) var(--ease-spring);
}
.cmxTraySticker:hover { background: var(--cmx-pane2); scale: 1.15; }

.cmxForm { display: flex; align-items: center; gap: var(--s-2); }
.cmxForm input {
  flex: 1; min-width: 0; padding: .75rem 1rem;
  border-radius: var(--radius-pill); border: 1px solid var(--cmx-hair);
  background: var(--surface-2); color: var(--text-hi);
  font: inherit; font-size: var(--fs-sm); outline: none;
  transition: border-color var(--dur) var(--ease-out);
}
.cmxForm input:focus { border-color: var(--y-yellow); }
.cmxSend {
  width: 2.6rem; height: 2.6rem; flex: none; display: grid; place-items: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--y-yellow); color: var(--y-black); font-size: .9rem;
  transition: transform var(--dur) var(--ease-spring), box-shadow var(--dur) var(--ease-out);
}
.cmxSend:hover { transform: scale(1.08) translateX(1px); box-shadow: var(--glow-yellow); }
.cmxSend:active { transform: scale(.94); }

/* ── SIDE ──────────────────────────────────────────────────────────────── */
.cmxSide { grid-area: side; min-height: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.cmxSide__h {
  font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; color: var(--text-lo); margin: 0; font-weight: 500;
}
.cmxPeople { list-style: none; margin: 0; padding: 0; display: grid; gap: .2rem;
  overflow-y: auto; min-height: 0; }
.cmxPerson a {
  display: flex; align-items: center; gap: var(--s-3);
  padding: .45rem; border-radius: var(--radius); text-decoration: none;
  transition: background-color var(--dur) var(--ease-out);
}
.cmxPerson a:hover { background: var(--cmx-pane); }
.cmxPerson span { min-width: 0; }
.cmxPerson b { display: block; font-size: var(--fs-sm); color: var(--text-hi); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmxPerson i { display: block; font-style: normal; font-size: .62rem; color: var(--text-lo);
  font-family: var(--font-mono); }
.cmxSide__note {
  margin: 0; padding: var(--s-3); border-radius: var(--radius);
  border: 1px dashed var(--cmx-hair); background: var(--cmx-pane);
  font-size: var(--fs-xs); color: var(--text-lo); line-height: var(--lh-body);
}
.cmxSide__note b { color: var(--text-mid); }

.cmxFlag {
  text-align: center; margin: 0; padding: var(--s-3);
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-lo);
}
.cmxFlag code { background: var(--surface-2); padding: .1rem .35rem; border-radius: 4px; }

/* ══════════════════════════════════════════════════════════════════════════
   PROFILE + DISCOVER
   ══════════════════════════════════════════════════════════════════════════ */
.cmpWrap { max-width: 62rem; margin-inline: auto; padding: var(--s-10) var(--gutter) var(--s-20); }

/* ══════════════════════════════════════════════════════════════════════════
   PROFILE HEADER
   A banner, an avatar sitting on its edge, identity in the middle, and ONE
   fixed-width action column on the right where every button is the same width.
   ══════════════════════════════════════════════════════════════════════════ */
/* NO overflow:hidden here. It was clipping the ••• menu, which is positioned
   inside this card — half the panel simply was not painted. The banner rounds
   its OWN top corners instead, which is the only thing the clip was for. */
.cmpHead {
  position: relative;
  border-radius: var(--radius-lg);
  border: 1px solid var(--cmx-hair);
  background: var(--surface-1);
}

/* Drawn, never an upload. An empty cover-photo slot on a profile nobody has
   filled in looks broken; this cannot. */
.cmpHead__banner {
  height: 7rem;
  border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
  overflow: hidden;
  background:
    radial-gradient(120% 180% at 12% 0%, rgb(240 198 74 / .30), transparent 58%),
    radial-gradient(90% 160% at 88% 10%, rgb(232 84 74 / .18), transparent 60%),
    linear-gradient(180deg, var(--cmx-pane2), transparent),
    var(--surface-2);
  position: relative;
}
.cmpHead__banner::after {
  content: ''; position: absolute; inset: 0;
  background-image:
    linear-gradient(to right, rgb(255 255 255 / .05) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(255 255 255 / .05) 1px, transparent 1px);
  background-size: 34px 34px;
  mask-image: linear-gradient(180deg, #000, transparent);
}

.cmpHead__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--s-5);
  align-items: start;
  padding: 0 var(--s-6) var(--s-6);
  /* The avatar rides up onto the banner. Negative margin on the ROW rather than
     on the avatar, so the two side columns keep a shared baseline. */
  margin-top: -2.6rem;
}

.cmpHead__pic {
  border: 0; padding: 0; background: none; position: relative;
  border-radius: 50%; line-height: 0;
  /* The ring is what separates a dark portrait from a dark banner. */
  box-shadow: 0 0 0 4px var(--surface-1), 0 0 0 5px var(--cmx-hair);
  transition: box-shadow var(--dur) var(--ease-out);
}
button.cmpHead__pic { cursor: zoom-in; }
button.cmpHead__pic:hover, button.cmpHead__pic:focus-visible {
  box-shadow: 0 0 0 4px var(--surface-1), 0 0 0 6px rgb(240 198 74 / .75);
  outline: none;
}
/* The affordance. Without it a picture that opens is indistinguishable from
   one that does not — and the flat variant genuinely does not. */
.cmpHead__zoom {
  position: absolute; right: .1rem; bottom: .1rem;
  width: 1.9rem; height: 1.9rem; display: grid; place-items: center;
  border-radius: 50%; background: var(--y-yellow); color: var(--y-black);
  box-shadow: 0 0 0 3px var(--surface-1);
  opacity: 0; scale: .8;
  transition: opacity var(--dur) var(--ease-out), scale var(--dur) var(--ease-spring);
}
.cmpHead__zoom svg { width: .95rem; height: .95rem; }
button.cmpHead__pic:hover .cmpHead__zoom,
button.cmpHead__pic:focus-visible .cmpHead__zoom { opacity: 1; scale: 1; }

.cmpHead__id { min-width: 0; padding-top: 3rem; }
.cmpHead__name { font-family: var(--font-display); font-size: var(--fs-h2); font-weight: 500;
  color: var(--text-hi); margin: 0; line-height: 1.05; }
.cmpHead__at {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-2);
  margin: .35rem 0 0; font-family: var(--font-mono); font-size: var(--fs-sm);
  color: var(--accent-ink);
}
.cmpHead__bio { margin: var(--s-3) 0 0; color: var(--text-mid); font-size: var(--fs-body);
  line-height: var(--lh-body); max-width: 52ch; }
.cmpHead__meta { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: var(--s-4) 0 0; }

/* Chips, not a run-on line. Three facts separated by spaces read as one
   sentence with the punctuation missing. */
.cmpChip {
  padding: .25rem .6rem; border-radius: var(--radius-pill);
  border: 1px solid var(--cmx-hair); background: var(--cmx-pane);
  font-size: var(--fs-xs); color: var(--text-mid);
}
.cmpTag {
  padding: .15rem .5rem; border-radius: var(--radius-pill);
  border: 1px solid var(--cmx-hair); background: var(--cmx-pane);
  /* --font-body. There is no --font-sans in tokens.css, and an undefined
     custom property invalidates the WHOLE declaration silently — so this tag
     was inheriting the mono stack from .cmpHead__at above it. */
  font-family: var(--font-body); font-size: .62rem; font-weight: 600;
  color: var(--text-lo); letter-spacing: 0;
}
.cmpTag--on { border-color: rgb(62 158 95 / .45); color: var(--y-green); background: rgb(62 158 95 / .1); }
.cmpTag--lock { border-color: rgb(240 198 74 / .4); color: var(--accent-ink); }

/* ── THE ACTION COLUMN ──────────────────────────────────────────────────────
   One fixed width so every button is the same width, ordered by how often it
   is wanted. Destructive actions are NOT here — they are behind the ••• menu.
   Disconnect and Block used to sit out in the open as yellow underlined links,
   louder than the primary button and adjacent to each other, which is a
   misclick away from severing a relationship. Rare and irreversible belongs one
   deliberate step away, not one accidental one. -------------------------- */
.cmpAct {
  width: 13rem; flex: none; padding-top: 3rem;
  display: grid; gap: var(--s-2); align-content: start;
}
.cmpAct .cmxBtn { width: 100%; }
.cmpAct__conn { display: grid; gap: var(--s-2); }
.cmpAct__note {
  margin: 0; font-size: var(--fs-xs); color: var(--text-lo);
  line-height: var(--lh-body); text-align: center;
}

.cmpMore { position: relative; justify-self: center; }
.cmpMore__btn { width: auto !important; padding: .35rem 1rem; letter-spacing: .1em; }
.cmpMore__panel {
  position: absolute; right: 0; top: calc(100% + .4rem); z-index: 30;
  width: 17rem; padding: .35rem;
  border-radius: var(--radius); border: 1px solid var(--cmx-hair);
  background: var(--surface-2); backdrop-filter: var(--cmx-glass);
  box-shadow: 0 24px 60px -20px rgb(0 0 0 / .9);
  animation: cmxRise .18s var(--ease-out);
}
.cmpMore__panel[hidden] { display: none; }
.cmpMore__item {
  display: grid; gap: .15rem; width: 100%; text-align: left;
  padding: .55rem .7rem; border: 0; border-radius: var(--radius);
  background: none; cursor: pointer; font: inherit;
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-hi);
  transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.cmpMore__item i { font-style: normal; font-size: var(--fs-xs); font-weight: 400;
  color: var(--text-lo); line-height: var(--lh-body); }
.cmpMore__item:hover { background: var(--cmx-pane2); }
.cmpMore__item.is-danger:hover { background: rgb(232 84 74 / .14); color: var(--y-red); }

/* ── STATS ─────────────────────────────────────────────────────────────── */
.cmpStats {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-3); margin: var(--s-4) 0 var(--s-3);
}
.cmpStat {
  padding: var(--s-4) var(--s-5); border-radius: var(--radius-lg);
  border: 1px solid var(--cmx-hair); background: var(--cmx-pane);
  transition: border-color var(--dur) var(--ease-out);
}
/* Connections is marked, because it is the one people will assume is just the
   other two added up. */
.cmpStat--key { border-color: rgb(240 198 74 / .35);
  background: linear-gradient(160deg, rgb(240 198 74 / .1), transparent 70%), var(--cmx-pane); }
.cmpStat b { display: block; font-family: var(--font-display); font-size: var(--fs-h2);
  font-weight: 500; color: var(--text-hi); line-height: 1; }
.cmpStat--key b { color: var(--accent-ink); }
.cmpStat span { font-size: var(--fs-xs); color: var(--text-lo); }

.cmpLegend {
  margin: 0 0 var(--s-6); padding: var(--s-3) var(--s-5);
  border-radius: var(--radius); border: 1px dashed var(--cmx-hair);
  font-size: var(--fs-xs); color: var(--text-lo); line-height: var(--lh-body);
}
.cmpLegend b { color: var(--text-mid); }

.cmpPrivate {
  text-align: center; padding: var(--s-20) var(--gutter);
  display: grid; justify-items: center; gap: var(--s-3);
}
.cmpPrivate__lock { font-size: 2.5rem; margin: 0; }
.cmpPrivate h2 { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  color: var(--text-hi); margin: 0; }
.cmpPrivate p { color: var(--text-mid); margin: 0; max-width: 44ch; }

/* discover */
.cmpSearch { display: flex; gap: var(--s-3); margin-bottom: var(--s-6); }
.cmpSearch input {
  flex: 1; padding: .85rem 1.2rem; border-radius: var(--radius-pill);
  border: 1px solid var(--cmx-hair); background: var(--surface-1); color: var(--text-hi);
  font: inherit; font-size: var(--fs-body); outline: none;
}
.cmpSearch input:focus { border-color: var(--y-yellow); }

.cmpGrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-4); }
/* Same rule as .cmxRoom: no geometry change on its own hover. The lift moves
   to a shadow and a glow, which are paint-only and cannot move the hit area
   out from under the cursor. */
.cmpCard {
  display: grid; gap: var(--s-3); padding: var(--s-5);
  border-radius: var(--radius-lg); border: 1px solid var(--cmx-hair);
  background: var(--surface-1);
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out),
              background-color var(--dur) var(--ease-out);
}
.cmpCard:hover {
  border-color: rgb(240 198 74 / .45);
  background: var(--cmx-pane);
  box-shadow: 0 10px 30px -18px rgb(0 0 0 / .8), 0 0 0 1px rgb(240 198 74 / .12);
}
.cmpCard__top { display: flex; align-items: center; gap: var(--s-3); }
.cmpCard__top a { text-decoration: none; min-width: 0; }
.cmpCard__top b { display: block; font-size: var(--fs-body); color: var(--text-hi); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmpCard__top i { font-style: normal; font-family: var(--font-mono); font-size: .65rem; color: var(--accent-ink); }
.cmpCard__bio { margin: 0; font-size: var(--fs-sm); color: var(--text-mid); line-height: var(--lh-body); }
.cmpCard__meta { margin: 0; font-size: var(--fs-xs); color: var(--text-lo); }

/* ══════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   Each breakpoint restates the grid AREAS it changes. An element left holding
   an area name that no longer exists is auto-placed into an implicit row — a
   silent gap that reports no error.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 78rem) {
  .cmxShell {
    grid-template-columns: var(--cmx-rail) minmax(0, 1fr);
    grid-template-areas: "rail main";
  }
  /* The people list is the first thing to go: it repeats what the avatar
     stack in the header already says. */
  .cmxSide { display: none; }
  .cmpGrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 56rem) {
  .cmxShell {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "main";
    height: calc(100svh - 4.5rem);
  }
  .cmxRail { display: none; }
  /* Two columns: the avatar keeps its slot, the actions drop to a full-width
     row underneath. A 13rem action column beside a 30rem identity block is
     what makes a tablet layout feel cramped. */
  .cmpHead__row { grid-template-columns: auto minmax(0, 1fr); }
  /* Explicit columns, not auto-columns. With `grid-auto-columns: 1fr` the •••
     took an equal third of the row, so the two real actions were squeezed to
     pay for a control nobody uses often. It gets `auto`; they split the rest. */
  .cmpAct { grid-column: 1 / -1; width: 100%; padding-top: 0;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
    grid-auto-flow: column; align-items: start; }
  .cmpAct__note { grid-column: 1 / -1; text-align: left; }
  .cmpMore { justify-self: end; }
  .cmpMore__btn { width: auto !important; }
}

@media (max-width: 46rem) {
  .cmxShell { padding-inline: var(--s-3); }
  .cmxTop { padding: var(--s-3) var(--s-4); gap: var(--s-2); }
  .cmxTop__body p { display: none; }
  /* The stack stays — it was never the cause of the overflow here; an
     undeclared grid column on .cmxMain was. These are just to keep a long room
     name from pushing the bell off the row. */
  .cmxTop__body h2 { font-size: var(--fs-body-lg); overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
  .cmxWho { min-width: 0; }
  .cmxWho__txt { overflow: hidden; text-overflow: ellipsis; }
  .cmxLog { padding: var(--s-4) var(--s-4) var(--s-2); }
  .cmxComposer { padding: var(--s-3) var(--s-4) var(--s-4); }
  .cmxForm input { font-size: 1rem; }   /* 16px — below it iOS zooms on focus */
  .cmpGrid { grid-template-columns: minmax(0, 1fr); }
  .cmpStats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-2); }
  .cmpStat { padding: var(--s-3); text-align: center; }
  .cmpStat b { font-size: var(--fs-h3); }
  /* The whole row used to be display:none here, which meant delete, report and
     reactions did not exist at all on a phone — on the device most of these
     students actually use. Now the reaction emoji drop (they are in the •••
     menu on touch) and the ••• itself stays, permanently visible because there
     is no hover to reveal it with. */
  .cmxMsg__tools {
    display: flex; opacity: 1; translate: 0 0; pointer-events: auto;
    border: 0; background: none; box-shadow: none; padding: 0;
    top: .05rem;
  }
  .cmxMsg__tools .cmxTool[data-react],
  .cmxMsg__tools .cmxTool__sep { display: none; }
  .cmxTool--more {
    width: 1.9rem; height: 1.9rem;
    border: 1px solid var(--cmx-hair); background: var(--surface-1); color: var(--text-lo);
  }
}

@media (max-width: 40rem) {
  /* Centre everything. A 104px avatar beside a wrapped name in a 360px column
     leaves neither enough room to be the thing you look at first. */
  .cmpHead__row { grid-template-columns: minmax(0, 1fr); justify-items: center;
    text-align: center; padding-inline: var(--s-4); }
  .cmpHead__id { padding-top: 0; }
  .cmpHead__at, .cmpHead__meta { justify-content: center; }
  .cmpHead__bio { margin-inline: auto; }
  .cmpAct { grid-auto-flow: row; }
  .cmpMore { justify-self: stretch; }
  .cmpMore__panel { right: auto; left: 0; width: 100%; }
  .cmpLegend { padding: var(--s-3); }
}

@media (prefers-reduced-motion: reduce) {
  .cmpMore__panel { animation: none; }
  .cmxSky__orb, .cmxDot, .cmxMsg, .cmxSticker, .cmxSys,
  .cmxTyping span, .cmxTray, .cmxRx__chip.is-bump { animation: none; }
  .cmxRoom::before, .cmxRoom__emoji, .cmxBtn { transition: none; }
  .cmxLog { scroll-behavior: auto; }
}

/* ══════════════════════════════════════════════════════════════════════════
   SCROLLBARS
   The default macOS/Chrome bar is a grey slab that sits outside the design
   language of everything else on the page. These are scoped to the community
   pages rather than set globally — a scrollbar restyle is the kind of change
   that quietly breaks a page nobody was looking at.

   Both syntaxes, deliberately. `scrollbar-width`/`scrollbar-color` is the
   standard and the only thing Firefox honours; ::-webkit-scrollbar is
   non-standard but is what Chrome and Safari actually use, and it is the only
   one of the two that can do a gradient or a hover state.
   ══════════════════════════════════════════════════════════════════════════ */
.page-cmx, .page-cmp {
  scrollbar-width: thin;
  scrollbar-color: rgb(240 198 74 / .45) transparent;
}

.page-cmx ::-webkit-scrollbar,
.page-cmp ::-webkit-scrollbar,
.page-cmx::-webkit-scrollbar,
.page-cmp::-webkit-scrollbar { width: 10px; height: 10px; }

.page-cmx ::-webkit-scrollbar-track,
.page-cmp ::-webkit-scrollbar-track,
.page-cmx::-webkit-scrollbar-track,
.page-cmp::-webkit-scrollbar-track {
  background: transparent;
}

.page-cmx ::-webkit-scrollbar-thumb,
.page-cmp ::-webkit-scrollbar-thumb,
.page-cmx::-webkit-scrollbar-thumb,
.page-cmp::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: linear-gradient(180deg, rgb(240 198 74 / .5), rgb(240 198 74 / .22));
  /* A transparent border with background-clip is the only way to inset a
     webkit thumb — there is no padding on the pseudo-element. */
  border: 3px solid transparent;
  background-clip: content-box;
  transition: background-color var(--dur) var(--ease-out);
}

.page-cmx ::-webkit-scrollbar-thumb:hover,
.page-cmp ::-webkit-scrollbar-thumb:hover,
.page-cmx::-webkit-scrollbar-thumb:hover,
.page-cmp::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, var(--y-yellow), rgb(240 198 74 / .5));
  background-clip: content-box;
}

/* The corner where a vertical and a horizontal bar meet renders as an opaque
   grey square by default, which is the single most visible piece of unstyled
   chrome on a dark page. */
.page-cmx ::-webkit-scrollbar-corner,
.page-cmp ::-webkit-scrollbar-corner { background: transparent; }

/* The message log gets a slimmer one — a 10px bar beside a 4px-tall reaction
   chip looks like furniture rather than an affordance. */
.cmxLog::-webkit-scrollbar,
.cmxRooms::-webkit-scrollbar,
.cmxPeople::-webkit-scrollbar { width: 8px; }
.cmxLog { scrollbar-color: rgb(240 198 74 / .35) transparent; }

/* ══════════════════════════════════════════════════════════════════════════
   NOTIFICATIONS
   ══════════════════════════════════════════════════════════════════════════ */
.cmxBell { position: relative; flex: none; }
.cmxBell__badge {
  position: absolute; top: -.2rem; right: -.2rem;
  min-width: 1.05rem; height: 1.05rem; padding: 0 .2rem;
  display: grid; place-items: center; border-radius: 999px;
  background: var(--y-red); color: var(--y-white);
  font-family: var(--font-mono); font-size: .55rem; font-weight: 700;
  box-shadow: 0 0 0 2px var(--surface-1);
  animation: cmxPing .5s var(--ease-spring);
}
.cmxBell__badge[hidden] { display: none; }
@keyframes cmxPing { from { scale: 0; } to { scale: 1; } }

.cmxBell__panel {
  position: absolute; top: calc(100% + .6rem); right: 0; z-index: 40;
  width: min(22rem, 82vw); max-height: 24rem; overflow-y: auto;
  border-radius: var(--radius); border: 1px solid var(--cmx-hair);
  background: var(--surface-1); box-shadow: 0 30px 70px -30px rgb(0 0 0 / .9);
  animation: cmxDrop .24s var(--ease-out);
}
.cmxBell__panel[hidden] { display: none; }
@keyframes cmxDrop { from { opacity: 0; translate: 0 -8px; } to { opacity: 1; translate: 0 0; } }

.cmxBell__panel header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--cmx-hair);
  position: sticky; top: 0; background: var(--surface-1);
}
.cmxBell__panel header b { font-size: var(--fs-sm); color: var(--text-hi); }
.cmxBell__panel ul { list-style: none; margin: 0; padding: 0; }
.cmxBell__panel li { border-bottom: 1px solid var(--cmx-hair); }
.cmxBell__panel li:last-child { border-bottom: 0; }
.cmxBell__panel a {
  display: grid; gap: .15rem; padding: var(--s-3) var(--s-4); text-decoration: none;
  transition: background-color var(--dur) var(--ease-out);
}
.cmxBell__panel a:hover { background: var(--cmx-pane); }
.cmxBell__row b { font-size: var(--fs-sm); color: var(--text-hi); }
.cmxBell__row i { font-style: normal; font-size: var(--fs-xs); color: var(--text-mid); display: block;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmxBell__row time { font-family: var(--font-mono); font-size: .55rem; color: var(--text-lo); }
/* Unread carries a left bar. Colour alone would be the only signal, and it is
   the one a colour-blind reader cannot use. */
.cmxBell__panel li.is-unread a { border-left: 3px solid var(--y-yellow); background: rgb(240 198 74 / .05); }
.cmxBell__empty { padding: var(--s-8) var(--s-4); text-align: center; color: var(--text-lo); font-size: var(--fs-sm); }

@media (prefers-reduced-motion: reduce) {
  .cmxBell__badge, .cmxBell__panel { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LIVE SEARCH RESULTS
   ══════════════════════════════════════════════════════════════════════════ */
.cmpLive { display: grid; gap: .3rem; margin-top: var(--s-4); }
.cmpLive[hidden] { display: none; }
.cmpLive__row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: var(--s-4);
  padding: var(--s-3) var(--s-4); border-radius: var(--radius);
  border: 1px solid transparent; text-decoration: none;
  animation: cmpRowIn .28s var(--ease-out) backwards;
  transition: background-color var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease-out), translate var(--dur) var(--ease-out);
}
@keyframes cmpRowIn { from { opacity: 0; translate: 0 6px; } to { opacity: 1; translate: 0 0; } }
.cmpLive__row:hover {
  background: var(--cmx-pane); border-color: rgb(240 198 74 / .35); translate: 3px 0;
}
.cmpLive__body { min-width: 0; display: grid; gap: .1rem; }
.cmpLive__body b { font-size: var(--fs-body); color: var(--text-hi); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmpLive__body i { font-style: normal; font-family: var(--font-mono); font-size: .65rem;
  color: var(--accent-ink); }
.cmpLive__bio { font-size: var(--fs-xs); color: var(--text-lo);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmpLive__meta { font-family: var(--font-mono); font-size: .62rem; color: var(--text-lo);
  white-space: nowrap; }
.cmpLive__empty { padding: var(--s-10) 0; text-align: center; color: var(--text-lo); margin: 0; }

/* ══════════════════════════════════════════════════════════════════════════
   PROFILE PEEK
   ══════════════════════════════════════════════════════════════════════════ */
.cmxMsg__av.is-linked, .cmxMsg__who b.is-linked { cursor: pointer; }
.cmxMsg__av.is-linked .ycAv { transition: scale var(--dur) var(--ease-spring); }
.cmxMsg__av.is-linked:hover .ycAv { scale: 1.1; }
.cmxMsg__who b.is-linked:hover { color: var(--accent-ink); text-decoration: underline;
  text-underline-offset: 3px; }
.cmxMsg__av.is-linked:focus-visible, .cmxMsg__who b.is-linked:focus-visible {
  outline: 2px solid var(--y-yellow); outline-offset: 3px; border-radius: var(--radius-sm);
}

.cmxPeek {
  position: fixed; inset: 0; z-index: 340;
  display: grid; place-items: center; padding: var(--gutter);
  animation: cmxFadeIn .22s var(--ease-out);
}
.cmxPeek[hidden] { display: none; }
@keyframes cmxFadeIn { from { opacity: 0; } to { opacity: 1; } }

.cmxPeek__veil {
  position: absolute; inset: 0; border: 0; cursor: zoom-out;
  background: rgb(4 4 6 / .8); backdrop-filter: blur(10px);
}
.cmxPeek__box {
  position: relative; z-index: 1;
  width: min(22rem, 100%); padding: var(--s-8) var(--s-6) var(--s-6);
  display: grid; justify-items: center; gap: var(--s-2); text-align: center;
  border-radius: var(--radius-lg); border: 1px solid var(--cmx-hair);
  background: linear-gradient(170deg, var(--cmx-pane2), transparent 55%), var(--surface-1);
  box-shadow: 0 40px 100px -35px rgb(0 0 0 / .95);
  animation: cmxPeekIn .3s var(--ease-spring);
}
@keyframes cmxPeekIn { from { opacity: 0; scale: .92; translate: 0 10px; } to { opacity: 1; scale: 1; translate: 0 0; } }

.cmxPeek__x {
  position: absolute; top: var(--s-3); right: var(--s-3);
  width: 1.9rem; height: 1.9rem; display: grid; place-items: center; cursor: pointer;
  border-radius: 50%; border: 1px solid var(--cmx-hair); background: none;
  color: var(--text-lo); font-size: 1.1rem; line-height: 1;
  transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out),
              rotate var(--dur) var(--ease-out);
}
.cmxPeek__x:hover { background: var(--y-red); color: var(--y-white); border-color: transparent; rotate: 90deg; }

.cmxPeek__av .ycAv { box-shadow: 0 0 0 3px rgb(240 198 74 / .25); }
.cmxPeek__name { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  color: var(--text-hi); margin: var(--s-2) 0 0; line-height: 1.2; }
.cmxPeek__at { margin: 0; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--accent-ink); }
.cmxPeek__bio { margin: var(--s-2) 0 0; font-size: var(--fs-sm); color: var(--text-mid);
  line-height: var(--lh-body); }
.cmxPeek__bio[hidden] { display: none; }
.cmxPeek__meta { margin: 0; font-size: var(--fs-xs); color: var(--text-lo); }

.cmxPeek__stats {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s-4);
  margin: var(--s-3) 0; padding: var(--s-3) 0;
  border-block: 1px solid var(--cmx-hair); width: 100%;
  font-size: var(--fs-xs); color: var(--text-lo);
}
.cmxPeek__stats b { color: var(--text-hi); font-size: var(--fs-body); }
.cmxPeek__stats [hidden] { display: none; }

.cmxPeek__act { display: grid; gap: var(--s-2); width: 100%; }
.cmxPeek__act [hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .cmxPeek, .cmxPeek__box, .cmpLive__row { animation: none; }
  .cmpLive__row:hover, .cmxMsg__av.is-linked:hover .ycAv { translate: none; scale: 1; }
}

/* ── ANONYMOUS PEEK + REVEAL ─────────────────────────────────────────────── */
.cmxPeek__anon { width: 100%; display: grid; gap: var(--s-2); margin: var(--s-2) 0; }
.cmxPeek__anon[hidden] { display: none; }
.cmxPeek__anonLead {
  margin: 0; font-size: var(--fs-sm); color: var(--text-hi);
  padding: var(--s-2) var(--s-3); border-radius: var(--radius);
  background: var(--surface-2); border: 1px dashed var(--cmx-hair);
}
.cmxPeek__anonLead b { color: var(--accent-ink); }
.cmxPeek__anonBody { margin: 0; font-size: var(--fs-xs); color: var(--text-lo);
  line-height: var(--lh-body); text-align: left; }
.cmxPeek__askState {
  margin: 0; font-size: var(--fs-xs); color: var(--text-lo);
  padding: var(--s-2); border-radius: var(--radius); background: var(--cmx-pane);
}
.cmxPeek__askState[hidden] { display: none; }

.cmxBell__ask {
  padding: var(--s-4); background: rgb(240 198 74 / .07);
  border-left: 3px solid var(--y-yellow);
}
.cmxBell__ask p { margin: 0 0 var(--s-3); font-size: var(--fs-sm); color: var(--text-mid);
  line-height: var(--lh-body); }
.cmxBell__ask p b { color: var(--text-hi); }
.cmxBell__askAct { display: grid; gap: var(--s-2); }
.cmxBell__askAct .cmxBtn { width: 100%; font-size: var(--fs-xs); padding: .5rem .9rem; }
.cmxBell__askDone { margin: 0 !important; font-size: var(--fs-xs); color: var(--accent-ink); }

/* ── ATTACHMENTS ─────────────────────────────────────────────────────────── */
.cmxClip { position: relative; cursor: pointer; }
.cmxClip input { position: absolute; inset: 0; opacity: 0; width: 0; height: 0; }
.cmxClip:focus-within { border-color: var(--y-yellow); color: var(--accent-ink); }

.cmxShot {
  /* A block box does not answer to text-align, so an image inside a
     right-aligned message stayed pinned to the left of its own bubble column.
     margin-inline-start: auto is what actually moves it. */
  display: block; max-width: min(20rem, 100%); border-radius: var(--radius);
  overflow: hidden; border: 1px solid var(--cmx-hair); background: var(--surface-2);
  transition: border-color var(--dur) var(--ease-out), scale var(--dur) var(--ease-out);
}
.cmxShot:hover { border-color: rgb(240 198 74 / .5); scale: 1.01; }
.cmxShot img {
  display: block; width: 100%; height: auto;
  /* Bounded so a tall screenshot cannot take the whole scrollback. */
  max-height: 22rem; object-fit: cover;
}

.cmxUp {
  display: flex; align-items: center; gap: var(--s-3);
  margin-bottom: var(--s-2); padding: var(--s-2) var(--s-3);
  border-radius: var(--radius); border: 1px solid var(--cmx-hair);
  background: var(--cmx-pane); font-size: var(--fs-xs); color: var(--text-mid);
}
.cmxUp[hidden] { display: none; }
.cmxUp__bar { flex: 0 0 4rem; height: 3px; border-radius: 3px; background: var(--surface-2); overflow: hidden; display: none; }
.cmxUp.is-busy .cmxUp__bar { display: block; }
/* Indeterminate on purpose: fetch() gives no upload progress without dropping
   to XHR, and a fake percentage that jumps to 90 and waits is worse than an
   honest shuttle. */
.cmxUp__bar i {
  display: block; width: 40%; height: 100%;
  background: linear-gradient(to right, var(--y-yellow-700), var(--y-yellow));
  animation: cmxShuttle 1.1s var(--ease-inout) infinite;
}
@keyframes cmxShuttle { from { translate: -100% 0; } to { translate: 250% 0; } }

@media (prefers-reduced-motion: reduce) {
  .cmxUp__bar i { animation: none; width: 100%; }
  .cmxShot:hover { scale: 1; }
}

.cmxMsg.is-mine .cmxShot  { margin-left: auto; }
.cmxMsg.is-mine .cmxSticker,
.cmxMsg.is-mine .cmxGif    { margin-left: auto; }
/* The sticker is a text glyph, so it DOES follow text-align — but the GIF card
   is inline-flex and the sticker sits in a block, so both are pinned
   explicitly rather than relying on which one happens to inherit. */

/* ── NOTIFICATION AVATARS ────────────────────────────────────────────────── */
.cmxBell__panel a.cmxBell__row {
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start; gap: var(--s-3);
}
.cmxBell__av { flex: none; }
.cmxBell__txt { min-width: 0; display: grid; gap: .1rem; }
.cmxBell__askHead { display: flex; align-items: flex-start; gap: var(--s-3); margin-bottom: var(--s-3); }
.cmxBell__askHead p { margin: 0 !important; }

/* ══════════════════════════════════════════════════════════════════════════
   IMAGE VIEWER
   ══════════════════════════════════════════════════════════════════════════ */
button.cmxShot { position: relative; padding: 0; cursor: zoom-in; border: 1px solid var(--cmx-hair); }
.cmxShot__hint {
  position: absolute; top: .5rem; right: .5rem;
  width: 1.7rem; height: 1.7rem; display: grid; place-items: center;
  border-radius: 50%; background: rgb(8 8 11 / .7); backdrop-filter: blur(6px);
  color: var(--y-white); font-size: .8rem;
  opacity: 0; transition: opacity var(--dur) var(--ease-out);
}
.cmxShot:hover .cmxShot__hint, .cmxShot:focus-visible .cmxShot__hint { opacity: 1; }

.cmxView {
  position: fixed; inset: 0; z-index: 400;
  display: grid; place-items: center; padding: clamp(.4rem, 2vw, 1.5rem);
  animation: cmxFadeIn .22s var(--ease-out);
}
.cmxView[hidden] { display: none; }
.cmxView__veil {
  position: absolute; inset: 0; border: 0; cursor: zoom-out;
  background: rgb(3 3 5 / .93); backdrop-filter: blur(10px);
}
.cmxView__shell {
  position: relative; z-index: 1;
  width: min(78rem, 100%); height: min(50rem, 94dvh);
  display: grid;
  /* The COLUMN is declared, not just the rows. With rows alone the implicit
     column is max-content and the toolbar's unwrapped width would stretch the
     shell past the viewport — the same trap the flashcard popup hit. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--cmx-hair); background: var(--surface-1);
  box-shadow: 0 50px 130px -40px rgb(0 0 0 / .95);
  animation: cmxPeekIn .3s var(--ease-out);
}

.cmxView__bar {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--cmx-hair);
  background: var(--cmx-pane);
}
.cmxView__by { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: var(--s-2); }
.cmxView__by b { font-size: var(--fs-sm); color: var(--text-hi);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmxView__by time { font-family: var(--font-mono); font-size: .6rem; color: var(--text-lo); }

.cmxView__tools { display: flex; align-items: center; gap: .4rem; flex: none; }
.cmxView__tools .cmxIcon { width: 2.1rem; height: 2.1rem; text-decoration: none; }

/* Every control is a drawn path, not a text glyph. ⭳ has no glyph in the macOS
   UI font and rendered as a solid black bar — the most important button in the
   viewer looked like a rendering fault. A path is a path on every device. */
.cmxView svg, .cmxPhoto svg {
  width: 1.05rem; height: 1.05rem; fill: none;
  stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}

/* Zoom is one grouped control rather than three loose circles, so the row
   parses as [zoom] [rotate] [link] [DOWNLOAD] [close] at a glance. */
.cmxView__zoom {
  display: flex; align-items: center; gap: .1rem;
  padding: .15rem; border-radius: var(--radius-pill);
  border: 1px solid var(--cmx-hair); background: var(--cmx-pane);
}
.cmxView__zoom .cmxIcon { border: 0; background: none; width: 1.8rem; height: 1.8rem; }
.cmxView__pct {
  font-family: var(--font-mono); font-size: .62rem; color: var(--text-lo);
  min-width: 2.9rem; text-align: center; user-select: none;
}

/* DOWNLOAD is the only labelled control in the bar, and that is the point.
   Saving the picture is what people open a viewer for; leaving it as one
   ambiguous icon among six makes the common action the hardest to find. */
.cmxView__dl {
  display: inline-flex; align-items: center; gap: .45rem; flex: none;
  padding: .45rem .85rem; border-radius: var(--radius-pill);
  background: var(--y-yellow); color: var(--y-black);
  font-size: var(--fs-xs); font-weight: 700; text-decoration: none; cursor: pointer;
  box-shadow: var(--glow-yellow);
  transition: filter var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.cmxView__dl:hover { filter: brightness(1.08); box-shadow: var(--glow-yellow), 0 6px 18px -6px rgb(240 198 74 / .5); }
.cmxView__dl svg { stroke-width: 2.2; }

/* Paging arrows, over the image. Keyboard has ← →, touch has swipe; a mouse
   had neither. Hidden by the script when there is only one image. */
.cmxView__nav {
  position: absolute; top: 50%; translate: 0 -50%; z-index: 2;
  width: 2.6rem; height: 2.6rem; display: grid; place-items: center;
  border-radius: 50%; cursor: pointer;
  border: 1px solid var(--cmx-hair); background: rgb(8 8 11 / .72);
  backdrop-filter: blur(8px); color: var(--text-hi);
  opacity: 0; transition: opacity var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.cmxView__nav[hidden] { display: none; }
.cmxView__nav--prev { left: var(--s-3); }
.cmxView__nav--next { right: var(--s-3); }
.cmxView__pane:hover .cmxView__nav, .cmxView__nav:focus-visible { opacity: 1; }
.cmxView__nav:hover { background: rgb(240 198 74 / .9); color: var(--y-black); }

.cmxView__pane {
  position: relative;
  overflow: hidden; min-height: 0; display: grid; place-items: center;
  /* A checker under a transparent PNG, so alpha reads as alpha rather than as
     the page showing through a hole in the image. */
  background:
    repeating-conic-gradient(rgb(255 255 255 / .022) 0% 25%, transparent 0% 50%) 50% / 24px 24px,
    #06070a;
  cursor: default;
}
.cmxView__pane.is-zoomed { cursor: grab; }
.cmxView__pane.is-panning { cursor: grabbing; }
.cmxView__pane img {
  max-width: 100%; max-height: 100%;
  /* Composed from custom properties the script writes — it never sets
     `transform` itself, so nothing here can be clobbered by another writer. */
  translate: var(--x, 0px) var(--y, 0px);
  scale: var(--z, 1);
  rotate: var(--r, 0deg);
  transition: scale .18s var(--ease-out), rotate .3s var(--ease-out);
  user-select: none; -webkit-user-drag: none;
}
.cmxView__pane.is-panning img { transition: none; }

.cmxView__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-top: 1px solid var(--cmx-hair);
  font-family: var(--font-mono); font-size: .62rem; color: var(--text-lo);
}
.cmxView__keys kbd {
  padding: .05rem .3rem; border-radius: 4px;
  border: 1px solid var(--cmx-hair); background: var(--surface-2);
}

/* ── viewer on a tablet ─────────────────────────────────────────────────── */
@media (max-width: 60rem) {
  /* Copy-link is a desktop habit — there is nowhere obvious to paste it on a
     tablet, and the share sheet is a long-press away on the image itself. */
  .cmxView__tools [data-view-copy] { display: none; }
}

/* ── viewer on a phone ──────────────────────────────────────────────────── */
@media (max-width: 46rem) {
  .cmxView { padding: 0; }
  /* Edge to edge. A 12px gutter around a photo on a 375px screen costs 6% of
     the only thing the user opened this to look at. */
  .cmxView__shell { width: 100%; height: 100dvh; border-radius: 0; border: 0; }

  .cmxView__bar { padding: var(--s-3); gap: var(--s-2); }
  .cmxView__by b { font-size: var(--fs-xs); }
  /* The keyboard hints name keys a phone does not have. */
  .cmxView__keys { display: none; }
  .cmxView__foot { padding: var(--s-2) var(--s-3); }

  /* Zoom and rotate go: pinch already zooms, double-tap already toggles, and
     the device rotates the whole screen. What is left is the one control a
     phone genuinely cannot do for itself — DOWNLOAD — plus close. */
  .cmxView__zoom,
  .cmxView__tools [data-view-rotate] { display: none; }
  .cmxView__dl { padding: .5rem .9rem; }

  /* Arrows are permanently visible on touch, where there is no hover to
     reveal them — but only when there is more than one image to page to. */
  .cmxView__nav { opacity: 1; width: 2.3rem; height: 2.3rem; }
}

@media (prefers-reduced-motion: reduce) {
  .cmxView, .cmxView__shell { animation: none; }
  .cmxView__pane img { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   PROFILE PHOTO — 1:1
   A portrait wants none of the image viewer's machinery. No zoom, no rotate,
   no paging: just the picture, big, square, with a way to save it.
   ══════════════════════════════════════════════════════════════════════════ */
.cmxPhoto {
  position: fixed; inset: 0; z-index: 420;
  display: grid; place-items: center; padding: clamp(1rem, 5vw, 3rem);
  animation: cmxFadeIn .2s var(--ease-out);
}
.cmxPhoto[hidden] { display: none; }
.cmxPhoto__veil {
  position: absolute; inset: 0; border: 0; cursor: zoom-out;
  background: rgb(3 3 5 / .92); backdrop-filter: blur(14px);
}
.cmxPhoto__box {
  position: relative; z-index: 1; margin: 0;
  width: min(30rem, 100%);
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--cmx-hair); background: var(--surface-1);
  box-shadow: 0 50px 130px -40px rgb(0 0 0 / .95);
  animation: cmxPeekIn .28s var(--ease-out);
}

/* THE SQUARE. aspect-ratio: 1 on the frame and object-fit: cover on the image,
   so a portrait or a landscape upload is CENTRE-CROPPED to 1:1 rather than
   letterboxed. A square frame with bars down the sides is not a square frame,
   it is a rectangle with a border. */
.cmxPhoto__frame {
  aspect-ratio: 1; width: 100%;
  background:
    repeating-conic-gradient(rgb(255 255 255 / .02) 0% 25%, transparent 0% 50%) 50% / 22px 22px,
    #06070a;
}
.cmxPhoto__frame img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%;
  display: block; user-select: none; -webkit-user-drag: none;
}

.cmxPhoto__cap {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-top: 1px solid var(--cmx-hair);
  background: var(--cmx-pane);
}
.cmxPhoto__cap b { font-size: var(--fs-sm); color: var(--text-hi); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmxPhoto__dl {
  display: inline-flex; align-items: center; gap: .4rem; flex: none;
  padding: .4rem .8rem; border-radius: var(--radius-pill);
  background: var(--y-yellow); color: var(--y-black);
  font-size: var(--fs-xs); font-weight: 700; text-decoration: none; cursor: pointer;
  transition: filter var(--dur) var(--ease-out);
}
.cmxPhoto__dl:hover { filter: brightness(1.08); }
.cmxPhoto__x {
  position: absolute; top: var(--s-3); right: var(--s-3); z-index: 2;
  background: rgb(8 8 11 / .72); backdrop-filter: blur(8px);
}
.cmxPhoto__x:hover { background: var(--y-red); color: var(--y-white); border-color: transparent; }


@media (max-width: 46rem) {
  .cmxPhoto { padding: var(--s-3); }
  .cmxPhoto__box { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .cmxPhoto, .cmxPhoto__box { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   DIRECT MESSAGES
   Two panes on a desktop; on a phone the list IS the page and the conversation
   replaces it. A 320px column trying to hold both is neither.
   ══════════════════════════════════════════════════════════════════════════ */
.dmShell {
  position: relative; z-index: 1;
  max-width: 76rem; margin-inline: auto;
  padding: var(--s-5) var(--gutter) 0;
  display: grid;
  grid-template-columns: 21rem minmax(0, 1fr);
  gap: var(--s-4);
  height: calc(100svh - 5.5rem);
}

/* ── the list ──────────────────────────────────────────────────────────── */
.dmList {
  min-height: 0; display: grid;
  /* Column declared, not just rows — with rows alone the implicit column
     resolves to max-content and the rows size to their longest preview. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr);
  border-radius: var(--radius-lg); border: 1px solid var(--cmx-hair);
  background: var(--surface-1); overflow: hidden;
}
.dmList__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-4) var(--s-4) var(--s-3);
}
.dmList__head h1 { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  color: var(--text-hi); margin: 0; }

.dmTabs { display: flex; gap: .2rem; padding: 0 var(--s-3) var(--s-3); }
.dmTab {
  flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: .35rem;
  padding: .45rem .3rem; border-radius: var(--radius-pill); cursor: pointer;
  border: 1px solid transparent; background: none; font: inherit;
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-lo);
  transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.dmTab:hover { color: var(--text-hi); background: var(--cmx-pane); }
.dmTab.is-on { background: var(--cmx-pane2); color: var(--text-hi); border-color: var(--cmx-hair); }
.dmTab__n {
  font-family: var(--font-mono); font-size: .58rem; padding: .05rem .3rem;
  border-radius: var(--radius-pill); background: var(--y-yellow); color: var(--y-black);
}

.dmPane { display: none; overflow-y: auto; min-height: 0; padding: 0 var(--s-2) var(--s-3); }
.dmPane.is-on { display: block; }
.dmEmpty { margin: var(--s-5) var(--s-3); font-size: var(--fs-sm); color: var(--text-lo);
  line-height: var(--lh-body); }

.dmRow {
  position: relative; isolation: isolate;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: var(--s-3);
  padding: var(--s-3); border-radius: var(--radius); text-decoration: none;
  transition: background-color var(--dur) var(--ease-out);
}
/* Same no-jitter rule as the room list: the accent bar moves, the row does not. */
.dmRow::before {
  content: ''; position: absolute; z-index: -1; left: 0; top: .5rem; bottom: .5rem;
  width: 3px; border-radius: 3px; background: var(--y-yellow);
  scale: 1 0; transition: scale var(--dur) var(--ease-spring);
}
.dmRow:hover { background: var(--cmx-pane); }
.dmRow:hover::before, .dmRow.is-on::before { scale: 1 1; }
.dmRow.is-on { background: var(--cmx-pane2); }
.dmRow__body { min-width: 0; }
.dmRow__body b { display: block; font-size: var(--fs-sm); color: var(--text-hi); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dmRow__body i { display: block; font-style: normal; font-size: var(--fs-xs); color: var(--text-lo);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dmRow__meta { display: grid; gap: .25rem; justify-items: end; flex: none; }
.dmRow__meta time { font-family: var(--font-mono); font-size: .58rem; color: var(--text-lo); }
.dmRow__n {
  min-width: 1.15rem; padding: .05rem .3rem; border-radius: var(--radius-pill);
  background: var(--y-yellow); color: var(--y-black);
  font-family: var(--font-mono); font-size: .58rem; font-weight: 700; text-align: center;
}
.dmRow__go { font-size: .62rem; color: var(--accent-ink); font-weight: 600; }

.dmReq {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s-3) var(--s-3); align-items: center;
  padding: var(--s-3); border-radius: var(--radius);
  border: 1px solid var(--cmx-hair); background: var(--cmx-pane);
  margin-bottom: var(--s-2);
}
.dmReq__body { min-width: 0; }
.dmReq__body b { display: block; font-size: var(--fs-sm); color: var(--text-hi);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dmReq__body i {
  display: block; font-style: normal; font-size: var(--fs-xs); color: var(--text-lo);
  /* One line. A college name wrapping to two pushes the row taller and leaves
     the avatar floating against the middle of a paragraph. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dmReq__act { grid-column: 1 / -1; display: flex; gap: var(--s-2); }
.dmReq__act .cmxBtn { flex: 1; }
.cmxBtn--sm { padding: .4rem .7rem; font-size: var(--fs-xs); }
.dmReq__done { grid-column: 1 / -1; margin: 0; font-size: var(--fs-xs); color: var(--text-lo); }

/* ── the conversation ──────────────────────────────────────────────────── */
.dmMain {
  min-height: 0; min-width: 0; display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  border-radius: var(--radius-lg); border: 1px solid var(--cmx-hair);
  background: linear-gradient(180deg, var(--cmx-pane2), transparent 30%), var(--surface-1);
  overflow: hidden;
}
.dmBlank {
  grid-row: 1 / -1; display: grid; place-content: center; justify-items: center;
  gap: var(--s-3); padding: var(--s-8); text-align: center;
}
.dmBlank__ico { font-size: 2.4rem; margin: 0; }
.dmBlank h2 { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  color: var(--text-hi); margin: 0; }
.dmBlank p { margin: 0; color: var(--text-mid); max-width: 38ch; line-height: var(--lh-body); }

.dmTop {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--cmx-hair);
}
.dmTop__back { display: none; font-size: 1.4rem; text-decoration: none; }
.dmTop__who { display: flex; align-items: center; gap: var(--s-3); text-decoration: none;
  min-width: 0; flex: 1; }
.dmTop__who b { display: block; font-size: var(--fs-body); color: var(--text-hi); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dmTop__who i { font-style: normal; font-family: var(--font-mono); font-size: .62rem;
  color: var(--accent-ink); }
.dmTop__tag { flex: none; font-size: .62rem; color: var(--text-lo);
  padding: .2rem .5rem; border-radius: var(--radius-pill); background: var(--cmx-pane); }

.dmLog {
  overflow-y: auto; min-height: 0; scroll-behavior: smooth;
  padding: var(--s-5) var(--s-5) var(--s-2);
  display: flex; flex-direction: column; gap: var(--s-3);
}
.dmLog__start { margin: auto auto var(--s-5); text-align: center; font-size: var(--fs-sm);
  color: var(--text-lo); max-width: 34ch; line-height: var(--lh-body); }
.dmLog__start b { color: var(--text-mid); }

.dmMsg {
  max-width: min(72%, 34rem); align-self: flex-start;
  display: grid; gap: .2rem; justify-items: start;
  animation: cmxRise .26s var(--ease-out) both;
}
.dmMsg.is-mine { align-self: flex-end; justify-items: end; }
.dmBubble {
  padding: .6rem .9rem; border-radius: 1.1rem 1.1rem 1.1rem .3rem;
  background: var(--surface-2); color: var(--text-hi);
  font-size: var(--fs-sm); line-height: var(--lh-body);
  overflow-wrap: anywhere;
}
.dmMsg.is-mine .dmBubble {
  border-radius: 1.1rem 1.1rem .3rem 1.1rem;
  background: var(--y-yellow); color: var(--y-black); font-weight: 500;
}
.dmMsg__meta { margin: 0; display: flex; align-items: center; gap: .35rem;
  font-family: var(--font-mono); font-size: .55rem; color: var(--text-lo); }
.dmTick { color: var(--text-lo); }
.dmTick.is-read { color: var(--y-green); }
.dmShot img { max-width: 15rem; }

.dmComposer { padding: var(--s-3) var(--s-4) var(--s-4); border-top: 1px solid var(--cmx-hair); }
.dmGone { margin: 0; padding: var(--s-3); text-align: center; font-size: var(--fs-sm);
  color: var(--text-lo); border-radius: var(--radius); background: var(--cmx-pane); }

/* ── DM responsive ─────────────────────────────────────────────────────── */
@media (max-width: 64rem) {
  .dmShell { grid-template-columns: 17rem minmax(0, 1fr); }
}

@media (max-width: 52rem) {
  /* ONE PANE AT A TIME. Which one is decided by the SERVER — .is-open is set
     when a thread is open — rather than by a script, so the right pane is
     painted on the first frame instead of flashing the wrong one. */
  .dmShell {
    grid-template-columns: minmax(0, 1fr);
    padding-inline: var(--s-3);
    height: calc(100svh - 4.5rem);
  }
  .dmShell .dmMain { display: none; }
  .dmShell.is-open .dmList { display: none; }
  .dmShell.is-open .dmMain { display: grid; }
  .dmTop__back { display: grid; }
  .dmMsg { max-width: 84%; }
  .dmLog { padding: var(--s-4) var(--s-3) var(--s-2); }
  .dmComposer { padding: var(--s-3); }
  .dmList { border-radius: var(--radius); }
  /* 16px — anything smaller and iOS zooms the whole page on focus. */
  .dmComposer input[type="text"] { font-size: 1rem; }
}

/* Copy-link confirmation. A class, not a text swap: the button's content is an
   inline <svg>, and writing "✓" into it would delete the icon permanently. */
.cmxView__tools [data-view-copy].is-done { color: var(--y-green); border-color: var(--y-green); }
.cmxView__tools [data-view-copy].is-done svg { stroke-dasharray: 0; }

/* The way into Messages, with the only two counts on the page that mean
   "something is waiting on you". */
.cmxRail__msgs { display: flex; align-items: center; justify-content: center; gap: var(--s-2); }
.cmxRail__n {
  min-width: 1.2rem; padding: .05rem .35rem; border-radius: var(--radius-pill);
  background: var(--y-yellow); color: var(--y-black);
  font-family: var(--font-mono); font-size: .6rem; font-weight: 700;
}
.cmxRail__foot { display: grid; gap: var(--s-2); }

/* Follow and Connect sit side by side on a discover card. Same reason they are
   two buttons everywhere else: two different decisions. */
.cmpCard__act { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.cmpCard__act .cmxBtn { flex: 1; min-width: 6rem; }


/* Connect / Message inside the profile peek card. */
.cmxPeek__row { display: flex; gap: var(--s-2); }
.cmxPeek__row .cmxBtn { flex: 1; }
.cmxPeek__connState {
  margin: 0; font-size: var(--fs-xs); color: var(--text-lo);
  line-height: var(--lh-body); text-align: center;
}
.cmxPeek__connState[hidden] { display: none; }



/* ══════════════════════════════════════════════════════════════════════════
   DELETED MESSAGES
   A tombstone, never a gap. A conversation that silently loses a line leaves
   people re-reading for something that is not there any more.
   ══════════════════════════════════════════════════════════════════════════ */
.cmxGone {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .5rem .8rem; border-radius: 1.1rem;
  border: 1px dashed var(--cmx-hair); background: transparent;
  font-size: var(--fs-sm); font-style: italic; color: var(--text-lo);
}
.cmxGone span { font-style: normal; opacity: .7; }

/* The delete menu. Two items, each saying what it actually does — "delete" on
   its own is the word people mean two different things by. */
.cmxDel {
  position: fixed; z-index: 500; width: 17rem; padding: .35rem;
  border-radius: var(--radius); border: 1px solid var(--cmx-hair);
  background: var(--surface-2); backdrop-filter: var(--cmx-glass);
  box-shadow: 0 24px 60px -20px rgb(0 0 0 / .9);
  animation: cmxRise .16s var(--ease-out);
}
.cmxDel__item {
  display: grid; gap: .15rem; width: 100%; text-align: left;
  padding: .55rem .7rem; border: 0; border-radius: var(--radius);
  background: none; cursor: pointer; font: inherit;
  transition: background-color var(--dur) var(--ease-out);
}
.cmxDel__item b { font-size: var(--fs-sm); font-weight: 600; color: var(--text-hi); }
.cmxDel__item i { font-style: normal; font-size: var(--fs-xs); color: var(--text-lo);
  line-height: var(--lh-body); }
.cmxDel__item:hover { background: var(--cmx-pane2); }
.cmxDel__item.is-danger:hover { background: rgb(232 84 74 / .14); }
.cmxDel__item.is-danger:hover b { color: var(--y-red); }
.cmxDel__cancel {
  width: 100%; padding: .45rem; margin-top: .2rem; border: 0; border-radius: var(--radius);
  background: none; cursor: pointer; font: inherit; font-size: var(--fs-xs);
  color: var(--text-lo); border-top: 1px solid var(--cmx-hair);
}
.cmxDel__cancel:hover { color: var(--text-hi); }

/* The DM ••• — the same control, in the same place relative to the bubble, as
   the rooms. One gesture to learn rather than two.

   It hangs in the gutter on the OUTER side of the bubble, so it never covers
   the text. .dmMsg is capped at 72% width, which is what guarantees the gutter
   exists to hang it in. */
.dmMsg { position: relative; }
.dmMsg__more {
  position: absolute; top: .35rem; z-index: 3;
  width: 1.7rem; height: 1.7rem; display: grid; place-items: center;
  border-radius: 50%; cursor: pointer;
  border: 1px solid var(--cmx-hair); background: var(--surface-2); color: var(--text-lo);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out), color var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease-out);
}
.dmMsg__more svg { width: .95rem; height: .95rem; }
.dmMsg.is-mine .dmMsg__more { left: -2.2rem; }
.dmMsg:not(.is-mine) .dmMsg__more { right: -2.2rem; }
.dmMsg:hover .dmMsg__more, .dmMsg__more:focus-visible { opacity: 1; }
.dmMsg__more:hover { color: var(--text-hi); border-color: rgb(240 198 74 / .5); }

@media (hover: none) {
  /* Permanently visible, because there is no hover to reveal it with — and
     tucked INSIDE the bubble column, since a 2.2rem gutter does not exist at
     phone widths and the button would sit off the screen edge. */
  .dmMsg__more { opacity: .5; top: -.35rem; }
  .dmMsg.is-mine .dmMsg__more { left: auto; right: -.2rem; }
  .dmMsg:not(.is-mine) .dmMsg__more { right: auto; left: -.2rem; }
}

/* Reactions inside the menu, for touch — there is no hover row to tap there. */
.cmxDel__rx {
  display: flex; gap: .2rem; padding: .35rem .3rem;
  border-bottom: 1px solid var(--cmx-hair); margin-bottom: .2rem;
}
.cmxDel__emo {
  flex: 1; padding: .35rem 0; border: 0; border-radius: var(--radius);
  background: none; cursor: pointer; font-size: 1.15rem; line-height: 1;
  transition: background-color var(--dur) var(--ease-out), scale var(--dur) var(--ease-spring);
}
.cmxDel__emo:active { scale: 1.25; }
.cmxDel__emo:hover { background: var(--cmx-pane2); }

/* ══════════════════════════════════════════════════════════════════════════
   QUESTIONS AND ANSWERS
   A question is a message wearing a frame — same log, same poll, same delete.
   Only the chrome differs, so only the chrome is styled here.
   ══════════════════════════════════════════════════════════════════════════ */
.cmxQ {
  display: grid; gap: var(--s-2);
  padding: var(--s-4); border-radius: var(--radius);
  border: 1px solid rgb(240 198 74 / .28);
  background: linear-gradient(160deg, rgb(240 198 74 / .07), transparent 60%), var(--surface-2);
}
/* Answered is the state worth colouring: it is the proof the room works. */
.cmxQ.is-solved {
  border-color: rgb(62 158 95 / .38);
  background: linear-gradient(160deg, rgb(62 158 95 / .08), transparent 60%), var(--surface-2);
}
.cmxQ__tag { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin: 0; }
.cmxQ__badge {
  padding: .1rem .45rem; border-radius: var(--radius-pill);
  background: var(--y-yellow); color: var(--y-black);
  font-size: .55rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
}
.cmxQ.is-solved .cmxQ__badge { background: var(--y-green); color: var(--y-white); }
.cmxQ__unit, .cmxQ__n {
  font-family: var(--font-mono); font-size: .58rem; color: var(--text-lo);
}
.cmxQ__body { font-size: var(--fs-body); color: var(--text-hi); line-height: var(--lh-body); }
.cmxQ__reply {
  justify-self: start; padding: .35rem .8rem; border-radius: var(--radius-pill);
  border: 1px solid var(--cmx-hair); background: var(--cmx-pane); cursor: pointer;
  font: inherit; font-size: var(--fs-xs); font-weight: 600; color: var(--accent-ink);
  transition: border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.cmxQ__reply:hover { border-color: var(--y-yellow); background: var(--cmx-pane2); }

/* ── the answers panel ──────────────────────────────────────────────────── */
.cmxAns {
  position: fixed; inset: 0; z-index: 430;
  display: grid; place-items: center; padding: clamp(.5rem, 3vw, 2rem);
  animation: cmxFadeIn .2s var(--ease-out);
}
.cmxAns[hidden] { display: none; }
.cmxAns__veil { position: absolute; inset: 0; border: 0; cursor: pointer;
  background: rgb(3 3 5 / .85); backdrop-filter: blur(10px); }
.cmxAns__box {
  position: relative; z-index: 1; width: min(38rem, 100%); max-height: 90dvh;
  display: grid; grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--cmx-hair); background: var(--surface-1);
  box-shadow: 0 50px 130px -40px rgb(0 0 0 / .95);
  animation: cmxPeekIn .26s var(--ease-out);
}
.cmxAns__box > header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--cmx-hair);
  background: var(--cmx-pane);
}
.cmxAns__q { margin: .35rem 0 0; font-size: var(--fs-body); color: var(--text-hi);
  line-height: var(--lh-body); }
.cmxAns__list { overflow-y: auto; min-height: 0; padding: var(--s-4) var(--s-5); display: grid; gap: var(--s-4);
  scrollbar-width: thin; scrollbar-color: rgb(240 198 74 / .45) transparent; }
.cmxAns__empty { margin: 0; font-size: var(--fs-sm); color: var(--text-lo); line-height: var(--lh-body); }

.cmxAns__one { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s-3); }
.cmxAns__one.is-accepted {
  padding: var(--s-3); margin: calc(var(--s-3) * -1);
  border-radius: var(--radius);
  background: linear-gradient(160deg, rgb(62 158 95 / .1), transparent 60%);
  border: 1px solid rgb(62 158 95 / .3);
}
.cmxAns__body { min-width: 0; display: grid; gap: .25rem; }
.cmxAns__who { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin: 0; }
.cmxAns__who b { font-size: var(--fs-sm); color: var(--text-hi); }
.cmxAns__who time { font-family: var(--font-mono); font-size: .58rem; color: var(--text-lo); }
.cmxAns__tick { font-size: .6rem; font-weight: 700; color: var(--y-green); }
.cmxAns__text { margin: 0; font-size: var(--fs-sm); color: var(--text-mid);
  line-height: var(--lh-body); overflow-wrap: anywhere; }
.cmxAns__accept {
  justify-self: start; margin-top: .35rem;
  padding: .3rem .7rem; border-radius: var(--radius-pill); cursor: pointer;
  border: 1px solid rgb(62 158 95 / .45); background: rgb(62 158 95 / .1);
  font: inherit; font-size: var(--fs-xs); font-weight: 600; color: var(--y-green);
}
.cmxAns__accept:hover { background: rgb(62 158 95 / .2); }

.cmxAns__form { padding: var(--s-4) var(--s-5); border-top: 1px solid var(--cmx-hair);
  background: var(--cmx-pane); display: grid; gap: var(--s-3); }
.cmxAns__form textarea {
  width: 100%; padding: .7rem .9rem; border-radius: var(--radius); resize: vertical;
  border: 1px solid var(--cmx-hair); background: var(--surface-0); color: var(--text-hi);
  font: inherit; font-size: var(--fs-sm); line-height: var(--lh-body); outline: none;
}
.cmxAns__form textarea:focus { border-color: var(--y-yellow); }
.cmxAns__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.cmxAns__hint { font-size: var(--fs-xs); color: var(--text-lo); }

/* ── reputation ─────────────────────────────────────────────────────────────
   One number, counting one thing: answers somebody else said solved their
   problem. Not messages sent — a score that rewards volume produces volume.
   ══════════════════════════════════════════════════════════════════════════ */
.cmxRep {
  padding: .05rem .4rem; border-radius: var(--radius-pill);
  font-size: .55rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  border: 1px solid var(--cmx-hair); color: var(--text-lo);
}
.cmxRep.is-helper  { border-color: rgb(62 158 95 / .45);  color: var(--y-green); }
.cmxRep.is-regular { border-color: rgb(240 198 74 / .5);  color: var(--accent-ink); }
.cmxRep.is-legend  { border-color: rgb(240 198 74 / .8);  color: var(--y-black);
  background: var(--y-yellow); }

@media (max-width: 46rem) {
  .cmxAns { padding: 0; }
  .cmxAns__box { width: 100%; height: 100dvh; max-height: none; border-radius: 0; border: 0; }
  .cmxAns__form textarea { font-size: 1rem; }
}
@media (prefers-reduced-motion: reduce) { .cmxAns, .cmxAns__box { animation: none; } }

/* Why somebody is being suggested. "Suggested" with no reason feels arbitrary,
   and an arbitrary recommendation is one people scroll past. */
.cmpWhy {
  margin: 0; font-size: var(--fs-xs); font-weight: 600; color: var(--accent-ink);
}

/* ══════════════════════════════════════════════════════════════════════════
   COMMUNITY SUB-NAV
   Six surfaces that were reachable only from whichever one you landed on.
   One strip on all of them; a count on the two things waiting on the user and
   nothing else, because a badge on everything is a badge on nothing.
   ══════════════════════════════════════════════════════════════════════════ */
.cnBar {
  position: relative; z-index: 2;
  border-bottom: 1px solid var(--cmx-hair);
  background: linear-gradient(180deg, var(--cmx-pane), transparent);
}
.cnBar__inner {
  max-width: var(--cmx-max); margin-inline: auto;
  display: flex; gap: .2rem; align-items: center;
  padding: var(--s-2) var(--gutter);
  overflow-x: auto; scrollbar-width: none;
}
.cnBar__inner::-webkit-scrollbar { display: none; }
.cnBar__a {
  display: inline-flex; align-items: center; gap: .45rem; flex: none;
  padding: .5rem .85rem; border-radius: var(--radius-pill);
  text-decoration: none; white-space: nowrap;
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-lo);
  transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.cnBar__a:hover { color: var(--text-hi); background: var(--cmx-pane2); }
.cnBar__a.is-on { color: var(--text-hi); background: var(--cmx-pane2);
  box-shadow: inset 0 -2px 0 var(--y-yellow); }
.cnBar__i { font-size: .95rem; line-height: 1; }
.cnBar__a--end { margin-left: auto; }
.cnBar__n {
  min-width: 1.15rem; padding: .05rem .3rem; border-radius: var(--radius-pill);
  background: var(--y-yellow); color: var(--y-black);
  font-family: var(--font-mono); font-size: .58rem; font-weight: 700; text-align: center;
}
@media (max-width: 46rem) {
  .cnBar__inner { padding-inline: var(--s-3); }
  .cnBar__a { padding: .45rem .7rem; font-size: var(--fs-xs); }
  /* On a phone the strip already scrolls; pinning "You" to the right would
     push it permanently off the end. */
  .cnBar__a--end { margin-left: 0; }
}

/* Told where the consequence is felt, not only in settings. Somebody who
   cannot find themselves in a people search should learn why on that page. */
.cmpAgeNudge {
  margin: 0 0 var(--s-4); padding: var(--s-4) var(--s-5);
  border-radius: var(--radius); border: 1px dashed rgb(240 198 74 / .4);
  background: rgb(240 198 74 / .06);
  font-size: var(--fs-sm); color: var(--text-lo); line-height: var(--lh-body);
}
.cmpAgeNudge b { color: var(--text-hi); }

/* ── endless people feed ──────────────────────────────────────────────────── */
.cmpMore {
  display: block; width: 100%; margin: var(--s-8) 0 0;
  padding: var(--s-5); border-radius: var(--radius);
  border: 1px dashed var(--cmx-hair, rgb(255 255 255 / .12));
  background: none; cursor: pointer;
  font: inherit; font-size: var(--fs-sm); color: var(--text-lo);
}
.cmpMore[hidden], .cmpEnd[hidden] { display: none; }

/* ── the invite that closes the feed ───────────────────────────────────── */
.cmpInvite {
  margin: var(--s-10) 0 0; padding: var(--s-8);
  border-radius: var(--radius-lg);
  border: 1px solid rgb(240 198 74 / .28);
  background:
    radial-gradient(80% 120% at 0% 0%, rgb(240 198 74 / .08), transparent 60%),
    var(--surface-1);
  text-align: center;
}
.cmpInvite[hidden] { display: none; }
.cmpInvite__k {
  margin: 0 0 var(--s-2);
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--text-lo);
}
.cmpInvite__h {
  margin: 0 0 var(--s-2);
  font-family: var(--font-display); font-size: var(--fs-h2);
  letter-spacing: -.02em; color: var(--text-hi);
}
.cmpInvite__p {
  margin: 0 auto var(--s-6); max-width: 46ch;
  font-size: var(--fs-sm); line-height: var(--lh-body); color: var(--text-lo);
}
.cmpInvite__row {
  display: flex; flex-wrap: wrap; gap: var(--s-2); justify-content: center;
}
.cmpShare {
  display: inline-flex; align-items: center; justify-content: center;
  padding: .6rem 1.1rem; border-radius: var(--radius-pill);
  border: 1px solid var(--surface-line); background: var(--surface-2);
  cursor: pointer; font: inherit; font-size: var(--fs-sm); font-weight: 600;
  color: var(--text-hi); text-decoration: none;
  transition: border-color var(--dur) var(--ease-out),
              background-color var(--dur) var(--ease-out);
}
.cmpShare:hover { border-color: var(--y-yellow); background: rgb(240 198 74 / .1); }
.cmpShare[hidden] { display: none; }
.cmpShare--wa:hover { border-color: #25d366; background: rgb(37 211 102 / .12); }
.cmpShare--native, .cmpShare--copy { background: var(--y-yellow); color: var(--y-black); border-color: transparent; }
.cmpShare--native:hover, .cmpShare--copy:hover { background: #ffd965; border-color: transparent; }

@media (max-width: 46rem) {
  .cmpInvite { padding: var(--s-5); }
  /* Full-width targets on a phone — a row of five pills wraps into a ragged
     block that is hard to hit. */
  .cmpInvite__row { flex-direction: column; }
  .cmpShare { width: 100%; }
}

/* The whole card is a click target. cursor:pointer is the only affordance that
   says so — without it the card looks inert and nobody tries. */
.cmpCard.is-clickable { cursor: pointer; }
/* Everything genuinely interactive inside keeps the normal arrow/pointer, so
   the two are visually distinguishable. */
.cmpCard.is-clickable a,
.cmpCard.is-clickable button,
.cmpCard.is-clickable [role="button"] { cursor: pointer; }
