/* Void League.
 *
 * Black and white, with purple as the only colour on the page. Every neutral
 * is pulled a few degrees toward violet so the greys read as chosen rather
 * than inherited, and the accent never has to fight a competing hue.
 *
 * CONTRAST IS A RULE HERE, not an afterthought. The previous version dimmed
 * locked achievements with opacity, which composited an already quiet grey
 * toward the panel behind it and landed at 1.78:1. That is invisible, and for
 * a new player it was most of the page. So: nothing on this site conveys state
 * through opacity. State is carried by glow, and every text colour below is
 * measured against the surface it sits on.
 *
 *   white   on ink   19.99:1      muted  on ink    7.22:1
 *   text    on ink   14.68:1      faint  on ink    5.18:1
 *   violet  on ink    5.05:1      hot    on ink    7.43:1
 *
 * violet-deep is 2.81:1 and is therefore a border and a glow, never text.
 */
:root{
  --black:#000000; --ink:#08080C; --ink-2:#101018; --ink-3:#16161F;
  --edge:#1E1E2A; --edge-hot:#33234D;
  --white:#FFFFFF; --text:#DCDCE6; --muted:#9A9AAB; --faint:#808095;
  --violet:#A855F7; --hot:#C77DFF; --deep:#6D28D9;

  /* One glow, three strengths. Rarity, focus and hover all speak this. */
  --glow-1:0 0 10px rgba(168,85,247,.20);
  --glow-2:0 0 20px rgba(168,85,247,.34);
  --glow-3:0 0 34px rgba(168,85,247,.50), 0 0 70px rgba(168,85,247,.18);
  --text-glow:0 0 18px rgba(199,125,255,.55);

  --rail:238px; --max:1280px;
  --f-display:"Chakra Petch",ui-sans-serif,system-ui,sans-serif;
  --f-body:"IBM Plex Sans",ui-sans-serif,system-ui,sans-serif;
  --f-mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,monospace;
  color-scheme:dark;
}
*{box-sizing:border-box}
body{
  margin:0; background:var(--black); color:var(--text);
  font-family:var(--f-body); font-size:15px; line-height:1.62;
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{font-family:var(--f-display);color:var(--white);margin:0;
  text-wrap:balance;letter-spacing:.005em}
h1{font-size:clamp(30px,4.4vw,44px);font-weight:700;line-height:1.06}
h2{font-size:21px;font-weight:600}
h3{font-size:16px;font-weight:600}
a{color:var(--hot);text-decoration:none}
a:hover{color:var(--white);text-shadow:var(--text-glow)}
:focus-visible{outline:2px solid var(--violet);outline-offset:3px;border-radius:2px;
  box-shadow:var(--glow-2)}

/* The spike is the one structural mark. It appears at every level of the page
   so the shape, not a colour swatch, is what people recognise. */
.spike{width:0;height:0;flex:none;
  border-left:5px solid transparent;border-right:5px solid transparent;
  border-bottom:13px solid var(--violet);
  filter:drop-shadow(0 0 6px rgba(168,85,247,.6))}
.spike.sm{border-left-width:3px;border-right-width:3px;border-bottom-width:8px}
.spike.gold{border-bottom-color:var(--hot)}

/* ---- shell -------------------------------------------------------------- */
.shell{display:flex;min-height:100vh}
.rail{
  width:var(--rail);flex:none;background:var(--ink);
  border-right:1px solid var(--edge);
  padding:22px 0 32px;position:sticky;top:0;height:100vh;overflow-y:auto;
}
.brand{display:flex;align-items:center;gap:11px;padding:0 20px 20px;
  border-bottom:1px solid var(--edge);margin-bottom:14px}
.brand-mark{width:34px;height:34px;flex:none;position:relative}
.brand-mark::before{content:"";position:absolute;inset:3px;border-radius:50%;
  border:2px solid var(--violet);background:#000;box-shadow:var(--glow-2)}
.brand-mark::after{content:"";position:absolute;left:50%;top:-1px;transform:translateX(-50%);
  width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;
  border-bottom:36px solid var(--hot);filter:drop-shadow(0 0 8px rgba(199,125,255,.8))}
.brand-name{font-family:var(--f-display);font-weight:700;color:var(--white);
  font-size:15px;line-height:1.15;letter-spacing:.02em}
.brand-sub{font-family:var(--f-mono);font-size:10px;color:var(--faint);
  letter-spacing:.14em;text-transform:uppercase}
.navgroup{padding:14px 20px 6px;font-family:var(--f-mono);font-size:10px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--faint)}
.nav a{display:flex;align-items:center;gap:10px;padding:9px 20px;color:var(--muted);
  font-size:14px;border-left:2px solid transparent;transition:color .14s,background .14s}
.nav a:hover{background:var(--ink-2);color:var(--white);text-shadow:none}
.nav a.on{background:linear-gradient(90deg,rgba(168,85,247,.14),transparent 70%);
  color:var(--white);border-left-color:var(--violet);box-shadow:inset 2px 0 12px rgba(168,85,247,.25)}
.nav a .spike{opacity:0;transition:opacity .14s}
.nav a.on .spike{opacity:1}

.main{flex:1;min-width:0}

/* The live count is the only thing on the page that changes by itself, so it
   is the only thing that lights up without being touched. */
.live{margin:0 20px 14px;padding:12px 14px;border:1px solid var(--edge);
  border-radius:3px;background:var(--ink-2)}
.live .n{font-family:var(--f-display);font-size:20px;font-weight:700;color:var(--muted);
  font-variant-numeric:tabular-nums;line-height:1}
.live .l{font-family:var(--f-mono);font-size:10px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--faint);margin-top:5px}
.live.on{border-color:var(--edge-hot);box-shadow:var(--glow-1)}
.live.on .n{color:var(--hot);text-shadow:var(--text-glow)}

