/* Chat characters ("Portrait" skin) -- see web/static/js/portrait.js.
   Geometry is the approved mockup's (docs/mockups/portrait_skin_mockup.html),
   namespaced pt-* so nothing here can match an existing chat/player rule. */
#portrait-stage {
  position: absolute; left: 0; right: 0; bottom: 0; height: 78%;
  pointer-events: none; overflow: hidden; z-index: 6;
}
/* Any layer over the Twitch iframe pauses it: on a Twitch surface the stage is
   moved to the chat panel (portrait.js placeInChat); never show it in the player. */
body.twitch-safe-surface #player-wrap > #portrait-stage,
body.hide-player #portrait-stage { display: none; }
#portrait-stage.pt-in-chat { position: fixed; right: auto; bottom: auto; z-index: 20; }
/* Cinema's chat panel is z-index 26; the characters stand in front of it. */
body.layout-cinema #portrait-stage.pt-in-chat { z-index: 30; }

#portrait-stage .pt-sp { position: absolute; left: 0; bottom: 6px; width: 90px; height: 124px; will-change: transform; }
#portrait-stage .pt-char { position: absolute; left: 0; bottom: 0; width: 90px; height: 124px; transform-origin: 45px 124px; }
#portrait-stage .pt-body { position: absolute; bottom: 0; left: 15px; width: 60px; height: 62px; border-radius: 26px 26px 12px 12px; border: 3px solid #111; box-sizing: border-box; }
#portrait-stage .pt-arm { position: absolute; top: 70px; width: 15px; height: 40px; border: 3px solid #111; border-radius: 8px; transform-origin: 7px 5px; box-sizing: border-box; }
#portrait-stage .pt-arm.pt-l { left: -9px; }
#portrait-stage .pt-arm.pt-r { left: 51px; }
#portrait-stage .pt-head { position: absolute; bottom: 55px; left: 12px; width: 66px; height: 62px; border-radius: 50%; border: 3px solid #111; transform-origin: 33px 60px; box-sizing: border-box; }
#portrait-stage .pt-eye { position: absolute; top: 21px; width: 8px; height: 11px; border-radius: 50%; background: #111; }
#portrait-stage .pt-eye.pt-l { left: 15px; }
#portrait-stage .pt-eye.pt-r { left: 33px; }
#portrait-stage .pt-mouth { position: absolute; top: 37px; left: 21px; width: 14px; height: 3px; border-radius: 3px; background: #111; }
#portrait-stage .pt-sp.pt-talk .pt-mouth { height: 9px; border-radius: 0 0 8px 8px; }
#portrait-stage .pt-hat { position: absolute; left: 50%; transform: translateX(-50%); pointer-events: none; }
/* 3D layer: one canvas behind the bubbles; the 2D body stays in the DOM as the
   fallback and is hidden once a speaker has a 3D guy. */
#portrait-stage .pt-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* 3D or nothing (Sean, 2026-09-29: "we don't want a 2d fallback"): the flat body is
   never drawn. It stays in the DOM because the physics and scenes read its box. */
#portrait-stage .pt-sp .pt-char { visibility: hidden; }
#portrait-stage .pt-hat3 { position: absolute; transform-origin: 50% 100%; z-index: 5; }

#portrait-stage .pt-bubble, .pt-bubble.pt-fly {
  position: absolute; bottom: 132px; left: 34px; min-width: 110px; max-width: 260px; width: max-content;
  background: #fff; color: #141414; border: 3px solid #111; border-radius: 14px;
  padding: 5px 11px 7px; font-weight: 600; font-size: 16px; line-height: 1.2;
  transform-origin: 10px 100%; overflow-wrap: anywhere; text-shadow: none;
}
#portrait-stage .pt-bubble .pt-nm, .pt-fly .pt-nm { display: block; font-weight: 700; font-size: 11px; line-height: 1.3; margin-bottom: 1px; }
#portrait-stage .pt-bubble .pt-tx img, .pt-fly .pt-tx img { max-height: 96px; max-width: 200px; vertical-align: middle; }
/* Line-level semantics that do not live in the rendered HTML follow the words
   into the bubble. The text itself reuses the chat classes (greentext, gold,
   dirt); /say needs this namespaced equivalent because its chat rule requires
   a .chat-line ancestor. */
#portrait-stage .pt-bubble.chat-say .pt-tx, .pt-bubble.pt-fly.chat-say .pt-tx {
  color: #ff4d4d; font-size: 1.45em; font-weight: 700; line-height: 1.25;
}
#portrait-stage .pt-bubble.gold-name .pt-nm, .pt-bubble.pt-fly.gold-name .pt-nm { color: #c8a84b !important; }
#portrait-stage .pt-bubble.gold-mine .pt-nm, .pt-bubble.pt-fly.gold-mine .pt-nm { color: #a9924a !important; }
/* [sp]spoiler[/sp] stays hidden in the bubble too: chat's rule is scoped to
   .chat-text, so the bubble printed it in the clear. A solid bar; the bubble
   takes no pointer, so it is read by hovering the chat line as always. */
