/* ==========================================================================
   Black Cambry 2026 - Base Styles
   Full responsive layout is implemented in task 15.1.
   This file provides foundational styles needed by the auth module.
   ========================================================================== */

:root {
  /* Palette inspired by the legacy cambry games page (slate + cool greys
     + soft pink accent). Tokens are kept generic so other views (admin,
     trips, attendees) ride the same theme without view-specific
     hardcoding. WCAG contrast on the games view was checked: cool-white
     #e8edf0 on #1f2c38 ≈ 12.0 (AAA), muted #a6b7bf ≈ 7.0 (AAA), and
     pink accent #f896ff ≈ 7.4 (AAA for body, AA Large for accents). */
  --color-bg: #1f2c38;        /* deep slate, replaces the prior near-black */
  --color-surface: #2a3a4a;   /* card chrome, one step lighter than bg */
  --color-primary: #3a526a;   /* used as a darker fill in chip/button bases */
  --color-accent: #f896ff;    /* signature soft pink from games.css loader */
  --color-accent-hover: #ffb1ff; /* lighter pink for :hover/:focus */
  --color-on-accent: #1f2c38; /* dark text for buttons/badges riding the
                                 light pink accent (white on pink fails AA) */
  --color-text: #e8edf0;      /* cool white for body copy */
  --color-text-muted: #a6b7bf; /* muted blue-grey for secondary text */
  --color-error: #ff8aa1;     /* warm pink reds — sits well next to accent */
  --color-success: #7fd197;   /* soft green; passes AA on the slate bg */
  --radius: 8px;
  --shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  /* Yantramanav from Google Fonts (loaded in index.html). System sans
     stack as a fallback for the brief window before the webfont resolves
     so the page never falls back to serifs. */
  font-family: 'Yantramanav', -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  background-color: var(--color-bg);
  color: var(--color-text);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Header */
.app-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 1rem;
  background-color: var(--color-surface);
  box-shadow: var(--shadow);
  position: sticky;
  top: 0;
  z-index: 100;
}

/* Header brand block. The "Black Cambry 2026" wordmark used to live
   here but was removed — the active-trip indicator below took over
   as the only header label. The brand wrapper is kept so the
   indicator's flex-shrink behavior stays scoped. */
.header-brand {
  flex-shrink: 1;
  min-width: 0;
}

/* Contextual header slot — views can mount a single line of content
   here. The games view fills it with the rank-hint + counter; on
   pages that don't use it the slot is empty and ``:empty`` collapses
   it to zero width via ``display:none`` below. */
.app-header__context {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 1rem;
  min-width: 0;
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

.app-header__context:empty {
  display: none;
}

/* Games-view hint + counter that mounts into the header context slot.
   Hint reads first, counter sits right-aligned. On narrow viewports
   we let the hint truncate before the counter (which is short) so
   the count stays visible. */
.header-context__hint {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
  color: var(--color-text);
}

.header-context__count {
  flex-shrink: 0;
  font-weight: 500;
  color: var(--color-text-muted);
}

/* Active trip indicator (task 21.1, Requirements 18.4 / 18.5).
   Sits inside .header-brand. Mounted by
   js/active_trip_indicator.js with the resolved trip's nickname or
   name. The opaque trip_id is never displayed. Empty by default so
   the layout doesn't shift before the label arrives.

   Now the only label in the header (the "Black Cambry 2026" wordmark
   was dropped), so it carries primary visual weight. */
.active-trip-indicator {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
  /* Truncate very long trip names instead of letting them wrap and
     pushing the header taller. A title attribute is not set because
     the full name is also visible in the dropdown when present. */
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* Reserve a single line of height even when empty so the header
     doesn't reflow when the label arrives asynchronously. */
  min-height: 1.1em;
}

/* Navigation. The nav menu is now a slide-out drawer triggered by
   the hamburger button at the far-left of the header. Same pattern
   on every viewport — desktop and mobile share the drawer interaction
   so users always know where to find Trips/Admin/Sign Out. */
.app-nav {
  /* The nav element wraps the slide-out drawer so toggling the
     ``.nav-open`` class on it can drive the drawer's transform. */
  position: relative;
}

.app-nav .nav-links {
  list-style: none;
  margin: 0;
  padding: 0;

  /* Slide-out drawer: fixed-position panel anchored to the left edge
     of the viewport. Hidden off-screen via translateX until the user
     opens the menu. */
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: min(280px, 80vw);
  display: flex;
  flex-direction: column;
  background-color: var(--color-surface);
  box-shadow: 6px 0 18px rgba(0, 0, 0, 0.45);
  padding: 4rem 0 1rem;
  transform: translateX(-100%);
  transition: transform 0.22s ease-out;
  z-index: 110;
  /* Make scrollable when the menu is taller than the viewport. */
  overflow-y: auto;
}

.app-nav.nav-open .nav-links {
  transform: translateX(0);
}

.app-nav .nav-links a {
  display: block;
  padding: 0.85rem 1.25rem;
  color: var(--color-text-muted);
  text-decoration: none;
  font-size: 1rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  transition: color 0.2s, background-color 0.2s;
}

.app-nav .nav-links a:hover,
.app-nav .nav-links a.active,
.app-nav .nav-links a:focus-visible {
  color: var(--color-text);
  background-color: rgba(255, 255, 255, 0.04);
  outline: none;
}

/* Backdrop dim while the menu is open. Click-anywhere-to-close
   already happens via the nav script; this only adds a visible cue. */
.app-nav.nav-open::before {
  content: '';
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 105;
}

/* Hamburger button — always visible (when there's more than one nav
   entry to choose from), far left of the header. The slide-out drawer
   pattern means we use the same toggle on every viewport, so this
   rule has no media query around it. The ``.hidden`` utility class
   collapses it when the menu has zero or one rows; that's typically
   non-admin users during voting status, where Games is the only
   destination and the menu would feel pointless. */
.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  color: var(--color-text);
}

