:root {
  /* Page background - a bright periwinkle-to-indigo wash with a diamond
     lattice texture layered on top (see body{} below), matching the
     PokemonTeamListCreator-ChampionsBeta sibling tool's shared identity. */
  --bg-a: #6b63e6;
  --bg-b: #262456;

  /* Card panels (event cards, calendar chips, detail cards, filter inputs). */
  --panel: #f5f3fc;
  --panel-2: #e9e6f8;
  --border: #ded7f2;

  /* Page-level text - sits directly on the gradient (topbar brand, h1, lede, footnote). */
  --text: #ffffff;
  --muted: #d6d3f7;
  --muted-2: #c1bbe8;

  /* Panel-level text - sits on the pale panel surface above. */
  --ink: #333a72;
  --ink-dim: #6b6f9c;

  /* The fee pill deliberately wears the OTHER mode's colors (dark mode's
     panel-2/ink here, and light mode's below) so it always pops as a
     little inverted accent against its own card instead of blending in. */
  --cost-bg: #241e3a;
  --cost-text: #ece9fb;

  --accent: #4a4fc9;
  --accent-strong: #363aa0;
  --badge-bg: #ece8fa;
  --shadow: 0 16px 34px rgba(18, 14, 54, .28);
  --radius: 22px;
  --radius-sm: 10px;
  --radius-btn: 999px;

  /* The signature pill button - a fully-rounded white pill with dark navy
     text and a green/yellow gradient glow on hover. Kept identical between
     light/dark mode, like the sibling tool's own buttons. */
  --btn-bg: #ffffff;
  --btn-text: #333a72;
  --btn-hover-a: #abe049;
  --btn-hover-b: #ecf461;

  --cup-color: #a8710a;
  --cup-bg: rgba(168, 113, 10, .12);
  --challenge-color: #c22e83;
  --challenge-bg: rgba(194, 46, 131, .1);
  --tournament-color: var(--accent);
  --tournament-bg: var(--badge-bg);
  --league-color: #7a6f96;
  --league-bg: var(--panel-2);

  --vg-color: #d62b2b;
  --tcg-color: #1d6fe0;
  --pgo-color: #17914a;

  --favorite-color: #d99a00;
}

/* Dark mode - toggled via [data-theme] on <html> (see the pre-paint inline
   script in index.html/event.html's <head>, which applies this before first
   paint from a saved cookie, and the #theme-toggle handler in app.js, which
   flips it and saves the cookie back). */
:root[data-theme="dark"] {
  --bg-a: #38316e;
  --bg-b: #100e26;

  --panel: #1f1a30;
  --panel-2: #241e3a;
  --border: #322b4a;

  --text: #f1eefc;
  --muted: #a79bc9;
  --muted-2: #7a6f96;

  --ink: #ece9fb;
  --ink-dim: #a6a3d4;

  --cost-bg: #e9e6f8;
  --cost-text: #333a72;

  --accent: #9d6bff;
  --accent-strong: #c4a6ff;
  --badge-bg: #2a2244;
  --shadow: 0 16px 34px rgba(4, 3, 18, .5);

  --cup-color: #f0b429;
  --cup-bg: rgba(240, 180, 41, .16);
  --challenge-color: #ff7fc4;
  --challenge-bg: rgba(255, 127, 196, .14);
  --league-color: #8f83b3;

  --vg-color: #f04545;
  --tcg-color: #4599ff;
  --pgo-color: #3ecf6e;

  --favorite-color: #ffd166;
}

* {
  box-sizing: border-box;
  transition: background-color .3s ease, color .3s ease, border-color .3s ease;
}

/* overflow-x is deliberately NOT set on html/body: doing so makes body the
   sticky containing block instead of the viewport, which silently breaks
   `.topbar`'s `position: sticky`. max-width still guards against any single
   element pushing the page wider than the viewport. */
html, body { max-width: 100%; }

/* Fallback color behind body's own background - mobile browsers can reveal
   a sliver beneath it during elastic overscroll bounce (rubber-banding past
   the top/bottom of the page), which without this shows through as the
   browser's default white instead of the page's own dark backdrop. */
html { background: var(--bg-b); }

