/* Asteroid Wars — landing page. Standalone CSS, no framework deps. */

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

html,
body {
  margin: 0;
  padding: 0;
}

/* 3D background canvas. Fixed and behind all content, faded in only once the
   scene is actually running so there is never a flash of black canvas over the
   flat fallback. `pointer-events: none` keeps the whole page clickable. */
#landing-bg {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s ease;
}

#landing-bg.is-live {
  opacity: 1;
}

/* Content sits above the canvas. Without this the hero and cards render
   underneath it and vanish. */
.landing,
#auth-bar {
  position: relative;
  z-index: 1;
}

/* With the scene running, drop the page's own backdrop so the sky shows
   through, and keep only a soft vignette at the top so the hero text stays
   legible against a busy starfield. `body.bg-live` is set by landing-bg.js at
   the same moment the canvas fades in, so the swap is covered by the same
   1.2s transition and never flashes. */
/* With the lattice drawing over it, drop the DOM title's own fill so the two
   don't double up. The element, its text and its layout box all stay — this is
   purely visual, so the <h1> remains the heading crawlers and screen readers
   see, and the lattice anchors to its live bounding rect. */
body.bg-live .title {
  color: transparent;
  background: none;
  text-shadow: none;
  opacity: 0.001;
  transition: opacity 1.2s ease;
}

body.bg-live .landing {
  background: linear-gradient(
    to bottom,
    rgba(11, 13, 18, 0.72) 0%,
    rgba(11, 13, 18, 0.35) 38%,
    rgba(11, 13, 18, 0.55) 100%
  );
  transition: background 1.2s ease;
}

body {
  background: #0b0d12;
  color: #e8eaed;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body.modal-open {
  overflow: hidden;
}

/* ─────────── auth bar (top-right) ─────────── */

#auth-bar {
  position: fixed;
  top: 14px;
  right: 18px;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
}

.auth-signin {
  background: rgba(255, 255, 255, 0.10);
  color: #eef3fb;
  border: 1px solid rgba(255, 255, 255, 0.32);
  padding: 7px 14px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.5px;
  cursor: pointer;
  font-family: inherit;
  transition: filter 0.15s ease;
}

.auth-signin:hover {
  filter: brightness(1.18);
}

.auth-chip {
  display: flex;
  align-items: center;
  gap: 6px;
}

.auth-chip-name {
  background: rgba(255, 255, 255, 0.04);
  color: #f0e0ff;
  border: 1px solid rgba(255, 255, 255, 0.1);
  padding: 7px 12px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1px;
  cursor: pointer;
  font-family: inherit;
  transition:
    border-color 0.15s ease,
    background 0.15s ease;
}

.auth-chip-name:hover {
  border-color: rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.08);
}

/* ─────────── modal scrim + dialog ─────────── */

.modal-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  backdrop-filter: blur(4px);
  z-index: 100;
}

.modal-scrim[hidden] {
  display: none;
}

.modal-dialog {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 101;
  padding: 24px;
  pointer-events: none;
}

.modal-dialog[hidden] {
  display: none;
}

.modal-dialog > * {
  pointer-events: auto;
}

.modal-card {
  background: #15171f;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 8px;
  padding: 28px 32px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 320px;
  max-width: 420px;
  width: 100%;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
}

.modal-card.profile-menu {
  min-width: 280px;
  max-width: 320px;
}

.modal-heading {
  margin: 0;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: 1.5px;
  color: #f0e0ff;
}

.modal-sub {
  margin: 0 0 6px 0;
  font-size: 12px;
  opacity: 0.7;
  line-height: 1.5;
}

.modal-email {
  color: #a5f3fc;
  font-weight: 600;
}

.modal-input {
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.15);
  padding: 10px 12px;
  border-radius: 4px;
  font-size: 13px;
  font-family: inherit;
  letter-spacing: 0.6px;
  outline: none;
  transition: border-color 0.15s ease;
}

.modal-input:focus {
  border-color: rgba(255, 255, 255, 0.55);
}

.modal-input.modal-code {
  font-size: 24px;
  letter-spacing: 8px;
  text-align: center;
  padding: 14px;
  font-weight: 700;
}

.modal-error {
  margin: 0;
  min-height: 14px;
  font-size: 11px;
  color: #ef4444;
  letter-spacing: 0.3px;
}

.modal-error:empty {
  /* keep its height so layout doesn't jump when an error appears */
}

.modal-button {
  background: transparent;
  color: inherit;
  border: 1px solid rgba(255, 255, 255, 0.15);
  padding: 10px 16px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.5px;
  cursor: pointer;
  font-family: inherit;
  transition:
    filter 0.15s ease,
    border-color 0.15s ease,
    background 0.15s ease;
}

.modal-button:disabled {
  opacity: 0.55;
  cursor: progress;
}

