/* messages.css — Direct Messages P2 (/en/messages + the thread). Locked --bb-* candy system: flat
   fills, hard 1–2px borders, hard offset shadows, ZERO blur/glass/rgba-overlays (anti-clunk law).
   Bevel ONLY what is pressed (tabs, buttons — the .bb-btn family carries its own); panels, rows and
   message bubbles are SURFACES (flat fill + hard border, square corners — bubbles carry NO radius).
   Gold = the active tab. Buttons reuse .bb-btn / --gold / --secondary untouched from components.css.
   The avatar circle (border-radius: 50%) is the site's established avatar shape, not a panel. */

.page-messages { background-color: var(--bb-field); }

.dm { padding-block: var(--space-xl) var(--space-3xl); }

.dm__title {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-2xl);
  color: var(--bb-warmwhite);
  margin-block-end: var(--space-lg);
}

/* ── flash feedback (query-string PRG signals + send errors) ─────────────────────────────────── */

.dm__flash {
  padding: var(--space-sm) var(--space-md);
  margin-block-end: var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-mint);
  border-radius: var(--bb-radius);
  color: var(--bb-warmwhite);
  font-size: var(--fs-sm);
}
.dm__flash--err { border-color: var(--bb-gold); }

/* ── the tabs — pressable, so beveled; GOLD = active (pressed-in, the category-tab language) ──── */

.dm__tabs {
  display: flex;
  gap: var(--space-sm);
  margin-block-end: var(--space-md);
}

.dm-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: 44px;                     /* WCAG 2.5.5 touch target */
  padding: var(--space-sm) var(--space-lg);
  background-color: var(--bb-frame);
  color: var(--bb-warmwhite);
  border: 1px solid var(--bb-black);
  border-radius: var(--bb-radius);
  font-family: var(--font-condensed);
  font-weight: 700;
  font-size: var(--fs-md);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  box-shadow:                                /* hard 2px raised bevel — zero blur */
    inset 2px 2px 0 rgba(255, 255, 255, 0.28),
    inset -2px -2px 0 rgba(14, 10, 12, 0.55);
}
.dm-tab:hover { filter: brightness(1.12); }  /* the button hover law — brightness ONLY */
.dm-tab.is-active {
  background-color: var(--bb-gold);
  color: var(--bb-frame);
  box-shadow:                                /* reversed = pressed in */
    inset 2px 2px 0 rgba(14, 10, 12, 0.32),
    inset -2px -2px 0 rgba(255, 255, 255, 0.5);
}

/* ── the list panel + rows (surfaces — flat, hard-bordered, never beveled) ───────────────────── */

.dm__empty {
  padding: var(--space-xl);
  background-color: var(--bb-warmwhite);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: 4px 4px 0 0 var(--bb-black);   /* hard offset — never blur */
  color: var(--bb-frame);
  text-align: center;
}

.dm__list {
  display: flex;
  flex-direction: column;
  background-color: var(--bb-warmwhite);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: 4px 4px 0 0 var(--bb-black);   /* hard offset — never blur */
  overflow: hidden;
}

.dm-row {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md);
  color: var(--bb-frame);
  min-block-size: 44px;
}
.dm-row + .dm-row,
.dm-row + .dm-row--request,
.dm-row--request + .dm-row--request { border-block-start: 1px solid var(--bb-dimpink); }
a.dm-row:hover { background-color: var(--bb-dimpink); }   /* flat fill swap — a row is a surface, not a button */

.dm-row--request { flex-wrap: wrap; }
.dm-row__link {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex: 1 1 240px;
  min-inline-size: 0;
  color: var(--bb-frame);
}
.dm-row__link:hover { color: var(--bb-berry); }

.dm-row__avatar {
  flex: 0 0 auto;
  inline-size: 42px;
  block-size: 42px;
  border-radius: 50%;             /* the established avatar-circle shape (the CSS silhouette fallback) */
  background-color: var(--bb-canvas);
  border: 2px solid var(--bb-black);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.dm-row__avatar img { inline-size: 36px; block-size: 36px; image-rendering: pixelated; }

.dm-row__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-inline-size: 0;
  flex: 1 1 auto;
}
.dm-row__who { display: flex; align-items: baseline; gap: var(--space-sm); min-inline-size: 0; }
.dm-row__name { color: var(--bb-berry); overflow-wrap: anywhere; }
.dm-row__handle {
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  color: var(--bb-frame);
}
.dm-row__preview {
  font-size: var(--fs-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-inline-size: 100%;
}

.dm-row__meta {
  margin-inline-start: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-xs);
  flex: 0 0 auto;
}
.dm-row__time {
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  color: var(--bb-berry);
  white-space: nowrap;
}
.dm-row__pip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 22px;
  block-size: 22px;
  padding-inline: var(--space-xs);
  background-color: var(--bb-gold);           /* the gold unread pip */
  color: var(--bb-frame);
  border: 1px solid var(--bb-black);
  border-radius: var(--bb-radius);
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  line-height: 1;
}

