/* ============================================================
   Destiny 2 atmosphere — scoped to body.d2.

   RULE: this file dresses the PAGE SHELL only — background,
   hero, ambient light, marketing headings, section bands.
   It must never restyle the product mockups: cards, tabs,
   tables, tiles, rings, bars, character cards and the hero
   marquee stay exactly as the Supernova app renders them,
   and the brand nav, logo, buttons and progress bar stay brand.
   ============================================================ */

body.d2 {
  background: #05070d
}

/* ---- ambient layers (behind the UI, never interactive) ---------------- */
#stars,
#hud,
#glow {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0
}

#stars {
  opacity: .9
}

/* faint HUD grid, faded out toward the edges */
#hud {
  opacity: .55;
  background-image:
    linear-gradient(rgba(150, 190, 240, .045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(150, 190, 240, .045) 1px, transparent 1px);
  background-size: 68px 68px;
  -webkit-mask-image: radial-gradient(ellipse 75% 65% at 50% 28%, #000, transparent 78%);
  mask-image: radial-gradient(ellipse 75% 65% at 50% 28%, #000, transparent 78%)
}

/* the Light follows your cursor */
#glow {
  background: radial-gradient(circle 360px at var(--tx, 50%) var(--ty, 40%), rgba(198, 226, 255, .13), rgba(120, 175, 255, .05) 45%, transparent 70%);
  mix-blend-mode: screen;
  transition: opacity .4s;
  opacity: 0
}

body.d2.lit #glow {
  opacity: 1
}

/* page content sits above the atmosphere */
body.d2 .gswitch-bar,
body.d2 header.hero,
body.d2 .navcards,
body.d2 section,
body.d2 footer {
  position: relative;
  z-index: 1
}

/* translucent bands so the starfield shows through behind the app panels */
body.d2 section.blk {
  background: transparent
}

body.d2 section.blk.alt {
  background: rgba(19, 23, 33, .68)
}

/* the video-backed section keeps its own treatment, just cooler */
body.d2 .secveil {
  background: linear-gradient(180deg, rgba(19, 23, 33, .95) 0%, rgba(11, 15, 24, .72) 22%, rgba(11, 15, 24, .72) 78%, rgba(19, 23, 33, .95) 100%)
}

/* ---- hero: orbit of the Traveler -------------------------------------- */
body.d2 .hero {
  min-height: 720px
}

/* the Traveler: a soft sphere of Light above the horizon */
body.d2 .hero::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -22%;
  width: min(620px, 68vw);
  height: min(620px, 68vw);
  transform: translateX(-50%);
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 46%, rgba(255, 255, 255, .17), rgba(196, 222, 255, .07) 40%, rgba(120, 170, 255, .03) 58%, transparent 66%);
  filter: blur(3px);
  animation: traveler 11s ease-in-out infinite alternate;
  z-index: 1;
  pointer-events: none
}

@keyframes traveler {
  from {
    opacity: .78;
    transform: translateX(-50%) scale(.985)
  }

  to {
    opacity: 1;
    transform: translateX(-50%) scale(1.015)
  }
}

body.d2 .hero .veil {
  background:
    radial-gradient(ellipse 60% 64% at 50% 40%, rgba(5, 7, 13, 0) 0%, rgba(5, 7, 13, .48) 58%, rgba(5, 7, 13, .93) 100%),
    linear-gradient(180deg, rgba(5, 7, 13, .42), rgba(5, 7, 13, .10) 32%, rgba(5, 7, 13, .95))
}

body.d2 .wordmark .game-logo {
  animation: lightGlow 6s ease-in-out infinite alternate
}

@keyframes lightGlow {
  from {
    filter: drop-shadow(0 0 16px rgba(190, 220, 255, .28)) drop-shadow(0 3px 12px rgba(0, 0, 0, .8))
  }

  to {
    filter: drop-shadow(0 0 34px rgba(210, 232, 255, .5)) drop-shadow(0 3px 12px rgba(0, 0, 0, .8))
  }
}

/* ---- marketing typography (page copy only, never widget chrome) ------- */
body.d2 h1,
body.d2 .sec-title,
body.d2 .fcopy h2,
body.d2 .cta h2 {
  font-family: 'Chakra Petch', 'Work Sans', sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 2.4px
}

body.d2 h1 {
  font-size: 40px;
  line-height: 1.26;
  text-shadow: 0 2px 6px rgba(0, 0, 0, .8), 0 0 40px rgba(150, 200, 255, .22)
}

body.d2 .sec-title {
  font-size: 27px;
  color: #eef4ff
}

body.d2 .fcopy h2 {
  font-size: 24px;
  color: #eef4ff
}

body.d2 .eyebrow {
  color: #6fc0f2;
  letter-spacing: 4.5px;
  font-family: 'Chakra Petch', sans-serif;
  font-weight: 600
}

body.d2 .lede {
  color: #d9e6f7 !important;
  letter-spacing: .2px
}

/* Destiny-style rule: hairline with a struck diamond */
body.d2 .sec-title::after {
  content: "";
  display: block;
  width: 210px;
  height: 8px;
  margin: 15px auto 0;
  background:
    linear-gradient(90deg, transparent, rgba(111, 192, 242, .5) 24%, rgba(111, 192, 242, .5) 76%, transparent) center 4px/100% 1px no-repeat,
    conic-gradient(from 45deg, rgba(160, 215, 250, .95) 0 25%, transparent 0 100%) center/8px 8px no-repeat
}

/* ---- closing sections ------------------------------------------------- */
body.d2 .cta {
  background:
    radial-gradient(ellipse 62% 130% at 50% -30%, rgba(120, 175, 255, .16), transparent 70%),
    linear-gradient(180deg, rgba(15, 19, 29, .85), rgba(5, 7, 13, .94))
}

body.d2 footer {
  background: linear-gradient(180deg, rgba(10, 13, 20, .9), rgba(4, 6, 11, .96))
}

/* sections resolve out of the dark */
body.d2 .reveal {
  filter: blur(5px);
  transition: opacity .8s cubic-bezier(.2, .7, .2, 1), transform .8s cubic-bezier(.2, .7, .2, 1), filter .8s ease
}

body.d2 .reveal.in {
  filter: blur(0)
}

/* ---- motion / small screens ------------------------------------------- */
@media(prefers-reduced-motion:reduce) {

  body.d2 .hero::before,
  body.d2 .wordmark .game-logo {
    animation: none
  }

  body.d2 .reveal {
    filter: none
  }

  #stars,
  #glow {
    display: none
  }
}

@media(max-width:820px) {
  body.d2 h1 {
    font-size: 29px
  }

  body.d2 .sec-title {
    font-size: 22px
  }

  #glow {
    display: none
  }
}

/* ============================================================
   Career Stats widget — matched to the app's .career-stats-widget-1

   .cs-grid / .cs-col / .cs-foot are shared with the Dota page, so every rule
   here is scoped to body.d2. The app's structural differences are: headings
   left-aligned above each dial rather than centred, a rule between the two
   halves, and a four-cell footer whose right pair is right-aligned.
   ============================================================ */

/* the divider sits between the columns, so the grid gains a middle track */
body.d2 .cs-grid {
  grid-template-columns: 1fr auto 1fr;
  column-gap: 24px;
}

body.d2 .cs-div {
  width: 1px;
  align-self: stretch;
  background: #454857;
}

/* Left-aligned like the app, but at the shared .csub type the Dota page uses */
body.d2 .cs-col {
  align-items: stretch;
  padding: 0;
}

body.d2 .csub {
  text-align: left;
  margin-bottom: 10px;
}

/* the dial itself stays centred under its heading */
body.d2 .cs-col .dial {
  margin: 0 auto;
}

/* app: .bottom-kpi-group — four cells, the right pair right-aligned */
body.d2 .cs-foot {
  grid-template-columns: repeat(4, 1fr);
}

body.d2 .cs-foot .cf.end {
  text-align: right;
}

body.d2 .cs-foot .cf.end .n {
  justify-content: flex-end;
}

/* app: .icon-challenger */
body.d2 .rank-ic {
  width: 22px;
  height: 22px;
  object-fit: cover;
  flex: none;
}

/* app: .current-rank-name / .lp-number — truncate rather than wrap, so a long
   value like "Guardian Rank 4" can't push the footer onto a second line */
body.d2 .cs-foot .cf .l,
body.d2 .cs-foot .cf .n {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}


/* ---- Your KPIs: the app's analytics tiles --------------------------------
   .dst defaults to var(--panel-2), which is #0b0810 — the same value as --bg,
   so the tiles were invisible against the section. These use the surface the
   rest of the page's cards use (#1c1c24 / #454857) so they read as floating.
   Four across, two rows, as on the app's Overall screen. */
/*
  Eight tiles as 3 + 3 + 2, with the final pair stretched across the full row.
  Six equal tracks: each tile spans 2 (three per row), and the last two span 3
  so they fill the width between them instead of leaving a gap.
*/
body.d2 .kpitiles {
  grid-template-columns: repeat(6, 1fr);
  gap: 10px;
  margin-top: 0;
}

body.d2 .kpitiles .dst {
  grid-column: span 2;
}

body.d2 .kpitiles .dst:nth-last-child(-n + 2) {
  grid-column: span 3;
}

/* styles.css drops .dstats to two columns here; let it, and clear the spans */
@media (max-width: 900px) {

  body.d2 .kpitiles .dst,
  body.d2 .kpitiles .dst:nth-last-child(-n + 2) {
    grid-column: auto;
  }
}

body.d2 .kpitiles .dst {
  background: #24252C;
  border: 1px solid #454857;
}

body.d2 .kpitiles .dh {
  gap: 6px;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
}


body.d2 .dsti {
  width: 14px;
  height: 14px;
  object-fit: contain;
  flex: none;
  opacity: .9;
}

/* ---- Top Playlist ---------------------------------------------------------
   The app's table has no rank column, so .c4 drops its 18px track here and the
   name column takes the space. Each row carries its Bungie activity-mode icon,
   sized like the app's .weapon-icon. */
body.d2 .thead.c4,
body.d2 .thr.c4 {
  grid-template-columns: 1fr 48px 48px 48px 48px;
}

body.d2 .thead.c4 .thl {
  grid-column: 1;
}

body.d2 .thr.c4 .thh img {
  width: 22px;
  height: 22px;
  object-fit: contain;
  flex: none;
  border-radius: 4px;
}

/*
  Split progress line, as in the app: .performance-bar-filled and
  .performance-bar-remaining are two separate segments with a gap between them.
  The markup here is a single track with one fill span, so the track goes
  transparent, the span becomes the filled segment and ::after supplies the
  remainder — flex `gap` does the rest. bars() still animates the span's width,
  which resolves against .mini because the span is flex: none.
*/
body.d2 .kpitiles .mini {
  display: flex;
  gap: 4px;
  background: transparent;
  overflow: visible;
}

body.d2 .kpitiles .mini>span {
  flex: none;
  border-radius: 9px;
}

body.d2 .kpitiles .mini::after {
  content: "";
  flex: 1;
  min-width: 0;
  border-radius: 9dvh;
  background: #2f3039;
}

/* ============================================================
   Review every match — the app's match drilldown
   ============================================================ */

/* Match header card: status + playlist, map name, meta chips */
body.d2 .mhead {
  background: linear-gradient(213deg, rgb(47 201 45 / 13%), rgba(0, 0, 0, 0) 40%), #24252C;
  border: 1px solid #454857;
  border-radius: 9px;
  padding: 16px 18px;
  /* background: #24252C; */
  border: 1px solid #454857;
}

body.d2 .mhrow {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
  flex-direction: row-reverse;
  justify-content: space-between;
}

body.d2 .mhstat {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: var(--grade-green);
  background: rgba(34, 197, 94, .12);
  border: 1px solid rgba(34, 197, 94, .45);
  border-radius: 999px;
  padding: 3px 9px;
}

body.d2 .mhmode {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .7px;
  text-transform: uppercase;
  color: var(--muted);
}

body.d2 .mhmode img {
  width: 14px;
  height: 14px;
  object-fit: contain;
  opacity: .9;
}

