/* ==========================================================
   SURFCHAT — appearance
   This file controls how everything LOOKS. Nothing here
   touches the database. Edit freely.
   ========================================================== */

:root {
  /* Every surface is the same pitch black. Separation comes
     from hairlines and type weight, never a lighter panel. */
  --void:  #000000;

  /* One hue, five stops. */
  --glow:   #9dffdd;   /* brightest — your own messages */
  --signal: #4cffb8;   /* the accent */
  --deep:   #1f8f6b;   /* second tone */
  --muted:  #3f8c74;   /* timestamps, labels */
  --line:   #10382a;   /* hairlines */

  --text:   #cdffec;

  /* The one exception to black-and-mint. Reports need to read as
     "something is wrong" at a glance, and a mint card among mint
     cards doesn't. Kept in the same luminous register as the mint
     so it still looks like the same screen. */
  --alert:      #ff5c6e;
  --alert-deep: #7d2230;

  --mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* Emoji come from the operating system, not from us — every
     platform ships its own set and there is no way to override
     that without shipping a font of several megabytes. What this
     does is name the modern ones explicitly and in order, so a
     machine with more than one installed picks the current one
     instead of an ancient fallback. */
  --emoji: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji',
           'Segoe UI Symbol', 'Android Emoji', 'EmojiSymbols', sans-serif;
  --sans: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;

  /* Scanline strength. Raise toward 0.30 for harsher,
     drop toward 0.06 for barely-there. */
  --scan-strength: 0.14;
}

* , *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--void);
}

body {
  color: var(--text);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;

  /* Kill the grey flash iOS paints over anything you tap. We give
     our own feedback with :active states below. */
  -webkit-tap-highlight-color: transparent;
}

/* Chrome shouldn't be selectable — long-pressing a button to get
   a "Copy" bubble is never what anyone wanted. Message text and
   bios stay selectable, since copying those is the point. */
button, .tab, .row, .who-row, .mark, .av, .eyebrow, .wordmark, .msg-meta {
  -webkit-user-select: none;
  user-select: none;
}
.bubble, .prof-bio, .report-quote, .lk-url, .log-line, input, textarea {
  -webkit-user-select: text;
  user-select: text;
}

/* Tells the browser these are tap targets, not things to
   double-tap-zoom on. Removes the old click delay. */
button, .tab, .row, .who-row, label, a { touch-action: manipulation; }

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input  { font: inherit; color: inherit; background: none; border: none; outline: none; }
input::placeholder { color: var(--muted); }
:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

.mono    { font-family: var(--mono); }
.eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- CRT scanlines ---------- */
.scan {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0,0,0,0) 0px,
    rgba(0,0,0,0) 2px,
    rgba(0,0,0,var(--scan-strength)) 2px,
    rgba(0,0,0,var(--scan-strength)) 3px
  );

  /* A full-screen fixed layer sitting above everything is cheap
     on some browsers and very expensive on others — Firefox on
     Android was redrawing this gradient across the whole screen
     on every scroll frame, which is exactly what "runs badly"
     feels like. These three lines hand it to the compositor as
     its own layer, so it's painted once and then just sits there
     while the page moves underneath. */
  transform: translateZ(0);
  will-change: transform;
  contain: strict;
}

/* ---------- app shell ----------
   Height, in order of preference:
     --vvh  measured live in JS from the visible viewport. This is
            the only thing iOS gets right when the keyboard is up.
     100dvh modern browsers, accounts for the sliding address bar
     100vh  last resort
   Without this, an open keyboard on iOS pushes the message box
   off the bottom of the screen and you type blind. */
.app {
  position: fixed;
  inset: 0;
  /* Three deep on purpose: each line only applies if the browser
     understands it, so an old one still gets a usable height.
     The max() is a floor — if the measured value ever comes back
     nonsense, the app doesn't collapse to nothing. */
  height: 100vh;
  height: 100dvh;
  height: max(var(--vvh, 100dvh), 320px);
  display: flex;
  flex-direction: column;
  overflow: hidden;

  /* keeps the header clear of the iPhone camera cutout */
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

/* ---------- header ---------- */
.header { flex: none; border-bottom: 1px solid var(--line); }
.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 13px 16px;
}
.wordmark {
  display: flex;
  align-items: center;
  min-width: 0;
}
/* ---- HEADER LOGO SIZE ----
   This one number controls it. Lower it to shrink, raise to grow. */
.wordmark-img {
  height: 26px;
  width: auto;
  display: block;
  /* No tint. The artwork is white with a heavy black outline, so
     on this background the outline disappears and the letterforms
     read on their own. */
}


.status {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--muted);
}
.dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--deep);
  transition: background 0.3s;
}
.dot.is-live { background: var(--signal); }

/* ---------- auth ---------- */
.auth { flex: 1; display: grid; place-items: center; padding: 24px; overflow-y: auto; }
.auth[hidden] { display: none; }
.auth-card { width: 100%; max-width: 340px; }

/* The sign-in mark. No filter on it — this one keeps its own
   colours, unlike the wordmark in the top bar which is tinted to
   sit in the palette. */
.auth-brand {
  display: block;
  width: 200px;
  max-width: 100%;
  height: auto;
  margin: 0 auto 16px;
}
.auth-title {
  text-align: center;
  font-family: var(--mono);
  font-size: 26px;
  font-weight: 600;
  letter-spacing: 0.3em;
  color: var(--signal);
  margin: 0 0 8px;
}
.auth-sub {
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.55;
  margin: 0 0 24px;
  text-align: center;
}

.field { margin-bottom: 15px; }
.field label { display: block; margin-bottom: 6px; }
.field input {
  width: 100%;
  border: 1px solid var(--line);
  padding: 12px 13px;
  font-family: var(--mono);
  /* 16px minimum: anything smaller makes iOS zoom in on tap */
  font-size: 16px;
}
.field input:focus { border-color: var(--signal); }

