:root {
  --bg: #080c08;
  --panel: #111811;
  --panel2: #16201608;
  --line: #1f2d1f;
  --line-soft: #182518;
  --green: #36e25a;
  --green-dim: #1f7d36;
  --text: #e7f3e7;
  --muted: #7d917d;
  --accent: #c6ff3a;
  --danger: #ff6b6b;
  --warn: #f5c451;
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }   /* no blue tap-flash on touch */
html, body { height: 100%; overflow-x: hidden; max-width: 100%; background: var(--bg); }
body {
  margin: 0;
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  background:
    radial-gradient(1100px 540px at 72% -12%, rgba(54, 226, 90, .075), transparent 62%),
    radial-gradient(900px 620px at -8% 108%, rgba(198, 255, 58, .035), transparent 55%),
    var(--bg);
  color: var(--text);
}
::selection { background: rgba(54, 226, 90, .3); }
.hidden { display: none !important; }
.muted { color: var(--muted); }
.fine { color: var(--muted); font-size: 12px; margin: 6px 0 0; }
.err { color: var(--danger); font-size: 13px; min-height: 16px; margin-top: 6px; }
/* Empty status/error slots collapse so modals don't trail dead space; they take
   their row back the moment a message lands in them. */
.status:empty, .err:empty { display: none; }

/* Full-page blocked notice for kicked/banned users (body is swapped for this;
   the stylesheet survives). Message text is admin-configurable. */
.blocked-screen { min-height: 100vh; min-height: 100dvh; display: grid; place-items: center; padding: 20px; }
.blocked-card {
  width: 100%; max-width: 360px; text-align: center;
  background: linear-gradient(180deg, #131c13, #0f160f);
  border: 1px solid var(--line); border-radius: 20px; padding: 30px 26px;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .5);
}
.blocked-icon { font-size: 42px; line-height: 1; filter: drop-shadow(0 0 18px rgba(255, 107, 107, .35)); }
.blocked-title { margin: 12px 0 6px; font-size: 22px; letter-spacing: -.02em; color: var(--danger); }
.blocked-msg { margin: 0; color: var(--muted); line-height: 1.55; }
.blocked-reason {
  margin: 16px 0 0; padding: 10px 12px; border-radius: 10px; text-align: left;
  font-size: 13px; line-height: 1.5; color: var(--text); word-break: break-word;
  border: 1px solid var(--danger-dim, #5a2a2a); background: rgba(255, 107, 107, .07);
}
.blocked-reason-label {
  display: block; font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--danger); font-weight: 700; margin-bottom: 3px;
}
.blocked-reconnect { margin-top: 18px; padding: 11px 26px; }

.logo { color: var(--green); text-shadow: 0 0 10px rgba(54, 226, 90, .55); }
.brand { font-weight: 800; font-size: 26px; letter-spacing: -0.5px; }
.brand.sm { font-size: 18px; flex: none; }   /* the logo never yields space to a long name */

button { cursor: pointer; font: inherit; border-radius: 10px; border: 1px solid var(--line);
  transition: border-color .15s ease, background .15s ease, color .15s ease, box-shadow .15s ease, filter .15s ease;
  user-select: none; -webkit-user-select: none; }   /* don't word-select a button's label on tap */