body.d2 .mhmap {
  font-size: 26px;
  font-weight: 700;
  color: #fff;
  line-height: 1.15;
  margin-bottom: 12px;
}

body.d2 .mhchips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

body.d2 .mhc {
  font-size: 12px;
  color: #cfd2dc;
  background: #24252c;
  border: 1px solid #454857;
  border-radius: 999px;
  padding: 5px 12px;
}

/* the tile's K/D/A reads as one figure, so all three parts are white; the
   scoreboard still colours its own via .kd .g / .kd .r / .kd .a */
body.d2 .mtiles .dv b {
  color: #fff;
}

body.d2 .mtiles {
  margin-top: 0;
}




body.d2 .sbmvp {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .6px;
  color: var(--gold);
  background: rgba(234, 179, 8, .12);
  border: 1px solid rgba(234, 179, 8, .5);
  border-radius: 3px;
  padding: 1px 5px;
}

body.d2 .sbyou {
  font-size: 12px;
  font-weight: 500;
  color: var(--grade-teal);
}





/* ---- Review every match: compact ----------------------------------------
   Short and sharp — the drilldown sits in a narrow column beside the copy, so
   everything here is tightened well below the full-page proportions the app
   uses. Scoped by [data-g="mh"] so the KPI tiles in the analytics section keep
   their own sizing. */
body.d2 [data-g="mh"] .mhead {
  padding: 10px 12px;
  border-radius: 6px;
}

body.d2 [data-g="mh"] .mhrow {
  gap: 8px;
  margin-bottom: 6px;
}

body.d2 [data-g="mh"] .mhstat {
  font-size: 10px;
  padding: 3px 7px;
}

body.d2 [data-g="mh"] .mhmode {
  font-size: 10px;
}

body.d2 [data-g="mh"] .mhmode img {
  width: 12px;
  height: 12px;
}

body.d2 [data-g="mh"] .mhmap {
  font-size: 17px;
  margin-bottom: 8px;
}

body.d2 [data-g="mh"] .mhc {
  font-size: 12px;
  padding: 3px 9px;
}

body.d2 [data-g="mh"] .pshd {
  font-size: 13px;
  margin: 12px 0 8px;
}







/* Six tiles as 3 + 3. Type and surface come from .kpitiles, so these match the
   Your KPIs tab; only the column count differs — .kpitiles' 6-track span layout
   would leave an orphan at six tiles. */
body.d2 .mtiles {
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

/* the K/D/A tile carries the split inline, coloured like the scoreboard */
body.d2 .mtiles .dv i {
  font-style: normal;
  font-weight: 400;
  color: var(--dim);
  padding: 0 4px;
}

/* scoreboard rows */




/* .kpitiles spans the last pair across a row; the match tiles are a plain row
   of five, and that rule's pseudo-class outranks a bare `.mtiles .dst`. */
body.d2 .mtiles .dst,
body.d2 .mtiles .dst:nth-last-child(-n + 2) {
  grid-column: auto;
}

/* Recent Matches carries the .mtable helper classes onto table.sb: the win /
   loss row tint, the result colours and the class swatch. */
/* a win is a short green wash in from the left edge; a defeat gets no tint at
   all — the red "Defeat" in the result column already says it, and a full-width
   red row read as an error */
body.d2 [data-g="mh"] table.sb tr.win {
  background: linear-gradient(90deg, rgba(34, 197, 94, .16), rgba(34, 197, 94, 0) 26%);
}

body.d2 [data-g="mh"] table.sb .res.w {
  color: var(--grade-green);
}

body.d2 [data-g="mh"] table.sb .res.l {
  color: var(--muted);
}

body.d2 [data-g="mh"] table.sb .cellf {
  display: flex;
  align-items: center;
  gap: 7px;
}

body.d2 [data-g="mh"] table.sb .gic {
  width: 16px;
  height: 16px;
  object-fit: contain;
  flex: none;
}

/* GUARDIAN is a text column, so it and its header both align left — table.sb
   right-aligns everything after the first column by default */
/* RESULT and GUARDIAN are both text columns, so both align left — table.sb
   right-aligns everything after the first column by default */
body.d2 [data-g="mh"][data-i="2"] table.sb th:nth-child(2),
body.d2 [data-g="mh"][data-i="2"] table.sb td:nth-child(2),
body.d2 [data-g="mh"][data-i="2"] table.sb th:nth-child(3),
body.d2 [data-g="mh"][data-i="2"] table.sb td:nth-child(3) {
  text-align: left;
}

/* the numeric block starts at K/D/A, so give it room off the text columns */
body.d2 [data-g="mh"][data-i="2"] table.sb th:nth-child(4),
body.d2 [data-g="mh"][data-i="2"] table.sb td:nth-child(4) {
  padding-left: 20px;
}

/* ---- Scoreboard on the shared .sb table ---------------------------------- */
/* .dire is the Dota page's winning-side chip; the fireteam here lost */
body.d2 .side.loss {
  color: var(--grade-red);
  background: rgba(244, 39, 70, .12);
}

/* type comes from the shared table.sb (13px, 12px headers) — only the row
   padding is trimmed from Dota's 9px, to keep this widget short */
body.d2 [data-g="mh"] table.sb th {
  padding: 0 8px 7px;
}

body.d2 [data-g="mh"] table.sb td {
  padding: 9px 8px;
}

/* every column but the player reads as a figure, so right-align them */
body.d2 [data-g="mh"] table.sb th:not(:first-child),
body.d2 [data-g="mh"] table.sb td:not(:first-child) {
  text-align: right;
}

body.d2 [data-g="mh"] table.sb td.kd {
  white-space: nowrap;
}

body.d2 .kd .a {
  color: #b39ddb;
}


/* ============================================================
   The gear suite — a section per module, each in the page's
   .fsplit copy-beside-tabs shape:

     #s-builder   the loadout planner
     #s-optimise  gear chosen for an activity
     #s-compare   two builds head to head

   Everything below lives inside a .subpane > .card in a .fdemo
   column, so every widget is built for roughly 580px and the
   panes are kept close in height — they share a grid cell, and
   one tall pane would leave the rest of the group short.
   ============================================================ */

/* ---- shared: section label + its count, e.g. "Weapons  3/3 slots" ---- */
body.d2 .gbrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin: 16px 0 7px;
}

body.d2 .gbh {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  letter-spacing: .1em;
  text-transform: uppercase;
}

body.d2 .gbc {
  font-size: 12.5px;
  color: #a8aaaf;
  text-align: right;
}

/* the glyph on a section label. Inline rather than a flex child, so .gbrow
   keeps the baseline alignment .gbh shares across the Builder, Optimise
   and Compare sections. Lives here with .gbh and .gbc, not in any one
   section's block — it is used by all of them. */
body.d2 .gbhi {
  width: 14px;
  height: 14px;
  object-fit: contain;
  opacity: .75;
  vertical-align: -2px;
  margin-right: 7px;
}

/* ---- shared: compact bar rows -----------------------------------------
   .score's defaults are sized for a full-width column; in a pane the
   label and value have to give the bar room. */
body.d2 .gbrail {
  gap: 7px;
}

body.d2 .gbrail .srow {
  font-size: 13px;
  gap: 9px;
}

/* a narrower label column: the longest of these is "Survivability", and
   the width it does not need is width the bar does */
body.d2 .gbrail .srow .nm {
  /* "Survivability" plus its glyph needs ~97px; 104 keeps clear of the bar */
  width: 104px;
  flex: none;
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* a lighter track than the page default, so the unfilled part of the bar
   reads as a bar rather than as a gap in the card */
body.d2 .gbrail .srow .bx {
  height: 6px;
  background: #3a3c47;
}

body.d2 .gbrail .srow .vl {
  width: 50px;
  flex: none;
  text-align: right;
  font-weight: 700;
  color: #fff;
}

/* "a dot marks a stat your playstyle leans on" */
body.d2 .gbdot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--grade-teal);
  flex: none;
}

/* ============================================================
   Gear Builder (#s-builder)
   ============================================================ */

/* subclass strip above the gear — Super, abilities, aspects, fragments */
body.d2 .gbsetup {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 20px;
  align-items: flex-start;
}

body.d2 .gbsg {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

body.d2 .gbsgl {
  font-size: 12px;
  color: #a8aaaf;
  letter-spacing: .04em;
}

body.d2 .gbsgl em {
  font-style: normal;
  color: var(--grade-green);
  margin-left: 4px;
}

body.d2 .gbsgv {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
}

/* ---- gear tiles --------------------------------------------------------
   Stacked, not side by side: with the art above it, the name gets the
   tile's full width instead of competing with a thumbnail for it. That is
   what lets the tiles be this small — at 173px a name has room on one
   line, where beside the art it had 133px and wrapped to three. */
body.d2 .gbslots {
  display: grid;
  gap: 6px;
}

body.d2 .gbslots.w3 {
  grid-template-columns: repeat(3, 1fr);
}

/* five armour tiles, laid out the way .kpitiles handles an odd count: six
   tracks, two apiece, the trailing pair widening to three so the second row
   fills the width instead of stopping two thirds short */
body.d2 .gbslots.w5 {
  grid-template-columns: repeat(6, 1fr);
}

body.d2 .gbslots.w5>.gbslot {
  grid-column: span 2;
}

body.d2 .gbslots.w5>.gbslot:nth-last-child(-n + 2) {
  grid-column: span 3;
}

body.d2 .gbslot {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 8px;
  background: #24252C;
  border: 1px solid #454857;
  border-radius: 6px;
}

/* the one exotic a build is allowed. A border alone read as "slightly
   different tile"; the wash, the lit art and the gold label together read
   as the exception it is. The gradient sits on a pseudo so it can be gold
   at the corner and gone by the time it reaches the name. */
body.d2 .gbslot.ex {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-color: rgba(206, 174, 51, .55);
}

body.d2 .gbslot.ex::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(125% 95% at 0% 0%, rgba(206, 174, 51, .18), transparent 64%);
}

body.d2 .gbslot.ex .isl {
  border-color: #ceae33;
  box-shadow: 0 0 10px rgba(206, 174, 51, .35);
}

body.d2 .gbslot.ex .gbk {
  color: #ceae33;
}

body.d2 .gbk {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #a8aaaf;
}

/* the slot label sits beside the art rather than above it, so the tile
   opens on one line instead of two — and the name below still gets the
   tile's full width, which is what keeps it off a second line */
body.d2 .gbhead {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 7px;
}

body.d2 .gbslot .isl {
  width: 24px;
  height: 24px;
  border-radius: 4px;
}

body.d2 .gbslot .isl img {
  border-radius: 3px;
}