/* ---- shared furniture --------------------------------------------------- */
.eyebrow{font-family:var(--f-mono);font-size:11px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--violet)}
.lede{font-size:16px;max-width:64ch;color:var(--muted);text-wrap:pretty}
.hint{font-size:13.5px;color:var(--faint);max-width:66ch;margin:10px 0 0}
.sechead{display:flex;align-items:center;gap:11px;margin:40px 0 14px}
.sechead .rule{flex:1;height:1px;
  background:linear-gradient(90deg,var(--edge-hot),var(--edge) 40%,transparent)}
.panel{background:var(--ink);border:1px solid var(--edge);border-radius:3px}
.pad{padding:18px 20px}
.grid{display:grid;gap:14px}
.mono{font-family:var(--f-mono);font-variant-numeric:tabular-nums}
.dim{color:var(--faint)}
.caption{font-size:12.5px;color:var(--faint);margin-top:8px;max-width:70ch;text-wrap:pretty}
.note{border-left:2px solid var(--violet);padding:2px 0 2px 15px;color:var(--muted);
  font-size:14px;box-shadow:-8px 0 22px -14px rgba(168,85,247,.9)}

.tag{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;font-family:var(--f-mono);
  font-size:11px;letter-spacing:.07em;text-transform:uppercase;padding:4px 9px;
  border-radius:2px;border:1px solid var(--edge);color:var(--muted);background:var(--ink-2)}
a.tag:hover{color:var(--white);border-color:var(--violet);box-shadow:var(--glow-1);
  text-shadow:none}
.tag.violet{color:var(--hot);border-color:var(--edge-hot);box-shadow:var(--glow-1)}
.tag.gold{color:var(--white);border-color:var(--violet)}
.tag.live{color:var(--hot);border-color:var(--edge-hot);box-shadow:var(--glow-1)}
.tag.off{color:var(--faint)}
/* Removed from the league. The result stands as a record, the person is struck
 * through. Line-through alone would carry no meaning to a screen reader and no
 * meaning without colour, so the link also carries a title and the player page
 * shows a Removed tag next to the name. */
.tag.red{color:#FCA5A5;border-color:#7F1D1D}
a.banned,h1.banned{text-decoration:line-through;text-decoration-thickness:2px;
  text-decoration-color:#EF4444;opacity:.62}
a.banned:hover{opacity:.85}
.dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none;
  box-shadow:0 0 8px currentColor}

/* ---- hero --------------------------------------------------------------- */
/* The banner is a background, not an <img>: it repeats nothing the copy does
 * not already say, so it stays out of the accessibility tree entirely.
 *
 * Two layouts, and deliberately nothing between them. Above 1300px the panel
 * is wide enough to hold the art behind the copy: the artwork anchors right,
 * the text column is capped so it stops before the frozen tee, and a scrim
 * carries the ground from solid ink on the left to nothing by the time it
 * reaches the ice. Narrower than that there is not room for both, so the art
 * drops to a full width strip along the bottom, sized by its own aspect ratio
 * so nothing is ever cropped out of it, and the copy gets its width back.
 * There is no viewport at which a line of text sits on a tee's face.
 *
 * Both layers are pseudo elements, so the markup did not have to change.
 * isolation keeps their negative z-index inside this panel rather than
 * punching through to the band behind it. */
.hero{position:relative;overflow:hidden;border:1px solid var(--edge);border-radius:4px;
  background:radial-gradient(760px 420px at 8% -14%,rgba(168,85,247,.17),transparent 68%),
    var(--ink);
  padding:34px 32px calc(26px + 36.8%);margin-top:16px;isolation:isolate}
/* 36.8% is 624/1696: the artwork's own proportion, so the strip is exactly as
   tall as the image wants to be and the padding above reserves exactly that.
   The gradient on top is a short wash over the first sixth, which is all it
   takes to stop the strip's top edge reading as a pasted rectangle. Any
   deeper and it would swallow the hammer. */
.hero::before{content:"";position:absolute;left:0;right:0;bottom:0;z-index:-2;
  aspect-ratio:1696/624;
  background:
    linear-gradient(180deg,rgba(8,8,12,.9) 0,rgba(8,8,12,0) 16%) center/100% 100% no-repeat,
    url("/hero.webp") center/100% 100% no-repeat}
.hero > *{position:relative}
.hero h1{margin-bottom:14px}
.hero .lede{margin:0;color:var(--text)}
.herofoot{display:flex;gap:10px;flex-wrap:wrap;margin-top:22px;align-items:center}

@media (min-width:1240px){
  /* Wide enough: the art goes behind the copy. The scrim is a few degrees off
     horizontal so the fade does not read as a ruled line, and it holds full
     ink well past where the paragraph ends before it lets the laser through. */
  .hero{padding:40px 36px 36px;min-height:clamp(400px,30vw,470px);
    display:flex;flex-direction:column;justify-content:center}
  .hero::after{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
    background:
      radial-gradient(700px 470px at 4% 0,rgba(168,85,247,.18),transparent 70%),
      linear-gradient(96deg,var(--ink) 0,var(--ink) 30%,rgba(8,8,12,.96) 40%,
        rgba(8,8,12,.80) 48%,rgba(8,8,12,.42) 58%,rgba(8,8,12,.10) 68%,
        rgba(8,8,12,0) 77%)}
  /* The top third of the artwork is empty sky, so the headline is allowed to
     run wider than the paragraph sitting under it. */
  .hero h1{max-width:min(27ch,64%)}
  .hero .lede{max-width:min(56ch,46%)}
}

/* ---- stat figures ------------------------------------------------------- */
.figs{display:grid;grid-template-columns:repeat(auto-fit,minmax(138px,1fr));gap:1px;
  background:var(--edge);border:1px solid var(--edge);border-radius:3px;overflow:hidden}
.fig{background:var(--ink);padding:15px 17px;transition:background .14s}
.fig:hover{background:var(--ink-2)}
.fig .n{font-family:var(--f-display);font-size:27px;font-weight:700;color:var(--white);
  line-height:1;font-variant-numeric:tabular-nums}
.fig .n small{font-size:13px;color:var(--faint);font-weight:500}
.fig .l{font-family:var(--f-mono);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint);margin-top:8px}

