.scoreboard { position: relative; z-index: 8; }
.match-status { position: relative; overflow: hidden; }
.match-status:has(.pull-toast:not([hidden])) > span { opacity: 0 !important; }
.match-status .pull-toast,
.match-status .pull-toast.pull-toast--above {
  position: absolute !important;
  inset: 0 !important;
  z-index: 4;
  display: grid;
  place-content: center;
  gap: 2px;
  width: 100% !important;
  min-width: 0 !important;
  max-width: 100% !important;
  min-height: 100%;
  margin: 0 !important;
  padding: 4px 8px !important;
  overflow: hidden !important;
  border-radius: inherit !important;
  opacity: 1;
  transform: none !important;
}
/* Classic gift toast width.
   The toast is pinned to the centre column, which is 324px of a 708px
   scoreboard and 308px once padded. "StreamSupporter +500 -> Blue Team" needs
   ~343px, so BOTH ends ellipsised: "StreamSupp... +500 -> Blue Tea...".

   An earlier attempt widened the box instead — inset-inline:-59% with the
   parent's overflow lifted — which bought the room by painting an opaque toast
   across both team panels for its 2.8s. The centre column IS the space between
   the panels, so leaving that column was never the fix; it just moved the
   damage from the text onto the scoreboard.

   The box stays in its column. What was actually wrong is one row below: the
   shrink order the comments describe was never expressible, because the
   username and the team both carried flex-shrink:1 and therefore shrank
   together, proportionally, instead of in sequence. See the row rules further
   down — the team no longer shrinks, so the username is the sole shrink target
   and the fit pass takes over once its stub is reached. */