body.d2 .gbn {
  font-size: 13px;
  font-weight: 500;
  color: #fff;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

body.d2 .gbs {
  font-size: 12.5px;
  font-weight: 400;
  color: var(--dim);
  overflow-wrap: anywhere;
}

/* rarity, the way the game colours it */
body.d2 .rar-leg {
  color: #b39ddb
}

body.d2 .rar-exo {
  color: #ceae33
}

/* the perk row pushes to the bottom, so it lines up across the weapon
   tiles even when one name wraps and its neighbours do not */
body.d2 .gbperks {
  display: flex;
  gap: 4px;
  margin-top: auto;
  padding-top: 8px;
}

body.d2 .gbperks i {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #3b3f4d;
  border: 1px solid #555a6b;
  display: block;
}

/* an unfilled socket on the weapon itself */
body.d2 .gbperks i.em {
  background: transparent;
  border-style: dashed;
}

/* ---- perks --------------------------------------------------------------
   A perk is the same object in both places it appears: a round art socket
   that says what it does on hover. The builder tile shows only the socket;
   the Weapons picker puts the name beside it. */
body.d2 .pslot {
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: help;
}

body.d2 .pslot:hover,
body.d2 .pslot:focus-visible {
  z-index: 3;
}

body.d2 .isl.perk {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #3b3f4d;
  border-color: #555a6b;
}

body.d2 .isl.perk img {
  border-radius: 50%;
  padding: 2px;
}

body.d2 .pslot:hover .isl.perk {
  border-color: #fff;
}

/* the tip: what it does, then what it moves */
body.d2 .ptip {
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  z-index: 4;
  width: 196px;
  padding: 9px 11px;
  border-radius: 8px;
  background: #1c1c24;
  border: 1px solid #454857;
  box-shadow: 0 12px 28px rgba(0, 0, 0, .55);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(4px);
  transition: opacity .15s ease, transform .15s ease, visibility .15s;
}

/* the last column would hang off the card, so it opens leftward */
body.d2 .wpc:last-child .ptip,
body.d2 .gbperks .pslot:nth-last-child(-n + 2) .ptip {
  left: auto;
  right: 0;
}

body.d2 .pslot:hover .ptip,
body.d2 .pslot:focus-within .ptip {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

body.d2 .ptip b {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
}

body.d2 .ptd {
  display: block;
  font-size: 12px;
  color: var(--muted);
  line-height: 1.45;
  margin-top: 4px;
}

body.d2 .ptm {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid #333642;
}

body.d2 .ptmod {
  font-size: 12px;
  font-weight: 600;
  border-radius: 4px;
  padding: 2px 7px;
}

body.d2 .ptmod.up {
  color: var(--grade-green);
  background: rgba(34, 197, 94, .12);
}

body.d2 .ptmod.down {
  color: var(--grade-red);
  background: rgba(244, 39, 70, .12);
}

/* ---- the Weapons picker -------------------------------------------------- */
/* stacked in a column, so each slot fills the column width rather than
   shrinking to its own label */
body.d2 .wpc .pslot {
  gap: 8px;
  align-self: stretch;
  padding: 9px 9px 9px 9px;
  border: 1px solid #454857;
  border-radius: 6px;
  background: #24252C;
}

body.d2 .wpc .pslot.on {
  border-color: rgba(122, 236, 243, .55);
  background: rgba(122, 236, 243, .10);
}

body.d2 .wpn {
  font-size: 12.5px;
  color: white;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

body.d2 .wpc .pslot.on .wpn {
  color: #fff;
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {

  body.d2 .ptip {
    transition: none;
  }
}

/* ---- the fit read-out -------------------------------------------------- */
body.d2 .gbfitline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 9px;
  margin-bottom: 12px;
}

body.d2 .gbfit {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .5px;
  color: var(--grade-yellow);
  border: 1px solid rgba(234, 179, 8, .5);
  background: rgba(234, 179, 8, .10);
  border-radius: 999px;
  padding: 3px 11px;
  white-space: nowrap;
}

body.d2 .gbjob {
  font-size: 13px;
  color: var(--muted);
}

body.d2 .gbjob b {
  color: #fff;
}

/* the off-role warning — the one place the rail disagrees with the build */
body.d2 .gbwarn {
  font-size: 13px;
  color: #cfd2dc;
  line-height: 1.45;
  background: rgba(234, 179, 8, .07);
  border: 1px solid rgba(234, 179, 8, .3);
  border-radius: 7px;
  padding: 10px 11px;
  margin-top: 12px;
}

body.d2 .gbwarn b {
  font-weight: 600;
  color: #fff;
}

body.d2 .gbcall {
  font-size: 13px;
  color: #cfd2dc;
  line-height: 1.45;
  border-left: 2px solid var(--grade-green);
  padding: 2px 0 2px 10px;
  margin-top: 12px;
}

body.d2 .gbcall b {
  color: #fff;
}

/* ---- build health ------------------------------------------------------ */
/* .rp is a teal pill; a passing verdict has to carry the green through the
   tint as well as the text */
body.d2 .gbverd.ok {
  color: var(--grade-green);
  background: rgba(34, 197, 94, .1);
}

body.d2 .gbchips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

body.d2 .gbchip {
  font-size: 12px;
  font-weight: 500;
  border-radius: 999px;
  padding: 5px 12px;
  border: 1px solid;
}

body.d2 .gbchip.ok {
  color: var(--grade-green);
  border-color: rgba(34, 197, 94, .45);
  background: rgb(34 197 94 / 7%);
}

body.d2 .gbchip.warn {
  color: var(--grade-yellow);
  border-color: rgba(234, 179, 8, .45);
  background: rgba(234, 179, 8, .10);
}

/* Build budget, rebuild cost and weapon record all use the Your KPIs tile
   from #s-analytics — same .dst box, same 14/19/13 type scale — so a stat
   tile reads identically wherever it appears on the page. .kpitiles lays
   six tracks and spans the last pair; these are a plain row of four. */
body.d2 .gbbudget {
  grid-template-columns: repeat(2, 1fr);
  margin-top: 10px;
}

body.d2 .gbbudget .dst,
body.d2 .gbbudget .dst:nth-last-child(-n + 2) {
  grid-column: auto;
}

/* .kpitiles clips its header to one line — at four across the labels here
   need the second line more than they need the single-line silhouette */
body.d2 .gbbudget .dh {
  white-space: normal;
  line-height: 1.25;
}

/* ---- suggested swaps ---------------------------------------------------- */
body.d2 .gbswaps {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

body.d2 .gbswap {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 9px;
  background: #24252C;
  border: 1px solid #454857;
  border-radius: 8px;
  padding: 11px 12px;
}

/* the slot label reads the way it does on a gear tile — 500, not 700 —
   and never breaks: "CLASS ITEM" wrapped when the column was 76px */
body.d2 .gbsk {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #a8aaaf;
  white-space: nowrap;
}

body.d2 .gbsn {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  min-width: 0;
}

body.d2 .gbsn em {
  display: block;
  font-style: normal;
  font-weight: 400;
  font-size: 12px;
  color: #a8aaaf;
  text-transform: capitalize;
  /* one line under the item name; a long "replaces …" ends in an ellipsis */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body.d2 .gbsd {
  font-size: 14px;
  font-weight: 500;
  color: var(--grade-green);
}

body.d2 .gbsb {
  font-size: 12px;
  font-weight: 500;
  color: white;
  border: 1px solid #555a6b;
  border-radius: 4px;
  padding: 4px 10px;
  white-space: nowrap;
  margin-left: 8px;
}


/* ---- item art in the builder -----------------------------------------
   .isl is the slot the vault cards use, so a planned piece renders the
   same as an equipped one, whether it is in the subclass strip, a gear
   row or a swap. */
body.d2 .gbsetup .isls {
  gap: 4px;
}

/* the swap row gains an art column ahead of the slot label */
body.d2 .gbswap {
  grid-template-columns: auto 92px minmax(0, 1fr) auto auto;
}

body.d2 .gbswap .isl {
  width: 26px;
  height: 26px;
}

/* the playstyle axes carry the same glyphs the drift radar labels use */
body.d2 .gbrail .srow .nm .axis-glyph {
  width: 12px;
  height: 12px;
  opacity: .75;
}

/* ---- rule / source chips ------------------------------------------------ */
body.d2 .gbrcs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

body.d2 .gbrc {
  font-size: 13px;
  color: white;
  background: #24252C;
  border: 1px solid #454857;
  border-radius: 999px;
  padding: 5px 12px 5px 10px;
  text-transform: capitalize;
}

/* a tick and a cross rather than a bullet: these are a promise and a
   limit, and the mark is what tells the two apart at a glance */
body.d2 .gbrc::before {
  font-weight: 700;
  margin-right: 6px;
}

body.d2 .gbrc.y::before {
  content: "✓";
  color: var(--grade-green);
}

body.d2 .gbrc.n::before {
  content: "✕";
  color: var(--grade-red);
}

/* .p is a plain picker chip — no mark */
body.d2 .gbrc.p {
  padding: 5px 12px;
}

body.d2 .gbrc b {
  color: #fff;
  font-weight: 700;
}

body.d2 .gbrc.p.on {
  color: #fff;
  font-weight: 600;
  border-color: rgba(255, 255, 255, .5);
}

/* ============================================================
   Optimise (#s-optimise)

   Every widget here carries the icon the app uses for the thing it
   names: Guardian class glyphs, subclass element glyphs, and the
   shared role glyphs (shield for Survivor, assist for Support). The
   rebuild rows take a category glyph rather than item art — these
   are recommendations, and the manifest icon for a piece you don't
   own yet would imply you do.
   ============================================================ */

/* ---- who you are playing, and what for ---------------------------------
   The Guardian is settled before this pane opens, so the bar across the
   top states it rather than offering a choice. The activity below is the
   choice, and those are real buttons — they change the readiness panel,
   so they have to be reachable and pressable, not just styled to look it. */
body.d2 .opgone {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 11px 12px;
  background: #24252C;
  border: 1px solid var(--el, #454857);
  border-radius: 6px;
}

/* the class glyph on a tile washed in that Guardian's own element */
body.d2 .opgi {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 7px;
  border: 1px solid var(--el);
}

body.d2 .opgi::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--el);
  opacity: .14;
}

body.d2 .opgi img {
  position: relative;
  width: 15px;
  height: 15px;
  object-fit: contain;
}

body.d2 .opgn {
  font-size: 14px;
  font-weight: 700;
  color: #fff;
}

body.d2 .opgs {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--el);
}

/* power and fit sit together at the far end of the bar */
body.d2 .opgp {
  margin-left: auto;
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
}

body.d2 .opgp i {
  font-style: normal;
  color: var(--grade-yellow);
  margin-right: 4px;
}

body.d2 .opgone .fit-chip {
  font-size: 12px;
  padding: 3px 9px;
}

/* the activity picker */
body.d2 .opact {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

body.d2 .opa {
  font: inherit;
  font-size: 12px;
  color: var(--muted);
  background: transparent;
  border: 1px solid #3a3d4a;
  border-radius: 6px;
  padding: 6px 11px;
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease;
}

body.d2 .opa:hover {
  color: #fff;
  border-color: #6b7180;
}

/* the chosen activity is outlined, not filled — a solid pill at this size
   pulled far more weight than the bar above it */
body.d2 .opa.on {
  color: #fff;
  font-weight: 600;
  border-color: #fff;
}

body.d2 .opa:focus-visible {
  outline: 2px solid var(--grade-teal);
  outline-offset: 2px;
}


/* the three sections each carry a .gbrow label now, so the blocks below
   them take their spacing from that rather than setting their own */
body.d2 .optcard>.gbrow:first-of-type {
  margin-top: 0;
}

/* ---- readiness ----------------------------------------------------------
   Tinted by verdict, so the panel reads before it is read. */
/* the banner element persists across ticks — only its contents are
   replaced — so the verdict tint can cross-fade rather than snap */
body.d2 .opban {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 8px 10px;
  padding: 11px 12px;
  border: 1px solid;
  border-left-width: 3px;
  border-radius: 6px;
  transition: background .3s ease, border-color .3s ease;
}

body.d2 .opban.is-marginal {
  border-color: rgba(234, 179, 8, .3);
  border-left-color: var(--grade-yellow);
  background: rgba(234, 179, 8, .06);
}

body.d2 .opban.is-ready {
  border-color: rgba(34, 197, 94, .3);
  border-left-color: var(--grade-green);
  background: rgba(34, 197, 94, .06);
}

body.d2 .opban.is-locked {
  border-color: rgba(244, 39, 70, .3);
  border-left-color: var(--grade-red);
  background: rgba(244, 39, 70, .06);
}


/* Each tick replaces the banner's children, and a brand-new element runs
   its animation on insert — which is why this is an animation and not a
   transition. Staggered so the verdict lands before the detail does. */
@keyframes optIn {
  from {
    opacity: 0;
    transform: translateY(5px)
  }

  to {
    opacity: 1;
    transform: none
  }
}

body.d2 .opban>* {
  animation: optIn .3s cubic-bezier(.2, .9, .2, 1) both;
}

body.d2 .opban .opbm {
  animation-delay: .05s
}

body.d2 .opban .opbchecks {
  animation-delay: .1s
}

@media (prefers-reduced-motion: reduce) {

  body.d2 .opban,
  body.d2 .opban>* {
    animation: none;
    transition: none;
  }
}

body.d2 .opbt {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  white-space: nowrap;
}

body.d2 .is-marginal .opbt {
  color: var(--grade-yellow)
}

body.d2 .is-ready .opbt {
  color: var(--grade-green)
}

body.d2 .is-locked .opbt {
  color: var(--grade-red)
}

body.d2 .opbm {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  /* one line beside the verdict tag; a long verdict ends in an ellipsis
     instead of wrapping */
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body.d2 .opbchecks {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  padding-top: 9px;
  border-top: 1px solid rgba(255, 255, 255, .08);
}

body.d2 .opbc {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--dim);
}

body.d2 .opbc img {
  width: 12px;
  height: 12px;
  object-fit: contain;
  flex: none;
}

body.d2 .opbc.ok b {
  color: var(--grade-green)
}

body.d2 .opbc.wn b {
  color: var(--grade-yellow)
}

/* a failed check on a locked activity is the reason it is locked */
body.d2 .is-locked .opbc.wn b {
  color: var(--grade-red)
}

body.d2 .opbc b {
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {

  body.d2 .opa {
    transition: none;
  }
}

/* ---- four ways to build it ---------------------------------------------- */
body.d2 .opways {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin: 20px 0px 20px;
}

body.d2 .opw {
  background: #24252C;
  border: 1px solid #454857;
  border-radius: 6px;
  padding: 11px 12px;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 3px 8px;
}


/* the role glyph: a shield for Survivor, the assist mark for Support */
body.d2 .opwi {
  grid-row: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 4px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid #454857;
}

body.d2 .opwi img {
  width: 12px;
  height: 12px;
  object-fit: contain;
}

body.d2 .opwr {
  font-size: 14px;
  font-weight: 600;
  color: #fff;
}

/* the shared chip, so an approach grades the same way a build does:
   green when it clears, amber when it is close, orange when it is not */
body.d2 .opw .fit-chip {
  font-size: 12px;
  padding: 3px 9px;
  justify-self: end;
}

body.d2 .opwe {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 14px;
  font-weight: 400;
  color: var(--el);
}

body.d2 .opwn {
  grid-column: 1 / -1;
  font-size: 13px;
  color: #a8aaaf;
  line-height: 1.35;
}

/* keeping your subclass costs nothing, and that is the point of the line */
body.d2 .opwn.keep {
  color: var(--grade-green);
}

/* ---- the rebuild, pick by pick -----------------------------------------
   Sockets under a row title, the same shape the Guardian cards use, since
   it is the same thing being shown. .vsec and friends come from there; the
   only difference is the padding, because this one sits inside a card that
   already has its own. */
body.d2 .opreb .vsec {
  padding: 12px 0 0;
}

body.d2 .opreb .vsec h5 {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

body.d2 .opreb .vsec h5 .gbc {
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}

/* ---- hover detail on a socket -------------------------------------------
   Fifteen picks will not carry a reason line each — the detail waits until
   a socket is pointed at or tabbed to. */
body.d2 .oslot {
  position: relative;
  display: inline-flex;
  cursor: help;
}

body.d2 .oslot:hover,
body.d2 .oslot:focus-visible {
  z-index: 3;
  outline: none;
}

body.d2 .oslot:hover .isl,
body.d2 .oslot:focus-visible .isl {
  border-color: #fff;
}

body.d2 .otip {
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  z-index: 4;
  width: 208px;
  padding: 10px 12px;
  border-radius: 8px;
  background: #1c1c24;
  border: 1px solid #454857;
  box-shadow: 0 12px 28px rgba(0, 0, 0, .55);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(4px);
  transition: opacity .15s ease, transform .15s ease, visibility .15s;
}

/* the last two in a row open leftward, or they hang off the card */
body.d2 .isls .oslot:nth-last-child(-n + 2) .otip {
  left: auto;
  right: 0;
}

body.d2 .oslot:hover .otip,
body.d2 .oslot:focus-visible .otip {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

body.d2 .oth {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

body.d2 .oth b {
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  line-height: 1.25;
}

body.d2 .otf {
  font-style: normal;
  font-size: 13px;
  font-weight: 700;
  color: var(--grade-yellow);
}

body.d2 .otf.hi {
  color: var(--grade-green);
}

body.d2 .otf.lo {
  color: var(--grade-orange);
}

body.d2 .otk {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--dim);
  margin-top: 2px;
}

body.d2 .otl {
  display: block;
  font-size: 12px;
  color: #cfd2dc;
  line-height: 1.45;
  margin-top: 7px;
}

body.d2 .otr {
  display: block;
  font-size: 12px;
  color: var(--dim);
  margin-top: 7px;
  padding-top: 7px;
  border-top: 1px solid #333642;
}

@media (prefers-reduced-motion: reduce) {

  body.d2 .otip {
    transition: none;
  }
}


/* bigger sockets than the Guardian cards use: this pane has one build to
   show rather than three, and the art is the point of it */
body.d2 .opreb .isl {
  width: 32px;
  height: 32px;
  border-radius: 4px;
}

body.d2 .opreb .isl img {
  border-radius: 4px;
}

body.d2 .opreb .isls {
  gap: 7px;
}

body.d2 .opreb .vrows {
  gap: 14px 22px;
}

body.d2 .oplede {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.55;
  margin: 0;
}

body.d2 .oplede b {
  color: #fff;
  font-weight: 600;
}

/* what changing element actually costs — the picks that cannot come with
   you, and the two things that improve because you moved */
body.d2 .opcost {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 16px;
  padding: 11px 12px;
  border: 1px solid rgba(234, 179, 8, .3);
  border-left: 3px solid var(--grade-yellow);
  border-radius: 8px;
  background: rgba(234, 179, 8, .06);
}

body.d2 .opct {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--grade-yellow);
}

body.d2 .opcl,
body.d2 .opck {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
}

body.d2 .opcl b {
  color: white;
  font-weight: 500;
}

/* what you gain reads green — it is the other side of the ledger */
body.d2 .opck b {
  color: var(--grade-green);
  font-weight: 600;
}

/* ---- what it costs -------------------------------------------------------- */
body.d2 .opjump {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 4px;
}

/* the same 19px the .dv tile values use directly below it, so the jump
   and the numbers it pays for are read at one size */
body.d2 .opjf {
  font-size: 23px;
  font-weight: 500;
  color: #a8aaaf;
}

body.d2 .opja {
  font-size: 16px;
  color: var(--faint);
}

body.d2 .opjt {
  font-size: 23px;
  font-weight: 700;
  color: var(--grade-green);
}

body.d2 .opjl {
  flex: 1 1 140px;
  font-size: 13px;
  color: var(--dim);
  line-height: 1.4;
}

body.d2 .opjl b {
  color: #cfd2dc;
}

/* gear you can't equip yet is shown, not hidden, with what it would score */
body.d2 .oplock {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 3px 10px;
  background: #24252C;
  border: 1px solid #454857;
  border-radius: 6px;
  padding: 11px 12px;
  align-items: center;
}

body.d2 .oplkn {
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  color: #fff;
}

body.d2 .oplkn em {
  display: flex;
  align-items: center;
  gap: 5px;
  font-style: normal;
  font-weight: 600;
  font-size: 13px;
  color: var(--el, var(--dim));
}

/* the art is larger here than in a socket row — one item, and it is the
   thing being described */
body.d2 .oplock .isl {
  width: 34px;
  height: 34px;
  border-radius: 6px;
}

body.d2 .oplock .isl img {
  border-radius: 6px;
}

/* the shared chip again: what it would score is the same kind of number
   as what everything else scores, so it is read the same way */
body.d2 .oplock .fit-chip {
  font-size: 13px;
  padding: 3px 9px;
}

body.d2 .oplkr {
  grid-column: 1 / -1;
  font-size: 13px;
  color: var(--dim);
  line-height: 1.4;
  margin-top: 4px;
}

body.d2 .oplkr b {
  color: var(--grade-green);
  font-weight: 500;
}

/* ============================================================
   Compare and meta builds (#s-compare)
   ============================================================ */

body.d2 .cmpick {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 5px;
  margin-top: 24px;
  margin-bottom: 24px;
}

/* Each side carries its element: --el tints the top edge and the wash,
   --eld is the same hue with alpha (a hex custom property can't take one
   inside a gradient, so the pair is passed inline the way .gmod does it). */
body.d2 .cmp2 {
  position: relative;
  background: #24252C;
  border: 1px solid #454857;
  border-top: 2px solid var(--el);
  border-radius: 6px;
  padding: 10px 12px 11px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  min-width: 0;
  overflow: hidden;
}

body.d2 .cmp2::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 90% at 0% 0%, var(--eld), transparent 62%);
  pointer-events: none;
}

body.d2 .cmp2>* {
  position: relative;
}

body.d2 .cmpt {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  padding-right: 58px;
}

/* the fit score rides the card corner, out of the content flow — .cmp2>*
   makes every other child position:relative, so this needs to outrank it */
body.d2 .cmp2>.fit-chip {
  position: absolute;
  top: 8px;
  right: 9px;
  z-index: 2;
  font-size: 12px;
  padding: 2px 8px;
}

body.d2 .cmpl {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--el);
}

/* where the build came from — the one thing Compare treats as neutral,
   so the two sources read as labels, not as a ranking */
body.d2 .cmsrc {
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  color: #fff;
  white-space: nowrap;
  text-transform: uppercase;
  letter-spacing: 0.9px;
}

body.d2 .cmpb {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* same element-tinted Guardian badge the saved-template rows use, one
   size down — a Compare card and a template row are the same object */
body.d2 .build-row-icon.cmpi {
  width: 32px;
  height: 32px;
}

body.d2 .build-row-icon.cmpi img {
  width: 18px;
  height: 18px;
  border-radius: 3px;
}

body.d2 .cmpn {
  font-size: 14px;
  font-weight: 600;
  color: #fff;
  line-height: 1.2;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* the two builds share a job, so the fit score is what tells them apart —
   it sits on the meta line rather than competing with the source badge */
body.d2 .cmps {
  font-size: 12.5px;
  color: #a8aaaf;
  line-height: 1.3;
}

/* super, aspects then fragments, in the order the subclass screen lists
   them — the super is ringed in the element so the row has a starting point */
body.d2 .cmsg {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: 8px;
  padding-top: 9px;
  border-top: 1px solid #3a3c47;
}

body.d2 .cmskl {
  font-size: 12px;
  font-weight: 400;
  letter-spacing: .3px;
  color: #a8aaaf;
  text-transform: capitalize;
}

body.d2 .cmsk {
  gap: 4px;
}

body.d2 .cmsx {
  width: 22px;
  height: 22px;
  border-radius: 5px;
}

body.d2 .cmps .cmel {
  position: relative;
  padding-left: 16px;
  font-weight: 500;
  color: white;
}

body.d2 .cmps .cmel::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 12px;
  height: 12px;
  background-image: var(--element-icon);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

/* a picker chip that names a source carries that source's glyph */
body.d2 .gbrc img {
  width: 12px;
  height: 12px;
  object-fit: contain;
  margin-right: 6px;
  vertical-align: -2px;
  opacity: .9;
}

body.d2 .cmvs {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: #a8aaaf;
  border: 1px solid #454857;
  border-radius: 999px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1px;
}

body.d2 .gbcmpban {
  font-size: 13px;
  color: #cfd2dc;
  line-height: 1.5;
  background: rgba(234, 179, 8, .07);
  border: 1px solid rgba(234, 179, 8, .3);
  border-left-width: 3px;
  border-radius: 8px;
  padding: 11px 12px;
}

body.d2 .gbcmpban b {
  color: #fff;
}

/* The scoreline: both fit scores either side of a tally of who took which
   measure. The pips are the four tiles below, compressed — so the headline
   and the detail can never disagree. */
body.d2 .cmboard {
  display: flex;
  flex-direction: column;
  padding: 12px 14px;
  margin-bottom: 10px;
  border: 1px solid #454857;
  border-radius: 6px;
  background: #24252C;
}

body.d2 .cmbrow {
  display: flex;
  align-items: center;
  gap: 12px;
}

body.d2 .cmbs {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}

body.d2 .cmbr {
  align-items: flex-end;
  text-align: right;
}

body.d2 .cmbk {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: #a8aaaf;
}

body.d2 .cmbv {
  font-size: 26px;
  font-weight: 500;
  line-height: 1.05;
  color: white;
}

body.d2 .cmbn {
  font-size: 12px;
  color: #a8aaaf;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

body.d2 .cmbs.is-win .cmbk,
body.d2 .cmbs.is-win .cmbv {
  color: var(--grade-green);
}

body.d2 .cmbmid {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  flex: none;
}

body.d2 .cmbw {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: white;
  white-space: nowrap;
}

body.d2 .cmbpips {
  display: flex;
  gap: 4px;
}

body.d2 .cmbpips i {
  width: 18px;
  height: 4px;
  border-radius: 2px;
  background: #454857;
}

body.d2 .cmbpips i.w {
  background: var(--grade-green);
}

/* the ruling that makes the comparison legitimate, under the scoreline
   it qualifies rather than above it as its own strip */
body.d2 .cmbf {
  margin: 11px 0 0;
  padding-top: 10px;
  border-top: 1px solid #3a3c47;
  font-size: 12px;
  color: white;
  line-height: 1.35;
  text-align: left;
}

body.d2 .cmbc {
  font-size: 12px;
  color: #a8aaaf;
  white-space: nowrap;
  text-transform: capitalize;
}

/* .kpitiles lays six tracks and spans the last pair across a row; the verdict
   is a plain row of four, and that rule's pseudo-class outranks a bare
   `.gbverdict .dst`. It also zeroes .dstats' top margin — put it back. */
body.d2 .gbverdict {
  grid-template-columns: repeat(4, 1fr);
  margin-top: 10px;
}

body.d2 .gbverdict .dst,
body.d2 .gbverdict .dst:nth-last-child(-n + 2) {
  grid-column: auto;
}

body.d2 .gbverdict .dst .dh {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: #a8aaaf;
}

body.d2 .gbverdict .dst .dv {
  font-size: 18px;
  font-weight: 400;
}

body.d2 .gbverdict .dst .dv.neg {
  color: var(--grade-red);
}

body.d2 .gbverdict .dst .dv.pos {
  color: var(--grade-green);
}

body.d2 .gbverdict .dst .dl {
  font-size: 13px;
}

/* in "73 · 79" the side that took the measure is the one you should read */
body.d2 .gbverdict .dst .dl .cmwn {
  color: #fff;
  font-weight: 500;
}

body.d2 .gbverdict .dst .dl .pos {
  color: var(--grade-green);
  font-weight: 600;
}

/* the per-stat delta hangs off the end of a normal bar row */
/* the legend above supplies the gap now */
body.d2 .cmside {
  margin-top: 0;
}

/* Both builds on one row, mirrored about a centre gutter: A fills leftward
   from the middle, B fills rightward, so a longer bar always means more of
   the stat whichever side it is on. Values sit against their own bar. */
body.d2 .cmside .srow {
  display: grid;
  grid-template-columns: 82px 26px minmax(0, 1fr) minmax(0, 1fr) 26px 34px;
  align-items: center;
  gap: 0 8px;
}

body.d2 .cmside .srow .nm {
  width: auto;
}

body.d2 .cmside .cva,
body.d2 .cmside .cvb {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
}

body.d2 .cmside .cva {
  text-align: right;
}

body.d2 .cmside .cvb {
  text-align: left;
}

/* A grows from the right edge of its track back towards the name */
body.d2 .cmside .bxa>span {
  margin-left: auto;
}

body.d2 .cmside .bxa {
  border-radius: 4px 1px 1px 4px;
}

body.d2 .cmside .bxb {
  border-radius: 1px 4px 4px 1px;
}

/* which side is which, once, above the rail */
body.d2 .cmlg {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .3px;
  color: white;
}

body.d2 .cmlg span:last-child {
  text-align: right;
}

body.d2 .cmd {
  width: 34px;
  flex: none;
  text-align: center;
  font-size: 12px;
  font-weight: 700;
}

body.d2 .cmd.up {
  color: var(--grade-green);
}

body.d2 .cmd.down {
  color: var(--grade-red);
}

/* ============================================================
   Gear intro (#s-gear) — the five modules as an index

   Each tile is a link into its own section, so the row doubles as
   navigation for the run that follows. Every tile carries its own
   accent, set inline as --acc (solid) and --accd (the same colour
   at low alpha, since a hex custom property can't be given alpha
   in a gradient). The accent drives the glyph, the index numeral,
   the top edge and the hover state, so six tiles read as six
   things rather than one grid of grey.
   ============================================================ */

/* Five tiles, laid out the way .kpitiles handles an odd count: six tracks,
   two apiece, and the trailing pair widens to three so the last row fills
   the width instead of leaving a hole. */
body.d2 .gmods {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 12px;
  max-width: 980px;
  margin: 0 auto;
}

body.d2 .gmod {
  grid-column: span 2;
}

body.d2 .gmod:nth-last-child(-n + 2) {
  grid-column: span 3;
}

body.d2 .gmod {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 150px;
  padding: 16px 17px 13px;
  border: 1px solid #3c3f4d;
  border-radius: 8px;
  background: #171824;
  text-align: left;
  color: inherit;
}

/* the accent wash — strongest at the corner the glyph sits in, gone by
   the time it reaches the copy, so the text never fights it */
body.d2 .gmod::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(110% 90% at 0% 0%, var(--accd), transparent 62%);
  opacity: .85;
}

/* a hairline of the accent along the top edge */
body.d2 .gmod::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--acc);
  opacity: .45;
}

/* the index numeral — large, set back, and never in the way of the copy */
body.d2 .gmx {
  position: absolute;
  top: 10px;
  right: 14px;
  font-family: 'Chakra Petch', 'Work Sans', sans-serif;
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  color: var(--acc);
}

body.d2 .gmi {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 9px;
  background: var(--accd);
  border: 1px solid var(--acc);
  margin-bottom: 12px;
}

body.d2 .gmi img {
  width: 20px;
  height: 20px;
  object-fit: contain;
  display: block;
}

body.d2 .gmn {
  display: block;
  font-size: 15px;
  font-weight: 700;
  color: #fff;
  letter-spacing: .2px;
  margin-bottom: 4px;
}

body.d2 .gml {
  display: block;
  font-size: 13px;
  color: var(--muted);
  line-height: 1.45;
}

/* the module's headline number, on a rule that separates it from the blurb */
body.d2 .gmf {
  display: flex;
  align-items: center;
  margin-top: 10px;
  padding-top: 11px;
  border-top: 1px solid #2c2f3b;
}

body.d2 .gmf b {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--acc);
}

@media (prefers-reduced-motion: reduce) {

  body.d2 .subpane.on .radar-profile,
  body.d2 .subpane.on .radar-last10 {
    animation: none;
  }
}

/* ---- the element palette ----------------------------------------------
   One custom property per subclass, set by the el- class an element-coloured
   component carries. Anything inside then reads var(--el), so a new surface
   needs no new colour rules — only `class="el-<subclass>"`. */
body.d2 .el-solar {
  --el: #FF5600
}

body.d2 .el-void {
  --el: #B185DF
}

body.d2 .el-arc {
  --el: #7AECF3
}

body.d2 .el-stasis {
  --el: #4D88FF
}

body.d2 .el-strand {
  --el: #35E366
}

body.d2 .el-prismatic {
  --el: #DB86D6
}

/* ---- saved templates --------------------------------------------------- */
/* the button sits against a two-line title block, so centre it on that
   block rather than pinning it to the eyebrow */
body.d2 .panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 13px;
}

body.d2 .peyebrow {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: #a8aaaf;
  margin-bottom: 3px;
}

body.d2 .ptitle {
  font-size: 16px;
  font-weight: 700;
  color: #fff;
  line-height: 1.2;
}

body.d2 .build-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* min-height rather than vertical padding: the 12px glyph and the 12px
   label set different line boxes, and padding alone left the button a
   pixel short of square-shouldered. */
body.d2 .bmini {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  padding: 0 14px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  color: #fff;
  background: transparent;
  border: 1px solid #fff;
  border-radius: 6px;
  white-space: nowrap;
}

body.d2 .bmini svg {
  display: block;
  flex: none;
}

body.d2 .build-list {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

body.d2 .build-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 11px;
  background: #24252C;
  border: 1px solid #454857;
  border-radius: 6px;
  padding: 11px 12px;
}

/* the class glyph on a tile washed in the build's own element colour */
body.d2 .build-row-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 28px;
  border: 1px solid var(--el);
}