.primary { background: linear-gradient(180deg, #41e765, #2cc94e); color: #04140a; font-weight: 700; border: none; padding: 11px 16px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), 0 3px 12px rgba(54, 226, 90, .16); }
.primary:hover { filter: brightness(1.07); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), 0 4px 16px rgba(54, 226, 90, .26); }
.primary:active { filter: brightness(.96); }
.accent { background: var(--accent); color: #1a1f05; font-weight: 700; border: none; padding: 10px 14px; }
.ghost { background: transparent; color: var(--text); padding: 7px 12px; flex: none; white-space: nowrap; }
.ghost:hover { border-color: var(--green-dim); background: rgba(54, 226, 90, .06); }

input {
  background: #0c120c; border: 1px solid var(--line); color: var(--text);
  border-radius: 10px; padding: 11px 13px; font: inherit; outline: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}
input:focus { border-color: var(--green-dim); box-shadow: 0 0 0 3px rgba(54, 226, 90, .1); }

/* ---- Auth / connect ---- */
.auth { min-height: 100vh; min-height: 100dvh; display: grid; place-items: center; padding: 20px; overflow-y: auto;
  /* Keep the Connect button clear of the on-screen keyboard. Chrome's VirtualKeyboard API
     overlays the content and exposes the keyboard height via env(); Brave resizes the
     viewport instead, so 100dvh shrinks. Either way the centred card stays above it. */
  padding-bottom: calc(20px + env(keyboard-inset-height, 0px)); position: relative; }
/* A soft green aura behind the card. Absolutely positioned so it can never add
   scrollable height (short/landscape viewports); paints before the card — no z-index. */
.auth::before {
  content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  pointer-events: none;
  width: min(560px, 90vw); height: min(560px, 90vw); border-radius: 50%;
  background: radial-gradient(circle, rgba(54, 226, 90, .08), transparent 65%);
}
.auth-card {
  position: relative;
  width: 100%; max-width: 380px;
  background: linear-gradient(180deg, #131c13, #0f160f);
  border: 1px solid var(--line); border-radius: 20px; padding: 30px 28px;
  display: flex; flex-direction: column; gap: 11px; text-align: center;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .5);
}
@media (prefers-reduced-motion: no-preference) {
  .auth-card { animation: auth-in .45s ease .05s backwards; }
}
@keyframes auth-in { from { opacity: 0; transform: translateY(8px); } }
.auth-card .brand { font-size: 28px; }
.auth-card input { text-align: center; }
.auth-card .primary { padding: 13px 16px; font-size: 15px; border-radius: 12px; }
.auth-note { border-top: 1px solid var(--line-soft); padding-top: 12px; margin-top: 4px; line-height: 1.5; }
/* "By logging in you accept the Privacy Policy" — always shown, link opens the popup. */
.auth-privacy { margin-top: 0; }
.linklike { background: none; border: none; padding: 0; font: inherit; cursor: pointer;
  color: var(--accent); text-decoration: underline; }
.linklike:hover { color: var(--green); }
/* The policy popup: a wider card with left-aligned document styling. */
.privacy-card { width: min(600px, 100%); text-align: left; }
.privacy-date { color: var(--muted); font-weight: 400; font-size: 12px; margin-left: 6px; }
.privacy-body { overflow-y: auto; min-height: 0; font-size: 13px; line-height: 1.6; color: var(--text); }
.privacy-body::-webkit-scrollbar { width: 8px; }
.privacy-body::-webkit-scrollbar-thumb { background: var(--line); border-radius: 8px; }
.privacy-body h3 { margin: 14px 0 4px; font-size: 13px; color: var(--green); }
.privacy-body p { margin: 6px 0; }
.privacy-body ul { margin: 6px 0; padding-left: 20px; }
.privacy-body li { margin: 2px 0; }
/* Amber safety warning on the connect screen. */
.auth-disclaimer { margin: 4px 0 0; padding: 10px 12px; border-radius: 10px; text-align: left;
  font-size: 12.5px; line-height: 1.55; color: #e8d9b0;
  border: 1px solid rgba(245, 196, 81, .32); background: rgba(245, 196, 81, .07); }
.auth-disclaimer b { color: var(--warn); }
.tagline { color: var(--muted); margin: 0 0 10px; }
.tabs { display: flex; gap: 6px; margin-bottom: 4px; }
.tab { flex: 1; background: #0c120c; color: var(--muted); padding: 9px; }
.tab.active { color: var(--green); border-color: var(--green-dim); }

/* ---- App layout ---- */
/* On Chromium the keyboard overlays the content (VirtualKeyboard API in app.js), so
   the app stays full-height and never reflows; app.js slides it up by the keyboard
   height with a transform — compositor-only, true 60fps. The transition smooths the
   slide; the standard UI-motion curve (little deceleration tail) tracks the keyboard's
   close better than ease, so the box doesn't hang as it settles. */
.app { height: 100vh; height: 100dvh; display: flex; flex-direction: column; transition: transform .2s cubic-bezier(0.4, 0, 0.2, 1); will-change: transform; }
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  /* Horizontal padding grows on wide screens so the header content lines up with
     the centred chat column (.chatcol max-width below). */
  padding: 12px max(18px, calc((100% - 1028px) / 2 + 14px));
  border-bottom: 1px solid var(--line-soft);
  background: linear-gradient(180deg, rgba(15, 24, 15, .9), rgba(10, 16, 10, .82));
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  overflow: hidden; max-height: 120px;   /* generous, never clips; the cap so it can transition to 0 */
  transition: max-height .22s ease, padding .22s ease, opacity .18s ease;
}
/* Resize-mode browsers (Brave / iOS) don't slide #app, so collapse the header up out of
   the way while the keyboard is open — the conversation gets the full height, matching
   the way Chrome's transform slides the header off the top. (app.js toggles .kb-up.) */
.app.kb-up .topbar { max-height: 0; padding-top: 0; padding-bottom: 0; opacity: 0; border-bottom-color: transparent; }
.wallet { display: flex; align-items: center; gap: 10px; font-size: 12px; min-width: 0; flex: 1; justify-content: flex-end; }
/* Name + address stacked into one block, sized to roughly match the button height.
   The name truncates with an ellipsis instead of being hidden on small screens.
   The block is clickable: it opens your own profile (where your balance lives). */
.me-id { display: flex; flex-direction: column; align-items: flex-end; justify-content: center; min-width: 0; line-height: 1.2; }
#me-card { cursor: pointer; border-radius: 6px; padding: 2px 4px; min-width: 0; overflow: hidden; }
#me-card:hover { background: var(--panel); }
#me-card:hover .me, #me-card:focus-visible .me { text-decoration: underline; }
.me { font-weight: 700; font-size: 13px; color: var(--green); white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.addr { color: var(--muted); font-size: 11px; font-family: ui-monospace, monospace; white-space: nowrap; }

.banner {
  background: #2a1f08; color: var(--accent); border-bottom: 1px solid #4a3a10;
  padding: 9px 16px; font-size: 13px;
}
.status { color: var(--green); font-size: 13px; min-height: 16px; }
.status a, .send-flash a { color: var(--accent); }

.layout { flex: 1; display: grid; grid-template-columns: 1fr; min-height: 0; }
/* The conversation reads as a centred column on wide screens instead of hugging
   the left edge of the viewport; width-capped, so mobile is unchanged. */
.chatcol { display: flex; flex-direction: column; min-height: 0; padding: 0 14px 14px; gap: 10px;
  width: 100%; max-width: 1028px; margin: 0 auto; }   /* no top padding: the feed meets the header border with no gap */
.messages {
  flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 8px;
  font-size: 15px; line-height: 1.45;
  /* Messages fade out as they slide under the header instead of being cut off. */
  mask-image: linear-gradient(180deg, transparent 0, #000 16px);
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 16px);
  /* A scroll container clips overflow-x, which would cut Lumina's orb glow at the left
     edge. Extend the clip box left into .chatcol's padding with a negative margin + equal
     padding — the content stays put, but the glow can now bleed into the margin. */
  margin-left: -10px; padding-left: 10px;
  scrollbar-width: none; -ms-overflow-style: none;   /* hide scrollbar — overlay-style, like mobile */
}
.messages::-webkit-scrollbar { display: none; }
/* A short feed hugs the composer: this flex spacer absorbs the leftover height
   ABOVE the rows, so messages start at the bottom and grow upward until the feed
   overflows (then the spacer is 0 and normal scrolling takes over). Safer than
   justify-content:flex-end, which breaks scrolling of overflowing flex content.
   (When the feed is empty, the watermark rules below repurpose this ::before.) */
.messages::before { content: ""; flex: 1 1 auto; }
/* A row hidden by a pending (undoable) moderator delete — gone from layout and
   grouping, restored if Undo is clicked before the window expires. */
.msg.pending-del { display: none !important; }
/* ---- Empty-feed watermark ----
   Pure CSS off :empty — the feed is JS-built (no whitespace nodes; 'hello' resets it
   with innerHTML=''), so the watermark needs no JS and vanishes the moment any row
   lands. The delayed fade-in keeps it from flashing during the brief empty gap
   between login and the history replay. */
.messages:empty { justify-content: center; align-items: center; gap: 12px; }
.messages:empty::before {
  /* Both watermark strings follow the admin-configured branding (see applyBranding). */
  content: var(--brand-logo, "▲"); font-size: 44px; line-height: 1; flex: none;   /* not the spacer here */
  color: var(--green); opacity: .3;
  text-shadow: 0 0 26px rgba(54, 226, 90, .4);
  animation: feed-hush .6s ease .5s backwards;
}
.messages:empty::after {
  content: var(--empty-chat-text, "Quiet in here — say !aeon online");
  color: var(--muted); font-size: 14px;
  animation: feed-hush .6s ease .5s backwards;
}
@keyframes feed-hush { from { opacity: 0; } }
/* Wrapper lets the scrolled-up affordances float over the bottom of the feed. */
.msgwrap { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.scroll-aff {
  position: absolute; right: 12px; bottom: 10px; z-index: 5;
  /* A right-anchored row: the @ mentions chip sits LEFT of the scroll arrow when
     both show; alone, either one hugs the corner. Empty = zero footprint. */
  display: flex; flex-direction: row; align-items: center; gap: 8px; pointer-events: none;
}
.scroll-aff > * { pointer-events: auto; }
.scroll-down {
  position: relative;
  width: 34px; height: 34px; border-radius: 50%; background: rgba(17, 24, 17, .88); border: 1px solid var(--line);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: var(--green); font-size: 16px; line-height: 1; display: grid; place-items: center; box-shadow: 0 4px 14px #0008;
  touch-action: none;   /* let the drag-up gesture own touches on the arrow (no scroll takeover) */
  transition: transform .15s ease, border-color .12s ease, color .12s ease;   /* snap-back + armed tint */
}
.scroll-down:hover { border-color: var(--green-dim); }
/* Dragged past the threshold → will jump to the newest message on release. */
.scroll-down.armed { border-color: var(--green); color: var(--accent); background: #0f1a0f; }
/* Badge on the down-arrow: how many messages are below the current view. Grey while
   they're all already seen; green (and pulsing) while some are new (unseen) arrivals. */
.scroll-count {
  position: absolute; top: -7px; right: -7px; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 999px; background: var(--muted); color: #0a0e0a; font-weight: 700;
  font-size: 11px; line-height: 18px; text-align: center; box-shadow: 0 2px 6px #0006;
}
.scroll-count.hidden { display: none; }
.scroll-count.unseen { background: var(--green); color: #04140a; }   /* green until all new messages are seen */
.scroll-count.alert { animation: scroll-badge-pulse 1.4s ease-out infinite; }   /* pulse only until acknowledged */
@keyframes scroll-badge-pulse {
  0%   { box-shadow: 0 2px 6px #0006, 0 0 0 0 rgba(54, 226, 90, .5); }
  70%  { box-shadow: 0 2px 6px #0006, 0 0 0 7px rgba(54, 226, 90, 0); }
  100% { box-shadow: 0 2px 6px #0006, 0 0 0 0 rgba(54, 226, 90, 0); }
}
.msg { display: flex; gap: 8px; align-items: baseline; }
/* Chat messages stack the username over a text box; send/system/inspect lines keep
   the default inline row. align-items:flex-start lets the box hug its content. */
/* Chat row: the sender's avatar in the left gutter, then the bubble. Your own
   messages (.mine) drop the avatar and push the bubble to the right. */
.msg.chat { align-items: flex-start; gap: 8px; }
.msg.chat.mine { justify-content: flex-end; }
.msg.chat.cont { margin-top: -3px; }   /* tuck continuations closer to the message above */
/* Round colour-coded initial; the hue comes from the row (set per wallet in JS). */
.avatar {
  flex: none; width: 30px; height: 30px; margin-top: 1px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  font-weight: 700; font-size: 14px; line-height: 1;
  user-select: none; -webkit-user-select: none;
  background: linear-gradient(180deg, hsl(var(--hue, 145deg) 45% 30%), hsl(var(--hue, 145deg) 42% 21%));
  color: hsl(var(--hue, 145deg) 80% 82%);
  border: 1px solid hsl(var(--hue, 145deg) 45% 40%);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .35);
}
.avatar:hover { filter: brightness(1.12); }
/* An uploaded picture fills the disc; the hue-coded disc stays behind it as the
   loading backdrop (and the letter fallback when there's no picture). */
.avatar { overflow: hidden; position: relative; }
.avatar > .avatar-img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%; }
.msg.chat.cont .avatar { visibility: hidden; }   /* name + avatar show once per run; gutter stays reserved */
/* Sender name: capped to one line with an ellipsis so a long name never wraps. */
.msg .who { color: var(--green); font-weight: 700; cursor: pointer; margin-right: 6px;
  display: inline-block; max-width: 12em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
/* Name as the box's first line, tinted to match the sender's avatar. */
.msg .bubble .who { display: block; margin: 0 0 2px; font-size: 13px;
  color: hsl(var(--hue, 145deg) 70% 70%); }
.msg.chat.mine .who { display: none; }   /* your own messages don't repeat your name */
.msg .who:hover { text-decoration: underline; }
.msg .when { color: var(--muted); font-size: 11px; margin-left: auto; white-space: nowrap; flex: none; font-variant-numeric: tabular-nums; }
/* Timestamp + reply counter ride at the end of the message box, muted and small.
   Not selectable: double-clicking the timestamp starts a reply, so the OS shouldn't
   word-select it (the body text stays selectable). */
.msg .bubble .meta { display: inline-flex; gap: 6px; align-items: baseline; margin-left: 8px; vertical-align: baseline;
  user-select: none; -webkit-user-select: none; }
.msg .bubble .when { margin-left: 0; }
.msg .reply-count { color: var(--muted); font-size: 11px; white-space: nowrap; }
.msg .reply-count:empty { display: none; }
/* The message text (with its timestamp) sits in a subtle box below the username, so
   it's clear what's selectable/quotable. min-width:0 + overflow-wrap let a long
   unbroken string wrap inside the box instead of bursting off-screen on mobile. */
.msg .bubble {
  min-width: 0; max-width: min(80%, 600px); cursor: text;
  position: relative; z-index: 1;
  padding: 6px 12px; border-radius: 14px;
  background: linear-gradient(180deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, .026));
  border: 1px solid var(--line-soft);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
  overflow-wrap: anywhere; word-break: break-word;
  transition: border-color .15s ease;
}
.msg .bubble:hover { border-color: var(--green-dim); }
/* Your own messages: a green-tinted bubble on the right. */
.msg.chat.mine .bubble { background: linear-gradient(180deg, rgba(54, 226, 90, .17), rgba(54, 226, 90, .09)); border-color: rgba(54, 226, 90, .32); }
.msg.chat.mine .bubble:hover { border-color: var(--green); }
/* (No entry animation on new rows: the feed scrolls down for the new bubble
   instantly, so a fade-in left a brief blank gap — the whole chat appeared to
   jump up until the bubble finished appearing.) */
/* Within a run, square off the stacked edge so consecutive bubbles read as one block:
   the left edge for others, the right edge for your own messages. */
.msg.chat.cont .bubble { border-top-left-radius: 5px; }
.msg.chat.has-cont .bubble { border-bottom-left-radius: 5px; }
.msg.chat.mine.cont .bubble { border-top-left-radius: 13px; border-top-right-radius: 5px; }
.msg.chat.mine.has-cont .bubble { border-bottom-left-radius: 13px; border-bottom-right-radius: 5px; }
/* A message starting with "!" is a one-tap re-post: only the red TEXT is clickable
   to send; the rest of the box (e.g. the timestamp) opens the normal menu. */
/* The red "!" text is a one-tap re-post button: not selectable, so clicking it fast
   (or double-clicking) never flashes an OS word selection. */
.msg .bubble.bang .body { color: var(--danger); cursor: pointer; user-select: none; -webkit-user-select: none; }
.msg .bubble.bang:hover { border-color: var(--danger); }
.msg .body { color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; }
/* The timestamp/counter sits inline at the end of the text. When it doesn't fit, an
   inline bubble would be sized to the wider one-line max-content, leaving empty space
   to the right. JS adds .meta-wrap in that case: a column layout makes the box hug the
   text and drops the meta onto its own line at the bottom-right. */
.msg.chat .bubble.meta-wrap { display: flex; flex-direction: column; align-items: stretch; }
.msg.chat .bubble.meta-wrap .meta { align-self: flex-end; margin-left: 0; }
.msg .body a.msglink { color: var(--accent); text-decoration: underline; cursor: pointer; word-break: break-all; }
.msg .body a.msglink:hover { filter: brightness(1.1); }

/* ---- Same-author grouping ----
   A continuation hides the repeated name + avatar (the gutter stays reserved) so a
   sender's messages cluster under one avatar, with the stacked bubble edges squared
   off (see .cont/.has-cont radii above). */
.msg.cont .who { display: none; }   /* name shows once, on the run's first message */
/* @mentions are capped with an ellipsis so a long name can't push the row wide. */
/* @mention of someone: a green-outlined pill; mentioning yourself fills it in solid. */
.mention { display: inline-flex; align-items: baseline; max-width: 12em; vertical-align: bottom;
  color: var(--green); font-weight: 600; border: 1px solid var(--green-dim); border-radius: 5px; padding: 0 6px; }
.mention .mn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.mention.me { background: var(--green); color: #04140a; border-color: var(--green); font-weight: 700; }
/* Reply quote-preview: the cited author over a one-line snippet of their message,
   with a left accent bar. Clicking it jumps to (and flashes) the original. The
   snippet contributes no intrinsic width (width:0 + min-width:100% so a nowrap line
   can't force horizontal overflow); JS (refitQuote) floors the box at the snippet's
   natural width capped at the row — so a short quote caps the box where the snippet
   ends, and a long one fills the row and the snippet truncates with an ellipsis. */
.quote-ref {
  display: block; cursor: pointer; margin: 0 0 4px; padding: 2px 8px;
  border-left: 3px solid var(--green-dim); border-radius: 4px;
  background: rgba(54, 226, 90, .08); max-width: 100%;
}
.quote-ref:hover { background: rgba(54, 226, 90, .14); border-left-color: var(--green); }
.quote-author { display: block; color: var(--green); font-weight: 700; font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quote-snippet { display: block; color: var(--muted); font-size: 12px;
  width: 0; min-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quote-missing { font-style: italic; opacity: .8; }
/* Flash the cited message when its quote is clicked. Highlight the bubble, not
   the .msg row (which is full-width), so the tint stays inside the box. The accent
   ring (box-shadow) shows on any bubble — including your own green ones, whose
   .mine background/border would otherwise swallow a green flash. */
.msg.quote-flash .bubble { background: rgba(198, 255, 58, .07); border-color: var(--accent);
  transition: background .2s ease, border-color .2s ease; }
.msg.chat.mine.quote-flash .bubble { border-color: var(--accent); }   /* beat .mine's higher specificity */
.profile-card { gap: 14px; }
/* Header: the sender's colour-coded avatar beside their name + wallet. */
.profile-top { display: flex; align-items: center; gap: 13px; }
.profile-avatar { width: 52px; height: 52px; font-size: 22px; flex: none; cursor: default; margin-top: 0;
  /* A soft ring in the user's own hue lifts the avatar off the card. */
  box-shadow: 0 0 0 3px hsl(var(--hue, 145deg) 50% 45% / .22), 0 4px 12px rgba(0, 0, 0, .4); }
/* Your own profile avatar is the upload button: pencil badge + hover ring. The
   badge sits outside the disc's clip, so overflow stays visible here. */
.profile-avatar.avatar-editable { cursor: pointer; overflow: visible; }
.profile-avatar.avatar-editable > .avatar-img { clip-path: circle(50%); }
.profile-avatar.avatar-editable:hover,
.profile-avatar.avatar-editable:focus-visible { filter: brightness(1.15); }
.profile-avatar.avatar-editable.busy { opacity: .55; pointer-events: none; }
.avatar-edit-badge {
  position: absolute; right: -4px; bottom: -4px; width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center; font-size: 11px; line-height: 1;
  background: var(--panel); color: var(--accent); border: 1px solid var(--line);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .45);
}
/* The PFP cropper's styles are injected by the shared /cropper.js. */

/* Someone else's picture on their profile card zooms to full size on tap. */
.profile-avatar.avatar-zoomable { cursor: zoom-in; }
.profile-avatar.avatar-zoomable:hover,
.profile-avatar.avatar-zoomable:focus-visible { filter: brightness(1.15); }
.pfp-lightbox { cursor: zoom-out; }
/* The stored square at its native size (≤256px), capped so it fits small screens. */
.pfp-full { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.pfp-full img { max-width: min(80vw, 320px); max-height: 60vh; border-radius: 14px;
  border: 1px solid var(--line); background: var(--panel);
  box-shadow: 0 0 0 3px hsl(var(--hue, 145deg) 50% 45% / .22), 0 24px 64px rgba(0, 0, 0, .6); }
.pfp-full figcaption { font-weight: 700; font-size: 14px; color: hsl(var(--hue, 145deg) 70% 70%); }

/* "Remove picture": a quiet text action under the wallet address. */
.p-pfp-remove { background: none; border: none; padding: 0; margin-top: 2px; align-self: flex-start;
  color: var(--muted); font-size: 12px; text-decoration: underline; border-radius: 4px; }
.p-pfp-remove:hover { color: var(--danger); }
.profile-id { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.profile-name { font-weight: 700; font-size: 17px; color: var(--text); word-break: break-word;
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.profile-you { color: var(--muted); font-weight: 400; font-size: 12px; }
.profile-addr { color: var(--muted); font-family: ui-monospace, monospace; font-size: 12px; word-break: break-all; }
/* Stat tiles on your own profile: balance spans the row (it's wider), with the two
   community stats — online now + soaks — side by side beneath it. */
.profile-stats { position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.pstat { min-width: 0; background: linear-gradient(180deg, rgba(255, 255, 255, .045), rgba(255, 255, 255, .02));
  border: 1px solid var(--line-soft);
  border-radius: 10px; padding: 9px 11px; display: flex; flex-direction: column; gap: 3px; }
.pstat-wide { grid-column: 1 / -1; }
.pstat-label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
/* Small "i" on a stat; click it to reveal an explanation popover below the stats. */
.pstat-info { display: inline-flex; align-items: center; justify-content: center; width: 13px; height: 13px;
  border-radius: 50%; border: 1px solid var(--muted); background: transparent; color: var(--muted);
  font-size: 9px; font-weight: 700; line-height: 1; text-transform: none; letter-spacing: 0;
  padding: 0; cursor: pointer; vertical-align: middle; }
.pstat-info:hover { color: var(--green); border-color: var(--green); }
.pstat-tip { position: absolute; top: 100%; left: 0; right: 0; margin-top: 6px; z-index: 3;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px;
  font-size: 11px; color: var(--muted); line-height: 1.45; box-shadow: 0 8px 20px #000a;
  text-transform: none; letter-spacing: 0; }
.pstat-val { color: var(--text); font-size: 17px; font-weight: 700; display: flex; align-items: center; gap: 6px;
  font-variant-numeric: tabular-nums; }
.pstat-val .pbal-val { color: var(--green); }
.pbal-refresh { display: inline-flex; align-items: center; justify-content: center; background: transparent;
  border: none; color: var(--muted); cursor: pointer; font-size: 15px; line-height: 1; padding: 0; border-radius: 50%; }
.pbal-refresh:hover { color: var(--green); }
.pbal-refresh:disabled { opacity: .5; cursor: default; }
.pbal-refresh.spin { animation: spin .6s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
/* Claim-username control (your own profile): a CTA + hint, replaced by a locked badge. */
.profile-claim { display: flex; flex-direction: column; gap: 5px; }
.profile-claim .accent { align-self: flex-start; }
.profile-claim-hint { color: var(--muted); font-size: 12px; line-height: 1.35; }
/* Claimed: the green badge with a small red Unclaim button to its right. */
.profile-claim.is-claimed { flex-direction: row; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.profile-claim-badge { color: var(--green); font-size: 13px; font-weight: 600; line-height: 1.4; }
.profile-claim-badge b { font-weight: 800; }
.profile-claim .p-unclaim {
  flex: none; color: var(--danger); background: transparent; border: 1px solid var(--danger);
  border-radius: 6px; font-size: 11px; font-weight: 600; padding: 1px 8px;
}
.profile-claim .p-unclaim:hover { background: rgba(255, 107, 107, .12); border-color: var(--danger); }
/* Local /help (and similar) notices: a plain, left-aligned muted line — no "system" label. */
.msg.sysmsg { display: block; }
.sysline { transition: opacity .6s ease; }
.sysline.fading { opacity: 0; }
.sysline .sysbody { color: var(--muted); font-size: 12px; font-style: italic; letter-spacing: .02em; }
.inspect-link { color: var(--accent); cursor: pointer; font-weight: 600; }
.inspect-link:hover { text-decoration: underline; }
/* Lumina — the system bot. A fixed green avatar (the ▲ brand mark) + a small BOT tag
   mark its event bubbles (sends, soaks, inspects, kicks, bans) as official. */
/* Lumina's avatar: a glowing white orb (bright core, soft halo). */
.msg.lumina .avatar.lumina-avatar {
  background: radial-gradient(circle at 40% 35%, #ffffff 0%, #f5f9ff 46%, #dcebff 100%);
  border: none; cursor: default;
  box-shadow: 0 0 4px 1px rgba(255, 255, 255, .55), 0 0 8px 1px rgba(190, 220, 255, .3);
}
/* Don't set display here — let the grouping rule (.msg.cont .who) still hide the
   repeated name on a run of Lumina events, the same as user messages. */
.msg.lumina .bubble .who { color: #fff; cursor: default; }
.msg.lumina .bubble .who:hover { text-decoration: none; }
/* "system" tag: plain text in the same muted grey as the timestamp, not a pill. */
.lumina-badge { margin-left: 7px; vertical-align: baseline; font-weight: 400;
  font-size: 11px; color: var(--muted); letter-spacing: .01em; }
/* Lumina's event text uses the announcement yellow (the old soak colour); links stay green. */
.msg.lumina .body { color: var(--accent); }
.msg.lumina .body b { color: var(--accent); font-weight: 700; }
.msg.lumina .body a { color: var(--green); }
/* Cue to the right of an inspected name: signals the name is clickable to open
   the Unicode inspector. */
.inspect-cue { margin-left: 3px; font-size: .85em; opacity: .75; }
.inspect-link:hover .inspect-cue { opacity: 1; }
.inspect-overlay { position: fixed; inset: 0; background: rgba(2, 6, 2, .64); display: grid; place-items: center; z-index: 80;
  /* Same keyboard-inset handling as .modal: a profile card can be opened while the
     chat keyboard stays up (tapping a name keeps composer focus — see restoreKeyboard). */
  padding: 16px 16px calc(16px + env(keyboard-inset-height, 0px));
  transition: padding-bottom .2s cubic-bezier(0.4, 0, 0.2, 1);
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); }
.inspect-card { width: min(440px, 100%); max-height: 80vh; max-height: 85dvh; overflow-y: auto; background: linear-gradient(180deg, #131c13, #0f160f); border: 1px solid var(--line); border-radius: 14px; padding: 18px; display: flex; flex-direction: column; gap: 10px;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .55); }
.inspect-head { word-break: break-word; }
.inspect-head { font-weight: 700; }

/* @-mentions chip: floats over the feed beside the scroll arrow (see .scroll-aff),
   styled as the same round glassy button. */
.mentions-btn {
  position: relative; flex: none; padding: 0;
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(17, 24, 17, .88); border: 1px solid var(--line);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: var(--green); font-size: 15px; font-weight: 700; line-height: 1;
  display: grid; place-items: center; box-shadow: 0 4px 14px #0008;
}
.mentions-btn:hover { border-color: var(--green-dim); }
/* Staff-only settings wheel: a compact icon button to the moderation dashboard,
   so the header still fits the mentions button + identity block on mobile. */
.admin-btn { flex: none; padding: 6px 8px; line-height: 0; }
.admin-btn svg { width: 16px; height: 16px; display: block; }
.mentions-btn.alert { color: var(--accent); border-color: var(--accent); }
.mentions-count { position: absolute; top: -6px; right: -6px; min-width: 16px; height: 16px; padding: 0 4px;
  background: var(--accent); color: #101810; border-radius: 8px; font-size: 10px; font-weight: 800;
  display: flex; align-items: center; justify-content: center; }
/* Count chip beside the panel title. */
.mentions-total { display: inline-flex; align-items: center; justify-content: center; margin-left: 6px;
  min-width: 20px; padding: 1px 8px; border-radius: 999px; vertical-align: 2px;
  background: rgba(54, 226, 90, .12); color: var(--green); font-size: 11px; font-weight: 700; }
/* The list scrolls on its own (scrollbar hidden, like the feed) so the title and the
   Clear all / Close row stay put however long it grows. */
.mentions-list { display: flex; flex-direction: column; gap: 6px; max-height: min(52vh, 440px); overflow-y: auto;
  scrollbar-width: none; -ms-overflow-style: none; }
.mentions-list::-webkit-scrollbar { display: none; }
/* A row: the sender's hue-coded avatar, then name + reply/mention chip + time over a
   two-line snippet. The whole row is a button that jumps to the message. */
.mention-row { display: flex; align-items: flex-start; gap: 10px; text-align: left;
  background: linear-gradient(180deg, rgba(255, 255, 255, .04), rgba(255, 255, 255, .02));
  border: 1px solid var(--line-soft); border-radius: 10px; padding: 9px 11px; color: var(--text); }
.mention-row:hover { border-color: var(--green-dim); background: rgba(54, 226, 90, .07); }
.mention-row-avatar { margin-top: 1px; }
.mention-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.mention-row-top { display: flex; align-items: baseline; gap: 8px; font-size: 13px; }
.mention-row-top b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.mention-row-kind { flex: none; color: var(--muted); font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px; }
.mention-row-time { color: var(--muted); font-size: 11px; flex: none; margin-left: auto; font-variant-numeric: tabular-nums; }
.mention-row-snip { color: var(--muted); font-size: 12px; word-break: break-word;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.mentions-empty { color: var(--muted); font-size: 13px; line-height: 1.5; text-align: center; padding: 14px 8px 6px; }
/* Clear all wipes the list — tint it toward danger on hover so it reads as such. */
.mentions-clear:hover { color: var(--danger); border-color: var(--danger); background: rgba(255, 107, 107, .08); }
/* The inspected name spelled out one cell per character; suspicious cells go red,
   invisibles/spaces show a muted stand-in glyph. */
.ic-preview { display: flex; flex-wrap: wrap; gap: 3px; padding: 10px;
  background: #0a100a; border: 1px solid var(--line-soft); border-radius: 10px;
  font-size: 18px; line-height: 1; }
.ic-cell { display: inline-grid; place-items: center; min-width: 26px; min-height: 30px; padding: 0 5px;
  border-radius: 7px; background: rgba(255, 255, 255, .04); border: 1px solid var(--line-soft); }
.ic-cell.sus { background: rgba(255, 107, 107, .13); border-color: var(--danger); color: var(--danger); }
.ic-ghost { color: var(--muted); font-size: 12px; }
/* Verdict banner: green all-clear or red look-alike warning, count at the right. */
.ic-verdict { display: flex; align-items: baseline; gap: 8px; font-size: 13px; font-weight: 600;
  border-radius: 10px; padding: 9px 12px; }
.ic-verdict.ok { color: var(--green); background: rgba(54, 226, 90, .08); border: 1px solid rgba(54, 226, 90, .25); }
.ic-verdict.warn { color: var(--danger); background: rgba(255, 107, 107, .08); border: 1px solid rgba(255, 107, 107, .3); }
.ic-count { margin-left: auto; flex: none; font-weight: 400; color: var(--muted); font-size: 11px; }
.inspect-tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.inspect-tbl th { text-align: left; color: var(--muted); font-size: 10px; text-transform: uppercase; padding: 5px 6px; border-bottom: 1px solid var(--line); }
.inspect-tbl td { padding: 5px 6px; border-bottom: .5px solid var(--line); }
.inspect-tbl tr.sus td { background: rgba(255, 107, 107, .12); }
.ic-ch { font-size: 16px; }
.ic-inv { color: var(--danger); font-style: italic; font-size: 11px; }
.inspect-close { align-self: flex-end; background: var(--green); color: #04140a; font-weight: 700; border: none; border-radius: 8px; padding: 8px 16px; }
.modtool {
  margin-left: 8px; color: var(--muted); cursor: pointer; opacity: 0; flex: none;
  font-size: 13px; user-select: none;
}
.msg:hover .modtool { opacity: .7; }
.modtool:hover { opacity: 1; color: var(--accent); }
.mod-menu {
  position: fixed; z-index: 100; background: var(--panel); border: 1px solid var(--line);
  border-radius: 8px; padding: 4px; display: flex; flex-direction: column; min-width: 152px;
  box-shadow: 0 8px 24px #000a;
}
.mod-menu.hidden { display: none; }
.mod-menu button {
  background: transparent; border: none; color: var(--text); text-align: left;
  padding: 8px 12px; border-radius: 6px; font: inherit; font-size: 13px; cursor: pointer;
}
.mod-menu button:hover { background: #11181166; }
.mod-menu button[data-act="ban"] { color: var(--danger); }

/* ---- Custom message selection: self-drawn highlight + draggable pins ---- */
#sel-overlay { position: fixed; inset: 0; z-index: 101; pointer-events: none; }
#sel-overlay.hidden { display: none; }
.sel-hl-layer { position: absolute; inset: 0; pointer-events: none; }
.sel-hl { position: fixed; background: rgba(198, 255, 58, .28); }   /* square, like a native selection — no rounded line-ends */
.sel-handle { position: fixed; width: 2px; background: var(--accent); pointer-events: auto; touch-action: none; z-index: 102; display: none; }
.sel-handle::before { content: ""; position: absolute; left: 50%; width: 15px; height: 15px; border-radius: 50%; background: var(--accent); box-shadow: 0 1px 4px #000a; }
.sel-handle.start::before { top: 0; transform: translate(-50%, -100%); }
.sel-handle.end::before { bottom: 0; transform: translate(-50%, 100%); }
.sel-handle::after { content: ""; position: absolute; left: 50%; top: 50%; width: 42px; height: 64px; transform: translate(-50%, -50%); }   /* fat invisible grab area for touch */
#sel-overlay.dragging .sel-handle { pointer-events: none; }   /* while dragging, let the caret hit-test reach the text under the pin (capture still delivers the drag) */
.chatform button.copy-mode { background: var(--accent); color: #1a1f05; }
.sel-adjust { flex: none; padding: 9px 12px; font-size: 15px; line-height: 1; }
/* While selecting, hide the Message box (you're copying, not typing): its draft is
   preserved and can't shift left, and the freed width guarantees Copy stays fully
   on-screen even on narrow phones. The ◀ ▶ + Copy controls sit at the right. */
.chatform.selecting { gap: 8px; justify-content: flex-end; align-items: center; }   /* ◀ ▶ vertically centered with Copy */
.chatform.selecting textarea { display: none; }
/* Copy is the same element as Send: keep it bottom-aligned (like the base flex-end
   form) so swapping the label doesn't nudge it up a pixel off Send's position. */
.chatform.selecting button[type="submit"] { align-self: flex-end; }

/* Live toast/flash container. Floats over the bottom-right of the message feed
   rather than taking a row above the composer — overlaying a message is fine
   since notices auto-clear after a few seconds. Sits above the scroll-to-bottom
   arrow so the two never overlap. */
.ticker {
  position: absolute; right: 12px; bottom: 54px; z-index: 6;
  max-width: min(280px, 78%); pointer-events: none;
  display: flex; justify-content: flex-end;
}
.ticker:empty { display: none; }
.send-flash {
  background: rgba(17, 24, 17, .94);
  border: 1px solid var(--accent); border-left-width: 3px;
  padding: 6px 10px; border-radius: 8px;
  font-size: 13px; color: var(--accent); text-align: right;
  box-shadow: 0 6px 18px #000a; pointer-events: auto;
  display: flex; align-items: center; gap: 8px;
}
.send-flash > span { min-width: 0; }
.send-flash.danger { color: var(--danger); border-color: var(--danger); }
.send-flash.warn { color: var(--warn); border-color: var(--warn); }
.toast-x { background: none; border: none; color: inherit; flex: none;
  font-size: 13px; line-height: 1; padding: 2px; opacity: 0.75; }
.toast-x:hover { opacity: 1; }
/* Undo button on an optimistic-delete toast. */
.undo-btn { background: transparent; border: 1px solid var(--accent); color: var(--accent);
  border-radius: 6px; font-size: 12px; font-weight: 700; padding: 2px 10px; flex: none; }
.undo-btn:hover { background: rgba(198, 255, 58, .12); }


/* The composer sits in a subtle raised dock: a translucent panel around the input
   and Send, lifting it off the feed the way modern chat clients do. */
.chatform { position: relative; display: flex; gap: 8px; align-items: flex-end; min-height: 45px;   /* min-height keeps the row from shrinking when the Message box is hidden for selection (no 1px chat shift) */
  background: linear-gradient(180deg, rgba(20, 30, 20, .72), rgba(14, 22, 14, .72));
  border: 1px solid var(--line-soft); border-radius: 16px; padding: 7px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.chatform button[type="submit"] { min-width: 74px; text-align: center; border-radius: 11px; }   /* fixed width so "Send" and "Copy" are exactly the same size */
.chatform input { flex: 1; }
/* Multi-line composer: grows with content (capped in JS), Send stays bottom-aligned.
   min-width:0 keeps a long pasted/typed token from pushing the Send button off-screen. */
.chatform textarea {
  flex: 1; min-width: 0; resize: none; line-height: 1.35; max-height: 120px; overflow-y: auto;
  background: #0a100a; border: 1px solid transparent; color: var(--text);
  border-radius: 11px; padding: 10px 13px; font: inherit; outline: none;
  scrollbar-width: none; -ms-overflow-style: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.chatform textarea::-webkit-scrollbar { display: none; }
.chatform textarea:focus { border-color: var(--green-dim); box-shadow: 0 0 0 3px rgba(54, 226, 90, .08); }

/* @-mention autocomplete: a list that floats just above the composer. */
.mention-pop {
  position: absolute; left: 0; bottom: calc(100% + 6px); z-index: 40;
  min-width: 180px; max-width: min(320px, 80vw); max-height: 200px; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  padding: 4px; box-shadow: 0 8px 24px #000a; scrollbar-width: none;
}
.mention-pop::-webkit-scrollbar { display: none; }
.mention-pop.hidden { display: none; }
.mention-pop button {
  display: block; width: 100%; text-align: left; background: transparent; border: none;
  color: var(--text); padding: 8px 12px; border-radius: 6px; font: inherit; font-size: 13px;
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mention-pop button.active, .mention-pop button:hover { background: #11181166; color: var(--green); }

/* ---- Modals ---- */
.modal { position: fixed; inset: 0; background: rgba(2, 6, 2, .64); display: grid; place-items: center; z-index: 50;
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  /* Chrome mobile overlays the on-screen keyboard over the page (VirtualKeyboard API,
     see app.js) — a card centred in the full viewport sits half-hidden behind it.
     Shrink the centring area by the keyboard height, like .auth does; env() is 0 when
     the keyboard is closed and in resize-mode browsers (Brave/iOS), where the fixed
     overlay already tracks the shrunken viewport. The transition follows the keyboard's
     open/close instead of teleporting the card. */
  padding-bottom: env(keyboard-inset-height, 0px);
  transition: padding-bottom .2s cubic-bezier(0.4, 0, 0.2, 1); }
.modal-card {
  width: 340px; background: linear-gradient(180deg, #131c13, #0f160f); border: 1px solid var(--line);
  border-radius: 16px; padding: 22px; display: flex; flex-direction: column; gap: 10px;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .55);
}
.modal-card.wide { width: 460px; max-width: 94vw; }
.modal-card h3 { margin: 0 0 4px; }
.modal-card h4 { margin: 10px 0 4px; font-size: 13px; color: var(--muted); }
/* Send modal: recipient identity up top — their hue-coded avatar beside the name,
   with the FULL destination address underneath (verify before you sign). */
.send-head { display: flex; align-items: center; gap: 12px; }
.send-avatar { width: 44px; height: 44px; font-size: 19px; flex: none; cursor: default; margin-top: 0; }
.send-id { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.send-id h3 { margin: 0; font-size: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.send-addr { margin: 0; color: var(--muted); font-size: 11px; line-height: 1.45;
  font-family: ui-monospace, monospace; word-break: break-all; }
/* Amount field: the number is the point — bigger and bold, the token symbol pinned
   inside the field's right edge. */
.amount-wrap { position: relative; }
.amount-wrap input { width: 100%; font-size: 18px; font-weight: 650; padding-right: 58px; }
.amount-unit { position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  color: var(--muted); font-size: 11px; font-weight: 800; letter-spacing: .08em; pointer-events: none; }
/* Quick-amount chips. */
.quick { display: flex; gap: 6px; }
.quick button { flex: 1; background: rgba(54, 226, 90, .05); color: var(--text); padding: 8px;
  border-color: var(--line-soft); border-radius: 9px; font-weight: 600; }
.quick button:hover { border-color: var(--green-dim); background: rgba(54, 226, 90, .12); color: var(--green); }
/* Soak confirm: the summary is the money line — body-size text, not fine print. */
.soak-summary { margin: 0; font-size: 14px; line-height: 1.5; color: var(--text); }
.soak-count-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 13px;
  background: rgba(255, 255, 255, .03); border: 1px solid var(--line-soft); border-radius: 10px; padding: 8px 8px 8px 12px; }
.soak-count-row input { width: 84px; flex: none; text-align: center; }
.soak-note { margin-top: -4px; min-height: 15px; }
.row { display: flex; gap: 8px; justify-content: flex-end; }
/* Profile actions sit below a hairline divider, separating them from the identity/stats. */
.profile-card .row { border-top: 1px solid var(--line-soft); padding-top: 13px; }
.withdraw { display: flex; flex-direction: column; gap: 8px; }
.history { max-height: 200px; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
.hrow { display: flex; justify-content: space-between; padding: 6px 8px; background: #0c120c; border-radius: 7px; }
.hrow .pos { color: var(--green); } .hrow .neg { color: var(--danger); }

@media (max-width: 720px) {
  /* Cap the header height so app.js can collapse it (.kb-up) when the keyboard opens. */
  .topbar { max-height: 64px; }
}
@media (max-width: 600px) {
  .msg .when { font-size: 10px; }
  .inspect-overlay { padding: 8px; }
  .inspect-card { padding: 14px; gap: 8px; }
  .inspect-tbl { font-size: 12px; }
  .inspect-tbl th, .inspect-tbl td { padding: 4px 5px; }
}
