:root {
  --social-ink: #f4e9c8;
  --social-muted: #aab7a3;
  --social-leaf: #95cf8b;
  --social-blue: #8bd6e7;
  --social-violet: #d5b7ef;
  /* Magenta is the one hue reserved for "addressed to YOU": whispers, mentions,
     the unread boundary. Nothing else in this panel may borrow it. */
  --social-you: #ff7eff;
  /* The same butter-gold as the plan card's `.go` — gold means THE primary
     action, and in this panel that is a send button holding words. */
  --social-gold-hi: #f1d27d;
  --social-gold-lo: #c99443;
  /* Six stable traveler-name pigments, hashed per social id in social-chat.js
     (row[data-hue="0"…"5"]). All are pre-checked ≥4.5:1 against the panel's
     ~#101b15 ground — do not darken them without re-checking. */
  --social-hue-0: #a8d59a;   /* sage   */
  --social-hue-1: #8bd6e7;   /* sky    */
  --social-hue-2: #e7c873;   /* amber  */
  --social-hue-3: #f0a48f;   /* coral  */
  --social-hue-4: #cfaef2;   /* lilac  */
  --social-hue-5: #7fd6c2;   /* teal   */
  /* Near-opaque on purpose: at .965 the meadow's rooftops read straight through
     the log on a phone and the panel stopped looking like paper. */
  --social-paper: rgba(16, 27, 21, .992);
  --social-line: rgba(222, 192, 112, .2);
  --chat-h: 528px;              /* JS overrides this from the persisted drag height */
  /* One source of truth for the panel's width: the social dock parks itself
     immediately to the right of the open panel and must not drift out of step
     with it. Change it here or the two separate. */
  --chat-w: min(440px, calc(100vw - 28px));
}

/* The page has no global box-sizing reset, so every `min-height` in this panel was
   being read as a CONTENT height and then had its own padding and border added on
   top: the composer's min-height:52 measured 67px, the header's 46 measured 57.
   Sizes here are meant to be the sizes you see. Scoped to the chat surfaces so it
   cannot reach another lane's markup. */
#chatlog,
#chatcontext,
#fieldnote,
#chatlog *,
#chatcontext *,
#fieldnote * { box-sizing: border-box; }

#fieldnote[hidden],
#chatcontext[hidden],
#chatreply[hidden],
#chatsuggest[hidden],
#chatjump[hidden],
#chattarget[hidden] { display: none !important; }

/* Wider and taller than the old 390×540: a panel people hang out in. `vh` is
   keyboard-blind on iOS, so every bound here is dvh minus the live --kb offset. */
#chatlog {
  /* MUST stay fixed. .panel supplies position:fixed and mmo.html supplies the
     bottom-left anchor; overriding this to `relative` drops the panel into normal
     document flow at the top-left, behind the HUD. `fixed` already establishes a
     containing block for the grip and the jump pill, so `relative` buys nothing. */
  position: fixed;
  left: calc(14px + env(safe-area-inset-left, 0px));
  bottom: calc(14px + env(safe-area-inset-bottom, 0px) + var(--kb, 0px));
  width: var(--chat-w);
  height: var(--chat-h);
  max-height: min(72dvh, calc(100dvh - var(--kb, 0px) - 34px));
  overflow: hidden;
  color: var(--social-ink);
  background:
    linear-gradient(180deg, rgba(31, 48, 36, .992), var(--social-paper)),
    repeating-linear-gradient(0deg, transparent 0 27px, rgba(240, 213, 139, .035) 28px);
  border-color: rgba(206, 178, 100, .36);
  border-radius: 15px;
  box-shadow: 0 16px 46px rgba(0, 0, 0, .5), inset 0 1px rgba(255, 250, 223, .07);
}

/* Drag the top edge to resize on desktop; on a phone the same grabber switches
   the sheet between peek and expanded. */
#chatgrip {
  height: 11px;
  flex: none;
  display: grid;
  place-items: center;
  cursor: ns-resize;
  touch-action: none;
}

#chatgrip::before {
  content: "";
  width: 42px;
  height: 3px;
  border-radius: 3px;
  background: rgba(222, 192, 112, .26);
  transition: background .14s ease;
}

#chatgrip:hover::before,
#chatgrip:focus-visible::before { background: rgba(240, 213, 139, .55); }
#chatgrip:focus-visible { outline: 2px solid rgba(149, 207, 139, .5); outline-offset: -2px; }

#chatloghdr {
  /* display:flex must live HERE: the legacy mmo.html header rule that used to
     supply it is retired, and without it the close button sits glued to the
     title instead of at the right edge. */
  display: flex;
  min-height: 46px;
  padding: 5px 6px;
  gap: 4px;
  align-items: center;
  border-bottom-color: var(--social-line);
  background: rgba(11, 21, 16, .38);
}

#chattabs {
  display: flex;
  min-width: 0;
  flex: 1;
  gap: 2px;
}

/* WOR-164: a single stream does not wear a tablist. The dest line names it. */
#chattabs[data-sparse="1"],
#chattabs[hidden] { display: none; }

/* Every rule here is scoped to #chatloghdr on purpose. mmo.html carries a
   `#chatloghdr button { margin-left: auto; color: #a9b79e; font: inherit }` rule
   (plus a danger-red :hover) meant for the old close button, and an id selector
   outranks a bare `.chat-tab`: it shoved the four tabs apart with auto margins,
   erased the active tab's gold entirely, and turned a hovered tab #e77d72 —
   the danger colour. Matching its specificity is what wins the cascade back. */
#chatloghdr .chat-tab {
  min-width: 0;
  flex: 1;
  height: 36px;
  margin: 0;
  padding: 0 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: var(--social-muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 9px;
  cursor: pointer;
  font: 600 12px ui-rounded, system-ui, sans-serif;
  letter-spacing: .005em;
  transition: color .15s ease, background .15s ease, border-color .15s ease;
}

#chatloghdr .chat-tab .ui-icon { width: 16px; height: 16px; opacity: .82; transition: opacity .15s ease; }
#chatloghdr .chat-tab:hover {
  color: var(--social-ink);
  background: rgba(255, 255, 255, .04);
}

