/* Streambar Scene — the full-canvas overlay (an overlay of type `scene`).
 *
 * The rules this file keeps, and must keep:
 *   * ONE fixed canvas, 1920 x 1080, and TRANSPARENT. A browser source that
 *     covers the whole scene must never black out what is underneath it, so
 *     every pixel this stylesheet does not paint shows the game, the desktop
 *     and the camera through.
 *   * NO COORDINATES LIVE HERE. Every block is positioned out of the CSS
 *     variables scene.js writes from SCENE_GEOMETRY (--sc-*, --s1-*, --s2-*,
 *     --br-*), so moving a block is an edit in the module's table and nowhere
 *     else. What a preset changes HERE is only which way a block flows.
 *   * NO CAMERA HOLE. A scene masks nothing out: the blocks hug the edges and
 *     the middle is left clear for the creator's camera.
 *   * Colours arrive as CSS VARIABLES the module wrote from the overlay's own
 *     theme and its colour roles (031), never as tenant text in a rule. Each
 *     role reads `var(--r-…, <what the theme gives>)`, so an unset one inherits
 *     exactly what was drawn before it existed (scene.js, roleVars).
 *   * THE TWELVE BACKGROUNDS ARE PURE CSS, gradients keyed off
 *     [data-bg="preset"][data-bgp="…"]. Only an upload loads an image, and only
 *     after the module has proved its URL one of our own storage objects.
 *   * MOTION IS `transform` and `opacity`, nothing else, and [data-motion] or
 *     the media query switches every bit of it off.
 */

:root { color-scheme: normal; }

.sn {
  position: relative;
  width: 1920px;
  height: 1080px;
  box-sizing: border-box;
  overflow: hidden;
  background: transparent;
  pointer-events: none;
  /* The theme, as the module writes it, with the product defaults behind every
     one — a scene draws something even if a config never arrives. Each colour
     has its `-rgb` triple beside it, because most of what this file does with a
     colour is a TINT of it (a translucent panel, a hairline, a chip wash). */
  --sn-primary: #7B2FBE;    --sn-primary-rgb: 123, 47, 190;
  --sn-secondary: #1A0A2E;  --sn-secondary-rgb: 26, 10, 46;
  --sn-accent: #E5B800;     --sn-accent-rgb: 229, 184, 0;
  --sn-text: #FFFFFF;       --sn-text-rgb: 255, 255, 255;
  --sn-brand: #7B2FBE;      --sn-brand-rgb: 123, 47, 190;
  --sn-panel: #1A0A2E;      --sn-panel-rgb: 26, 10, 46;
  /* The background's own two stops (kind `colour` / `gradient`). */
  --sn-bg1: #101418;        --sn-bg1-rgb: 16, 20, 24;
  --sn-bg2: #1E2A38;
  /* The logo dial (theme.logoSize / 100) and the fade the rotation spends. */
  --sn-logo: 1;
  --sn-fade: 280ms;
  /* THE PANEL. Every block that carries writing sits on one, so it stays
     legible over a white stadium light or a near-black studio alike. */
  --sn-plate-a: rgba(var(--r-scores-bg-rgb, var(--sn-panel-rgb)), .82);
  --sn-plate-b: rgba(var(--r-scores-bg-end-rgb, var(--r-scores-bg-rgb, var(--sn-panel-rgb))), .94);
  --sn-spot-a: rgba(var(--r-bar-bg-rgb, var(--sn-panel-rgb)), .80);
  --sn-spot-b: rgba(var(--r-bar-bg-rgb, var(--sn-panel-rgb)), .93);
  --sn-hair: rgba(var(--r-scores-chip-rgb, var(--sn-accent-rgb)), .30);
  font-family: var(--sn-font, system-ui, -apple-system, "Segoe UI", sans-serif);
  font-weight: 700;
  font-synthesis: none;
  color: var(--sn-text);
  opacity: 1;
  transition: opacity 0.3s ease;
}
/* Hidden is the bar's own rule: the source stays in the scene and stops
   drawing. Never display/geometry — OBS keeps the page warm either way. */