body.d2 .build-row-icon::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--el);
  opacity: .14;
}

body.d2 .build-row-icon img {
  position: relative;
  width: 18px;
  height: 18px;
  object-fit: contain;
}

body.d2 .build-row-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

body.d2 .build-row-name {
  font-size: 14px;
  font-weight: 600;
  color: #fff;
  line-height: 1.25;
  overflow-wrap: anywhere;
  text-transform: capitalize;
}

body.d2 .build-row-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 0px;
  font-size: 13px;
  color: #a8aaaf;
}

body.d2 .build-row-meta .sep {
  color: #3f4250;
}

body.d2 .build-row-meta .is-class {
  text-transform: capitalize;
}

body.d2 .build-row-meta .is-element {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: 400;
  color: white;
}

body.d2 .choice-icon {
  width: 12px;
  height: 12px;
  object-fit: contain;
  flex: none;
}

/* the fit chip is the row's headline, so it stands in its own column
   beside the actions rather than trailing the subclass line */
body.d2 .build-row>.fit-chip {
  font-size: 12px;
  padding: 3px 9px;
}

body.d2 .build-row-actions {
  display: flex;
  align-items: center;
  gap: 3px;
}

/* one grey for all four — these show what the app offers, they are not
   controls on this page, so none of them is singled out */