#chatloghdr .chat-tab:hover .ui-icon { opacity: 1; }
#chatloghdr .chat-tab.on {
  color: #f9e7ad;
  border-color: rgba(218, 186, 98, .34);
  background: linear-gradient(180deg, rgba(114, 101, 54, .34), rgba(86, 76, 41, .16));
}

#chatloghdr .chat-tab.on .ui-icon { opacity: 1; }

/* Only whispers and mentions ever get a number. Ordinary field traffic gets the
   quiet dot below — a count there would mean "something happened", which is noise. */
#chatloghdr .chat-tab-count {
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  display: inline-grid;
  place-items: center;
  box-sizing: border-box;
  color: #0d1710;
  background: #ff7eff;
  border-radius: 9px;
  font: 700 11px ui-rounded, system-ui, sans-serif;
}

.chat-tab-count[hidden] { display: none !important; }
/* The tab's own display:inline-flex silently defeats the UA [hidden] rule —
   without this, every tab renders even when the launch posture hides it. */
#chatloghdr .chat-tab[hidden] { display: none !important; }
.chat-tab.quiet::after {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--social-leaf);
}

#chatloghdr #chatlogx {
  width: 34px;
  height: 34px;
  /* the header's only right-edge anchor since the legacy mmo.html rule retired */
  margin: 0 0 0 auto;
  padding: 8px;
  flex: none;
  color: var(--social-muted);
  border-radius: 9px;
}

#chatloghdr #chatlogx:hover { color: var(--social-ink); }
#chatloghdr #chatlogx .ui-icon { width: 17px; height: 17px; }

#chatsubhdr {
  min-height: 30px;
  padding: 0 9px 0 11px;
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--social-muted);
  border-bottom: 1px solid rgba(198, 168, 91, .12);
  font-size: 11px;
  letter-spacing: .01em;
}

#chatonline::before {
  content: "";
  width: 6px;
  height: 6px;
  display: inline-block;
  margin-right: 5px;
  border-radius: 50%;
  background: var(--social-leaf);
  box-shadow: 0 0 8px rgba(149, 207, 139, .55);
}

/* Both of these are settings, not actions, and a bordered pill in the meta strip
   read louder than the message list it sits above. Ghost at rest, surface on
   hover: still one tap away, no longer the brightest thing in the panel. */
#chathidden,
#chattypingtoggle {
  min-height: 24px;
  padding: 2px 6px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: #8d9c8f;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 7px;
  cursor: pointer;
  font: 11px ui-rounded, system-ui, sans-serif;
  letter-spacing: .01em;
}

/* One auto margin only. The toggle is inserted before #chathidden, so it is the
   first of the pair and pushes the whole group to the right edge. */
#chattypingtoggle { margin-left: auto; }
#chathidden[hidden] { display: none !important; }
#chathidden .ui-icon,
#chattypingtoggle .ui-icon { width: 13px; height: 13px; opacity: .75; }
#chathidden:hover,
#chattypingtoggle:hover {
  color: #f1e4bd;
  background: rgba(255, 255, 255, .055);
  border-color: rgba(198, 168, 91, .2);
}

#chathidden:hover .ui-icon,
#chattypingtoggle:hover .ui-icon { opacity: 1; }
#chattypingtoggle.off { color: #6f7d73; }
#chattypingtoggle.off .ui-icon { opacity: .4; }

#chatlog.cross-world #chatonline::after {
  content: " · both worlds";
  color: #d5c68f;
}
body.clover-world #chatlog.cross-world #chatonline::after { content: none; }

#voicelog {
  padding: 8px 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 100px;
  overscroll-behavior: contain;
  /* see the auto-margin pair below */
  /* Browser scroll anchoring tries to hold EXISTING content still, which fights a log that
     appends at the bottom. We own the pin ourselves (measure at-bottom, insert, restore). */
  overflow-anchor: none;
  scrollbar-width: thin;
  scrollbar-color: rgba(183, 163, 101, .38) transparent;
}

/* Conversation hugs the composer. A tall panel holding two notes at the TOP with
   300px of nothing under them reads as broken; the same two notes sitting on the
   composer read as a thread that hasn't got going yet.
   Auto margins, deliberately, and not `justify-content: flex-end` — that clips
   the first rows of an overflowing scroll container in every engine. An auto
   margin simply resolves to 0 once the content is taller than the box. */
#voicelog::before { content: ""; flex: none; margin-top: auto; }
/* Nothing to hug when the tab is empty, so the empty state centres itself
   instead. Set by renderAll(), so the picker with real messages behind it
   still stacks from the bottom. */
#voicelog.is-empty > .chat-empty { margin-bottom: auto; }

/* Safari ignores scrollbar-color, so the thin brass thumb is restated in
   webkit terms; Chromium prefers the standard properties above and skips these. */
#voicelog::-webkit-scrollbar { width: 9px; }
#voicelog::-webkit-scrollbar-track { background: transparent; }
#voicelog::-webkit-scrollbar-thumb {
  background: rgba(183, 163, 101, .32);
  background-clip: padding-box;
  border: 3px solid transparent;
  border-radius: 8px;
}

#voicelog::-webkit-scrollbar-thumb:hover { background-color: rgba(183, 163, 101, .5); }

.chat-message {
  position: relative;
  padding: 7px 5px;
  display: grid;
  grid-template-columns: 31px minmax(0, 1fr) 43px;
  gap: 7px;
  border-radius: 10px;
  transition: background .14s ease;
}

/* Telegram's grouping rhythm: ~2px inside a breath (the log's own gap), ~10px
   between breaths. The margin lands on the row that STARTS a group so trimming
   a follow row never orphans spacing. */
.chat-message + .chat-message:not(.follow) { margin-top: 8px; }

/* A follow row is the same voice continuing within four minutes: the avatar
   goes away entirely — its 31px grid column is fixed, so the text's left edge
   still lines up with the group above it. */
