/* ==========================================================================
   css/components/message-extras.css
   Split from app.html (build 2026.10.08.9-beta), original lines 4250-4401.
   Rules are in their ORIGINAL order; load order is defined in index.html.
   ========================================================================== */
/* -- MENTIONS -------------------------------------------------- */
/* ── ICON CRISPNESS ────────────────────────────────────────────────────────────────────
   The icons are authored on a 24-unit grid but displayed at 14-16px, so the viewBox scale
   is 0.667 and a stroke-width of 2 lands on 1.333 DEVICE pixels. A 1.333px line cannot sit
   on the pixel grid, so it gets antialiased across two pixels - which is the "blurry" look.
   Measured in the running app: 70 of 83 visible icons had a fractional stroke. The handful
   drawn at 12px (scale exactly 0.5, stroke -> 1.0px) were the sharp ones, which is the
   proof of the mechanism.

   The window renders at devicePixelRatio 1 (CefSharp HwndHost: 1 CSS px == 1 device px), so
   there is no spare resolution to hide the half-pixel.

   vector-effect:non-scaling-stroke takes the stroke OUT of the viewBox transform, so the
   width below is in device pixels directly - 1px, on the grid, at every icon size. It has
   to target the shapes: vector-effect applies to geometry and does NOT inherit from <svg>.

   Deliberately NOT shape-rendering:crispEdges - that disables antialiasing outright and
   turns every curve and diagonal into a staircase. */
svg path, svg line, svg circle, svg rect, svg polyline, svg polygon, svg ellipse {
  vector-effect: non-scaling-stroke;
}
/* NO blanket stroke-width here any more. This used to be
       svg[stroke]:not([stroke="none"]) { stroke-width: 1px; }
   which overrode the width EVERY icon declares - measured live, an icon with
   `stroke-width="2.2"` computed to `1px`. The reasoning was half right: with
   non-scaling-stroke the width is in CSS pixels and 1px does land on the grid. What it misses
   is that a stroke is centred ON its path, so a 1px one straddles the boundary with half a
   pixel either side and has NO solid core - every line becomes two rows at ~50% coverage,
   which is the thin washed-out look. A 2px stroke straddles the same boundary but keeps a
   solid interior pixel, so it reads as a line rather than a smudge.
   The widths now come from the icons themselves, normalised to 2 across the 24-unit grid in
   GlobalRiftCss.css. Add nothing here without checking that file first. */