.nav-toggle:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Three-bar hamburger icon (CSS only, no images) */
.nav-toggle-icon,
.nav-toggle-icon::before,
.nav-toggle-icon::after {
  display: block;
  width: 22px;
  height: 2px;
  background-color: currentColor;
  transition: transform 0.2s, top 0.2s, background-color 0.2s;
  border-radius: 2px;
}

.nav-toggle-icon {
  position: relative;
}

.nav-toggle-icon::before,
.nav-toggle-icon::after {
  content: '';
  position: absolute;
  left: 0;
}

.nav-toggle-icon::before { top: -7px; }
.nav-toggle-icon::after  { top:  7px; }

/* Animate to "X" when nav is open */
.app-nav.nav-open ~ .nav-toggle .nav-toggle-icon,
/* Fallback when DOM order means the toggle precedes the nav: keep
   the legacy selector working too. */
.app-nav.nav-open .nav-toggle-icon {
  background-color: transparent;
}

.app-nav.nav-open ~ .nav-toggle .nav-toggle-icon::before,
.app-nav.nav-open .nav-toggle-icon::before {
  top: 0;
  transform: rotate(45deg);
}

.app-nav.nav-open ~ .nav-toggle .nav-toggle-icon::after,
.app-nav.nav-open .nav-toggle-icon::after {
  top: 0;
  transform: rotate(-45deg);
}

.hidden {
  display: none !important;
}

/* Main content */
.app-main {
  flex: 1;
  padding: 1.5rem;
  max-width: 1200px;
  margin: 0 auto;
  width: 100%;
}

/* Footer */
.app-footer {
  text-align: center;
  padding: 1rem;
  color: var(--color-text-muted);
  font-size: 0.8rem;
}

/* ==========================================================================
   Auth Module Styles
   ========================================================================== */

.header-auth {
  display: flex;
  align-items: center;
  /* Pin the avatar to the right edge of the header regardless of
     what other slots (active trip indicator, context, nav) consume
     in the middle. ``margin-left: auto`` consumes any remaining
     horizontal space inside the flex container. */
  margin-left: auto;
  flex-shrink: 0;
}

.auth-profile {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.auth-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--color-accent);
}

.auth-avatar-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--color-primary);
  color: var(--color-text-muted);
}

.auth-signout-btn {
  background: transparent;
  border: 1px solid var(--color-text-muted);
  color: var(--color-text-muted);
  padding: 0.35rem 0.75rem;
  border-radius: var(--radius);
  font-size: 0.8rem;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s;
}

.auth-signout-btn:hover {
  color: var(--color-text);
  border-color: var(--color-text);
}

.auth-signin {
  display: flex;
  align-items: center;
}

.auth-error {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.auth-error-message {
  color: var(--color-error);
  font-size: 0.85rem;
  max-width: 200px;
}

.auth-retry-btn {
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  border: none;
  padding: 0.5rem 1rem;
  border-radius: var(--radius);
  font-size: 0.85rem;
  cursor: pointer;
  transition: opacity 0.2s;
}

.auth-retry-btn:hover {
  opacity: 0.85;
}

/* ===========================================================================
 * Sign-in splash
 *
 * Full-viewport overlay shown until the user signs in. Mirrors the
 * legacy cambry app: cambry11.png cover photo as the backdrop, dark
 * scrim for legibility over arbitrary spots in the image, then a
 * centered title / tagline / Google button stack.
 *
 * The body gets ``.auth-splash-active`` while this is up so the rest
 * of the app shell (header, drawer, main view) hides behind the
 * splash without flashing partial content.
 * =========================================================================== */

body.auth-splash-active .app-header,
body.auth-splash-active .app-nav,
body.auth-splash-active main,
body.auth-splash-active .nav-toggle,
body.auth-splash-active .nav-overlay {
  visibility: hidden;
}

.auth-splash {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background-color: var(--color-bg);
  background-image: url('../images/cambry11.png');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.5rem;
  /* Keep the splash on a smooth fade out so the avatar can render
     without an abrupt cut. JS adds .auth-splash--leaving and removes
     the node after this transition completes. */
  opacity: 1;
  transition: opacity 0.2s ease-out;
}

.auth-splash--leaving {
  opacity: 0;
  pointer-events: none;
}

/* Dark scrim sits between the photo and the content so the title and
   tagline stay legible regardless of which area of cambry11.png is
   visible. Linear gradient toward the bottom keeps the artwork
   readable at the top while letting the content sit on a darker
   foundation below. */
.auth-splash__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(31, 44, 56, 0.45) 0%,
    rgba(31, 44, 56, 0.7) 60%,
    rgba(31, 44, 56, 0.85) 100%
  );
  pointer-events: none;
}

.auth-splash__content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  max-width: 32rem;
  text-align: center;
}

.auth-splash__title {
  font-family: 'Yantramanav', sans-serif;
  font-size: clamp(2rem, 6vw, 3.25rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-text);
  margin: 0;
  line-height: 1.05;
  /* Soft drop shadow so the type holds even where the scrim is light. */
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}

.auth-splash__tagline {
  font-family: 'Yantramanav', sans-serif;
  font-size: clamp(0.95rem, 2vw, 1.05rem);
  font-weight: 300;
  color: var(--color-text);
  margin: 0;
  line-height: 1.5;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.5);
  max-width: 28rem;
}

.auth-splash__button {
  margin-top: 0.5rem;
  /* Google's rendered button is its own iframe — we only need to
     center it here. The pill / filled_black / continue_with config in
     auth.js gives it the right shape and copy. */
  display: flex;
  justify-content: center;
}

.auth-denied {
  display: flex;
  align-items: center;
}

.auth-denied-text {
  color: var(--color-error);
  font-size: 0.85rem;
  font-weight: 600;
}