body.d2 .icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 6px;
  color: var(--dim);
}

body.d2 .icon-btn svg {
  display: block;
}

/* PvE / PvP reads as a mode, not as more of the subclass line */
body.d2 .is-mode {
  font-weight: 400;
  color: #a8aaaf;
}

/* ============================================================
   Vault (#s-vault)
   ============================================================ */

/* ---- playstyle drift: the radar, and what moved -----------------------
   Radar beside the read-out, then the five axes and the Guardians that
   need a look, both running the full width underneath. The pane is about
   580px, so the radar is capped rather than left to take half of it. */
body.d2 .drift {
  display: grid;
  grid-template-columns: 194px minmax(0, 1fr);
  gap: 0 18px;
  align-items: center;
}

body.d2 .drift-axes,
body.d2 .drift-chars {
  grid-column: 1 / -1;
}

/* smaller than the column it sits in, and centred there, so the axis
   glyphs keep clear of the card edge instead of hugging it */
body.d2 .radar-canvas {
  position: relative;
  width: 100%;
  max-width: 158px;
  margin: 0 auto;
  aspect-ratio: 1;
}

body.d2 .radar-grid {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}

/* the web: three rings, a hub and five spokes, all barely there — the two
   plotted shapes are what the eye is meant to land on */
body.d2 .radar-grid .ring,
body.d2 .radar-grid .hub {
  fill: none;
  stroke: rgba(255, 255, 255, .10);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

body.d2 .radar-grid .ring-3 {
  stroke: rgba(255, 255, 255, .20);
}

body.d2 .radar-grid .spokes line {
  stroke: rgba(255, 255, 255, .08);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

body.d2 .radar-dot {
  fill: #3CB4CC;
  stroke: #3CB4CC;
  stroke-width: 1.2;
  vector-effect: non-scaling-stroke;
}

/* axis glyphs sit outside the outer ring, centred on their own spoke */
body.d2 .radar-icon {
  position: absolute;
  width: 15px;
  height: 15px;
  object-fit: contain;
  opacity: .72;
  transform: translate(-50%, -50%);
}

body.d2 .radar-icon--top {
  left: 50%;
  top: -1%;
}

body.d2 .radar-icon--right {
  left: 98%;
  top: 34%;
}

body.d2 .radar-icon--br {
  left: 80%;
  top: 92%;
}

body.d2 .radar-icon--bl {
  left: 20%;
  top: 92%;
}

body.d2 .radar-icon--left {
  left: 2%;
  top: 34%;
}

/* the two shapes grow out of the hub when the tab opens. Re-adding .on
   restarts the animation, so it replays each time you come back to the tab. */
body.d2 .subpane.on .radar-profile,
body.d2 .subpane.on .radar-last10 {
  transform-origin: 150px 150px;
  animation: driftIn .8s cubic-bezier(.2, .9, .2, 1) both;
}

body.d2 .subpane.on .radar-last10 {
  animation-delay: .1s;
}

@keyframes driftIn {
  from {
    transform: scale(.55);
    opacity: 0
  }

  to {
    transform: scale(1);
    opacity: 1
  }
}


/* ---- hover detail on each axis ----------------------------------------
   One hit per vertex, sitting on whichever of the two shapes reaches
   furthest out on that axis — that is the point the eye goes to. The
   target is larger than the dot it covers so it is not a pixel hunt, and
   it is focusable, so the detail is reachable without a pointer. */
body.d2 .radar-hit {
  position: absolute;
  width: 24px;
  height: 24px;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: help;
}

body.d2 .radar-hit:hover,
body.d2 .radar-hit:focus-visible {
  z-index: 3;
  outline: none;
}

body.d2 .radar-hit-ring {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid transparent;
  transition: border-color .15s ease, background .15s ease, transform .15s ease;
}

body.d2 .radar-hit:hover .radar-hit-ring,
body.d2 .radar-hit:focus-visible .radar-hit-ring {
  border-color: #fff;
  background: rgba(255, 255, 255, .14);
  transform: scale(1.15);
}

/* the axis that actually moved carries the amber standing, not just on hover */
body.d2 .radar-hit.is-moved .radar-hit-ring {
  border-color: #f6ba49;
}

/* ---- the tooltip ------------------------------------------------------
   The radar is only ~158px across, so a centred tooltip would hang off the
   card at the left and right vertices. Each hit says which way to open:
   .align-start grows rightward, .align-end leftward, .is-below downward
   for the top vertex, which has no room above it. */
body.d2 .radar-tooltip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%) translateY(4px);
  width: 212px;
  padding: 10px 12px;
  border-radius: 8px;
  background: #1c1c24;
  border: 1px solid #454857;
  box-shadow: 0 12px 28px rgba(0, 0, 0, .55);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .15s ease, transform .15s ease, visibility .15s;
}