.match-status .pull-toast[hidden] { display: none !important; }
.match-status .pull-toast.pull-toast--show { animation: center-toast-enter 2800ms ease-out forwards !important; }
.match-status .pull-toast strong {
  display: flex !important;
  align-items: baseline;
  justify-content: center;
  gap: 5px;
  min-width: 0;
  font-size: clamp(.88rem, 2.15vw, 1.15rem) !important;
  line-height: 1 !important;
  white-space: nowrap;
}
.pull-toast-username { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 900; }
.pull-toast-points { flex: 0 0 auto; font-size: 1.08em; font-weight: 1000; }
.pull-toast-team { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .72em; }
.match-status .pull-toast small {
  display: block !important;
  max-width: 100%;
  overflow: hidden;
  font-size: clamp(.5rem, 1.2vw, .62rem) !important;
  font-weight: 850;
  line-height: 1;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mode-stack,
.streamer-hud-body[data-hud-size="small"] .mode-stack {
  position: relative !important;
  top: auto !important;
  z-index: 7;
  display: flex !important;
  min-height: 22px !important;
  margin: 2px 0 1px !important;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
/* Classic rope offset. --classic-rope-lift is defined in styles.css so the
   density decision lives in one place; this rule only applies it. */
.mode-stack + .rope-stage {
  margin-top: var(--classic-rope-lift, 4px) !important;
}
.last-chance-counter-banner {
  --counter-color: var(--rope-left-color);
  display: inline-grid;
  grid-template-columns: auto auto;
  gap: 1px 7px;
  align-items: center;
  width: min(340px, 64vw);
  padding: 3px 9px 4px;
  border: 1px solid color-mix(in srgb, var(--counter-color) 55%, transparent);
  border-radius: 8px;
  color: #fff;
  background: rgba(3,8,14,.88);
  box-shadow: 0 2px 7px rgba(0,0,0,.46), 0 0 8px color-mix(in srgb, var(--counter-color) 24%, transparent);
  text-shadow: 0 1px 2px #000, 0 0 4px #000;
}
.last-chance-counter-banner[hidden] { display: none !important; }
.last-chance-counter-banner[data-team="red"] { --counter-color: var(--rope-right-color); }
.last-chance-counter-banner strong { font-size: clamp(.58rem,1.4vw,.76rem); font-weight: 1000; letter-spacing: .04em; }
.last-chance-counter-banner #counter-detail { justify-self: end; color: var(--counter-color); font-size: clamp(.5rem,1.1vw,.64rem); font-weight: 950; }
.last-chance-counter-banner #counter-numbers { grid-column: 1 / -1; font-size: clamp(.48rem,1vw,.6rem); font-weight: 900; }
.counter-progress-track { grid-column: 1 / -1; height: 3px; overflow: hidden; border-radius: 4px; background: rgba(255,255,255,.13); }
.counter-progress-track i { display: block; width: 0; height: 100%; border-radius: inherit; background: var(--counter-color); transition: width 180ms linear; }
body[data-counter-state="pending"] .last-chance-counter-banner { animation: counter-urgent 1s ease-in-out infinite alternate; }
body[data-countered] .rope-stage .rope { animation: counter-save-wave 1.8s cubic-bezier(.16,1,.3,1) both !important; }
@keyframes counter-urgent { to { border-color: color-mix(in srgb, var(--counter-color) 82%, white); } }
@keyframes counter-save-wave { 28% { transform: translateX(var(--pull-shift,8px)) scaleX(1.04); filter: brightness(1.45); } 58% { transform: translateX(calc(var(--pull-shift,8px) * -.3)) scaleX(.99); } }
@media (max-width: 620px) {
  .last-chance-counter-banner { width: min(270px,76vw); padding: 3px 6px; }
}
@media (prefers-reduced-motion: reduce) {
  body[data-counter-state="pending"] .last-chance-counter-banner,
  body[data-countered] .rope-stage .rope { animation: none !important; }
}

/* Shared viewer Experience card: one readable notification component for
   manual and Director Experiences. */
.mode-stack .experience-banner:not([hidden]),
body[data-overlay-text-size] .overlay-shell.has-experience-mode .experience-banner:not([hidden]) {
  display: grid !important;
  grid-template-columns: 30px minmax(0, 1fr) !important;
  grid-template-rows: auto auto !important;
  align-items: center !important;
  column-gap: 9px !important;
  row-gap: 5px !important;
  width: min(410px, 78vw) !important;
  min-height: 52px !important;
  padding: 10px 14px !important;
  border: 1px solid color-mix(in srgb, var(--event-accent-color, #7ee9ff) 56%, rgba(255,255,255,.18)) !important;
  border-radius: 12px !important;
  color: #fff !important;
  background: rgba(3, 7, 13, .9) !important;
  box-shadow: 0 7px 18px rgba(0,0,0,.45) !important;
  animation: experience-card-enter .28s ease-out both !important;
}
.mode-stack .experience-banner .experience-icon {
  display: grid !important;
  grid-row: 1 / -1 !important;
  place-items: center !important;
  width: 30px !important;
  height: 30px !important;
  font-size: 1.1rem !important;
  line-height: 1 !important;
}
.mode-stack .experience-banner #experience-name {
  display: block !important;
  min-width: 0 !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
  color: #fff !important;
  font-size: clamp(.8rem, 2.3vw, 1.08rem) !important;
  font-weight: 950 !important;
  letter-spacing: .035em !important;
  line-height: 1.1 !important;
}
.mode-stack .experience-banner #experience-message {
  display: block !important;
  min-width: 0 !important;
  color: rgba(255,255,255,.96) !important;
  font-size: clamp(.68rem, 1.85vw, .88rem) !important;
  font-weight: 750 !important;
  line-height: 1.25 !important;
  text-transform: none !important;
  white-space: normal !important;
  overflow-wrap: anywhere !important;
}
.mode-stack .experience-banner[data-display-mode="minimal"] {
  grid-template-columns: 24px minmax(0, 1fr) !important;
  grid-template-rows: 1fr !important;
  width: min(300px, 70vw) !important;
  min-height: 34px !important;
  padding: 6px 10px !important;
}
.mode-stack .experience-banner[data-display-mode="minimal"] .experience-icon { width: 24px !important; height: 24px !important; font-size: .95rem !important; }
.mode-stack .experience-banner[data-display-mode="minimal"] #experience-name { font-size: clamp(.7rem, 2vw, .9rem) !important; }
@keyframes experience-card-enter { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: translateY(0); } }
body[data-neutral-rope-style="customSingle"][data-progress-fill="marker"] .rope-stage .rope {
  border-color: color-mix(in srgb, var(--moving-marker-single-color) 72%, white) !important;
  background:
    repeating-linear-gradient(118deg, rgba(255,255,255,.34) 0 3px, rgba(0,0,0,.3) 3px 7px, rgba(255,255,255,.17) 7px 11px, rgba(0,0,0,.34) 11px 15px),
    linear-gradient(var(--moving-marker-single-color), var(--moving-marker-single-color)) !important;
  background-blend-mode: overlay, normal !important;
}
body[data-neutral-rope-style="customSingle"][data-progress-fill="marker"] .rope-stage .rope::before,
body[data-neutral-rope-style="customSingle"][data-progress-fill="marker"] .rope-stage .rope::after {
  background: radial-gradient(circle at 32% 28%, rgba(255,255,255,.9), var(--moving-marker-single-color) 48%, color-mix(in srgb, var(--moving-marker-single-color) 68%, black)) !important;
}
body[data-neutral-rope-style="customSplit"][data-progress-fill="marker"] .rope-stage .rope {
  border-color: rgba(255,255,255,.26) !important;
  background:
    repeating-linear-gradient(118deg, rgba(255,255,255,.32) 0 3px, rgba(0,0,0,.3) 3px 7px, rgba(255,255,255,.17) 7px 11px, rgba(0,0,0,.34) 11px 15px),
    linear-gradient(90deg, var(--moving-marker-left-color) 0 49.55%, #fff 49.8% 50.2%, var(--moving-marker-right-color) 50.45% 100%) !important;
  background-blend-mode: overlay, normal !important;
}
body[data-neutral-rope-style="customSplit"][data-progress-fill="marker"] .rope-stage .rope::before {
  background: radial-gradient(circle at 32% 28%, #fff, var(--moving-marker-left-color) 48%, color-mix(in srgb, var(--moving-marker-left-color) 68%, black)) !important;
}
body[data-neutral-rope-style="customSplit"][data-progress-fill="marker"] .rope-stage .rope::after {
  background: radial-gradient(circle at 32% 28%, #fff, var(--moving-marker-right-color) 48%, color-mix(in srgb, var(--moving-marker-right-color) 68%, black)) !important;
}
.mode-stack .smart-event-banner,
.mode-stack .experience-banner,
body[data-overlay-text-size] .overlay-shell.has-smart-event .smart-event-banner:not([hidden]),
body[data-overlay-text-size] .overlay-shell.has-experience-mode .experience-banner:not([hidden]) {
  display: inline-grid !important;
  grid-template-columns: auto auto auto !important;
  align-items: center;
  justify-content: center;
  gap: 5px !important;
  width: max-content !important;
  min-width: 0 !important;
  max-width: min(360px, 52vw) !important;
  min-height: 18px !important;
  padding: 2px 8px !important;
  overflow: visible !important;
  border-radius: 7px !important;
  white-space: nowrap;
}
.mode-stack .experience-banner {
  border-color: rgba(255,255,255,.14) !important;
  color: rgba(255,255,255,.86) !important;
  background: rgba(3,8,14,.56) !important;
  box-shadow: 0 2px 5px rgba(0,0,0,.24) !important;
}
.mode-stack .smart-event-banner {
  border-color: color-mix(in srgb, currentColor 46%, transparent) !important;
  color: #ffd86f !important;
  background: rgba(3,8,14,.82) !important;
  box-shadow: 0 2px 7px rgba(0,0,0,.4), 0 0 7px color-mix(in srgb, currentColor 20%, transparent) !important;
}
.mode-stack .experience-icon,
body[data-overlay-text-size] .mode-stack .experience-icon {
  display: inline-grid !important;
  place-items: center;
  min-width: 13px !important;
  color: currentColor !important;
  font-size: .68rem !important;
  font-weight: 1000 !important;
  line-height: 1 !important;
  letter-spacing: 0 !important;
}
.mode-stack .smart-event-banner span:not(.experience-icon),
.mode-stack .experience-banner span:not(.experience-icon),
body[data-overlay-text-size] .mode-stack .smart-event-banner span:not(.experience-icon),
body[data-overlay-text-size] .mode-stack .experience-banner span:not(.experience-icon) {
  display: block !important;
  min-width: 0 !important;
  max-width: none !important;
  overflow: visible !important;
  color: #fff !important;
  font-size: .61rem !important;
  font-weight: 950 !important;
  line-height: 1 !important;
  letter-spacing: .045em !important;
  text-overflow: clip !important;
  white-space: nowrap !important;
}
.mode-stack .smart-event-banner strong,
.mode-stack .experience-banner strong,
body[data-overlay-text-size] .mode-stack .smart-event-banner strong,
body[data-overlay-text-size] .mode-stack .experience-banner strong {
  display: block !important;
  min-width: 0;
  max-width: none !important;
  overflow: visible !important;
  color: currentColor !important;
  font-size: .53rem !important;
  font-weight: 900 !important;
  line-height: 1 !important;
  letter-spacing: .035em;
  text-overflow: clip !important;
  white-space: nowrap !important;
}
.mode-stack .smart-event-banner.is-activating {
  min-height: 22px !important;
  padding: 3px 10px !important;
  animation: experience-activate .9s cubic-bezier(.16,1,.3,1) both !important;
}
.mode-stack .smart-event-banner.is-activating .experience-icon,
.mode-stack .smart-event-banner.is-activating #smart-event-name {
  color: currentColor !important;
  text-shadow: 0 1px 2px #000, 0 0 4px #000 !important;
}
.mode-stack .smart-event-banner.is-activating #smart-event-message {
  color: currentColor !important;
  font-size: .58rem !important;
  text-shadow: 0 1px 2px #000, 0 0 4px #000 !important;
}
.overlay-shell.smart-event-ending .smart-event-banner { animation: experience-fade-out .7s ease forwards !important; }
.overlay-shell.smart-event-ending .experience-banner { opacity: 0 !important; }
@keyframes center-toast-enter {
  0% { opacity: 0; transform: translateY(4px) scale(.96); }
  10%, 84% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-2px); }
}
@keyframes experience-activate {
  0% { opacity: 0; transform: translateY(3px) scale(.96); }
  30%, 88%, 100% { opacity: 1; transform: none; }
}
@keyframes experience-fade-out { to { opacity: 0; transform: translateY(2px); } }
@media (max-width: 620px) {
  .mode-stack .experience-banner strong { display: none !important; }
  .mode-stack .smart-event-banner:not(.is-activating) .experience-icon { display: none !important; }
  .mode-stack .smart-event-banner,
  .mode-stack .experience-banner { max-width: min(250px, 64vw) !important; padding-inline: 6px !important; }
}
@media (max-width: 420px) {
  .mode-stack .smart-event-banner.is-activating strong { display: block !important; font-size: .47rem !important; }
  .mode-stack .smart-event-banner.is-activating { max-width: 92vw !important; }
  .mode-stack .experience-icon { display: none !important; }
  .match-status .pull-toast small { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .match-status .pull-toast.pull-toast--show,
  .mode-stack .smart-event-banner,
  .mode-stack .experience-banner { animation: none !important; }
}

/* Bounded match reactions. Strong color remains local to the rope and accents. */
body { --motion-distance: 1; --motion-particles: 1; --idle-tension-duration: 5.8s; --idle-energy-duration: 7.2s; }
body[data-animation-intensity="low"] { --motion-distance: .58; --idle-tension-duration: 7.8s; --idle-energy-duration: 9.6s; }
body[data-animation-intensity="high"] { --motion-distance: 1.32; --idle-tension-duration: 4.5s; --idle-energy-duration: 5.8s; }
body[data-animation-intensity="off"],
.streamer-hud-body[data-streamer-hud-animations="off"] { --motion-distance: 0; }
.streamer-hud-body[data-streamer-hud-animations="reduced"] { --motion-distance: .42; }

body[data-battle-marker-style="ring"] .flag-knot { animation: none !important; }

/* Real physical rope sway plays for BOTH Idle Rope Motion options - "Energy
   Flow" previously meant ONLY the thin moving highlight streak below, with no
   actual rope movement at all, which read as broken/absent to a creator who
   had that option selected instead of "Gentle". Energy Flow now gets the
   same sway plus its own streak layered on top, so it's strictly more, not
   an entirely different (and easy to mistake for non-functional) effect. */
/* The Idle Style preset is excluded here rather than overridden later: this
   rule is !important and highly specific, so a later "stop animating" rule
   loses to it. Still and Subtle both mean no ambient rope motion. */
body[data-idle-rope-motion="gentle"]:not([data-animation-intensity="off"]):not([data-tug]):not([data-winner]):not([data-idle-style-preset="still"]):not([data-idle-style-preset="subtle"]) .rope-stage .rope,
body[data-idle-rope-motion="energy"]:not([data-animation-intensity="off"]):not([data-tug]):not([data-winner]):not([data-idle-style-preset="still"]):not([data-idle-style-preset="subtle"]) .rope-stage .rope {
  animation: idle-rope-tension var(--idle-tension-duration) ease-in-out infinite !important;
  transform-origin: center;
}
body[data-idle-rope-motion="energy"]:not([data-animation-intensity="off"]):not([data-tug]) .rope-stage__track::after {
  content: "" !important;
  display: block !important;
  position: absolute;
  z-index: 3;
  top: 50%;
  left: 3%;
  width: 18%;
  height: 9px;
  border-radius: 999px;
  opacity: calc(.32 * var(--motion-distance));
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--rope-left-color) 65%, white), color-mix(in srgb, var(--rope-right-color) 65%, white), transparent);
  transform: translate(-120%,-50%);
  animation: idle-rope-energy var(--idle-energy-duration) linear infinite;
  pointer-events: none;
}
.streamer-hud-body[data-streamer-hud-animations="reduced"][data-idle-rope-motion="gentle"] .rope-stage .rope { animation-duration: 9s !important; }
.streamer-hud-body[data-streamer-hud-animations="reduced"][data-idle-rope-motion="energy"] .rope-stage__track::after,
.streamer-hud-body[data-streamer-hud-animations="off"] .rope-stage__track::after { display: none !important; }
body[data-tug] .rope-stage__track::after { animation-play-state: paused !important; opacity: 0 !important; }
/* A genuinely visible taut-and-slack pull plus a real side-to-side sway, not
   a 0.8%-scale flicker: the rope stretches tighter with a brightened
   highlight and eases back, while gently rocking via skewY so it reads as
   swaying, not just pulsing in place. Scales with BOTH --motion-distance
   (Animation Intensity) AND --idle-breathe-amplitude-base (Idle Style) -
   previously only the former, so changing Idle Style had no visible effect
   on the single most prominent element on the whole overlay.
   transform-origin:center is set where this animation is applied. */