/* ---------- password show/hide ---------- */
.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 46px; }   /* clear room for the eye */

.pw-eye {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: 44px;
  display: grid;
  place-items: center;
  color: var(--muted);
}
.pw-eye:hover { color: var(--signal); }
.pw-eye svg { width: 19px; height: 19px; display: block; }

/* Show the open eye by default; swap once the password is visible,
   so the icon reflects what the next click will do. */
.pw-eye .eye-shut { display: none; }
.pw-eye.is-on { color: var(--signal); }
.pw-eye.is-on .eye-open { display: none; }
.pw-eye.is-on .eye-shut { display: block; }

/* ---------- buttons ---------- */
.btn-solid {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  font-weight: 600;
  color: var(--void);
  background: var(--signal);
  padding: 13px 18px;
}
.btn-solid:disabled { background: var(--line); color: var(--muted); cursor: default; }
.btn-wide { width: 100%; margin-top: 6px; }
.btn-flex { flex: 1; }

.btn-ghost {
  flex: 1;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  border: 1px solid var(--line);
  padding: 12px;
  color: var(--muted);
}
.btn-ghost:hover { border-color: var(--muted); }

.btn-ghost-sm {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--signal);
  border: 1px solid var(--line);
  padding: 6px 10px;
}
.btn-ghost-sm:hover { border-color: var(--signal); }

/* HOME is a real link, not a button, so it needs these to sit
   flush with the others. */
a.btn-ghost-sm {
  text-decoration: none;
  display: inline-block;
  cursor: pointer;
}

.linkish {
  display: block;
  width: 100%;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--muted);
  padding: 14px 0 0;
  text-align: center;
}
.linkish:hover { color: var(--signal); }
.linkish[hidden] { display: none; }

.note { font-family: var(--mono); font-size: 11px; line-height: 1.6; margin: 14px 0 0; color: var(--muted); }
.note.is-bad { color: var(--glow); }

/* ---------- tabs ---------- */
.tabs { display: flex; border: 1px solid var(--line); margin-bottom: 20px; }
.tab {
  flex: 1;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  padding: 10px;
  color: var(--muted);
}
.tab.is-on { color: var(--signal); box-shadow: inset 0 -2px 0 var(--signal); }

/* ---------- layout ----------
   The media query at the bottom is the thermostat: below
   720px wide, show one pane at a time. Above, show both. */
.body { flex: 1; display: flex; min-height: 0; }
.body[hidden] { display: none; }
.pane { display: flex; flex-direction: column; min-height: 0; min-width: 0; flex: 1; }
.list-pane { border-right: 1px solid var(--line); }

@media (max-width: 719px) {
  .list-pane, .chat-pane { display: none; }
  .list-pane.is-active, .chat-pane.is-active { display: flex; }
}
@media (min-width: 720px) {
  .list-pane { flex: 0 0 300px; }
  .back { display: none !important; }
}

/* ---------- chat list ---------- */
.list-head {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 13px 16px;
}
.head-actions { display: flex; gap: 8px; }

.scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;  /* older iOS only; Firefox ignores it */ }

.row {
  display: flex;
  gap: 12px;
  width: 100%;
  padding: 12px 16px;
  text-align: left;
  border-left: 2px solid transparent;
}
.row:hover { border-left-color: var(--deep); }
.row.is-open { border-left-color: var(--signal); }
.row.is-open .name { color: var(--signal); }
.row.is-open .mark { border-color: var(--signal); }

.mark {
  flex: none;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 12px;
  color: var(--signal);
}
.mark.is-group { color: var(--deep); }

