/* ==========================================================================
   Gravijet — statistics
   Design language: Minecraft's own GUI.

   Grounded in a specific screen, not "gaming vibes": the multiplayer server
   list and the Statistics screen. That is exactly what this site is — a long
   list of entries and numbers over the pause-menu dirt — so the game already
   solved it, and the rules are lifted rather than invented:

     - Dirt background, tiled and darkened, as the pause menu draws it.
     - Content sits in ENTRIES: near-black translucent rows with a hard 2px
       black edge and a faint top-inner highlight. Hover outlines them white,
       the way the game highlights the selected server. Nothing floats.
     - Real widgets (buttons, the status window) use the GUI grammar: raised
       bevels — light top-left, dark bottom-right, 2px, never blurred. Item
       slots are the inverse (dark top-left) and hold only icons, never text,
       because that is the one thing the game never does.
     - The palette is the game's 16 chat colours (§0-§f) plus the GUI greys.
       Rank colours already arrive in that palette, so the site and the server
       speak the same one. Gold marks the leader; green wins, red loses.
     - Text carries Minecraft's hard drop shadow at 25% of its own colour —
       the single most recognisable thing about the game's typography. Light
       text on dark ground only; the dark labels inside a grey panel have none.
     - The type scale is quantised to the font's pixel grid (8/16/24/32/48).
       A pixel font at 17px is mush: the game scales in whole pixels, so do we.
     - image-rendering: pixelated. Nothing is ever smoothed.
     - The chrome belongs to the picture too: the pointer and the scrollbar are
       drawn on the same 2px grid, so nothing on screen is stock browser.

   Sound follows the same principle one floor down — see sound.js. The game runs
   its whole interface on one click and repitches it; so do we.
   ========================================================================== */

:root {
  /* --- GUI greys, from the inventory window --- */
  --panel: #c6c6c6;
  --panel-hi: #ffffff;
  --panel-lo: #555555;
  --slot: #8b8b8b;
  --slot-hi: #ffffff;
  --slot-lo: #373737;
  --outline: #000000;

  /* --- the button, and the three text colours the game gives it --- */
  --btn: #6c6c6c;
  --btn-hover: #7b7b88;  /* the hovered texture: lighter, faintly blue */
  --btn-off: #4c4c4c;
  --btn-fg: #ffffff;
  --btn-fg-hi: #ffffa0;  /* Button.getFGColor() while hovered — NOT chat yellow */
  --btn-fg-off: #a0a0a0; /* ...and while disabled */

  --label: #404040;      /* the colour of the word "Inventory" */
  --label-dim: #6b6b6b;

  /* --- the entry surface: the server-list row ---
     Opaque enough that the dirt stops competing with the text. At 0.62 every
     letter had a noisy brown texture showing through it, which is most of why
     the page read as hard to follow. */
  --entry: rgba(0, 0, 0, 0.84);
  --entry-hi: rgba(0, 0, 0, 0.93);

  /* --- the 16 chat colours (§0-§f) --- */
  --mc-dark-blue: #0000aa;
  --mc-dark-green: #00aa00;
  --mc-dark-aqua: #00aaaa;
  --mc-dark-red: #aa0000;
  --mc-dark-purple: #aa00aa;
  --mc-gold: #ffaa00;
  --mc-gray: #aaaaaa;
  --mc-dark-gray: #555555;
  --mc-blue: #5555ff;
  --mc-green: #55ff55;
  --mc-aqua: #55ffff;
  --mc-red: #ff5555;
  --mc-light-purple: #ff55ff;
  --mc-yellow: #ffff55;
  --mc-white: #ffffff;

  /* semantic — chosen from that palette, not invented */
  --win: var(--mc-green);
  --loss: var(--mc-red);

  --font: 'Monocraft', ui-monospace, monospace;
  /* A Minecraft screen is a narrow window centred on the dirt, never a
     full-width dashboard. At 1180 the rows were mostly empty middle — a match
     read "BedFight ... 600px of nothing ... LOSS" — which is what "poured in"
     looks like. Pulling the column in costs no content and gives the dirt back
     its job of framing the window. */
  --container: 1000px;
  --gut: 16px;
}