.dm-row__actions {
  display: flex;
  gap: var(--space-sm);
  margin-inline-start: auto;
  padding-inline-start: calc(42px + var(--space-md));   /* align under the text, clear of the avatar */
}

.dm__pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  margin-block-start: var(--space-lg);
  flex-wrap: wrap;
}

/* ── the thread ──────────────────────────────────────────────────────────────────────────────── */

.dm__back {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  color: var(--bb-warmwhite);
  font-family: var(--font-condensed);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dm__back:hover { color: var(--bb-gold); }

.dm-thread__head {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-block: var(--space-sm) var(--space-md);
}
.dm-thread__name {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-xl);
  color: var(--bb-warmwhite);
}
.dm-thread__name-link { color: var(--bb-warmwhite); }
.dm-thread__name-link:hover { color: var(--bb-gold); }
.dm-thread__handle {
  font-family: var(--font-mono);
  font-size: var(--fs-lg);
  color: var(--bb-gold);
}

.dm-thread__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  background-color: var(--bb-canvas);          /* the stage — bubbles pop on it */
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: 4px 4px 0 0 var(--bb-black);     /* hard offset — never blur */
}

.dm-thread__pageturn {
  align-self: center;
  min-block-size: 44px;
  display: inline-flex;
  align-items: center;
  color: var(--bb-frame);
  font-family: var(--font-condensed);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: var(--fs-sm);
}
.dm-thread__pageturn:hover { color: var(--bb-berry); }

.dm-thread__none {
  padding: var(--space-lg);
  text-align: center;
  color: var(--bb-frame);
}

.dm-thread__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Bubbles are SURFACES: flat fill, hard 1px border, SQUARE corners (no radius — the Web 1.0 law),
   no bevel, no shadow. Received = panel plum, sent = berry; both warm-white text. */
.dm-msg {
  max-inline-size: 78%;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--bb-black);
  border-radius: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dm-msg--theirs {
  align-self: flex-start;
  background-color: var(--bb-panel);
  color: var(--bb-warmwhite);
}
.dm-msg--mine {
  align-self: flex-end;
  background-color: var(--bb-berry);
  color: var(--bb-warmwhite);
}
.dm-msg__body { overflow-wrap: anywhere; white-space: pre-wrap; }
.dm-msg__time {
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  opacity: 0.75;
  align-self: flex-end;
}

/* A purged row (P4) — a muted placeholder, never an empty bubble. */
.dm-msg--purged {
  background-color: var(--bb-dimpink);
  color: var(--bb-frame);
}
.dm-msg__body--placeholder { font-style: italic; }
.dm-msg--purged .dm-msg__time { opacity: 0.6; }

/* ── composer + state notices ────────────────────────────────────────────────────────────────── */

.dm-thread__notice,
.dm-thread__readonly {
  margin-block-start: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-gold);
  border-radius: var(--bb-radius);
  color: var(--bb-warmwhite);
  font-size: var(--fs-sm);
}

.dm-thread__gate {
  margin-block-start: var(--space-md);
  padding: var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: 4px 4px 0 0 var(--bb-black);     /* hard offset — never blur */
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.dm-thread__gate-text { color: var(--bb-warmwhite); }
.dm-thread__gate-actions { display: flex; gap: var(--space-sm); flex-wrap: wrap; }

.dm-composer {
  margin-block-start: var(--space-md);
  padding: var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: 4px 4px 0 0 var(--bb-black);     /* hard offset — never blur */
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.dm-composer__input {
  inline-size: 100%;
  padding: var(--space-sm) var(--space-md);
  background-color: var(--bb-warmwhite);
  color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  font-size: var(--fs-md);
  line-height: 1.5;
  min-block-size: 44px;
  max-block-size: 240px;
}
.dm-composer__input:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }
.dm-composer__input:disabled { opacity: 0.6; cursor: not-allowed; }
.dm-composer__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}
.dm-composer__count {
  font-family: var(--font-mono);
  font-size: var(--fs-lg);
  color: var(--bb-warmwhite);
}

/* ── the decline confirmation modal (overlays tier — z 500 per the layer convention) ─────────── */

