/* NAVCSS:START (generated by nav-build.js - do not hand-edit) */
/* ---- the header is ONE row, from the Figma frame ----
   Founder 2026-09-13: "make sure we are in one line with the figma design. we
   strayed a bit away from it." The Figma header (node 18-739, Frame 28) is a
   single 53px row: the lime wordmark on the left, Login and Feedback on the
   right, a 1px #2b2b2b rule underneath, body/medium type (Geist 500, 14px).
   The site had grown a second row of uppercase monospace segments under it.

   The games stay IN the row, as plain text links between the wordmark and the
   buttons. The frame has nothing there, but the locked rule is that every page
   is reachable from the top navigation, and a header that hides the other four
   games behind the logo breaks that. Plain text is the least visible way to
   keep the rule without inventing a component the design does not have.

   Below 900px the row cannot hold the wordmark, six links and two buttons, so
   the bar drops onto its own scrolling line under the wordmark - the one width
   where the old two-row layout was the right answer. The Figma file has no
   narrow frame to follow. */
.top{overflow-x:hidden;border-bottom-color:#2b2b2b}
/* 52 plus the 1px rule is the frame's 53.
   The wordmark: Icon/Fill/film at 32px, then the name 2px after it in Mulish 800
   24px, both accent/primary. Here rather than in theme-build's COMPONENTS
   because nav.css is the one stylesheet EVERY page links, the 55 generated
   Marvel Rivals pages and privacy and account included. (0,2,x) so it beats
   each page's own .brand rules without an id or !important. */
.top .brand{display:inline-flex;align-items:center;gap:2px;min-height:44px;flex:0 1 auto;min-width:0;text-decoration:none;color:var(--nb-on,var(--gs-on,#c8ff62))}
.top .brand .mk{width:32px;height:32px;flex:0 0 auto;color:var(--nb-on,var(--gs-on,#c8ff62))}
.top .brand b{display:inline;font-family:var(--disp,"Mulish",system-ui,sans-serif);font-weight:800;font-size:24px;line-height:1;letter-spacing:0;text-transform:none;white-space:nowrap;color:var(--nb-on,var(--gs-on,#c8ff62))}
.top .brand .crumb{display:none}
/* On a phone the three items fit by giving up spacing, then the name's size,
   then the name itself - the link keeps it as its accessible name. */
@media(max-width:420px){.top > .wrap, .top > .in, .top > .topin{column-gap:8px}.nb-gap{min-width:0}.nb-login{margin-left:0}}
@media(max-width:420px){.top .brand b{font-size:20px}}
@media(max-width:380px){.top .brand b{font-size:18px}}
@media(max-width:340px){.top .brand b{display:none}}
/* THE WORDMARK GIVES WAY, THE BUTTONS DO NOT (2026-09-21).
   The four rules above are keyed to WIDTH, and the thing that overruns this
   header is TEXT SIZE - a reader who sets 150% or 200% in their browser or
   phone keeps the same 390px viewport, so not one of those queries fires.
   Measured: the brand text run goes 110px -> 164px -> 219px at 100/150/200%,
   and from 150% up it is painted UNDER the Login/Feedback group on every page,
   which reads as a broken page rather than a big-text page. The .nb-in strip it
   runs into is overflow-x:auto with the scrollbar deliberately hidden, so
   Feedback simply leaves the viewport with nothing on screen saying it is there.
   b is a flex item here, so it is already blockified: overflow+ellipsis is all
   it needs, and min-width:0 is what actually lets a flex item shrink below its
   content. The order of sacrifice is deliberate - the wordmark is decoration on
   a page that already says what it is, while Login and Feedback are the only
   two things in this header a reader can DO, and Feedback is how they tell us
   the site is hard to use at that text size. */
.top .brand b{min-width:0;overflow:hidden;text-overflow:ellipsis}
.top .brand{overflow:hidden}
/* The pills must never be the thing that shrinks or scrolls away. */
.top .nb-login,.top .nb-i{flex:0 0 auto}
.top > .wrap, .top > .in, .top > .topin{flex-wrap:nowrap;height:auto;min-height:52px;padding-top:0;padding-bottom:0;row-gap:0;column-gap:16px}
/* ONE HEADER BOX ON EVERY PAGE. Each page family sized its header by its own
   content column: 1120px on the game pages, 1126 on the homepage, 1100 on the
   Marvel Rivals hero pages, 1160 on Login and What's coming, 820 on Privacy. So
   the wordmark sat at 108, 109, 110, 92 and 250px on a 1280px screen and the
   whole header jumped sideways between pages (founder, 2026-09-14: "on login
   the top header ... is different width a little"). The header is the part that
   must not move, so it takes the game pages' box everywhere and each page's
   CONTENT keeps its own width underneath. html raises the specificity past the
   pages' own .top .in rules. */
html .top > .wrap, html .top > .in, html .top > .topin{box-sizing:border-box;width:100%;max-width:1120px;margin-left:auto;margin-right:auto;padding-left:clamp(16px,4vw,28px);padding-right:clamp(16px,4vw,28px)}
/* ...and one surface. Measured on staging the same day: Login, verify and reset
   still painted the retired sapphire rgba(13,17,23,.86), Privacy the same at .92,
   the hero pages #0a0a0a at .92 and the blur was 8px on some pages and 9 on
   others. Privacy also had no font smoothing, so on a Mac its Login and Feedback
   rendered visibly heavier. The game pages are the reference. */
html .top{background:rgba(10,10,10,.86);-webkit-backdrop-filter:blur(9px);backdrop-filter:blur(9px);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
/* The header height is measured by the JS below and published as --topbar-h,
   so an anchor jump lands under it rather than beneath it. Per-element
   scroll-margin-top still ADDS to this. */
html{scroll-padding-top:calc(var(--topbar-h,53px) + 14px)}
/* min-width:0 is load-bearing, not tidiness. A flex item's automatic minimum
   size is its CONTENT, so without it the bar pushes the header wider than the
   viewport, .top clips the overflow, and the links past the edge simply are not
   on screen. The document does not scroll, so an overflow check measuring
   innerWidth against scrollWidth calls it clean. */
.nb{flex:1 1 auto;order:5;position:relative;min-width:0;max-width:100%}
/* flex:1 because a page may make every nav in its header a flex container
   (the homepage does: .top nav{display:flex}), and then this row shrink-wraps
   and Login and Feedback stop reaching the right edge. */
.nb-in{display:flex;flex:1 1 auto;align-items:center;gap:0;min-width:0;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;-ms-overflow-style:none}
.nb-in::-webkit-scrollbar{display:none}
.nb-gap{flex:1 1 auto;min-width:12px}
.nb-grp{display:flex;align-items:center;flex:0 0 auto}
.nb-sep{flex:0 0 auto;align-self:center;width:1px;height:20px;background:var(--line2);margin:0 10px}
/* text-decoration:none is not redundant. The generated Marvel Rivals pages
   carry a page-level link underline, and it bled straight into the bar - every
   segment shipped underlined on 55 pages. A shared component states what it
   wants rather than inheriting whatever the page decided. */
.nb-i{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  min-height:32px;padding:0 10px;white-space:nowrap;text-decoration:none;
  font-family:var(--body);font-size:14px;font-weight:500;line-height:1;letter-spacing:-.01em;text-transform:none;
  color:var(--ink2);border:1px solid transparent;border-radius:8px;
  transition:color .15s var(--ease,ease),background .15s var(--ease,ease),border-color .15s var(--ease,ease)}
a.nb-i:hover{color:var(--ink)}
/* Where you are. Lime text AND an underline: colour alone would fail WCAG
   1.4.1, and a filled pill here would compete with the one filled pill the
   design does have, which is Login. */
.nb-game.on,.nb-sec.on,.nb-soon.on,.nb-all.on,.nb-acct.on{color:var(--nb-on,var(--gs-on,#c8ff62));font-weight:600;
  text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:7px}
.nb-sec{color:var(--ink3)}
a.nb-sec:hover{color:var(--ink)}
/* the label is "<span>Marvel </span>Rivals" and .nb-i is a flex container, so
   the span is its own flex item and the trailing space inside it collapses.
   It shipped reading MARVELRIVALS once already, on the control this replaces. */
.nb-i .lbl-long{white-space:pre}
.nb-soon{gap:7px}
/* Login and Feedback, measured off the frame (nodes 18-744, 18-745): 32px tall,
   6px radius, 7/12 padding, 16px apart. Login is
   accent/primary with bg/100 ink - white on lime is 1.4:1, so it reads
   --accent-ink rather than a literal and tracks the theme. Feedback is
   accent/secondary #1f1f1f with the border/elevated rule, white at 15%.
   32px AT EVERY WIDTH, including phones. Adam, 2026-09-19: "fuck what claude.md
   says and make it like figma". The frame fixes the height at 32 and says nothing
   about a breakpoint, so there is no phone bump - and there never was one in this
   file, only a comment and a CLAUDE.md line claiming it, both of which were wrong
   for as long as they existed. What it costs is stated rather than hidden: 60x32
   and 88x32 clear WCAG 2.5.8 AA (24x24) and sit under this project's own 44px
   guidance, which the founder has overruled for these two controls. Every other
   control on the site still answers to 44. */
.nb-login{background:var(--nb-on,var(--gs-on,#c8ff62));border-color:var(--nb-on,var(--gs-on,#c8ff62));
  color:var(--nb-on-ink,var(--gs-on-ink,#0a0a0a));font-weight:600;padding:0 11px;margin-left:8px;border-radius:6px}
.nb-login:hover{filter:brightness(1.06)}
.nb-login.on{background:var(--nb-on,var(--gs-on,#c8ff62));color:var(--nb-on-ink,var(--gs-on-ink,#0a0a0a));text-decoration:none}
.nb-feedback{background:#1f1f1f;border-color:rgba(255,255,255,.15);color:#f5f5f5;padding:0 11px;margin-left:16px;border-radius:6px}
/* 11px, not the frame's 12: .nb-i carries a 1px border, and the frame's 60 and
   88px widths include theirs. */
a.nb-feedback:hover{border-color:rgba(255,255,255,.35);color:#fff}
/* Signed in, the lime Login pill becomes a quiet Profile pill with the reader's
   avatar (WHO_JS). Dark like Feedback: a signed-in reader needs no call to
   action, and the one lime pill is for the people who still need to sign in.
   Scoped through .top so no page's own .nb-login rule outranks it. */
.top .nb-login.nb-prof{background:#1f1f1f;border-color:rgba(255,255,255,.15);color:#f5f5f5;gap:7px;padding:0 11px 0 5px;filter:none}
.top a.nb-login.nb-prof:hover{border-color:rgba(255,255,255,.35);color:#fff}
.top .nb-login.nb-prof.on{border-color:var(--nb-on,var(--gs-on,#c8ff62))}
.nb-av{width:22px;height:22px;flex:0 0 auto;border-radius:50%;display:inline-grid;place-items:center;
  font:700 12px/1 var(--mono,ui-monospace,monospace);color:#0a0a0a;background:#c8ff62}
/* A phone has no room for "Profile" beside the wordmark and Feedback (measured
   at 390px: the wordmark cut to "Patchno..." and Feedback ran off the edge), so
   the pill is the avatar alone. The word is clipped, not display:none, so it
   stays in the accessible name with the aria-label. */
@media(max-width:560px){.top .nb-login.nb-prof{padding:0 4px}
  .nb-prof .nb-pt{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}}
.nb-acct{color:var(--ink2)}
a.nb-acct:hover{color:var(--ink)}
/* The dot is the whole "fun" budget: one small live signal that something is in
   there. Decorative and aria-hidden - the label already says Soon - and it
   stops dead under reduced motion. */
.sn-dot{width:6px;height:6px;border-radius:50%;background:var(--nb-on,var(--gs-on,#c8ff62));flex:0 0 auto;position:relative}
.sn-dot::after{content:"";position:absolute;inset:-3px;border-radius:50%;border:1px solid var(--nb-on,var(--gs-on,#c8ff62));opacity:.7;
  animation:snPing 2.6s cubic-bezier(.22,1,.36,1) infinite}
@keyframes snPing{0%{transform:scale(.6);opacity:.75}55%{transform:scale(1.9);opacity:0}100%{transform:scale(1.9);opacity:0}}
a.nb-soon:hover .sn-dot::after{animation:none;opacity:0}
@media(prefers-reduced-motion:reduce){.sn-dot::after{animation:none;opacity:.55;transform:scale(1.25)}.nb-i{transition:none}}
/* The long labels go first: "Zomboid" and "Rivals" still name the game. */
@media(max-width:1180px){.nb-i .lbl-long{display:none}}
/* No two-row fallback any more: the wordmark, Login and Feedback fit on one
   line down to 320px, which is the only reason the second row ever existed. */

/* ---- page to page: the header stays put, the content fades ----
   Founder 2026-09-13: "clicking between pages makes the whole website flash ...
   comparing to reddit with a sidebar - while the content changes and flashes
   the sidebar still stays the same." Every page here is its own document, so a
   click was a hard cut of the whole screen, header included.

   A cross-document view transition fixes that without turning the site into a
   single-page app. The browser keeps the old page on screen until the new one
   has rendered and then swaps it in one frame; an element with a
   view-transition-name is matched across the two pages and held in place. The
   header and the Discord button are the same on every page, so they do not move
   at all. BOTH pages have to opt in for it to run. The opt-in itself,
   @view-transition, is NOT in this file: in an external stylesheet Chrome
   ignored it on any real connection. It is an inline <style> written by
   NAVCSS_TAG into every page head. Only the names and the no-fade rule live here.

   THE CONTENT CUTS, IT DOES NOT FADE. The first version crossfaded over 180ms,
   and an independent check caught the homepage and a game page overlapping
   mid-fade: two copies of the headline and of "Catch me up", a few pixels
   apart, which reads as a rendering glitch rather than polish. The pages share
   a layout closely enough that any fade shows that double exposure. An instant
   swap under a header that does not move is what the Reddit comparison
   actually describes, and it is also what reduced motion needs.

   Browsers without support (Firefox today) navigate exactly as before.

   scrollbar-gutter: a page shorter than the window has no classic scrollbar, so
   on Windows the whole layout shifted 15px sideways moving between a short page
   and a long one. Reserving the gutter keeps the header in the same place. */
/* ---- the Discord button, for pages that carry the markup ----
   The 8 hand-kept pages declare these rules themselves and their own copy
   wins (a page's <style> comes after this linked sheet), so nothing changes
   for them. It lives here so a GENERATED page needs only the markup:
   marvel-rivals/cosmetics.html and stat-changes.html had no Discord link of
   any kind, which the 2026-08-30 rule that every page is reachable from the
   navigation already forbids. Adding a ninth copy of the CSS to each
   generator was the alternative and it is how the other eight drifted. */
/* ---------- floating Discord button ---------- */
/* Was a 48px circular icon-only FAB. Founder 2026-08-21: it "gets lost in the
   noise". An unlabelled glyph in a page corner reads as chrome, not an
   invitation, so it is a labelled bar in Discord blurple. The blurple is
   deliberately NOT the page accent - the one element on screen that is not
   sapphire is the one element that gets noticed. White on #5865f2 is 4.61:1 and
   on the gradient's dark end 7.21:1, so the label clears AA at every point.
   Second pass the same day: the square was too tall and stacked its label over
   two lines. Founder wanted it lower, shorter, wider, one line. A wide bar also
   reads as a button instead of an app badge, which a square in a page corner
   always slightly does. */
.dfab{position:fixed;right:26px;bottom:24px;z-index:60;min-height:54px;border-radius:14px;
  display:inline-flex;align-items:center;gap:11px;padding:0 20px 0 17px;
  background:linear-gradient(112deg,#5865f2 0%,#4149bd 100%);border:1px solid #7c86f8;
  color:#fff;text-decoration:none;white-space:nowrap;isolation:isolate;
  box-shadow:0 12px 30px -12px rgba(0,0,0,.85),0 0 0 1px rgba(6,9,17,.5),0 0 20px -8px rgba(88,101,242,.65);
  transition:transform .32s var(--ease),opacity .22s var(--ease),filter .32s var(--ease),box-shadow .32s var(--ease)}
.dfab svg{width:22px;height:22px;flex:none;
  transition:transform .32s var(--ease)}
.dfab .dlbl{font-family:var(--mono);font-size:12px;line-height:1;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase}
/* The ambient ping. Founder asked for something around it "to show users its
   there". It is a ring that leaves the button and fades, not a loop that pulses
   the button itself - the page's own rule is that a control which animates
   forever reads as decoration. The keyframes are silent from 55% on, so roughly
   two of every four seconds are completely still, and hover stops it outright
   because by then it has done its job. */
.dfab::before{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:-1;
  box-shadow:0 0 0 0 rgba(124,134,248,.55);
  animation:dPing 4.2s cubic-bezier(.22,1,.36,1) infinite}
/* Blur on the expanding shadow, not just spread: a spread-only ring is a
   hard-edged slab creeping out of the button and reads as a rendering glitch on
   a dark page. With blur it reads as a glow leaving the button. */
@keyframes dPing{
  0%{box-shadow:0 0 2px 0 rgba(124,134,248,.6)}
  55%{box-shadow:0 0 16px 13px rgba(124,134,248,0)}
  100%{box-shadow:0 0 16px 13px rgba(124,134,248,0)}}
.dfab:hover::before,.dfab:focus-visible::before{animation:none}
/* The hover sheen slides via background-position, never transform: a transform
   would carry the pseudo-element's own rounded clip along with it and the band
   would show outside the button's edge. */
.dfab::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:linear-gradient(105deg,transparent 38%,rgba(255,255,255,.26) 50%,transparent 62%);
  background-size:250% 100%;background-position:150% 0;
  transition:background-position .8s var(--ease)}
.dfab:hover::after,.dfab:focus-visible::after{background-position:-50% 0}
.dfab:hover,.dfab:focus-visible{transform:translateY(-2px) scale(1.025);filter:brightness(1.06);
  box-shadow:0 18px 40px -12px rgba(0,0,0,.9),0 0 0 1px rgba(6,9,17,.5),0 0 34px -6px rgba(88,101,242,.85)}
.dfab:hover svg{transform:scale(1.12) rotate(-6deg)}
.dfab:active{transform:translateY(0) scale(.985);transition-duration:.1s}
.dfab:focus-visible{outline:2px solid #fff;outline-offset:3px}
@media(prefers-reduced-motion:reduce){
  .dfab,.dfab svg,.dfab::after{transition:none}
  .dfab::before{animation:none}
  .dfab:hover,.dfab:focus-visible{transform:none}
  .dfab:hover svg{transform:none}}
@media(max-width:520px){.dfab{min-height:48px;right:16px;bottom:18px;border-radius:12px;gap:9px;padding:0 16px 0 14px}
  .dfab svg{width:20px;height:20px}.dfab .dlbl{font-size:12px;letter-spacing:.04em}}
/* Safe area comes LAST so it wins over the narrow-screen offsets above. It is
   the narrow screens that need it: an iPhone's home indicator sits exactly
   where a bottom-right button lands, and the old rule was ordered before the
   520px block, so the one viewport that has an inset was the one ignoring it. */
@supports(padding:max(0px)){
  .dfab{right:max(26px,env(safe-area-inset-right));bottom:max(24px,env(safe-area-inset-bottom))}
  @media(max-width:520px){.dfab{right:max(16px,env(safe-area-inset-right));bottom:max(18px,env(safe-area-inset-bottom))}}}
/* No hide-on-scroll. The button used to yield while the reader scrolled down
   and slide back after a 700ms idle timer, so it read as flickering in and out
   and was missing at the moment someone went looking for it. Founder
   2026-08-21: "make sure it's always there." The overlap that behaviour was
   guarding against is real - a floating bar sits over whatever is in the
   bottom-right corner - but a CTA that is absent when wanted costs more than
   an occasional mis-tap. Do not reintroduce a tuck, a fade, or an
   auto-hide. */
/* pn-consent.js publishes the open consent bar's height as --pnc-h. margin-bottom
   on a fixed element moves the border box up without touching transform, so the
   hover lift and the scroll tuck both keep working while it is shifted. */
html.pnc-on .dfab{margin-bottom:calc(var(--pnc-h,0px) + 12px)}

/* ---- the phone: the Discord bar must not sit on the controls ----
   audit/mobile-check.js found the floating Discord bar covering a real control
   31 times across five game pages and the homepage at phone widths - the scope
   picker's own select at 70%, "Expand all" at 96%, a filter pill at 100%, the
   homepage game search at 43%. At 320px the bar is 174px wide, more than half
   the screen, and it is fixed, so whatever it lands on is simply not tappable.

   The founder's two locked decisions about this button are both kept:
   it is ALWAYS there (no tuck, no fade, no hide-on-scroll - three earlier
   attempts at those are recorded as wrong), and it is LABELLED, because an
   unlabelled glyph in a page corner reads as chrome rather than an invitation.
   What changes on a phone is only how much room the label needs: "Join the"
   drops and the button still says DISCORD. 174px becomes about 120px, which is
   a third of the width and a little under half the area.

   The other half is scroll room. Content in the LAST screenful can never be
   scrolled out from under a fixed button, so that coverage is permanent rather
   than momentary; reserving the bar's height plus its offset at the end of the
   document means nothing is ever trapped there. :has() keeps the reservation on
   the pages that actually carry the button - and where it is unsupported the
   rule is dropped whole, which is exactly the behaviour we have today. */
/* CLIPPED, not display:none. display:none would take "Join the" out of the
   accessibility tree too, so VoiceOver would announce the button as "Discord"
   on exactly the devices this rule targets. Clipping hides it from the layout
   and keeps it in the accessible name. */
@media(max-width:520px){html .dfab .dl-long{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}}
/* ON A PHONE IT STOPS FLOATING (2026-09-19, founder: "can you put the button
   somehow on top of the page or something. or a popup on mobile? then in the
   footer?").

   A fixed bar bottom-right on a 320-412px screen sits on the controls, and no
   amount of shrinking fixes that: audit/mobile-check.js still measured it
   covering a real control 15 times after it went from 174px to 115px. The only
   thing that NEVER covers content is an element in normal flow.

   The <nav aria-label="Quick links"> wrapper is already the last element in the
   body, so position:static alone lands it at the end as a full-width band,
   with no markup moved on any of the 65 pages that carry it. It is still on
   every page, still labelled, still the same link - it just waits at the bottom
   instead of standing on the filter pills.

   A popup was considered and rejected: it would cover MORE than the bar does,
   it needs a dismiss control, and a dismissed popup is gone - which is the one
   thing the founder's "make sure it's always there" rule forbids.

   Desktop is untouched. There is room for a floating button on a 1280px screen
   and it has never covered anything there.

   The ambient ping goes too: a glow leaving the edges of a full-width band
   reads as a rendering fault, and the reason it existed - to notice a small
   button in a corner - does not apply to a band across the page.

   NOT a substitute for the footer's own Discord button, which only 6 of the 65
   pages have. This is the one placement that reaches all of them. */
/* WIDTH IS NOT THE TEST, and testing only width left the worst case floating.
   A phone in landscape is 667px WIDE and 375px TALL, so it sailed over a
   max-width:560px breakpoint and kept a fixed bar on the shortest viewport
   the site ever renders in - audit/mobile-check.js caught all four survivors
   at [667] and nothing in portrait. A short viewport is exactly where a
   floating bottom bar costs the most, so either dimension being small is
   enough to dock it. */
@media(pointer:coarse) and (max-width:560px),(pointer:coarse) and (max-height:560px){
  /* display:flex, not the inline-flex it floats with - an inline box shrinks to
     its text and the band came out half the width of the screen. */
  html .dfab{position:static;display:flex;width:100%;box-sizing:border-box;margin:26px 0 0;
    border-radius:0;min-height:60px;justify-content:center;gap:11px;padding:0 18px;
    box-shadow:none;border-left:0;border-right:0}
  html .dfab .dl-long{position:static;width:auto;height:auto;overflow:visible;clip:auto;clip-path:none}
  html .dfab::before{animation:none;box-shadow:none}
  html .dfab:hover,html .dfab:focus-visible{transform:none}
  html.pnc-on .dfab{margin-bottom:0}
  /* Nothing floats any more, so the reserved scroll room is dead weight...
     EXCEPT WHILE THE CONSENT BAR IS UP, which is every first visit (2026-09-21).
     That bar IS still fixed, 174-232px of it, and zeroing both compensations
     here meant the docked Discord band - the CTA the founder locked as "always
     there" - sat underneath it at the true bottom of the page with no scroll
     room to clear it. Measured on /apex at 390x844, first visit, scrolled to
     maxScroll: the band hit-tested 0/15, every sample point landing on the bar.
     The footer's Privacy link went the same way, which is worse than it sounds:
     it is the page's only explanation of the very choice the bar is asking the
     reader to make.
     Reserving the bar's own published height is the whole fix. pn-consent.js
     measures the open bar and publishes it as --pnc-h, so this tracks a bar that
     wraps to two or three lines on a narrow phone instead of guessing a number.
     The 16px is breathing room, not a guess at the bar.
     It is scoped to html.pnc-on, so the moment the reader answers the question
     the padding goes with the bar and the page is exactly as it was. */
  html:has(#dFab) body{padding-bottom:0}
  html.pnc-on body{padding-bottom:calc(var(--pnc-h,0px) + 16px)}
}
/* Above 560px on a touch device (a tablet) it still floats, so it keeps the
   room it needs to not sit on the last card. */
@media(pointer:coarse) and (min-width:561px) and (min-height:561px){html:has(#dFab) body{padding-bottom:calc(84px + env(safe-area-inset-bottom,0px))}}

/* Skeleton placeholders (founder, 2026-09-22: "make sure sites has skeleton
   loading for slow loadings"). A grey block stands where content is still on
   its way, the same size as what replaces it, so nothing jumps when it lands.
   Here because nav.css is the one stylesheet every page links: the game pages,
   the account page and the costume panel on 57 generated pages all use it.
   - It FADES IN after 200ms. A fast load replaces it before it is ever seen, so
     a skeleton never flickers on a good connection; only a real wait shows it.
   - Reduced motion: no shimmer, and the fade is instant (the delay stays, so
     the no-flicker rule still holds).
   - Always aria-hidden. The containers that use it carry their own one-line
     "Loading" status for a screen reader, never a list of grey boxes. */
.pn-sk{display:block;position:relative;overflow:hidden;background:rgba(255,255,255,.075);border-radius:6px;animation:pnSkIn .3s ease-out .2s both}
.pn-sk::after{content:"";position:absolute;inset:0;transform:translateX(-100%);background:linear-gradient(90deg,rgba(255,255,255,0),rgba(255,255,255,.07),rgba(255,255,255,0));animation:pnSk 1.6s ease-in-out infinite}
.pn-skl{height:12px;margin:0 0 10px;max-width:100%}
/* The one-line "Loading" a screen reader hears beside a placeholder. Its own
   name because the generated pages do not all carry a .vh utility. */
.pn-vh{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
.pn-skl.w90{width:90%}.pn-skl.w75{width:75%}.pn-skl.w60{width:60%}.pn-skl.w40{width:40%}.pn-skl.w25{width:25%}
@keyframes pnSk{to{transform:translateX(100%)}}
@keyframes pnSkIn{from{opacity:0}to{opacity:1}}
@media(prefers-reduced-motion:reduce){.pn-sk{animation-duration:.01s}.pn-sk::after{animation:none;display:none}}
/* A dropdown the engine has not filled yet is an empty white box with a
   chevron, which reads as broken. Until its first option lands it shimmers.
   !important because the pages restyle the field with a background SHORTHAND
   under an id (#picker .sel-field .dd), which resets the image; the rule only
   ever matches the moment before the engine fills the list. Position is left
   to the animation, which a normal declaration cannot hold still. */
select.dd:empty{background-image:linear-gradient(90deg,rgba(128,128,128,0) 30%,rgba(128,128,128,.22) 50%,rgba(128,128,128,0) 70%)!important;background-size:300% 100%!important;background-repeat:no-repeat!important;animation:pnSkBg 1.6s ease-in-out infinite;color:transparent}
@keyframes pnSkBg{from{background-position:100% 0}to{background-position:0 0}}
@media(prefers-reduced-motion:reduce){select.dd:empty{animation:none;background-position:50% 0!important}}

.top{view-transition-name:pn-top}
#dFab{view-transition-name:pn-discord}
::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none}
html{scrollbar-gutter:stable}
/* Login dialog, measured off the Figma frame "home_login dialog" (node 21-1748,
   2026-09-23, through the Plugin API, not a picture). Clicking the header's
   Login pill opens it over the page the reader is on instead of sending them to
   /account. Card 336 wide, #0e0e0e, radius 8, padding 32, 32 between the
   wordmark and the form, 16 between form rows; drop shadow 0 1 25.5 6
   #454545 at 25%; backdrop #282828 at 46%. Labels Geist 400 14 #a3a3a3 at -1%;
   fields #1f1f1f, radius 16, 48 tall, padding 9/12; "Forgot password" Geist 500
   12 lime; Sign In lime, radius 14, 48 tall, Geist 500 18 #0a0a0a; "Welcome back"
   Geist 500 18 #f5f5f5. Not in the frame and added on purpose: a close button
   (a phone has no Escape and a screen reader needs a named way out) and the
   error line. Fields are 16px on a touch device (the 2026-09-21 narrowing:
   iOS zooms on focus under 16px). */
html dialog.pnl{position:fixed;inset:0;margin:auto;width:fit-content;height:fit-content;padding:0;border:0;background:transparent;color:#f5f5f5;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);max-height:calc(100dvh - 32px);overflow:visible}
dialog.pnl::backdrop{background:rgba(40,40,40,.46)}
.pnl-card{position:relative;box-sizing:border-box;width:336px;max-width:100%;background:#0e0e0e;border-radius:8px;padding:32px;display:flex;flex-direction:column;align-items:center;gap:32px;
  box-shadow:0 1px 25.5px 6px rgba(69,69,69,.25);max-height:calc(100vh - 32px);max-height:calc(100dvh - 32px);overflow-y:auto;font-family:"Geist",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
.pnl-mark{margin:0;font-family:"Mulish",system-ui,sans-serif;font-weight:800;font-size:24px;line-height:30px;color:#c8ff62}
.pnl-form{width:100%;display:flex;flex-direction:column;gap:16px;margin:0}
.pnl-h{margin:0;font-weight:500;font-size:18px;line-height:23px;letter-spacing:0;color:#f5f5f5;text-align:center}
.pnl-f{display:flex;flex-direction:column;gap:4px}
.pnl-row{display:flex;align-items:center;justify-content:space-between;min-height:18px}
.pnl-f label{font-weight:400;font-size:14px;line-height:18px;letter-spacing:-.01em;color:#a3a3a3}
.pnl-f input{box-sizing:border-box;width:100%;height:48px;padding:9px 12px;border:1px solid transparent;border-radius:16px;background:#1f1f1f;color:#f5f5f5;
  font:400 14px/18px "Geist",system-ui,sans-serif;letter-spacing:-.01em;outline:none}
.pnl-f input::placeholder{color:#a3a3a3;opacity:1}
.pnl-f input:focus-visible{border-color:#c8ff62}
.pnl-a{font-weight:500;font-size:12px;line-height:16px;color:#c8ff62;text-decoration:none}
.pnl-a:hover{text-decoration:underline}
.pnl-go{height:48px;width:100%;border:0;border-radius:14px;background:#c8ff62;color:#0a0a0a;font:500 18px/23px "Geist",system-ui,sans-serif;cursor:pointer}
.pnl-go:hover{filter:brightness(1.06)}
.pnl-go:disabled{opacity:.6;cursor:progress}
.pnl-go:focus-visible,.pnl-a:focus-visible,.pnl-up a:focus-visible,.pnl-x:focus-visible{outline:2px solid #c8ff62;outline-offset:2px}
.pnl-msg{margin:0;font-size:14px;line-height:18px;color:#ff8a80;text-align:center}
.pnl-msg:empty{display:none}
.pnl-end{display:flex;flex-direction:column;gap:4px}
.pnl-up{margin:0;display:flex;flex-direction:column;gap:1px;text-align:center;font-size:14px;line-height:18px;letter-spacing:-.01em;color:#a3a3a3}
.pnl-up a{font-weight:500;color:#c8ff62;text-decoration:none;align-self:center}
.pnl-up a:hover{text-decoration:underline}
.pnl-x{position:absolute;top:8px;right:8px;width:32px;height:32px;display:grid;place-items:center;border:0;border-radius:8px;background:transparent;color:#a3a3a3;cursor:pointer}
.pnl-x:hover{color:#f5f5f5;background:#1f1f1f}
@media(pointer:coarse){.pnl-f input{font-size:16px}}
@media(prefers-reduced-motion:no-preference){dialog.pnl[open] .pnl-card{animation:pnlIn .18s cubic-bezier(.22,1,.36,1)}}
@keyframes pnlIn{from{opacity:0;transform:translateY(8px) scale(.98)}to{opacity:1;transform:none}}
html.pnl-lock{overflow:hidden}
/* NAVCSS:END */
/* FIGMAHEADER (generated by figma/home-build.js from Figma BSWaSYkR5Uw2kJBc4J2ukR frames 403:5416 + 403:5448, file modified 2026-10-17T19:10:43Z - do not hand-edit) */
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:300 700;font-display:swap;src:url(/fonts/space-grotesk-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:300 700;font-display:swap;src:url(/fonts/space-grotesk-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C4,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:100 700;font-display:swap;src:url(/fonts/ibm-plex-sans-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:100 700;font-display:swap;src:url(/fonts/ibm-plex-sans-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C4,U+2113,U+2C60-2C7F,U+A720-A7FF}
html .top{border-radius:0!important;backdrop-filter:none!important;-webkit-backdrop-filter:none!important;padding:0!important;height:48px!important;min-height:0!important;box-sizing:border-box!important;overflow:visible!important;border:0!important}
html:not(.pn-hold) .top{background-color:#161616!important;box-shadow:inset 0 -1px 0 0 #393939!important}
html .top > .wrap,html .top > .in,html .top > .topin{margin:0!important;padding:0!important;border:0!important;border-radius:0!important;align-items:center!important;justify-content:flex-start!important;gap:0!important;flex:0 0 auto!important;text-align:left!important;position:static!important;clip:auto!important;clip-path:none!important;overflow:visible!important;white-space:normal!important;min-height:0!important;min-width:0!important;height:48px!important;width:auto!important;max-width:none!important;transform:none!important;filter:none!important;text-transform:none!important;display:flex!important;flex-direction:row!important;align-self:auto!important;box-sizing:border-box!important;padding-left:8px!important;padding-right:0!important}
html:not(.pn-hold) .top > .wrap,html:not(.pn-hold) .top > .in,html:not(.pn-hold) .top > .topin{box-shadow:none!important;background:transparent!important;text-shadow:none!important}
html .top .brand{margin:0!important;padding:15px 32px 15px 8px!important;border:0!important;border-radius:0!important;align-items:center!important;justify-content:flex-start!important;gap:3px!important;flex:0 0 auto!important;text-align:left!important;position:static!important;clip:auto!important;clip-path:none!important;overflow:visible!important;white-space:normal!important;min-height:0!important;min-width:0!important;height:auto!important;width:auto!important;max-width:none!important;transform:none!important;filter:none!important;text-transform:none!important;display:flex!important;flex-direction:row!important;align-self:stretch!important;box-sizing:border-box!important;flex-shrink:0!important;text-decoration:none!important}
html:not(.pn-hold) .top .brand{box-shadow:none!important;background:transparent!important;text-shadow:none!important}
html .top .brand .mk,html .top .brand .crumb{display:none!important}
html .top::before,html .top::after,html .top .brand::before,html .top .brand::after,html .top nav.nb::before,html .top nav.nb::after{content:none!important;display:none!important}
html .top .brand b{text-transform:none!important;text-decoration:none!important;font-family:'Space Grotesk',ui-sans-serif,system-ui,sans-serif!important;font-weight:700!important;font-size:14px!important;line-height:18px!important;letter-spacing:0.16px!important;display:inline!important;overflow:visible!important}
html:not(.pn-hold) .top .brand b{text-shadow:none!important;color:#f4f4f4!important}
html .top nav.nb{margin:0 0 0 auto!important;padding:0!important;border:0!important;border-radius:0!important;align-items:flex-start!important;justify-content:flex-end!important;gap:0!important;flex:1 1 0!important;text-align:left!important;position:static!important;clip:auto!important;clip-path:none!important;overflow:visible!important;white-space:normal!important;min-height:0!important;min-width:0!important;height:auto!important;width:auto!important;max-width:none!important;transform:none!important;filter:none!important;text-transform:none!important;align-self:stretch!important;display:flex!important}
html:not(.pn-hold) .top nav.nb{box-shadow:none!important;background:transparent!important;text-shadow:none!important}
html .top .nb-in{margin:0!important;padding:0!important;border:0!important;border-radius:0!important;align-items:flex-start!important;justify-content:flex-end!important;gap:0!important;flex:0 0 auto!important;text-align:left!important;position:static!important;clip:auto!important;clip-path:none!important;overflow:visible!important;white-space:normal!important;min-height:0!important;min-width:0!important;height:auto!important;width:auto!important;max-width:none!important;transform:none!important;filter:none!important;text-transform:none!important;display:flex!important;flex-direction:row!important;box-sizing:border-box!important}
html:not(.pn-hold) .top .nb-in{box-shadow:none!important;background:transparent!important;text-shadow:none!important}
html .top .nb-gap{display:none!important}
html .top .nb-i{margin:0!important;padding:14px 14px 14px 14px!important;border:0!important;border-radius:0!important;align-items:center!important;justify-content:center!important;gap:0!important;flex:0 0 auto!important;text-align:left!important;position:relative!important;clip:auto!important;clip-path:none!important;overflow:visible!important;white-space:normal!important;min-height:0!important;min-width:0!important;height:48px!important;width:48px!important;max-width:none!important;transform:none!important;filter:none!important;text-transform:none!important;display:flex!important;flex-direction:row!important;box-sizing:border-box!important}
html:not(.pn-hold) .top .nb-i{box-shadow:inset 0 -1px 0 0 #393939!important;background:transparent!important;text-shadow:none!important;color:#f4f4f4!important}
html .top .nb-i .nb-t{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0 0 0 0)!important;clip-path:inset(50%)!important;white-space:nowrap!important;border:0!important}
html .top .nb-feedback::before{content:""!important;display:block!important;width:20px!important;height:20px!important;flex-shrink:0!important}
html:not(.pn-hold) .top .nb-feedback::before{background-color:currentColor!important;-webkit-mask:url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M11.875%201.875H18.125C18.8147%201.875%2019.375%202.43531%2019.375%203.125V6.875C19.375%207.56469%2018.8147%208.125%2018.125%208.125H16.6107L15.5286%2010L14.4458%209.375L15.8893%206.875H18.125V3.125H11.875V6.875H13.75V8.125H11.875C11.1853%208.125%2010.625%207.56469%2010.625%206.875V3.125C10.625%202.43531%2011.1853%201.875%2011.875%201.875Z%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M9.375%2018.75H8.125V15.625C8.12388%2014.5899%207.28506%2013.7511%206.25%2013.75H3.75C2.71494%2013.7511%201.87612%2014.5899%201.875%2015.625V18.75H0.625V15.625C0.627062%2013.8999%202.02494%2012.5021%203.75%2012.5H6.25C7.97506%2012.5021%209.37294%2013.8999%209.375%2015.625V18.75Z%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M5%206.25C6.03556%206.25%206.875%207.08944%206.875%208.125C6.875%209.16056%206.03556%2010%205%2010C3.96444%2010%203.125%209.16056%203.125%208.125C3.12619%207.08994%203.96494%206.25119%205%206.25ZM5%205C3.27413%205%201.875%206.39913%201.875%208.125C1.875%209.85087%203.27413%2011.25%205%2011.25C6.72587%2011.25%208.125%209.85087%208.125%208.125C8.125%206.39913%206.72587%205%205%205Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat!important;mask:url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M11.875%201.875H18.125C18.8147%201.875%2019.375%202.43531%2019.375%203.125V6.875C19.375%207.56469%2018.8147%208.125%2018.125%208.125H16.6107L15.5286%2010L14.4458%209.375L15.8893%206.875H18.125V3.125H11.875V6.875H13.75V8.125H11.875C11.1853%208.125%2010.625%207.56469%2010.625%206.875V3.125C10.625%202.43531%2011.1853%201.875%2011.875%201.875Z%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M9.375%2018.75H8.125V15.625C8.12388%2014.5899%207.28506%2013.7511%206.25%2013.75H3.75C2.71494%2013.7511%201.87612%2014.5899%201.875%2015.625V18.75H0.625V15.625C0.627062%2013.8999%202.02494%2012.5021%203.75%2012.5H6.25C7.97506%2012.5021%209.37294%2013.8999%209.375%2015.625V18.75Z%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M5%206.25C6.03556%206.25%206.875%207.08944%206.875%208.125C6.875%209.16056%206.03556%2010%205%2010C3.96444%2010%203.125%209.16056%203.125%208.125C3.12619%207.08994%203.96494%206.25119%205%206.25ZM5%205C3.27413%205%201.875%206.39913%201.875%208.125C1.875%209.85087%203.27413%2011.25%205%2011.25C6.72587%2011.25%208.125%209.85087%208.125%208.125C8.125%206.39913%206.72587%205%205%205Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat!important}
html .top .nb-login:not(.nb-prof)::before{content:""!important;display:block!important;width:20px!important;height:20px!important;flex-shrink:0!important}
html:not(.pn-hold) .top .nb-login:not(.nb-prof)::before{background-color:currentColor!important;-webkit-mask:url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M16.25%2018.75H8.75C8.41848%2018.75%208.10054%2018.6183%207.86612%2018.3839C7.6317%2018.1495%207.5%2017.8315%207.5%2017.5V15.625H8.75V17.5H16.25V2.5H8.75V4.375H7.5V2.5C7.5%202.16848%207.6317%201.85054%207.86612%201.61612C8.10054%201.3817%208.41848%201.25%208.75%201.25H16.25C16.5815%201.25%2016.8995%201.3817%2017.1339%201.61612C17.3683%201.85054%2017.5%202.16848%2017.5%202.5V17.5C17.5%2017.8315%2017.3683%2018.1495%2017.1339%2018.3839C16.8995%2018.6183%2016.5815%2018.75%2016.25%2018.75Z%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M9.11875%2012.8687L11.3562%2010.625H2.5V9.375H11.3562L9.11875%207.13125L10%206.25L13.75%2010L10%2013.75L9.11875%2012.8687Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat!important;mask:url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M16.25%2018.75H8.75C8.41848%2018.75%208.10054%2018.6183%207.86612%2018.3839C7.6317%2018.1495%207.5%2017.8315%207.5%2017.5V15.625H8.75V17.5H16.25V2.5H8.75V4.375H7.5V2.5C7.5%202.16848%207.6317%201.85054%207.86612%201.61612C8.10054%201.3817%208.41848%201.25%208.75%201.25H16.25C16.5815%201.25%2016.8995%201.3817%2017.1339%201.61612C17.3683%201.85054%2017.5%202.16848%2017.5%202.5V17.5C17.5%2017.8315%2017.3683%2018.1495%2017.1339%2018.3839C16.8995%2018.6183%2016.5815%2018.75%2016.25%2018.75Z%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M9.11875%2012.8687L11.3562%2010.625H2.5V9.375H11.3562L9.11875%207.13125L10%206.25L13.75%2010L10%2013.75L9.11875%2012.8687Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat!important}
html .top .nb-feedback{order:1!important}
html .top .nb-login{order:2!important}
html .top .nb-login.nb-prof .nb-pt{display:none!important}
html .top .nb-login.nb-prof .nb-av{width:20px!important;height:20px!important;font-size:11px!important;line-height:20px!important;margin:0!important;border-radius:0!important}
/* /FIGMAHEADER */