@keyframes idle-rope-tension {
  0% { transform: scaleX(calc(1 - 0.038 * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))) skewY(calc(-1.7deg * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))) translateY(calc(-2px * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))); filter: brightness(calc(1 - 0.06 * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))); }
  25% { transform: scaleX(1) skewY(calc(1.1deg * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))) translateY(calc(1px * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))); filter: brightness(1); }
  50% { transform: scaleX(calc(1 + 0.042 * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))) skewY(calc(1.7deg * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))) translateY(calc(2px * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))); filter: brightness(calc(1 + 0.08 * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))); }
  75% { transform: scaleX(1) skewY(calc(-1.1deg * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))) translateY(calc(-1px * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))); filter: brightness(1); }
  100% { transform: scaleX(calc(1 - 0.038 * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))) skewY(calc(-1.7deg * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))) translateY(calc(-2px * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))); filter: brightness(calc(1 - 0.06 * var(--motion-distance, 1) * var(--idle-breathe-amplitude-base, 1))); }
}
@keyframes idle-rope-energy { to { transform: translate(570%,-50%); } }

/* Ambient breathing: panels, marker, center status, and the shell's own
   glow move very slightly at all times so the overlay never reads as a
   static image, even when nothing has happened for a while. Amplitude is
   driven by --idle-breathe-amplitude-base (set from the creator's Idle
   Style preset) and multiplied per-element by match state below. Every
   element gets its own animation-delay so they drift out of phase with
   each other rather than pulsing in lockstep. */