.dm-modal { position: fixed; inset: 0; z-index: 500; display: flex; align-items: center; justify-content: center; }
.dm-modal__backdrop {
  position: absolute;
  inset: 0;
  background-color: var(--bb-black);
  opacity: 0.55;                               /* a flat scrim — no blur, no rgba literal */
}
.dm-modal__dialog {
  position: relative;
  z-index: 1;
  inline-size: min(92vw, 420px);
  padding: var(--space-lg);
  background-color: var(--bb-warmwhite);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: 4px 4px 0 0 var(--bb-black);     /* hard offset — never blur */
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.dm-modal__title {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-xl);
  color: var(--bb-frame);
}
.dm-modal__text { color: var(--bb-frame); }
.dm-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-md);
  align-items: center;
}
.dm-modal__cancel {
  min-block-size: 44px;
  padding-inline: var(--space-sm);
  color: var(--bb-berry);
  font-family: var(--font-condensed);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: underline;                  /* a plain text action — never an outline-only button */
}
.dm-modal__cancel:hover { color: var(--bb-frame); }

/* ── small screens ───────────────────────────────────────────────────────────────────────────── */

@media (max-width: 576px) {
  .dm-row { padding: var(--space-sm) var(--space-md); }
  .dm-row__handle { display: none; }           /* keep rows to one clean line on narrow phones */
  .dm-row__actions { padding-inline-start: 0; inline-size: 100%; justify-content: flex-end; }
  .dm-msg { max-inline-size: 90%; }
  .dm-thread__handle { display: none; }
}

/* ── P3A: block + mute ───────────────────────────────────────────────────────────────────────────
   Same laws as the rest of this file: --bb-* tokens only, hard 2px borders and hard OFFSET shadows
   (never a blur), brightness-only hovers, and --bb-radius on surfaces. */

/* The "Blocked accounts" entry point under the folder tabs — quiet, secondary to Inbox/Requests. */
.dm__manage { margin-block: 0 var(--space-md); }
.dm__manage-link {
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  color: var(--bb-breadcrumb);
}
.dm__manage-link:hover { color: var(--bb-gold); }

/* The blocked-accounts page's standing note — states plainly that unblocking restores no follows. */
.dm__blocked-note {
  margin-block: 0 var(--space-md);
  padding: var(--space-sm) var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-gold);
  border-radius: var(--bb-radius);
  color: var(--bb-warmwhite);
  font-size: var(--fs-sm);
}

/* A blocked row reuses the inbox row shell; only the action column differs. */
.dm-row--blocked { align-items: center; }

/* Mute toggle — a small, quiet control in the thread header. Pushed to the far end so it never
   competes with the counterpart's name. */
.dm-thread__mute { margin-inline-start: auto; }
.dm-mute-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: 44px;
  min-inline-size: 44px;
  padding-inline: var(--space-sm);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  color: var(--bb-warmwhite);
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: filter var(--transition-fast);
}
.dm-mute-btn:hover { filter: brightness(1.12); }         /* the button hover law — brightness ONLY */
.dm-mute-btn:active { box-shadow: inset 2px 2px 0 0 var(--bb-black); }
.dm-mute-btn:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }
/* Gold = active state, exactly as everywhere else in the system. */
.dm-mute-btn.is-muted {
  background-color: var(--bb-gold);
  color: var(--bb-frame);
}

.dm-thread__muted-note {
  margin-block: 0 var(--space-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: var(--bb-breadcrumb);
}

/* The BLOCKER's banner. Only ever rendered for the member who did the blocking — the blocked party
   sees the ordinary read-only thread and a neutral notice, never this. */
.dm-thread__blocked {
  margin-block-start: var(--space-md);
  padding: var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: 4px 4px 0 0 var(--bb-black);     /* hard offset — never blur */
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.dm-thread__blocked-text { color: var(--bb-warmwhite); }

/* ── P3B: reporting + the message footer row ─────────────────────────────────────────────────────
   Same laws as the rest of this file: --bb-* tokens only (zero hex), hard 2px borders and hard OFFSET
   shadows (never a blur), brightness-only hovers, --bb-radius on surfaces, gold = active. */

/* The bubble's bottom line: timestamp + the Report control, instead of the control stacking under the
   text. CENTRE-aligned, not baseline — the Report button carries a 44px touch target, and aligning a
   44px box to a one-line timestamp's baseline leaves the two visibly off each other. */
.dm-msg__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  margin-block-start: var(--space-xs);
}

/* The timestamp carries `align-self: flex-end` from P2, when the BUBBLE was the flex container and the
   time was meant to sit at its bottom. Inside this row that self-alignment wins over the row's own
   `center` and pins the timestamp 10px below the Report control. Neutralised here rather than by
   editing the P2 rule, so nothing else that renders a timestamp is affected. */
.dm-msg__foot .dm-msg__time { align-self: center; }

/* Report — a quiet text action, not a button competing with the message. Deliberately low-key: it
   should be findable when needed and invisible the rest of the time. */
.dm-msg__report-btn {
  /* inline-flex + centre so the LABEL sits in the middle of the 44px target rather than at its top,
     which is what makes it line up with the timestamp beside it. */
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;                        /* WCAG 2.5.5 — the target, not the ink */
  padding-inline: var(--space-xs);
  /* P10 — this used to declare the berry token, which sat at 1.55:1 on the plum received bubble the
     control lives on. Inherits the bubble's own foreground now, exactly like the timestamp beside it.
     (Named in prose, not written as a value — see the note on .dm-msg__action-btn.) */
  color: inherit;
  opacity: 0.85;
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: underline;                  /* a plain text action — never an outline-only button */
  cursor: pointer;
  transition: opacity var(--transition-fast), filter var(--transition-fast);
}
.dm-msg__report-btn:hover { filter: brightness(1.12); opacity: 1; }
.dm-msg__report-btn:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }

/* Already reported — a settled state, not a second invitation. Matches the button's height so the two
   states of the same control don't make adjacent bubbles different sizes. */
.dm-msg__reported {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: inherit;                              /* P10 — see .dm-msg__report-btn */
  opacity: 0.75;
}

/* The wellbeing line, shown for the selfharm reason. Gold-bordered like the other standing notices, so
   it reads as information rather than as an alarm. Copy comes from System_Parameters, never from CSS. */
.dm-support {
  margin-block: var(--space-sm) 0;
  padding: var(--space-sm) var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-gold);
  border-radius: var(--bb-radius);
  color: var(--bb-warmwhite);
  font-size: var(--fs-sm);
}

/* ── the report fields inside the shared modal ─────────────────────────────────────────────────── */

.dm-modal__report {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.dm-modal__label {
  color: var(--bb-frame);
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Field surfaces match the composer exactly — warm-white fill, hard border, square-ish --bb-radius. */
.dm-modal__select,
.dm-modal__textarea {
  inline-size: 100%;
  padding: var(--space-sm) var(--space-md);
  background-color: var(--bb-warmwhite);
  color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  font-size: var(--fs-md);
  line-height: 1.5;
  min-block-size: 44px;
}
.dm-modal__textarea { max-block-size: 140px; }
.dm-modal__select:focus-visible,
.dm-modal__textarea:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }

/* Inside the modal the dialog is warm-white, so the note inverts: light panel, mulberry ink, gold edge. */
.dm-modal__support {
  padding: var(--space-sm) var(--space-md);
  background-color: var(--bb-dimpink);
  border: 2px solid var(--bb-gold);
  border-radius: var(--bb-radius);
  color: var(--bb-frame);
  font-size: var(--fs-sm);
}

/* "Also block this person" — the combined action. The whole row is the label, so the 44px target
   covers the text as well as the box. */
.dm-modal__check {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-block-size: 44px;
  color: var(--bb-frame);
  font-size: var(--fs-sm);
  cursor: pointer;
}
.dm-modal__check-input {
  inline-size: 20px;
  block-size: 20px;
  accent-color: var(--bb-berry);
  cursor: pointer;
}
.dm-modal__check-input:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }

@media (max-width: 576px) {
  /* On a narrow bubble the timestamp and Report stop sharing a line rather than crushing together. */
  .dm-msg__foot { flex-wrap: wrap; gap: var(--space-sm); }
}

/* ── P5: presence, typing, reconnect + the appear-offline toggle ─────────────────────────────────
   Same laws as the rest of this file: --bb-* tokens only (ZERO hex), hard 1–2px borders, no blur, no
   glow, no radius on surfaces, gold = active, brightness-only hovers. Presence is a small HARD-EDGED
   status square (a status LED, not a soft glowing dot): mint = online, gold = away, and offline is an
   unfilled outline — with the text label beside it always carrying the state too, so colour is never
   the only cue (a11y). */

.dm-presence {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}
.dm-presence__dot {
  inline-size: 10px;
  block-size: 10px;
  border: 1px solid var(--bb-black);
  background-color: transparent;               /* offline = unfilled outline */
}
.dm-presence[data-state="online"] .dm-presence__dot { background-color: var(--bb-mint); }
.dm-presence[data-state="away"]   .dm-presence__dot { background-color: var(--bb-gold); }
.dm-presence__label {
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  color: var(--bb-breadcrumb);
}
.dm-presence[data-state="online"] .dm-presence__label { color: var(--bb-mint); }
.dm-presence[data-state="away"]   .dm-presence__label { color: var(--bb-gold); }

/* The peer's transient typing signal — quiet mono text under the panel; reserved line-height so its
   appearance never shoves the composer around mid-keystroke. */
.dm-typing {
  margin-block: var(--space-xs) 0;
  min-block-size: 20px;
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  color: var(--bb-breadcrumb);
}

/* The connection-state affordance: visible only while the socket is down/reconnecting. Gold on the
   candy field reads as "attention, not alarm"; it never blocks anything (the form POST still works). */
.dm-rt-hint {
  margin-block: var(--space-xs) 0;
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  color: var(--bb-gold);
}