/* ---- steps -------------------------------------------------------------- */
.steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(248px,1fr));gap:1px;
  background:var(--edge);border:1px solid var(--edge);border-radius:3px;overflow:hidden}
.step{background:var(--ink);padding:19px 21px 21px;position:relative;transition:background .14s}
.step:hover{background:var(--ink-2)}
.step .no{font-family:var(--f-mono);font-size:11px;letter-spacing:.18em;color:var(--violet);
  text-shadow:var(--text-glow)}
.step h3{margin:9px 0 7px}
.step p{margin:0;font-size:13.5px;color:var(--muted)}
.step code{font-family:var(--f-mono);font-size:12.5px;background:var(--ink-3);
  border:1px solid var(--edge-hot);border-radius:2px;padding:1px 6px;color:var(--hot)}
.step .addr{display:block;margin-top:11px;font-family:var(--f-mono);font-size:13px;
  color:var(--hot);word-break:break-all;text-shadow:var(--text-glow)}

/* ---- pipeline ----------------------------------------------------------- */
.flow{display:flex;align-items:stretch;overflow-x:auto;padding:2px}
.flow .box{background:var(--ink);border:1px solid var(--edge);border-radius:3px;
  padding:13px 15px;min-width:158px;flex:1}
.flow .box .k{font-family:var(--f-mono);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--violet)}
.flow .box .v{font-family:var(--f-display);font-weight:600;color:var(--white);
  font-size:14px;margin-top:5px}
.flow .box .d{font-size:12.5px;color:var(--faint);margin-top:5px;line-height:1.45}
.flow .arrow{flex:none;width:28px;display:grid;place-items:center;color:var(--violet);
  font-family:var(--f-mono);font-size:16px;text-shadow:var(--text-glow)}

/* ---- tables ------------------------------------------------------------- */
/* Whether a column has room is a question about the box, not the screen. The
   same homepage table is 603px wide in one column at 1680 and 292px wide at
   900, where the two-up layout starts but the columns are still narrower than
   the table. So the drop is a container query, and the viewport rule further
   down stays only as the fallback for browsers without them: there it degrades
   to the old sideways scroll, which is what it did before anyway. */
.tablewrap{overflow-x:auto;border:1px solid var(--edge);border-radius:3px;background:var(--ink);
  container-type:inline-size}
@container (max-width:520px){
  .opt{display:none}
  table.trim{min-width:0}
}
table{border-collapse:collapse;width:100%;min-width:540px}
th{font-family:var(--f-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--faint);text-align:right;padding:12px 15px;font-weight:500;
  border-bottom:1px solid var(--edge);background:var(--ink-2)}
th:first-child,td:first-child{text-align:left}
td{padding:11px 15px;border-bottom:1px solid var(--edge);font-variant-numeric:tabular-nums;
  font-family:var(--f-mono);font-size:13px;text-align:right;color:var(--text)}
tr:last-child td{border-bottom:0}
/* A hover state that lights the row from its left edge, so the eye keeps the
   line it is reading across a wide table. */