.row-text { flex: 1; min-width: 0; }
.row-top { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.name { font-family: var(--mono); font-size: 13px; font-weight: 500; }
.time { font-family: var(--mono); font-size: 10px; color: var(--muted); flex: none; }
.preview {
  font-size: 13px;
  color: var(--muted);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- conversation ---------- */
.chat-pane { position: relative; }
.convo { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.convo[hidden] { display: none; }

.chat-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 16px;
  border-bottom: 1px solid var(--line);
}
.chat-head-text { min-width: 0; }
.back { font-family: var(--mono); font-size: 17px; color: var(--muted); padding: 4px 6px 4px 0; }

.msgs {
  flex: 1;
  overflow-y: auto;
  /* Keeps scrolling on its own layer instead of repainting the
     whole conversation each frame. */
  transform: translateZ(0);
  -webkit-overflow-scrolling: touch;  /* older iOS only; Firefox ignores it */
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Every message sits on the left, yours included, so the eye has
   one column to follow instead of zigzagging down the page.
   Width comes from the content, up to 80% — left as a plain block
   it filled the whole column, which pushed the action row far off
   to the side of a three-word message. */
.msg { max-width: 80%; width: fit-content; }
.msg-meta {
  display: flex; gap: 8px; align-items: baseline;
  font-family: var(--mono); font-size: 10px; color: var(--muted);
  margin-bottom: 4px;
}
/* The name carries the line, so it gets a little more weight than
   the timestamp beside it. */
.msg-meta .who { font-size: 12px; color: var(--text); }
/* No border. The box was stretching to the full width of the
   message column regardless of how little was in it, which drew a
   huge empty rectangle around three words. Yours and theirs are
   told apart by side and colour instead. */
.bubble {
  padding: 2px 0;
  font-size: 14.5px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.msg.is-mine .bubble { color: var(--glow); }

/* ---------- video in messages ---------- */
.msg-video {
  display: block;
  width: 100%;
  max-width: 420px;
  max-height: 340px;
  margin-top: 8px;
  border: 1px solid var(--line);
  background: var(--void);
  outline: none;
}
.bubble.is-image-only .msg-video { margin-top: 0; }

.attach-thumb.is-video {
  display: grid;
  place-items: center;
  color: var(--signal);
  font-size: 15px;
}
.attach-strip.is-working { opacity: 0.5; }

/* ---------- images in messages ---------- */
.msg-img-link { display: block; margin-top: 8px; line-height: 0; }
.bubble.is-image-only { padding: 0; }
.bubble.is-image-only .msg-img-link { margin-top: 0; }

.msg-img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 340px;
  border: 1px solid var(--line);
  background: var(--void);
  object-fit: contain;
}
.msg-img-link:hover .msg-img { border-color: var(--signal); }
.msg-img-gone {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  border: 1px dashed var(--line);
  padding: 14px;
  text-align: center;
  margin-top: 8px;
}

/* ---------- the staged attachment ---------- */
.attach-strip {
  flex: none;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 16px;
  border-top: 1px solid var(--line);
}
.attach-strip[hidden] { display: none; }
.attach-thumb {
  flex: none;
  width: 42px;
  height: 42px;
  border: 1px solid var(--line);
  background: var(--void) center/cover no-repeat;
}
.attach-info { flex: 1; min-width: 0; }
.attach-info .eyebrow { display: block; text-transform: none; letter-spacing: 0.06em; }
.attach-info .eyebrow:first-child {
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 3px;
}

/* ---------- composer buttons ---------- */
.compose-btn {
  flex: none;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  color: var(--muted);
  border: 1px solid var(--line);
}
.compose-btn i { font-size: 15px; }
.compose-btn:hover { color: var(--signal); border-color: var(--signal); }
.compose-btn.is-on { color: var(--signal); border-color: var(--signal); }

/* ---------- emoji ---------- */
.emoji-panel {
  flex: none;
  border-top: 1px solid var(--line);
  background: var(--void);
}
.emoji-panel[hidden] { display: none; }

.emoji-tabs {
  display: flex;
  overflow-x: auto;
  border-bottom: 1px solid var(--line);
  -webkit-overflow-scrolling: touch;  /* older iOS only; Firefox ignores it */
}
.emoji-tab {
  flex: none;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  color: var(--muted);
  padding: 10px 12px;
  white-space: nowrap;
}
.emoji-tab.is-on { color: var(--signal); box-shadow: inset 0 -2px 0 var(--signal); }

.emoji-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
  max-height: 172px;
  overflow-y: auto;
  padding: 6px;
  -webkit-overflow-scrolling: touch;  /* older iOS only; Firefox ignores it */
}
.emoji {
  font-family: var(--emoji);
  font-size: 21px;
  line-height: 1;
  padding: 7px 0;
  border-radius: 0;
}
.emoji:hover { background: rgba(76, 255, 184, 0.1); }

/* ---------- reactions ---------- */
/* Collapses when there are no reactions, rather than holding a
   blank strip under every message. */
.msg-foot { display: flex; flex-wrap: wrap; gap: 3px; }
.msg-foot:not(:empty) { margin-top: 5px; }

.react {
  display: none;
  align-items: center;
  gap: 4px;
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1;
  color: var(--muted);
  padding: 4px 5px;
}
.react i { font-size: 11px; }
.react-n:empty { display: none; }

/* A reaction someone has actually left always shows. Otherwise
   nobody could see what a message had collected without waving a
   cursor over every line. The empty ones are what hides. */
.react.has-count { display: inline-flex; }
.react.is-mine { color: var(--signal); }

/* Desktop: the whole row appears on hover. Wrapped in a hover
   query so a phone — which reports a permanent fake hover on the
   last thing tapped — doesn't get stuck showing them. */
@media (hover: hover) {
  .msg:hover .react { display: inline-flex; }
  .react:hover { color: var(--signal); }
}

/* Phones: tapping the message reveals the row. */
.msg.is-picking .react { display: inline-flex; }
.msg.is-picking .bubble { border-color: var(--deep); }

/* ---------- mentions ---------- */
.mention {
  color: var(--signal);
  font-weight: 500;
  cursor: pointer;
}
.mention:hover { text-decoration: underline; text-underline-offset: 2px; }

/* Being named should be findable when you scroll back. */
.mention.is-me {
  color: var(--void);
  background: var(--signal);
  padding: 1px 4px;
  font-weight: 600;
}

.mention-pop {
  flex: none;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
  max-height: 180px;
  overflow-y: auto;
}
.mention-pop[hidden] { display: none; }
.mention-opt {
  font-family: var(--mono);
  font-size: 13px;
  text-align: left;
  padding: 11px 16px;
  color: var(--text);
}
.mention-opt.is-on { color: var(--signal); background: rgba(76, 255, 184, 0.08); }
.mention-opt:hover { color: var(--signal); }

/* ---------- private message alert ---------- */
.row.is-dm-alert { border-left-color: var(--glow); }
.row.is-dm-alert .name { color: var(--glow); }
.row.is-dm-alert .preview { color: var(--text); }
.row.is-dm-alert .unread { background: var(--glow); }

/* ---------- sound toggle ---------- */
#soundBtn { padding: 6px 9px; }
#soundBtn i { font-size: 12px; }
#soundBtn.is-off { color: var(--muted); }

/* On desktop the icon says it all. In the phone menu every other
   row is a word, and a lone icon in a list of words reads as a
   mistake — so the label appears there. */
.btn-word { display: none; margin-left: 9px; }

/* ---------- rules ---------- */
.rules { margin: 0; padding-left: 22px; }
.rules li {
  font-size: 13.5px;
  line-height: 1.55;
  margin-bottom: 13px;
  padding-left: 4px;
}
.rules li::marker { color: var(--signal); font-family: var(--mono); }
.rules b { color: var(--signal); font-weight: 500; }
.rules span {
  display: block;
  color: var(--muted);
  font-size: 12.5px;
  margin-top: 3px;
}

.rules-bottom {
  border-top: 1px solid var(--line);
  margin-top: 18px;
  padding-top: 16px;
  font-size: 13.5px;
  line-height: 1.6;
}
.rules-bottom b { color: var(--signal); font-weight: 500; }

/* ---------- audio player ---------- */
.audio {
  display: flex;
  align-items: center;
  gap: 11px;
  border: 1px solid var(--line);
  padding: 10px 12px;
  margin-top: 8px;
  min-width: 260px;
  max-width: 420px;
}
.bubble.is-image-only .audio { margin-top: 0; }
.msg.is-mine .audio { border-color: var(--deep); }
.audio.is-playing { border-color: var(--signal); }

.audio-play {
  flex: none;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  color: var(--signal);
}
.audio-play i { font-size: 12px; }
.audio-play:hover { border-color: var(--signal); }
.audio.is-playing .audio-play { border-color: var(--signal); }

.audio-main { flex: 1; min-width: 0; text-align: left; }
.audio-name {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 7px;
}

/* The bar is a thin track with the played part filled in, and a
   real range input laid over it so dragging and keyboard control
   work without having to reinvent either. */
.audio-bar { position: relative; height: 14px; display: flex; align-items: center; }
.audio-bar::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 2px;
  background: var(--line);
}
.audio-fill {
  position: absolute;
  left: 0;
  height: 2px;
  width: 0;
  background: var(--signal);
  pointer-events: none;
}
.audio-seek {
  position: relative;
  width: 100%;
  height: 14px;
  margin: 0;
  padding: 0;
  background: none;
  border: none;
  outline: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.audio-seek::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 11px;
  height: 11px;
  background: var(--signal);
  border: none;
  border-radius: 0;
  cursor: pointer;
}
.audio-seek::-moz-range-thumb {
  width: 11px;
  height: 11px;
  background: var(--signal);
  border: none;
  border-radius: 0;
  cursor: pointer;
}
.audio-seek::-moz-range-track { background: none; }

.audio-time {
  flex: none;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--muted);
  text-align: right;
  line-height: 1.5;
}
.audio-at { display: block; color: var(--text); }
.audio-of { display: block; }