.chat-message.follow .chat-avatar {
  /* visibility, not display: removing the item from grid flow slid the body
     into the 31px avatar column and wrapped it one word per line. */
  visibility: hidden;
  height: 0;
  border: 0;
}
/* Grouped lines sit close: the follow row sheds its top padding, and (where
   :has is available) the row it continues sheds its bottom. Mine rows are
   exempt — their padding is the bubble's interior, and the 2px flex gap is
   already the whole distance between bubbles. */
.chat-message.follow:not(.mine) { padding-top: 1px; }
.chat-message:not(.mine):has(+ .chat-message.follow:not(.mine)) { padding-bottom: 2px; }
/* The meta header collapses on follows — EXCEPT while the row is pending or
   failed, where the ◌ / Retry status is the only truth the player has about
   whether their words landed. */
.chat-message.follow:not(.sending):not(.failed) .chat-message-meta { display: none; }

/* Live arrivals rise gently into place. renderAll never sets .arrive — replaying
   80 rows of history with staggered entrances would read as a glitch. */
.chat-message.arrive { animation: chat-arrive .19s cubic-bezier(.22, .9, .3, 1); }
@keyframes chat-arrive {
  from { opacity: 0; transform: translateY(5px); }
  to { opacity: 1; transform: none; }
}

/* Pending: dimmed, never removed. The words stay on screen through a failure —
   losing what you typed is a worse outcome than a slow send. */
.chat-message.sending { opacity: .62; }
/* A frozen ◌ next to a dimmed row reads as a rendering glitch; a slow breath
   reads as "on its way". One element moves, and it stops the moment it lands. */
.chat-message.sending .chat-status { animation: chat-sending 1.15s ease-in-out infinite; }
@keyframes chat-sending { 0%, 100% { opacity: .4; } 50% { opacity: 1; } }
/* .failed's red wash lives below the .mine bubble rules — it must outrank them,
   because a failed row is almost always your own. */

.chat-status {
  margin-left: 4px;
  padding: 0 5px;
  color: #8f9c90;
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: default;
  font: 700 11px ui-rounded, system-ui, sans-serif;
}

.chat-status[hidden] { display: none !important; }
/* The ✓ replaces the ◌ in the same element, so confirming a send moves nothing.
   It blooms in leaf-green and the JS clears it 1.6s later — a receipt that
   visits rather than a permanent column. */
.chat-status.sent {
  color: var(--social-leaf);
  animation: chat-sent-in .28s cubic-bezier(.34, 1.4, .64, 1);
}

@keyframes chat-sent-in {
  from { opacity: 0; transform: scale(.55); }
  to { opacity: 1; transform: none; }
}

.chat-message.failed .chat-status {
  min-height: 24px;
  padding-inline: 8px;
  color: #f0bdb6;
  background: rgba(206, 106, 94, .18);
  border: 1px solid rgba(206, 106, 94, .35);
  cursor: pointer;
}

.chat-message.failed .chat-status:hover { background: rgba(206, 106, 94, .3); }

/* A boundary, not a highlight: frozen on open so it stays where you stopped
   reading. Magenta on purpose — unread-that-matters is "addressed to you". */
.chat-divider {
  margin: 9px 2px 5px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--social-you);
  font: 700 11px ui-rounded, system-ui, sans-serif;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.chat-divider span {
  padding: 1px 8px;
  background: rgba(255, 126, 255, .09);
  border-radius: 999px;
}

.chat-divider::before,
.chat-divider::after {
  content: "";
  height: 1px;
  flex: 1;
  background: linear-gradient(90deg, transparent, rgba(255, 126, 255, .42), transparent);
}

/* The day chip is a quiet brass waypoint floating mid-stream — a margin note,
   not a message. No border: the wash alone is enough at this size. */
.chat-day {
  align-self: center;
  margin: 13px 0 7px;
  flex: none;
}

.chat-day span {
  display: inline-block;
  padding: 2px 11px;
  color: #d3c48e;
  background: rgba(217, 187, 105, .09);
  border-radius: 999px;
  font: 600 11px ui-rounded, system-ui, sans-serif;
  letter-spacing: .045em;
}

.chat-message:hover,
.chat-message:focus-within { background: rgba(255, 255, 255, .035); }

/* ---- your own messages: right-hugging moss bubbles ------------------------
   No avatar, no author label — position IS the authorship, the way every
   messenger the player already knows says "mine". The bubble stays low-key
   moss (never gold, never magenta) so the log keeps reading as paper. */
.chat-message.mine {
  grid-template-columns: minmax(0, 1fr);
  align-self: flex-end;
  width: fit-content;
  max-width: 80%;
  padding: 6px 10px 7px;
  background: linear-gradient(160deg, rgba(94, 134, 82, .28), rgba(64, 98, 62, .2));
  border-radius: 13px 13px 5px 13px;
  box-shadow: inset 0 1px rgba(255, 250, 223, .05);
}

/* Follow bubbles square their top-right corner toward the group above. */
.chat-message.mine.follow { border-radius: 13px 5px 5px 13px; }
.chat-message.mine .chat-avatar { display: none; }
.chat-message.mine .chat-message-tools { display: none; }
/* "You" is redundant on a right-aligned bubble; the meta row keeps only the
   quiet facts — recipient chip, world chip, timestamp, send status. */
.chat-message.mine .chat-author { display: none; }
.chat-message.mine .chat-message-meta { min-height: 16px; }
/* The bubble already carries a tint; the row hover wash would just muddy it. */
.chat-message.mine:hover,
.chat-message.mine:focus-within {
  background: linear-gradient(160deg, rgba(94, 134, 82, .28), rgba(64, 98, 62, .2));
}

/* ---- addressed to you: whispers wear the magenta thread -------------------
   An inset accent instead of a border so the grid never shifts width. */
.chat-message.whisper {
  background: rgba(255, 126, 255, .055);
  box-shadow: inset 2px 0 rgba(255, 126, 255, .55);
}

/* Your half of a whisper thread: the moss bubble takes a faint magenta cast
   and carries the thread's accent on its outer edge. */
.chat-message.mine.whisper {
  background: linear-gradient(160deg, rgba(148, 104, 142, .26), rgba(96, 70, 96, .2));
  box-shadow: inset -2px 0 rgba(255, 126, 255, .45);
}

/* Failed beats every bubble tint above (moss AND whisper-magenta) by source
   order — a lost message may not look cozy. */