/* The manage strip grew a second control (the presence toggle) — same quiet row, spread apart. */
.dm__manage {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}

/* "Appear offline" — the same quiet control language as the thread's Mute button (frame fill, hard
   border, brightness hover, pressed-in active, GOLD = on). */
.dm-appear-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: 44px;                        /* WCAG 2.5.5 touch target */
  padding-inline: var(--space-sm);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  color: var(--bb-warmwhite);
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: filter var(--transition-fast);
}
.dm-appear-btn:hover { filter: brightness(1.12); }        /* the button hover law — brightness ONLY */
.dm-appear-btn:active { box-shadow: inset 2px 2px 0 0 var(--bb-black); }
.dm-appear-btn:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }
.dm-appear-btn.is-on {
  background-color: var(--bb-gold);
  color: var(--bb-frame);
}

/* ── P6 · NUDGE ──────────────────────────────────────────────────────────────────────────────────
   A nudge is an interruption, so its control is deliberately the QUIETEST thing in the composer area:
   a small, low-emphasis button in the established Mute/Appear-offline language (frame fill, hard 2px
   border, brightness-only hover, pressed-in active) — never a second CTA competing with the gold Send.
   The buzz itself is transform-only, so it composites on the GPU and touches no layout. */

.dm-nudge {
  margin-block-start: var(--space-sm);
  display: flex;
  justify-content: flex-end;
}

.dm-nudge__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-block-size: 44px;                        /* WCAG 2.5.5 touch target */
  padding-inline: var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  color: var(--bb-warmwhite);
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: filter var(--transition-fast);
}
.dm-nudge__btn:hover { filter: brightness(1.12); }        /* the button hover law — brightness ONLY */
.dm-nudge__btn:active { box-shadow: inset 2px 2px 0 0 var(--bb-black); }
.dm-nudge__btn:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }

/* Cooling down. Muted rather than hidden, so the control's absence is never mistaken for the feature
   not existing — and the label carries the countdown, so the state is never colour-only. */
.dm-nudge__btn:disabled {
  cursor: not-allowed;
  filter: none;
  opacity: 0.55;
}

.dm-nudge__icon { font-size: var(--fs-md); line-height: 1; }

/* The inline nudge row in the transcript. A nudge belongs to the THREAD, not to a side, so its row drops
   the bubble entirely — centred, no fill, no border, no padding — and the marker below is the only
   visual. The row keeps its foot (timestamp + Report), because a nudge stays a real, reportable message. */
.dm-msg--nudge {
  align-self: center;
  max-inline-size: 100%;
  padding: 0;
  border: none;
  background-color: transparent;
  color: var(--bb-frame);
  align-items: center;
}

.dm-msg__body--nudge {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  background-color: var(--bb-dimpink);
  border: 1px solid var(--bb-black);
  border-radius: var(--bb-radius);
  color: var(--bb-frame);
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dm-msg__nudge-icon { font-size: var(--fs-md); line-height: 1; }

/* THE BUZZ. The classic MSN shake, as a short transform-only wobble on the thread panel. Transform and
   opacity only — no layout properties, no blurred shadow, nothing that would reflow the page while a
   member is reading it. */
@keyframes dm-buzz {
  0%, 100% { transform: translate3d(0, 0, 0); }
  10%      { transform: translate3d(-6px, 2px, 0); }
  20%      { transform: translate3d(5px, -3px, 0); }
  30%      { transform: translate3d(-5px, -2px, 0); }
  40%      { transform: translate3d(4px, 3px, 0); }
  55%      { transform: translate3d(-3px, 1px, 0); }
  70%      { transform: translate3d(2px, -1px, 0); }
  85%      { transform: translate3d(-1px, 0, 0); }
}
.dm-thread__panel.is-buzzing { animation: dm-buzz 600ms ease-in-out 1; }

/* The calm indicator — the reduced-motion branch's only visible signal, and also what a member sees
   when the shake has finished. Static by construction: no animation, no transition. */
.dm-buzz-note {
  margin-block-start: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background-color: var(--bb-gold);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  color: var(--bb-frame);
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: center;
}

/* MANDATORY (§5.4): a member who has asked for reduced motion never gets the shake. The global reset
   already neuters animation DURATION site-wide, and the JS refuses to add the class at all — this rule
   is the third, belt-and-braces layer, so no future edit to either can quietly reintroduce movement. */
@media (prefers-reduced-motion: reduce) {
  .dm-thread__panel.is-buzzing { animation: none; }
}

/* ── P8 · EMOJI — the composer picker + inline thread rendering ──────────────────────────────────
   Same laws as the rest of this file: --bb-* tokens only (ZERO hex), hard 1–2px borders, no blur, no
   glass, square corners on surfaces, gold = active/selected, brightness-only hovers. The PANEL is a
   SURFACE (flat warm-white fill, hard border, hard OFFSET shadow — never beveled: bevel only what is
   pressed); the emoji TILES are the pressed things, so they carry the raised-bevel / pressed-in
   treatment of every other pressable in the system. */

/* The under-composer tools row: picker left, nudge right. The nudge keeps its own P6 styling; only
   its row placement moves in here (margin-inline auto keeps it right even when it is the only child). */
.dm-tools {
  margin-block-start: var(--space-sm);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-md);
  position: relative;              /* the picker panel anchors to this row, never the viewport */
}
.dm-tools .dm-nudge { margin-block-start: 0; margin-inline-start: auto; }