body { --idle-breathe-amplitude-base: 1; }
body[data-idle-style-preset="still"] { --idle-breathe-amplitude-base: 0 !important; }
/* Subtle is the default, and it is deliberately still between heartbeats:
   nothing breathes continuously, so the occasional flourish is the only
   motion a viewer ever sees. The louder presets keep a reduced ambient
   breath (amplitudes come from IDLE_PRESETS in OverlayView.js). */
body[data-idle-style-preset="subtle"] { --idle-breathe-amplitude-base: 0 !important; }
body[data-idle-style-preset="energetic"] { --idle-breathe-amplitude-base: .35; }
body[data-idle-style-preset="neon"] { --idle-breathe-amplitude-base: .5; }
body[data-idle-style-preset="arcade"] { --idle-breathe-amplitude-base: .65; }

.team-panel--blue, .team-panel--red, .match-status, .flag {
  --idle-breathe-amplitude: var(--idle-breathe-amplitude-base);
}
body[data-leader="blue"] .team-panel--blue,
body[data-leader="red"] .team-panel--red {
  --idle-breathe-amplitude: calc(var(--idle-breathe-amplitude-base) * 1.6);
}
body[data-lead-magnitude="dominant"][data-leader="blue"] .team-panel--blue,
body[data-lead-magnitude="dominant"][data-leader="red"] .team-panel--red {
  --idle-breathe-amplitude: calc(var(--idle-breathe-amplitude-base) * 2.2);
}

body[data-animation-intensity]:not([data-animation-intensity="off"]):not([data-priority-active]) .team-panel--blue {
  animation: idle-panel-breathe 5.4s ease-in-out infinite;
  animation-delay: 0s;
}
body[data-animation-intensity]:not([data-animation-intensity="off"]):not([data-priority-active]) .team-panel--red {
  animation: idle-panel-breathe 5.4s ease-in-out infinite;
  animation-delay: .9s;
}
body[data-animation-intensity]:not([data-animation-intensity="off"]):not([data-priority-active]) .match-status {
  animation: idle-panel-breathe 6.2s ease-in-out infinite;
  animation-delay: 1.8s;
}
/* Targets .flag-motion (an inner wrapper with no position-critical transform
   of its own), not .flag itself (whose transform places it on the rope in
   both Classic and Compact layouts) - so this can rotate/translate freely
   without fighting that positioning. */
body[data-animation-intensity]:not([data-animation-intensity="off"]):not([data-priority-active])[data-battle-marker-style]:not([data-battle-marker-style="territory"]):not([data-tug]) .flag-motion {
  animation: idle-marker-breathe 4.8s ease-in-out infinite;
  animation-delay: 2.6s;
}
body[data-animation-intensity]:not([data-animation-intensity="off"]):not([data-priority-active]) .overlay-shell {
  animation: idle-glow-breathe 7s ease-in-out infinite;
  animation-delay: .4s;
}

/* Still means still: don't just zero the amplitude, stop animating at all.
   Subtle gets the same treatment — a zero-amplitude animation still ticks
   every frame and still defeats the "nothing is moving" read we want between
   heartbeats. The rope is handled at its own rule above, which is !important
   and too specific to override from here. */
body[data-idle-style-preset="still"] .team-panel--blue,
body[data-idle-style-preset="still"] .team-panel--red,
body[data-idle-style-preset="still"] .match-status,
body[data-idle-style-preset="still"] .flag-motion,
body[data-idle-style-preset="still"] .overlay-shell,
body[data-idle-style-preset="subtle"] .team-panel--blue,
body[data-idle-style-preset="subtle"] .team-panel--red,
body[data-idle-style-preset="subtle"] .match-status,
body[data-idle-style-preset="subtle"] .flag-motion,
body[data-idle-style-preset="subtle"] .overlay-shell {
  animation: none !important;
}

/* The travelling sweep is ambient motion by definition, so it only belongs to
   the presets that opt into ambience. */
body[data-idle-style-preset="still"] .rope-stage__track::after,
body[data-idle-style-preset="subtle"] .rope-stage__track::after {
  display: none !important;
}

@keyframes idle-panel-breathe {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: calc(1 - .028 * var(--idle-breathe-amplitude, 1)); transform: scale(calc(1 - .005 * var(--idle-breathe-amplitude, 1))); }
}
/* Genuine sway + bob, not a scale flicker - reads as the flag/marker
   actually hanging and moving on its pole. Amplitude follows the same
   --idle-breathe-amplitude-base used by every other idle animation (creator's
   Idle Style preset), so Still still means still and Arcade is clearly livelier. */