.chat-message.failed,
.chat-message.mine.failed { background: rgba(151, 71, 63, .16); }

.chat-message.npc_scene { background: rgba(104, 76, 138, .09); }
.chat-message.activity { background: rgba(139, 117, 53, .08); }

.chat-avatar {
  width: 29px;
  height: 29px;
  display: grid;
  place-items: center;
  color: var(--social-blue);
  border: 1px solid rgba(139, 214, 231, .2);
  border-radius: 10px 10px 10px 4px;
  background: rgba(65, 115, 112, .17);
}

.chat-message.npc_scene .chat-avatar { color: var(--social-violet); border-color: rgba(213, 183, 239, .22); }
.chat-message.activity .chat-avatar { color: #e7c873; border-color: rgba(231, 200, 115, .24); }
.chat-avatar .ui-icon { width: 17px; height: 17px; }
.chat-message-body { min-width: 0; }

.chat-message-meta {
  min-height: 18px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.chat-author {
  max-width: 150px;
  padding: 0;
  overflow: hidden;
  color: var(--social-blue);
  background: none;
  border: 0;
  cursor: pointer;
  font: 700 12px ui-rounded, system-ui, sans-serif;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-author:not(:disabled):hover { color: #b8ecfa; text-decoration: underline; text-underline-offset: 2px; }
.chat-author:disabled { cursor: default; }

/* ---- six stable traveler pigments -----------------------------------------
   The JS hashes each traveler's social id to data-hue 0–5, so a voice keeps
   its colour across sessions and a busy room reads as people, not a wall.
   The avatar's frame and glyph drink the same pigment. These outrank the
   base .chat-author blue by specificity; the hue survives hover (underline
   still signals the tap target). Whisper magenta below outranks them by
   source order — magenta always wins on an addressed-to-you row. */
.chat-message[data-hue="0"] .chat-author { color: var(--social-hue-0); }
.chat-message[data-hue="1"] .chat-author { color: var(--social-hue-1); }
.chat-message[data-hue="2"] .chat-author { color: var(--social-hue-2); }
.chat-message[data-hue="3"] .chat-author { color: var(--social-hue-3); }
.chat-message[data-hue="4"] .chat-author { color: var(--social-hue-4); }
.chat-message[data-hue="5"] .chat-author { color: var(--social-hue-5); }
.chat-message[data-hue="0"] .chat-avatar { color: var(--social-hue-0); border-color: rgba(168, 213, 154, .28); background: rgba(168, 213, 154, .08); }
.chat-message[data-hue="1"] .chat-avatar { color: var(--social-hue-1); border-color: rgba(139, 214, 231, .28); background: rgba(139, 214, 231, .08); }
.chat-message[data-hue="2"] .chat-avatar { color: var(--social-hue-2); border-color: rgba(231, 200, 115, .28); background: rgba(231, 200, 115, .08); }
.chat-message[data-hue="3"] .chat-avatar { color: var(--social-hue-3); border-color: rgba(240, 164, 143, .28); background: rgba(240, 164, 143, .08); }
.chat-message[data-hue="4"] .chat-avatar { color: var(--social-hue-4); border-color: rgba(207, 174, 242, .28); background: rgba(207, 174, 242, .08); }
.chat-message[data-hue="5"] .chat-avatar { color: var(--social-hue-5); border-color: rgba(127, 214, 194, .28); background: rgba(127, 214, 194, .08); }

.chat-message.npc_scene .chat-author { color: var(--social-violet); }
.chat-message.activity .chat-author { color: #e7c873; }
/* Last on purpose: a whisperer's name is magenta no matter which pigment the
   hue hash dealt them — precious beats palette. */
.chat-message.whisper .chat-author { color: var(--social-you); }
.chat-message.whisper .chat-avatar { color: var(--social-you); border-color: rgba(255, 126, 255, .3); background: rgba(255, 126, 255, .07); }

/* 11px floor, and it is not decoration: the world chip is the whole point of a
   cross-world field. Below the floor it was unreadable at arm's length. */
.chat-world {
  flex: none;
  padding: 0 5px;
  color: #c2b691;
  background: rgba(217, 187, 105, .1);
  border-radius: 5px;
  font-size: 11px;
  line-height: 15px;
}

.chat-message time { margin-left: auto; flex: none; color: #8a9a8d; font-size: 11px; }
.chat-text { color: var(--social-ink); font-size: 13px; line-height: 1.44; overflow-wrap: anywhere; }
/* A note that is only emoji is a wave, not a sentence. */
.chat-text.emoji-only { margin-top: 1px; font-size: 24px; line-height: 1.18; }
/* The quoted line sits on a faint sky wash so it reads as "borrowed words",
   not a second message. */
.chat-reply-quote {
  margin: 0 0 4px;
  padding: 2px 8px 2px 7px;
  overflow: hidden;
  color: #97a698;
  background: rgba(139, 214, 231, .06);
  border-left: 2px solid rgba(139, 214, 231, .38);
  border-radius: 3px 7px 7px 3px;
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-reactions { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.chat-reactions[hidden] { display: none !important; }
.chat-reactions button {
  padding: 1px 6px;
  color: #d9d5bd;
  background: rgba(255, 255, 255, .05);
  border: 1px solid rgba(255, 255, 255, .07);
  border-radius: 8px;
  cursor: pointer;
  font: 11px ui-rounded, system-ui, sans-serif;
  line-height: 17px;
  transition: background .12s ease, border-color .12s ease, transform .12s ease;
}

/* Chips lift a hair on hover — held reactions should feel like small objects. */
.chat-reactions button:not([hidden]):hover {
  background: rgba(255, 255, 255, .09);
  border-color: rgba(240, 213, 139, .3);
  transform: translateY(-1px);
}

.chat-message-tools {
  display: flex;
  align-items: flex-start;
  opacity: .24;
  transition: opacity .14s;
}

.chat-message:hover .chat-message-tools,
.chat-message:focus-within .chat-message-tools { opacity: 1; }
.chat-message-tools button {
  width: 22px;
  height: 27px;
  padding: 3px;
  display: grid;
  place-items: center;
  color: #a7b2a8;
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

.chat-message-tools button:hover { color: #f5df9f; background: rgba(255, 255, 255, .05); }

/* Smiley-PLUS, the universal "add a reaction" affordance. The plus is what
   distinguishes it from the composer's plain 🙂, which inserts an emoji. */
.chat-react-add { position: relative; }
.chat-react-add::after {
  content: "+";
  position: absolute;
  right: 1px;
  bottom: 2px;
  color: var(--social-leaf);
  font: 700 10px ui-rounded, system-ui, sans-serif;
  line-height: 1;
  text-shadow: 0 0 3px rgba(9, 17, 13, .9);
  pointer-events: none;
}
.chat-message-tools .ui-icon { width: 14px; height: 14px; }

.chat-empty {
  min-height: 122px;
  padding: 22px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: #93a297;
  text-align: center;
}

/* A mark, not a void. An empty tab with nothing but two greyed lines in it is
   the difference between "considered" and "unfinished". */
.chat-empty-mark {
  width: 42px;
  height: 42px;
  margin-bottom: 3px;
  display: grid;
  place-items: center;
  flex: none;
  color: #8bab92;
  background: radial-gradient(circle at 50% 42%, rgba(149, 207, 139, .16), rgba(149, 207, 139, 0) 68%);
  border: 1px solid rgba(198, 168, 91, .16);
  border-radius: 14px 14px 14px 5px;
  box-shadow: 0 0 18px rgba(149, 207, 139, .07);
}

.chat-empty-mark .ui-icon { width: 19px; height: 19px; }
.chat-empty b { color: #dcd0aa; font: 700 14px ui-rounded, system-ui, sans-serif; letter-spacing: .005em; }
.chat-empty span { max-width: 274px; font-size: 12px; line-height: 1.45; text-wrap: balance; }
.chat-empty.chat-presence-picker { min-height: auto; padding: 12px 10px 9px; }
.chat-empty.chat-presence-picker .chat-empty-mark { display: none; }
.chat-people { width: 100%; margin-top: 10px; display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.chat-people button {
  min-height: 34px;
  padding: 5px 10px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: #c9e7e1;
  background: rgba(72, 113, 105, .18);
  border: 1px solid rgba(139, 214, 231, .18);
  border-radius: 9px;
  cursor: pointer;
  font: 12px ui-rounded, system-ui, sans-serif;
  text-align: left;
}

.chat-people button:hover { background: rgba(88, 136, 127, .28); border-color: rgba(139, 214, 231, .34); }
.chat-people .ui-icon { width: 15px; height: 15px; flex: none; opacity: .85; }
.chat-person-name { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 1px; }
/* Presence is content: where they are, never how long they have been here. */
.chat-person-where { color: #8ba192; font-size: 11px; letter-spacing: .01em; }
.chat-thread-count {
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  display: inline-grid;
  place-items: center;
  box-sizing: border-box;
  color: #0d1710;
  background: #ff7eff;
  border-radius: 9px;
  font: 700 11px ui-rounded, system-ui, sans-serif;
}

/* A zero-height rail sitting exactly on the log's bottom edge. The pill used to
   hang off the panel's bottom with a hand-tuned offset, which drifted every time
   the composer changed height and sat ON TOP of the reply/whisper-target rows
   whenever those were open. Anchored here it is always just inside the log. */
#chatjumpanchor {
  position: relative;
  height: 0;
  flex: none;
  z-index: 6;
}

/* Appears only when you have scrolled off the live edge. Same < 48px predicate as
   the auto-follow, so the pill and the pin can never disagree. */
#chatjump {
  position: absolute;
  left: 50%;
  bottom: 9px;
  z-index: 6;
  padding: 5px 11px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transform: translateX(-50%);
  color: #0e1a12;
  background: linear-gradient(180deg, #b7e3ac, #8fc985);
  border: 1px solid rgba(20, 35, 26, .35);
  border-radius: 12px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .4);
  cursor: pointer;
  font: 700 11px ui-rounded, system-ui, sans-serif;
  white-space: nowrap;
}

#chatjump:hover { filter: brightness(1.06); box-shadow: 0 8px 22px rgba(0, 0, 0, .45); }
#chatjump:active { filter: brightness(.97); }
.chat-jump-arrow { font-size: 11px; }

/* [hidden] toggles display, so un-hiding restarts this: the pill hops up from
   the composer edge instead of blinking into place. */
#chatjump:not([hidden]) { animation: chat-jump-in .2s cubic-bezier(.2, .9, .25, 1.2); }
@keyframes chat-jump-in {
  from { opacity: 0; transform: translateX(-50%) translateY(7px); }
  to { opacity: 1; transform: translateX(-50%); }
}

/* Fixed height by design: a row that grows and shrinks fights the scroll pin. */
#chattyping {
  height: 17px;
  padding: 0 11px;
  flex: none;
  overflow: hidden;
  color: #8ba192;
  font-size: 11px;
  font-style: italic;
  line-height: 17px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* A slow sheen crosses "…is typing" — alive without adding a single pixel of
   height. Both guards matter: without background-clip support the transparent
   fill would erase the text, and reduced-motion keeps the plain grey line. */
@media (prefers-reduced-motion: no-preference) {
  @supports (-webkit-background-clip: text) {
    #chattyping {
      background: linear-gradient(90deg, #8ba192 0 38%, #cfe0c8 50%, #8ba192 62% 100%);
      background-size: 220% 100%;
      -webkit-background-clip: text;
      background-clip: text;
      -webkit-text-fill-color: transparent;
      animation: chat-typing-sheen 2.1s linear infinite;
    }
  }
}

@keyframes chat-typing-sheen {
  from { background-position: 110% 0; }
  to { background-position: -110% 0; }
}

#chatsuggest {
  position: absolute;
  left: 7px;
  right: 7px;
  bottom: calc(100% - 3px);
  z-index: 7;
  max-height: min(208px, calc(100dvh - var(--kb, 0px) - 150px));
  padding: 4px;
  overflow-y: auto;
  background: rgba(15, 25, 19, .99);
  border: 1px solid rgba(198, 168, 91, .35);
  border-radius: 11px;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, .5);
  overscroll-behavior: contain;
}

.chat-suggest-option {
  min-height: 32px;
  padding: 5px 8px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: #dce6d8;
  border-radius: 7px;
  cursor: pointer;
  font: 12px ui-rounded, system-ui, sans-serif;
}

.chat-suggest-option.on { background: rgba(149, 207, 139, .16); }
.chat-suggest-glyph { font-size: 16px; line-height: 1; }
.chat-suggest-label { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-suggest-hint { color: #8ba192; font-size: 11px; }

#chatemoji {
  width: 38px;
  height: 38px;
  padding: 0;
  flex: none;
  display: grid;
  place-items: center;
  color: #dfead6;
  background: transparent;
  border: 1px solid rgba(198, 168, 91, .22);
  border-radius: 10px;
  cursor: pointer;
  font-size: 17px;
  line-height: 1;
}

#chatemoji[hidden] { display: none !important; }
#chatemoji:hover { background: rgba(255, 255, 255, .06); }
#chatemoji:disabled { opacity: .45; cursor: default; }

#chatreply,
#chattarget {
  min-height: 28px;
  padding: 4px 9px;
  display: flex;
  align-items: center;
  gap: 7px;
  color: #aebbb0;
  background: rgba(8, 16, 12, .42);
  border-top: 1px solid rgba(198, 168, 91, .12);
  font-size: 11px;
}

#chatreply span,
#chattarget span { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#chatreply button,
#chattarget button {
  width: 24px;
  height: 24px;
  padding: 4px;
  color: #9aaba0;
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}
#chatreply .ui-icon,
#chattarget .ui-icon { width: 14px; height: 14px; }

#chatsend {
  position: relative;   /* #chatsuggest anchors to the composer's top edge */
  min-height: 52px;
  padding: 7px;
  gap: 7px;
  align-items: center;
  flex-wrap: wrap;
  background: rgba(8, 17, 12, .5);
  border-top-color: rgba(198, 168, 91, .17);
}

#chatdest {
  flex: 1 0 100%;
  margin: 0 2px -1px;
  color: var(--social-leaf);
  font: 600 11px ui-rounded, system-ui, sans-serif;
  letter-spacing: .03em;
}

/* border-box, and it matters: nothing else in this file resets it, so the input
   was content-box and min-height:38px meant 38 + 16 padding + 2 border = 56px.
   The composer stood 71px tall against a 39px send button. */
#chatmsg {
  box-sizing: border-box;
  min-height: 38px;
  padding: 7px 10px;
  color: #f7edcf;
  background: rgba(10, 20, 15, .95);
  border-color: rgba(198, 168, 91, .28);
  border-radius: 10px;
  font-size: 14px;
}

#chatmsg { transition: border-color .16s ease, box-shadow .16s ease; }
#chatmsg::placeholder { color: #7d8c81; }
#chatmsg:focus {
  border-color: rgba(149, 207, 139, .7);
  box-shadow: 0 0 0 3px rgba(149, 207, 139, .1);
}

/* Quiet outline at rest; THE gold CTA the moment there are words to send.
   The gradient lives on a cross-fading ::before layer because gradients
   themselves cannot transition — this is what makes it "light up" as you
   type instead of snapping. Same butter-gold recipe as the plan card's .go,
   and the only gold in this panel. */
#chatgo {
  position: relative;
  width: 38px;
  height: 38px;
  padding: 8px;
  flex: none;
  overflow: hidden;
  color: #dfead6;
  border-color: rgba(149, 207, 139, .35);
  border-radius: 10px;
  transition: color .18s ease, border-color .18s ease, box-shadow .18s ease,
    transform .08s ease;
}

#chatgo::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, var(--social-gold-hi), var(--social-gold-lo));
  border-radius: inherit;
  opacity: 0;
  transition: opacity .18s ease;
  pointer-events: none;
}

#chatgo .ui-icon { position: relative; }
#chatgo.ready {
  color: #22170e;
  border-color: var(--social-gold-hi);
  box-shadow: 0 0 13px rgba(241, 210, 125, .28);
}

#chatgo.ready::before { opacity: 1; }

#chatgo:disabled,
#chatmsg:disabled { opacity: .45; cursor: default; }

#chatcontext {
  position: fixed;
  z-index: 45;
  width: 168px;
  padding: 5px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: rgba(15, 25, 19, .99);
  border: 1px solid rgba(198, 168, 91, .35);
  border-radius: 11px;
  box-shadow: 0 16px 38px rgba(0, 0, 0, .55);
}