.attach-thumb.is-audio {
  display: grid;
  place-items: center;
  color: var(--signal);
  font-size: 18px;
}

/* ---------- links in messages ---------- */
.msg-link {
  color: var(--signal);
  text-decoration: underline;
  text-underline-offset: 2px;
  overflow-wrap: anywhere;
}
.msg-link:hover { color: var(--glow); }
.msg.is-mine .msg-link { color: var(--glow); }

/* ---------- edit / delete ---------- */
.edited { font-style: italic; opacity: 0.75; }


.msg-actions[hidden] { display: none; }

.act {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  border: 1px solid var(--line);
  padding: 5px 9px;
  color: var(--muted);
}
.act:hover { border-color: var(--signal); color: var(--signal); }
.act.is-armed { border-color: var(--glow); color: var(--glow); }
.act:disabled { opacity: 0.5; cursor: default; }

.edit-box {
  width: 100%;
  min-width: 200px;
  border: none;
  background: none;
  color: var(--glow);
  font-family: var(--sans);
  font-size: 14.5px;
  line-height: 1.5;
  resize: vertical;
  outline: none;
  padding: 0;
}
.edit-row { display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px; }

.placeholder, .empty {
  flex: 1;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 32px;
}
.placeholder[hidden] { display: none; }
.placeholder p, .empty p { color: var(--muted); font-size: 14px; margin: 8px 0 0; }

/* ---------- composer ---------- */
.composer {
  flex: none;
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 12px 16px;
  border-top: 1px solid var(--line);
}
.input {
  flex: 1;
  border: 1px solid var(--line);
  padding: 12px 13px;
  font-size: 16px;   /* again: 16px stops iOS from zooming */
}
.input:focus { border-color: var(--signal); }

/* ---------- header actions ---------- */
.header-right { display: flex; align-items: center; gap: 10px; }
.header-actions { display: flex; gap: 8px; }

/* the three-bar toggle, only on narrow screens */
.menu-toggle {
  display: none;
  width: 44px;
  height: 40px;
  padding: 11px 10px;
  flex-direction: column;
  justify-content: space-between;
  align-items: stretch;
}
.menu-toggle span {
  display: block;
  height: 2px;
  background: var(--signal);
  transition: transform 0.18s, opacity 0.18s;
}
.menu-toggle.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.menu-toggle.is-open span:nth-child(2) { opacity: 0; }
.menu-toggle.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

@media (max-width: 719px) {
  .header-right .status { display: none; }
  .menu-toggle { display: flex; }

  /* Seven buttons across the top of a phone is unusable, so they
     drop into a panel instead. */
  .header-actions {
    position: absolute;
    top: 100%;
    right: 10px;
    z-index: 45;
    flex-direction: column;
    gap: 0;
    background: var(--void);
    border: 1px solid var(--line);
    min-width: 168px;
    display: none;
  }
  .header-actions.is-open { display: flex; }
  .header-actions .btn-ghost-sm {
    border: none;
    border-bottom: 1px solid var(--line);
    text-align: left;
    padding: 15px 16px;
    font-size: 12px;
  }
  .header-actions .btn-ghost-sm:last-child { border-bottom: none; }
  .header-actions .btn-ghost-sm:active { background: var(--line); }
}

.header { position: relative; }