body.d2 .radar-hit.is-below .radar-tooltip {
  bottom: auto;
  top: calc(100% + 8px);
  transform: translateX(-50%) translateY(-4px);
}

body.d2 .radar-hit.align-start .radar-tooltip {
  left: 0;
  transform: translateX(-4px) translateY(4px);
}

body.d2 .radar-hit.align-end .radar-tooltip {
  left: auto;
  right: 0;
  transform: translateX(4px) translateY(4px);
}

body.d2 .radar-hit:hover .radar-tooltip,
body.d2 .radar-hit:focus-visible .radar-tooltip {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

body.d2 .radar-hit.is-below:hover .radar-tooltip,
body.d2 .radar-hit.is-below:focus-visible .radar-tooltip {
  transform: translateX(-50%) translateY(0);
}

body.d2 .radar-hit.align-start:hover .radar-tooltip,
body.d2 .radar-hit.align-start:focus-visible .radar-tooltip {
  transform: translateX(0) translateY(0);
}

body.d2 .radar-hit.align-end:hover .radar-tooltip,
body.d2 .radar-hit.align-end:focus-visible .radar-tooltip {
  transform: translateX(0) translateY(0);
}

body.d2 .rt-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}

body.d2 .rt-icon-title {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

body.d2 .rt-icon {
  width: 14px;
  height: 14px;
  object-fit: contain;
  opacity: .85;
  flex: none;
}

body.d2 .rt-title {
  font-size: 13px;
  font-weight: 700;
  color: #fff;
}

body.d2 .rt-value {
  font-size: 12px;
  font-weight: 700;
  color: var(--dim);
  white-space: nowrap;
}

body.d2 .rt-value.is-up {
  color: var(--grade-green);
}

body.d2 .rt-value.is-down {
  color: var(--grade-red);
}

/* the two readings side by side, each under its own colour from the legend */
body.d2 .rt-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

body.d2 .rt-leg {
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  padding-left: 8px;
  border-left: 2px solid;
}

body.d2 .rt-leg em {
  display: block;
  font-style: normal;
  font-weight: 400;
  font-size: 11px;
  color: var(--dim);
}

body.d2 .rt-leg.is-profile {
  border-left-color: #4291F2;
}

body.d2 .rt-leg.is-last10 {
  border-left-color: #f6ba49;
}

body.d2 .rt-desc {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.45;
  margin: 8px 0 0;
  padding-top: 8px;
  border-top: 1px solid #333642;
}

@media (prefers-reduced-motion: reduce) {

  body.d2 .radar-tooltip,
  body.d2 .radar-hit-ring {
    transition: none;
  }
}

/* ---- the read-out ------------------------------------------------------ */
/* a narrower measure for the read-out — a long line costs more to scan
   than the extra wrap does */
body.d2 .drift-body {
  min-width: 0;
  max-width: 340px;
}

/* the archetypes hold the first line; the window always takes the second,
   so the row gap is zero and .shift-window sets its own spacing */
body.d2 .drift-shift {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 8px;
  margin-bottom: 10px;
}

body.d2 .shift-from {
  color: #64748b;
  font-size: 17px;
  line-height: 1.15;
  font-weight: 300;
  text-decoration: line-through rgba(148, 163, 184, 0.45);
}

body.d2 .shift-arrow {
  font-size: 17px;
  color: var(--faint);
}

/* the archetype you are now is the headline of the line, so it takes the
   brand gradient as a text fill. The solid colour underneath is the
   fallback: if background-clip: text is not honoured, the transparent
   fill below would otherwise render the word invisible. */
body.d2 .shift-to {
  font-size: 23px;
  font-weight: 600;
  line-height: 1.15;
  color: #4BD1E4;
  background-image: linear-gradient(100deg, #4BD1E4 0%, #4291F2 45%, #7727FB 75%, #CD28E2 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

@supports ((background-clip: text) or (-webkit-background-clip: text)) {

  body.d2 .shift-to {
    -webkit-text-fill-color: transparent;
  }
}

/* always breaks to its own line under the two archetypes */
body.d2 .shift-window {
  flex: 0 0 100%;
  margin-top: 3px;
  font-size: 13px;
  color: #a8aaaf;
}

body.d2 .drift-text {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
  margin: 0 0 12px;
}

body.d2 .drift-text b {
  color: #fff;
}

/* the legend names the two shapes on the radar, in their own colours */
body.d2 .radar-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
}

body.d2 .radar-key {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  font-weight: 600;
  color: #fff;
}

body.d2 .radar-key::before {
  content: "";
  width: 14px;
  height: 3px;
  border-radius: 2px;
  flex: none;
}

body.d2 .radar-key.is-profile::before {
  background: linear-gradient(90deg, #4291F2, #CD28E2);
}

body.d2 .radar-key.is-last10::before {
  background: #f6ba49;
}

body.d2 .radar-key em {
  font-style: normal;
  font-weight: 400;
  color: var(--dim);
}

/* ---- the five axes ----------------------------------------------------- */
body.d2 .drift-axes {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: 38px;
}

body.d2 .drift-axis {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--muted);
  background: #24252C;
  border: 1px solid #454857;
  border-radius: 999px;
  padding: 5px 12px 5px 9px;
}

body.d2 .axis-glyph {
  width: 13px;
  height: 13px;
  object-fit: contain;
  opacity: .8;
  flex: none;
}

body.d2 .drift-axis b {
  font-weight: 600;
  color: #cfd2dc;
}

/* the axis that actually moved carries the amber — everything else held */
body.d2 .drift-axis.is-moved {
  color: var(--grade-yellow);
  border-color: rgba(234, 179, 8, .5);
  background: rgba(234, 179, 8, .08);
}

body.d2 .drift-axis.is-moved .axis-glyph {
  opacity: 1;
}

body.d2 .drift-axis.is-moved b {
  color: var(--grade-yellow);
}

/* ---- the Guardians that need a look ------------------------------------ */
body.d2 .drift-chars {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  margin-top: 24px;
  padding-top: 14px;
  border-top: 1px solid #22262c;
}

body.d2 .drift-chars-label {
  font-size: 12px;
  font-weight: 500;
  color: #fff;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* a hairline after the label, so the three Guardians read as its list */
body.d2 .drift-chars-label::after {
  content: "";
  display: inline-block;
  width: 1px;
  height: 11px;
  margin-left: 14px;
  background: #3a3d4a;
  vertical-align: -1px;
}

body.d2 .drift-char {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: #cfd2dc;
}

body.d2 .class-icon {
  width: 14px;
  height: 14px;
  object-fit: contain;
  opacity: .85;
  flex: none;
}

body.d2 .drift-char .delta {
  font-weight: 700;
  color: var(--grade-red);
}

/* ---- shared: the fit chip ---------------------------------------------
   One chip, used by the Guardian cards, the saved-build rows and the
   Optimise Guardian tiles alike. */
body.d2 .fit-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  font-size: 13px;
  font-weight: 700;
  border-radius: 999px;
  padding: 3px 9px;
  border: 1px solid;
  white-space: nowrap;
}

body.d2 .fit-chip span {
  font-weight: 600;
  font-size: 11px;
  letter-spacing: .5px;
}

body.d2 .fit-chip.is-fair {
  color: var(--grade-yellow);
  border-color: rgba(234, 179, 8, .5);
  background: rgba(234, 179, 8, .12);
}

body.d2 .fit-chip.is-weak {
  color: var(--grade-orange);
  border-color: rgba(249, 115, 22, .5);
  background: rgba(249, 115, 22, .12);
}

body.d2 .fit-chip.is-good {
  color: var(--grade-green);
  border-color: rgba(34, 197, 94, .5);
  background: rgba(34, 197, 94, .12);
}

/* ---- shared: the item-art socket --------------------------------------
   The app's .icon-slot, used by the Guardian cards, the builder's gear and
   subclass slots, the swap rows and the Optimise rebuild rows — so a piece
   of gear looks the same whether it is equipped, planned or suggested.
   `contain`, not `cover`: manifest art is square and already padded, and
   cover crops the frame these icons are drawn with. */
body.d2 .isl {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  border-radius: 4px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, .14);
}

body.d2 .isl img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 4px;
  display: block;
}

/* an unearned fragment slot stays visible — the gap is the information */
body.d2 .isl.em {
  background: #0E0E10;
  border: 1px solid #2a2d34;
}

/* ---- the Guardian roll ------------------------------------------------
   Same stacking trick as .card.gcards: the three Guardians share one grid
   cell so the card keeps a single height and nothing jumps as it rolls.
   The section tab above already says Your Guardians, and the card rolls
   through the three on its own, so it carries no pips of its own — the
   panes are its only row.

   The card itself mirrors the app's .character-card: the banner
   header with its readability scrim, the element and archetype pills, the
   sentence-case row titles, and the 28px icon sockets. */
body.d2 .card.vcards {
  display: grid;
  grid-template-columns: 100%;
  align-content: start;
  padding: 0;
  overflow: hidden;
  cursor: pointer;
  /* clicking steps to the next Guardian */
}

body.d2 .card.vcards>.subpane {
  grid-area: 1 / 1;
}

/* .card.vcards carries no padding of its own, so the card already sits
   flush to the pane edges — the banner needs no negative margin to reach
   them, and its own 16px padding sets the inset for everything below. */
body.d2 .vcard {
  border-radius: inherit;
}

/* ---- header ------------------------------------------------------------
   The emblem banner is set per card as --banner; the scrim that keeps the
   name and the numbers readable sits in ::after, so a light emblem darkens
   the same way a dark one does. */
body.d2 .vhead {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px;
  background-image: var(--banner);
  background-size: cover;
  background-position: top center;
  background-repeat: no-repeat;
  overflow: hidden;
}

body.d2 .vhead::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to bottom,
      rgba(15, 16, 20, .35) 0%,
      rgba(15, 16, 20, .45) 55%,
      rgba(15, 16, 20, .80) 100%);
}