#chatcontext .context-heading {
  padding: 5px 8px 3px;
  color: #96a49a;
  font: 700 11px ui-rounded, system-ui, sans-serif;
  letter-spacing: .07em;
  text-transform: uppercase;
}

#chatcontext button {
  min-height: 36px;
  padding: 6px 9px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: #dce6d8;
  background: transparent;
  border: 0;
  border-radius: 7px;
  cursor: pointer;
  font: 12px ui-rounded, system-ui, sans-serif;
  text-align: left;
}

#chatcontext button:hover { background: rgba(255, 255, 255, .055); }
#chatcontext .ui-icon { width: 16px; height: 16px; color: #a9c8bd; }
#chatcontext.reaction-picker {
  width: auto;
  padding: 4px;
  flex-direction: row;
}
#chatcontext.reaction-picker .reaction-choice {
  width: 40px;
  min-width: 40px;
  height: 40px;
  min-height: 40px;
  padding: 0;
  display: grid;
  place-items: center;
  font-size: 18px;
  transition: transform .13s cubic-bezier(.34, 1.56, .64, 1);
}

/* Springy, like picking a berry: the glyph swells toward your pointer and
   dips as you commit. The generic button hover wash stays subtle beneath it. */
#chatcontext.reaction-picker .reaction-choice:hover,
#chatcontext.reaction-picker .reaction-choice:focus-visible { transform: scale(1.24); }
#chatcontext.reaction-picker .reaction-choice:active { transform: scale(.92); }