.mention { background:var(--accent-dim);color:var(--accent-bright);border-radius:3px;padding:0 3px;font-weight:600;cursor:pointer;transition:background 0.1s }
.mention:hover { background:var(--accent);color:#fff }
.mention-everyone { background:#ed424530;color:#ed4245;border-radius:3px;padding:0 3px;font-weight:600 }
.channel-link { background:var(--accent-dim);color:var(--accent-bright);border-radius:3px;padding:0 4px;font-weight:600;cursor:pointer;transition:background 0.1s;white-space:nowrap }
.channel-link:hover { background:var(--accent);color:#fff }
.mention-everyone:hover { background:#ed4245;color:#fff }

/* -- MENTION AUTOCOMPLETE -------------------------------------- */
.mention-dropdown { position:absolute;bottom:calc(100% + 4px);left:0;right:0;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:var(--radius-md);overflow-y:auto;max-height:260px;z-index:200;box-shadow:var(--shadow-lg);display:none }
.mention-dropdown.open { display:block; animation: menu-in 0.22s var(--ease-out, ease) }
.mention-item { display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;font-size:13px;transition:background 0.1s }
.mention-item:hover,.mention-item.active { background:var(--bg-hover) }
.mention-item-name { font-weight:600;color:var(--text-primary) }
.mention-item-discrim { font-size:11px;color:var(--text-muted) }

/* -- EMOJI AUTOCOMPLETE ---------------------------------------- */
.emoji-ac { position:absolute;bottom:calc(100% + 4px);left:0;right:0;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:var(--radius-md);overflow:hidden;z-index:201;box-shadow:var(--shadow-lg);display:none;flex-direction:column }
.emoji-ac.open { display:flex; animation: menu-in 0.22s var(--ease-out, ease) }
.emoji-ac-header { font-size:10px;font-weight:700;color:var(--text-muted);padding:7px 12px 4px;text-transform:uppercase;letter-spacing:.7px;border-bottom:1px solid var(--border);flex-shrink:0 }
.emoji-ac-list { overflow-y:auto;max-height:260px }
.emoji-ac-item { display:flex;align-items:center;gap:10px;padding:7px 12px;cursor:pointer;transition:background .1s }
.emoji-ac-item:hover,.emoji-ac-item.sel { background:var(--bg-hover) }
.emoji-ac-icon { font-size:22px;width:28px;text-align:center;flex-shrink:0;line-height:1 }
.emoji-ac-name { font-size:14px;color:var(--text-primary) }

/* -- ATTACHMENT PREVIEWS (above input) -------------------------- */
/* Sits flush at the TOP of the composer box (transparent so it inherits the box's fill),
   so a staged attachment reads as part of the message box - not a floating card above it. */
.attach-preview-bar { display:flex;gap:10px;padding:10px 14px 2px;flex-wrap:wrap;background:transparent; }
.attach-preview-bar:empty { display:none; }
.attach-preview-card { position:relative;border:1px solid var(--border-strong);border-radius:var(--radius-md);overflow:hidden;background:var(--bg-surface);display:flex;flex-direction:column;max-width:280px; }
.attach-preview-card img { max-width:280px;max-height:200px;object-fit:contain;display:block;background:#000; }
.attach-preview-card .attach-preview-name { font-size:12px;color:var(--text-muted);padding:5px 10px 6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
.attach-preview-file { display:flex;align-items:center;gap:10px;padding:12px 16px;min-width:200px; }
.attach-preview-file-icon { font-size:28px;flex-shrink:0; }
.attach-preview-file-info { min-width:0; }
.attach-preview-file-name { font-size:13px;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
.attach-preview-file-size { font-size:11px;color:var(--text-muted); }
.attach-preview-remove { position:absolute;top:6px;right:6px;width:24px;height:24px;border-radius:50%;background:rgba(0,0,0,.8);border:1px solid rgba(255,255,255,.15);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s; }
.attach-preview-remove:hover { background:var(--red,#ed4245); }
/* Spoiler toggle on staged uploads + blurred preview when marked */
.attach-preview-spoiler { position:absolute;top:6px;right:36px;width:24px;height:24px;border-radius:50%;background:rgba(0,0,0,.8);border:1px solid rgba(255,255,255,.15);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s; }
.attach-preview-spoiler:hover { background:var(--accent); }
.attach-preview-spoiler.on { background:var(--accent); }
.attach-preview-card.spoiler-marked img { filter: blur(14px); }
/* Uploading state: dim the card + spin an overlay so you can SEE it's in progress. */
.attach-preview-card.uploading { min-width:200px; min-height:64px; }
.attach-preview-card.uploading > :not(.attach-uploading-overlay) { opacity:.35; }
.attach-uploading-overlay { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; background:rgba(0,0,0,.35); backdrop-filter:blur(1px); }
.attach-uploading-overlay .spin { width:22px; height:22px; border-radius:50%; border:2.5px solid rgba(255,255,255,.25); border-top-color:var(--accent); animation:attachSpin .8s linear infinite; }
.attach-uploading-overlay .lbl { font-size:10.5px; font-weight:700; color:#fff; letter-spacing:.04em; }
@keyframes attachSpin { to { transform:rotate(360deg); } }
/* Drag-and-drop: a full-area drop target that appears while dragging files over chat. */
#dropOverlay { position:fixed; inset:0; z-index:100010; display:none; align-items:center; justify-content:center; background:color-mix(in srgb, var(--accent) 16%, rgba(0,0,0,.55)); backdrop-filter:blur(2px); pointer-events:none; }
#dropOverlay.show { display:flex; }
#dropOverlay .drop-card { border:2px dashed var(--accent); border-radius:16px; padding:34px 46px; text-align:center; background:var(--bg-surface); box-shadow:var(--shadow-lg); }
#dropOverlay .drop-card .di { font-size:40px; }
#dropOverlay .drop-card .dt { margin-top:8px; font-size:16px; font-weight:800; color:var(--text-primary); }
#dropOverlay .drop-card .ds { margin-top:3px; font-size:12.5px; color:var(--text-muted); }
/* Spoiler media in messages: blurred + labeled until clicked */
.spoiler-media { position:relative;display:inline-block;cursor:pointer;overflow:hidden;border-radius:8px; }
.spoiler-media:not(.revealed) > * { filter: blur(30px); pointer-events:none; }
.spoiler-media:not(.revealed)::after { content:'SPOILER'; position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,.78);color:#fff;font-size:12px;font-weight:800;letter-spacing:.08em;padding:6px 16px;border-radius:999px;pointer-events:none; }

/* -- IMAGE/VIDEO EMBED in messages -------------------------------- */
.msg-file-badge { display:inline-flex;align-items:center;gap:8px;background:var(--bg-surface);border:1px solid var(--border-strong);border-radius:8px;padding:8px 10px;margin-top:6px;max-width:420px;min-width:200px }
.msg-image-embed { margin-top:6px;max-width:400px }
/* Broken/removed image placeholder (outlined) shown when an image 404s. */
.img-broken { display:inline-flex;align-items:center;gap:8px;margin-top:6px;padding:12px 14px;border:1px dashed var(--border-strong);border-radius:var(--radius-md);background:var(--bg-surface);color:var(--text-muted);font-size:13px;font-weight:500 }
/* width/height ATTRIBUTES on the img give the intrinsic ratio before it loads, but they are
   only honoured as a RATIO if both dimensions are auto - otherwise the attribute height is
   used literally and max-width squashes the picture. With auto/auto plus the two max
   constraints the browser scales it correctly AND reserves the box up front, so the messages
   below do not move when the image arrives.
   The background makes that reserved space read as a deliberate placeholder while it fills,
   rather than a blank gap. */
.msg-image-embed img {
  max-width:100%;max-height:300px;width:auto;height:auto;
  border-radius:var(--radius-md);display:block;cursor:pointer;transition:opacity 0.1s;
  background:var(--bg-surface);
}
/* The frozen-frame canvas stands in for a GIF while the window is unfocused. Same box, so
   swapping between them cannot shift anything. */
.msg-image-embed canvas.gif-frozen { max-width:100%;max-height:300px;border-radius:var(--radius-md);display:block;cursor:pointer }
.msg-image-embed img:hover { opacity:0.9 }
.msg-image-embed video { max-width:100%;max-height:300px;border-radius:var(--radius-md);display:block;background:#000 }
.msg-image-embed audio { display:block;margin-top:4px }
.msg-image-name { font-size:11px;color:var(--text-muted);margin-top:3px }

/* -- INLINE MESSAGE EDIT --------------------------------------- */
.msg-edit-wrap { margin-top:2px }
.msg-edit-input { width:100%;background:var(--bg-elevated);border:1px solid var(--accent);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-body);font-size:14px;padding:8px 12px;resize:none;min-height:36px;max-height:200px;overflow-y:auto;box-shadow:0 0 0 2px var(--accent-glow) }
.msg-edit-hint { font-size:11px;color:var(--text-muted);margin-top:4px }
.msg-edit-hint span { color:var(--accent);cursor:pointer;text-decoration:underline }

/* -- LINK EMBEDS ----------------------------------------------- */
/* Discord's .embedFull is `background: var(--background-surface-high); border: 1px solid
   var(--border-subtle); border-inline-start: 4px solid var(--border-normal)` - a full outline
   PLUS the thick start edge, not the start edge alone. The outline is what separates it from
   the message above when the fill sits close to the background, which under a theme is most of
   the time. The 4px start keeps carrying the site's own colour (YouTube red, and so on).
   516px is Discord's width too - its .gridContainer is the only max-width on an embed. */
.link-embed { margin-top:8px;max-width:516px;border:1px solid var(--border);border-left:4px solid var(--accent);background:var(--bg-elevated);border-radius:0 var(--radius-md) var(--radius-md) 0;padding:12px;display:flex;gap:12px;align-items:flex-start }
/* 16:9, and sized for the 516px card. At 80x60 in a card that wide a short title left a
   visible empty third on the right. */
.link-embed-thumb { width:128px;height:72px;border-radius:var(--radius-sm);object-fit:cover;flex-shrink:0 }
.link-embed-body { flex:1;min-width:0 }
.link-embed-site { font-size:11px;color:var(--text-muted);margin-bottom:2px;text-transform:uppercase;letter-spacing:.6px }
.link-embed-title { font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis }
.link-embed-title a { color:var(--text-primary);text-decoration:none }
.link-embed-title a:hover { text-decoration:underline }
.link-embed-desc { font-size:12px;color:var(--text-secondary);line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden }
.link-embed-yt { border-left-color:#ff0000 }
/* Discord's video embeds are not a small side thumbnail - `.embedVideoImageComponent` is
   `width:100%`, so the preview spans the card with the text ABOVE it. That is the whole reason
   theirs reads so much bigger than a 516px card with a 128px thumb in the corner.
   column-reverse because the DOM order is [thumb, body]; reversing puts the text on top
   without touching the builder. Shorts keep the side-thumb layout - a 9:16 preview at full
   width would be a wall. */
.link-embed-yt:not(.link-embed-short) { flex-direction: column-reverse; align-items: stretch; gap: 10px; }
.link-embed-yt:not(.link-embed-short) .yt-thumb-wrap { width: 100%; }
.link-embed-yt:not(.link-embed-short) .link-embed-thumb { width: 100%; height: auto; aspect-ratio: 16 / 9; }
.link-embed-yt:not(.link-embed-short) .link-embed-title { white-space: normal; }
.link-embed-invite { border-left-color:var(--accent) }
.link-embed-profile { border-left-color:var(--green) }

/* -- ROLE BADGE / HOVER TOOLTIP -------------------------------- */
.role-badge { display:inline-flex;align-items:center;font-size:10px;font-weight:700;letter-spacing:.4px;border-radius:3px;padding:1px 5px;margin-left:4px;vertical-align:middle;cursor:default;position:relative }
.role-badge:hover .role-badge-tip { opacity:1;pointer-events:auto }
.role-badge-tip { position:absolute;bottom:calc(100% + 4px);left:50%;transform:translateX(-50%);background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:6px;padding:4px 8px;font-size:11px;font-weight:500;white-space:nowrap;color:var(--text-primary);opacity:0;pointer-events:none;transition:opacity 0.15s;z-index:100 }
.badge-owner { background:#faa61a22;color:#faa61a }
.badge-team  { background:#6c63ff33;color:#a78bfa }
.badge-mod   { background:#3ba55d22;color:#3ba55d }
.badge-svg-wrap { position:relative;display:inline-flex;align-items:center;margin-left:4px;vertical-align:middle;cursor:default }
/* Badge tooltip is rendered by JS (fixed-position) so it can't be clipped by a
   scroll container or hidden behind message images - suppress the CSS one. */
.badge-svg-wrap::after, .member-device::after { content:none !important; display:none !important }
/* Device icons: inline so they flow with the name (no overlap/squish), white with
   a dark halo so they stay legible on ANY background - dark, light or a busy image. */
.member-device {
  display: inline-block; vertical-align: -2px; line-height: 0;
  color: #fff; opacity: .92;
}
.member-device svg { display: block; }

