/* BuddyMGR structure. Colors, gradients and borders live in the skin files,
   never here, so a new skin is one stylesheet and no markup changes. */

* { box-sizing: border-box; }

/* [hidden] loses to any later rule that sets display, and this file sets
   display on almost everything. Without this the sign-up form draws under the
   sign-in form and both are on screen at once. Declared once, up here, so no
   future component has to remember. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
  /* Nothing in BuddyMGR is ever allowed to scroll sideways. */
  overflow-x: hidden;
}

/* One height for the two strips along the bottom, so the line between the
   contact list and the conversation runs straight across. */
:root { --bottom-strip: 26px; }

body {
  font-family: var(--font);
  font-size: var(--font-size);
  color: var(--text);
  background: var(--desktop);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--desktop-pad);
  -webkit-font-smoothing: antialiased;
}

.screen { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
.screen[hidden] { display: none; }

/* ---------- the loading screen ---------- */

/* Neither the sign-in screen nor the app is laid out until we know which one
   is right. The splash covers the window from the first paint, the correct
   screen is built underneath it, and then it fades away. Nothing is ever shown
   on its way to being replaced. */
.booting #screen-signin, .booting #screen-app { display: none !important; }

#screen-boot {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--desktop);
  transition: opacity .26s ease;
}
.boot-fading #screen-boot { opacity: 0; }
.boot-done #screen-boot { display: none; }

.boot-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 26px 34px;
  background: var(--win-bg);
  border: 1px solid var(--win-border);
  border-radius: var(--win-radius);
  box-shadow: var(--win-shadow);
}
/* The mark breathes, with a ring going out behind it on the same beat. The ring
   is a pseudo-element so it costs no markup and cannot be caught by a layout. */
.boot-logo {
  position: relative;
  animation: bm-boot-pulse 1.8s ease-in-out infinite;
}
.boot-mark {
  position: relative;
  display: grid;
  place-items: center;
}
.boot-mark::after {
  content: "";
  position: absolute;
  width: 76px;
  height: 76px;
  border-radius: 50%;
  border: 1px solid var(--accent-strong);
  opacity: 0;
  animation: bm-boot-ring 1.8s ease-out infinite;
}
@keyframes bm-boot-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}
@keyframes bm-boot-ring {
  0%   { transform: scale(.85); opacity: .55; }
  70%  { transform: scale(1.45); opacity: 0; }
  100% { transform: scale(1.45); opacity: 0; }
}
.boot-name { font-size: 19px; }
.boot-say { color: var(--muted); font-size: 11px; }
/* Three dots that actually run, rather than a full stop that sits there. */
.boot-dots {
  display: inline-block;
  width: 1.1em;
  font-style: normal;
  text-align: left;
}
.boot-dots::after {
  content: "";
  animation: bm-boot-dots 1.2s steps(4, end) infinite;
}
@keyframes bm-boot-dots {
  0%   { content: ""; }
  25%  { content: "."; }
  50%  { content: ".."; }
  75%  { content: "..."; }
  100% { content: ""; }
}

/* An indeterminate sweep: it says "working" without pretending to know how
   far through it is. */
.boot-bar {
  position: relative;
  width: 168px;
  height: 6px;
  border-radius: 3px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  overflow: hidden;
}
.boot-bar i {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 42%;
  border-radius: 3px;
  /* The accent, not the title bar gradient: the aero title bar is a dark
     teal glass that all but vanished against a pale track. Carried as a
     gradient so the block has a leading edge and a trail rather than sliding
     back and forth as one solid lump. */
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--accent-strong) 15%, transparent) 0%,
    var(--accent-strong) 55%,
    color-mix(in srgb, var(--accent-strong) 15%, transparent) 100%);
  box-shadow: 0 0 8px color-mix(in srgb, var(--accent-strong) 45%, transparent);
  animation: bm-boot-sweep 1.15s ease-in-out infinite;
}
/* A sheen crossing the track, a beat behind the bar. */
.boot-bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, .35) 50%, transparent 100%);
  transform: translateX(-100%);
  animation: bm-boot-sheen 2.3s ease-in-out infinite;
}
@keyframes bm-boot-sheen {
  0%, 60% { transform: translateX(-100%); }
  100%    { transform: translateX(100%); }
}
@keyframes bm-boot-sweep {
  0% { left: -45%; }
  100% { left: 103%; }
}

@media (prefers-reduced-motion: reduce) {
  .boot-logo { animation: none; }
  .boot-mark::after { animation: none; opacity: 0; }
  .boot-bar i { animation: none; left: 0; width: 100%; opacity: .5; }
  .boot-bar::after { animation: none; opacity: 0; }
  /* Steps() on content would still flicker, so it settles on the full stop. */
  .boot-dots::after { animation: none; content: "..."; }
  #screen-boot { transition: none; }
}

/* ---------- window chrome ---------- */

.window {
  display: flex;
  flex-direction: column;
  background: var(--win-bg);
  border: 1px solid var(--win-border);
  border-radius: var(--win-radius);
  box-shadow: var(--win-shadow);
  overflow: hidden;
  min-width: 0;
}

.window-app { width: 100%; height: 100%; max-width: 1180px; }
.window-signin { width: 340px; max-width: 100%; max-height: 100%; overflow-y: auto; }