body {
  margin: 0;
  font-family: 'Montserrat', sans-serif;
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

/* The gradient+lattice backdrop lives on a fixed full-viewport layer behind
   the content, NOT directly on body's own (scrollable) background. Two
   reasons:
   - `background-attachment: fixed` reliably flashes/blanks the background
     during momentum scrolling and overscroll bounce on mobile Safari/Chrome
     (confirmed live) - an actual `position: fixed` element doesn't hit that
     bug, it's a different mechanism entirely.
   - A background painted directly on body stretches across body's FULL
     scrollable height, not one screen's worth - on a long event list
     (hundreds of cards) that smears one diagonal gradient across many
     screens' worth of height, so the color you see depends on how far
     you've scrolled and how tall that particular page/view happens to be
     (confirmed live: looked like it was "changing color randomly"). Fixed
     to the viewport, it's always the same gradient regardless of scroll
     position or page length. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, .05) 0 2px, transparent 2px 46px),
    repeating-linear-gradient(45deg, rgba(255, 255, 255, .05) 0 2px, transparent 2px 46px),
    linear-gradient(160deg, var(--bg-a), var(--bg-b) 65%);
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-strong); text-decoration: underline; }
button { font-family: inherit; }

/* ---------- Entrance animations ---------- */

@keyframes fadeSlideDown {
  from { opacity: 0; transform: translateY(-12px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes fadeSlideUp {
  from { opacity: 0; transform: translateY(16px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: no-preference) {
  .topbar { animation: fadeSlideDown .5s ease both; }
  .container { animation: fadeSlideUp .5s ease both; animation-delay: .08s; }
}

/* Topbar - a translucent, blurred tint of the page gradient so it reads as
   floating chrome over the same gradient world (rather than a hard-contrast
   solid bar), while staying sticky for the view/lang controls. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 24px;
  background: color-mix(in srgb, var(--bg-b) 55%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 4px 16px -6px rgba(0, 0, 0, .35);
  gap: 12px;
  flex-wrap: wrap;
}

.brand-group { display: flex; align-items: center; gap: 12px; min-width: 0; text-decoration: none; color: inherit; }
.brand-group:hover { text-decoration: none; color: inherit; }

.logo {
  height: 46px;
  width: 46px;
  flex-shrink: 0;
  object-fit: contain;
  background: #fff;
  border-radius: 50%;
  padding: 6px;
  box-shadow: 0 6px 14px rgba(10, 8, 40, .35);
}

.brand-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.topbar .brand,
h1 {
  color: var(--text);
  font-weight: 800;
}
.topbar .brand {
  font-size: 1.1rem;
  letter-spacing: -.01em;
  line-height: 1.15;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.brand-tagline {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  background: rgba(255, 255, 255, .14);
  color: rgba(255, 255, 255, .85);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: .68rem;
  font-weight: 600;
  white-space: nowrap;
}

.container { max-width: 900px; margin: 0 auto; padding: 24px 20px 72px; }

h1 {
  margin: 0 0 6px;
  font-size: clamp(1.5rem, 4vw, 2.1rem);
  letter-spacing: -.01em;
  line-height: 1.1;
}
.lede { color: var(--muted); margin-top: 0; }
.meta { color: var(--muted); margin-top: -4px; font-size: .9rem; }
.muted { color: var(--muted-2); }

.topbar-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* View toggle + language toggle - both sit on the topbar, so their styling
   is fixed (not theme-driven): a translucent white track with a solid white
   active pill, matching the sibling tool's white pill-button treatment. */
.view-toggle,
.lang-toggle {
  display: flex;
  gap: 3px;
  background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .2);
  border-radius: var(--radius-btn);
  padding: 3px;
}
.toggle-btn,
.lang-btn {
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, .8);
  font-size: .82rem;
  font-weight: 700;
  padding: 7px 14px;
  border-radius: var(--radius-btn);
  cursor: pointer;
  white-space: nowrap;
}
.toggle-btn.active,
.lang-btn.active {
  background: #fff;
  color: #17141f;
  box-shadow: var(--shadow);
}

.lang-toggle { padding: 3px; }
.lang-btn { font-size: .76rem; font-weight: 800; padding: 6px 11px; }

/* Light/dark toggle - a sliding day/night switch (orange track + sun icon
   in light mode, dark track + moon icon in dark mode). */
#theme-toggle {
  position: relative;
  flex-shrink: 0;
  width: 52px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: var(--radius-btn);
  cursor: pointer;
  background: linear-gradient(90deg, #ffcf6b, #ff9a3c);
  box-shadow: 0 6px 14px rgba(10, 8, 40, .35), inset 0 0 0 2px rgba(255, 255, 255, .3);
  transition: background .3s ease, transform .15s ease;
}
:root[data-theme="dark"] #theme-toggle {
  background: linear-gradient(90deg, #2a2650, #100e26);
}
#theme-toggle:hover { transform: translateY(-2px); }
#theme-toggle:active .theme-switch-knob { width: 26px; }
:root[data-theme="dark"] #theme-toggle:active .theme-switch-knob { transform: translateX(21px); }

#theme-toggle .theme-switch-icon {
  position: absolute;
  top: 50%;
  width: 13px;
  height: 13px;
  transform: translateY(-50%);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity .2s ease;
}
#theme-toggle .theme-switch-sun { right: 7px; opacity: 1; }
#theme-toggle .theme-switch-moon { left: 7px; opacity: 0; }
:root[data-theme="dark"] #theme-toggle .theme-switch-sun { opacity: 0; }
:root[data-theme="dark"] #theme-toggle .theme-switch-moon { opacity: 1; }

#theme-toggle .theme-switch-knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 3px 6px rgba(0, 0, 0, .3);
  transition: transform .3s ease, width .15s ease;
}
:root[data-theme="dark"] #theme-toggle .theme-switch-knob { transform: translateX(24px); }

/* Filter bar */
.filter-bar { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; }
.filter-bar input, .filter-bar select, .filter-bar button {
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  font-size: .88rem;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.filter-bar input:focus, .filter-bar select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.filter-bar input[type="text"] { flex: 1 1 160px; min-width: 140px; }
.filter-bar input[type="date"] { flex: 1 1 130px; min-width: 120px; }
.filter-bar button {
  cursor: pointer;
  font-weight: 700;
  letter-spacing: .03em;
  flex: 0 0 auto;
  background: var(--btn-bg);
  color: var(--btn-text);
  border: 2px solid transparent;
  border-radius: var(--radius-btn);
  box-shadow: 0 4px 8px rgba(51, 58, 114, .25);
  transition: background .2s ease, border-color .2s ease, color .2s ease, transform .15s ease;
}
.filter-bar button:hover {
  background: linear-gradient(90deg, var(--btn-hover-a), var(--btn-hover-b));
  border-color: #fff;
  color: #234;
  transform: translateY(-2px);
}
.filter-bar button:active { transform: scale(.97); }
.filter-bar button.btn-reset {
  background: rgba(255, 255, 255, .14);
  color: var(--text);
  border: 2px solid rgba(255, 255, 255, .3);
  box-shadow: none;
}
.filter-bar button.btn-reset:hover {
  background: rgba(255, 255, 255, .24);
  color: var(--text);
  border-color: rgba(255, 255, 255, .3);
  transform: none;
}
.filter-label { align-self: center; color: var(--muted); font-size: .82rem; white-space: nowrap; }
.pill-select { min-width: 150px; flex: 1 1 150px; }

/* Filter toggle - mobile-only "Filtri" button that collapses everything
   past the search box, so the filter form doesn't dominate the screen
   before the user has even seen a result. Hidden on desktop, where the
   filter-panel-body below is always expanded (display:contents) and this
   button would be redundant. */
.filter-bar button.filter-toggle {
  display: none;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  background: rgba(255, 255, 255, .14);
  color: var(--text);
  border: 2px solid rgba(255, 255, 255, .3);
  border-radius: var(--radius-btn);
  font-weight: 700;
  cursor: pointer;
}
.filter-toggle-chevron { margin-left: auto; transition: transform .2s ease; flex-shrink: 0; }
.filter-toggle[aria-expanded="true"] .filter-toggle-chevron { transform: rotate(180deg); }
.filter-count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: .72rem;
  font-weight: 800;
}
/* The custom display:inline-flex above is an author-stylesheet rule, which
   beats the UA stylesheet's `[hidden] { display: none }` regardless of
   specificity (author normal always wins over user-agent normal) - without
   this, the `hidden` attribute set in JS would be silently ignored and the
   badge would show even at count 0. */
.filter-count-badge[hidden] { display: none; }

/* On desktop the panel's own children flow straight into .filter-bar's flex
   layout as if this wrapper weren't there - `display: contents` drops the
   wrapper from the box tree while keeping its children in the DOM (and
   thus toggleable) for the mobile collapse below. */
.filter-panel-body { display: contents; }

.filter-checkbox-group {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 14px;
  flex: 1 1 100%;
  min-width: 0;
}

/* Game toggles: a real checkbox input, visually hidden, with the label
   styled as a chip button - dim/muted border+text when unchecked, a
   bright colored border with a glow and full-brightness text when
   checked. One color per game (kept as a --toggle-color custom property
   so the on/off rule below is written once, not three times). */
.toggle-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  padding: 8px 16px;
  border-radius: 14px;
  border: 2px solid color-mix(in srgb, var(--toggle-color) 35%, var(--border));
  background: var(--panel-2);
  color: color-mix(in srgb, var(--toggle-color) 40%, var(--ink-dim));
  font-weight: 700;
  font-size: .82rem;
  cursor: pointer;
  transition: border-color .15s ease, color .15s ease, box-shadow .15s ease, background-color .15s ease;
}
/* .filter-bar .toggle-chip input (not just .game-toggle input): the mobile
   `.filter-bar input { width: 100% }` override further down has equal
   selector specificity and appears later in the file, so on source order
   alone it would win and un-hide this input - bumping specificity here
   avoids relitigating the same bug fixed earlier for .filter-checkbox. */
.filter-bar .toggle-chip input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.toggle-chip span { pointer-events: none; }
.toggle-chip:has(input:checked) {
  border-color: var(--toggle-color);
  color: var(--ink);
  background: color-mix(in srgb, var(--toggle-color) 16%, var(--panel-2));
  box-shadow: 0 0 0 1px var(--toggle-color), 0 0 14px color-mix(in srgb, var(--toggle-color) 50%, transparent);
  font-weight: 800;
}

.game-toggle-vg { --toggle-color: var(--vg-color); }
.game-toggle-tcg { --toggle-color: var(--tcg-color); }
.game-toggle-pgo { --toggle-color: var(--pgo-color); }

.type-toggle-cup { --toggle-color: var(--cup-color); }
.type-toggle-challenge { --toggle-color: var(--challenge-color); }
.type-toggle-tournament { --toggle-color: var(--tournament-color); }
.type-toggle-league { --toggle-color: var(--league-color); }

.extra-toggle-favorites { --toggle-color: var(--favorite-color); }

/* "Near me" is a one-shot action (triggers a geolocation prompt), not a
   simple on/off filter like the toggle-chips above, so it gets its own
   plain-button treatment matching .btn-reset/.filter-toggle instead -
   .active reflects "currently sorting by distance", not a hover/focus state. */
.filter-bar button.near-me-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  background: rgba(255, 255, 255, .14);
  color: var(--text);
  border: 2px solid rgba(255, 255, 255, .3);
  border-radius: var(--radius-btn);
  font-weight: 700;
  cursor: pointer;
}
.filter-bar button.near-me-btn.active {
  background: #fff;
  color: var(--btn-text);
  border-color: #fff;
}
.filter-bar button.near-me-btn:disabled { opacity: .6; cursor: wait; }

