/* ============================================================
   The Saturday Stream Team — Raid Train Tracker
   Neon / glass modern theme
   ============================================================ */

:root {
  /* Base surfaces — deep blue-violet night with a hint of cyan */
  --bg: #07070f;
  --bg-alt: #0d0d1a;
  --card: rgba(255, 255, 255, 0.04);
  --card-solid: #121222;
  --border: rgba(255, 255, 255, 0.09);
  --border-strong: rgba(255, 255, 255, 0.16);

  /* Neon accents */
  --purple: #7c3aed;
  --purple-light: #c084fc;
  --neon: #a855f7;
  --cyan: #22d3ee;
  --text: #f4f2ff;
  --text-dim: #9d9ab8;

  /* Status + data colours (kept recognisable from the old skin) */
  --green: #34d399;
  --yellow: #fbbf24;
  --red: #f87171;
  --blue: #60a5fa;
  --pink: #f472b6;

  /* Shared effects */
  --glow-purple: 0 0 12px rgba(168, 85, 247, 0.45);
  --glow-cyan: 0 0 12px rgba(34, 211, 238, 0.4);
  --glass: linear-gradient(160deg, rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.015));
  --card-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
}

* { box-sizing: border-box; }

html { scrollbar-color: #3a3654 transparent; }

body {
  margin: 0;
  font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--text);
  background:
    radial-gradient(1100px 520px at 12% -10%, rgba(124, 58, 237, 0.16), transparent 60%),
    radial-gradient(900px 480px at 95% 0%, rgba(34, 211, 238, 0.1), transparent 55%),
    radial-gradient(800px 600px at 50% 115%, rgba(168, 85, 247, 0.08), transparent 60%),
    var(--bg);
  background-attachment: fixed;
}

/* ---------- Nav ---------- */

nav {
  position: sticky;
  top: 0;
  z-index: 900;
  background: rgba(9, 9, 20, 0.78);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 0 rgba(168, 85, 247, 0.25), 0 10px 34px rgba(0, 0, 0, 0.45);
  padding: 13px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

nav .brand {
  font-weight: 800;
  font-size: 17px;
  letter-spacing: 0.02em;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}
nav .brand span {
  background: linear-gradient(90deg, var(--purple-light), var(--cyan));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--purple-light);
}

nav .links {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  row-gap: 4px;
}

nav .links a {
  color: var(--text-dim);
  text-decoration: none;
  margin-left: 20px;
  font-size: 13.5px;
  font-weight: 600;
  padding: 4px 2px;
  border-radius: 6px;
  border-bottom: 2px solid transparent;
  transition: color 0.15s, text-shadow 0.15s, border-color 0.15s;
}
nav .links a:hover {
  color: var(--text);
  text-shadow: 0 0 14px rgba(192, 132, 252, 0.8);
}
nav .links a.active {
  color: var(--text);
  border-bottom-color: var(--neon);
  filter: drop-shadow(0 0 6px rgba(168, 85, 247, 0.7));
}
/* The signed-in Twitch account in the nav. It replaces the admin links — a
   Twitch session never sees (or has) an admin page to link to. */
nav .links a.nav-user {
  color: var(--purple-light);
}
/* Twitch's own brand purple, used for the connect/claim buttons so the
   account link reads the same everywhere it appears. */
/* Doubled selector so it beats the later .btn / .btn:hover rules. */
.btn.btn-twitch {
  background: #9146ff;
  color: #fff;
}
.btn.btn-twitch:hover { background: #a46bff; }

/* ---------- Nav dropdowns (Raid Trains / Roster groups) ---------- */

/* The group wrapper behaves exactly like the old bare link in the flex
   row — spacing moves onto the wrapper so the trigger anchor stays flush. */
nav .links .nav-drop {
  position: relative;
  margin-left: 20px;
}
nav .links .nav-drop > a {
  margin-left: 0;
}
/* Caret hints that extra pages live under this one */
.nav-drop > a::after {
  content: " \25BE";
  font-size: 10px;
  opacity: 0.65;
}
.nav-drop-menu {
  position: absolute;
  top: 100%;
  left: -10px;
  z-index: 50;
  min-width: 148px;
  padding: 6px;
  background: rgba(13, 13, 28, 0.97);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55), 0 0 18px rgba(168, 85, 247, 0.12);
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity 0.15s, transform 0.15s, visibility 0.15s;
}
.nav-drop:hover .nav-drop-menu,
.nav-drop:focus-within .nav-drop-menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
/* Higher specificity than "nav .links a" so the row-link rules (margins,
   underline hover) don't leak into the menu items. */
nav .links .nav-drop-menu a {
  display: block;
  margin: 0;
  padding: 7px 10px;
  border-bottom: none;
  border-radius: 7px;
  white-space: nowrap;
}
nav .links .nav-drop-menu a:hover {
  background: rgba(168, 85, 247, 0.12);
  border-bottom: none;
}
nav .links .nav-drop-menu a.active {
  border-bottom: none;
  background: rgba(168, 85, 247, 0.15);
  filter: none;
}
/* Keep a subtle glow on the trigger while one of its dropdown pages is
   the page you're actually on (only that page's own item says "active"). */
nav .links .nav-drop.has-active > a {
  color: var(--text);
  border-bottom-color: rgba(168, 85, 247, 0.55);
}

/* ---------- Page scaffold ---------- */

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 30px 20px 60px;
}

h1 {
  font-size: 27px;
  margin-bottom: 4px;
  letter-spacing: 0.01em;
  background: linear-gradient(90deg, var(--text) 55%, var(--purple-light));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  text-shadow: none;
}
h2 { font-size: 19px; margin-top: 0; }
.subtitle { color: var(--text-dim); margin-top: 0; margin-bottom: 24px; }

/* ---------- Glass cards ---------- */

.card {
  background: var(--glass), var(--card-solid);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 20px;
  margin-bottom: 22px;
  box-shadow: var(--card-shadow);
  position: relative;
  overflow: hidden;
}
.card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(192, 132, 252, 0.55), rgba(34, 211, 238, 0.35), transparent);
  pointer-events: none;
}

/* ---------- Stats ---------- */

.stats-row-heading {
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-dim);
  margin: 0 0 12px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.stats-row-heading::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, rgba(168, 85, 247, 0.5), transparent);
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 16px;
  margin-bottom: 26px;
}
.stat {
  background: var(--glass), var(--card-solid);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 18px;
  text-align: center;
  box-shadow: var(--card-shadow);
  transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
.stat .num {
  font-size: 30px;
  font-weight: 800;
  color: var(--purple-light);
  text-shadow: 0 0 18px rgba(168, 85, 247, 0.5);
}
.stat .label { color: var(--text-dim); font-size: 13px; margin-top: 4px; }
.stat[role="button"], a.stat { cursor: pointer; }
.stat[role="button"]:hover, a.stat:hover {
  border-color: rgba(192, 132, 252, 0.65);
  transform: translateY(-2px);
  box-shadow: 0 10px 32px rgba(0, 0, 0, 0.5), var(--glow-purple);
}
.stat[role="button"]:focus-visible, a.stat:focus-visible { outline: 2px solid var(--neon); outline-offset: 2px; }

/* Dashboard quick-link tiles (roster / statistics / map) */
a.stat { display: block; text-decoration: none; }
a.stat:hover .num { filter: drop-shadow(0 0 10px rgba(168, 85, 247, 0.6)); }
.stat-icon { font-size: 26px; line-height: 1.2; }
.stat-go {
  margin-top: 10px;
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--purple-light);
  opacity: 0.8;
  transition: opacity 0.15s, text-shadow 0.15s;
}
a.stat:hover .stat-go { opacity: 1; text-shadow: 0 0 12px rgba(192, 132, 252, 0.8); }

/* ---------- Percentage rows (stats modals) ---------- */

.percent-row {
  display: grid;
  grid-template-columns: 110px 1fr 70px;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
}
.percent-row:not(:last-child) { border-bottom: 1px solid var(--border); }
.percent-bar-track {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border);
  border-radius: 6px;
  height: 12px;
  overflow: hidden;
}
.percent-bar-fill {
  height: 100%;
  border-radius: 6px;
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.25) inset;
  transition: width 0.3s ease;
}
.percent-value { text-align: right; font-size: 13px; color: var(--text-dim); }
.percent-value strong { color: var(--text); font-size: 14px; }