/* ------------------------------------------------------------------ reset */
* { box-sizing: border-box; margin: 0; padding: 0; border-radius: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  color: var(--mc-white);
  background: #0d0a07 url('../img/dirt.png') repeat;
  background-size: 64px 64px;
  image-rendering: pixelated;
  overflow-x: hidden;
  /* Sticky footer: the column is the page, and #app absorbs the slack, so a
     short page (a 404, an empty ladder) still puts the footer on the floor
     instead of halfway up the screen. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
#app { flex: 1 0 auto; }
.footer { flex-shrink: 0; }
img { display: block; max-width: 100%; image-rendering: pixelated; }
a { color: inherit; text-decoration: none; }
button { font: inherit; border: none; background: none; color: inherit; }
::selection { background: var(--mc-green); color: #000; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 2px solid var(--mc-white); outline-offset: 2px; }

/* ----------------------------------------------------------------- cursors */
/* The crosshair, everywhere — it's what you're looking at while you play, so
   it's what the mouse is here. Nothing changes on hover: the game doesn't swap
   pointers to tell you a thing is interactive, it lights the thing up, and this
   design already does that (white outline on buttons, white edge on rows).
   Minecraft draws the crosshair by inverting what's behind it, which CSS can't
   do to a cursor; the black outline does that job the only way the web allows.
   The keyword after each url() is the fallback if a browser rejects the image. */
/* Links and buttons are named explicitly rather than left to inherit from body:
   the browser's own sheet says a:link { cursor: pointer }, and an inherited
   value loses to any rule that actually matches — including the browser's. */
body, a[href], button, summary, [role='button'] { cursor: url('../img/crosshair.png') 16 16, crosshair; }
/* The one exception. A crosshair is for aiming at a thing; typing means aiming
   between two letters, and that's what an I-beam is for. */
input, textarea { cursor: url('../img/cursor-text.png') 16 16, text; }

/* -------------------------------------------------------------- scrollbar */
/* The game does have this widget, so it's lifted rather than invented: a black
   trough and a raised grey thumb — a #8b8b8b body lit #c6c6c6 from the top-left.
   Same grammar as .raised, so it belongs to the buttons. Chromium needs the
   pseudo-elements; Firefox only takes the two colours and draws its own shape,
   which is why the bevel is stated twice and can't be shared. */
html { scrollbar-color: #8b8b8b #000; }  /* inherits to every inner scroller */
::-webkit-scrollbar { width: 14px; height: 14px; }
::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: #000; }
::-webkit-scrollbar-thumb {
  background: #8b8b8b;
  box-shadow: inset 2px 2px 0 #c6c6c6, inset -2px -2px 0 #555555;
}
::-webkit-scrollbar-thumb:hover { background: #9b9b9b; }

/* ------------------------------------------------- the three core widgets */
/* ENTRY — the server-list row. Where all text and data live. */
.entry {
  background: var(--entry);
  border: 2px solid var(--outline);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.09);
}
a.entry:hover, .entry.hoverable:hover { background: var(--entry-hi); border-color: var(--mc-white); }

/* RAISED — real GUI widgets: buttons, the status window. */
.raised {
  background: var(--panel);
  border: 2px solid var(--outline);
  box-shadow: inset 2px 2px 0 var(--panel-hi), inset -2px -2px 0 var(--panel-lo);
  color: var(--label);
}
/* SUNKEN — an item slot. Icons only, never text. */
.slot {
  background: var(--slot);
  box-shadow: inset 2px 2px 0 var(--slot-lo), inset -2px -2px 0 var(--slot-hi);
}

/* --------------------------------------------------------------- typography */
/* Quantised to the pixel grid. */
h1 { font-size: 48px; line-height: 1; }
h2 { font-size: 32px; line-height: 1; }
h3 { font-size: 24px; line-height: 1; }
h4 { font-size: 16px; line-height: 1; }
h1, h2, h3, h4 { font-weight: 700; }

/* Minecraft's drop shadow: 25% of the text's own colour, offset one pixel.
   One class per colour it's used with — the game computes this per glyph. */
.sh      { text-shadow: 2px 2px 0 #3f3f3f; }
.sh-gray { text-shadow: 2px 2px 0 #2a2a2a; }
.sh-gold { text-shadow: 2px 2px 0 #3f2a00; }
.sh-yel  { text-shadow: 2px 2px 0 #3f3f15; }
.sh-grn  { text-shadow: 2px 2px 0 #153f15; }
.sh-red  { text-shadow: 2px 2px 0 #3f1515; }
.sh-pur  { text-shadow: 2px 2px 0 #3f153f; }

/* ------------------------------------------------------------------ layout */
.container { width: 100%; max-width: var(--container); margin: 0 auto; padding: 0 var(--gut); }

/* SEPARATION — by tone, not by a line.
   Air alone didn't work: with nothing marking where one block ended and the next
   began, the gaps read as empty rather than as structure. But a drawn rule was
   worse — a lit 2px band spanning the whole page shouted louder than anything it
   divided, and a bright grey bar belongs to no part of this game. Minecraft
   separates a screen by darkening the region between its header and footer; you
   read the boundary off the change in ground, not off a stripe. So the sections
   alternate, and the edge of each band is the separator. */
.section { padding: 34px 0; }
.section:nth-child(even) { background: rgba(0, 0, 0, 0.45); }

/* Where a band can't reach — inside a panel, or between filters and their table
   — a rule stands in. It sits on flat dark ground rather than on dirt, so it
   needs far less light than the page-wide version did to register at all. */
.rule { height: 2px; background: rgba(255, 255, 255, 0.13); }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.section-head h2 { color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.section-head p { color: var(--mc-gray); font-size: 16px; max-width: 44ch; text-shadow: 2px 2px 0 #2a2a2a; }

.eyebrow { display: inline-block; font-size: 16px; color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; margin-bottom: 6px; }

/* --------------------------------------------------------------------- nav */
/* The in-game HUD strip. */
/* Opaque, not 82%: content scrolled underneath and read straight through the
   bar. A sticky header has to be a lid, not a tint. */
.nav { position: sticky; top: 0; z-index: 50; background: #0a0a0a; border-bottom: 2px solid var(--outline); box-shadow: 0 2px 0 rgba(255, 255, 255, 0.1); }
.nav-inner { display: flex; align-items: center; gap: 16px; height: 56px; }
/* Never shrink: the nav is a flex row, and letting the brand give way clips it
   to "GRAVIJE". The search field is the one that should yield. */
.brand { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.brand img { width: 32px; height: 32px; image-rendering: auto; } /* the logo isn't pixel art */
.brand .word { font-size: 16px; font-weight: 700; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.brand .word em { font-style: normal; color: var(--mc-aqua); }
.brand .word { white-space: nowrap; }
.nav-links { display: flex; gap: 4px; margin-left: 8px; }
.nav-links a { font-size: 16px; color: var(--mc-gray); padding: 4px 8px; text-shadow: 2px 2px 0 #2a2a2a; }
.nav-links a:hover { color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; }
.nav-links a.active { color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
/* the game's yellow selector arrow */
.nav-links a.active::before { content: '> '; color: var(--mc-yellow); }
.nav-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }

.live-pill { display: inline-flex; align-items: center; gap: 8px; font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.live-dot { width: 8px; height: 8px; background: var(--mc-dark-gray); }
.live-pill.on { color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.live-pill.on .live-dot { background: var(--mc-green); }

/* search — a sunken text field */
.search { position: relative; }
.search-input {
  width: 200px; height: 32px; padding: 0 8px;
  background: #000; border: 2px solid var(--slot-lo);
  color: var(--mc-white); font: 400 16px var(--font); text-shadow: 2px 2px 0 #3f3f3f;
}
.search-input::placeholder { color: #6b6b6b; text-shadow: none; }
.search-input:focus { outline: none; border-color: var(--mc-white); }
.search-results {
  position: absolute; top: 36px; right: 0; width: 300px; z-index: 60;
  background: rgba(0, 0, 0, 0.94); border: 2px solid var(--mc-white); padding: 2px;
}
.search-results:empty { display: none; }
.search-row { display: flex; align-items: center; gap: 8px; padding: 5px 6px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.search-row:hover, .search-row.sel { background: var(--mc-dark-blue); }
.search-row img { width: 24px; height: 24px; }
.search-row .nm { font-size: 16px; }
.search-row .rk { font-size: 16px; margin-left: auto; }

/* ----------------------------------------------------------------- buttons */
/* The game's button as the texture actually is: a 2px black outline, a light
   top-left bevel, a body that darkens toward the bottom, a dark bottom-right.
   What makes it unmistakably Minecraft's is the pair of states the game spends
   its own code on — Button.getFGColor() returns 0xffffa0 while hovered and
   0xa0a0a0 while disabled — together with the white outline its hovered texture
   carries. Chat yellow (#ffff55) is a different colour doing a different job;
   it marks selection here, and only hover gets #ffffa0. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 16px; padding: 9px 16px; min-height: 40px;
  border: 2px solid var(--outline);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.10) 0%, rgba(255, 255, 255, 0) 48%, rgba(0, 0, 0, 0.16) 100%) var(--btn);
  box-shadow: inset 2px 2px 0 rgba(255, 255, 255, 0.42), inset -2px -2px 0 rgba(0, 0, 0, 0.45);
  color: var(--btn-fg); text-shadow: 2px 2px 0 #3f3f3f;
}
.btn:hover {
  background-color: var(--btn-hover);
  border-color: var(--mc-white);
  color: var(--btn-fg-hi); text-shadow: 2px 2px 0 #3f3f28;
}
/* Pressed: the bevel flips, so the lit edge moves to the bottom-right and the
   body's gradient turns over with it. */
.btn:active {
  box-shadow: inset -2px -2px 0 rgba(255, 255, 255, 0.30), inset 2px 2px 0 rgba(0, 0, 0, 0.50);
  background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.16) 0%, rgba(255, 255, 255, 0) 52%, rgba(255, 255, 255, 0.08) 100%);
}
.btn-primary { background-color: #3c8527; } /* the green "Play" button */
.btn-primary:hover { background-color: #4a9c30; }

/* A small square GUI button — currently just the sound toggle. */
/* -------------------------------------------------------------------- hero */
.hero { padding: 32px 0 24px; }
.hero-grid { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: 24px; align-items: start; }
.hero-copy h1 { color: var(--mc-white); text-shadow: 3px 3px 0 #3f3f3f; }
.hero-copy h1 .acid { color: var(--mc-yellow); text-shadow: 3px 3px 0 #3f3f15; display: block; }
.hero-copy .lede { color: var(--mc-gray); font-size: 16px; max-width: 44ch; margin: 16px 0 22px; text-shadow: 2px 2px 0 #2a2a2a; }
.hero-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.hero-tags { display: flex; gap: 16px; margin-top: 20px; flex-wrap: wrap; }
.chip { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.chip b { color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; font-weight: 400; }

/* The status window — a real GUI panel, the one place the grey shows. */
.readout { padding: 8px; }
.readout-bar { padding: 2px 4px 8px; font-size: 16px; color: var(--label); font-weight: 700; display: flex; justify-content: space-between; gap: 8px; }
.readout-body { }
/* Inside the grey panel the game uses dark text with NO shadow — the shadow is
   for light text on the world behind it. White-on-#c6c6c6 would be the exact
   mistake this design language exists to avoid. */
.readout-big { display: flex; align-items: center; gap: 12px; padding: 14px 12px; }
.readout-big .v { font-size: 48px; font-weight: 700; color: #000; line-height: 1; }
.readout-big .u { font-size: 16px; color: var(--label); line-height: 1.25; }
.readout-rows { margin-top: 8px; }
.rr-row { display: flex; justify-content: space-between; gap: 12px; padding: 6px 4px; font-size: 16px; color: var(--label); }
.rr-row + .rr-row { border-top: 2px solid rgba(0, 0, 0, 0.13); }
.rr-row .v { font-weight: 700; color: #000; }

/* ------------------------------------------------------------ telemetry row */
.telemetry { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.telemetry .cell { padding: 14px 12px; }
.telemetry .cell .v { font-size: 24px; font-weight: 700; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.telemetry .cell .l { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; margin-top: 4px; }

/* --------------------------------------------------------- mode list */
/* The multiplayer server list, which is what this actually is: pick a mode and
   go. Icon, name and blurb left; the numbers right, on their own column so they
   line up down the list instead of drifting with the text beside them. */
.mode-list { display: flex; flex-direction: column; gap: 4px; }
.mode-row { display: flex; align-items: center; gap: 16px; padding: 14px 16px; }
.mode-row .glyph { width: 32px; height: 32px; color: var(--mc-aqua); flex-shrink: 0; }
.mode-row .glyph svg { width: 32px; height: 32px; }
.mode-row .m-id { flex: 1; min-width: 0; }
.mode-row .m-id h3 { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.mode-row .tag { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; margin-top: 3px; }
.mode-row .m-foot { display: flex; gap: 12px; flex-shrink: 0; }
.mode-row .m-foot > div { min-width: 116px; text-align: right; }
.mode-row .m-foot .mv { font-size: 16px; font-weight: 700; color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; }
.mode-row .m-foot .ml { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; margin-top: 3px; }
.mode-row .m-badge { width: 44px; text-align: right; flex-shrink: 0; }
.mode-row.enter:hover h3 { color: var(--btn-fg-hi); text-shadow: 2px 2px 0 #3f3f28; }
.mode-row.soon { opacity: 0.6; }

.badge { display: inline-block; font-size: 16px; color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; }
.badge.soon { color: var(--mc-light-purple); text-shadow: 2px 2px 0 #3f153f; }
.badge.live { color: var(--mc-green); text-shadow: 2px 2px 0 #153f15; }

/* ------------------------------------------------------------- leaderboard */
/* Two ranks of control, and they have to look it. The mode tabs are the raised
   button: they change which ladder you're on. The metric chips below only
   re-sort the one you're already on, so they're a flat inline toggle. Both were
   .btn before — two rows of identical grey widgets stacked on each other, with
   nothing saying they did different jobs. */
/* The controls sit on their own darker ground, the way the game floors its
   header and footer apart from the content between them. That band is what
   separates the filters from the table — the table needs no rule above it once
   the thing above it is visibly a different surface. */
.lb-controls {
  background: rgba(0, 0, 0, 0.45);
  border: 2px solid rgba(0, 0, 0, 0.6);
  padding: 12px;
  margin-bottom: 18px;
}
.lb-tabs { display: flex; gap: 6px; flex-wrap: wrap; }
#lb-filters:not(:empty) { margin-top: 12px; padding-top: 12px; border-top: 2px solid rgba(255, 255, 255, 0.1); }

/* Ranking-by and filtering-to are two different questions, so each gets its own
   labelled row. As one flat run of identical chips they read as a single
   choice, and picking a kit looked like picking a stat. */
.lb-row { display: flex; align-items: flex-start; gap: 12px; }
.lb-row + .lb-row { margin-top: 10px; padding-top: 10px; border-top: 2px solid rgba(255, 255, 255, 0.08); }
.lb-row-label {
  flex: 0 0 auto;
  width: 40px;
  padding-top: 6px;
  font-size: 16px;
  color: var(--mc-dark-gray);
  text-shadow: 2px 2px 0 #1a1a1a;
}
.lb-row .chips { display: flex; flex-wrap: wrap; gap: 6px; flex: 1; min-width: 0; }
.lb-tab { font-size: 16px; padding: 9px 16px; min-height: 40px; display: inline-flex; align-items: center; }
/* Disabled the way the game disables: not faded out, but a flatter, darker
   widget with grey text. Opacity would have dimmed the outline too, and the
   outline is what tells you it's still a button. */
.btn:disabled, .lb-tab.dead, .lb-tab.dead:hover {
  background-color: var(--btn-off);
  background-image: none;
  border-color: var(--outline);
  box-shadow: inset 2px 2px 0 rgba(255, 255, 255, 0.14), inset -2px -2px 0 rgba(0, 0, 0, 0.3);
  color: var(--btn-fg-off); text-shadow: 2px 2px 0 #282828;
}
/* selected = pressed in, like a toggled game option */
.lb-tab.active {
  background: var(--mc-dark-gray);
  box-shadow: inset -2px -2px 0 rgba(255, 255, 255, 0.28), inset 2px 2px 0 rgba(0, 0, 0, 0.5);
  color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15;
}
/* The secondary rank: sunken, not raised. Deliberately quieter than a button. */
.metric-chip {
  font-size: 16px; padding: 5px 10px; min-height: 30px;
  display: inline-flex; align-items: center;
  background: rgba(0, 0, 0, 0.55); border: 2px solid rgba(255, 255, 255, 0.11);
  color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a;
}
.metric-chip:hover { border-color: var(--mc-white); color: var(--btn-fg-hi); text-shadow: 2px 2px 0 #3f3f28; }
.metric-chip.active {
  background: var(--mc-dark-gray); border-color: var(--mc-white);
  color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15;
}

/* The board: a list of server-list entries. */
.board { display: flex; flex-direction: column; gap: 4px; }
.board-head, .board-row { display: grid; align-items: center; gap: 8px; }
.board-head { padding: 0 10px 4px; }
.board-head span { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.board-head span.r { text-align: right; }
.board-row { padding: 6px 10px; min-height: 48px; }
.rank-badge { font-size: 16px; font-weight: 700; color: var(--mc-gray); text-align: center; text-shadow: 2px 2px 0 #2a2a2a; }
/* only the leader is called out — in the game's gold */
.board-row.top1 .rank-badge { color: var(--mc-gold); text-shadow: 2px 2px 0 #3f2a00; }

.player-cell { display: flex; align-items: center; gap: 8px; min-width: 0; }
.player-cell img { width: 32px; height: 32px; flex-shrink: 0; }
.player-cell .pc-name { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.player-cell .pc-rank { font-size: 16px; text-shadow: 2px 2px 0 #2a2a2a; }
.board-row .val { text-align: right; font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.board-row .val.primary { font-weight: 700; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.online-dot { width: 8px; height: 8px; background: var(--mc-green); flex-shrink: 0; display: inline-block; }

/* ------------------------------------------------------------- player page */
.crumb { display: inline-flex; align-items: center; gap: 6px; font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; margin: 14px 0; }
.crumb::before { content: '< '; color: var(--mc-yellow); }
.crumb:hover { color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; }

.profile-hero { display: grid; grid-template-columns: 168px 1fr; gap: 8px; }
/* the skin sits in an item slot — the one place a slot is correct: it holds a picture */
.skin-wrap { display: grid; place-items: center; padding: 12px; }
.skin-wrap img { height: 196px; width: auto; }
.p-id { padding: 16px 18px; display: flex; flex-direction: column; justify-content: center; }
.p-id .p-rank { font-size: 16px; text-shadow: 2px 2px 0 #2a2a2a; }
.p-id h1 { font-size: 32px; color: var(--mc-white); text-shadow: 3px 3px 0 #3f3f3f; margin: 6px 0 10px; }
.p-status { display: inline-flex; align-items: center; gap: 8px; font-size: 16px; width: fit-content; }
.p-status.online { color: var(--mc-green); text-shadow: 2px 2px 0 #153f15; }
.p-status.offline { color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.p-meta { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 14px; }
.p-meta .pm .k { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.p-meta .pm .v { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }

.p-summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 8px; }
.sum-tile { padding: 14px 16px; }
.sum-tile .v { font-size: 32px; font-weight: 700; color: var(--mc-white); text-shadow: 3px 3px 0 #3f3f3f; }
.sum-tile .l { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; margin-top: 4px; }

/* Each mode is a container window with a title bar. */
.panel { margin-top: 12px; }
.panel-head { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-bottom: 2px solid var(--outline); background: rgba(0, 0, 0, 0.4); }
.panel-head .glyph { width: 24px; height: 24px; color: var(--mc-aqua); }
.panel-head .glyph svg { width: 24px; height: 24px; }
.panel-head h3 { font-size: 24px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.panel-head .ph-sub { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; margin-top: 3px; }
.panel-head .ph-right { margin-left: auto; }
.panel-body { padding: 16px 18px 18px; }

/* The Statistics screen is a LIST — label left, value right, one per line — and
   tiles were the mistake. A fixed grid of backgrounded tiles can't help leaving
   holes: Practice's 9 values orphaned a tenth cell, FFA's 4 left a gap, and an empty
   cell with a background on it reads as a bug rather than as nothing. A list has
   no cells to leave empty. Three columns keeps each label near its own number;
   at two the value drifts half a panel away from what it's naming. */
.stat-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 28px; }
.stat {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 7px 2px; border-bottom: 2px solid rgba(255, 255, 255, 0.07);
}
.stat .l { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
/* Hierarchy here is weight and colour, not size — the game's own list does the
   same, and a pixel font can't be scaled between 16 and 24 without going mushy. */
.stat .v { font-size: 16px; font-weight: 700; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; white-space: nowrap; }
.stat .v.win { color: var(--mc-green); text-shadow: 2px 2px 0 #153f15; }
.stat .v.loss { color: var(--mc-red); text-shadow: 2px 2px 0 #3f1515; }

/* Each block inside a panel gets the separator too — Win/loss, Kits and Recent
   matches are different things and ran straight into one another. */
.block { padding: 18px 0 0; margin-top: 18px; border-top: 2px solid rgba(255, 255, 255, 0.13); }
.block-label { font-size: 16px; color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; margin-bottom: 10px; }

/* The win/loss bar is the XP bar: a sunken trough with a hard fill. */
.wl-bar { height: 16px; display: flex; background: var(--mc-dark-red); box-shadow: inset 2px 2px 0 rgba(0,0,0,0.55); }
.wl-bar .w { background: var(--mc-green); }
.wl-legend { display: flex; justify-content: space-between; font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; margin-top: 6px; }

/* kits */
/* 220, not 172: at 172 the cards came out 188px wide and "FireballFight" plus
   its elo needs 181px of the 168px inside one, so the longest kit names ellipsed
   away — and a truncated kit name is not a kit name. */
.kit-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 4px; }
.kit-card { padding: 10px; background: rgba(0, 0, 0, 0.32); }
.kit-card:hover { background: rgba(0, 0, 0, 0.5); }
.kit-card .kc-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
/* "FireballFight" plus its elo overran the card and collided with the next one
   ("FireballFight 1.00NoDebuff"). A flex item won't shrink below its content
   unless it's told it may. */
.kit-card .kc-name { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kit-card .kc-elo { font-size: 16px; color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; flex-shrink: 0; }
.kit-card .kc-stats { display: flex; gap: 10px; margin-top: 8px; }
.kit-card .kc-stats .ks { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.kit-card .kc-stats .ks b { color: var(--mc-white); font-weight: 400; text-shadow: 2px 2px 0 #3f3f3f; }
.kit-card .kc-bar { height: 8px; margin-top: 9px; background: var(--mc-dark-red); box-shadow: inset 1px 1px 0 rgba(0,0,0,0.55); }
.kit-card .kc-bar .w { height: 100%; background: var(--mc-green); }

/* matches — chat-log rows */
.matches { display: flex; flex-direction: column; gap: 3px; }
.match { display: grid; grid-template-columns: 8px 1fr auto; align-items: center; gap: 10px; padding: 8px; background: rgba(0, 0, 0, 0.32); }
.match .res { width: 8px; height: 30px; }
.match .res.w { background: var(--mc-green); }
.match .res.l { background: var(--mc-red); }
.match .m-info .mk { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.match .m-info .mo { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.match .m-info .mo a { color: var(--mc-aqua); }
.match .m-info .mo a:hover { color: var(--mc-yellow); }
.match .m-right { text-align: right; }
.match .m-right .rr { font-size: 16px; }
.match .m-right .rr.w { color: var(--mc-green); text-shadow: 2px 2px 0 #153f15; }
.match .m-right .rr.l { color: var(--mc-red); text-shadow: 2px 2px 0 #3f1515; }
.match .m-right .rd { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }

/* empty / soon */
.empty { padding: 18px 8px; color: var(--mc-gray); font-size: 16px; text-shadow: 2px 2px 0 #2a2a2a; }
.soon-state { display: flex; align-items: center; gap: 12px; padding: 16px 12px; }
.soon-state .glyph { width: 32px; height: 32px; color: var(--mc-light-purple); flex-shrink: 0; }
.soon-state .glyph svg { width: 32px; height: 32px; }
.soon-state h4 { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.soon-state p { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; margin-top: 4px; }

/* directory */
.dir-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(236px, 1fr)); gap: 6px; }
.dir-card { display: flex; align-items: center; gap: 10px; padding: 8px 10px; }
.dir-card img { width: 32px; height: 32px; flex-shrink: 0; }
.dir-card .dn { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; display: flex; align-items: center; gap: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dir-card .dr { font-size: 16px; text-shadow: 2px 2px 0 #2a2a2a; }
.dir-card .dt { margin-left: auto; text-align: right; font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; line-height: 1.3; flex-shrink: 0; }

/* ------------------------------------------------------------------ footer */
.footer { margin-top: 28px; padding: 22px 0 36px; background: rgba(0, 0, 0, 0.62); border-top: 2px solid var(--outline); }
.footer-inner { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; align-items: center; }
.footer .f-copy { color: var(--mc-gray); font-size: 16px; text-shadow: 2px 2px 0 #2a2a2a; margin-top: 6px; }
.footer .f-links { display: flex; gap: 16px; }
.footer .f-links a { color: var(--mc-gray); font-size: 16px; text-shadow: 2px 2px 0 #2a2a2a; }
.footer .f-links a:hover { color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; }

/* ------------------------------------------------------------------- rules */
.rule-text {
  color: var(--mc-gray); font-size: 16px; line-height: 1.55;
  text-shadow: 2px 2px 0 #2a2a2a; max-width: 68ch; margin: 0;
}
/* A ladder reads like the Statistics screen: which offence, what happens, how
   long. Colour carries the severity — a ban is not a mute in a different font. */
.ladder { display: flex; flex-direction: column; }
.rung {
  display: grid; grid-template-columns: 46px 1fr auto; gap: 12px; align-items: baseline;
  padding: 7px 2px; border-bottom: 2px solid rgba(255, 255, 255, 0.07);
}
.rung:last-child { border-bottom: 0; }
.rung .rn { color: var(--mc-dark-gray); text-shadow: 2px 2px 0 #1a1a1a; }
.rung .rt { color: var(--mc-gold); text-shadow: 2px 2px 0 #3f2a00; }
.rung .rt.hard { color: var(--mc-red); text-shadow: 2px 2px 0 #3f1515; }
.rung .rd { color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
/* A rung with no duration configured is not a punishment. Grey it rather than
   let it sit in the same white as the ones that do something. */
.rung .rd.unset { color: var(--mc-dark-gray); text-shadow: 2px 2px 0 #1a1a1a; }

/* ------------------------------------------------------------------- forms */
.container.narrow { max-width: 760px; }
.frow { margin-bottom: 14px; }
.flabel {
  display: block; margin-bottom: 5px; font-size: 16px; line-height: 1.4;
  color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a;
}
/* A sunken slot, like every text field in the game. */
.fld {
  width: 100%; font: inherit; font-size: 16px; padding: 8px 10px;
  background: rgba(0, 0, 0, 0.75); color: var(--mc-white);
  border: 2px solid rgba(0, 0, 0, 0.6);
  box-shadow: inset 2px 2px 0 rgba(0, 0, 0, 0.5), inset -2px -2px 0 rgba(255, 255, 255, 0.12);
  text-shadow: 2px 2px 0 #3f3f3f;
}
.fld:focus { outline: none; border-color: var(--mc-white); }
textarea.fld { resize: vertical; min-height: 84px; line-height: 1.5; }
select.fld { cursor: url('../img/crosshair.png') 16 16, crosshair; }
.factions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.fmsg { font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }
.fmsg.bad { color: var(--mc-red); text-shadow: 2px 2px 0 #3f1515; }
.fmsg.ok { color: var(--mc-green); text-shadow: 2px 2px 0 #153f15; }

/* Question and answer, as read by whoever has to judge it. */
.qa-row { padding: 8px 0; border-bottom: 2px solid rgba(255, 255, 255, 0.07); }
.qa-row:last-child { border-bottom: 0; }
.qa-q { font-size: 16px; color: var(--mc-dark-gray); text-shadow: 2px 2px 0 #1a1a1a; margin-bottom: 3px; }
.qa-a { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; line-height: 1.5; white-space: pre-wrap; }

/* ------------------------------------------------------------------- who */
.who { display: flex; align-items: center; gap: 7px; }
.who img { image-rendering: auto; border-radius: 50%; flex-shrink: 0; }
.who .wn { font-size: 16px; color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; max-width: 12ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who .wd { font-size: 16px; color: var(--mc-yellow); text-shadow: 2px 2px 0 #3f3f15; }
.who .wo {
  font: inherit; font-size: 16px; line-height: 1; padding: 2px 6px;
  background: none; border: 0; color: var(--mc-dark-gray); text-shadow: 2px 2px 0 #1a1a1a;
}
.who .wo:hover { color: var(--mc-red); text-shadow: 2px 2px 0 #3f1515; }
.who-btn { font-size: 16px; padding: 6px 12px; min-height: 32px; display: inline-flex; align-items: center; }
.dcount {
  margin-left: 7px; padding: 0 5px; font-size: 16px;
  background: var(--mc-dark-red); color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f;
}

/* -------------------------------------------------------------------- misc */
/* The loader is the game's loading bar: a sunken trough filling green. */
.loader { padding: 44px 0; }
.loader .ring { height: 16px; background: var(--slot); box-shadow: inset 2px 2px 0 var(--slot-lo), inset -2px -2px 0 var(--slot-hi); position: relative; overflow: hidden; }
.loader .ring::after { content: ''; position: absolute; top: 3px; bottom: 3px; left: 3px; background: var(--mc-green); animation: load 1.2s steps(10) infinite; }
@keyframes load { from { width: 0; } to { width: calc(100% - 6px); } }
.loader::after { content: 'Loading...'; display: block; margin-top: 8px; font-size: 16px; color: var(--mc-gray); text-shadow: 2px 2px 0 #2a2a2a; }

.notice { padding: 56px 0; }
.notice h2 { color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; margin-bottom: 8px; }
.notice p { color: var(--mc-gray); font-size: 16px; text-shadow: 2px 2px 0 #2a2a2a; max-width: 52ch; }
.notice a { color: var(--mc-aqua); }
.notice a:hover { color: var(--mc-yellow); }

/* -------------------------------------------------------------- responsive */
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
  .telemetry { grid-template-columns: repeat(2, 1fr); }
  .p-summary { grid-template-columns: repeat(2, 1fr); }
  .stat-row { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  /* The links used to be hidden here, which left a phone with no navigation at
     all — survivable with three of them and the footer as a backstop, not with
     six. They drop to their own row instead and scroll sideways if they must. */
  .nav-inner { flex-wrap: wrap; row-gap: 8px; }
  .nav-links {
    order: 3; width: 100%; margin-left: 0; gap: 2px;
    overflow-x: auto; scrollbar-width: none;
  }
  .nav-links::-webkit-scrollbar { display: none; }
  .nav-links a { padding: 4px 6px; white-space: nowrap; }
  /* brand + live count + search will not fit on one line at 390px; the count
     is already on the home page, so the nav keeps brand and search. */
  .live-pill { display: none; }
  /* "GRAVIJET STATS" wraps at this width; the suffix is the part to lose. */
  .brand .word em { display: none; }
  .nav-inner { gap: 10px; }
  .search-input { width: 100%; min-width: 0; }
  .search { flex: 1; min-width: 0; }
  .telemetry, .p-summary { grid-template-columns: 1fr; }
  /* The row can't hold four things side by side this narrow: the numbers go
     under the name, and the blurb goes — the name and its stats are the point. */
  .mode-row { flex-wrap: wrap; gap: 8px 12px; }
  .mode-row .m-id { flex: 1 0 60%; }
  .mode-row .tag { display: none; }
  .mode-row .m-foot > div { min-width: 0; text-align: left; }
  .mode-row .m-foot { gap: 20px; flex: 1 0 100%; }
  .stat-row { grid-template-columns: 1fr; }
  /* Four numbers and a name don't fit in 390px — the name lost, and came out as
     "Tops...", which is the one column nobody can afford to lose. So below this
     width a board keeps the rank, the player, and the single metric it's sorted
     by; the rest are a tap away on the profile. Each board sets its own columns
     inline (they differ per mode), hence the !important — there's no stylesheet
     selector that can outrank a style attribute. */
  .board-row, .board-head { grid-template-columns: 32px 1fr 72px !important; }
  .board-row .val.sec { display: none; }
  .board-head span.r ~ span.r { display: none; }
  .profile-hero { grid-template-columns: 1fr; }
  .hero-copy h1, .notice h2 { font-size: 32px; }
}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }

/* --------------------------------------------------------------- link code */
.linkcode { margin-top: 14px; }
/* The one thing on the page that has to survive being read off a screen and
   retyped into a chat box, so it gets the size to match. */
.lc-code {
  font-size: 32px; letter-spacing: 4px; color: var(--mc-yellow);
  text-shadow: 3px 3px 0 #3f3f15; text-align: center; padding: 14px 0;
  background: rgba(0, 0, 0, 0.55); border: 2px solid rgba(255, 255, 255, 0.11);
  user-select: all;
}
.lc-steps { margin: 12px 0 0; padding-left: 22px; color: var(--mc-gray); font-size: 16px; line-height: 1.7; text-shadow: 2px 2px 0 #2a2a2a; }
.lc-steps b { color: var(--mc-white); text-shadow: 2px 2px 0 #3f3f3f; }
.lc-ttl { margin-top: 8px; font-size: 16px; color: var(--mc-dark-gray); text-shadow: 2px 2px 0 #1a1a1a; }
