.online-timeline {
  width: 100%;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 16px;
  padding: 24px 28px 72px;
  margin-bottom: 20px;
}
.online-timeline-title {
  margin: 0 0 32px;
  text-align: center;
  font-size: 18px;
  font-weight: 700;
}
.online-timeline-track-wrap {
  padding: 44px 16px 0;
}
.online-timeline-track {
  position: relative;
  height: 6px;
  background: var(--bg);
  border-radius: 999px;
}
.online-timeline-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 0;
  background: var(--amber);
  border-radius: 999px;
  transition: width 0.3s;
}
.online-timeline-dots {
  position: relative;
  width: 100%;
  height: 0;
}
.online-timeline-point {
  position: absolute;
  top: 0;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.online-timeline-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--panel-border);
}
.online-timeline-point.reached .online-timeline-dot {
  background: var(--amber);
  border-color: var(--amber);
}
.online-timeline-label {
  position: absolute;
  top: 20px;
  /* Centred explicitly rather than by the flex parent's static position: the
     booster note below is far wider than "180 นาที", and letting the label size
     itself around it pulled the whole label off the dot. */
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  text-align: center;
}
.online-timeline-label-note {
  display: block;
  margin-top: 2px;
  font-size: 10px;
  color: var(--discord);
  /* One line, like the minute above it. Letting it wrap made the label box as
     wide as the note's cap and as tall as however many lines it broke into, so
     this one mark's text sat differently from every other mark's. The wording is
     kept short instead — the blurple dot and icon already carry the meaning. */
  white-space: nowrap;
}

/* Boosting — the mark is live for this player, so the dot is Discord's colour.
   Both booster rules carry an extra class over `.reached .online-timeline-dot`
   on purpose: they have to outrank it wherever they appear in the file, or a
   reached booster mark would go amber like any other. */
.online-timeline-point.booster.boosting .online-timeline-dot {
  background: var(--discord);
  border-color: var(--discord);
}
/* Not boosting: grey, the same as any mark that pays out nothing for you. */
.online-timeline-point.booster:not(.boosting) .online-timeline-dot {
  background: var(--bg);
  border-color: var(--panel-border);
}
.online-timeline-point.booster:not(.boosting) .online-timeline-reward-icon {
  border-color: var(--panel-border);
  /* Reachable but not claimable, and the icon should not look like a prize
     waiting to be collected. */
  opacity: 0.55;
}
.online-timeline-point.booster:not(.boosting) .online-timeline-label-note {
  color: var(--text-dim);
}
.online-timeline-point.booster.boosting .online-timeline-reward-icon {
  border-color: var(--discord);
}

/* Several rewards can share one minute — a row, so the second does not land on
   top of the first. */
.online-timeline-reward-icons {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
}
.online-timeline-reward-icon {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  font-size: 16px;
  overflow: hidden;
}
.online-timeline-reward-icon img {
  max-width: 24px;
  max-height: 24px;
  object-fit: contain;
}
.online-timeline-point.reached .online-timeline-reward-icon {
  border-color: var(--amber);
}

.online-timeline-current {
  position: absolute;
  top: 0;
  transform: translate(-50%, -50%);
  z-index: 2;
}
.online-timeline-current-pin {
  width: 3px;
  height: 40px;
  background: var(--blue);
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
}
.online-timeline-current-flag {
  position: absolute;
  top: 40px;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  background: var(--blue);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 6px;
}
.online-timeline-current-flag::after {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-bottom-color: var(--blue);
}

.welcome-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 12px;
  min-height: 50vh;
}

.welcome-title {
  margin: 0;
  font-size: 72px;
  font-weight: 800;
  color: var(--text);
}
.welcome-sub { margin: 0; color: var(--text-dim); font-size: 16px; }
.welcome-sub a { color: var(--olive-bright); }

@media (max-width: 520px) {
  .welcome-title { font-size: 36px; }
}

.player-stats {
  width: 100%;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 16px;
  padding: 24px 28px;
}

.player-stats-title {
  margin: 0 0 16px;
  text-align: center;
  font-size: 18px;
  font-weight: 700;
}

.player-stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 16px;
}

.player-stat-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  background: var(--bg);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  padding: 16px 12px;
}

.player-stat-value {
  font-size: 24px;
  font-weight: 800;
  color: var(--olive-bright);
}

.player-stat-label {
  font-size: 12px;
  color: var(--text-dim);
}

.player-stats-empty {
  margin: 0;
  text-align: center;
  color: var(--text-dim);
  font-size: 13px;
}


/* The Discord link panel. Shown to everybody: somebody who has never boosted
   needs to know the perk exists before they would consider it. */
.discord-link[hidden] {
  /* Author-origin `display: flex` below outranks the UA's
     `[hidden] { display: none }`, so without this the panel ignores the hidden
     attribute — and renderDiscordLink()'s failure path, which leaves it hidden
     on purpose, would show an empty panel instead of nothing. */
  display: none;
}

.discord-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-left: 3px solid var(--discord);
  border-radius: var(--radius);
  padding: 16px 20px;
  margin-bottom: 20px;
}

/* Boosting gets Discord's pink rather than a badge of its own — the colour is
   already the signal, and a second one would just be louder. */
.discord-link.is-boosting {
  border-left-color: #ff73fa;
  background: linear-gradient(90deg, rgba(255, 115, 250, 0.08), var(--panel) 40%);
}

.discord-link-main {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.discord-link-icon {
  flex: none;
  filter: brightness(0) saturate(100%) invert(45%) sepia(70%) saturate(3000%) hue-rotate(215deg);
}

.discord-link.is-boosting .discord-link-icon {
  filter: brightness(0) saturate(100%) invert(70%) sepia(60%) saturate(2000%) hue-rotate(275deg);
}

.discord-link-title { font-weight: 600; font-size: 14.5px; }
.discord-link-sub { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
.discord-link-actions { display: flex; gap: 8px; }