/* Error / Access Denied container */
.error-container {
  text-align: center;
  padding: 3rem 1.5rem;
}

.error-container h2 {
  color: var(--color-error);
  margin-bottom: 1rem;
}

.error-container p {
  color: var(--color-text-muted);
  margin-bottom: 0.75rem;
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
}

.error-container .auth-retry-btn {
  margin-top: 1.5rem;
}

/* Welcome / unauthenticated landing view */
.welcome-view {
  text-align: center;
  padding: 3rem 1.5rem;
  max-width: 560px;
  margin: 0 auto;
}

.welcome-view__title {
  color: var(--color-text);
  margin-bottom: 1rem;
}

.welcome-view__text,
.welcome-view__hint {
  color: var(--color-text-muted);
  margin-bottom: 0.75rem;
}

.welcome-view__hint {
  font-size: 0.95rem;
}

/* ==========================================================================
   Games View Styles
   ========================================================================== */

.games-view {
  width: 100%;
}

.games-loading {
  text-align: center;
  color: var(--color-text-muted);
  padding: 3rem 1rem;
  font-size: 1rem;
}

.games-empty {
  text-align: center;
  padding: 4rem 1.5rem;
  color: var(--color-text-muted);
  font-size: 1.1rem;
}

/* Instructions banner — the cambry-style rank prompt sits above the
   grid as a centred two-line block. Title is light and airy, subtitle
   is muted with a white-emphasised "in order of preference" highlight.
   Hidden via ``.hidden`` once the user makes their first pick so the
   prompt gets out of the way. */
.games-instructions {
  text-align: center;
  padding: 1.25rem 1rem 0.75rem;
  margin-bottom: 0.5rem;
}

.games-instructions__title {
  font-size: 1.25rem;
  font-weight: 400;
  color: var(--color-text);
  letter-spacing: 0.01em;
  margin: 0;
}

.games-instructions__subtitle {
  font-size: 0.95rem;
  font-weight: 300;
  color: var(--color-text-muted);
  margin: 0.2rem 0 0;
}

.games-instructions__highlight {
  color: var(--color-text);
  font-weight: 500;
}

.games-list {
  display: grid;
  /* Two cards per row on mobile, scaling up with viewport. The
     auto-fill with minmax handles the upscaling for free at the
     larger breakpoints below — this rule covers everything narrower
     than 768px, where we'd previously been single-column. The 150px
     minimum is tight but sufficient for the legacy-style city image
     plus a one-line teams row beneath. */
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 0.75rem;
  /* Bottom padding leaves room for the floating Submit Votes pill so
     it doesn't visually obscure the last row of cards. The value
     accommodates the pill (~48px tall) plus its bottom anchor and
     a comfortable buffer. */
  padding: 0.5rem 0 5rem;
}

/* Game Card */
.game-card {
  background-color: var(--color-surface);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
  transition: transform 0.2s, box-shadow 0.2s;
  display: flex;
  flex-direction: column;
  /* Performance (Requirement 2.5): defer rendering work for off-screen cards.
     Browsers that support content-visibility skip layout/paint for cards outside
     the viewport, which speeds up Time to Interactive on long lists.
     contain-intrinsic-size reserves space so the scrollbar doesn't jump. */
  content-visibility: auto;
  contain-intrinsic-size: 0 420px;
}

.game-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}

/* Card header — a slim strip carrying the date + time only. The
   matchup (team logos overlaid on the city image, plus the team
   names below) is the visual focal point; this strip keeps the
   "when" out of the way. */
.game-card__header {
  padding: 0.3rem 0.6rem;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.4rem;
  background-color: var(--color-primary);
  /* Small font so the date and time always fit on a single line at
     the narrowest card width (~150px). The longest "Wednesday,
     September 17" comes in around 130px at 0.7rem Yantramanav, with
     the right-aligned time fitting in the remaining space. */
  font-size: 0.7rem;
  line-height: 1.15;
  white-space: nowrap;
}

.game-card__date {
  font-weight: 500;
  color: var(--color-text);
  /* Ellipsis-truncate the (very rare) overflow so we never wrap
     onto two lines and disturb the card geometry. */
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.game-card__time {
  color: var(--color-text-muted);
  flex-shrink: 0;
}

/* Team logos overlaid on the city image. Two absolutely-positioned
   anchors — away team bottom-left, home team bottom-right — borrowed
   from the legacy cambry layout. */
.game-card__team-logo {
  width: 48px;
  height: 48px;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  flex-shrink: 0;
  /* Heavy stacked drop-shadow so the logos punch off the image even
     against bright skies / busy stadium photos. The two layers
     reinforce each other: a tight inner shadow defines the silhouette
     edge, and a wider, more diffuse outer shadow lifts the logo off
     the photo. Both shadows render to one another via the SVG
     vector — no rectangular halo around the bounding box. */
  filter:
    drop-shadow(0 1px 2px rgba(0, 0, 0, 0.9))
    drop-shadow(0 4px 12px rgba(0, 0, 0, 0.7));
  z-index: 2;
}

.game-card__team-logo--away,
.game-card__team-logo--home {
  position: absolute;
  bottom: 0.5rem;
}

.game-card__team-logo--away {
  left: 0.5rem;
}

.game-card__team-logo--home {
  right: 0.5rem;
}

/* Placeholder treatment when no logo asset is committed for a team
   (manifest returned null). Keep the same footprint as the real logo
   so the row's geometry doesn't shift, and surface two-letter team
   initials so the user still knows which team is which. */
.game-card__team-logo--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(31, 44, 56, 0.85);
  color: var(--color-text);
  border-radius: 50%;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  /* Match the stronger stacked shadow of the real logos so the
     placeholder chip pops the same way against the city photo. */
  filter:
    drop-shadow(0 1px 2px rgba(0, 0, 0, 0.9))
    drop-shadow(0 4px 12px rgba(0, 0, 0, 0.7));
}