/* ---------- mobile refinements ---------- */
@media (max-width: 719px) {

  /* Anything you tap needs to be reachable with a thumb. These
     were all sized for a mouse pointer. */
    .act { padding: 10px 13px; font-size: 11px; }
  .react { padding: 7px 8px; }
  .react i { font-size: 14px; }
  .msg-foot { gap: 4px; }
  .compose-btn { width: 42px; height: 42px; }
  .compose-btn i { font-size: 17px; }
  .composer { gap: 7px; padding: 10px 12px; }
  .emoji-grid { grid-template-columns: repeat(auto-fill, minmax(44px, 1fr)); max-height: 150px; }
  .emoji { font-size: 23px; padding: 9px 0; }
  .msg-img { max-height: 260px; }
  .audio { min-width: 0; padding: 11px; gap: 9px; }
  .audio-play { width: 40px; height: 40px; }
  .audio-play i { font-size: 14px; }
  .audio-seek::-webkit-slider-thumb { width: 15px; height: 15px; }
  .audio-seek::-moz-range-thumb { width: 15px; height: 15px; }
  .audio-bar { height: 18px; }
  .msg-video { max-height: 260px; }

  /* A lone icon among menu rows of words looks like an error. */
  #soundBtn .btn-word { display: inline; }

  /* Picking a name is a tap target like any other. */
  .mention-opt { padding: 14px 16px; font-size: 14px; }
  .mention-pop { max-height: 132px; }

  /* Tick boxes are drawn small by default and are awkward to hit,
     even though the whole row is clickable. */
  .perm-row input { width: 21px; height: 21px; }
  .perm-row { padding: 14px 0; }

  .chan-topic { padding: 11px 12px; }
  .color-row input[type="color"] { width: 52px; height: 46px; }
  .rules li { margin-bottom: 16px; }
  .lk-kill { width: 44px; min-height: 42px; }
  .back { padding: 10px 12px 10px 0; font-size: 20px; }
  .info-btn { width: 38px; height: 38px; }
  .info-btn i { font-size: 16px; }
  .btn-ghost-sm { padding: 9px 12px; }
  .tab { padding: 13px 8px; }
  .row { padding: 14px 16px; }
  .who-row { padding: 13px 16px; }

  /* Bubbles get wider — 78% of a phone screen wastes the margin */
  .msg { max-width: 92%; }
  .msg-meta .who { font-size: 12.5px; }
  .prof-banner { height: 96px; margin: -18px -16px 14px; }
  .banner-edit { height: 76px; }
  .gradient-label input { width: 20px; height: 20px; }
  .rename-in { padding: 11px 12px; }
  .load-older { padding: 11px 20px; font-size: 11px; }
  .blocked-row { padding: 12px 0; }

  /* Modals go edge to edge. A centred card with 24px of margin on
     a phone is a card with no room in it. */
  .veil { padding: 0; place-items: stretch; }
  .modal, .modal-wide {
    max-width: 100%;
    width: 100%;
    height: 100%;
    max-height: 100%;
    border: none;
    border-radius: 0;
    padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
    padding-top: calc(18px + env(safe-area-inset-top));
    display: flex;
    flex-direction: column;
    overflow-y: auto;
  }
  .mod-body, .help-body, .prof-body { max-height: none; flex: 1; }
  .modal-actions { margin-top: auto; padding-top: 18px; }

  /* Report action buttons wrap into a tidy grid rather than a
     ragged row */
  .report-acts { display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px; }
  .report-acts .act { text-align: center; }

  .link-row { flex-wrap: wrap; }
  .link-row .lk-label { flex: 1 1 100%; }
  .link-row .lk-addr { flex: 1 1 auto; }

  .prof-pic { width: 66px; height: 66px; font-size: 20px; }
  .term-add { flex-wrap: wrap; }
  .term-add input { flex: 1 1 100%; }
}

/* Visible feedback on press, since we removed the grey flash */
.btn-ghost-sm:active, .act:active, .tab:active,
.btn-solid:active, .btn-ghost:active { opacity: 0.6; }
.row:active, .who-row:active { background: rgba(76, 255, 184, 0.06); }

/* Very small phones: stop the wordmark crowding the toggle */
@media (max-width: 380px) {
  .wordmark-img { height: 21px; }
  .header-row { padding: 11px 12px; }
}

/* Landscape phones are short. Give the messages the room. */
@media (max-height: 460px) and (orientation: landscape) {
  .header-row { padding: 7px 16px; }
  .msgs { padding: 12px 16px; }
  .list-head { padding: 9px 16px; }
}

/* ---------- list tabs ---------- */
.list-tabs {
  flex: none;
  margin: 0 16px 8px;
}

/* ---------- channel info ---------- */
.info-btn {
  flex: none;
  margin-left: 8px;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  color: var(--muted);
}
.info-btn i { font-size: 14px; }
.info-btn:hover { color: var(--signal); }
.info-btn[hidden] { display: none; }

.topic-body {
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  border-left: 2px solid var(--line);
  padding-left: 12px;
  margin: 4px 0 4px;
}
.topic-body.is-empty { color: var(--muted); font-style: italic; }

/* ---------- who's online ---------- */
.who-pane { border-left: 1px solid var(--line); }
.who-btn { flex: none; margin-left: auto; }
.who-close { display: none; }

.who-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  font-family: var(--mono);
  font-size: 12.5px;
}
.who-row .dot { background: var(--signal); animation: none; }
.who-row.is-me { color: var(--signal); }
.who-empty { padding: 14px 16px; color: var(--muted); font-size: 13px; }

/* Three columns on a wide screen. Below that the online list
   becomes a panel you call up with the USERS button, because
   three columns on a phone is two columns too many. */
@media (min-width: 1000px) {
  .who-pane { flex: 0 0 210px; display: flex !important; }
  .who-btn { display: none; }
}
@media (max-width: 999px) {
  .who-pane { display: none; }
  .who-pane.is-active { display: flex; }
  .who-close { display: block; }
}
@media (min-width: 720px) and (max-width: 999px) {
  .who-pane.is-active { flex: 0 0 210px; }
}

/* ---------- help ---------- */
/* The scrolling area reaches the full width of the panel, so its
   scrollbar sits on the edge where you expect it. Previously it
   was inset by a padding and a max-width, which parked a second
   scrollbar in the middle of the panel with a dead strip beyond
   it. The comfortable reading width is applied to the text
   inside instead. */