/* The trigger — the established quiet-control language (Mute / Appear-offline / Nudge): frame fill,
   hard 2px border, brightness-only hover, pressed-in active, GOLD when the panel is open. A <summary>,
   so it is keyboard-operable (Enter/Space) with no JS at all. NOTE: .dm-picker itself is deliberately
   NOT positioned — the panel's containing block must be the full-width .dm-tools row (positioned
   above), or the absolute panel would size against the ~110px trigger instead of the row. */
.dm-picker__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-block-size: 44px;                        /* WCAG 2.5.5 touch target */
  padding-inline: var(--space-md);
  background-color: var(--bb-frame);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  color: var(--bb-warmwhite);
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: filter var(--transition-fast);
}
.dm-picker__trigger:hover { filter: brightness(1.12); }   /* the button hover law — brightness ONLY */
.dm-picker__trigger:active { box-shadow: inset 2px 2px 0 0 var(--bb-black); }
.dm-picker__trigger:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }
.dm-picker[open] .dm-picker__trigger {
  background-color: var(--bb-gold);            /* gold = the open/active state, as everywhere */
  color: var(--bb-frame);
}
.dm-picker__icon { font-size: var(--fs-md); line-height: 1; }

/* The panel — a SURFACE, opening ABOVE the trigger so it sits by the composer rather than pushing the
   page down. Anchored to the tools row, capped and internally scrolling, so it can never overflow the
   viewport at 375px. */
.dm-picker__panel {
  position: absolute;
  inset-block-end: calc(100% + var(--space-sm));
  inset-inline-start: 0;
  inline-size: min(100%, 360px);
  min-inline-size: min(100%, 280px);
  max-block-size: 320px;
  overflow-y: auto;
  padding: var(--space-md);
  background-color: var(--bb-warmwhite);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: 4px 4px 0 0 var(--bb-black);     /* hard offset — never blur */
  z-index: 100;                                /* dropdowns tier (the z-index layer convention) */
}

/* The P7 admin's free-form category, as a quiet group label. */
.dm-picker__cat {
  color: var(--bb-berry);
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-block: var(--space-sm) var(--space-xs);
}
.dm-picker__cat:first-child { margin-block-start: 0; }

.dm-picker__grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

/* A tile is PRESSED, so it is beveled — the reaction-chip treatment (raised bevel at rest, brightness
   hover, reversed pressed-in on :active), at the 44px touch-target size around a 32px sprite. */
.dm-picker__tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 44px;
  block-size: 44px;                            /* WCAG 2.5.5 — the target, not the ink */
  background-color: var(--bb-dimpink);
  border: 1px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow:                                  /* hard 2px raised bevel — zero blur */
    inset 2px 2px 0 0 rgba(255, 255, 255, 0.85),
    inset -2px -2px 0 0 rgba(14, 10, 12, 0.22);
  cursor: pointer;
}
.dm-picker__tile:hover { filter: brightness(1.12); }      /* the button hover law — brightness ONLY */
.dm-picker__tile:active {
  box-shadow: inset 2px 2px 0 0 rgba(14, 10, 12, 0.32), inset -2px -2px 0 0 rgba(255, 255, 255, 0.5);
  transform: translateY(1px);
}
.dm-picker__tile:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }

/* THE SPRITE RENDERER — the same .rx-sprite mechanism as the reaction bar (social.css owns the
   canonical copy; this page does not load social.css, so the ONE rule rides here too — same class,
   same inline-var contract, same steps() path, same frame-1 reduced-motion static, pinned to the
   canonical copy by a source-guard test so the two can never drift). 32px on-screen (the 64px frames
   render retina-crisp, pixelated); a message-size instance overrides to 64 below. */
.rx-sprite {
  display: inline-block;
  inline-size: 32px;
  block-size: 32px;
  background-repeat: no-repeat;
  background-size: 32px auto;
  background-position-y: 0;
  image-rendering: pixelated;
  animation-name: rx-play;
  animation-duration: var(--rx-dur, 0.8s);
  animation-timing-function: steps(var(--rx-frames, 1));
  animation-iteration-count: infinite;
}
@keyframes rx-play {
  from { background-position-y: 0; }
  to   { background-position-y: var(--rx-shift, 0px); }
}
@media (prefers-reduced-motion: reduce) {
  /* The spec's free static fallback: frame 1 of the same sheet. */
  .rx-sprite { animation: none; background-position-y: 0; }
}