/* City placeholder: same height as the real city panel, but a flat
   slate gradient instead of a bitmap. The city name still renders on
   top via .game-card__city-name. */
.game-card__city-image--placeholder {
  background: linear-gradient(
    135deg,
    var(--color-surface) 0%,
    var(--color-primary) 100%
  );
}

.game-card__vs {
  font-size: 0.75rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  font-weight: 600;
}

/* City image section. The image is the focal point of every card;
   the city name overlays the top edge as a banner, the avg-travel
   badge sits opposite it (top-right), and the two team logos
   anchor the bottom corners. Switched from flex layout to plain
   relative positioning so each overlay can pin to its own corner
   independently. */
.game-card__city-image {
  width: 100%;
  /* Aspect-ratio rather than fixed height so the photo scales with
     the card's width — at the narrowest cards (~150px) we end up
     around 80px tall, at wider ones (~240px) closer to 130px. The
     city-name overlay and avg-travel badge both stay legible across
     this range. */
  aspect-ratio: 16 / 9;
  background-size: cover;
  background-position: center;
  position: relative;
}

/* City name banner — sits across the TOP of the image. Reads first,
   above all the other overlays, since it answers "where is this
   game?" — the most important question the card answers. */
.game-card__city-name {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: block;
  padding: 0.4rem 0.75rem;
  /* Gradient runs from solid black at the top to transparent at the
     bottom, opposite of the original bottom-banner direction. This
     keeps the photo legible below the band where the team logos sit. */
  background: linear-gradient(rgba(0, 0, 0, 0.78), transparent);
  font-size: 0.9rem;
  font-weight: 600;
  color: #fff;
  line-height: 1.2;
  /* Add a subtle text shadow so the city name reads even when the
     gradient softens against a bright photo. */
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
  z-index: 1;
}

/* Average-travel badge — borrowed from the legacy cambry card. Sits
   in the top-right corner of the city image showing the average
   flight time across all attendees. The personalized "from your
   home city" travel time still lives in the details row below.
   The badge uses a translucent slate fill so it reads against any
   city photo and degrades to a solid pill on the slate placeholder. */
.game-card__avg-travel-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0;
  padding: 0.25rem 0.5rem;
  border-radius: 6px;
  background-color: rgba(31, 44, 56, 0.78);
  /* The overlay needs to sit above the bottom-gradient that the
     city-name span paints; explicit z-index keeps that ordering
     stable regardless of source order. */
  z-index: 2;
  pointer-events: none;
  /* A subtle backdrop-blur softens busy city photos without making
     the badge feel heavyweight. Falls back to the solid slate when
     unsupported (Safari < 15.4). */
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.game-card__avg-travel-label {
  font-size: 0.55rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--color-text-muted);
  line-height: 1.1;
}

.game-card__avg-travel-time {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-accent);
  line-height: 1.1;
}

/* Details section. Three blocks: teams (left), personal travel
   (centre), action icons (right). At narrow card widths we let the
   icons wrap onto a second row rather than crushing the team name. */
.game-card__details {
  padding: 0.5rem 0.75rem;
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

/* Personalized travel time. Now occupies the left half of the
   details row (the team-name block was removed — that information
   is communicated visually by the team logos overlaid on the city
   image). Reads as a two-line cluster: "From <home city>" on top,
   the duration in larger weight below, mirroring the legacy cambry
   layout's "From Vancouver / 2:29:00" pattern. */
.game-card__personal-travel {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  /* Take the freed real estate from the dropped team-name block. */
  flex: 1 1 auto;
  min-width: 0;
}

.game-card__personal-travel-label {
  color: var(--color-text-muted);
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  /* Truncate long "From <city>" labels instead of wrapping; full
     text is in the parent's title attribute. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.game-card__personal-travel-time {
  color: var(--color-text);
  font-weight: 600;
  /* Bigger than before so the duration reads as the primary content
     of the details row, matching the visual weight the team-name
     used to carry. */
  font-size: 1rem;
  line-height: 1.1;
}

.game-card__personal-travel-time--missing {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  color: var(--color-text-muted);
  font-weight: 400;
  font-size: 0.85rem;
}

.game-card__missing-icon {
  color: #ff9800;
  flex-shrink: 0;
}

.game-card__personal-travel--missing {
  opacity: 0.8;
}

.game-card__icons {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.game-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Compact at the new card density. Mobile bumps to 44×44 below to
     hit the WCAG 2.5.5 tap-target minimum. */
  width: 32px;
  height: 32px;
  border-radius: 4px;
  color: var(--color-text);
  text-decoration: none;
  transition: opacity 0.2s, color 0.2s;
}

/* Branded icon assets (TopGolf, K1 Speed speedometer, plug / unplug)
   from the legacy cambry app. The SVGs ship as white-on-transparent
   so they sit cleanly on the slate card background; we let them fill
   essentially the entire icon container so the wordmark on the
   TopGolf glyph stays legible. */
.game-card__icon-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Block clicks on the image so the wrapping <a> / <span> picks
     them up — important for the activity links to open in a new
     tab via target="_blank". */
  pointer-events: none;
}

.game-card__icon--active {
  opacity: 1;
  color: var(--color-text);
  cursor: pointer;
}

.game-card__icon--active:hover {
  color: var(--color-accent);
}

.game-card__icon--dimmed {
  opacity: 0.2;
  pointer-events: none;
  cursor: default;
}

.game-card__icon--plug-connected {
  opacity: 1;
  color: var(--color-success);
}

.game-card__icon--plug-disconnected {
  opacity: 0.2;
}

/* Responsive: multi-column grid at 768px+. Slightly higher minimum
   so cards have room for the avg-travel badge on the image without
   the badge dominating; auto-fill keeps cards uniformly sized. */
@media (min-width: 768px) {
  .games-list {
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 1rem;
  }
}

/* ==========================================================================
   Voting UI Styles
   ========================================================================== */