.modal-button.primary {
  background: linear-gradient(180deg, #ffffff 0%, #cdd7e5 100%);
  border-color: transparent;
  color: #0b0d12;
}

.modal-button.primary:hover:not(:disabled) {
  filter: brightness(1.15);
}

.modal-button.ghost {
  background: transparent;
  color: rgba(255, 255, 255, 0.55);
}

.modal-button.ghost:hover:not(:disabled) {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.3);
}

.landing {
  min-height: 100vh;
  /* Opaque by default — this gradient IS the fallback background whenever the
     3D scene doesn't render (reduced motion, small screens, no WebGL). It sits
     above the canvas because the content needs to, so it has to become
     transparent once the scene is live; see body.bg-live below. */
  background: radial-gradient(ellipse at top, #131826 0%, #0b0d12 60%);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 48px 24px 96px;
  gap: 48px;
}

.hero {
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 36px;
}

.title {
  font-size: clamp(48px, 8vw, 96px);
  font-weight: 900;
  letter-spacing: 8px;
  margin: 0;
  background: linear-gradient(180deg, #f0f0f5 0%, #0c6dc2 100%);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  text-shadow: 0 0 32px rgba(214, 232, 255, 0.22);
}

.tagline {
  margin: 0;
  font-size: 13px;
  letter-spacing: 1.4px;
  opacity: 0.65;
}

/* Hero CTAs. Deliberately lighter than `.join-button` — the shard JOIN buttons
   must stay the brightest thing on the page, because playing is the primary
   action and these are only signposts on the way to it. Outlined rather than
   filled for the same reason. */
.hero-links {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 4px;
}

.hero-link {
  display: inline-block;
  padding: 8px 16px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 4px;
  color: #d6e2f2;
  text-decoration: none;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 2px;
  text-transform: uppercase;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.hero-link:hover {
  border-color: rgba(255, 255, 255, 0.4);
  background: rgba(255, 255, 255, 0.05);
  color: #ffffff;
}

/* Discord blurple — enough to be recognisable without shipping a logo asset,
   which would be one more file for the deploy to get wrong. */
.hero-link.discord {
  border-color: rgba(88, 101, 242, 0.5);
  color: #b9c1ff;
}

.hero-link.discord:hover {
  border-color: #5865f2;
  background: rgba(88, 101, 242, 0.14);
  color: #ffffff;
}

.section-title {
  font-size: 13px;
  letter-spacing: 3px;
  opacity: 0.55;
  margin: 0 0 16px;
  text-align: center;
  font-weight: 700;
}

.play,
.leaderboard {
  width: min(960px, 100%);
}

.game-cards {
  display: grid;
  /* Wider min so the info-pane + minimap layout breathes. The whole
     card column also wraps on small viewports because the .card-body-row
     children themselves wrap when the info pane runs out of room. */
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 16px;
  justify-content: center;
}

/* Below ~380px (the minimap + a usable info pane both stop fitting on
   one row): drop to a single-column layout inside the card so the
   minimap is on top, info pane underneath. */
@media (max-width: 420px) {
  .card-body-row {
    flex-direction: column;
  }
}

.game-card {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  padding: 20px 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  transition:
    border-color 0.18s ease,
    transform 0.18s ease;
}

.game-card:hover {
  border-color: rgba(255, 255, 255, 0.42);
  transform: translateY(-2px);
}

.card-title {
  margin: 0;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: 2px;
}

.card-status {
  margin: 0;
  font-size: 11px;
  letter-spacing: 1.2px;
  opacity: 0.6;
}

.card-counts {
  margin: 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
  letter-spacing: 1px;
}

.card-roster {
  margin: 0;
  font-size: 11px;
  letter-spacing: 0.6px;
  opacity: 0.75;
  display: flex;
  gap: 4px;
}

.card-roster .alpha {
  color: #ef4444;
}

.card-roster .beta {
  color: #3b82f6;
}

/* Card body row — minimap on the left, info pane (meta + killfeed) on
   the right. Two columns when there's enough width; CSS wraps onto two
   stacks on very narrow widths. */
.card-body-row {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  margin-top: 4px;
}

.info-pane {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
  min-width: 0;
}

/* Title + counts + status + roster in a single horizontal row that
   flex-wraps on narrower cards. On wide cards everything sits on one
   line; on narrow cards items wrap to additional lines as needed. */
.info-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 12px;
  row-gap: 4px;
}

.info-meta .card-title {
  margin: 0;
  /* Take whatever's available on this row; counts/status/roster fall to
     the next line when there's no room. */
  flex: 1 1 auto;
  min-width: 0;
}

.info-meta .card-counts,
.info-meta .card-status,
.info-meta .card-roster {
  margin: 0;
  flex: 0 0 auto;
}

/* Three stacked 3×3 layers — mirrors the in-game Minimap's altitude
   stack. Y is the vertical axis in-game; each layer represents one Y
   sector (0..2, matching the in-game displayed numbering). */
.sector-stack {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 0 0 auto;
}

/* "(X, Z)" legend above the stack — tells the viewer which axes the
   per-cell coord labels refer to. Y is in the per-layer label on the
   right side of each layer. */
.sector-axis-header {
  font-size: 8px;
  letter-spacing: 0.5px;
  color: rgba(255, 255, 255, 0.45);
  font-family: ui-monospace, "SFMono-Regular", monospace;
  padding-left: 4px;
  margin-bottom: 1px;
}

.sector-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.altitude-label {
  font-size: 10px;
  letter-spacing: 0.4px;
  color: rgba(255, 255, 255, 0.45);
  font-family: ui-monospace, "SFMono-Regular", monospace;
}

.sector-layer {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  width: 70px;
  padding: 3px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

/* Per-cell coordinate label — tiny, bottom-right, doesn't collide with
   the centered install dots. Slight text-shadow for legibility against
   bright faction colours (red/blue/orange). */
.coord-label {
  position: absolute;
  bottom: 1px;
  right: 2px;
  font-size: 7px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.7);
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.7);
  font-family: ui-monospace, "SFMono-Regular", monospace;
  letter-spacing: 0;
  pointer-events: none;
}

.sector {
  aspect-ratio: 1;
  border-radius: 2px;
  background: #2a2d35;
  position: relative;
  overflow: hidden;
}

.sector.alpha {
  background: #ef4444;
}

.sector.beta {
  background: #3b82f6;
}

.sector.contested {
  background: #f97316;
}

.sector.neutral {
  background: #2a2d35;
}

/* Small white dots inside each cell, one per installation (max 3). */
.install-dots {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1px;
}

.install-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.85);
  box-shadow: 0 0 2px rgba(255, 255, 255, 0.4);
}