/* The in-thread emoji: message-sized (64px — the sheet's native frame, 1:1 pixel-crisp; deliberately
   larger than a 32px reaction chip, MSN-style §5.4), inside the ordinary mine/theirs bubble. The
   inline --rx-shift is computed server-side for THIS display size. */
.dm-msg__emoji.rx-sprite {
  inline-size: 64px;
  block-size: 64px;
  background-size: 64px auto;
}
.dm-msg__body--emoji {
  display: flex;
  padding-block: var(--space-xs);
}

/* P8.1 — an emoji INSIDE a sentence. 32px is an exact 2:1 downscale of the 64px sheet, so nearest
   neighbour drops whole pixels and the art stays crisp (any in-between size, 36 or 48, samples the
   sheet unevenly and muddies it). Against 16px body text it reads at roughly the MSN proportion.
   A message that is ONLY emoji does NOT use this rule — it renders at the native 64px through
   .dm-msg__emoji above, so a one-emoji message looks exactly like a P8 emoji message.
   vertical-align: middle centres the sprite on the text's own middle; the line box grows to fit it,
   which is correct — an inline image is part of the line, not an overlay on it. */
.dm-msg__inline-emoji.rx-sprite {
  inline-size: 32px;
  block-size: 32px;
  background-size: 32px auto;
  vertical-align: middle;
  /* Keeps a run of emoji from drifting apart, and stops one at the start of a line from indenting it. */
  margin-inline: 1px;
}

/* The neutral fallback for an unresolvable id — a quiet fact, never a broken image. */
.dm-msg__body--emoji-missing {
  font-family: var(--font-mono);
  font-style: italic;
  opacity: 0.75;
}

@media (max-width: 576px) {
  /* The panel hugs the tools row's full width on a narrow phone — the row lives inside the page
     container, so this can never push horizontal overflow. */
  .dm-picker__panel { inline-size: 100%; min-inline-size: 100%; max-block-size: 260px; }
}

/* ── P9: read receipts, per-message actions, the doll-share card + its picker ────────────────────
   Same laws as the rest of this file: --bb-* tokens only (ZERO hex), hard 1–2px borders, hard offset
   shadows, no blur, no glow, no radius beyond --bb-radius, brightness-only hovers, 44px targets.

   Per §5.1 these controls are deliberately UNDER-STYLED: the deferred thread-UI design pass will
   rework per-message affordances (the report icon, row spacing, the vibration), so chrome invested
   here would be thrown away. They are quiet, legible, reachable, and no more. */

/* The read receipt. Quiet by construction — VT323 at the small size, muted, and only ever ONE of them
   visible per thread. It sits in the footer beside the timestamp, not inside the bubble: it is
   metadata about the message, not part of what was said.

   ── P10 BUG FIX: THIS WAS INVISIBLE, AND THE ARITHMETIC SAYS SO ────────────────────────────────────
   It used to declare `color: var(--bb-berry)`. "Seen" renders on the caller's OWN messages and only
   those, and `.dm-msg--mine` is filled with `var(--bb-berry)` — the SAME token. Berry ink on a berry
   bubble is a contrast ratio of 1.00:1: not "hard to read", not "low contrast", but literally the same
   colour, on every sent message, always. That is why the marker could never be found.

   The fix is to stop overriding at all. `.dm-msg__time` sits immediately beside this element, declares
   no colour of its own, and has always been perfectly legible — because it inherits the foreground its
   BUBBLE established (`--bb-warmwhite` on both mine and theirs, `--bb-frame` on a purged row). Doing
   the same here is not a colour choice; it is deferring to the one element that already owns the
   contract, so this can never again disagree with the surface it is drawn on — including any bubble
   variant added later. Measured against the shipped tokens: 6.4:1 on berry, 9.8:1 on the plum panel
   (WCAG AA needs 4.5:1). The opacity keeps it quieter than the message body without touching hue. */
.dm-msg__seen {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: inherit;                              /* the bubble's own --bb-* foreground — see above */
  opacity: 0.8;
  white-space: nowrap;
}

/* Unsend + Delete-for-me. Identical treatment to the Report control they sit beside — a quiet text
   action, findable when wanted and invisible the rest of the time — so the footer reads as one row of
   peers rather than a toolbar. The form wrapper carries no box of its own. */
.dm-msg__action { display: inline-flex; }