/* Voting error message — kept for the games view's inline error
   reporting (e.g. submit failure). No longer wrapped in the previous
   ``.voting-toolbar`` banner since the hint and count moved into the
   global header context slot. */
.voting-toolbar__error {
  display: block;
  width: 100%;
  color: var(--color-error);
  font-size: 0.85rem;
  margin-bottom: 0.5rem;
}

/* Floating submit button — fixed position pill anchored to the
   bottom of the viewport, only shown when the user has at least
   one selection. Sits above the cards so it's reachable without
   scrolling back to the top of the page. */
.voting-floating-submit {
  position: fixed;
  /* Account for safe-area on devices with home-indicator notches. */
  bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  border: none;
  padding: 0.85rem 1.5rem;
  border-radius: 999px;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.2s ease-out, box-shadow 0.2s, opacity 0.2s;
  /* Soft pink halo so the floating pill feels lifted off the cards
     below; the box-shadow uses an rgba derived from the accent so
     it stays consistent if the palette is retuned. */
  box-shadow: 0 6px 18px rgba(248, 150, 255, 0.25),
              0 2px 4px rgba(0, 0, 0, 0.4);
}

.voting-floating-submit:hover:not(:disabled) {
  transform: translateX(-50%) translateY(-2px);
  box-shadow: 0 10px 24px rgba(248, 150, 255, 0.35),
              0 3px 6px rgba(0, 0, 0, 0.4);
}

.voting-floating-submit:disabled {
  cursor: progress;
  opacity: 0.85;
}

.voting-floating-submit__label {
  font-weight: 600;
}

/* The "N / 4" pill nested in the floating button. Uses a darkened
   on-accent fill so it reads as a separate count chip rather than
   blending into the rest of the button label. */
.voting-floating-submit__count {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background-color: rgba(31, 44, 56, 0.25);
  color: var(--color-on-accent);
  font-size: 0.8rem;
  font-weight: 600;
}

/* Game card selected state — borrowed from the legacy cambry's
   "thick pink ring" treatment. The card has ``overflow: hidden``
   to clip the city image to the rounded corners, which also clips
   any ``box-shadow: inset`` we'd put on it. So we paint the ring
   via a ``::after`` pseudo-element absolutely positioned over the
   card's full extents — that pseudo isn't a child overflowing the
   card, so the rounded corners don't clip it.
   The exterior glow uses a regular (non-inset) box-shadow on the
   card itself; that paints outside ``overflow`` and is fine. */
.game-card--selected {
  position: relative;
  /* Soft pink halo so the selected card lifts off the grid. */
  box-shadow: 0 0 0 1px var(--color-accent), 0 0 18px rgba(248, 150, 255, 0.5);
}

.game-card--selected::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 3px solid var(--color-accent);
  border-radius: inherit;
  /* Sit above the city image and all other card contents but
     below the rank badge (which uses ``z-index: 10``). */
  z-index: 5;
  /* Don't intercept clicks — the underlying card still handles
     selection toggling. */
  pointer-events: none;
}

/* Slightly tinted header for selected cards keeps the date strip
   colour-coordinated with the ring. */
.game-card--selected .game-card__header {
  background-color: color-mix(in srgb, var(--color-accent) 18%, var(--color-primary));
}

/* Rank badge */
.game-card__rank-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  font-size: 0.85rem;
  font-weight: 700;
  border-radius: 50%;
  /* Soft pink glow tinted to match the accent itself rather than the
     legacy red box-shadow. Subtler against the slate background. */
  box-shadow: 0 2px 6px rgba(248, 150, 255, 0.45);
  z-index: 10;
  pointer-events: none;
}

/* Make game cards look clickable for voting */
.game-card {
  cursor: pointer;
  user-select: none;
}

.game-card:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* ==========================================================================
   Results View Styles
   ========================================================================== */

.results-view {
  width: 100%;
}

.results-view__title {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--color-text);
  margin-bottom: 1.5rem;
}

.results-loading {
  text-align: center;
  color: var(--color-text-muted);
  padding: 3rem 1rem;
  font-size: 1rem;
}

.results-empty {
  text-align: center;
  padding: 4rem 1.5rem;
  color: var(--color-text-muted);
  font-size: 1.1rem;
}

.results-chart-wrapper {
  position: relative;
  width: 100%;
  height: 300px;
  background-color: var(--color-surface);
  border-radius: var(--radius);
  padding: 1rem;
  box-shadow: var(--shadow);
}

@media (min-width: 768px) {
  .results-chart-wrapper {
    height: 400px;
    padding: 1.5rem;
  }
}

@media (min-width: 1200px) {
  .results-chart-wrapper {
    height: 500px;
    padding: 2rem;
  }
}


/* ==========================================================================
   Admin View Styles
   ========================================================================== */

.admin-view {
  width: 100%;
}

.admin-view__title {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--color-text);
  margin-bottom: 1.5rem;
}

.admin-loading {
  text-align: center;
  color: var(--color-text-muted);
  padding: 3rem 1rem;
  font-size: 1rem;
}

/* Sections */
.admin-section {
  background-color: var(--color-surface);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1.5rem;
  box-shadow: var(--shadow);
}

.admin-section__title {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--color-text);
  margin-bottom: 1rem;
}