/* Location search - a geocoded-place autocomplete alongside the plain
   name/store text search (#q), combinable with it and with "Vicino a me"
   (which just calls the same selectLocation() with the browser's own
   coordinates instead of a geocoded result - see index.html). */
.location-search-wrap { position: relative; flex: 1 1 200px; min-width: 160px; }
.location-search-wrap input { width: 100%; box-sizing: border-box; }
.location-suggestions {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 20;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  max-height: 260px;
  overflow-y: auto;
}
.location-suggestion-item {
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  font-size: .86rem;
  color: var(--ink);
  cursor: pointer;
}
.location-suggestion-item:hover,
.location-suggestion-item:focus {
  background: color-mix(in srgb, var(--accent) 14%, var(--panel));
}
.location-suggestion-empty {
  padding: 8px 10px;
  font-size: .86rem;
  color: var(--muted);
}
.location-radius-select { flex: 0 0 auto; min-width: 100px; }
/* Small round "x" to drop the current location filter - same base treatment
   as .near-me-btn/.btn-reset, just circular and icon-sized instead of a
   labeled pill. */
.filter-bar button.location-clear-btn {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  line-height: 1;
  background: rgba(255, 255, 255, .14);
  color: var(--text);
  border: 2px solid rgba(255, 255, 255, .3);
  border-radius: 50%;
}
/* Same author-stylesheet-beats-UA-stylesheet pitfall as .filter-count-badge
   above: the explicit `display: inline-flex` right above this would
   otherwise win over the browser's own `[hidden] { display: none }` rule
   and show the button even while its `hidden` attribute is set. */