body.d2 .vh-left {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

body.d2 .vav {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid #2a2d34;
  background: #1f2127;
  object-fit: cover;
  flex: 0 0 auto;
}

body.d2 .vtit {
  display: grid;
  min-width: 0;
}

body.d2 .vtit h4 {
  margin: 0;
  font-size: 20px;
  line-height: 1.2;
  font-weight: 600;
  color: #f3f5f7;
}

body.d2 .vsubt {
  font-size: 13px;
  color: #aeb6c2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.d2 .vh-right {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  flex: 0 0 auto;
}

body.d2 .vpw {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: #f5b301;
  white-space: nowrap;
}

body.d2 .vpw .star {
  font-size: 14px;
  line-height: 1;
  display: inline-block;
  transform: translateY(-.5px);
}

/* ---- element and archetype pills --------------------------------------- */
body.d2 .vtags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 12px 16px 14px;
}

body.d2 .vtag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  font-size: 12px;
  color: #d5d9e0;
  background: #0E0E10;
  border: 1px solid transparent;
  border-radius: 4px;
}

/* the element glyph is a background on ::before, so the pill's own text
   stays centred whatever the icon's aspect ratio is */
body.d2 .vel {
  position: relative;
  padding-left: 26px;
  color: #fff;
  border-color: var(--el);
  text-transform: capitalize;
}

body.d2 .vel::before {
  content: "";
  position: absolute;
  left: 6px;
  width: 14px;
  height: 14px;
  background-image: var(--element-icon);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

body.d2 .varch {
  padding: 0 8px;
  min-height: 24px;
}

body.d2 .vtag-emblem {
  width: 16px;
  height: 16px;
  object-fit: contain;
  flex: 0 0 auto;
}

/* ---- subclass and loadout ---------------------------------------------- */
body.d2 .vsec {
  padding: 12px 16px;
  border-top: 1px solid #22262c;
  margin-top: 12px;
}

body.d2 .vsec h5 {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  letter-spacing: .1em;
  text-transform: uppercase;
}

body.d2 .vrows {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-start;
}

body.d2 .vcol {
  display: grid;
  gap: 6px;
  min-width: 0;
}

/* the fragment row sits under its three siblings, not beside them */
body.d2 .vsec>.vcol {
  margin-top: 12px;
}

body.d2 .vrt {
  font-size: 12px;
  color: #7c8491;
  letter-spacing: .04em;
}

/* ---- icon sockets ------------------------------------------------------ */
body.d2 .isls {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

/* the Bungie mark is a 3.4:1 wordmark, so the square icon box beside it
   would shrink it to 6px tall — size it by height and let width follow */
body.d2 .dchip img.dlogo {
  width: auto;
  height: 17px;
  margin-bottom: 10px;
}

/* the Destiny wordmark is wider and heavier than the Dota one, so it reads
   larger at the shared 19px — bring it down rather than change both */
body.d2 .dplogo {
  height: 14px;
}

/* A long publisher name wrapped to a second line and pushed its whole row
   taller than the one below it — clip it instead, the favicon carries the
   rest of the identity. */
body.d2 .gc .src .sname {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The profile header wears the Guardian's own emblem, scrimmed the way the
   Vault card does it, with the live character portrait in place of Dota's
   rank medal. Scoped to d2 — .pcard-top is shared with the Dota page. */
body.d2 .pcard-top {
  position: relative;
  margin: -13px -14px 0;
  padding: 14px;
  border-bottom: none;
  border-radius: 9px 9px 0 0;
  background-image: var(--banner);
  background-size: cover;
  background-position: top center;
  background-repeat: no-repeat;
  overflow: hidden;
}

body.d2 .pcard-top::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to bottom,
      rgba(15, 16, 20, .55) 0%,
      rgba(15, 16, 20, .68) 55%,
      rgba(15, 16, 20, .92) 100%);
}

body.d2 .pcard-top>* {
  position: relative;
  z-index: 1;
}

body.d2 .pcard-rank {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .22);
  background: #1f2127;
  object-fit: cover;
}

body.d2 .pcard-id {
  min-width: 0;
}

body.d2 .pcard-since {
  color: #cfd2dc;
}

/* Signature Guardians: the shared .sighero stacks wide hero art over its
   label, but a super is a small square mark — set it beside the text and
   ring it in that Guardian's element, so the row reads Solar / Void / Arc. */
body.d2 .sighero {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 9px;
}

body.d2 .sighero img {
  width: 34px;
  height: 34px;
  flex: none;
  object-fit: contain;
  margin-bottom: 0;
  border-radius: 5px;
  background: var(--eld);
  border: 1px solid var(--el);
}

body.d2 .shtx {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

body.d2 .sighero .shn {
  line-height: 1.2;
  overflow-wrap: anywhere;
}

body.d2 .sighero .shg {
  font-size: 11px;
  line-height: 1.2;
}

/* ---- the review footer ------------------------------------------------- */
body.d2 .vrev {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  padding: 12px 16px;
  border-top: 1px solid #22262c;
  background: rgba(234, 179, 8, .05);
}

/* the badge is centred by the flex row, not by a vertical-align guess —
   a 14px circle next to 13px text never lines up on the baseline */
body.d2 .vrn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: #cfd2dc;
}

body.d2 .vrn::before {
  content: "!";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  flex: none;
  line-height: 1;
  border-radius: 50%;
  border: 1px solid var(--grade-yellow);
  color: var(--grade-yellow);
  font-size: 11px;
  font-weight: 700;
}

body.d2 .vrn b {
  color: var(--grade-yellow);
}

body.d2 .vrb {
  font-size: 12px;
  font-weight: 600;
  color: #cfd2dc;
  border: 1px solid #555a6b;
  border-radius: 6px;
  padding: 5px 12px;
  white-space: nowrap;
}

/* ============================================================
   Weapons (#s-weapons)
   ============================================================ */

body.d2 .wgrid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
}

/* the filter chips are a control strip, not a heading — give the results
   room to read as a separate list */
body.d2 .gbrcs+.wgrid {
  margin-top: 10px;
}

/* art | name + type | fit score — the icon spans both text rows */
body.d2 .wc {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  gap: 0 9px;
  align-content: start;
  background: #24252C;
  border: 1px solid #454857;
  border-radius: 6px;
  padding: 10px 10px;
  min-width: 0;
}

body.d2 .wc.on {
  border-color: rgba(242, 137, 107, .55);
}

body.d2 .wci {
  grid-row: 1 / 3;
  align-self: center;
}

body.d2 .wcn {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  line-height: 1.2;
  min-width: 0;
  overflow-wrap: anywhere;
}

body.d2 .wcf {
  font-size: 14px;
  font-weight: 700;
  color: var(--grade-yellow);
  text-align: right;
  line-height: 1.2;
}

body.d2 .wcf.hi {
  color: var(--grade-green);
}

body.d2 .wcf.lo {
  color: var(--grade-orange);
}

body.d2 .wct {
  grid-column: 2 / 3;
  font-size: 12.5px;
  font-weight: 400;
  color: #a8aaaf;
  line-height: 1.3;
  margin-top: 2px;
}

body.d2 .wcm {
  grid-column: 1 / -1;
  font-size: 12.5px;
  color: #a8aaaf;
  margin-top: 7px;
  padding-top: 6px;
  border-top: 1px solid #333642;
  line-height: 1.3;
}

body.d2 .wcm b {
  font-weight: 500;
  color: #cfd2dc;
}

/* damage type carries its own glyph — Kinetic has none in game, so it
   stays a plain label rather than borrowing someone else's icon */
body.d2 .wcm b.wel {
  position: relative;
  padding-left: 16px;
}

body.d2 .wcm b.wel::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 12px;
  height: 12px;
  background-image: var(--element-icon);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

body.d2 .wcm b.solar {
  color: #FF5600;
}

body.d2 .wcm b.void {
  color: #B185DF;
}

body.d2 .wcm b.arc {
  color: #7AECF3;
}

/* gear you don't own still shows — with what it would be worth */
body.d2 .wcm i {
  font-style: normal;
  color: var(--grade-orange);
}

/* the Crucible / PvE split under a weapon's totals */
/* Six tiles at three across: the four totals, then the Crucible / PvE split
   that used to sit under them as a label list. .kpitiles spans the trailing
   pair across a row for odd counts — six divides evenly, so clear it. A
   tighter tier than the KPI tabs: these are a breakdown, not the headline. */
body.d2 .wrectiles {
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 10px;
}

body.d2 .wrectiles .dst,
body.d2 .wrectiles .dst:nth-last-child(-n + 2) {
  grid-column: auto;
  padding: 8px 9px;
  gap: 3px;
}

body.d2 .wrectiles .dh {
  font-size: 12px;
  gap: 5px;
}

body.d2 .wrectiles .dsti {
  width: 12px;
  height: 12px;
}

body.d2 .wrectiles .dv {
  font-size: 16px;
}

body.d2 .wrectiles .dl {
  font-size: 11px;
}