.help-body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;  /* older iOS only; Firefox ignores it */
  margin-top: 4px;
  flex: 1;
  min-height: 0;
}
.help-body h3 {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--signal);
  margin: 20px 0 7px;
}
.help-body h3:first-child { margin-top: 0; }
.help-body p { font-size: 13.5px; line-height: 1.6; margin: 0 0 10px; color: var(--text); }
.help-body ul { margin: 0 0 10px; padding-left: 18px; }
.help-body li { font-size: 13.5px; line-height: 1.6; margin-bottom: 5px; }
.help-body b { color: var(--signal); font-weight: 500; }

/* ---------- send refusals ---------- */
.send-error {
  flex: none;
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--glow);
  border-top: 1px solid var(--deep);
  padding: 9px 16px;
}
.send-error[hidden] { display: none; }

/* ---------- moderator stats ---------- */
.mod-stats {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  margin-bottom: 14px;
}
.stat { background: var(--void); padding: 9px 6px; text-align: center; }
.stat-n { font-family: var(--mono); font-size: 16px; color: var(--signal); }
@media (max-width: 460px) {
  .mod-stats { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- audit log ---------- */
.log-row { padding: 9px 0; border-bottom: 1px solid var(--line); }
.log-line { font-family: var(--mono); font-size: 12px; overflow-wrap: anywhere; }
.log-when { font-family: var(--mono); font-size: 10px; color: var(--muted); margin-top: 3px; }

.term-ban { color: var(--void); background: var(--signal); }

/* ---------- reports run red ----------
   Scoped to the reports pane only. The USERS tab reuses the same
   card markup and stays mint, so red always means "someone
   flagged this", never just "this is a card". */
#modReports .report { border-color: var(--alert-deep); }
#modReports .report-who { color: var(--alert); }
#modReports .report-quote { border-left-color: var(--alert-deep); }
#modReports .report-reason { color: var(--alert-deep); }
#modReports .act:hover { border-color: var(--alert); color: var(--alert); }
#modReports .act.is-armed { border-color: var(--alert); color: var(--alert); }

/* The MOD button only goes red when there's actually something
   in the queue. A permanently red button stops meaning anything. */
.mod-btn.has-reports {
  color: var(--alert);
  border-color: var(--alert-deep);
}
.tab.has-reports { color: var(--alert); }
.tab.is-on.has-reports { box-shadow: inset 0 -2px 0 var(--alert); }

/* ---------- name colour ---------- */
.color-row { display: flex; gap: 8px; align-items: stretch; }
.color-row input[type="color"] {
  flex: none;
  width: 46px;
  height: 42px;
  padding: 3px;
  border: 1px solid var(--line);
  background: var(--void);
  cursor: pointer;
}
.color-hex {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--line);
  padding: 11px 13px;
  font-family: var(--mono);
  font-size: 16px;
  text-transform: lowercase;
}
.color-hex:focus { border-color: var(--signal); }

.color-preview {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--signal);
  border: 1px solid var(--line);
  padding: 10px 12px;
  margin-top: 8px;
  overflow-wrap: anywhere;
}
.color-preview.is-default { color: var(--text); }

/* ---------- permissions ---------- */
.perm-list { display: flex; flex-direction: column; }
.perm-row {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}
.perm-row:last-child { border-bottom: none; }
.perm-row input {
  flex: none;
  width: 17px;
  height: 17px;
  margin-top: 1px;
  accent-color: var(--signal);
  cursor: pointer;
}
.perm-text { display: flex; flex-direction: column; gap: 3px; }
.perm-label { font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em; }
.perm-hint { font-size: 12px; color: var(--muted); line-height: 1.45; }
.perm-row input:checked + .perm-text .perm-label { color: var(--signal); }

/* ---------- channel form ---------- */
.chan-topic {
  width: 100%;
  border: 1px solid var(--line);
  padding: 9px 10px;
  font-size: 16px;
  margin-bottom: 8px;
}
.chan-topic:focus { border-color: var(--signal); }


/* Anywhere an emoji is shown on its own it uses that stack. In
   running text the sans-serif takes the letters and the emoji
   font takes the pictures, which browsers handle automatically. */
.react-ch, .react-opt, .emoji, .emoji-tab-ch, .attach-thumb {
  font-family: var(--emoji);
}
.bubble, .preview, .msg-link { font-family: var(--sans), var(--emoji); }




/* ---------- crash banner ---------- */
#crashBar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
  background: var(--void);
  border-top: 1px solid var(--alert-deep);
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--alert);
}
.crash-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.crash-reload {
  flex: none;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--void);
  background: var(--alert);
  padding: 8px 12px;
}
.crash-hide {
  flex: none;
  font-size: 16px;
  line-height: 1;
  color: var(--alert-deep);
  padding: 4px 8px;
}

/* ---------- message actions ----------
   Shown on hover with a pointer, and on tap without one. They sit
   under the message rather than floating over it, so nothing is
   ever covered by the thing you reached for. */
.msg-actions {
  display: flex;
  gap: 6px;
  margin-top: 5px;
  visibility: hidden;
}

@media (hover: hover) {
  .msg:hover .msg-actions { visibility: visible; }
}
.msg.is-picking .msg-actions { visibility: visible; }

/* Report reads as a warning without leaving the palette — the
   same alert red used for the moderation queue. */
.act-report { color: var(--alert-deep); border-color: var(--alert-deep); }
.act-report:hover { color: var(--alert); border-color: var(--alert); }
.act-report.is-armed { color: var(--alert); border-color: var(--alert); }