.filter-bar button.location-clear-btn[hidden] { display: none; }

/* Sort toggle (Distanza/Data) - only shown alongside an active location
   filter (see selectLocation()/clearLocation() in index.html), so it
   reuses the filter-bar's own theme-aware pill-track look rather than the
   fixed white-on-purple .view-toggle in the topbar, which is styled for
   sitting directly on that header background instead. */
.sort-toggle {
  display: flex;
  gap: 3px;
  flex: 0 0 auto;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  padding: 3px;
}
.sort-toggle[hidden] { display: none; }
/* Prefixed with .filter-bar button throughout (matching .near-me-btn's own
   selector shape) rather than plain .sort-toggle-btn - .filter-bar button
   and its sibling `.filter-bar input, .filter-bar select, .filter-bar
   button` rule both include an element selector, which otherwise ties or
   beats a same-single-class selector on specificity and silently wins
   individual properties (padding/background/etc, and on hover the accent
   fill). */
.filter-bar button.sort-toggle-btn {
  background: transparent;
  border: none;
  color: var(--ink-dim);
  font-size: .82rem;
  font-weight: 700;
  padding: 7px 14px;
  border-radius: var(--radius-btn);
  cursor: pointer;
  white-space: nowrap;
}
.filter-bar button.sort-toggle-btn.active { background: var(--accent); color: #fff; }

.matches-summary { color: var(--text); font-size: .88rem; font-weight: 600; margin: 0 0 14px; }
.matches-summary:empty { display: none; }

/* Type badges (used on the detail page; list/calendar cards use color + icon instead) */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border-radius: 999px;
  padding: 4px 11px;
  font-size: .72rem;
  font-weight: 700;
  white-space: nowrap;
  background: var(--badge-bg);
  color: var(--accent);
}
.badge .type-icon-img { width: 16px; height: 16px; object-fit: contain; }
.badge.type-cup { background: var(--cup-bg); color: var(--cup-color); }
.badge.type-challenge { background: var(--challenge-bg); color: var(--challenge-color); }
.badge.type-tournament { background: var(--tournament-bg); color: var(--tournament-color); }
.badge.type-league { background: var(--league-bg); color: var(--league-color); }