tbody tr{transition:background .12s}
tbody tr:hover{background:linear-gradient(90deg,rgba(168,85,247,.13),transparent 60%)}
tbody tr:hover td:first-child{box-shadow:inset 2px 0 0 var(--violet)}
td.name{font-family:var(--f-body);font-size:14px;color:var(--white)}
td.name a{color:var(--white)}
td.name a:hover{color:var(--hot);text-shadow:var(--text-glow)}
.rank{color:var(--faint);width:36px}
.rank.top{color:var(--hot);text-shadow:var(--text-glow)}
.won{color:var(--hot);text-shadow:var(--text-glow)}

/* ---- tiers -------------------------------------------------------------- */
.tier{display:inline-flex;align-items:center;gap:7px;font-family:var(--f-mono);
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;white-space:nowrap;
  color:var(--muted)}
.tier .pips{display:flex;gap:2px}
.pip{width:4px;height:11px;background:var(--edge);border-radius:1px}
.pip.on{background:var(--violet);box-shadow:0 0 6px rgba(168,85,247,.9)}
.tierlist{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));gap:1px;
  background:var(--edge);border:1px solid var(--edge);border-radius:3px;overflow:hidden}
.tiercell{background:var(--ink);padding:13px 15px;transition:background .14s}
.tiercell:hover{background:var(--ink-2)}
.tiercell .t{font-family:var(--f-display);font-weight:600;color:var(--white);font-size:14px}
.tiercell .r{font-family:var(--f-mono);font-size:11px;color:var(--faint);margin-top:4px}

/* ---- match cards -------------------------------------------------------- */
.match{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:16px;
  padding:17px 20px;border-bottom:1px solid var(--edge)}