#chatlogbtn {
  width: 45px;
  height: 45px;
  padding: 10px;
  display: grid;
  place-items: center;
  color: #d6e6d4;
  background: linear-gradient(180deg, rgba(38, 61, 45, .96), rgba(20, 35, 26, .97));
  border-color: rgba(196, 174, 105, .38);
  border-radius: 14px 14px 14px 6px;
  box-shadow: 0 8px 25px rgba(0, 0, 0, .35);
}

#chatlogbtn .ui-icon { width: 22px; height: 22px; }
#chatlogbtn.has-unread { border-color: rgba(149, 207, 139, .6); box-shadow: 0 8px 25px rgba(0, 0, 0, .35), 0 0 16px rgba(149, 207, 139, .12); }
/* Positioning lives HERE now, not in mmo.html's inline style — and the [hidden]
   guard matters: this rule's display:grid outranked the UA [hidden] rule, so the
   empty pill sat on the launcher from first paint. */
#chatlogbtn .ct[hidden] { display: none !important; }
#chatlogbtn .ct {
  position: absolute;
  right: -5px;
  top: -5px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  background: #9ad891;
  border: 2px solid #14231a;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 700;
}

#chatlogbtn .ct.dot {
  min-width: 9px;
  width: 9px;
  height: 9px;
  padding: 0;
  right: -1px;
  top: -1px;
  border-width: 1px;
}