.loading, .empty-state { color: var(--muted); padding: 24px; text-align: center; }
.showing-note { color: var(--muted); font-size: .82rem; margin: 10px 0 0; }
.footnote { color: var(--muted-2); font-size: .78rem; margin-top: 28px; }

/* Event cards - shared by List view and the Calendar day list */
.event-list { display: flex; flex-direction: column; gap: 14px; }
.event-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
  background: var(--panel);
  color: var(--ink);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .8), var(--shadow);
  cursor: pointer;
  transition: transform .12s ease;
}
.event-card:hover { transform: translateY(-2px); }
.event-card:active { transform: translateY(0); }

/* Favorite star - absolutely positioned over the card's top-right corner
   rather than laid out inline, so it doesn't have to fight the title/cost
   pill for space in the already-tight title row. The body's own
   padding-right below clears space under it. */
.favorite-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: none;
  background: color-mix(in srgb, var(--favorite-color) 14%, var(--panel));
  color: var(--favorite-color);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform .15s ease, background-color .15s ease;
}
.favorite-btn svg { fill: none; stroke: currentColor; }
.favorite-btn.is-favorite svg { fill: currentColor; }
.favorite-btn:hover { transform: scale(1.12); }
.favorite-btn:active { transform: scale(.92); }
.favorite-btn-lg { position: static; width: 34px; height: 34px; flex-shrink: 0; }
.favorite-btn-lg svg { width: 18px; height: 18px; }

.event-card-icon {
  flex: 0 0 44px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.15rem;
  background: var(--badge-bg);
}
/* All four types now show badge artwork (cup/challenge use the official
   league badges, tournament/league fall back to the generic Play! Pokémon
   badge) - it already carries its own red/white/black frame, so a tinted
   circle behind it would just compete with that rather than add anything. */
.event-card-icon.type-cup, .event-card-icon.type-challenge,
.event-card-icon.type-tournament, .event-card-icon.type-league { background: transparent; }
.event-card-icon .type-icon-img { width: 100%; height: 100%; object-fit: contain; }

.event-card-body { flex: 1; min-width: 0; padding-right: 28px; }

.event-card-games { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 6px; }
.game-pill {
  display: inline-flex;
  align-items: center;
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .02em;
  padding: 1px 8px;
  border-radius: 999px;
  line-height: 1.5;
}
/* Mixed against --panel, not transparent: these pills also appear on the
   event detail page's .detail-badges, which sits directly on the page's
   own gradient background rather than inside a card - a transparent tint
   there left barely any backing behind the colored text, hard to read
   especially in light mode. Mixing against --panel gives a solid pale/dark
   backing that works the same whether the pill is on a card or the
   gradient. */
.game-pill-vg { background: color-mix(in srgb, var(--vg-color) 18%, var(--panel)); color: var(--vg-color); }
.game-pill-tcg { background: color-mix(in srgb, var(--tcg-color) 18%, var(--panel)); color: var(--tcg-color); }
.game-pill-pgo { background: color-mix(in srgb, var(--pgo-color) 18%, var(--panel)); color: var(--pgo-color); }