.sn[data-hidden="1"] { opacity: 0; }
.sn[data-case="upper"] .sn-text { text-transform: uppercase; }

/* ---- the background layer ----------------------------------------------
 * ONE node, whole and unmasked: there is no camera hole to cut. */
.sn-bg {
  position: absolute;
  left: 0; top: 0; right: 0; bottom: 0;
}

/* THE TWELVE PRESETS. Each is a stack of gradients over a flat colour, and each
   has to read well behind BOTH white and dark writing — hence the vignette or
   wash that keeps its middle away from the ends of the range. */
.sn[data-bg="preset"][data-bgp="stadium"] .sn-bg {
  background-color: #0A1526;
  background-image:
    radial-gradient(58% 44% at 20% 14%, rgba(150, 195, 255, .30), transparent 72%),
    radial-gradient(52% 40% at 80% 10%, rgba(95, 150, 240, .24), transparent 72%),
    radial-gradient(90% 65% at 50% 108%, rgba(4, 10, 22, .92), transparent 70%),
    linear-gradient(180deg, #0E2039, #050B16);
}
.sn[data-bg="preset"][data-bgp="dark"] .sn-bg {
  background-color: #07090C;
  background-image: linear-gradient(180deg, #171C25, #05070A);
}
.sn[data-bg="preset"][data-bgp="neon"] .sn-bg {
  background-color: #120A20;
  background-image:
    repeating-linear-gradient(90deg, rgba(var(--sn-accent-rgb), .16) 0 2px, transparent 2px 96px),
    repeating-linear-gradient(0deg, rgba(var(--sn-accent-rgb), .12) 0 2px, transparent 2px 96px),
    radial-gradient(72% 58% at 50% 0%, rgba(150, 70, 255, .34), transparent 72%),
    linear-gradient(180deg, #1D0E35, #090512);
}
.sn[data-bg="preset"][data-bgp="wood"] .sn-bg {
  background-color: #3A2416;
  background-image:
    repeating-linear-gradient(92deg, rgba(0, 0, 0, .12) 0 7px, rgba(255, 236, 200, .04) 7px 17px),
    radial-gradient(72% 55% at 50% 2%, rgba(255, 198, 124, .18), transparent 72%),
    linear-gradient(180deg, #5E3D24, #27170D);
}
.sn[data-bg="preset"][data-bgp="pitch"] .sn-bg {
  background-color: #14572A;
  background-image:
    repeating-linear-gradient(90deg, rgba(255, 255, 255, .05) 0 128px, transparent 128px 256px),
    radial-gradient(84% 62% at 50% 0%, rgba(196, 255, 204, .12), transparent 72%),
    linear-gradient(180deg, #1B6E35, #0A381B);
}
.sn[data-bg="preset"][data-bgp="light"] .sn-bg {
  background-color: #ECEEF2;
  background-image:
    radial-gradient(70% 55% at 24% 8%, rgba(255, 255, 255, .95), transparent 72%),
    linear-gradient(180deg, #F5F6F9, #D5DAE3);
}
.sn[data-bg="preset"][data-bgp="carbon"] .sn-bg {
  background-color: #0B0D10;
  background-image:
    repeating-linear-gradient(45deg, rgba(255, 255, 255, .055) 0 3px, transparent 3px 9px),
    repeating-linear-gradient(-45deg, rgba(255, 255, 255, .045) 0 3px, transparent 3px 9px),
    radial-gradient(86% 70% at 50% 0%, rgba(120, 140, 165, .16), transparent 74%),
    linear-gradient(180deg, #171A1F, #05070A);
}
.sn[data-bg="preset"][data-bgp="aurora"] .sn-bg {
  background-color: #041018;
  background-image:
    radial-gradient(62% 46% at 26% 8%, rgba(60, 235, 190, .32), transparent 72%),
    radial-gradient(56% 42% at 72% 2%, rgba(120, 110, 255, .30), transparent 72%),
    radial-gradient(70% 50% at 50% 104%, rgba(2, 8, 14, .92), transparent 70%),
    linear-gradient(180deg, #08283A, #030A12);
}
.sn[data-bg="preset"][data-bgp="sunset"] .sn-bg {
  background-color: #2A0E1E;
  background-image:
    radial-gradient(76% 52% at 50% 6%, rgba(255, 176, 92, .38), transparent 74%),
    radial-gradient(60% 44% at 18% 26%, rgba(255, 96, 128, .26), transparent 72%),
    linear-gradient(180deg, #4A1B33, #170716);
}
.sn[data-bg="preset"][data-bgp="navy"] .sn-bg {
  background-color: #070E1C;
  background-image:
    radial-gradient(74% 58% at 50% 34%, rgba(56, 96, 170, .34), transparent 76%),
    radial-gradient(100% 78% at 50% 100%, rgba(2, 5, 12, .94), transparent 66%),
    linear-gradient(180deg, #102040, #04080F);
}
.sn[data-bg="preset"][data-bgp="nebula"] .sn-bg {
  background-color: #0B0518;
  background-image:
    radial-gradient(48% 38% at 30% 22%, rgba(196, 92, 255, .34), transparent 70%),
    radial-gradient(44% 34% at 74% 14%, rgba(92, 128, 255, .26), transparent 70%),
    radial-gradient(56% 44% at 58% 88%, rgba(255, 96, 176, .18), transparent 72%),
    linear-gradient(180deg, #1B0B34, #06030E);
}
.sn[data-bg="preset"][data-bgp="steel"] .sn-bg {
  background-color: #2B2F35;
  background-image:
    repeating-linear-gradient(90deg, rgba(255, 255, 255, .05) 0 2px, rgba(0, 0, 0, .05) 2px 5px),
    radial-gradient(84% 64% at 50% 0%, rgba(255, 255, 255, .16), transparent 74%),
    linear-gradient(180deg, #4A5058, #1C2026);
}
/* …and the three the creator chooses the colours of. */
.sn[data-bg="colour"] .sn-bg { background-color: var(--sn-bg1); }
.sn[data-bg="gradient"] .sn-bg { background-image: linear-gradient(160deg, var(--sn-bg1), var(--sn-bg2)); }
/* An UPLOAD: the module writes the url() inline once the URL has been proved
   one of ours. `dark` sits behind it as a flat colour, so a slow object never
   flashes the scene see-through on the way in. */
.sn[data-bg="upload"] .sn-bg {
  background-color: #0B0E13;
  background-size: cover;
  background-position: 50% 50%;
  background-repeat: no-repeat;
}

/* ---- the brand tag: logo + handle + LIVE --------------------------------
 * The LOGO and the HANDLE — the username, and only that. A scene draws no brand
 * name: the module sends no node for one. The tag GROWS to what is written on
 * it, inside one 940px run, and the handle's size (--sn-hp) steps down with its
 * length — measured per face by scene-fit.js, so it is never cut. */
.sn-brand {
  position: absolute;
  left: var(--br-x); top: var(--br-y);
  width: max-content; max-width: var(--br-w); height: var(--br-h);
  display: flex;
  align-items: center;
  gap: 18px;
  box-sizing: border-box;
  padding: 0 20px;
  border-radius: 14px;
  background: linear-gradient(180deg, var(--sn-spot-a), var(--sn-spot-b));
  border: 1px solid rgba(var(--r-bar-accent-rgb, var(--sn-accent-rgb)), .28);
  color: var(--r-bar-text, var(--sn-text));
}
/* DESK's tag is the one in a RIGHT-hand corner: pinned by its right edge, from
   its own block, so it grows leftwards where the others grow right. */
.sn[data-preset="desk"] .sn-brand { left: auto; right: calc(1920px - var(--br-x) - var(--br-w)); }
.sn-brand.is-off { opacity: 0; }
/* NO HANDLE, JUST THE MARK: with nothing written beside it the plate has
   nothing left to hold together, so it goes and the logo stands on its own, on
   the grid line its block already had. The LIVE pill stays (see scene.js). */
.sn-brand.is-bare {
  padding: 0;
  border: 0;
  background: none;
}
.sn-logo {
  flex: 0 0 auto;
  height: calc(90px * var(--sn-logo));
  max-width: calc(260px * var(--sn-logo));
  object-fit: contain;
}
.sn-logo.is-off { display: none; }
.sn-handle {
  font-size: var(--sn-hp, 26px);
  min-width: 0;              /* the run is the last word: clip, never overhang it */
  line-height: 1.1;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  color: rgba(var(--r-bar-text-rgb, var(--sn-text-rgb)), .68);
}
.sn-handle:empty { display: none; }
/* The LIVE pill — on the tag, and only when the overlay is live. */
.sn-live {
  display: none;
  flex: 0 0 auto;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--r-bar-accent, var(--sn-accent));
  color: #000;
}
.sn[data-live="1"] .sn-live { display: flex; }
.sn-dot {
  width: 12px; height: 12px;
  border-radius: 50%;
  background: #B3121B;
  animation: snPulse 1.6s ease-in-out infinite;
}
.sn-live-t { font-size: 20px; letter-spacing: .12em; }

/* ---- the scores block ---------------------------------------------------
 * STACKED, always: a competition chip, then a row per game, paged by
 * SCENE_GEOMETRY's `rows`. Arena is the one preset whose block is a STRIP
 * rather than a column, so its page flows across instead of down — the same
 * nodes, one flex-direction apart. */
.sn-scores {
  position: absolute;
  left: var(--sc-x); top: var(--sc-y);
  width: var(--sc-w); height: var(--sc-h);
  box-sizing: border-box;
  border-radius: 16px;
  background: linear-gradient(180deg, var(--sn-plate-a), var(--sn-plate-b));
  border: 1px solid var(--sn-hair);
  color: var(--r-scores-text, var(--sn-text));
}
.sn-scores.is-off { opacity: 0; }
.sn-sc-page {
  position: absolute;
  left: 16px; right: 16px; top: 12px; bottom: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
  transition: opacity var(--sn-fade) ease;
}
.sn-sc-page.is-out { opacity: 0; }
.sn[data-preset="arena"] .sn-sc-page { flex-direction: row; align-items: center; gap: 22px; }

.sn-group {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 34px;
  padding-left: 12px;
  border-left: 4px solid var(--r-scores-chip, var(--sn-accent));
}
.sn-gr-crest { width: 26px; height: 26px; object-fit: contain; }
.sn-gr-name {
  font-size: 20px;
  letter-spacing: .10em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  color: var(--r-scores-chip, var(--sn-accent));
}
.sn-game {
  flex: 1 1 0;
  min-height: 56px;
  max-height: 128px;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  border-top: 2px solid rgba(var(--r-scores-divider-rgb, var(--sn-text-rgb)), .14);
}
.sn-sc-page > .sn-group + .sn-game { border-top: 0; }
.sn[data-preset="arena"] .sn-game { border-top: 0; min-height: 0; gap: 12px; }
.sn-g-crest { flex: 0 0 auto; width: 32px; height: 32px; object-fit: contain; }
/* THE ROW'S SIZES ARE MEASURED, not chosen (scene-fit.js): studio and desk draw
   at these — small enough that neither has to trim a team's short name — and
   arena, which has twice the width, steps UP at the foot of this section. */
.sn-g-abbr {
  flex: 1 1 0;
  min-width: 0;              /* a column IS its half of what the row has left */
  font-size: 24px;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  color: var(--r-scores-abbr, var(--r-scores-text, var(--sn-text)));
}
.sn-g-abbr.is-a { text-align: right; }
.sn-g-score {
  flex: 0 0 auto;
  min-width: 40px;
  text-align: center;
  font-size: 32px;
  line-height: 1;
  color: var(--r-scores-score, var(--r-scores-text, var(--sn-text)));
}
.sn-g-dash { flex: 0 0 auto; font-size: 22px; color: var(--r-scores-divider, rgba(var(--sn-text-rgb), .45)); }
.sn-g-time {
  flex: 0 0 auto;
  min-width: 56px;
  max-width: 136px;          /* the worst SCORES_LABELS + padding, at arena's size */
  overflow: hidden;
  white-space: nowrap;
  text-align: center;
  padding: 5px 8px;
  border-radius: 8px;
  font-size: 16px;
  letter-spacing: .06em;
  background: rgba(var(--r-scores-status-rgb, var(--sn-text-rgb)), .10);
  color: rgba(var(--r-scores-status-rgb, var(--sn-text-rgb)), .55);
}
.sn-game[data-status="live"] .sn-g-time {
  background: var(--r-scores-live, var(--r-scores-status, var(--sn-accent)));
  color: #000;
  animation: snFlash 1.6s ease-in-out infinite;
}
/* ARENA ALONE: a 1766px page, twice studio's, so its row steps back UP to the
   sizes the scene shipped with (scene-fit.js, SCORES_CSS_ARENA). */
.sn[data-preset="arena"] .sn-g-crest { width: 40px; height: 40px; }
.sn[data-preset="arena"] .sn-g-abbr { font-size: 30px; }
.sn[data-preset="arena"] .sn-g-score { min-width: 46px; font-size: 42px; }
.sn[data-preset="arena"] .sn-g-dash { font-size: 26px; }
.sn[data-preset="arena"] .sn-g-time { min-width: 76px; padding: 5px 10px; font-size: 18px; }

/* ---- the message spots -------------------------------------------------
 * One message at a time, faded. NOTHING IS EVER CUT SHORT: the panel limits it
 * to what this box holds in the creator's face (scene-fit.js). */
.sn-spot {
  position: absolute;
  left: var(--s1-x); top: var(--s1-y);
  width: var(--s1-w); height: var(--s1-h);
  box-sizing: border-box;
  border-radius: 14px;
  background: linear-gradient(180deg, var(--sn-spot-a), var(--sn-spot-b));
  border: 1px solid rgba(var(--r-bar-accent-rgb, var(--sn-accent-rgb)), .24);
  color: var(--r-bar-text, var(--sn-text));
}
.sn-spot.is-2 {
  left: var(--s2-x); top: var(--s2-y);
  width: var(--s2-w); height: var(--s2-h);
}
.sn-spot.is-off { opacity: 0; }
.sn-msg {
  display: flex;
  align-items: center;
  gap: 16px;
  height: 100%;
  padding: 0 28px;
  box-sizing: border-box;
  transition: opacity var(--sn-fade) ease;
}
.sn-msg.is-out { opacity: 0; }
.sn-emoji { flex: 0 0 auto; font-size: 42px; line-height: 1; }
.sn-emoji.is-off { display: none; }
.sn-text {
  flex: 1 1 0;
  min-width: 0;
  font-size: 42px;
  line-height: 1.1;
  letter-spacing: .02em;
  white-space: nowrap;
  overflow: hidden;
}

/* ---- how a spot is DRAWN: [data-style] ---------------------------------
 * Three looks, ONE box: the geometry is the module's grid whichever is chosen,
 * so the limits scene-fit measured hold for all of them. `pill` is the only one
 * that moves a wall, by padding — 56px, which IS scene-fit.js's
 * SPOT_CSS.padXPill and what the pill's own tighter limit is divided out of.
 * Keep the two equal. */
.sn-spot[data-style="pill"] { border-radius: 999px; }
.sn-spot[data-style="pill"] .sn-msg { padding: 0 56px; }
/* `text` takes the plate away and leaves the type, which then has to read over
 * whatever the creator has behind it: a SHADOW, never a filter — a filter would
 * rasterise the whole run and cost a compositor layer per frame. The border
 * stays, transparent, so the box is exactly the same box. */
.sn-spot[data-style="text"] { background: none; border-color: transparent; }
.sn-spot[data-style="text"] .sn-text,
.sn-spot[data-style="text"] .sn-emoji { text-shadow: 0 2px 6px rgba(0, 0, 0, .75), 0 0 20px rgba(0, 0, 0, .5); }
/* ---- where it sits: [data-align] --------------------------------------- */
.sn-spot[data-align="centre"] .sn-msg { justify-content: center; }
.sn-spot[data-align="centre"] .sn-text { flex: 0 1 auto; }

/* ---- how it MOVES: [data-rot] ------------------------------------------
 * The rotation, and not to be confused with `data-motion`, which is the
 * reduced-motion answer read once at mount. Fade is the cross-fade above.
 * VERTICAL rolls the outgoing line up and out and brings the next one up from
 * below — two keyframes rather than one transition, so the incoming half always
 * starts from under the box whatever the outgoing half left behind. */
.sn[data-rot="vertical"] .sn-msg { transition: none; }
.sn[data-rot="vertical"] .sn-msg.is-out { animation: snRollOut var(--sn-fade) ease forwards; }
.sn[data-rot="vertical"] .sn-msg.is-in { animation: snRollIn var(--sn-fade) ease; }
/* HORIZONTAL is the marquee, and a CSS animation and nothing else: there is no
 * rAF on this page. The module measures the run, divides the travel by the
 * speed step's pixels a second and writes both ends and the duration as
 * variables, so the whole of it is one transform on a linear ramp. */
.sn[data-rot="horizontal"] .sn-spot { overflow: hidden; }
.sn[data-rot="horizontal"] .sn-msg {
  width: max-content;
  transition: none;
  animation: snMarquee var(--tk-dur, 30s) linear infinite;
  will-change: transform;
}
.sn[data-rot="horizontal"] .sn-text { flex: 0 0 auto; overflow: visible; }

/* ---- the locked scene --------------------------------------------------
 * A lapsed Pro account keeps its scene on screen as a plain dark inner frame
 * and our small tag (scene.js, build). It must never black anything out. */
.sn-lock {
  position: absolute;
  left: 0; top: 0; right: 0; bottom: 0;
  border: 6px solid rgba(10, 12, 16, .85);
  border-radius: 6px;
}
.sn-tag {
  position: absolute;
  right: 60px;
  bottom: 120px;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: .08em;
  padding: 8px 16px;
  border-radius: 8px;
  background: rgba(10, 12, 16, .70);
  color: rgba(255, 255, 255, .78);
}

/* ---- motion ------------------------------------------------------------
 * Five keyframes, on opacity and transform and NOTHING else, every one of them
 * switched off by either the media query or the module's own
 * [data-motion="reduce"] — the same answer read once at mount, so the pair
 * never disagree. With them off a message is rewritten instantly and a marquee
 * would stand still, which is why the module steps a ticker down to the
 * one-at-a-time rotation the moment it reads reduced motion. */
@keyframes snPulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@keyframes snFlash { 0%, 100% { opacity: 1; } 50% { opacity: .72; } }
@keyframes snRollOut { from { transform: translateY(0); opacity: 1; } to { transform: translateY(-46px); opacity: 0; } }
@keyframes snRollIn { from { transform: translateY(46px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
@keyframes snMarquee { from { transform: translateX(var(--tk-x0, 100%)); } to { transform: translateX(var(--tk-x1, -100%)); } }

.sn[data-motion="reduce"],
.sn[data-motion="reduce"] * { animation: none !important; transition: none !important; }
@media (prefers-reduced-motion: reduce) {
  .sn, .sn * { animation: none !important; transition: none !important; }
}