/* ---------- replies ---------- */
.quote {
  display: block;
  width: 100%;
  text-align: left;
  border-left: 2px solid var(--deep);
  padding: 2px 0 2px 9px;
  margin-bottom: 5px;
  opacity: 0.75;
}
.quote:hover { opacity: 1; border-color: var(--signal); }
.quote-who {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--signal);
  margin-bottom: 1px;
}
.quote-text {
  display: block;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.quote.is-gone .quote-text { font-style: italic; }

/* A brief flash when you jump to the quoted message, so the eye
   lands in the right place instead of hunting. */
.msg.is-flash .bubble {
  animation: msg-flash 1.4s ease-out;
}
@keyframes msg-flash {
  0%, 40% { background: rgba(76, 255, 184, 0.16); }
  100%    { background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .msg.is-flash .bubble { animation: none; outline: 1px solid var(--signal); }
}

.reply-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 16px;
  border-top: 1px solid var(--line);
}
.reply-bar[hidden] { display: none; }
.reply-mark { flex: none; width: 2px; align-self: stretch; background: var(--signal); }
.reply-info { flex: 1; min-width: 0; }
.reply-text {
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 2px;
}
#replyCancel { font-size: 15px; line-height: 1; padding: 5px 10px; }

@media (max-width: 719px) {
  .msg-actions { gap: 5px; }
  .msg-actions .act { padding: 8px 10px; font-size: 10px; }
  #replyCancel { padding: 9px 13px; }
}

/* ---------- reaction picker ----------
   This floats above everything and is placed by script. Without
   position:fixed the coordinates it sets mean nothing and the
   whole thing lands at the bottom of the page, which is exactly
   what it was doing. */
#reactPop {
  position: fixed;
  z-index: 70;
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  max-width: 268px;
  padding: 6px;
  border: 1px solid var(--line);
  background: var(--void);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.9);
}
#reactPop[hidden] { display: none; }

.react-opt {
  font-family: var(--emoji);
  font-size: 21px;
  line-height: 1;
  padding: 6px;
  min-width: 38px;
}
.react-opt:hover { background: rgba(76, 255, 184, 0.12); }
.react-more {
  font-family: var(--mono);
  font-size: 15px;
  color: var(--muted);
}
.react-more:hover { color: var(--signal); }


@media (max-width: 719px) {
  .react-opt { font-size: 24px; padding: 8px; min-width: 44px; }
  #reactPop { max-width: 300px; }
  }

/* ---------- account details ---------- */
.user-facts { margin-bottom: 11px; }
.user-fact {
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.7;
}
.uf-k { flex: 0 0 76px; color: var(--muted); letter-spacing: 0.1em; }
.uf-v { flex: 1; min-width: 0; overflow-wrap: anywhere; color: var(--text); }
.user-fact.is-email .uf-v { color: var(--glow); }

@media (max-width: 719px) {
  .user-fact { flex-direction: column; gap: 0; line-height: 1.5; margin-bottom: 7px; }
  .uf-k { flex: none; }
}

/* ---------- older messages ---------- */
.load-older-wrap { display: flex; justify-content: center; padding: 4px 0 14px; }
.load-older {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--muted);
  border: 1px solid var(--line);
  padding: 8px 16px;
}
.load-older:hover { border-color: var(--signal); color: var(--signal); }
.load-older:disabled { opacity: 0.5; cursor: default; }

/* ---------- blocked list ---------- */
.blocked-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
}
.blocked-row:last-child { border-bottom: none; }
.blocked-name { flex: 1; font-family: var(--mono); font-size: 13px; overflow-wrap: anywhere; }

#pvBlock.is-armed { border-color: var(--glow); color: var(--glow); }

/* ---------- gradient names ----------
   The colour is painted as a background and then clipped to the
   shape of the letters, which is the only way to fade text across
   two colours. */
.has-gradient {
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* ---------- profile banner ---------- */
.prof-banner {
  height: 110px;
  margin: -20px -20px 16px;
  background: var(--void) center/cover no-repeat;
  border-bottom: 1px solid var(--line);
}
.prof-banner[hidden] { display: none; }

.banner-edit {
  height: 84px;
  background: var(--void) center/cover no-repeat;
  border: 1px solid var(--line);
  margin-bottom: 8px;
}
.banner-edit.is-empty::after {
  content: "no banner";
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--muted);
  display: grid;
  place-items: center;
  height: 100%;
}
.banner-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ---------- second colour ---------- */
.gradient-label {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 12px 0 9px;
  cursor: pointer;
}
.gradient-label input {
  width: 16px;
  height: 16px;
  accent-color: var(--signal);
  cursor: pointer;
}
.gradient-label input:disabled { opacity: 0.4; cursor: default; }
#peColor2Row[hidden] { display: none; }

/* ---------- renaming a channel ---------- */
.rename-row { display: flex; gap: 7px; margin-top: 9px; }
.rename-in {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--signal);
  padding: 8px 10px;
  font-family: var(--mono);
  font-size: 16px;
}

/* ---------- moderation ---------- */
.mod-btn { color: var(--glow); border-color: var(--deep); }

.mod-body { overflow-y: auto; max-height: min(62vh, 620px); margin-bottom: 4px; }

.report {
  border: 1px solid var(--line);
  padding: 12px;
  margin-bottom: 10px;
}
.report-top {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: baseline;
  margin-bottom: 8px;
}
.report-who { font-family: var(--mono); font-size: 13px; color: var(--signal); }
.report-from { font-family: var(--mono); font-size: 10px; color: var(--muted); }

.report-quote {
  font-size: 13.5px;
  line-height: 1.55;
  border-left: 2px solid var(--line);
  padding-left: 10px;
  margin-bottom: 8px;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
.report-reason { font-family: var(--mono); font-size: 11px; color: var(--muted); margin-bottom: 10px; }
.report-reason:empty { display: none; }
.report-acts { display: flex; flex-wrap: wrap; gap: 7px; }

.term-add { display: flex; gap: 7px; margin-bottom: 8px; }
.term-add input {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--line);
  padding: 9px 10px;
  font-family: var(--mono);
  font-size: 16px;
}
.term-add input:focus { border-color: var(--signal); }
.term-sev {
  border: 1px solid var(--line);
  background: var(--void);
  color: var(--text);
  font-family: var(--mono);
  font-size: 11px;
  padding: 0 8px;
  outline: none;
}
.term-hint { margin: 0 0 14px; }