.match:last-child{border-bottom:0}
a.match:hover{background:var(--ink-2);text-shadow:none}
a.match:hover .score{text-shadow:var(--text-glow)}
.side{min-width:0}
.side.r{text-align:right}
.side .who{font-family:var(--f-display);font-weight:600;color:var(--white);font-size:15px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.side .who.won{color:var(--hot)}
.score{font-family:var(--f-display);font-size:25px;font-weight:700;color:var(--white);
  font-variant-numeric:tabular-nums;white-space:nowrap;transition:text-shadow .14s}
.score .l{color:var(--faint)}

/* ---- search ------------------------------------------------------------- */
.search{display:flex;gap:10px;max-width:470px}
.search input{flex:1;background:var(--ink);border:1px solid var(--edge);border-radius:3px;
  color:var(--white);font-family:var(--f-body);font-size:15px;padding:11px 14px;
  transition:border-color .14s,box-shadow .14s}
.search input::placeholder{color:var(--faint)}
.search input:focus{outline:none;border-color:var(--violet);box-shadow:var(--glow-2)}
.hits{margin-top:12px;display:grid;gap:1px;background:var(--edge);border:1px solid var(--edge);
  border-radius:3px;overflow:hidden;max-width:470px}
.hit{background:var(--ink);padding:10px 14px;display:flex;justify-content:space-between;
  gap:12px;color:var(--text);font-size:14px}
a.hit:hover{background:var(--ink-2);color:var(--white);text-shadow:none;
  box-shadow:inset 2px 0 0 var(--violet)}
.hit .when{font-family:var(--f-mono);font-size:11px;color:var(--faint)}

/* ---- profile ------------------------------------------------------------ */
.phead{display:flex;align-items:flex-end;gap:14px;flex-wrap:wrap;margin:10px 0 6px}
.movelist{display:grid;gap:1px;background:var(--edge)}
.move{background:var(--ink);display:flex;justify-content:space-between;gap:12px;
  padding:9px 16px;font-family:var(--f-mono);font-size:12.5px;color:var(--muted)}
.move .up{color:var(--hot);text-shadow:var(--text-glow)}
.move .down{color:var(--faint)}

/* ---- achievements -------------------------------------------------------
   The whole point of this block. Rarity is carried by how much a card glows,
   never by how transparent it is: dimming with opacity is what put the last
   version at 1.78:1. A locked card keeps full strength text and simply does
   not light up. */
/* Three across, stated rather than inferred. auto-fill decided the count from
   whatever space was left over, which meant the card size moved every time
   anything else on the page changed width. */
.badges{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
.badges[hidden]{display:none}
@media (max-width:1040px){.badges{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:700px){.badges{grid-template-columns:minmax(0,1fr)}}

/* Rarity was carried entirely by border colour and glow, which tells you there
   is a difference without telling you what it is. The tier now says its own
   name. Colour still does the fast read; the word settles it. */
.bhead{display:flex;align-items:baseline;gap:10px;justify-content:space-between}
.brar{font-family:var(--f-mono);font-size:9px;letter-spacing:.15em;text-transform:uppercase;
  white-space:nowrap;padding:2px 6px;border:1px solid currentColor;border-radius:2px;
  color:var(--faint);opacity:.75;flex:none}
.badge.r2 .brar{color:var(--violet)}
.badge.r3 .brar{color:var(--hot)}
.badge.r4 .brar{color:#FBBF24}
.badge.got .brar{opacity:1}
.badge.locked .brar{opacity:.42}

/* Nine categories stacked meant scrolling past eight to reach the ninth. */
.achtabs{display:flex;flex-wrap:wrap;gap:6px;margin:18px 0 16px}
.achtab{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint);background:var(--ink);border:1px solid var(--edge);border-radius:3px;
  padding:7px 11px;cursor:pointer;display:inline-flex;align-items:center;gap:8px;
  transition:color .14s,border-color .14s,background .14s}
.achtab:hover{color:var(--text);border-color:var(--edge-hot)}
.achtab .n{font-size:10px;color:var(--faint);opacity:.8}
.achtab[aria-selected="true"]{color:var(--white);border-color:var(--violet);
  background:var(--ink-2);box-shadow:var(--glow-1)}
.achtab[aria-selected="true"] .n{color:var(--hot);opacity:1}
.achtab[aria-selected="true"] .n.done{color:#FBBF24}
.achtab:focus-visible{outline:2px solid var(--hot);outline-offset:2px}
.badge{background:var(--ink);border:1px solid var(--edge);border-radius:3px;padding:18px;
  display:flex;gap:16px;align-items:flex-start;transition:border-color .16s,box-shadow .16s}
.bico{width:56px;height:56px;flex:none;border-radius:3px;display:grid;place-items:center;
  background:var(--ink-2);border:1px solid var(--edge)}
/* The drawn spike was sized for a 36px box and looks lost in a 56px one. The
   sixty odd achievements still waiting on art use it, so it scales with the box
   rather than sitting in the middle of it. */
.bico .spike{border-bottom-color:var(--edge);filter:none;transform:scale(1.5)}
.bbody{min-width:0;flex:1}
.bname{font-family:var(--f-display);font-weight:600;font-size:15px;color:var(--muted)}
.bdesc{font-size:12.5px;color:var(--faint);line-height:1.46;margin-top:3px}
.bprog{font-family:var(--f-mono);font-size:11px;color:var(--faint);margin-top:6px}
.bar{height:3px;background:var(--edge);border-radius:2px;margin-top:10px;overflow:hidden}
.bar i{display:block;height:100%;background:var(--deep)}

/* Earned. Four steps of the same colour, from a quiet outline to something
   that lights the card it sits in. */
.badge.got{background:var(--ink-2)}
.badge.got .bname{color:var(--white)}
.badge.got .bdesc{color:var(--muted)}
.badge.got .bico{border-color:var(--deep);background:#150A22}
.badge.got .bico .spike{border-bottom-color:var(--violet);
  filter:drop-shadow(0 0 5px rgba(168,85,247,.8))}

/* r1 is the common tier and earns no glow on purpose: if the quietest one
   lit up, none of the others would mean anything. Stated rather than left
   implicit, so nobody later reads its absence as an oversight. */
.badge.r1.got{border-color:var(--edge)}
.badge.r2.got{border-color:var(--edge-hot);box-shadow:var(--glow-1)}
.badge.r3.got{border-color:var(--deep);box-shadow:var(--glow-2)}
.badge.r3.got .bico{border-color:var(--violet)}
.badge.r4.got{border-color:var(--violet);box-shadow:var(--glow-3);background:#0E0817}
.badge.r4.got .bname{color:var(--hot);text-shadow:var(--text-glow)}
.badge.r4.got .bico{border-color:var(--hot);background:#1B0D2B}
.badge.r4.got .bico .spike{border-bottom-color:var(--hot);
  filter:drop-shadow(0 0 8px rgba(199,125,255,1))}

/* Locked, but still legible. A legendary that is out of reach should still
   look worth reaching for, so it keeps a hint of the border it would earn. */
.badge.locked .bar i{background:var(--deep)}
/* Same depth inversion the drawn spikes use: locked recedes, earned comes
   forward. Greyed and dim rather than hidden, so you can still see what you
   are working towards. */
.bico .bimg{width:48px;height:48px;display:block;object-fit:contain}
.badge.locked .bimg{opacity:.3;filter:grayscale(1) contrast(.8)}
.badge.got .bimg{opacity:1;filter:drop-shadow(0 0 5px rgba(168,85,247,.40))}
.badge.r4.got .bimg{filter:drop-shadow(0 0 7px rgba(217,70,239,.55))}
@media (prefers-reduced-motion:reduce){.bimg{transition:none}}
.badge.r4.locked{border-color:var(--edge-hot)}
.badge.r4.locked .bar i{background:var(--violet)}
.badge:hover{border-color:var(--violet)}

.achgroup{font-family:var(--f-mono);font-size:10px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--violet);margin:24px 0 10px}

/* ---- reference page ----------------------------------------------------- */
.qa{border-bottom:1px solid var(--edge);padding:17px 0}
.qa:last-child{border-bottom:0}
.qa h3{margin-bottom:7px}
.qa p{margin:0 0 8px;max-width:72ch;font-size:14.5px;color:var(--muted)}
.qa p:last-child{margin-bottom:0}
.qa .num{font-family:var(--f-mono);color:var(--hot);font-variant-numeric:tabular-nums}

/* ---- prizes ------------------------------------------------------------- */
.prize{display:grid;grid-template-columns:58px minmax(0,1fr);gap:16px;padding:19px 20px;
  border-bottom:1px solid var(--edge)}
.prize:last-child{border-bottom:0}
.place{font-family:var(--f-display);font-size:25px;font-weight:700;color:var(--faint);
  text-align:center;font-variant-numeric:tabular-nums}
.prize.first .place{color:var(--hot);text-shadow:var(--text-glow)}
.prize.second .place{color:var(--violet)}
.prize.third .place{color:var(--deep)}
.prize ul{margin:8px 0 0;padding-left:18px;font-size:13.5px;color:var(--muted)}
.prize li{margin:4px 0}

/* ---- misc --------------------------------------------------------------- */
.empty{border:1px dashed var(--edge-hot);border-radius:3px;padding:36px 28px;
  text-align:center;background:var(--ink)}
.empty h3{margin-bottom:9px}
.empty p{max-width:52ch;margin:0 auto;color:var(--muted);font-size:14px}
.empty .todo{margin-top:16px;font-family:var(--f-mono);font-size:12px;color:var(--hot);
  text-shadow:var(--text-glow)}
.cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:14px}
.srv{display:flex;align-items:center;justify-content:space-between;gap:14px;
  flex-wrap:wrap;padding:13px 18px;border-bottom:1px solid var(--edge)}
.srv > *{min-width:0}
.srv:last-child{border-bottom:0}
.srv .addr,.srv .srvnote{font-family:var(--f-mono);font-size:12px;color:var(--faint)}
.srv .addr{white-space:nowrap}
/* Wide enough for one line: run them together with a separator. Narrower: they
   are two block elements already, so they stack and no separator is left
   dangling at the end of a line. */
@media (min-width:601px){
  .srv .addr,.srv .srvnote{display:inline}
  /* Every character escaped, spaces included. A CSS hex escape always eats
     one following whitespace as its terminator, whether it is written with
     four digits or six, so a literal space after it never survives. */
  .srv .addr + .srvnote::before{content:"\0020\0000b7\0020"}
}
.view{display:none}
.view.on{display:block;animation:in .24s ease-out}
.loading{color:var(--faint);font-family:var(--f-mono);font-size:12px;letter-spacing:.14em;
  text-transform:uppercase;padding:24px 0}
.err{border:1px solid #4A1D2E;background:#170A10;color:#FFA8C8;border-radius:3px;
  padding:14px 18px;font-size:14px}
@keyframes in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .view.on{animation:none}
  *{transition:none !important}
}

/* ---- layout utilities ---------------------------------------------------
   So no element needs a style attribute and the policy can forbid inline CSS. */
.h1gap{margin:10px 0 12px}
.mt14{margin-top:14px}
.mt16{margin-top:16px}
.mt18{margin-top:18px}
.mt22{margin-top:22px}
.mb14{margin-bottom:14px}
.p18{padding:18px}
.cardgrid{grid-template-columns:repeat(auto-fit,minmax(250px,1fr))}


/* ---- season -------------------------------------------------------------
   The page is about a calendar and never showed one. These are the pieces
   that make where we are, what is next, and what it costs visible at a
   glance instead of described in prose. */
.countdown{position:relative;overflow:hidden;border:1px solid var(--edge-hot);
  border-radius:4px;background:var(--ink);padding:26px 28px;margin-top:18px;
  display:grid;grid-template-columns:auto 1fr;gap:30px;align-items:center;
  box-shadow:var(--glow-1)}
.countdown::before{content:"";position:absolute;right:-16%;top:-120%;width:52%;height:340%;
  background:radial-gradient(closest-side,rgba(168,85,247,.22),transparent 70%);
  pointer-events:none}
.countdown > *{position:relative}
.cd-num{font-family:var(--f-display);font-size:clamp(46px,7vw,68px);font-weight:700;
  color:var(--white);line-height:.92;font-variant-numeric:tabular-nums;
  text-shadow:0 0 30px rgba(199,125,255,.6)}
.cd-unit{font-family:var(--f-mono);font-size:11px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--hot);margin-top:9px}
.cd-body h3{margin-bottom:6px}
.cd-body p{margin:0;color:var(--muted);font-size:14px;max-width:56ch}
.cd-track{height:5px;background:var(--ink-3);border-radius:3px;margin-top:16px;overflow:hidden;
  border:1px solid var(--edge)}
.cd-track i{display:block;height:100%;background:linear-gradient(90deg,var(--deep),var(--hot));
  box-shadow:0 0 14px rgba(199,125,255,.8)}
.cd-scale{display:flex;justify-content:space-between;font-family:var(--f-mono);font-size:10.5px;
  color:var(--faint);margin-top:7px;letter-spacing:.06em}

.seasongrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(178px,1fr));gap:1px;
  background:var(--edge);border:1px solid var(--edge);border-radius:3px;overflow:hidden}
.sq{background:var(--ink);padding:16px 17px 17px;position:relative}
.sq .q{font-family:var(--f-mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--faint)}
.sq .nm{font-family:var(--f-display);font-weight:700;color:var(--muted);font-size:17px;
  margin-top:7px;line-height:1.1}
.sq .dt{font-family:var(--f-mono);font-size:11.5px;color:var(--faint);margin-top:5px}
/* The quarter the clock is actually in. Only one of these can ever be lit. */
.sq.now{background:var(--ink-2);box-shadow:inset 0 0 34px rgba(168,85,247,.14)}
.sq.now::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;
  background:var(--hot);box-shadow:0 0 12px rgba(199,125,255,.9)}