/* perk columns, as the picker lays them out */
body.d2 .wperks {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

body.d2 .wpc {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}

body.d2 .wpl {
  margin-bottom: 1px;
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* ---- shared additions -------------------------------------------------- */

/* an unchanged axis gets a dash, not a zero — nothing moved */
body.d2 .cmd {
  color: var(--dim);
}

/* ============================================================
   The gear suite — narrower viewports
   ============================================================ */

@media (max-width: 720px) {

  body.d2 .gbbudget {
    grid-template-columns: repeat(2, 1fr);
  }

  /* two across, spans cleared — the six-track trick is a wide-screen layout */
  body.d2 .gmods {
    grid-template-columns: repeat(2, 1fr);
  }

  body.d2 .gmod,
  body.d2 .gmod:nth-last-child(-n + 2) {
    grid-column: auto;
  }

  body.d2 .gbverdict {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 560px) {

  /* two across, spans cleared — the six-track trick is a wider layout —
     and the odd tile out takes the last row to itself */
  body.d2 .gbslots.w3,
  body.d2 .gbslots.w5 {
    grid-template-columns: repeat(2, 1fr);
  }

  body.d2 .gbslots.w5>.gbslot,
  body.d2 .gbslots.w5>.gbslot:nth-last-child(-n + 2) {
    grid-column: auto;
  }

  body.d2 .gbslots.w3>.gbslot:last-child,
  body.d2 .gbslots.w5>.gbslot:last-child {
    grid-column: 1 / -1;
  }

  body.d2 .gbbudget {
    grid-template-columns: repeat(2, 1fr);
  }

  body.d2 .opways {
    grid-template-columns: minmax(0, 1fr);
  }

  /* the slot label and the Apply button drop away; the art, the swap itself
     and what it is worth are what still has to fit */
  body.d2 .gbswap {
    grid-template-columns: auto minmax(0, 1fr) auto;
  }

  body.d2 .gbsk,
  body.d2 .gbsb {
    display: none;
  }

  /* A above B, with the vs between them */
  body.d2 .cmpick {
    grid-template-columns: minmax(0, 1fr);
    justify-items: stretch;
  }

  body.d2 .cmvs {
    justify-self: center;
  }

  body.d2 .gmods,
  body.d2 .wgrid,
  body.d2 .wperks {
    grid-template-columns: minmax(0, 1fr);
  }

  body.d2 .wrectiles {
    grid-template-columns: repeat(2, 1fr);
  }


  /* radar over the read-out rather than a column squeezed against text
     that no longer has room beside it — and the copy takes the full width
     back, since nothing is sharing the row with it */
  body.d2 .drift {
    grid-template-columns: minmax(0, 1fr);
  }

  body.d2 .drift-radar {
    max-width: 200px;
    margin: 0 auto 4px;
  }

  body.d2 .drift-body {
    max-width: none;
  }

  /* stacked: the fit chip sits top right beside the name, the actions drop to
     a second line under it, and the first two icons give way — the
     four-column row has no room for everything at this width */
  body.d2 .build-row {
    grid-template-columns: auto minmax(0, 1fr) auto;
    row-gap: 6px;
  }

  body.d2 .build-row-icon {
    grid-row: 1 / span 2;
  }

  body.d2 .build-row>.fit-chip {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    align-self: start;
  }

  body.d2 .build-row-body {
    grid-column: 2;
    grid-row: 1 / span 2;
  }

  body.d2 .build-row-actions {
    grid-column: 3;
    grid-row: 2;
    justify-self: end;
  }

  /* view and duplicate are conveniences; delete and open are the two the
     row can't do without */
  body.d2 .build-row-actions .icon-btn:nth-child(-n+2) {
    display: none;
  }

  /* six columns will not fit — drop the label to its own line and let the
     two bars keep the full width */
  body.d2 .cmside .srow {
    grid-template-columns: 26px minmax(0, 1fr) minmax(0, 1fr) 26px 30px;
    row-gap: 2px;
  }

  body.d2 .cmside .srow .nm {
    grid-column: 1 / -1;
  }

  body.d2 .gbrail .srow .nm {
    width: 104px;
  }
}

/* Recent Matches: no margin on its heading. The .pshd rule is shared with the
   Match overview pane, which needs the 12px above it to clear the header card —
   this pane opens on the heading, so it doesn't. */
body.d2 [data-g="mh"][data-i="2"] .pshd {
  margin: 0;
  font-size: 15px;
  margin-bottom: 6px;
}

/* Guardian cards: real Bungie manifest art in the icon slots.
   The colour-swatch spans stay for builds we haven't mapped icons for. */
body.d2 .icg img.ic,
body.d2 img.ic-sq {
  display: block;
  object-fit: cover;
  background: #2a2333;
}

body.d2 .icg img.ic {
  border: 1px solid rgba(255, 255, 255, .06);
}

/* the Guardian's equipped emblem, banner art behind the card header.
   The inline style supplies the scrim + url; sizing lives here. */
body.d2 .chr .ch.ch-emb {
  background-size: auto, cover;
  /* layer 1 = scrim gradient, layer 2 = banner art */
  background-position: center, center;
  background-repeat: no-repeat;
}

body.d2 .chr .ch.ch-emb .ic-sq {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .12);
}

/* banner art varies wildly in brightness, so the header text carries its own contrast */


body.d2 .ch-emb .cn {
  color: #fff;
}

body.d2 .ch-emb .cs {
  color: rgba(255, 255, 255, .74);
}

/* element glyph inside the subclass chip */
body.d2 .chip2 {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

body.d2 .chip2 img.elic {
  width: 11px;
  height: 11px;
  object-fit: contain;
  flex: none;
}

/* KPI tracker: a flat week reads as neither a win nor a loss */
/* The shared widget has two states — met, or amber for everything else —
   so a bar at 94% of target was painted the same warning colour as one at
   60%. The five grades spread across how far short of target the figure is:
   met 100%+, near 85, fair 70, short 55, far below that. */
body.d2 .kpit.met {
  --kt: var(--grade-green);
}

body.d2 .kpit.near {
  --kt: var(--grade-teal);
}

body.d2 .kpit.fair {
  --kt: var(--grade-yellow);
}

body.d2 .kpit.short {
  --kt: var(--grade-orange);
}

body.d2 .kpit.far {
  --kt: var(--grade-red);
}

body.d2 .kpid.neu {
  color: var(--dim);
  background: rgba(255, 255, 255, .04);
}

/* subclass emblem in the archetype chip — app: .tag--archetype .tag-emblem */
body.d2 .chip2 img.scic {
  width: 14px;
  height: 14px;
  object-fit: contain;
  flex: none;
}

/* the game switcher follows the page under the fixed site header (sticky, as
   in styles.css); the block above sets it relative to lift it over the
   atmosphere, so restate sticky here — it still stacks above the effects */
body.d2 .gswitch-bar {
  position: sticky;
  top: 0;
  z-index: 1000
}

@media(max-width:620px) {
  body.d2 .gswitch-bar {
    position: relative
  }
}

/* Career stats footer: four nowrap cells in one row overflow a narrow card
   (stacked layout / phones). Sized by the card itself, not the viewport:
   under 520px of card it becomes two rows of two (left cell left-aligned,
   right cell right-aligned), under 300px a single column. */
body.d2 .card:has(> .cs-foot) {
  container: csfoot / inline-size;
}

@container csfoot (max-width: 520px) {
  body.d2 .cs-foot {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: 14px;
  }

  body.d2 .cs-foot .cf:nth-child(odd) {
    text-align: left;
  }

  body.d2 .cs-foot .cf:nth-child(odd) .n {
    justify-content: flex-start;
  }

  body.d2 .cs-foot .cf:nth-child(even) {
    text-align: right;
  }

  body.d2 .cs-foot .cf:nth-child(even) .n {
    justify-content: flex-end;
  }
}

@container csfoot (max-width: 300px) {
  body.d2 .cs-foot {
    grid-template-columns: minmax(0, 1fr);
  }

  body.d2 .cs-foot .cf:nth-child(even) {
    text-align: left;
  }

  body.d2 .cs-foot .cf:nth-child(even) .n {
    justify-content: flex-start;
  }
}

/* ...and at phone width (about a 410px screen, ~332px of card) the Wins/Losses
   and Cleared/Entered pairs under each dial stack too */
@container csfoot (max-width: 345px) {
  body.d2 .cs-col .lgd {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
  }

  /* each stat: its bar across the top, label left and number right under it */
  body.d2 .cs-col .lg {
    width: 100%;
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: baseline;
    column-gap: 10px;
    text-align: left;
  }

  body.d2 .cs-col .lg .bar {
    grid-column: 1 / -1;
  }

  body.d2 .cs-col .lg .n {
    text-align: right;
  }
}

/* narrowest cards (300px and under): the PvP and PvE halves no longer fit side
   by side, so stack them, with the divider turned horizontal between them */
@container csfoot (max-width: 300px) {
  body.d2 .cs-grid {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 18px;
  }

  body.d2 .cs-div {
    width: auto;
    height: 1px;
  }
}

/* Your KPIs (eight tiles): keep the desktop 3 + 3 + 2 layout at every screen
   width — the shared 900px rule that clears the spans turned it into an
   uneven 6 + 2 — and only when the card itself gets too narrow for three
   tiles a row (labels like "Total super kills" start wrapping/clipping
   around 480px of card) switch to an even two-column grid. */
body.d2 .card:has(> .kpi8) {
  container: kpi8 / inline-size;
}

body.d2 .kpi8 {
  grid-template-columns: repeat(6, minmax(0, 1fr));
}

body.d2 .kpi8 .dst {
  grid-column: span 2;
}

body.d2 .kpi8 .dst:nth-last-child(-n + 2) {
  grid-column: span 3;
}

@container kpi8 (max-width: 480px) {
  body.d2 .kpi8 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  body.d2 .kpi8 .dst,
  body.d2 .kpi8 .dst:nth-last-child(-n + 2) {
    grid-column: auto;
  }
}

/* Top Playlists: the four numeric columns (Games, Kills, K/D, WR) at 13px,
   right-aligned under their right-aligned headings */
body.d2 .thr.c4>span:not(.thh) {
  font-size: 13px;
  text-align: right;
}

/* long playlist names ("Iron Banner Control") clip to one line with an
   ellipsis; the name track is minmax(0, 1fr) so it can shrink below its text
   instead of shoving into the number columns */
body.d2 .thead.c4,
body.d2 .thr.c4 {
  grid-template-columns: minmax(0, 1fr) 48px 48px 48px 48px;
}

body.d2 .thr.c4 .thh {
  min-width: 0;
}

body.d2 .thr.c4 .tht {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Your KPIs: two even columns from a 660px screen down (as well as when the
   card itself gets narrow), then one column once two-up would clip labels */
@media (max-width: 660px) {
  body.d2 .kpi8 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  body.d2 .kpi8 .dst,
  body.d2 .kpi8 .dst:nth-last-child(-n + 2) {
    grid-column: auto;
  }
}

@container kpi8 (max-width: 300px) {
  body.d2 .kpi8 {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Match overview tiles (six, 3 + 3): stay three across while shrinking, go to
   two even columns at a 660px screen, and to one column once the card is too
   narrow for two (labels like "Opponents Defeated" would clip) */
body.d2 .card:has(> .mtiles) {
  container: mtiles / inline-size;
}

@media (max-width: 660px) {
  body.d2 .mtiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@container mtiles (max-width: 300px) {
  body.d2 .mtiles {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ...and a single column on a 450px screen */
@media (max-width: 450px) {
  body.d2 .mtiles {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Match scoreboard on phones (540px and below): drop Opp. Def. and Precision so
   Player, Score and K/D/A keep their room */
@media (max-width: 540px) {

  body.d2 .sbscore th:nth-child(4),
  body.d2 .sbscore td:nth-child(4),
  body.d2 .sbscore th:nth-child(5),
  body.d2 .sbscore td:nth-child(5) {
    display: none;
  }
}

/* ...and at 420px the "kill share" note beside your name goes too */
@media (max-width: 420px) {
  body.d2 .sbyou {
    display: none;
  }
}

/* Recent Matches on phones (500px and below): keep Date, Result and K/D/A;
   drop Guardian, KDA and Dur */
@media (max-width: 500px) {

  body.d2 .sbrecent th:nth-child(3),
  body.d2 .sbrecent td:nth-child(3),
  body.d2 .sbrecent th:nth-child(5),
  body.d2 .sbrecent td:nth-child(5),
  body.d2 .sbrecent th:nth-child(6),
  body.d2 .sbrecent td:nth-child(6) {
    display: none;
  }
}
/* 440px and below: the cost tiles (Glimmer / Modules / Shards / Mod energy)
   go one per row, and every section label's note ("includes a subclass
   respec", "synced from Bungie"…) drops under its heading, left-aligned */
@media (max-width: 440px) {
  body.d2 .gbbudget {
    grid-template-columns: minmax(0, 1fr);
  }

  body.d2 .gbrow {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }

  body.d2 .gbc {
    text-align: left;
  }

  /* the "73 › 80" fit jump keeps its numbers on one line and the
     explanation wraps onto its own line beneath them */
  body.d2 .opjl {
    flex-basis: 100%;
  }
}

/* 420px and below: gear slots (weapons and armour) one per row as compact
   list rows — the item icon on the left, the slot ("KINETIC") stacked over
   the item name beside it. The rarity/type line and the perk icons go. */
@media (max-width: 420px) {
  body.d2 .gbslots.w3,
  body.d2 .gbslots.w5 {
    grid-template-columns: minmax(0, 1fr);
  }

  body.d2 .gbslots>.gbslot,
  body.d2 .gbslots.w5>.gbslot:nth-last-child(-n + 2),
  body.d2 .gbslots>.gbslot:last-child {
    grid-column: auto;
  }

  body.d2 .gbslot {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: 10px;
    align-items: center;
  }

  body.d2 .gbslot .gbhead {
    display: contents;
  }

  body.d2 .gbslot .gbhead .isl {
    grid-column: 1;
    grid-row: 1 / span 2;
  }

  body.d2 .gbslot .gbk {
    grid-column: 2;
    grid-row: 1;
    align-self: end;
  }

  body.d2 .gbslot .gbn {
    grid-column: 2;
    grid-row: 2;
    align-self: start;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  body.d2 .gbslot .gbs,
  body.d2 .gbslot .gbperks {
    display: none;
  }
}

/* weapon record tiles: one per row at 400px and below */
@media (max-width: 400px) {
  body.d2 .wrectiles {
    grid-template-columns: minmax(0, 1fr);
  }

  body.d2 .wrectiles .dst,
  body.d2 .wrectiles .dst:nth-last-child(-n + 2) {
    grid-column: auto;
  }
}

/* Signature Guardians at 620px and below: icon above the name and line */
@media (max-width: 620px) {
  body.d2 .sighero {
    flex-direction: column;
    text-align: center;
  }
}

/* ...and at 440px and below the three tiles stack one per row, each going
   back to the icon beside its name and line */
@media (max-width: 440px) {
  body.d2 .sigrow {
    grid-template-columns: minmax(0, 1fr);
  }

  body.d2 .sighero {
    flex-direction: row;
    text-align: left;
  }
}
