:root{
  --bg:#0f1216;
  --card:#171b21;
  --border:#262c35;
  --text:#e7ebf0;
  --muted:#9aa4b2;
  --accent:#7fd6f2;
  --accent-bright:#66f5ff;
  --accent-soft:rgba(127,214,242,.14);
  --ok:#4caf7d;
  /* Not cyan - cyan means "you can touch it" here. This is the ember
     orange already drifting behind the header. */
  --warn:#ffb765;
  /* Not the same value as --red below, and the difference is
     load-bearing: this one carries the "Guild not found" line on a card
     over the photo, where #e15c5c measured 4.27:1 at the photo's
     brightest point - under the 4.5 AA floor. Two steps of lightness,
     same hue and saturation, put it at 4.58. */
  --err:#e36565;
  --blue:#5b8ff0;
  --green:#4caf7d;
  --red:#e15c5c;
  /* The same two colours again, for the job the pair above cannot do. A
     fill may be dark - there is nothing in a dot or a bar to read - but
     --red as small text measures 3.49:1, and on a dark surface every
     legible red is a lighter one. So shapes keep --red and --green, and
     letters take these. Saturation, not lightness, is what keeps a
     lighter red reading as red rather than as salmon: this one clears
     4.5 by a tenth where the K/D figure actually sits, and a step darker
     is 4.09 and fails. css/maps.css has its own lighter pair. */
  --red-ink:#fb6a68;
  --green-ink:#3fbd83;
}
*{box-sizing:border-box;}
body{
  margin:0;
  padding:0;
  background:radial-gradient(circle at top, #1a1f27, #0f1216 60%);
  color:var(--text);
  font-family:'Segoe UI', system-ui, -apple-system, sans-serif;
}
/* ==========================================================
   BACKGROUND EFFECTS
   Fixed layer stack behind the page, in its own div so layers can
   be added without fighting over ::before/::after.
   ========================================================== */
.fx{
  /* Not `inset:0`. On mobile Chromium a fixed element is sized by the
     visual viewport, which changes height as the URL bar hides, and
     `cover` rescales every time - so the photo zoomed in and out while
     scrolling. lvh is the one height the URL bar cannot move. */
  position:fixed;left:0;top:0;width:100%;
  height:100vh;   /* Chromium already reads vh as the large viewport... */
  height:100lvh;  /* ...and this says so by spec rather than by habit. */
  pointer-events:none;z-index:0;overflow:hidden;
  /* Own stacking context, so the blend below cannot reach the page. */
  isolation:isolate;
  /* Nothing in here affects anything outside it, and saying so keeps a
     moving ember from invalidating the page above. paint is what
     overflow:hidden already does; this just makes it a promise the
     compositor can act on. */
  contain:layout paint style;
}
.fx-l{position:absolute;inset:0;}

/* The page's own colour, as the surface the photo blends into. */
.fx-ground{background:radial-gradient(circle at top, #1a1f27, #0f1216 60%);}

/* mix-blend-mode:luminosity keeps only the light and dark of the
   screenshot and takes hue and saturation from the layer under it, so
   the photo comes back in the site's slate. That is why one this busy
   can sit behind text, and why swapping it needs no colour grading. */
.fx-photo{
  /* Left of centre: the castle is in the left third, and `center`
     cropped it away on a tall window. */
  background:url("../assets/wvw13-1.webp") 22% 42%/cover no-repeat;
  filter:brightness(.72) contrast(1.04) blur(1.5px);
  mix-blend-mode:luminosity;
  opacity:.58;
  /* Its own layer. A full-viewport blur under a blend mode is the most
     expensive thing on the page, and the embers moving in the same box
     were dirtying it. transform, not filter: naming filter here would
     say the blur is about to change. */
  will-change:transform;
}

/* Darkest through the middle where the text sits. Inverse of a
   vignette. */
.fx-scrim{
  background:
    radial-gradient(ellipse 62% 56% at 50% 45%,
      rgba(15,18,22,.84), rgba(15,18,22,.44) 62%, rgba(15,18,22,.10) 100%),
    linear-gradient(180deg,
      rgba(15,18,22,.55), transparent 28%, transparent 70%, rgba(15,18,22,.66));
}

/* The canvas the ember field is drawn into, deliberately a sibling of
   .fx and not a layer inside it. .fx is an isolated group with a
   full-viewport mix-blend-mode layer, and a blend has to re-read its
   backdrop: while the embers lived in there, anything moving meant
   composing the group again - photo, blur and blend - 144 times a
   second. Out here nothing carries a blend mode and it lands on the same
   pixels. Everything drawn inside is in js/boot.js. */
.fx-embers{
  position:fixed;left:0;top:0;width:100%;
  height:100vh;   /* lvh for the same reason as .fx above. */
  height:100lvh;
  pointer-events:none;z-index:0;overflow:hidden;
  contain:layout paint style;
}

/* A finished check reaches the background: the field lifts, flares
   and settles, so the impact and the scenery read as one event. */
.fx-embers.surge{animation:ember-surge 2.8s cubic-bezier(.15,.8,.35,1);}
@keyframes ember-surge{
  0%{filter:brightness(1);transform:translateY(0);}
  7%{filter:brightness(2.5) saturate(1.3);transform:translateY(-12px);}
  100%{filter:brightness(1);transform:translateY(0);}
}
/* Nothing loops while the window is not being watched. `*` rather than a
   list of the looping animations, because a list misses whatever gets
   added next - it already missed the first-place sheen. Paused, not
   disabled, so coming back is instant. Set in idleWhenUnwatched,
   js/boot.js. The exceptions are the animations that run once and are
   supposed to end: freezing those is a stuck frame, not a pause. */
.is-idle *:not(.value-changed):not(.delta):not(.match-panel),
.is-idle *::before,
.is-idle *:not(.side-pulse)::after{animation-play-state:paused !important;}

/* ==========================================================
   SCROLLBARS
   The popovers and the map's detail column. -webkit- pseudo-elements
   and not scrollbar-width/scrollbar-color: declaring those in
   Chromium 121+ turns these rules off entirely. Firefox keeps its
   own scrollbar, which is the trade.
   ========================================================== */
.info-popover::-webkit-scrollbar,
.wvw-detail::-webkit-scrollbar{width:9px;}

.info-popover::-webkit-scrollbar-track,
.wvw-detail::-webkit-scrollbar-track{background:transparent;}

/* Not ::-webkit-resizer. When an element is resizable AND scrolls,
   Chromium reserves a square below the scrollbar for the grip and
   paints it here, opaque near-white. Styling the track and the thumb
   never reached it. */
.info-popover::-webkit-scrollbar-corner,
.wvw-detail::-webkit-scrollbar-corner{background:transparent;}

/* Transparent border clipped to the padding box: 5px of colour with
   air either side, instead of a 9px bar against the wall. */
.info-popover::-webkit-scrollbar-thumb,
.wvw-detail::-webkit-scrollbar-thumb{
  background-color:rgba(154,164,178,.30);
  border:2px solid transparent;
  background-clip:padding-box;
  border-radius:99px;
}
.info-popover::-webkit-scrollbar-thumb:hover,
.wvw-detail::-webkit-scrollbar-thumb:hover{
  background-color:rgba(127,214,242,.55);
}

/* Firefox, which has none of the above. It cannot go outside this
   @supports: declaring scrollbar-width in Chromium turns every -webkit-
   rule above off, and Firefox does not know ::-webkit-scrollbar, so it
   is the only engine that gets here. */
@supports not selector(::-webkit-scrollbar) {
  .info-popover,
  .wvw-detail{
    scrollbar-width:thin;
    scrollbar-color:rgba(154,164,178,.45) transparent;
  }
}

/* ==========================================================
   THE REST OF THE BROWSER'S OWN CHROME
   Four things the user agent still paints in the OS palette. Bare
   selectors on purpose: scoping them to today's elements means the
   next input quietly gets the white one back.
   ========================================================== */

/* The resize grip, and the one rule here that is scoped: `resize` is on
   two unalike elements. The textarea is a solid sunken field and keeps
   the browser's grip. On the popover the narrowed scrollbar leaves that
   corner 9px wide, where anything drawn reads as a smudge. */
.info-popover--guilds::-webkit-resizer{background:none;}

/* Background only, never a colour: a colour means a team on this page
   (see standings.css), and forcing selected text to one shade would
   break that for as long as the selection lasts. */
::selection{background-color:rgba(127,214,242,.22);}

/* The UA grey read as content. Dim enough to say "example", still
   4.99:1. */
::placeholder{color:#79838f;opacity:1;}

/* Keyboard focus. On the element types and not the classes: there are
   thirteen kinds of button here, and listing them means the fourteenth
   gets Chromium's white ring back. Outline, never box-shadow - the
   buttons carry their relief in stacked box-shadows and a focus rule
   that set one would wipe it. The offset puts the ring on the dark card
   behind the button. .wvw-marker opts out in maps.css. */
button:focus-visible,
a:focus-visible{
  outline:2px solid var(--accent-bright);
  outline-offset:2px;
}

/* Android's default is a grey-black wash. On the root because the
   property inherits, so it covers whatever gets written next. */
html{-webkit-tap-highlight-color:rgba(127,214,242,.18);}
