:root {
  --ink: #17171a;
  --muted: #6b6b73;
  --line: #e4e4e8;
  --bg: #fff;
  --accent: #b4341f;
  --radius: 6px;
}
@media (prefers-color-scheme: dark) {
  :root {
    --ink: #ececef;
    --muted: #9a9aa3;
    --line: #2c2c31;
    --bg: #131316;
  }
}
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font:
    16px/1.55 ui-sans-serif,
    system-ui,
    -apple-system,
    "Segoe UI",
    sans-serif;
}
a {
  color: inherit;
}
main {
  max-width: 46rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 4rem;
}

header.site {
  display: flex;
  gap: 1rem;
  align-items: center;
  flex-wrap: wrap;
  max-width: 46rem;
  margin: 0 auto;
  padding: 1.25rem;
  border-bottom: 1px solid var(--line);
}
.brand {
  font-weight: 700;
  text-decoration: none;
  letter-spacing: -0.01em;
}
.search {
  display: flex;
  gap: 0.5rem;
  margin-left: auto;
  flex-wrap: wrap;
}
.search input,
.search select,
.search button {
  font: inherit;
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  color: inherit;
}
.search button {
  cursor: pointer;
}
/* An input's default width is wider than its box, so without this the row cannot
   shrink and the header overflows instead of wrapping. */
.search input {
  min-width: 0;
}
.search__q {
  flex: 1 1 9rem;
}
.search__near {
  white-space: nowrap;
}
.search__near.is-active {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}
.search__near[disabled] {
  cursor: progress;
  color: var(--muted);
}
/* Every icon on this site sits beside the word it illustrates and is marked
   aria-hidden, so none of them has to carry meaning alone. Sized in em to track the
   text they label, and dimmed so they support the word rather than compete with it.
   Fixed width keeps a column of links aligned when the glyphs differ. */
.fa-solid,
.fa-brands,
.fa-regular {
  font-size: 0.85em;
  opacity: 0.7;
  width: 1em;
  text-align: center;
}
/* Full strength where the icon is the affordance: the primary button, and the
   Facebook mark, which is recognised by its colour and shape rather than read. */
.button .fa-solid,
.actions .fa-brands {
  opacity: 1;
}

.results__filter {
  margin: 0.2rem 0 0;
  color: var(--muted);
  font-size: 0.9rem;
}
/* Loading ----------------------------------------------------------------- */

/* Every page already sets aria-busy on #app around its fetches, and the shell ships
   with it set, so this one selector covers every request the site makes — including
   the first, before any script has run. Delayed by 300ms because a bar that flashes
   for the length of a cached response reads as a glitch rather than as progress. */
#app[aria-busy="true"]::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10;
  width: 30%;
  height: 2px;
  background: var(--accent);
  opacity: 0;
  animation: loading 1s ease-in-out 0.3s infinite;
}
@keyframes loading {
  from {
    opacity: 1;
    transform: translateX(-100%);
  }
  to {
    opacity: 1;
    transform: translateX(333%);
  }
}

/* It says the same thing standing still, and this is the one element on the site that
   would otherwise move on its own. */
@media (prefers-reduced-motion: reduce) {
  #app[aria-busy="true"]::before {
    width: 100%;
    animation: loading-still 0s linear 0.3s forwards;
  }
}
@keyframes loading-still {
  to {
    opacity: 0.55;
  }
}

.search__error {
  margin: 0.4rem 0 0;
  color: var(--accent);
  font-size: 0.85rem;
}

/* A bill reads as one line whether or not anybody has resolved the names in it, so
   the separator is quiet and a resolved name is a link rather than a badge. */
.event__lineup .sep,
.detail__lineup .sep {
  color: var(--muted);
}

h1 {
  font-size: 1.6rem;
  letter-spacing: -0.02em;
  margin: 0 0 0.25rem;
}
h2 {
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 2rem 0 0.5rem;
  font-weight: 600;
}
.lede {
  color: var(--muted);
  margin-top: 0;
}

.presets {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 1rem 0 0;
}
.presets a {
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.3rem 0.8rem;
  font-size: 0.9rem;
}

.events {
  display: flex;
  flex-direction: column;
}
.event {
  display: grid;
  grid-template-columns: 11rem 1fr auto;
  gap: 1rem;
  padding: 0.75rem 0;
  border-top: 1px solid var(--line);
  align-items: baseline;
}
.event__when {
  color: var(--muted);
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}
.event__title {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 600;
}
.event__title a {
  text-decoration: none;
}
.event__title a:hover {
  text-decoration: underline;
}
.event__lineup,
.event__where {
  margin: 0.15rem 0 0;
  font-size: 0.88rem;
  color: var(--muted);
}
.event__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.2rem;
  font-size: 0.85rem;
  white-space: nowrap;
}
.price {
  font-variant-numeric: tabular-nums;
}
/* Sold out. The only availability state published (§5) and the only one styled:
   there is no "available" class because there is no such claim to make. Weight and
   colour rather than a filled badge — it belongs to the row it is in, and sixty
   rows with three loud badges in them read as an error state. */