@keyframes idle-marker-breathe {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  25% { transform: translateY(calc(-7px * var(--idle-breathe-amplitude-base, 1))) rotate(calc(-6deg * var(--idle-breathe-amplitude-base, 1))); }
  75% { transform: translateY(calc(5px * var(--idle-breathe-amplitude-base, 1))) rotate(calc(6deg * var(--idle-breathe-amplitude-base, 1))); }
}
@keyframes idle-glow-breathe {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(calc(1 + .025 * var(--idle-breathe-amplitude-base, 1))); }
}

/* A real glowing border pulse, not just the brightness-filter breathe above.
   .overlay-shell's own box-shadow is locked !important in Compact for bar
   positioning, so this lives on its own dedicated element instead (a
   deliberately simple fix for the parity problem: a plain sibling layer with
   no property locks works identically in both layouts, no layout-specific
   CSS needed at all). Reuses --overlay-glow-color so it always matches
   whatever glow color the creator already picked. */
.idle-glow-ring {
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
}
body[data-animation-intensity]:not([data-animation-intensity="off"]):not([data-priority-active]):not([data-idle-style-preset="still"]):not([data-idle-style-preset="subtle"]) .idle-glow-ring {
  animation: idle-glow-ring-pulse 6.4s ease-in-out infinite;
  animation-delay: 3.2s;
}
body[data-idle-style-preset="still"] .idle-glow-ring {
  animation: none !important;
  opacity: 0 !important;
}
.streamer-hud-body[data-streamer-hud-animations="off"] .idle-glow-ring {
  animation: none !important;
  opacity: 0 !important;
}
.streamer-hud-body[data-streamer-hud-animations="reduced"] .idle-glow-ring {
  animation-duration: 10s !important;
}
/* --overlay-glow-color defaults to black, which would make this invisible
   for almost everyone out of the box - blended toward white so there's
   always a real, visible glow by default, while a creator who customizes
   that color to something else still sees their own color come through. */