.event-card-title-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.event-card-title { font-weight: 800; font-size: .95rem; min-width: 0; }
.event-card-title.type-cup { color: var(--cup-color); }
.event-card-title.type-challenge { color: var(--challenge-color); }
.event-card-title.type-tournament { color: var(--tournament-color); }
.event-card-title.type-league { color: var(--ink); }
.event-card-cost {
  flex: 0 0 auto;
  font-weight: 800;
  font-size: .82rem;
  color: var(--cost-text);
  background: var(--cost-bg);
  padding: 3px 9px;
  border-radius: 999px;
  white-space: nowrap;
}

/* Date/address rows - a small Feather-style icon plus text, replacing the
   previous plain subtitle/address text lines. */
.event-card-meta-row {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  color: var(--ink-dim);
  font-size: .8rem;
  line-height: 1.4;
  margin-top: 3px;
}
.event-card-meta-row svg { flex-shrink: 0; margin-top: 2px; }
.event-card-meta-row span { min-width: 0; }
.event-card-meta-address span {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.event-card-inactive { opacity: .55; }

/* Calendar view: month nav + horizontal day-strip picker + selected day's cards */
.cal-nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.cal-month-label { font-weight: 800; font-size: 1.02rem; color: var(--text); }
.cal-nav-btn {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 50%;
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  color: var(--ink);
  font-size: 1.1rem;
  line-height: 1;
}
.cal-nav-btn:hover { border-color: var(--accent); color: var(--accent); }

.cal-daystrip {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 2px 2px 10px;
  margin-bottom: 16px;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}
.cal-daystrip::-webkit-scrollbar { display: none; }
.cal-day-chip {
  flex: 0 0 auto;
  width: 52px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 10px 0 9px;
  border-radius: var(--radius);
  background: var(--panel);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .8);
  cursor: pointer;
  scroll-snap-align: center;
  color: var(--ink);
}
.cal-day-chip .num { font-weight: 800; font-size: 1.05rem; line-height: 1; }
.cal-day-chip .dow { font-size: .64rem; color: var(--ink-dim); text-transform: uppercase; letter-spacing: .03em; }
.cal-day-chip .dots { margin-top: 1px; }
.cal-day-chip.today { box-shadow: inset 0 0 0 2px var(--accent); }
.cal-day-chip.selected { background: var(--accent); box-shadow: none; color: #fff; }
.cal-day-chip.selected .dow { color: rgba(255, 255, 255, .82); }

.cal-day-heading { font-size: .95rem; font-weight: 700; margin: 4px 0 12px; color: var(--text); }
.cal-day-heading .count { color: var(--muted); font-weight: 500; font-size: .82rem; margin-left: 6px; }

.cal-mode-toggle { margin-bottom: 12px; }

/* Full month grid (Google Calendar-style) - an alternative to the day-strip
   above, toggled via .cal-mode-toggle. */
.cal-weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 5px;
  margin-bottom: 6px;
}
.cal-weekdays span {
  text-align: center;
  font-size: .68rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .03em;
}
.cal-month-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 5px;
  margin-bottom: 16px;
}
.cal-grid-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 7px 0;
  border-radius: var(--radius-sm);
  background: var(--panel);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .8);
  color: var(--ink);
  cursor: pointer;
  font-size: .78rem;
}
.cal-grid-cell .num { font-weight: 700; line-height: 1; }
.cal-grid-cell-outside { opacity: .4; }
.cal-grid-cell.today { box-shadow: inset 0 0 0 2px var(--accent); }
.cal-grid-cell.selected { background: var(--accent); box-shadow: none; color: #fff; }

/* Per-game dots (day-strip chips and month-grid cells alike) - true game
   colors even when a cell is selected/highlighted, since the whole point is
   spotting which games run that day at a glance. */
.dots { display: flex; gap: 2px; }
.dot { width: 5px; height: 5px; border-radius: 50%; }
.dot-vg { background: var(--vg-color); }
.dot-tcg { background: var(--tcg-color); }
.dot-pgo { background: var(--pgo-color); }

/* Map view */
#map-view { border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
#map { height: 65vh; min-height: 420px; width: 100%; background: var(--panel-2); }
.leaflet-popup-content { font-size: .85rem; }
.popup-title { font-weight: 700; margin-bottom: 4px; }
.popup-meta { color: #555; font-size: .8rem; }

/* Event detail page */
.back-link { display: inline-flex; align-items: center; gap: 6px; margin-bottom: 16px; color: var(--muted); font-weight: 600; font-size: .86rem; }
.back-link:hover { color: var(--text); text-decoration: none; }

.detail-badges { display: flex; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; align-items: center; }
.detail-title-row { display: flex; align-items: center; gap: 10px; }
.detail-title { font-size: 1.3rem; margin: 0 0 4px; letter-spacing: -.01em; color: var(--text); }
.detail-subtitle { color: var(--muted); font-size: .95rem; margin-bottom: 20px; }
.venue-link { color: var(--muted); text-decoration: underline dotted; text-underline-offset: 2px; }
.venue-link:hover { color: var(--text); text-decoration: underline solid; }

.detail-card {
  background: var(--panel);
  color: var(--ink);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .8), var(--shadow);
  padding: 18px 20px;
  margin-bottom: 16px;
}

/* Fixed 3 columns on desktop - paired with the field order in event.html's
   metaGrid array (date/cost/signup-start, then signup-end/email/phone),
   this reliably splits into exactly two rows of three regardless of which
   optional fields (registration dates, phone) happen to be present. Mobile
   overrides back to the old auto-fill behavior below. */
.detail-meta-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.detail-meta-label { font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-dim); margin-bottom: 3px; }
/* overflow-wrap so long unbroken values (an email address has no spaces
   to break at) wrap inside their own narrow grid column instead of
   overflowing into the next one - real bug hit when the column minmax
   was narrowed to fit 6 fields on one row (see .detail-meta-grid). */