/* ---------- Leaderboards ---------- */

.leaderboard-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 4px;
}
.leaderboard-row:not(:last-child) { border-bottom: 1px solid var(--border); }
.leaderboard-rank {
  width: 22px;
  text-align: center;
  font-weight: 800;
  color: var(--text-dim);
  flex-shrink: 0;
}
.leaderboard-rank.is-top3 { color: var(--purple-light); text-shadow: var(--glow-purple); }
.leaderboard-name { flex: 1; min-width: 0; font-weight: 600; color: var(--text); }
.leaderboard-count { font-weight: 700; color: var(--purple-light); white-space: nowrap; }
.leaderboard-detail {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--text-dim);
}

/* ---------- Admin datasheet (datasheet.php) ---------- */

/* Two-up cards that fold to one column when the viewport can't hold both.
   The .card margin is dropped inside the grid so the gap alone sets the
   rhythm — otherwise the last row would carry an extra 22px underneath. */
.datasheet-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 18px;
  margin-bottom: 26px;
}
.datasheet-grid .card { margin-bottom: 0; }
.datasheet-card h2 { font-size: 16px; margin-bottom: 4px; }
.datasheet-note {
  color: var(--text-dim);
  font-size: 12px;
  line-height: 1.45;
  margin: 0 0 12px;
}

/* Six summary figures: the generic auto-fit grid strands the sixth on a
   row of its own, so these tracks stay fixed at 3×2 (2×3 on phones). */
.datasheet-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin-bottom: 26px;
}
.datasheet-stats .stat { padding: 16px; }
@media (max-width: 640px) {
  .datasheet-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The dock's raid total (Team Raids) sits on its own under the achievements,
   one full-width stat tile rather than a narrow one beside empty space. */
.datasheet-raid-stats {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  margin-bottom: 26px;
}
.datasheet-raid-stats .stat { padding: 16px; }

/* The single-person highlights (most recent debut / cover). */
.datasheet-spotlight {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 2px;
}
.datasheet-spotlight:not(:last-child) { border-bottom: 1px solid var(--border); }
.datasheet-spotlight-name { font-size: 19px; font-weight: 800; color: var(--text); }
.datasheet-spotlight-meta { color: var(--text-dim); font-size: 13px; margin-top: 2px; }

/* The split behind one person's drop count (same wording as the RRI
   tooltips, so a figure can be checked against what it's made of). */
.datasheet-split {
  display: block;
  color: var(--text-dim);
  font-size: 11.5px;
  font-weight: 500;
  margin-top: 2px;
}

/* Someone since removed from the roster: still ranked (the record is the
   record), just marked so the name can't be mistaken for a current member. */
.datasheet-removed {
  display: inline-block;
  margin-left: 7px;
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-dim);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  vertical-align: middle;
}

/* Achievements: appearance milestones and join anniversaries. Compact
   rows — these are lists to scan, so they skip the rank column the drop
   boards use and give the milestone its own pill on the right. */
.datasheet-milestone {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 2px;
}
.datasheet-milestone:not(:last-child) { border-bottom: 1px solid var(--border); }
.datasheet-milestone-body { flex: 1; min-width: 0; }
.datasheet-milestone-name {
  font-weight: 700;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.datasheet-badge {
  flex-shrink: 0;
  padding: 3px 10px;
  border: 1px solid rgba(168, 85, 247, 0.32);
  border-radius: 999px;
  background: rgba(168, 85, 247, 0.15);
  color: var(--text-dim);
  font-size: 11.5px;
  white-space: nowrap;
}
.datasheet-badge strong { color: var(--purple-light); font-size: 13px; margin-right: 2px; }

/* ---------- Roster grid ---------- */

.roster-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 16px;
}
.roster-card {
  position: relative;
  background: var(--glass), var(--card-solid);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 16px;
  cursor: pointer;
  box-shadow: var(--card-shadow);
  transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
.roster-card:hover {
  border-color: rgba(192, 132, 252, 0.6);
  transform: translateY(-2px);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5), var(--glow-purple);
}
.roster-card.is-inactive { opacity: 0.55; }
/* The artist's name links to their own page (/artist/[slug]); the card
   itself navigates there too, so the link stays visually part of the card. */
.roster-card-name a { color: inherit; text-decoration: none; }
.roster-card-name a:hover { color: var(--purple-light); }
.roster-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding-right: 20px;
}
.roster-card-head .avatar-thumb { margin-right: 0; }
.roster-card-name { font-size: 15px; font-weight: 700; color: var(--text); }
.roster-card-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}
.pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border: 1px solid currentColor;
  background: rgba(255, 255, 255, 0.03);
  white-space: nowrap;
}
.roster-card-meta { font-size: 13px; color: var(--text-dim); line-height: 1.7; }
.roster-card-meta strong { color: var(--text); }
.roster-card-delete {
  position: absolute;
  top: 12px;
  right: 12px;
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 13px;
  padding: 4px 6px;
  border-radius: 6px;
  line-height: 1;
}
.roster-card-delete:hover { color: var(--red); background: rgba(248, 113, 113, 0.12); }

/* ---------- Dashboard: Twitch embed ---------- */

.twitch-embed-card { padding: 14px; }
.twitch-embed-caption {
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
  margin: 2px 0 12px;
}
.twitch-embed-caption a { color: var(--purple-light); }
.twitch-embed-caption a:hover { text-shadow: 0 0 12px rgba(192, 132, 252, 0.8); }
#twitch-embed { display: flex; justify-content: center; }
#twitch-embed iframe { max-width: 100%; }

/* ---------- Dashboard: Live Now ---------- */

.live-now-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 14px;
}
.live-card {
  display: block;
  text-decoration: none;
  border-color: rgba(52, 211, 153, 0.4);
  box-shadow: var(--card-shadow), 0 0 16px rgba(52, 211, 153, 0.08);
  transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
.live-card:hover {
  border-color: rgba(52, 211, 153, 0.85);
  transform: translateY(-2px);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5), 0 0 22px rgba(52, 211, 153, 0.3);
}
.live-now-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 700;
  color: var(--green);
  margin-top: 2px;
}
/* Current stream title on a Live Now card (between name and the live label) */
.live-card-title {
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-dim);
  margin-top: 1px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
/* The member currently playing in the dashboard's stream embed */
.live-card.is-featured {
  border-color: var(--neon);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5), 0 0 22px rgba(168, 85, 247, 0.45);
}
.live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 8px rgba(52, 211, 153, 0.8);
  animation: pulse-live 1.5s infinite;
}

/* ---------- Roster view modal ---------- */

.view-modal-body {
  cursor: pointer;
  border-radius: 8px;
  transition: background 0.15s;
}
.view-modal-body:hover { background: rgba(168, 85, 247, 0.08); }
.info-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 11px 4px;
  border-bottom: 1px solid var(--border);
}
.info-row:last-of-type { border-bottom: none; }
.info-row .label { color: var(--text-dim); font-size: 13px; }

.view-modal-trains { padding-top: 4px; }
.view-modal-trains select {
  width: 100%;
  padding: 9px 10px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-size: 13px;
}

/* ---------- Tables ---------- */