/* Form elements */
.admin-form-row {
  display: flex;
  align-items: flex-end;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.admin-form-group {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.admin-label {
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.admin-input {
  background-color: var(--color-bg);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius);
  color: var(--color-text);
  padding: 0.5rem 0.75rem;
  font-size: 0.9rem;
  min-width: 160px;
  transition: border-color 0.2s;
}

.admin-input:focus {
  outline: none;
  border-color: var(--color-accent);
}

.admin-input::placeholder {
  color: var(--color-text-muted);
  opacity: 0.6;
}

/* Buttons */
.admin-btn {
  padding: 0.5rem 1.25rem;
  border-radius: var(--radius);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  border: none;
  transition: opacity 0.2s, background-color 0.2s;
  white-space: nowrap;
}

.admin-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.admin-btn--primary {
  background-color: var(--color-accent);
  color: var(--color-on-accent);
}

.admin-btn--primary:hover:not(:disabled) {
  opacity: 0.85;
}

.admin-btn--confirm {
  background-color: var(--color-success);
  color: var(--color-on-accent);
}

.admin-btn--confirm:hover:not(:disabled) {
  opacity: 0.85;
}

.admin-btn--cancel {
  background-color: transparent;
  border: 1px solid var(--color-text-muted);
  color: var(--color-text-muted);
}

.admin-btn--cancel:hover:not(:disabled) {
  border-color: var(--color-text);
  color: var(--color-text);
}

/* Team multi-select grid */
.admin-team-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.4rem;
  max-height: 240px;
  overflow-y: auto;
  padding: 0.5rem;
  background-color: var(--color-bg);
  border-radius: var(--radius);
  border: 1px solid var(--color-primary);
  margin-bottom: 1rem;
}

.admin-team-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8rem;
  color: var(--color-text);
  cursor: pointer;
  padding: 0.25rem 0.4rem;
  border-radius: 4px;
  transition: background-color 0.15s;
}

.admin-team-label:hover {
  background-color: var(--color-primary);
}

.admin-team-checkbox {
  accent-color: var(--color-accent);
  width: 14px;
  height: 14px;
  cursor: pointer;
}

/* Status messages */
.admin-status {
  font-size: 0.85rem;
  margin-top: 0.5rem;
  padding: 0.35rem 0;
}

.admin-status--info {
  color: var(--color-text-muted);
}

.admin-status--success {
  color: var(--color-success);
}

.admin-status--error {
  color: var(--color-error);
}

/* Error display */
.admin-error {
  font-size: 0.85rem;
  color: var(--color-error);
  min-height: 1.2em;
  margin-top: 0.5rem;
}

.admin-error--visible {
  color: var(--color-error);
}

/* Preview table */
.admin-preview {
  margin-top: 0.5rem;
}

.admin-preview__title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
  margin-bottom: 0.75rem;
}

.admin-preview__warning {
  color: #ff9800;
  font-size: 0.85rem;
  margin-bottom: 0.75rem;
  padding: 0.5rem 0.75rem;
  background-color: rgba(255, 152, 0, 0.1);
  border-radius: var(--radius);
  border-left: 3px solid #ff9800;
}

.admin-preview__table-wrapper {
  overflow-x: auto;
  margin-bottom: 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--color-primary);
}

.admin-preview__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8rem;
}

.admin-preview__table th,
.admin-preview__table td {
  padding: 0.5rem 0.75rem;
  text-align: left;
  border-bottom: 1px solid var(--color-primary);
  white-space: nowrap;
}

.admin-preview__table th {
  background-color: var(--color-primary);
  color: var(--color-text-muted);
  font-weight: 600;
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.5px;
}

.admin-preview__table td {
  color: var(--color-text);
}

.admin-preview__table a {
  color: var(--color-accent);
  text-decoration: none;
}

.admin-preview__table a:hover {
  text-decoration: underline;
}

.admin-preview-row--duplicate {
  background-color: rgba(255, 152, 0, 0.08);
}

.admin-duplicate-flag {
  color: #ff9800;
  font-weight: 600;
  font-size: 0.75rem;
}

.admin-no-link {
  color: var(--color-text-muted);
}

/* Preview actions */
.admin-preview__actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
}

/* Responsive adjustments */
@media (max-width: 767px) {
  .admin-form-row {
    flex-direction: column;
    align-items: stretch;
  }

  .admin-input {
    min-width: unset;
    width: 100%;
  }

  .admin-team-grid {
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    max-height: 200px;
  }

  .admin-preview__table {
    font-size: 0.7rem;
  }

  .admin-preview__table th,
  .admin-preview__table td {
    padding: 0.4rem 0.5rem;
  }

  .admin-preview__actions {
    flex-direction: column;
  }

  .admin-btn {
    width: 100%;
    text-align: center;
  }
}


/* ==========================================================================
   Payments View Styles
   ========================================================================== */

.payments-view {
  width: 100%;
}

.payments-view__title {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--color-text);
  margin-bottom: 1.5rem;
}

.payments-loading,
.payments-empty {
  text-align: center;
  color: var(--color-text-muted);
  padding: 1.5rem 1rem;
  font-size: 0.95rem;
}

.payments-form-section,
.payments-section {
  background-color: var(--color-surface);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1.5rem;
  box-shadow: var(--shadow);
}

.payments-section__title {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--color-text);
  margin-bottom: 1rem;
}

/* Form layout */
.payments-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.payments-form__row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.payments-form__group {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  flex: 1 1 200px;
  min-width: 0;
}

.payments-form__label {
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.payments-form__input {
  background-color: var(--color-bg);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius);
  color: var(--color-text);
  padding: 0.55rem 0.75rem;
  font-size: 0.95rem;
  min-height: 44px;
  transition: border-color 0.2s;
  width: 100%;
}

.payments-form__input:focus {
  outline: none;
  border-color: var(--color-accent);
}

.payments-form__error {
  color: var(--color-error);
  font-size: 0.8rem;
  min-height: 1em;
}

.payments-form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.25rem;
}

.payments-form__submit {
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  border: none;
  padding: 0.6rem 1.4rem;
  border-radius: var(--radius);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  min-height: 44px;
  min-width: 160px;
  transition: opacity 0.2s, background-color 0.2s;
}

.payments-form__submit:hover:not(:disabled) {
  opacity: 0.85;
}

.payments-form__submit:disabled {
  background-color: var(--color-primary);
  color: var(--color-text-muted);
  cursor: not-allowed;
  opacity: 0.6;
}