.detail-meta-value { font-size: .92rem; overflow-wrap: break-word; }

.detail-section-title { font-size: .78rem; color: var(--ink-dim); text-transform: uppercase; letter-spacing: .06em; font-weight: 700; margin: 0 0 10px; }
.detail-description { white-space: pre-line; line-height: 1.6; font-size: .92rem; }
.detail-address { line-height: 1.5; }

.detail-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 4px 0 16px; }
/* Class selectors, not tag-qualified (a.btn) - the ics/share actions are
   <button> elements (they run JS, not navigate), and need the exact same
   pill styling as the <a> actions they sit alongside. */
.detail-actions .btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--btn-bg);
  color: var(--btn-text);
  font-weight: 700;
  font-size: .88rem;
  padding: 10px 18px;
  border-radius: var(--radius-btn);
  border: 2px solid transparent;
  box-shadow: 0 4px 8px rgba(51, 58, 114, .25);
  cursor: pointer;
  font-family: inherit;
  transition: background .2s ease, border-color .2s ease, color .2s ease, transform .15s ease;
}
.detail-actions .btn:hover {
  background: linear-gradient(90deg, var(--btn-hover-a), var(--btn-hover-b));
  border-color: #fff;
  color: #234;
  text-decoration: none;
  transform: translateY(-2px);
}
.detail-actions .btn:active { transform: scale(.97); }
.detail-actions .btn-secondary {
  background: transparent;
  color: var(--text);
  border: 2px solid rgba(255, 255, 255, .5);
  box-shadow: none;
}
.detail-actions .btn-secondary:hover {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .8);
  color: var(--text);
}

/* "Add to calendar" popover (Google/Outlook/ICS) - anchored to its own
   wrapper (not .detail-actions itself) so the menu's absolute position is
   relative to the button, not the whole button row. */
.calendar-menu-wrap { position: relative; display: inline-flex; }
.calendar-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  /* Well above Leaflet's own panes (tile/marker/popup panes go up to
     z-index 700) - neither .calendar-menu-wrap nor #detail-map establishes
     its own stacking context, so without this the map's internal z-indices
     compare directly against this menu's and win, rendering on top of it. */
  z-index: 1000;
  min-width: 210px;
  max-width: calc(100vw - 40px);
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px;
  background: var(--panel);
  color: var(--ink);
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .8), var(--shadow);
}
/* Same author-vs-UA-stylesheet gotcha as .filter-count-badge above: the
   display:flex here would otherwise silently override the `hidden`
   attribute's own display:none. */
.calendar-menu[hidden] { display: none; }
.calendar-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  color: var(--ink);
  font-family: inherit;
  font-size: .86rem;
  font-weight: 600;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.calendar-menu-item:hover { background: var(--panel-2); }

.detail-attributes { display: flex; flex-wrap: wrap; gap: 6px; }

#detail-map, #venue-map { height: 260px; border-radius: var(--radius-sm); overflow: hidden; margin-top: 12px; }
.venue-filter-bar { margin: 20px 0 6px; }

.cookie-banner {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: 16px;
  z-index: 40;
  max-width: 560px;
  margin: 0 auto;
  background: var(--panel);
  color: var(--ink-dim);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .8), var(--shadow);
  padding: 14px 16px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.cookie-banner p { margin: 0; font-size: .8rem; line-height: 1.4; flex: 1 1 220px; }