.titlebar {
  display: flex;
  align-items: center;
  gap: 6px;
  height: var(--title-height);
  padding: 0 4px 0 6px;
  background: var(--title-bg);
  color: var(--title-text);
  flex: none;
  user-select: none;
}
.titlebar-icon img { width: 16px; height: 16px; display: block; }
.titlebar-text { font-weight: bold; font-size: 12px; text-shadow: var(--title-shadow); flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.titlebar-buttons { display: flex; gap: 2px; flex: none; }

.tb-btn {
  width: 22px; height: 16px;
  border: 1px solid var(--tb-border);
  border-radius: 3px;
  background: var(--tb-face);
  color: var(--tb-glyph);
  font-size: 10px; line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.tb-btn:disabled { cursor: default; opacity: .85; }
.tb-btn:not(:disabled):hover { border-color: var(--tb-border-hover); background: var(--tb-face-hover); }
.tb-min::before { content: "\2013"; }
.tb-max::before { content: "\25a1"; }
.tb-close { background: var(--tb-close-face); }
.tb-close::before { content: "\2715"; }

/* ---------- menus ---------- */

.menubar {
  position: relative;
  display: flex;
  /* Wraps rather than clips. The bar measures 280px and the contact window
     gives it more than that, but a different DPI, a text scaling setting or a
     font fallback all move that number, and a menu you cannot reach is worse
     than one on two lines. Nothing is ever hidden or scrolled sideways. */
  flex-wrap: wrap;
  gap: 2px;
  padding: 2px 4px;
  background: var(--menu-bg);
  border-bottom: 1px solid var(--menu-border);
  flex: none;
  user-select: none;
}
.menu-item {
  padding: 2px 8px;
  font-size: 12px;
  color: var(--menu-text);
  cursor: default;
  border: 1px solid transparent;
  border-radius: 3px;
}
.menu-item:hover, .menu-item.open { background: var(--menu-hover); border-color: var(--menu-hover-border); }

.menu-popup {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 60;
  min-width: 200px;
  padding: 3px;
  background: var(--popup-bg);
  border: 1px solid var(--popup-border);
  border-radius: var(--popup-radius);
  box-shadow: 0 6px 18px rgba(0,0,0,.28);
}
.menu-popup[hidden] { display: none; }
.menu-option {
  display: block;
  width: 100%;
  text-align: left;
  padding: 5px 10px;
  border: 1px solid transparent;
  border-radius: 3px;
  background: none;
  font: inherit;
  color: var(--text);
  cursor: pointer;
}
.menu-option:hover { background: var(--menu-hover); border-color: var(--menu-hover-border); }
.menu-sep { height: 1px; margin: 3px 6px; background: var(--menu-border); }

/* ---------- sign in ---------- */

.signin-body { padding: 12px 22px 18px; background: var(--signin-bg); flex: 1; }
.signin-brand { font-size: 17px; margin: 4px 0 10px; }
.brand-buddy { color: var(--brand-1); font-weight: bold; }
.brand-mgr { color: var(--brand-2); font-weight: bold; }
.brand-word { color: var(--text); }
.signin-hero { display: flex; justify-content: center; padding: 6px 0 14px; }
.signin-hero img { border: 1px solid var(--dp-border); background: #fff; padding: 4px; border-radius: var(--dp-radius); }

.signin-form { display: block; }
.field-label { display: block; margin: 10px 0 3px; font-size: 12px; color: var(--label); }
.field-label.inline { display: inline; margin: 0 6px 0 0; }
.field {
  width: 100%;
  padding: 3px 6px;
  height: 24px;
  font: inherit;
  color: var(--text);
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: var(--input-radius);
}
.field:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }

/* A chevron must never touch the border, so the arrow is drawn with room
   around it and the text is padded clear of the arrow. */
select.field, .status-select, .select {
  appearance: none;
  padding-right: 26px;
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 8px 5px;
}
.signin-status { margin: 12px 0 10px; display: flex; align-items: center; }
.signin-status .field { width: auto; min-width: 130px; }

.check {
  display: flex; align-items: center; gap: 8px;
  margin: 7px 0; font-size: 12px; color: var(--text);
  cursor: pointer; user-select: none;
}
.check input { margin: 0; }

/* A switch rather than a tick box. It is still a checkbox underneath, so it
   keeps the keyboard, the label and the form behaviour; only the paint
   changes, and every colour comes from the theme so all twenty two agree. */
.switch {
  appearance: none; -webkit-appearance: none;
  position: relative; flex: none; cursor: pointer;
  width: 38px; height: 20px; border-radius: 10px; margin: 0;
  background: var(--btn-face);
  border: 1px solid var(--btn-border);
  transition: background .14s ease, border-color .14s ease;
}
.switch::after {
  content: ""; position: absolute; top: 1px; left: 1px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--muted);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
  transition: transform .14s ease, background .14s ease;
}
.switch:hover { border-color: var(--btn-border-hover); }
.switch:checked {
  background: var(--selected-bg);
  border-color: var(--btn-default-border);
}
.switch:checked::after { transform: translateX(18px); background: var(--accent); }
.switch:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.switch:disabled { opacity: .55; cursor: default; }

.signin-actions { display: flex; gap: 8px; justify-content: center; margin: 16px 0 6px; }
.signin-error {
  margin: 10px 0 0; padding: 6px 8px; font-size: 12px;
  color: var(--error-text); background: var(--error-bg); border: 1px solid var(--error-border);
  border-radius: 3px;
}
.signin-error[hidden] { display: none; }
.signin-links { display: flex; justify-content: space-between; gap: 10px; margin-top: 18px; font-size: 12px; }
.signin-links a { color: var(--link); }
.signin-footer {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px; font-size: 12px;
  background: var(--footer-bg); border-top: 1px solid var(--footer-border);
}

/* ---------- buttons ---------- */

.button {
  min-width: 78px;
  padding: 4px 14px;
  font: inherit;
  color: var(--btn-text);
  background: var(--btn-face);
  border: 1px solid var(--btn-border);
  border-radius: var(--btn-radius);
  cursor: pointer;
}
.button:hover:not(:disabled) { border-color: var(--btn-border-hover); background: var(--btn-face-hover); }
.button:disabled { opacity: .5; cursor: default; }
.button.default { border-color: var(--btn-default-border); box-shadow: var(--btn-default-glow); font-weight: bold; }

/* ---------- app layout ---------- */

.app-body { flex: 1; display: flex; min-height: 0; min-width: 0; }

.pane-contacts {
  width: 268px;
  flex: none;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--pane-bg);
  border-right: 1px solid var(--pane-border);
}
.pane-splitter { width: 0; flex: none; }
.pane-chat { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--chat-bg); }