.sold-out {
  color: var(--accent);
  font-weight: 600;
}
p.sold-out {
  margin: 1.5rem 0 -0.75rem;
  font-weight: 500;
}
.details {
  font-weight: 600;
  text-decoration: none;
}
.distance,
.space {
  color: var(--muted);
}

.detail__when {
  color: var(--muted);
  margin-bottom: 0.25rem;
}
.unknown {
  color: var(--muted);
}
.facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.3rem 1rem;
  margin: 1.5rem 0;
}
.facts dt {
  color: var(--muted);
  font-size: 0.9rem;
}
.facts dd {
  margin: 0;
}
.actions {
  display: flex;
  gap: 1rem;
  align-items: center;
  margin: 1.5rem 0;
}
.button {
  background: var(--ink);
  color: var(--bg);
  text-decoration: none;
  padding: 0.5rem 1rem;
  border-radius: var(--radius);
  font-weight: 600;
}
.provenance {
  margin-top: 3rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.85rem;
}

.notice {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-left: 3px solid var(--accent);
  padding: 0.6rem 0.8rem;
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.9rem;
}
.empty,
.count {
  color: var(--muted);
}
.more {
  margin-top: 1.5rem;
}

.venues,
.uncovered {
  list-style: none;
  padding: 0;
}
.venues li,
.uncovered li {
  padding: 0.5rem 0;
  border-top: 1px solid var(--line);
}
.venues .city,
.venues .count,
.venues .operated,
.uncovered .city,
.uncovered .reason {
  color: var(--muted);
  font-size: 0.9rem;
}

pre {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  padding: 0.8rem;
  border-radius: var(--radius);
  overflow-x: auto;
}

footer.site {
  max-width: 46rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 3rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.85rem;
}

@media (max-width: 34rem) {
  .event {
    grid-template-columns: 1fr;
    gap: 0.2rem;
  }
  .event__meta {
    align-items: flex-start;
    flex-direction: row;
    gap: 0.8rem;
  }
  .search {
    margin-left: 0;
    width: 100%;
  }
  .search__q {
    flex-basis: 100%;
  }
}

.facets {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin: 1rem 0 0.5rem;
}
.facet {
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.25rem 0.7rem;
  font-size: 0.85rem;
}
.facet.is-active {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}
.facet__count {
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
}
.facets__coverage {
  color: var(--muted);
  font-size: 0.82rem;
  margin: 0.25rem 0 1rem;
}
.genres {
  color: var(--muted);
  font-size: 0.8rem;
}

.facet.is-clear {
  border-style: dashed;
}
.facet.is-excluded {
  border-color: var(--accent);
  color: var(--accent);
}
.facets--excluded {
  margin-top: 0;
}
/* The radius chips are the same control as breadth — a quiet row of alternatives
   refining a filter that is already on — so they are drawn the same way. */
.breadths,
.radii {
  display: flex;
  gap: 0.3rem;
  flex-wrap: wrap;
  margin: 0.25rem 0 0.5rem;
  font-size: 0.8rem;
}
.breadth,
.radius {
  text-decoration: none;
  color: var(--muted);
  padding: 0.2rem 0.5rem;
  border-radius: var(--radius);
}
.breadth.is-active,
.radius.is-active {
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  color: var(--ink);
}

.by-genre {
  margin-top: 2rem;
}

/* Status ------------------------------------------------------------------ */

table.status {
  width: 100%;
  border-collapse: collapse;
  margin: 1.5rem 0;
  font-size: 0.9rem;
}
table.status th {
  text-align: left;
  font-weight: 600;
  color: var(--muted);
  padding: 0.4rem 0.6rem 0.4rem 0;
  border-bottom: 1px solid var(--line);
}
table.status td {
  padding: 0.5rem 0.6rem 0.5rem 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
table.status tr.is-bad td {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.status__role {
  color: var(--muted);
  font-size: 0.85em;
}
.status__never {
  color: var(--muted);
}
.status__bad {
  color: var(--accent);
}
/* Neither good nor bad: something is happening and will have finished by the time
   anybody acts on it. */
.status__running {
  color: var(--muted);
  font-style: italic;
}
.status__note {
  color: var(--muted);
  font-size: 0.85rem;
  max-width: 46rem;
}

.status__alert {
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 0.8rem 1rem;
  margin: 1.5rem 0;
}
.status__alert h2 {
  margin: 0 0 0.4rem;
  font-size: 1rem;
}
.status__problems {
  margin: 0;
  padding-left: 1.1rem;
}

/* Deliberate, so it gets none of the alert's border or accent — it sits at the
   bottom and reads as a footnote rather than as something to act on. */
.status__disabled {
  margin: 2.5rem 0 1.5rem;
  color: var(--muted);
}
.status__disabled h2 {
  margin: 0 0 0.4rem;
  font-size: 1rem;
  font-weight: 500;
}
.status__problems li {
  margin: 0.15rem 0;
}

/* How many genres an event did not have room to show. Quiet: it is a hint that the
   tagging is broad, not a number anybody needs to read. */
.genres__more {
  opacity: 0.6;
}
/* On a detail page the chips are the whole list rather than a top-N, so they wrap
   under the venue line instead of sitting in a right-hand column. */
.facets--detail {
  margin: 0.75rem 0 0;
}