.payments-form__status {
  font-size: 0.85rem;
}

.payments-form__status--success {
  color: var(--color-success);
}

.payments-form__status--error {
  color: var(--color-error);
}

.payments-form__status--info {
  color: var(--color-text-muted);
}

/* Ledger (net balances) */
.payments-ledger {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0;
  margin: 0;
}

.payments-ledger__item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.85rem;
  background-color: var(--color-bg);
  border-radius: var(--radius);
  border: 1px solid var(--color-primary);
  font-size: 0.9rem;
}

.payments-ledger__item--me {
  border-color: var(--color-accent);
}

.payments-ledger__from,
.payments-ledger__to {
  font-weight: 600;
  color: var(--color-text);
}

.payments-ledger__owes {
  color: var(--color-text-muted);
  font-size: 0.85rem;
}

.payments-ledger__amount {
  margin-left: auto;
  color: var(--color-accent);
  font-weight: 700;
}

/* Payment list table */
.payments-list__table-wrapper {
  overflow-x: auto;
  border-radius: var(--radius);
  border: 1px solid var(--color-primary);
}

.payments-list__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.payments-list__table th,
.payments-list__table td {
  padding: 0.55rem 0.75rem;
  text-align: left;
  border-bottom: 1px solid var(--color-primary);
}

.payments-list__table th {
  background-color: var(--color-primary);
  color: var(--color-text-muted);
  font-weight: 600;
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.5px;
  white-space: nowrap;
}

.payments-list__table td {
  color: var(--color-text);
}

.payments-list__row:last-child td {
  border-bottom: none;
}

.payments-list__date {
  color: var(--color-text-muted);
  white-space: nowrap;
}

.payments-list__arrow {
  color: var(--color-text-muted);
  text-align: center;
  width: 1.5rem;
}

.payments-list__amount {
  font-weight: 700;
  color: var(--color-accent);
  white-space: nowrap;
  text-align: right;
}

@media (max-width: 767px) {
  .payments-form__row {
    flex-direction: column;
    gap: 0.75rem;
  }

  .payments-form__group {
    flex: 1 1 auto;
  }

  .payments-form__submit {
    width: 100%;
  }

  .payments-list__table {
    font-size: 0.78rem;
  }

  .payments-list__table th,
  .payments-list__table td {
    padding: 0.45rem 0.5rem;
  }
}


/* ==========================================================================
   Responsive Navigation, Tap Targets, and Layout Safety (Task 15.1)
   Requirements: 2.1, 2.2, 2.3, 2.4, 2.6
   ========================================================================== */

/* Prevent horizontal scrollbar across all viewports (320px–2560px).
   Use overflow-x: clip so position: sticky on .app-header keeps working. */
html {
  overflow-x: clip;
}

body {
  overflow-x: clip;
  max-width: 100%;
}

/* Allow header to wrap on narrow screens so brand + auth + nav stack cleanly */
@media (max-width: 767px) {
  .app-header {
    flex-wrap: wrap;
    padding: 0.5rem 0.75rem;
    gap: 0.5rem;
  }

  .header-brand h1 {
    font-size: 1.1rem;
  }

  /* Bump the hamburger to a 44×44 touch target on mobile (WCAG 2.5.5).
     Desktop sits at 40×40; everything else is identical. */
  .nav-toggle {
    width: 44px;
    height: 44px;
  }
}

/* Tap targets — ensure interactive elements meet 44x44 CSS pixels on mobile.
   Desktop sits at 36×36, so we bump up here to clear the WCAG 2.5.5
   tap-target minimum on touch. */
@media (max-width: 767px) {
  .game-card__icon {
    width: 44px;
    height: 44px;
  }

  .game-card__rank-badge {
    width: 32px;
    height: 32px;
    font-size: 0.95rem;
  }

  .voting-floating-submit,
  .admin-btn,
  .auth-retry-btn,
  .auth-signout-btn,
  .payments-form__submit {
    min-height: 44px;
  }

  /* Form controls comfortable on touch */
  .admin-input,
  .payments-form__input {
    min-height: 44px;
  }

  /* Buttons inside any form layout default to 44px tall */
  button,
  [role="button"],
  input[type="submit"],
  input[type="button"] {
    min-height: 44px;
  }

  /* Avoid forcing 44px on very compact inline controls (e.g., team-grid checkboxes)
     where the parent label is already an adequate hit area */
  .admin-team-checkbox {
    min-height: 0;
  }
}

/* Form layouts: enforce stacked labels above inputs below 768px.
   Most form-groups already use column direction; this is a defensive default
   for any view-specific form rows that group inputs side-by-side. */
@media (max-width: 767px) {
  .admin-form-row,
  .payments-form__row {
    flex-direction: column;
    align-items: stretch;
  }

  .admin-form-group,
  .payments-form__group {
    width: 100%;
    flex: 1 1 auto;
  }
}

/* Wide viewport tweak — slightly larger card minimums on big screens (≥1200px) */
@media (min-width: 1200px) {
  .games-list {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  }

  .app-main {
    padding: 2rem;
  }
}

/* Very wide displays (≥1920px) */
@media (min-width: 1920px) {
  .app-main {
    max-width: 1600px;
  }
}

/* ==========================================================================
   Trip Selector Dropdown (Task 15.2)
   Mounted by js/trip_selector.js into #trip-selector in the header. Sits
   between the nav and the auth container. Hidden when fewer than two
   non-archived/non-cancelled trips exist; the module toggles the .hidden
   class. Hidden on viewports below 480px to keep the mobile header clean.
   ========================================================================== */

.trip-selector {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.5rem;
}

.trip-selector__label {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.trip-selector__select {
  background-color: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius);
  padding: 0.35rem 0.55rem;
  font-size: 0.85rem;
  font-weight: 500;
  min-height: 32px;
  max-width: 220px;
  cursor: pointer;
  transition: border-color 0.2s;
}