#fieldnote {
  position: fixed;
  left: calc(68px + env(safe-area-inset-left, 0px));
  bottom: calc(18px + env(safe-area-inset-bottom, 0px) + var(--kb, 0px));
  z-index: 4;
  width: min(330px, calc(100vw - 130px));
  min-height: 40px;
  padding: 7px 11px 7px 8px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: #dfe8d9;
  background: linear-gradient(90deg, rgba(21, 37, 27, .94), rgba(17, 29, 22, .86));
  border: 1px solid rgba(149, 207, 139, .25);
  border-radius: 7px 12px 12px 7px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, .32);
  pointer-events: none;
  font-size: 11.5px;
  line-height: 1.35;
}

#fieldnote.arrive { animation: fieldnote-in .24s cubic-bezier(.2, .9, .25, 1.15); }
#fieldnote b { color: #99d8cf; }
.fieldnote-glyph {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  flex: none;
  color: #9ad891;
  background: rgba(149, 207, 139, .1);
  border-radius: 9px 9px 9px 3px;
}
.fieldnote-glyph .ui-icon { width: 16px; height: 16px; }
@keyframes fieldnote-in { from { opacity: 0; transform: translateX(-9px) scale(.98); } to { opacity: 1; transform: none; } }

body.chatlog #fieldnote,
body.convo #fieldnote,
body.selftalk #fieldnote,
body.cine #fieldnote,
body.riding #fieldnote,
body.tut #fieldnote,
body.boot #fieldnote { display: none !important; }

.npc-audience {
  min-width: 112px;
  height: 34px;
  padding: 0 9px;
  display: none;
  align-items: center;
  gap: 6px;
  color: #b8c2b7;
  background: rgba(8, 16, 12, .42);
  border: 1px solid rgba(198, 168, 91, .22);
  border-radius: 9px;
  cursor: pointer;
  font: 11px ui-rounded, system-ui, sans-serif;
}

