/* The game's own font, built from its bitmaps: one pixel of a glyph is an eighth of the size,
   so sizes in multiples of 8 px keep every pixel whole. Bold is the game's: the glyph drawn
   twice, a pixel apart. */
@font-face {
  font-family: 'Minecraft';
  src: url("/media/font/minecraft.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Minecraft';
  src: url("/media/font/minecraft-bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

:root {
  --night: #07110d;
  --night-2: #0c1d16;
  --emerald: #50c878;
  --emerald-deep: #2e8b57;
  --mint: #b6f2cf;
  --white: #f4fbf6;
  --silver: #aeb9c2;
  --muted: #8fa39a;
  --glass: rgba(14, 34, 25, 0.62);
  --line: rgba(80, 200, 120, 0.22);
  --bad: #e07a68;
  --pixel: 'Minecraft', ui-monospace, monospace;
  --sans: 'Inter', system-ui, -apple-system, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, monospace;
  --max: 64rem;
  --radius: 8px; /* every panel and control but feed lines and player cards */
  color-scheme: dark;
}

*, *::before, *::after {
  box-sizing: border-box;
}

[hidden] {
  display: none !important;
}

html {
  background: var(--night);
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--white);
  background:
    radial-gradient(120% 80% at 70% 0%, #123326 0%, transparent 60%),
    linear-gradient(180deg, #0a1a14 0%, var(--night) 55%, #050c09 100%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ── Canvases: stars behind everything, gems above everything ── */
#sky, #gems, #allays {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

#sky {
  z-index: 0;
}

#gems {
  z-index: 20;
}

/* Allays fly behind the page, over the stars: the canvas comes after #sky at the same level,
   and the hero, main, and footer sit above both, so text stays clear and cards show them faintly. */
#allays {
  z-index: 0;
}

/* ── Hero ── */
.hero {
  position: relative;
  z-index: 1;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: 6rem 1rem 5rem;
  overflow: hidden;
}

.moon {
  position: absolute;
  top: clamp(2rem, 9vh, 6rem);
  right: clamp(-4rem, 8vw, 10rem);
  width: clamp(9rem, 22vw, 17rem);
  aspect-ratio: 1;
  border-radius: 50%;
  /* Craters: a dark floor, a light rim on the far side, soft edges. */
  background:
    radial-gradient(circle at 33% 37%, rgba(96, 110, 103, 0.55) 0 6%, rgba(96, 110, 103, 0.25) 8.5%, rgba(255, 255, 255, 0.18) 9.5%, transparent 11%),
    radial-gradient(circle at 63% 29%, rgba(96, 110, 103, 0.45) 0 3.5%, rgba(255, 255, 255, 0.16) 5.5%, transparent 7%),
    radial-gradient(circle at 57% 63%, rgba(96, 110, 103, 0.5) 0 9%, rgba(96, 110, 103, 0.22) 12.5%, rgba(255, 255, 255, 0.16) 14%, transparent 16%),
    radial-gradient(circle at 26% 67%, rgba(96, 110, 103, 0.35) 0 3%, rgba(255, 255, 255, 0.12) 4.5%, transparent 6%),
    radial-gradient(circle at 76% 50%, rgba(96, 110, 103, 0.3) 0 2.5%, transparent 4.5%),
    radial-gradient(ellipse 40% 26% at 46% 50%, rgba(120, 134, 126, 0.28), transparent 70%),
    radial-gradient(circle at 35% 30%, #f1f6f2 0%, #cdd8d1 45%, #8f9c95 100%);
  box-shadow:
    0 0 60px 10px rgba(182, 242, 207, 0.18),
    0 0 180px 40px rgba(80, 200, 120, 0.12),
    inset -18px -14px 40px rgba(7, 17, 13, 0.55);
  animation: moon-float 12s ease-in-out infinite;
  opacity: 0;
  transition: opacity 1s;
}

.sky-static .moon {
  opacity: 1;
}

@keyframes moon-float {
  50% { transform: translateY(-10px); }
}

/* ── Synced sky: the game's sky colors behind the first screen. app.js sets --sky (the
   zenith), --fog (the horizon, where the sun and moon set), and --glow, the sunrise and
   sunset light on the sun's side. Below the horizon the page's own night takes over. ── */
/* Registered as colors, so the once-a-second updates blend over that second, as the game's
   sky turns without steps. */
@property --sky {
  syntax: "<color>";
  inherits: true;
  initial-value: transparent;
}

@property --fog {
  syntax: "<color>";
  inherits: true;
  initial-value: transparent;
}

@property --glow {
  syntax: "<color>";
  inherits: true;
  initial-value: transparent;
}

:root {
  transition: --sky 1s linear, --fog 1s linear, --glow 1s linear;
}

body::before, body::after {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 110svh;
  z-index: 0;
  pointer-events: none;
}

body::before {
  background: linear-gradient(180deg, var(--sky, transparent) 0%, var(--fog, transparent) 55%, transparent 100%);
}

body::after {
  background: radial-gradient(55% 40% at var(--glow-x, 0%) 55%, var(--glow, transparent), transparent 75%);
}

.sky-synced .moon {
  display: none;
}

/* The game's own sprites, scaled up without smoothing, on one shared orbit. */
.sky-body {
  position: absolute;
  top: 0;
  left: 0;
  aspect-ratio: 1;
  pointer-events: none;
  opacity: 0;
  transition: translate 1s linear, opacity 1s linear;
}

.sky-still .sky-body {
  transition: none;
}

.sky-sun {
  width: clamp(8rem, 16vw, 13rem);
}

/* Both sprites are 32 pixels; the game draws the moon at 20 units to the sun's 30. */
.sky-moon {
  width: clamp(5.33rem, 10.67vw, 8.67rem);
}

.sky-sprite {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
}

/* Light around each body; the moon's follows its phase. */
.sky-glow {
  position: absolute;
  inset: -110%;
  border-radius: 50%;
}

.sky-sun .sky-glow {
  inset: -60%;
  background: radial-gradient(closest-side, rgba(255, 236, 160, 0.42), rgba(255, 200, 100, 0.14) 45%, transparent);
}

.sky-moon .sky-glow {
  inset: -17%;
  background: radial-gradient(closest-side, rgba(214, 236, 255, 0.34), rgba(182, 242, 207, 0.1) 45%, transparent);
  opacity: var(--light, 1);
  transition: opacity 2s linear;
}

.world-clock {
  margin: 0.35rem 0 0;
  font-family: var(--pixel);
  font-size: 12px;
  color: var(--mint);
  opacity: 0.75;
}

/* ── Weather: a white flash for lightning ── */
.weather-flash {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.weather-flash {
  background: radial-gradient(120% 90% at 50% 0%, rgba(235, 240, 255, 0.9), rgba(200, 215, 255, 0.35) 60%, transparent);
  opacity: 0;
}

/* ── Sword cursor ── */
.has-sword-cursor,
.has-sword-cursor * {
  cursor: none !important;
}

.sword-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 40;
  pointer-events: none;
  opacity: 0;
  transform-origin: 0 0;
  image-rendering: pixelated;
}

.hero-inner {
  position: relative;
  width: min(100%, 56rem);
  text-align: center;
}

/* Under a bright day sky the hero's small text turns light and gets a shadow, as the game
   shadows its own text. The title keeps its own drop shadow. */
.sky-synced .hero-inner :is(.eyebrow, .lede, .hint, .world-clock, .address-label) {
  color: var(--white);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 0 10px rgba(0, 0, 0, 0.3);
}

.eyebrow {
  margin: 0 0 1rem;
  font-family: var(--pixel);
  font-size: 12px;
  color: var(--mint);
  opacity: 0.85;
}

.title {
  display: inline-block;
  margin: 0;
  padding: 0 0.05em 0.08em;
  font-family: var(--pixel);
  font-weight: 400;
  font-size: 40px;
  line-height: 0.95;
  color: var(--emerald);
  background: linear-gradient(180deg, #d9ffe8 0%, #7be3a2 38%, var(--emerald) 60%, var(--emerald-deep) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  /* A blocky drop shadow, like the Minecraft title. */
  filter:
    drop-shadow(0 0.06em 0 #12402a)
    drop-shadow(0 0 24px rgba(80, 200, 120, 0.35));
}

/* Whole font pixels at every width: 5, 8, 10, then 13 px each. Regular, not bold: doubled
   pixels fill the gaps in M and d at this size. */
@media (min-width: 480px) {
  .title {
    font-size: 64px;
  }
}

@media (min-width: 800px) {
  .title {
    font-size: 80px;
  }
}

@media (min-width: 1100px) {
  .title {
    font-size: 104px;
  }
}

.lede {
  margin: 1.25rem auto 1.75rem;
  max-width: 30rem;
  font-size: clamp(1.02rem, 2.6vw, 1.2rem);
  color: var(--silver);
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 1.5rem;
  padding: 0.4rem 0.9rem;
  font-size: 0.92rem;
  color: var(--white);
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(8px);
}

.status .dot {
  width: 0.55rem;
  height: 0.55rem;
  flex: none;
  border-radius: 50%;
  background: var(--muted);
}

.status[data-state="online"] .dot {
  background: var(--emerald);
  box-shadow: 0 0 0 0 rgba(80, 200, 120, 0.6);
  animation: pulse 2s ease-out infinite;
}

.status[data-state="offline"] .dot {
  background: var(--bad);
}

@keyframes pulse {
  to { box-shadow: 0 0 0 0.6rem rgba(80, 200, 120, 0); }
}

.address {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas: "label action" "code action";
  align-items: center;
  gap: 0.15rem 1rem;
  width: min(100%, 26rem);
  margin: 0 auto;
  padding: 0.85rem 0.9rem 0.85rem 1.2rem;
  font: inherit;
  text-align: left;
  color: inherit;
  background: linear-gradient(180deg, rgba(80, 200, 120, 0.16), rgba(46, 139, 87, 0.1));
  border: 1px solid rgba(80, 200, 120, 0.45);
  border-radius: var(--radius);
  box-shadow: 0 10px 40px -12px rgba(80, 200, 120, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

.address:hover {
  transform: translateY(-2px);
  border-color: var(--emerald);
  box-shadow: 0 16px 50px -12px rgba(80, 200, 120, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.address:focus-visible {
  outline: 3px solid var(--mint);
  outline-offset: 3px;
}

.address-label {
  grid-area: label;
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.address code {
  grid-area: code;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: clamp(0.92rem, 3.6vw, 1.1rem);
  color: var(--white);
}

.address code.address-long {
  font-size: clamp(0.75rem, 3.3vw, 1.1rem);
}

.address-action {
  grid-area: action;
  padding: 0.5rem 0.9rem;
  font-family: var(--pixel);
  font-size: 12px;
  color: #062012;
  background: var(--emerald);
  border-radius: var(--radius);
  box-shadow: inset 0 -3px 0 var(--emerald-deep);
}

.hint {
  margin: 0.9rem 0 0;
  font-size: 0.85rem;
  color: var(--muted);
}

.click-hint {
  position: absolute;
  bottom: 1.5rem;
  left: 50%;
  margin: 0;
  transform: translateX(-50%);
  font-family: var(--pixel);
  font-size: 12px;
  color: var(--mint);
  opacity: 0.55;
  white-space: nowrap;
  animation: hint 3s ease-in-out infinite;
}

@keyframes hint {
  50% { opacity: 0.25; }
}

/* ── Content ── */
main {
  position: relative;
  z-index: 1;
  width: min(100% - 2rem, var(--max));
  margin: 0 auto;
}

.links {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 1rem;
}

.card {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.25rem 1.3rem;
  color: inherit;
  text-decoration: none;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(10px);
  transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}

.card:hover {
  transform: translateY(-3px);
  border-color: rgba(80, 200, 120, 0.6);
  background: rgba(18, 46, 33, 0.75);
}

.card:focus-visible {
  outline: 3px solid var(--mint);
  outline-offset: 3px;
}

.card-icon {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  flex: none;
  font-size: 1.5rem;
  background: rgba(80, 200, 120, 0.14);
  border-radius: var(--radius);
}

.card-icon img {
  width: 32px;
  height: 32px;
  image-rendering: pixelated;
}

.card-body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.card-title {
  font-family: var(--pixel);
  font-size: 16px;
  color: var(--white);
}

.card-text {
  font-size: 0.92rem;
  color: var(--silver);
}

.card-arrow {
  margin-left: auto;
  font-size: 1.3rem;
  color: var(--emerald);
  transition: transform 0.18s ease;
}

.card:hover .card-arrow {
  transform: translateX(4px);
}

.section {
  margin-top: 4.5rem;
}

h2 {
  margin: 0 0 1.25rem;
  font-family: var(--pixel);
  font-size: 24px;
  font-weight: 700;
  color: var(--emerald);
}

/* Each section title links to itself, so a #playing or #fame link lands on it; a # shows on hover. */
.anchor {
  color: inherit;
  text-decoration: none;
}

.anchor::after {
  content: " #";
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.15s;
}

.anchor:is(:hover, :focus-visible)::after {
  opacity: 1;
}

.section,
.playing {
  scroll-margin-top: 1.5rem;
}

.steps {
  display: grid;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.steps li {
  position: relative;
  padding: 1rem 1.2rem 1rem 3.6rem;
  color: var(--silver);
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  counter-increment: step;
}

.steps li::before {
  content: counter(step);
  position: absolute;
  left: 1rem;
  top: 50%;
  display: grid;
  place-items: center;
  width: 1.8rem;
  height: 1.8rem;
  transform: translateY(-50%);
  padding-left: 0.125em;
  font-family: var(--pixel);
  color: #062012;
  background: var(--emerald);
  border-radius: var(--radius);
  box-shadow: inset 0 -3px 0 var(--emerald-deep);
}

.steps b {
  color: var(--white);
  font-weight: 600;
}

.steps code {
  font-family: var(--mono);
  font-size: 0.9em;
  color: var(--mint);
}

.features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: 1rem;
}

.feature {
  padding: 1.2rem 1.3rem;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.feature h3 {
  display: flex;
  align-items: center;
  gap: 4px;
  margin: 0 0 0.35rem;
  font-family: var(--pixel);
  font-size: 16px;
  color: var(--white);
  font-weight: 400;
}

/* An experience orb before each title, in the game's own cycle of colors. */
.feature h3::before {
  content: "";
  display: inline-block;
  width: 32px;
  height: 32px;
  flex: none;
  margin: -8px 0 -8px -8px;
  background: url("/media/item/experience-orb.png") 0 0 / 512px 32px;
  image-rendering: pixelated;
  animation: orb-colors 0.628s steps(16) infinite;
}

@keyframes orb-colors {
  to { background-position: -512px 0; }
}

/* Item icons in buttons and counts. */
.item-icon {
  width: 16px;
  height: 16px;
  margin-right: 6px;
  vertical-align: -3px;
  image-rendering: pixelated;
}

.feature p {
  margin: 0;
  font-size: 0.94rem;
  color: var(--silver);
}

/* ── Event feed: chat lines and advancement toasts, as the game draws them ── */
.feed-list {
  display: grid;
  gap: 0.6rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.feed-item {
  display: grid;
  grid-template-columns: 32px 1fr auto;
  align-items: center;
  gap: 0.85rem;
  min-height: 52px;
  padding: 6px 14px;
  font-family: var(--pixel);
  background: rgba(0, 0, 0, 0.35);
  border-radius: 4px;
  font-size: 12px;
}

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

.feed-line {
  color: var(--white);
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.45);
}

.feed-join .feed-line,
.feed-leave .feed-line {
  color: #ffff55;
  text-shadow: 2px 2px 0 #3f3f15;
}

.feed-death .feed-line::before {
  content: "☠ ";
  color: #ff5555;
}

/* Advancement toasts: dark slate, a bevelled border, a yellow or purple heading. */
.feed-advancement,
.feed-challenge {
  background: #212121;
  border: 2px solid #000;
  border-radius: 3px;
  box-shadow: inset 0 0 0 2px #555, inset 0 0 0 4px #2b2b2b;
}

.feed-heading {
  font-size: 12px;
  color: #ffff55;
  text-shadow: 2px 2px 0 #3f3f15;
}

.feed-challenge .feed-heading {
  color: #ff55ff;
  text-shadow: 2px 2px 0 #3f153f;
}

.feed-title {
  overflow: hidden;
  color: var(--white);
  white-space: nowrap;
  text-overflow: ellipsis;
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.5);
}

.feed-time {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}

.feed-more {
  display: block;
  margin-top: 0.8rem;
  padding: 0.85rem 1rem;
  font-family: var(--pixel);
  font-size: 12px;
  text-align: center;
  text-decoration: none;
  color: var(--white);
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: 4px;
  transition: border-color 0.2s, background 0.2s;
}

.feed-more:hover {
  border-color: var(--emerald);
  background: rgba(80, 200, 120, 0.12);
}

.feed-more:focus-visible {
  outline: 3px solid var(--mint);
  outline-offset: 2px;
}

.feed-empty {
  padding: 0.85rem;
  font-family: var(--pixel);
  color: var(--muted);
  background: rgba(0, 0, 0, 0.25);
  border-radius: 4px;
}

/* ── Playing now: everyone online with their HUD, beside a beating 3D heart ── */
.playing {
  margin-bottom: 3rem;
}

.playing-head {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-bottom: 1.1rem;
}

.playing-head h2 {
  margin: 0;
}

.playing-count {
  margin: 0.2rem 0 0;
  font-size: 0.95rem;
  color: var(--silver);
}

.heart-emblem {
  display: grid;
  place-items: center;
  flex: none;
  width: 6rem;
  height: 6rem;
  margin: -1rem 0;
}

.heart-emblem canvas {
  width: 100%;
  height: 100%;
  cursor: grab;
  touch-action: none;
}

.heart-emblem canvas:active {
  cursor: grabbing;
}

.heart-emblem.has-model .heart-fallback {
  display: none;
}

/* Until the model loads, or if it can't: a big pixel heart from the same sprites. */
.heart-fallback {
  width: 54px;
  height: 54px;
  background: url("/media/hud/hud.png") -108px 0 / 1188px 240px;
  image-rendering: pixelated;
}

/* One full-width row per player, like the feed's lines. */
.players {
  display: grid;
  gap: 0.6rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.player {
  position: relative;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.85rem;
  padding: 0.7rem 0.95rem;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid transparent;
  border-radius: 4px;
  animation: pop-in 0.35s ease-out;
}

.player.is-me {
  border-color: var(--emerald);
  background: rgba(80, 200, 120, 0.12);
}

.player .head {
  border-radius: 4px;
}

.player-body {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-width: 0;
}

.player-name {
  display: block;
  min-width: 0;
  overflow: hidden;
  font-family: var(--pixel);
  font-size: 16px;
  color: var(--white);
  white-space: nowrap;
  text-overflow: ellipsis;
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.45);
}

/* The Tab list's connection bars from the game, at twice their size: unknown, then 1 to 5 bars. */
.ping {
  flex: none;
  width: 20px;
  height: 16px;
  background: url("/media/hud/ping.png") calc(var(--bars, 0) * -20px) 0 / 120px 16px;
  image-rendering: pixelated;
}

/* The HUD as the game draws it at twice its size: hearts left, food right, experience under both. */
.hud {
  --gap: 40px;
  position: relative;
  display: grid;
  gap: 2px;
  width: calc(2 * 162px + var(--gap));
  border-radius: 3px;
  outline: none;
}

.hud:focus-visible {
  outline: 2px solid var(--mint);
  outline-offset: 4px;
}

.hud-bars {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--gap);
}

/* Hearts sit 16 px apart and overlap by 2, ten to a row; more rows stack upward, as in the
   game, and absorption adds golden ones after the red. */
.hearts,
.food {
  display: flex;
  flex-wrap: wrap-reverse;
  width: 162px;
  padding-right: 2px;
}

.food {
  flex-direction: row-reverse;
  flex-wrap: nowrap;
}

/* Two layers from the game's sprite sheet, at twice its size: the container, then the heart or drumstick. */
.heart,
.drumstick {
  flex: none;
  width: 18px;
  height: 18px;
  margin-right: -2px;
  background-image: url("/media/hud/hud.png"), url("/media/hud/hud.png");
  background-size: 396px 80px;
  image-rendering: pixelated;
}

.food .drumstick {
  margin: 0 0 0 -2px;
  background-position: calc(var(--fx, 10) * -18px) -36px, calc(var(--ex, 0) * -18px) -36px;
}

/* A hurt player's hearts blink between their usual look and the blinking one. */
@property --blink {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

.heart {
  background-position:
    calc((var(--fx, 10) + var(--blink) * (var(--bx, 10) - var(--fx, 10))) * -18px) calc(var(--fy, 2) * -18px),
    calc(var(--blink) * -18px) calc(var(--r, 0) * -18px);
}

.hearts-hurt {
  animation: heart-blink 0.6s steps(1);
}

@keyframes heart-blink {
  0%, 34%, 68% { --blink: 1; }
  17%, 51%, 85% { --blink: 0; }
}

/* Low on health: each heart jumps on its own beat, as in the game. */
.hearts-low .heart {
  animation: heart-shake 0.45s steps(1) infinite;
  animation-delay: calc(var(--i) * -0.11s);
}

.hearts .heart-pop {
  animation: heart-pop 0.3s ease-out;
}

@keyframes heart-shake {
  0% { transform: translateY(0); }
  25% { transform: translateY(-2px); }
  50% { transform: translateY(1px); }
  75% { transform: translateY(-1px); }
}

@keyframes heart-pop {
  from { transform: scale(1.6); }
}

/* Experience: the green bar under hearts and food, from the first heart to the last drumstick,
   with the level over its middle, its lower part on the bar, as in the game. */
.xp {
  position: relative;
}

.xp-number {
  position: absolute;
  bottom: 4px;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--pixel);
  font-size: 16px;
  line-height: 1;
  color: #80ff20;
  text-shadow: 2px 0 0 #000, -2px 0 0 #000, 0 2px 0 #000, 0 -2px 0 #000;
  pointer-events: none;
}

/* The game's bar, 182 × 5 at twice its height, stretched to the row's width; the progress
   layer on top is clipped to the fraction. */
.xp-bar {
  position: relative;
  display: block;
  height: 10px;
  background: url("/media/hud/hud.png") 0 -60px / 108.7912% 80px;
  image-rendering: pixelated;
}

.xp-fill {
  position: absolute;
  inset: 0;
  background: inherit;
  background-position: 0 -70px;
  clip-path: inset(0 calc(100% - var(--progress, 0%)) 0 0);
  transition: clip-path 0.4s ease-out;
}

.xp-no-bar .xp-bar {
  visibility: hidden;
}

/* The exact numbers, under the HUD on hover or tap. */
.hud-value {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 2;
  padding: 0.15rem 0.5rem;
  font-family: var(--pixel);
  font-size: 12px;
  color: var(--white);
  white-space: nowrap;
  max-width: calc(100vw - 3rem);
  overflow: hidden;
  text-overflow: ellipsis;
  background: rgba(0, 0, 0, 0.9);
  border-radius: 4px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.hud:hover .hud-value,
.hud:focus .hud-value {
  opacity: 1;
}

/* Narrow rows: the HUD goes under the name, hearts above food, both from the left. */
@media (max-width: 720px) {
  .player {
    grid-template-columns: 40px minmax(0, 1fr);
    row-gap: 0.6rem;
  }

  .hud {
    grid-column: 1 / -1;
    width: 100%;
    max-width: calc(2 * 162px + var(--gap));
  }

  .hud-value {
    left: 0;
    right: auto;
  }
}

@media (max-width: 400px) {
  .hud-bars {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }

  .hud {
    max-width: 164px;
  }

  /* Food sits right above the bar here, so the level needs its own room. */
  .xp {
    margin-top: 12px;
  }
}

@media (max-width: 480px) {
  .heart-emblem {
    width: 4.5rem;
    height: 4.5rem;
  }
}

.head {
  flex: none;
  image-rendering: pixelated;
}

@keyframes pop-in {
  from { opacity: 0; transform: translateY(4px) scale(0.92); }
}

/* ── Hall of fame: one card per record, top three in gold, silver, and bronze ── */
.world-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.8rem;
}

.world-tile {
  padding: 1rem 1.05rem;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.world-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 500;
}

.world-value {
  margin: 0.55rem 0 0.25rem;
  font-family: var(--pixel);
  font-size: 24px;
  font-weight: 700;
  color: var(--white);
  font-variant-numeric: tabular-nums;
}

.world-note {
  margin: 0;
  color: var(--muted);
  font-size: 0.8rem;
}

.fame-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 0.8rem;
}

.fame-card {
  display: flex;
  flex-direction: column;
  padding: 1rem 1.05rem;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* A record nobody is in yet: the words sit in the middle of the card. */
.fame-none {
  flex: 1;
  display: grid;
  place-items: center;
  min-height: 6rem;
  margin: 0;
  padding-left: 0.125em;
  font: 12px var(--pixel);
  color: var(--muted);
  text-align: center;
}

.fame-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.75rem;
  font-family: var(--pixel);
  font-size: 16px;
  color: var(--white);
  font-weight: 400;
}

.fame-icon {
  width: 24px;
  height: 24px;
  image-rendering: pixelated;
}

.fame-list {
  display: grid;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.fame-row {
  display: grid;
  grid-template-columns: 1.4rem 24px 1fr auto;
  align-items: center;
  gap: 0.55rem;
  padding: 0.3rem 0.5rem;
  font-family: var(--pixel);
  color: var(--silver);
  background: rgba(0, 0, 0, 0.25);
  border-radius: var(--radius);
  font-size: 12px;
}

.fame-rank {
  text-align: center;
  color: var(--muted);
}

.fame-name {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.fame-value {
  display: inline-flex;
  align-items: center;
  font-family: var(--pixel);
  color: var(--white);
}

.fame-place-1 {
  color: var(--white);
  background: linear-gradient(90deg, rgba(255, 215, 90, 0.16), rgba(0, 0, 0, 0.25) 70%);
}

.fame-place-1 .fame-rank {
  color: #ffd75a;
  text-shadow: 2px 2px 0 #4a3a0a;
}

.fame-place-2 .fame-rank {
  color: #d4dde4;
}

.fame-place-3 .fame-rank {
  color: #d99a62;
}

/* ── /events: filters and a virtual list; app.js isn't loaded there ── */
.events-page {
  position: relative;
  z-index: 1;
  width: min(100% - 2rem, 48rem);
  margin: 0 auto;
  padding: 2.5rem 0 4rem;
}

.back {
  font-family: var(--pixel);
  color: var(--mint);
  text-decoration: none;
}

.back:hover {
  color: var(--emerald);
}

.events-title {
  margin: 1.25rem 0 0.35rem;
  font-family: var(--pixel);
  font-size: 32px;
  font-weight: 700;
  line-height: 1.05;
  color: var(--emerald);
}

.events-lede {
  margin: 0 0 1.75rem;
  color: var(--silver);
}

.filters {
  display: grid;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

.filter-players,
.filter-dates {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.35rem 0.75rem;
  font: 12px var(--pixel);
  color: var(--silver);
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
}

.filter-chip:hover {
  color: var(--white);
  border-color: var(--emerald);
}

.filter-chip[aria-pressed="true"] {
  color: var(--night);
  background: var(--emerald);
  border-color: var(--emerald);
  box-shadow: inset 0 -3px 0 var(--emerald-deep);
}

.filter-chip:focus-visible,
.date-field input:focus-visible {
  outline: 3px solid var(--mint);
  outline-offset: 2px;
}

.date-field {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--pixel);
  color: var(--muted);
}

.date-field input {
  padding: 0.3rem 0.5rem;
  font: 500 0.9rem var(--mono);
  color: var(--white);
  color-scheme: dark;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.events-count {
  margin: 0 0 0.75rem;
  font-family: var(--pixel);
  color: var(--muted);
}

/* Rows sit at fixed offsets: events.js sets translate and the list height. */
.events-list {
  position: relative;
  display: block;
}

.events-list .feed-item {
  position: absolute;
  inset: 0 0 auto;
  height: 52px;
}

.events-list .feed-line {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Under the third place, pushed to the bottom so buttons in a row line up. */
.fame-more {
  display: block;
  margin-top: auto;
  padding-top: 0.6rem;
  font-family: var(--pixel);
  font-size: 12px;
  text-align: center;
  text-decoration: none;
  color: var(--mint);
}

.fame-more::before {
  content: "";
  display: block;
  margin-bottom: 0.6rem;
  border-top: 1px solid var(--line);
}

.fame-more:hover {
  color: var(--emerald);
}

.fame-more:focus-visible {
  outline: 3px solid var(--mint);
  outline-offset: 2px;
}

/* /fame: the full standings, rows at fixed offsets set by fame.js. */
.find-me {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.find-me input {
  flex: 1 1 12rem;
  max-width: 18rem;
  padding: 0.35rem 0.6rem;
  font: 500 0.95rem var(--mono);
  color: var(--white);
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.find-me input:focus-visible {
  outline: 3px solid var(--mint);
  outline-offset: 2px;
}

.fame-standings {
  position: relative;
  display: block;
}

.fame-standings .fame-row {
  position: absolute;
  inset: 0 0 auto;
  height: 42px;
  grid-template-columns: 2.6rem 24px 1fr auto;
  font-size: 12px;
}

.fame-row.is-me {
  color: var(--white);
  background: rgba(80, 200, 120, 0.22);
  box-shadow: inset 0 0 0 2px var(--emerald);
}

.fame-row.is-me .fame-name::after {
  content: " · you";
  color: var(--emerald);
}

.footer {
  position: relative;
  z-index: 1;
  padding: 4rem 1rem 3rem;
  text-align: center;
}

.footer a {
  font-family: var(--pixel);
  color: var(--muted);
  text-decoration: none;
}

.footer a:hover {
  color: var(--emerald);
}

.footer .credit {
  margin: 1rem 0 0;
  font-size: 0.75rem;
  color: var(--muted);
  opacity: 0.7;
}

.footer .credit a {
  font-family: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

::selection {
  color: var(--night);
  background: var(--emerald);
}

@media (max-width: 640px) {
  .moon {
    top: 1.5rem;
    right: -2.5rem;
  }

  .sky-static .moon {
    opacity: 0.8;
  }

  .links {
    margin-top: 0;
  }

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

  .world-value {
    font-size: 16px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .feature h3::before {
    animation: none;
  }

  .moon, .click-hint, .status[data-state="online"] .dot, .player, .heart, .hearts {
    animation: none;
  }
}

.relay-note {
  margin-top: 0.35rem;
}

.relay-note code {
  font-family: var(--mono);
  color: var(--mint);
}

/* Rolling numbers: a column per digit shows one line of a 0–9 strip. */
.roll-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.roll {
  display: inline-flex;
  vertical-align: bottom;
  line-height: 1.2;
  white-space: pre;
  user-select: none;
}

.roll-char,
.roll-col {
  display: block;
  height: 1.2em;
}

.roll-col {
  overflow: hidden;
}

.roll-strip {
  display: flex;
  flex-direction: column;
}

.roll-spin {
  transition: translate 0.9s cubic-bezier(0.2, 0.7, 0.15, 1);
}

/* ── The game's GUI sprites, behind `gameTextures` in feed.js. Text at 12 px is GUI scale 1.5,
   so sprites next to it draw at 1.5 too, the toasts behind player cards included. ── */

/* Shared: a sprite nine-sliced behind the element, drawn from --sprite, --slice, and --edge. */
.game-textures :is(.player, .feed-item, .mc-button) {
  position: relative;
  isolation: isolate;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  backdrop-filter: none;
}

.game-textures :is(.player, .feed-item, .mc-button)::before {
  content: "";
  position: absolute;
  inset: 0;
  margin: 0;
  z-index: -1;
  border: var(--edge) solid transparent;
  border-image-source: var(--sprite);
  border-image-slice: var(--slice) fill;
  border-image-width: var(--edge);
  border-image-repeat: repeat;
  image-rendering: pixelated;
  pointer-events: none;
}

/* The virtual list on /events places its lines itself. */
.game-textures .events-list .feed-item {
  position: absolute;
}

/* Every feed line is the same toast, joins and deaths too; advancements add their heading.
   Player cards share it, so Playing now looks like the feed. */
.game-textures :is(.player, .feed-item) {
  --sprite: url("/media/gui/toast-advancement.png");
  --slice: 4;
  --edge: 6px;
}

/* The button: the game's, lit while hovered or pressed, white text with its shadow. The
   extra font pixel on the left balances the gap the font leaves after each glyph. */
.mc-button {
  --sprite: url("/media/gui/button.png");
  --slice: 3;
  --edge: 4.5px;
}

.game-textures .mc-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 40px;
  padding: 0 12px 0 calc(12px + 0.125em);
  font: 12px var(--pixel);
  color: #fff;
  text-shadow: 1.5px 1.5px 0 #3f3f3f;
  text-decoration: none;
  cursor: pointer;
}

.game-textures :is(.feed-more, .fame-more) {
  display: flex;
}

/* Filters and Show sit beside 30 px fields, so they stay that tall. */
.game-textures .filter-chip {
  min-height: 30px;
}

.game-textures .mc-button[hidden] {
  display: none;
}

.game-textures .fame-more {
  margin-top: auto;
}

/* Space above the button only while it shows, so the card's padding stays even. */
.game-textures .fame-card .fame-list:has(+ .fame-more:not([hidden])) {
  margin-bottom: 0.75rem;
}

.game-textures :is(.mc-button:is(:hover, :focus-visible), .mc-button[aria-pressed="true"], .address:is(:hover, :focus-visible) .mc-button) {
  --sprite: url("/media/gui/button-highlighted.png");
}

.game-textures .mc-button[aria-pressed="true"] {
  color: #ffff55;
  text-shadow: 1.5px 1.5px 0 #3f3f15;
}

/* Text fields: the game's black field with a grey edge, white once focused. */
.game-textures :is(.address code, .date-field input, .find-me input) {
  padding: 6px 8px;
  font: 12px var(--pixel);
  color: #e0e0e0;
  background: none;
  border: 2px solid transparent;
  border-image: url("/media/gui/text-field.png") 1 fill / 2px stretch;
  border-radius: 0;
  image-rendering: pixelated;
}

.game-textures :is(.date-field input:focus, .find-me input:focus, .address:is(:hover, :focus-visible) code) {
  border-image-source: url("/media/gui/text-field-highlighted.png");
  outline: none;
}

/* The address as on the game's Add Server screen: a grey label over the field, Copy beside it. */
.game-textures .address {
  grid-template-areas: "label ." "code action";
  align-items: stretch;
  gap: 4px 8px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.game-textures .address:hover {
  transform: none;
}

.game-textures .address-label {
  font: 12px var(--pixel);
  letter-spacing: 0;
  text-transform: none;
  color: #a0a0a0;
}

.game-textures .address code {
  font-size: 16px;
  padding: 8px 10px;
}

/* The status as the server's entry in the game's server list, selected: icon, name, a line on
   who plays, the player count, and ping bars. Grey at rest, white under the cursor. */
.status-icon,
.status-name,
.status-count,
.status-ping {
  display: none;
}

.game-textures .status {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) auto 15px;
  grid-template-areas: "icon name count ping" "icon text text text";
  align-items: center;
  gap: 2px 8px;
  width: min(100%, 26rem);
  margin: 0 auto 1.5rem;
  padding: 4px 8px 4px 4px;
  font: 12px var(--pixel);
  text-align: left;
  background: #000;
  border: 2px solid #808080;
  border-radius: 0;
  backdrop-filter: none;
}

.game-textures .status:hover {
  border-color: #fff;
}

.game-textures .status .dot {
  display: none;
}

.game-textures .status-icon {
  display: block;
  grid-area: icon;
  width: 48px;
  height: 48px;
}

.game-textures .status-name {
  display: block;
  grid-area: name;
  color: #fff;
}

.game-textures #status-text {
  grid-area: text;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: #aaa;
}

.game-textures .status[data-state="offline"] #status-text {
  color: #ff5555;
}

.game-textures .status-count {
  display: block;
  grid-area: count;
  color: #808080;
}

/* Bars by TPS; crossed out with no connection; the game's pinging cycle while it asks. */
.game-textures .status-ping {
  display: block;
  grid-area: ping;
  width: 15px;
  height: 12px;
  background: url("/media/gui/pinging.png") 0 0 / 120px 12px;
  image-rendering: pixelated;
  animation: pinging 0.8s steps(8) infinite;
}

@keyframes pinging {
  to { background-position: -120px 0; }
}

.game-textures .status-ping[data-bars] {
  background-size: 15px 12px;
  animation: none;
}

.game-textures .status-ping[data-bars="5"] { background-image: url("/media/gui/ping-5.png"); }
.game-textures .status-ping[data-bars="4"] { background-image: url("/media/gui/ping-4.png"); }
.game-textures .status-ping[data-bars="3"] { background-image: url("/media/gui/ping-3.png"); }
.game-textures .status-ping[data-bars="2"] { background-image: url("/media/gui/ping-2.png"); }
.game-textures .status-ping[data-bars="1"] { background-image: url("/media/gui/ping-1.png"); }
.game-textures .status-ping[data-bars="none"] { background-image: url("/media/gui/ping-unreachable.png"); }