.term-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}
.term-word { flex: 1; font-family: var(--mono); font-size: 13px; overflow-wrap: anywhere; }
.term-tag { font-family: var(--mono); font-size: 9px; letter-spacing: 0.12em; padding: 3px 6px; }
.term-block { color: var(--void); background: var(--glow); }
.term-flag { color: var(--muted); border: 1px solid var(--line); }

/* ---------- unread ---------- */
.row { align-items: center; }
.unread {
  flex: none;
  align-self: center;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  color: var(--void);
  background: var(--signal);
  padding: 2px 6px;
  margin-left: 8px;
}
.row.has-unread .name { color: var(--signal); }
.row.has-unread .preview { color: var(--text); }

/* ---------- avatars ---------- */
.av {
  flex: none;
  width: 18px;
  height: 18px;
  background: var(--void) center/cover no-repeat;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 8px;
  color: var(--deep);
  overflow: hidden;
}
.av.av-lg { width: 38px; height: 38px; font-size: 12px; color: var(--signal); }

.who-row { cursor: pointer; }
.who-row:hover .who-name { color: var(--signal); }
.msg-meta .who { cursor: pointer; }
.msg-meta .who:hover { color: var(--signal); }

/* ---------- profile ---------- */
.prof-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 18px;
}
.prof-pic {
  flex: none;
  width: 78px;
  height: 78px;
  border: 1px solid var(--line);
  background: var(--void) center/cover no-repeat;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 24px;
  color: var(--signal);
}
.prof-id { min-width: 0; }
.prof-name {
  font-family: var(--mono);
  font-size: 17px;
  font-weight: 600;
  color: var(--signal);
  overflow-wrap: anywhere;
}

.prof-pic-actions { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.pic-btn { cursor: pointer; display: inline-block; }
.pic-hint { margin-top: 2px; }

.prof-body { overflow-y: auto; max-height: min(52vh, 520px); }
.prof-body h3 {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--signal);
  margin: 18px 0 7px;
}
.prof-body h3:first-child { margin-top: 0; }
.prof-bio {
  font-size: 13.5px;
  line-height: 1.6;
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.prof-bio.is-empty, .prof-none { color: var(--muted); font-style: italic; }

.prof-link {
  display: block;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--text);
  border: 1px solid var(--line);
  padding: 9px 11px;
  margin-bottom: 7px;
  text-decoration: none;
  overflow-wrap: anywhere;
}
.prof-link:hover { border-color: var(--signal); color: var(--signal); }
.prof-link .lk-url { display: block; color: var(--muted); font-size: 10.5px; margin-top: 3px; }
.prof-link:hover .lk-url { color: var(--deep); }

/* ---------- profile editor ---------- */
.prof-input {
  width: 100%;
  border: 1px solid var(--line);
  background: var(--void);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  padding: 11px 13px;
  resize: vertical;
  outline: none;
}
.prof-input:focus { border-color: var(--signal); }
.char-count { text-align: right; margin-top: 5px; }

.link-row { display: flex; gap: 7px; margin-bottom: 7px; }
.link-row input {
  border: 1px solid var(--line);
  padding: 9px 10px;
  font-family: var(--mono);
  font-size: 16px;
  min-width: 0;
}
.link-row input:focus { border-color: var(--signal); }
.link-row .lk-label { flex: 0 0 34%; }
.link-row .lk-addr { flex: 1; }
.link-row .lk-kill {
  flex: none;
  border: 1px solid var(--line);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 13px;
  width: 34px;
}
.link-row .lk-kill:hover { border-color: var(--glow); color: var(--glow); }
.add-link { margin-top: 4px; }

/* ---------- modal ---------- */
.veil {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.9);
  display: grid;
  place-items: center;
  padding: 24px;
  z-index: 40;
  overflow-y: auto;
}
.veil[hidden] { display: none; }
/* Panels grow with the window instead of staying phone-width on a
   monitor, but stop before they sprawl — a line of text past about
   90 characters is tiring to read, however much room there is.
   Each panel gets a width suited to what's in it. */
.modal {
  width: 100%;
  max-width: min(92vw, 420px);
  max-height: min(88vh, 760px);
  overflow-y: auto;
  border: 1px solid var(--line);
  background: var(--void);
  padding: 22px;
}

/* Reading panels — rules, help, a profile. */
.modal-wide {
  max-width: min(92vw, 680px);
  max-height: min(90vh, 900px);
  display: flex;
  flex-direction: column;
}

/* Each panel gets the width its contents actually need, rather
   than one size for all of them. A form is easier to read narrow;
   a table of users is not. */
#modModal   .modal { max-width: min(94vw, 1000px); max-height: min(92vh, 1000px); }
#infoModal  .modal { max-width: min(92vw, 760px); }
#helpModal  .modal { max-width: min(92vw, 760px); }
#profileModal .modal { max-width: min(92vw, 620px); }
#newModal   .modal { max-width: min(92vw, 480px); }
#reportModal .modal { max-width: min(92vw, 540px); }
#topicModal .modal { max-width: min(92vw, 560px); }
#permModal  .modal { max-width: min(92vw, 520px); }

/* Reading a long block of text at 1000px wide is unpleasant, so
   the prose inside a wide panel stays at a comfortable measure. */
.help-body > * { max-width: 68ch; }
.modal h2 {
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: 0.1em;
  font-weight: 600;
  margin: 0 0 5px;
  color: var(--signal);
}
.modal-sub { color: var(--muted); font-size: 13px; line-height: 1.5; margin: 0 0 18px; }
.modal-actions { display: flex; gap: 10px; margin-top: 18px; }