.cookie-banner button {
  flex: 0 0 auto;
  background: var(--btn-bg);
  color: var(--btn-text);
  border: 2px solid transparent;
  border-radius: var(--radius-btn);
  padding: 8px 16px;
  font-weight: 700;
  font-size: .82rem;
  cursor: pointer;
  box-shadow: 0 4px 8px rgba(51, 58, 114, .25);
  transition: background .2s ease, transform .15s ease;
}
.cookie-banner button:hover {
  background: linear-gradient(90deg, var(--btn-hover-a), var(--btn-hover-b));
  transform: translateY(-2px);
}
.cookie-banner button:active { transform: scale(.97); }

/* Floating back-to-top button - same white-pill/lime-hover family as every
   other button, just circular. Hidden (not just transparent) until scrolled
   past the threshold, so it's never in the way or focus-tabbable early. */
.back-to-top {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 30;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--btn-bg);
  color: var(--btn-text);
  border: 2px solid transparent;
  box-shadow: 0 4px 12px rgba(51, 58, 114, .3);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity .2s ease, transform .2s ease, background .2s ease, border-color .2s ease, color .2s ease;
}
.back-to-top.visible { opacity: 1; visibility: visible; transform: translateY(0); }
.back-to-top:hover {
  background: linear-gradient(90deg, var(--btn-hover-a), var(--btn-hover-b));
  border-color: #fff;
  color: #234;
  transform: translateY(-2px);
}
.back-to-top.visible:hover { transform: translateY(-2px); }
.back-to-top:active { transform: scale(.94); }
/* Nudged up while the cookie banner occupies the same corner. */
body.has-cookie-banner .back-to-top { bottom: 90px; }

.inactive-note {
  background: var(--badge-bg);
  color: var(--ink-dim);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  font-size: .85rem;
  margin-bottom: 16px;
}

/* Mobile */
@media (max-width: 640px) {
  .topbar { padding: 8px 12px; gap: 8px; }
  .topbar .brand { font-size: .95rem; }
  /* Drops the tagline pill's own line under the brand name - on a narrow
     screen the brand row plus the controls row (which don't fit alongside
     it and wrap below) already make the topbar tall enough without it. */
  .brand-tagline { display: none; }
  .logo { height: 34px; width: 34px; }
  .container { padding: 16px 12px 48px; }
  h1 { font-size: 1.15rem; }

  .filter-bar { flex-direction: column; align-items: stretch; }
  /* Reset flex-basis, not just width: in a column flex container the main
     axis is vertical, so the desktop rules' `flex: 1 1 160px` etc. would
     otherwise set each input's HEIGHT to 160px instead of its width.
     Matches the same selector specificity as those type-specific desktop
     rules (input[type=...]) so this override actually wins. */
  .filter-bar input, .filter-bar input[type="text"], .filter-bar input[type="date"], .filter-bar select, .filter-bar button, .location-search-wrap, .sort-toggle {
    flex: 0 0 auto;
    width: 100%;
  }
  /* min-width: 0 overrides flex items' default min-width:auto (their
     content's natural nowrap width), which otherwise blocks flex-shrink
     from actually applying and pushes the second button off-screen. */
  .filter-bar button.sort-toggle-btn { flex: 1 1 0; min-width: 0; }
  .filter-bar button.filter-toggle { display: flex; }
  /* On mobile the wrapper is no longer inert (display:contents) - it's a
     real column of its own so the whole block can be hidden behind the
     toggle above without the form dominating the screen before the user
     has even seen a result. */
  .filter-panel-body {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    width: 100%;
  }
  .filter-panel-body.collapsed { display: none; }
  .pill-select { min-width: 0; }

  .event-card { padding: 14px 15px; gap: 12px; }
  .event-card-icon { flex-basis: 40px; width: 40px; height: 40px; font-size: 1.05rem; }

  #map { height: 55vh; min-height: 320px; }

  .detail-card { padding: 14px 16px; }
  .detail-title { font-size: 1.12rem; }
  /* Back to auto-fill on mobile (unlike desktop's fixed 3 columns) - lets
     however many narrow columns actually fit wrap naturally instead of
     squeezing 3 fixed columns into a much narrower card. */
  .detail-meta-grid { grid-template-columns: repeat(auto-fill, minmax(115px, 1fr)); }

  .cal-weekdays, .cal-month-grid { gap: 4px; }
  .cal-grid-cell { font-size: .72rem; border-radius: 8px; }

  .back-to-top { right: 14px; bottom: 14px; width: 42px; height: 42px; }
  /* The cookie banner text wraps to more lines at this width, so it's
     taller - needs more clearance than the desktop offset. */
  body.has-cookie-banner .back-to-top { bottom: 150px; }
}