.pt-bubble .pt-tx .spoiler { color: #141414; background: #141414; border-radius: 2px; padding: 0 2px; }
/* The bubble in flight from a retreating character to their chat line (portrait.js handoff). */
.pt-bubble.pt-fly { position: fixed; bottom: auto; min-width: 0; z-index: 2147483000; pointer-events: none;
  transform-origin: 0 50%; box-sizing: border-box; will-change: transform, opacity; }
/* The chat line the words landed on lights up once, then settles to normal. */
/* A heal in a game-action scene: a green plus rises off the patient. */
#portrait-stage .pt-plus { position: absolute; left: 34px; bottom: 120px; color: #3fbf5f; font: 800 30px/1 sans-serif;
  text-shadow: 0 2px 0 #0c3a18; pointer-events: none; animation: pt-plus 1.1s ease-out forwards; }
@keyframes pt-plus { from { transform: translateY(10px) scale(.6); opacity: 0; } 25% { opacity: 1; } to { transform: translateY(-46px) scale(1.1); opacity: 0; } }
/* A hit's contact beat (portrait.js impact()). Artist tower 3d/10-realtime-vfx.md:
   one clear silhouette (a star) plus one ring; two colours, a near-white core and
   one hot mid; front-loaded - most of its size in the first ~15%, then it lingers
   and snaps out. Centered on its left/top point. */
#portrait-stage .pt-burst { position: absolute; width: 0; height: 0; pointer-events: none; z-index: 5; }
#portrait-stage .pt-burst i, #portrait-stage .pt-burst b { position: absolute; left: -26px; top: -26px; width: 52px; height: 52px; }
#portrait-stage .pt-burst i { background: #fff8e6;
  clip-path: polygon(50% 0, 60% 36%, 100% 30%, 66% 55%, 84% 100%, 50% 70%, 16% 100%, 34% 55%, 0 30%, 40% 36%);
  animation: pt-burst-star 300ms cubic-bezier(.1, .9, .2, 1) forwards; }
#portrait-stage .pt-burst b { border: 4px solid #ff6a2b; border-radius: 50%; box-sizing: border-box;
  animation: pt-burst-ring 340ms cubic-bezier(.1, .9, .2, 1) forwards; }
@keyframes pt-burst-star { 0% { transform: scale(.25) rotate(-12deg); opacity: 1; } 15% { transform: scale(1) rotate(0); opacity: 1; }
  70% { transform: scale(1.08); opacity: 1; } 100% { transform: scale(1.1); opacity: 0; } }
@keyframes pt-burst-ring { 0% { transform: scale(.3); opacity: 1; } 18% { transform: scale(1.1); } 100% { transform: scale(1.7); opacity: 0; border-width: 1px; } }
/* A Molotov in flight (portrait.js throwBottle()): a green bottle with a lit rag,
   centred on its left/top point. The flames on the victim are 3D (portrait3d burn). */
#portrait-stage .pt-bottle { position: absolute; width: 0; height: 0; pointer-events: none; z-index: 6; }
#portrait-stage .pt-bottle::before { content: ""; position: absolute; left: -5px; top: -9px; width: 10px; height: 18px;
  background: #3f8a3a; border: 2px solid #111; border-radius: 3px 3px 2px 2px; box-sizing: border-box; }
#portrait-stage .pt-bottle::after { content: ""; position: absolute; left: -4px; top: -19px; width: 8px; height: 11px; background: #ffb02e;
  clip-path: polygon(50% 0, 100% 70%, 70% 100%, 30% 100%, 0 70%); }
/* 2D fallback body (no WebGL): the same whole-silhouette white flash. */
#portrait-stage .pt-sp.pt-flash .pt-char { filter: brightness(0) invert(1); }
/* The speaker's line waits, its space kept, while his character delivers it. */
#messagebuffer .chat-line.pt-pending { visibility: hidden; }
/* Live danmu is already painted in the full-screen overlay. It remains a chat
   history line after reconnect, but is not duplicated in live chat/bubbles. */
#messagebuffer .chat-line.pt-danmu-overlay-only { display: none; }
/* The landing light is a Web Animation (portrait.js leaveWords), never a CSS
   animation here: that would replace .shotKilled / .wololo-converted's own. */
#portrait-stage .pt-bubble::after { content: ""; position: absolute; left: 14px; bottom: -12px; border: 9px solid transparent; border-top: 12px solid #111; border-bottom: 0; }
#portrait-stage .pt-bubble::before { content: ""; position: absolute; left: 17px; bottom: -7px; border: 6px solid transparent; border-top: 8px solid #fff; border-bottom: 0; z-index: 1; }
#portrait-stage .pt-sp.pt-small .pt-bubble { font-size: 12px; max-width: 170px; padding: 3px 8px 5px; }
#portrait-stage .pt-sp.pt-hidebubble .pt-bubble { display: none; }
#portrait-stage .pt-sp.pt-flip .pt-bubble { left: auto; right: 34px; }
#portrait-stage .pt-sp.pt-flip .pt-bubble::after { left: auto; right: 14px; }
#portrait-stage .pt-sp.pt-flip .pt-bubble::before { left: auto; right: 17px; }
#portrait-stage .pt-count { font-weight: 700; font-size: 12px; background: #111; color: #fff; border-radius: 2px; padding: 0 5px; margin-left: 6px; vertical-align: 2px; }
