  /* ── Emoji Picker (quick reaction hover bar) ── */
  .emoji-picker {
    position: fixed; z-index: 200;
    background: var(--raised); border: 1px solid var(--border-mid);
    border-radius: var(--radius-m); padding: var(--space-sm);
    box-shadow: var(--shadow-m);
    backdrop-filter: blur(var(--blur));
    animation: fadeIn 100ms;
  }
  .emoji-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px; }
  .emoji-btn {
    width: 32px; height: 32px; border: none; background: transparent;
    border-radius: var(--radius-s); cursor: pointer; font-size: 18px;
    display: flex; align-items: center; justify-content: center;
    transition: background var(--transition);
  }
  .emoji-btn:hover { background: var(--elevated); }

  /* ── Call body inside a DM message-group (Telegram-style) ── */
  .msg-call {
    display: inline-flex; align-items: center; gap: var(--space-md);
    margin-top: 2px;
  }
  .msg-call .msg-call-icon {
    color: var(--accent);
    display: flex; align-items: center; justify-content: center;
  }
  .msg-call .msg-call-info {
    display: flex; flex-direction: column; gap: 2px;
  }
  .msg-call .msg-call-title {
    font-size: var(--font-md); font-weight: 500; color: var(--ink);
  }
  .msg-call .msg-call-status {
    font-size: var(--font-sm); color: var(--ink-muted);
    display: inline-flex; align-items: center; gap: 4px;
  }
  .msg-call .msg-call-status.declined,
  .msg-call .msg-call-status.missed { color: var(--coral); }
  .msg-call .msg-call-arrow { font-weight: 700; }
  .msg-call .msg-call-arrow.in  { color: var(--coral); }
  .msg-call .msg-call-arrow.out { color: var(--live); }

  /* ── Voice-channel call summary card (Этап 6.1) ──
     Rendered inside a centered .system-message; the inner spans must NOT
     inherit the generic .system-message span pill, so reset them first. */
  .system-message.vcall-msg span {
    padding: 0; background: none; border-radius: 0;
    font-size: inherit; color: inherit;
  }
  .msg-voice-call {
    display: inline-flex; align-items: center; gap: var(--space-md);
    max-width: 480px;
    padding: 8px 14px;
    border: 1px solid var(--border); border-radius: var(--radius-m);
    background: var(--raised);
  }
  .msg-voice-call .vcall-icon {
    flex-shrink: 0; display: flex; align-items: center; justify-content: center;
    color: var(--accent);
  }
  .msg-voice-call .vcall-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
  .msg-voice-call .vcall-title { font-size: var(--font-sm); font-weight: 600; color: var(--ink); }
  .msg-voice-call .vcall-sub { font-size: var(--font-xs); color: var(--ink-muted); }
  .msg-voice-call .vcall-avatars {
    display: inline-flex; align-items: center; margin-left: var(--space-sm);
  }
  .msg-voice-call .vcall-av {
    width: 24px; height: 24px; border-radius: 50%; overflow: hidden;
    margin-left: -6px; border: 2px solid var(--raised); flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
  }
  .msg-voice-call .vcall-av:first-child { margin-left: 0; }
  .msg-voice-call .vcall-av img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .msg-voice-call .vcall-more {
    background: var(--glass); color: var(--ink-muted);
    font-size: var(--font-2xs); font-weight: 600;
  }
  .msg-voice-call .vcall-rec {
    margin-left: var(--space-sm);
    font-size: var(--font-xs); font-weight: 600; color: var(--accent); text-decoration: none;
  }
  .msg-voice-call .vcall-rec:hover { text-decoration: underline; }
  .msg-voice-call .vcall-time {
    margin-left: var(--space-sm); font-size: var(--font-xs); color: var(--ink-ghost);
  }

  /* ── System messages (DM group events: invited/removed/left) ── */
  .system-message {
    display: flex;
    justify-content: center;
    padding: var(--space-xs) var(--space-lg);
    margin: var(--space-sm) 0;
  }
  .system-message span {
    padding: 4px 10px;
    background: var(--glass);
    border-radius: var(--radius-pill);
    font-size: var(--font-xs);
    color: var(--ink-ghost);
  }

  /* ── Message edit / reply / receipts ── */
  /* "ред." indicator now uses .kc-pill.kc-pill--edited (components.css) */
  .msg-reply-quote {
    display: flex; flex-direction: column; gap: 2px;
    padding: 2px 0 2px 10px; margin-bottom: var(--space-xs);
    border-left: 3px solid var(--accent);
    background: transparent; cursor: pointer;
    font-size: var(--font-sm); color: var(--ink);
    line-height: 1.4;
  }
  .msg-reply-quote .reply-author { color: var(--ink); font-weight: 600; }
  .msg-reply-quote .reply-text { color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

  /* Tiny pin glyph rendered inline at the end of .msg-text, mirroring
     the .kc-pill--edited "ред." marker. Muted ink, sits flush after
     the body and the optional "ред." pill. The strip on top + side
     panel carry the full attribution; the bubble only needs a hint. */
  .msg-pin-mark {
    display: inline-flex; align-items: center;
    margin-left: 4px;
    color: var(--ink-muted);
    vertical-align: middle;
    line-height: 1;
  }
  .msg-pin-mark svg { flex: 0 0 auto; }

  /* Forwarded-from snapshot block (Telegram-style "↗ Переслано из X — Author"
     attribution + the original body in a left-bordered quote). Receivers
     click the badge to jump to the original via kcGoToOriginal. The badge
     row sits above the body so the visual order matches reading order. */
  .msg-forwarded-from {
    display: flex; align-items: center; gap: 6px;
    margin-top: var(--space-xs);
    padding: 2px 0 2px 10px;
    border-left: 3px solid var(--accent);
    font-size: var(--font-xs); color: var(--ink-muted);
    cursor: pointer;
    line-height: 1.4;
  }
  .msg-forwarded-from:hover { color: var(--ink); }
  .msg-forwarded-arrow {
    color: var(--accent);
    display: inline-flex;
    align-items: center;
  }
  .msg-forwarded-author {
    font-weight: 600; color: var(--ink);
    margin-left: 4px;
  }
  .msg-forwarded-body {
    padding: 2px 0 2px 10px;
    margin-bottom: var(--space-xs);
    border-left: 3px solid var(--accent);
    color: var(--ink);
    line-height: 1.5;
    word-wrap: break-word;
    white-space: pre-wrap;
  }

  .msg-highlight { animation: kcHighlight 1.5s ease; }
  @keyframes kcHighlight {
    0%   { background: var(--accent-soft); }
    40%  { background: color-mix(in srgb, var(--accent) 18%, transparent); }
    100% { background: transparent; }
  }
  .msg-read-status {
    font-size: var(--font-xs);
    line-height: 1;
    margin-left: 4px;
    color: var(--ink-ghost);
    transition: color var(--transition);
    user-select: none;
    -webkit-user-select: none;
  }
  .msg-read-status.read { color: var(--accent); }
  .msg-status {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 14px;
    margin-left: 4px;
    font-size: var(--font-xs);
    line-height: 1;
    user-select: none;
    -webkit-user-select: none;
  }
  .msg-status--sending { color: var(--ink-ghost); }
  .msg-status--failed {
    color: var(--coral);
    cursor: pointer;
    border-radius: var(--radius-s);
    transition: background var(--transition);
  }
  .msg-status--failed:hover { background: var(--glass-hover); }
  .composer-edit-bar {
    display: flex; align-items: flex-start; gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
    background: transparent;
    border-bottom: 1px solid var(--border);
    font-size: var(--font-sm);
    color: var(--ink);
  }
  .composer-edit-bar-bar {
    flex-shrink: 0;
    width: 3px;
    align-self: stretch;
    border-radius: 2px;
    background: var(--accent);
  }
  .composer-edit-bar-body {
    flex: 1;
    min-width: 0;
    line-height: 1.35;
  }
  .composer-edit-bar-title {
    color: var(--accent);
    font-weight: 600;
    font-size: var(--font-sm);
    margin-bottom: 2px;
  }
  .composer-edit-bar-text {
    color: var(--ink-secondary);
    font-size: var(--font-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    word-break: break-word;
  }
  .composer-edit-bar-text > * { display: inline; margin: 0; }
  .composer-edit-bar-text strong { font-weight: 700; }
  .composer-edit-bar-text em { font-style: italic; }
  .composer-edit-bar-text code {
    font-family: 'SF Mono', 'Fira Code', monospace; font-size: var(--font-sm);
    background: var(--surface); padding: 1px 5px; border-radius: 4px;
  }
  .composer-edit-bar-lp {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 4px;
  }
  .composer-edit-bar-lp-thumb {
    flex-shrink: 0;
    width: 40px; height: 40px;
    border-radius: var(--radius-s);
    background: var(--surface);
    overflow: hidden;
    display: flex; align-items: center; justify-content: center;
  }
  .composer-edit-bar-lp-thumb img {
    width: 100%; height: 100%;
    object-fit: cover;
  }
  .composer-edit-bar-lp-meta {
    flex: 1; min-width: 0;
    line-height: 1.3;
  }
  .composer-edit-bar-lp-title {
    color: var(--ink);
    font-weight: 600;
    font-size: var(--font-sm);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .composer-edit-bar-lp-desc {
    color: var(--ink-muted);
    font-size: var(--font-sm);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  .composer-edit-bar-atts {
    margin-top: 6px;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .composer-edit-bar .cancel-edit {
    flex-shrink: 0;
    width: 24px; height: 24px;
    display: flex; align-items: center; justify-content: center;
    background: none; border: none; border-radius: 50%;
    color: var(--ink-muted); cursor: pointer;
    transition: color var(--transition), background var(--transition);
  }
  .composer-edit-bar .cancel-edit:hover { color: var(--ink); background: var(--glass-hover); }
  .composer-reply-bar {
    display: flex; align-items: center; gap: var(--space-md);
    padding: var(--space-sm) var(--space-lg);
    background: var(--raised); border-radius: var(--radius-m);
    margin: 0 var(--space-lg) var(--space-xs);
  }
  .composer-reply-bar .reply-icon {
    flex-shrink: 0; display: inline-flex; color: var(--accent);
  }
  .composer-reply-bar .reply-body {
    flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px;
  }
  .composer-reply-bar .reply-title {
    color: var(--accent); font-weight: 600; font-size: var(--font-sm); line-height: 1.3;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .composer-reply-bar .reply-text {
    color: var(--ink); font-size: var(--font-sm); line-height: 1.3;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .composer-reply-bar .cancel-reply {
    flex-shrink: 0; display: flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; border-radius: 50%;
    background: transparent; border: none; color: var(--ink-ghost);
    cursor: pointer; padding: 0;
    transition: color var(--transition), background var(--transition);
  }
  .composer-reply-bar .cancel-reply:hover { color: var(--ink); background: var(--glass); }

  /* ── Markdown in messages ── */
  .msg-text strong { font-weight: 700; }
  .msg-text em { font-style: italic; color: var(--ink-secondary); }

  /* P1.8 — custom emoji inline. 22px matches a 2-line text height
     for the body row, same convention Slack/Discord land on. The
     compact size keeps line height consistent with regular text. */
  .kc-custom-emoji {
    display: inline-block;
    vertical-align: middle;
    width: 22px;
    height: 22px;
    margin: -2px 1px 0;
    object-fit: contain;
  }
  .msg-text pre {
    background: var(--deep); padding: var(--space-md); border-radius: var(--radius-s);
    margin-top: var(--space-xs); overflow-x: auto; font-family: 'SF Mono', 'Fira Code', monospace;
    font-size: var(--font-sm); line-height: 1.5; color: var(--ink-secondary);
  }
  .msg-text a { color: var(--accent); text-decoration: none; }
  .msg-text a:hover { text-decoration: underline; }
  .msg-text blockquote {
    border-left: 3px solid var(--border-mid); padding-left: var(--space-md);
    color: var(--ink-muted); margin: var(--space-xs) 0;
  }
  .msg-text u { text-decoration: underline; text-underline-offset: 2px; }
  .msg-text .kc-list {
    margin: var(--space-xs) 0; padding-left: var(--space-lg);
  }
  .msg-text .kc-list li { margin: 2px 0; }

  /* ── Empty states ── */
  .empty-state {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    padding: 60px var(--space-xl); text-align: center; flex: 1;
  }
  .empty-state-icon {
    width: 80px; height: 80px; border-radius: var(--radius-xl);
    background: var(--raised); display: flex; align-items: center; justify-content: center;
    margin-bottom: var(--space-xl); color: var(--ink-ghost);
  }
  .empty-state h3 { font-size: 18px; font-weight: 600; color: var(--ink-secondary); margin-bottom: var(--space-sm); }
  .empty-state p { font-size: var(--font-sm); color: var(--ink-muted); max-width: 320px; line-height: 1.5; }

  /* ── Delete confirm ── */
  .delete-confirm {
    position: fixed; inset: 0; z-index: 250; display: none;
    align-items: center; justify-content: center;
    background: rgba(0,0,0,0.6); backdrop-filter: blur(2px);
  }
  .delete-confirm.visible { display: flex; }
  .delete-confirm-box {
    background: var(--raised); border: 1px solid var(--border-mid); border-radius: var(--radius-xl);
    padding: var(--space-xl); max-width: 400px; width: 100%;
    box-shadow: var(--shadow-l); animation: slideUp 200ms ease;
    backdrop-filter: blur(var(--blur));
  }
  .delete-confirm-box h4 { font-size: var(--font-lg); font-weight: 700; color: var(--ink); margin-bottom: var(--space-sm); }
  .delete-confirm-box p { font-size: var(--font-sm); color: var(--ink-muted); margin-bottom: var(--space-xl); line-height: 1.5; }
  .delete-confirm-actions { display: flex; gap: var(--space-sm); justify-content: flex-end; }
  /* `.btn-cancel-del` / `.btn-confirm-del` are legacy aliases for
     `.btn-ghost` / `.btn-danger` — full styles live in components.css. */

  /* ── Code blocks ── */
  .kc-code-block {
    margin: var(--space-xs) 0;
    border-radius: var(--radius-m);
    overflow: hidden;
    border: 1px solid var(--glass);
    max-width: 100%;
  }
  .kc-code-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 6px 12px;
    background: #1a1d23;
    border-bottom: 1px solid var(--glass);
    user-select: none;
  }
  .kc-code-lang {
    font-size: var(--font-2xs); font-family: monospace; color: #abb2bf;
    text-transform: uppercase; letter-spacing: 0.05em;
  }
  .kc-code-actions { display: flex; align-items: center; gap: 6px; }
  .kc-code-wrap-btn {
    background: none; border: none; cursor: pointer; padding: 2px 6px;
    color: #abb2bf; font-size: var(--font-2xs); border-radius: 3px;
    transition: background var(--transition);
  }
  .kc-code-wrap-btn:hover { background: var(--color-white-12); color: #e6e6e6; }
  .kc-code-wrap-btn.active { color: var(--accent); }
  .kc-code-copy {
    background: none; border: none; cursor: pointer; padding: 2px 8px;
    color: #abb2bf; font-size: var(--font-2xs); border-radius: 3px;
    transition: background var(--transition), color var(--transition);
  }
  .kc-code-copy:hover { background: var(--color-white-12); color: #e6e6e6; }
  .kc-code-copy.copied { color: #98c379; }
  .kc-code-pre {
    margin: 0; padding: var(--space-md) var(--space-lg); background: #282c34;
    overflow-x: auto; font-size: var(--font-sm); line-height: 1.6;
    font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
  }
  .kc-code-pre.wrap-lines code { white-space: pre-wrap; word-break: break-all; }
  .kc-code-pre code.hljs { padding: 0; background: transparent; font-size: inherit; }

  /* Soft syntax highlight — pastel role-coloring instead of the default rainbow.
     Five gentle hues built around the DS pastel palette:
       keywords/operators → soft blurple
       strings/regexp     → soft sage
       numbers/literals   → soft amber
       functions/titles   → soft cyan
       comments           → muted gray italic
     Default text stays ink. */
  .hljs { color: var(--ink) !important; }

  .hljs-keyword, .hljs-selector-tag, .hljs-built_in,
  .hljs-meta-keyword, .hljs-operator, .hljs-tag {
    color: #a5b4fc !important;
    font-weight: normal;
  }
  .hljs-string, .hljs-meta .hljs-string, .hljs-regexp, .hljs-link,
  .hljs-template-variable, .hljs-template-tag {
    color: var(--pastel-olive) !important;
  }
  .hljs-number, .hljs-literal, .hljs-symbol, .hljs-bullet {
    color: var(--pastel-sand) !important;
  }
  .hljs-title, .hljs-section, .hljs-name,
  .hljs-class .hljs-title, .hljs-function .hljs-title,
  .hljs-attr, .hljs-attribute, .hljs-property, .hljs-type {
    color: var(--pastel-sea) !important;
  }
  .hljs-comment, .hljs-quote, .hljs-doctag, .hljs-meta .hljs-meta-string {
    color: var(--ink-muted) !important;
    font-style: italic;
  }
  .hljs-variable, .hljs-params, .hljs-punctuation { color: var(--ink) !important; }
  .hljs-deletion  { color: var(--pastel-rose) !important; }
  .hljs-addition  { color: var(--pastel-olive) !important; background: transparent !important; }
  .hljs-emphasis  { font-style: italic; }
  .hljs-strong    { font-weight: 600; }
  code.kc-inline-code {
    background: var(--color-white-12); border-radius: 3px;
    padding: 1px 5px; font-size: 0.875em;
    font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
    color: #e06c75;
  }

  /* wrap icon svg */
  .kc-wrap-icon { width:14px; height:14px; display:inline-block; vertical-align:middle; }
  .kc-copy-icon { width:14px; height:14px; display:inline-block; vertical-align:middle; }

  /* ── Link preview cards ── */
  .link-preview {
    display: flex; margin-top: 8px; max-width: 480px;
    border-radius: var(--radius-m); overflow: hidden;
    border: 1px solid var(--border); background: var(--surface);
    cursor: pointer; text-decoration: none;
    transition: border-color var(--transition);
  }
  .link-preview:hover { border-color: var(--accent); }
  .link-preview-accent { width: 4px; background: var(--accent); flex-shrink: 0; }
  .link-preview-body { padding: 10px 12px; flex: 1; min-width: 0; }
  .link-preview-site {
    display: flex; align-items: center; gap: 6px;
    font-size: var(--font-2xs); color: var(--ink-muted); margin-bottom: 4px;
  }
  .link-preview-favicon { width: 14px; height: 14px; border-radius: 2px; }
  .link-preview-title {
    font-size: var(--font-md); font-weight: 600; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    margin-bottom: 2px;
  }
  .link-preview-desc {
    font-size: var(--font-xs); color: var(--ink-muted); line-height: 1.4;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .link-preview-image {
    width: 90px; flex-shrink: 0; object-fit: cover;
    aspect-ratio: 1 / 1; background: var(--surface);
  }
  .link-preview-image.kc-loaded { background: transparent; }
  .link-preview-image-large {
    width: 100%; height: 200px; object-fit: cover; display: block;
    /* Fixed-height reservation so layout never reflows after image decode (Safari jitters
       otherwise when aspect-ratio is dropped post-load). */
    background: var(--surface);
  }
  .link-preview-image-large.kc-loaded { background: transparent; }
  .link-preview-large {
    flex-direction: column;
  }
  .link-preview-large .link-preview-body { order: 2; }

  /* Discord-style inline video. The host wraps the preview thumbnail
     while the user is browsing; on play it swaps to an <iframe> in
     place. The host carries the SAME aspect-ratio in both states so
     the surrounding card doesn't reflow on mount — and image preview
     matches the eventual player size pixel-for-pixel.
       Default 16/9 covers regular YouTube + Kinescope before the m3u8
     manifest resolves; _kcSetInlineAspect overrides inline when the
     real source aspect is something else (vertical / 4:3 / ultrawide).
       max-height caps how tall a wide-aspect card can grow so messages
     don't dominate the channel feed (Discord behaviour). For vertical
     sources the wrap carries data-aspect-mode="vertical" — we flip to
     a height-anchored layout so the card stays a narrow vertical strip
     instead of stretching to the full message column. The flag is set
     for YouTube Shorts at parse time and for any Kinescope source whose
     m3u8 manifest reports portrait dimensions. */
  .link-preview-image-host {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 400px;
    background: var(--surface);
    overflow: hidden;
  }
  .link-preview-wrap[data-aspect-mode="vertical"] .link-preview-image-host {
    width: auto;
    height: 400px;
    aspect-ratio: 9 / 16;
    max-width: 100%;
  }
  .link-preview-image-host .link-preview-image-large {
    width: 100%; height: 100%;
  }
  .link-preview-play-overlay {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: var(--on-accent);
    background: rgba(0, 0, 0, 0.18);
    transition: background var(--transition);
    pointer-events: none;
  }
  .link-preview-image-host:hover .link-preview-play-overlay {
    background: rgba(0, 0, 0, 0.36);
  }
  .link-preview-play-overlay > svg {
    filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.55));
    opacity: 0.92;
  }
  .link-preview-image-host--playing .link-preview-play-overlay {
    display: none;
  }
  .kc-inline-player {
    width: 100%;
    height: 100%;
    display: block;
    border: 0;
    background: #000;
  }

  /* Copy-link button on link-preview cards. Sibling of <a>, not a child
     — nesting <button> inside <a> is invalid HTML. Hidden by default,
     fades in on hover of the whole preview wrap. */
  .link-preview-wrap {
    position: relative;
    /* Wrap is just a positioning anchor — the inner <a> still owns the
       max-width / margin-top, so layout is unchanged. */
    max-width: 480px;
  }
  .link-preview-copy {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm, 6px);
    background: var(--surface);
    color: var(--ink-muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity var(--transition), background var(--transition), color var(--transition);
  }
  .link-preview-wrap:hover .link-preview-copy,
  .link-preview-copy:focus-visible {
    opacity: 1;
  }
  .link-preview-copy:hover {
    /* Solid `var(--surface)` (#292929), not a translucent token — a
       transparent bg would leak the poster image through the button.
       Hover lift is carried by border + ink instead. */
    background: var(--surface);
    border-color: var(--accent);
    color: var(--ink);
  }

  /* ── Spoiler ── */
  /* Dark fallback shown before canvas is initialised (spoiler.js takes over). */
  .kc-spoiler {
    color: transparent;
    background-color: var(--deep);
    border-radius: 4px;
    padding: 0 3px;
    cursor: pointer;
    user-select: none;
    transition: color var(--transition-slow);
  }
  .kc-spoiler.revealed {
    color: inherit;
    background-color: var(--glass);
    user-select: text;
  }
  /* Composer — no canvas, text always readable for editing */
  .kc-formattable .kc-spoiler {
    color: var(--ink);
    background-color: rgba(0,0,0,0.3);
    user-select: text;
  }