.sq.now .q{color:var(--hot)}
.sq.now .nm{color:var(--white)}
.sq.next .nm{color:var(--text)}

/* Kept or reset, said in a column rather than a paragraph. */
td.keep{color:var(--hot);text-shadow:var(--text-glow)}
td.gone{color:var(--faint)}

/* This one is prose, not numbers. The shared right alignment that keeps the
   ladder's digits in a column makes every sentence here start at a different x. */
#season-reset th,#season-reset td{text-align:left}
#season-reset td.name{width:31%}
#season-reset th:nth-child(2),#season-reset td:nth-child(2){width:120px}
#season-reset td:last-child{font-family:var(--f-body);font-size:13.5px;color:var(--muted)}
/* The ladder needs a 540px floor so its numbers stay in columns. This one is
   sentences, and the sentence column is the whole point, so let it wrap on a
   phone instead of hiding behind a sideways scroll. */
#season-reset table{min-width:0}
@media (max-width:640px){
  #season-reset td.name{width:auto}
  #season-reset th:nth-child(2),#season-reset td:nth-child(2){width:66px}
  #season-reset td,#season-reset th{padding-left:11px;padding-right:11px}
  #season-reset td:last-child{font-size:12.5px}
}

.podium{display:grid;grid-template-columns:repeat(auto-fit,minmax(258px,1fr));gap:14px;
  align-items:start}