table { width: 100%; border-collapse: collapse; }
.table-wrap {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.table-wrap table { min-width: 760px; }
th, td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
th {
  color: var(--text-dim);
  font-weight: 700;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}
tbody tr, table tr { transition: background 0.12s; }
table tr:hover td { background: rgba(168, 85, 247, 0.05); }
tr:last-child td { border-bottom: none; }

/* ---------- Buttons ---------- */

.btn {
  display: inline-block;
  background: linear-gradient(135deg, var(--purple) 0%, #9333ea 60%, var(--neon) 100%);
  color: #fff;
  border: none;
  padding: 9px 16px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
  box-shadow: 0 0 0 1px rgba(192, 132, 252, 0.35), var(--glow-purple);
  transition: box-shadow 0.15s, transform 0.15s, filter 0.15s;
}
.btn:hover {
  filter: brightness(1.15);
  transform: translateY(-1px);
  box-shadow: 0 0 0 1px rgba(192, 132, 252, 0.6), 0 0 20px rgba(168, 85, 247, 0.6);
}
.btn:active { transform: translateY(0); }

.btn-secondary {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-strong);
  color: var(--text);
  box-shadow: none;
}
.btn-secondary:hover {
  background: rgba(168, 85, 247, 0.12);
  border-color: rgba(192, 132, 252, 0.55);
  box-shadow: 0 0 14px rgba(168, 85, 247, 0.25);
  filter: none;
}

.btn-danger {
  background: rgba(248, 113, 113, 0.08);
  border: 1px solid rgba(248, 113, 113, 0.55);
  color: var(--red);
  box-shadow: none;
}
.btn-danger:hover {
  background: rgba(248, 113, 113, 0.85);
  border-color: var(--red);
  color: #180a0a;
  box-shadow: 0 0 16px rgba(248, 113, 113, 0.45);
  filter: none;
}
.btn-small { padding: 5px 10px; font-size: 12px; border-radius: 7px; }

form.inline { display: inline; }

/* ---------- Forms ---------- */

.form-row { margin-bottom: 14px; }
.form-row label { display: block; font-size: 13px; color: var(--text-dim); margin-bottom: 5px; }
.form-row input, .form-row select, .form-row textarea {
  width: 100%;
  padding: 9px 10px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-size: 14px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.form-row textarea { resize: vertical; min-height: 60px; }
.form-row input:focus, .form-row select:focus, .form-row textarea:focus {
  outline: none;
  border-color: var(--neon);
  box-shadow: 0 0 0 3px rgba(168, 85, 247, 0.18);
}
.form-row input::placeholder, .form-row textarea::placeholder { color: rgba(157, 154, 184, 0.6); }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
/* The 32 social-link inputs on the artist editor: a slightly tighter
   grid than the main fields so the section doesn't dwarf the form. */
.social-grid { gap: 12px 16px; }
.social-grid .form-row label { font-size: 12.5px; }

/* ---------- Badges ---------- */

.badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
}
.badge-planned { background: rgba(96, 165, 250, 0.14); color: var(--blue); }
.badge-progress { background: rgba(251, 191, 36, 0.14); color: var(--yellow); }
.badge-completed { background: rgba(52, 211, 153, 0.14); color: var(--green); }
.badge-cancelled { background: rgba(248, 113, 113, 0.14); color: var(--red); }
.badge-active { background: rgba(52, 211, 153, 0.14); color: var(--green); }
.badge-inactive { background: rgba(157, 154, 184, 0.14); color: var(--text-dim); }
.badge-emea { background: rgba(96, 165, 250, 0.14); color: var(--blue); }
.badge-apac { background: rgba(244, 114, 182, 0.14); color: var(--pink); }
.badge-amer { background: rgba(167, 139, 250, 0.16); color: #b7a3ff; }
.badge-cat-ds { background: rgba(157, 154, 184, 0.14); color: var(--text-dim); }
.badge-cat-primary { background: rgba(52, 211, 153, 0.14); color: var(--green); }
.badge-cat-secondary { background: rgba(251, 191, 36, 0.14); color: var(--yellow); }
.badge-cat-backup { background: rgba(248, 113, 113, 0.14); color: var(--red); }
.badge-cat-closed { background: rgba(157, 154, 184, 0.18); color: #d6d3e8; }
.badge-cat-admin { background: rgba(192, 132, 252, 0.16); color: var(--purple-light); }
.badge-cat-support { background: rgba(34, 211, 238, 0.15); color: var(--cyan); }
.badge-cover { background: rgba(96, 165, 250, 0.16); color: #7db8fb; }
.badge-risk-low { background: rgba(52, 211, 153, 0.14); color: var(--green); }
.badge-risk-medium { background: rgba(251, 191, 36, 0.14); color: var(--yellow); }
.badge-risk-high { background: rgba(248, 113, 113, 0.14); color: var(--red); }
.badge-raidpal { background: rgba(255, 138, 43, 0.16); color: #ffa053; }

/* ---------- Modals (dialog) ---------- */

dialog {
  background: linear-gradient(165deg, #171730, #101020);
  color: var(--text);
  border: 1px solid rgba(192, 132, 252, 0.3);
  border-radius: 16px;
  padding: 0;
  width: 100%;
  max-width: 560px;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.7), 0 0 40px rgba(168, 85, 247, 0.12);
}
dialog::backdrop {
  background: rgba(4, 4, 10, 0.7);
  backdrop-filter: blur(4px);
}
.modal-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 18px 22px;
  border-bottom: 1px solid var(--border);
}
.modal-head h2 { margin: 0; font-size: 18px; }
.modal-stats {
  padding: 12px 22px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 8px;
  background: rgba(168, 85, 247, 0.07);
}
.modal-close {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 20px;
  cursor: pointer;
  line-height: 1;
  transition: color 0.15s, text-shadow 0.15s;
}
.modal-close:hover { color: var(--text); text-shadow: 0 0 12px rgba(192, 132, 252, 0.9); }
.modal-body { padding: 20px 22px; overflow-x: auto; }
#typedConfirmInput {
  width: 100%;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-size: 14px;
}
#typedConfirmInput:focus { outline: none; border-color: var(--red); box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.18); }
#typedConfirmExpected { color: var(--text); font-family: monospace; }
.modal-foot {
  padding: 16px 22px;
  border-top: 1px solid var(--border);
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}

/* ---------- Flash messages ---------- */

.flash {
  padding: 12px 16px;
  border-radius: 10px;
  margin-bottom: 20px;
  font-size: 14px;
  font-weight: 600;
}
.flash-success {
  background: rgba(52, 211, 153, 0.1);
  border: 1px solid rgba(52, 211, 153, 0.55);
  color: var(--green);
  box-shadow: 0 0 18px rgba(52, 211, 153, 0.12);
}
.flash-error {
  background: rgba(248, 113, 113, 0.1);
  border: 1px solid rgba(248, 113, 113, 0.55);
  color: var(--red);
  box-shadow: 0 0 18px rgba(248, 113, 113, 0.12);
}

.empty { color: var(--text-dim); font-style: italic; padding: 10px 0; }

.section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 14px;
  gap: 12px;
  flex-wrap: wrap;
}

/* ---------- Raid order / live tracking ---------- */

.stop-order {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--purple), var(--neon));
  color: #fff;
  font-weight: 800;
  font-size: 13px;
  margin-right: 10px;
  box-shadow: 0 0 10px rgba(168, 85, 247, 0.5);
}

.drag-handle {
  cursor: grab;
  color: var(--text-dim);
  font-size: 16px;
  text-align: center;
  user-select: none;
  width: 28px;
}
tr[draggable="true"]:active { cursor: grabbing; }
tr[draggable="true"].dragging { opacity: 0.4; background: var(--bg-alt); }
tr[draggable="true"] { transition: background 0.1s; }