.trip-selector__select:focus {
  outline: none;
  border-color: var(--color-accent);
}

/* Hide the dropdown on very narrow viewports — the header already has
   brand + auth + hamburger competing for room. The dropdown stays
   functional on tablet and desktop. */
@media (max-width: 479px) {
  .trip-selector {
    display: none !important;
  }
}


/* ==========================================================================
   Reselection Prompt Modal (Task 21.2, Requirements 20.1 / 20.2 / 20.3 / 20.5)
   Mounted by js/reselection_prompt.js into document.body when any
   trip-scoped fetch returns the flat 409 ``{"error": "no_live_active_trip"}``
   envelope. The root element is a positioned overlay; a sibling backdrop
   dims the page; the card is centered. Once dismissed (or after the user
   navigates to Trips via the admin button), the modal is removed and
   suppressed for the rest of the page load.
   ========================================================================== */

.reselection-modal-root {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.reselection-modal-root .modal-backdrop {
  position: absolute;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.55);
  /* The backdrop is intentionally not click-to-dismiss — the user must
     engage with the explicit Dismiss / Go to Trips controls. */
}

.reselection-modal {
  position: relative;
  background-color: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.5rem;
  max-width: 420px;
  width: 100%;
  /* Sit above the backdrop within the same stacking context. */
  z-index: 1;
}

.reselection-modal__title {
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: 0.75rem;
}

.reselection-modal__message {
  font-size: 0.95rem;
  line-height: 1.4;
  color: var(--color-text-muted);
  margin-bottom: 1.25rem;
}

.reselection-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.reselection-modal__primary,
.reselection-modal__secondary {
  font-size: 0.9rem;
  font-weight: 600;
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius);
  cursor: pointer;
  border: 1px solid transparent;
  min-height: 36px;
  transition: background-color 0.15s, border-color 0.15s;
}

.reselection-modal__primary {
  background-color: var(--color-accent);
  color: var(--color-text);
}

.reselection-modal__primary:hover,
.reselection-modal__primary:focus {
  outline: none;
  background-color: var(--color-accent-hover);
}

.reselection-modal__secondary {
  background-color: transparent;
  color: var(--color-text);
  border-color: var(--color-primary);
}

.reselection-modal__secondary:hover,
.reselection-modal__secondary:focus {
  outline: none;
  border-color: var(--color-accent);
}


/* ==========================================================================
   Admin "Game Pool" view (rewritten in the bulk-select pass)
   Adds chip-based team filters, a sticky-toolbar bulk-add UI, and an
   "Already Added / Remove" affordance per row. The legacy admin-team-grid
   styles above are kept in case any other view reuses them; the new
   class names are admin-team-chip(s) and admin-pool-*.
   ========================================================================== */

.admin-view__lead {
  color: var(--color-text-muted);
  font-size: 0.9rem;
  margin-bottom: 1.25rem;
  max-width: 800px;
  line-height: 1.5;
}

.admin-pool-filters {
  margin-bottom: 1rem;
}

.admin-team-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.4rem 0;
}

.admin-team-chip {
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  border: 1px solid var(--color-primary);
  border-radius: 999px;
  padding: 0.3rem 0.75rem;
  font-size: 0.8rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
  min-height: 32px;
}

.admin-team-chip:hover {
  border-color: var(--color-accent);
  color: var(--color-text);
}

.admin-team-chip--active {
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  border-color: var(--color-accent);
}

.admin-team-chip-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.4rem;
}

/* ----- Toolbar above the pool table ------------------------------------ */

.admin-pool-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1rem 0 0.5rem;
  padding: 0.5rem 0.75rem;
  background-color: var(--color-bg);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius);
  position: sticky;
  top: 64px; /* below the sticky header */
  z-index: 5;
}

.admin-pool-count {
  color: var(--color-text-muted);
  font-size: 0.85rem;
}

.admin-pool-spacer {
  flex: 1 1 auto;
}

/* ----- Pool table additions -------------------------------------------- */

.admin-pool-th--checkbox {
  width: 36px;
  text-align: center;
}

.admin-pool-row-checkbox {
  accent-color: var(--color-accent);
  width: 16px;
  height: 16px;
  cursor: pointer;
}

.admin-pool-row--added {
  background-color: rgba(76, 175, 80, 0.08);
}

.admin-pool-row--added td {
  color: var(--color-text-muted);
}

.admin-status-badge {
  display: inline-flex;
  align-items: center;
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.admin-status-badge--added {
  background-color: var(--color-success);
  color: var(--color-on-accent);
}

.admin-pool-remove-btn {
  background-color: transparent;
  color: var(--color-text-muted);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius);
  padding: 0.25rem 0.5rem;
  margin-left: 0.4rem;
  font-size: 0.75rem;
  cursor: pointer;
  min-height: 28px;
  transition: color 0.15s, border-color 0.15s;
}

.admin-pool-remove-btn:hover {
  color: var(--color-error);
  border-color: var(--color-error);
}

/* Pool table on mobile: drop venue + status columns to keep it readable. */
@media (max-width: 767px) {
  .admin-pool-toolbar {
    /* Don't try to be sticky on mobile; the header is already big. */
    position: static;
    top: auto;
  }
}


/* Sortable column headers in the Game Pool table. The ▲/▼ indicator
   slot is always present so the column width doesn't shift when the
   sort changes. */
.admin-pool-th-sortable {
  cursor: pointer;
  user-select: none;
  transition: color 0.15s, background-color 0.15s;
}

.admin-pool-th-sortable:hover {
  background-color: var(--color-primary);
  color: var(--color-text);
}

.admin-pool-th-sortable--active {
  color: var(--color-accent);
}

.admin-sort-indicator {
  display: inline-block;
  min-width: 0.75em;
  margin-left: 0.25rem;
  font-size: 0.7em;
  vertical-align: middle;
}
