/* V94 — multi-game hub pages (/games and /games/{slug}). One stylesheet, both pages. */

.nxg-hero { padding: 92px 0 40px; text-align: center; }
.nxg-kicker { margin: 0 0 10px; color: #45f882; font: 800 11px/1 Poppins, sans-serif; letter-spacing: .28em; text-transform: uppercase; }
.nxg-kicker a { color: #45f882; text-decoration: none; }
.nxg-kicker a:hover { text-decoration: underline; }
.nxg-hero h1 { margin: 0 0 12px; font: 800 clamp(38px, 6vw, 64px)/1 Rajdhani, Poppins, sans-serif; letter-spacing: -.02em; color: #fff; }
.nxg-hero h1 em { font-style: normal; color: #45f882; text-shadow: 0 0 34px rgba(69, 248, 130, .4); }
.nxg-sub { max-width: 520px; margin: 0 auto 24px; color: #93a0b4; font-size: 15px; line-height: 1.7; }

.nxg-search-wrap { max-width: 440px; margin: 0 auto; }
.nxg-search {
  width: 100%; box-sizing: border-box; padding: 14px 20px; font: 500 14px/1.3 Poppins, sans-serif;
  color: #eef2f8; background: rgba(10, 16, 24, .85); border: 1px solid rgba(139, 152, 178, .25);
  border-radius: 999px; transition: border-color .15s ease, box-shadow .15s ease;
}
.nxg-search:focus { outline: none; border-color: rgba(69, 248, 130, .55); box-shadow: 0 0 0 4px rgba(69, 248, 130, .1); }
.nxg-search::placeholder { color: #5b6577; }

.nxg-grid-section { padding: 12px 0 90px; }
.nxg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; }
.nxg-loading, .nxg-empty { grid-column: 1/-1; padding: 60px 20px; text-align: center; color: #5b6577; border: 1px dashed rgba(139, 152, 178, .25); border-radius: 16px; }

/* game cards (/games) */
.nxg-card {
  display: flex; flex-direction: column; gap: 12px; padding: 22px; text-decoration: none;
  border: 1px solid rgba(139, 152, 178, .18); border-radius: 18px;
  background: linear-gradient(160deg, rgba(69, 248, 130, .05), transparent 45%), #0a121a;
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.nxg-card:hover { transform: translateY(-4px); border-color: rgba(69, 248, 130, .45); box-shadow: 0 18px 46px rgba(0, 0, 0, .4), 0 0 26px rgba(69, 248, 130, .08); }
.nxg-card h3 { margin: 0; font: 700 21px/1.15 Rajdhani, Poppins, sans-serif; color: #fff; }
.nxg-card-art { position: relative; height: 150px; border-radius: 14px; display: grid; place-items: center;
  overflow: hidden; border: 1px solid rgba(139, 152, 178, .12);
  background:
    radial-gradient(320px 150px at 78% 12%, rgba(69, 248, 130, .30), transparent 62%),
    radial-gradient(260px 170px at 14% 92%, rgba(77, 120, 255, .22), transparent 60%),
    linear-gradient(160deg, #101a27, #070c13); }
.nxg-card-art::after { content: ""; position: absolute; width: 132px; height: 132px; border-radius: 50%;
  border: 1px solid rgba(69, 248, 130, .28); box-shadow: 0 0 44px rgba(69, 248, 130, .18) inset; }
.nxg-card-art img { width: 100%; height: 100%; object-fit: cover; }
.nxg-card-art img.nxg-card-icon { position: relative; z-index: 1; width: 92px; height: 92px; border-radius: 20px;
  object-fit: cover; box-shadow: 0 10px 30px rgba(0, 0, 0, .5), 0 0 0 1px rgba(69, 248, 130, .25); }
.nxg-card-art span { position: relative; z-index: 1; font: 800 52px/1 Rajdhani, sans-serif; letter-spacing: .04em;
  color: #eafff2; text-shadow: 0 0 34px rgba(69, 248, 130, .75), 0 2px 10px rgba(0, 0, 0, .6); }
.nxg-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.nxg-chip { padding: 4px 11px; border-radius: 999px; font: 700 10px/1.4 Poppins, sans-serif; letter-spacing: .05em; text-transform: uppercase;
  color: #45f882; background: rgba(69, 248, 130, .1); border: 1px solid rgba(69, 248, 130, .22); }
.nxg-chip.dim { color: #93a0b4; background: rgba(139, 152, 178, .08); border-color: rgba(139, 152, 178, .18); }
.nxg-open { margin-top: auto; color: #45f882; font: 700 12px/1 Poppins, sans-serif; }

/* game section bar (layout/game-bar.php) — the pill row of the old in-page tabs,
   built out of links now that each section is its own page. */
/* margin-bottom only: on lol-rank-boost.php this class shares its element with
   that page's .wrap container, and a margin shorthand would cancel its auto
   left/right centring. */
.nxg-sectionbar-wrap { margin-bottom: 4px; text-align: center; }
.nxg-sectionbar {
  display: inline-flex; flex-wrap: wrap; justify-content: center; gap: 6px; max-width: 100%;
  padding: 6px; border: 1px solid rgba(139, 152, 178, .2); border-radius: 999px; background: rgba(8, 14, 21, .8);
}
.nxg-sectionlink {
  display: inline-flex; align-items: center; gap: 7px; padding: 10px 22px; border-radius: 999px;
  font: 700 13px/1 Poppins, sans-serif; color: #93a0b4; text-decoration: none; white-space: nowrap;
  transition: color .15s ease, background .15s ease, box-shadow .15s ease;
}
.nxg-sectionlink:hover { color: #eef2f8; text-decoration: none; }
.nxg-sectionlink.on, .nxg-sectionlink.on:hover { color: #04150a; background: linear-gradient(180deg, #5cff94, #37e874); box-shadow: 0 2px 14px rgba(69, 248, 130, .35); }
.nxg-sectionlink:focus-visible { outline: 2px solid #45f882; outline-offset: 2px; }
/* A game with no boost page keeps its slot, visibly unavailable rather than a dead link. */
.nxg-sectionlink.is-soon { color: #55606f; cursor: default; }
.nxg-sectionlink.is-soon em {
  font: 800 8.5px/1 Poppins, sans-serif; font-style: normal; letter-spacing: .1em; text-transform: uppercase;
  padding: 4px 7px; border-radius: 999px; color: #93a0b4;
  background: rgba(139, 152, 178, .12); border: 1px solid rgba(139, 152, 178, .2);
}
/* Wraps into two rows before it can push a 375px page sideways. */
@media (max-width: 520px) {
  .nxg-sectionbar { border-radius: 20px; }
  .nxg-sectionlink { padding: 9px 15px; font-size: 12px; gap: 6px; }
}

/* product cards inside a game hub */
.nxg-product { display: flex; flex-direction: column; gap: 10px; padding: 16px 16px 18px; text-decoration: none;
  border: 1px solid rgba(139, 152, 178, .18); border-radius: 15px; background: #0a121a;
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease; }
.nxg-product:hover { transform: translateY(-3px); border-color: rgba(69, 248, 130, .4); box-shadow: 0 14px 36px rgba(0, 0, 0, .35); }
.nxg-product-media { position: relative; aspect-ratio: 1.25/1; min-height: 150px; border-radius: 12px;
  overflow: hidden; display: grid; place-items: center;
  background: radial-gradient(circle at 50% 38%, rgba(69, 248, 130, .12), transparent 62%), #071018;
  border: 1px solid rgba(139, 152, 178, .1); }
.nxg-product-media img { width: 100%; height: 100%; object-fit: contain; padding: 14px; box-sizing: border-box;
  transition: transform .25s ease; filter: drop-shadow(0 6px 18px rgba(0, 0, 0, .45)); }
/* Product art often carries its own frame/glow; a big zoom crops it against the
   media box. Keep the lift subtle so nothing is clipped at any card width. */
.nxg-product:hover .nxg-product-media img { transform: scale(1.02); }
/* a product with no image never renders as an empty hole */
.nxg-product-media:empty::after { content: "\2726"; font-size: 30px; color: rgba(69, 248, 130, .35); }
.nxg-product h4 { margin: 0; font: 700 15.5px/1.3 Poppins, sans-serif; color: #fff;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 40px; }
.nxg-product-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  margin-top: auto; padding-top: 4px; }
.nxg-price { color: #45f882; font: 800 17px/1 Rajdhani, Poppins, sans-serif; }
.nxg-cat { color: #5b6577; font: 700 9px/1 Poppins, sans-serif; letter-spacing: .08em; text-transform: uppercase; }

@media (max-width: 767px) {
  .nxg-hero { padding: 66px 0 28px; }
  .nxg-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
}

/* ---------- V96: homepage Popular Games banner slider ---------- */
.nx-popgames-track { display: flex; gap: 16px; overflow-x: auto; padding: 4px 2px 14px;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
/* With one or two games the banners stretch instead of stranding a small tile. */
.nx-popgames-track:has(> .nx-popgame:only-child) > .nx-popgame { flex: 1 1 100%; }
.nx-popgames-track:has(> .nx-popgame:nth-child(2):last-child) > .nx-popgame { flex: 1 1 calc(50% - 8px); }
.nx-popgames-track::-webkit-scrollbar { display: none; }
/* V109 — the card is a mount, the picture is what is mounted on it. The name sits
   at the top where a caption belongs, and the art below it is inset by the card's
   own padding so it reads as a framed plate rather than as the card's own edge. */
.nx-popgame { position: relative; flex: 0 0 min(560px, 88vw); border-radius: 22px;
  overflow: hidden; scroll-snap-align: start; text-decoration: none; display: flex; flex-direction: column;
  padding: 4px 14px 14px;
  border: 1px solid rgba(139, 152, 178, .2);
  background: linear-gradient(180deg, #0d141d 0%, #080d14 58%, #060a10 100%);
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease; }
.nx-popgame:hover { transform: translateY(-4px); border-color: rgba(69, 248, 130, .5);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .45), 0 0 26px rgba(69, 248, 130, .1); }
/* The picture's own box. 8:3 is the shape of the 1600x600 banners the artwork
   pipeline produces, so `cover` here neither crops nor letterboxes: the WHOLE
   picture lands, edge to edge, at every viewport width — that property is the
   point of this box and nothing below may take it back. A game that ships art in
   some other shape is cropped rather than distorted, which is the safer failure;
   the box paints a ground under it so a transparent PNG never shows the card.
   The frame is deliberate: a hairline, a dark inner ring so the art never bleeds
   into the border, and a drop shadow that lifts the plate off the card. */
.nx-popgame-stage { position: relative; display: block; width: 100%; aspect-ratio: 8 / 3;
  /* flex: 0 0 auto — the card is a flex column and slides stretch to the tallest one in
     the track. Without this the picture is what gets shrunk to absorb that stretch, and
     8:3 quietly stops being 8:3. The caption absorbs it instead (see .nx-popgame-body). */
  flex: 0 0 auto;
  border-radius: 15px; overflow: hidden; background-color: #05090e;
  background-size: cover; background-position: center; background-repeat: no-repeat;
  border: 1px solid rgba(139, 152, 178, .26);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .6), 0 12px 30px rgba(0, 0, 0, .5);
  transition: border-color .18s ease, box-shadow .18s ease; }
.nx-popgame:hover .nx-popgame-stage { border-color: rgba(69, 248, 130, .42);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .6), 0 14px 34px rgba(0, 0, 0, .55), 0 0 22px rgba(69, 248, 130, .12); }
/* The caption sits ABOVE the art, on the card's own ground — no plate, no veil,
   nothing printed over the picture at all. Name left, chips right, so the block
   stays one line deep and gives the picture the rest of the card. */
/* flex: 1 1 auto — every slide in the track is as tall as the tallest, and a game with a
   long name wraps its caption. The caption is the part that grows to take that up, so the
   picture stays flush against the card's bottom inset on every slide instead of floating
   with dead space beneath it. align-content centres the caption in whatever it gets. */
.nx-popgame-body { width: 100%; padding: 14px 4px 13px; flex: 1 1 auto;
  display: flex; align-items: baseline; align-content: center; justify-content: space-between;
  gap: 10px 16px; flex-wrap: wrap; }
.nx-popgame-body strong { min-width: 0; color: #fff; font: 800 30px/1.05 Rajdhani, Poppins, sans-serif;
  letter-spacing: .01em; text-shadow: 0 2px 12px rgba(0, 0, 0, .6); }
.nx-popgame-chips { display: flex; gap: 6px; flex-shrink: 0; }
.nx-popgame-chips span { padding: 3px 10px; border-radius: 999px; font: 700 9.5px/1.4 Poppins, sans-serif;
  letter-spacing: .06em; text-transform: uppercase; color: #45f882;
  background: rgba(4, 17, 11, .72); border: 1px solid rgba(69, 248, 130, .3); }

/* ---------- V96: game-hub top-up filter chips ---------- */
/* ---------- V118: region-first top-up hub, split into two shelves ---------- */
/* The container stops being the grid when it holds shelves — each shelf owns its own
   grid instead. Without this every shelf would collapse into one auto-fill column. */
.nxg-products.nxg-shelved { display: block; }
.nxg-shelf { margin: 0 0 42px; }
.nxg-shelf:last-child { margin-bottom: 0; }
.nxg-shelf-head { margin: 0 0 14px; }
.nxg-shelf-title { margin: 0; font: 800 20px/1.2 Rajdhani, Poppins, sans-serif; color: #fff;
  letter-spacing: .02em; text-transform: uppercase; }
.nxg-shelf-note { margin: 5px 0 0; font: 500 13px/1.55 Poppins, sans-serif; color: #7c8698; max-width: 62ch; }
.nxg-shelf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; }

/* V122 — the currency entry point.
   The hub's own region rail (.nxg-regionbar / .nxg-region / .nxg-region-flag) used to sit
   here. It is gone with the denomination grid it filtered: region is chosen on the
   configurator now, beside the price it changes, and the flag chip still exists there —
   .wc-region-tab in assets/css/product-region-v68.css. This is what replaced both: one
   wide card per currency, sized so it reads as a door rather than as the first tile of a
   grid that is not there. */
.nxg-shelf-entry { margin-bottom: 34px; }
.nxg-entry { display: flex; align-items: center; gap: 18px; text-decoration: none;
  padding: 16px 20px; border-radius: 16px; background: rgba(8, 14, 21, .72);
  border: 1px solid rgba(139, 152, 178, .18);
  transition: border-color .16s ease, background-color .16s ease, transform .16s ease; }
.nxg-entry:hover { border-color: rgba(69, 248, 130, .5); background: rgba(11, 21, 17, .82);
  transform: translateY(-1px); }
.nxg-entry:focus-visible { outline: 2px solid #45f882; outline-offset: 3px; }
.nxg-entry-media { flex: 0 0 auto; width: 68px; height: 68px; border-radius: 14px;
  overflow: hidden; background: rgba(69, 248, 130, .05);
  border: 1px solid rgba(69, 248, 130, .14); }
.nxg-entry-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.nxg-entry-copy { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.nxg-entry-meta { display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  font: 700 14px/1.2 Poppins, sans-serif; color: #eef2f8; }
.nxg-entry-meta i { font-style: normal; color: #55637a; }
.nxg-entry-go { display: inline-flex; align-self: flex-start; align-items: center;
  padding: 8px 16px; border-radius: 999px; font: 700 13px/1 Poppins, sans-serif;
  color: #06120b; background: #45f882; }
.nxg-entry:hover .nxg-entry-go { background: #6dffa1; }
@media (max-width: 767px) {
  .nxg-shelf-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
  .nxg-entry { gap: 13px; padding: 13px 14px; }
  .nxg-entry-media { width: 54px; height: 54px; border-radius: 11px; }
  .nxg-entry-meta { font-size: 13px; }
}

.nxg-filterbar { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.nxg-filter { padding: 8px 18px; border-radius: 999px; cursor: pointer; text-transform: capitalize;
  font: 700 12px/1 Poppins, sans-serif; color: #93a0b4; background: rgba(8, 14, 21, .8);
  border: 1px solid rgba(139, 152, 178, .22); transition: all .15s ease; }
.nxg-filter:hover { color: #eef2f8; border-color: rgba(69, 248, 130, .4); }
.nxg-filter.on { color: #04150a; background: linear-gradient(180deg, #5cff94, #37e874);
  border-color: transparent; box-shadow: 0 2px 12px rgba(69, 248, 130, .3); }

/* ---------- V97: header dropdown + boost game picker ---------- */
/* The Store menu was translucent enough to read the hero art through it. */
.main-menu ul li ul.sub-menu,
.main-menu .sub-menu {
  background: linear-gradient(180deg, #0f1620, #0a0f16) !important;
  border: 1px solid rgba(69, 248, 130, .22) !important;
  box-shadow: 0 26px 60px rgba(0, 0, 0, .72), 0 0 0 1px rgba(0, 0, 0, .5) !important;
  backdrop-filter: none !important;
}
.main-menu ul li ul.sub-menu li a {
  border-radius: 9px !important;
  transition: background .15s ease, color .15s ease, padding-left .15s ease;
}
.main-menu ul li ul.sub-menu li a:hover {
  background: rgba(69, 248, 130, .1) !important;
  color: #45f882 !important;
  padding-left: 18px !important;
}

@media (max-width: 767px) {
  .nx-popgame { flex: 0 0 88vw; }
  .nx-popgame-body strong { font-size: 24px; }
  .nxg-card-art { height: 120px; }
}

/* ---------- V99: boost page game picker (custom listbox) ---------- */
.rb-tabs-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.rb-gamepick { position: relative; display: inline-flex; align-items: center; gap: 12px; margin: 6px auto 4px; }
.rb-gamepick-label { font: 800 10.5px/1 Poppins, sans-serif; letter-spacing: .18em; color: #6e7c8c; }
.rb-gamepick-btn {
  display: inline-flex; align-items: center; gap: 11px; min-width: 240px; padding: 13px 18px; cursor: pointer;
  font: 800 15px/1 Rajdhani, Poppins, sans-serif; letter-spacing: .01em; color: #eef2f8;
  background: linear-gradient(180deg, rgba(17, 26, 37, .98), rgba(9, 14, 21, .98));
  border: 1px solid rgba(69, 248, 130, .3); border-radius: 14px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .04);
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.rb-gamepick-btn:hover { border-color: rgba(69, 248, 130, .6); box-shadow: 0 12px 34px rgba(0, 0, 0, .5), 0 0 22px rgba(69, 248, 130, .14); }
.rb-gamepick-btn:active { transform: translateY(1px); }
.rb-gamepick-icon { color: #45f882; font-size: 15px; }
.rb-gamepick-caret { margin-left: auto; color: #6e7c8c; font-size: 11px; transition: transform .18s ease; }
.rb-gamepick.open .rb-gamepick-caret { transform: rotate(180deg); color: #45f882; }
.rb-gamepick-list {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 60; margin: 0; padding: 6px; list-style: none;
  background: linear-gradient(180deg, #121b26, #0a0f16); border: 1px solid rgba(69, 248, 130, .25); border-radius: 14px;
  box-shadow: 0 26px 60px rgba(0, 0, 0, .7); max-height: 320px; overflow-y: auto;
}
.rb-gamepick-opt {
  display: flex; align-items: center; gap: 11px; padding: 12px 14px; border-radius: 10px; cursor: pointer;
  font: 700 14px/1 Poppins, sans-serif; color: #c3ceda; transition: background .13s ease, color .13s ease;
}
.rb-gamepick-opt i { color: #6e7c8c; font-size: 14px; width: 16px; text-align: center; }
.rb-gamepick-opt em { margin-left: auto; font-style: normal; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: #5b6577; }
.rb-gamepick-opt:hover, .rb-gamepick-opt:focus-visible { background: rgba(69, 248, 130, .1); color: #eef2f8; outline: none; }
.rb-gamepick-opt:hover i { color: #45f882; }
.rb-gamepick-opt.on { background: rgba(69, 248, 130, .14); color: #45f882; }
.rb-gamepick-opt.on i { color: #45f882; }

/* Embedded boost builder (?embed=1): drop the site chrome, keep the tool.
   Nothing on the site embeds it any more — the boosting tab navigates to the
   real page — but the mode still works for any link already pointing at it. */
body.rb-embed .th-header, body.rb-embed .nx-site-footer, body.rb-embed footer,
body.rb-embed .nx-skip-link, body.rb-embed #nx-ambient, body.rb-embed .rb-gamepick,
body.rb-embed .breadcumb-wrapper { display: none !important; }
body.rb-embed .rb-hero { padding-top: 18px !important; }
body.rb-embed { background: #070c13 !important; }

/* ---------- V101: designed banner for a game with no uploaded artwork ---------- */
/* V108: the lit ground belongs to the picture's box now, not to the whole card. */
.nx-popgame-art .nx-popgame-stage {
  background:
    radial-gradient(420px 220px at 78% 12%, hsla(var(--nx-hue, 145), 85%, 55%, .28), transparent 62%),
    radial-gradient(340px 240px at 10% 92%, hsla(calc(var(--nx-hue, 145) + 45), 70%, 50%, .18), transparent 60%),
    linear-gradient(160deg, #0e1826, #060a11);
}
.nx-popgame-canvas { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
/* faint brand grid so the empty half of the card still has texture */
.nx-popgame-canvas::before {
  content: ""; position: absolute; inset: 0; opacity: .5;
  background-image:
    linear-gradient(rgba(139, 152, 178, .07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(139, 152, 178, .07) 1px, transparent 1px);
  background-size: 46px 46px;
  -webkit-mask-image: radial-gradient(80% 70% at 70% 30%, #000, transparent 75%);
  mask-image: radial-gradient(80% 70% at 70% 30%, #000, transparent 75%);
}
.nx-popgame-glyph {
  position: absolute; right: 4%; top: 50%; transform: translateY(-50%);
  width: 190px; height: 190px; object-fit: contain;
  filter: drop-shadow(0 18px 44px rgba(0, 0, 0, .6)) drop-shadow(0 0 34px hsla(var(--nx-hue, 145), 90%, 55%, .35));
}
.nx-popgame-watermark {
  position: absolute; left: -2%; bottom: 4%;
  font: 800 96px/0.85 Rajdhani, Poppins, sans-serif; letter-spacing: -.03em; white-space: nowrap;
  color: transparent; -webkit-text-stroke: 1px rgba(238, 242, 248, .09);
}
.nx-popgame-body { position: relative; z-index: 2; }
@media (max-width: 767px) {
  .nx-popgame-glyph { width: 118px; height: 118px; right: 3%; }
  .nx-popgame-watermark { font-size: 62px; }
}

/* The same treatment for the /games grid card when no icon is configured. */
.nxg-card-art span { position: relative; z-index: 1; }

/* ---------- V104: the game hub hero wears the game's own banner ---------- */
.nxg-hero-art {
  position: relative;
  background-size: cover;
  background-position: center 32%;
  background-repeat: no-repeat;
}
/* Text protection belongs here, not baked into the image: the hero is centred copy
   over full-bleed art and needs far more cover than the slider card, which puts its
   caption in a corner. One image, two layouts, each darkened to taste. */
.nxg-hero-art::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(5, 7, 11, .55) 0%, rgba(5, 7, 11, .78) 55%, #05070b 100%),
    radial-gradient(120% 80% at 50% 42%, transparent 30%, rgba(5, 7, 11, .6) 100%);
}
.nxg-hero-art > .container { position: relative; z-index: 1; }
@media (max-width: 767px) {
  /* Phones show a tall crop of a wide image, so lean harder on the scrim than on the art. */
  .nxg-hero-art::before { background: linear-gradient(180deg, rgba(5, 7, 11, .68), rgba(5, 7, 11, .88) 60%, #05070b 100%); }
}

/* ---------- V105: the artwork gets the space it deserves ---------- */

/* /games grid — the tile IS the picture. It used to be a 92px badge inside a lit
   frame, which showed a tenth of the art. Square, edge to edge, and the tile drops
   its own frame so the artwork's rounded edge is the only edge you see. */
.nxg-card-art { aspect-ratio: 1 / 1; height: auto; }
.nxg-card-has-art { background: none; border: 0; overflow: visible; }
.nxg-card-has-art::after { content: none; }
/* Matches the existing `.nxg-card-art img.nxg-card-icon` rule's weight and then some,
   otherwise the old 92px badge sizing wins and the tile stays mostly empty. */
.nxg-card-art.nxg-card-has-art img.nxg-card-icon {
  position: relative; z-index: 1;
  width: 100%; height: 100%; border-radius: 0; object-fit: cover;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .5);
  transition: transform .2s ease;
}
.nxg-card:hover .nxg-card-art.nxg-card-has-art img.nxg-card-icon { transform: translateY(-2px) scale(1.015); }

/* Product tile — square, matching the square product art. A 1.25:1 box letterboxed
   the square art and rendered its hairline frame at a sub-pixel height, so the
   bottom edge of the frame vanished while the top survived: the "eaten" bottom the
   owner kept seeing. Same shape in and out, and the line lands on whole pixels. */
.nxg-product-media { aspect-ratio: 1 / 1; min-height: 0; }
.nxg-product-media img { padding: 8px; }

/* The game hub hero — see the V108 block below, which replaces this sizing. */
.nxg-hero-art { display: grid; align-items: center; padding: 40px 0 34px; }

/* Homepage "Pick a game" — one banner at a time, full width. The card's height
   now comes from the picture's own 8:3 box plus the caption strip, so no
   aspect-ratio is imposed on the card itself. */
.nx-popgames-track > .nx-popgame { flex: 0 0 100%; }
.nx-popgames-track:has(> .nx-popgame:only-child) > .nx-popgame,
.nx-popgames-track:has(> .nx-popgame:nth-child(2):last-child) > .nx-popgame { flex: 0 0 100%; }
/* V109 — the caption sits above the art now and inside the card's own padding, so its
   side inset is the card's, not a second one stacked on top of it. Only the vertical
   rhythm is set here: enough air above the name to clear the card's top edge, a smaller
   gap below so the name reads as attached to the picture it labels. */
.nx-popgames-track > .nx-popgame .nx-popgame-body { padding: 16px 6px 14px; }
.nx-popgames-track > .nx-popgame .nx-popgame-body strong { font-size: clamp(26px, 3.4vw, 42px); }

.nx-popgames-nav { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 14px; }
.nx-popgames-dots { display: flex; gap: 7px; }
.nx-popgames-dot { width: 9px; height: 9px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(139, 152, 178, .45); background: transparent; transition: all .18s ease; }
.nx-popgames-dot:hover { border-color: rgba(69, 248, 130, .7); }
.nx-popgames-dot.on { background: #45f882; border-color: #45f882; box-shadow: 0 0 12px rgba(69, 248, 130, .55); }
.nx-popgames-count { min-width: 46px; text-align: center; color: #93a0b4;
  font: 700 12px/1 Poppins, sans-serif; letter-spacing: .06em; font-variant-numeric: tabular-nums; }
/* "2 / 3" is one left-to-right expression, not two numbers in a sentence. Under
   html.nx-rtl this span inherits direction:rtl, so the bidi algorithm reorders the
   two number runs around the slash and the counter READS "3 / 2" while holding the
   correct value — the position and the total swap places on screen. Same fix
   nexus-custom-v34.css already applies to .prod-price, .ord-paynum and
   .nx-flash-timer; `isolate` rather than that block's `embed` because the counter
   is self-contained and should not enter into a bidi relationship with the dots
   and arrows beside it at all. tabular-nums above is deliberately left alone. */
html.nx-rtl .nx-popgames-count { direction: ltr; unicode-bidi: isolate; }
.nx-popgames-arrow { width: 34px; height: 34px; border-radius: 50%; cursor: pointer; line-height: 1;
  font: 700 19px/1 Poppins, sans-serif; color: #93a0b4;
  background: rgba(10, 16, 24, .9); border: 1px solid rgba(139, 152, 178, .28); transition: all .18s ease; }
.nx-popgames-arrow:hover { color: #45f882; border-color: rgba(69, 248, 130, .55); }
.nx-popgames-dot:focus-visible, .nx-popgames-arrow:focus-visible { outline: 2px solid #45f882; outline-offset: 2px; }
@media (max-width: 767px) {
  .nx-popgames-arrow { display: none; }
}

/* ================= V108 ================= */

/* ---------- the rotation you can see ----------
   A rail directly under the banner, exactly as wide as the banner (the track's
   2px side padding is matched here), filling left to right over the dwell time.
   games.js reaches the end of the fill and advances in the same frame, so the
   rail is the timer rather than an animation that happens to look like one. */
.nx-popgames-progress {
  height: 3px; margin: 0 2px; border-radius: 999px; overflow: hidden;
  background: rgba(139, 152, 178, .18);
}
.nx-popgames-progress > i {
  display: block; height: 100%; border-radius: inherit;
  transform: scaleX(0); transform-origin: left center;
  background: linear-gradient(90deg, #37e874, #5cff94);
  box-shadow: 0 0 12px rgba(69, 248, 130, .45);
}
/* `transform-origin` takes physical keywords only — there is no flow-relative
   `inline-start` for it — so the mirror is an explicit override, which is how the
   rest of this codebase handles RTL (see html.nx-rtl in nexus-custom-v34.css and
   the other page stylesheets). Without it the rail filled right-to-left against
   Arabic's reading direction, emptying as the dwell ran down. The gradient turns
   with it so the bright end stays on the growing edge rather than the parked one. */
html.nx-rtl .nx-popgames-progress > i {
  transform-origin: right center;
  background: linear-gradient(270deg, #37e874, #5cff94);
}
/* Interaction parks the rail; it dims so a stationary bar reads as deliberate. */
.nx-popgames-progress.is-held { background: rgba(139, 152, 178, .1); }
.nx-popgames-progress.is-held > i { box-shadow: none; }
/* Belt and braces: games.js never builds the rail under reduced motion, and if
   the preference is switched on mid-visit it hides the element. */
@media (prefers-reduced-motion: reduce) {
  .nx-popgames-progress { display: none; }
}
.nx-popgames-nav { margin-top: 12px; }

/* ---------- the game hub hero ----------
   520px of `cover` showed 92% of the banner on a desktop and 42% on a phone: the
   box was a different shape from the picture, so the picture lost its edges. The
   box is the picture's shape now (8:3), floored so it is never a letterbox strip,
   which means the whole banner is on screen at every width — and the hero is
   taller than it was on every screen wider than a phone.
   Height = max(floor, 37.5vw), and 37.5vw is exactly 100vw * 3/8 — the height the
   banner needs when it spans the viewport. `background-size: 100% auto` pins the
   art to the top at full width; where the floor makes the box taller than the art,
   the remainder is the hero's own ground, which is exactly where the copy sits.
   `aspect-ratio` is deliberately NOT used: this hero is a child of a flex/grid
   <main>, so a ratio resolves the *width* from the height and blew the page 320px
   wider than the viewport. A vw-based min-height cannot do that — it only ever
   sets height, so the worst a mis-measured viewport can cost is a few pixels of
   ground, never a horizontal scrollbar. */
/* ══ V120: ONE TREATMENT, FOUR HEROES — AND ONE CROP ══
   `.nxg-hero-art` is the game hub's; `.nxg-arthero` is the same thing on
   /rank-boost, /valorant-rank-boost, /lol-rank-boost and /accounts?game=. All
   four crop the SAME way now. What differs between them is a HEIGHT, and the
   height is a variable — never a second cropping model.

   WHY COVER, EVERYWHERE. The rule used to be `background-size: 100% auto`: pin
   the picture to the top edge at full width and let the box be whatever height
   it liked. Measured, that decoupling is what broke the set. At 375px the art
   band is 375 * 600/1600 = 141px tall, but /accounts?game= is a 330px hero, so
   the heading landed 169px down — under the picture, on bare #05070b, with the
   banner finished above it. The same rule left 76px of bare ground beneath the
   art on /lol-rank-boost at 1280. Each page then bought its own way out (the hub
   already used `cover` on its stage layer, /accounts switched to `cover` above
   1500px, the tournament hero forced `cover` outright), which is precisely the
   drift. `cover` cannot leave a gap at any width, so there is nothing to patch.

   IT COSTS NO RESOLUTION. `cover` scales by max(W/1600, H/600). With the height
   floors below that is W/1600 at every desktop width and H/600 < 1 on a phone —
   the same scale `100% auto` was already applying, or smaller. The crop is free.

   THE FOCAL POINT is two custom properties the page hands over per game, so a
   face is never taken from the dead centre of the frame by default. See
   nxg_hero_art_style() in layout/game-art.php; the owner sets it per game in
   Settings -> Games catalogue, and the 58%/38% fallback is the same point
   api/lib/game_art.php composed the 1600x600 banner around, so the banner crop
   and the page crop agree about where the subject is instead of fighting.

   THE PICTURE ARRIVES AS A CUSTOM PROPERTY on all four, not as an inline
   background-image on three of them and a property on the fourth. /accounts is
   painted by the theme with `background: ... !important`, which an inline
   longhand loses to; giving every page the same plumbing means one rule here
   instead of a rule and an exception. */
.nxg-hero-art, .nxg-arthero {
  position: relative; box-sizing: border-box;
  display: grid; align-items: end;
  background-color: #05070b;
  background-image: var(--nxg-hero-art, none);
  background-size: cover;
  background-position: var(--nxg-focus-x, 58%) var(--nxg-focus-y, 38%);
  background-repeat: no-repeat;
  background-origin: border-box; /* the art starts at the very top edge, not below the padding */
  min-height: var(--nxg-hero-h, 430px);
}

/* The only per-page difference: how tall the band is. The hub is the game's
   landing page and gets the deepest band; the section heroes carry a page of
   tools underneath and get a slightly shallower one. The vw term keeps an
   ultrawide screen from showing a letterbox strip; the px floor is the owner's
   own V111 note — "the banners are bigger than they need to be". */
.nxg-hero-art { --nxg-hero-h: max(430px, 26vw); padding: 40px 0 46px; }
.rb-hero.nxg-arthero { --nxg-hero-h: max(380px, 23vw); padding: 40px 0 34px; }
@media (max-width: 900px) {
  .nxg-hero-art { --nxg-hero-h: 360px; padding-bottom: 38px; }
  .rb-hero.nxg-arthero { --nxg-hero-h: 330px; }
}
@media (max-width: 560px) {
  .nxg-hero-art { --nxg-hero-h: 310px; padding-bottom: 30px; }
  .rb-hero.nxg-arthero { --nxg-hero-h: 290px; padding-bottom: 26px; }
}

/* The theme paints this element with `background: ... !important` and
   `padding: 120px 0 80px !important` (nexus-custom-v34.css). Nothing short of
   !important longhands takes it back — but every value below is a restatement of
   the shared rule above, not a treatment of its own, so the two cannot drift. */
.breadcumb-wrapper.nxg-arthero {
  background-image: var(--nxg-hero-art) !important;
  background-color: #05070b !important;
  background-size: cover !important;
  background-position: var(--nxg-focus-x, 58%) var(--nxg-focus-y, 38%) !important;
  background-repeat: no-repeat !important;
  --nxg-hero-h: max(380px, 23vw);
  padding: 40px 0 30px !important;
}
@media (max-width: 900px) { .breadcumb-wrapper.nxg-arthero { --nxg-hero-h: 330px; } }
@media (max-width: 560px) {
  .breadcumb-wrapper.nxg-arthero { --nxg-hero-h: 290px; padding: 40px 0 26px !important; }
}

/* Scrim rebalance. The old wash was a flat 0.55 -> 0.78 -> solid over the whole
   frame plus a full-frame vignette: measured against the source file it left only
   24% of the picture showing, which is exactly the "muted" the owner reported.
   The frame-wide wash is now light — a short top ramp so the sticky header stays
   legible, and a bottom fade so the art hands over to the page instead of ending
   on a hard edge. Nothing else covers the picture. */
.nxg-hero-art::before,
.nxg-arthero::before,
.breadcumb-wrapper.nxg-arthero::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: linear-gradient(180deg,
    rgba(5, 7, 11, .52) 0%, rgba(5, 7, 11, .08) 20%, rgba(5, 7, 11, 0) 38%,
    rgba(5, 7, 11, .10) 58%, rgba(5, 7, 11, .62) 82%, #05070b 97%);
}
@media (max-width: 767px) {
  /* A phone shows the banner as a full-width band across the top with the copy on
     the ground below it, so the fade only has to see the band out. */
  .nxg-hero-art::before,
  .nxg-arthero::before,
  .breadcumb-wrapper.nxg-arthero::before {
    background: linear-gradient(180deg,
      rgba(5, 7, 11, .44) 0%, rgba(5, 7, 11, .04) 18%, rgba(5, 7, 11, 0) 32%,
      rgba(5, 7, 11, .5) 58%, #05070b 78%);
  }
}
/* Text protection is LOCAL, attached to the copy rather than to a percentage of
   the hero. A gradient tuned to "the height where the title usually lands" is a
   guess that changes with every viewport — and while tuning this one, moving a
   single stop swung the measured worst-case contrast between 4.7:1 and 19.8:1.
   A plate that travels with the copy cannot drift: wherever the title goes, its
   ground goes too, and every pixel of the picture outside it is left alone.

   V120 — ONE PLATE, EVERY COPY BLOCK. There were two, and each missed a page.

   The hub had a `closest-side` ellipse. An ellipse is thin exactly where a wide
   heading's ends are: "League Of Legends" measures 983px at 1280 and its first
   and last words sat outside the dense middle, on raw splash art — measured
   1.07:1 against white. The section heroes had a better plate, but it was
   selected on `> .container`, and /lol-rank-boost's copy block is a `.wrap`, so
   that page had no plate at all: 1.02:1, the worst number on the site.

   So: one rule, listing every element that is actually a hero's copy block. */
.nxg-hero-art > .container,
.nxg-arthero > .container,
.nxg-arthero > .wrap {
  position: relative; z-index: 1;
  /* The hero is `display: grid`, and a grid item with `margin: 0 auto` shrinks to
     its own content instead of filling the track. `.container` carries width:100%
     already so this changes nothing there; `.wrap` does not, and without it the
     /lol-rank-boost copy block collapsed from 1180px to 812px the moment the
     shared rule made that hero a grid. */
  width: 100%;
}
.nxg-hero-art > .container::before,
.nxg-arthero > .container::before,
.nxg-arthero > .wrap::before {
  /* Vertical bleed only. A negative horizontal inset pushed the plate ~2px past
     the viewport at 375px and put a horizontal scrollbar on the page. */
  content: ""; position: absolute; inset: -34px 0 -40px; z-index: -1; pointer-events: none;
  /* Dense across the whole block and soft on all four edges: the vertical fade
     comes from the gradient, the horizontal fade from the mask, so there is no
     rectangle to see. */
  background: linear-gradient(180deg,
    transparent 0%, rgba(3, 5, 8, .84) 9%, rgba(3, 5, 8, .86) 88%, transparent 100%);
  /* The side fade is 14px, not a percentage, and that is the whole point. A copy
     block's own horizontal padding is what holds the heading off its edge, and
     the tightest of them is 15px (.container at 375px; .wrap is 24px, .container
     at 1280px is 32px). At the old 7% the fade ran 26px into a 375px block, so
     the first 11px of every heading sat on a plate that was already fading out.
     14px completes inside the narrowest padding on the site, which means the
     heading is never anywhere but full density — at any width, on any page. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
}
/* And the copy carries a little of its own, so readability never rests on one
   gradient stop landing where the art happens to be brightest. */
.nxg-hero-art h1, .nxg-arthero h1, .nxg-arthero .breadcumb-title {
  text-shadow: 0 2px 18px rgba(3, 5, 8, .85), 0 1px 3px rgba(3, 5, 8, .9);
}
/* The tagline's 24px tail existed to separate it from the section bar that used
   to sit under it inside the hero. The bar is a strip below the hero now, so the
   tail is just a hole. */
.nxg-hero-art .nxg-sub { margin-bottom: 0; }
.nxg-hero-art .nxg-sub, .nxg-hero-art .nxg-kicker,
.nxg-arthero .rb-sub, .nxg-arthero .breadcumb-menu {
  text-shadow: 0 1px 10px rgba(3, 5, 8, .9);
}
/* The boost hero's game picker sits on the art too and must stay a control, not a
   ghost, so it gets a solid ground of its own rather than borrowing the plate. */
.nxg-arthero .rb-gp-btn, .nxg-arthero .rb-gp-change {
  background: linear-gradient(180deg, rgba(13, 20, 29, .97), rgba(7, 12, 18, .97));
}

/* ---------- the game section strip ----------
   Was: a pill row inside the hero, under the tagline, reading as hero decoration.
   Now: an opaque navigation rail on its own surface, directly beneath the hero and
   visibly not part of the artwork behind it. Full page width, because the strip is
   a sibling of the hero rather than a resident of a container; deliberately not
   sticky, since the site header already sticks and two stacked bars would eat a
   third of a 375px viewport. */
.nxg-gamestrip { position: relative; z-index: 3; }
.nxg-gamestrip-nav {
  background: linear-gradient(180deg, rgba(10, 16, 24, .97), rgba(6, 10, 16, .99));
  border-top: 1px solid rgba(69, 248, 130, .22);
  border-bottom: 1px solid rgba(139, 152, 178, .16);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .38);
}
.nxg-gamestrip-inner {
  display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  max-width: 1320px; margin: 0 auto; padding: 10px 16px; box-sizing: border-box;
}
.nxg-gamestrip-id { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.nxg-gamestrip-id img { width: 28px; height: 28px; border-radius: 8px; object-fit: cover; flex: none; }
.nxg-gamestrip-id b {
  font: 800 15px/1 Rajdhani, Poppins, sans-serif; letter-spacing: .02em; color: #eef2f8;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Inside the strip the pill row is left-of-nothing: it is the strip's own rail,
   so it drops the floating-pill container and sits flush. Specificity 0,2,0 beats
   the base .nxg-sectionbar rule at 0,1,0 — the file has been bitten before by a
   new rule losing to an older, more specific one. */
.nxg-gamestrip .nxg-sectionbar {
  border: 0; background: none; padding: 0; gap: 4px; justify-content: flex-start;
  border-radius: 0; flex: 0 1 auto;
}
.nxg-gamestrip .nxg-sectionlink { padding: 9px 20px; font-size: 13px; }
.nxg-gamestrip-nav .nxg-sectionlink:hover { background: rgba(139, 152, 178, .12); }
.nxg-gamestrip-nav .nxg-sectionlink.on:hover { background: linear-gradient(180deg, #5cff94, #37e874); }
/* The wrapper only exists to centre a floating pill row; a full-width strip does
   not want its 4px tail. */
.nxg-sectionbar-wrap:has(> .nxg-gamestrip) { margin-bottom: 0; text-align: left; }
@media (max-width: 640px) {
  .nxg-gamestrip-id { display: none; }
  .nxg-gamestrip-inner { justify-content: center; padding: 8px 10px; }
  .nxg-gamestrip .nxg-sectionbar { justify-content: center; width: 100%; }
  .nxg-gamestrip .nxg-sectionlink { padding: 9px 14px; font-size: 12px; }
}
/* A caller's wrapper that the strip has vacated must take up no room at all;
   [hidden] alone loses to the theme's display rules on some of these pages. */
.nxg-sectionbar-wrap[hidden] { display: none !important; }

/* On a phone the caption should not rival the picture it belongs to. The
   .nx-popgames-track > … prefix matches the weight of the rule it is trimming
   (0,3,1); a bare .nx-popgame-body here would silently lose to it. */
@media (max-width: 767px) {
  .nx-popgames-track > .nx-popgame .nx-popgame-body { padding: 12px 4px 11px; }
  /* Same 0,3,1 weight, for the same reason: the name has to be trimmed alongside the
     padding or the chips get squeezed off its line. */
  .nx-popgames-track > .nx-popgame .nx-popgame-body strong { font-size: clamp(21px, 5.6vw, 26px); }
  .nx-popgames-track > .nx-popgame { padding: 4px 10px 12px; }
}

/* ── V109: the hub card's clip and its brand mark ──────────────────────────────
   The owner picked one animation per game. It layers over the square key art
   rather than replacing it, so a card that never plays — reduced motion, a
   blocked autoplay, a slow connection — is exactly the card that shipped before.

   Both rules below are written at 0,2,1 on purpose. `.nxg-card-art img` already
   claims width/height/object-fit at 0,1,1, so a bare `.nxg-card-logo` would lose
   to it and the mark would stretch to fill the whole tile. */
.nxg-card-art.nxg-card-has-clip { overflow: hidden; border-radius: 14px; }

.nxg-card-art video.nxg-card-clip {
  position: absolute; inset: 0; z-index: 2;
  width: 100%; height: 100%; object-fit: cover; object-position: center;
  border: 0; pointer-events: none;
  opacity: 0; transition: opacity .3s ease;
}
/* Only once the decoder reports real frames — see the `playing` listener in
   games.js. Fading on `play()` instead would show the element's black box. */
.nxg-card.is-playing .nxg-card-art video.nxg-card-clip { opacity: 1; }

.nxg-card-art img.nxg-card-logo {
  position: absolute; z-index: 3; inset: auto auto 10px 10px;
  width: 46px; height: 46px; padding: 7px; border-radius: 13px;
  object-fit: contain;
  background: rgba(7, 12, 19, .62);
  border: 1px solid rgba(255, 255, 255, .14);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .5);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  pointer-events: none;
  transition: transform .18s ease, border-color .18s ease;
}
.nxg-card:hover .nxg-card-art img.nxg-card-logo {
  transform: translateY(-2px); border-color: rgba(69, 248, 130, .5);
}

@media (max-width: 575px) {
  .nxg-card-art img.nxg-card-logo { width: 38px; height: 38px; padding: 6px; border-radius: 11px; inset: auto auto 8px 8px; }
}

/* Belt and braces: the script already refuses to call play() under this
   preference, so the clip would sit at opacity 0 anyway. */
@media (prefers-reduced-motion: reduce) {
  .nxg-card-art video.nxg-card-clip { display: none; }
}

/* ══ V109: the live hero — banner and clip swapping, name written across them ══
   Deliberately NOT called .nxg-arthero. That class already carries a tuned
   treatment for /rank-boost, /valorant-rank-boost and /accounts, with its own
   height floors and its own copy plate; reusing the name would have quietly
   applied two sets of those rules to one element. This is a new name and the hub
   keeps .nxg-hero-art underneath it for the scrim, the plate and the height. */

.nxg-livehero { position: relative; overflow: hidden; --nxg-accent: #45f882; }

/* The media stack sits under the hero's existing ::before wash and copy plate,
   which is why those keep working untouched. */
.nxg-livehero-stage { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
/* V120 — the stage layer is the hub's picture, so it crops on exactly the same
   terms as the other three heroes' backgrounds. It used to be `center top`
   while they were `top center` on a different sizing model entirely, which is
   how the hub came to show a different slice of the same artwork from its own
   boost and accounts pages. Same properties, same defaults, one crop. */
.nxg-livehero-layer {
  position: absolute; inset: 0; display: block; border: 0;
  width: 100%; height: 100%;
  background-size: cover; background-repeat: no-repeat;
  background-position: var(--nxg-focus-x, 58%) var(--nxg-focus-y, 38%);
  object-fit: cover;
  object-position: var(--nxg-focus-x, 58%) var(--nxg-focus-y, 38%);
  opacity: 0; transition: opacity .9s ease;
}
.nxg-livehero-layer.is-on { opacity: 1; }
/* Without JS exactly one layer is server-rendered and already carries .is-on, so
   the banner is a plain background picture and nothing here is load-bearing. */

.nxg-livehero-body { position: relative; z-index: 1; }

/* The name and its mark on one line, the mark optically aligned to the cap height
   rather than the line box — a logo centred on a 100px line sits visibly low. */
.nxg-livehero-name { display: flex; align-items: center; gap: clamp(10px, 1.4vw, 20px); }
.nxg-livehero-name > img.nxg-livehero-logo {
  flex: 0 0 auto; width: clamp(38px, 4.4vw, 64px); height: auto; aspect-ratio: 1;
  object-fit: contain; align-self: center; margin-top: .06em;
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, .7));
}
.nxg-livehero-title { flex: 1 1 auto; min-width: 0; margin: 0; }

/* The rotation rail, same idea as the homepage carousel's: it IS the clock, not a
   picture of one — game-hero.js paints it from the same elapsed counter that
   decides when to swap, so what you see and what happens cannot drift apart. */
.nxg-livehero-rail {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  height: 3px; background: rgba(255, 255, 255, .1); overflow: hidden;
}
.nxg-livehero-rail > i {
  display: block; height: 100%; transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--nxg-accent), color-mix(in srgb, var(--nxg-accent) 45%, #fff));
}
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .nxg-livehero-rail > i { background: var(--nxg-accent); }
}

/* The accent, read out of the game's own logo, reaches the copy that sits on the
   banner. Each rule is scoped to .nxg-livehero so no other hero is touched. */
.nxg-livehero .nxg-kicker a { color: var(--nxg-accent); }
.nxg-livehero .nxg-kicker { color: color-mix(in srgb, var(--nxg-accent) 62%, #cfe0ee); }
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .nxg-livehero .nxg-kicker { color: #cfe0ee; }
}

/* ── the masked heading ──
   Structure comes from assets/js/nexus-masked-heading.js. .nxmh-measure is the
   real, readable text: it is what a screen reader announces and what the browser
   lays out. Everything else is stacked on top of it. */
.nxmh { position: relative; width: 100%; margin: 0; padding: 0; text-wrap: balance; -webkit-font-smoothing: antialiased; }
.nxmh-word { display: inline-block; white-space: pre; }
.nxmh-word:not(:last-child)::after { content: " "; }
.nxmh-baseline { display: inline-block; width: 0; height: 0; }
.nxmh-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.nxmh-reveal { position: absolute; inset: 0; display: block; pointer-events: none; }
.nxmh-clip { position: absolute; inset: 0; display: block; }
.nxmh-media { position: absolute; inset: 0; display: block; will-change: transform; }
.nxmh-layer { position: absolute; inset: 0; display: block; transition: opacity .9s ease; }
.nxmh-source { display: block; width: 100%; height: 100%; object-fit: cover; user-select: none; -webkit-user-drag: none; }

/* The measuring copy is what shows until — and if — the mask is ready, so it is
   styled to be a perfectly good heading on its own: the artwork filling the
   letters is an enhancement, never the only reason the name is visible. Once the
   mask is up the fill takes over and the outline stays as its edge. */
.nxg-livehero .nxmh-measure { color: #f2f7ff; }
.nxg-livehero.has-masked-title .nxmh-measure {
  color: transparent;
  -webkit-text-stroke: 1px color-mix(in srgb, var(--nxg-accent) 55%, rgba(255, 255, 255, .55));
}
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .nxg-livehero.has-masked-title .nxmh-measure { -webkit-text-stroke: 1px rgba(255, 255, 255, .5); }
}
/* A stroke cannot be seen through the letters it outlines, so the drop shadow is
   what lifts the whole word off a busy banner. */
.nxg-livehero.has-masked-title .nxmh-reveal { filter: drop-shadow(0 3px 16px rgba(3, 5, 8, .85)); }

/* Reduced motion: the mask stays (it is not motion, it is a fill), the swapping
   and the drift do not. game-hero.js and the heading module both check the same
   preference; this only removes the transitions they would otherwise animate. */
@media (prefers-reduced-motion: reduce) {
  .nxg-livehero-layer, .nxmh-layer { transition: none; }
  .nxg-livehero-rail { display: none; }
}

/* ── the homepage slider's clip ──
   The owner wants the gif on the front page, not the still. The still stays as the
   ground underneath, so a clip that has not decoded yet, or is refused autoplay,
   leaves the banner exactly as it was rather than showing a black rectangle. */
.nx-popgame-clip {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; object-fit: cover; object-position: center;
  border: 0; pointer-events: none;
  opacity: 0; transition: opacity .45s ease;
}
.nx-popgame.is-playing .nx-popgame-clip { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .nx-popgame-clip { display: none; } }

/* The mark and the name are one object and centre together. The name was full-width
   before, which pushed the mark to the far left of the container and left the two
   looking unrelated. The heading now shrinks to its own words — and because a
   heading that sizes itself from its own width would chase its own tail, the type
   scale reads the row instead (sizeFrom: 'parent' in game-hero.js). */
.nxg-livehero .nxg-livehero-name { justify-content: center; }
.nxg-livehero .nxg-livehero-title { flex: 0 1 auto; width: auto; max-width: 100%; }
@media (max-width: 560px) {
  /* At phone width the mark beside a large word steals room the word needs more. */
  .nxg-livehero .nxg-livehero-name > img.nxg-livehero-logo { display: none; }
}

/* ── the section strip, in the game's colour ──
   The owner said the Top-up / Boosting / Accounts bar did not look good. It is
   navigation and it stays — it is the only route to a game's boosting and accounts
   pages — but it now belongs to the game it sits under instead of being a grey
   band: the accent comes from the same colour the banner above it was themed with,
   the active item is filled rather than outlined, and the rail is seated against
   the hero with a hairline of that colour instead of floating free. */
.nxg-gamestrip { --nxg-accent: #45f882; }
.nxg-livehero + .nxg-gamestrip .nxg-gamestrip-nav,
.nxg-gamestrip .nxg-gamestrip-nav { border-top: 1px solid color-mix(in srgb, var(--nxg-accent) 34%, transparent); }
.nxg-gamestrip .nxg-sectionlink.on {
  background: color-mix(in srgb, var(--nxg-accent) 20%, transparent);
  border-color: color-mix(in srgb, var(--nxg-accent) 62%, transparent);
  color: color-mix(in srgb, var(--nxg-accent) 78%, #ffffff);
}
.nxg-gamestrip .nxg-sectionlink:hover { border-color: color-mix(in srgb, var(--nxg-accent) 48%, transparent); }
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .nxg-gamestrip .nxg-gamestrip-nav { border-top-color: rgba(69, 248, 130, .34); }
  .nxg-gamestrip .nxg-sectionlink.on { background: rgba(69, 248, 130, .2); border-color: rgba(69, 248, 130, .62); color: #eafff2; }
}

/* The carousel dots measure 9x9 — a finger is ~40px. Rather than resize the dot
   (which is a deliberate visual), the tappable area is grown with an overlay that
   is absolutely positioned, so it costs no layout. Width is capped at the 16px
   measured dot pitch: any wider and neighbouring dots would overlap and the wrong
   slide would answer the tap. Height has no neighbour, so it gets the full 44px.
   9x9 = 81px^2 before, 16x44 = 704px^2 after. */
.nx-popgames-dot { position: relative; }
.nx-popgames-dot::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%); width: 16px; height: 44px;
}

/* ── V111: the banner is too tall, and the game picker was trapped under the strip ──
   The owner: "the banners are bigger than they need to be — they're very good and
   nice though". So the artwork is kept and only the box shrinks. 600px was sized for
   a hero that also had to hold a swapping clip and a progress rail; without those it
   is simply a lot of scrolling before the products start.

   V120 moved these numbers up into the shared treatment as `--nxg-hero-h`, beside
   the three other heroes' floors, so the set can be read in one place. They are the
   same numbers; nothing shrank or grew here. */

/* "Change game" opened its list UNDER the section strip. Not a z-index value problem —
   the list is z-index 60 already — but a stacking-context one: the strip is a later
   SIBLING of the hero, so it paints over anything inside the hero no matter how high
   that thing's z-index climbs within its own context. The hero has to outrank the
   strip itself. They never overlap, so nothing else changes. */
.rb-hero, .nxg-hero-game { position: relative; z-index: 4; }
.nxg-gamestrip { z-index: 3; }

/* V119 — search affordances: a clear button inside the field and a live result count.
   The wrap is already the positioning context for the input; the button rides inside it. */
.nxg-search-wrap { position: relative; }
.nxg-search-clear { position: absolute; inset-inline-end: 10px; top: 50%; transform: translateY(-50%);
  width: 26px; height: 26px; border: 0; border-radius: 50%; cursor: pointer; line-height: 1;
  font: 700 18px/1 Poppins, sans-serif; color: #93a0b4; background: rgba(139, 152, 178, .16); }
.nxg-search-clear:hover { color: #04150a; background: #45f882; }
.nxg-search-clear:focus-visible { outline: 2px solid #45f882; outline-offset: 2px; }
.nxg-search-count { margin: 8px 0 0; min-height: 1em;
  font: 600 12px/1 Poppins, sans-serif; color: #7c8698; }

/* V140 — one cleaner service hero -------------------------------------------------
   Keep the splash art visible. The previous copy protector was a full container-width
   84–86% black plate, which became an obvious rectangle on bright League/VALORANT art. */
.nxg-hero-art {
  /* V150: match the supplied artwork instead of forcing every game into a shallow
     19vw strip. For our 2048×682 banners this resolves to ~33.3vw, so the desktop
     view shows the composition rather than eating faces/weapons off the top/bottom. */
  --nxg-hero-h: clamp(360px, var(--nxg-art-ratio, 33.333vw), 682px);
  padding: 28px 0 30px;
}
.rb-hero.nxg-arthero,
.breadcumb-wrapper.nxg-arthero {
  --nxg-hero-h: clamp(340px, var(--nxg-art-ratio, 33.333vw), 620px);
}
.breadcumb-wrapper.nxg-arthero { padding: 30px 0 26px !important; }

.nxg-hero-art > .container::before,
.nxg-arthero > .container::before,
.nxg-arthero > .wrap::before {
  inset: -28px auto -30px 50%;
  width: min(1040px, calc(100% - 24px));
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center,
    rgba(3,5,8,.72) 0%, rgba(3,5,8,.56) 42%, rgba(3,5,8,.24) 62%, transparent 78%);
  -webkit-mask-image: none;
          mask-image: none;
  border-radius: 42%;
  filter: blur(.01px);
}
/* V150 normalized wide VALORANT art no longer needs a destructive 16:9 crop override. */

@media (max-width: 900px) {
  .nxg-hero-art { --nxg-hero-h: 290px; padding-bottom: 24px; }
  .rb-hero.nxg-arthero, .breadcumb-wrapper.nxg-arthero { --nxg-hero-h: 275px; }
}
@media (max-width: 560px) {
  .nxg-hero-art { --nxg-hero-h: 245px; padding: 22px 0 20px; }
  .rb-hero.nxg-arthero, .breadcumb-wrapper.nxg-arthero { --nxg-hero-h: 235px; }
  .breadcumb-wrapper.nxg-arthero { padding: 24px 0 20px !important; }
  .nxg-hero-art > .container::before,
  .nxg-arthero > .container::before,
  .nxg-arthero > .wrap::before {
    inset: -20px auto -22px 50%; width: calc(100% - 12px);
    background: radial-gradient(ellipse at center, rgba(3,5,8,.74), rgba(3,5,8,.42) 55%, transparent 82%);
  }
}


/* V150 — /games#topup gets its own three-game artwork. The normal all-games hub
   stays intentionally clean; JS adds .is-topup only when the Top-up section is active. */
.nxg-hero.is-topup {
  position: relative;
  min-height: clamp(390px, 33.333vw, 650px);
  background-color: #05070b;
  background-image: var(--nxg-hub-art, none);
  background-size: cover;
  background-position: 50% 50%;
  background-repeat: no-repeat;
  display: grid;
  align-items: end;
  overflow: hidden;
}
.nxg-hero.is-topup::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(4,7,11,.30), rgba(4,7,11,.08) 36%, rgba(4,7,11,.72) 88%, #05070b 100%);
}
.nxg-hero.is-topup > .container { position: relative; z-index: 1; padding-bottom: 28px; }
@media (max-width: 900px) {
  .nxg-hero.is-topup { min-height: 340px; background-position: 50% 45%; }
}
@media (max-width: 560px) {
  .nxg-hero.is-topup { min-height: 310px; background-position: 50% 42%; }
}

/* V150 final framing: once the viewport becomes narrower than the supplied 3:1
   artwork, preserve the whole horizontal composition instead of zooming into one
   face. Any extra hero height becomes intentional dark copy ground below the art. */
@media (max-width: 900px) {
  .nxg-hero-art, .nxg-arthero {
    background-size: 100% auto;
    background-position: center top;
  }
  .breadcumb-wrapper.nxg-arthero {
    background-size: 100% auto !important;
    background-position: center top !important;
  }
  .nxg-livehero-layer {
    background-size: 100% auto;
    background-position: center top;
    background-color: #05070b;
  }
  .nxg-hero.is-topup {
    background-size: 100% auto;
    background-position: center top;
  }
}