tr.is-live-stop { background: rgba(248, 113, 113, 0.08); }
tr.is-live-stop:hover td { background: rgba(248, 113, 113, 0.08); }
tr.is-live-stop .stop-order { background: linear-gradient(135deg, #ef4444, var(--red)); box-shadow: 0 0 12px rgba(248, 113, 113, 0.6); }
tr.is-next-stop { background: rgba(52, 211, 153, 0.06); }
.live-indicator {
  display: inline-block;
  margin-left: 6px;
  font-size: 11px;
  font-weight: 700;
  vertical-align: middle;
}
tr.is-live-stop .live-indicator { color: var(--red); animation: pulse-live 1.5s infinite; }
tr.is-next-stop .live-indicator { color: var(--green); }
@keyframes pulse-live {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* ---------- Misc ---------- */

.muted { color: var(--text-dim); }
a { color: var(--purple-light); }
a:hover { text-shadow: 0 0 12px rgba(192, 132, 252, 0.7); }

.actions-cell { white-space: nowrap; }

/* ---------- Avatars ---------- */

.avatar-thumb {
  display: inline-block;
  border-radius: 50%;
  object-fit: cover;
  vertical-align: middle;
  margin-right: 8px;
  flex-shrink: 0;
  border: 1px solid rgba(192, 132, 252, 0.35);
  box-shadow: 0 0 8px rgba(168, 85, 247, 0.25);
}
.avatar-placeholder {
  text-align: center;
  color: #fff;
  font-weight: 700;
}
.avatar-upload-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 16px;
}
.avatar-preview-wrap { position: relative; }
.avatar-upload-controls { display: flex; flex-direction: column; gap: 6px; }
.avatar-upload-controls input[type="file"] { font-size: 13px; }
.avatar-remove-label { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-dim); }

/* ---------- Public raid train preview hero ---------- */

.preview-hero {
  background:
    radial-gradient(500px 220px at 8% 0%, rgba(168, 85, 247, 0.18), transparent 65%),
    radial-gradient(420px 200px at 95% 10%, rgba(34, 211, 238, 0.12), transparent 60%),
    linear-gradient(160deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.01)), var(--card-solid);
  border: 1px solid rgba(192, 132, 252, 0.35);
  border-radius: 16px;
  padding: 28px 28px 24px;
  margin-bottom: 22px;
  box-shadow: var(--card-shadow), 0 0 44px rgba(168, 85, 247, 0.1);
  position: relative;
  overflow: hidden;
}
.preview-hero::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--neon), var(--cyan), transparent);
  opacity: 0.9;
}
.preview-hero-top { display: flex; gap: 8px; margin-bottom: 10px; }
.preview-title { font-size: 30px; margin: 0 0 4px; }
.preview-date { color: var(--text-dim); margin: 0 0 18px; font-size: 15px; }
.preview-progress-wrap { display: flex; align-items: center; gap: 12px; max-width: 420px; }
.preview-progress-track {
  flex: 1;
  height: 8px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.preview-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--purple), var(--neon), var(--cyan));
  border-radius: 6px;
  box-shadow: 0 0 12px rgba(168, 85, 247, 0.7);
  transition: width 0.3s ease;
}

tr.is-done-stop { opacity: 0.55; }

/* ---------- Team map (Leaflet) — dark-theme overrides ---------- */

.leaflet-popup-content-wrapper {
  background: var(--card-solid);
  color: var(--text);
  border: 1px solid rgba(192, 132, 252, 0.35);
  border-radius: 10px;
  box-shadow: 0 10px 32px rgba(0, 0, 0, 0.55), 0 0 20px rgba(168, 85, 247, 0.15);
}
.leaflet-popup-tip { background: var(--card-solid); border: 1px solid rgba(192, 132, 252, 0.35); }
.leaflet-popup-content { font-size: 13px; margin: 12px 14px; }
.leaflet-container a.leaflet-popup-close-button { color: var(--text-dim); }
.leaflet-control-attribution { background: rgba(13, 13, 26, 0.85) !important; color: var(--text-dim) !important; }
.leaflet-control-attribution a { color: var(--text-dim) !important; }

/* Dark-mode look for standard (light-colored) OpenStreetMap tiles, applied
   client-side via CSS rather than depending on a specific dark tile
   provider staying free/keyless — see map.php. Markers/popups are excluded
   automatically since this only targets the tile layer's own pane. */
.map-dark-tiles {
  filter: invert(1) hue-rotate(180deg) brightness(0.92) contrast(0.9) saturate(0.9);
}

/* ---------- Green halo for a roster member currently live on Twitch ---------- */

.avatar-thumb.is-live-on-twitch {
  box-shadow: 0 0 0 3px var(--green), 0 0 14px 3px rgba(52, 211, 153, 0.65);
  animation: pulse-live-halo 2s ease-in-out infinite;
}
@keyframes pulse-live-halo {
  0%, 100% { box-shadow: 0 0 0 3px var(--green), 0 0 14px 3px rgba(52, 211, 153, 0.65); }
  50% { box-shadow: 0 0 0 3px var(--green), 0 0 20px 5px rgba(52, 211, 153, 0.35); }
}

/* ---------- News / blog ---------- */

/* A narrower reading column than the app's 1100px shell, centered. */
.news-item, .news-post, .news-not-found, .artist-hero, .artist-embed-card, .artist-stats, .artist-tabs, .artist-edit-stats, .artist-trains-card, .not-found-card {
  max-width: 860px;
  margin-left: auto;
  margin-right: auto;
}

.news-item-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}
.news-item-head h2 { margin-bottom: 0; font-size: 21px; }
.news-item-head h2 a { color: var(--text); text-decoration: none; }
.news-item-head h2 a:hover { color: var(--purple-light); }
.news-item-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  white-space: nowrap;
}
.news-item-date { color: var(--text-dim); font-size: 13px; }
.news-item-excerpt {
  color: var(--text-dim);
  line-height: 1.6;
  margin: 10px 0 16px;
}
.news-item-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.news-post-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}
.news-post-title { font-size: 30px; margin-bottom: 20px; line-height: 1.2; }

.news-post-body { font-size: 15.5px; line-height: 1.7; }
.news-post-body p { margin: 0 0 18px; }
.news-post-body p:last-child { margin-bottom: 0; }
.news-post-body h2 { font-size: 22px; margin: 30px 0 12px; }
.news-post-body h3 { font-size: 18px; margin: 26px 0 10px; }
.news-post-body ul { margin: 0 0 18px; padding-left: 24px; }
.news-post-body li { margin-bottom: 7px; }
.news-post-body a { color: var(--purple-light); }
.news-post-body strong { color: var(--text); }

/* Credits page: includes/credits.php puts a roster artist's avatar inline
   just before their name. It sits a little below the text baseline so the
   name's first line stays visually aligned with the circle. */
.credits-body .avatar-thumb { vertical-align: -7px; }

.news-post-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  max-width: 860px;
  margin: -8px auto 0;
}

/* Editor: the slash-prefixed hint under the body field. */
.form-hint { color: var(--text-dim); font-size: 12.5px; line-height: 1.6; margin: 7px 0 0; }
.form-hint code { background: rgba(255, 255, 255, 0.06); border: 1px solid var(--border); border-radius: 5px; padding: 1px 5px; font-size: 12px; }
.form-hint strong { color: var(--text); font-weight: 600; }

/* Editor: published/draft choice. */
.news-status-options { display: flex; flex-direction: column; gap: 8px; }
.form-row label.news-status-option {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 14px;
  color: var(--text);
  margin-bottom: 0;
  cursor: pointer;
}
.form-row label.news-status-option input { width: auto; }

.badge-draft { background: rgba(251, 191, 36, 0.14); color: var(--yellow); }

/* ---------- Artist page (/artist/[artist-name]) ---------- */

/* Hero header — same gradient treatment as the raid train preview hero. */
.artist-hero {
  background:
    radial-gradient(500px 220px at 8% 0%, rgba(168, 85, 247, 0.18), transparent 65%),
    radial-gradient(420px 200px at 95% 10%, rgba(34, 211, 238, 0.12), transparent 60%),
    linear-gradient(160deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.01)), var(--card-solid);
  border: 1px solid rgba(192, 132, 252, 0.35);
  border-radius: 16px;
  padding: 24px 28px;
  margin-bottom: 22px;
  box-shadow: var(--card-shadow), 0 0 44px rgba(168, 85, 247, 0.1);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}
.artist-hero-main { display: flex; align-items: center; gap: 18px; min-width: 0; }
.artist-hero-id h1 {
  font-size: 30px;
  margin: 0 0 10px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.artist-hero-meta { margin: 10px 0 0; color: var(--text-dim); font-size: 13px; }
/* Small social/website icons under the role/join-date line — one per
   platform the artist has a link for (monochrome white SVGs in
   assets/icons/, so they read on the gradient whatever the brand color). */
.artist-social {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}
.artist-social a {
  display: inline-flex;
  opacity: 0.72;
  transition: opacity 0.12s, transform 0.12s;
}
.artist-social a:hover { opacity: 1; transform: translateY(-1px); }
.artist-social a:focus-visible { opacity: 1; outline: 2px solid var(--purple); outline-offset: 2px; border-radius: 4px; }
.artist-social img { display: block; width: 18px; height: 18px; }
.artist-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.artist-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Twitch embed of the artist's own channel — the script sizes the iframe
   fixed, so force it to fill the card at 16:9 instead. */
#artist-twitch-embed { display: flex; justify-content: center; }
.artist-embed-card #artist-twitch-embed iframe {
  width: 100% !important;
  height: auto !important;
  aspect-ratio: 16 / 9;
}