/* Killfeed — visually distinct inner card sitting below the info meta.
   Its own background + border makes it clearly a "live events" panel
   rather than another piece of meta. Up to 3 rows, freshest at top. */
.killfeed {
  list-style: none;
  margin: 0;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 10.5px;
  letter-spacing: 0.2px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 6px;
  min-height: 56px;
}

.killfeed.empty {
  align-items: flex-start;
  justify-content: center;
  padding: 12px 10px;
}

.killfeed.empty::after {
  content: "no recent events";
  font-size: 10px;
  opacity: 0.3;
  letter-spacing: 0.5px;
}

.killfeed-row {
  color: rgba(255, 255, 255, 0.78);
  display: flex;
  gap: 4px;
  align-items: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.kf-killer {
  color: #fef3c7;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.kf-victim {
  color: rgba(255, 255, 255, 0.5);
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.kf-mark {
  color: #ef4444;
  font-size: 10px;
  opacity: 0.85;
  flex: 0 0 auto;
}

/* Greyed-out card for OFFLINE / stale / not-joinable shards.
   Leaves them visible so the player can still see the picker,
   but visually de-emphasised. */
.game-card.stale,
.game-card.not-joinable {
  opacity: 0.55;
}

.game-card.stale:hover,
.game-card.not-joinable:hover {
  border-color: rgba(255, 255, 255, 0.1);
  transform: none;
}

.join-button.disabled {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.4);
  box-shadow: none;
  cursor: not-allowed;
  pointer-events: none;
}

.join-button {
  display: inline-block;
  margin-top: 8px;
  padding: 10px 18px;
  background: linear-gradient(180deg, #ffffff 0%, #cdd7e5 100%);
  color: #0b0d12;
  text-align: center;
  text-decoration: none;
  font-weight: 800;
  letter-spacing: 3px;
  font-size: 13px;
  border-radius: 4px;
  box-shadow: 0 6px 20px rgba(210, 226, 245, 0.24);
  transition: filter 0.15s ease;
}

.join-button:hover {
  filter: brightness(1.15);
}

.placeholder {
  background: rgba(255, 255, 255, 0.02);
  border: 1px dashed rgba(255, 255, 255, 0.12);
  border-radius: 8px;
  padding: 28px;
  text-align: center;
  color: rgba(255, 255, 255, 0.5);
  font-size: 13px;
}

.lb-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 8px;
  overflow: hidden;
}

.lb-row {
  display: grid;
  grid-template-columns: 56px 1fr 80px 80px;
  align-items: center;
  padding: 10px 16px;
  font-size: 13px;
  letter-spacing: 0.2px;
  border-top: 1px solid rgba(255, 255, 255, 0.04);
}

.lb-row:first-child {
  border-top: none;
}

.lb-rank {
  color: #eaf2ff;
  font-weight: 700;
}

.lb-name {
  font-weight: 600;
  color: rgba(255, 255, 255, 0.92);
}

.lb-value {
  text-align: right;
  color: rgba(255, 255, 255, 0.78);
  font-variant-numeric: tabular-nums;
}

.lb-matches {
  text-align: right;
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.footer {
  margin-top: auto;
  opacity: 0.4;
  font-size: 11px;
  letter-spacing: 1.5px;
}

/* Manual link. The footer as a whole sits at 0.4 opacity; the link lifts to
   full on hover so it reads as clickable rather than as more small print. */
.footer-sep {
  margin: 0 6px;
}

.footer-link {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
}

.footer-link:hover {
  color: #7fd7ff;
}