/* P10 — same fix, same reason as .dm-msg__seen above. Unsend renders on the caller's OWN messages, so
   declaring the berry token here painted berry on berry (1.00:1 — invisible); Delete-for-me renders on
   both sides, so on a received bubble it was that same ink on the plum panel — 1.55:1, which is why
   these could only be found by hunting. Inheriting the bubble's own foreground fixes all three surfaces
   at once, including the light purged bubble (9.1:1) where a warm-white would itself be unreadable.
   (The old token is named in prose rather than written as a value: a source guard asserts it is absent
   from these rules, and a comment spelling it out would trip that guard from inside the very rule it
   protects — the trap auth3, P8.1 and P9 each recorded.) */
.dm-msg__action-btn {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;                        /* WCAG 2.5.5 — the target, not the ink */
  padding-inline: var(--space-xs);
  color: inherit;                              /* the bubble's own --bb-* foreground */
  opacity: 0.85;                               /* findable, still quieter than the message body */
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: underline;                  /* a plain text action — never an outline-only button */
  cursor: pointer;
  transition: opacity var(--transition-fast), filter var(--transition-fast);
  white-space: nowrap;
}

/* Brightness alone is the button hover law, but it is nearly a no-op on ink this close to white — so
   the emphasis lift does the visible work and the law's mechanism is kept alongside it. No colour
   swap, no glow: the control gets clearer, not different. */
.dm-msg__action-btn:hover { filter: brightness(1.12); opacity: 1; }
.dm-msg__action-btn:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }

/* The footer now holds up to five things (time, Seen, Report, Unsend, Delete). Let it wrap rather than
   squeeze, and give the actions a start-aligned group so they stay together when it does. */
.dm-msg__foot { flex-wrap: wrap; row-gap: var(--space-xs); }

/* ── the shared doll card ──────────────────────────────────────────────────────────────────────
   Mirrors the search/Discover card (Views/Search/_PostCard) — stage over body, author line, caption —
   at bubble scale. The bubble already supplies the surface, so the card itself is a flat warm-white
   panel with a hard border: no second shadow stacked inside a shadowed bubble. */
/* .dm-msg__body carries `white-space: pre-wrap` so a typed message keeps its own line breaks. That is
   wrong for a container whose child is an element: the newlines Razor emits BETWEEN the tags would
   render as visible blank space above and below the card. Reset it for this one variant. */
.dm-msg__body--doll { white-space: normal; }

.dm-doll {
  display: block;
  inline-size: min(100%, 200px);
  background-color: var(--bb-warmwhite);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  overflow: hidden;
}

.dm-doll__stage {
  display: block;
  background-color: var(--bb-canvas);          /* the doll stage colour, as on every other card */
  border-block-end: 2px solid var(--bb-black);
}

.dm-doll__stage img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  image-rendering: pixelated;                  /* pixel art, upscaled crisp — never smoothed */
}

.dm-doll__stage:hover { filter: brightness(1.12); }
.dm-doll__stage:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: -2px; }

.dm-doll__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-xs) var(--space-sm);
}

.dm-doll__author {
  font-family: var(--font-condensed);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--bb-frame);
}

a.dm-doll__author { text-decoration: underline; }
a.dm-doll__author:hover { filter: brightness(1.12); }
a.dm-doll__author:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }

.dm-doll__caption {
  font-size: var(--fs-sm);
  color: var(--bb-frame);
  overflow-wrap: anywhere;                     /* a long caption wraps rather than widening the card */
}

/* The degraded state — the post was unpublished, deleted or taken down, so the render-time lookup came
   back empty. A quiet fact in the bubble's own voice; never a broken image, never a dead link. */
.dm-doll--gone {
  font-family: var(--font-mono);
  font-style: italic;
  opacity: 0.75;
}

/* ── the share picker ──────────────────────────────────────────────────────────────────────────
   Reuses the emoji picker's disclosure, panel and trigger wholesale (.dm-picker*); only the grid and
   the tile size differ, because a doll thumbnail needs more room than a 32px sprite. */
.dm-share__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: var(--space-xs);
}

/* A tile is PRESSED, so it is beveled — the same raised-at-rest / brightness-hover / pressed-in-active
   treatment as an emoji tile, sized around a 64px thumbnail. */
.dm-share__tile {
  display: flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 44px;
  min-block-size: 44px;
  padding: 2px;
  background-color: var(--bb-canvas);
  border: 2px solid var(--bb-black);
  border-radius: var(--bb-radius);
  box-shadow: inset 2px 2px 0 0 var(--bb-warmwhite), inset -2px -2px 0 0 var(--bb-berry);
  cursor: pointer;
}

.dm-share__tile img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  image-rendering: pixelated;
}

.dm-share__tile:hover { filter: brightness(1.12); }
.dm-share__tile:active {
  box-shadow: inset 2px 2px 0 0 var(--bb-berry), inset -2px -2px 0 0 var(--bb-warmwhite);
}
.dm-share__tile:focus-visible { outline: 2px solid var(--bb-gold); outline-offset: 2px; }