/* Statistics strip: six cells on one line, wrapping only when the screen
   can't hold all six. */
.artist-stats {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 10px;
}
.artist-stat {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 6px;
  text-align: center;
  min-width: 0;
}
.artist-stat .label {
  display: block;
  color: var(--text-dim);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 5px;
}
.artist-stat strong {
  display: block;
  font-size: 18px;
  color: var(--text);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.artist-stat .sub { display: block; font-size: 11px; color: var(--text-dim); margin-top: 2px; }

@media (max-width: 899px) {
  .artist-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .artist-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .artist-hero { padding: 20px 18px; }
  .artist-hero-id h1 { font-size: 24px; }
}

/* Read-only stats strip on the artist editor page — the figures the old
   edit modal's header used to show, as a card of their own. */
.artist-edit-stats {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 13px;
  color: var(--text);
}

/* Tabbed section under the statistics. Only the Bio tab is rendered to
   start with; the other four appear once the flag at the top of
   artist_view.php is flipped. */
.artist-tablist {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin: -4px 0 16px;
}
.artist-tab-btn {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 8px 8px 0 0;
  color: var(--text-dim);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 9px 15px;
  margin-bottom: -1px;
  cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.artist-tab-btn:hover { color: var(--text); background: rgba(255, 255, 255, 0.04); }
.artist-tab-btn[aria-selected="true"] {
  color: var(--purple-light);
  border-bottom-color: var(--purple);
}
.artist-tab-btn:focus-visible { outline: 2px solid var(--purple); outline-offset: -2px; }
.artist-tab-panel { min-width: 0; }
.artist-tab-panel:focus-visible { outline: 2px solid var(--purple); outline-offset: 2px; }
.artist-tab-panel .muted { font-size: 13.5px; margin: 0; }
/* Bio reuses the News post typography (news-post-body) — just trim the
   first/last block margins so it sits cleanly inside the card. */
.artist-bio > :first-child { margin-top: 0; }
.artist-bio > :last-child { margin-bottom: 0; }

/* Embed players in the Sets / Video / Music tabs: one per filled-in link,
   stacked. Videos keep a responsive 16:9 box; audio players take the
   height their platform's widget is built for; links that can't embed
   render as a dashed "open on …" card instead. */
.artist-embed-stack { display: flex; flex-direction: column; gap: 16px; }
.artist-embed-stack iframe {
  display: block;
  width: 100%;
  border: 0;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.35);
}
.artist-embed-16x9 { position: relative; aspect-ratio: 16 / 9; }
.artist-embed-16x9 iframe { position: absolute; inset: 0; height: 100%; }
.artist-embed-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  border: 1px dashed var(--border);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--purple-light);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.12s, border-color 0.12s;
}
.artist-embed-fallback:hover { background: rgba(168, 85, 247, 0.08); border-color: var(--purple); }

.artist-trains-wrap { margin-top: 0; }
.artist-trains-wrap > .label { color: var(--text-dim); font-size: 13px; display: block; margin-bottom: 4px; }
.artist-trains { list-style: none; margin: 0; padding: 0; }
.artist-trains li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
  padding: 10px 4px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.artist-trains li:last-child { border-bottom: none; }
.artist-trains .muted { font-size: 12.5px; white-space: nowrap; }

/* ---------- Post-raid report (raid_report.php) ---------- */

/* The report is deliberately a reading page: a full-width title/date hero,
   three equal summary lists, a 2/3 + 1/3 split for the lineup and the dock
   raids, then two half-width runner cards. Every grid folds to one column
   below 900px so the same markup works on a phone. */

.report-hero {
  padding: 26px 28px;
  background:
    radial-gradient(760px 240px at 12% 0%, rgba(124, 58, 237, 0.24), transparent 62%),
    radial-gradient(600px 200px at 92% 100%, rgba(34, 211, 238, 0.12), transparent 65%),
    var(--glass), var(--card-solid);
}
.report-hero-top,
.report-hero-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
.report-kicker {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cyan);
}
.report-title {
  font-size: clamp(30px, 5vw, 46px);
  line-height: 1.08;
  margin: 12px 0 6px;
}
.report-date {
  font-size: 18px;
  color: var(--text);
  margin: 0;
}
.report-hero-foot { margin-top: 22px; }
.report-hero-meta {
  margin: 0;
  color: var(--text-dim);
  font-size: 13px;
}
.report-live-note {
  display: block;
  margin-top: 4px;
  color: var(--yellow);
  font-size: 12px;
}
.report-hero-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.report-lists {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
  margin-bottom: 22px;
}
.report-split {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 18px;
  margin-bottom: 22px;
}
.report-halves {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}
.report-lists .card,
.report-split .card,
.report-halves .card { margin-bottom: 0; height: 100%; }

.report-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.report-card-head h2 { margin: 0; font-size: 17px; }
.report-count {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 700;
  color: var(--purple-light);
}
.report-note {
  color: var(--text-dim);
  font-size: 12px;
  line-height: 1.45;
  margin: 4px 0 12px;
}

.report-list { list-style: none; margin: 0; padding: 0; }
.report-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
}
.report-list li:last-child { border-bottom: none; }
.report-list-main { flex: 1; min-width: 0; }
.report-list-name { font-weight: 600; line-height: 1.35; }
.report-list-meta {
  display: block;
  color: var(--text-dim);
  font-size: 11.5px;
  line-height: 1.45;
  margin-top: 3px;
}
.report-list-meta .badge { margin: 0 4px 2px 0; }
.report-lineup-order {
  flex-shrink: 0;
  width: 22px;
  text-align: center;
  font-weight: 800;
  font-size: 13px;
  color: var(--text-dim);
}
.report-lineup-badges {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: 45%;
}
.report-ba {
  flex-shrink: 0;
  padding: 3px 8px;
  border: 1px solid rgba(34, 211, 238, 0.35);
  border-radius: 999px;
  background: rgba(34, 211, 238, 0.08);
  color: var(--cyan);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.02em;
  white-space: nowrap;
  cursor: help;
}
.report-ba-note { margin-top: -4px; }
.report-ba-note strong { color: var(--cyan); }

.report-runner-card { display: flex; flex-direction: column; }
.report-runner-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
.report-runner-title {
  margin: 0;
  font-size: 19px;
}
.report-runner-window {
  margin: 3px 0 0;
  color: var(--text-dim);
  font-size: 12px;
}
.report-runner-main {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 18px 0 6px;
}
.report-runner-name {
  font-size: 21px;
  font-weight: 800;
  line-height: 1.2;
}
.report-runner-evidence {
  margin: 5px 0 0;
  color: var(--text-dim);
  font-size: 12.5px;
  line-height: 1.45;
}
.report-subrunner {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.report-subrunner-label {
  flex-basis: 100%;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.report-subrunner-name { font-weight: 700; }
.report-subrunner .report-list-meta { display: inline; margin: 0; }

@media (max-width: 900px) {
  .report-lists,
  .report-split,
  .report-halves { grid-template-columns: minmax(0, 1fr); }
  .report-lineup-badges { max-width: none; }
}

/* ---------- Site footer ---------- */

/* Rendered by includes/footer.php under the content on every page. It runs
   the full width of the window (no max-width cap), with the copyright
   notice and the Credits link sitting side by side on the left. */
.site-footer {
  width: 100%;
  border-top: 1px solid var(--border);
  margin-top: 8px;
  padding: 16px 24px 30px;
  box-sizing: border-box;
}
.site-footer-inner {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 18px;
  flex-wrap: wrap;
  width: 100%;
  font-size: 13px;
}
.site-footer-copy { color: var(--text-dim); }
.site-footer-links { display: flex; gap: 18px; }
.site-footer-links a { color: var(--text-dim); text-decoration: none; }
.site-footer-links a:hover { color: var(--text); text-decoration: underline; }

/* ---------- Flyer page (3:4 poster) ---------- */

/* flyer.php renders a fixed 1080x1440 canvas — exactly 3:4 — and scales
   it to the viewport with a CSS transform (--s, set by the page's inline
   script), so the poster's proportions are identical on every screen and
   a screenshot of it is always 3:4. Everything inside is designed in
   canvas pixels; nothing inside reflows with the browser window. */

body.flyer-body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 16px 16px 28px;
}

/* Controls sit above the canvas, never on it. */
.flyer-toolbar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
}
.flyer-toolbar select {
  background: rgba(255, 255, 255, 0.05);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 13px;
  font-weight: 600;
  max-width: min(58vw, 440px);
}
.flyer-toolbar select option { background: var(--card-solid); color: var(--text); }
.flyer-chip {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 12px;
}