/* ---------- me panel ---------- */

.me-panel { display: flex; gap: 8px; padding: 8px; background: var(--me-bg); border-bottom: 1px solid var(--pane-border); flex: none; }
.dp {
  flex: none;
  padding: 3px;
  background: var(--dp-bg);
  border: 1px solid var(--dp-border);
  border-radius: var(--dp-radius);
  box-shadow: var(--dp-shadow);
  line-height: 0;
  cursor: default;
}
.dp img { width: 48px; height: 48px; object-fit: cover; display: block; border-radius: var(--dp-inner-radius); }
.dp-me { cursor: pointer; border-width: 1px; }
.dp-me:hover { border-color: var(--accent); }
.dp-them img, .dp-mine img { width: 88px; height: 88px; }

.me-text { min-width: 0; flex: 1; display: flex; flex-direction: column; justify-content: center; gap: 3px; }
.me-name-row { display: flex; align-items: center; gap: 4px; min-width: 0; }
.me-name { font-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.status-select {
  font: inherit; font-size: 11px;
  color: var(--muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  padding: 1px 22px 1px 4px;
  max-width: 132px;
  cursor: pointer;
}
.status-select:hover { border-color: var(--hover-border); background: var(--input-bg); }
.me-personal {
  font: inherit; font-size: 11px; font-style: italic;
  color: var(--muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  padding: 1px 4px;
  width: 100%;
}
.me-personal:hover { border-color: var(--hover-border); }
.me-personal:focus { outline: none; background: var(--input-bg); border-color: var(--accent); font-style: normal; color: var(--text); }

/* ---------- contact toolbar and tree ---------- */

.contact-toolbar { display: flex; gap: 2px; padding: 4px; background: var(--toolbar-bg); border-bottom: 1px solid var(--toolbar-border); flex: none; }
.ct-btn {
  display: flex; align-items: center; gap: 4px;
  padding: 3px 7px; font: inherit; font-size: 11px;
  color: var(--text); background: none;
  border: 1px solid transparent; border-radius: 3px; cursor: pointer;
}
.ct-btn:hover { background: var(--menu-hover); border-color: var(--menu-hover-border); }
.ct-ico { width: 16px; height: 16px; flex: none; background-repeat: no-repeat; background-position: center; background-size: contain; }

/* Icons are drawn as SVG files rather than emoji on purpose: emoji depend on
   a color emoji font being installed, and a Linux box without one renders
   every toolbar button as an empty box. */
.ico-add        { background-image: url("/img/ico-add.svg"); }
.ico-group      { background-image: url("/img/ico-group.svg"); }
.ico-dp         { background-image: url("/img/ico-dp.svg"); }
.ico-invite     { background-image: url("/img/ico-invite.svg"); }
.ico-file       { background-image: url("/img/ico-file.svg"); }
.ico-video      { background-image: url("/img/ico-video.svg"); }
.ico-voice      { background-image: url("/img/ico-voice.svg"); }
.ico-activities { background-image: url("/img/ico-activities.svg"); }
.ico-games      { background-image: url("/img/ico-games.svg"); }
.ico-font       { background-image: url("/img/ico-font.svg"); }
.ico-emoticon   { background-image: url("/img/ico-emoticon.svg"); }
.ico-wink       { background-image: url("/img/ico-wink.svg"); }
.ico-nudge      { background-image: url("/img/ico-nudge.svg"); }
.ico-clip       { background-image: url("/img/ico-clip.svg"); }
.ico-image      { background-image: url("/img/ico-image.svg"); }

.contact-search { padding: 5px 6px; flex: none; border-bottom: 1px solid var(--pane-border); }
.contact-search input {
  width: 100%; height: 22px; padding: 2px 7px; font: inherit; font-size: 11px;
  color: var(--text); background: var(--input-bg);
  border: 1px solid var(--input-border); border-radius: 11px;
}
.contact-search input:focus { outline: none; border-color: var(--accent); }

.contact-tree { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 4px 0; min-height: 0; }

.group-head {
  display: flex; align-items: center; gap: 4px;
  padding: 3px 8px; font-weight: bold; font-size: 12px;
  color: var(--group-text); cursor: pointer; user-select: none;
  border: 1px solid transparent;
}
.group-head:hover { border-color: var(--hover-border); }
.group-arrow { width: 9px; text-align: center; font-size: 9px; color: var(--muted); }
.group-head.collapsed + .group-body { display: none; }
.group-body { padding-bottom: 4px; }

.contact-row {
  display: flex; align-items: center; gap: 6px;
  padding: 2px 8px 2px 16px;
  cursor: pointer;
  border: 1px solid transparent;
  min-width: 0;
}
.contact-row:hover { background: var(--hover-bg); border-color: var(--hover-border); }
.contact-row.selected { background: var(--selected-bg); border-color: var(--selected-border); }
.contact-row .dot { width: 10px; height: 10px; border-radius: 50%; flex: none; border: 1px solid rgba(0,0,0,.35); }
.contact-row .mini-dp { width: 18px; height: 18px; border-radius: 2px; object-fit: cover; flex: none; border: 1px solid var(--dp-border); background: #fff; }
.contact-row .names { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.contact-row .cname { color: var(--text); }
.contact-row .cpm { color: var(--muted); font-size: 11px; }
.contact-row.offline .cname { color: var(--muted); }
.contact-row .badge {
  flex: none; min-width: 17px; height: 15px; padding: 0 4px;
  border-radius: 8px; background: var(--badge-bg); color: var(--badge-text);
  font-size: 10px; font-weight: bold; line-height: 15px; text-align: center;
}
.contact-row .row-btn {
  flex: none; border: 1px solid var(--btn-border); background: var(--btn-face);
  border-radius: 3px; font: inherit; font-size: 10px; padding: 1px 6px; cursor: pointer; color: var(--btn-text);
}
.contact-row .row-btn:hover { border-color: var(--btn-border-hover); }

/* The two strips along the bottom of the window sit side by side, so they
   share a height. The advertisement used to wrap onto a second line and stand
   twenty pixels taller than the status line beside it. */
.ad-strip, .chat-status { height: var(--bottom-strip); box-sizing: border-box; }

.ad-strip {
  flex: none; display: flex; align-items: center; justify-content: center;
  padding: 0 8px; font-size: 11px; text-align: center;
  /* One line, whatever it says: the height is shared and wrapping breaks it. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--ad-text); background: var(--ad-bg);
  cursor: pointer; border: 1px solid transparent; border-top-color: var(--pane-border);
}
.ad-strip:hover { border-color: var(--btn-default-border); text-decoration: underline; }

/* ---------- tabs ---------- */

.tabstrip {
  display: flex; gap: 2px; align-items: flex-end;
  padding: 4px 6px 0; flex: none;
  background: var(--tabstrip-bg);
  border-bottom: 1px solid var(--tab-border);
  overflow: hidden;
}
.tab {
  display: flex; align-items: center; gap: 5px;
  max-width: 190px; min-width: 0;
  padding: 4px 6px 4px 9px;
  font-size: 12px;
  color: var(--tab-text);
  background: var(--tab-face);
  border: 1px solid var(--tab-border);
  border-bottom: none;
  border-radius: var(--tab-radius) var(--tab-radius) 0 0;
  cursor: pointer;
}
.tab:hover { background: var(--tab-face-hover); }
.tab.active { background: var(--tab-face-active); color: var(--tab-text-active); font-weight: bold; }
.tab .tab-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tab .tab-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.tab .tab-close {
  flex: none; border: none; background: none; cursor: pointer; font-size: 11px;
  color: var(--muted); padding: 0 2px; border-radius: 2px; line-height: 1;
}
.tab .tab-close:hover { background: var(--tb-close-face); color: #fff; }
.tab .tab-badge {
  flex: none; min-width: 16px; height: 14px; padding: 0 4px; border-radius: 7px;
  background: var(--badge-bg); color: var(--badge-text); font-size: 10px; line-height: 14px; text-align: center;
}

.chat-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; color: var(--muted); }
.chat-empty[hidden] { display: none; }

.chat { flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.chat[hidden] { display: none; }

.chat-toolbar { display: flex; gap: 2px; padding: 4px 6px; flex: none; background: var(--toolbar-bg); border-bottom: 1px solid var(--toolbar-border); overflow: hidden; }
.chat-tool {
  display: flex; flex-direction: var(--tool-dir); align-items: center; gap: 3px;
  padding: 3px 7px; font: inherit; font-size: 11px; color: var(--text);
  background: none; border: 1px solid transparent; border-radius: 3px; cursor: pointer; white-space: nowrap;
}
.chat-tool:hover { background: var(--menu-hover); border-color: var(--menu-hover-border); }

.chat-to { flex: none; padding: 4px 10px; font-size: 12px; color: var(--text); background: var(--to-bg); border-bottom: 1px solid var(--to-border); }
.chat-to b { color: var(--accent-strong); }

/* The transcript keeps a floor. A big board like Mine Flags would otherwise
   take the whole tab and leave one line of conversation under it, which
   defeats the point of playing in the chat window. */
.chat-main { flex: 1; display: flex; gap: 6px; min-height: 118px; padding: 6px; }
.transcript-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column;
  background: var(--transcript-bg); border: 1px solid var(--transcript-border); border-radius: var(--transcript-radius); }
.transcript { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 6px 8px; min-height: 0; overflow-wrap: anywhere; }
.typing-line { flex: none; height: 17px; padding: 0 8px; font-size: 11px; font-style: italic; color: var(--muted); }

.dp-column { flex: none; display: flex; flex-direction: column; gap: 6px; justify-content: space-between; }

.line { margin: 1px 0; }
.line .who { font-weight: bold; color: var(--them-name); }
.line.mine .who { color: var(--me-name-color); }
.line .said { color: var(--text); }
.line .stamp { color: var(--muted); font-size: 10px; margin-left: 6px; }
.line.system { color: var(--muted); font-style: italic; margin: 5px 0; }
.line.nudge { color: var(--nudge-text); font-weight: bold; margin: 5px 0; }
.line.wink { color: var(--nudge-text); margin: 5px 0; }
.line img.emo { width: 19px; height: 19px; vertical-align: -4px; }
.wink-overlay img { width: 96px; height: 96px; }
.line .filecard {
  display: inline-flex; align-items: center; gap: 8px; margin: 3px 0; padding: 6px 9px;
  background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 4px;
}
.line .filecard a { color: var(--link); }
.line .filecard .fsize { color: var(--muted); font-size: 11px; }
.line .shot { display: block; margin: 4px 0; max-width: min(320px, 100%); max-height: 240px; border: 1px solid var(--card-border); border-radius: 3px; }
.history-more { display: block; margin: 4px auto 8px; }

/* ---------- compose ---------- */

.compose { flex: none; margin: 0 6px 6px; background: var(--transcript-bg); border: 1px solid var(--transcript-border); border-radius: var(--transcript-radius); }
.compose-toolbar { display: flex; gap: 2px; padding: 3px 5px; border-bottom: 1px solid var(--toolbar-border); background: var(--compose-bar-bg); }
.fmt-btn {
  min-width: 26px; height: 24px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  font: inherit; font-size: 13px; line-height: 1;
  background: none; border: 1px solid transparent; border-radius: 3px; cursor: pointer;
}
.fmt-btn .ct-ico { width: 18px; height: 18px; }
.fmt-btn:hover { background: var(--menu-hover); border-color: var(--menu-hover-border); }
.fmt-a { font-weight: bold; font-family: Georgia, serif; }

.compose-row { display: flex; gap: 6px; padding: 6px; align-items: stretch; }
.compose-row textarea {
  flex: 1; min-width: 0; resize: none;
  font: inherit; color: var(--text);
  background: transparent; border: none; padding: 2px 4px;
}
.compose-row textarea:focus { outline: none; }
.compose-buttons { flex: none; display: flex; flex-direction: column; gap: 4px; }
.compose-buttons .button { min-width: 66px; padding: 3px 10px; }

.chat-status {
  flex: none; display: flex; align-items: center;
  padding: 0 10px; font-size: 11px; color: var(--muted);
  border-top: 1px solid var(--toolbar-border); background: var(--toolbar-bg);
}

.statusbar { flex: none; padding: 3px 10px; font-size: 11px; color: var(--muted); background: var(--toolbar-bg); border-top: 1px solid var(--toolbar-border); }

/* ---------- emoticon and wink panels ---------- */

.emoticon-panel, .wink-panel {
  position: fixed; z-index: 70;
  padding: 6px;
  background: var(--popup-bg);
  border: 1px solid var(--popup-border);
  border-radius: var(--popup-radius);
  box-shadow: 0 8px 22px rgba(0,0,0,.3);
  max-width: min(320px, calc(100vw - 20px));
  max-height: 300px;
  overflow-y: auto;
}
.emoticon-panel[hidden], .wink-panel[hidden] { display: none; }
.emo-grid { display: grid; grid-template-columns: repeat(8, 28px); gap: 2px; }
.emo-btn { width: 28px; height: 28px; padding: 2px; background: none; border: 1px solid transparent; border-radius: 3px; cursor: pointer; }
.emo-btn img { width: 22px; height: 22px; display: block; margin: 0 auto; }
.emo-btn:hover { background: var(--menu-hover); border-color: var(--menu-hover-border); }
.emo-head { font-size: 11px; font-weight: bold; color: var(--muted); margin: 5px 0 3px; }
.wink-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 3px; }
.wink-btn {
  display: flex; align-items: center; gap: 6px; padding: 5px 8px;
  font: inherit; font-size: 12px; text-align: left;
  background: none; border: 1px solid transparent; border-radius: 3px; cursor: pointer; color: var(--text);
}
.wink-btn:hover { background: var(--menu-hover); border-color: var(--menu-hover-border); }
.wink-btn .wface { width: 24px; height: 24px; flex: none; }

/* ---------- modal ---------- */

.modal-backdrop {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(0,0,0,.35);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.modal-backdrop[hidden] { display: none; }
.modal { width: 380px; max-width: 100%; max-height: 100%; }
.modal.modal-wide { width: 640px; }
.modal-body { padding: 14px 16px; overflow-y: auto; }
.modal-body p { margin: 0 0 10px; }
.modal-body .field { margin-bottom: 8px; }
.modal-footer { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 14px; background: var(--footer-bg); border-top: 1px solid var(--footer-border); }
.modal-list { max-height: 220px; overflow-y: auto; border: 1px solid var(--input-border); border-radius: 3px; background: var(--input-bg); }
.modal-list label { display: flex; align-items: center; gap: 7px; padding: 4px 8px; cursor: pointer; }
.modal-list label:hover { background: var(--hover-bg); }

/* Picking people. Several independent answers get a switch each; picking one
   of a list is a row you choose, because a switch would say the answers were
   independent when choosing one unchooses the last. */
.pick-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 6px 9px; cursor: pointer;
  border-bottom: 1px solid var(--card-border);
}
.pick-row:last-child { border-bottom: 0; }
.pick-who { display: flex; align-items: center; gap: 7px; min-width: 0; }
.pick-who .dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: none; }
.pick-one.chosen { background: var(--selected-bg); font-weight: bold; color: var(--accent-strong); }
.pick-radio { margin: 0; flex: none; accent-color: var(--accent); }

/* ---------- toasts ---------- */

.toast-stack { position: fixed; right: 12px; bottom: 12px; z-index: 100; display: flex; flex-direction: column; gap: 8px; max-width: min(320px, calc(100vw - 24px)); }
/* Three at a time. A conversation that arrives all at once used to paper over
   the whole window with its own notifications. */
.toast-stack .toast:nth-last-child(n+4) { display: none; }
.toast {
  padding: 8px 12px;
  background: var(--toast-bg); color: var(--toast-text);
  border: 1px solid var(--toast-border); border-radius: 4px;
  box-shadow: 0 6px 18px rgba(0,0,0,.3);
  font-size: 12px;
  cursor: pointer;
}
.toast b { display: block; margin-bottom: 2px; }

/* ---------- nudge ---------- */

@keyframes bm-shake {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-7px, 3px); }
  20% { transform: translate(6px, -4px); }
  30% { transform: translate(-6px, -3px); }
  40% { transform: translate(5px, 4px); }
  50% { transform: translate(-4px, 2px); }
  60% { transform: translate(4px, -2px); }
  70% { transform: translate(-3px, 1px); }
  80% { transform: translate(2px, 2px); }
  90% { transform: translate(-1px, -1px); }
}
.nudging { animation: bm-shake .6s ease-in-out 1; }

@media (prefers-reduced-motion: reduce) {
  .nudging { animation: none; outline: 3px solid var(--nudge-text); outline-offset: -3px; }
}

/* ---------- winks ---------- */

.wink-overlay {
  position: absolute; inset: 0; z-index: 20;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
  animation: bm-wink 2.2s ease-out 1 forwards;
}
@keyframes bm-wink {
  0% { opacity: 0; transform: scale(.3) rotate(-18deg); }
  18% { opacity: 1; transform: scale(1.15) rotate(6deg); }
  32% { transform: scale(1) rotate(0deg); }
  78% { opacity: 1; }
  100% { opacity: 0; transform: scale(1.3); }
}
.transcript-wrap { position: relative; }

/* ---------- narrow screens ---------- */

@media (max-width: 820px) {
  body { padding: 0; }
  .window-app { border-radius: 0; border: none; max-width: none; }
  .app-body { position: relative; }
  .pane-contacts { width: 100%; border-right: none; }
  .pane-chat { display: none; }
  /* On a phone the two panes take turns rather than sitting side by side,
     because a side by side layout at this width is what creates a sideways
     scrollbar. */
  body.show-chat .pane-contacts { display: none; }
  body.show-chat .pane-chat { display: flex; }
  .dp-column { display: none; }
  .tabstrip { overflow-x: hidden; flex-wrap: wrap; }
  .chat-toolbar { flex-wrap: wrap; }
}

/* Emoji rendered from a shortcode sit a shade large next to 12px Tahoma,
   which is what makes them read as emoticons rather than as text. */
/* Naming the emoji fonts explicitly matters: without it a Linux box with no
   emoji font in its default stack draws every face as an empty box. */
.emo-char, .line.wink .said {
  font-family: var(--emoji-font);
  font-size: 1.25em; line-height: 1; vertical-align: -2px;
}
.line.wink .said { font-size: inherit; }
.line a { color: var(--link); }
.menu-option.checked::before { content: "\2713 "; color: var(--accent-strong); }

/* ---------- theme picker ---------- */

.skin-group {
  font-size: 11px; font-weight: bold; color: var(--muted);
  margin: 12px 0 5px; text-transform: uppercase; letter-spacing: .04em;
}
.skin-group:first-of-type { margin-top: 4px; }

.skin-grid {
  display: grid;
  /* Wraps to whatever width it is given, so the picker never scrolls sideways. */
  grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
  gap: 8px;
}

.skin-tile {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 8px; font: inherit; color: var(--text);
  background: var(--card-bg);
  /* Always bordered, only the colour changes, so hover shifts nothing. */
  border: 2px solid var(--card-border);
  border-radius: 5px; cursor: pointer;
}
.skin-tile:hover { border-color: var(--accent); background: var(--hover-bg); }
.skin-tile:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.skin-tile.chosen { border-color: var(--btn-default-border); background: var(--selected-bg); }
.skin-tile.chosen .skin-tile-name { color: var(--accent-strong); font-weight: bold; }
.skin-tile-name { font-size: 11px; line-height: 1.2; text-align: center; }

/* The swatch is a real miniature window. A skin is a plain class, so putting
   it on this element gives its children that theme's own variables: these are
   the actual gradients, not a guess at them. */
.skin-mini {
  display: block; width: 100%; height: 62px; overflow: hidden;
  border: 1px solid var(--win-border);
  border-radius: 4px;
  background: var(--win-bg);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
}
.skin-mini-bar {
  display: flex; align-items: center; justify-content: flex-end;
  height: 13px; padding: 0 3px;
  background: var(--title-bg);
}
.skin-mini-dot {
  width: 7px; height: 5px; border-radius: 1px;
  background: var(--tb-close-face);
}
.skin-mini-body { display: block; padding: 5px 5px 0; background: var(--pane-bg); height: 100%; }
.skin-mini-row {
  display: block; height: 5px; margin-bottom: 4px; border-radius: 2px;
  background: var(--selected-bg); border: 1px solid var(--selected-border);
}
.skin-mini-row.short { width: 62%; background: var(--hover-bg); border-color: var(--hover-border); }
.skin-mini-btn {
  display: block; width: 40px; height: 11px; margin-top: 7px; border-radius: 2px;
  background: var(--btn-face); border: 1px solid var(--btn-default-border);
  box-shadow: var(--btn-default-glow);
}

.emo-skins { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.emo-skins span { color: var(--muted); font-size: 11px; }

/* Dark themes: hand the browser's own furniture the news, so scrollbars and
   the caret stop being white on black. */
.skin-is-dark ::selection { background: var(--selected-bg); color: var(--text); }

/* ---------- scrollbars ---------- */

/* The thumb is literally the app's button face, so a scrollbar belongs to
   whichever of the sixteen themes is on rather than being a grey stripe from
   the browser.

   Chromium and Firefox must never be given both halves of this. The moment
   scrollbar-color is set, Chromium abandons the pseudo-elements below and
   draws its own native widget, stepper arrows and all, silently ignoring every
   rule here. So the standard properties are scoped to browsers that have no
   ::-webkit-scrollbar at all, which is Firefox.

   Gated on a fine pointer: touch browsers draw a transient overlay scrollbar
   that fades by itself, and styling it there parks a permanent rail down the
   side of the content. */
@media (pointer: fine) {
  @supports not selector(::-webkit-scrollbar) {
    * { scrollbar-width: thin; scrollbar-color: var(--btn-border) transparent; }
  }

  ::-webkit-scrollbar { width: 14px; height: 14px; }
  ::-webkit-scrollbar-track {
    background: var(--card-bg);
    border-left: 1px solid var(--card-border);
  }
  ::-webkit-scrollbar-thumb {
    background: var(--btn-face);
    border: 1px solid var(--btn-border);
    border-radius: 3px;
    /* A little inset so the thumb floats in the rail instead of filling it. */
    background-clip: padding-box;
  }
  ::-webkit-scrollbar-thumb:hover {
    background: var(--btn-face-hover);
    border-color: var(--btn-border-hover);
  }
  ::-webkit-scrollbar-thumb:active { background: var(--selected-bg); }
  ::-webkit-scrollbar-corner { background: var(--card-bg); }
  /* The stepper arrows at each end are not part of this look. */
  ::-webkit-scrollbar-button { display: none; width: 0; height: 0; }

  /* The transcript and the contact list are the two that scroll most, and both
     sit on white rather than on the panel. */
  .transcript::-webkit-scrollbar-track,
  .contact-list::-webkit-scrollbar-track { background: var(--transcript-bg); }
}

/* ---------- contact groups ---------- */

.group-choices { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 12px; }
.group-choice {
  font: inherit; font-size: 12px; padding: 5px 10px; cursor: pointer;
  color: var(--text); background: var(--card-bg);
  border: 2px solid var(--card-border); border-radius: 4px;
}
.group-choice:hover { border-color: var(--accent); background: var(--hover-bg); }
.group-choice.chosen {
  border-color: var(--btn-default-border); background: var(--selected-bg);
  font-weight: bold; color: var(--accent-strong);
}
.group-choice:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* The hint only shows in a group nobody is in yet, which is exactly when
   somebody needs telling what the group is for. */
.group-empty {
  padding: 5px 10px 6px 24px;
  font-size: 11px; font-style: italic; color: var(--muted);
}

.contact-row.dragging { opacity: .45; }

/* While a contact is being dragged, every group says it will take it. Without
   this the list looks inert and nobody tries dragging a second time. */
/* The header already carries a 1px transparent border, so only the colour and
   the style change here. Adding or removing a side would shift the row. */
.contact-tree.sorting .group-head { border-color: var(--card-border); border-style: dashed; }
.group-head.drop-target {
  background: var(--selected-bg);
  border-color: var(--accent);
  border-style: dashed;
  color: var(--accent-strong);
}

/* The name of the server, when somebody has changed it from ours. */
.brand-plain { font-weight: bold; color: var(--accent-strong); }
.signin-welcome {
  margin: 0 0 12px; padding: 8px 10px;
  font-size: 12px; line-height: 1.45; color: var(--label);
  background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 4px;
}

/* ---------- quick sign-in, for a server you are working on ---------- */

.quick-logins { margin: 14px 0 4px; text-align: left; }
.quick-warn {
  font-size: 10px; line-height: 1.4; margin-bottom: 6px;
  padding: 5px 7px; border-radius: 3px;
  color: var(--error-text); background: var(--error-bg); border: 1px solid var(--error-border);
}
.quick-row { display: flex; flex-direction: column; gap: 4px; }
.quick-account {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 8px; font: inherit; text-align: left; cursor: pointer;
  color: var(--text); background: var(--card-bg);
  border: 1px solid var(--card-border); border-radius: 4px;
}
.quick-account:hover { border-color: var(--accent); background: var(--hover-bg); }
.quick-account:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.quick-account.is-suspended { opacity: .5; cursor: default; }
.quick-dp {
  width: 24px; height: 24px; flex: none; object-fit: cover;
  background: var(--dp-bg); border: 1px solid var(--dp-border); border-radius: 2px;
}
.quick-names { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.quick-name { display: flex; align-items: center; gap: 5px; font-weight: bold; font-size: 12px; }
.quick-handle { font-size: 10px; color: var(--muted); }

/* Little status pills, used beside a name in the panel and on the quick
   sign-in list. Defined here because both pages load this stylesheet and only
   one of them loads the panel's. */
.pill {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 9px;
  font-size: 10px; font-weight: bold;
  background: var(--selected-bg); color: var(--accent-strong);
  border: 1px solid var(--selected-border);
}
.pill-susp { background: var(--error-bg); color: var(--error-text); border-color: var(--error-border); }

/* ---------- inside somebody else's page ---------- */

/* A widget in the corner of a site is not a window on a desktop: it has no
   title bar of its own to imitate, and the desktop background behind it would
   be a grey box in the middle of their design. */
.is-embedded body { padding: 0; background: var(--win-bg); }
.is-embedded .titlebar { display: none; }
.is-embedded .screen { padding: 0; }
.is-embedded .window {
  width: 100%; height: 100vh; max-width: none; max-height: none;
  border: 0; border-radius: 0; box-shadow: none;
}

/* ---------- conversation scenes ---------- */

/* The tile itself is translucent, so this only sets how it repeats: the
   transcript keeps whatever colour the theme gives it underneath. */
.transcript { background-repeat: repeat; }

.scene-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 8px;
}
.scene-tile {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 7px; font: inherit; cursor: pointer; color: var(--text);
  background: var(--card-bg);
  border: 2px solid var(--card-border); border-radius: 5px;
}
.scene-tile:hover { border-color: var(--accent); background: var(--hover-bg); }
.scene-tile.chosen { border-color: var(--btn-default-border); background: var(--selected-bg); }
.scene-tile.chosen .scene-name { font-weight: bold; color: var(--accent-strong); }
.scene-swatch {
  width: 100%; height: 54px; border-radius: 3px;
  background-color: var(--transcript-bg);
  background-repeat: repeat;
  border: 1px solid var(--transcript-border);
}
/* None is a plain transcript, drawn as one rather than as an empty box. */
.scene-none::after {
  content: ""; display: block; height: 100%;
  background: linear-gradient(180deg, transparent 40%, var(--card-border) 40%, var(--card-border) 42%, transparent 42%,
    transparent 60%, var(--card-border) 60%, var(--card-border) 62%, transparent 62%);
  margin: 0 8px;
}
.scene-name { font-size: 11px; line-height: 1.2; }

/* ---------- calls ---------- */

.call-panel {
  /* A column with a ceiling, so a call can never take more than a bit under
     half of the conversation. Below that ceiling the video keeps its natural
     size; above it the video is the part that gives way, because the buttons
     and the timer have to stay reachable. */
  /* Shrinkable, not fixed. The rows below it (the transcript's floor, the
     compose box, the status strip) all refuse to give way, so if the panel
     will not either the bottom of the window is simply pushed off the screen,
     which is what happened: the Nudge button and the status line went missing
     for the length of the call. */
  flex: 0 1 auto; max-height: 45%;
  display: flex; flex-direction: column;
  margin-bottom: 6px;
  background: var(--card-bg); border: 1px solid var(--card-border);
  border-radius: 5px; overflow: hidden;
}
.call-bar {
  flex: none;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 10px;
}
.call-status { font-weight: bold; color: var(--accent-strong); }
.call-buttons { display: flex; gap: 6px; }
.button-hangup { border-color: #c0392b; color: #c0392b; font-weight: bold; }
.button-hangup:hover { background: var(--error-bg); border-color: #a02618; }
.button.off { color: var(--muted); }

/* A ringing panel should be noticed from across the room. */
.call-panel[data-state="ringing"] {
  border-color: var(--btn-default-border);
  animation: call-ring 1s ease-in-out infinite;
}
@keyframes call-ring {
  0%, 100% { box-shadow: 0 0 0 0 rgba(232, 149, 31, .5); }
  50% { box-shadow: 0 0 0 4px rgba(232, 149, 31, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .call-panel[data-state="ringing"] { animation: none; }
}

/* Their picture large, yours in the corner, which is the arrangement everybody
   already understands.

   Two things this has to get right. The dark area spans the whole panel and the
   picture is centred inside it, because a 4:3 picture in a wide window
   otherwise sits against one edge with a slab of white beside it. And the
   height is capped rather than driven by the aspect ratio: a call happens in
   the middle of a conversation, and a picture tall enough to push the
   transcript off the screen makes the conversation unusable for as long as the
   call lasts. */
.call-video {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  background: #16324f;
  flex: 1 1 auto;
  height: clamp(140px, 30vh, 260px);
  min-height: 110px;
}
.call-video[hidden] { display: none; }
/* contain rather than cover: cropping the top of somebody's head to fill a box
   is worse than a little dark space either side of them. */
.call-them {
  height: 100%; width: auto; max-width: 100%;
  object-fit: contain; display: block; background: #16324f;
}
.call-me {
  position: absolute; right: 10px; bottom: 10px;
  width: 25%; max-width: 120px; border-radius: 4px;
  border: 2px solid rgba(255, 255, 255, .85);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .4);
  object-fit: cover; background: #16324f;
}