.pod{background:var(--ink);border:1px solid var(--edge);border-radius:3px;padding:20px;
  transition:border-color .16s,box-shadow .16s}
.pod:hover{border-color:var(--violet)}
.pod-rank{display:flex;align-items:center;gap:8px;font-family:var(--f-mono);font-size:10px;
  letter-spacing:.18em;text-transform:uppercase;color:var(--faint);margin-bottom:11px}
.pod h3{margin-bottom:12px}
/* No list bullets. A browser default disc on a black page looks like nobody
   looked at it, which is what was there before. */
.pod-list{display:grid;gap:9px}
.pod-list span{display:block;position:relative;padding-left:16px;font-size:13.5px;
  color:var(--muted);line-height:1.5}
.pod-list span::before{content:"";position:absolute;left:0;top:.55em;width:5px;height:5px;
  background:var(--deep);transform:rotate(45deg)}
.pod.first{border-color:var(--violet);box-shadow:var(--glow-2);background:#0E0817}
.pod.first .pod-rank{color:var(--hot)}
.pod.first .pod-rank .spike{border-bottom-color:var(--hot)}
.pod.first .pod-list span::before{background:var(--hot);box-shadow:0 0 7px rgba(199,125,255,.9)}
.pod.second{border-color:var(--edge-hot);box-shadow:var(--glow-1)}
.pod.second .pod-list span::before{background:var(--violet)}
.pod .spike{border-left-width:3px;border-right-width:3px;border-bottom-width:9px;
  border-bottom-color:var(--deep);filter:none}
.pod.second .spike{border-bottom-color:var(--violet)}

@media (max-width:700px){
  .countdown{grid-template-columns:minmax(0,1fr);gap:18px;padding:22px}
}
@media (max-width:560px){
  /* Three labels under a 300px bar run into each other, and the middle one is
     the only one the bar itself already says. */
  .cd-scale span:nth-child(2){display:none}
}

/* ==========================================================================
   BANDS
   --------------------------------------------------------------------------
   The page is black and white in the literal sense: a view is a stack of full
   bleed bands and the ground alternates as you scroll. The rail stays black
   throughout, which is what makes the change register as a band rather than as
   a page that could not decide.

   Nothing below restyles a component for the white ground. Every colour in
   this stylesheet already goes through a token, so a light band simply
   redefines the tokens and the whole component set follows. The only things
   that need naming twice are the literal rgba values that assume a black
   ground, and those are in the block after this one.

   Contrast on the white ground, measured against the band itself (#F6F5FA),
   which is the worse of the two surfaces:

     white  18.1:1      muted  7.52:1      violet  5.25:1
     text   13.8:1      faint  5.15:1      hot     6.55:1

   deep is 2.12:1 here, exactly as it is 2.81:1 on black: a border and a fill,
   never text. The rule holds on both grounds.
   ========================================================================== */
.band{background:var(--black)}
.bandin{max-width:var(--max);margin:0 auto;padding:38px 30px 46px}
.view > .band:first-child .bandin{padding-top:34px}
.view > .band:last-child .bandin{padding-bottom:92px}
/* A band already opens with space, so the first heading in one must not add
   its own or the seam gets a dead strip above it. */
.bandin > .sechead:first-child,.bandin > .eyebrow:first-child{margin-top:0}/* The second ground.
 *
 * The bands used to alternate black and white. The white half was legible and
 * carefully built, but it kept breaking the room the rest of the site is in,
 * and every component needed a light twin to survive it: ninety lines of
 * overrides for one of the two states. The alternation now runs between two
 * darks instead. Far enough apart that the seam reads, close enough that
 * nothing needs restyling for it, so this block is tokens and a border and
 * that is all it will ever need to be. */
/* The one link that leaves. Discord's own mark, in their blurple, because
   their brand rules allow blurple, black or white and nothing else. Not
   tinting it to match the rail is the point: it should not look like one of
   ours, because it is not one of ours. The spike icons here are 0 wide plus a
   border, so the mark is given the same footprint by hand. */
.nav a.ext{color:var(--hot)}
.nav a.ext .dico{width:16px;height:12px;flex:none;display:block;opacity:.92}
.nav a.ext:hover .dico{opacity:1}
.nav a.ext::after{content:"\2197";margin-left:auto;opacity:.55;font-size:11px}

.band.alt{
  --black:#0C0A14; --ink:#12101C; --ink-2:#191627; --ink-3:#201C31;
  --edge:#272238; --edge-hot:#3D2C5C;
  background:var(--black);
  border-block:1px solid var(--edge-hot);
}


/* ==========================================================================
   MOBILE MENU
   --------------------------------------------------------------------------
   Under 880px the rail used to flatten into two rows of wrapped links above
   the content, which cost a third of the first screen and got worse with every
   page added. It is now a drawer: a sticky bar with the wordmark, the live
   count and a toggle, and the same rail slides in over a scrim.
   ========================================================================== */
.topbar{display:none}
.tb-name{font-family:var(--f-display);font-weight:700;font-size:15px;color:var(--white);
  letter-spacing:.02em}
.tb-live{margin-left:auto;display:flex;align-items:baseline;gap:6px;
  font-family:var(--f-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--faint)}
.tb-live .n{font-family:var(--f-display);font-size:16px;font-weight:700;color:var(--muted);
  font-variant-numeric:tabular-nums}
.tb-live.on .n{color:var(--hot);text-shadow:var(--text-glow)}
.burger{width:42px;height:36px;flex:none;display:grid;align-content:center;gap:5px;
  justify-items:center;padding:0;cursor:pointer;border-radius:3px;
  background:var(--ink-2);border:1px solid var(--edge);
  transition:border-color .16s,box-shadow .16s}
.burger span{display:block;width:19px;height:2px;border-radius:2px;background:var(--text);
  transition:transform .2s ease,opacity .15s ease}
.burger[aria-expanded="true"]{border-color:var(--violet);box-shadow:var(--glow-2)}
.burger[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){opacity:0}
.burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
.scrim{position:fixed;inset:0;z-index:50;background:rgba(0,0,0,.64);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}

@media (max-width:880px){
  .topbar{display:flex;align-items:center;gap:10px;position:sticky;top:0;z-index:40;
    padding:10px 14px;background:var(--ink);border-bottom:1px solid var(--edge)}
  .shell{flex-direction:column}
  .rail{position:fixed;top:0;left:0;bottom:0;width:min(300px,86vw);height:100dvh;z-index:60;
    transform:translateX(-100%);transition:transform .22s ease;
    border-right:1px solid var(--edge-hot);padding-bottom:24px}
  .rail.open{transform:none;box-shadow:14px 0 44px rgba(0,0,0,.72)}
  body.menu-open{overflow:hidden}
  .bandin{padding:24px 18px 34px}
  .view > .band:first-child .bandin{padding-top:22px}
  .view > .band:last-child .bandin{padding-bottom:72px}
  .cols{grid-template-columns:minmax(0,1fr)}
  .hero{padding:26px 22px calc(20px + 36.8%)}
  /* Under 560px the full artwork becomes a 130px ribbon and the tees stop
     reading at all. Same file, cropped by the box instead: the strip is given
     a squarer ratio and the image is sized to its height and anchored right,
     which shows the right two thirds of the picture at nearly twice the scale.
     No second asset, and the one already cached is reused. */
  @media (max-width:560px){
    .hero{padding-bottom:calc(20px + 57.14%)}
    /* Columns marked opt by the table helper. Dropped only from summaries,
       never from a view that is the only place a number appears. Once they are
       gone the table's min-width is the last thing forcing a sideways scroll,
       so a table that had any of them loses its floor too. */
    .opt{display:none}
    table{min-width:430px}
    table.trim{min-width:0}
    th{padding:11px 9px}
    td{padding:10px 9px;font-size:12.5px}
    .match{gap:10px;padding:14px 13px}
    .score{font-size:21px}
    .hero::before{aspect-ratio:7/4;
      background:
        linear-gradient(180deg,rgba(8,8,12,.9) 0,rgba(8,8,12,0) 16%) center/100% 100% no-repeat,
        url("/hero.webp") right center/auto 100% no-repeat}
  }
  br.wide{display:none}
  .match{gap:10px;padding:14px}
}
@media (min-width:881px){
  .scrim{display:none}
}