/* Stage reserves the scaled footprint; the canvas itself is transformed
   out of flow, so no scrollbars appear around the oversized design. */
.flyer-stage {
  position: relative;
  flex-shrink: 0;
  width: calc(1080px * var(--s, 0.5));
  height: calc(1440px * var(--s, 0.5));
}

.flyer {
  position: absolute;
  top: 0;
  left: 0;
  box-sizing: border-box;
  width: 1080px;
  height: 1440px; /* 3:4 exactly */
  transform: scale(var(--s, 0.5));
  transform-origin: top left;
  display: flex;
  flex-direction: column;
  padding: 56px 64px 44px;
  overflow: hidden;
  border-radius: 26px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: #fff;
  background:
    radial-gradient(760px 460px at 12% -6%, var(--accent-glow, rgba(124, 58, 237, 0.4)), transparent 62%),
    radial-gradient(900px 640px at 108% 106%, rgba(124, 58, 237, 0.38), transparent 62%),
    linear-gradient(170deg, #12122a 0%, #0a0a16 55%, #0c0a18 100%);
  box-shadow: 0 34px 90px rgba(0, 0, 0, 0.65), 0 0 70px var(--accent-soft, rgba(124, 58, 237, 0.16));
}

/* Thin inner frame. A real element rather than .flyer::after so the PNG
   export (which clones the DOM, not pseudo-elements) keeps it. */
.flyer-frame {
  position: absolute;
  inset: 18px;
  border: 1px solid var(--accent-soft, rgba(124, 58, 237, 0.2));
  border-radius: 16px;
  pointer-events: none;
}

/* Compact header band: logo on the left third, train title (with the
   date under it) on the right two thirds — kept small so the lineup
   below gets the rest of the canvas and can use large, readable type. */
.flyer-head {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
}
.flyer-head-row {
  display: flex;
  align-items: center;
  gap: 30px;
}
.flyer-head-brand {
  flex: 0 0 31%;
  display: flex;
  justify-content: center;
}
.flyer-logo {
  width: 210px;
  height: 210px;
  object-fit: contain;
  filter: drop-shadow(0 0 36px var(--accent-mid, rgba(124, 58, 237, 0.34)));
}
.flyer-head-main {
  flex: 1 1 auto;
  min-width: 0;
  text-align: left;
}
.flyer-title {
  font-size: var(--title-fs, 54px);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: 0.01em;
  margin: 18px 0 0;
  /* Room inside the line-clamp box for descenders (the tail of a "y" in
     "Saturday" otherwise gets clipped by the box edge right above the
     date); counts toward the clamped height, budgeted in flyer.php. */
  padding: 4px 0 14px;
  max-width: 100%;
  overflow-wrap: anywhere;
  /* Hard cap at two lines so a very long train name can never push the
     lineup off the canvas; only pathological titles get an ellipsis. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-wrap: balance;
  background: linear-gradient(180deg, #ffffff 35%, var(--accent-bright, #c084fc));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.flyer-date {
  font-size: 42px; /* under the title in its own column — still large */
  line-height: 1.15; /* pinned: flyer.php computes the lineup budget from this */
  font-weight: 800;
  letter-spacing: -0.01em;
  margin-top: 12px;
  color: #fff;
  text-shadow: 0 0 30px rgba(255, 255, 255, 0.18);
}
/* Sits UNDER the lineup with the website, small per spec. */
.flyer-bottom {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.flyer-divider {
  flex: 0 0 auto;
  align-self: stretch;
  height: 3px;
  border-radius: 2px;
  margin: 16px 0 0;
  background: linear-gradient(90deg, transparent, var(--accent-bright, #c084fc), transparent);
  opacity: 0.85;
}

/* One column of rows: avatar, name, set time — one per artist. */
.flyer-lineup {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
}
.flyer-stop {
  flex: 0 0 auto;
  height: var(--row-h, 40px);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 16px 0 14px;
  border-radius: 14px;
  border-left: 4px solid var(--accent-bright, #c084fc);
  background: linear-gradient(90deg, var(--accent-soft, rgba(124, 58, 237, 0.16)), rgba(255, 255, 255, 0.035));
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  border-right: 1px solid rgba(255, 255, 255, 0.06);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.flyer-stop.is-open { opacity: 0.7; }
.flyer-stop .avatar-thumb {
  margin-right: 0;
  border-color: var(--accent-bright, #c084fc);
  box-shadow: 0 0 10px var(--accent-mid, rgba(124, 58, 237, 0.34));
}
/* Unify the initials placeholders to the flyer's accent instead of one
   random hue per artist (the inline style sets it, hence !important). */
.flyer .avatar-placeholder {
  background: linear-gradient(160deg, var(--accent-mid, rgba(124, 58, 237, 0.34)), rgba(0, 0, 0, 0.35)) !important;
}
.flyer-stop-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--stop-fs, 20px);
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: 0.01em;
}
.flyer-stop.is-open .flyer-stop-name { color: var(--text-dim); }
/* Debut pill: this DJ's first-ever set (flyer.php computes it from the
   stop's history across non-cancelled trains). Hollow, so it can never be
   mistaken for one of the time chips, and a real element — not a pseudo —
   so the PNG export keeps it. It sits between the name and the time
   columns: the name flexes down to make room, while the fixed-width time
   columns (and therefore the zone-header alignment) don't move. */
.flyer-stop-debut {
  flex: 0 0 auto;
  font-size: calc(var(--stop-fs, 20px) * 0.55);
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-bright, #c084fc);
  background: var(--accent-soft, rgba(124, 58, 237, 0.16));
  border: 1px solid var(--accent-mid, rgba(124, 58, 237, 0.34));
  border-radius: 999px;
  line-height: 1;
  /* letter-spacing trails past the last letter — pad the left a touch
     more so the word looks centred in the pill */
  padding: 0.34em 0.62em 0.32em 0.78em;
  white-space: nowrap;
}
.flyer-stop-time {
  flex: 0 0 auto;
  font-size: calc(var(--stop-fs, 20px) * 1.05);
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--accent-bright, #c084fc);
  background: var(--accent-mid, rgba(124, 58, 237, 0.34));
  border: 1px solid var(--accent-soft, rgba(124, 58, 237, 0.16));
  border-radius: 999px;
  line-height: 1;
  padding: 5px 15px;
  white-space: nowrap;
}

/* ---- Five-zone time conversions ----------------------------------------
   Each row shows its set time in five zones: AEST/AEDT and CET/CEST left
   of the home BST/GMT pill, EDT/EST and PDT/PST right of it. The header
   row above the lineup names the zones (season-picked by flyer.php) and
   MUST stay aligned with the row cells — both use --tz-col-w and the same
   6px gap, and both containers end at the rows' right padding. */
.flyer-stop-tzs,
.flyer-tz-labels {
  --tz-col-w: min(calc(var(--stop-fs, 20px) * 4.2), 100px);
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
}
.flyer-tz-time {
  flex: 0 0 auto;
  width: var(--tz-col-w);
  text-align: center;
  font-size: calc(var(--stop-fs, 20px) * 0.92);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.72);
  line-height: 1;
  white-space: nowrap;
}
/* "+1" on a conversion that rolls past midnight in its zone — only
   rendered outside the AEST column (flyer.php), where it would push the
   time off-centre. Accent colour + superscript so it reads as an
   annotation, not part of the time. */
.flyer-tz-next {
  font-size: 0.6em;
  font-weight: 800;
  margin-left: 1px;
  color: var(--accent-bright, #c084fc);
  vertical-align: super;
  line-height: 0;
}
.flyer-stop-tzs .flyer-stop-time {
  width: var(--tz-col-w);
  text-align: center;
  box-sizing: border-box;
  padding: 5px 0;
}

/* "Artists" + zone-abbreviation header sitting directly above the lineup;
   its padding mirrors the rows' (4px border-left + 14px pad) minus the
   border, so the labels land exactly over the time columns. */
.flyer-lineup-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 16px 0 18px;
}
.flyer-lineup-label {
  flex: 1 1 auto;
  min-width: 0;
  font-size: calc(var(--stop-fs, 20px) * 1.1);
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
}
.flyer-tz-labels span {
  flex: 0 0 auto;
  width: var(--tz-col-w);
  text-align: center;
  font-size: calc(var(--stop-fs, 20px) * 0.72);
  font-weight: 800;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.55);
  white-space: nowrap;
}
/* The home (BST/GMT) column reads brighter so the eye finds its own time. */
.flyer-tz-labels span.is-home {
  color: var(--accent-bright, #c084fc);
}
.flyer-empty {
  margin: auto;
  font-size: 40px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.55);
  text-align: center;
}

.flyer-foot {
  flex: 0 0 auto;
  margin-top: 14px;
  text-align: center;
  font-size: 20px; /* deliberately small — the website along the bottom */
  line-height: 1.3; /* pinned: flyer.php computes the lineup budget from this */
  font-weight: 600;
  letter-spacing: 0.14em;
}
.flyer-foot a {
  color: rgba(255, 255, 255, 0.58);
  text-decoration: none;
}
.flyer-foot a:hover { color: var(--accent-bright, #c084fc); text-shadow: none; }

/* ============================================================
   Flyer — the standard "banner" layout (flyer.php)
   ?layout=classic selects the older compact poster
   ============================================================ */

/* The standard arrangement: the title becomes an oversized hero
   with the date and a dot-separated tagline beneath it, the lineup gets a
   "DJ Lineup" banner, and each stop is a numbered chevron plate whose five
   timezone times sit in fixed columns UNDER the artist's name. The zones
   are named once, in a label row above the plates — the same point the
   classic layout puts them — so each value is a bare time and the width
   that frees up goes into larger type.

   The reference poster for this layout sets its panels over a photographic
   background; this variant deliberately has none — the canvas keeps the
   app's own gradient. Every flourish here is a REAL element (the dot
   separators, the banner rules, the chevron plates, the number badges)
   rather than a ::before/::after, because the PNG export clones the DOM
   and would drop pseudo-elements. */

.flyer-coast {
  /* The even rows borrow the app's cyan, so the lineup alternates the logo
     accent with cyan down the poster (the reference's two-colour rhythm,
     in TSST colours). */
  --coast-alt: #0e7490;
  --coast-alt-bright: #22d3ee;
  --coast-gap: 10px;
  /* The number badge's width and digit size come from flyer.php, which
     knows whether this train's ladder runs into two digits — every badge
     takes the same width either way, so the plates stay aligned. */
  --coast-num-w: 60px;
  --coast-num-fs: 30px;
  /* Plate metrics, named so the zone-label row above the plates can mirror
     them exactly — that's what lands its abbreviations over the columns of
     times below. Each of these is also a term in flyer.php's width sums. */
  --coast-row-gap: 10px;   /* number badge ↔ plate */
  --coast-plate-pad: 2px;  /* the plate's own accent border */
  --coast-plate-gap: 16px; /* avatar ↔ text inside the plate */
  --coast-plate-pl: 12px;
  --coast-plate-pr: 34px;
  /* Set times, and the zone labels over them, both sized by flyer.php: with
     the labels out of the values, the width they free goes into the type. */
  --coast-time-fs: 18px;
  --coast-time-gap: 12px;
  --coast-col-w: auto;     /* the compact shape pins one column's width */
  --coast-label-fs: 12px;
  padding: 38px 46px 28px;
}

/* ---- Hero: logo, oversized title, date, tagline ---- */

.coast-head {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.coast-logo {
  width: var(--coast-logo, 128px);
  height: var(--coast-logo, 128px);
  object-fit: contain;
  filter: drop-shadow(0 0 30px var(--accent-mid, rgba(124, 58, 237, 0.34)));
}
.coast-title {
  margin: 4px 0 0;
  font-size: var(--title-fs, 64px);
  font-weight: 900;
  font-style: italic;
  line-height: 1;
  letter-spacing: -0.005em;
  max-width: 100%;
  overflow-wrap: anywhere;
  /* Two lines max, exactly like the classic layout, so a long train name
     can never push the lineup off the canvas. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-wrap: balance;
  background: linear-gradient(180deg, #ffffff 32%, var(--accent-bright, #c084fc));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.coast-date {
  margin-top: 10px;
  font-size: 30px;
  font-weight: 800;
  font-style: italic;
  letter-spacing: 0.04em;
  line-height: 1.1;
  color: #fff;
  text-shadow: 0 0 26px rgba(255, 255, 255, 0.2);
}
/* The reference's "ONE SIGNAL • TWO COASTS • A WHOLE COMMUNITY" rule. */
.coast-tagline {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-top: 12px;
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.66);
  white-space: nowrap;
}
.coast-tagline i {
  flex: 0 0 auto;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent-bright, #c084fc);
  box-shadow: 0 0 8px var(--accent-glow, rgba(124, 58, 237, 0.42));
}

/* ---- "DJ Lineup" banner between the hero and the rows ---- */

.coast-lineup-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 18px 0 10px;
}
.coast-lineup-label {
  font-size: 34px;
  font-weight: 900;
  font-style: italic;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1;
  color: #fff;
  text-shadow: 0 0 24px var(--accent-glow, rgba(124, 58, 237, 0.42));
}
/* The short accent strokes flanking the banner text. */
.coast-rule {
  flex: 1 1 auto;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--accent-bright, #c084fc));
}
.coast-rule:last-child {
  background: linear-gradient(90deg, var(--accent-bright, #c084fc), transparent);
}

/* ---- The five zones, named once over the columns ---- */

/* The same point the classic layout puts them: a header row directly above
   the times. Every spacer in here mirrors a plate's own metric — the
   number badge's width, the avatar's size, the plate's padding and its
   gaps — which is what lands the abbreviations exactly over the values
   beneath them, in both the stacked and the compact shape. */
.coast-tz-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--coast-row-gap, 10px);
  margin: 0 0 6px;
}
.coast-tz-badge { flex: 0 0 var(--coast-num-w, 60px); }
.coast-tz-plate {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--coast-plate-pad, 2px);
}
.coast-tz-in {
  display: flex;
  align-items: center;
  gap: var(--coast-plate-gap, 16px);
  padding: 0 var(--coast-plate-pr, 34px) 0 var(--coast-plate-pl, 12px);
  font-size: var(--coast-label-fs, 12px);
  line-height: 1.2; /* pinned: flyer.php budgets this row's height */
}
/* Stands in for a plate's avatar. (Its height doesn't matter — it only has
   to be as wide as the avatar it replaces.) */
.coast-tz-avatar { flex: 0 0 var(--avatar, 40px); }
/* Stands in for the name when the compact rows put it beside the times. */
.coast-tz-name { display: none; flex: 1 1 auto; }
.coast-tz-grid {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  /* The rows' own track list, so the labels land on the columns. */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--coast-time-gap, 12px);
}
.coast-tz-grid span {
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
  white-space: nowrap;
  overflow: hidden;
}
/* The home (BST/GMT) column reads brighter, exactly as it does in the
   classic layout's header. */
.coast-tz-grid span.is-home { color: var(--accent-bright, #c084fc); }

/* ---- Rows: number badge + chevron plate ---- */

.coast-lineup {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--coast-gap);
}
.coast-stop {
  flex: 0 0 auto;
  height: var(--row-h, 40px);
  display: flex;
  align-items: center;
  gap: var(--coast-row-gap, 10px);
  /* Odd rows wear the logo accent, even rows the app's cyan. */
  --coast-row-accent: var(--accent, #7c3aed);
  --coast-row-bright: var(--accent-bright, #c084fc);
}
.coast-stop:nth-child(even) {
  --coast-row-accent: var(--coast-alt);
  --coast-row-bright: var(--coast-alt-bright);
}
.coast-stop.is-open { opacity: 0.72; }

/* The brush-chevron number: a solid block with the digit knocked out in
   the canvas's dark, pointing right the way the reference poster's
   hand-painted numerals do. */
.coast-num {
  flex: 0 0 auto;
  width: var(--coast-num-w);
  height: calc(var(--row-h, 40px) - 6px);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--coast-num-fs, 30px);
  font-weight: 900;
  font-style: italic;
  line-height: 1;
  color: #080812;
  background: linear-gradient(140deg, var(--coast-row-bright), var(--coast-row-accent));
  clip-path: polygon(0 0, 100% 6%, 72% 50%, 100% 94%, 0 100%);
  /* Centre the digit in the solid part of the chevron, left of the pointed
     tail — where the reference poster paints its numbers. */
  padding-right: calc(var(--coast-num-w, 60px) * 0.28);
  box-shadow: 0 0 18px var(--accent-soft, rgba(124, 58, 237, 0.16));
}

/* Two layers, because a clip-path would cut a real border off: the outer
   plate is the accent outline, the inner one the dark panel that leaves
   2px of it showing. */
.coast-plate {
  flex: 1 1 auto;
  min-width: 0;
  height: calc(var(--row-h, 40px) - 4px);
  padding: var(--coast-plate-pad, 2px);
  background: linear-gradient(90deg, var(--coast-row-bright), var(--coast-row-accent) 45%, rgba(255, 255, 255, 0.14));
  clip-path: polygon(0 0, calc(100% - 22px) 0, 100% 50%, calc(100% - 22px) 100%, 0 100%);
}
.coast-plate-in {
  height: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--coast-plate-gap, 16px);
  padding: 0 var(--coast-plate-pr, 34px) 0 var(--coast-plate-pl, 12px);
  clip-path: inherit;
  background: linear-gradient(90deg, var(--accent-mid, rgba(124, 58, 237, 0.34)) 0%, rgba(10, 10, 22, 0.94) 46%, rgba(12, 10, 24, 0.9) 100%);
}
/* Square avatars here (the classic layout keeps its circles). */
.flyer-coast .avatar-thumb {
  margin-right: 0;
  border-radius: 12px;
  border: 2px solid var(--coast-row-bright, #c084fc);
  box-shadow: 0 0 14px var(--accent-mid, rgba(124, 58, 237, 0.34));
}

.coast-stop-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: calc(var(--row-h, 40px) * 0.05);
}
.coast-stop-name-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.coast-stop-name-row .flyer-stop-name {
  flex: 0 1 auto;
  font-size: var(--stop-fs, 20px);
  font-weight: 800;
  line-height: 1.05;
  /* The tight line-height above is what earns the row its space, but the
     clip box the ellipsis needs is the padding box — and at 1.05 the ink
     of an underscore ("aske_origin") hangs ~0.07em BELOW it, so the glyph
     was being cut away and the name read as two words. Padding extends
     the paint/clip region downward without moving the glyphs, generously
     (0.3em) so the ink stays inside on the fallback fonts too. Only the
     paint/clip region grows — the negative margin of the same size keeps
     that pad out of the flex row's layout height, so no row moves and no
     name shifts against its times. */
  padding-bottom: 0.3em;
  margin-bottom: -0.3em;
}

/* The five set times, in the columns the label row above names — five
   equal tracks in the roomier shape, fixed-width ones when the rows are
   compact, which is what keeps every plate's columns (and the labels over
   them) in the same place. The home (BST/GMT) value is the bright one, so
   the eye still finds the schedule's own time first. */
.coast-times {
  display: grid;
  /* minmax(0, 1fr) rather than 1fr: equal tracks whatever a value's width,
     so a "+1" in one row can never nudge that row's columns out of line
     with every other row's — and with the labels above them. */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--coast-time-gap, 12px);
  font-size: var(--coast-time-fs, 18px);
  font-weight: 700;
  line-height: 1.15;
  color: rgba(255, 255, 255, 0.74);
  white-space: nowrap;
  overflow: hidden;
}
.coast-tz { min-width: 0; }
.coast-tz.is-home { color: #fff; }
/* Sits in the value's own column, after the time — a real element, so the
   PNG export keeps it. */
.coast-dot {
  display: inline-block;
  vertical-align: middle;
  width: 0.2em;
  height: 0.2em;
  margin-left: 0.3em;
  border-radius: 50%;
  background: var(--coast-row-bright, #c084fc);
}

/* ---- Dense variant: a long lineup (flyer.php flags it with .is-dense) ----
   Below ~62px a row can't hold the name and the times as two comfortable
   lines, so the times move back up beside the name and each plate takes
   the single line the classic layout already uses. Same poster, same
   everything else — just a tighter row. Only the outer paddings change
   here; the times keep the same columns and the zone labels stay in the
   row above, tracking them. */
.flyer-coast.is-dense {
  --coast-plate-gap: 12px;
  --coast-plate-pr: 28px;
  --coast-time-gap: 8px;
}
.flyer-coast.is-dense .coast-stop-text {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.flyer-coast.is-dense .coast-stop-name-row { flex: 1 1 auto; }
/* Fixed-width columns pinned to the plate's right edge, so the times (and
   the labels above them) start at the same x on every row. */
.flyer-coast.is-dense .coast-times {
  flex: 0 0 auto;
  grid-template-columns: repeat(5, var(--coast-col-w, auto));
}
.flyer-coast.is-dense .coast-tz-avatar { display: none; }
.flyer-coast.is-dense .coast-tz-name { display: block; }
.flyer-coast.is-dense .coast-tz-grid {
  flex: 0 0 auto;
  grid-template-columns: repeat(5, var(--coast-col-w, auto));
}
/* "+1" on a conversion that rolled past midnight — same rule as the
   classic layout: shown everywhere except the AEST column, where Sydney's
   own time would carry the marker on nearly every row. Small, because in
   these columns a value already fills most of its track: flyer.php sized
   the type to leave room for this marker (and the dot it replaces). */
.flyer-coast .flyer-tz-next { font-size: 0.42em; }

.coast-bottom {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 14px;
}
.flyer-coast .flyer-foot { margin-top: 0; font-size: 17px; }
.flyer-coast .flyer-empty { font-size: 34px; }

@media print {
  body.flyer-body { padding: 0; background: #fff; gap: 0; }
  .flyer-toolbar { display: none; }
  .flyer-stage { width: 1080px; height: 1440px; }
  .flyer { transform: none; box-shadow: none; }
}

/* ---------- Slot claims (Twitch) ---------- */
/* A claim is a reservation REQUEST shown against an open (TBA) slot on the
   public preview. It is deliberately understated: the admin still has to
   book the DJ in, so it shouldn't look like a filled slot. */
.slot-claim {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 5px;
  font-size: 12px;
  line-height: 1.5;
}
.slot-claim-badge {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid rgba(145, 70, 255, 0.65);
  background: rgba(145, 70, 255, 0.18);
  color: #c9a6ff;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}
.slot-claim-form { display: inline; margin: 0; }
.slot-claim-form .btn { padding: 3px 9px; font-size: 11px; }
.slot-claim-hint { font-size: 12px; color: var(--text-dim); }

/* ---------- Small screens ---------- */

@media (max-width: 640px) {
  nav { padding: 10px 16px; }
  nav .links a { margin-left: 0; margin-right: 16px; }
  nav .links .nav-drop { margin-left: 0; margin-right: 16px; }
  .preview-hero { padding: 20px 18px; }
  .preview-title { font-size: 24px; }
}