body.convo .npc-audience { display: inline-flex; }
.npc-audience .ui-icon { width: 15px; height: 15px; }
.npc-audience.open { color: #d9efd2; border-color: rgba(149, 207, 139, .5); background: rgba(79, 117, 75, .21); }

@media (max-width: 560px) {
  /* Two-stage bottom sheet. The world stays visible above even when expanded —
     that is what keeps this a game with a chat panel and not a chat app. */
  #chatlog {
    left: max(7px, env(safe-area-inset-left, 0px));
    right: max(7px, env(safe-area-inset-right, 0px));
    bottom: calc(7px + env(safe-area-inset-bottom, 0px) + var(--kb, 0px));
    width: auto;
    height: min(45dvh, calc(100dvh - var(--kb, 0px) - env(safe-area-inset-bottom, 0px) - 14px));
    /* The keyboard offset also moves the sheet upward. Bound its height by the
       space left above that offset so the header never slips off-screen. */
    max-height: calc(100dvh - var(--kb, 0px) - env(safe-area-inset-bottom, 0px) - 14px);
    border-radius: 15px;
    transition: height .18s cubic-bezier(.2, .9, .25, 1);
  }

  /* 85dvh put the sheet's header UNDER the HUD (which runs to ~178px on a 844px
     phone) and left a 118px sliver of world. 70dvh keeps the tabs clear of the
     HUD and leaves a real strip of meadow — this is a game with a chat panel. */
  #chatlog[data-sheet="expanded"] {
    height: min(70dvh, calc(100dvh - var(--kb, 0px) - env(safe-area-inset-bottom, 0px) - 14px));
  }

  /* The HUD sits at z-index 5 and owns the top lane. With the keyboard up the
     sheet legitimately fills everything above it, and at z-index 4 the HUD then
     painted straight through the tab row. An open sheet outranks the HUD; a
     closed one never overlaps it, so nothing is hidden that you needed.
     Still below the traveler card (26), the context menu (45) and the emoji
     picker (46), all of which open FROM this panel. */
  body.chatlog #chatlog { z-index: 6; }

  #chatgrip { height: 26px; }
  #chatgrip::before { width: 46px; height: 4px; }
  #chatjump { bottom: 10px; padding: 8px 14px; font-size: 12px; }
  #chattyping { height: 18px; font-size: 11.5px; line-height: 18px; }
  #chatsuggest { max-height: min(40dvh, calc(100dvh - var(--kb, 0px) - 130px)); }
  .chat-suggest-option { min-height: 44px; }
  #chatemoji { width: 44px; height: 44px; font-size: 19px; }
  #chathidden,
  #chattypingtoggle { min-height: 36px; padding-inline: 8px; }
  #chatloghdr { min-height: 54px; padding: 5px; gap: 3px; }
  /* Labels stay on. Four unlabelled glyphs is a guessing game, and Field vs Nearby
     vs Whispers is exactly the distinction a first-time player has to get right.
     Stacked, they cost 0 horizontal room and still clear the 44px target. */
  #chatloghdr .chat-tab {
    height: 44px;
    min-height: 44px;
    flex: 1;
    gap: 1px;
    padding: 0 3px;
    flex-direction: column;
    justify-content: center;
  }

  #chatloghdr .chat-tab span {
    display: block;
    max-width: 100%;
    overflow: hidden;
    font-size: 11px;
    line-height: 1.1;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  #chatloghdr .chat-tab .ui-icon { width: 17px; height: 17px; }
  /* The unread chip cannot ride the flow in a stacked tab — it would push the
     label off. Corner it instead. */
  #chatloghdr .chat-tab { position: relative; }
  #chatloghdr .chat-tab-count { position: absolute; top: 2px; right: 2px; }
  #chatloghdr #chatlogx { width: 44px; height: 44px; }
  #chatloghdr #chatlogx .ui-icon { width: 19px; height: 19px; }
  #voicelog { padding: 7px 6px; }
  /* No hover on a phone: the tools column dies and long-press owns reactions
     and actions, so rows get their full width back and grouped lines sit tight. */
  .chat-message { padding: 7px 3px; grid-template-columns: 30px minmax(0, 1fr); gap: 6px; }
  /* Narrow sheet: the bubble may take more of the line before wrapping. */
  .chat-message.mine { grid-template-columns: minmax(0, 1fr); max-width: 86%; padding: 6px 10px 7px; }
  .chat-message-tools { display: none; }
  .chat-message-tools button { width: 44px; min-width: 44px; height: 44px; min-height: 44px; }
  .chat-text { font-size: 14px; line-height: 1.42; }
  .chat-author { font-size: 13px; max-width: 160px; }
  #chatmsg { min-height: 44px; font-size: 16px; }
  #chatgo { width: 44px; height: 44px; }
  #chatsend { min-height: 58px; padding-bottom: max(7px, env(safe-area-inset-bottom, 0px)); }
  #chatreply button,
  #chattarget button { width: 44px; height: 44px; }
  .chat-people button,
  #chatcontext button { min-height: 44px; }
  #chatcontext.reaction-picker .reaction-choice {
    width: 44px;
    min-width: 44px;
    height: 44px;
    min-height: 44px;
  }
  #fieldnote {
    left: max(8px, env(safe-area-inset-left, 0px));
    bottom: calc(94px + env(safe-area-inset-bottom, 0px) + var(--kb, 0px));
    width: calc(100vw - max(16px, env(safe-area-inset-left, 0px) + env(safe-area-inset-right, 0px)));
    box-sizing: border-box;
    padding: 6px 9px 6px 6px;
    font-size: 11px;
  }
  /* The social dock owns the corner anchor now. These offsets were written when
     the launcher was position:fixed; against the relative-positioned flex child
     it became an 8px nudge that broke the column's left edge. Size only. */
  #chatlogbtn {
    width: 44px;
    height: 44px;
  }
  .npc-audience { min-width: 74px; width: auto; height: 44px; min-height: 44px; padding: 0 8px; justify-content: center; font-size: 11px; }
  .npc-audience span { display: inline; }
}

@media (pointer: coarse) {
  .chat-message-tools { display: none; }
  .chat-message {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }
  .chat-message-tools button,
  #chatcontext button,
  #chatemoji,
  #chatcontext.reaction-picker .reaction-choice {
    min-width: 44px;
    min-height: 44px;
  }
  #chatmsg { min-height: 44px; font-size: 16px; }
  .chat-suggest-option { min-height: 44px; }
  .chat-message.failed .chat-status { min-height: 32px; padding-inline: 10px; }
  #chatgrip { height: 26px; }
  .npc-audience { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  #fieldnote.arrive,
  #chatlogbtn.pop,
  .chat-message.arrive,
  .chat-status.sent,
  #chatjump:not([hidden]),
  .chat-message.sending .chat-status { animation: none !important; }
  #chatlog { transition: none !important; }
  /* The typing sheen never engages here (it is gated behind no-preference),
     and the tactile scales settle to plain state changes. */
  .chat-reactions button:not([hidden]):hover,
  #chatcontext.reaction-picker .reaction-choice:hover,
  #chatcontext.reaction-picker .reaction-choice:focus-visible,
  #chatcontext.reaction-picker .reaction-choice:active { transform: none; }
}

/* The room's name when the tablist is sparse — the header must never be an
   empty bar with a lone close button. */
#chattitle {
  min-width: 0;
  flex: 1;
  padding: 0 5px;
  overflow: hidden;
  color: #e8dcb2;
  font: 700 13px ui-rounded, system-ui, sans-serif;
  letter-spacing: .01em;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#chattitle[hidden] { display: none !important; }

/* YOUR reaction wears the leaf: tapping it takes it back (server-side toggle). */
.chat-reactions button.yours {
  color: #dff0d2;
  background: rgba(149, 207, 139, .16);
  border-color: rgba(149, 207, 139, .5);
}
.chat-reactions button.yours:not([hidden]):hover {
  background: rgba(149, 207, 139, .24);
  border-color: rgba(149, 207, 139, .7);
}
/* The picker marks the choice you already hold the same way. */
#chatcontext.reaction-picker .reaction-choice.current {
  background: rgba(149, 207, 139, .18);
  border-radius: 9px;
  box-shadow: inset 0 0 0 1px rgba(149, 207, 139, .55);
}

/* The long-press sheet: reactions ride the top row, actions stack below. */
#chatcontext.row-menu { width: 236px; }
#chatcontext.row-menu .context-reactions {
  display: flex;
  justify-content: space-between;
  padding: 2px 2px 4px;
  border-bottom: 1px solid rgba(198, 168, 91, .18);
  margin-bottom: 3px;
}
#chatcontext.row-menu .reaction-choice {
  width: 42px;
  min-width: 42px;
  height: 42px;
  min-height: 42px;
  padding: 0;
  display: grid;
  place-items: center;
  font-size: 18px;
}
#chatcontext.row-menu .reaction-choice.current {
  background: rgba(149, 207, 139, .18);
  border-radius: 9px;
  box-shadow: inset 0 0 0 1px rgba(149, 207, 139, .55);
}