@keyframes idle-glow-ring-pulse {
  0%, 100% { opacity: 0; box-shadow: 0 0 0 0 color-mix(in srgb, var(--overlay-glow-color, #fff) 40%, white); }
  50% { opacity: calc(.6 * var(--idle-breathe-amplitude-base, 1)); box-shadow: 0 0 calc(16px * var(--idle-breathe-amplitude-base, 1)) calc(2px * var(--idle-breathe-amplitude-base, 1)) color-mix(in srgb, var(--overlay-glow-color, #fff) 40%, white); }
}

/* Independent cloth flutter, layered on top of the whole-marker sway above -
   only when the marker is actually a flag (a ring/trophy/etc has no cloth to
   flutter). The keyframe itself (styles.css) already scales by
   --idle-breathe-amplitude-base. Needs real specificity + !important to win
   over styles.css's defensive ".flag-cloth { animation: none; }" /
   ".flag[data-edge] .flag-cloth { animation: none; }" resets. */
body[data-battle-marker-style="flag"][data-animation-intensity]:not([data-animation-intensity="off"]):not([data-priority-active]):not([data-idle-style-preset="still"]):not([data-tug]) .flag-cloth {
  animation: flag-wave var(--flag-wave-duration, 3.8s) ease-in-out infinite !important;
}
body[data-idle-style-preset="still"] .flag-cloth {
  animation: none !important;
}
.streamer-hud-body[data-streamer-hud-animations="off"] .flag-cloth {
  animation: none !important;
}
.streamer-hud-body[data-streamer-hud-animations="reduced"] .flag-cloth {
  animation-duration: 6.5s !important;
}

/* Heartbeat: an occasional, irregular flourish distinct from the steady
   breathing loop above, so idle time doesn't just read as one repeating
   animation. Timing/randomization lives in JS (OverlayView.maybeFireHeartbeat);
   this is just the one-shot visual per flourish type. */
body[data-heartbeat="sweep"] .match-status::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 40%, color-mix(in srgb, var(--event-accent-color, #ffd66b) 22%, transparent) 50%, transparent 60%);
  animation: heartbeat-sweep 1.4s ease-out both;
  pointer-events: none;
}
body[data-heartbeat="pulse"] .overlay-shell {
  animation: heartbeat-pulse 1.5s ease-out both;
}
body[data-heartbeat="drift"] .flag {
  animation: heartbeat-drift 1.6s ease-in-out both;
}
@keyframes heartbeat-sweep { 0% { opacity: 0; } 40% { opacity: 1; } 100% { opacity: 0; } }
@keyframes heartbeat-pulse { 0%, 100% { filter: brightness(1); } 45% { filter: brightness(1.06); } }
@keyframes heartbeat-drift { 0%, 100% { transform: translate(-50%, -50%); } 50% { transform: translate(-50%, calc(-50% - 2px)); } }

/* ===================================================================
   Per-Experience whole-overlay visual identities. Each keys off the
   existing per-Experience CSS class already applied to .overlay-shell by
   OverlayView.renderExperience (experience-doublePoints etc., unchanged) —
   no new class-management logic, only new rules reacting to it.
   =================================================================== */

/* Double Points: aggressive, big, exciting. Pull-impulse magnitude itself is
   scaled in JS (getExperienceImpactMultiplier); this covers the ambient
   tempo (quicker breathing while active) and a bold one-shot activation
   burst — not a permanently doubled visual weight. */
.overlay-shell.experience-double-points .team-panel--blue,
.overlay-shell.experience-double-points .team-panel--red,
.overlay-shell.experience-double-points .match-status {
  animation-duration: 3.2s !important;
}
body[data-experience-activating] .overlay-shell.experience-double-points {
  animation: double-points-activate 900ms ease-out both;
}
@keyframes double-points-activate {
  0%, 100% { filter: brightness(1) saturate(1); }
  30% { filter: brightness(1.22) saturate(1.18); }
}

/* Critical Hit: sudden, sharp, impactful. Keyed to body[data-critical-hit-impact],
   set only for the specific gift that actually crit (OverlayView.
   triggerCriticalHitImpact / isCriticalHitModifier) — not the whole time the
   Experience pool is enabled, so this reads as a moment, not a mood. */
body[data-critical-hit-impact] .overlay-shell::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 20;
  background: #fff;
  opacity: 0;
  animation: critical-hit-flash 450ms ease-out both;
  pointer-events: none;
  border-radius: inherit;
}
@keyframes critical-hit-flash { 0% { opacity: 0; } 16% { opacity: .55; } 100% { opacity: 0; } }

body[data-critical-hit-impact] .overlay-shell::after {
  content: "CRITICAL";
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 21;
  transform: translate(-50%, -50%) scale(.5);
  font-weight: 950;
  font-size: clamp(1rem, 4vw, 2rem);
  letter-spacing: .08em;
  color: #fff;
  text-shadow: 0 0 14px rgba(255,90,90,.85), 0 2px 4px rgba(0,0,0,.6);
  opacity: 0;
  animation: critical-hit-callout 650ms cubic-bezier(.2,1.4,.3,1) both;
  pointer-events: none;
  white-space: nowrap;
}
@keyframes critical-hit-callout {
  0% { opacity: 0; transform: translate(-50%,-50%) scale(.5); }
  25% { opacity: 1; transform: translate(-50%,-50%) scale(1.08); }
  45% { transform: translate(-50%,-50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%,-50%) scale(1); }
}

/* Screen shake: hard-gated per explicit product rule — off under reduced
   motion (media query below), off for Still and Subtle idle presets, brief
   and small even at maximum, and never applied to text-bearing elements
   directly (it moves the whole shell, not the score/labels independently). */
body[data-critical-hit-impact]:not([data-idle-style-preset="still"]):not([data-idle-style-preset="subtle"]) .overlay-shell {
  animation: critical-hit-shake 380ms ease-out both;
}
@keyframes critical-hit-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-2px); }
  40% { transform: translateX(2px); }
  60% { transform: translateX(-1px); }
  80% { transform: translateX(1px); }
}

/* Chain Bonus: momentum via continuous glow/saturation scaling with chain
   length (--chain-energy, 0-1, set by OverlayView.updateChainEnergy), plus a
   one-shot release when the chain breaks (body[data-chain-release]). No
   literal chain-link geometry in v1, per product direction. */
.overlay-shell.experience-chain-bonus {
  filter: saturate(calc(1 + .35 * var(--chain-energy, 0))) brightness(calc(1 + .08 * var(--chain-energy, 0)));
  box-shadow: 0 0 calc(10px + 30px * var(--chain-energy, 0)) color-mix(in srgb, var(--event-accent-color, #a884ff) calc(30% + 40% * var(--chain-energy, 0)), transparent);
  transition: filter 400ms ease, box-shadow 400ms ease;
}
body[data-chain-release] .overlay-shell.experience-chain-bonus {
  animation: chain-release 900ms cubic-bezier(.2,.9,.3,1) both;
}
@keyframes chain-release {
  0% { filter: saturate(1.35) brightness(1.08); }
  30% { filter: saturate(1.6) brightness(1.3); transform: scale(1.01); }
  100% { filter: saturate(1) brightness(1); transform: scale(1); }
}

/* Lucky Side: uncertainty and direction. A soft color sweep toward the
   currently-selected team plus a persistent marker lean, driven by
   shell[data-lucky-side-team] (OverlayView.updateLuckySideDirection). Reruns
   on every render, so a side change corrects immediately with no stale state. */
.overlay-shell[data-lucky-side-team] {
  background: linear-gradient(var(--lucky-side-angle, 90deg), color-mix(in srgb, var(--event-accent-color, #65e89b) 14%, transparent), transparent 55%);
}
.overlay-shell[data-lucky-side-team="blue"] { --lucky-side-angle: 90deg; }
.overlay-shell[data-lucky-side-team="red"] { --lucky-side-angle: 270deg; }
.overlay-shell[data-lucky-side-team="blue"] .flag { --lucky-side-lean: -6deg; }
.overlay-shell[data-lucky-side-team="red"] .flag { --lucky-side-lean: 6deg; }
body[data-idle-style-preset]:not([data-idle-style-preset="still"]) .overlay-shell[data-lucky-side-team]:not([data-tug]) .flag {
  transform: translate(-50%, -50%) rotate(var(--lucky-side-lean, 0deg));
  transition: transform 500ms ease;
}

/* Gold Rush: reward and richness — kept deliberately restrained. Only a low
   sepia/saturation filter (not a full-yellow wash) and text-color variables
   are never touched here, so contrast is unaffected. */
.overlay-shell.experience-gold-rush {
  filter: sepia(.1) saturate(1.08);
}
.overlay-shell.experience-gold-rush .rope-stage__track::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 30%, rgba(255,214,107,.35) 48%, transparent 66%);
  background-size: 220% 100%;
  background-position: 120% 0;
  animation: gold-rush-shimmer 2.6s linear infinite;
  pointer-events: none;
}
body[data-priority-active] .overlay-shell.experience-gold-rush .rope-stage__track::before {
  animation-play-state: paused;
  opacity: .3;
}
@keyframes gold-rush-shimmer { from { background-position: 120% 0; } to { background-position: -20% 0; } }

.particle-effect--coins i {
  background: linear-gradient(160deg, #ffe9a8, #ffd166 55%, #caa23a) !important;
  border-radius: 50% !important;
  box-shadow: 0 0 6px rgba(255, 214, 107, .8), inset 0 0 2px rgba(255,255,255,.6) !important;
}

body[data-tug] .rope-stage .rope {
  animation: bounded-rope-pull var(--pull-duration,620ms) cubic-bezier(.2,.82,.25,1) both !important;
  transform-origin: center;
}
body[data-tug][data-progress-fill="territory"] .rope-stage::after {
  animation: bounded-seam-recoil var(--pull-duration,620ms) cubic-bezier(.2,.82,.25,1) both !important;
}
body[data-tug]:not([data-battle-marker-style="territory"]) .flag-motion {
  animation: bounded-marker-jerk var(--pull-duration,620ms) cubic-bezier(.2,.82,.25,1) both !important;
}
body[data-tug][data-battle-marker-style="flag"] .flag-cloth {
  animation: flag-pull-flutter var(--pull-duration,620ms) ease-out 1 !important;
}
body[data-tug][data-battle-marker-style="ring"] .flag-knot { animation: bounded-ring-jerk 620ms ease-out both !important; }
body[data-tug][data-battle-marker-style="streamerLogo"] .streamer-logo-marker { animation: bounded-logo-tilt 620ms ease-out both !important; }

.overlay-shell[data-pull-size="medium"] .pull-toast-points { animation: points-impact-medium 620ms ease-out; }
.overlay-shell[data-pull-size="large"] .pull-toast-points,
.overlay-shell[data-pull-size="massive"] .pull-toast-points { animation: points-impact-large 780ms ease-out; }
.overlay-shell[data-pull-size="massive"] .rope-stage::before {
  opacity: .72 !important;
  filter: brightness(1.25);
}
.overlay-shell[data-pull-size="massive"] .pull-toast { box-shadow: 0 4px 12px rgba(0,0,0,.48), 0 0 9px rgba(255,255,255,.24) !important; }

body[data-lead-change="blue"] .team-panel--blue,
body[data-lead-change="red"] .team-panel--red { animation: team-lead-pulse 1.2s ease-out both !important; }
body[data-lead-change] .match-status { animation: center-status-pulse 1.2s ease-out both; }
body[data-tie-reaction="true"] .team-panel--blue,
body[data-tie-reaction="true"] .team-panel--red { animation: balanced-tie-pulse 1.05s ease-out both !important; }
body[data-tie-reaction="true"] .rope-stage::after,
body[data-tie-reaction="true"] .center-marker { animation: balanced-center-burst 1.05s ease-out both !important; }

body[data-winner="blue"] .team-panel--blue,
body[data-winner="red"] .team-panel--red { animation: winner-panel-surge 1.1s ease-out both !important; }
body[data-winner="blue"] .team-panel--red,
body[data-winner="red"] .team-panel--blue { opacity: .72; }
body[data-victory-style="off"] .victory-burst,
body[data-victory-style="clean"] .victory-burst { display: none !important; }
body[data-victory-style="maximum"][data-winner] .winner-message { animation: winner-maximum 1.15s ease-out both; }
body[data-victory-style="off"][data-winner] .overlay-shell { animation: none !important; }


@keyframes bounded-rope-pull {
  0%,100% { transform: translateX(0) scaleX(1); }
  25% { transform: translateX(var(--pull-shift,3px)) scaleX(var(--pull-stretch,1.018)); }
  50% { transform: translateX(var(--pull-recoil-shift,-2px)) scaleX(var(--pull-recoil,.992)); }
  72% { transform: translateX(var(--pull-settle-shift,1px)) scaleX(1.003); }
}
@keyframes bounded-seam-recoil { 35% { margin-left: var(--pull-shift,3px); } 70% { margin-left: var(--pull-recoil-shift,-2px); } }
@keyframes bounded-marker-jerk { 30% { transform: translateX(var(--pull-shift,3px)) rotate(var(--pull-lean,4deg)); } 70% { transform: translateX(var(--pull-recoil-shift,-2px)) rotate(var(--pull-recoil-lean,-1.5deg)); } }
@keyframes bounded-ring-jerk { 30% { transform: rotate(var(--pull-lean,5deg)) scale(1.045); } 70% { transform: rotate(var(--pull-recoil-lean,-2deg)); } }
@keyframes bounded-logo-tilt { 30% { transform: translate(-50%,-50%) rotate(var(--pull-lean,5deg)) scale(1.055); } 100% { transform: translate(-50%,-50%); } }
@keyframes flag-pull-flutter { 0%,100% { transform: none; } 28% { transform: rotate(var(--pull-lean,4deg)) skewY(4deg) scaleX(1.07); } 62% { transform: rotate(var(--pull-recoil-lean,-1deg)) skewY(-2deg) scaleX(.98); } }
@keyframes points-impact-medium { 32% { transform: scale(1.09); } }
@keyframes points-impact-large { 28% { transform: translateY(-1px) scale(1.17); text-shadow: 0 0 8px currentColor; } 70% { transform: scale(.99); } }
@keyframes team-lead-pulse { 38% { filter: brightness(1.28); transform: translateY(-1px); } }
@keyframes center-status-pulse { 40% { filter: brightness(1.22); } }
@keyframes balanced-tie-pulse { 38% { filter: brightness(1.2); } }
@keyframes balanced-center-burst { 38% { filter: brightness(1.45); } }
@keyframes winner-panel-surge { 35% { filter: brightness(1.42); transform: translateY(-2px) scale(1.018); } }
@keyframes winner-maximum { 30% { transform: scale(1.07); filter: brightness(1.35); } }
@keyframes frenzy-ripple { 45% { letter-spacing: .085em; } }
@keyframes reverse-cue { 35% { transform: translateX(-3px); } 62% { transform: translateX(3px); } }
@keyframes chaos-accent { 35% { transform: translateX(2px); filter: hue-rotate(10deg); } 55% { transform: translateX(-2px); } }
@keyframes underdog-energy { 42% { filter: brightness(1.28); } }

@media (prefers-reduced-motion: reduce) {
  body { --motion-distance: .18; }
  .particle-effect, .victory-burst { display: none !important; }
  body[data-tug] .rope-stage .rope,
  body[data-tug] .flag-motion,
  body[data-lead-change] .team-panel,
  body[data-tie-reaction] .team-panel,
  body[data-winner] .team-panel,
  .smart-event-banner { animation-duration: 1ms !important; animation-iteration-count: 1 !important; }
  body[data-idle-rope-motion] .rope-stage .rope { animation: none !important; }
  body[data-idle-rope-motion] .rope-stage__track::after { display: none !important; }
  body { --idle-breathe-amplitude-base: 0 !important; }
  .team-panel--blue, .team-panel--red, .match-status, .flag, .flag-cloth, .overlay-shell, .idle-glow-ring { animation: none !important; }
  body[data-heartbeat] .match-status::after,
  body[data-heartbeat] .overlay-shell,
  body[data-heartbeat] .flag { animation: none !important; }
  /* Screen shake specifically must never run under reduced motion, per
     explicit product rule — not just shortened like other one-shot effects. */
  body[data-critical-hit-impact] .overlay-shell { animation: none !important; }
  .overlay-shell::before,
  .overlay-shell::after,
  .overlay-shell.experience-chain-bonus,
  .overlay-shell.experience-gold-rush .rope-stage__track::before {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
  .overlay-shell[data-lucky-side-team] .flag { transition-duration: 1ms !important; }
}
.streamer-hud-body[data-streamer-hud-animations="off"] *,
.streamer-hud-body[data-streamer-hud-animations="off"] *::before,
.streamer-hud-body[data-streamer-hud-animations="off"] *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; }
.streamer-hud-body[data-streamer-hud-animations="reduced"] .particle-effect,
.streamer-hud-body[data-streamer-hud-animations="off"] .particle-effect { display: none !important; }

/* Overlay Designer: hover/selection affordances. These attributes are only
   ever set by the control panel's click delegate when this page is loaded
   inside the Overlay Designer's preview iframe — a real browser-source load
   (OBS/TikTok Studio) never sets them, so this never affects what viewers see. */
/* Editable regions read as clickable before any hover state registers, and
   only inside the designer preview — a real browser source never sets
   data-designer-preview, so the capture surface keeps its default cursor. */
body[data-designer-preview="true"] [data-designer-component] {
  cursor: pointer;
}

[data-designer-hover] {
  outline: 2px solid rgba(255, 255, 255, 0.55) !important;
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(255, 255, 255, 0.1) !important;
  transition: outline-color 120ms ease, box-shadow 120ms ease;
  cursor: pointer;
}

[data-designer-selected] {
  outline: 2px solid #28b8ff !important;
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(40, 184, 255, 0.22), 0 0 22px rgba(40, 184, 255, 0.35) !important;
  animation: designer-selected-pulse 2.2s ease-in-out infinite;
}

@keyframes designer-selected-pulse {
  0%, 100% { box-shadow: 0 0 0 5px rgba(40, 184, 255, 0.22), 0 0 14px rgba(40, 184, 255, 0.28); }
  50% { box-shadow: 0 0 0 6px rgba(40, 184, 255, 0.3), 0 0 26px rgba(40, 184, 255, 0.45); }
}

body[data-designer-focus] [data-designer-component]:not([data-designer-selected]) {
  animation-play-state: paused !important;
  opacity: 0.55;
  transition: opacity 220ms ease;
}

body[data-designer-focus] [data-designer-selected] {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  [data-designer-selected] { animation: none; }
}

/* The territory seam sparkles on a 1.1s loop, which is the fastest ambient
   motion on the overlay and the most eye-catching at the exact point a viewer
   watches. Still and Subtle hold the seam steady; the glow stays, the motion
   stops. Declared here rather than in styles.css so every idle-preset gate
   lives together. */
body[data-idle-style-preset="still"] .rope-stage::after,
body[data-idle-style-preset="subtle"] .rope-stage::after {
  animation: none !important;
}

/* Gift toast fit. OverlayView measures the rendered line against the centre
   panel and sets --toast-fit-scale when it would overflow; the username
   ellipsises first, and only once that is exhausted does the line scale.
   The username is the one part that may be sacrificed, so it absorbs the
   shrink; the gift value never does. */
/* The Text Size control owns these sizes via --overlay-toast-*-size, at a
   higher specificity than a .match-status-scoped rule can reach, so the fit
   scale multiplies that variable rather than trying to outrank it. Text Size
   therefore stays meaningful and the toast still fits. */
body[data-overlay-text-size] .pull-toast strong {
  font-size: calc(var(--overlay-toast-primary-size) * var(--toast-fit-scale, 1)) !important;
}
.match-status .pull-toast small {
  font-size: calc(clamp(.5rem, 1.2vw, .62rem) * var(--toast-fit-scale, 1)) !important;
}
/* Flex sizing for the row is declared once, in the deterministic-layout block
   below; repeating a conflicting flex-shrink here is how the shrink order got
   lost the first time. */
.pull-toast-username { min-width: 2.5ch; }

/* Deterministic gift-toast layout.

   place-content:center sizes the grid TRACK to max-content, so bounding the
   line itself achieved nothing — the item was 100% of an already-too-wide
   track. Give the track an explicit minmax(0, 1fr) so it can never exceed the
   centre panel, and the flex children below finally have a width to shrink
   against.

   The hierarchy the row then enforces, in order of what may be sacrificed:
     username  shrinks first, ellipsises, floors at a readable stub
     team      does not shrink; only a runaway team name is capped
     value     never shrinks, never ellipsises — it is the message

   The order has to be built out of flex-shrink, not out of source order.
   Giving the username and the team flex-shrink:1 each — which is what "shrinks
   first / shrinks second" was written as — makes flexbox remove space from
   both at once, in proportion to their base widths, so a line that is 35px too
   wide ellipsises the name AND the team rather than the name alone. Only one
   item may carry shrink for the sequence to hold, and it is the username.

   Once the username is down to its stub the row can no longer absorb anything,
   scrollWidth finally exceeds the panel, and OverlayView.fitPullToast scales
   the whole line — the designed last resort, which the double-shrink used to
   pre-empt by quietly destroying the text first. */
.match-status .pull-toast {
  grid-template-columns: minmax(0, 1fr) !important;
  justify-items: center !important;
  align-content: center !important;
}
.match-status .pull-toast > strong {
  /* Fills the column instead of shrinking to its own content.
     justify-items:center above would otherwise size this box to fit-content,
     and two separate rules measure against it: the team's max-width cap below,
     and OverlayView.fitPullToast's available width. Against a fit-content box
     both are circular — the allowance is derived from the very content it is
     meant to bound — so a SHORT username made the box narrow, which tightened
     the team's cap until "-> BLUE TEAM" ellipsised, and drove the fit pass down
     to its legibility floor on a line that had 150px to spare. The flex
     justify-content:center on the same element still centres the line, so this
     is invisible except that both measurements now refer to real space. */
  width: 100%;
  max-width: 100%;
  min-width: 0;
}
.pull-toast-username {
  flex: 1 1 auto;
  min-width: 2.5ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pull-toast-points {
  flex: 0 0 auto;
  overflow: visible;
  text-overflow: clip;
}
.pull-toast-team {
  /* Holds its width so the username absorbs the overflow alone. The cap is not
     a shrink — it only stops a custom team name long enough to crowd out the
     viewer entirely from taking more than its share of the line. "The line" is
     the full column, which is only true because strong is width:100% above. */
  flex: 0 0 auto;
  max-width: 45%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
